Merge branch 'master' of https://github.com/Budibase/budibase into property-panel/screen-and-page-props

This commit is contained in:
Conor_Mack 2020-06-03 19:48:35 +01:00
commit 058b31b397
59 changed files with 522 additions and 323 deletions

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#6A78D1;}
.st2{fill:#49C39E;}
.st3{fill:#F2545B;}
.st4{fill:#F5C26B;}
</style>
<g>
<g>
<path class="st0" d="M215,14.1l136.2,79.8c9.3,5.4,15,15.5,15,26.4v159.5c0,10.9-5.7,20.9-15,26.4L215,385.9
c-9.3,5.4-20.7,5.4-30,0L48.8,306.2c-9.3-5.4-15-15.5-15-26.4V120.2c0-10.9,5.7-20.9,15-26.4L185,14.1
C194.3,8.6,205.7,8.6,215,14.1z"/>
</g>
<g>
<path class="st1" d="M288.8,273.7l-83,43.6c-3.6,1.9-8,1.9-11.6,0l-83-43.6c-8.2-4.3-8.2-15.5,0-19.8l83-43.6
c3.6-1.9,8-1.9,11.6,0l83,43.6C297.1,258.3,297.1,269.4,288.8,273.7z"/>
<path class="st2" d="M288.8,231.2l-83,43.6c-3.6,1.9-8,1.9-11.6,0l-83-43.6c-8.2-4.3-8.2-15.5,0-19.8l83-43.6
c3.6-1.9,8-1.9,11.6,0l83,43.6C297.1,215.7,297.1,226.9,288.8,231.2z"/>
<path class="st3" d="M288.8,188.6l-83,43.6c-3.6,1.9-8,1.9-11.6,0l-83-43.6c-8.2-4.3-8.2-15.5,0-19.8l83-43.6
c3.6-1.9,8-1.9,11.6,0l83,43.6C297.1,173.1,297.1,184.3,288.8,188.6z"/>
<path class="st4" d="M288.8,146l-83,43.6c-3.6,1.9-8,1.9-11.6,0l-83-43.6c-8.2-4.3-8.2-15.5,0-19.8l83-43.6c3.6-1.9,8-1.9,11.6,0
l83,43.6C297.1,130.6,297.1,141.7,288.8,146z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,8 +1,7 @@
/* Budibase Component Styles */ /* Budibase Component Styles */
.header { .header {
font-size: 0.75rem; font-size: 0.75rem;
color: #000333; color: var(--ink);
opacity: 0.4;
text-transform: uppercase; text-transform: uppercase;
margin-top: 1rem; margin-top: 1rem;
font-weight: 500; font-weight: 500;
@ -81,10 +80,9 @@
max-width: 250px; max-width: 250px;
height: 35px; height: 35px;
border-radius: 3px; border-radius: 3px;
border: 1px solid #DBDBDB; border: 1px solid var(--grey-dark);
text-align: left; text-align: left;
letter-spacing: 0.7px; color: var(--ink);
color: #000333;
font-size: 16px; font-size: 16px;
padding-left: 5px; padding-left: 5px;
} }
@ -103,27 +101,32 @@
} }
.budibase__table { .budibase__table {
border: 1px solid #ccc; border: 1px solid var(--grey-dark);
background: #fff; background: #fff;
border-radius: 2px; border-radius: 2px;
} }
.budibase__table thead { .budibase__table thead {
background: #fafafa; background: var(--blue-light);
} }
.budibase__table thead > tr > th { .budibase__table thead > tr > th {
color: var(--button-text); color: var(--ink);
text-transform: capitalize; text-transform: capitalize;
font-weight: 500; font-weight: 500;
} }
.budibase__table tr { .budibase__table tr {
border-bottom: 1px solid #ccc; border-bottom: 1px solid var(--grey-light);
} }
.button--toggled { .button--toggled {
background: #fafafa; background: var(--blue-light);
color: var(--button-text); color: var(--ink-light);
padding: 10px; width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
} }

View File

@ -3,6 +3,7 @@ const apiCall = method => async (url, body) => {
method: method, method: method,
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"User-Agent": "Budibase Builder",
}, },
body: body && JSON.stringify(body), body: body && JSON.stringify(body),
}) })
@ -14,11 +15,11 @@ const apiCall = method => async (url, body) => {
return response return response
} }
const post = apiCall("POST") export const post = apiCall("POST")
const get = apiCall("GET") export const get = apiCall("GET")
const patch = apiCall("PATCH") export const patch = apiCall("PATCH")
const del = apiCall("DELETE") export const del = apiCall("DELETE")
const put = apiCall("PUT") export const put = apiCall("PUT")
export default { export default {
post, post,

View File

@ -1,6 +1,7 @@
<script> <script>
export let disabled = false export let disabled = false
export let hidden = false export let hidden = false
export let secondary = false
export let primary = true export let primary = true
export let cancel = false export let cancel = false
export let alert = false export let alert = false
@ -11,6 +12,7 @@
on:click on:click
class="button" class="button"
class:hidden class:hidden
class:secondary
class:primary class:primary
class:alert class:alert
class:cancel class:cancel
@ -22,12 +24,14 @@
<style> <style>
.primary { .primary {
color: #ffffff; color: #ffffff;
background: #0055ff; background: var(--blue);
border: solid 1px var(--blue);
} }
.alert { .alert {
color: rgba(255, 0, 31, 1); color: white;
background: rgba(255, 0, 31, 0.1); background: #e26d69;
border: solid 1px #e26d69;
} }
.cancel { .cancel {
@ -35,18 +39,22 @@
background: none; background: none;
} }
.secondary {
color: var(--ink);
border: solid 1px var(--grey-dark);
background: white;
}
.button { .button {
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 500;
border-radius: 5px; border-radius: 3px;
border: none;
padding: 10px 20px; padding: 10px 20px;
height: 45px; height: 40px;
} }
.button:hover { .button:hover {
cursor: pointer; cursor: pointer;
font-weight: 600;
filter: saturate(90%); filter: saturate(90%);
} }

View File

@ -14,8 +14,7 @@
background: var(--secondary80); background: var(--secondary80);
color: var(--white); color: var(--white);
font-family: "Courier New", Courier, monospace; font-family: "Courier New", Courier, monospace;
width: 95%; height: 200px;
height: 100px;
border-radius: 5px; border-radius: 5px;
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<script> <script>
import {onMount} from "svelte" import { onMount } from "svelte"
import { buildStyle } from "../../helpers.js" import { buildStyle } from "../../helpers.js"
export let value = "" export let value = ""
export let textAlign = "left" export let textAlign = "left"
@ -18,11 +18,19 @@
onChange(_value) onChange(_value)
} }
$: displayValue = suffix && value && value.endsWith(suffix) ? value.replace(new RegExp(`${suffix}$`), "") : (value || "") $: displayValue =
suffix && value && value.endsWith(suffix)
? value.replace(new RegExp(`${suffix}$`), "")
: value || ""
</script> </script>
<input class:centerPlaceholder type="text" value={displayValue} {placeholder} {style} on:change={e => handleChange(e.target.value)} /> <input
class:centerPlaceholder
type="text"
value={displayValue}
{placeholder}
{style}
on:change={e => handleChange(e.target.value)} />
<style> <style>
input { input {

View File

@ -13,7 +13,9 @@
value.splice(idx, 1, val !== "auto" ? val + suffix : val) value.splice(idx, 1, val !== "auto" ? val + suffix : val)
value = value value = value
let _value = value.map(v => (!v.endsWith(suffix) && v !== "auto" ? v + suffix : v)) let _value = value.map(v =>
!v.endsWith(suffix) && v !== "auto" ? v + suffix : v
)
onChange(_value) onChange(_value)
} }
@ -44,5 +46,4 @@
.inputs-group { .inputs-group {
flex: 1; flex: 1;
} }
</style> </style>

View File

@ -50,10 +50,10 @@
<style> <style>
.uk-modal-dialog { .uk-modal-dialog {
border-radius: 0.3rem; border-radius: 0.3rem;
width: 60%; width: 520px;
height: 80vh; height: 80vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0; padding: 40px;
} }
</style> </style>

View File

@ -20,11 +20,9 @@
<style> <style>
.select-container { .select-container {
font-size: 14px; font-size: 14px;
color: var(--secondary60);
font-weight: bold;
position: relative; position: relative;
max-width: 400px; border: var(--grey-dark) 1px solid;
min-width: 275px; max-width: 256px;
} }
.adjusted { .adjusted {
@ -43,7 +41,7 @@
font-family: sans-serif; font-family: sans-serif;
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
color: #000333; color: var(--ink);
padding: 0 40px 0px 20px; padding: 0 40px 0px 20px;
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
@ -63,6 +61,6 @@
width: 30px; width: 30px;
height: 30px; height: 30px;
pointer-events: none; pointer-events: none;
color: var(--secondary100); color: var(--ink);
} }
</style> </style>

View File

@ -145,19 +145,19 @@
} }
table { table {
border: 1px solid #ccc; border: 1px solid var(--grey-dark);
background: #fff; background: #fff;
border-radius: 3px; border-radius: 3px;
border-collapse: collapse; border-collapse: collapse;
} }
thead { thead {
background: #f9f9f9; background: var(--blue-light);
border: 1px solid #ccc; border: 1px solid var(--grey-dark);
} }
thead th { thead th {
color: var(--button-text); color: var(--ink);
text-transform: capitalize; text-transform: capitalize;
font-weight: 500; font-weight: 500;
font-size: 14px; font-size: 14px;
@ -166,14 +166,14 @@
} }
tbody tr { tbody tr {
border-bottom: 1px solid #ccc; border-bottom: 1px solid var(--grey-dark);
transition: 0.3s background-color; transition: 0.3s background-color;
color: var(--secondary100); color: var(--ink);
font-size: 14px; font-size: 14px;
} }
tbody tr:hover { tbody tr:hover {
background: #fafafa; background: var(--grey-light);
} }
.table-controls { .table-controls {

View File

@ -35,7 +35,7 @@
} }
</script> </script>
<header> <div class="heading">
{#if !showFieldView} {#if !showFieldView}
<i class="ri-list-settings-line button--toggled" /> <i class="ri-list-settings-line button--toggled" />
<h3 class="budibase__title--3">Create / Edit Model</h3> <h3 class="budibase__title--3">Create / Edit Model</h3>
@ -43,22 +43,20 @@
<i class="ri-file-list-line button--toggled" /> <i class="ri-file-list-line button--toggled" />
<h3 class="budibase__title--3">Create / Edit Field</h3> <h3 class="budibase__title--3">Create / Edit Field</h3>
{/if} {/if}
</header> </div>
{#if !showFieldView} {#if !showFieldView}
<div class="padding"> <div class="padding">
<h4 class="budibase__label--big">Settings</h4>
{#if $store.errors && $store.errors.length > 0} {#if $store.errors && $store.errors.length > 0}
<ErrorsBox errors={$store.errors} /> <ErrorsBox errors={$store.errors} />
{/if} {/if}
<div class="textbox">
<Textbox label="Name" bind:text={model.name} /> <Textbox label="Name" bind:text={model.name} />
</div>
<div class="table-controls"> <div class="table-controls">
<span class="budibase__label--big">Fields</span> <span class="label">Fields</span>
<h4 class="hoverable new-field" on:click={() => (showFieldView = true)}> <div class="hoverable new-field" on:click={() => (showFieldView = true)}>
Add new field Add new field
</h4> </div>
</div> </div>
<table class="uk-table fields-table budibase__table"> <table class="uk-table fields-table budibase__table">
@ -67,7 +65,6 @@
<th>Edit</th> <th>Edit</th>
<th>Name</th> <th>Name</th>
<th>Type</th> <th>Type</th>
<th>Values</th>
<th /> <th />
</tr> </tr>
</thead> </thead>
@ -90,9 +87,9 @@
{/each} {/each}
</tbody> </tbody>
</table> </table>
<div class="uk-margin"> <footer>
<ActionButton color="secondary" on:click={saveModel}>Save</ActionButton> <ActionButton color="secondary" on:click={saveModel}>Save</ActionButton>
</div> </footer>
</div> </div>
{:else} {:else}
<FieldView <FieldView
@ -104,41 +101,63 @@
<style> <style>
.padding { .padding {
padding: 20px; padding-top: 40px;
}
.label {
font-size: 14px;
font-weight: 500;
}
.textbox {
margin: 0px 40px 0px 40px;
font-size: 14px;
font-weight: 500;
} }
.new-field { .new-field {
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
color: var(--button-text); color: var(--blue);
} }
.fields-table { .fields-table {
margin: 1rem 1rem 0rem 0rem; margin: 8px 40px 0px 40px;
border-collapse: collapse; border-collapse: collapse;
width: 88%;
} }
tbody > tr:hover { tbody > tr:hover {
background-color: var(--primary10); background-color: var(--grey-light);
} }
.table-controls { .table-controls {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin: 0px 40px;
} }
.ri-more-line:hover { .ri-more-line:hover {
cursor: pointer; cursor: pointer;
} }
heading { .heading {
padding: 20px 20px 0 20px; padding: 40px 40px 0 40px;
display: flex; display: flex;
align-items: center; align-items: center;
} }
h3 { h3 {
margin: 0 0 0 10px; margin: 0 0 0 10px;
color: var(--ink);
}
footer {
background-color: var(--grey-light);
margin-top: 40px;
padding: 20px 40px 20px 40px;
display: flex;
justify-content: flex-end;
} }
</style> </style>

View File

@ -79,19 +79,28 @@
</form> </form>
</div> </div>
<footer> <footer>
<div class="button">
<ActionButton secondary on:click={goBack}>Cancel</ActionButton>
</div>
<ActionButton primary on:click={save}>Save</ActionButton> <ActionButton primary on:click={save}>Save</ActionButton>
<ActionButton alert on:click={goBack}>Cancel</ActionButton>
</footer> </footer>
<style> <style>
.root { .root {
margin: 20px; margin: 40px;
} }
footer { footer {
padding: 20px; padding: 20px 40px;
border-radius: 0 0 5px 5px; border-radius: 0 0 5px 5px;
bottom: 0; bottom: 0;
left: 0; left: 0;
background: #fafafa; background: var(--grey-light);
display: flex;
align-items: center;
justify-content: flex-end;
}
.button {
margin-right: 20px;
} }
</style> </style>

View File

@ -39,51 +39,49 @@
} }
</script> </script>
<heading> <div class="header">
<i class="ri-eye-line button--toggled" /> <i class="ri-eye-line button--toggled" />
<h3 class="budibase__title--3">Create / Edit View</h3> <h3 class="budibase__title--3">Create / Edit View</h3>
</heading> </div>
<form on:submit|preventDefault class="uk-form-stacked root"> <form on:submit|preventDefault class="uk-form-stacked root">
<h4 class="budibase__label--big">Settings</h4>
{#if $store.errors && $store.errors.length > 0} {#if $store.errors && $store.errors.length > 0}
<ErrorsBox errors={$store.errors} /> <ErrorsBox errors={$store.errors} />
{/if} {/if}
<div class="uk-grid-small" uk-grid> <div class="main">
<div class="uk-width-1-2@s"> <div class="uk-grid-small" uk-grid>
<Textbox bind:text={view.name} label="Name" /> <div class="uk-width-1-2@s">
<Textbox bind:text={view.name} label="Name" />
</div>
</div>
<div class="code-snippets">
{#each Object.values(SNIPPET_EDITORS) as snippetType}
<span
class="snippet-selector__heading hoverable"
class:highlighted={currentSnippetEditor === snippetType}
on:click={() => (currentSnippetEditor = snippetType)}>
{snippetType}
</span>
{/each}
{#if currentSnippetEditor === SNIPPET_EDITORS.MAP}
<CodeArea bind:text={view.map} label="Map" />
{:else if currentSnippetEditor === SNIPPET_EDITORS.FILTER}
<CodeArea bind:text={view.filter} label="Filter" />
{:else if currentSnippetEditor === SNIPPET_EDITORS.REDUCE}
<CodeArea bind:text={view.reduce} label="Reduce" />
{/if}
</div> </div>
</div> </div>
<div class="buttons">
<h4 class="budibase__label--big">Snippets</h4> <div class="button">
{#each Object.values(SNIPPET_EDITORS) as snippetType} <ActionButton secondary on:click={deleteView}>Delete</ActionButton>
<span </div>
class="snippet-selector__heading hoverable" <ActionButton color="secondary" on:click={saveView}>Save</ActionButton>
class:highlighted={currentSnippetEditor === snippetType} </div>
on:click={() => (currentSnippetEditor = snippetType)}>
{snippetType}
</span>
{/each}
{#if currentSnippetEditor === SNIPPET_EDITORS.MAP}
<CodeArea bind:text={view.map} label="Map" />
{:else if currentSnippetEditor === SNIPPET_EDITORS.FILTER}
<CodeArea bind:text={view.filter} label="Filter" />
{:else if currentSnippetEditor === SNIPPET_EDITORS.REDUCE}
<CodeArea bind:text={view.reduce} label="Reduce" />
{/if}
<ActionButton color="secondary" on:click={saveView}>Save</ActionButton>
<ActionButton alert on:click={deleteView}>Delete</ActionButton>
</form> </form>
<style> <style>
.root { .root {
height: 100%; height: 100%;
padding: 15px;
}
.snippet-selector__heading {
margin-right: 20px;
opacity: 0.7;
} }
.highlighted { .highlighted {
@ -92,11 +90,38 @@
h3 { h3 {
margin: 0 0 0 10px; margin: 0 0 0 10px;
color: var(--ink);
} }
heading { .snippet-selector__heading {
padding: 20px 20px 0 20px; margin-right: 20px;
font-size: 14px;
color: var(--ink-lighter);
}
.header {
padding: 20px 40px 0 40px;
display: flex; display: flex;
align-items: center; align-items: center;
} }
.main {
margin: 20px 40px 0px 40px;
}
.code-snippets {
margin: 20px 0px 20px 0px;
}
.buttons {
display: flex;
justify-content: flex-end;
background-color: var(--grey-light);
margin: 0 40px;
padding: 20px 0;
}
.button {
margin-right: 20px;
}
</style> </style>

View File

@ -22,7 +22,11 @@
</script> </script>
<form on:submit|preventDefault class="uk-form-stacked"> <form on:submit|preventDefault class="uk-form-stacked">
<div> <div class="main">
<div class="heading">
<i class="ri-list-settings-line button--toggled" />
<div class="title">Create User</div>
</div>
<div class="uk-margin"> <div class="uk-margin">
<label class="uk-form-label" for="form-stacked-text">Username</label> <label class="uk-form-label" for="form-stacked-text">Username</label>
<input class="uk-input" type="text" bind:value={username} /> <input class="uk-input" type="text" bind:value={username} />
@ -41,18 +45,40 @@
</div> </div>
</div> </div>
<footer> <footer>
<ActionButton alert on:click={onClosed}>Cancel</ActionButton> <div class="button">
<ActionButton secondary on:click={onClosed}>Cancel</ActionButton>
</div>
<ActionButton disabled={!valid} on:click={createUser}>Save</ActionButton> <ActionButton disabled={!valid} on:click={createUser}>Save</ActionButton>
</footer> </footer>
</form> </form>
<style> <style>
div { .main {
padding: 30px; padding: 40px 40px 20px 40px;
} }
.title {
font-size: 24px;
font-weight: 700;
color: var(--ink);
margin-left: 12px;
}
.heading {
display: flex;
align-items: baseline;
}
footer { footer {
display: flex;
align-items: center;
justify-content: flex-end;
padding: 20px; padding: 20px;
background: #fafafa; background: var(--grey-light);
border-radius: 0.5rem; border-radius: 0 0 5px 5px;
}
.button {
margin-right: 20px;
} }
</style> </style>

View File

@ -5,6 +5,7 @@
import { AppsIcon, InfoIcon, CloseIcon } from "components/common/Icons/" import { AppsIcon, InfoIcon, CloseIcon } from "components/common/Icons/"
import { getContext } from "svelte" import { getContext } from "svelte"
import { fade } from "svelte/transition" import { fade } from "svelte/transition"
import { post } from "builderStore/api"
const { open, close } = getContext("simple-modal") const { open, close } = getContext("simple-modal")
@ -33,15 +34,7 @@
const data = { name, description } const data = { name, description }
loading = true loading = true
try { try {
const response = await fetch("/api/applications", { const response = await post("/api/applications", data)
method: "POST", // *GET, POST, PUT, DELETE, etc.
credentials: "same-origin", // include, *same-origin, omit
headers: {
"Content-Type": "application/json",
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
const res = await response.json() const res = await response.json()

View File

@ -17,6 +17,7 @@ export default ({
body, html { body, html {
height: 100%!important; height: 100%!important;
font-family: Roboto !important;
} }
.lay-__screenslot__text { .lay-__screenslot__text {
width: 100%; width: 100%;

View File

@ -151,7 +151,7 @@
} }
</script> </script>
<div class="root" on:click|stopPropagation={() => {}}> <div class="root boundary" on:click|stopPropagation={() => {}}>
<button> <button>
<MoreIcon /> <MoreIcon />
</button> </button>

View File

@ -28,7 +28,7 @@
{/each} {/each}
</select> </select>
{:else} {:else}
<Input on:change={onChange} value={parameter.value} /> <Input {onChange} value={parameter.value} />
<button on:click={() => (isOpen = !isOpen)}> <button on:click={() => (isOpen = !isOpen)}>
<div class="icon" style={`transform: rotate(${isOpen ? 0 : 90}deg);`}> <div class="icon" style={`transform: rotate(${isOpen ? 0 : 90}deg);`}>
<ArrowDownIcon size={36} /> <ArrowDownIcon size={36} />

View File

@ -1,5 +1,5 @@
<script> <script>
import {buildStyle} from "../../helpers.js" import { buildStyle } from "../../helpers.js"
export let value = "" export let value = ""
export let text = "" export let text = ""
export let icon = "" export let icon = ""
@ -8,19 +8,24 @@
export let selected = false export let selected = false
export let fontWeight = "" export let fontWeight = ""
$: style = buildStyle({padding, fontWeight}) $: style = buildStyle({ padding, fontWeight })
$: useIcon = !!icon $: useIcon = !!icon
</script> </script>
<div class="flatbutton" {style} class:selected on:click={() => onClick(value || text)}> <div
class="flatbutton"
{style}
class:selected
on:click={() => onClick(value || text)}>
{#if useIcon} {#if useIcon}
<i class={icon} /> <i class={icon} />
{:else} {:else}
<span>{@html text}</span> <span>
{@html text}
</span>
{/if} {/if}
</div> </div>
<style> <style>
.flatbutton { .flatbutton {
cursor: pointer; cursor: pointer;
@ -42,7 +47,7 @@
color: #ffffff; color: #ffffff;
} }
i{ i {
font-size: 20px; font-size: 20px;
} }
</style> </style>

View File

@ -28,10 +28,8 @@
onChange(val) onChange(val)
} }
const checkSelected = val => const checkSelected = val =>
isMultiSelect ? value.includes(val) : value === val isMultiSelect ? value.includes(val) : value === val
</script> </script>
<div class="flatbutton-group"> <div class="flatbutton-group">

View File

@ -0,0 +1,16 @@
<script>
import { backendUiStore } from "builderStore"
export let value
</script>
<div class="uk-margin block-field">
<div class="uk-form-controls">
<select class="budibase__input" on:change {value}>
<option value="" />
{#each $backendUiStore.models as model}
<option value={model._id}>{model.name}</option>
{/each}
</select>
</div>
</div>

View File

@ -1,6 +1,6 @@
<script> <script>
import { onMount, beforeUpdate } from "svelte" import { onMount, beforeUpdate } from "svelte"
import {buildStyle} from "../../helpers.js" import { buildStyle } from "../../helpers.js"
export let options = [] export let options = []
export let value = "" export let value = ""
export let styleBindingProperty export let styleBindingProperty
@ -214,10 +214,10 @@
height: auto; height: auto;
padding: 5px 0px; padding: 5px 0px;
cursor: pointer; cursor: pointer;
padding-left: 10px padding-left: 10px;
} }
li:hover { li:hover {
background-color:#e6e6e6 background-color: #e6e6e6;
} }
</style> </style>

View File

@ -1,6 +1,7 @@
import Input from "../common/Input.svelte" import Input from "../common/Input.svelte"
import OptionSelect from "./OptionSelect.svelte" import OptionSelect from "./OptionSelect.svelte"
import Checkbox from "../common/Checkbox.svelte" import Checkbox from "../common/Checkbox.svelte"
import ModelSelect from "components/userInterface/ModelSelect.svelte"
import { all } from "./propertyCategories.js" import { all } from "./propertyCategories.js"
@ -261,25 +262,47 @@ export default {
}, },
{ {
name: "Login", name: "Login",
_component: "@budibase/standard-components/login",
description: description:
"A component that automatically generates a login screen for your app.", "A component that automatically generates a login screen for your app.",
icon: "ri-login-box-fill", icon: "ri-login-box-fill",
children: [], children: [],
properties: { design: { ...all } }, properties: {
design: { ...all },
settings: [
{
label: "Name",
key: "name",
control: Input,
},
{
label: "Logo",
key: "logo",
control: Input,
},
],
},
}, },
{ {
name: "Table", name: "Table",
_component: "@budibase/standard-components/datatable",
description: "A component that generates a table from your data.", description: "A component that generates a table from your data.",
icon: "ri-archive-drawer-fill", icon: "ri-archive-drawer-fill",
properties: { design: { ...all } }, properties: {
design: { ...all },
settings: [{ label: "Model", key: "model", control: ModelSelect }],
},
children: [], children: [],
}, },
{ {
name: "Form", name: "Form",
description: "A component that generates a form from your data.", description: "A component that generates a form from your data.",
icon: "ri-file-edit-fill", icon: "ri-file-edit-fill",
properties: { design: { ...all } }, properties: {
_component: "@budibase/materialdesign-components/Form", design: { ...all },
settings: [{ label: "Model", key: "model", control: ModelSelect }],
},
_component: "@budibase/standard-components/dataform",
template: { template: {
component: "@budibase/materialdesign-components/Form", component: "@budibase/materialdesign-components/Form",
description: "Form for saving a record", description: "Form for saving a record",
@ -292,7 +315,10 @@ export default {
_component: "@budibase/standard-components/datachart", _component: "@budibase/standard-components/datachart",
description: "Shiny chart", description: "Shiny chart",
icon: "ri-bar-chart-fill", icon: "ri-bar-chart-fill",
properties: { design: { ...all } }, properties: {
design: { ...all },
settings: [{ label: "Model", key: "model", control: ModelSelect }],
},
children: [], children: [],
}, },
{ {
@ -300,7 +326,10 @@ export default {
_component: "@budibase/standard-components/datalist", _component: "@budibase/standard-components/datalist",
description: "Shiny list", description: "Shiny list",
icon: "ri-file-list-fill", icon: "ri-file-list-fill",
properties: { design: { ...all } }, properties: {
design: { ...all },
settings: [{ label: "Model", key: "model", control: ModelSelect }],
},
children: [], children: [],
}, },
{ {
@ -336,8 +365,13 @@ export default {
children: [], children: [],
properties: { properties: {
design: { ...all }, design: { ...all },
settings: [{ label: "Logo URL", key: "logoUrl", control: Input }, ], settings: [
}, { label: "Logo URL", key: "logoUrl", control: Input },
{ label: "Title", key: "title", control: Input },
{ label: "Color", key: "color", control: Input },
{ label: "Background", key: "backgroundColor", control: Input },
],
},
}, },
], ],
}, },

View File

@ -5,9 +5,9 @@
</script> </script>
<div class="uk-margin block-field"> <div class="uk-margin block-field">
<label class="uk-form-label">Model</label>
<div class="uk-form-controls"> <div class="uk-form-controls">
<select class="budibase__input" bind:value> <select class="budibase__input" bind:value>
<option value="" />
{#each $backendUiStore.models as model} {#each $backendUiStore.models as model}
<option value={model}>{model.name}</option> <option value={model}>{model.name}</option>
{/each} {/each}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,6 +1,7 @@
<script> <script>
import Modal from "svelte-simple-modal" import Modal from "svelte-simple-modal"
import { store } from "builderStore" import { store } from "builderStore"
import { get } from "builderStore/api"
import { fade } from "svelte/transition" import { fade } from "svelte/transition"
import { isActive, goto, layout } from "@sveltech/routify" import { isActive, goto, layout } from "@sveltech/routify"
@ -14,7 +15,7 @@
let promise = getPackage() let promise = getPackage()
async function getPackage() { async function getPackage() {
const res = await fetch(`/api/${application}/appPackage`) const res = await get(`/api/${application}/appPackage`)
const pkg = await res.json() const pkg = await res.json()
if (res.ok) { if (res.ok) {

View File

@ -5,14 +5,14 @@
import { onMount } from "svelte" import { onMount } from "svelte"
import ActionButton from "components/common/ActionButton.svelte" import ActionButton from "components/common/ActionButton.svelte"
import IconButton from "components/common/IconButton.svelte" import IconButton from "components/common/IconButton.svelte"
import { get } from "builderStore/api"
import Spinner from "components/common/Spinner.svelte" import Spinner from "components/common/Spinner.svelte"
import CreateAppModal from "components/start/CreateAppModal.svelte" import CreateAppModal from "components/start/CreateAppModal.svelte"
let promise = getApps() let promise = getApps()
async function getApps() { async function getApps() {
const res = await fetch(`/api/applications`) const res = await get("/api/applications")
const json = await res.json() const json = await res.json()
if (res.ok) { if (res.ok) {

View File

@ -3,7 +3,7 @@
"version": "0.0.32", "version": "0.0.32",
"description": "Budibase CLI", "description": "Budibase CLI",
"repository": "https://github.com/Budibase/Budibase", "repository": "https://github.com/Budibase/Budibase",
"homepage": "https://budibase.com", "homepage": "https://www.budibase.com",
"main": "src/cli.js", "main": "src/cli.js",
"bin": { "bin": {
"budi": "bin/budi" "budi": "bin/budi"

View File

@ -16,3 +16,6 @@ PORT=4001
# error level for koa-pino # error level for koa-pino
LOG_LEVEL=error LOG_LEVEL=error
# Budibase app directory
BUDIBASE_DIR=~/.budibase

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#6A78D1;}
.st2{fill:#49C39E;}
.st3{fill:#F2545B;}
.st4{fill:#F5C26B;}
</style>
<g>
<g>
<path class="st0" d="M215,14.1l136.2,79.8c9.3,5.4,15,15.5,15,26.4v159.5c0,10.9-5.7,20.9-15,26.4L215,385.9
c-9.3,5.4-20.7,5.4-30,0L48.8,306.2c-9.3-5.4-15-15.5-15-26.4V120.2c0-10.9,5.7-20.9,15-26.4L185,14.1
C194.3,8.6,205.7,8.6,215,14.1z"/>
</g>
<g>
<path class="st1" d="M288.8,273.7l-83,43.6c-3.6,1.9-8,1.9-11.6,0l-83-43.6c-8.2-4.3-8.2-15.5,0-19.8l83-43.6
c3.6-1.9,8-1.9,11.6,0l83,43.6C297.1,258.3,297.1,269.4,288.8,273.7z"/>
<path class="st2" d="M288.8,231.2l-83,43.6c-3.6,1.9-8,1.9-11.6,0l-83-43.6c-8.2-4.3-8.2-15.5,0-19.8l83-43.6
c3.6-1.9,8-1.9,11.6,0l83,43.6C297.1,215.7,297.1,226.9,288.8,231.2z"/>
<path class="st3" d="M288.8,188.6l-83,43.6c-3.6,1.9-8,1.9-11.6,0l-83-43.6c-8.2-4.3-8.2-15.5,0-19.8l83-43.6
c3.6-1.9,8-1.9,11.6,0l83,43.6C297.1,173.1,297.1,184.3,288.8,188.6z"/>
<path class="st4" d="M288.8,146l-83,43.6c-3.6,1.9-8,1.9-11.6,0l-83-43.6c-8.2-4.3-8.2-15.5,0-19.8l83-43.6c3.6-1.9,8-1.9,11.6,0
l83,43.6C297.1,130.6,297.1,141.7,288.8,146z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 KiB

View File

@ -5,8 +5,9 @@ const newid = require("../../db/newid")
const env = require("../../environment") const env = require("../../environment")
const instanceController = require("./instance") const instanceController = require("./instance")
const { resolve, join } = require("path") const { resolve, join } = require("path")
const { copy, readJSON, writeJSON, exists } = require("fs-extra") const { copy, exists, readFile, writeFile } = require("fs-extra")
const { exec } = require("child_process") const { exec } = require("child_process")
const sqrl = require("squirrelly")
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const db = new CouchDB(ClientDb.name(env.CLIENT_ID)) const db = new CouchDB(ClientDb.name(env.CLIENT_ID))
@ -82,16 +83,27 @@ const createEmptyAppPackage = async (ctx, app) => {
await copy(templateFolder, newAppFolder) await copy(templateFolder, newAppFolder)
const packageJsonPath = join(appsFolder, app._id, "package.json") await updateJsonFile(join(appsFolder, app._id, "package.json"), {
const packageJson = await readJSON(packageJsonPath) name: npmFriendlyAppName(app.name),
})
packageJson.name = npmFriendlyAppName(app.name) await updateJsonFile(
join(appsFolder, app._id, "pages", "main", "page.json"),
await writeJSON(packageJsonPath, packageJson) app
)
await updateJsonFile(
join(appsFolder, app._id, "pages", "unauthenticated", "page.json"),
app
)
return newAppFolder return newAppFolder
} }
const updateJsonFile = async (filePath, app) => {
const json = await readFile(filePath, "utf8")
const newJson = sqrl.Render(json, app)
await writeFile(filePath, newJson, "utf8")
}
const runNpmInstall = async newAppFolder => { const runNpmInstall = async newAppFolder => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const cmd = `cd ${newAppFolder} && npm install` const cmd = `cd ${newAppFolder} && npm install`

View File

@ -10,6 +10,12 @@ exports.fetch = async function(ctx) {
ctx.body = body.rows.map(row => row.doc) ctx.body = body.rows.map(row => row.doc)
} }
exports.find = async function(ctx) {
const db = new CouchDB(ctx.params.instanceId)
const model = await db.get(ctx.params.id)
ctx.body = model
}
exports.create = async function(ctx) { exports.create = async function(ctx) {
const db = new CouchDB(ctx.params.instanceId) const db = new CouchDB(ctx.params.instanceId)
const newModel = { const newModel = {

View File

@ -13,7 +13,6 @@ exports.serveBuilder = async function(ctx) {
} }
exports.serveApp = async function(ctx) { exports.serveApp = async function(ctx) {
// TODO: update homedir stuff to wherever budi is run
// default to homedir // default to homedir
const appPath = resolve( const appPath = resolve(
budibaseAppsDir(), budibaseAppsDir(),
@ -26,7 +25,6 @@ exports.serveApp = async function(ctx) {
} }
exports.serveComponentLibrary = async function(ctx) { exports.serveComponentLibrary = async function(ctx) {
// TODO: update homedir stuff to wherever budi is run
// default to homedir // default to homedir
let componentLibraryPath = resolve( let componentLibraryPath = resolve(
budibaseAppsDir(), budibaseAppsDir(),

View File

@ -38,6 +38,7 @@ router
ctx.config = { ctx.config = {
latestPackagesFolder: budibaseAppsDir(), latestPackagesFolder: budibaseAppsDir(),
jwtSecret: env.JWT_SECRET, jwtSecret: env.JWT_SECRET,
useAppRootPath: true,
} }
ctx.isDev = env.NODE_ENV !== "production" && env.NODE_ENV !== "jest" ctx.isDev = env.NODE_ENV !== "production" && env.NODE_ENV !== "jest"
await next() await next()

View File

@ -43,6 +43,7 @@ router
router router
.get("/api/:instanceId/models", authorized(BUILDER), modelController.fetch) .get("/api/:instanceId/models", authorized(BUILDER), modelController.fetch)
.get("/api/:instanceId/models/:id", authorized(BUILDER), modelController.find)
.post("/api/:instanceId/models", authorized(BUILDER), modelController.create) .post("/api/:instanceId/models", authorized(BUILDER), modelController.create)
// .patch("/api/:instanceId/models", controller.update) // .patch("/api/:instanceId/models", controller.update)
.delete( .delete(

View File

@ -22,6 +22,7 @@ exports.supertest = async () => {
exports.defaultHeaders = { exports.defaultHeaders = {
Accept: "application/json", Accept: "application/json",
Cookie: ["builder:token=test-admin-secret"], Cookie: ["builder:token=test-admin-secret"],
"user-agent": "Budibase Builder",
} }
exports.createModel = async (request, instanceId, model) => { exports.createModel = async (request, instanceId, model) => {

View File

@ -13,23 +13,34 @@ module.exports = async (ctx, next) => {
return return
} }
if (ctx.cookies.get("builder:token") === env.ADMIN_SECRET) { const appToken = ctx.cookies.get("budibase:token")
ctx.isAuthenticated = true const builderToken = ctx.cookies.get("builder:token")
ctx.isBuilder = true const isBuilderAgent = ctx.headers["user-agent"] === "Budibase Builder"
// all admin api access should auth with buildertoken and 'Budibase Builder user agent
const shouldAuthAsBuilder = isBuilderAgent && builderToken
if (shouldAuthAsBuilder) {
if (builderToken === env.ADMIN_SECRET) {
ctx.isAuthenticated = true
ctx.isBuilder = true
} else {
ctx.isAuthenticated = false
ctx.isBuilder = false
}
await next() await next()
return return
} }
const token = ctx.cookies.get("budibase:token") if (!appToken) {
if (!token) {
ctx.isAuthenticated = false ctx.isAuthenticated = false
await next() await next()
return return
} }
try { try {
const jwtPayload = jwt.verify(token, ctx.config.jwtSecret) const jwtPayload = jwt.verify(appToken, ctx.config.jwtSecret)
ctx.user = { ctx.user = {
...jwtPayload, ...jwtPayload,

View File

@ -1,5 +1,5 @@
{ {
"name": "name", "name": "{{ name }}",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"author": "", "author": "",

View File

@ -1,5 +1,5 @@
{ {
"title": "Test App", "title": "{{ name }}",
"favicon": "./_shared/favicon.png", "favicon": "./_shared/favicon.png",
"stylesheets": [], "stylesheets": [],
"componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"], "componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"],
@ -9,8 +9,10 @@
"_id": 0, "_id": 0,
"type": "div", "type": "div",
"_styles": { "_styles": {
"layout": {}, "active": {},
"position": {} "hover": {},
"normal": {},
"selected": {}
}, },
"_code": "" "_code": ""
}, },

View File

@ -1,19 +1,45 @@
{ {
"title": "Test App", "componentLibraries": [
"favicon": "./_shared/favicon.png", "@budibase/standard-components",
"stylesheets": [], "@budibase/materialdesign-components"
"componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"], ],
"props" : { "title": "{{ name }}",
"_component": "@budibase/standard-components/container", "favicon": "./_shared/favicon.png",
"_children": [], "stylesheets": [],
"_id": 1, "props": {
"type": "div", "_component": "@budibase/standard-components/container",
"_styles": { "_children": [
"layout": {}, {
"position": {} "_id": "686c252d-dbf2-4e28-9078-414ba4719759",
}, "_component": "@budibase/standard-components/login",
"_code": "" "_styles": {
}, "normal": {},
"_css": "", "hover": {},
"uiFunctions": "" "active": {},
"selected": {}
},
"_code": "",
"loginRedirect": "",
"usernameLabel": "Username",
"passwordLabel": "Password",
"loginButtonLabel": "Login",
"buttonClass": "",
"inputClass": "",
"_children": [],
"name": "{{ name }}",
"logo": ""
}
],
"_id": 1,
"type": "div",
"_styles": {
"layout": {},
"position": {}
},
"_code": "",
"className": "",
"onLoad": []
},
"_css": "",
"uiFunctions": ""
} }

View File

@ -28,8 +28,7 @@ module.exports = async (config, appId, pageName, pkg) => {
await savePageJson(appPath, pageName, pkg) await savePageJson(appPath, pageName, pkg)
} }
const rootPath = (config, appname) => const rootPath = (config, appId) => (config.useAppRootPath ? `/${appId}` : "")
config.useAppRootPath ? `/${appname}` : ""
const copyClientLib = async (appPath, pageName) => { const copyClientLib = async (appPath, pageName) => {
const sourcepath = require.resolve("@budibase/client") const sourcepath = require.resolve("@budibase/client")

View File

@ -1,14 +1,17 @@
<!doctype html> <!doctype html>
<html> <html>
<head>
<meta charset='utf8'>
<meta name='viewport' content='width=device-width'>
<title>{{ title }}</title> <head>
<meta charset='utf8'>
<meta name='viewport' content='width=device-width'>
<title>{{ title }}</title>
<link rel='icon' type='image/png' href='{{ favicon }}'> <link rel='icon' type='image/png' href='{{ favicon }}'>
<style> <style>
html, body { html,
body {
font-family: Roboto;
height: 100%; height: 100%;
width: 100%; width: 100%;
margin: 0px; margin: 0px;
@ -39,4 +42,5 @@
loadBudibase(); loadBudibase();
</script> </script>
</body> </body>
</html> </html>

View File

@ -59,6 +59,7 @@
"props": { "props": {
"logo": "asset", "logo": "asset",
"loginRedirect": "string", "loginRedirect": "string",
"name": "string",
"usernameLabel": { "usernameLabel": {
"type": "string", "type": "string",
"default": "Username" "default": "Username"

View File

@ -20,17 +20,17 @@
height: "400", height: "400",
dataFormat: "json", dataFormat: "json",
dataSource: { dataSource: {
data: $store[model._id] || [], data: $store[model] || [],
}, },
} }
async function fetchData() { async function fetchData() {
const FETCH_RECORDS_URL = `/api/${_instanceId}/all_${model._id}/records` const FETCH_RECORDS_URL = `/api/${_instanceId}/views/all_${model}`
const response = await _bb.api.get(FETCH_RECORDS_URL) const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) { if (response.status === 200) {
const json = await response.json() const json = await response.json()
store.update(state => { store.update(state => {
state[model._id] = json state[model] = json
return state return state
}) })
} else { } else {

View File

@ -8,19 +8,32 @@
let username let username
let password let password
let newModel = { let newModel = {
modelId: model._id, modelId: model,
} }
let store = _bb.store let store = _bb.store
let schema = {}
let modelDef = {}
$: fields = Object.keys(model.schema) $: if (model && model.length !== 0) {
fetchModel()
}
$: fields = Object.keys(schema)
async function fetchModel() {
const FETCH_MODEL_URL = `/api/${_instanceId}/models/${model}`
const response = await _bb.api.get(FETCH_MODEL_URL)
modelDef = await response.json()
schema = modelDef.schema
}
async function save() { async function save() {
const SAVE_RECORD_URL = `/api/${_instanceId}/records` const SAVE_RECORD_URL = `/api/${_instanceId}/${model}/records`
const response = await _bb.api.post(SAVE_RECORD_URL, newModel) const response = await _bb.api.post(SAVE_RECORD_URL, newModel)
const json = await response.json() const json = await response.json()
store.update(state => { store.update(state => {
state[model._id] = [...state[model._id], json] state[model._id] = [...state[model], json]
return state return state
}) })
} }
@ -46,14 +59,14 @@
</script> </script>
<form class="uk-form" on:submit|preventDefault> <form class="uk-form" on:submit|preventDefault>
<h4>{model.name}</h4> <h4>{modelDef.name}</h4>
<div> <div>
{#each fields as field} {#each fields as field}
<div class="uk-margin"> <div class="uk-margin">
<label class="form-label" for="form-stacked-text">{field}</label> <label class="form-label" for="form-stacked-text">{field}</label>
<input <input
class="uk-input" class="uk-input"
type={model.schema[field].type === 'string' ? 'text' : model.schema[field].type} type={schema[field].type === 'string' ? 'text' : schema[field].type}
on:change={handleInput(field)} /> on:change={handleInput(field)} />
</div> </div>
{/each} {/each}

View File

@ -10,13 +10,15 @@
let store = _bb.store let store = _bb.store
async function fetchData() { async function fetchData() {
const FETCH_RECORDS_URL = `/api/${_instanceId}/all_${model._id}/records` if (!model || !model.length) return
const FETCH_RECORDS_URL = `/api/${_instanceId}/views/all_${model}`
const response = await _bb.api.get(FETCH_RECORDS_URL) const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) { if (response.status === 200) {
const json = await response.json() const json = await response.json()
store.update(state => { store.update(state => {
state[model._id] = json state[model] = json
return state return state
}) })
} else { } else {
@ -24,7 +26,8 @@
} }
} }
$: data = $store[model._id] || [] $: data = $store[model] || []
$: if (model) fetchData()
onMount(async () => { onMount(async () => {
await fetchData() await fetchData()

View File

@ -10,13 +10,14 @@
let store = _bb.store let store = _bb.store
async function fetchData() { async function fetchData() {
const FETCH_RECORDS_URL = `/api/${_instanceId}/all_${model._id}/records` const FETCH_RECORDS_URL = `/api/${_instanceId}/views/all_${model}`
const response = await _bb.api.get(FETCH_RECORDS_URL) const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) { if (response.status === 200) {
const json = await response.json() const json = await response.json()
store.update(state => { store.update(state => {
state[model._id] = json state[model] = json
return state return state
}) })
@ -26,7 +27,8 @@
} }
} }
$: data = $store[model._id] || [] $: data = $store[model] || []
$: if (model) fetchData()
onMount(async () => { onMount(async () => {
await fetchData() await fetchData()

View File

@ -13,7 +13,7 @@
$: target = openInNewTab ? "_blank" : "_self" $: target = openInNewTab ? "_blank" : "_self"
</script> </script>
<a href={url} bind:this={anchorElement} {target}>{text}</a> <a href={_bb.relativeUrl(url)} bind:this={anchorElement} {target}>{text}</a>
<style> <style>
.textDecoration { .textDecoration {

View File

@ -1,10 +1,9 @@
<script> <script>
import Button from "./Button.svelte" import Button from "./Button.svelte"
export let usernameLabel = "Username"
export let passwordLabel = "Password"
export let loginButtonLabel = "Login" export let loginButtonLabel = "Login"
export let logo = "" export let logo = ""
export let name = ""
export let buttonClass = "" export let buttonClass = ""
export let inputClass = "" export let inputClass = ""
@ -51,14 +50,23 @@
</div> </div>
{/if} {/if}
<h1 class="header-content">Log in to {name}</h1>
<div class="form-root"> <div class="form-root">
<div class="label">{usernameLabel}</div>
<div class="control"> <div class="control">
<input bind:value={username} type="text" class={_inputClass} /> <input
bind:value={username}
type="text"
placeholder="Username"
class={_inputClass} />
</div> </div>
<div class="label">{passwordLabel}</div>
<div class="control"> <div class="control">
<input bind:value={password} type="password" class={_inputClass} /> <input
bind:value={password}
type="password"
placeholder="Password"
class={_inputClass} />
</div> </div>
</div> </div>
@ -77,28 +85,42 @@
<style> <style>
.root { .root {
height: 100%; height: 100%;
display: grid; display: flex;
grid-template-columns: [left] 1fr [middle] auto [right] 1fr; flex-direction: column;
grid-template-rows: [top] 1fr [center] auto [bottom] 1fr; align-items: center;
justify-content: center;
} }
.content { .content {
grid-column-start: middle; display: flex;
grid-row-start: center; flex-direction: column;
width: 400px; align-items: center;
justify-content: center;
} }
.logo-container { .logo-container {
margin-bottom: 20px; margin-bottom: 10px;
} }
.logo-container > img { .logo-container > img {
max-width: 100%; max-width: 200px;
} }
.login-button-container { .login-button-container {
text-align: right; margin-top: 6px;
margin-top: 20px; max-width: 100%;
}
.header-content {
font-family: Inter;
font-weight: 700;
color: #1f1f1f;
font-size: 48px;
line-height: 72px;
margin-bottom: 30px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
font-feature-settings: "case" "rlig" "calt" 0;
} }
.incorrect-details-panel { .incorrect-details-panel {
@ -114,48 +136,55 @@
} }
.form-root { .form-root {
display: grid; display: flex;
grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/ flex-direction: column;
align-items: center;
width: 300px;
} }
.label {
grid-column-start: label;
padding: 5px 10px;
vertical-align: middle;
}
.control { .control {
grid-column-start: control; padding: 6px 0px;
padding: 5px 10px;
}
.default-input {
font-family: inherit;
font-size: inherit;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
width: 100%; width: 100%;
} }
.default-button { .default-input {
font-family: inherit; font-family: Inter;
font-size: inherit; font-size: 14px;
padding: 0.4em; color: #393c44;
padding: 2px 6px 2px 12px;
margin: 0 0 0.5em 0; margin: 0 0 0.5em 0;
box-sizing: border-box; box-sizing: border-box;
border: 1px solid #ccc; border: 0.5px solid #d8d8d8;
border-radius: 2px; border-radius: 4px;
color: #000333; width: 100%;
height: 40px;
transition: border-color 100ms ease-in 0s;
outline-color: #797979;
}
.default-button {
font-family: Inter;
font-size: 16px;
padding: 0.4em;
box-sizing: border-box;
border-radius: 4px;
color: white;
background-color: #393c44;
outline: none; outline: none;
width: 300px;
height: 40px;
cursor: pointer;
transition: all 0.2s ease 0s;
overflow: hidden;
outline: none;
user-select: none;
white-space: nowrap;
text-align: center;
} }
.default-button:active { .default-button:hover {
background-color: #f9f9f9; background-color: white;
} border-color: #393c44;
color: #393c44;
.default-button:focus {
border-color: #f9f9f9;
} }
</style> </style>