Conor Mack 7 years ago
parent
commit
20de9f9dd0
  1. 4
      packages/bootstrap-components/src/Form.svelte
  2. 2
      packages/bootstrap-components/src/Nav.svelte
  3. 2
      packages/bootstrap-components/src/Test/TestApp.svelte
  4. 157
      packages/builder/src/builderStore/store.js
  5. 114
      packages/builder/src/userInterface/ComponentInstanceEditor.svelte
  6. 139
      packages/builder/src/userInterface/ComponentPropSelector.svelte
  7. 6
      packages/builder/src/userInterface/ComponentSearch.svelte
  8. 12
      packages/builder/src/userInterface/ComponentSelector.svelte
  9. 4
      packages/builder/src/userInterface/ComponentsHierarchy.svelte
  10. 142
      packages/builder/src/userInterface/ComponentsList.svelte
  11. 79
      packages/builder/src/userInterface/ComponentsPaneSwitcher.svelte
  12. 5
      packages/builder/src/userInterface/CurrentItemPreview.svelte
  13. 117
      packages/builder/src/userInterface/EditComponentProps.svelte
  14. 161
      packages/builder/src/userInterface/GeneratedComponents.svelte
  15. 138
      packages/builder/src/userInterface/NewComponent.svelte
  16. 2
      packages/builder/src/userInterface/PageView.svelte
  17. 120
      packages/builder/src/userInterface/PropArraySelector.svelte
  18. 25
      packages/builder/src/userInterface/PropControl.svelte
  19. 105
      packages/builder/src/userInterface/PropsView.svelte
  20. 6
      packages/builder/src/userInterface/SettingsView.svelte
  21. 31
      packages/builder/src/userInterface/UserInterfaceRoot.svelte
  22. 65
      packages/builder/src/userInterface/pagesParsing/buildPropsHierarchy.js
  23. 166
      packages/builder/src/userInterface/pagesParsing/createProps.js
  24. 17
      packages/builder/src/userInterface/pagesParsing/findDependencies.js
  25. 6
      packages/builder/src/userInterface/pagesParsing/getRootComponent.js
  26. 34
      packages/builder/src/userInterface/pagesParsing/renameScreen.js
  27. 23
      packages/builder/src/userInterface/pagesParsing/searchComponents.js
  28. 29
      packages/builder/src/userInterface/pagesParsing/types.js
  29. 20
      packages/builder/src/userInterface/pagesParsing/validatePages.js
  30. 133
      packages/builder/src/userInterface/pagesParsing/validateProps.js
  31. 32
      packages/builder/tests/buildPropsHierarchy.spec.js
  32. 32
      packages/builder/tests/componentDependencies.spec.js
  33. 165
      packages/builder/tests/createDefaultProps.spec.js
  34. 56
      packages/builder/tests/expandPropDef.spec.js
  35. 57
      packages/builder/tests/getComponentInfo.spec.js
  36. 79
      packages/builder/tests/renameComponent.spec.js
  37. 61
      packages/builder/tests/renameScreen.spec.js
  38. 51
      packages/builder/tests/searchComponentsProps.spec.js
  39. 184
      packages/builder/tests/testData.js
  40. 26
      packages/builder/tests/validatePages.spec.js
  41. 244
      packages/builder/tests/validateProps.spec.js
  42. 51
      packages/client/src/createApp.js
  43. 4
      packages/client/src/index.js
  44. 71
      packages/server/builder/budibase-client.esm.mjs
  45. 49
      packages/server/builder/bundle.css
  46. 102
      packages/server/builder/bundle.css.map
  47. 65293
      packages/server/builder/bundle.js
  48. 2
      packages/server/builder/bundle.js.map
  49. 26
      packages/server/middleware/routers.js
  50. 38
      packages/server/tests/builder.spec.js
  51. 20
      packages/server/utilities/builder/index.js
  52. 164
      packages/standard-components/components.json
  53. 1
      packages/standard-components/package.json
  54. 9
      packages/standard-components/public/bundle.css
  55. 22
      packages/standard-components/public/bundle.css.map
  56. 4500
      packages/standard-components/public/bundle.js
  57. 2
      packages/standard-components/public/bundle.js.map
  58. 96
      packages/standard-components/src/Div.svelte
  59. 59
      packages/standard-components/src/Form.svelte
  60. 62
      packages/standard-components/src/Grid.svelte
  61. 49
      packages/standard-components/src/If.svelte
  62. 22
      packages/standard-components/src/Input.svelte
  63. 2
      packages/standard-components/src/Login.svelte
  64. 19
      packages/standard-components/src/Nav.svelte
  65. 74
      packages/standard-components/src/Panel.svelte
  66. 22
      packages/standard-components/src/Select.svelte
  67. 115
      packages/standard-components/src/StackPanel.svelte
  68. 4
      packages/standard-components/src/Test/TestApp.svelte
  69. 18
      packages/standard-components/src/Test/createApp.js
  70. 102
      packages/standard-components/src/Test/props.js
  71. 1
      packages/standard-components/src/Text.svelte
  72. 48
      packages/standard-components/src/Textbox.svelte
  73. 22
      packages/standard-components/src/button.svelte
  74. 6
      packages/standard-components/src/index.js

4
packages/bootstrap-components/src/Form.svelte

@ -21,12 +21,12 @@ $ : {
for(let el in htmlElements) { for(let el in htmlElements) {
if(formControls[el].control.controlPosition === "Before Label") { if(formControls[el].control.controlPosition === "Before Label") {
_bb.insertComponent( _bb.insertChildren(
_bb.props.formControls[el].control, _bb.props.formControls[el].control,
htmlElements[el], htmlElements[el],
htmlElements[el].childNodes.find(n => n.tagName === "LABEL")); htmlElements[el].childNodes.find(n => n.tagName === "LABEL"));
} else { } else {
_bb.appendComponent( _bb.appendChildren(
_bb.props.formControls[el].control, _bb.props.formControls[el].control,
htmlElements[el]); htmlElements[el]);
} }

2
packages/bootstrap-components/src/Nav.svelte

@ -79,7 +79,7 @@ const SelectItem = (index) => {
} }
if(index >= 0) if(index >= 0)
currentComponent = _bb.hydrateComponent( currentComponent = _bb.hydrateChildren(
_bb.props.items[index].component, componentElement); _bb.props.items[index].component, componentElement);
} }

2
packages/bootstrap-components/src/Test/TestApp.svelte

@ -13,7 +13,7 @@ let currentComponent;
$: { $: {
if(_bb && currentComponent) { if(_bb && currentComponent) {
_bb.hydrateComponent(testProps, currentComponent); _bb.hydrateChildren(testProps, currentComponent);
} }
} }

157
packages/builder/src/builderStore/store.js

@ -15,9 +15,9 @@ import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesOb
import { buildPropsHierarchy } from "../userInterface/pagesParsing/buildPropsHierarchy" import { buildPropsHierarchy } from "../userInterface/pagesParsing/buildPropsHierarchy"
import api from "./api"; import api from "./api";
import { isRootComponent, getExactComponent } from "../userInterface/pagesParsing/searchComponents"; import { isRootComponent, getExactComponent } from "../userInterface/pagesParsing/searchComponents";
import { rename } from "../userInterface/pagesParsing/renameComponent"; import { rename } from "../userInterface/pagesParsing/renameScreen";
import { import {
getComponentInfo, getNewComponentInfo getNewComponentInfo, getScreenInfo
} from "../userInterface/pagesParsing/createProps"; } from "../userInterface/pagesParsing/createProps";
import { import {
loadLibs, loadLibUrls, loadGeneratorLibs loadLibs, loadLibUrls, loadGeneratorLibs
@ -36,12 +36,12 @@ export const getStore = () => {
pages:defaultPagesObject(), pages:defaultPagesObject(),
mainUi:{}, mainUi:{},
unauthenticatedUi:{}, unauthenticatedUi:{},
allComponents:[], components:[],
currentFrontEndItem:null, currentFrontEndItem:null,
currentComponentInfo:null, currentComponentInfo:null,
currentComponentIsNew:false,
currentFrontEndType:"none", currentFrontEndType:"none",
currentPageName: "", currentPageName: "",
currentComponentProps:null,
currentNodeIsNew: false, currentNodeIsNew: false,
errors: [], errors: [],
activeNav: "database", activeNav: "database",
@ -74,14 +74,14 @@ export const getStore = () => {
store.saveLevel = saveLevel(store); store.saveLevel = saveLevel(store);
store.deleteLevel = deleteLevel(store); store.deleteLevel = deleteLevel(store);
store.setActiveNav = setActiveNav(store); store.setActiveNav = setActiveNav(store);
store.saveDerivedComponent = saveDerivedComponent(store); store.saveScreen = saveScreen(store);
store.refreshComponents = refreshComponents(store); store.refreshComponents = refreshComponents(store);
store.addComponentLibrary = addComponentLibrary(store); store.addComponentLibrary = addComponentLibrary(store);
store.renameDerivedComponent = renameDerivedComponent(store); store.renameScreen = renameScreen(store);
store.deleteDerivedComponent = deleteDerivedComponent(store); store.deleteScreen = deleteScreen(store);
store.setCurrentComponent = setCurrentComponent(store); store.setCurrentScreen = setCurrentScreen(store);
store.setCurrentPage = setCurrentPage(store); store.setCurrentPage = setCurrentPage(store);
store.createDerivedComponent = createDerivedComponent(store); store.createScreen = createScreen(store);
store.removeComponentLibrary =removeComponentLibrary(store); store.removeComponentLibrary =removeComponentLibrary(store);
store.addStylesheet = addStylesheet(store); store.addStylesheet = addStylesheet(store);
store.removeStylesheet = removeStylesheet(store); store.removeStylesheet = removeStylesheet(store);
@ -120,10 +120,9 @@ const initialise = (store, initial) => async () => {
initial.hasAppPackage = true; initial.hasAppPackage = true;
initial.hierarchy = pkg.appDefinition.hierarchy; initial.hierarchy = pkg.appDefinition.hierarchy;
initial.accessLevels = pkg.accessLevels; initial.accessLevels = pkg.accessLevels;
initial.derivedComponents = values(pkg.derivedComponents); initial.screens = values(pkg.screens);
initial.generators = generatorsArray(pkg.rootComponents.generators); initial.generators = generatorsArray(pkg.components.generators);
initial.allComponents = combineComponents( initial.components = values(pkg.components.components);
pkg.derivedComponents, pkg.rootComponents.components);
initial.actions = values(pkg.appDefinition.actions); initial.actions = values(pkg.appDefinition.actions);
initial.triggers = pkg.appDefinition.triggers; initial.triggers = pkg.appDefinition.triggers;
@ -175,17 +174,6 @@ const showFrontend = store => () => {
}) })
} }
const combineComponents = (root, derived) => {
const all = []
for(let r in root) {
all.push(root[r]);
}
for(let d in derived) {
all.push(derived[d]);
}
return all;
}
const newRecord = (store, useRoot) => () => { const newRecord = (store, useRoot) => () => {
store.update(s => { store.update(s => {
s.currentNodeIsNew = true; s.currentNodeIsNew = true;
@ -440,55 +428,50 @@ const setActiveNav = store => navName => {
const createShadowHierarchy = hierarchy => const createShadowHierarchy = hierarchy =>
constructHierarchy(JSON.parse(JSON.stringify(hierarchy))); constructHierarchy(JSON.parse(JSON.stringify(hierarchy)));
const saveDerivedComponent = store => (derivedComponent) => { const saveScreen = store => (screen) => {
store.update(s => { store.update(s => {
return _saveScreen(store, s, screen);
})
};
const components = pipe(s.allComponents, [ const _saveScreen = (store, s, screen) => {
filter(c => c.name !== derivedComponent.name), const screens = pipe(s.screens, [
concat([derivedComponent]) filter(c => c.name !== screen.name),
]); concat([screen])
]);
const derivedComponents = pipe(s.derivedComponents, [ s.screens = screens;
filter(c => c.name !== derivedComponent.name), s.currentFrontEndItem = screen;
concat([derivedComponent]) s.currentComponentInfo = getScreenInfo(
]); s.components, screen);
s.allComponents = components; api.post(`/_builder/api/${s.appname}/screen`, screen)
s.derivedComponents = derivedComponents; .then(() => savePackage(store, s));
s.currentFrontEndItem = derivedComponent;
s.currentComponentInfo = getComponentInfo(
s.allComponents, derivedComponent.name);
s.currentComponentIsNew = false;
api.post(`/_builder/api/${s.appname}/derivedcomponent`, derivedComponent)
.then(() => savePackage(store, s));
return s; return s;
}) }
};
const createDerivedComponent = store => componentName => { const createScreen = store => (screenName, layoutComponentName) => {
store.update(s => { store.update(s => {
const newComponentInfo = getNewComponentInfo( const newComponentInfo = getNewComponentInfo(
s.allComponents, componentName); s.components, layoutComponentName, screenName);
s.currentFrontEndItem = newComponentInfo.component; s.currentFrontEndItem = newComponentInfo.component;
s.currentComponentInfo = newComponentInfo; s.currentComponentInfo = newComponentInfo;
s.currentFrontEndType = "component"; s.currentFrontEndType = "screen";
s.currentComponentIsNew = true;
return s; return _saveScreen(store, s, newComponentInfo.component);
}); });
}; };
const createGeneratedComponents = store => components => { const createGeneratedComponents = store => components => {
store.update(s => { store.update(s => {
s.allComponents = [...s.allComponents, ...components]; s.components = [...s.components, ...components];
s.derivedComponents = [...s.derivedComponents, ...components]; s.screens = [...s.screens, ...components];
const doCreate = async () => { const doCreate = async () => {
for(let c of components) { for(let c of components) {
await api.post(`/_builder/api/${s.appname}/derivedcomponent`, c); await api.post(`/_builder/api/${s.appname}/screen`, c);
} }
await savePackage(store, s); await savePackage(store, s);
@ -500,55 +483,56 @@ const createGeneratedComponents = store => components => {
}); });
}; };
const deleteDerivedComponent = store => name => { const deleteScreen = store => name => {
store.update(s => { store.update(s => {
const allComponents = pipe(s.allComponents, [ const components = pipe(s.components, [
filter(c => c.name !== name) filter(c => c.name !== name)
]); ]);
const derivedComponents = pipe(s.derivedComponents, [ const screens = pipe(s.screens, [
filter(c => c.name !== name) filter(c => c.name !== name)
]); ]);
s.allComponents = allComponents; s.components = components;
s.derivedComponents = derivedComponents; s.screens = screens;
if(s.currentFrontEndItem.name === name) { if(s.currentFrontEndItem.name === name) {
s.currentFrontEndItem = null; s.currentFrontEndItem = null;
s.currentFrontEndType = ""; s.currentFrontEndType = "";
} }
api.delete(`/_builder/api/${s.appname}/derivedcomponent/${name}`); api.delete(`/_builder/api/${s.appname}/screen/${name}`);
return s; return s;
}) })
} }
const renameDerivedComponent = store => (oldname, newname) => { const renameScreen = store => (oldname, newname) => {
store.update(s => { store.update(s => {
const { const {
allComponents, pages, error, changedComponents screens, pages, error, changedScreens
} = rename(s.pages, s.allComponents, oldname, newname); } = rename(s.pages, s.screens, oldname, newname);
if(error) { if(error) {
// should really do something with this // should really do something with this
return s; return s;
} }
s.allComponents = allComponents; s.screens = screens;
s.pages = pages; s.pages = pages;
if(s.currentFrontEndItem.name === oldname) if(s.currentFrontEndItem.name === oldname)
s.currentFrontEndItem.name = newname; s.currentFrontEndItem.name = newname;
const saveAllChanged = async () => { const saveAllChanged = async () => {
for(let cname of changedComponents) { for(let screenName of changedScreens) {
const changedComponent = getExactComponent(allComponents, cname); const changedScreen
await api.post(`/_builder/api/${s.appname}/derivedcomponent`, changedComponent); = getExactComponent(screens, screenName);
await api.post(`/_builder/api/${s.appname}/screen`, changedScreen);
} }
} }
api.patch(`/_builder/api/${s.appname}/derivedcomponent`, { api.patch(`/_builder/api/${s.appname}/screen`, {
oldname, newname oldname, newname
}) })
.then(() => saveAllChanged()) .then(() => saveAllChanged())
@ -597,7 +581,7 @@ const addComponentLibrary = store => async lib => {
componentsArray.push(components[c]); componentsArray.push(components[c]);
} }
s.allComponents = pipe(s.allComponents, [ s.components = pipe(s.components, [
filter(c => !c.name.startsWith(`${lib}/`)), filter(c => !c.name.startsWith(`${lib}/`)),
concat(componentsArray) concat(componentsArray)
]); ]);
@ -643,20 +627,20 @@ const removeStylesheet = store => stylesheet => {
const refreshComponents = store => async () => { const refreshComponents = store => async () => {
const components = const componentsAndGenerators =
await api.get(`/_builder/api/${db.appname}/rootcomponents`).then(r => r.json()); await api.get(`/_builder/api/${db.appname}/components`).then(r => r.json());
const rootComponents = pipe(components.components, [ const components = pipe(componentsAndGenerators.components, [
keys, keys,
map(k => ({...components[k], name:k})) map(k => ({...componentsAndGenerators[k], name:k}))
]); ]);
store.update(s => { store.update(s => {
s.allComponents = pipe(s.allComponents, [ s.components = pipe(s.components, [
filter(c => !isRootComponent(c)), filter(c => !isRootComponent(c)),
concat(rootComponents) concat(components)
]); ]);
s.generators = components.generators; s.generators = componentsAndGenerators.generators;
return s; return s;
}); });
}; };
@ -668,8 +652,14 @@ const savePackage = (store, s) => {
triggers:s.triggers, triggers:s.triggers,
actions: keyBy("name")(s.actions), actions: keyBy("name")(s.actions),
props: { props: {
main: buildPropsHierarchy(s.allComponents, s.pages.main.appBody), main: buildPropsHierarchy(
unauthenticated: buildPropsHierarchy(s.allComponents, s.pages.unauthenticated.appBody) s.components,
s.screens,
s.pages.main.appBody),
unauthenticated: buildPropsHierarchy(
s.components,
s.screens,
s.pages.unauthenticated.appBody)
} }
}; };
@ -682,13 +672,12 @@ const savePackage = (store, s) => {
return api.post(`/_builder/api/${s.appname}/appPackage`, data); return api.post(`/_builder/api/${s.appname}/appPackage`, data);
} }
const setCurrentComponent = store => componentName => { const setCurrentScreen = store => screenName => {
store.update(s => { store.update(s => {
const component = getExactComponent(s.allComponents, componentName); const screen = getExactComponent(s.screens, screenName);
s.currentFrontEndItem = component; s.currentFrontEndItem = screen;
s.currentFrontEndType = "component"; s.currentFrontEndType = "screen";
s.currentComponentIsNew = false; s.currentComponentInfo = getScreenInfo(s.components, screen);
s.currentComponentInfo = getComponentInfo(s.allComponents, component.name);
return s; return s;
}) })
} }

114
packages/builder/src/userInterface/ComponentInstanceEditor.svelte

@ -1,114 +0,0 @@
<script>
import PropsView from "./PropsView.svelte";
import IconButton from "../common/IconButton.svelte";
import { getComponentInfo } from "./pagesParsing/createProps";
import { store } from "../builderStore";
import {
cloneDeep,
isUndefined
} from "lodash/fp";
import { fade, slide } from 'svelte/transition';
export let title = "";
export let onGoBack = () => {};
export let instanceProps = {};
export let onPropsChanged = () => {};
let editingSubComponentName;
let editingSubComponentProps;
let editingSubComponentArrayIndex;
let editingSubComponentArrayPropName;
let editingSubComponentTitle;
let allComponents;
store.subscribe(s => {
allComponents = s.allComponents;
})
$: componentInfo = getComponentInfo(
allComponents, instanceProps._component);
const onSubComponentGoBack = () => {
editingSubComponentName = null;
editingSubComponentProps = null;
}
const onEditComponentProp = (propName, arrayIndex, arrayPropName) => {
editingSubComponentName = propName;
editingSubComponentTitle = isUndefined(arrayIndex)
? propName
: `${propName}[${arrayIndex}].${arrayPropName}`;
editingSubComponentProps = isUndefined(arrayIndex)
? instanceProps[propName]
: instanceProps[propName][arrayIndex][arrayPropName];
editingSubComponentArrayIndex = arrayIndex;
editingSubComponentArrayPropName = arrayPropName;
};
const onSubComponentPropsChanged = (subProps) => {
const newProps = cloneDeep(instanceProps);
if(isUndefined(editingSubComponentArrayIndex)) {
newProps[editingSubComponentName] = subProps;
} else {
newProps[editingSubComponentName]
[editingSubComponentArrayIndex]
[editingSubComponentArrayPropName] = subProps;
}
instanceProps = newProps;
onPropsChanged(newProps);
}
const propsChanged = newProps => {
instanceProps = newProps;
onPropsChanged(newProps);
}
</script>
<div>
<div class="title">
<IconButton icon="chevron-left"
on:click={onGoBack}/>
<span>{title}</span>
</div>
{#if editingSubComponentName}
<div in:slide={{delay: 250, duration: 300}}
out:fade>
<svelte:self onPropsChanged={onSubComponentPropsChanged}
onGoBack={onSubComponentGoBack}
instanceProps={editingSubComponentProps}
title={editingSubComponentTitle} />
</div>
{:else}
<PropsView {instanceProps}
{componentInfo}
onPropsChanged={propsChanged}
{onEditComponentProp} />
{/if}
</div>
<style>
.title {
padding:3px;
background-color: white;
color: var(--secondary100);
border-style:solid;
border-width: 1px 0 0 0;
border-color: var(--lightslate);
}
.title > span {
margin-left: 10px;
}
</style>

139
packages/builder/src/userInterface/ComponentPropSelector.svelte

@ -1,139 +0,0 @@
<script>
import {
last
} from "lodash/fp";
import IconButton from "../common/IconButton.svelte";
import ComponentSelector from "./ComponentSelector.svelte";
import Button from "../common/Button.svelte";
import ButtonGroup from "../common/ButtonGroup.svelte";
import UIkit from "uikit";
import {
getComponentInfo
} from "./pagesParsing/createProps";
import { store } from "../builderStore";
const emptyProps = () => ({_component:""})
export let props = emptyProps();
export let onValueChanged = () => {};
export let onComponentChosen = () => {};
export let onEdit = () => {};
export let disabled = false;
const CHOOSE_COMPONENT = "choose_component";
const CLEAR_COMPONENT = "clear_component";
let allComponents;
let modalElement;
let modalAction;
store.subscribe(s => {
allComponents = s.allComponents;
});
$: componentSelected = props._component.length > 0;
$: shortName = last(props._component.split("/"));
const chooseComponent = () => {
modalAction = CHOOSE_COMPONENT;
showDialog();
}
const clearComponent = () => {
modalAction = CLEAR_COMPONENT;
showDialog();
}
const componentChosen = (component) => {
const componentInfo = getComponentInfo(allComponents, component.name);
props = componentInfo.fullProps;
onValueChanged(props);
onComponentChosen();
hideDialog();
}
const hideDialog = () => {
UIkit.modal(modalElement).hide();
}
const showDialog = () => {
UIkit.modal(modalElement).show();
}
const confirmClearComponent = () => {
props = emptyProps();
onValueChanged(emptyProps());
hideDialog();
}
</script>
<div class="root uk-form-controls">
<div class:selectedname={componentSelected}>
{componentSelected ? shortName : "(none)"}
</div>
<div>
{#if !disabled && componentSelected}
<IconButton icon="edit"
on:click={() => onEdit()}/>
<IconButton icon="trash"
on:click={() => clearComponent()} />
{:else if !disabled && !componentSelected}
<IconButton icon="plus"
on:click={() => chooseComponent()} />
{/if}
</div>
</div>
<div bind:this={modalElement} uk-modal>
<div class="uk-modal-dialog" uk-overflow-auto>
{#if modalAction === CHOOSE_COMPONENT}
<div class="uk-modal-body">
<ComponentSelector onComponentChosen={componentChosen}
allowGenerators={false} />
</div>
{:else if modalAction === CLEAR_COMPONENT}
<div class="uk-modal-body">
Clear this component ?
</div>
<div class="uk-modal-footer">
<ButtonGroup>
<Button grouped
on:click={hideDialog}
color="secondary" >Cancel</Button>
<Button grouped
on:click={confirmClearComponent}>OK</Button>
</ButtonGroup>
</div>
{/if}
</div>
</div>
<style>
.root {
display: grid;
grid-template-columns: [name] 1fr [actions] auto;
}
.root > div:nth-child(1) {
grid-column-start: name;
color: var(--secondary50);
}
.root > div:nth-child(2) {
grid-column-start: actions;
}
.selectedname {
font-weight: bold;
color: var(--secondary);
}
</style>

6
packages/builder/src/userInterface/ComponentSearch.svelte

@ -5,17 +5,17 @@ import { store } from "../builderStore";
export let onComponentChosen = () => {}; export let onComponentChosen = () => {};
let allComponents = []; let components = [];
let phrase = ""; let phrase = "";
store.subscribe(s => { store.subscribe(s => {
allComponents = s.allComponents; components = s.components;
}); });
$: filteredComponents = $: filteredComponents =
!phrase !phrase
? [] ? []
: searchAllComponents(allComponents, phrase); : searchAllComponents(components, phrase);
</script> </script>

12
packages/builder/src/userInterface/ComponentSelector.svelte

@ -13,7 +13,7 @@ export let onComponentChosen;
export let onGeneratorChosen; export let onGeneratorChosen;
export let allowGenerators; export let allowGenerators;
let derivedComponents=[]; let screens=[];
let componentLibraries=[]; let componentLibraries=[];
const addRootComponent = (c, all, isGenerator) => { const addRootComponent = (c, all, isGenerator) => {
@ -41,16 +41,16 @@ const addRootComponent = (c, all, isGenerator) => {
store.subscribe(s => { store.subscribe(s => {
const newComponentLibraries = []; const newComponentLibraries = [];
const newDerivedComponents = []; const newscreens = [];
for(let comp of sortBy(["name"])(s.allComponents)) { for(let comp of sortBy(["name"])(s.components)) {
if(isRootComponent(comp)) { if(isRootComponent(comp)) {
addRootComponent( addRootComponent(
comp, comp,
newComponentLibraries, newComponentLibraries,
false); false);
} else { } else {
newDerivedComponents.push(comp); newscreens.push(comp);
} }
} }
@ -61,7 +61,7 @@ store.subscribe(s => {
true); true);
} }
derivedComponents = sortBy(["name"])(newDerivedComponents); screens = sortBy(["name"])(newscreens);
componentLibraries = newComponentLibraries; componentLibraries = newComponentLibraries;
}); });
@ -127,7 +127,7 @@ store.subscribe(s => {
<div class="library-container"> <div class="library-container">
{#each derivedComponents as component} {#each screens as component}
<div class="component" <div class="component"
on:click={() => onComponentChosen(component)}> on:click={() => onComponentChosen(component)}>

4
packages/builder/src/userInterface/ComponentsHierarchy.svelte

@ -88,7 +88,7 @@ const expandFolder = folder => {
} }
const isComponentSelected = (type, current,c) => const isComponentSelected = (type, current,c) =>
type==="component" type==="screen"
&& current && current
&& current.name === c.name && current.name === c.name
@ -136,7 +136,7 @@ $: {
{#each componentsThisLevel as component} {#each componentsThisLevel as component}
<div class="hierarchy-item component" class:selected={isComponentSelected($store.currentFrontEndType, $store.currentFrontEndItem, component.component)} <div class="hierarchy-item component" class:selected={isComponentSelected($store.currentFrontEndType, $store.currentFrontEndItem, component.component)}
on:click|stopPropagation={() => store.setCurrentComponent(component.component.name)}> on:click|stopPropagation={() => store.setCurrentScreen(component.component.name)}>
<span>{@html getIcon("circle", "7")}</span> <span>{@html getIcon("circle", "7")}</span>
<span class="title">{component.title}</span> <span class="title">{component.title}</span>
</div> </div>

142
packages/builder/src/userInterface/ComponentsList.svelte

@ -0,0 +1,142 @@
<script>
import {
isRootComponent
} from "./pagesParsing/searchComponents"
import { splitName } from "./pagesParsing/splitRootComponentName.js"
import { store } from "../builderStore";
import {
groupBy, keys, find, sortBy
} from "lodash/fp";
import { pipe } from "../common/core";
let componentLibraries=[];
const addRootComponent = (c, all) => {
const { libName } = splitName(c.name);
let group = find(r => r.libName === libName)(all);
if(!group) {
group = {
libName,
components: [],
generators: []
};
all.push(group);
}
group.components.push(c)
};
const onComponentChosen = (component) => {
};
store.subscribe(s => {
const newComponentLibraries = [];
for(let comp of sortBy(["name"])(s.components)) {
addRootComponent(
comp,
newComponentLibraries);
}
componentLibraries = newComponentLibraries;
});
</script>
<div class="root">
{#each componentLibraries as lib}
<div class="library-header">
{lib.libName}
</div>
<div class="library-container">
<div class="inner-header">
Components
</div>
{#each lib.components as component}
<div class="component"
on:click={() => onComponentChosen(component)}>
<div class="name">
{splitName(component.name).componentName}
</div>
<div class="description">
{component.description}
</div>
</div>
{/each}
</div>
{/each}
</div>
<style>
.root {
display: flex;
flex-direction: column;
}
.library-header {
font-size: 1.1em;
border-color: var(--primary25);
border-width: 1px 0px;
border-style: solid;
background-color: var(--primary10);
padding: 5px 0;
flex: 0 0 auto;
}
.library-container {
padding: 0 0 10px 10px;
flex: 1 1 auto;
min-height: 0px;
}
.inner-header {
font-size: 0.9em;
font-weight: bold;
margin-top: 7px;
margin-bottom: 3px;
}
.component {
padding: 2px 0px;
cursor: pointer;
}
.component:hover {
background-color: var(--lightslate);
}
.component > .name {
color: var(--secondary100);
display: inline-block;
}
.component > .description {
font-size: 0.8em;
color: var(--secondary75);
display: inline-block;
margin-left: 10px;
}
</style>

79
packages/builder/src/userInterface/ComponentsPaneSwitcher.svelte

@ -0,0 +1,79 @@
<script>
import EditComponentProps from "./EditComponentProps.svelte";
import ComponentsList from "./ComponentsList.svelte";
let selected="properties";
const isSelected = tab =>
selected === tab;
const selectTab = tab =>
selected = tab;
</script>
<div class="root">
<div class="switcher">
<button
class:selected={selected==="properties"}
on:click={() => selectTab("properties")}>
Properties
</button>
<button
class:selected={selected==="components"}
on:click={() => selectTab("components")}>
Components
</button>
</div>
<div class="panel">
{#if selected==="properties"}
<EditComponentProps />
{/if}
{#if selected==="components"}
<ComponentsList />
{/if}
</div>
</div>
<style>
.root {
height: 100%;
display: flex;
flex-direction: column;
}
.switcher {
flex: 0 0 auto;
}
.switcher > button {
display: inline-block;
background-color: rgba(0,0,0,0);
border-style: solid;
border-color: var(--slate);
margin: 5px;
padding: 5px;
cursor: pointer;
}
.switcher > .selected {
background-color: red;
}
.panel {
flex: 1 1 auto;
height: 0px;
overflow-y: auto;
}
</style>

5
packages/builder/src/userInterface/CurrentItemPreview.svelte

@ -24,7 +24,10 @@ store.subscribe(s => {
]); ]);
appDefinition = { appDefinition = {
componentLibraries: s.loadLibraryUrls(), componentLibraries: s.loadLibraryUrls(),
props: buildPropsHierarchy(s.allComponents, s.currentFrontEndItem), props: buildPropsHierarchy(
s.components,
s.screens,
s.currentFrontEndItem),
hierarchy: s.hierarchy, hierarchy: s.hierarchy,
appRootPath: "" appRootPath: ""
}; };

117
packages/builder/src/userInterface/EditComponent.svelte → packages/builder/src/userInterface/EditComponentProps.svelte

@ -8,11 +8,10 @@ import Textbox from "../common/Textbox.svelte";
import UIkit from "uikit"; import UIkit from "uikit";
import { pipe } from "../common/core"; import { pipe } from "../common/core";
import { import {
getComponentInfo getScreenInfo
} from "./pagesParsing/createProps"; } from "./pagesParsing/createProps";
import Button from "../common/Button.svelte"; import Button from "../common/Button.svelte";
import ButtonGroup from "../common/ButtonGroup.svelte"; import ButtonGroup from "../common/ButtonGroup.svelte";
import ComponentInstanceEditor from "./ComponentInstanceEditor.svelte";
import { import {
cloneDeep, cloneDeep,
@ -30,17 +29,11 @@ let name = "";
let description = ""; let description = "";
let tagsString = ""; let tagsString = "";
let nameInvalid = ""; let nameInvalid = "";
let componentDetailsExpanded = false;
let componentInfo; let componentInfo;
let modalElement let modalElement
let propsValidationErrors = []; let propsValidationErrors = [];
let editingComponentInstance;
let editingComponentInstancePropName="";
let editingComponentArrayIndex;
let editingComponentArrayPropName;
let editingComponentInstanceTitle;
let originalName=""; let originalName="";
let allComponents; let components;
let ignoreStore = false; let ignoreStore = false;
$: shortName = last(name.split("/")); $: shortName = last(name.split("/"));
@ -54,8 +47,7 @@ store.subscribe(s => {
description = component.description; description = component.description;
tagsString = join(", ")(component.tags); tagsString = join(", ")(component.tags);
componentInfo = s.currentComponentInfo; componentInfo = s.currentComponentInfo;
componentDetailsExpanded = s.currentComponentIsNew; components = s.components;
allComponents = s.allComponents;
}); });
const save = () => { const save = () => {
@ -73,12 +65,12 @@ const save = () => {
map(s => s.trim()) map(s => s.trim())
]); ]);
store.saveDerivedComponent(component); store.saveScreen(component);
ignoreStore = false; ignoreStore = false;
// now do the rename // now do the rename
if(name !== originalName) { if(name !== originalName) {
store.renameDerivedComponent(originalName, name); store.renameScreen(originalName, name);
} }
} }
@ -87,7 +79,7 @@ const deleteComponent = () => {
} }
const confirmDeleteComponent = () => { const confirmDeleteComponent = () => {
store.deleteDerivedComponent(component.name); store.deleteScreen(component.name);
hideDialog(); hideDialog();
} }
@ -99,7 +91,7 @@ const updateComponent = doChange => {
const newComponent = cloneDeep(component); const newComponent = cloneDeep(component);
doChange(newComponent); doChange(newComponent);
component = newComponent; component = newComponent;
componentInfo = getComponentInfo(allComponents, newComponent); componentInfo = getScreenInfo(components, newComponent);
} }
const onPropsChanged = newProps => { const onPropsChanged = newProps => {
@ -128,37 +120,6 @@ const showDialog = () => {
UIkit.modal(modalElement).show(); UIkit.modal(modalElement).show();
} }
const onEditComponentProp = (propName, arrayIndex, arrayPropName) => {
editingComponentInstance = isUndefined(arrayIndex)
? component.props[propName]
: component.props[propName][arrayIndex][arrayPropName];
editingComponentInstancePropName = propName;
editingComponentInstanceTitle = isUndefined(arrayIndex)
? propName
: `${propName}[${arrayIndex}].${arrayPropName}`;
editingComponentArrayIndex = arrayIndex;
editingComponentArrayPropName = arrayPropName;
}
const componentInstanceCancelEdit = () => {
editingComponentInstance = null;
editingComponentInstancePropName = "";
}
const componentInstancePropsChanged = (instanceProps) => {
updateComponent(newComponent => {
if(isUndefined(editingComponentArrayIndex)) {
newComponent.props[editingComponentInstancePropName] = instanceProps;
} else {
newComponent.props[editingComponentInstancePropName]
[editingComponentArrayIndex]
[editingComponentArrayPropName] = instanceProps;
}
});
}
</script> </script>
<div class="root"> <div class="root">
@ -177,53 +138,15 @@ const componentInstancePropsChanged = (instanceProps) => {
</div> </div>
</div> </div>
{#if editingComponentInstance}
<div class="component-props-container"> <div class="component-props-container">
<ComponentInstanceEditor onGoBack={componentInstanceCancelEdit}
title={editingComponentInstanceTitle}
instanceProps={editingComponentInstance}
onPropsChanged={componentInstancePropsChanged}/>
</div>
{:else}
<div class="component-props-container">
<div class="section-header padding" on:click={() => componentDetailsExpanded = !componentDetailsExpanded}>
<span style="margin-right: 7px">Component Details</span>
<IconButton icon={componentDetailsExpanded ? "chevron-down" : "chevron-right"}/>
</div>
{#if componentDetailsExpanded}
<div class="padding">
<div class="info-text">
<Textbox label="Name"
infoText="use forward slash to store in subfolders"
text={name}
on:change={ev => name = ev.target.value}
hasError={!!nameInvalid}/>
<Textbox label="Description"
on:change={ev => description = ev.target.value}
text={description}/>
<Textbox label="Tags"
infoText="comma separated"
on:change={ev => tagsString = ev.target.value}
text={tagsString}/>
</div>
</div>
{/if}
<div class="section-header padding">
<span>Properties</span>
</div>
<PropsView onValidate={onPropsValidate} <PropsView onValidate={onPropsValidate}
{componentInfo} {componentInfo}
{onPropsChanged} {onPropsChanged} />
{onEditComponentProp}/>
</div> </div>
{/if}
</div> </div>
@ -263,20 +186,13 @@ const componentInstancePropsChanged = (instanceProps) => {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} border-style: solid;
border-width: 1px 0 0 0;
.padding { border-color: var(--slate);
padding: 1rem 1rem 0rem 1rem;
}
.info-text {
color: var(--secondary100);
font-size: .8rem !important;
font-weight: bold;
} }
.title { .title {
padding: 2rem 1rem 1rem 1rem; padding: 1rem;
display: grid; display: grid;
grid-template-columns: [name] 1fr [actions] auto; grid-template-columns: [name] 1fr [actions] auto;
color: var(--secondary100); color: var(--secondary100);
@ -293,15 +209,6 @@ const componentInstancePropsChanged = (instanceProps) => {
grid-column-start: actions; grid-column-start: actions;
} }
.section-header {
display: grid;
grid-template-columns: [name] 1fr [actions] auto;
color: var(--secondary50);
font-size: .9rem;
font-weight: bold;
vertical-align: middle;
}
.component-props-container { .component-props-container {
flex: 1 1 auto; flex: 1 1 auto;
overflow-y: auto; overflow-y: auto;

161
packages/builder/src/userInterface/GeneratedComponents.svelte

@ -1,161 +0,0 @@
<script>
import { store } from "../builderStore";
import { splitName } from "./pagesParsing/splitRootComponentName";
import {
getIndexNodes, getRecordNodes, getIndexSchema, pipe
} from "../common/core";
import {
map, some, filter
} from "lodash/fp";
import Button from "../common/Button.svelte";
import { componentDependencies } from "./pagesParsing/findDependencies";
import { rename } from "./pagesParsing/renameComponent";
import { getExactComponent } from "./pagesParsing/searchComponents";
export let generator;
export let onConfirmGenerate;
let libName;
let componentName;
let libs;
let existingComponents;
let _generator;
let components;
let generateParameter;
let allGeneratedComponents;
let selectedComponents = [];
store.subscribe(s => {
libs = s.generatorLibraries;
generateParameter = {
indexes: getIndexNodes(s.hierarchy),
records: getRecordNodes(s.hierarchy),
helpers: {
indexSchema: getIndexSchema(s.hierarchy)
}
}
existingComponents = s.allComponents;
});
const componentExists = name =>
getExactComponent(existingComponents, name);
const componentsWithDependencies = () => {
const cmp = map(c => {
const dependants = componentDependencies(
{}, [...selectedComponents, ...existingComponents], c);
const exists = componentExists(c.name);
return {
dependants: dependants.dependantComponents,
component:c,
error: exists ? "a component by this name already exists" : ""
};
})(allGeneratedComponents);
components = cmp;
}
$ : {
if(generator && generator !== _generator) {
_generator = generator;
const sp = splitName(generator.name);
libName = sp.libName;
componentName = sp.componentName;
allGeneratedComponents = libs[libName][componentName](generateParameter);
selectedComponents =
filter(c => !componentExists(c.name))(allGeneratedComponents);
componentsWithDependencies();
}
}
const onSelectedChanged = component => ev => {
const newselectedComponents = filter(c => c.name !== component.component.name)(
selectedComponents);
if(ev.target.checked) {
newselectedComponents.push(component.component);
}
selectedComponents = newselectedComponents;
componentsWithDependencies();
}
const onNameChanged = component => ev => {
const newname = ev.target.value;
const oldname = component.component.name;
const result = rename({}, allGeneratedComponents, oldname, newname);
component.error = result.error || "";
allGeneratedComponents = [...result.allComponents];
selectedComponents = map(s => {
if(s.name === oldname) s.name = newname;
return s;
})(selectedComponents);
componentsWithDependencies();
}
const isComponentSelected = component =>
some(c => c.name === component.component.name)(selectedComponents);
</script>
{#each components as c}
<div class="component">
<div class="uk-inline">
<input type="checkbox"
disabled={c.dependants.length > 0}
class="uk-checkbox"
checked={isComponentSelected(c)}
on:change={onSelectedChanged(c)}>
<input type="text"
value={c.component.name}
on:change={onNameChanged(c)}
class="uk-input title {c.error ? 'uk-form-danger' : ''}">
{#if isComponentSelected(c)}
<span class="error">{c.error}</span>
{/if}
</div>
<div class="description">
{c.component.description}
</div>
</div>
{/each}
<div class="button-container">
<Button on:click={() => onConfirmGenerate(selectedComponents)}>Add Components</Button>
</div>
<style>
.component {
padding: 5px 0;
}
.component .title {
width: 300px
}
.component > .description {
font-size: 0.8em;
color: var(--secondary75);
}
.button-container {
text-align: right;
margin-top: 20px;
}
.error {
font-size: 10pt;
color: red;
}
</style>

138
packages/builder/src/userInterface/NewComponent.svelte

@ -8,100 +8,126 @@ import Button from "../common/Button.svelte";
import ButtonGroup from "../common/ButtonGroup.svelte"; import ButtonGroup from "../common/ButtonGroup.svelte";
import { pipe } from "../common/core"; import { pipe } from "../common/core";
import UIkit from "uikit"; import UIkit from "uikit";
import {
getNewComponentInfo
} from "./pagesParsing/createProps";
import { isRootComponent } from "./pagesParsing/searchComponents"; import { isRootComponent } from "./pagesParsing/searchComponents";
import GeneratedComponents from "./GeneratedComponents.svelte"; import { splitName } from "./pagesParsing/splitRootComponentName.js"
import { import {
cloneDeep, find, filter, some, map, includes
join,
split,
map,
keys,
isUndefined
} from "lodash/fp"; } from "lodash/fp";
import { assign } from "lodash"; import { assign } from "lodash";
export const show = () => {
UIkit.modal(componentSelectorModal).show();
}
let componentSelectorModal; let componentSelectorModal;
let generatorOptionsModal; let layoutComponents;
let allComponents; let layoutComponent;
let generator; let screens;
let name="";
let saveAttempted=false;
store.subscribe(s => { store.subscribe(s => {
allComponents = s.allComponents;
})
export const close = () => { layoutComponents = pipe(s.components, [
UIkit.modal(componentSelectorModal).hide(); filter(c => includes("layout")(c.tags)),
if(generatorOptionsModal) UIkit.modal(generatorOptionsModal).hide(); map(c => ({name:c.name, ...splitName(c.name)}))
generator = null; ]);
}
export const show = () => { layoutComponent = layoutComponent
UIkit.modal(componentSelectorModal).show(); ? find(c => c.name === layoutComponent.name)(layoutComponents)
} : layoutComponents[0];
const onComponentChosen = (c) => { screens = s.screens;
store.createDerivedComponent(c.name); });
close();
} const save = () => {
saveAttempted = true;
const onGeneratorChosen = (g) => { const isValid = name.length > 0
generator = g; && !screenNameExists(name)
&& layoutComponent;
if(!isValid) return;
store.createScreen(name, layoutComponent.name);
UIkit.modal(componentSelectorModal).hide(); UIkit.modal(componentSelectorModal).hide();
UIkit.modal(generatorOptionsModal).show();
} }
const onConfirmGenerate = (components) => { const cancel = () => {
store.createGeneratedComponents(components); UIkit.modal(componentSelectorModal).hide();
UIkit.modal(generatorOptionsModal).hide();
generator = null;
} }
const screenNameExists = (name) =>
some(s => s.name.toLowerCase() === name.toLowerCase())(screens)
</script> </script>
<div bind:this={componentSelectorModal} id="new-component-modal" uk-modal> <div bind:this={componentSelectorModal} id="new-component-modal" uk-modal>
<div class="uk-modal-dialog" uk-overflow-auto> <div class="uk-modal-dialog" uk-overflow-auto>
<div class="uk-modal-header"> <div class="uk-modal-header">
<h1>New Component</h1> <h1>New Screen</h1>
</div> </div>
<div class="uk-modal-body"> <div class="uk-modal-body uk-form-horizontal">
<ComponentSelector onComponentChosen={onComponentChosen} <div class="uk-margin">
onGeneratorChosen={onGeneratorChosen} <label class="uk-form-label">Name</label>
allowGenerators={true} /> <div class="uk-form-controls">
<input class="uk-input uk-form-small"
class:uk-form-danger={saveAttempted && (name.length === 0 || screenNameExists(name))}
bind:value={name} >
</div>
</div>
<div class="uk-margin">
<label class="uk-form-label">Layout Component</label>
<div class="uk-form-controls">
<select class="uk-select uk-form-small"
bind:value={layoutComponent}
class:uk-form-danger={saveAttempted && !layoutComponent}>
{#each layoutComponents as comp}
<option value={comp}>
{comp.componentName} - {comp.libName}
</option>
{/each}
</select>
</div>
</div>
<ButtonGroup style="float: right;">
<Button color="primary" grouped on:click={save}>Create Screen</Button>
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button>
</ButtonGroup>
</div> </div>
</div> </div>
</div> </div>
<div bind:this={generatorOptionsModal} uk-modal>
<div class="uk-modal-dialog" uk-overflow-auto>
{#if generator} <style>
h1 {
<div class="uk-modal-header"> font-size:1.2em;
<h1>Generator - {generator ? generator.name : ""}</h1> }
</div>
<div class="uk-modal-body"> .component-option {
<GeneratedComponents generator={generator} border-style: solid;
onConfirmGenerate={onConfirmGenerate} /> border-color: var(--slate);
</div> border-width: 0 0 1px 0;
padding: 3px;
}
{/if} .component-option:hover {
</div> background-color: var(--lightslate);
</div> }
.component-name {
font-size: 11pt;
}
<style> .lib-name {
h1 { font-size: 9pt;
font-size:1.2em;
} }
</style> </style>

2
packages/builder/src/userInterface/PageView.svelte

@ -20,7 +20,7 @@ store.subscribe(s => {
page = s.pages[s.currentPageName]; page = s.pages[s.currentPageName];
if(!page) return; if(!page) return;
title = page.index.title; title = page.index.title;
components = pipe(s.allComponents, [ components = pipe(s.components, [
filter(s => !isRootComponent(s)), filter(s => !isRootComponent(s)),
concat([notSeletedComponent]) concat([notSeletedComponent])
]); ]);

120
packages/builder/src/userInterface/PropArraySelector.svelte

@ -1,120 +0,0 @@
<script>
import IconButton from "../common/IconButton.svelte";
import {
createArrayElementProps
} from "./pagesParsing/createProps";
import PropControl from "./PropControl.svelte";
import {
some,
cloneDeep,
} from "lodash/fp";
import { validateProps } from "./pagesParsing/validateProps";
export let parentProps;
export let propDef;
export let onValueChanged;
export let onValidate = () => {};
export let onEditComponentProp = () => {};
let value = [];
let elementDefinitionArray;
let elementErrors = {};
$: {
const elArray = [];
for(let elProp in propDef.elementDefinition) {
if(elProp === "_component") continue;
elArray.push({
...propDef.elementDefinition[elProp],
____name: elProp
});
}
elementDefinitionArray = elArray;
value = parentProps[propDef.____name];
}
const addElement = () => {
const newElement = createArrayElementProps(
propDef.____name,
propDef.elementDefinition).props;
value = [...value, newElement];
onValueChanged(value);
}
const validate = (index, elementProps) => {
elementErrors[index] = validateProps(
propDef.elementDefinition, elementProps, [], true);
onValidate(elementErrors[index]);
return elementErrors[index].length === 0;
}
const setProp = (index) => (name, propValue) => {
const newValue = cloneDeep(value);
const newProps = cloneDeep(newValue[index]);
newProps[name] = propValue;
newValue[index] = newProps;
value = newValue;
if(validate(index, newProps))
onValueChanged(newValue);
}
let fieldHasError = index => propName =>
some(e => e.propName === propName)(elementErrors[index]);
const onEditComponent = (index, propName) => () => {
onEditComponentProp(index, propName);
}
</script>
<div class="root">
<div class="item-container">
{#each value as item, index}
<div class="item-inner-container">
{#each elementDefinitionArray as propDef}
<PropControl setProp={setProp(index)}
fieldHasError={fieldHasError(index)}
{propDef}
props={item}
{index}
onEditComponent={onEditComponent(index, propDef.____name)}
disabled={false} />
{/each}
</div>
{/each}
<div class="addelement-container"
on:click={addElement}>
<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);
}
.item-container {
padding-left: 3px;
background: var(--secondary10);
}
</style>

25
packages/builder/src/userInterface/PropControl.svelte

@ -3,8 +3,6 @@
import Checkbox from "../common/Checkbox.svelte"; import Checkbox from "../common/Checkbox.svelte";
import Textbox from "../common/Textbox.svelte"; import Textbox from "../common/Textbox.svelte";
import Dropdown from "../common/Dropdown.svelte"; import Dropdown from "../common/Dropdown.svelte";
import ComponentPropSelector from "./ComponentPropSelector.svelte";
import PropArraySelector from "./PropArraySelector.svelte";
import EventListSelector from "./EventListSelector.svelte"; import EventListSelector from "./EventListSelector.svelte";
import StateBindingControl from "./StateBindingControl.svelte"; import StateBindingControl from "./StateBindingControl.svelte";
@ -15,7 +13,6 @@ export let propDef = {};
export let props = {}; export let props = {};
export let disabled; export let disabled;
export let index; export let index;
export let onEditComponent = () => {};
$: isOdd = (index % 2 !== 0); $: isOdd = (index % 2 !== 0);
@ -28,32 +25,14 @@ const setComponentProp = (props) => {
<div class="root" > <div class="root" >
{#if propDef.type === "component"} {#if propDef.type === "event"}
<div class="prop-label">{propDef.____name}</div>
<ComponentPropSelector label={propDef.____name}
props={props[propDef.____name]}
{disabled}
onEdit={onEditComponent}
onComponentChosen={onEditComponent}
onValueChanged={setComponentProp}/>
{:else if propDef.type === "array"}
<div class="prop-label">{propDef.____name}</div>
<PropArraySelector parentProps={props}
{propDef}
onValueChanged={setComponentProp}
onEditComponentProp={onEditComponent} />
{:else if propDef.type === "event"}
<div class="prop-label">{propDef.____name}</div> <div class="prop-label">{propDef.____name}</div>
<EventListSelector parentProps={props} <EventListSelector parentProps={props}
{propDef} {propDef}
onValueChanged={setComponentProp} /> onValueChanged={setComponentProp} />
{:else} {:else }
<div class="prop-label">{propDef.____name}</div> <div class="prop-label">{propDef.____name}</div>
<StateBindingControl value={props[propDef.____name]} <StateBindingControl value={props[propDef.____name]}

105
packages/builder/src/userInterface/PropsView.svelte

@ -1,27 +1,16 @@
<script> <script>
import { import {
keys, keys, map, some, includes,
map, cloneDeep, isEqual, sortBy,
some, filter, difference
includes,
cloneDeep,
isEqual,
sortBy,
filter,
difference
} from "lodash/fp"; } from "lodash/fp";
import { pipe } from "../common/core"; import { pipe } from "../common/core";
import { import { getInstanceProps } from "./pagesParsing/createProps";
getComponentInfo ,
getInstanceProps
} from "./pagesParsing/createProps";
import { getExactComponent } from "./pagesParsing/searchComponents";
import Checkbox from "../common/Checkbox.svelte"; import Checkbox from "../common/Checkbox.svelte";
import Textbox from "../common/Textbox.svelte"; import Textbox from "../common/Textbox.svelte";
import Dropdown from "../common/Dropdown.svelte"; import Dropdown from "../common/Dropdown.svelte";
import { validateProps } from "./pagesParsing/validateProps"; import { validateProps } from "./pagesParsing/validateProps";
import ComponentPropSelector from "./ComponentPropSelector.svelte";
import PropControl from "./PropControl.svelte"; import PropControl from "./PropControl.svelte";
import IconButton from "../common/IconButton.svelte"; import IconButton from "../common/IconButton.svelte";
@ -30,17 +19,12 @@ export let onValidate = () => {};
export let componentInfo; export let componentInfo;
export let instanceProps = null; export let instanceProps = null;
export let onPropsChanged = () => {}; export let onPropsChanged = () => {};
export let onEditComponentProp = () => {};
let errors = []; let errors = [];
let props = {}; let props = {};
let propsDefinitions = []; let propsDefinitions = [];
let inheritedPropsDefinitions = [];
let inheritedExpanded = false;
let isInstance = false; let isInstance = false;
const isPropInherited = name =>
includes(name)(componentInfo.inheritedProps);
$: { $: {
if(componentInfo) if(componentInfo)
@ -52,14 +36,6 @@ $: {
propsDefinitions = pipe(componentInfo.propsDefinition, [ propsDefinitions = pipe(componentInfo.propsDefinition, [
keys, keys,
filter(k => !isPropInherited(k)),
map(k => ({...componentInfo.propsDefinition[k], ____name:k})),
sortBy("____name")
]);
inheritedPropsDefinitions = pipe(componentInfo.propsDefinition, [
keys,
filter(k => isPropInherited(k)),
map(k => ({...componentInfo.propsDefinition[k], ____name:k})), map(k => ({...componentInfo.propsDefinition[k], ____name:k})),
sortBy("____name") sortBy("____name")
]); ]);
@ -92,7 +68,7 @@ let setProp = (name, value) => {
} }
const validate = (finalProps) => { const validate = (finalProps) => {
errors = validateProps(componentInfo.propsDefinition, finalProps, [], false); errors = validateProps(componentInfo.rootComponent, finalProps, [], false);
onValidate(errors); onValidate(errors);
return errors.length === 0; return errors.length === 0;
} }
@ -100,49 +76,26 @@ const validate = (finalProps) => {
const fieldHasError = (propName) => const fieldHasError = (propName) =>
some(e => e.propName === propName)(errors); some(e => e.propName === propName)(errors);
const onEditComponent = (propName) => (arrayIndex, arrayPropName) => {
onEditComponentProp(propName, arrayIndex, arrayPropName);
}
</script> </script>
<div class="root"> <div class="root">
<form class="uk-form-stacked"> <form class="uk-form-stacked form-root">
{#each propsDefinitions as propDef, index} {#each propsDefinitions as propDef, index}
<PropControl {setProp} <div class="prop-container">
{fieldHasError}
{propDef}
{props}
{index}
onEditComponent={onEditComponent(propDef.____name)}
disabled={false} />
{/each}
{#if inheritedPropsDefinitions.length > 0} <PropControl {setProp}
<div class="inherited-title padding"> {fieldHasError}
<div>Inherited</div> {propDef}
<div> {props}
<IconButton icon={inheritedExpanded ? "chevron-down" : "chevron-right"} {index}
on:click={() => inheritedExpanded = !inheritedExpanded}/> disabled={false} />
</div>
</div>
{/if}
{#if inheritedExpanded} </div>
{#each inheritedPropsDefinitions as propDef, index}
<PropControl {setProp} {/each}
{fieldHasError}
{propDef}
{props}
{index}
disabled={true} />
{/each}
{/if}
</form> </form>
@ -155,29 +108,17 @@ const onEditComponent = (propName) => (arrayIndex, arrayPropName) => {
.root { .root {
font-size:10pt; font-size:10pt;
width: 100%;
} }
.padding { .form-root {
padding: 0 10px; display: flex;
} flex-wrap: wrap;
.inherited-title {
padding: 1rem 1rem 1rem 1rem;
display: grid;
grid-template-columns: [name] 1fr [actions] auto;
color: var(--secondary100);
font-size: .9rem;
font-weight: bold;
}
.inherited-title > div:nth-child(1) {
grid-column-start: name;
color: var(--secondary50);
} }
.inherited-title > div:nth-child(2) { .prop-container {
grid-column-start: actions; flex: 1 1 auto;
color: var(--secondary100); min-width: 250px;
} }
</style> </style>

6
packages/builder/src/userInterface/SettingsView.svelte

@ -11,14 +11,14 @@ let addNewLib = "";
let addNewStylesheet = ""; let addNewStylesheet = "";
let addComponentError = ""; let addComponentError = "";
let modalElement; let modalElement;
let allComponents; let components;
store.subscribe(s => { store.subscribe(s => {
allComponents = s.allComponents; components = s.components;
}) })
const removeLibrary = lib => { const removeLibrary = lib => {
const dependencies = libraryDependencies(allComponents, lib); const dependencies = libraryDependencies(components, lib);
if(dependencies.length > 0) return; if(dependencies.length > 0) return;
store.removeComponentLibrary(lib); store.removeComponentLibrary(lib);
} }

31
packages/builder/src/userInterface/UserInterfaceRoot.svelte

@ -2,7 +2,6 @@
import ComponentsHierarchy from "./ComponentsHierarchy.svelte"; import ComponentsHierarchy from "./ComponentsHierarchy.svelte";
import PagesList from "./PagesList.svelte" import PagesList from "./PagesList.svelte"
import EditComponent from "./EditComponent.svelte";
import { store } from "../builderStore"; import { store } from "../builderStore";
import getIcon from "../common/icon"; import getIcon from "../common/icon";
import { isComponent } from "./pagesParsing/searchComponents"; import { isComponent } from "./pagesParsing/searchComponents";
@ -12,6 +11,7 @@ import NewComponent from "./NewComponent.svelte";
import CurrentItemPreview from "./CurrentItemPreview.svelte"; import CurrentItemPreview from "./CurrentItemPreview.svelte";
import SettingsView from "./SettingsView.svelte"; import SettingsView from "./SettingsView.svelte";
import PageView from "./PageView.svelte"; import PageView from "./PageView.svelte";
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte";
let newComponentPicker; let newComponentPicker;
const newComponent = () => { const newComponent = () => {
@ -32,7 +32,7 @@ const settings = () => {
<div class="components-list-container"> <div class="components-list-container">
<div class="nav-group-header"> <div class="nav-group-header">
<div>{@html getIcon("sidebar","18")}</div> <div>{@html getIcon("sidebar","18")}</div>
<span class="components-nav-header">Components</span> <span class="components-nav-header">Screens</span>
<div> <div>
<IconButton icon="settings" <IconButton icon="settings"
size="14px" size="14px"
@ -42,7 +42,7 @@ const settings = () => {
</div> </div>
</div> </div>
<div class="nav-items-container"> <div class="nav-items-container">
<ComponentsHierarchy components={$store.derivedComponents}/> <ComponentsHierarchy components={$store.screens}/>
</div> </div>
</div> </div>
@ -58,17 +58,17 @@ const settings = () => {
</div> </div>
<div> <div class="preview-pane">
{#if $store.currentFrontEndType === "component"} {#if $store.currentFrontEndType === "screen"}
<CurrentItemPreview /> <CurrentItemPreview />
{:else if $store.currentFrontEndType === "page"} {:else if $store.currentFrontEndType === "page"}
<PageView /> <PageView />
{/if} {/if}
</div> </div>
{#if $store.currentFrontEndType === "component"} {#if $store.currentFrontEndType === "screen"}
<div class="properties-pane"> <div class="components-pane">
<EditComponent /> <ComponentsPaneSwitcher />
</div> </div>
{/if} {/if}
@ -83,22 +83,25 @@ const settings = () => {
.root { .root {
display: grid; display: grid;
grid-template-columns: [uiNav] 250px [preview] auto [properties] 300px; grid-template-columns: 250px 1fr 300px;
height: 100%; height: 100%;
width: 100%; width: 100%;
overflow-y: auto;
} }
.ui-nav { .ui-nav {
grid-column-start: uiNav; grid-column: 1;
background-color: var(--secondary5); background-color: var(--secondary5);
height: 100%; height: 100%;
} }
.properties-pane { .preview-pane {
grid-column-start: properties; grid-column: 2;
}
.components-pane {
grid-column: 3;
background-color: var(--secondary5); background-color: var(--secondary5);
height: 100%; min-height: 0px;
overflow-y: hidden; overflow-y: hidden;
} }

65
packages/builder/src/userInterface/pagesParsing/buildPropsHierarchy.js

@ -2,51 +2,47 @@ import {
getComponentInfo, createProps, getInstanceProps getComponentInfo, createProps, getInstanceProps
} from "./createProps"; } from "./createProps";
export const buildPropsHierarchy = (allComponents, baseComponent) => { export const buildPropsHierarchy = (components, screens, baseComponent) => {
const buildProps = (componentName, propsDefinition, derivedFromProps) => { const allComponents = [...components, ...screens];
const {props} = createProps(componentName, propsDefinition, derivedFromProps); const buildProps = (componentDefinition, derivedFromProps) => {
props._component = componentName;
const {props} = createProps(componentDefinition, derivedFromProps);
const propsDefinition = componentDefinition.props;
props._component = componentDefinition.name;
for(let propName in props) { for(let propName in props) {
if(propName === "_component") continue; if(propName === "_component") continue;
const propDef = propsDefinition[propName]; const propDef = propsDefinition[propName];
if(!propDef) continue; if(!propDef) continue;
if(propDef.type === "component") { if(propName === "_children") {
const subComponentProps = props[propName]; const childrenProps = props[propName];
if(!subComponentProps._component) continue; if(!childrenProps
|| childrenProps.length === 0) {
const propComponentInfo = getComponentInfo( continue;
allComponents, subComponentProps._component); }
const subComponentInstanceProps = getInstanceProps(
propComponentInfo,
subComponentProps
);
props[propName] = buildProps(
propComponentInfo.rootComponent.name,
propComponentInfo.propsDefinition,
subComponentInstanceProps);
} else if(propDef.type === "array") { props[propName] = [];
const propsArray = props[propName];
const newPropsArray = []; for(let child of childrenProps) {
let index = 0; const propComponentInfo = getComponentInfo(
for(let element of propsArray) { allComponents, child._component);
newPropsArray.push(
const subComponentInstanceProps = getInstanceProps(
propComponentInfo,
child
);
props[propName].push(
buildProps( buildProps(
`${propName}#array_element#`, propComponentInfo.rootComponent.name,
propDef.elementDefinition, propComponentInfo.propsDefinition,
element)); subComponentInstanceProps));
index++;
} }
}
props[propName] = newPropsArray;
}
} }
return props; return props;
@ -58,8 +54,7 @@ export const buildPropsHierarchy = (allComponents, baseComponent) => {
const baseComponentInfo = getComponentInfo(allComponents, baseComponent); const baseComponentInfo = getComponentInfo(allComponents, baseComponent);
return buildProps( return buildProps(
baseComponentInfo.rootComponent.name, baseComponentInfo.rootComponent,
baseComponentInfo.propsDefinition,
baseComponentInfo.fullProps); baseComponentInfo.fullProps);
} }

166
packages/builder/src/userInterface/pagesParsing/createProps.js

@ -1,44 +1,12 @@
import { import {
isString, isString, isUndefined, find, keys, uniq,
isUndefined, some, filter, reduce, cloneDeep, includes,last
find,
keys,
uniq,
some,
filter,
reduce,
cloneDeep,
includes,
last
} from "lodash/fp"; } from "lodash/fp";
import { types, expandPropsDefinition } from "./types"; import { types, expandComponentDefinition } from "./types";
import { assign } from "lodash"; import { assign } from "lodash";
import { pipe } from "../../common/core"; import { pipe } from "../../common/core";
import { isRootComponent } from "./searchComponents"; import { isRootComponent } from "./searchComponents";
import { ensureShardNameIsInShardMap } from "../../../../core/src/indexing/sharding";
export const createPropDefinitionForDerived = (allComponents, componentName) => {
const {propDef, derivedProps} = getComponentInfo(allComponents, componentName);
const hasDerivedProp = k => pipe(derivedProps, [
keys,
uniq,
some(key => key === k)
]);
return pipe(propDef, [
keys,
filter(k => !hasDerivedProp(k)),
reduce((obj, k) => {
obj[k] = propDef[k];
return obj;
}, {}),
expandPropsDefinition
])
}
export const traverseForProps = getComponentInfo;
export const getInstanceProps = (componentInfo, props) => { export const getInstanceProps = (componentInfo, props) => {
const finalProps = cloneDeep(componentInfo.fullProps); const finalProps = cloneDeep(componentInfo.fullProps);
@ -50,89 +18,80 @@ export const getInstanceProps = (componentInfo, props) => {
return finalProps; return finalProps;
} }
export const getNewComponentInfo = (allComponents, inherits) => { export const getNewComponentInfo = (components, rootComponent, name) => {
const parentcomponent = find(c => c.name === inherits)(allComponents);
const component = { const component = {
name:"", name: name || "",
description:"", description:"",
inherits, props:{
props:{}, _component: rootComponent
tags:parentcomponent.tags }
}; };
return getComponentInfo( return getComponentInfo(
allComponents, components,
inherits, component);
[component],
{});
} }
export const getComponentInfo = (allComponents, comp, stack=[], subComponentProps=null) => { export const getScreenInfo = (components, screen) => {
const component = isString(comp)
? find(c => c.name === comp)(allComponents)
: comp;
const cname = isString(comp) ? comp : comp.name;
if(isRootComponent(component)) {
subComponentProps = subComponentProps||{};
const p = createProps(cname, component.props, subComponentProps);
const rootProps = createProps(cname, component.props);
const inheritedProps = [];
const targetComponent = stack.length > 0
? last(stack)
: component;
if(stack.length > 0) {
for(let prop in subComponentProps) {
const hasProp = pipe(targetComponent.props, [
keys,
includes(prop)]);
if(!hasProp)
inheritedProps.push(prop);
}
}
const unsetProps = pipe(p.props, [
keys,
filter(k => !includes(k)(keys(subComponentProps)) && k !== "_component")
]);
const fullProps = cloneDeep(p.props);
fullProps._component = targetComponent.name;
return ({
propsDefinition:expandPropsDefinition(component.props),
inheritedProps,
rootDefaultProps: rootProps.props,
unsetProps,
fullProps: fullProps,
errors: p.errors,
component: targetComponent,
rootComponent: component
});
}
return getComponentInfo( return getComponentInfo(
allComponents, components,
component.inherits, screen);
[component, ...stack],
{...component.props, ...subComponentProps});
} }
export const createProps = (componentName, propsDefinition, derivedFromProps) => { export const getComponentInfo = (components, comp) => {
const targetComponent = isString(comp)
? find(c => c.name === comp)(components)
: comp;
let component;
let subComponent;
if(isRootComponent(targetComponent)) {
component = targetComponent;
} else {
subComponent = targetComponent;
component = find(c => c.name === subComponent.props._component)(
components);
}
const subComponentProps = subComponent ? subComponent.props : {};
const p = createProps(component, subComponentProps);
const rootProps = createProps(component);
const unsetProps = pipe(p.props, [
keys,
filter(k => !includes(k)(keys(subComponentProps)) && k !== "_component")
]);
const fullProps = cloneDeep(p.props);
fullProps._component = targetComponent.name;
return ({
propsDefinition:expandComponentDefinition(component),
rootDefaultProps: rootProps.props,
unsetProps,
fullProps: fullProps,
errors: p.errors,
component: targetComponent,
rootComponent: component
});
}
export const createProps = (componentDefinition, derivedFromProps) => {
const error = (propName, error) => const error = (propName, error) =>
errors.push({propName, error}); errors.push({propName, error});
const props = { const props = {
_component: componentName _component: componentDefinition.name
}; };
const errors = []; const errors = [];
if(!componentName) if(!componentDefinition.name)
error("_component", "Component name not supplied"); error("_component", "Component name not supplied");
for(let propDef in propsDefinition) { const propsDef = componentDefinition.props;
const parsedPropDef = parsePropDef(propsDefinition[propDef]); for(let propDef in propsDef) {
const parsedPropDef = parsePropDef(propsDef[propDef]);
if(parsedPropDef.error) if(parsedPropDef.error)
error(propDef, parsedPropDef.error); error(propDef, parsedPropDef.error);
else else
@ -143,15 +102,16 @@ export const createProps = (componentName, propsDefinition, derivedFromProps) =>
assign(props, derivedFromProps); assign(props, derivedFromProps);
} }
if(componentDefinition.children !== false
&& isUndefined(props._children)) {
props._children = [];
}
return ({ return ({
props, errors props, errors
}); });
} }
export const createArrayElementProps = (arrayPropName, elementDefinition) =>
createProps(
`#${arrayPropName}#array_element`,
elementDefinition);
const parsePropDef = propDef => { const parsePropDef = propDef => {
const error = message => ({error:message, propDef}); const error = message => ({error:message, propDef});

17
packages/builder/src/userInterface/pagesParsing/findDependencies.js

@ -5,7 +5,7 @@ import {
} from "lodash/fp"; } from "lodash/fp";
import { isRootComponent } from "./searchComponents"; import { isRootComponent } from "./searchComponents";
export const libraryDependencies = (allComponents, lib) => { export const libraryDependencies = (components, lib) => {
const componentDependsOnLibrary = comp => { const componentDependsOnLibrary = comp => {
if(isRootComponent(comp)) { if(isRootComponent(comp)) {
@ -13,21 +13,24 @@ export const libraryDependencies = (allComponents, lib) => {
return (libName === lib); return (libName === lib);
} }
return componentDependsOnLibrary( return componentDependsOnLibrary(
find(c => c.name === comp.inherits)(allComponents) find(c => c.name === comp.props._component)(
components)
); );
} }
return filter(c => !isRootComponent(c) return filter(c => !isRootComponent(c)
&& componentDependsOnLibrary(c))( && componentDependsOnLibrary(c))(
allComponents components
); );
} }
export const componentDependencies = (pages, allComponents, dependsOn) => { export const componentDependencies = (pages, screens, components, dependsOn) => {
const allComponents = [
...cloneDeep(components),
...cloneDeep(screens)];
pages = cloneDeep(pages); pages = cloneDeep(pages);
allComponents = cloneDeep(allComponents);
const dependantComponents = []; const dependantComponents = [];
const dependantPages = []; const dependantPages = [];
@ -63,7 +66,7 @@ export const componentDependencies = (pages, allComponents, dependsOn) => {
continue; continue;
} }
if(component.inherits === dependsOn.name) { if(component.props._component === dependsOn.name) {
dependantComponents.push(component); dependantComponents.push(component);
continue; continue;
} }

6
packages/builder/src/userInterface/pagesParsing/getRootComponent.js

@ -1,10 +1,10 @@
import { isRootComponent } from "./searchComponents"; import { isRootComponent } from "./searchComponents";
import { find } from "lodash/fp"; import { find } from "lodash/fp";
export const getRootComponent = (componentName, allComponents) => { export const getRootComponent = (componentName, components) => {
const component = find(c => c.name === componentName)(allComponents); const component = find(c => c.name === componentName)(components);
if(isRootComponent(component)) return component; if(isRootComponent(component)) return component;
return getRootComponent(component.inherits, allComponents); return getRootComponent(component.props._component, components);
} }

34
packages/builder/src/userInterface/pagesParsing/renameComponent.js → packages/builder/src/userInterface/pagesParsing/renameScreen.js

@ -2,18 +2,18 @@ import {
isPlainObject, isArray, cloneDeep isPlainObject, isArray, cloneDeep
} from "lodash/fp"; } from "lodash/fp";
import { import {
isRootComponent, getExactComponent getExactComponent
} from "./searchComponents"; } from "./searchComponents";
export const rename = (pages, allComponents, oldname, newname) => { export const rename = (pages, screens, oldname, newname) => {
pages = cloneDeep(pages); pages = cloneDeep(pages);
allComponents = cloneDeep(allComponents); screens = cloneDeep(screens);
const changedComponents = []; const changedScreens = [];
const existingWithNewName = getExactComponent(allComponents, newname); const existingWithNewName = getExactComponent(screens, newname);
if(existingWithNewName) return { if(existingWithNewName) return {
allComponents, pages, error: "Component by that name already exists" components: screens, pages, error: "Component by that name already exists"
}; };
const traverseProps = (props) => { const traverseProps = (props) => {
@ -38,28 +38,24 @@ export const rename = (pages, allComponents, oldname, newname) => {
} }
for(let component of allComponents) { for(let screen of screens) {
if(isRootComponent(component)) {
continue;
}
let hasEdited = false; let hasEdited = false;
if(component.name === oldname) { if(screen.name === oldname) {
component.name = newname; screen.name = newname;
hasEdited = true; hasEdited = true;
} }
if(component.inherits === oldname) { if(screen.props._component === oldname) {
component.inherits = newname; screen.props._component = newname;
hasEdited = true; hasEdited = true;
} }
hasEdited = traverseProps(component.props) || hasEdited; hasEdited = traverseProps(screen.props) || hasEdited;
if(hasEdited && component.name !== newname) if(hasEdited && screen.name !== newname)
changedComponents.push(component.name); changedScreens.push(screen.name);
} }
for(let pageName in pages) { for(let pageName in pages) {
@ -69,7 +65,7 @@ export const rename = (pages, allComponents, oldname, newname) => {
} }
} }
return {allComponents, pages, changedComponents}; return {screens, pages, changedScreens};
} }

23
packages/builder/src/userInterface/pagesParsing/searchComponents.js

@ -11,14 +11,15 @@ import {
const normalString = s => (s||"").trim().toLowerCase(); const normalString = s => (s||"").trim().toLowerCase();
export const isRootComponent = c => isComponent(c) && isUndefined(c.inherits); export const isRootComponent = c =>
isComponent(c) && isUndefined(c.props._component);
export const isComponent = c => { export const isComponent = c => {
const hasProp = (n) => !isUndefined(c[n]); const hasProp = (n) => !isUndefined(c[n]);
return hasProp("name") && hasProp("props"); return hasProp("name") && hasProp("props");
} }
export const searchAllComponents = (allComponents, phrase) => { export const searchAllComponents = (components, phrase) => {
const hasPhrase = (...vals) => const hasPhrase = (...vals) =>
pipe(vals, [ pipe(vals, [
@ -31,35 +32,35 @@ export const searchAllComponents = (allComponents, phrase) => {
if(isRootComponent(c)) return false; if(isRootComponent(c)) return false;
const parent = getExactComponent( const parent = getExactComponent(
allComponents, components,
c.inherits); c.props._component);
return componentMatches(parent); return componentMatches(parent);
} }
return filter(componentMatches)(allComponents); return filter(componentMatches)(components);
} }
export const getExactComponent = (allComponents, name) => { export const getExactComponent = (components, name) => {
const stringEquals = (s1, s2) => const stringEquals = (s1, s2) =>
normalString(s1) === normalString(s2); normalString(s1) === normalString(s2);
return pipe(allComponents,[ return pipe(components,[
find(c => stringEquals(c.name, name)) find(c => stringEquals(c.name, name))
]); ]);
} }
export const getAncestorProps = (allComponents, name, found=[]) => { export const getAncestorProps = (components, name, found=[]) => {
const thisComponent = getExactComponent( const thisComponent = getExactComponent(
allComponents, name); components, name);
if(isRootComponent(thisComponent)) if(isRootComponent(thisComponent))
return [thisComponent.props, ...found]; return [thisComponent.props, ...found];
return getAncestorProps( return getAncestorProps(
allComponents, components,
thisComponent.inherits, thisComponent.props._component,
[{...thisComponent.props}, [{...thisComponent.props},
...found]); ...found]);

29
packages/builder/src/userInterface/pagesParsing/types.js

@ -5,7 +5,8 @@ import {
isArray, isArray,
isObjectLike, isObjectLike,
isPlainObject, isPlainObject,
every every,
isUndefined
} from "lodash/fp"; } from "lodash/fp";
import { import {
@ -20,8 +21,7 @@ const defaultDef = typeName => () => ({
type: typeName, type: typeName,
required:false, required:false,
default:types[typeName].default(), default:types[typeName].default(),
options: typeName === "options" ? [] : undefined, options: typeName === "options" ? [] : undefined
elementDefinition: typeName === "array" ? {} : undefined
}); });
const propType = (defaultValue, isOfType, defaultDefinition) => ({ const propType = (defaultValue, isOfType, defaultDefinition) => ({
@ -42,18 +42,25 @@ const expandSingleProp = propDef => {
} }
} }
if(p.type === "array") {
p.elementDefinition = expandPropsDefinition(p.elementDefinition);
}
return p; return p;
} }
export const expandPropsDefinition = propsDefinition => { export const expandComponentDefinition = componentDefinition => {
const expandedProps = {}; const expandedProps = {};
for(let p in propsDefinition) { const expandedComponent = {...componentDefinition};
expandedProps[p] = expandSingleProp(propsDefinition[p]);
for(let p in componentDefinition.props) {
expandedProps[p] = expandSingleProp(
componentDefinition.props[p]);
}
expandedComponent.props = expandedProps;
if(expandedComponent.children !== false) {
expandedComponent.children = true;
} }
return expandedProps;
return expandedComponent;
} }
const isComponent = isObjectLike; const isComponent = isObjectLike;
@ -75,9 +82,7 @@ export const types = {
string: propType(() => "", isString, defaultDef("string")), string: propType(() => "", isString, defaultDef("string")),
bool: propType(() => false, isBoolean, defaultDef("bool")), bool: propType(() => false, isBoolean, defaultDef("bool")),
number: propType(() => 0, isNumber, defaultDef("number")), number: propType(() => 0, isNumber, defaultDef("number")),
array: propType(() => [], isArray, defaultDef("array")),
options: propType(() => "", isString, defaultDef("options")), options: propType(() => "", isString, defaultDef("options")),
component: propType(() => ({_component:""}), isComponent, defaultDef("component")),
asset: propType(() => "", isString, defaultDef("asset")), asset: propType(() => "", isString, defaultDef("asset")),
event: propType(() => [], isEventList, defaultDef("event")), event: propType(() => [], isEventList, defaultDef("event")),
state: propType(() => emptyState(), isBound, defaultDef("state")) state: propType(() => emptyState(), isBound, defaultDef("state"))

20
packages/builder/src/userInterface/pagesParsing/validatePages.js

@ -14,9 +14,9 @@ export const validatePage = (page, getComponent) => {
const errors = []; const errors = [];
const error = message => errors.push(message); const error = message => errors.push(message);
const noIndex = !page.index || !page.index._component; const noIndex = !page.index;
if(noIndex) { if(noIndex) {
error("Must choose a component for your index.html"); error("Page does not define an index member");
} }
if(!page.appBody if(!page.appBody
@ -25,14 +25,18 @@ export const validatePage = (page, getComponent) => {
error("App body must be set toa valid JSON file"); error("App body must be set toa valid JSON file");
} }
/* Commenting this for now
* index is a load of static members just now, but maybe useful
for pageLayout props (which is just a pipe dream at time of writing)
const indexHtmlErrors = noIndex const indexHtmlErrors = noIndex
? [] ? []
: pipe( : pipe(
recursivelyValidate(page.index, getComponent), [ recursivelyValidate(page.index, getComponent), [
map(e => `Index.html: ${e.error}`) map(e => `Index.html: ${e.error}`)
]); ]);
*/
return [...errors, ...indexHtmlErrors]; return errors;
} }
export const validatePages = (pages, getComponent) => { export const validatePages = (pages, getComponent) => {

133
packages/builder/src/userInterface/pagesParsing/validateProps.js

@ -23,14 +23,6 @@ const makeError = (errors, propName, stack) => (message) =>
export const recursivelyValidate = (rootProps, getComponent, stack=[]) => { export const recursivelyValidate = (rootProps, getComponent, stack=[]) => {
const getComponentPropsDefinition = componentName => {
if(componentName.includes(":")) {
const [parentComponent, arrayProp] = componentName.split(":");
return getComponent(parentComponent)[arrayProp].elementDefinition;
}
return getComponent(componentName);
}
if(!rootProps._component) { if(!rootProps._component) {
const errs = []; const errs = [];
makeError(errs, "_component", stack)("Component is not set"); makeError(errs, "_component", stack)("Component is not set");
@ -38,79 +30,56 @@ export const recursivelyValidate = (rootProps, getComponent, stack=[]) => {
// this would break everything else anyway // this would break everything else anyway
} }
const propsDef = getComponentPropsDefinition( const componentDef = getComponent(
rootProps._component); rootProps._component);
const getPropsDefArray = (def) => pipe(def, [
keys,
map(k => def[k].name
? expandPropDef(def[k])
: ({
...expandPropDef(def[k]),
name:k }))
]);
const propsDefArray = getPropsDefArray(propsDef);
const errors = validateProps( const errors = validateProps(
propsDef, componentDef,
rootProps, rootProps,
stack, stack,
true); true);
const validateChildren = (_defArray, _props, _stack) => pipe(_defArray, [ const validateChildren = (_props, _stack) =>
filter(d => d.type === "component"), !_props._children
map(d => recursivelyValidate( ? []
_props[d.name], : pipe(_props._children, [
getComponentPropsDefinition, map(child => recursivelyValidate(
[..._stack, d.name])), child,
flatten getComponent,
]); [..._stack, _props._children.indexOf(child)]))
]);
const childErrors = validateChildren( const childErrors = validateChildren(
propsDefArray, rootProps, stack); rootProps, stack);
const childArrayErrors = pipe(propsDefArray, [
filter(d => d.type === "array"),
map(d => pipe(rootProps[d.name], [
map(elementProps => pipe(d.elementDefinition, [
getPropsDefArray,
arr => validateChildren(
arr,
elementProps,
[...stack,
`${d.name}[${indexOf(elementProps)(rootProps[d.name])}]`])
]))
]))
]);
return flattenDeep([errors, ...childErrors, ...childArrayErrors]); return flattenDeep([errors, ...childErrors]);
} }
const expandPropDef = propDef => { const expandPropDef = propDef =>
const p = isString(propDef) isString(propDef)
? types[propDef].defaultDefinition() ? types[propDef].defaultDefinition()
: propDef; : propDef;
if(p.type === "array" && isString(p.elementDefinition)) {
p.elementDefinition = types[p.elementDefinition].defaultDefinition()
}
return p;
}
export const validateProps = (propsDefinition, props, stack=[], isFinal=true, isArrayElement=false) => {
export const validateProps = (componentDefinition, props, stack=[], isFinal=true) => {
const errors = []; const errors = [];
if(isFinal && !props._component && !isArrayElement) { if(isFinal && !props._component) {
makeError(errors, "_component", stack)("Component is not set"); makeError(errors, "_component", stack)("Component is not set");
return errors; return errors;
// this would break everything else anyway // this would break everything else anyway
} }
for(let propDefName in propsDefinition) { const propsDefinition = componentDefinition.props;
for(let propDefName in props) {
if(propDefName === "_component") continue; if(propDefName === "_component") continue;
if(propDefName === "_children") continue;
if(propDefName === "_layout") continue;
const propDef = expandPropDef(propsDefinition[propDefName]); const propDef = expandPropDef(propsDefinition[propDefName]);
@ -129,9 +98,7 @@ export const validateProps = (propsDefinition, props, stack=[], isFinal=true, is
} }
if(isBinding(propValue)) { if(isBinding(propValue)) {
if(propDef.type === "array" if(propDef.type === "event") {
|| propDef.type === "component"
|| propDef.type === "event") {
error(`Cannot apply binding to type ${propDef.type}`); error(`Cannot apply binding to type ${propDef.type}`);
continue; continue;
} }
@ -141,23 +108,7 @@ export const validateProps = (propsDefinition, props, stack=[], isFinal=true, is
continue; continue;
} }
if(propDef.type === "array") {
let index = 0;
for(let arrayItem of propValue) {
const arrayErrs = validateProps(
propDef.elementDefinition,
arrayItem,
[...stack, `${propDefName}[${index}]`],
isFinal,
true
)
for(let arrErr of arrayErrs) {
errors.push(arrErr);
}
index++;
}
}
if(propDef.type === "options" if(propDef.type === "options"
&& propValue && propValue
&& !isBinding(propValue) && !isBinding(propValue)
@ -170,33 +121,15 @@ export const validateProps = (propsDefinition, props, stack=[], isFinal=true, is
return errors; return errors;
} }
export const validatePropsDefinition = (propsDefinition) => { export const validateComponentDefinition = (componentDefinition) => {
const { errors } = createProps("dummy_component_name", propsDefinition); const { errors } = createProps(componentDefinition);
const propDefinitions = expandPropDef(componentDefinition.props);
// arrar props without elementDefinition pipe(propDefinitions, [
pipe(propsDefinition, [
keys,
map(k => ({
propDef:propsDefinition[k],
propName:k
})),
filter(d => d.propDef.type === "array" && !d.propDef.elementDefinition),
each(d => makeError(errors, d.propName)(`${d.propName} does not have a definition for it's item props`))
]);
const arrayPropValidationErrors = pipe(propsDefinition, [
keys,
map(k => propsDefinition[k]),
filter(d => d.type === "array" && d.elementDefinition),
map(d => validatePropsDefinition(d.elementDefinition)),
flatten
]);
pipe(propsDefinition, [
keys, keys,
map(k => ({ map(k => ({
propDef:propsDefinition[k], propDef:propDefinitions[k],
propName:k propName:k
})), })),
filter(d => d.propDef.type === "options" filter(d => d.propDef.type === "options"
@ -204,7 +137,7 @@ export const validatePropsDefinition = (propsDefinition) => {
each(d => makeError(errors, d.propName)(`${d.propName} does not have any options`)) each(d => makeError(errors, d.propName)(`${d.propName} does not have any options`))
]); ]);
return [...errors, ...arrayPropValidationErrors] return errors;
} }

32
packages/builder/tests/buildPropsHierarchy.spec.js

@ -1,4 +1,4 @@
import { allComponents } from "./testData"; import { componentsAndScreens } from "./testData";
import { import {
find find
} from "lodash/fp"; } from "lodash/fp";
@ -7,43 +7,25 @@ import { buildPropsHierarchy } from "../src/userInterface/pagesParsing/buildProp
describe("buildPropsHierarchy", () => { describe("buildPropsHierarchy", () => {
it("should build a complex component with arrays and components", () => { it("should build a complex component children", () => {
const components = allComponents(); const {components, screens} = componentsAndScreens();
const allprops = buildPropsHierarchy( const allprops = buildPropsHierarchy(
components, "ButtonGroup"); components, screens, "ButtonGroup");
expect(allprops._component).toEqual("budibase-components/div"); expect(allprops._component).toEqual("budibase-components/div");
const primaryButtonProps = () => ({ const primaryButtonProps = () => ({
_component: "budibase-components/Button", _component: "budibase-components/Button"
css:"btn-primary",
content: {_component:""},
contentText: "",
size:""
}); });
const headerButton = primaryButtonProps();
expect(allprops.header).toEqual(headerButton);
const button1 = primaryButtonProps(); const button1 = primaryButtonProps();
button1.contentText = "Button 1"; button1.contentText = "Button 1";
expect(allprops.children[0]).toEqual({ expect(allprops._children[0]).toEqual(button1);
_component: "children#array_element#",
control: button1
});
const button2 = primaryButtonProps(); const button2 = primaryButtonProps();
button2.contentText = "Button 2"; button2.contentText = "Button 2";
expect(allprops.children[1]).toEqual({ expect(allprops._children[1]).toEqual(button2)
_component: "children#array_element#",
control: button2
})
}); });
}); });

32
packages/builder/tests/componentDependencies.spec.js

@ -1,12 +1,7 @@
import {
searchAllComponents,
getExactComponent,
getAncestorProps
} from "../src/userInterface/pagesParsing/searchComponents";
import { import {
componentDependencies componentDependencies
} from "../src/userInterface/pagesParsing/findDependencies"; } from "../src/userInterface/pagesParsing/findDependencies";
import { allComponents } from "./testData"; import { componentsAndScreens } from "./testData";
import { some, find } from "lodash/fp" import { some, find } from "lodash/fp"
describe("component dependencies", () => { describe("component dependencies", () => {
@ -19,37 +14,29 @@ describe("component dependencies", () => {
it("should include component that inheirts", () => { it("should include component that inheirts", () => {
const components = allComponents(); const {components, screens} = componentsAndScreens();
const result = componentDependencies( const result = componentDependencies(
{}, components, get(components, "budibase-components/TextBox")); {}, screens, components,
get([...components, ...screens], "budibase-components/TextBox"));
expect(contains(result.dependantComponents, "common/SmallTextbox")).toBe(true); expect(contains(result.dependantComponents, "common/SmallTextbox")).toBe(true);
}); });
it("should include component that nests", () => { it("should include component that nests", () => {
const components = allComponents(); const {components, screens} = componentsAndScreens();
const result = componentDependencies( const result = componentDependencies(
{}, components, get(components, "PrimaryButton")); {}, screens, components,
get([...components, ...screens], "budibase-components/Button"));
expect(contains(result.dependantComponents, "ButtonGroup")).toBe(true); expect(contains(result.dependantComponents, "ButtonGroup")).toBe(true);
}); });
it("shouldinclude component that nests inside arrays", () => {
const components = allComponents();
const result = componentDependencies(
{}, components, get(components, "common/PasswordBox"));
expect(contains(result.dependantComponents, "ButtonGroup")).toBe(true);
});
it("should include components n page apbody", () => { it("should include components n page apbody", () => {
const components = allComponents(); const {components, screens} = componentsAndScreens();
const pages = { const pages = {
main: { main: {
appBody: "PrimaryButton" appBody: "PrimaryButton"
@ -57,7 +44,8 @@ describe("component dependencies", () => {
}; };
const result = componentDependencies( const result = componentDependencies(
pages, components, get(components, "PrimaryButton")); pages, screens, components,
get([...components, ...screens], "PrimaryButton"));
expect(result.dependantPages).toEqual(["main"]); expect(result.dependantPages).toEqual(["main"]);
}); });

165
packages/builder/tests/createDefaultProps.spec.js

@ -10,46 +10,45 @@ import {
describe("createDefaultProps", () => { describe("createDefaultProps", () => {
it("should create a object with single string value, when default string field set", () => { const getcomponent = () => ({
const propDef = { name:"some_component",
props: {
fieldName: {type:"string", default:"something"} fieldName: {type:"string", default:"something"}
}; }
});
const { props, errors } = createProps("some_component",propDef); it("should create a object with single string value, when default string field set", () => {
const { props, errors } = createProps(getcomponent());
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.fieldName).toBeDefined(); expect(props.fieldName).toBeDefined();
expect(props.fieldName).toBe("something"); expect(props.fieldName).toBe("something");
expect(keys(props).length).toBe(2); expect(keys(props).length).toBe(3);
}); });
it("should set component name", () => { it("should set component name", () => {
const propDef = {
fieldName: {type:"string", default:"something"}
};
const { props, errors } = createProps("some_component",propDef); const { props, errors } = createProps(getcomponent());
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props._component).toBe("some_component"); expect(props._component).toBe("some_component");
}); });
it("should return error when component name not supplied", () => { it("should return error when component name not supplied", () => {
const propDef = { const comp = getcomponent();
fieldName: {type:"string", default:"something"} comp.name = "";
};
const { errors } = createProps("",propDef); const { errors } = createProps(comp);
expect(errors.length).toEqual(1); expect(errors.length).toEqual(1);
}); });
it("should create a object with single blank string value, when no default", () => { it("should create a object with single blank string value, when no default", () => {
const propDef = { const comp = getcomponent();
fieldName: {type:"string"} comp.props.fieldName = {type:"string"};
};
const { props, errors } = createProps("some_component",propDef); const { props, errors } = createProps(comp);
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.fieldName).toBeDefined(); expect(props.fieldName).toBeDefined();
@ -57,11 +56,10 @@ describe("createDefaultProps", () => {
}); });
it("should create a object with single blank string value, when prop definition is 'string' ", () => { it("should create a object with single blank string value, when prop definition is 'string' ", () => {
const propDef = { const comp = getcomponent();
fieldName: "string" comp.props.fieldName = "string";
};
const { props, errors } = createProps("some_component",propDef); const { props, errors } = createProps(comp);
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.fieldName).toBeDefined(); expect(props.fieldName).toBeDefined();
@ -69,11 +67,10 @@ describe("createDefaultProps", () => {
}); });
it("should create a object with single fals value, when prop definition is 'bool' ", () => { it("should create a object with single fals value, when prop definition is 'bool' ", () => {
const propDef = { const comp = getcomponent();
isVisible: "bool" comp.props.isVisible = "bool";
};
const { props, errors } = createProps("some_component",propDef); const { props, errors } = createProps(comp);
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.isVisible).toBeDefined(); expect(props.isVisible).toBeDefined();
@ -81,35 +78,44 @@ describe("createDefaultProps", () => {
}); });
it("should create a object with single 0 value, when prop definition is 'number' ", () => { it("should create a object with single 0 value, when prop definition is 'number' ", () => {
const propDef = {
width: "number" const comp = getcomponent();
}; comp.props.width = "number";
const { props, errors } = createProps("some_component",propDef); const { props, errors } = createProps(comp);
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.width).toBeDefined(); expect(props.width).toBeDefined();
expect(props.width).toBe(0); expect(props.width).toBe(0);
}); });
it("should create a object with single empty array, when prop definition is 'array' ", () => { it("should create a object with empty _children array, when children===true ", () => {
const propDef = { const comp = getcomponent();
columns: "array" comp.children = true;
};
const { props, errors } = createProps("some_component",propDef); const { props, errors } = createProps(comp);
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.columns).toBeDefined(); expect(props._children).toBeDefined();
expect(props.columns).toEqual([]); expect(props._children).toEqual([]);
});
it("should create a object without _children array, when children===false ", () => {
const comp = getcomponent();
comp.children = false;
const { props, errors } = createProps(comp);
expect(errors).toEqual([]);
expect(props._children).not.toBeDefined();
}); });
it("should create a object with single empty array, when prop definition is 'event' ", () => { 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); const comp = getcomponent();
comp.props.onClick = "event";
const { props, errors } = createProps(comp);
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.onClick).toBeDefined(); expect(props.onClick).toBeDefined();
@ -117,52 +123,72 @@ describe("createDefaultProps", () => {
}); });
it("should create a object with empty state when prop def is 'state' ", () => { it("should create a object with empty state when prop def is 'state' ", () => {
const propDef = {
data: "state"
};
const { props, errors } = createProps("some_component",propDef); const comp = getcomponent();
comp.props.data = "state";
const { props, errors } = createProps(comp);
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.data[BB_STATE_BINDINGPATH]).toBeDefined(); expect(props.data[BB_STATE_BINDINGPATH]).toBeDefined();
expect(props.data[BB_STATE_BINDINGPATH]).toBe(""); expect(props.data[BB_STATE_BINDINGPATH]).toBe("");
}); });
it("should create a object with single empty component props, when prop definition is 'component' ", () => { it("should create a object children array when children == true ", () => {
const propDef = {
content: "component" const comp = getcomponent();
}; comp.children = true;
const { props, errors } = createProps("some_component",propDef); const { props, errors } = createProps(comp);
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.content).toBeDefined(); expect(props._children).toBeDefined();
expect(props.content).toEqual({_component:""}); expect(props._children).toEqual([]);
});
it("should create a _children array when children not defined ", () => {
const comp = getcomponent();
const { props, errors } = createProps(comp);
expect(errors).toEqual([]);
expect(props._children).toBeDefined();
expect(props._children).toEqual([]);
});
it("should not create _children array when children=false ", () => {
const comp = getcomponent();
comp.children = false;
const { props, errors } = createProps(comp);
expect(errors).toEqual([]);
expect(props._children).not.toBeDefined();
}); });
it("should create an object with multiple prop names", () => { it("should create an object with multiple prop names", () => {
const propDef = {
fieldName: "string", const comp = getcomponent();
fieldLength: { type: "number", default: 500 } comp.props.fieldName = "string";
}; comp.props.fieldLength = { type: "number", default: 500 };
const { props, errors } = createProps("some_component",propDef); const { props, errors } = createProps(comp);
expect(errors).toEqual([]); expect(errors).toEqual([]);
expect(props.fieldName).toBeDefined(); expect(props.fieldName).toBeDefined();
expect(props.fieldName).toBe(""); expect(props.fieldName).toBe("");
expect(props.fieldLength).toBeDefined(); expect(props.fieldLength).toBeDefined();
expect(props.fieldLength).toBe(500); expect(props.fieldLength).toBe(500);
expect(keys(props).length).toBe(3);
}) })
it("should return error when invalid type", () => { it("should return error when invalid type", () => {
const propDef = { const comp = getcomponent();
fieldName: "invalid type name", comp.props.fieldName = "invalid type name";
fieldLength: { type: "invalid type name "} comp.props.fieldLength = { type: "invalid type name "};
};
const { errors } = createProps("some_component",propDef); const { errors } = createProps(comp);
expect(errors.length).toBe(2); expect(errors.length).toBe(2);
expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy(); expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy();
@ -170,11 +196,12 @@ describe("createDefaultProps", () => {
}); });
it("should return error default value is not of declared type", () => { it("should return error default value is not of declared type", () => {
const propDef = {
fieldName: {type:"string", default: 1}
};
const { errors } = createProps("some_component",propDef); const comp = getcomponent();
comp.props.fieldName = {type:"string", default: 1}
const { errors } = createProps(comp);
expect(errors.length).toBe(1); expect(errors.length).toBe(1);
expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy(); expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy();
@ -186,11 +213,15 @@ describe("createDefaultProps", () => {
fieldLength: { type: "number", default: 500} fieldLength: { type: "number", default: 500}
}; };
const comp = getcomponent();
comp.props.fieldName = "string";
comp.props.fieldLength = { type: "number", default: 500};
const derivedFrom = { const derivedFrom = {
fieldName: "surname" fieldName: "surname"
}; };
const { props, errors } = createProps("some_component",propDef, derivedFrom); const { props, errors } = createProps(comp, derivedFrom);
expect(errors.length).toBe(0); expect(errors.length).toBe(0);
expect(props.fieldName).toBe("surname"); expect(props.fieldName).toBe("surname");

56
packages/builder/tests/expandPropDef.spec.js

@ -1,44 +1,50 @@
import { expandPropsDefinition } from "../src/userInterface/pagesParsing/types"; import { expandComponentDefinition } from "../src/userInterface/pagesParsing/types";
const propDef = { const componentDef = () => ({
label: "string", name: "comp",
width: {type:"number"}, props: {
color: {type:"string", required:true}, label: "string",
child: "component", width: {type:"number"},
navitems: { color: {type:"string", required:true},
type: "array",
elementDefinition: {
name: {type:"string"},
height: "number"
}
} }
} })
describe("expandPropDefintion", () => { describe("expandPropDefintion", () => {
it("should expand property defined as string, into default for that type", () => { it("should expand property defined as string, into default for that type", () => {
const result = expandPropsDefinition(propDef); const result = expandComponentDefinition(componentDef());
expect(result.label.type).toBe("string"); expect(result.props.label.type).toBe("string");
expect(result.label.required).toBe(false); expect(result.props.label.required).toBe(false);
}); });
it("should add members to property defined as object, when members do not exist", () => { it("should add members to property defined as object, when members do not exist", () => {
const result = expandPropsDefinition(propDef); const result = expandComponentDefinition(componentDef());
expect(result.width.required).toBe(false); expect(result.props.width.required).toBe(false);
}); });
it("should not override existing memebers", () => { it("should not override existing memebers", () => {
const result = expandPropsDefinition(propDef); const result = expandComponentDefinition(componentDef());
expect(result.color.required).toBe(true); expect(result.props.color.required).toBe(true);
}); });
it("should also expand out elementdefinition of array", () => { it("should set children=true when not included", () => {
const result = expandPropsDefinition(propDef); const result = expandComponentDefinition(componentDef());
expect(result.navitems.elementDefinition.height.type).toBe("number"); expect(result.children).toBe(true);
}) });
it("should not change children when specified", () => {
const c = componentDef();
c.children = false;
const result = expandComponentDefinition(c);
expect(result.children).toBe(false);
c.children = true;
const result2 = expandComponentDefinition(c);
expect(result2.children).toBe(true);
});
}) })

57
packages/builder/tests/getComponentInfo.spec.js

@ -1,11 +1,12 @@
import { import {
getInstanceProps, getInstanceProps,
getComponentInfo getScreenInfo ,
getComponentInfo
} from "../src/userInterface/pagesParsing/createProps"; } from "../src/userInterface/pagesParsing/createProps";
import { import {
keys, some keys, some, find
} from "lodash/fp"; } from "lodash/fp";
import { allComponents } from "./testData"; import { componentsAndScreens } from "./testData";
@ -13,7 +14,7 @@ describe("getComponentInfo", () => {
it("should return default props for root component", () => { it("should return default props for root component", () => {
const result = getComponentInfo( const result = getComponentInfo(
allComponents(), componentsAndScreens().components,
"budibase-components/TextBox"); "budibase-components/TextBox");
expect(result.errors).toEqual([]); expect(result.errors).toEqual([]);
@ -26,19 +27,10 @@ describe("getComponentInfo", () => {
}); });
}); });
it("should return no inherited for root component", () => {
const result = getComponentInfo(
allComponents(),
"budibase-components/TextBox");
expect(result.inheritedProps).toEqual([]);
});
it("getInstanceProps should set supplied props on top of default props", () => { it("getInstanceProps should set supplied props on top of default props", () => {
const result = getInstanceProps( const result = getInstanceProps(
getComponentInfo( getComponentInfo(
allComponents(), componentsAndScreens().components,
"budibase-components/TextBox"), "budibase-components/TextBox"),
{size:"small"}); {size:"small"});
@ -51,11 +43,18 @@ describe("getComponentInfo", () => {
}); });
}); });
});
it("should return correct props for derived component", () => { describe("getScreenInfo", () => {
const result = getComponentInfo(
allComponents(), const getScreen = (screens, name) =>
"common/SmallTextbox"); find(s => s.name === name)(screens);
it("should return correct props for screen", () => {
const {components, screens} = componentsAndScreens();
const result = getScreenInfo(
components,
getScreen(screens, "common/SmallTextbox"));
expect(result.errors).toEqual([]); expect(result.errors).toEqual([]);
expect(result.fullProps).toEqual({ expect(result.fullProps).toEqual({
@ -68,9 +67,10 @@ describe("getComponentInfo", () => {
}); });
it("should return correct props for twice derived component", () => { it("should return correct props for twice derived component", () => {
const result = getComponentInfo( const {components, screens} = componentsAndScreens();
allComponents(), const result = getScreenInfo(
"common/PasswordBox"); components,
getScreen(screens, "common/PasswordBox"));
expect(result.errors).toEqual([]); expect(result.errors).toEqual([]);
expect(result.fullProps).toEqual({ expect(result.fullProps).toEqual({
@ -82,19 +82,12 @@ describe("getComponentInfo", () => {
}); });
}); });
it("should list inheirted props as those that are defined in ancestor, derived components", () => {
const result = getComponentInfo(
allComponents(),
"common/PasswordBox");
// size is inherited from SmallTextbox
expect(result.inheritedProps).toEqual(["size"]);
});
it("should list unset props as those that are only defined in root", () => { it("should list unset props as those that are only defined in root", () => {
const result = getComponentInfo( const {components, screens} = componentsAndScreens();
allComponents(), const result = getScreenInfo(
"common/PasswordBox"); components,
getScreen(screens, "common/PasswordBox"));
expect(result.unsetProps).toEqual([ expect(result.unsetProps).toEqual([
"placeholder", "label"]); "placeholder", "label"]);

79
packages/builder/tests/renameComponent.spec.js

@ -1,79 +0,0 @@
import {
searchAllComponents,
getExactComponent,
getAncestorProps
} from "../src/userInterface/pagesParsing/searchComponents";
import {
rename
} from "../src/userInterface/pagesParsing/renameComponent";
import { allComponents } from "./testData";
describe("rename component", () => {
it("should change the name of the component, duh", () => {
const components = allComponents();
const result = rename({}, components, "PrimaryButton", "MainButton");
const newComponent = getExactComponent(result.allComponents, "MainButton");
const oldComponent = getExactComponent(result.allComponents, "Primary");
expect(oldComponent).toBeUndefined();
expect(newComponent).toBeDefined();
expect(newComponent.name).toBe("MainButton");
});
it("should chnge name on inherits", () => {
const components = allComponents();
const result = rename({}, components, "common/SmallTextbox", "common/TinyTextbox");
const passwordTextbox = getExactComponent(result.allComponents, "common/PasswordBox");
expect(passwordTextbox.inherits).toBe("common/TinyTextbox");
});
it("should change name of nested _components", () => {
const components = allComponents();
const result = rename({}, components, "PrimaryButton", "MainButton");
const buttonGroup = getExactComponent(result.allComponents, "ButtonGroup");
expect(buttonGroup.props.header._component).toBe("MainButton");
});
it("should change name of nested _components inside arrays", () => {
const components = allComponents();
const result = rename({}, components, "PrimaryButton", "MainButton");
const buttonGroup = getExactComponent(result.allComponents, "ButtonGroup");
expect(buttonGroup.props.children[0].control._component).toBe("MainButton");
});
it("should change name of page appBody", () => {
const components = allComponents();
const pages = {
main: {
appBody: "PrimaryButton"
}
};
const result = rename(pages, components, "PrimaryButton", "MainButton");
expect(result.pages.main.appBody).toBe("MainButton");
});
it("should return a list of changed components", () => {
const components = allComponents();
const result = rename({}, components, "PrimaryButton", "MainButton");
expect(result.changedComponents).toEqual(["ButtonGroup"]);
const result2 = rename({}, components, "common/SmallTextbox", "common/TinyTextBox");
expect(result2.changedComponents).toEqual(["common/PasswordBox"]);
});
})

61
packages/builder/tests/renameScreen.spec.js

@ -0,0 +1,61 @@
import {
getExactComponent
} from "../src/userInterface/pagesParsing/searchComponents";
import {
rename
} from "../src/userInterface/pagesParsing/renameScreen";
import { componentsAndScreens } from "./testData";
describe("rename component", () => {
it("should change the name of the component, duh", () => {
const {screens} = componentsAndScreens();
const result = rename({}, screens, "PrimaryButton", "MainButton");
const newComponent = getExactComponent(result.screens, "MainButton");
const oldComponent = getExactComponent(result.screens, "Primary");
expect(oldComponent).toBeUndefined();
expect(newComponent).toBeDefined();
expect(newComponent.name).toBe("MainButton");
});
/* this may be usefull if we have user defined components
it("should change name of nested _components", () => {
const {screens} = componentsAndScreens();
const result = rename({}, screens, "PrimaryButton", "MainButton");
const buttonGroup = getExactComponent(result.screens, "ButtonGroup");
expect(buttonGroup.props.header[0]._component).toBe("MainButton");
});
*/
it("should change name of page appBody", () => {
const {screens} = componentsAndScreens();
const pages = {
main: {
appBody: "PrimaryButton"
}
};
const result = rename(pages, screens, "PrimaryButton", "MainButton");
expect(result.pages.main.appBody).toBe("MainButton");
});
/* this may be usefull if we have user defined components
it("should return a list of changed components", () => {
const {screens} = componentsAndScreens();
const result = rename({}, screens, "PrimaryButton", "MainButton");
expect(result.changedScreens).toEqual(["ButtonGroup"]);
const result2 = rename({}, screens, "common/SmallTextbox", "common/TinyTextBox");
expect(result2.changedScreens).toEqual(["Field"]);
});
*/
})

51
packages/builder/tests/searchComponentsProps.spec.js

@ -3,42 +3,31 @@ import {
getExactComponent, getExactComponent,
getAncestorProps getAncestorProps
} from "../src/userInterface/pagesParsing/searchComponents"; } from "../src/userInterface/pagesParsing/searchComponents";
import { allComponents } from "./testData"; import { componentsAndScreens } from "./testData";
describe("searchAllComponents", () => { describe("searchAllComponents", () => {
it("should match derived component by name", () => { it("should match component by name", () => {
const results = searchAllComponents( const results = searchAllComponents(
allComponents(), componentsAndScreens().components,
"password" "Textbox"
); );
expect(results.length).toBe(1); expect(results.length).toBe(1);
expect(results[0].name).toBe("common/PasswordBox"); expect(results[0].name).toBe("budibase-components/TextBox");
}); });
it("should match derived component by tag", () => { it("should match component by tag", () => {
const results = searchAllComponents( const results = searchAllComponents(
allComponents(), componentsAndScreens().components,
"mask" "record"
); );
expect(results.length).toBe(1); expect(results.length).toBe(1);
expect(results[0].name).toBe("common/PasswordBox"); expect(results[0].name).toBe("budibase-components/RecordView");
});
it("should match component if ancestor matches", () => {
const results = searchAllComponents(
allComponents(),
"smalltext"
);
expect(results.length).toBe(2);
}); });
@ -46,8 +35,9 @@ describe("searchAllComponents", () => {
describe("getExactComponent", () => { describe("getExactComponent", () => {
it("should get component by name", () => { it("should get component by name", () => {
const {components, screens} = componentsAndScreens();
const result = getExactComponent( const result = getExactComponent(
allComponents(), [...components, ...screens],
"common/SmallTextbox" "common/SmallTextbox"
) )
@ -56,8 +46,9 @@ describe("getExactComponent", () => {
}); });
it("should return nothing when no result (should not fail)", () => { it("should return nothing when no result (should not fail)", () => {
const {components, screens} = componentsAndScreens();
const result = getExactComponent( const result = getExactComponent(
allComponents(), [...components, ...screens],
"bla/bla/bla" "bla/bla/bla"
) )
@ -71,29 +62,29 @@ describe("getAncestorProps", () => {
it("should return props of root component", () => { it("should return props of root component", () => {
const result = getAncestorProps( const result = getAncestorProps(
allComponents(), componentsAndScreens().components,
"budibase-components/TextBox" "budibase-components/TextBox"
); );
expect(result).toEqual([ expect(result).toEqual([
allComponents()[0].props componentsAndScreens().components[0].props
]); ]);
}); });
it("should return props of all ancestors and current component, in order", () => { it("should return props of inherited and current component, in order", () => {
const components = allComponents(); const {components, screens} = componentsAndScreens();
const allComponentsAndScreens = [...components, ...screens];
const result = getAncestorProps( const result = getAncestorProps(
components, allComponentsAndScreens,
"common/PasswordBox" "common/PasswordBox"
); );
expect(result).toEqual([ expect(result).toEqual([
components[0].props, allComponentsAndScreens[0].props,
{...components[4].props}, {...allComponentsAndScreens[5].props}
{...components[5].props}
]); ]);
}); });

184
packages/builder/tests/testData.js

@ -1,98 +1,102 @@
export const allComponents = () => ([ export const componentsAndScreens = () => ({
{ components: [
name: "budibase-components/TextBox", {
tags: ["Text", "input"], name: "budibase-components/TextBox",
props: { tags: ["Text", "input"],
size: {type:"options", options:["small", "medium", "large"]}, children: false,
isPassword: "bool", props: {
placeholder: "string", size: {type:"options", options:["small", "medium", "large"]},
label:"string" isPassword: "bool",
} placeholder: "string",
}, label:"string"
{
name: "budibase-components/Button",
tags: ["input"],
props: {
size: {type:"options", options:["small", "medium", "large"]},
css: "string",
content: "component",
contentText: "string"
}
},
{
name: "budibase-components/div",
tags: ["input"],
props: {
width: "number",
header : "component",
children: {
type:"array",
elementDefinition: {
control: "component"
}
} }
},
{
name: "budibase-components/Button",
tags: ["input"],
children: true,
props: {
size: {type:"options", options:["small", "medium", "large"]},
css: "string",
contentText: "string"
}
},
{
name: "budibase-components/div",
tags: ["input"],
props: {
width: "number",
}
},
{
name:"budibase-components/RecordView",
tags: ["record"],
props: {
data: "state"
}
} }
}, ],
{ screens: [
name:"budibase-components/RecordView", {
tags: ["record"], name: "common/SmallTextbox",
props: { props: {
data: "state" _component: "budibase-components/TextBox",
} size: "small"
}, }
{ },
inherits:"budibase-components/TextBox",
name: "common/SmallTextbox", {
props: { name: "common/PasswordBox",
size: "small" tags: ["mask"],
} props: {
}, _component: "budibase-components/TextBox",
isPassword: true,
{ size: "small"
inherits:"common/SmallTextbox", }
name: "common/PasswordBox", },
tags: ["mask"],
props: {
isPassword: true
}
},
{
inherits:"budibase-components/Button",
name:"PrimaryButton",
props: {
css:"btn-primary"
}
},
{
inherits:"budibase-components/div",
name:"ButtonGroup",
props: {
width: 100, {
header: { name:"PrimaryButton",
_component: "PrimaryButton" props: {
}, _component:"budibase-components/Button",
children: [ css:"btn-primary"
{ }
control: { },
_component: "PrimaryButton",
{
name:"ButtonGroup",
props: {
_component:"budibase-components/div",
width: 100,
_children: [
{
_component: "budibase-components/Button",
contentText: "Button 1" contentText: "Button 1"
} },
}, {
{ _component: "budibase-components/Button",
control: {
_component: "PrimaryButton",
contentText: "Button 2" contentText: "Button 2"
},
{
_component: "budibase-components/TextBox",
isPassword: true,
size: "small"
} }
}, ]
{ }
control: { },
_component: "common/PasswordBox",
{
name:"Field",
props: {
_component:"budibase-components/div",
_children:[
{
_component: "common/SmallTextbox"
} }
} ]
] }
} },
} ]
});
])

26
packages/builder/tests/validatePages.spec.js

@ -6,21 +6,13 @@ import {
const validPages = () => ({ const validPages = () => ({
"main" : { "main" : {
"index" : { "index" : {
"_component": "testIndexHtml", "title": "My Cool App"
"title": "My Cool App",
"customScripts": [
{"url": "MyCustomComponents.js"}
]
}, },
"appBody" : "./main.app.json" "appBody" : "./main.app.json"
}, },
"unauthenticated" : { "unauthenticated" : {
"index" : { "index" : {
"_component": "testIndexHtml", "title": "My Cool App - Login"
"title": "My Cool App - Login",
"customScripts": [
{"url": "MyCustomComponents.js"}
]
}, },
"appBody" : "./unauthenticated.app.json" "appBody" : "./unauthenticated.app.json"
}, },
@ -29,12 +21,9 @@ const validPages = () => ({
const getComponent = name => ({ const getComponent = name => ({
testIndexHtml : { testIndexHtml : {
title: "string", name: "testIndexHtml",
customScripts: { props: {
type:"array", title: "string",
elementDefinition: {
url: "string"
}
} }
} }
}[name]) }[name])
@ -53,11 +42,6 @@ describe("validate single page", () => {
let page = validPages().main; let page = validPages().main;
delete page.index; delete page.index;
expect(validatePage(page, getComponent).length).toEqual(1); expect(validatePage(page, getComponent).length).toEqual(1);
page.index = {title:"something"}; // no _component
const noComponent = validatePage(page, getComponent);
expect(noComponent.length).toEqual(1);
}); });
it("should return error when appBody is not set, or set incorrectly", () => { it("should return error when appBody is not set, or set incorrectly", () => {

244
packages/builder/tests/validateProps.spec.js

@ -1,5 +1,5 @@
import { import {
validatePropsDefinition, validateComponentDefinition,
validateProps, validateProps,
recursivelyValidate recursivelyValidate
} from "../src/userInterface/pagesParsing/validateProps"; } from "../src/userInterface/pagesParsing/validateProps";
@ -11,61 +11,22 @@ import {
// 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.
describe("validatePropsDefinition", () => { describe("validateComponentDefinition", () => {
it("should recursively validate array props and return no errors when valid", () => {
const propsDef = {
columns : {
type: "array",
elementDefinition: {
width: "number",
units: {
type: "string",
default: "px"
}
}
}
}
const errors = validatePropsDefinition(propsDef);
expect(errors).toEqual([]);
});
it("should recursively validate array props and return errors when invalid", () => {
const propsDef = {
columns : {
type: "array",
elementDefinition: {
width: "invlid type",
units: {
type: "string",
default: "px"
}
}
}
}
const errors = validatePropsDefinition(propsDef);
expect(errors.length).toEqual(1);
expect(errors[0].propName).toBe("width");
});
it("should return error when no options for options field", () => { it("should return error when no options for options field", () => {
const propsDef = { const compDef = {
size: { name:"some_component",
type: "options", props: {
options: [] size: {
type: "options",
options: []
}
} }
} };
const errors = validatePropsDefinition(propsDef); const errors = validateComponentDefinition(compDef);
expect(errors.length).toEqual(1); expect(errors.length).toEqual(1);
expect(errors[0].propName).toBe("size"); expect(errors[0].propName).toBe("size");
@ -74,14 +35,17 @@ describe("validatePropsDefinition", () => {
it("should not return error when options field has options", () => { it("should not return error when options field has options", () => {
const propsDef = { const compDef = {
size: { name: "some_component",
type: "options", props: {
options: ["small", "medium", "large"] size: {
type: "options",
options: ["small", "medium", "large"]
}
} }
} };
const errors = validatePropsDefinition(propsDef); const errors = validateComponentDefinition(compDef);
expect(errors).toEqual([]); expect(errors).toEqual([]);
@ -89,30 +53,34 @@ describe("validatePropsDefinition", () => {
}); });
const validPropDef = { const validComponentDef = {
size: { name: "some_component",
type: "options", props: {
options: ["small", "medium", "large"], size: {
default:"medium" type: "options",
}, options: ["small", "medium", "large"],
rowCount : "number", default:"medium"
columns : { },
type: "array", rowCount : "number"
elementDefinition: {
width: "number",
units: {
type: "string",
default: "px"
}
}
} }
};
const childComponentDef = {
name: "child_component",
props: {
width: "number",
units: {
type: "string",
default: "px"
}
}
}; };
const validProps = () => { const validProps = () => {
const { props } = createProps("some_component", validPropDef);
props.columns.push( const { props } = createProps(validComponentDef);
createProps("childcomponent", validPropDef.columns.elementDefinition).props); props._children.push(
createProps(childComponentDef));
return props; return props;
} }
@ -120,7 +88,7 @@ describe("validateProps", () => {
it("should have no errors with a big list of valid props", () => { it("should have no errors with a big list of valid props", () => {
const errors = validateProps(validPropDef, validProps(), [], true); const errors = validateProps(validComponentDef, validProps(), [], true);
expect(errors).toEqual([]); expect(errors).toEqual([]);
}); });
@ -129,7 +97,7 @@ describe("validateProps", () => {
const props = validProps(); const props = validProps();
props.rowCount = "1"; props.rowCount = "1";
const errors = validateProps(validPropDef, props, [], true); const errors = validateProps(validComponentDef, props, [], true);
expect(errors.length).toEqual(1); expect(errors.length).toEqual(1);
expect(errors[0].propName).toBe("rowCount"); expect(errors[0].propName).toBe("rowCount");
@ -139,92 +107,92 @@ describe("validateProps", () => {
const props = validProps(); const props = validProps();
props.size = "really_small"; props.size = "really_small";
const errors = validateProps(validPropDef, props, [], true); const errors = validateProps(validComponentDef, props, [], true);
expect(errors.length).toEqual(1); expect(errors.length).toEqual(1);
expect(errors[0].propName).toBe("size"); expect(errors[0].propName).toBe("size");
}); });
it("should return error with invalid array item", () => {
const props = validProps();
props.columns[0].width = "seven";
const errors = validateProps(validPropDef, props, [], true);
expect(errors.length).toEqual(1);
expect(errors[0].propName).toBe("width");
});
it("should not return error when has binding", () => { it("should not return error when has binding", () => {
const props = validProps(); const props = validProps();
props.columns[0].width = setBinding({path:"some_path"}); props._children[0].width = setBinding({path:"some_path"});
props.size = setBinding({path:"other path", fallback:"small"}); props.size = setBinding({path:"other path", fallback:"small"});
const errors = validateProps(validPropDef, props, [], true); const errors = validateProps(validComponentDef, props, [], true);
expect(errors.length).toEqual(0); expect(errors.length).toEqual(0);
}); });
}); });
describe("recursivelyValidateProps", () => { describe("recursivelyValidateProps", () => {
const rootComponent = { const rootComponent = {
width: "number", name: "rootComponent",
child: "component", children: true,
navitems: { props: {
type: "array", width: "number"
elementDefinition: {
name: "string",
icon: "component"
}
} }
}; };
const todoListComponent = { const todoListComponent = {
showTitle: "bool", name: "todoListComponent",
header: "component" props:{
showTitle: "bool"
}
}; };
const headerComponent = { const headerComponent = {
text: "string" name: "headerComponent",
} props: {
text: "string"
}
};
const iconComponent = { const iconComponent = {
iconName: "string" name: "iconComponent",
} props: {
iconName: "string"
}
};
const navItemComponent = {
name: "navItemComponent",
props: {
text: "string"
}
};
const getComponent = name => ({ const getComponent = name => ({
rootComponent, rootComponent,
todoListComponent, todoListComponent,
headerComponent, headerComponent,
iconComponent iconComponent,
navItemComponent
})[name]; })[name];
const rootProps = () => ({ const rootProps = () => ({
_component: "rootComponent", _component: "rootComponent",
width: 100, width: 100,
child: { _children: [{
_component: "todoListComponent", _component: "todoListComponent",
showTitle: true, showTitle: true,
header: { _children : [
_component: "headerComponent", {
text: "Your todo list" _component: "navItemComponent",
} text: "todos"
}, },
navitems: [ {
{ _component: "headerComponent",
name: "Main", text: "Your todo list"
icon: { },
{
_component: "iconComponent", _component: "iconComponent",
iconName:"fa fa-list" iconName: "fa fa-list"
} },
}, {
{
name: "Settings",
icon: {
_component: "iconComponent", _component: "iconComponent",
iconName:"fa fa-cog" iconName:"fa fa-cog"
} }
} ]
] }]
}); });
it("should return no errors for valid structure", () => { it("should return no errors for valid structure", () => {
@ -245,38 +213,20 @@ describe("recursivelyValidateProps", () => {
it("should return error on first nested child component", () => { it("should return error on first nested child component", () => {
const root = rootProps(); const root = rootProps();
root.child.showTitle = "yeeeoooo"; root._children[0].showTitle = "yeeeoooo";
const result = recursivelyValidate(root, getComponent); const result = recursivelyValidate(root, getComponent);
expect(result.length).toBe(1); expect(result.length).toBe(1);
expect(result[0].stack).toEqual(["child"]); expect(result[0].stack).toEqual([0]);
expect(result[0].propName).toBe("showTitle"); expect(result[0].propName).toBe("showTitle");
}); });
it("should return error on second nested child component", () => { it("should return error on second nested child component", () => {
const root = rootProps(); const root = rootProps();
root.child.header.text = false; root._children[0]._children[0].text = false;
const result = recursivelyValidate(root, getComponent); const result = recursivelyValidate(root, getComponent);
expect(result.length).toBe(1); expect(result.length).toBe(1);
expect(result[0].stack).toEqual(["child", "header"]); expect(result[0].stack).toEqual([0,0]);
expect(result[0].propName).toBe("text"); expect(result[0].propName).toBe("text");
}); });
it("should return error on invalid array prop", () => {
const root = rootProps();
root.navitems[1].name = false;
const result = recursivelyValidate(root, getComponent);
expect(result.length).toBe(1);
expect(result[0].propName).toBe("name");
expect(result[0].stack).toEqual(["navitems[1]"]);
});
it("should return error on invalid array child", () => {
const root = rootProps();
root.navitems[1].icon.iconName = false;
const result = recursivelyValidate(root, getComponent);
expect(result.length).toBe(1);
expect(result[0].propName).toBe("iconName");
expect(result[0].stack).toEqual(["navitems[1]", "icon"]);
});
}); });

51
packages/client/src/createApp.js

@ -15,32 +15,37 @@ import { isBound } from "./state/isState";
export const createApp = (componentLibraries, appDefinition, user) => { export const createApp = (componentLibraries, appDefinition, user) => {
const _initialiseComponent = (parentContext, hydrate) => (props, htmlElement, context, anchor=null) => { const _initialiseChildren = (parentContext, hydrate) => (childrenProps, htmlElement, context, anchor=null) => {
const {componentName, libName} = splitName(props._component); const childComponents = [];
if(!componentName || !libName) return; for(let childProps of childrenProps) {
const {componentName, libName} = splitName(childProps._component);
const {initialProps, bind} = setupBinding( if(!componentName || !libName) return;
store, props, coreApi,
context || parentContext, appDefinition.appRootPath);
const {initialProps, bind} = setupBinding(
const componentProps = { store, childProps, coreApi,
...initialProps, context || parentContext, appDefinition.appRootPath);
_bb:bb(context || parentContext, props)
};
const component = new (componentLibraries[libName][componentName])({
target: htmlElement, const componentProps = {
props: componentProps, ...initialProps,
hydrate, _bb:bb(context || parentContext, childProps)
anchor };
});
const component = new (componentLibraries[libName][componentName])({
target: htmlElement,
props: componentProps,
hydrate,
anchor
});
bind(component); bind(component);
childComponents.push(component);
}
return component; return childComponents;
} }
const coreApi = createCoreApi(appDefinition, user); const coreApi = createCoreApi(appDefinition, user);
@ -86,10 +91,10 @@ export const createApp = (componentLibraries, appDefinition, user) => {
} }
const bb = (context, props) => ({ const bb = (context, props) => ({
hydrateComponent: _initialiseComponent(context, true), hydrateChildren: _initialiseChildren(context, true),
appendComponent: _initialiseComponent(context, false), appendChildren: _initialiseChildren(context, false),
insertComponent: (props, htmlElement, anchor, context) => insertChildren: (props, htmlElement, anchor, context) =>
_initialiseComponent(context, false)(props, htmlElement, context, anchor), _initialiseChildren(context, false)(props, htmlElement, context, anchor),
store, store,
relativeUrl, relativeUrl,
api, api,

4
packages/client/src/index.js

@ -34,8 +34,8 @@ export const loadBudibase = async (componentLibraries, props) => {
} }
const _app = createApp(componentLibraries, appDefinition, user); const _app = createApp(componentLibraries, appDefinition, user);
_app.hydrateComponent( _app.hydrateChildren(
props, [props],
document.body); document.body);
}; };

71
packages/server/builder/budibase-client.esm.mjs

File diff suppressed because one or more lines are too long

49
packages/server/builder/bundle.css

@ -1,49 +1,46 @@
main.svelte-15fmzor{height:100%;width:100%;font-family:"Roboto", Helvetica, Arial, sans-serif} main.svelte-15fmzor{height:100%;width:100%;font-family:"Roboto", Helvetica, Arial, sans-serif}
.root.svelte-y7jhgd{height:100%;width:100%;display:flex;flex-direction:column}.top-nav.svelte-y7jhgd{flex:0 0 auto;height:25px;background:white;padding:5px;width:100%}.content.svelte-y7jhgd{flex:1 1 auto;width:100%;height:100px}.content.svelte-y7jhgd>div.svelte-y7jhgd{height:100%;width:100%}.topnavitem.svelte-y7jhgd{cursor:pointer;color:var(--secondary50);padding:0px 15px;font-weight:600;font-size:.9rem}.topnavitem.svelte-y7jhgd:hover{color:var(--secondary75);font-weight:600}.active.svelte-y7jhgd{color:var(--primary100);font-weight:900}
.root.svelte-e4n7zy{position:fixed;margin:0 auto;text-align:center;top:20%;width:100%}.inner.svelte-e4n7zy{display:inline-block;margin:auto}.logo.svelte-e4n7zy{width:300px;margin-bottom:40px}.root.svelte-e4n7zy .option{width:250px}.app-link.svelte-e4n7zy{margin-top:10px;display:block} .root.svelte-e4n7zy{position:fixed;margin:0 auto;text-align:center;top:20%;width:100%}.inner.svelte-e4n7zy{display:inline-block;margin:auto}.logo.svelte-e4n7zy{width:300px;margin-bottom:40px}.root.svelte-e4n7zy .option{width:250px}.app-link.svelte-e4n7zy{margin-top:10px;display:block}
.root.svelte-y7jhgd{height:100%;width:100%;display:flex;flex-direction:column}.top-nav.svelte-y7jhgd{flex:0 0 auto;height:25px;background:white;padding:5px;width:100%}.content.svelte-y7jhgd{flex:1 1 auto;width:100%;height:100px}.content.svelte-y7jhgd>div.svelte-y7jhgd{height:100%;width:100%}.topnavitem.svelte-y7jhgd{cursor:pointer;color:var(--secondary50);padding:0px 15px;font-weight:600;font-size:.9rem}.topnavitem.svelte-y7jhgd:hover{color:var(--secondary75);font-weight:600}.active.svelte-y7jhgd{color:var(--primary100);font-weight:900}
button.svelte-bxuckr{border-style:none;background-color:rgba(0,0,0,0);cursor:pointer;outline:none}button.svelte-bxuckr:hover{color:var(--hovercolor)}button.svelte-bxuckr:active{outline:none} button.svelte-bxuckr{border-style:none;background-color:rgba(0,0,0,0);cursor:pointer;outline:none}button.svelte-bxuckr:hover{color:var(--hovercolor)}button.svelte-bxuckr:active{outline:none}
.border-normal.svelte-vnon4v{border-radius:var(--borderradiusall)}.border-left.svelte-vnon4v{border-radius:var(--borderradius) 0 0 var(--borderradius)}.border-right.svelte-vnon4v{border-radius:0 var(--borderradius) var(--borderradius) 0}.border-middle.svelte-vnon4v{border-radius:0}button.svelte-vnon4v{border-style:solid;padding:7.5px 15px;cursor:pointer;margin:5px;border-radius:5px}.primary.svelte-vnon4v{background-color:var(--primary100);border-color:var(--primary100);color:var(--white)}.primary.svelte-vnon4v:hover{background-color:var(--primary75);border-color:var(--primary75)}.primary.svelte-vnon4v:active{background-color:var(--primarydark);border-color:var(--primarydark)}.primary-outline.svelte-vnon4v{background-color:var(--white);border-color:var(--primary100);color:var(--primary100)}.primary-outline.svelte-vnon4v:hover{background-color:var(--primary10)}.primary-outline.svelte-vnon4v:pressed{background-color:var(--primary25)}.secondary.svelte-vnon4v{background-color:var(--secondary100);border-color:var(--secondary100);color:var(--white)}.secondary.svelte-vnon4v:hover{background-color:var(--secondary75);border-color:var(--secondary75)}.secondary.svelte-vnon4v:pressed{background-color:var(--secondarydark);border-color:var(--secondarydark)}.secondary-outline.svelte-vnon4v{background-color:var(--white);border-color:var(--secondary100);color:var(--secondary100)}.secondary-outline.svelte-vnon4v:hover{background-color:var(--secondary10)}.secondary-outline.svelte-vnon4v:pressed{background-color:var(--secondary25)}.success.svelte-vnon4v{background-color:var(--success100);border-color:var(--success100);color:var(--white)}.success.svelte-vnon4v:hover{background-color:var(--success75);border-color:var(--success75)}.success.svelte-vnon4v:pressed{background-color:var(--successdark);border-color:var(--successdark)}.success-outline.svelte-vnon4v{background-color:var(--white);border-color:var(--success100);color:var(--success100)}.success-outline.svelte-vnon4v:hover{background-color:var(--success10)}.success-outline.svelte-vnon4v:pressed{background-color:var(--success25)}.deletion.svelte-vnon4v{background-color:var(--deletion100);border-color:var(--deletion100);color:var(--white)}.deletion.svelte-vnon4v:hover{background-color:var(--deletion75);border-color:var(--deletion75)}.deletion.svelte-vnon4v:pressed{background-color:var(--deletiondark);border-color:var(--deletiondark)}.deletion-outline.svelte-vnon4v{background-color:var(--white);border-color:var(--deletion100);color:var(--deletion100)}.deletion-outline.svelte-vnon4v:hover{background-color:var(--deletion10)}.deletion-outline.svelte-vnon4v:pressed{background-color:var(--deletion25)} .border-normal.svelte-vnon4v{border-radius:var(--borderradiusall)}.border-left.svelte-vnon4v{border-radius:var(--borderradius) 0 0 var(--borderradius)}.border-right.svelte-vnon4v{border-radius:0 var(--borderradius) var(--borderradius) 0}.border-middle.svelte-vnon4v{border-radius:0}button.svelte-vnon4v{border-style:solid;padding:7.5px 15px;cursor:pointer;margin:5px;border-radius:5px}.primary.svelte-vnon4v{background-color:var(--primary100);border-color:var(--primary100);color:var(--white)}.primary.svelte-vnon4v:hover{background-color:var(--primary75);border-color:var(--primary75)}.primary.svelte-vnon4v:active{background-color:var(--primarydark);border-color:var(--primarydark)}.primary-outline.svelte-vnon4v{background-color:var(--white);border-color:var(--primary100);color:var(--primary100)}.primary-outline.svelte-vnon4v:hover{background-color:var(--primary10)}.primary-outline.svelte-vnon4v:pressed{background-color:var(--primary25)}.secondary.svelte-vnon4v{background-color:var(--secondary100);border-color:var(--secondary100);color:var(--white)}.secondary.svelte-vnon4v:hover{background-color:var(--secondary75);border-color:var(--secondary75)}.secondary.svelte-vnon4v:pressed{background-color:var(--secondarydark);border-color:var(--secondarydark)}.secondary-outline.svelte-vnon4v{background-color:var(--white);border-color:var(--secondary100);color:var(--secondary100)}.secondary-outline.svelte-vnon4v:hover{background-color:var(--secondary10)}.secondary-outline.svelte-vnon4v:pressed{background-color:var(--secondary25)}.success.svelte-vnon4v{background-color:var(--success100);border-color:var(--success100);color:var(--white)}.success.svelte-vnon4v:hover{background-color:var(--success75);border-color:var(--success75)}.success.svelte-vnon4v:pressed{background-color:var(--successdark);border-color:var(--successdark)}.success-outline.svelte-vnon4v{background-color:var(--white);border-color:var(--success100);color:var(--success100)}.success-outline.svelte-vnon4v:hover{background-color:var(--success10)}.success-outline.svelte-vnon4v:pressed{background-color:var(--success25)}.deletion.svelte-vnon4v{background-color:var(--deletion100);border-color:var(--deletion100);color:var(--white)}.deletion.svelte-vnon4v:hover{background-color:var(--deletion75);border-color:var(--deletion75)}.deletion.svelte-vnon4v:pressed{background-color:var(--deletiondark);border-color:var(--deletiondark)}.deletion-outline.svelte-vnon4v{background-color:var(--white);border-color:var(--deletion100);color:var(--deletion100)}.deletion-outline.svelte-vnon4v:hover{background-color:var(--deletion10)}.deletion-outline.svelte-vnon4v:pressed{background-color:var(--deletion25)}
.root.svelte-17zel0b{display:grid;grid-template-columns:250px 1fr 300px;height:100%;width:100%}.ui-nav.svelte-17zel0b{grid-column:1;background-color:var(--secondary5);height:100%}.preview-pane.svelte-17zel0b{grid-column:2}.components-pane.svelte-17zel0b{grid-column:3;background-color:var(--secondary5);min-height:0px;overflow-y:hidden}.pages-list-container.svelte-17zel0b{padding-top:2rem}.components-nav-header.svelte-17zel0b{font-size:.9rem}.nav-group-header.svelte-17zel0b{font-size:.9rem;padding-left:1rem}.nav-items-container.svelte-17zel0b{padding:1rem 1rem 0rem 1rem}.nav-group-header.svelte-17zel0b{display:grid;grid-template-columns:[icon] auto [title] 1fr [button] auto;padding:2rem 1rem 0rem 1rem;font-size:.9rem;font-weight:bold}.nav-group-header.svelte-17zel0b>div.svelte-17zel0b:nth-child(1){padding:0rem .5rem 0rem 0rem;vertical-align:bottom;grid-column-start:icon;margin-right:5px}.nav-group-header.svelte-17zel0b>span.svelte-17zel0b:nth-child(2){margin-left:5px;vertical-align:bottom;grid-column-start:title;margin-top:auto}.nav-group-header.svelte-17zel0b>div.svelte-17zel0b:nth-child(3){vertical-align:bottom;grid-column-start:button;cursor:pointer;color:var(--primary75)}.nav-group-header.svelte-17zel0b>div.svelte-17zel0b:nth-child(3):hover{color:var(--primary75)}
.root.svelte-q8uz1n{height:100%;display:flex}.content.svelte-q8uz1n{flex:1 1 auto;height:100%;background-color:var(--white);margin:0}.nav.svelte-q8uz1n{flex:0 1 auto;width:300px;height:100%} .root.svelte-q8uz1n{height:100%;display:flex}.content.svelte-q8uz1n{flex:1 1 auto;height:100%;background-color:var(--white);margin:0}.nav.svelte-q8uz1n{flex:0 1 auto;width:300px;height:100%}
.root.svelte-rjo9m0{display:grid;grid-template-columns:[uiNav] 250px [preview] auto [properties] 300px;height:100%;width:100%;overflow-y:auto}.ui-nav.svelte-rjo9m0{grid-column-start:uiNav;background-color:var(--secondary5);height:100%}.properties-pane.svelte-rjo9m0{grid-column-start:properties;background-color:var(--secondary5);height:100%;overflow-y:hidden}.pages-list-container.svelte-rjo9m0{padding-top:2rem}.components-nav-header.svelte-rjo9m0{font-size:.9rem}.nav-group-header.svelte-rjo9m0{font-size:.9rem;padding-left:1rem}.nav-items-container.svelte-rjo9m0{padding:1rem 1rem 0rem 1rem}.nav-group-header.svelte-rjo9m0{display:grid;grid-template-columns:[icon] auto [title] 1fr [button] auto;padding:2rem 1rem 0rem 1rem;font-size:.9rem;font-weight:bold}.nav-group-header.svelte-rjo9m0>div.svelte-rjo9m0:nth-child(1){padding:0rem .5rem 0rem 0rem;vertical-align:bottom;grid-column-start:icon;margin-right:5px}.nav-group-header.svelte-rjo9m0>span.svelte-rjo9m0:nth-child(2){margin-left:5px;vertical-align:bottom;grid-column-start:title;margin-top:auto}.nav-group-header.svelte-rjo9m0>div.svelte-rjo9m0:nth-child(3){vertical-align:bottom;grid-column-start:button;cursor:pointer;color:var(--primary75)}.nav-group-header.svelte-rjo9m0>div.svelte-rjo9m0:nth-child(3):hover{color:var(--primary75)} .root.svelte-117bbrk{padding-bottom:10px;padding-left:10px;font-size:.9rem;color:var(--secondary50);font-weight:bold}.hierarchy-item.svelte-117bbrk{cursor:pointer;padding:5px 0px}.hierarchy-item.svelte-117bbrk:hover{color:var(--secondary100)}.component.svelte-117bbrk{margin-left:5px}.selected.svelte-117bbrk{color:var(--primary100);font-weight:bold}.title.svelte-117bbrk{margin-left:10px}
.uk-modal-dialog.svelte-91ta29{border-radius:.3rem}
.root.svelte-1r2dipt{color:var(--secondary50);font-size:.9rem;font-weight:bold}.hierarchy-item.svelte-1r2dipt{cursor:pointer;padding:5px 0px}.hierarchy-item.svelte-1r2dipt:hover{color:var(--secondary)}.component.svelte-1r2dipt{margin-left:5px}.currentfolder.svelte-1r2dipt{color:var(--secondary100)}.selected.svelte-1r2dipt{color:var(--primary100);font-weight:bold}.title.svelte-1r2dipt{margin-left:10px}
.root.svelte-r1aen3{height:100%;display:flex;flex-direction:column;border-style:solid;border-width:1px 0 0 0;border-color:var(--slate)}.title.svelte-r1aen3{padding:1rem;display:grid;grid-template-columns:[name] 1fr [actions] auto;color:var(--secondary100);font-size:.9rem;font-weight:bold}.title.svelte-r1aen3>div.svelte-r1aen3:nth-child(1){grid-column-start:name;color:var(--secondary100)}.title.svelte-r1aen3>div.svelte-r1aen3:nth-child(2){grid-column-start:actions}.component-props-container.svelte-r1aen3{flex:1 1 auto;overflow-y:auto}
.section-container.svelte-yk1mmr{padding:15px;border-style:dotted;border-width:1px;border-color:var(--lightslate);border-radius:2px}.section-container.svelte-yk1mmr:nth-child(1){margin-bottom:15px}.row-text.svelte-yk1mmr{margin-right:15px;color:var(--primary100)}input.svelte-yk1mmr{margin-right:15px}p.svelte-yk1mmr>span.svelte-yk1mmr{margin-left:30px}.header.svelte-yk1mmr{display:grid;grid-template-columns:[title] 1fr [icon] auto}.header.svelte-yk1mmr>div.svelte-yk1mmr:nth-child(1){grid-column-start:title}.header.svelte-yk1mmr>div.svelte-yk1mmr:nth-child(2){grid-column-start:icon}
.root.svelte-18ccx5u{display:flex;flex-direction:column}.library-header.svelte-18ccx5u{font-size:1.1em;border-color:var(--primary25);border-width:1px 0px;border-style:solid;background-color:var(--primary10);padding:5px 0;flex:0 0 auto}.library-container.svelte-18ccx5u{padding:0 0 10px 10px;flex:1 1 auto;min-height:0px}.inner-header.svelte-18ccx5u{font-size:0.9em;font-weight:bold;margin-top:7px;margin-bottom:3px}.component.svelte-18ccx5u{padding:2px 0px;cursor:pointer}.component.svelte-18ccx5u:hover{background-color:var(--lightslate)}.component.svelte-18ccx5u>.name.svelte-18ccx5u{color:var(--secondary100);display:inline-block}.component.svelte-18ccx5u>.description.svelte-18ccx5u{font-size:0.8em;color:var(--secondary75);display:inline-block;margin-left:10px}
h1.svelte-11kb98w{font-size:1.2em}
.component-container.svelte-12kdu9y{grid-row-start:middle;grid-column-start:middle;position:relative;overflow:hidden;padding-top:56.25%;margin:auto}.component-container.svelte-12kdu9y iframe.svelte-12kdu9y{border:0;height:100%;left:0;position:absolute;top:0;width:100%}
h4.svelte-sqtlby{margin-top:20px} h4.svelte-sqtlby{margin-top:20px}
.root.svelte-wfv60d{height:100%;position:relative;padding:1.5rem}.actions-header.svelte-wfv60d{flex:0 1 auto}.node-view.svelte-wfv60d{overflow-y:auto;flex:1 1 auto} .root.svelte-1ersoxu{padding:15px}.help-text.svelte-1ersoxu{color:var(--slate);font-size:10pt}
.items-root.svelte-19lmivt{display:flex;flex-direction:column;max-height:100%;height:100%;background-color:var(--secondary5)}.nav-group-header.svelte-19lmivt{display:grid;grid-template-columns:[icon] auto [title] 1fr [button] auto;padding:2rem 1rem 0rem 1rem;font-size:.9rem;font-weight:bold}.nav-group-header.svelte-19lmivt>div.svelte-19lmivt:nth-child(1){padding:0rem .7rem 0rem 0rem;vertical-align:bottom;grid-column-start:icon;margin-right:5px}.nav-group-header.svelte-19lmivt>div.svelte-19lmivt:nth-child(2){margin-left:5px;vertical-align:bottom;grid-column-start:title;margin-top:auto}.nav-group-header.svelte-19lmivt>div.svelte-19lmivt:nth-child(3){vertical-align:bottom;grid-column-start:button;cursor:pointer;color:var(--primary75)}.nav-group-header.svelte-19lmivt>div.svelte-19lmivt:nth-child(3):hover{color:var(--primary75)}.hierarchy-title.svelte-19lmivt{flex:auto 1 1}.hierarchy.svelte-19lmivt{display:flex;flex-direction:column;flex:1 0 auto;height:100px}.hierarchy-items-container.svelte-19lmivt{flex:1 1 auto;overflow-y:auto} .items-root.svelte-19lmivt{display:flex;flex-direction:column;max-height:100%;height:100%;background-color:var(--secondary5)}.nav-group-header.svelte-19lmivt{display:grid;grid-template-columns:[icon] auto [title] 1fr [button] auto;padding:2rem 1rem 0rem 1rem;font-size:.9rem;font-weight:bold}.nav-group-header.svelte-19lmivt>div.svelte-19lmivt:nth-child(1){padding:0rem .7rem 0rem 0rem;vertical-align:bottom;grid-column-start:icon;margin-right:5px}.nav-group-header.svelte-19lmivt>div.svelte-19lmivt:nth-child(2){margin-left:5px;vertical-align:bottom;grid-column-start:title;margin-top:auto}.nav-group-header.svelte-19lmivt>div.svelte-19lmivt:nth-child(3){vertical-align:bottom;grid-column-start:button;cursor:pointer;color:var(--primary75)}.nav-group-header.svelte-19lmivt>div.svelte-19lmivt:nth-child(3):hover{color:var(--primary75)}.hierarchy-title.svelte-19lmivt{flex:auto 1 1}.hierarchy.svelte-19lmivt{display:flex;flex-direction:column;flex:1 0 auto;height:100px}.hierarchy-items-container.svelte-19lmivt{flex:1 1 auto;overflow-y:auto}
.root.svelte-wfv60d{height:100%;position:relative;padding:1.5rem}.actions-header.svelte-wfv60d{flex:0 1 auto}.node-view.svelte-wfv60d{overflow-y:auto;flex:1 1 auto}
.root.svelte-nd1yft{height:100%;position:relative;padding:1.5rem} .root.svelte-nd1yft{height:100%;position:relative;padding:1.5rem}
.root.svelte-apja7r{height:100%;position:relative}.actions-header.svelte-apja7r{flex:0 1 auto}.node-view.svelte-apja7r{overflow-y:auto;flex:1 1 auto} .root.svelte-apja7r{height:100%;position:relative}.actions-header.svelte-apja7r{flex:0 1 auto}.node-view.svelte-apja7r{overflow-y:auto;flex:1 1 auto}
.root.svelte-117bbrk{padding-bottom:10px;padding-left:10px;font-size:.9rem;color:var(--secondary50);font-weight:bold}.hierarchy-item.svelte-117bbrk{cursor:pointer;padding:5px 0px}.hierarchy-item.svelte-117bbrk:hover{color:var(--secondary100)}.component.svelte-117bbrk{margin-left:5px}.selected.svelte-117bbrk{color:var(--primary100);font-weight:bold}.title.svelte-117bbrk{margin-left:10px}
h1.svelte-16jkjx9{font-size:1.2em}
.root.svelte-1r2dipt{color:var(--secondary50);font-size:.9rem;font-weight:bold}.hierarchy-item.svelte-1r2dipt{cursor:pointer;padding:5px 0px}.hierarchy-item.svelte-1r2dipt:hover{color:var(--secondary)}.component.svelte-1r2dipt{margin-left:5px}.currentfolder.svelte-1r2dipt{color:var(--secondary100)}.selected.svelte-1r2dipt{color:var(--primary100);font-weight:bold}.title.svelte-1r2dipt{margin-left:10px}
.uk-modal-dialog.svelte-vwwrf9{border-radius:.3rem}
.root.svelte-1abif7s{height:100%;display:flex;flex-direction:column}.padding.svelte-1abif7s{padding:1rem 1rem 0rem 1rem}.info-text.svelte-1abif7s{color:var(--secondary100);font-size:.8rem !important;font-weight:bold}.title.svelte-1abif7s{padding:2rem 1rem 1rem 1rem;display:grid;grid-template-columns:[name] 1fr [actions] auto;color:var(--secondary100);font-size:.9rem;font-weight:bold}.title.svelte-1abif7s>div.svelte-1abif7s:nth-child(1){grid-column-start:name;color:var(--secondary100)}.title.svelte-1abif7s>div.svelte-1abif7s:nth-child(2){grid-column-start:actions}.section-header.svelte-1abif7s{display:grid;grid-template-columns:[name] 1fr [actions] auto;color:var(--secondary50);font-size:.9rem;font-weight:bold;vertical-align:middle}.component-props-container.svelte-1abif7s{flex:1 1 auto;overflow-y:auto}
.root.svelte-1ersoxu{padding:15px}.help-text.svelte-1ersoxu{color:var(--slate);font-size:10pt}
.section-container.svelte-yk1mmr{padding:15px;border-style:dotted;border-width:1px;border-color:var(--lightslate);border-radius:2px}.section-container.svelte-yk1mmr:nth-child(1){margin-bottom:15px}.row-text.svelte-yk1mmr{margin-right:15px;color:var(--primary100)}input.svelte-yk1mmr{margin-right:15px}p.svelte-yk1mmr>span.svelte-yk1mmr{margin-left:30px}.header.svelte-yk1mmr{display:grid;grid-template-columns:[title] 1fr [icon] auto}.header.svelte-yk1mmr>div.svelte-yk1mmr:nth-child(1){grid-column-start:title}.header.svelte-yk1mmr>div.svelte-yk1mmr:nth-child(2){grid-column-start:icon}
.component-container.svelte-teqoiq{grid-row-start:middle;grid-column-start:middle;position:relative;overflow:hidden;padding-top:56.25%;margin:auto}.component-container.svelte-teqoiq iframe.svelte-teqoiq{border:0;height:100%;left:0;position:absolute;top:0;width:100%}
.root.svelte-x3bf9z{display:flex}.root.svelte-x3bf9z:last-child{border-radius:0 var(--borderradius) var(--borderradius) 0}.root.svelte-x3bf9z:first-child{border-radius:var(--borderradius) 0 0 var(--borderradius)}.root.svelte-x3bf9z:not(:first-child):not(:last-child){border-radius:0} .root.svelte-x3bf9z{display:flex}.root.svelte-x3bf9z:last-child{border-radius:0 var(--borderradius) var(--borderradius) 0}.root.svelte-x3bf9z:first-child{border-radius:var(--borderradius) 0 0 var(--borderradius)}.root.svelte-x3bf9z:not(:first-child):not(:last-child){border-radius:0}
.edit-button.svelte-zm41av{cursor:pointer;color:var(--secondary25)}.title.svelte-zm41av{margin:3rem 0rem 0rem 0rem;font-weight:700}.table-content.svelte-zm41av{font-weight:500;font-size:.9rem}tr.svelte-zm41av:hover .edit-button.svelte-zm41av{color:var(--secondary75)} .library-header.svelte-chhyel{font-size:1.1em;border-color:var(--primary25);border-width:1px 0px;border-style:solid;background-color:var(--primary10);padding:5px 0}.library-container.svelte-chhyel{padding:0 0 10px 10px}.inner-header.svelte-chhyel{font-size:0.9em;font-weight:bold;margin-top:7px;margin-bottom:3px}.component.svelte-chhyel{padding:2px 0px;cursor:pointer}.component.svelte-chhyel:hover{background-color:var(--lightslate)}.component.svelte-chhyel>.name.svelte-chhyel{color:var(--secondary100);display:inline-block}.component.svelte-chhyel>.description.svelte-chhyel{font-size:0.8em;color:var(--secondary75);display:inline-block;margin-left:10px}
.info-text.svelte-1gx0gkl{font-size:0.7rem;color:var(--secondary50)}
.root.svelte-t6vms4{font-size:10pt;width:100%}.form-root.svelte-t6vms4{display:flex;flex-wrap:wrap}.prop-container.svelte-t6vms4{flex:1 1 auto;min-width:250px}
.nav-item.svelte-1i5jqm7{padding:1.5rem 1rem 0rem 1rem;font-size:.9rem;font-weight:bold;cursor:pointer;flex:0 0 auto}.nav-item.svelte-1i5jqm7:hover{background-color:var(--primary10)}.active.svelte-1i5jqm7{background-color:var(--primary10)}
.dropdown-background.svelte-11ifkop{position:fixed;top:0;left:0;width:100vw;height:100vh}.root.svelte-11ifkop{cursor:pointer;z-index:1}.dropdown-content.svelte-11ifkop{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-11ifkop:not(:focus){display:none}.action-row.svelte-11ifkop{padding:7px 10px;cursor:pointer}.action-row.svelte-11ifkop:hover{background-color:var(--primary100);color:var(--white)} .dropdown-background.svelte-11ifkop{position:fixed;top:0;left:0;width:100vw;height:100vh}.root.svelte-11ifkop{cursor:pointer;z-index:1}.dropdown-content.svelte-11ifkop{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-11ifkop:not(:focus){display:none}.action-row.svelte-11ifkop{padding:7px 10px;cursor:pointer}.action-row.svelte-11ifkop:hover{background-color:var(--primary100);color:var(--white)}
.edit-button.svelte-lhfdtn{cursor:pointer;color:var(--secondary25)}tr.svelte-lhfdtn:hover .edit-button.svelte-lhfdtn{color:var(--secondary75)}.title.svelte-lhfdtn{margin:3rem 0rem 0rem 0rem;font-weight:700}.table-content.svelte-lhfdtn{font-weight:500;font-size:.9rem}
.root.svelte-17ju2r{display:block;font-size:.9rem;width:100%;cursor:pointer;color:var(--secondary50);font-weight:500}.title.svelte-17ju2r{padding-top:.5rem;padding-right:.5rem}.title.svelte-17ju2r:hover{background-color:var(--secondary10)}.active.svelte-17ju2r{background-color:var(--primary10)} .root.svelte-17ju2r{display:block;font-size:.9rem;width:100%;cursor:pointer;color:var(--secondary50);font-weight:500}.title.svelte-17ju2r{padding-top:.5rem;padding-right:.5rem}.title.svelte-17ju2r:hover{background-color:var(--secondary10)}.active.svelte-17ju2r{background-color:var(--primary10)}
.nav-item.svelte-1i5jqm7{padding:1.5rem 1rem 0rem 1rem;font-size:.9rem;font-weight:bold;cursor:pointer;flex:0 0 auto}.nav-item.svelte-1i5jqm7:hover{background-color:var(--primary10)}.active.svelte-1i5jqm7{background-color:var(--primary10)}
.root.svelte-18xd5y3{height:100%;padding:2rem}.settings-title.svelte-18xd5y3{font-weight:700}.title.svelte-18xd5y3{margin:3rem 0rem 0rem 0rem;font-weight:700}.recordkey.svelte-18xd5y3{font-size:14px;font-weight:600;color:var(--primary100)}.fields-table.svelte-18xd5y3{margin:1rem 1rem 0rem 0rem;border-collapse:collapse}.add-field-button.svelte-18xd5y3{cursor:pointer}.edit-button.svelte-18xd5y3{cursor:pointer;color:var(--secondary25)}.edit-button.svelte-18xd5y3:hover{cursor:pointer;color:var(--secondary75)}th.svelte-18xd5y3{text-align:left}td.svelte-18xd5y3{padding:1rem 5rem 1rem 0rem;margin:0;font-size:14px;font-weight:500}.field-label.svelte-18xd5y3{font-size:14px;font-weight:500}thead.svelte-18xd5y3>tr.svelte-18xd5y3{border-width:0px 0px 1px 0px;border-style:solid;border-color:var(--secondary75);margin-bottom:20px}tbody.svelte-18xd5y3>tr.svelte-18xd5y3{border-width:0px 0px 1px 0px;border-style:solid;border-color:var(--primary10)}tbody.svelte-18xd5y3>tr.svelte-18xd5y3:hover{background-color:var(--primary10)}tbody.svelte-18xd5y3>tr:hover .edit-button.svelte-18xd5y3{color:var(--secondary75)}.index-container.svelte-18xd5y3{border-style:solid;border-width:0 0 1px 0;border-color:var(--secondary25);padding:10px;margin-bottom:5px}.index-label.svelte-18xd5y3{color:var(--slate)}.index-name.svelte-18xd5y3{font-weight:bold;color:var(--primary100)}.index-container.svelte-18xd5y3 code.svelte-18xd5y3{margin:0;display:inline;background-color:var(--primary10);color:var(--secondary100);padding:3px}.index-field-row.svelte-18xd5y3{margin:1rem 0rem 0rem 0rem}.no-indexes.svelte-18xd5y3{margin:1rem 0rem 0rem 0rem;font-family:var(--fontnormal);font-size:14px}
.root.svelte-ehsf0i{display:block;font-size:.9rem;width:100%;cursor:pointer;font-weight:bold}.title.svelte-ehsf0i{font:var(--fontblack);padding-top:10px;padding-right:5px;padding-bottom:10px;color:var(--secondary100)}.title.svelte-ehsf0i:hover{background-color:var(--secondary10)} .root.svelte-ehsf0i{display:block;font-size:.9rem;width:100%;cursor:pointer;font-weight:bold}.title.svelte-ehsf0i{font:var(--fontblack);padding-top:10px;padding-right:5px;padding-bottom:10px;color:var(--secondary100)}.title.svelte-ehsf0i:hover{background-color:var(--secondary10)}
.edit-button.svelte-zm41av{cursor:pointer;color:var(--secondary25)}.title.svelte-zm41av{margin:3rem 0rem 0rem 0rem;font-weight:700}.table-content.svelte-zm41av{font-weight:500;font-size:.9rem}tr.svelte-zm41av:hover .edit-button.svelte-zm41av{color:var(--secondary75)}
.edit-button.svelte-lhfdtn{cursor:pointer;color:var(--secondary25)}tr.svelte-lhfdtn:hover .edit-button.svelte-lhfdtn{color:var(--secondary75)}.title.svelte-lhfdtn{margin:3rem 0rem 0rem 0rem;font-weight:700}.table-content.svelte-lhfdtn{font-weight:500;font-size:.9rem}
.root.svelte-wgyofl{padding:1.5rem;width:100%;align-items:right} .root.svelte-wgyofl{padding:1.5rem;width:100%;align-items:right}
.root.svelte-pq2tmv{height:100%;padding:15px}.allowed-records.svelte-pq2tmv{margin:20px 0px}.allowed-records.svelte-pq2tmv>span.svelte-pq2tmv{margin-right:30px} .root.svelte-pq2tmv{height:100%;padding:15px}.allowed-records.svelte-pq2tmv{margin:20px 0px}.allowed-records.svelte-pq2tmv>span.svelte-pq2tmv{margin-right:30px}
.info-text.svelte-1gx0gkl{font-size:0.7rem;color:var(--secondary50)} .root.svelte-1hs1zh2{height:100%;padding:2rem}.settings-title.svelte-1hs1zh2{font-weight:700}.title.svelte-1hs1zh2{margin:3rem 0rem 0rem 0rem;font-weight:700}.recordkey.svelte-1hs1zh2{font-size:14px;font-weight:600;color:var(--primary100)}.fields-table.svelte-1hs1zh2{margin:1rem 1rem 0rem 0rem;border-collapse:collapse}.add-field-button.svelte-1hs1zh2{cursor:pointer}.edit-button.svelte-1hs1zh2{cursor:pointer;color:var(--secondary25)}.edit-button.svelte-1hs1zh2:hover{cursor:pointer;color:var(--secondary75)}th.svelte-1hs1zh2{text-align:left}td.svelte-1hs1zh2{padding:1rem 5rem 1rem 0rem;margin:0;font-size:14px;font-weight:500}.field-label.svelte-1hs1zh2{font-size:14px;font-weight:500}thead.svelte-1hs1zh2>tr.svelte-1hs1zh2{border-width:0px 0px 1px 0px;border-style:solid;border-color:var(--secondary75);margin-bottom:20px}tbody.svelte-1hs1zh2>tr.svelte-1hs1zh2{border-width:0px 0px 1px 0px;border-style:solid;border-color:var(--primary10)}tbody.svelte-1hs1zh2>tr.svelte-1hs1zh2:hover{background-color:var(--primary10)}tbody.svelte-1hs1zh2>tr:hover .edit-button.svelte-1hs1zh2{color:var(--secondary75)}.index-container.svelte-1hs1zh2{border-style:solid;border-width:0 0 1px 0;border-color:var(--secondary25);padding:10px;margin-bottom:5px}.index-label.svelte-1hs1zh2{color:var(--slate)}.index-name.svelte-1hs1zh2{font-weight:bold;color:var(--primary100)}.index-container.svelte-1hs1zh2 code.svelte-1hs1zh2{margin:0;display:inline;background-color:var(--primary10);color:var(--secondary100);padding:3px}.index-field-row.svelte-1hs1zh2{margin:1rem 0rem 0rem 0rem}.no-indexes.svelte-1hs1zh2{margin:1rem 0rem 0rem 0rem;font-family:var(--fontnormal);font-size:14px}
.library-header.svelte-chhyel{font-size:1.1em;border-color:var(--primary25);border-width:1px 0px;border-style:solid;background-color:var(--primary10);padding:5px 0}.library-container.svelte-chhyel{padding:0 0 10px 10px}.inner-header.svelte-chhyel{font-size:0.9em;font-weight:bold;margin-top:7px;margin-bottom:3px}.component.svelte-chhyel{padding:2px 0px;cursor:pointer}.component.svelte-chhyel:hover{background-color:var(--lightslate)}.component.svelte-chhyel>.name.svelte-chhyel{color:var(--secondary100);display:inline-block}.component.svelte-chhyel>.description.svelte-chhyel{font-size:0.8em;color:var(--secondary75);display:inline-block;margin-left:10px}
.component.svelte-3sgo90{padding:5px 0}.component.svelte-3sgo90 .title.svelte-3sgo90{width:300px
}.component.svelte-3sgo90>.description.svelte-3sgo90{font-size:0.8em;color:var(--secondary75)}.button-container.svelte-3sgo90{text-align:right;margin-top:20px}.error.svelte-3sgo90{font-size:10pt;color:red}
.root.svelte-47ohpz{font-size:10pt}.padding.svelte-47ohpz{padding:0 10px}.inherited-title.svelte-47ohpz{padding:1rem 1rem 1rem 1rem;display:grid;grid-template-columns:[name] 1fr [actions] auto;color:var(--secondary100);font-size:.9rem;font-weight:bold}.inherited-title.svelte-47ohpz>div.svelte-47ohpz:nth-child(1){grid-column-start:name;color:var(--secondary50)}.inherited-title.svelte-47ohpz>div.svelte-47ohpz:nth-child(2){grid-column-start:actions;color:var(--secondary100)}
.title.svelte-dhe1ge{padding:3px;background-color:white;color:var(--secondary100);border-style:solid;border-width:1px 0 0 0;border-color:var(--lightslate)}.title.svelte-dhe1ge>span.svelte-dhe1ge{margin-left:10px}
.root.svelte-16sjty9{padding:2rem;border-radius:2rem}.uk-grid-small.svelte-16sjty9{padding:1rem}.option-container.svelte-16sjty9{border-style:dotted;border-width:1px;border-color:var(--primary75);padding:3px;margin-right:5px}
input.svelte-9fre0g{margin-right:7px} input.svelte-9fre0g{margin-right:7px}
.root.svelte-1v0yya9{padding:1rem 1rem 0rem 1rem}.prop-label.svelte-1v0yya9{font-size:0.8rem;color:var(--secondary100);font-weight:bold}
.error-container.svelte-ole1mk{padding:10px;border-style:solid;border-color:var(--deletion100);border-radius:var(--borderradiusall);background:var(--deletion75)}.error-row.svelte-ole1mk{padding:5px 0px} .error-container.svelte-ole1mk{padding:10px;border-style:solid;border-color:var(--deletion100);border-radius:var(--borderradiusall);background:var(--deletion75)}.error-row.svelte-ole1mk{padding:5px 0px}
.root.svelte-16sjty9{padding:2rem;border-radius:2rem}.uk-grid-small.svelte-16sjty9{padding:1rem}.option-container.svelte-16sjty9{border-style:dotted;border-width:1px;border-color:var(--primary75);padding:3px;margin-right:5px}
textarea.svelte-di7k4b{padding:3px;margin-top:5px;margin-bottom:10px;background:var(--lightslate);color:var(--white);font-family:'Courier New', Courier, monospace;width:95%;height:100px;border-radius:5px} textarea.svelte-di7k4b{padding:3px;margin-top:5px;margin-bottom:10px;background:var(--lightslate);color:var(--white);font-family:'Courier New', Courier, monospace;width:95%;height:100px;border-radius:5px}
.root.svelte-1v0yya9{padding:1rem 1rem 0rem 1rem}.prop-label.svelte-1v0yya9{font-size:0.8rem;color:var(--secondary100);font-weight:bold}
.root.svelte-ogh8o0{display:grid;grid-template-columns:[name] 1fr [actions] auto}.root.svelte-ogh8o0>div.svelte-ogh8o0:nth-child(1){grid-column-start:name;color:var(--secondary50)}.root.svelte-ogh8o0>div.svelte-ogh8o0:nth-child(2){grid-column-start:actions}.selectedname.svelte-ogh8o0{font-weight:bold;color:var(--secondary)}
textarea.svelte-1kv2xk7{width:300px;height:200px}
.addelement-container.svelte-r1ft9p{cursor:pointer;padding:3px 0px;text-align:center}.addelement-container.svelte-r1ft9p:hover{background-color:var(--primary25);margin-top:5px}.control-container.svelte-r1ft9p{padding-left:3px;background:var(--secondary10)}.separator.svelte-r1ft9p{width:60%;margin:10px auto;border-style:solid;border-width:1px 0 0 0;border-color:var(--primary25)} .addelement-container.svelte-r1ft9p{cursor:pointer;padding:3px 0px;text-align:center}.addelement-container.svelte-r1ft9p:hover{background-color:var(--primary25);margin-top:5px}.control-container.svelte-r1ft9p{padding-left:3px;background:var(--secondary10)}.separator.svelte-r1ft9p{width:60%;margin:10px auto;border-style:solid;border-width:1px 0 0 0;border-color:var(--primary25)}
.unbound-container.svelte-jubmd5{display:flex;margin:.5rem 0rem .5rem 0rem}.unbound-container.svelte-jubmd5>.svelte-jubmd5:nth-child(1){width:auto;flex:1 0 auto;font-size:0.8rem;color:var(--secondary100);border-radius:.2rem}.bound-header.svelte-jubmd5{display:flex}.bound-header.svelte-jubmd5>div.svelte-jubmd5:nth-child(1){flex:1 0 auto;width:30px;color:var(--secondary50);padding-left:5px}.binding-prop-label.svelte-jubmd5{color:var(--secondary50)} .unbound-container.svelte-jubmd5{display:flex;margin:.5rem 0rem .5rem 0rem}.unbound-container.svelte-jubmd5>.svelte-jubmd5:nth-child(1){width:auto;flex:1 0 auto;font-size:0.8rem;color:var(--secondary100);border-radius:.2rem}.bound-header.svelte-jubmd5{display:flex}.bound-header.svelte-jubmd5>div.svelte-jubmd5:nth-child(1){flex:1 0 auto;width:30px;color:var(--secondary50);padding-left:5px}.binding-prop-label.svelte-jubmd5{color:var(--secondary50)}
.addelement-container.svelte-199q8jr{cursor:pointer;padding:3px 0px;text-align:center}.addelement-container.svelte-199q8jr:hover{background-color:var(--primary25)}.item-container.svelte-199q8jr{padding-left:3px;background:var(--secondary10)} textarea.svelte-1kv2xk7{width:300px;height:200px}
.type-selector-container.svelte-1b6pj9u{display:flex}.type-selector.svelte-1b6pj9u{border-color:var(--primary50);border-radius:2px;width:50px;flex:1 0 auto} .type-selector-container.svelte-1b6pj9u{display:flex}.type-selector.svelte-1b6pj9u{border-color:var(--primary50);border-radius:2px;width:50px;flex:1 0 auto}
.root.svelte-rj4q22{height:100%;display:flex;flex-direction:column}.switcher.svelte-rj4q22{flex:0 0 auto}.switcher.svelte-rj4q22>button.svelte-rj4q22{display:inline-block;background-color:rgba(0,0,0,0);border-style:solid;border-color:var(--slate);margin:5px;padding:5px;cursor:pointer}.switcher.svelte-rj4q22>.selected.svelte-rj4q22{background-color:red}.panel.svelte-rj4q22{flex:1 1 auto;height:0px;overflow-y:auto}
/*# sourceMappingURL=bundle.css.map */ /*# sourceMappingURL=bundle.css.map */

102
packages/server/builder/bundle.css.map

File diff suppressed because one or more lines are too long

65293
packages/server/builder/bundle.js

File diff suppressed because one or more lines are too long

2
packages/server/builder/bundle.js.map

File diff suppressed because one or more lines are too long

26
packages/server/middleware/routers.js

@ -6,12 +6,12 @@ const { resolve } = require("path");
const send = require('koa-send'); const send = require('koa-send');
const { const {
getPackageForBuilder, getPackageForBuilder,
getRootComponents, getComponents,
savePackage, savePackage,
getApps, getApps,
saveDerivedComponent, saveScreen,
renameDerivedComponent, renameScreen,
deleteDerivedComponent, deleteScreen,
componentLibraryInfo componentLibraryInfo
} = require("../utilities/builder"); } = require("../utilities/builder");
@ -162,9 +162,9 @@ module.exports = (config, app) => {
ctx.request.body); ctx.request.body);
ctx.response.status = StatusCodes.OK; ctx.response.status = StatusCodes.OK;
}) })
.get("/_builder/api/:appname/rootcomponents", async (ctx) => { .get("/_builder/api/:appname/components", async (ctx) => {
try { try {
ctx.body = getRootComponents( ctx.body = getComponents(
config, config,
ctx.params.appname, ctx.params.appname,
ctx.query.lib); ctx.query.lib);
@ -194,26 +194,26 @@ module.exports = (config, app) => {
ctx.body = info.generators; ctx.body = info.generators;
ctx.response.status = StatusCodes.OK; ctx.response.status = StatusCodes.OK;
}) })
.post("/_builder/api/:appname/derivedcomponent", async (ctx) => { .post("/_builder/api/:appname/screen", async (ctx) => {
await saveDerivedComponent( await saveScreen(
config, config,
ctx.params.appname, ctx.params.appname,
ctx.request.body); ctx.request.body);
ctx.response.status = StatusCodes.OK; ctx.response.status = StatusCodes.OK;
}) })
.patch("/_builder/api/:appname/derivedcomponent", async (ctx) => { .patch("/_builder/api/:appname/screen", async (ctx) => {
await renameDerivedComponent( await renameScreen(
config, config,
ctx.params.appname, ctx.params.appname,
ctx.request.body.oldname, ctx.request.body.oldname,
ctx.request.body.newname); ctx.request.body.newname);
ctx.response.status = StatusCodes.OK; ctx.response.status = StatusCodes.OK;
}) })
.delete("/_builder/api/:appname/derivedcomponent/*", async (ctx) => { .delete("/_builder/api/:appname/screen/*", async (ctx) => {
const name = ctx.request.path.replace( const name = ctx.request.path.replace(
`/_builder/api/${ctx.params.appname}/derivedcomponent/`, ""); `/_builder/api/${ctx.params.appname}/screen/`, "");
await deleteDerivedComponent( await deleteScreen(
config, config,
ctx.params.appname, ctx.params.appname,
decodeURI(name)); decodeURI(name));

38
packages/server/tests/builder.spec.js

@ -5,8 +5,8 @@ const testPages = require("../appPackages/testApp/pages.json");
const testComponents = require("../appPackages/testApp/customComponents/components.json"); const testComponents = require("../appPackages/testApp/customComponents/components.json");
const testMoreComponents = require("../appPackages/testApp/moreCustomComponents/components.json"); const testMoreComponents = require("../appPackages/testApp/moreCustomComponents/components.json");
const statusCodes = require("../utilities/statusCodes"); const statusCodes = require("../utilities/statusCodes");
const derivedComponent1 = require("../appPackages/testApp/components/myTextBox.json"); const screen1 = require("../appPackages/testApp/components/myTextBox.json");
const derivedComponent2 = require("../appPackages/testApp/components/subfolder/otherTextBox.json"); const screen2 = require("../appPackages/testApp/components/subfolder/otherTextBox.json");
const { readJSON, pathExists, unlink } = require("fs-extra"); const { readJSON, pathExists, unlink } = require("fs-extra");
const app = require("./testApp")(); const app = require("./testApp")();
@ -49,36 +49,36 @@ it("/apppackage should get pages", async () => {
expect(body.pages).toEqual(testPages); expect(body.pages).toEqual(testPages);
}); });
it("/apppackage should get rootComponents", async () => { it("/apppackage should get components", async () => {
const {body} = await app.get("/_builder/api/testApp/appPackage") const {body} = await app.get("/_builder/api/testApp/appPackage")
.expect(statusCodes.OK); .expect(statusCodes.OK);
expect(body.rootComponents["./customComponents/textbox"]).toBeDefined(); expect(body.components["./customComponents/textbox"]).toBeDefined();
expect(body.rootComponents["./moreCustomComponents/textbox"]).toBeDefined(); expect(body.components["./moreCustomComponents/textbox"]).toBeDefined();
expect(body.rootComponents["./customComponents/textbox"]) expect(body.components["./customComponents/textbox"])
.toEqual(testComponents.textbox); .toEqual(testComponents.textbox);
expect(body.rootComponents["./moreCustomComponents/textbox"]) expect(body.components["./moreCustomComponents/textbox"])
.toEqual(testMoreComponents.textbox); .toEqual(testMoreComponents.textbox);
}); });
it("/apppackage should get derivedComponents", async () => { it("/apppackage should get screens", async () => {
const {body} = await app.get("/_builder/api/testApp/appPackage") const {body} = await app.get("/_builder/api/testApp/appPackage")
.expect(statusCodes.OK); .expect(statusCodes.OK);
const expectedComponents = { const expectedComponents = {
"myTextBox" : {...derivedComponent1, name:"myTextBox"}, "myTextBox" : {...screen1, name:"myTextBox"},
"subfolder/otherTextBox": {...derivedComponent2, name:"subfolder/otherTextBox"} "subfolder/otherTextBox": {...screen2, name:"subfolder/otherTextBox"}
}; };
expect(body.derivedComponents).toEqual(expectedComponents); expect(body.screens).toEqual(expectedComponents);
}); });
it("should be able to create new derived component", async () => { it("should be able to create new derived component", async () => {
const newDerivedComponent = { const newscreen = {
name: "newTextBox", name: "newTextBox",
inherits: "./customComponents/textbox", inherits: "./customComponents/textbox",
props: { props: {
@ -86,17 +86,17 @@ it("should be able to create new derived component", async () => {
} }
}; };
await app.post("/_builder/api/testApp/derivedcomponent", newDerivedComponent) await app.post("/_builder/api/testApp/screen", newscreen)
.expect(statusCodes.OK); .expect(statusCodes.OK);
const componentFile = "./appPackages/testApp/components/newTextBox.json"; const componentFile = "./appPackages/testApp/components/newTextBox.json";
expect(await pathExists(componentFile)).toBe(true); expect(await pathExists(componentFile)).toBe(true);
expect(await readJSON(componentFile)).toEqual(newDerivedComponent); expect(await readJSON(componentFile)).toEqual(newscreen);
}); });
it("should be able to update derived component", async () => { it("should be able to update derived component", async () => {
const updatedDerivedComponent = { const updatedscreen = {
name: "newTextBox", name: "newTextBox",
inherits: "./customComponents/textbox", inherits: "./customComponents/textbox",
props: { props: {
@ -104,15 +104,15 @@ it("should be able to update derived component", async () => {
} }
}; };
await app.post("/_builder/api/testApp/derivedcomponent", updatedDerivedComponent) await app.post("/_builder/api/testApp/screen", updatedscreen)
.expect(statusCodes.OK); .expect(statusCodes.OK);
const componentFile = "./appPackages/testApp/components/newTextBox.json"; const componentFile = "./appPackages/testApp/components/newTextBox.json";
expect(await readJSON(componentFile)).toEqual(updatedDerivedComponent); expect(await readJSON(componentFile)).toEqual(updatedscreen);
}); });
it("should be able to rename derived component", async () => { it("should be able to rename derived component", async () => {
await app.patch("/_builder/api/testApp/derivedcomponent", { await app.patch("/_builder/api/testApp/screen", {
oldname: "newTextBox", newname: "anotherSubFolder/newTextBox" oldname: "newTextBox", newname: "anotherSubFolder/newTextBox"
}).expect(statusCodes.OK); }).expect(statusCodes.OK);
@ -124,7 +124,7 @@ it("should be able to rename derived component", async () => {
}); });
it("should be able to delete derived component", async () => { it("should be able to delete derived component", async () => {
await app.delete("/_builder/api/testApp/derivedcomponent/anotherSubFolder/newTextBox") await app.delete("/_builder/api/testApp/screen/anotherSubFolder/newTextBox")
.expect(statusCodes.OK); .expect(statusCodes.OK);
const componentFile = "./appPackages/testApp/components/anotherSubFolder/newTextBox.json"; const componentFile = "./appPackages/testApp/components/anotherSubFolder/newTextBox.json";

20
packages/server/utilities/builder/index.js

@ -37,10 +37,10 @@ module.exports.getPackageForBuilder = async (config, appname) => {
pages, pages,
rootComponents: await getRootComponents(appPath, pages), components: await getComponents(appPath, pages),
derivedComponents: keyBy("name")( screens: keyBy("name")(
await fetchDerivedComponents(appPath)) await fetchscreens(appPath))
}); });
} }
@ -60,7 +60,7 @@ module.exports.getApps = async (config, master) => {
const componentPath = (appPath, name) => const componentPath = (appPath, name) =>
join(appPath, "components", name + ".json"); join(appPath, "components", name + ".json");
module.exports.saveDerivedComponent = async (config, appname, component) => { module.exports.saveScreen = async (config, appname, component) => {
const appPath = appPackageFolder(config, appname); const appPath = appPackageFolder(config, appname);
const compPath = componentPath(appPath, component.name); const compPath = componentPath(appPath, component.name);
await ensureDir(dirname(compPath)); await ensureDir(dirname(compPath));
@ -70,7 +70,7 @@ module.exports.saveDerivedComponent = async (config, appname, component) => {
{encoding:"utf8", flag:"w", spaces:2}); {encoding:"utf8", flag:"w", spaces:2});
} }
module.exports.renameDerivedComponent = async (config, appname, oldName, newName) => { module.exports.renameScreen = async (config, appname, oldName, newName) => {
const appPath = appPackageFolder(config, appname); const appPath = appPackageFolder(config, appname);
const oldComponentPath = componentPath( const oldComponentPath = componentPath(
@ -85,7 +85,7 @@ module.exports.renameDerivedComponent = async (config, appname, oldName, newName
newComponentPath); newComponentPath);
} }
module.exports.deleteDerivedComponent = async (config, appname, name) => { module.exports.deleteScreen = async (config, appname, name) => {
const appPath = appPackageFolder(config, appname); const appPath = appPackageFolder(config, appname);
const componentFile = componentPath(appPath, name); const componentFile = componentPath(appPath, name);
await unlink(componentFile); await unlink(componentFile);
@ -102,7 +102,7 @@ module.exports.componentLibraryInfo = async (config, appname, lib) => {
}; };
const getRootComponents = async (appPath, pages ,lib) => { const getComponents = async (appPath, pages ,lib) => {
let libs; let libs;
if(!lib) { if(!lib) {
@ -131,7 +131,7 @@ const getRootComponents = async (appPath, pages ,lib) => {
return {components, generators}; return {components, generators};
} }
const fetchDerivedComponents = async (appPath, relativePath = "") => { const fetchscreens = async (appPath, relativePath = "") => {
const currentDir = join(appPath, "components", relativePath); const currentDir = join(appPath, "components", relativePath);
@ -159,7 +159,7 @@ const fetchDerivedComponents = async (appPath, relativePath = "") => {
components.push(component); components.push(component);
} else { } else {
const childComponents = await fetchDerivedComponents( const childComponents = await fetchscreens(
appPath, join(relativePath, item) appPath, join(relativePath, item)
); );
@ -172,4 +172,4 @@ const fetchDerivedComponents = async (appPath, relativePath = "") => {
return components; return components;
} }
module.exports.getRootComponents = getRootComponents; module.exports.getComponents = getComponents;

164
packages/standard-components/components.json

@ -28,13 +28,11 @@
} }
}, },
"button" : { "button" : {
"importPath": "button",
"name": "Button", "name": "Button",
"description": "an html <button />", "description": "an html <button />",
"props": { "props": {
"contentText": { "type": "string", "default": "Button" }, "contentText": { "type": "string", "default": "Button" },
"contentComponent": "component", "className": "string",
"className": {"type": "string", "default": "default"},
"disabled": "bool", "disabled": "bool",
"onClick": "event", "onClick": "event",
"background": "string", "background": "string",
@ -48,7 +46,6 @@
"tags": ["button"] "tags": ["button"]
}, },
"login" : { "login" : {
"importPath": "Login",
"name": "Login Control", "name": "Login Control",
"description": "A control that accepts username, password an also handles password resets", "description": "A control that accepts username, password an also handles password resets",
"props" : { "props" : {
@ -62,35 +59,7 @@
}, },
"tags": ["login", "credentials", "password", "logon"] "tags": ["login", "credentials", "password", "logon"]
}, },
"form" : {
"importPath": "Form",
"name": "Form",
"description": "A form - allgned fields with labels",
"props" : {
"containerClass": "string",
"formControls": {
"type":"array",
"elementDefinition": {
"label": "string",
"control":"component"
}
}
},
"tags": ["form"]
},
"textbox" : {
"importPath": "Textbox",
"name": "Textbox",
"description": "An input type=text or password",
"props" : {
"value": "string",
"hideValue": "bool",
"className": {"type": "string", "default": "default"}
},
"tags": ["form"]
},
"input" : { "input" : {
"importPath": "Input",
"name": "Input", "name": "Input",
"description": "An HTML input", "description": "An HTML input",
"props" : { "props" : {
@ -105,14 +74,14 @@
"tel", "time", "week"], "tel", "time", "week"],
"default":"text" "default":"text"
}, },
"className": {"type": "string", "default": "default"} "className": "string"
}, },
"tags": ["form"] "tags": ["form"]
}, },
"select" : { "select" : {
"importPath": "Input",
"name": "Input", "name": "Input",
"description": "An HTML input", "description": "An HTML input",
"children": false,
"props" : { "props" : {
"value": "string", "value": "string",
"options": { "options": {
@ -122,72 +91,16 @@
"value":"string" "value":"string"
} }
}, },
"className": {"type": "string", "default": "default"} "className": "string"
}, },
"tags": ["form"] "tags": ["form"]
}, },
"stackpanel": {
"importPath": "StackPanel",
"name": "StackPanel",
"description": "Layout elements in a stack, either horizontally or vertically",
"props" : {
"direction": {
"type": "options",
"options": ["horizontal", "vertical"],
"default":"horizontal"
},
"children": {
"type":"array",
"elementDefinition": {
"control":"component"
}
},
"width": {"type":"string","default":"auto"},
"height": {"type":"string","default":"auto"},
"containerClass":"string",
"itemContainerClass":"string",
"data": "state",
"dataItemComponent": "component",
"onLoad": "event"
},
"tags": ["div", "container", "layout", "panel"]
},
"grid": {
"importPath": "Grid",
"name": "Grid",
"description": "CSS Grid layout ",
"props" : {
"gridTemplateRows": "string",
"gridTemplateColumns": "string",
"children": {
"type":"array",
"elementDefinition": {
"component":"component",
"gridColumnStart":"string",
"gridColumnEnd":"string",
"gridRowStart":"string",
"gridRowEnd":"string",
"justifySelf": {
"type":"options",
"options":["start", "center", "end", "stretch"],
"default":"stretch"
}
}
},
"width": {"type":"string","default":"auto"},
"height": {"type":"string","default":"auto"},
"containerClass":"string",
"itemContainerClass":"string"
},
"tags": ["div", "container", "layout", "panel", "grid"]
},
"text": { "text": {
"importPath": "Text",
"name": "Text", "name": "Text",
"description": "stylable block of text", "description": "stylable block of text",
"children": false,
"props" : { "props" : {
"value": "string", "value": "string",
"containerClass": "string",
"font": "string", "font": "string",
"color": "string", "color": "string",
"textAlign": { "textAlign": {
@ -203,49 +116,11 @@
"options": [ "options": [
"top", "middle", "bottom" "top", "middle", "bottom"
] ]
},
"display": {
"type": "options",
"default":"inline",
"options": [
"inline", "block", "inline-block"
]
}
},
"tags": ["div", "container"]
},
"panel": {
"importPath": "Panel",
"name": "Panel",
"description": "A stylable div with a component inside",
"props" : {
"text": "string",
"component": "component",
"containerClass": "string",
"background": "string",
"border": "string",
"borderRadius":"string",
"font": "string",
"color": "string",
"padding": "string",
"margin": "string",
"hoverColor": "string",
"hoverBackground": "string",
"height":"string",
"width":"string",
"onClick": "event",
"display": {
"type": "options",
"default":"inline",
"options": [
"inline", "block", "inline-block"
]
} }
}, },
"tags": ["div", "container"] "tags": ["div", "container"]
}, },
"nav": { "nav": {
"importPath": "Nav",
"name": "Nav", "name": "Nav",
"description": "A nav - a side bar of buttons that control the currently active component", "description": "A nav - a side bar of buttons that control the currently active component",
"props" : { "props" : {
@ -257,13 +132,6 @@
"selectedItemBorder": "string", "selectedItemBorder": "string",
"itemHoverBackground": {"type" :"string", "default":"gainsboro"}, "itemHoverBackground": {"type" :"string", "default":"gainsboro"},
"itemHoverColor": {"type" :"string", "default":"black"}, "itemHoverColor": {"type" :"string", "default":"black"},
"items": {
"type": "array",
"elementDefinition" : {
"title": "string",
"component": "component"
}
},
"selectedItem":"string", "selectedItem":"string",
"hideNavBar":"bool" "hideNavBar":"bool"
@ -271,7 +139,6 @@
"tags": ["nav", "navigation", "sidebar"] "tags": ["nav", "navigation", "sidebar"]
}, },
"table": { "table": {
"importPath": "Table",
"name": "Table", "name": "Table",
"description": "An HTML table", "description": "An HTML table",
"props" : { "props" : {
@ -293,20 +160,10 @@
"tags": ["table"] "tags": ["table"]
}, },
"div": { "div": {
"importPath": "Div",
"name": "Div", "name": "Div",
"description": "An HTML div tag", "description": "An HTML div tag",
"props" : { "props" : {
"children": {
"type":"array",
"elementDefinition": {
"component":"component",
"className": "string"
}
},
"className":"string", "className":"string",
"data": "state",
"dataItemComponent": "component",
"onLoad": "event" "onLoad": "event"
}, },
"tags": ["div", "container", "layout"] "tags": ["div", "container", "layout"]
@ -370,16 +227,5 @@
"className":"string" "className":"string"
}, },
"tags": [] "tags": []
},
"if": {
"importPath": "if",
"name": "If",
"description": "An if condition.. if (CONDITION) THEN [display component A] ELSE [display component B]",
"props" : {
"condition": "string",
"thenComponent":{"type":"component", "required":true},
"elseComponent":"component"
},
"tags": []
} }
} }

1
packages/standard-components/package.json

@ -13,6 +13,7 @@
}, },
"devDependencies": { "devDependencies": {
"@budibase/client": "^0.0.15", "@budibase/client": "^0.0.15",
"@nx-js/compiler-util": "^2.0.0",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",

9
packages/standard-components/public/bundle.css

@ -1,13 +1,8 @@
#current_component.svelte-1xqz9vm{height:100%;width:100%} #current_component.svelte-uuhis1{height:100%;width:100%}
.root.svelte-10kw8to{display:grid}
.root.svelte-crnq0a{height:100%;display:grid;grid-template-columns:[left] 1fr [middle] auto [right] 1fr;grid-template-rows:[top] 1fr [center] auto [bottom] 1fr}.content.svelte-crnq0a{grid-column-start:middle;grid-row-start:center;width:400px}.logo-container.svelte-crnq0a{margin-bottom:20px .root.svelte-crnq0a{height:100%;display:grid;grid-template-columns:[left] 1fr [middle] auto [right] 1fr;grid-template-rows:[top] 1fr [center] auto [bottom] 1fr}.content.svelte-crnq0a{grid-column-start:middle;grid-row-start:center;width:400px}.logo-container.svelte-crnq0a{margin-bottom:20px
}.logo-container.svelte-crnq0a>img.svelte-crnq0a{max-width:100%}.login-button-container.svelte-crnq0a{text-align:right;margin-top:20px}.incorrect-details-panel.svelte-crnq0a{margin-top:30px;padding:10px;border-style:solid;border-width:1px;border-color:maroon;border-radius:1px;text-align:center;color:maroon;background-color:mistyrose}.form-root.svelte-crnq0a{display:grid;grid-template-columns:[label] auto [control] 1fr}.label.svelte-crnq0a{grid-column-start:label;padding:5px 10px;vertical-align:middle}.control.svelte-crnq0a{grid-column-start:control;padding:5px 10px}.default-input.svelte-crnq0a{font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;width:100%}.default-button.svelte-crnq0a{font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;color:#333;background-color:#f4f4f4;outline:none}.default-button.svelte-crnq0a:active{background-color:#ddd}.default-button.svelte-crnq0a:focus{border-color:#666} }.logo-container.svelte-crnq0a>img.svelte-crnq0a{max-width:100%}.login-button-container.svelte-crnq0a{text-align:right;margin-top:20px}.incorrect-details-panel.svelte-crnq0a{margin-top:30px;padding:10px;border-style:solid;border-width:1px;border-color:maroon;border-radius:1px;text-align:center;color:maroon;background-color:mistyrose}.form-root.svelte-crnq0a{display:grid;grid-template-columns:[label] auto [control] 1fr}.label.svelte-crnq0a{grid-column-start:label;padding:5px 10px;vertical-align:middle}.control.svelte-crnq0a{grid-column-start:control;padding:5px 10px}.default-input.svelte-crnq0a{font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;width:100%}.default-button.svelte-crnq0a{font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;color:#333;background-color:#f4f4f4;outline:none}.default-button.svelte-crnq0a:active{background-color:#ddd}.default-button.svelte-crnq0a:focus{border-color:#666}
.form-root.svelte-m9d6ue{display:grid;grid-template-columns:[label] auto [control] 1fr}.label.svelte-m9d6ue{grid-column-start:label;padding:5px 10px;vertical-align:middle}.control.svelte-m9d6ue{grid-column-start:control;padding:5px 10px}.overflow.svelte-m9d6ue{grid-column-start:overflow}.full-width.svelte-m9d6ue{width:100%}
.panel.svelte-1nuhpxd:hover{background:var(--hoverBackground);color:var(--hoverColor)}
.root.svelte-aihwli{height:100%;width:100%;grid-template-columns:[navbar] auto [content] 1fr;display:grid}.navbar.svelte-aihwli{grid-column:navbar;background:var(--navBarBackground);border:var(--navBarBorder);color:var(--navBarColor)}.navitem.svelte-aihwli{padding:10px 17px;cursor:pointer}.navitem.svelte-aihwli:hover{background:var(--itemHoverBackground);color:var(--itemHoverColor)}.navitem.selected.svelte-aihwli{background:var(--selectedItemBackground);border:var(--selectedItemBorder);color:var(--selectedItemColor)}.content.svelte-aihwli{grid-column:content} .root.svelte-aihwli{height:100%;width:100%;grid-template-columns:[navbar] auto [content] 1fr;display:grid}.navbar.svelte-aihwli{grid-column:navbar;background:var(--navBarBackground);border:var(--navBarBorder);color:var(--navBarColor)}.navitem.svelte-aihwli{padding:10px 17px;cursor:pointer}.navitem.svelte-aihwli:hover{background:var(--itemHoverBackground);color:var(--itemHoverColor)}.navitem.selected.svelte-aihwli{background:var(--selectedItemBackground);border:var(--selectedItemBorder);color:var(--selectedItemColor)}.content.svelte-aihwli{grid-column:content}
.default.svelte-1ec4wqj{width:100%;font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;width:100%}.default.svelte-1ec4wqj:disabled{color:#ccc} .default.svelte-1smqkrd{font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;color:#333;background-color:#f4f4f4;outline:none}.default.svelte-1smqkrd:active{background-color:#ddd}.default.svelte-1smqkrd:focus{border-color:#666}.border.svelte-1smqkrd{border:var(--border)}.color.svelte-1smqkrd{color:var(--color)}.background.svelte-1smqkrd{background:var(--background)}.hoverBorder.svelte-1smqkrd:hover{border:var(--hoverBorder)}.hoverColor.svelte-1smqkrd:hover{color:var(--hoverColor)}.hoverBack.svelte-1smqkrd:hover{background:var(--hoverBackground)}
.horizontal.svelte-osi0db{display:inline-block}.vertical.svelte-osi0db{display:block}
.default.svelte-181okpd{font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;color:#333;background-color:#f4f4f4;outline:none}.default.svelte-181okpd:active{background-color:#ddd}.default.svelte-181okpd:focus{border-color:#666}.border.svelte-181okpd{border:var(--border)}.color.svelte-181okpd{color:var(--color)}.background.svelte-181okpd{background:var(--background)}.hoverBorder.svelte-181okpd:hover{border:var(--hoverBorder)}.hoverColor.svelte-181okpd:hover{color:var(--hoverColor)}.hoverBack.svelte-181okpd:hover{background:var(--hoverBackground)}
.table-default.svelte-h8rqk6{width:100%;margin-bottom:1rem;color:#212529;border-collapse:collapse}.table-default.svelte-h8rqk6 .thead-default .th-default.svelte-h8rqk6{vertical-align:bottom;border-bottom:2px solid #dee2e6;font-weight:bold}.table-default.svelte-h8rqk6 .th-default.svelte-h8rqk6{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6;font-weight:normal}.th-default.svelte-h8rqk6{text-align:inherit}.table-default.svelte-h8rqk6 .tbody-default .tr-default.svelte-h8rqk6:hover{color:#212529;background-color:rgba(0,0,0,.075);cursor:pointer} .table-default.svelte-h8rqk6{width:100%;margin-bottom:1rem;color:#212529;border-collapse:collapse}.table-default.svelte-h8rqk6 .thead-default .th-default.svelte-h8rqk6{vertical-align:bottom;border-bottom:2px solid #dee2e6;font-weight:bold}.table-default.svelte-h8rqk6 .th-default.svelte-h8rqk6{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6;font-weight:normal}.th-default.svelte-h8rqk6{text-align:inherit}.table-default.svelte-h8rqk6 .tbody-default .tr-default.svelte-h8rqk6:hover{color:#212529;background-color:rgba(0,0,0,.075);cursor:pointer}
/*# sourceMappingURL=bundle.css.map */ /*# sourceMappingURL=bundle.css.map */

22
packages/standard-components/public/bundle.css.map

@ -3,28 +3,18 @@
"file": "bundle.css", "file": "bundle.css",
"sources": [ "sources": [
"..\\src\\Test\\TestApp.svelte", "..\\src\\Test\\TestApp.svelte",
"..\\src\\Grid.svelte",
"..\\src\\Login.svelte", "..\\src\\Login.svelte",
"..\\src\\Form.svelte",
"..\\src\\Panel.svelte",
"..\\src\\Nav.svelte", "..\\src\\Nav.svelte",
"..\\src\\Textbox.svelte",
"..\\src\\StackPanel.svelte",
"..\\src\\Button.svelte", "..\\src\\Button.svelte",
"..\\src\\Table.svelte" "..\\src\\Table.svelte"
], ],
"sourcesContent": [ "sourcesContent": [
"<script>\nimport createApp from \"./createApp\";\nimport { props } from \"./props\";\n\nlet _bb;\n\nconst _appPromise = createApp();\n_appPromise.then(a => _bb = a);\n\nconst testProps = props.table;\n\nlet currentComponent;\n\n$: {\n if(_bb && currentComponent) {\n _bb.hydrateComponent(testProps, currentComponent);\n }\n}\n\n\n\n</script>\n\n{#await _appPromise}\nloading\n{:then _bb}\n\n<div id=\"current_component\" bind:this={currentComponent}>\n</div>\n\n{/await}\n\n\n<style>\n#current_component {\n height: 100%;\n width: 100%;\n}\n</style>\n\n", "<script>\r\nimport createApp from \"./createApp\";\r\nimport { props } from \"./props\";\r\n\r\nlet _bb;\r\n\r\nconst _appPromise = createApp();\r\n_appPromise.then(a => _bb = a);\r\n\r\nconst testProps = props.divWithAFewControls;\r\n\r\nlet currentComponent;\r\n\r\n$: {\r\n if(_bb && currentComponent) {\r\n _bb.hydrateChildren(testProps._children, currentComponent);\r\n }\r\n}\r\n\r\n\r\n\r\n</script>\r\n\r\n{#await _appPromise}\r\nloading\r\n{:then _bb}\r\n\r\n<div id=\"current_component\" bind:this={currentComponent}>\r\n</div>\r\n\r\n{/await}\r\n\r\n\r\n<style>\r\n#current_component {\r\n height: 100%;\r\n width: 100%;\r\n}\r\n</style>\r\n\r\n",
"<script>\r\nimport { onMount } from 'svelte'\r\nimport {buildStyle} from \"./buildStyle\";\r\n\r\nexport let gridTemplateRows =\"\";\r\nexport let gridTemplateColumns =\"\";\r\nexport let children = [];\r\nexport let width = \"auto\";\r\nexport let height = \"auto\";\r\nexport let containerClass=\"\";\r\nexport let itemContainerClass=\"\";\r\n\r\n/*\"gridColumnStart\":\"string\",\r\n\"gridColumnEnd\":\"string\",\r\n\"gridRowStart\":\"string\",\r\n\"gridRowEnd\":\"string\"*/\r\n\r\n\r\nexport let _bb;\r\n\r\nlet style=\"\";\r\nlet htmlElements = {};\r\n\r\n$ : {\r\n if(_bb && htmlElements) {\r\n for(let el in htmlElements) {\r\n _bb.hydrateComponent(\r\n children[el].control,\r\n htmlElements[el]\r\n );\r\n }\r\n }\r\n}\r\n\r\nconst childStyle = child => \r\n buildStyle({\r\n \"grid-column-start\": child.gridColumnStart,\r\n \"grid-column-end\": child.gridColumnEnd,\r\n \"grid-column\": child.gridColumn,\r\n \"grid-row-start\": child.gridRowStart,\r\n \"grid-row-end\": child.gridRowStart,\r\n \"grid-row\": child.gridRow,\r\n });\r\n\r\n</script>\r\n\r\n<div class=\"root {containerClass}\"\r\n style=\"width: {width}; height: {height}; grid-template-columns: {gridTemplateColumns}; grid-template-rows: {gridTemplateRows};\">\r\n {#each children as child, index}\r\n <div class=\"{itemContainerClass}\"\r\n style={childStyle(child)}\r\n bind:this={htmlElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n display: grid;\r\n}\r\n\r\n</style>", "<script>\n\nimport Button from \"./Button.svelte\";\n\nexport let usernameLabel = \"Username\";\nexport let passwordLabel = \"Password\";\nexport let loginButtonLabel = \"Login\";\nexport let loginRedirect = \"\";\nexport let logo = \"\";\nexport let buttonClass = \"\";\nexport let inputClass=\"\"\n\nexport let _bb;\n\nlet username = \"\";\nlet password = \"\";\nlet busy = false;\nlet incorrect = false;\nlet _logo = \"\";\nlet _buttonClass = \"\";\nlet _inputClass = \"\";\n\n$: {\n _logo = _bb.relativeUrl(logo);\n _buttonClass = buttonClass || \"default-button\";\n _inputClass = inputClass || \"default-input\";\n}\n\nconst login = () => {\n busy = true;\n _bb.api.post(\"/api/authenticate\", {username, password})\n .then(r => {\n busy = false;\n if(r.status === 200) {\n return r.json();\n } else {\n incorrect = true;\n return;\n }\n })\n .then(user => {\n if(user) {\n localStorage.setItem(\"budibase:user\", JSON.stringify(user));\n location.reload();\n }\n })\n}\n\n</script>\n\n<div class=\"root\">\n\n <div class=\"content\">\n\n {#if _logo}\n <div class=\"logo-container\">\n <img src={_logo} alt=\"logo\"/>\n </div>\n {/if}\n\n <div class=\"form-root\">\n <div class=\"label\">\n {usernameLabel}\n </div>\n <div class=\"control\">\n <input bind:value={username} type=\"text\" class={_inputClass}/>\n </div>\n <div class=\"label\">\n {passwordLabel}\n </div>\n <div class=\"control\">\n <input bind:value={password} type=\"password\" class={_inputClass}/>\n </div>\n </div>\n\n <div class=\"login-button-container\">\n <button disabled={busy} \n on:click={login}\n class={_buttonClass}>\n {loginButtonLabel}\n </button>\n </div>\n\n {#if incorrect}\n <div class=\"incorrect-details-panel\">\n Incorrect username or password\n </div>\n {/if}\n\n </div>\n\n</div>\n\n<style>\n\n.root {\n height: 100%;\n display:grid;\n grid-template-columns: [left] 1fr [middle] auto [right] 1fr;\n grid-template-rows: [top] 1fr [center] auto [bottom] 1fr;\n}\n\n.content {\n grid-column-start: middle;\n grid-row-start: center;\n width: 400px;\n}\n\n.logo-container {\n margin-bottom: 20px\n}\n\n.logo-container > img {\n max-width: 100%;\n}\n\n.login-button-container {\n text-align: right;\n margin-top: 20px;\n}\n\n.incorrect-details-panel {\n margin-top: 30px;\n padding: 10px;\n border-style: solid;\n border-width: 1px;\n border-color: maroon;\n border-radius: 1px;\n text-align: center;\n color: maroon;\n background-color: mistyrose;\n}\n\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n\n.default-input {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default-button {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default-button:active {\n\tbackground-color: #ddd;\n}\n\n.default-button:focus {\n\tborder-color: #666;\n}\n\n</style>",
"<script>\n\nimport Textbox from \"./Textbox.svelte\";\nimport Form from \"./Form.svelte\";\nimport Button from \"./Button.svelte\";\n\nexport let usernameLabel = \"Username\";\nexport let passwordLabel = \"Password\";\nexport let loginButtonLabel = \"Login\";\nexport let loginRedirect = \"\";\nexport let logo = \"\";\nexport let buttonClass = \"\";\nexport let inputClass=\"\"\n\nexport let _bb;\n\nlet username = \"\";\nlet password = \"\";\nlet busy = false;\nlet incorrect = false;\nlet _logo = \"\";\nlet _buttonClass = \"\";\nlet _inputClass = \"\";\n\n$: {\n _logo = _bb.relativeUrl(logo);\n _buttonClass = buttonClass || \"default-button\";\n _inputClass = inputClass || \"default-input\";\n}\n\nconst login = () => {\n busy = true;\n _bb.api.post(\"/api/authenticate\", {username, password})\n .then(r => {\n busy = false;\n if(r.status === 200) {\n return r.json();\n } else {\n incorrect = true;\n return;\n }\n })\n .then(user => {\n if(user) {\n localStorage.setItem(\"budibase:user\", JSON.stringify(user));\n location.reload();\n }\n })\n}\n\n</script>\n\n<div class=\"root\">\n\n <div class=\"content\">\n\n {#if _logo}\n <div class=\"logo-container\">\n <img src={_logo} alt=\"logo\"/>\n </div>\n {/if}\n\n <div class=\"form-root\">\n <div class=\"label\">\n {usernameLabel}\n </div>\n <div class=\"control\">\n <input bind:value={username} type=\"text\" class={_inputClass}/>\n </div>\n <div class=\"label\">\n {passwordLabel}\n </div>\n <div class=\"control\">\n <input bind:value={password} type=\"password\" class={_inputClass}/>\n </div>\n </div>\n\n <div class=\"login-button-container\">\n <button disabled={busy} \n on:click={login}\n class={_buttonClass}>\n {loginButtonLabel}\n </button>\n </div>\n\n {#if incorrect}\n <div class=\"incorrect-details-panel\">\n Incorrect username or password\n </div>\n {/if}\n\n </div>\n\n</div>\n\n<style>\n\n.root {\n height: 100%;\n display:grid;\n grid-template-columns: [left] 1fr [middle] auto [right] 1fr;\n grid-template-rows: [top] 1fr [center] auto [bottom] 1fr;\n}\n\n.content {\n grid-column-start: middle;\n grid-row-start: center;\n width: 400px;\n}\n\n.logo-container {\n margin-bottom: 20px\n}\n\n.logo-container > img {\n max-width: 100%;\n}\n\n.login-button-container {\n text-align: right;\n margin-top: 20px;\n}\n\n.incorrect-details-panel {\n margin-top: 30px;\n padding: 10px;\n border-style: solid;\n border-width: 1px;\n border-color: maroon;\n border-radius: 1px;\n text-align: center;\n color: maroon;\n background-color: mistyrose;\n}\n\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n\n.default-input {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default-button {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default-button:active {\n\tbackground-color: #ddd;\n}\n\n.default-button:focus {\n\tborder-color: #666;\n}\n\n</style>", "<script>\r\nimport cssVars from \"./cssVars\";\r\n\r\nexport let navBarBackground = \"\";\r\nexport let navBarBorder=\"\";\r\nexport let navBarColor=\"\";\r\nexport let selectedItemBackground=\"\";\r\nexport let selectedItemColor=\"\";\r\nexport let selectedItemBorder=\"\";\r\nexport let itemHoverBackground=\"\";\r\nexport let itemHoverColor=\"\";\r\nexport let hideNavBar=false;\r\nexport let selectedItem=\"\";\r\n\r\nexport let _children;\r\nexport let _bb;\r\n\r\nlet selectedIndex = -1;\r\nlet styleVars={};\r\nlet components = {};\r\nlet componentElements = {}\r\n\r\n\r\nconst hasComponentElements = () => \r\n Object.getOwnPropertyNames(componentElements).length > 0;\r\n\r\n$: {\r\n styleVars = {\r\n navBarBackground, navBarBorder,\r\n navBarColor, selectedItemBackground,\r\n selectedItemColor, selectedItemBorder,\r\n itemHoverBackground, itemHoverColor\r\n };\r\n\r\n if(_children && _children.length > 0 && hasComponentElements()) {\r\n const currentSelectedItem = selectedIndex > 0\r\n ? _children[selectedIndex].title\r\n : \"\";\r\n if(selectedItem && currentSelectedItem !== selectedItem) {\r\n let i=0;\r\n for(let child of _children) {\r\n if(child.title === selectedItem) {\r\n onSelectItem(i)();\r\n }\r\n i++;\r\n }\r\n } else if(!currentSelectedItem) {\r\n onSelectItem(0);\r\n }\r\n }\r\n}\r\n\r\nconst onSelectItem = (index) => () => {\r\n selectedIndex = index;\r\n if(!components[index]) {\r\n const comp = _bb.hydrateChildren(\r\n _children[index].children, componentElements[index]);\r\n components[index] = comp; \r\n }\r\n}\r\n\r\n\r\n</script>\r\n\r\n<div class=\"root\" use:cssVars={styleVars}>\r\n {#if !hideNavBar}\r\n <div class=\"navbar\">\r\n {#each _children as navItem, index}\r\n <div class=\"navitem\"\r\n on:click={onSelectItem(index)}\r\n class:selected={selectedIndex === index}>\r\n {navItem.title}\r\n </div>\r\n {/each}\r\n </div>\r\n {/if}\r\n {#each _children as navItem, index}\r\n <div class=\"content\"\r\n bind:this={componentElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n height: 100%;\r\n width:100%;\r\n grid-template-columns: [navbar] auto [content] 1fr;\r\n display: grid;\r\n}\r\n\r\n.navbar {\r\n grid-column: navbar;\r\n background: var(--navBarBackground);\r\n border: var(--navBarBorder);\r\n color: var(--navBarColor);\r\n}\r\n\r\n.navitem {\r\n padding: 10px 17px;\r\n cursor: pointer;\r\n}\r\n\r\n.navitem:hover {\r\n background: var(--itemHoverBackground);\r\n color: var(--itemHoverColor);\r\n}\r\n\r\n.navitem.selected {\r\n background: var(--selectedItemBackground);\r\n border: var(--selectedItemBorder);\r\n color: var(--selectedItemColor);\r\n}\r\n\r\n.content {\r\n grid-column: content;\r\n}\r\n\r\n</style>\r\n\r\n",
"<script>\nexport let containerClass = \"\";\nexport let formControls = [];\n\nexport let _bb;\n\nlet htmlElements = {};\nlet labels = {};\n\n$ : {\n let cIndex = 0;\n for(let c of formControls) {\n labels[cIndex] = c.label;\n cIndex++;\n }\n\n if(_bb && htmlElements) {\n for(let el in htmlElements) {\n _bb.hydrateComponent(\n formControls[el].control,\n htmlElements[el]\n );\n }\n }\n}\n\n</script>\n\n<div class=\"form-root {containerClass}\">\n {#each formControls as child, index}\n <div class=\"label\">{labels[index]}</div>\n <div class=\"control\"\n bind:this={htmlElements[index]}>\n </div>\n {/each}\n</div>\n\n<style>\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n.overflow {\n grid-column-start: overflow;\n}\n.full-width {\n width: 100%;\n}\n</style>", "<script>\r\nimport cssVars from \"./cssVars\";\r\nimport {buildStyle} from \"./buildStyle\";\r\nexport let className = \"default\";\r\nexport let disabled = false;\r\nexport let contentText;\r\nexport let onClick;\r\nexport let background;\r\nexport let color;\r\nexport let border;\r\nexport let padding;\r\nexport let hoverColor;\r\nexport let hoverBackground;\r\nexport let hoverBorder;\r\nexport let _children;\r\n\r\nexport let _bb;\r\nlet theButton;\r\nlet cssVariables;\r\nlet buttonStyles;\r\n\r\nlet customHoverColorClass;\r\nlet customHoverBorderClass;\r\nlet customHoverBackClass;\r\n\r\nlet customClasses = \"\";\r\n\r\nconst createClasses = (classes) => {\r\n\tlet all = \"\";\r\n\tfor(let cls in classes) {\r\n\t\tif(classes[cls]) {\r\n\t\t\tall = all + \" \" + cls;\r\n\t\t}\r\n\t}\r\n\treturn all;\r\n}\r\n\r\n$:{\r\n\tif(_bb && theButton && _children && _children.length)\r\n\t\t_bb.hydrateChildren(_children, theButton);\r\n}\r\n\r\n$:{\r\n\tcssVariables = {\r\n\t\thoverColor, hoverBorder,\r\n\t\thoverBackground,\r\n\t\tbackground, color, border,\r\n\t};\r\n\r\n\tbuttonStyles = buildStyle({\r\n\t\tpadding\r\n\t});\t\r\n\t\r\n\tcustomClasses = createClasses({\r\n\t\thoverColor, hoverBorder, hoverBackground,\r\n\t\tbackground, border, color\r\n\t});\r\n}\r\n\r\n\r\n\r\n\r\nconst clickHandler = () => {\r\n\t_bb.call(onClick);\r\n}\r\n\r\n</script>\r\n\r\n\r\n<button bind:this={theButton}\r\n\t\tuse:cssVars={cssVariables} \r\n\t\tclass=\"{className} {customClasses}\" \r\n\t\tdisabled={disabled || false} \r\n\t\ton:click={clickHandler} \r\n\t\tstyle={buttonStyles}>\r\n {#if !_children || _children.length === 0}\r\n {contentText}\r\n {/if}\r\n</button>\r\n\r\n\r\n<style>\r\n\r\n.default {\r\n\tfont-family: inherit;\r\n\tfont-size: inherit;\r\n\tpadding: 0.4em;\r\n\tmargin: 0 0 0.5em 0;\r\n\tbox-sizing: border-box;\r\n\tborder: 1px solid #ccc;\r\n\tborder-radius: 2px;\r\n\tcolor: #333;\r\n\tbackground-color: #f4f4f4;\r\n\toutline: none;\r\n}\r\n\r\n.default:active {\r\n\tbackground-color: #ddd;\r\n}\r\n\r\n.default:focus {\r\n\tborder-color: #666;\r\n}\r\n\r\n.border {\r\n\tborder: var(--border);\r\n}\r\n\r\n.color {\r\n\tcolor: var(--color);\r\n}\r\n\r\n.background {\r\n\tbackground: var(--background);\r\n}\r\n\r\n.hoverBorder:hover {\r\n\tborder: var(--hoverBorder);\r\n}\r\n\r\n.hoverColor:hover {\r\n\tcolor: var(--hoverColor);\r\n}\r\n\r\n.hoverBack:hover {\r\n\tbackground: var(--hoverBackground);\r\n}\r\n\r\n\r\n</style>",
"<script>\nimport {buildStyle} from \"./buildStyle\";\nimport cssVars from \"./cssVars\";\n\nexport let component=\"\";\nexport let text=\"\";\nexport let containerClass=\"\";\nexport let background=\"\";\nexport let border=\"\";\nexport let borderRadius=\"\";\nexport let font=\"\";\nexport let display=\"\";\nexport let textAlign=\"\";\nexport let color=\"\";\nexport let padding=\"\";\nexport let margin=\"\";\nexport let hoverBackground=\"\";\nexport let hoverColor=\"\";\nexport let onClick;\nexport let height;\nexport let width;\n\nexport let _bb;\n\nlet styleVars;\nlet style=\"\";\nlet componentElement;\nlet componentInitialised=false;\n\n$: {\n style=buildStyle({\n border, background, font, margin,\n padding, display, color, height, width,\n \"text-align\": textAlign,\n \"border-radius\":borderRadius,\n cursor: onClick ? \"pointer\" : \"none\"\n });\n\n if(_bb && component && componentElement && !componentInitialised) {\n _bb.hydrateComponent(component, componentElement);\n componentInitialised = true;\n }\n\n styleVars = {\n hoverBackground:hoverBackground || background, \n hoverColor:hoverColor || color\n }\n}\n\nconst clickHandler = () => {\n if(onClick) {\n _bb.call(onClick);\n }\n}\n\n</script>\n\n<div class=\"{containerClass} panel\" \n style={style}\n use:cssVars={styleVars}\n bind:this={componentElement}\n on:click={clickHandler}>\n {component && component._component ? \"\" : text}\n</div>\n\n<style>\n\n.panel:hover {\n background: var(--hoverBackground);\n color: var(--hoverColor);\n\n}\n\n</style>\n", "<script>\r\n\r\nexport let columns=[];\r\nexport let data=\"\";\r\nexport let tableClass=\"\";\r\nexport let theadClass=\"\";\r\nexport let tbodyClass=\"\";\r\nexport let trClass=\"\";\r\nexport let thClass=\"\";\r\nexport let onRowClick;\r\n\r\nexport let _bb;\r\n\r\nconst rowClickHandler = (row) => () => {\r\n _bb.call(onRowClick, row);\r\n}\r\n\r\nconst cellValue = (colIndex, row) => {\r\n const val = _bb.getStateOrValue(\r\n _bb.props.columns[colIndex].value\r\n , row)\r\n return val;\r\n}\r\n\r\n\r\n</script>\r\n\r\n <table class={tableClass}>\r\n <thead class={theadClass}>\r\n <tr class={trClass}>\r\n {#each columns as col}\r\n <th class={thClass}>{col.title}</th>\r\n {/each}\r\n </tr>\r\n </thead>\r\n <tbody class={tbodyClass}>\r\n {#if data}\r\n {#each data as row}\r\n <tr class={trClass}\r\n on:click={rowClickHandler(row)} >\r\n {#each columns as col, index}\r\n <th class={thClass}>{cellValue(index, row)}</th>\r\n {/each}\r\n </tr>\r\n {/each}\r\n {/if}\r\n </tbody>\r\n</table> \r\n\r\n<style>\r\n\r\n.table-default {\r\n width: 100%;\r\n margin-bottom: 1rem;\r\n color: #212529;\r\n border-collapse: collapse;\r\n}\r\n\r\n.table-default .thead-default .th-default {\r\n vertical-align: bottom;\r\n border-bottom: 2px solid #dee2e6;\r\n font-weight: bold;\r\n}\r\n\r\n.table-default .th-default {\r\n padding: .75rem;\r\n vertical-align: top;\r\n border-top: 1px solid #dee2e6;\r\n font-weight: normal;\r\n}\r\n\r\n.th-default {\r\n text-align: inherit;\r\n}\r\n\r\n.table-default .tbody-default .tr-default:hover {\r\n color: #212529;\r\n background-color: rgba(0,0,0,.075);\r\n cursor: pointer;\r\n}\r\n\r\n</style>"
"<script>\r\nimport cssVars from \"./cssVars\";\r\n\r\nexport let navBarBackground = \"\";\r\nexport let navBarBorder=\"\";\r\nexport let navBarColor=\"\";\r\nexport let selectedItemBackground=\"\";\r\nexport let selectedItemColor=\"\";\r\nexport let selectedItemBorder=\"\";\r\nexport let itemHoverBackground=\"\";\r\nexport let itemHoverColor=\"\";\r\nexport let items = [];\r\nexport let hideNavBar=false;\r\nexport let selectedItem=\"\";\r\n\r\nexport let _bb;\r\n\r\nlet selectedIndex = -1;\r\nlet styleVars={};\r\nlet components = {};\r\nlet componentElements = {}\r\n\r\n\r\nconst hasComponentElements = () => \r\n Object.getOwnPropertyNames(componentElements).length > 0;\r\n\r\n$: {\r\n styleVars = {\r\n navBarBackground, navBarBorder,\r\n navBarColor, selectedItemBackground,\r\n selectedItemColor, selectedItemBorder,\r\n itemHoverBackground, itemHoverColor\r\n };\r\n\r\n if(items && items.length > 0 && hasComponentElements()) {\r\n const currentSelectedItem = selectedIndex > 0\r\n ? items[selectedIndex].title\r\n : \"\";\r\n if(selectedItem && currentSelectedItem !== selectedItem) {\r\n let i=0;\r\n for(let item of items) {\r\n if(item.title === selectedItem) {\r\n onSelectItem(i)();\r\n }\r\n i++;\r\n }\r\n } else if(!currentSelectedItem) {\r\n onSelectItem(0);\r\n }\r\n }\r\n}\r\n\r\nconst onSelectItem = (index) => () => {\r\n selectedIndex = index;\r\n if(!components[index]) {\r\n const comp = _bb.hydrateComponent(\r\n items[index].component, componentElements[index]);\r\n components[index] = comp; \r\n }\r\n}\r\n\r\n\r\n</script>\r\n\r\n<div class=\"root\" use:cssVars={styleVars}>\r\n {#if !hideNavBar}\r\n <div class=\"navbar\">\r\n {#each items as navItem, index}\r\n <div class=\"navitem\"\r\n on:click={onSelectItem(index)}\r\n class:selected={selectedIndex === index}>\r\n {navItem.title}\r\n </div>\r\n {/each}\r\n </div>\r\n {/if}\r\n {#each items as navItem, index}\r\n\r\n <div class=\"content\"\r\n bind:this={componentElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n height: 100%;\r\n width:100%;\r\n grid-template-columns: [navbar] auto [content] 1fr;\r\n display: grid;\r\n}\r\n\r\n.navbar {\r\n grid-column: navbar;\r\n background: var(--navBarBackground);\r\n border: var(--navBarBorder);\r\n color: var(--navBarColor);\r\n}\r\n\r\n.navitem {\r\n padding: 10px 17px;\r\n cursor: pointer;\r\n}\r\n\r\n.navitem:hover {\r\n background: var(--itemHoverBackground);\r\n color: var(--itemHoverColor);\r\n}\r\n\r\n.navitem.selected {\r\n background: var(--selectedItemBackground);\r\n border: var(--selectedItemBorder);\r\n color: var(--selectedItemColor);\r\n}\r\n\r\n.content {\r\n grid-column: content;\r\n}\r\n\r\n</style>\r\n\r\n",
"<script>\n\nexport let value=\"\";\nexport let hideValue = false;\nexport let className = \"default\";\n\nexport let _bb;\n\nlet actualValue = \"\";\n\nconst onchange = (ev) => {\n\tif(_bb) {\n\t\t_bb.setStateFromBinding(_bb.bindings.value, ev.target.value);\n\t}\n}\n\n</script>\n\n{#if hideValue}\n<input class={className} \n\t type=\"password\" \n\t value={value} \n\t on:change={onchange}/>\n{:else}\n<input class={className} \n\t type=\"text\" \n\t value={value}\n\t on:change={onchange}/>\n{/if}\n\n<style>\n.default {\n width: 100%;\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default:disabled {\n\tcolor: #ccc;\n}\n\n</style>",
"<script>\n\nimport { emptyProps } from \"./emptyProps\";\n\nexport let direction = \"horizontal\";\nexport let children = [];\nexport let width = \"auto\";\nexport let height = \"auto\";\nexport let containerClass=\"\";\nexport let itemContainerClass=\"\";\nexport let onLoad;\n\nexport let data=[];\nexport let dataItemComponent;\n\nexport let _bb;\n\nlet staticHtmlElements = {};\nlet staticComponents = {};\nlet dataBoundElements = {};\nlet dataBoundComponents = {};\n\nlet onLoadCalled = false;\n\nconst hasDataBoundComponents = () => \n Object.getOwnPropertyNames(dataBoundComponents).length > 0;\n\nconst hasData = () => \n Array.isArray(data) && data.length > 0;\n\nconst hasStaticComponents = () => {\n return Object.getOwnPropertyNames(staticComponents).length > 0;\n}\n\n$: {\n\n if(staticHtmlElements) {\n if(hasStaticComponents()) {\n for(let c in staticComponents) {\n staticComponents[c].$destroy();\n }\n staticComponents = {};\n }\n\n for(let el in staticHtmlElements) {\n staticComponents[el] = _bb.hydrateComponent(\n children[el].control,\n staticHtmlElements[el]\n );\n }\n }\n \n\n if(hasDataBoundComponents()) {\n for(let c in dataBoundComponents) {\n dataBoundComponents[c].$destroy();\n }\n dataBoundComponents = {};\n }\n\n if(hasData()) {\n let index = 0;\n for(let d in dataBoundElements) {\n _bb.hydrateComponent(\n dataItemComponent,\n dataBoundElements[d],\n data[parseInt(d)]\n );\n }\n }\n\n if(!onLoadCalled && onLoad && !onLoad.isPlaceholder) {\n _bb.call(onLoad);\n onLoadCalled = true;\n }\n}\n\n\n</script>\n\n<div class=\"root {containerClass}\"\n style=\"width: {width}; height: {height}\">\n\n {#if children}\n {#each children as child, index}\n <div class={direction}>\n <div class=\"{itemContainerClass}\"\n bind:this={staticHtmlElements[index]}>\n </div>\n </div>\n {/each}\n {/if}\n\n {#if data && data.length > 0}\n {#each data as child, index}\n <div class={direction}>\n <div class=\"{itemContainerClass}\"\n bind:this={dataBoundElements[index]}>\n </div>\n </div>\n {/each}\n {/if}\n</div>\n\n<style>\n\n.horizontal {\n display:inline-block;\n}\n\n.vertical {\n display: block;\n}\n\n</style>",
"<script>\nimport cssVars from \"./cssVars\";\nimport {buildStyle} from \"./buildStyle\";\nexport let className = \"default\";\nexport let disabled = false;\nexport let contentText;\nexport let contentComponent;\nexport let onClick;\nexport let background;\nexport let color;\nexport let border;\nexport let padding;\nexport let hoverColor;\nexport let hoverBackground;\nexport let hoverBorder;\n\nexport let _bb;\nlet contentComponentContainer;\nlet cssVariables;\nlet buttonStyles;\n\nlet customHoverColorClass;\nlet customHoverBorderClass;\nlet customHoverBackClass;\n\nlet customClasses = \"\";\n\nconst createClasses = (classes) => {\n\tlet all = \"\";\n\tfor(let cls in classes) {\n\t\tif(classes[cls]) {\n\t\t\tall = all + \" \" + cls;\n\t\t}\n\t}\n\treturn all;\n}\n\t\n\n$:{\n\tif(_bb && contentComponentContainer && contentComponent._component)\n\t\t_bb.hydrateComponent(contentComponent, contentComponentContainer);\n\n\tcssVariables = {\n\t\thoverColor, hoverBorder,\n\t\thoverBackground,\n\t\tbackground, color, border,\n\t};\n\n\tbuttonStyles = buildStyle({\n\t\tpadding\n\t});\t\n\t\n\tcustomClasses = createClasses({\n\t\thoverColor, hoverBorder, hoverBackground,\n\t\tbackground, border, color\n\t});\n\t\n}\n\n\n\n\nconst clickHandler = () => {\n\t_bb.call(onClick);\n}\n\n</script>\n\n\n<button use:cssVars={cssVariables} \n\t\tclass=\"{className} {customClasses}\" \n\t\tdisabled={disabled || false} \n\t\ton:click={clickHandler} \n\t\tstyle={buttonStyles}>\n {#if contentComponent && contentComponent._component}\n\t<div bind:this={contentComponentContainer}>\n\t</div>\n {:else if contentText}\n {contentText}\n {:else}\n <slot />\n {/if}\n</button>\n\n\n<style>\n\n.default {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default:active {\n\tbackground-color: #ddd;\n}\n\n.default:focus {\n\tborder-color: #666;\n}\n\n.border {\n\tborder: var(--border);\n}\n\n.color {\n\tcolor: var(--color);\n}\n\n.background {\n\tbackground: var(--background);\n}\n\n.hoverBorder:hover {\n\tborder: var(--hoverBorder);\n}\n\n.hoverColor:hover {\n\tcolor: var(--hoverColor);\n}\n\n.hoverBack:hover {\n\tbackground: var(--hoverBackground);\n}\n\n\n</style>",
"<script>\r\n\r\nexport let columns=[];\r\nexport let data=\"\";\r\nexport let tableClass=\"\";\r\nexport let theadClass=\"\";\r\nexport let tbodyClass=\"\";\r\nexport let trClass=\"\";\r\nexport let thClass=\"\";\r\nexport let onRowClick;\r\n\r\nexport let _bb;\r\n\r\nconst rowClickHandler = (row) => () => {\r\n _bb.call(onRowClick, row);\r\n}\r\n\r\nconst cellValue = (colIndex, row) => \r\n _bb.getStateOrValue(\r\n _bb.bindings.columns[colIndex].value\r\n , row)\r\n\r\n\r\n</script>\r\n\r\n <table class={tableClass}>\r\n <thead class={theadClass}>\r\n <tr class={trClass}>\r\n {#each columns as col}\r\n <th class={thClass}>{col.title}</th>\r\n {/each}\r\n </tr>\r\n </thead>\r\n <tbody class={tbodyClass}>\r\n {#if data}\r\n {#each data as row}\r\n <tr class={trClass}\r\n on:click={rowClickHandler(row)} >\r\n {#each columns as col, index}\r\n <th class={thClass}>{cellValue(index, row)}</th>\r\n {/each}\r\n </tr>\r\n {/each}\r\n {/if}\r\n </tbody>\r\n</table> \r\n\r\n<style>\r\n\r\n.table-default {\r\n width: 100%;\r\n margin-bottom: 1rem;\r\n color: #212529;\r\n border-collapse: collapse;\r\n}\r\n\r\n.table-default .thead-default .th-default {\r\n vertical-align: bottom;\r\n border-bottom: 2px solid #dee2e6;\r\n font-weight: bold;\r\n}\r\n\r\n.table-default .th-default {\r\n padding: .75rem;\r\n vertical-align: top;\r\n border-top: 1px solid #dee2e6;\r\n font-weight: normal;\r\n}\r\n\r\n.th-default {\r\n text-align: inherit;\r\n}\r\n\r\n.table-default .tbody-default .tr-default:hover {\r\n color: #212529;\r\n background-color: rgba(0,0,0,.075);\r\n cursor: pointer;\r\n}\r\n\r\n</style>"
], ],
"names": [], "names": [],
"mappings": "AAkCA,kBAAkB,eAAC,CAAC,AAChB,MAAM,CAAE,IAAI,CACZ,KAAK,CAAE,IAAI,AACf,CAAC;ACqBD,KAAK,eAAC,CAAC,AACH,OAAO,CAAE,IAAI,AACjB,CAAC;ACqCD,KAAK,cAAC,CAAC,AACH,MAAM,CAAE,IAAI,CACZ,QAAQ,IAAI,CACZ,qBAAqB,CAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAC3D,kBAAkB,CAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,AAC5D,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,MAAM,CACzB,cAAc,CAAE,MAAM,CACtB,KAAK,CAAE,KAAK,AAChB,CAAC,AAED,eAAe,cAAC,CAAC,AACb,aAAa,CAAE,IAAI;AACvB,CAAC,AAED,6BAAe,CAAG,GAAG,cAAC,CAAC,AACnB,SAAS,CAAE,IAAI,AACnB,CAAC,AAED,uBAAuB,cAAC,CAAC,AACrB,UAAU,CAAE,KAAK,CACjB,UAAU,CAAE,IAAI,AACpB,CAAC,AAED,wBAAwB,cAAC,CAAC,AACtB,UAAU,CAAE,IAAI,CAChB,OAAO,CAAE,IAAI,CACb,YAAY,CAAE,KAAK,CACnB,YAAY,CAAE,GAAG,CACjB,YAAY,CAAE,MAAM,CACpB,aAAa,CAAE,GAAG,CAClB,UAAU,CAAE,MAAM,CAClB,KAAK,CAAE,MAAM,CACb,gBAAgB,CAAE,SAAS,AAC/B,CAAC,AAED,UAAU,cAAC,CAAC,AACR,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,AACrD,CAAC,AAED,MAAM,cAAC,CAAC,AACJ,iBAAiB,CAAE,KAAK,CACxB,OAAO,CAAE,GAAG,CAAC,IAAI,CACjB,cAAc,CAAE,MAAM,AAC1B,CAAC,AACD,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,OAAO,CAC1B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AAED,cAAc,cAAC,CAAC,AACf,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACnB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,eAAe,cAAC,CAAC,AAChB,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACtB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,CACX,gBAAgB,CAAE,OAAO,CACzB,OAAO,CAAE,IAAI,AACd,CAAC,AAED,6BAAe,OAAO,AAAC,CAAC,AACvB,gBAAgB,CAAE,IAAI,AACvB,CAAC,AAED,6BAAe,MAAM,AAAC,CAAC,AACtB,YAAY,CAAE,IAAI,AACnB,CAAC;AC9ID,UAAU,cAAC,CAAC,AACR,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,AACrD,CAAC,AAED,MAAM,cAAC,CAAC,AACJ,iBAAiB,CAAE,KAAK,CACxB,OAAO,CAAE,GAAG,CAAC,IAAI,CACjB,cAAc,CAAE,MAAM,AAC1B,CAAC,AACD,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,OAAO,CAC1B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AACD,SAAS,cAAC,CAAC,AACP,iBAAiB,CAAE,QAAQ,AAC/B,CAAC,AACD,WAAW,cAAC,CAAC,AACT,KAAK,CAAE,IAAI,AACf,CAAC;ACUD,qBAAM,MAAM,AAAC,CAAC,AACV,UAAU,CAAE,IAAI,iBAAiB,CAAC,CAClC,KAAK,CAAE,IAAI,YAAY,CAAC,AAE5B,CAAC;ACeD,KAAK,cAAC,CAAC,AACH,MAAM,CAAE,IAAI,CACZ,MAAM,IAAI,CACV,qBAAqB,CAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAClD,OAAO,CAAE,IAAI,AACjB,CAAC,AAED,OAAO,cAAC,CAAC,AACL,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,IAAI,kBAAkB,CAAC,CACnC,MAAM,CAAE,IAAI,cAAc,CAAC,CAC3B,KAAK,CAAE,IAAI,aAAa,CAAC,AAC7B,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,OAAO,CAAE,IAAI,CAAC,IAAI,CAClB,MAAM,CAAE,OAAO,AACnB,CAAC,AAED,sBAAQ,MAAM,AAAC,CAAC,AACZ,UAAU,CAAE,IAAI,qBAAqB,CAAC,CACtC,KAAK,CAAE,IAAI,gBAAgB,CAAC,AAChC,CAAC,AAED,QAAQ,SAAS,cAAC,CAAC,AACf,UAAU,CAAE,IAAI,wBAAwB,CAAC,CACzC,MAAM,CAAE,IAAI,oBAAoB,CAAC,CACjC,KAAK,CAAE,IAAI,mBAAmB,CAAC,AACnC,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,WAAW,CAAE,OAAO,AACxB,CAAC;ACvFD,QAAQ,eAAC,CAAC,AACN,KAAK,CAAE,IAAI,CACd,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACnB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,uBAAQ,SAAS,AAAC,CAAC,AAClB,KAAK,CAAE,IAAI,AACZ,CAAC;AC6DD,WAAW,cAAC,CAAC,AACT,QAAQ,YAAY,AACxB,CAAC,AAED,SAAS,cAAC,CAAC,AACP,OAAO,CAAE,KAAK,AAClB,CAAC;ACzBD,QAAQ,eAAC,CAAC,AACT,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACtB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,CACX,gBAAgB,CAAE,OAAO,CACzB,OAAO,CAAE,IAAI,AACd,CAAC,AAED,uBAAQ,OAAO,AAAC,CAAC,AAChB,gBAAgB,CAAE,IAAI,AACvB,CAAC,AAED,uBAAQ,MAAM,AAAC,CAAC,AACf,YAAY,CAAE,IAAI,AACnB,CAAC,AAED,OAAO,eAAC,CAAC,AACR,MAAM,CAAE,IAAI,QAAQ,CAAC,AACtB,CAAC,AAED,MAAM,eAAC,CAAC,AACP,KAAK,CAAE,IAAI,OAAO,CAAC,AACpB,CAAC,AAED,WAAW,eAAC,CAAC,AACZ,UAAU,CAAE,IAAI,YAAY,CAAC,AAC9B,CAAC,AAED,2BAAY,MAAM,AAAC,CAAC,AACnB,MAAM,CAAE,IAAI,aAAa,CAAC,AAC3B,CAAC,AAED,0BAAW,MAAM,AAAC,CAAC,AAClB,KAAK,CAAE,IAAI,YAAY,CAAC,AACzB,CAAC,AAED,yBAAU,MAAM,AAAC,CAAC,AACjB,UAAU,CAAE,IAAI,iBAAiB,CAAC,AACnC,CAAC;ACjFD,cAAc,cAAC,CAAC,AACZ,KAAK,CAAE,IAAI,CACX,aAAa,CAAE,IAAI,CACnB,KAAK,CAAE,OAAO,CACd,eAAe,CAAE,QAAQ,AAC7B,CAAC,AAED,4BAAc,CAAC,cAAc,CAAC,WAAW,cAAC,CAAC,AACvC,cAAc,CAAE,MAAM,CACtB,aAAa,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAChC,WAAW,CAAE,IAAI,AACrB,CAAC,AAED,4BAAc,CAAC,WAAW,cAAC,CAAC,AACxB,OAAO,CAAE,MAAM,CACf,cAAc,CAAE,GAAG,CACnB,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAC7B,WAAW,CAAE,MAAM,AACvB,CAAC,AAED,WAAW,cAAC,CAAC,AACT,UAAU,CAAE,OAAO,AACvB,CAAC,AAED,4BAAc,CAAC,cAAc,CAAC,yBAAW,MAAM,AAAC,CAAC,AAC7C,KAAK,CAAE,OAAO,CACd,gBAAgB,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAClC,MAAM,CAAE,OAAO,AACnB,CAAC" "mappings": "AAkCA,kBAAkB,cAAC,CAAC,AAChB,MAAM,CAAE,IAAI,CACZ,KAAK,CAAE,IAAI,AACf,CAAC;AC0DD,KAAK,cAAC,CAAC,AACH,MAAM,CAAE,IAAI,CACZ,QAAQ,IAAI,CACZ,qBAAqB,CAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAC3D,kBAAkB,CAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,AAC5D,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,MAAM,CACzB,cAAc,CAAE,MAAM,CACtB,KAAK,CAAE,KAAK,AAChB,CAAC,AAED,eAAe,cAAC,CAAC,AACb,aAAa,CAAE,IAAI;AACvB,CAAC,AAED,6BAAe,CAAG,GAAG,cAAC,CAAC,AACnB,SAAS,CAAE,IAAI,AACnB,CAAC,AAED,uBAAuB,cAAC,CAAC,AACrB,UAAU,CAAE,KAAK,CACjB,UAAU,CAAE,IAAI,AACpB,CAAC,AAED,wBAAwB,cAAC,CAAC,AACtB,UAAU,CAAE,IAAI,CAChB,OAAO,CAAE,IAAI,CACb,YAAY,CAAE,KAAK,CACnB,YAAY,CAAE,GAAG,CACjB,YAAY,CAAE,MAAM,CACpB,aAAa,CAAE,GAAG,CAClB,UAAU,CAAE,MAAM,CAClB,KAAK,CAAE,MAAM,CACb,gBAAgB,CAAE,SAAS,AAC/B,CAAC,AAED,UAAU,cAAC,CAAC,AACR,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,AACrD,CAAC,AAED,MAAM,cAAC,CAAC,AACJ,iBAAiB,CAAE,KAAK,CACxB,OAAO,CAAE,GAAG,CAAC,IAAI,CACjB,cAAc,CAAE,MAAM,AAC1B,CAAC,AACD,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,OAAO,CAC1B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AAED,cAAc,cAAC,CAAC,AACf,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACnB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,eAAe,cAAC,CAAC,AAChB,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACtB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,CACX,gBAAgB,CAAE,OAAO,CACzB,OAAO,CAAE,IAAI,AACd,CAAC,AAED,6BAAe,OAAO,AAAC,CAAC,AACvB,gBAAgB,CAAE,IAAI,AACvB,CAAC,AAED,6BAAe,MAAM,AAAC,CAAC,AACtB,YAAY,CAAE,IAAI,AACnB,CAAC;AC7FD,KAAK,cAAC,CAAC,AACH,MAAM,CAAE,IAAI,CACZ,MAAM,IAAI,CACV,qBAAqB,CAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAClD,OAAO,CAAE,IAAI,AACjB,CAAC,AAED,OAAO,cAAC,CAAC,AACL,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,IAAI,kBAAkB,CAAC,CACnC,MAAM,CAAE,IAAI,cAAc,CAAC,CAC3B,KAAK,CAAE,IAAI,aAAa,CAAC,AAC7B,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,OAAO,CAAE,IAAI,CAAC,IAAI,CAClB,MAAM,CAAE,OAAO,AACnB,CAAC,AAED,sBAAQ,MAAM,AAAC,CAAC,AACZ,UAAU,CAAE,IAAI,qBAAqB,CAAC,CACtC,KAAK,CAAE,IAAI,gBAAgB,CAAC,AAChC,CAAC,AAED,QAAQ,SAAS,cAAC,CAAC,AACf,UAAU,CAAE,IAAI,wBAAwB,CAAC,CACzC,MAAM,CAAE,IAAI,oBAAoB,CAAC,CACjC,KAAK,CAAE,IAAI,mBAAmB,CAAC,AACnC,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,WAAW,CAAE,OAAO,AACxB,CAAC;AClCD,QAAQ,eAAC,CAAC,AACT,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACtB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,CACX,gBAAgB,CAAE,OAAO,CACzB,OAAO,CAAE,IAAI,AACd,CAAC,AAED,uBAAQ,OAAO,AAAC,CAAC,AAChB,gBAAgB,CAAE,IAAI,AACvB,CAAC,AAED,uBAAQ,MAAM,AAAC,CAAC,AACf,YAAY,CAAE,IAAI,AACnB,CAAC,AAED,OAAO,eAAC,CAAC,AACR,MAAM,CAAE,IAAI,QAAQ,CAAC,AACtB,CAAC,AAED,MAAM,eAAC,CAAC,AACP,KAAK,CAAE,IAAI,OAAO,CAAC,AACpB,CAAC,AAED,WAAW,eAAC,CAAC,AACZ,UAAU,CAAE,IAAI,YAAY,CAAC,AAC9B,CAAC,AAED,2BAAY,MAAM,AAAC,CAAC,AACnB,MAAM,CAAE,IAAI,aAAa,CAAC,AAC3B,CAAC,AAED,0BAAW,MAAM,AAAC,CAAC,AAClB,KAAK,CAAE,IAAI,YAAY,CAAC,AACzB,CAAC,AAED,yBAAU,MAAM,AAAC,CAAC,AACjB,UAAU,CAAE,IAAI,iBAAiB,CAAC,AACnC,CAAC;AC3ED,cAAc,cAAC,CAAC,AACZ,KAAK,CAAE,IAAI,CACX,aAAa,CAAE,IAAI,CACnB,KAAK,CAAE,OAAO,CACd,eAAe,CAAE,QAAQ,AAC7B,CAAC,AAED,4BAAc,CAAC,cAAc,CAAC,WAAW,cAAC,CAAC,AACvC,cAAc,CAAE,MAAM,CACtB,aAAa,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAChC,WAAW,CAAE,IAAI,AACrB,CAAC,AAED,4BAAc,CAAC,WAAW,cAAC,CAAC,AACxB,OAAO,CAAE,MAAM,CACf,cAAc,CAAE,GAAG,CACnB,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAC7B,WAAW,CAAE,MAAM,AACvB,CAAC,AAED,WAAW,cAAC,CAAC,AACT,UAAU,CAAE,OAAO,AACvB,CAAC,AAED,4BAAc,CAAC,cAAc,CAAC,yBAAW,MAAM,AAAC,CAAC,AAC7C,KAAK,CAAE,OAAO,CACd,gBAAgB,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAClC,MAAM,CAAE,OAAO,AACnB,CAAC"
} }

4500
packages/standard-components/public/bundle.js

File diff suppressed because it is too large

2
packages/standard-components/public/bundle.js.map

File diff suppressed because one or more lines are too long

96
packages/standard-components/src/Div.svelte

@ -1,107 +1,19 @@
<script> <script>
export let children = []; export let _children = [];
export let className=""; export let className="";
export let data=[];
export let dataItemComponent;
export let onLoad; export let onLoad;
export let _bb; export let _bb;
let rootDiv; let rootDiv;
let staticComponentsApplied=false; $:{
let dataBoundComponents = []; if(_bb && rootDiv && _children && _children.length)
let previousData; _bb.hydrateChildren(_children, theButton);
let onLoadCalled = false;
let staticHtmlElements = {};
const hasData = () =>
Array.isArray(data) && data.length > 0;
const staticElementsInitialised = () => {
if(!children) return false;
if(children.filter(c => c.className).length === Object.keys(staticHtmlElements).length)
return true;
}
const getStaticAnchor = (elementIndex) => {
const nextElements = Object.keys(staticHtmlElements).filter(k => k > elementIndex);
return nextElements.length === 0
? null
: staticHtmlElements[Math.min(...nextElements)];
}
$: {
if(rootDiv) {
if(children && children.length > 0
&& !staticComponentsApplied
&& staticElementsInitialised()) {
let index = 0;
for(let child of _bb.props.children) {
if(child.className) {
_bb.hydrateComponent(
child.component,
staticHtmlElements[index]);
} else {
const anchor = getStaticAnchor(index);
if(!anchor) {
_bb.appendComponent(
child.component,
rootDiv);
} else {
_bb.insertComponent(
child.component,
rootDiv,
anchor);
}
}
index += 1;
}
staticComponentsApplied = true;
}
if(previousData !== data) {
for(let c of dataBoundComponents) {
dataBoundComponents[c].$destroy();
}
dataBoundComponents = [];
if(hasData()) {
let index = 0;
for(let dataItem of data) {
_bb.appendComponent(
_bb.props.dataItemComponent,
rootDiv,
dataItem
);
}
}
}
if(!onLoadCalled && onLoad && !onLoad.isPlaceholder) {
_bb.call(onLoad);
onLoadCalled = true;
}
}
} }
</script> </script>
<div class="{className}" bind:this={rootDiv}> <div class="{className}" bind:this={rootDiv}>
{#each children as child, index}
{#if child.className}
<div class="{child.className}"
bind:this={staticHtmlElements[index]}>
</div>
{/if}
{/each}
</div> </div>

59
packages/standard-components/src/Form.svelte

@ -1,59 +0,0 @@
<script>
export let containerClass = "";
export let formControls = [];
export let _bb;
let htmlElements = {};
let labels = {};
$ : {
let cIndex = 0;
for(let c of formControls) {
labels[cIndex] = c.label;
cIndex++;
}
if(_bb && htmlElements) {
for(let el in htmlElements) {
_bb.hydrateComponent(
_bb.props.formControls[el].control,
htmlElements[el]
);
}
}
}
</script>
<div class="form-root {containerClass}">
{#each formControls as child, index}
<div class="label">{labels[index]}</div>
<div class="control"
bind:this={htmlElements[index]}>
</div>
{/each}
</div>
<style>
.form-root {
display: grid;
grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/
}
.label {
grid-column-start: label;
padding: 5px 10px;
vertical-align: middle;
}
.control {
grid-column-start: control;
padding: 5px 10px;
}
.overflow {
grid-column-start: overflow;
}
.full-width {
width: 100%;
}
</style>

62
packages/standard-components/src/Grid.svelte

@ -1,62 +0,0 @@
<script>
import { onMount } from 'svelte'
import {buildStyle} from "./buildStyle";
export let gridTemplateRows ="";
export let gridTemplateColumns ="";
export let children = [];
export let containerClass="";
export let itemContainerClass="";
/*"gridColumnStart":"string",
"gridColumnEnd":"string",
"gridRowStart":"string",
"gridRowEnd":"string"*/
export let _bb;
let style="";
let htmlElements = {};
let isInitilised = false;
$ : {
if(!isInitilised && _bb && htmlElements && Object.keys(htmlElements).length > 0) {
for(let el in htmlElements) {
_bb.hydrateComponent(
_bb.props.children[el].component,
htmlElements[el]
);
}
isInitilised = true;
}
}
const childStyle = child =>
buildStyle({
"grid-column-start": child.gridColumnStart,
"grid-column-end": child.gridColumnEnd,
"grid-row-start": child.gridRowStart,
"grid-row-end": child.gridRowStart
});
</script>
<div class="root {containerClass}"
style="grid-template-columns: {gridTemplateColumns}; grid-template-rows: {gridTemplateRows};">
{#each children as child, index}
<div class="{itemContainerClass}"
style={childStyle(child)}
bind:this={htmlElements[index]}>
</div>
{/each}
</div>
<style>
.root {
display: grid;
width: 100%;
height: 100%;
}
</style>

49
packages/standard-components/src/If.svelte

@ -1,49 +0,0 @@
<script>
export let condition;
export let thenComponent;
export let elseComponent;
export let _bb;
let element;
let currentEvalResult=null;
let currentComponent;
let state;
_bb.store.subscribe(s => {
state = s;
})
$: {
if(_bb && element && state) {
let result;
try {
let $store = state;
let $context = _bb.context;
result = !!(Function(`"use strict";return (function($store, $context) { return (${condition}); });`)()($store, $context));
} catch(e) {
console.log("If condition eval error: " + e.message)
}
if(result !== currentEvalResult) {
currentEvalResult = result;
if(currentComponent) {
currentComponent.$destroy();
}
if(result) {
currentComponent = _bb.hydrateComponent(
_bb.props.thenComponent,element);
} else if(elseComponent && elseComponent._component) {
currentComponent = _bb.hydrateComponent(
_bb.props.elseComponent,element);
}
}
}
}
</script>
<div bind:this={element}></div>

22
packages/standard-components/src/Input.svelte

@ -1,7 +1,7 @@
<script> <script>
export let value=""; export let value="";
export let className = "default"; export let className = "";
export let type = "text"; export let type = "text";
export let _bb; export let _bb;
@ -20,23 +20,3 @@ const onchange = (ev) => {
type={type} type={type}
value={value} value={value}
on:change={onchange}/> on:change={onchange}/>
<style>
.default {
width: 100%;
font-family: inherit;
font-size: inherit;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
width: 100%;
}
.default:disabled {
color: #ccc;
}
</style>

2
packages/standard-components/src/Login.svelte

@ -1,7 +1,5 @@
<script> <script>
import Textbox from "./Textbox.svelte";
import Form from "./Form.svelte";
import Button from "./Button.svelte"; import Button from "./Button.svelte";
export let usernameLabel = "Username"; export let usernameLabel = "Username";

19
packages/standard-components/src/Nav.svelte

@ -9,10 +9,10 @@ export let selectedItemColor="";
export let selectedItemBorder=""; export let selectedItemBorder="";
export let itemHoverBackground=""; export let itemHoverBackground="";
export let itemHoverColor=""; export let itemHoverColor="";
export let items = [];
export let hideNavBar=false; export let hideNavBar=false;
export let selectedItem=""; export let selectedItem="";
export let _children;
export let _bb; export let _bb;
let selectedIndex = -1; let selectedIndex = -1;
@ -32,14 +32,14 @@ $: {
itemHoverBackground, itemHoverColor itemHoverBackground, itemHoverColor
}; };
if(items && items.length > 0 && hasComponentElements()) { if(_children && _children.length > 0 && hasComponentElements()) {
const currentSelectedItem = selectedIndex > 0 const currentSelectedItem = selectedIndex > 0
? items[selectedIndex].title ? _children[selectedIndex].title
: ""; : "";
if(selectedItem && currentSelectedItem !== selectedItem) { if(selectedItem && currentSelectedItem !== selectedItem) {
let i=0; let i=0;
for(let item of items) { for(let child of _children) {
if(item.title === selectedItem) { if(child.title === selectedItem) {
onSelectItem(i)(); onSelectItem(i)();
} }
i++; i++;
@ -53,8 +53,8 @@ $: {
const onSelectItem = (index) => () => { const onSelectItem = (index) => () => {
selectedIndex = index; selectedIndex = index;
if(!components[index]) { if(!components[index]) {
const comp = _bb.hydrateComponent( const comp = _bb.hydrateChildren(
_bb.props.items[index].component, componentElements[index]); _children[index].children, componentElements[index]);
components[index] = comp; components[index] = comp;
} }
} }
@ -65,7 +65,7 @@ const onSelectItem = (index) => () => {
<div class="root" use:cssVars={styleVars}> <div class="root" use:cssVars={styleVars}>
{#if !hideNavBar} {#if !hideNavBar}
<div class="navbar"> <div class="navbar">
{#each items as navItem, index} {#each _children as navItem, index}
<div class="navitem" <div class="navitem"
on:click={onSelectItem(index)} on:click={onSelectItem(index)}
class:selected={selectedIndex === index}> class:selected={selectedIndex === index}>
@ -74,8 +74,7 @@ const onSelectItem = (index) => () => {
{/each} {/each}
</div> </div>
{/if} {/if}
{#each items as navItem, index} {#each _children as navItem, index}
<div class="content" <div class="content"
bind:this={componentElements[index]}> bind:this={componentElements[index]}>
</div> </div>

74
packages/standard-components/src/Panel.svelte

@ -1,74 +0,0 @@
<script>
import {buildStyle} from "./buildStyle";
import cssVars from "./cssVars";
export let component="";
export let text="";
export let containerClass="";
export let background="";
export let border="";
export let borderRadius="";
export let font="";
export let display="";
export let textAlign="";
export let color="";
export let padding="";
export let margin="";
export let hoverBackground="";
export let hoverColor="";
export let onClick;
export let height;
export let width;
export let _bb;
let styleVars;
let style="";
let componentElement;
let componentInitialised=false;
$: {
style=buildStyle({
border, background, font, margin,
padding, display, color, height, width,
"text-align": textAlign,
"border-radius":borderRadius,
cursor: onClick ? "pointer" : "none"
});
if(_bb && component && componentElement && !componentInitialised) {
_bb.hydrateComponent(_bb.props.component, componentElement);
componentInitialised = true;
}
styleVars = {
hoverBackground:hoverBackground || background,
hoverColor:hoverColor || color
}
}
const clickHandler = () => {
if(onClick) {
_bb.call(onClick);
}
}
</script>
<div class="{containerClass} panel"
style={style}
use:cssVars={styleVars}
bind:this={componentElement}
on:click={clickHandler}>
{component && component._component ? "" : text}
</div>
<style>
.panel:hover {
background: var(--hoverBackground);
color: var(--hoverColor);
}
</style>

22
packages/standard-components/src/Select.svelte

@ -1,7 +1,7 @@
<script> <script>
export let value=""; export let value="";
export let className = "default"; export let className = "";
export let type = "text"; export let type = "text";
export let options = []; export let options = [];
@ -25,23 +25,3 @@ const onchange = (ev) => {
<option id={opt.id ? opt.id : opt.value}>{opt.value}</option> <option id={opt.id ? opt.id : opt.value}>{opt.value}</option>
{/each} {/each}
</select> </select>
<style>
.default {
width: 100%;
font-family: inherit;
font-size: inherit;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
width: 100%;
}
.default:disabled {
color: #ccc;
}
</style>

115
packages/standard-components/src/StackPanel.svelte

@ -1,115 +0,0 @@
<script>
import { emptyProps } from "./emptyProps";
export let direction = "horizontal";
export let children = [];
export let width = "auto";
export let height = "auto";
export let containerClass="";
export let itemContainerClass="";
export let onLoad;
export let data=[];
export let dataItemComponent;
export let _bb;
let staticHtmlElements = {};
let staticComponents = {};
let dataBoundElements = {};
let dataBoundComponents = {};
let onLoadCalled = false;
const hasDataBoundComponents = () =>
Object.getOwnPropertyNames(dataBoundComponents).length > 0;
const hasData = () =>
Array.isArray(data) && data.length > 0;
const hasStaticComponents = () => {
return Object.getOwnPropertyNames(staticComponents).length > 0;
}
$: {
if(staticHtmlElements) {
if(hasStaticComponents()) {
for(let c in staticComponents) {
staticComponents[c].$destroy();
}
staticComponents = {};
}
for(let el in staticHtmlElements) {
staticComponents[el] = _bb.hydrateComponent(
_bb.props.children[el].control,
staticHtmlElements[el]
);
}
}
if(hasDataBoundComponents()) {
for(let c in dataBoundComponents) {
dataBoundComponents[c].$destroy();
}
dataBoundComponents = {};
}
if(hasData()) {
let index = 0;
for(let d in dataBoundElements) {
_bb.hydrateComponent(
_bb.props.dataItemComponent,
dataBoundElements[d],
data[parseInt(d)]
);
}
}
if(!onLoadCalled && onLoad && !onLoad.isPlaceholder) {
_bb.call(onLoad);
onLoadCalled = true;
}
}
</script>
<div class="root {containerClass}"
style="width: {width}; height: {height}">
{#if children}
{#each children as child, index}
<div class={direction}>
<div class="{itemContainerClass}"
bind:this={staticHtmlElements[index]}>
</div>
</div>
{/each}
{/if}
{#if data && data.length > 0}
{#each data as child, index}
<div class={direction}>
<div class="{itemContainerClass}"
bind:this={dataBoundElements[index]}>
</div>
</div>
{/each}
{/if}
</div>
<style>
.horizontal {
display:inline-block;
}
.vertical {
display: block;
}
</style>

4
packages/standard-components/src/Test/TestApp.svelte

@ -7,13 +7,13 @@ let _bb;
const _appPromise = createApp(); const _appPromise = createApp();
_appPromise.then(a => _bb = a); _appPromise.then(a => _bb = a);
const testProps = props.table; const testProps = props.divWithAFewControls;
let currentComponent; let currentComponent;
$: { $: {
if(_bb && currentComponent) { if(_bb && currentComponent) {
_bb.hydrateComponent(testProps, currentComponent); _bb.hydrateChildren(testProps._children, currentComponent);
} }
} }

18
packages/standard-components/src/Test/createApp.js

@ -1,12 +1,10 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import Login from "../Login.svelte"; import Login from "../Login.svelte";
import Grid from "../Grid.svelte"; import Input from "../Input.svelte";
import Form from "../Form.svelte";
import Textbox from "../Textbox.svelte";
import Text from "../Text.svelte"; import Text from "../Text.svelte";
import Nav from "../Nav.svelte"; import Nav from "../Nav.svelte";
import Panel from "../Panel.svelte"; import H1 from "../H1.svelte";
import StackPanel from "../StackPanel.svelte"; import Div from "../Div.svelte";
import Table from "../Table.svelte"; import Table from "../Table.svelte";
import Button from "../Button.svelte"; import Button from "../Button.svelte";
import { createApp } from "@budibase/client/src/createApp"; import { createApp } from "@budibase/client/src/createApp";
@ -16,15 +14,13 @@ export default async () => {
const componentLibraries = { const componentLibraries = {
components : { components : {
login : Login, login : Login,
grid : Grid, input : Input,
form : Form,
textbox : Textbox,
text: Text, text: Text,
nav: Nav, nav: Nav,
panel: Panel,
table: Table, table: Table,
stackpanel: StackPanel, button: Button,
button: Button div: Div,
h1: H1
} }
} }

102
packages/standard-components/src/Test/props.js

@ -1,6 +1,37 @@
export const props = { export const props = {
divWithAFewControls : {
_component:"components/div",
_children: [
{
_component:"components/h1",
text: "This is an <h1> component"
},
{
_component:"components/text",
value: "Label for field"
},
{
_component:"components/input",
type:"text"
},
{
_component:"components/button",
_children: [
{
_component:"components/text",
value:"☢"
},
{
_component:"components/text",
value:"Click Me"
},
]
}
]
},
login: { _component:"components/login" }, login: { _component:"components/login" },
form: { form: {
@ -109,77 +140,6 @@ export const props = {
}, },
grid: {
_component: "components/grid",
gridTemplateColumns: "[left] auto [center] auto [right] auto",
gridTemplateRows: "[top] auto [middle] auto [bottom] auto",
children : [
{
control: {
_component: "components/text",
value: "1",
background: "blue",
textAlign:"center",
color: "white"
},
gridColumn: "left",
gridRow: "top"
},
{
control: {
_component: "components/text",
value: "2",
background: "red",
textAlign:"center",
color: "white",
padding: "10px"
},
gridColumn: "center",
gridRow: "middle"
},
{
control: {
_component: "components/text",
value: "3",
background: "yellow",
textAlign:"center",
color: "black"
},
gridColumn: "right",
gridRow: "bottom"
}
]
},
boundStackPanel: {
_component: "components/stackpanel",
direction: "horizontal",
children: [
{
control: {
_component: "components/text",
value: "STATIC"
}
}
],
data: {
"##bbstate":"people"
},
dataItemComponent: {
_component: "components/panel",
text: {
"##bbstate":"name",
"##bbsource":"context",
"##bbstatefallback": "balls to that"
},
padding: "10px",
border: "5px solid black",
margin: "10px",
hoverColor: "white",
hoverBackground: "black",
height:"200px",
weight:"200px"
}
},
hiddenNav: { hiddenNav: {
_component: "components/stackpanel", _component: "components/stackpanel",
children: [ children: [

1
packages/standard-components/src/Text.svelte

@ -8,7 +8,6 @@ export let font="";
export let textAlign=""; export let textAlign="";
export let verticalAlign="" export let verticalAlign=""
export let color=""; export let color="";
export let display="";
export let _bb; export let _bb;

48
packages/standard-components/src/Textbox.svelte

@ -1,48 +0,0 @@
<script>
export let value="";
export let hideValue = false;
export let className = "default";
export let _bb;
let actualValue = "";
const onchange = (ev) => {
if(_bb) {
_bb.setStateFromBinding(_bb.props.value, ev.target.value);
}
}
</script>
{#if hideValue}
<input class={className}
type="password"
value={value}
on:change={onchange}/>
{:else}
<input class={className}
type="text"
value={value}
on:change={onchange}/>
{/if}
<style>
.default {
width: 100%;
font-family: inherit;
font-size: inherit;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
width: 100%;
}
.default:disabled {
color: #ccc;
}
</style>

22
packages/standard-components/src/button.svelte

@ -4,7 +4,6 @@ import {buildStyle} from "./buildStyle";
export let className = "default"; export let className = "default";
export let disabled = false; export let disabled = false;
export let contentText; export let contentText;
export let contentComponent;
export let onClick; export let onClick;
export let background; export let background;
export let color; export let color;
@ -13,9 +12,10 @@ export let padding;
export let hoverColor; export let hoverColor;
export let hoverBackground; export let hoverBackground;
export let hoverBorder; export let hoverBorder;
export let _children;
export let _bb; export let _bb;
let contentComponentContainer; let theButton;
let cssVariables; let cssVariables;
let buttonStyles; let buttonStyles;
@ -34,12 +34,13 @@ const createClasses = (classes) => {
} }
return all; return all;
} }
$:{ $:{
if(_bb && contentComponentContainer && contentComponent._component) if(_bb && theButton && _children && _children.length)
_bb.hydrateComponent(_bb.props.contentComponent, contentComponentContainer); _bb.hydrateChildren(_children, theButton);
}
$:{
cssVariables = { cssVariables = {
hoverColor, hoverBorder, hoverColor, hoverBorder,
hoverBackground, hoverBackground,
@ -54,7 +55,6 @@ $:{
hoverColor, hoverBorder, hoverBackground, hoverColor, hoverBorder, hoverBackground,
background, border, color background, border, color
}); });
} }
@ -67,18 +67,14 @@ const clickHandler = () => {
</script> </script>
<button use:cssVars={cssVariables} <button bind:this={theButton}
use:cssVars={cssVariables}
class="{className} {customClasses}" class="{className} {customClasses}"
disabled={disabled || false} disabled={disabled || false}
on:click={clickHandler} on:click={clickHandler}
style={buttonStyles}> style={buttonStyles}>
{#if contentComponent && contentComponent._component} {#if !_children || _children.length === 0}
<div bind:this={contentComponentContainer}>
</div>
{:else if contentText}
{contentText} {contentText}
{:else}
<slot />
{/if} {/if}
</button> </button>

6
packages/standard-components/src/index.js

@ -1,20 +1,14 @@
export {default as button} from "./Button.svelte"; export {default as button} from "./Button.svelte";
export {default as div} from "./Div.svelte"; export {default as div} from "./Div.svelte";
export {default as form} from "./Form.svelte";
export {default as grid} from "./Grid.svelte";
export {default as h1} from "./H1.svelte"; export {default as h1} from "./H1.svelte";
export {default as h2} from "./H2.svelte"; export {default as h2} from "./H2.svelte";
export {default as h3} from "./H3.svelte"; export {default as h3} from "./H3.svelte";
export {default as h4} from "./H4.svelte"; export {default as h4} from "./H4.svelte";
export {default as h5} from "./H5.svelte"; export {default as h5} from "./H5.svelte";
export {default as h6} from "./H6.svelte"; export {default as h6} from "./H6.svelte";
export {default as if} from "./If.svelte";
export {default as input} from "./Input.svelte"; export {default as input} from "./Input.svelte";
export {default as login} from "./Login.svelte"; export {default as login} from "./Login.svelte";
export {default as nav} from "./Nav.svelte"; export {default as nav} from "./Nav.svelte";
export {default as panel} from "./Panel.svelte";
export {default as select} from "./Select.svelte"; export {default as select} from "./Select.svelte";
export {default as stackpanel} from "./StackPanel.svelte";
export {default as table} from "./Table.svelte"; export {default as table} from "./Table.svelte";
export {default as text} from "./Text.svelte"; export {default as text} from "./Text.svelte";
export {default as textbox} from "./Textbox.svelte";

Loading…
Cancel
Save