base analytics and settings

This commit is contained in:
marblekirby 2019-09-23 01:01:27 +01:00
commit 011aa9f2cc
124 changed files with 25351 additions and 2664 deletions

View File

@ -2,4 +2,5 @@ Contributors
=== ===
* Michael Shanks - [@mjashanks](https://github.com/mjashanks) * Michael Shanks - [@mjashanks](https://github.com/mjashanks)
* Daniel Loudon - [@danbudi](https://github.com/danbudi) * Dan - [@danbudi](https://github.com/danbudi)
* Joe - [@joebudi](https://github.com/joebudi)

View File

@ -33,6 +33,7 @@
] ]
}, },
"dependencies": { "dependencies": {
"@budibase/client": "*",
"@nx-js/compiler-util": "^2.0.0", "@nx-js/compiler-util": "^2.0.0",
"date-fns": "^1.29.0", "date-fns": "^1.29.0",
"feather-icons": "^4.21.0", "feather-icons": "^4.21.0",
@ -42,8 +43,7 @@
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"shortid": "^2.2.8", "shortid": "^2.2.8",
"string_decoder": "^1.2.0", "string_decoder": "^1.2.0",
"uikit": "^3.1.7", "uikit": "^3.1.7"
"universal-analytics": "^0.4.20"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.5.5", "@babel/core": "^7.5.5",

View File

@ -33,7 +33,7 @@ const production = !process.env.ROLLUP_WATCH;
const lodash_fp_exports = ["union", "reduce", "isUndefined", "cloneDeep", "split", "some", "map", "filter", "isEmpty", "countBy", "includes", "last", "find", "constant", const lodash_fp_exports = ["union", "reduce", "isUndefined", "cloneDeep", "split", "some", "map", "filter", "isEmpty", "countBy", "includes", "last", "find", "constant",
"take", "first", "intersection", "mapValues", "isNull", "has", "isNumber", "isString", "isBoolean", "isDate", "isArray", "isObject", "clone", "values", "keyBy", "take", "first", "intersection", "mapValues", "isNull", "has", "isNumber", "isString", "isBoolean", "isDate", "isArray", "isObject", "clone", "values", "keyBy",
"keys", "orderBy", "concat", "reverse", "difference", "merge", "flatten", "each", "pull", "join", "defaultCase", "uniqBy", "every", "uniqWith", "isFunction", "groupBy", "keys", "orderBy", "concat", "reverse", "difference", "merge", "flatten", "each", "pull", "join", "defaultCase", "uniqBy", "every", "uniqWith", "isFunction", "groupBy",
"differenceBy", "intersectionBy", "isEqual", "max", "sortBy", "assign", "uniq", "trimChars", "trimCharsStart", "isObjectLike", "flattenDeep", "indexOf"]; "differenceBy", "intersectionBy", "isEqual", "max", "sortBy", "assign", "uniq", "trimChars", "trimCharsStart", "isObjectLike", "flattenDeep", "indexOf", "isPlainObject"];
const lodash_exports = ["toNumber", "flow", "isArray", "join", "replace", "trim", "dropRight", "takeRight", "head", "isUndefined", "isNull", "isNaN", "reduce", "isEmpty", const lodash_exports = ["toNumber", "flow", "isArray", "join", "replace", "trim", "dropRight", "takeRight", "head", "isUndefined", "isNull", "isNaN", "reduce", "isEmpty",
"constant", "tail", "includes", "startsWith", "findIndex", "isInteger", "isDate", "isString", "split", "clone", "keys", "isFunction", "merge", "has", "isBoolean", "isNumber", "constant", "tail", "includes", "startsWith", "findIndex", "isInteger", "isDate", "isString", "split", "clone", "keys", "isFunction", "merge", "has", "isBoolean", "isNumber",
@ -59,7 +59,8 @@ export default {
copy({ copy({
targets: [ targets: [
{ src: 'src/index.html', dest: outputpath }, { src: 'src/index.html', dest: outputpath },
{ src: 'src/favicon.png', dest: outputpath } { src: 'src/favicon.png', dest: outputpath },
{ src: 'node_modules/@budibase/client/dist/budibase-client.esm.mjs', dest: outputpath }
] ]
}), }),

View File

@ -48,7 +48,7 @@
main { main {
height: 100%; height: 100%;
width: 100%; width: 100%;
font-family: "Lato", Helvetica, Arial, sans-serif; font-family: "Roboto", Helvetica, Arial, sans-serif;
} }
.settings { .settings {

View File

@ -100,16 +100,19 @@ const getPermissionsString = perms => {
<style> <style>
.root { .root {
padding:10px; height: 100%;
position: relative;
padding: 1.5rem;
} }
.edit-button { .actions-header {
cursor:pointer; flex: 0 1 auto;
color: var(--white);
} }
tr:hover .edit-button { .node-view {
color: var(--secondary75); overflow-y: auto;
flex: 1 1 auto;
} }

View File

@ -81,8 +81,8 @@ const cancel = () => {
<div class=" uk-form-stacked" style="margin-bottom: 20px"> <div class=" uk-form-stacked" style="margin-bottom: 20px">
<label class="uk-form-label">Default Options</label> <label class="uk-form-label">Default Options</label>
<div class="uk-grid-small" uk-grid> <div class="uk-grid-small" uk-grid>
<input class="uk-input uk-width-1-4" placeholder="key" bind:value={optKey} > <input class="uk-input uk-width-1-4 uk-margin-right" placeholder="key" bind:value={optKey} >
<input class="uk-input uk-width-1-4" placeholder="value" bind:value={optValue} > <input class="uk-input uk-width-1-4 uk-margin-right" placeholder="value" bind:value={optValue} >
<Button color="primary-outline uk-width-1-4" on:click={addNewOption}>Add</Button> <Button color="primary-outline uk-width-1-4" on:click={addNewOption}>Add</Button>
</div> </div>
<div style="margin-top: 10px"> <div style="margin-top: 10px">
@ -93,8 +93,8 @@ const cancel = () => {
</div> </div>
<ButtonGroup> <ButtonGroup>
<Button color="primary" grouped on:click={save}>Save</Button> <Button color="secondary" grouped on:click={save}>Save</Button>
<Button color="secondary" grouped on:click={cancel}>Cancel</Button> <Button color="tertiary" grouped on:click={cancel}>Cancel</Button>
</ButtonGroup> </ButtonGroup>
@ -104,7 +104,12 @@ const cancel = () => {
<style> <style>
.root { .root {
padding: 10px; padding: 2rem;
border-radius: 2rem;
}
.uk-grid-small {
padding: 1rem;
} }
.option-container { .option-container {

View File

@ -45,13 +45,13 @@ let actionEditingFinished = (action) => {
</script> </script>
<h3>Actions</h3> <h3 class="title">Actions</h3>
{#if actionsArray} {#if actionsArray}
<table class="fields-table uk-table uk-table-small"> <table class="fields-table uk-table uk-table-small uk-table-striped">
<thead> <thead>
<tr> <tr>
<th>Description</th> <th >Description</th>
<th>Behaviour Source</th> <th>Behaviour Source</th>
<th>Behaviour Name</th> <th>Behaviour Name</th>
<th>Default Options</th> <th>Default Options</th>
@ -61,10 +61,10 @@ let actionEditingFinished = (action) => {
<tbody> <tbody>
{#each actionsArray as action} {#each actionsArray as action}
<tr> <tr>
<td >{action.name}</td> <td class="table-content">{action.name}</td>
<td >{action.behaviourSource}</td> <td class="table-content">{action.behaviourSource}</td>
<td >{action.behaviourName}</td> <td class="table-content">{action.behaviourName}</td>
<td >{@html getDefaultOptionsHtml(action.initialOptions)}</td> <td class="table-content">{@html getDefaultOptionsHtml(action.initialOptions)}</td>
<td class="edit-button"> <td class="edit-button">
<span on:click={() => onActionEdit(action)}>{@html getIcon("edit")}</span> <span on:click={() => onActionEdit(action)}>{@html getIcon("edit")}</span>
<span on:click={() => onActionDelete(action)}>{@html getIcon("trash")}</span> <span on:click={() => onActionDelete(action)}>{@html getIcon("trash")}</span>
@ -92,12 +92,21 @@ let actionEditingFinished = (action) => {
.edit-button { .edit-button {
cursor:pointer; cursor:pointer;
color: var(--white); color: var(--secondary25);
} }
tr:hover .edit-button { tr:hover .edit-button {
color: var(--secondary75); color: var(--secondary75);
} }
.title {
margin: 3rem 0rem 0rem 0rem;
font-weight: 700;
}
.table-content {
font-weight: 500;
font-size: .9rem;
}
</style> </style>

View File

@ -85,31 +85,45 @@ let onTriggerDelete = (trigger) => {
</script> </script>
<div class="root"> <div class="root">
<div class="actions-header">
<ButtonGroup>
<Button color="secondary"
grouped
on:click={newAction}>Create New Action</Button>
<Button color="tertiary"
grouped
on:click={newTrigger}>Create New Trigger</Button>
</ButtonGroup>
</div>
<ButtonGroup> <div class="node-view">
<Button color="secondary" <Actions {editingActionIsNew} {editingAction}
grouped {onActionEdit} {onActionDelete} {onActionSave}
on:click={newAction}>Create New Action</Button> {onActionCancel} />
<Button color="secondary"
grouped
on:click={newTrigger}>Create New Trigger</Button>
</ButtonGroup>
<Actions {editingActionIsNew} {editingAction} <Triggers {editingTriggerIsNew} {editingTrigger}
{onActionEdit} {onActionDelete} {onActionSave} {onTriggerEdit} {onTriggerDelete} {onTriggerSave}
{onActionCancel} /> {onTriggerCancel} />
</div>
<Triggers {editingTriggerIsNew} {editingTrigger}
{onTriggerEdit} {onTriggerDelete} {onTriggerSave}
{onTriggerCancel} />
</div> </div>
<style> <style>
.root { .root {
padding: 10px; height: 100%;
position: relative;
padding: 1.5rem;
} }
.actions-header {
flex: 0 1 auto;
}
.node-view {
overflow-y: auto;
flex: 1 1 auto;
}
</style> </style>

View File

@ -57,8 +57,8 @@ let save = () => {
</form> </form>
<ButtonGroup> <ButtonGroup>
<Button grouped on:click={save}>Save</Button> <Button color="primary" grouped on:click={save}>Save</Button>
<Button grouped on:click={cancel}>Cancel</Button> <Button color="tertiary" grouped on:click={cancel}>Cancel</Button>
</ButtonGroup> </ButtonGroup>
</div> </div>

View File

@ -27,10 +27,10 @@ let triggerEditingFinished = (trigger) => {
</script> </script>
<h3>Triggers</h3> <h3 class="title">Triggers</h3>
{#if $store.triggers} {#if $store.triggers}
<table class="fields-table uk-table uk-table-small"> <table class="fields-table uk-table uk-table-small uk-table-striped">
<thead> <thead>
<tr> <tr>
<th>Event</th> <th>Event</th>
@ -43,10 +43,10 @@ let triggerEditingFinished = (trigger) => {
<tbody> <tbody>
{#each $store.triggers as trigger} {#each $store.triggers as trigger}
<tr> <tr>
<td >{trigger.eventName}</td> <td class="table-content">{trigger.eventName}</td>
<td >{trigger.actionName}</td> <td class="table-content">{trigger.actionName}</td>
<td >{trigger.condition}</td> <td class="table-content">{trigger.condition}</td>
<td >{trigger.optionsCreator}</td> <td class="table-content">{trigger.optionsCreator}</td>
<td class="edit-button"> <td class="edit-button">
<span on:click={() => onTriggerEdit(trigger)}>{@html getIcon("edit")}</span> <span on:click={() => onTriggerEdit(trigger)}>{@html getIcon("edit")}</span>
<span on:click={() => onTriggerDelete(trigger)}>{@html getIcon("trash")}</span> <span on:click={() => onTriggerDelete(trigger)}>{@html getIcon("trash")}</span>
@ -74,7 +74,17 @@ let triggerEditingFinished = (trigger) => {
.edit-button { .edit-button {
cursor:pointer; cursor:pointer;
color: var(--white); color: var(--secondary25);
}
.title {
margin: 3rem 0rem 0rem 0rem;
font-weight: 700;
}
.table-content {
font-weight: 500;
font-size: .9rem;
} }
tr:hover .edit-button { tr:hover .edit-button {

View File

@ -1,5 +1,3 @@
import { isUndefined } from "lodash/fp";
const apiCall = (method) => (url, body) => const apiCall = (method) => (url, body) =>
fetch(url, { fetch(url, {
method: method, method: method,

View File

@ -11,6 +11,17 @@ export const loadLibs = async (appName, appPackage) => {
return allLibraries; return allLibraries;
} }
export const loadLibUrls = (appName, appPackage) => {
const allLibraries = [];
for(let lib of appPackage.pages.componentLibraries) {
const libUrl = makeLibraryUrl(appName, lib);
allLibraries.push({libName:lib, importPath:libUrl});
}
return allLibraries;
}
export const loadLib = async (appName, lib, allLibs) => { export const loadLib = async (appName, lib, allLibs) => {
allLibs[lib] = await import(makeLibraryUrl(appName, lib)); allLibs[lib] = await import(makeLibraryUrl(appName, lib));
return allLibs; return allLibs;

View File

@ -1,26 +1,14 @@
import { import {
hierarchy as hierarchyFunctions, hierarchy as hierarchyFunctions,
common
} from "../../../core/src"; } from "../../../core/src";
import { import {
filter, filter, cloneDeep, sortBy,
cloneDeep, map, last, keys, concat,
sortBy, find, isEmpty, reduce
map,
last,
keys,
concat,
find,
isEmpty,
groupBy,
reduce
} from "lodash/fp"; } from "lodash/fp";
import { import {
pipe, pipe, getNode, validate,
getNode, constructHierarchy, templateApi
validate,
constructHierarchy,
templateApi
} from "../common/core"; } from "../common/core";
import {writable} from "svelte/store"; import {writable} from "svelte/store";
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject" import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject"
@ -28,10 +16,9 @@ import { buildPropsHierarchy } from "../userInterface/pagesParsing/buildPropsHie
import api from "./api"; import api from "./api";
import { isRootComponent } from "../userInterface/pagesParsing/searchComponents"; import { isRootComponent } from "../userInterface/pagesParsing/searchComponents";
import { import {
getComponentInfo, getComponentInfo, getNewComponentInfo
getNewComponentInfo
} from "../userInterface/pagesParsing/createProps"; } from "../userInterface/pagesParsing/createProps";
import { loadLibs } from "./loadComponentLibraries"; import { loadLibs, loadLibUrls } from "./loadComponentLibraries";
let appname = ""; let appname = "";
@ -121,6 +108,7 @@ const initialise = (store, initial) => async () => {
.then(r => r.json()); .then(r => r.json());
initial.libraries = await loadLibs(appname, pkg); initial.libraries = await loadLibs(appname, pkg);
initial.loadLibraryUrls = () => loadLibUrls(appname, pkg);
initial.appname = appname; initial.appname = appname;
initial.pages = pkg.pages; initial.pages = pkg.pages;
initial.hasAppPackage = true; initial.hasAppPackage = true;

View File

@ -27,8 +27,10 @@ $: borderClass = grouped
button { button {
border-style: solid; border-style: solid;
padding: 7px 15px; padding: 7.5px 15px;
cursor:pointer; cursor:pointer;
margin:5px;
border-radius: 5px;
} }
/* ---- PRIMARY ----*/ /* ---- PRIMARY ----*/

View File

@ -11,11 +11,14 @@ export let label = "";
textarea { textarea {
padding:3px; padding:3px;
background: var(--darkslate); margin-top:5px;
margin-bottom:10px;
background: var(--lightslate);
color: var(--white); 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;
} }
</style> </style>

View File

@ -46,7 +46,7 @@ let isDroppedDown = false;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1; z-index: 1;
font-weight: normal; font-weight: normal;
border-style:solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-color: var(--secondary10); border-color: var(--secondary10);
} }

View File

@ -34,4 +34,8 @@ $: {
<style> <style>
.uk-modal-dialog {
border-radius: .3rem;
}
</style> </style>

View File

@ -0,0 +1,25 @@
import {
isString
} from "lodash/fp";
export const BB_STATE_BINDINGPATH = "##bbstate";
export const BB_STATE_FALLBACK = "##bbstatefallback";
export const isBinding = value =>
!isString(value)
&& value
&& isString(value[BB_STATE_BINDINGPATH])
&& value[BB_STATE_BINDINGPATH].length > 0;
export const setBinding = ({path, fallback}, binding={} ) => {
if(isNonEmptyString(path)) binding[BB_STATE_BINDINGPATH] = path;
if(isNonEmptyString(fallback)) binding[BB_STATE_FALLBACK] = fallback;
return binding
}
export const getBinding = binding => ({
path: binding[BB_STATE_BINDINGPATH] || "",
fallback: binding[BB_STATE_FALLBACK] || ""
});
const isNonEmptyString = s => isString(s) && s.length > 0;

View File

@ -0,0 +1 @@
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType";

View File

@ -1,3 +1,3 @@
import feather from "feather-icons"; import feather from "feather-icons";
const getIcon = (icon, size) => feather.icons[icon].toSvg({height:size||"24", width:size||"24"}); const getIcon = (icon, size) => feather.icons[icon].toSvg({height:size||"16", width:size||"16"});
export default getIcon; export default getIcon;

View File

@ -30,7 +30,7 @@ const deleteCurrentNode = () => {
</Button> </Button>
{#if !$store.currentNodeIsNew} {#if !$store.currentNodeIsNew}
<Button color="secondary" grouped on:click={openConfirmDelete}> <Button color="tertiary" grouped on:click={openConfirmDelete}>
Delete Delete
</Button> </Button>
{/if} {/if}
@ -54,9 +54,9 @@ const deleteCurrentNode = () => {
<style> <style>
.root { .root {
padding:5px; padding: 1.5rem;
top:0; width: 100%;
width:100%; align-items: right;
} }
</style> </style>

View File

@ -109,7 +109,7 @@ const save = () => {
<ButtonGroup style="float: right;"> <ButtonGroup style="float: right;">
<Button color="primary" grouped on:click={save}>Save</Button> <Button color="primary" grouped on:click={save}>Save</Button>
<Button color="secondary" grouped on:click={() => onFinished(false)}>Cancel</Button> <Button color="tertiary" grouped on:click={() => onFinished(false)}>Cancel</Button>
</ButtonGroup> </ButtonGroup>
</div> </div>

View File

@ -24,13 +24,13 @@ export let node;
.root { .root {
display: block; display: block;
font-size: 13pt; font-size: 1rem;
width: 100%; width: 100%;
cursor: pointer; cursor: pointer;
} }
.title { .title {
font: var(--bodytext); font: var(--fontblack);
padding-top: 10px; padding-top: 10px;
padding-right: 5px; padding-right: 5px;
padding-bottom: 10px; padding-bottom: 10px;

View File

@ -85,18 +85,21 @@ let getTypeOptions = typeOptions =>
<div class="root"> <div class="root">
<form class="uk-form-horizontal"> <form class="uk-form-horizontal">
<h3 class="settings-title">
Settings
</h3>
<Textbox label="Name" bind:text={record.name} /> <Textbox label="Name:" bind:text={record.name} />
<div>{record.nodeKey()}</div>
{#if !record.isSingle} {#if !record.isSingle}
<Textbox label="Collection Name" bind:text={record.collectionName} /> <Textbox label="Collection Name:" bind:text={record.collectionName} />
<Textbox label="Shard Factor" bind:text={record.allidsShardFactor} /> <Textbox label="Shard Factor:" bind:text={record.allidsShardFactor} />
{/if} {/if}
<div class="recordkey">{record.nodeKey()}</div>
</form> </form>
<h4> <h3 class="title">
Fields <span class="add-field-button" on:click={newField}>{@html getIcon("plus")}</span> Fields <span class="add-field-button" on:click={newField}>{@html getIcon("plus")}</span>
</h4> </h3>
{#if record.fields.length > 0} {#if record.fields.length > 0}
<table class="fields-table uk-table"> <table class="fields-table uk-table">
@ -138,9 +141,9 @@ let getTypeOptions = typeOptions =>
</Modal> </Modal>
{/if} {/if}
<h4> <h3 class="title">
Indexes Indexes
</h4> </h3>
{#each record.indexes as index} {#each record.indexes as index}
<div class="index-container"> <div class="index-container">
@ -166,7 +169,9 @@ let getTypeOptions = typeOptions =>
{/if} {/if}
</div> </div>
{:else} {:else}
(no indexes added) <div class="no-indexes">
No indexes added.
</div>
{/each} {/each}
</div> </div>
@ -176,25 +181,40 @@ let getTypeOptions = typeOptions =>
.root { .root {
height: 100%; height: 100%;
padding: 15px; padding: 2rem;
}
.settings-title {
font-weight: 700;
}
.title {
margin: 3rem 0rem 0rem 0rem;
font-weight: 700;
}
.recordkey {
font-size: 14px;
font-weight: 600;
color: var(--primary100);
} }
.fields-table { .fields-table {
margin:10px; margin: 1rem 1rem 0rem 0rem;
border-collapse:collapse; border-collapse:collapse;
} }
.add-field-button { .add-field-button {
margin-left:15px;
cursor:pointer; cursor:pointer;
} }
.edit-button { .edit-button {
cursor:pointer; cursor:pointer;
color: var(--white); color: var(--secondary25);
} }
.edit-button:hover { .edit-button:hover {
cursor:pointer;
color: var(--secondary75); color: var(--secondary75);
} }
@ -203,7 +223,7 @@ th {
} }
td { td {
padding: 5px 30px 5px 0px; padding: 1rem 5rem 1rem 0rem;
margin:0; margin:0;
} }
@ -255,7 +275,12 @@ tbody > tr:hover .edit-button {
} }
.index-field-row { .index-field-row {
margin-top: 7px; margin: 1rem 0rem 0rem 0rem;
} }
.no-indexes {
margin: 1rem 0rem 0rem 0rem;
font-family: var(--fontnormal);
font-size: 14px;
}
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -1,61 +1,47 @@
/* latin-ext */ /* latin-ext */
/* roboto-regular - latin-ext_latin */
@font-face { @font-face {
font-family: 'Lato'; font-family: 'Roboto';
font-weight: 400; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin-ext.woff2) format('woff2'); font-weight: 400;
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; src: local('Roboto'), local('Roboto-Regular'),
url('/_builder/assets/roboto-v20-latin-ext_latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('/_builder/assets/roboto-v20-latin-ext_latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* latin */ /* roboto-300 - latin-ext_latin */
@font-face { @font-face {
font-family: 'Lato'; font-family: 'Roboto';
font-weight: 400; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin.woff2) format('woff2'); font-weight: 300;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; src: local('Roboto Light'), local('Roboto-Light'),
url('/_builder/assets/roboto-v20-latin-ext_latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('/_builder/assets/roboto-v20-latin-ext_latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* roboto-500 - latin-ext_latin */
/* Black latin-ext */
@font-face { @font-face {
font-family: 'Lato Black'; font-family: 'Roboto';
font-weight: 900; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin-ext.woff2) format('woff2'); font-weight: 500;
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; src: local('Roboto Medium'), local('Roboto-Medium'),
url('/_builder/assets/roboto-v20-latin-ext_latin-500.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('/_builder/assets/roboto-v20-latin-ext_latin-500.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* Black latin */ /* roboto-700 - latin-ext_latin */
@font-face { @font-face {
font-family: 'Lato Black'; font-family: 'Roboto';
font-weight: 900; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin.woff2) format('woff2'); font-weight: 700;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; src: local('Roboto Bold'), local('Roboto-Bold'),
url('/_builder/assets/roboto-v20-latin-ext_latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('/_builder/assets/roboto-v20-latin-ext_latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* roboto-900 - latin-ext_latin */
/* Bold latin-ext */
@font-face { @font-face {
font-family: 'Lato Bold'; font-family: 'Roboto';
font-weight: 700; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin-ext.woff2) format('woff2'); font-weight: 900;
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; src: local('Roboto Black'), local('Roboto-Black'),
} url('/_builder/assets/roboto-v20-latin-ext_latin-900.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
/* Bold latin */ url('/_builder/assets/roboto-v20-latin-ext_latin-900.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
@font-face {
font-family: 'Lato Bold';
font-weight: 700;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Semi Bold latin-ext */
@font-face {
font-family: 'Lato Semi Bold';
font-weight: 600;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin-ext.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Semi Bold latin */
@font-face {
font-family: 'Lato Semi Bold';
font-weight: 600;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
} }

View File

@ -4,6 +4,7 @@
--primary50: #454CA080; --primary50: #454CA080;
--primary25: #454CA040; --primary25: #454CA040;
--primary10: #454CA01A; --primary10: #454CA01A;
--primary5: #454ca00c;
--primarydark: #3F448A; --primarydark: #3F448A;
--secondary100: #162B4DFF; --secondary100: #162B4DFF;
@ -11,6 +12,7 @@
--secondary50: #162B4D80; --secondary50: #162B4D80;
--secondary25: #162B4D40; --secondary25: #162B4D40;
--secondary10: #162B4D1A; --secondary10: #162B4D1A;
--secondary5: rgba(22, 43, 77, 0.068);
--secondarydark: #3F448A; --secondarydark: #3F448A;
--tertiary: #F2F5F7; --tertiary: #F2F5F7;
@ -30,17 +32,18 @@
--deletiondark: #CF4046; --deletiondark: #CF4046;
--white: #FFFFFF; --white: #FFFFFF;
--darkslate: #5C6B82; --darkslate: #1a202c;
--slate: #8B95A6; --slate: #a0aec0;
--lightslate: rgb(203, 212, 228); --lightslate: #f7fafc;
--borderradius: 2px; --borderradius: 2px;
--borderradiusall: 2px 2px 2px 2px; --borderradiusall: 2px 2px 2px 2px;
--fontblack: "Lato Black"; --fontblack: "Roboto Black";
--fontbold: "Lato Bold"; --fontbold: "Roboto Bold";
--fontsemibold: "Lato Semi Bold"; --fontsemibold: "Roboto Medium";
--fontnormal: "Lato"; --fontnormal: "Roboto";
--fontlight: "Roboto Light";
--bodytext: var(--fontnormal) "regular" var(--secondary100) 16pt; --bodytext: var(--fontnormal) "regular" var(--secondary100) 16pt;
--bigbodytext: var(--fontnormal) "regular" var(--secondary100) 20pt; --bigbodytext: var(--fontnormal) "regular" var(--secondary100) 20pt;
@ -79,7 +82,7 @@ h3 {
} }
h4 { h4 {
font-family: var(--fontsemibold); font-family: var(--fontbold);
font-size: 18pt; font-size: 18pt;
color: var(--secondary100); color: var(--secondary100);
} }

View File

@ -1,8 +1,11 @@
import App from "./App.svelte"; import App from "./App.svelte";
import "./global.css"; import "./global.css";
import "./fonts.css"; import "./fonts.css";
import "/assets/lato-latin-ext.woff2"; import "/assets/roboto-v20-latin-ext_latin-300";
import "/assets/lato-latin.woff2"; import "/assets/roboto-v20-latin-ext_latin-400";
import "/assets/roboto-v20-latin-ext_latin-500";
import "/assets/roboto-v20-latin-ext_latin-700";
import "/assets/roboto-v20-latin-ext_latin-900";
import "/assets/budibase-logo.png"; import "/assets/budibase-logo.png";
import "/assets/budibase-logo-only.png"; import "/assets/budibase-logo-only.png";
import "uikit/dist/css/uikit.min.css"; import "uikit/dist/css/uikit.min.css";

View File

@ -73,7 +73,7 @@ store.subscribe(db => {
.root { .root {
height: 100%; height: 100%;
background-color: var(--primary10); background-color: var(--secondary5);
} }
.items-root { .items-root {
@ -90,8 +90,8 @@ store.subscribe(db => {
.hierarchy-title-row { .hierarchy-title-row {
padding: 15px 7px; padding: 2rem 1rem 0rem 1rem;
font-size: 12pt; font-size: 10pt;
display: flex; display: flex;
font-weight: bold; font-weight: bold;
} }

View File

@ -43,15 +43,14 @@ store.subscribe(s => {
.root { .root {
display: block; display: block;
font-size: 12pt; font-size: 10pt;
width: 100%; width: 100%;
cursor: pointer; cursor: pointer;
} }
.title { .title {
padding-top: 5px; padding-top: .5rem;
padding-right: 5px; padding-right: .5rem;
padding-bottom: 5px;
color: var(--secondary100); color: var(--secondary100);
} }

View File

@ -25,8 +25,8 @@ const setActive = () =>
<style> <style>
.nav-item { .nav-item {
padding: 7px; padding: 1rem 1rem 0rem 1rem;
font-size: 12pt; font-size: 10pt;
font-weight: bold; font-weight: bold;
cursor: pointer; cursor: pointer;
flex: 0 0 auto; flex: 0 0 auto;

View File

@ -10,7 +10,7 @@ import {
} from "lodash/fp"; } from "lodash/fp";
import { fade, slide } from 'svelte/transition'; import { fade, slide } from 'svelte/transition';
export let propertyName = ""; export let title = "";
export let onGoBack = () => {}; export let onGoBack = () => {};
export let instanceProps = {}; export let instanceProps = {};
export let onPropsChanged = () => {}; export let onPropsChanged = () => {};
@ -18,6 +18,9 @@ export let onPropsChanged = () => {};
let editingSubComponentName; let editingSubComponentName;
let editingSubComponentProps; let editingSubComponentProps;
let editingSubComponentArrayIndex;
let editingSubComponentArrayPropName;
let editingSubComponentTitle;
let allComponents; let allComponents;
store.subscribe(s => { store.subscribe(s => {
@ -33,18 +36,28 @@ const onSubComponentGoBack = () => {
} }
const onEditComponentProp = (propName, arrayIndex, arrayPropName) => { const onEditComponentProp = (propName, arrayIndex, arrayPropName) => {
editingSubComponentName = isUndefined(arrayIndex) editingSubComponentName = propName;
editingSubComponentTitle = isUndefined(arrayIndex)
? propName ? propName
: `${propName}[${arrayIndex}].${arrayPropName}`; : `${propName}[${arrayIndex}].${arrayPropName}`;
editingSubComponentProps = isUndefined(arrayIndex) editingSubComponentProps = isUndefined(arrayIndex)
? instanceProps[propName] ? instanceProps[propName]
: instanceProps[propName][arrayIndex][arrayPropName]; : instanceProps[propName][arrayIndex][arrayPropName];
editingSubComponentArrayIndex = arrayIndex;
editingSubComponentArrayPropName = arrayPropName;
}; };
const onSubComponentPropsChanged = (subProps) => { const onSubComponentPropsChanged = (subProps) => {
const newProps = cloneDeep(instanceProps); const newProps = cloneDeep(instanceProps);
newProps[editingSubComponentName] = subProps; if(isUndefined(editingSubComponentArrayIndex)) {
newProps[editingSubComponentName] = subProps;
} else {
newProps[editingSubComponentName]
[editingSubComponentArrayIndex]
[editingSubComponentArrayPropName] = subProps;
}
instanceProps = newProps; instanceProps = newProps;
onPropsChanged(newProps); onPropsChanged(newProps);
} }
@ -62,7 +75,7 @@ const propsChanged = newProps => {
<div class="title"> <div class="title">
<IconButton icon="chevron-left" <IconButton icon="chevron-left"
on:click={onGoBack}/> on:click={onGoBack}/>
<span>{propertyName}</span> <span>{title}</span>
</div> </div>
{#if editingSubComponentName} {#if editingSubComponentName}
@ -71,7 +84,7 @@ const propsChanged = newProps => {
<svelte:self onPropsChanged={onSubComponentPropsChanged} <svelte:self onPropsChanged={onSubComponentPropsChanged}
onGoBack={onSubComponentGoBack} onGoBack={onSubComponentGoBack}
instanceProps={editingSubComponentProps} instanceProps={editingSubComponentProps}
propertyName={editingSubComponentName} /> title={editingSubComponentTitle} />
</div> </div>
{:else} {:else}
<PropsView {instanceProps} <PropsView {instanceProps}

View File

@ -18,7 +18,6 @@ export let props = emptyProps();
export let onValueChanged = () => {}; export let onValueChanged = () => {};
export let onComponentChosen = () => {}; export let onComponentChosen = () => {};
export let onEdit = () => {}; export let onEdit = () => {};
export let label = "";
export let disabled = false; export let disabled = false;
const CHOOSE_COMPONENT = "choose_component"; const CHOOSE_COMPONENT = "choose_component";
@ -70,7 +69,6 @@ const confirmClearComponent = () => {
</script> </script>
<label class="uk-form-label">{label}</label>
<div class="root uk-form-controls"> <div class="root uk-form-controls">
<div class:selectedname={componentSelected}> <div class:selectedname={componentSelected}>
{componentSelected ? shortName : "(none)"} {componentSelected ? shortName : "(none)"}

View File

@ -2,79 +2,47 @@
import { store } from "../builderStore"; import { store } from "../builderStore";
import { makeLibraryUrl } from "../builderStore/loadComponentLibraries"; import { makeLibraryUrl } from "../builderStore/loadComponentLibraries";
import { import {
last, last, split, map, join
split,
map,
join
} from "lodash/fp"; } from "lodash/fp";
import { pipe } from "../common/core"; import { pipe } from "../common/core";
import { splitName } from "./pagesParsing/splitRootComponentName" import { splitName } from "./pagesParsing/splitRootComponentName"
import { afterUpdate } from 'svelte'; import { afterUpdate } from 'svelte';
import { getRootComponent } from "./pagesParsing/getRootComponent"; import { getRootComponent } from "./pagesParsing/getRootComponent";
import { buildPropsHierarchy } from "./pagesParsing/buildPropsHierarchy";
if(!window.budibaseIframeConnector) {
window.budibaseIframeConnector = {
initialiseComponent(props, htmlElement) {
const rootComponent = getRootComponent(
props._component, allComponents);
const {componentName, libName} = splitName(
rootComponent.name);
new (libraries[libName][componentName])({
target: htmlElement,
props: {...props, _app: window.budibaseIframeConnector}
});
}
}
}
let component; let component;
let stylesheetLinks = ""; let stylesheetLinks = "";
let componentHtml = "";
let props;
let componentLibraryUrl = "";
let rootComponentName = ""; let rootComponentName = "";
let iframe;
let libraries; let libraries;
let allComponents; let allComponents;
let appDefinition = {};
store.subscribe(s => { store.subscribe(s => {
const {componentName, libName} = splitName( const {componentName, libName} = splitName(
s.currentComponentInfo.rootComponent.name); s.currentComponentInfo.rootComponent.name);
rootComponentName = componentName; rootComponentName = componentName;
props = s.currentComponentInfo.fullProps;
component = s.libraries[libName][componentName]; component = s.libraries[libName][componentName];
stylesheetLinks = pipe(s.pages.stylesheets, [ stylesheetLinks = pipe(s.pages.stylesheets, [
map(s => `<link rel="stylesheet" href="${s}"/>`), map(s => `<link rel="stylesheet" href="${s}"/>`),
join("\n") join("\n")
]); ]);
componentLibraryUrl = makeLibraryUrl(s.appname, libName); appDefinition = {
componentLibraries: s.loadLibraryUrls(),
props: buildPropsHierarchy(s.allComponents, s.currentFrontEndItem)
};
libraries = s.libraries; libraries = s.libraries;
allComponents = s.allComponents; allComponents = s.allComponents;
}); });
/*
afterUpdate(() => {
if(iframe) iframeLoaded();
});
*/
const iframeLoaded = () => {
setTimeout(() => {
iframe.style.height = (iframe.contentWindow.document.body.scrollHeight + 1).toString() + "px";
iframe.style.width = (iframe.contentWindow.document.body.scrollWidth + 1).toString() + "px";
}, 100);
}
</script> </script>
<div class="component-preview" > <div class="component-preview" >
<div class="component-container"> <div class="component-container">
<iframe bind:this={iframe} <iframe style="height: 100%; width: 100%"
on:load={iframeLoaded}
title="componentPreview" title="componentPreview"
srcdoc={ srcdoc={
`<html> `<html>
@ -82,16 +50,11 @@ const iframeLoaded = () => {
<head> <head>
${stylesheetLinks} ${stylesheetLinks}
<script> <script>
window["##BUDIBASE_APPDEFINITION##"] = ${JSON.stringify(appDefinition)};
import('${componentLibraryUrl}') import('./budibase-client.esm.mjs')
.then(module => { .then(module => {
const componentClass = module['${rootComponentName}']; module.loadBudibase();
const instance = new componentClass({ })
target: document.body,
props: {...${JSON.stringify(props)}, _app: window.parent.budibaseIframeConnector}
}) ;
})
</script> </script>
</head> </head>
<body> <body>

View File

@ -36,6 +36,10 @@ let modalElement
let propsValidationErrors = []; let propsValidationErrors = [];
let editingComponentInstance; let editingComponentInstance;
let editingComponentInstancePropName=""; let editingComponentInstancePropName="";
let editingComponentArrayIndex;
let editingComponentArrayPropName;
let editingComponentInstanceTitle;
let allComponents; let allComponents;
$: shortName = last(name.split("/")); $: shortName = last(name.split("/"));
@ -116,9 +120,13 @@ const onEditComponentProp = (propName, arrayIndex, arrayPropName) => {
editingComponentInstance = isUndefined(arrayIndex) editingComponentInstance = isUndefined(arrayIndex)
? component.props[propName] ? component.props[propName]
: component.props[propName][arrayIndex][arrayPropName]; : component.props[propName][arrayIndex][arrayPropName];
editingComponentInstancePropName = isUndefined(arrayIndex) editingComponentInstancePropName = propName;
editingComponentInstanceTitle = isUndefined(arrayIndex)
? propName ? propName
: `${propName}[${arrayIndex}].${arrayPropName}`; : `${propName}[${arrayIndex}].${arrayPropName}`;
editingComponentArrayIndex = arrayIndex;
editingComponentArrayPropName = arrayPropName;
} }
const componentInstanceCancelEdit = () => { const componentInstanceCancelEdit = () => {
@ -127,8 +135,15 @@ const componentInstanceCancelEdit = () => {
} }
const componentInstancePropsChanged = (instanceProps) => { const componentInstancePropsChanged = (instanceProps) => {
updateComponent(newComponent => updateComponent(newComponent => {
newComponent.props[editingComponentInstancePropName] = instanceProps); if(isUndefined(editingComponentArrayIndex)) {
newComponent.props[editingComponentInstancePropName] = instanceProps;
} else {
newComponent.props[editingComponentInstancePropName]
[editingComponentArrayIndex]
[editingComponentArrayPropName] = instanceProps;
}
});
} }
</script> </script>
@ -151,11 +166,11 @@ const componentInstancePropsChanged = (instanceProps) => {
{#if editingComponentInstance} {#if editingComponentInstance}
<ComponentInstanceEditor onGoBack={componentInstanceCancelEdit} <ComponentInstanceEditor onGoBack={componentInstanceCancelEdit}
propertyName={editingComponentInstancePropName} title={editingComponentInstanceTitle}
instanceProps={editingComponentInstance} instanceProps={editingComponentInstance}
onPropsChanged={componentInstancePropsChanged}/> onPropsChanged={componentInstancePropsChanged}/>
{:else} {:else}
<div> <div class="component-props-container">
<div class="section-header padding" on:click={() => componentDetailsExpanded = !componentDetailsExpanded}> <div class="section-header padding" on:click={() => componentDetailsExpanded = !componentDetailsExpanded}>
<span style="margin-right: 7px">Component Details</span> <span style="margin-right: 7px">Component Details</span>
@ -232,6 +247,8 @@ const componentInstancePropsChanged = (instanceProps) => {
border-style: solid; border-style: solid;
border-color: var(--lightslate); border-color: var(--lightslate);
border-width: 0px 0px 0px 1px; border-width: 0px 0px 0px 1px;
display:flex;
flex-direction: column;
} }
.padding { .padding {
@ -267,4 +284,8 @@ const componentInstancePropsChanged = (instanceProps) => {
margin-top: 20px; margin-top: 20px;
} }
.component-props-container {
flex: 1 1 auto;
overflow-y: auto;
}
</style> </style>

View File

@ -0,0 +1,97 @@
<script>
import IconButton from "../common/IconButton.svelte";
import EventSelector from "./EventSelector.svelte";
import {
filter
} from "lodash/fp";
import {EVENT_TYPE_MEMBER_NAME} from "../common/eventHandlers";
export let parentProps;
export let propDef;
export let onValueChanged;
export let onValidate = () => {};
let events = [];
let elementErrors = {};
$: {
events = parentProps[propDef.____name];
}
const addHandler = () => {
const newHandler = {parameters:{}};
newHandler[EVENT_TYPE_MEMBER_NAME] = "";
events = [...events, newHandler];
onValueChanged(events);
}
const onEventHandlerChanged = (oldEvent) => (newEvent) => {
const indexOfOldEvent = events.indexOf(oldEvent);
const newEvents = [...events];
newEvents.splice(
events.indexOf(oldEvent),
1,
newEvent);
events = newEvents;
onValueChanged(events);
}
const removeHandler = (index) => () => {
events = filter(e => e !== events[index])(events);
onValueChanged(events);
}
</script>
<div class="root">
<div class="control-container">
{#each events as ev, index}
<div class="handler-container">
<EventSelector onChanged={onEventHandlerChanged(ev)}
onRemoved={removeHandler(index)}
event={ev} />
</div>
<div class="separator"></div>
{/each}
<div class="addelement-container"
on:click={addHandler}>
<IconButton icon="plus"
size="12"/>
</div>
</div>
</div>
<style>
.addelement-container {
cursor: pointer;
padding: 3px 0px;
text-align: center;
}
.addelement-container:hover {
background-color: var(--primary25);
margin-top: 5px;
}
.control-container {
padding-left: 3px;
background: var(--secondary10);
}
.separator {
width: 60%;
margin: 10px auto;
border-style:solid;
border-width: 1px 0 0 0;
border-color: var(--primary25);
}
</style>

View File

@ -0,0 +1,104 @@
<script>
import IconButton from "../common/IconButton.svelte";
import StateBindingControl from "./StateBindingControl.svelte";
import {
find, map, keys, reduce
} from "lodash/fp";
import { pipe } from "../common/core";
import { EVENT_TYPE_MEMBER_NAME } from "../common/eventHandlers";
export let event;
export let onChanged;
export let onRemoved;
const events = [
{name:"", parameters: []},
{name:"Set State", parameters: ["path", "value"]},
{name:"Load Record", parameters: ["recordKey", "statePath"]},
];
let eventType;
let parameters = [];
$: {
if(event) {
eventType = event[EVENT_TYPE_MEMBER_NAME];
parameters = pipe(event.parameters, [
keys,
map(k => ({name:k, value:event.parameters[k]}))
]);
} else {
eventType = "";
parameters = [];
}
}
const eventChanged = (type, parameters) => {
const paramsAsObject = reduce(
(obj, p) => {
obj[p.name] = p.value;
return obj;
}
, {}
)(parameters)
const ev = {};
ev[EVENT_TYPE_MEMBER_NAME]=type;
ev.parameters = paramsAsObject;
onChanged(ev);
}
const eventTypeChanged = (ev) => {
const eType = find(e => e.name === ev.target.value)(events);
const emptyParameters = map(p => ({name:p, value:""}))(eType.parameters);
eventChanged(eType.name, emptyParameters);
}
const onParameterChanged = index => val => {
const newparameters = [...parameters];
newparameters[index].value = val;
eventChanged(eventType, newparameters);
}
</script>
<div class="type-selector-container">
<select class="type-selector uk-select uk-form-small " value={eventType} on:change={eventTypeChanged}>
{#each events as ev}
<option value={ev.name}>{ev.name}</option>
{/each}
</select>
<IconButton icon="trash"
size="12"
on:click={onRemoved}/>
</div>
{#if parameters}
{#each parameters as p, index}
<div>
{p.name}
</div>
<StateBindingControl onChanged={onParameterChanged(index)}
value={p.value} />
{/each}
{/if}
<style>
.type-selector-container {
display: flex;
}
.type-selector {
border-color: var(--primary50);
border-radius: 2px;
width: 50px;
flex: 1 0 auto;
}
</style>

View File

@ -18,7 +18,6 @@ export let onEditComponentProp = () => {};
let value = []; let value = [];
let elementDefinitionArray; let elementDefinitionArray;
let label = "";
let elementErrors = {}; let elementErrors = {};
$: { $: {
@ -31,7 +30,6 @@ $: {
}); });
} }
elementDefinitionArray = elArray; elementDefinitionArray = elArray;
label = propDef.____name;
value = parentProps[propDef.____name]; value = parentProps[propDef.____name];
} }
@ -73,9 +71,7 @@ const onEditComponent = (index, propName) => () => {
</script> </script>
<div class="root"> <div class="root">
<div>
{label}
</div>
<div class="item-container"> <div class="item-container">
{#each value as item, index} {#each value as item, index}

View File

@ -5,6 +5,8 @@ import Textbox from "../common/Textbox.svelte";
import Dropdown from "../common/Dropdown.svelte"; import Dropdown from "../common/Dropdown.svelte";
import ComponentPropSelector from "./ComponentPropSelector.svelte"; import ComponentPropSelector from "./ComponentPropSelector.svelte";
import PropArraySelector from "./PropArraySelector.svelte"; import PropArraySelector from "./PropArraySelector.svelte";
import EventListSelector from "./EventListSelector.svelte";
import StateBindingControl from "./StateBindingControl.svelte";
export let errors = []; export let errors = [];
export let setProp = () => {}; export let setProp = () => {};
@ -26,37 +28,39 @@ const setComponentProp = (props) => {
<div class="root" > <div class="root" >
{#if propDef.type === "component"}
{#if propDef.type === "bool"} <div class="prop-label">{propDef.____name}</div>
<Checkbox label={propDef.____name}
checked={props[propDef.____name]}
on:change={ev => setProp(propDef.____name, ev.target.checked)}
hasError={fieldHasError(propDef.____name)} />
{:else if propDef.type === "options"}
<Dropdown label={propDef.____name}
selected={props[propDef.____name]}
options={propDef.options}
on:change={ev => setProp(propDef.____name, ev.target.value)}
hasError={fieldHasError(propDef.____name)}/>
{:else if propDef.type === "component"}
<ComponentPropSelector label={propDef.____name} <ComponentPropSelector label={propDef.____name}
props={props[propDef.____name]} props={props[propDef.____name]}
{disabled} {disabled}
onEdit={onEditComponent} onEdit={onEditComponent}
onComponentChosen={onEditComponent} onComponentChosen={onEditComponent}
onValueChanged={setComponentProp}/> onValueChanged={setComponentProp}/>
{:else if propDef.type === "array"} {:else if propDef.type === "array"}
<div class="prop-label">{propDef.____name}</div>
<PropArraySelector parentProps={props} <PropArraySelector parentProps={props}
{propDef} {propDef}
onValueChanged={setComponentProp} onValueChanged={setComponentProp}
onEditComponentProp={onEditComponent}/> onEditComponentProp={onEditComponent} />
{:else if propDef.type === "event"}
<div class="prop-label">{propDef.____name}</div>
<EventListSelector parentProps={props}
{propDef}
onValueChanged={setComponentProp} />
{:else} {:else}
<Textbox label={propDef.____name}
text={props[propDef.____name]} <div class="prop-label">{propDef.____name}</div>
on:change={ev => setProp(propDef.____name, ev.target.value)} <StateBindingControl value={props[propDef.____name]}
margin={false} type={propDef.type}
hasError={fieldHasError(propDef.____name)} options={propDef.options}
{disabled}/> onChanged={v => setProp(propDef.____name, v)}/>
{/if} {/if}
</div> </div>
@ -70,4 +74,10 @@ const setComponentProp = (props) => {
border-color: var(--primary25); border-color: var(--primary25);
} }
.prop-label {
font-size: 0.9em;
color: var(--primary100);
font-weight: bold;
}
</style> </style>

View File

@ -0,0 +1,155 @@
<script>
import {
isString
} from "lodash/fp";
import IconButton from "../common/IconButton.svelte";
import {
isBinding, getBinding, setBinding
} from "../common/binding";
export let value="";
export let onChanged= () => {};
export let type="";
export let options=[];
let isBound=false;
let bindingPath="";
let bindingFallbackValue="";
let isExpanded = false;
let forceIsBound = false;
$: {
isBound= forceIsBound || isBinding(value);
if(isBound) {
const binding = getBinding(value);
bindingPath= binding.path;
bindingFallbackValue= binding.fallback;
} else {
bindingPath="";
bindingFallbackValue="";
}
}
const clearBinding = () => {
forceIsBound = false;
onChanged("");
}
const bind = (path, fallback) => {
if(!path) {
clearBinding("");
return;
}
const binding = setBinding({path, fallback});
onChanged(binding);
}
const setBindingPath = ev => {
forceIsBound = false;
bind(ev.target.value, bindingFallbackValue)
}
const setBindingFallback = ev => {
bind(bindingPath, ev.target.value);
}
const makeBinding = () => {
forceIsBound=true;
isExpanded=true;
}
</script>
{#if isBound}
<div>
<div class="bound-header">
<div>{isExpanded ? "" : bindingPath}</div>
<IconButton icon={isExpanded ? "chevron-up" : "chevron-down"}
size="12"
on:click={() => isExpanded=!isExpanded}/>
<IconButton icon="trash"
size="12"
on:click={clearBinding}/>
</div>
{#if isExpanded}
<div>
<div class="binding-prop-label">Binding Path</div>
<input class="uk-input uk-form-small"
value={bindingPath}
on:change={setBindingPath} >
<div class="binding-prop-label">Fallback Value</div>
<input class="uk-input uk-form-small"
value={bindingFallbackValue}
on:change={setBindingFallback} >
</div>
{/if}
</div>
{:else}
<div class="unbound-container">
{#if type === "bool"}
<div>
<IconButton icon={value == true ? "check-square" : "square"}
size="19"
on:click={() => value = !value}/>
</div>
{:else if type === "options"}
<select class="uk-select uk-form-small"
value={value}
on:change={ev => onChanged(ev.target.checked)}>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</select>
{:else}
<input class="uk-input uk-form-small"
on:change={ev => onChanged(ev.target.value)}
bind:value={value}
style="flex: 1 0 auto;" >
{/if}
<IconButton icon="link"
size="12"
on:click={makeBinding} />
</div>
{/if}
<style>
.unbound-container {
display:flex;
}
.unbound-container > *:nth-child(1) {
width:auto;
flex: 1 0 auto;
}
.bound-header {
display: flex;
}
.bound-header > div:nth-child(1) {
flex: 1 0 auto;
width: 30px;
color: var(--darkslate);
padding-left: 5px;
font-style: italic;
}
.binding-prop-label {
color: var(--darkslate);
}
</style>

View File

@ -88,6 +88,7 @@ const settings = () => {
grid-template-columns: [uiNav] 250px [preview] auto [properties] 300px; grid-template-columns: [uiNav] 250px [preview] auto [properties] 300px;
height: 100%; height: 100%;
width: 100%; width: 100%;
overflow-y: auto;
} }
.ui-nav { .ui-nav {
@ -100,6 +101,7 @@ const settings = () => {
grid-column-start: properties; grid-column-start: properties;
background-color: var(--primary10); background-color: var(--primary10);
height: 100%; height: 100%;
overflow-y: hidden;
} }
.pages-list-container { .pages-list-container {

View File

@ -1,7 +1,6 @@
import { import {
getComponentInfo, createProps, getInstanceProps getComponentInfo, createProps, getInstanceProps
} from "./createProps"; } from "./createProps";
import { cloneDeep } from "lodash/fp";
export const buildPropsHierarchy = (allComponents, baseComponent) => { export const buildPropsHierarchy = (allComponents, baseComponent) => {
@ -39,7 +38,7 @@ export const buildPropsHierarchy = (allComponents, baseComponent) => {
for(let element of propsArray) { for(let element of propsArray) {
newPropsArray.push( newPropsArray.push(
buildProps( buildProps(
`${propName}[${index}]`, `${propName}#array_element#`,
propDef.elementDefinition, propDef.elementDefinition,
element)); element));
index++; index++;

View File

@ -150,7 +150,7 @@ export const createProps = (componentName, propsDefinition, derivedFromProps) =>
export const createArrayElementProps = (arrayPropName, elementDefinition) => export const createArrayElementProps = (arrayPropName, elementDefinition) =>
createProps( createProps(
`#element#${arrayPropName}`, `#${arrayPropName}#array_element`,
elementDefinition); elementDefinition);
const parsePropDef = propDef => { const parsePropDef = propDef => {

View File

@ -3,9 +3,15 @@ import {
isBoolean, isBoolean,
isNumber, isNumber,
isArray, isArray,
isObjectLike isObjectLike,
isPlainObject,
every
} from "lodash/fp"; } from "lodash/fp";
import {
EVENT_TYPE_MEMBER_NAME
} from "../../common/eventHandlers";
const defaultDef = typeName => () => ({ const defaultDef = typeName => () => ({
type: typeName, type: typeName,
required:false, required:false,
@ -47,6 +53,14 @@ export const expandPropsDefinition = propsDefinition => {
} }
const isComponent = isObjectLike; const isComponent = isObjectLike;
const isEvent = e =>
isPlainObject(e)
&& isString(e[EVENT_TYPE_MEMBER_NAME])
&& isPlainObject(e.parameters);
const isEventList = e =>
isArray(e) && every(isEvent)(e);
export const types = { export const types = {
string: propType(() => "", isString, defaultDef("string")), string: propType(() => "", isString, defaultDef("string")),
@ -56,4 +70,5 @@ export const types = {
options: propType(() => "", isString, defaultDef("options")), options: propType(() => "", isString, defaultDef("options")),
component: propType(() => ({_component:""}), isComponent, defaultDef("component")), component: propType(() => ({_component:""}), isComponent, defaultDef("component")),
asset: propType(() => "", isString, defaultDef("asset")), asset: propType(() => "", isString, defaultDef("asset")),
event: propType(() => [], isEventList, defaultDef("event"))
}; };

View File

@ -1,21 +1,17 @@
import { types } from "./types"; import { types } from "./types";
import { import {
createProps, createProps, arrayElementComponentName
arrayElementComponentName
} from "./createProps"; } from "./createProps";
import { isString } from "util"; import { isString } from "util";
import { import {
includes, includes, filter, map, keys,
filter, flatten, flattenDeep, each,
map, indexOf, isUndefined
keys,
flatten,
flattenDeep,
each,
indexOf,
isUndefined
} from "lodash/fp"; } from "lodash/fp";
import { common } from "../../../../core/src"; import { common } from "../../../../core/src";
import {
isBinding
} from "../../common/binding";
const pipe = common.$; const pipe = common.$;
@ -133,7 +129,15 @@ export const validateProps = (propsDefinition, props, stack=[], isFinal=true) =>
continue; continue;
} }
if(!type.isOfType(propValue)) { if(isBinding(propValue)) {
if(propDef.type === "array"
|| propDef.type === "component"
|| propDef.type === "event") {
error(`Cannot apply binding to type ${propDef.type}`);
continue;
}
}
else if(!type.isOfType(propValue)) {
error(`Property ${propDefName} is not of type ${propDef.type}. Actual value ${propValue}`) error(`Property ${propDefName} is not of type ${propDef.type}. Actual value ${propValue}`)
continue; continue;
} }
@ -141,7 +145,6 @@ export const validateProps = (propsDefinition, props, stack=[], isFinal=true) =>
if(propDef.type === "array") { if(propDef.type === "array") {
let index = 0; let index = 0;
for(let arrayItem of propValue) { for(let arrayItem of propValue) {
arrayItem._component = arrayElementComponentName(props._component, propDefName);
const arrayErrs = validateProps( const arrayErrs = validateProps(
propDef.elementDefinition, propDef.elementDefinition,
arrayItem, arrayItem,
@ -157,6 +160,7 @@ export const validateProps = (propsDefinition, props, stack=[], isFinal=true) =>
if(propDef.type === "options" if(propDef.type === "options"
&& propValue && propValue
&& !isBinding(propValue)
&& !includes(propValue)(propDef.options)) { && !includes(propValue)(propDef.options)) {
error(`Property ${propDefName} is not one of allowed options. Acutal value is ${propValue}`); error(`Property ${propDefName} is not one of allowed options. Acutal value is ${propValue}`);
} }

View File

@ -103,6 +103,18 @@ describe("createDefaultProps", () => {
expect(props.columns).toEqual([]); expect(props.columns).toEqual([]);
}); });
it("should create a object with single empty array, when prop definition is 'event' ", () => {
const propDef = {
onClick: "event"
};
const { props, errors } = createProps("some_component",propDef);
expect(errors).toEqual([]);
expect(props.onClick).toBeDefined();
expect(props.onClick).toEqual([]);
});
it("should create a object with single empty component props, when prop definition is 'component' ", () => { it("should create a object with single empty component props, when prop definition is 'component' ", () => {
const propDef = { const propDef = {
content: "component" content: "component"

View File

@ -4,6 +4,9 @@ import {
recursivelyValidate recursivelyValidate
} from "../src/userInterface/pagesParsing/validateProps"; } from "../src/userInterface/pagesParsing/validateProps";
import { createProps } from "../src/userInterface/pagesParsing/createProps"; import { createProps } from "../src/userInterface/pagesParsing/createProps";
import {
setBinding
} from "../src/common/binding";
// not that allot of this functionality is covered // not that allot of this functionality is covered
// in createDefaultProps - as validate props uses that. // in createDefaultProps - as validate props uses that.
@ -151,6 +154,14 @@ describe("validateProps", () => {
expect(errors[0].propName).toBe("width"); expect(errors[0].propName).toBe("width");
}); });
it("should not return error when has binding", () => {
const props = validProps();
props.columns[0].width = setBinding({path:"some_path"});
props.size = setBinding({path:"other path", fallback:"small"});
const errors = validateProps(validPropDef, props, [], true);
expect(errors.length).toEqual(0);
});
}); });
describe("recursivelyValidateProps", () => { describe("recursivelyValidateProps", () => {

View File

@ -3,7 +3,7 @@
"version": "0.0.3", "version": "0.0.3",
"license": "MPL-2.0", "license": "MPL-2.0",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.esm.mjs",
"scripts": { "scripts": {
"build": "rollup -c", "build": "rollup -c",
"test": "jest", "test": "jest",

View File

@ -6,8 +6,8 @@ import { terser } from 'rollup-plugin-terser';
const lodash_fp_exports = [ const lodash_fp_exports = [
"find", "isUndefined", "split", "find", "isUndefined", "split",
"last", "union", "reduce", "last", "union", "reduce", "isObject",
"cloneDeep", "some"]; "cloneDeep", "some", "isArray"];
const lodash_exports = [ const lodash_exports = [
"flow","reduce", "constant", "head", "isEmpty", "flow","reduce", "constant", "head", "isEmpty",
@ -26,12 +26,19 @@ const coreExternal = [
export default { export default {
input: 'src/index.js', input: 'src/index.js',
output: { output: [
sourcemap: true, {
format: 'iife', sourcemap: true,
name: 'app', format: 'iife',
file: `./dist/budibase-client.js` name: 'app',
}, file: `./dist/budibase-client.js`
},
{
file: 'dist/budibase-client.esm.mjs',
format: 'esm',
sourcemap: 'inline'
}
],
plugins: [ plugins: [
resolve({ resolve({

View File

@ -0,0 +1,50 @@
import {
split,
last
} from "lodash/fp";
import {writable} from "svelte/store";
import { $ } from "./core/common";
import { setupBinding } from "./state/stateBinding";
export const createApp = componentLibraries => {
const initialiseComponent = (props, htmlElement) => {
const {componentName, libName} = splitName(props._component);
if(!componentName || !libName) return;
const {initialProps, bind} = setupBinding(store, props);
const component = new (componentLibraries[libName][componentName])({
target: htmlElement,
props: {...initialProps, _app}
});
bind(component);
}
const store = writable({});
const _app = {
initialiseComponent,
store
};
return _app;
}
const splitName = fullname => {
const componentName = $(fullname, [
split("/"),
last
]);
const libName =fullname.substring(
0, fullname.length - componentName.length - 1);
return {libName, componentName};
}

View File

@ -1,5 +1,32 @@
import { initialise } from "./initialise"; import { createApp } from "./createApp";
const appDefinition = window["##BUDIBASE_APPDEFINITION##"];
initialise(window.document, appDefinition); export const loadBudibase = async (componentLibraries, props) => {
const appDefinition = window["##BUDIBASE_APPDEFINITION##"];
if(!componentLibraries) {
const componentLibraryUrl = (lib) => "./" + trimSlash(lib)
const trimSlash = (str) => str.replace(/^\/+|\/+$/g, '');
componentLibraries = {};
for(let lib of appDefinition.componentLibraries) {
componentLibraries[lib.libName] = await import(
componentLibraryUrl(lib.importPath));
}
}
if(!props) {
props = appDefinition.props;
}
const _app = createApp(componentLibraries);
_app.initialiseComponent(
props,
document.body);
};
window.loadBudibase = loadBudibase;

View File

@ -1,22 +0,0 @@
import { writable } from "svelte/store";
import { initialiseComponent } from "./initialiseComponent";
export const initialise = async (document, appDefinition) => {
const componentLibraries = {};
for(let lib of appDefinition.componentLibraries) {
componentLibraries[lib.libName] = await import(
componentLibraryUrl(lib.importPath));
}
const store = writable({});
initialiseComponent(componentLibraries, store)(
appDefinition.props,
document.body);
}
const componentLibraryUrl = (lib) => "./" + trimSlash(lib)
const trimSlash = (str) => str.replace(/^\/+|\/+$/g, '');

View File

@ -1,36 +0,0 @@
import {
find,
isUndefined,
split,
last
} from "lodash/fp";
import { $ } from "./core/common";
export const initialiseComponent = (componentLibraries, store) => (props, htmlElement) => {
const _app = {
initialiseComponent: initialiseComponent(componentLibraries, store),
store
};
const {componentName, libName} = splitName(props._component);
new (componentLibraries[libName][componentName])({
target: htmlElement,
props: {...props, _app}
});
}
const splitName = fullname => {
const componentName = $(fullname, [
split("/"),
last
]);
const libName =fullname.substring(
0, fullname.length - componentName.length - 1);
return {libName, componentName};
}

View File

@ -0,0 +1,26 @@
import {
setState
} from "./setState";
import {
isArray, isUndefined
} from "lodash/fp";
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType";
export const eventHandlers = store => {
const handler = (parameters, execute) => ({
execute, parameters
});
const setStateHandler = ({path, value}) => setState(store, path, value);
return {
"Set State": handler(["path", "value"], setStateHandler)
};
};
export const isEventType = prop =>
isArray(prop)
&& prop.length > 0
&& !isUndefined(prop[0][EVENT_TYPE_MEMBER_NAME]);

View File

@ -0,0 +1,34 @@
import {
isUndefined,
isObject
} from "lodash/fp";
export const getState = (s, path, fallback) => {
const pathParts = path.split(".");
const safeGetPath = (obj, currentPartIndex=0) => {
const currentKey = pathParts[currentPartIndex];
if(pathParts.length - 1 == currentPartIndex) {
const value = obj[currentKey];
if(isUndefined(value))
return fallback;
else
return value;
}
if(obj[currentKey] === null
|| obj[currentKey] === undefined
|| !isObject(obj[currentKey])) {
return fallback;
}
return safeGetPath(obj[currentKey], currentPartIndex + 1);
}
return safeGetPath(s);
}

View File

@ -0,0 +1,33 @@
import {
isObject
} from "lodash/fp";
export const setState = (store, path, value) => {
const pathParts = path.split(".");
const safeSetPath = (obj, currentPartIndex=0) => {
const currentKey = pathParts[currentPartIndex];
if(pathParts.length - 1 == currentPartIndex) {
obj[currentKey] = value;
return;
}
if(obj[currentKey] === null
|| obj[currentKey] === undefined
|| !isObject(obj.currentKey)) {
obj[currentKey] = {};
}
safeSetPath(obj[currentKey], currentPartIndex + 1);
}
store.update(s => {
safeSetPath(s);
return s;
});
}

View File

@ -0,0 +1,118 @@
import {
isEventType, eventHandlers, EVENT_TYPE_MEMBER_NAME
} from "./eventHandlers";
import {
getState
} from "./getState";
export const BB_STATE_BINDINGPATH = "##bbstate";
export const BB_STATE_FALLBACK = "##bbstatefallback";
const doNothing = () => {};
export const setupBinding = (store, props) => {
const initialProps = {...props};
const boundProps = [];
const componentEventHandlers = [];
for(let propName in props) {
const val = initialProps[propName];
if(isState(val)) {
const binding = stateBinding(val);
const fallback = stateFallback(val);
boundProps.push({
stateBinding:binding,
fallback, propName
});
initialProps[propName] = fallback;
} else if(isEventType(val)) {
const handlers = { propName, handlers:[] };
componentEventHandlers.push(handlers);
for(let e of val) {
handlers.handlers.push({
handlerType: e[EVENT_TYPE_MEMBER_NAME],
parameters: e.parameters
})
}
initialProps[propName] = doNothing;
}
}
const bind = (component) => {
if(boundProps.length === 0 && componentEventHandlers.length === 0) return;
const handlerTypes = eventHandlers(store);
const unsubscribe = store.subscribe(s => {
const newProps = {};
for(let boundProp of boundProps) {
const val = getState(
s,
boundProp.stateBinding,
boundProp.fallback);
if(val === undefined && newProps[boundProp.propName] !== undefined) {
delete newProps[boundProp.propName];
}
if(val !== undefined) {
newProps[boundProp.propName] = val;
}
}
for(let boundHandler of componentEventHandlers) {
const closuredHandlers = [];
for(let h of boundHandler.handlers) {
const parameters = {};
for(let pname in h.parameters) {
const p = h.parameters[pname];
parameters[pname] = isState(p)
? getState(
s, p[BB_STATE_BINDINGPATH], p[BB_STATE_FALLBACK])
: p;
}
const handlerType = handlerTypes[h.handlerType];
closuredHandlers.push(() => handlerType.execute(parameters));
}
newProps[boundHandler.propName] = () => {
for(let runHandler of closuredHandlers) {
runHandler();
}
}
}
component.$set(newProps);
});
return unsubscribe;
}
return {
initialProps, bind
};
}
const isState = (prop) => prop[BB_STATE_BINDINGPATH] !== undefined;
const stateBinding = (prop) => prop[BB_STATE_BINDINGPATH];
const stateFallback = (prop) => prop[BB_STATE_FALLBACK];

View File

@ -0,0 +1,146 @@
import {
setupBinding,
BB_STATE_BINDINGPATH,
BB_STATE_FALLBACK
} from "../src/state/stateBinding";
import { EVENT_TYPE_MEMBER_NAME } from "../src/state/eventHandlers";
import {writable} from "svelte/store";
import { isFunction } from "lodash/fp";
describe("setupBinding", () => {
it("should correctly create initials props, including fallback values", () => {
const {store, props, component} = testSetup();
const {initialProps} = testSetupBinding(store, props, component);
expect(initialProps.boundWithFallback).toBe("Bob");
expect(initialProps.boundNoFallback).toBeUndefined();
expect(initialProps.unbound).toBe("hello");
expect(isFunction(initialProps.eventBound)).toBeTruthy();
initialProps.eventBound();
});
it("should update component bound props when store is updated", () => {
const {component, store, props} = testSetup();
const {bind} = testSetupBinding(store, props, component);
bind(component);
store.update(s => {
s.FirstName = "Bobby";
s.LastName = "Thedog";
s.Customer = {
Name: "ACME inc",
Address: ""
};
s.addressToSet = "123 Main Street"
return s;
});
expect(component.props.boundWithFallback).toBe("Bobby");
expect(component.props.boundNoFallback).toBe("Thedog");
expect(component.props.multiPartBound).toBe("ACME inc");
});
it("should not update unbound props when store is updated", () => {
const {component, store, props} = testSetup();
const {bind} = testSetupBinding(store, props, component);
bind(component);
store.update(s => {
s.FirstName = "Bobby";
s.LastName = "Thedog";
s.Customer = {
Name: "ACME inc",
Address: ""
};
s.addressToSet = "123 Main Street"
return s;
});
expect(component.props.unbound).toBe("hello");
});
it("should update event handlers on state change", () => {
const {component, store, props} = testSetup();
const {bind} = testSetupBinding(store, props, component);
bind(component);
expect(component.props.boundToEventOutput).toBe("initial address");
component.props.eventBound();
expect(component.props.boundToEventOutput).toBe("event fallback address");
store.update(s => {
s.addressToSet = "123 Main Street"
return s;
});
component.props.eventBound();
expect(component.props.boundToEventOutput).toBe("123 Main Street");
});
});
const testSetupBinding = (store, props, component) => {
const setup = setupBinding(store, props);
component.props = setup.initialProps; // svelte does this for us in real life
return setup;
}
const testSetup = () => {
const c = {};
c.props = {};
c.$set = propsToSet => {
for(let pname in propsToSet)
c.props[pname] = propsToSet[pname];
}
const binding = (path, fallback) => {
const b = {};
b[BB_STATE_BINDINGPATH] = path;
b[BB_STATE_FALLBACK] = fallback;
return b;
};
const event = (handlerType, parameters) => {
const e = {};
e[EVENT_TYPE_MEMBER_NAME] = handlerType;
e.parameters = parameters;
return e;
}
const props = {
boundWithFallback : binding("FirstName", "Bob"),
boundNoFallback : binding("LastName"),
unbound: "hello",
multiPartBound: binding("Customer.Name", "ACME"),
boundToEventOutput: binding("Customer.Address", "initial address"),
eventBound: [
event("Set State", {
path: "Customer.Address",
value: binding("addressToSet", "event fallback address")
})
]
}
return {
component:c,
store:writable({}),
props
};
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
{
"name": "Button",
"description": "PrimaryButton",
"inherits": "@budibase/standard-components/button",
"props": {
"contentText": "Save"
},
"tags": [
"button"
]
}

View File

@ -0,0 +1,15 @@
{
"name": "LoginForm",
"description": "",
"inherits": "@budibase/standard-components/formControl",
"props": {
"label": "Joe",
"labelContainerClass": "Joe",
"controlContainerClass": "Joe",
"containerClass": "Joe",
"fullWidth": true
},
"tags": [
"login"
]
}

View File

@ -0,0 +1,9 @@
{
"name": "joeTextBox",
"description": "",
"inherits": "subfolder/otherTextBox",
"props": {},
"tags": [
""
]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -156,14 +156,75 @@
] ]
}, },
"props": { "props": {
"main": {}, "main": {
"_component": "@budibase/standard-components/grid",
"gridTemplateRows": "",
"gridTemplateColumns": "[left] 1fr [right] 1fr",
"children": [
{
"_component": "children#array_element#",
"control": {
"_component": "@budibase/standard-components/button",
"contentText": "Click Me",
"contentComponent": {
"_component": ""
},
"className": "default",
"disabled": false,
"onClick": [
{
"##eventHandlerType": "Set State",
"parameters": {
"path": "SomeText",
"value": "hello !"
}
}
]
},
"gridColumn": "left",
"gridRow": "",
"gridColumnStart": "",
"gridColumnEnd": "",
"gridRowStart": "",
"gridRowEnd": ""
},
{
"_component": "children#array_element#",
"control": {
"_component": "@budibase/standard-components/button",
"contentText": {
"##bbstate": "SomeText",
"##bbstatefallback": "(none)"
},
"contentComponent": {
"_component": ""
},
"className": "default",
"disabled": false,
"onClick": []
},
"gridColumn": "",
"gridRow": "",
"gridColumnStart": "",
"gridColumnEnd": "",
"gridRowStart": "",
"gridRowEnd": ""
}
],
"width": "auto",
"height": "auto",
"containerClass": "",
"itemContainerClass": ""
},
"unauthenticated": { "unauthenticated": {
"_component": "budibase-standard-components/login", "_component": "@budibase/standard-components/grid",
"logo": "/_shared/budibase-logo.png", "gridTemplateRows": "",
"loginRedirect": "", "gridTemplateColumns": "[left] 1fr [right] 1fr",
"usernameLabel": "Username", "children": [],
"passwordLabel": "Password", "width": "auto",
"loginButtonLabel": "Login" "height": "auto",
"containerClass": "",
"itemContainerClass": ""
} }
} }
} }

View File

@ -0,0 +1,29 @@
{
"name": "Primary Button",
"description": "",
"inherits": "@budibase/standard-components/button",
"props": {
"onClick": [
{
"##eventHandlerType": "Load Record",
"parameters": {
"recordKey": "waa",
"statePath": "yea"
}
},
{
"##eventHandlerType": "Load Record",
"parameters": {
"recordKey": "ttt",
"statePath": "eee"
}
}
],
"contentText": {
"##bbstate": "yea"
}
},
"tags": [
"button"
]
}

View File

@ -0,0 +1,15 @@
{
"name": "/containers/two_columns",
"description": "",
"inherits": "@budibase/standard-components/grid",
"props": {
"gridTemplateColumns": "[left] 1fr [right] 1fr"
},
"tags": [
"div",
"container",
"layout",
"panel",
"grid"
]
}

View File

@ -0,0 +1,65 @@
{
"name": "mike",
"description": "",
"inherits": "containers/two_columns",
"props": {
"children": [
{
"_component": "#children#array_element",
"control": {
"_component": "@budibase/standard-components/button",
"contentText": "Click Me",
"contentComponent": {
"_component": ""
},
"className": "default",
"disabled": false,
"onClick": [
{
"##eventHandlerType": "Set State",
"parameters": {
"path": "SomeText",
"value": "hello !"
}
}
]
},
"gridColumn": "left",
"gridRow": "",
"gridColumnStart": "",
"gridColumnEnd": "",
"gridRowStart": "",
"gridRowEnd": ""
},
{
"_component": "#children#array_element",
"control": {
"_component": "@budibase/standard-components/button",
"contentText": {
"##bbstate": "SomeText",
"##bbstatefallback": "(none)"
},
"contentComponent": {
"_component": ""
},
"className": "default",
"disabled": false,
"onClick": []
},
"gridColumn": "",
"gridRow": "",
"gridColumnStart": "",
"gridColumnEnd": "",
"gridRowStart": "",
"gridRowEnd": ""
}
]
},
"tags": [
"div",
"container",
"layout",
"panel",
"grid"
]
}

View File

@ -1,13 +1,15 @@
{ {
"main": { "main": {
"index": {}, "index": {
"appBody": "" "title": "main yeo"
},
"appBody": "mike"
}, },
"unauthenticated": { "unauthenticated": {
"index": { "index": {
"title": "Test App Two" "title": "main"
}, },
"appBody": "login" "appBody": "containers/two_columns"
}, },
"componentLibraries": [ "componentLibraries": [
"@budibase/standard-components" "@budibase/standard-components"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
window['##BUDIBASE_APPDEFINITION##'] = {"hierarchy":{"name":"root","type":"root","children":[{"name":"customer","type":"record","fields":[{"name":"name","type":"string","typeOptions":{"maxLength":1000,"values":null,"allowDeclaredValuesOnly":false},"label":"name","getInitialValue":"default","getUndefinedValue":"default"}],"children":[{"name":"invoiceyooo","type":"record","fields":[{"name":"amount","type":"number","typeOptions":{"minValue":99999999999,"maxValue":99999999999,"decimalPlaces":2},"label":"amount","getInitialValue":"default","getUndefinedValue":"default"}],"children":[],"validationRules":[],"nodeId":2,"indexes":[],"allidsShardFactor":1,"collectionName":"invoices","isSingle":false}],"validationRules":[],"nodeId":1,"indexes":[{"name":"customer_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":5}],"allidsShardFactor":64,"collectionName":"customers","isSingle":false}],"pathMaps":[],"indexes":[{"name":"Yeo index","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[1],"nodeId":4},{"name":"everyones_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":6}],"nodeId":0},"componentLibraries":["budibase-standard-components"],"appRootPath":"/testApp2","props":{}} window['##BUDIBASE_APPDEFINITION##'] = {"hierarchy":{"name":"root","type":"root","children":[{"name":"customer","type":"record","fields":[{"name":"name","type":"string","typeOptions":{"maxLength":1000,"values":null,"allowDeclaredValuesOnly":false},"label":"name","getInitialValue":"default","getUndefinedValue":"default"}],"children":[{"name":"invoiceyooo","type":"record","fields":[{"name":"amount","type":"number","typeOptions":{"minValue":99999999999,"maxValue":99999999999,"decimalPlaces":2},"label":"amount","getInitialValue":"default","getUndefinedValue":"default"}],"children":[],"validationRules":[],"nodeId":2,"indexes":[],"allidsShardFactor":1,"collectionName":"invoices","isSingle":false}],"validationRules":[],"nodeId":1,"indexes":[{"name":"customer_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":5}],"allidsShardFactor":64,"collectionName":"customers","isSingle":false}],"pathMaps":[],"indexes":[{"name":"Yeo index","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[1],"nodeId":4},{"name":"everyones_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":6}],"nodeId":0},"componentLibraries":[{"importPath":"/lib/node_modules/@budibase/standard-components/dist/index.js","libName":"@budibase/standard-components"}],"appRootPath":"/testApp2","props":{"_component":"@budibase/standard-components/grid","gridTemplateRows":"","gridTemplateColumns":"[left] 1fr [right] 1fr","children":[{"_component":"children#array_element#","control":{"_component":"@budibase/standard-components/button","contentText":"Click Me","contentComponent":{"_component":""},"className":"default","disabled":false,"onClick":[{"##eventHandlerType":"Set State","parameters":{"path":"SomeText","value":"hello !"}}]},"gridColumn":"left","gridRow":"","gridColumnStart":"","gridColumnEnd":"","gridRowStart":"","gridRowEnd":""},{"_component":"children#array_element#","control":{"_component":"@budibase/standard-components/button","contentText":{"##bbstate":"SomeText","##bbstatefallback":"(none)"},"contentComponent":{"_component":""},"className":"default","disabled":false,"onClick":[]},"gridColumn":"","gridRow":"","gridColumnStart":"","gridColumnEnd":"","gridRowStart":"","gridRowEnd":""}],"width":"auto","height":"auto","containerClass":"","itemContainerClass":""}}

View File

@ -4,16 +4,26 @@
<meta charset='utf8'> <meta charset='utf8'>
<meta name='viewport' content='width=device-width'> <meta name='viewport' content='width=device-width'>
<title>Budibase App</title> <title>main yeo</title>
<link rel='icon' type='image/png' href='/testApp2/_shared/favicon.png'> <link rel='icon' type='image/png' href='/testApp2//_shared/favicon.png'>
<style>
<script src='/testApp2/budibase-client.js'></script> html, body {
height: 100%;
width: 100%;
}
</style>
<script src='/testApp2/clientAppDefinition.js'></script>
<script src='/testApp2/budibase-client.js'></script>
<script>
loadBudibase();
</script>
</head> </head>
<body id="app"> <body id="app">
<script src='/testApp2/clientAppDefinition.js'></script>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
window['##BUDIBASE_APPDEFINITION##'] = {"hierarchy":{"name":"root","type":"root","children":[{"name":"customer","type":"record","fields":[{"name":"name","type":"string","typeOptions":{"maxLength":1000,"values":null,"allowDeclaredValuesOnly":false},"label":"name","getInitialValue":"default","getUndefinedValue":"default"}],"children":[{"name":"invoiceyooo","type":"record","fields":[{"name":"amount","type":"number","typeOptions":{"minValue":99999999999,"maxValue":99999999999,"decimalPlaces":2},"label":"amount","getInitialValue":"default","getUndefinedValue":"default"}],"children":[],"validationRules":[],"nodeId":2,"indexes":[],"allidsShardFactor":1,"collectionName":"invoices","isSingle":false}],"validationRules":[],"nodeId":1,"indexes":[{"name":"customer_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":5}],"allidsShardFactor":64,"collectionName":"customers","isSingle":false}],"pathMaps":[],"indexes":[{"name":"Yeo index","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[1],"nodeId":4},{"name":"everyones_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":6}],"nodeId":0},"componentLibraries":["budibase-standard-components"],"appRootPath":"/testApp2","props":{"_component":"budibase-standard-components/login","logo":"/_shared/budibase-logo.png","loginRedirect":"","usernameLabel":"Username","passwordLabel":"Password","loginButtonLabel":"Login"}} window['##BUDIBASE_APPDEFINITION##'] = {"hierarchy":{"name":"root","type":"root","children":[{"name":"customer","type":"record","fields":[{"name":"name","type":"string","typeOptions":{"maxLength":1000,"values":null,"allowDeclaredValuesOnly":false},"label":"name","getInitialValue":"default","getUndefinedValue":"default"}],"children":[{"name":"invoiceyooo","type":"record","fields":[{"name":"amount","type":"number","typeOptions":{"minValue":99999999999,"maxValue":99999999999,"decimalPlaces":2},"label":"amount","getInitialValue":"default","getUndefinedValue":"default"}],"children":[],"validationRules":[],"nodeId":2,"indexes":[],"allidsShardFactor":1,"collectionName":"invoices","isSingle":false}],"validationRules":[],"nodeId":1,"indexes":[{"name":"customer_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":5}],"allidsShardFactor":64,"collectionName":"customers","isSingle":false}],"pathMaps":[],"indexes":[{"name":"Yeo index","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[1],"nodeId":4},{"name":"everyones_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":6}],"nodeId":0},"componentLibraries":[{"importPath":"/lib/node_modules/@budibase/standard-components/dist/index.js","libName":"@budibase/standard-components"}],"appRootPath":"/testApp2","props":{"_component":"@budibase/standard-components/grid","gridTemplateRows":"","gridTemplateColumns":"[left] 1fr [right] 1fr","children":[],"width":"auto","height":"auto","containerClass":"","itemContainerClass":""}}

View File

@ -4,16 +4,26 @@
<meta charset='utf8'> <meta charset='utf8'>
<meta name='viewport' content='width=device-width'> <meta name='viewport' content='width=device-width'>
<title>Test App Two</title> <title>main</title>
<link rel='icon' type='image/png' href='/testApp2/_shared/favicon.png'> <link rel='icon' type='image/png' href='/testApp2//_shared/favicon.png'>
<style>
<script src='/testApp2/budibase-client.js'></script> html, body {
height: 100%;
width: 100%;
}
</style>
<script src='/testApp2/clientAppDefinition.js'></script>
<script src='/testApp2/budibase-client.js'></script>
<script>
loadBudibase();
</script>
</head> </head>
<body id="app"> <body id="app">
<script src='/testApp2/clientAppDefinition.js'></script>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

View File

@ -1,43 +1 @@
main.svelte-pnvh8{height:100%;width:100%;font-family:"Lato", Helvetica, Arial, sans-serif}.settings.svelte-pnvh8{position:absolute;bottom:25px;right:25px} main.svelte-24wuof{height:100%;width:100%;font-family:"Roboto", Helvetica, Arial, sans-serif}.settings.svelte-24wuof{position:absolute;bottom:25px;right:25px}
.root.svelte-jymnqv{position:fixed;margin:0 auto;text-align:center;top:20%;width:100%}.inner.svelte-jymnqv{display:inline-block;margin:auto}.logo.svelte-jymnqv{width:300px;margin-bottom:40px}.root.svelte-jymnqv .option{width:250px}.app-link.svelte-jymnqv{margin-top:10px;display:block}
.root.svelte-h9y88z{height:100%;width:100%;display:flex;flex-direction:column}.top-nav.svelte-h9y88z{flex:0 0 auto;height:25px;background:white;border-style:solid;border-width:0px 0px 1px 0px;border-color:var(--lightslate);padding:5px;width:100%}.content.svelte-h9y88z{flex:1 1 auto;width:100%;height:100px}.content.svelte-h9y88z>div.svelte-h9y88z{height:100%;width:100%}.topnavitem.svelte-h9y88z{cursor:pointer;color:var(--slate);padding:0px 15px}.topnavitem.svelte-h9y88z:hover{color:var(--secondary75)}.active.svelte-h9y88z{color:var(--secondary100)}
.root.svelte-tavaga{height:100%;display:flex}.content.svelte-tavaga{flex:1 1 auto;height:100%;background-color:var(--white);margin:0}.nav.svelte-tavaga{flex:0 1 auto;width:300px;height:100%}
.border-normal.svelte-7rfkdx{border-radius:var(--borderradiusall)}.border-left.svelte-7rfkdx{border-radius:var(--borderradius) 0 0 var(--borderradius)}.border-right.svelte-7rfkdx{border-radius:0 var(--borderradius) var(--borderradius) 0}.border-middle.svelte-7rfkdx{border-radius:0}button.svelte-7rfkdx{border-style:solid;padding:7px 15px;cursor:pointer}.primary.svelte-7rfkdx{background-color:var(--primary100);border-color:var(--primary100);color:var(--white)}.primary.svelte-7rfkdx:hover{background-color:var(--primary75);border-color:var(--primary75)}.primary.svelte-7rfkdx:active{background-color:var(--primarydark);border-color:var(--primarydark)}.primary-outline.svelte-7rfkdx{background-color:var(--white);border-color:var(--primary100);color:var(--primary100)}.primary-outline.svelte-7rfkdx:hover{background-color:var(--primary10)}.primary-outline.svelte-7rfkdx:pressed{background-color:var(--primary25)}.secondary.svelte-7rfkdx{background-color:var(--secondary100);border-color:var(--secondary100);color:var(--white)}.secondary.svelte-7rfkdx:hover{background-color:var(--secondary75);border-color:var(--secondary75)}.secondary.svelte-7rfkdx:pressed{background-color:var(--secondarydark);border-color:var(--secondarydark)}.secondary-outline.svelte-7rfkdx{background-color:var(--white);border-color:var(--secondary100);color:var(--secondary100)}.secondary-outline.svelte-7rfkdx:hover{background-color:var(--secondary10)}.secondary-outline.svelte-7rfkdx:pressed{background-color:var(--secondary25)}.success.svelte-7rfkdx{background-color:var(--success100);border-color:var(--success100);color:var(--white)}.success.svelte-7rfkdx:hover{background-color:var(--success75);border-color:var(--success75)}.success.svelte-7rfkdx:pressed{background-color:var(--successdark);border-color:var(--successdark)}.success-outline.svelte-7rfkdx{background-color:var(--white);border-color:var(--success100);color:var(--success100)}.success-outline.svelte-7rfkdx:hover{background-color:var(--success10)}.success-outline.svelte-7rfkdx:pressed{background-color:var(--success25)}.deletion.svelte-7rfkdx{background-color:var(--deletion100);border-color:var(--deletion100);color:var(--white)}.deletion.svelte-7rfkdx:hover{background-color:var(--deletion75);border-color:var(--deletion75)}.deletion.svelte-7rfkdx:pressed{background-color:var(--deletiondark);border-color:var(--deletiondark)}.deletion-outline.svelte-7rfkdx{background-color:var(--white);border-color:var(--deletion100);color:var(--deletion100)}.deletion-outline.svelte-7rfkdx:hover{background-color:var(--deletion10)}.deletion-outline.svelte-7rfkdx:pressed{background-color:var(--deletion25)}
button.svelte-4po3k2{border-style:none;background-color:rgba(0,0,0,0);cursor:pointer;outline:none}button.svelte-4po3k2:hover{color:var(--hovercolor)}button.svelte-4po3k2:active{outline:none}
.root.svelte-1rbfs7z{display:grid;grid-template-columns:[uiNav] 250px [preview] auto [properties] 300px;height:100%;width:100%}.ui-nav.svelte-1rbfs7z{grid-column-start:uiNav;background-color:var(--primary10);height:100%}.properties-pane.svelte-1rbfs7z{grid-column-start:properties;background-color:var(--primary10);height:100%}.pages-list-container.svelte-1rbfs7z{padding-top:20px}.nav-group-header.svelte-1rbfs7z{font-size:10pt;padding-left:10px}.nav-items-container.svelte-1rbfs7z{padding-top:10px}.nav-group-header.svelte-1rbfs7z{display:grid;grid-template-columns:[icon] auto [title] 1fr [button] auto;padding:10px 2px 0px 7px}.nav-group-header.svelte-1rbfs7z>div.svelte-1rbfs7z:nth-child(1){padding:0px 7px 0px 0px;vertical-align:bottom;grid-column-start:icon;margin-right:5px}.nav-group-header.svelte-1rbfs7z>span.svelte-1rbfs7z:nth-child(2){margin-left:5px;vertical-align:bottom;grid-column-start:title;margin-top:auto}.nav-group-header.svelte-1rbfs7z>div.svelte-1rbfs7z:nth-child(3){vertical-align:bottom;grid-column-start:button;cursor:pointer;color:var(--slate)}.nav-group-header.svelte-1rbfs7z>div.svelte-1rbfs7z:nth-child(3):hover{color:var(--primary75)}
h4.svelte-o0id5a{margin-top:20px}
.root.svelte-85bkcq{height:100%;position:relative}.actions-header.svelte-85bkcq{flex:0 1 auto}.node-view.svelte-85bkcq{overflow-y:auto;flex:1 1 auto}
.root.svelte-1be865r{padding:10px}.edit-button.svelte-1be865r{cursor:pointer;color:var(--white)}tr.svelte-1be865r:hover .edit-button.svelte-1be865r{color:var(--secondary75)}
.root.svelte-1py90dy{height:100%;background-color:var(--primary10)}.items-root.svelte-1py90dy{display:flex;flex-direction:column;max-height:100%;height:10px}.hierarchy.svelte-1py90dy{flex:1 1 auto}.hierarchy-title-row.svelte-1py90dy{padding:15px 7px;font-size:12pt;display:flex;font-weight:bold}.hierarchy-title.svelte-1py90dy{flex:auto 1 1}.space-filler.svelte-1py90dy{flex:1 1 auto}
.root.svelte-1y6dy5x{padding:10px}
.root.svelte-1ba51k0{color:var(--secondary50)}.hierarchy-item.svelte-1ba51k0{cursor:pointer;padding:5px 0px}.hierarchy-item.svelte-1ba51k0:hover{color:var(--secondary75)}.component.svelte-1ba51k0{margin-left:5px}.currentfolder.svelte-1ba51k0{color:var(--secondary100)}.selected.svelte-1ba51k0{color:var(--primary100)}.title.svelte-1ba51k0{margin-left:10px}
.root.svelte-xai2hc{height:100%;border-style:solid;border-color:var(--lightslate);border-width:0px 0px 0px 1px}.padding.svelte-xai2hc{padding:0px 5px 0px 10px}.title.svelte-xai2hc{background-color:white;padding:3px;display:grid;grid-template-columns:[name] 1fr [actions] auto}.title.svelte-xai2hc>div.svelte-xai2hc:nth-child(1){grid-column-start:name;color:var(--secondary100)}.title.svelte-xai2hc>div.svelte-xai2hc:nth-child(2){grid-column-start:actions}.section-header.svelte-xai2hc{font-style:italic;color:var(--slate);border-style:solid;border-color:var(--lightslate);border-width:0px 0px 1px 0px}.section-header.svelte-xai2hc{vertical-align:middle;margin-top:20px}
.root.svelte-1iwq1b2{padding-bottom:10px;padding-left:10px;font-size:16px;color:var(--secondary50)}.hierarchy-item.svelte-1iwq1b2{cursor:pointer;padding:5px 0px}.hierarchy-item.svelte-1iwq1b2:hover{color:var(--secondary75)}.component.svelte-1iwq1b2{margin-left:5px}.selected.svelte-1iwq1b2{color:var(--primary100)}.title.svelte-1iwq1b2{margin-left:10px}
.component-preview.svelte-1rf8xuh{display:grid;grid-template-rows:[top] 1fr [middle] auto [bottom] 1fr;grid-template-columns:[left] 1fr [middle] auto [right] 1fr;grid-column-start:preview;height:100%}.component-container.svelte-1rf8xuh{grid-row-start:middle;grid-column-start:middle}
h1.svelte-2ukyrk{font-size:1.2em}
.root.svelte-is81a4{padding:15px}.help-text.svelte-is81a4{color:var(--slate);font-size:10pt}
.section-container.svelte-1t0x31f{padding:15px;border-style:dotted;border-width:1px;border-color:var(--lightslate);border-radius:2px}.section-container.svelte-1t0x31f:nth-child(1){margin-bottom:15px}.row-text.svelte-1t0x31f{margin-right:15px;color:var(--primary100)}input.svelte-1t0x31f{margin-right:15px}p.svelte-1t0x31f>span.svelte-1t0x31f{margin-left:30px}.header.svelte-1t0x31f{display:grid;grid-template-columns:[title] 1fr [icon] auto}.header.svelte-1t0x31f>div.svelte-1t0x31f:nth-child(1){grid-column-start:title}.header.svelte-1t0x31f>div.svelte-1t0x31f:nth-child(2){grid-column-start:icon}
.root.svelte-1rctf7f{display:block;font-size:13pt;width:100%;cursor:pointer}.title.svelte-1rctf7f{font:var(--bodytext);padding-top:10px;padding-right:5px;padding-bottom:10px;color:var(--secondary100)}.title.svelte-1rctf7f:hover{background-color:var(--secondary10)}
.root.svelte-gq7l8x{height:100%;padding:15px}.fields-table.svelte-gq7l8x{margin:10px;border-collapse:collapse}.add-field-button.svelte-gq7l8x{margin-left:15px;cursor:pointer}.edit-button.svelte-gq7l8x{cursor:pointer;color:var(--white)}.edit-button.svelte-gq7l8x:hover{color:var(--secondary75)}th.svelte-gq7l8x{text-align:left}td.svelte-gq7l8x{padding:5px 30px 5px 0px;margin:0}thead.svelte-gq7l8x>tr.svelte-gq7l8x{border-width:0px 0px 1px 0px;border-style:solid;border-color:var(--secondary75);margin-bottom:20px}tbody.svelte-gq7l8x>tr.svelte-gq7l8x{border-width:0px 0px 1px 0px;border-style:solid;border-color:var(--primary10)}tbody.svelte-gq7l8x>tr.svelte-gq7l8x:hover{background-color:var(--primary10)}tbody.svelte-gq7l8x>tr:hover .edit-button.svelte-gq7l8x{color:var(--secondary75)}.index-container.svelte-gq7l8x{border-style:solid;border-width:0 0 1px 0;border-color:var(--secondary25);padding:10px;margin-bottom:5px}.index-label.svelte-gq7l8x{color:var(--slate)}.index-name.svelte-gq7l8x{font-weight:bold;color:var(--primary100)}.index-container.svelte-gq7l8x code.svelte-gq7l8x{margin:0;display:inline;background-color:var(--primary10);color:var(--secondary100);padding:3px}.index-field-row.svelte-gq7l8x{margin-top:7px}
.root.svelte-1fkfoam{height:100%;padding:15px}.allowed-records.svelte-1fkfoam{margin:20px 0px}.allowed-records.svelte-1fkfoam>span.svelte-1fkfoam{margin-right:30px}
.root.svelte-160njkp{padding:5px;top:0;width:100%}
.dropdown-background.svelte-179p8ge{position:fixed;top:0;left:0;width:100vw;height:100vh}.root.svelte-179p8ge{cursor:pointer;z-index:1}.dropdown-content.svelte-179p8ge{position:absolute;background-color:var(--white);min-width:160px;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);z-index:1;font-weight:normal;border-style:solid;border-width:1px;border-color:var(--secondary10)}.dropdown-content.svelte-179p8ge:not(:focus){display:none}.action-row.svelte-179p8ge{padding:7px 10px;cursor:pointer}.action-row.svelte-179p8ge:hover{background-color:var(--primary100);color:var(--white)}
.nav-item.svelte-1e0ymco{padding:7px;font-size:12pt;font-weight:bold;cursor:pointer;flex:0 0 auto}.nav-item.svelte-1e0ymco:hover{background-color:var(--primary10)}.active.svelte-1e0ymco{background-color:var(--primary10)}
.root.svelte-d6wwkb{display:flex}.root.svelte-d6wwkb:last-child{border-radius:0 var(--borderradius) var(--borderradius) 0}.root.svelte-d6wwkb:first-child{border-radius:var(--borderradius) 0 0 var(--borderradius)}.root.svelte-d6wwkb:not(:first-child):not(:last-child){border-radius:0}
.edit-button.svelte-12jzg4k{cursor:pointer;color:var(--white)}tr.svelte-12jzg4k:hover .edit-button.svelte-12jzg4k{color:var(--secondary75)}
.edit-button.svelte-1le5bpl{cursor:pointer;color:var(--white)}tr.svelte-1le5bpl:hover .edit-button.svelte-1le5bpl{color:var(--secondary75)}
.root.svelte-10avxhq{display:block;font-size:12pt;width:100%;cursor:pointer}.title.svelte-10avxhq{padding-top:5px;padding-right:5px;padding-bottom:5px;color:var(--secondary100)}.title.svelte-10avxhq:hover{background-color:var(--secondary10)}.active.svelte-10avxhq{background-color:var(--primary10)}
.root.svelte-beukw7{font-size:10pt}.padding.svelte-beukw7{padding:0 10px}.inherited-title.svelte-beukw7{margin-top:40px;display:grid;grid-template-columns:[name] 1fr [actions] auto;border-style:solid;border-width:0px 0px 1px 0px;border-color:var(--lightslate);font-style:italic}.inherited-title.svelte-beukw7>div.svelte-beukw7:nth-child(1){grid-column-start:name;color:var(--slate)}.inherited-title.svelte-beukw7>div.svelte-beukw7:nth-child(2){grid-column-start:actions;color:var(--secondary100)}
.info-text.svelte-um9cf7{font-size:0.8em;color:var(--slate)}
.title.svelte-1pp53c5{padding:3px;background-color:white;color:var(--secondary100);border-style:solid;border-width:1px 0 0 0;border-color:var(--lightslate)}.title.svelte-1pp53c5>span.svelte-1pp53c5{margin-left:10px}
.component.svelte-13tuzj8{padding:5px;border-style:solid;border-width:0 0 1px 0;border-color:var(--lightslate);cursor:pointer}.component.svelte-13tuzj8:hover{background-color:var(--primary10)}.component.svelte-13tuzj8>.title.svelte-13tuzj8{font-size:13pt;color:var(--secondary100)}.component.svelte-13tuzj8>.description.svelte-13tuzj8{font-size:10pt;color:var(--primary75);font-style:italic}
textarea.svelte-1ooq0hh{padding:3px;background:var(--darkslate);color:var(--white);font-family:'Courier New', Courier, monospace;width:95%;height:100px}
.error-container.svelte-jwy920{padding:10px;border-style:solid;border-color:var(--deletion100);border-radius:var(--borderradiusall);background:var(--deletion75)}.error-row.svelte-jwy920{padding:5px 0px}
input.svelte-66516k{margin-right:7px}
.root.svelte-emcy8y{padding:10px}.option-container.svelte-emcy8y{border-style:dotted;border-width:1px;border-color:var(--primary75);padding:3px;margin-right:5px}
.root.svelte-woqcuf{display:grid;grid-template-columns:[name] 1fr [actions] auto}.root.svelte-woqcuf>div.svelte-woqcuf:nth-child(1){grid-column-start:name;color:var(--secondary50)}.root.svelte-woqcuf>div.svelte-woqcuf:nth-child(2){grid-column-start:actions}.selectedname.svelte-woqcuf{font-weight:bold;color:var(--secondary)}
.root.svelte-w5on8s{padding:3px 5px 7px 10px;border-style:dotted;border-width:0 0 1px 0;border-color:var(--primary25)}
textarea.svelte-1wfv4cc{width:300px;height:200px}
.addelement-container.svelte-jliz3p{cursor:pointer;padding:3px 0px;text-align:center}.addelement-container.svelte-jliz3p:hover{background-color:var(--primary25)}.item-container.svelte-jliz3p{padding-left:3px;background:var(--secondary10)}
/*# sourceMappingURL=bundle.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -1,61 +1,47 @@
/* latin-ext */ /* latin-ext */
/* roboto-regular - latin-ext_latin */
@font-face { @font-face {
font-family: 'Lato'; font-family: 'Roboto';
font-weight: 400; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin-ext.woff2) format('woff2'); font-weight: 400;
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; src: local('Roboto'), local('Roboto-Regular'),
url('/_builder/assets/roboto-v20-latin-ext_latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('/_builder/assets/roboto-v20-latin-ext_latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* latin */ /* roboto-300 - latin-ext_latin */
@font-face { @font-face {
font-family: 'Lato'; font-family: 'Roboto';
font-weight: 400; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin.woff2) format('woff2'); font-weight: 300;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; src: local('Roboto Light'), local('Roboto-Light'),
url('/_builder/assets/roboto-v20-latin-ext_latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('/_builder/assets/roboto-v20-latin-ext_latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* roboto-500 - latin-ext_latin */
/* Black latin-ext */
@font-face { @font-face {
font-family: 'Lato Black'; font-family: 'Roboto';
font-weight: 900; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin-ext.woff2) format('woff2'); font-weight: 500;
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; src: local('Roboto Medium'), local('Roboto-Medium'),
url('/_builder/assets/roboto-v20-latin-ext_latin-500.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('/_builder/assets/roboto-v20-latin-ext_latin-500.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* Black latin */ /* roboto-700 - latin-ext_latin */
@font-face { @font-face {
font-family: 'Lato Black'; font-family: 'Roboto';
font-weight: 900; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin.woff2) format('woff2'); font-weight: 700;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; src: local('Roboto Bold'), local('Roboto-Bold'),
url('/_builder/assets/roboto-v20-latin-ext_latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
url('/_builder/assets/roboto-v20-latin-ext_latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
} }
/* roboto-900 - latin-ext_latin */
/* Bold latin-ext */
@font-face { @font-face {
font-family: 'Lato Bold'; font-family: 'Roboto';
font-weight: 700; font-style: normal;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin-ext.woff2) format('woff2'); font-weight: 900;
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; src: local('Roboto Black'), local('Roboto-Black'),
} url('/_builder/assets/roboto-v20-latin-ext_latin-900.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
/* Bold latin */ url('/_builder/assets/roboto-v20-latin-ext_latin-900.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
@font-face {
font-family: 'Lato Bold';
font-weight: 700;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Semi Bold latin-ext */
@font-face {
font-family: 'Lato Semi Bold';
font-weight: 600;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin-ext.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Semi Bold latin */
@font-face {
font-family: 'Lato Semi Bold';
font-weight: 600;
src: local('Lato Regular'), local('Lato-Regular'), url(/_builder/assets/lato-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
} }

View File

@ -4,6 +4,7 @@
--primary50: #454CA080; --primary50: #454CA080;
--primary25: #454CA040; --primary25: #454CA040;
--primary10: #454CA01A; --primary10: #454CA01A;
--primary5: #454ca00c;
--primarydark: #3F448A; --primarydark: #3F448A;
--secondary100: #162B4DFF; --secondary100: #162B4DFF;
@ -11,6 +12,7 @@
--secondary50: #162B4D80; --secondary50: #162B4D80;
--secondary25: #162B4D40; --secondary25: #162B4D40;
--secondary10: #162B4D1A; --secondary10: #162B4D1A;
--secondary5: rgba(22, 43, 77, 0.068);
--secondarydark: #3F448A; --secondarydark: #3F448A;
--tertiary: #F2F5F7; --tertiary: #F2F5F7;
@ -30,17 +32,18 @@
--deletiondark: #CF4046; --deletiondark: #CF4046;
--white: #FFFFFF; --white: #FFFFFF;
--darkslate: #5C6B82; --darkslate: #1a202c;
--slate: #8B95A6; --slate: #a0aec0;
--lightslate: rgb(203, 212, 228); --lightslate: #f7fafc;
--borderradius: 2px; --borderradius: 2px;
--borderradiusall: 2px 2px 2px 2px; --borderradiusall: 2px 2px 2px 2px;
--fontblack: "Lato Black"; --fontblack: "Roboto Black";
--fontbold: "Lato Bold"; --fontbold: "Roboto Bold";
--fontsemibold: "Lato Semi Bold"; --fontsemibold: "Roboto Medium";
--fontnormal: "Lato"; --fontnormal: "Roboto";
--fontlight: "Roboto Light";
--bodytext: var(--fontnormal) "regular" var(--secondary100) 16pt; --bodytext: var(--fontnormal) "regular" var(--secondary100) 16pt;
--bigbodytext: var(--fontnormal) "regular" var(--secondary100) 20pt; --bigbodytext: var(--fontnormal) "regular" var(--secondary100) 20pt;
@ -79,7 +82,7 @@ h3 {
} }
h4 { h4 {
font-family: var(--fontsemibold); font-family: var(--fontbold);
font-size: 18pt; font-size: 18pt;
color: var(--secondary100); color: var(--secondary100);
} }

View File

@ -7,13 +7,22 @@
<title>{{ title }}</title> <title>{{ title }}</title>
<link rel='icon' type='image/png' href='{{ favicon }}'> <link rel='icon' type='image/png' href='{{ favicon }}'>
<style>
html, body {
height: 100%;
width: 100%;
}
</style>
{{ each(options.stylesheets) }} {{ each(options.stylesheets) }}
<link rel='stylesheet' href='{{ @this }}'> <link rel='stylesheet' href='{{ @this }}'>
{{ /each }} {{ /each }}
<script src='{{ appRootPath }}/clientAppDefinition.js'></script> <script src='{{ appRootPath }}/clientAppDefinition.js'></script>
<script src='{{ appRootPath }}/budibase-client.js'></script> <script src='{{ appRootPath }}/budibase-client.js'></script>
<script>
loadBudibase();
</script>
</head> </head>

Some files were not shown because too many files have changed in this diff Show More