Merge pull request #126 from shogunpurple/backend-design

Backend design
This commit is contained in:
Martin McKeaveney 2020-02-24 17:06:02 +00:00 committed by GitHub
commit e88d3e821a
35 changed files with 409 additions and 316 deletions

View File

@ -5,6 +5,7 @@
import { store, initialise } from "./builderStore" import { store, initialise } from "./builderStore"
import { onMount } from "svelte" import { onMount } from "svelte"
import IconButton from "./common/IconButton.svelte" import IconButton from "./common/IconButton.svelte"
import Spinner from "./common/Spinner.svelte"
let init = initialise() let init = initialise()
</script> </script>
@ -12,9 +13,9 @@
<main> <main>
{#await init} {#await init}
<div class="spinner-container">
<h1>loading</h1> <Spinner />
</div>
{:then result} {:then result}
{#if $store.hasAppPackage} {#if $store.hasAppPackage}
@ -52,4 +53,13 @@
bottom: 25px; bottom: 25px;
right: 25px; right: 25px;
} }
.spinner-container {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
</style> </style>

View File

@ -4,6 +4,7 @@
import Checkbox from "../common/Checkbox.svelte" import Checkbox from "../common/Checkbox.svelte"
import ButtonGroup from "../common/ButtonGroup.svelte" import ButtonGroup from "../common/ButtonGroup.svelte"
import Button from "../common/Button.svelte" import Button from "../common/Button.svelte"
import ActionButton from "../common/ActionButton.svelte"
import { validateAccessLevels } from "../common/core" import { validateAccessLevels } from "../common/core"
import ErrorsBox from "../common/ErrorsBox.svelte" import ErrorsBox from "../common/ErrorsBox.svelte"
@ -64,10 +65,11 @@
<form class="uk-form-horizontal"> <form class="uk-form-horizontal">
<Textbox label="Name" bind:text={clonedLevel.name} /> <Textbox label="Access Level Name" bind:text={clonedLevel.name} />
<h4 class="budibase__title--4">Permissions</h4>
{#each permissionMatrix as permission} {#each permissionMatrix as permission}
<div> <div class="permission-container">
<Checkbox <Checkbox
label={getPermissionName(permission.permission)} label={getPermissionName(permission.permission)}
checked={permission.hasPermission} checked={permission.hasPermission}
@ -77,15 +79,19 @@
</form> </form>
<ButtonGroup style="margin-top: 10px"> <div class="uk-modal-footer uk-text-right">
<Button color="primary" grouped on:click={save}>Save</Button> <ButtonGroup>
<Button color="secondary" grouped on:click={() => onFinished()}> <ActionButton primary grouped on:click={save}>Save</ActionButton>
Cancel <ActionButton alert grouped on:click={() => onFinished()}>
</Button> Cancel
</ButtonGroup> </ActionButton>
</ButtonGroup>
</div>
</div> </div>
<style> <style>
.permission-container {
margin-bottom: 10px;
}
</style> </style>

View File

@ -1,6 +1,7 @@
<script> <script>
import ButtonGroup from "../common/ButtonGroup.svelte" import ButtonGroup from "../common/ButtonGroup.svelte"
import Button from "../common/Button.svelte" import Button from "../common/Button.svelte"
import ActionButton from "../common/ActionButton.svelte"
import { store } from "../builderStore" import { store } from "../builderStore"
import { generateFullPermissions, getNewAccessLevel } from "../common/core" import { generateFullPermissions, getNewAccessLevel } from "../common/core"
import getIcon from "../common/icon" import getIcon from "../common/icon"
@ -47,11 +48,10 @@
</script> </script>
<div class="root"> <div class="root">
<ButtonGroup> <ButtonGroup>
<Button grouped color="secondary" on:click={createNewLevel}> <ActionButton primary on:click={createNewLevel}>
Create New Access Level Create New Access Level
</Button> </ActionButton>
</ButtonGroup> </ButtonGroup>
{#if $store.accessLevels} {#if $store.accessLevels}
@ -82,7 +82,11 @@
</table> </table>
{:else}(no actions added){/if} {:else}(no actions added){/if}
<Modal bind:isOpen={isEditing}> <Modal
onClosed={() => isEditing = false}
bind:isOpen={isEditing}
title={isEditing ? "Edit Access Level" : "Create Access Level"}
>
{#if isEditing} {#if isEditing}
<AccessLevelView <AccessLevelView
level={editingLevel} level={editingLevel}

View File

@ -1,6 +1,7 @@
<script> <script>
import Textbox from "../common/Textbox.svelte" import Textbox from "../common/Textbox.svelte"
import Button from "../common/Button.svelte" import Button from "../common/Button.svelte"
import ActionButton from "../common/ActionButton.svelte"
import ButtonGroup from "../common/ButtonGroup.svelte" import ButtonGroup from "../common/ButtonGroup.svelte"
import { cloneDeep, filter, keys, map, isUndefined } from "lodash/fp" import { cloneDeep, filter, keys, map, isUndefined } from "lodash/fp"
import ErrorsBox from "../common/ErrorsBox.svelte" import ErrorsBox from "../common/ErrorsBox.svelte"
@ -10,16 +11,15 @@
export let action export let action
export let onFinished = action => {} export let onFinished = action => {}
export let allActions export let allActions
export let isNew = true
let optKey = "" let optKey = ""
let optValue = "" let optValue = ""
let clonedAction = cloneDeep(action) let clonedAction = cloneDeep(action)
let initialOptions = pipe(action.initialOptions, [ let initialOptions = pipe(
keys, action.initialOptions,
map(k => ({ key: k, value: action.initialOptions[k] })), [keys, map(k => ({ key: k, value: action.initialOptions[k] }))]
]) )
let errors = [] let errors = []
const addNewOption = () => { const addNewOption = () => {
@ -44,17 +44,26 @@
const removeOption = opt => { const removeOption = opt => {
if (opt) { if (opt) {
delete clonedAction.initialOptions[opt.key] delete clonedAction.initialOptions[opt.key]
initialOptions = pipe(initialOptions, [filter(o => o.key !== opt.key)]) initialOptions = pipe(
initialOptions,
[filter(o => o.key !== opt.key)]
)
} }
} }
const save = () => { const save = () => {
const newActionsList = [ const newActionsList = [
...pipe(allActions, [filter(a => a !== action)]), ...pipe(
allActions,
[filter(a => a !== action)]
),
clonedAction, clonedAction,
] ]
errors = pipe(newActionsList, [validateActions, map(e => e.error)]) errors = pipe(
newActionsList,
[validateActions, map(e => e.error)]
)
if (errors.length === 0) onFinished(clonedAction) if (errors.length === 0) onFinished(clonedAction)
} }
@ -89,9 +98,7 @@
class="uk-input uk-width-1-4 uk-margin-right" class="uk-input uk-width-1-4 uk-margin-right"
placeholder="value" placeholder="value"
bind:value={optValue} /> bind:value={optValue} />
<Button color="primary-outline uk-width-1-4" on:click={addNewOption}> <ActionButton primary on:click={addNewOption}>Add</ActionButton>
Add
</Button>
</div> </div>
<div style="margin-top: 10px"> <div style="margin-top: 10px">
{#each initialOptions as option} {#each initialOptions as option}
@ -107,11 +114,12 @@
</div> </div>
</div> </div>
<ButtonGroup> <div class="uk-modal-footer uk-text-right">
<Button color="secondary" grouped on:click={save}>Save</Button> <ButtonGroup>
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> <ActionButton primary grouped on:click={save}>Save</ActionButton>
</ButtonGroup> <ActionButton alert grouped on:click={cancel}>Cancel</ActionButton>
</ButtonGroup>
</div>
</div> </div>
<style> <style>

View File

@ -19,20 +19,26 @@
let actionsArray = [] let actionsArray = []
store.subscribe(s => { store.subscribe(s => {
actionsArray = pipe(s.actions, [keys, map(k => s.actions[k])]) actionsArray = pipe(
s.actions,
[keys, map(k => s.actions[k])]
)
}) })
let getDefaultOptionsHtml = defaultOptions => let getDefaultOptionsHtml = defaultOptions =>
pipe(defaultOptions, [ pipe(
keys, defaultOptions,
map( [
k => keys,
`<span style="color:var(--slate)">${k}: </span>${JSON.stringify( map(
defaultOptions[k] k =>
)}` `<span style="color:var(--slate)">${k}: </span>${JSON.stringify(
), defaultOptions[k]
join("<br>"), )}`
]) ),
join("<br>"),
]
)
let actionEditingFinished = action => { let actionEditingFinished = action => {
if (action) { if (action) {
@ -43,7 +49,7 @@
} }
</script> </script>
<h3 class="title">Actions</h3> <h3 class="budibase__title--3">Actions</h3>
{#if actionsArray} {#if actionsArray}
<table class="fields-table uk-table uk-table-small uk-table-striped"> <table class="fields-table uk-table uk-table-small uk-table-striped">
@ -79,7 +85,9 @@
</table> </table>
{:else}(no actions added){/if} {:else}(no actions added){/if}
<Modal bind:isOpen={isEditing}> <Modal
title={editingActionIsNew ? 'Create Action' : 'Edit Action'}
bind:isOpen={isEditing}>
{#if isEditing} {#if isEditing}
<ActionView <ActionView
action={editingAction} action={editingAction}
@ -99,11 +107,6 @@
color: var(--secondary75); color: var(--secondary75);
} }
.title {
margin: 3rem 0rem 0rem 0rem;
font-weight: 700;
}
.table-content { .table-content {
font-weight: 500; font-weight: 500;
font-size: 0.9rem; font-size: 0.9rem;

View File

@ -2,6 +2,7 @@
import getIcon from "../common/icon" import getIcon from "../common/icon"
import { store } from "../builderStore" import { store } from "../builderStore"
import Button from "../common/Button.svelte" import Button from "../common/Button.svelte"
import ActionButton from "../common/ActionButton.svelte"
import ButtonGroup from "../common/ButtonGroup.svelte" import ButtonGroup from "../common/ButtonGroup.svelte"
import Actions from "./Actions.svelte" import Actions from "./Actions.svelte"
import Triggers from "./Triggers.svelte" import Triggers from "./Triggers.svelte"
@ -83,12 +84,12 @@
<div class="root"> <div class="root">
<div class="actions-header"> <div class="actions-header">
<ButtonGroup> <ButtonGroup>
<Button color="secondary" grouped on:click={newAction}> <ActionButton color="secondary" grouped on:click={newAction}>
Create New Action Create New Action
</Button> </ActionButton>
<Button color="tertiary" grouped on:click={newTrigger}> <ActionButton color="tertiary" grouped on:click={newTrigger}>
Create New Trigger Create New Trigger
</Button> </ActionButton>
</ButtonGroup> </ButtonGroup>
</div> </div>
@ -121,6 +122,7 @@
.actions-header { .actions-header {
flex: 0 1 auto; flex: 0 1 auto;
margin-bottom: 10px;
} }
.node-view { .node-view {

View File

@ -1,6 +1,7 @@
<script> <script>
import Textbox from "../common/Textbox.svelte" import Textbox from "../common/Textbox.svelte"
import Button from "../common/Button.svelte" import Button from "../common/Button.svelte"
import ActionButton from "../common/ActionButton.svelte"
import Dropdown from "../common/Dropdown.svelte" import Dropdown from "../common/Dropdown.svelte"
import ButtonGroup from "../common/ButtonGroup.svelte" import ButtonGroup from "../common/ButtonGroup.svelte"
import CodeArea from "../common/CodeArea.svelte" import CodeArea from "../common/CodeArea.svelte"
@ -13,7 +14,6 @@
export let onFinished = action => {} export let onFinished = action => {}
export let allTriggers export let allTriggers
export let allActions export let allActions
export let isNew = true
let clonedTrigger = cloneDeep(trigger) let clonedTrigger = cloneDeep(trigger)
let errors = [] let errors = []
@ -22,7 +22,10 @@
let cancel = () => onFinished() let cancel = () => onFinished()
let save = () => { let save = () => {
const newTriggersList = [ const newTriggersList = [
...pipe(allTriggers, [filter(t => t !== trigger)]), ...pipe(
allTriggers,
[filter(t => t !== trigger)]
),
clonedTrigger, clonedTrigger,
] ]
@ -51,19 +54,22 @@
options={['', ...actionNames]} options={['', ...actionNames]}
bind:selected={clonedTrigger.actionName} /> bind:selected={clonedTrigger.actionName} />
<CodeArea <CodeArea
label="Condition (javascript)" label="Condition"
javascript
bind:text={clonedTrigger.condition} /> bind:text={clonedTrigger.condition} />
<CodeArea <CodeArea
label="Action Options Creator (javascript)" label="Action Options Creator"
javascript
bind:text={clonedTrigger.optionsCreator} /> bind:text={clonedTrigger.optionsCreator} />
</form> </form>
<ButtonGroup> <div class="uk-modal-footer uk-text-right">
<Button color="primary" grouped on:click={save}>Save</Button> <ButtonGroup>
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> <ActionButton primary on:click={save}>Save</ActionButton>
</ButtonGroup> <ActionButton alert on:click={cancel}>Cancel</ActionButton>
</ButtonGroup>
</div>
</div> </div>
<style> <style>

View File

@ -23,7 +23,7 @@
} }
</script> </script>
<h3 class="title">Triggers</h3> <h3 class="budibase__title--3">Triggers</h3>
{#if $store.triggers} {#if $store.triggers}
<table class="fields-table uk-table uk-table-small uk-table-striped"> <table class="fields-table uk-table uk-table-small uk-table-striped">
@ -57,7 +57,10 @@
</table> </table>
{:else}(no triggers added){/if} {:else}(no triggers added){/if}
<Modal bind:isOpen={isEditing}> <Modal
title={editingTriggerIsNew ? 'Create Trigger' : 'Edit Trigger'}
onClosed={() => (isEditing = false)}
bind:isOpen={isEditing}>
{#if isEditing} {#if isEditing}
<TriggerView <TriggerView
trigger={editingTrigger} trigger={editingTrigger}

View File

@ -0,0 +1,93 @@
/* Budibase Component Styles */
.header {
font-size: 0.75rem;
color: #999;
text-transform: uppercase;
margin-top: 1rem;
font-weight: 500;
}
.budibase__title {
font-weight: 900;
font-size: 42px;
}
.budibase__title--2 {
font-weight: 700;
font-size: 32px;
}
.budibase__title--3 {
font-weight: 700;
font-size: 24px;
}
.budibase__title--4 {
font-weight: 700;
font-size: 18px;
}
.budibase__label--big {
font-weight: 400;
font-size: 14px;
opacity: 0.6;
text-transform: uppercase;
}
.budibase__label--medium {
font-weight: 500;
font-size: 12px;
opacity: 0.6;
text-transform: uppercase;
}
.budibase__label--small {
font-weight: 500;
font-size: 10px;
opacity: 0.6;
text-transform: uppercase;
}
.budibase__sub-heading {
font-weight: 500;
font-size: 16px;
opacity: 0.6;
}
.budibase__nav-item {
cursor: pointer;
padding: 0 7px 0 3px;
height: 35px;
margin: 5px 0;
border-radius: 0 5px 5px 0;
display: flex;
align-items: center;
font-weight: 500;
font-size: 0.8em;
}
.budibase__nav-item.selected {
color: var(--button-text);
background: var(--background-button) !important;
}
.budibase__nav-item:hover {
background: #fafafa;
}
.budibase__input {
width: 250px;
height: 35px;
border-radius: 3px;
border: 1px solid #DBDBDB;
text-align: left;
letter-spacing: 0.7px;
color: #163057;
font-size: 16px;
padding-left: 5px;
}
.uk-text-right {
display: flex;
justify-content: flex-start;
}

View File

@ -29,12 +29,12 @@
} }
.button { .button {
font-size: 18px; font-size: 14px;
font-weight: bold; font-weight: bold;
border-radius: 5px; border-radius: 5px;
border: none; border: none;
width: 167px; min-width: 120px;
height: 64px; height: 45px;
} }
.button:hover { .button:hover {

View File

@ -8,18 +8,9 @@
<style> <style>
.root { .root {
display: flex; display: grid;
} grid-auto-flow: column;
grid-gap: 5px;
.root:last-child { width: 50%;
border-radius: 0 var(--borderradius) var(--borderradius) 0;
}
.root:first-child {
border-radius: var(--borderradius) 0 0 var(--borderradius);
}
.root:not(:first-child):not(:last-child) {
border-radius: 0;
} }
</style> </style>

View File

@ -1,10 +1,17 @@
<script> <script>
import { JavaScriptIcon } from "../common/Icons"
// todo: use https://ace.c9.io // todo: use https://ace.c9.io
export let text = "" export let text = ""
export let label = "" export let label = ""
export let javascript = false
</script> </script>
<div>{label}</div> <div class="header">
{#if javascript}
<JavaScriptIcon />
{/if}
<span>{label}</span>
</div>
<textarea class="uk-textarea" bind:value={text} /> <textarea class="uk-textarea" bind:value={text} />
<style> <style>
@ -13,10 +20,18 @@
margin-top: 5px; margin-top: 5px;
margin-bottom: 10px; margin-bottom: 10px;
background: var(--lightslate); background: var(--lightslate);
color: var(--white);
font-family: "Courier New", Courier, monospace; font-family: "Courier New", Courier, monospace;
width: 95%; width: 95%;
height: 100px; height: 100px;
border-radius: 5px; border-radius: 5px;
} }
span {
margin-left: 5px;
}
.header {
display: flex;
align-items: center;
}
</style> </style>

View File

@ -1,57 +1,51 @@
<script> <script>
import Button from "./Button.svelte" import Button from "./Button.svelte"
import ButtonGroup from "./ButtonGroup.svelte" import ActionButton from "./ActionButton.svelte"
import UIkit from "uikit" import ButtonGroup from "./ButtonGroup.svelte"
import UIkit from "uikit"
export let title="" export let title = ""
export let body="" export let body = ""
export let okText = "OK" export let okText = "OK"
export let cancelText = "Cancel" export let cancelText = "Cancel"
export let onOk = ()=> {} export let onOk = () => {}
export let onCancel = ()=> {} export let onCancel = () => {}
export const show = () => { export const show = () => {
UIkit.modal(theModal).show() UIkit.modal(theModal).show()
} }
export const hide = () => { export const hide = () => {
UIkit.modal(theModal).hide() UIkit.modal(theModal).hide()
} }
let theModal; let theModal
const cancel = () => { const cancel = () => {
hide() hide()
onCancel() onCancel()
} }
const ok = () => {
hide()
onOk()
}
const ok = () => {
hide()
onOk()
}
</script> </script>
<div id="my-id" uk-modal bind:this={theModal}> <div id="my-id" uk-modal bind:this={theModal}>
<div class="uk-modal-dialog"> <div class="uk-modal-dialog">
<button class="uk-modal-close-default" type="button" uk-close></button> <button class="uk-modal-close-default" type="button" uk-close />
<div class="uk-modal-header"> <div class="uk-modal-header">
<h2 class="uk-modal-title">{title}</h2> <h4 class="budibase__title--4">{title}</h4>
</div> </div>
<div class="uk-modal-body"> <div class="uk-modal-body">
<slot>{body}</slot> <slot>{body}</slot>
</div> </div>
<div class="uk-modal-footer"> <div class="uk-modal-footer">
<ButtonGroup> <ButtonGroup>
<Button grouped color="primary" on:click={ok}> <ActionButton alert on:click={ok}>{okText}</ActionButton>
{okText} <ActionButton primary on:click={cancel}>{cancelText}</ActionButton>
</Button> </ButtonGroup>
<Button grouped color="secondary" on:click={cancel}>
{cancelText}
</Button>
</ButtonGroup>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,5 +1,6 @@
<script> <script>
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import Select from "../common/Select.svelte";
export let selected export let selected
export let label export let label
@ -17,7 +18,7 @@
<label class="uk-form-label">{label}</label> <label class="uk-form-label">{label}</label>
<div class="uk-form-controls"> <div class="uk-form-controls">
{#if multiple} {#if multiple}
<select <Select
class="uk-select uk-form-width-{width} uk-form-{size}" class="uk-select uk-form-width-{width} uk-form-{size}"
multiple multiple
bind:value={selected} bind:value={selected}
@ -27,9 +28,9 @@
{!textMember ? option : textMember(option)} {!textMember ? option : textMember(option)}
</option> </option>
{/each} {/each}
</select> </Select>
{:else} {:else}
<select <Select
class="uk-select uk-form-width-{width} uk-form-{size}" class="uk-select uk-form-width-{width} uk-form-{size}"
bind:value={selected} bind:value={selected}
on:change> on:change>
@ -38,7 +39,7 @@
{!textMember ? option : textMember(option)} {!textMember ? option : textMember(option)}
</option> </option>
{/each} {/each}
</select> </Select>
{/if} {/if}
</div> </div>
</div> </div>

View File

@ -17,7 +17,7 @@
class="dropdown-content" class="dropdown-content"
style="display: {isDroppedDown ? 'inline-block' : 'none'}"> style="display: {isDroppedDown ? 'inline-block' : 'none'}">
{#each actions as action} {#each actions as action}
<div class="action-row" on:click={action.onclick}>{action.label}</div> <div class="budibase__nav-item" on:click={action.onclick}>{action.label}</div>
{/each} {/each}
</div> </div>

View File

@ -0,0 +1,31 @@
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M0.220001 0.220001H19.78V19.7802H0.220001V0.220001Z"
fill="#F0DB4F" />
<path
d="M18.1792 15.115C18.0359 14.2227 17.4541 13.4731 15.7305 12.7742C15.1317
12.4991 14.4642 12.302 14.2653 11.8483C14.1947 11.5842 14.1853 11.4355 14.23
11.2756C14.3583 10.7569 14.9775 10.5952 15.4683 10.7439C15.7844 10.8499
16.0836 11.0934 16.2641 11.482C17.1081 10.9355 17.1064 10.9391 17.6958
10.5634C17.48 10.2289 17.3648 10.0745 17.2236 9.93142C16.7159 9.36439
16.0242 9.07235 14.918 9.0947L14.3417 9.16923C13.7895 9.30876 13.2633 9.5986
12.9547 9.9872C12.0287 11.0378 12.2928 12.8766 13.4195 13.6333C14.5295
14.4664 16.1601 14.6559 16.3684 15.435C16.5711 16.3888 15.6675 16.6975
14.7694 16.5878C14.1075 16.4502 13.7394 16.1138 13.3414 15.502C12.6089
15.926 12.6089 15.926 11.8558 16.3591C12.0344 16.7495 12.222 16.9263 12.5214
17.2645C13.9383 18.7017 17.4839 18.6311 18.1198 16.4558C18.1456 16.3811
18.3169 15.883 18.1792 15.115V15.115ZM10.8534 9.20985H9.0239L9.0164
13.9399C9.0164 14.9458 9.06843 15.868 8.90484 16.1506C8.63718 16.7066
7.94359 16.6377 7.62749 16.5299C7.30578 16.3717 7.14218 16.1469 6.95265
15.8291C6.90062 15.7378 6.86156 15.6672 6.84843 15.6617L5.36093
16.5727C5.60828 17.0803 5.97265 17.521 6.43937 17.8072C7.13656 18.2256
8.07359 18.3539 9.05359 18.1289C9.6914 17.9431 10.2417 17.5583 10.5298
16.9725C10.9464 16.2045 10.857 15.275 10.8533 14.2469C10.8626 12.5695
10.8534 10.8925 10.8534 9.20985Z"
fill="#323330" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -15,3 +15,4 @@ export { default as CheckIcon } from "./Check.svelte"
export { default as GridIcon } from "./Grid.svelte" export { default as GridIcon } from "./Grid.svelte"
export { default as ShapeIcon } from "./Shape.svelte" export { default as ShapeIcon } from "./Shape.svelte"
export { default as AddIcon } from "./Add.svelte" export { default as AddIcon } from "./Add.svelte"
export { default as JavaScriptIcon } from "./JavaScript.svelte"

View File

@ -1,9 +1,11 @@
<script> <script>
import UIkit from "uikit" import UIkit from "uikit"
import ActionButton from "../common/ActionButton.svelte"
export let isOpen = false export let isOpen = false
export let onClosed = () => {} export let onClosed = () => {}
export let id = "" export let id = ""
export let title
let ukModal let ukModal
let listenerAdded = false let listenerAdded = false
@ -25,11 +27,18 @@
</script> </script>
<div bind:this={ukModal} uk-modal {id}> <div bind:this={ukModal} uk-modal {id}>
<div class="uk-modal-dialog uk-modal-body" uk-overflow-auto> <div class="uk-modal-dialog" uk-overflow-auto>
{#if onClosed} {#if title}
<button class="uk-modal-close-default" type="button" uk-close /> <div class="uk-modal-header">
<h4 class="budibase__title--4">{title}</h4>
</div>
{/if} {/if}
<slot /> <div class="uk-modal-body">
{#if onClosed}
<button class="uk-modal-close-default" type="button" uk-close />
{/if}
<slot />
</div>
</div> </div>
</div> </div>

View File

@ -16,6 +16,6 @@
<div class="uk-margin"> <div class="uk-margin">
<label class="uk-form-label">{label}</label> <label class="uk-form-label">{label}</label>
<div class="uk-form-controls"> <div class="uk-form-controls">
<input class="uk-input" {value} on:change={inputChanged} /> <input class="budibase__input" {value} on:change={inputChanged} />
</div> </div>
</div> </div>

View File

@ -19,6 +19,7 @@
color: var(--secondary50); color: var(--secondary50);
font-weight: bold; font-weight: bold;
position: relative; position: relative;
max-width: 300px;
} }
select { select {

View File

@ -0,0 +1,5 @@
<script>
export let ratio = "4.5"
</script>
<span uk-spinner={`ratio: ${ratio}`} />

View File

@ -13,7 +13,7 @@
<label class="uk-form-label">{label}</label> <label class="uk-form-label">{label}</label>
<div class="uk-form-controls"> <div class="uk-form-controls">
<input <input
class="uk-input uk-form-width-{width} uk-form-{size}" class="budibase__input"
class:uk-form-danger={hasError} class:uk-form-danger={hasError}
on:change on:change
bind:value={text} bind:value={text}

View File

@ -1,5 +1,6 @@
<script> <script>
import Button from "../common/Button.svelte" import Button from "../common/Button.svelte"
import ActionButton from "../common/ActionButton.svelte"
import ButtonGroup from "../common/ButtonGroup.svelte" import ButtonGroup from "../common/ButtonGroup.svelte"
import { store } from "../builderStore" import { store } from "../builderStore"
import Modal from "../common/Modal.svelte" import Modal from "../common/Modal.svelte"
@ -20,14 +21,14 @@
<div class="root" style="left: {left}"> <div class="root" style="left: {left}">
<ButtonGroup> <ButtonGroup>
<Button color="secondary" grouped on:click={store.saveCurrentNode}> <ActionButton color="secondary" grouped on:click={store.saveCurrentNode}>
{#if $store.currentNodeIsNew}Create{:else}Update{/if} {#if $store.currentNodeIsNew}Create{:else}Update{/if}
</Button> </ActionButton>
{#if !$store.currentNodeIsNew} {#if !$store.currentNodeIsNew}
<Button color="tertiary" grouped on:click={openConfirmDelete}> <ActionButton alert grouped on:click={openConfirmDelete}>
Delete Delete
</Button> </ActionButton>
{/if} {/if}
</ButtonGroup> </ButtonGroup>
@ -37,15 +38,17 @@
</div> </div>
{/if} {/if}
<Modal bind:isOpen={confirmDelete}> <Modal onClosed={() => (confirmDelete = false)} bind:isOpen={confirmDelete}>
<div style="margin: 10px 0px 20px 0px"> <span>
Are you sure you want to delete {$store.currentNode.name} ? Are you sure you want to delete {$store.currentNode.name}?
</div> </span>
<div style="float:right"> <div class="uk-modal-footer uk-text-right">
<Button color="primary" on:click={deleteCurrentNode}>Yes</Button> <ButtonGroup>
<Button color="secondary" on:click={() => (confirmDelete = false)}> <ActionButton alert on:click={deleteCurrentNode}>Yes</ActionButton>
No <ActionButton primary on:click={() => (confirmDelete = false)}>
</Button> No
</ActionButton>
</ButtonGroup>
</div> </div>
</Modal> </Modal>
</div> </div>
@ -56,4 +59,11 @@
width: 100%; width: 100%;
align-items: right; align-items: right;
} }
.actions-modal-body {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
</style> </style>

View File

@ -7,6 +7,7 @@
import ValuesList from "../common/ValuesList.svelte" import ValuesList from "../common/ValuesList.svelte"
import ErrorsBox from "../common/ErrorsBox.svelte" import ErrorsBox from "../common/ErrorsBox.svelte"
import Checkbox from "../common/Checkbox.svelte" import Checkbox from "../common/Checkbox.svelte"
import ActionButton from "../common/ActionButton.svelte"
import DatePicker from "../common/DatePicker.svelte" import DatePicker from "../common/DatePicker.svelte"
import { import {
cloneDeep, cloneDeep,
@ -141,18 +142,14 @@
label="Max Length" label="Max Length"
bind:value={clonedField.typeOptions.maxLength} /> bind:value={clonedField.typeOptions.maxLength} />
{/if} {/if}
</form> </form>
<ButtonGroup style="float: right;"> <div class="uk-modal-footer uk-text-right">
<Button color="primary" grouped on:click={save}>Save</Button> <ButtonGroup>
<Button color="tertiary" grouped on:click={() => onFinished(false)}> <ActionButton primary on:click={save}>Save</ActionButton>
Cancel <ActionButton alert on:click={() => onFinished(false)}>
</Button> Cancel
</ButtonGroup> </ActionButton>
</ButtonGroup>
</div>
</div> </div>
<style>
</style>

View File

@ -14,15 +14,18 @@
store.subscribe($store => { store.subscribe($store => {
index = $store.currentNode index = $store.currentNode
indexableRecords = pipe($store.hierarchy, [ indexableRecords = pipe(
hierarchyFunctions.getFlattenedHierarchy, $store.hierarchy,
filter(hierarchyFunctions.isDecendant(index.parent())), [
filter(hierarchyFunctions.isRecord), hierarchyFunctions.getFlattenedHierarchy,
map(n => ({ filter(hierarchyFunctions.isDecendant(index.parent())),
node: n, filter(hierarchyFunctions.isRecord),
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds), map(n => ({
})), node: n,
]) isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds),
})),
]
)
}) })
const toggleAllowedRecord = record => { const toggleAllowedRecord = record => {
@ -40,7 +43,7 @@
<Textbox bind:text={index.name} label="Name" /> <Textbox bind:text={index.name} label="Name" />
<div class="allowed-records"> <div class="allowed-records">
<div>Records to Index</div> <div class="index-label">Records to Index</div>
{#each indexableRecords as rec} {#each indexableRecords as rec}
<input <input
type="checkbox" type="checkbox"
@ -55,11 +58,9 @@
bind:selected={index.indexType} bind:selected={index.indexType}
options={['ancestor', 'reference']} /> options={['ancestor', 'reference']} />
<CodeArea bind:text={index.map} label="Map (javascript)" /> <CodeArea bind:text={index.map} javascript label="Map" />
<CodeArea bind:text={index.filter} label="Filter (javascript expression)" /> <CodeArea bind:text={index.filter} javascript label="Filter" />
<CodeArea <CodeArea javascript bind:text={index.getShardName} label="Shard Name" />
bind:text={index.getShardName}
label="Shard Name (javascript expression)" />
</form> </form>
@ -76,4 +77,9 @@
.allowed-records > span { .allowed-records > span {
margin-right: 30px; margin-right: 30px;
} }
.index-label {
color: #333;
font-size: 0.875rem;
}
</style> </style>

View File

@ -94,7 +94,7 @@
<div class="root"> <div class="root">
<form class="uk-form-horizontal"> <form class="uk-form-horizontal">
<h3 class="settings-title">Settings</h3> <h3 class="budibase__title--3">Settings</h3>
<Textbox label="Name:" bind:text={record.name} on:change={nameChanged} /> <Textbox label="Name:" bind:text={record.name} on:change={nameChanged} />
{#if !record.isSingle} {#if !record.isSingle}
@ -104,7 +104,7 @@
<div class="recordkey">{record.nodeKey()}</div> <div class="recordkey">{record.nodeKey()}</div>
</form> </form>
<h3 class="title"> <h3 class="budibase__title--3">
Fields Fields
<span class="add-field-button" on:click={newField}> <span class="add-field-button" on:click={newField}>
{@html getIcon('plus')} {@html getIcon('plus')}
@ -150,6 +150,7 @@
{#if editingField} {#if editingField}
<Modal <Modal
title="Manage Index Fields"
bind:isOpen={editingField} bind:isOpen={editingField}
onClosed={() => onFinishedFieldEdit(false)}> onClosed={() => onFinishedFieldEdit(false)}>
<FieldView <FieldView
@ -160,7 +161,7 @@
</Modal> </Modal>
{/if} {/if}
<h3 class="title">Indexes</h3> <h3 class="budibase__title--3">Indexes</h3>
{#each record.indexes as index} {#each record.indexes as index}
<div class="index-container"> <div class="index-container">
@ -199,15 +200,6 @@
padding: 2rem; padding: 2rem;
} }
.settings-title {
font-weight: 700;
}
.title {
margin: 3rem 0rem 0rem 0rem;
font-weight: 700;
}
.recordkey { .recordkey {
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;

View File

@ -1,3 +1,5 @@
@import "./budibase.css";
:root { :root {
--primary100: #173157FF; --primary100: #173157FF;
--primary75: #454CA0BF; --primary75: #454CA0BF;

View File

@ -105,9 +105,8 @@
.nav-group-header { .nav-group-header {
display: grid; display: grid;
grid-template-columns: [icon] auto [title] 1fr [button] auto; grid-template-columns: [icon] auto [title] 1fr [button] auto;
padding: 2rem 1rem 0rem 1rem; padding: 2rem 1rem 1rem 1rem;
font-size: 0.9rem; font-size: 0.9rem;
font-weight: bold;
} }
.nav-group-header > div:nth-child(1) { .nav-group-header > div:nth-child(1) {
@ -137,13 +136,12 @@
.hierarchy-title { .hierarchy-title {
flex: auto 1 1; flex: auto 1 1;
text-transform: uppercase;
} }
.hierarchy { .hierarchy {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1 0 auto;
height: 100px;
} }
.hierarchy-items-container { .hierarchy-items-container {

View File

@ -13,16 +13,14 @@
if (s.currentNode) if (s.currentNode)
navActive = navActive =
s.activeNav === "database" && node.nodeId === s.currentNode.nodeId s.activeNav === "database" && node.nodeId === s.currentNode.nodeId
? "active"
: ""
}) })
</script> </script>
<div class="root"> <div
<div class="budibase__nav-item"
class="title {navActive}" on:click={() => store.selectExistingNode(node.nodeId)}
on:click={() => store.selectExistingNode(node.nodeId)} class:selected={navActive}>
style="padding-left: {20 + level * 20}px"> <div style="padding-left: {20 + level * 20}px">
{@html getIcon(icon, 12)} {@html getIcon(icon, 12)}
<span style="margin-left: 1rem">{node.name}</span> <span style="margin-left: 1rem">{node.name}</span>
</div> </div>
@ -37,27 +35,3 @@
{/each} {/each}
{/if} {/if}
</div> </div>
<style>
.root {
display: block;
font-size: 0.9rem;
width: 100%;
cursor: pointer;
color: var(--secondary50);
font-weight: 500;
}
.title {
padding-top: 0.5rem;
padding-right: 0.5rem;
}
.title:hover {
background-color: var(--secondary10);
}
.active {
background-color: var(--primary10);
}
</style>

View File

@ -8,28 +8,16 @@
let navActive = "" let navActive = ""
store.subscribe(db => { store.subscribe(db => {
navActive = db.activeNav === name ? "active" : "" navActive = db.activeNav === name
}) })
const setActive = () => store.setActiveNav(name) const setActive = () => store.setActiveNav(name)
</script> </script>
<div class="nav-item {navActive}" on:click={setActive}>{label}</div> <div class="budibase__nav-item backend-nav-item" class:selected={navActive} on:click={setActive}>{label}</div>
<style> <style>
.nav-item { .backend-nav-item {
padding: 1.5rem 1rem 0rem 1rem; padding-left: 25px;
font-size: 0.9rem;
font-weight: bold;
cursor: pointer;
flex: 0 0 auto;
}
.nav-item:hover {
background-color: var(--primary10);
}
.active {
background-color: var(--primary10);
} }
</style> </style>

View File

@ -56,7 +56,7 @@
{#each _screens as screen} {#each _screens as screen}
<div <div
class="hierarchy-item component" class="budibase__nav-item component"
class:selected={$store.currentComponentInfo._id === screen.component.props._id} class:selected={$store.currentComponentInfo._id === screen.component.props._id}
on:click|stopPropagation={() => store.setCurrentScreen(screen.title)}> on:click|stopPropagation={() => store.setCurrentScreen(screen.title)}>
@ -92,37 +92,16 @@
<ConfirmDialog <ConfirmDialog
bind:this={confirmDeleteDialog} bind:this={confirmDeleteDialog}
title="Confirm Delete" title="Confirm Delete"
body={`Are you sure you wish to delete this '${lastPartOfName(componentToDelete)}' component`} body={`Are you sure you wish to delete this '${lastPartOfName(componentToDelete)}' component?`}
okText="Delete Component" okText="Delete Component"
onOk={() => store.deleteComponent(componentToDelete)} /> onOk={() => store.deleteComponent(componentToDelete)} />
<style> <style>
.root { .root {
font-weight: 400; font-weight: 400;
font-size: 0.8rem;
color: #333; color: #333;
} }
.hierarchy-item {
cursor: pointer;
padding: 0 7px 0 3px;
height: 35px;
margin: 5px 0;
border-radius: 0 5px 5px 0;
display: flex;
align-items: center;
font-weight: 500;
}
.hierarchy-item:hover {
background: #fafafa;
}
.selected {
color: var(--button-text);
background: var(--background-button) !important;
}
.title { .title {
margin-left: 10px; margin-left: 10px;
margin-top: 2px; margin-top: 2px;

View File

@ -38,7 +38,7 @@
{#each components as component, index (component._id)} {#each components as component, index (component._id)}
<li on:click|stopPropagation={() => onSelect(component)}> <li on:click|stopPropagation={() => onSelect(component)}>
<div <div
class="item" class="budibase__nav-item item"
class:selected={currentComponent === component} class:selected={currentComponent === component}
style="padding-left: {level * 20 + 53}px"> style="padding-left: {level * 20 + 53}px">
<div>{get_capitalised_name(component._component)}</div> <div>{get_capitalised_name(component._component)}</div>
@ -98,7 +98,6 @@
border-radius: 3px; border-radius: 3px;
height: 35px; height: 35px;
align-items: center; align-items: center;
font-size: 0.8rem;
font-weight: normal; font-weight: normal;
} }
@ -129,12 +128,6 @@
color: var(--button-text); color: var(--button-text);
} }
.selected {
color: var(--button-text);
background: var(--background-button) !important;
font-weight: 500;
}
.reorder-buttons { .reorder-buttons {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -34,10 +34,17 @@
let selectedEvent = null let selectedEvent = null
$: { $: {
const componentDefinition = components.find(c => c.name === component._component) const componentDefinition = components.find(
c => c.name === component._component
)
events = Object.keys(componentDefinition.props) events = Object.keys(componentDefinition.props)
.filter(propName => componentDefinition.props[propName].type === EVENT_TYPE) .filter(
.map(propName => ({ name: propName, handlers: (component[propName] || []) })) propName => componentDefinition.props[propName].type === EVENT_TYPE
)
.map(propName => ({
name: propName,
handlers: component[propName] || [],
}))
} }
const openModal = event => { const openModal = event => {
@ -61,7 +68,8 @@
{#each events as event, index} {#each events as event, index}
{#if event.handlers.length > 0} {#if event.handlers.length > 0}
<div <div
class="handler-container hierarchy-item {selectedEvent && selectedEvent.index === index ? 'selected' : ''}" class:selected={selectedEvent && selectedEvent.index === index}
class="handler-container budibase__nav-item"
on:click={() => openModal({ ...event, index })}> on:click={() => openModal({ ...event, index })}>
<span class="event-name">{event.name}</span> <span class="event-name">{event.name}</span>
<span class="edit-text">EDIT</span> <span class="edit-text">EDIT</span>
@ -107,21 +115,9 @@
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
border: 2px solid #f9f9f9; border: 2px solid #f9f9f9;
height: 80px; height: 80px;
}
.hierarchy-item {
cursor: pointer;
padding: 11px 7px;
margin: 5px 0;
border-radius: 5px;
font-size: 1.5em;
width: 100%; width: 100%;
} }
.hierarchy-item:hover {
background: #f9f9f9;
}
.event-name { .event-name {
margin-top: 5px; margin-top: 5px;
font-weight: bold; font-weight: bold;

View File

@ -51,12 +51,12 @@
} }
</script> </script>
<div class="root"> <div
<div class="budibase__nav-item"
class="hierarchy-item component" class:selected={$store.currentComponentInfo._id === _layout.component.props._id}
class:selected={$store.currentComponentInfo._id === _layout.component.props._id} on:click|stopPropagation={() => store.setScreenType('page')}>
on:click|stopPropagation={() => store.setScreenType('page')}>
<div class="component">
<span <span
class="icon" class="icon"
class:rotate={$store.currentPreviewItem.name !== _layout.title}> class:rotate={$store.currentPreviewItem.name !== _layout.title}>
@ -87,37 +87,11 @@
<ConfirmDialog <ConfirmDialog
bind:this={confirmDeleteDialog} bind:this={confirmDeleteDialog}
title="Confirm Delete" title="Confirm Delete"
body={`Are you sure you wish to delete this '${lastPartOfName(componentToDelete)}' component`} body={`Are you sure you wish to delete this '${lastPartOfName(componentToDelete)}' component?`}
okText="Delete Component" okText="Delete Component"
onOk={() => store.deleteComponent(componentToDelete)} /> onOk={() => store.deleteComponent(componentToDelete)} />
<style> <style>
.root {
font-weight: 400;
font-size: 0.8rem;
color: #333;
}
.hierarchy-item {
cursor: pointer;
padding: 0 7px 0 3px;
height: 35px;
margin: 5px 0;
border-radius: 0 5px 5px 0;
display: flex;
align-items: center;
font-weight: 500;
}
.hierarchy-item:hover {
background: #fafafa;
}
.selected {
color: var(--button-text);
background: var(--background-button) !important;
}
.title { .title {
margin-left: 10px; margin-left: 10px;
margin-top: 2px; margin-top: 2px;

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><path fill="#F0DB4F" d="M1.408 1.408h125.184v125.185h-125.184z"/><path fill="#323330" d="M116.347 96.736c-.917-5.711-4.641-10.508-15.672-14.981-3.832-1.761-8.104-3.022-9.377-5.926-.452-1.69-.512-2.642-.226-3.665.821-3.32 4.784-4.355 7.925-3.403 2.023.678 3.938 2.237 5.093 4.724 5.402-3.498 5.391-3.475 9.163-5.879-1.381-2.141-2.118-3.129-3.022-4.045-3.249-3.629-7.676-5.498-14.756-5.355l-3.688.477c-3.534.893-6.902 2.748-8.877 5.235-5.926 6.724-4.236 18.492 2.975 23.335 7.104 5.332 17.54 6.545 18.873 11.531 1.297 6.104-4.486 8.08-10.234 7.378-4.236-.881-6.592-3.034-9.139-6.949-4.688 2.713-4.688 2.713-9.508 5.485 1.143 2.499 2.344 3.63 4.26 5.795 9.068 9.198 31.76 8.746 35.83-5.176.165-.478 1.261-3.666.38-8.581zm-46.885-37.793h-11.709l-.048 30.272c0 6.438.333 12.34-.714 14.149-1.713 3.558-6.152 3.117-8.175 2.427-2.059-1.012-3.106-2.451-4.319-4.485-.333-.584-.583-1.036-.667-1.071l-9.52 5.83c1.583 3.249 3.915 6.069 6.902 7.901 4.462 2.678 10.459 3.499 16.731 2.059 4.082-1.189 7.604-3.652 9.448-7.401 2.666-4.915 2.094-10.864 2.07-17.444.06-10.735.001-21.468.001-32.237z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB