#48 restructure. Screens in, user components out. _children static concept

This commit is contained in:
Michael Shanks 2020-01-17 23:06:42 +00:00
parent f2221e32e2
commit 4e09043e3a
62 changed files with 66610 additions and 1992 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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>

View File

@ -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>

View File

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

View File

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

View File

@ -88,7 +88,7 @@ const expandFolder = folder => {
}
const isComponentSelected = (type, current,c) =>
type==="component"
type==="screen"
&& current
&& current.name === c.name
@ -136,7 +136,7 @@ $: {
{#each componentsThisLevel as 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 class="title">{component.title}</span>
</div>

View File

@ -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>

View File

@ -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>

View File

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

View File

@ -8,11 +8,10 @@ import Textbox from "../common/Textbox.svelte";
import UIkit from "uikit";
import { pipe } from "../common/core";
import {
getComponentInfo
getScreenInfo
} from "./pagesParsing/createProps";
import Button from "../common/Button.svelte";
import ButtonGroup from "../common/ButtonGroup.svelte";
import ComponentInstanceEditor from "./ComponentInstanceEditor.svelte";
import {
cloneDeep,
@ -30,17 +29,11 @@ let name = "";
let description = "";
let tagsString = "";
let nameInvalid = "";
let componentDetailsExpanded = false;
let componentInfo;
let modalElement
let propsValidationErrors = [];
let editingComponentInstance;
let editingComponentInstancePropName="";
let editingComponentArrayIndex;
let editingComponentArrayPropName;
let editingComponentInstanceTitle;
let originalName="";
let allComponents;
let components;
let ignoreStore = false;
$: shortName = last(name.split("/"));
@ -54,8 +47,7 @@ store.subscribe(s => {
description = component.description;
tagsString = join(", ")(component.tags);
componentInfo = s.currentComponentInfo;
componentDetailsExpanded = s.currentComponentIsNew;
allComponents = s.allComponents;
components = s.components;
});
const save = () => {
@ -73,12 +65,12 @@ const save = () => {
map(s => s.trim())
]);
store.saveDerivedComponent(component);
store.saveScreen(component);
ignoreStore = false;
// now do the rename
if(name !== originalName) {
store.renameDerivedComponent(originalName, name);
store.renameScreen(originalName, name);
}
}
@ -87,7 +79,7 @@ const deleteComponent = () => {
}
const confirmDeleteComponent = () => {
store.deleteDerivedComponent(component.name);
store.deleteScreen(component.name);
hideDialog();
}
@ -99,7 +91,7 @@ const updateComponent = doChange => {
const newComponent = cloneDeep(component);
doChange(newComponent);
component = newComponent;
componentInfo = getComponentInfo(allComponents, newComponent);
componentInfo = getScreenInfo(components, newComponent);
}
const onPropsChanged = newProps => {
@ -128,37 +120,6 @@ const showDialog = () => {
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>
<div class="root">
@ -177,53 +138,15 @@ const componentInstancePropsChanged = (instanceProps) => {
</div>
</div>
{#if editingComponentInstance}
<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}
{componentInfo}
{onPropsChanged}
{onEditComponentProp}/>
{onPropsChanged} />
</div>
{/if}
</div>
@ -263,20 +186,13 @@ const componentInstancePropsChanged = (instanceProps) => {
height: 100%;
display: flex;
flex-direction: column;
}
.padding {
padding: 1rem 1rem 0rem 1rem;
}
.info-text {
color: var(--secondary100);
font-size: .8rem !important;
font-weight: bold;
border-style: solid;
border-width: 1px 0 0 0;
border-color: var(--slate);
}
.title {
padding: 2rem 1rem 1rem 1rem;
padding: 1rem;
display: grid;
grid-template-columns: [name] 1fr [actions] auto;
color: var(--secondary100);
@ -293,15 +209,6 @@ const componentInstancePropsChanged = (instanceProps) => {
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 {
flex: 1 1 auto;
overflow-y: auto;

View File

@ -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>

View File

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

View File

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

View File

@ -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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,9 +14,9 @@ export const validatePage = (page, getComponent) => {
const errors = [];
const error = message => errors.push(message);
const noIndex = !page.index || !page.index._component;
const noIndex = !page.index;
if(noIndex) {
error("Must choose a component for your index.html");
error("Page does not define an index member");
}
if(!page.appBody
@ -25,14 +25,18 @@ export const validatePage = (page, getComponent) => {
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
? []
: pipe(
recursivelyValidate(page.index, getComponent), [
map(e => `Index.html: ${e.error}`)
]);
? []
: pipe(
recursivelyValidate(page.index, getComponent), [
map(e => `Index.html: ${e.error}`)
]);
*/
return [...errors, ...indexHtmlErrors];
return errors;
}
export const validatePages = (pages, getComponent) => {

View File

@ -23,14 +23,6 @@ const makeError = (errors, propName, stack) => (message) =>
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) {
const errs = [];
makeError(errs, "_component", stack)("Component is not set");
@ -38,79 +30,56 @@ export const recursivelyValidate = (rootProps, getComponent, stack=[]) => {
// this would break everything else anyway
}
const propsDef = getComponentPropsDefinition(
const componentDef = getComponent(
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(
propsDef,
componentDef,
rootProps,
stack,
true);
const validateChildren = (_defArray, _props, _stack) => pipe(_defArray, [
filter(d => d.type === "component"),
map(d => recursivelyValidate(
_props[d.name],
getComponentPropsDefinition,
[..._stack, d.name])),
flatten
]);
const validateChildren = (_props, _stack) =>
!_props._children
? []
: pipe(_props._children, [
map(child => recursivelyValidate(
child,
getComponent,
[..._stack, _props._children.indexOf(child)]))
]);
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 p = isString(propDef)
? types[propDef].defaultDefinition()
: propDef;
if(p.type === "array" && isString(p.elementDefinition)) {
p.elementDefinition = types[p.elementDefinition].defaultDefinition()
}
return p;
}
const expandPropDef = propDef =>
isString(propDef)
? types[propDef].defaultDefinition()
: propDef;
export const validateProps = (propsDefinition, props, stack=[], isFinal=true, isArrayElement=false) => {
export const validateProps = (componentDefinition, props, stack=[], isFinal=true) => {
const errors = [];
if(isFinal && !props._component && !isArrayElement) {
if(isFinal && !props._component) {
makeError(errors, "_component", stack)("Component is not set");
return errors;
// 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 === "_children") continue;
if(propDefName === "_layout") continue;
const propDef = expandPropDef(propsDefinition[propDefName]);
@ -129,9 +98,7 @@ export const validateProps = (propsDefinition, props, stack=[], isFinal=true, is
}
if(isBinding(propValue)) {
if(propDef.type === "array"
|| propDef.type === "component"
|| propDef.type === "event") {
if(propDef.type === "event") {
error(`Cannot apply binding to type ${propDef.type}`);
continue;
}
@ -141,23 +108,7 @@ export const validateProps = (propsDefinition, props, stack=[], isFinal=true, is
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"
&& propValue
&& !isBinding(propValue)
@ -170,33 +121,15 @@ export const validateProps = (propsDefinition, props, stack=[], isFinal=true, is
return errors;
}
export const validatePropsDefinition = (propsDefinition) => {
const { errors } = createProps("dummy_component_name", propsDefinition);
export const validateComponentDefinition = (componentDefinition) => {
const { errors } = createProps(componentDefinition);
const propDefinitions = expandPropDef(componentDefinition.props);
// arrar props without elementDefinition
pipe(propsDefinition, [
pipe(propDefinitions, [
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,
map(k => ({
propDef:propsDefinition[k],
propDef:propDefinitions[k],
propName:k
})),
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`))
]);
return [...errors, ...arrayPropValidationErrors]
return errors;
}

View File

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

View File

@ -1,12 +1,7 @@
import {
searchAllComponents,
getExactComponent,
getAncestorProps
} from "../src/userInterface/pagesParsing/searchComponents";
import {
componentDependencies
} from "../src/userInterface/pagesParsing/findDependencies";
import { allComponents } from "./testData";
import { componentsAndScreens } from "./testData";
import { some, find } from "lodash/fp"
describe("component dependencies", () => {
@ -19,37 +14,29 @@ describe("component dependencies", () => {
it("should include component that inheirts", () => {
const components = allComponents();
const {components, screens} = componentsAndScreens();
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);
});
it("should include component that nests", () => {
const components = allComponents();
const {components, screens} = componentsAndScreens();
const result = componentDependencies(
{}, components, get(components, "PrimaryButton"));
{}, screens, components,
get([...components, ...screens], "budibase-components/Button"));
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", () => {
const components = allComponents();
const {components, screens} = componentsAndScreens();
const pages = {
main: {
appBody: "PrimaryButton"
@ -57,7 +44,8 @@ describe("component dependencies", () => {
};
const result = componentDependencies(
pages, components, get(components, "PrimaryButton"));
pages, screens, components,
get([...components, ...screens], "PrimaryButton"));
expect(result.dependantPages).toEqual(["main"]);
});

View File

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

View File

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

View File

@ -1,11 +1,12 @@
import {
getInstanceProps,
getComponentInfo
getScreenInfo ,
getComponentInfo
} from "../src/userInterface/pagesParsing/createProps";
import {
keys, some
keys, some, find
} 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", () => {
const result = getComponentInfo(
allComponents(),
componentsAndScreens().components,
"budibase-components/TextBox");
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", () => {
const result = getInstanceProps(
getComponentInfo(
allComponents(),
componentsAndScreens().components,
"budibase-components/TextBox"),
{size:"small"});
@ -51,11 +43,18 @@ describe("getComponentInfo", () => {
});
});
});
it("should return correct props for derived component", () => {
const result = getComponentInfo(
allComponents(),
"common/SmallTextbox");
describe("getScreenInfo", () => {
const getScreen = (screens, name) =>
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.fullProps).toEqual({
@ -68,9 +67,10 @@ describe("getComponentInfo", () => {
});
it("should return correct props for twice derived component", () => {
const result = getComponentInfo(
allComponents(),
"common/PasswordBox");
const {components, screens} = componentsAndScreens();
const result = getScreenInfo(
components,
getScreen(screens, "common/PasswordBox"));
expect(result.errors).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", () => {
const result = getComponentInfo(
allComponents(),
"common/PasswordBox");
const {components, screens} = componentsAndScreens();
const result = getScreenInfo(
components,
getScreen(screens, "common/PasswordBox"));
expect(result.unsetProps).toEqual([
"placeholder", "label"]);

View File

@ -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"]);
});
})

View File

@ -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"]);
});
*/
})

View File

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

View File

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

View File

@ -6,21 +6,13 @@ import {
const validPages = () => ({
"main" : {
"index" : {
"_component": "testIndexHtml",
"title": "My Cool App",
"customScripts": [
{"url": "MyCustomComponents.js"}
]
"title": "My Cool App"
},
"appBody" : "./main.app.json"
},
"unauthenticated" : {
"index" : {
"_component": "testIndexHtml",
"title": "My Cool App - Login",
"customScripts": [
{"url": "MyCustomComponents.js"}
]
"title": "My Cool App - Login"
},
"appBody" : "./unauthenticated.app.json"
},
@ -29,12 +21,9 @@ const validPages = () => ({
const getComponent = name => ({
testIndexHtml : {
title: "string",
customScripts: {
type:"array",
elementDefinition: {
url: "string"
}
name: "testIndexHtml",
props: {
title: "string",
}
}
}[name])
@ -53,11 +42,6 @@ describe("validate single page", () => {
let page = validPages().main;
delete page.index;
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", () => {

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

@ -1,49 +1,46 @@
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-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}
.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-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}
.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}
.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-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}
.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)}
.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)}
.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)}
.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}
.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)}
.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-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-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)}
.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}
.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}
input.svelte-9fre0g{margin-right:7px}
.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}
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}
.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}
.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)}
.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}
.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 */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

@ -5,8 +5,8 @@ const testPages = require("../appPackages/testApp/pages.json");
const testComponents = require("../appPackages/testApp/customComponents/components.json");
const testMoreComponents = require("../appPackages/testApp/moreCustomComponents/components.json");
const statusCodes = require("../utilities/statusCodes");
const derivedComponent1 = require("../appPackages/testApp/components/myTextBox.json");
const derivedComponent2 = require("../appPackages/testApp/components/subfolder/otherTextBox.json");
const screen1 = require("../appPackages/testApp/components/myTextBox.json");
const screen2 = require("../appPackages/testApp/components/subfolder/otherTextBox.json");
const { readJSON, pathExists, unlink } = require("fs-extra");
const app = require("./testApp")();
@ -49,36 +49,36 @@ it("/apppackage should get pages", async () => {
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")
.expect(statusCodes.OK);
expect(body.rootComponents["./customComponents/textbox"]).toBeDefined();
expect(body.rootComponents["./moreCustomComponents/textbox"]).toBeDefined();
expect(body.components["./customComponents/textbox"]).toBeDefined();
expect(body.components["./moreCustomComponents/textbox"]).toBeDefined();
expect(body.rootComponents["./customComponents/textbox"])
expect(body.components["./customComponents/textbox"])
.toEqual(testComponents.textbox);
expect(body.rootComponents["./moreCustomComponents/textbox"])
expect(body.components["./moreCustomComponents/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")
.expect(statusCodes.OK);
const expectedComponents = {
"myTextBox" : {...derivedComponent1, name:"myTextBox"},
"subfolder/otherTextBox": {...derivedComponent2, name:"subfolder/otherTextBox"}
"myTextBox" : {...screen1, name:"myTextBox"},
"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 () => {
const newDerivedComponent = {
const newscreen = {
name: "newTextBox",
inherits: "./customComponents/textbox",
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);
const componentFile = "./appPackages/testApp/components/newTextBox.json";
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 () => {
const updatedDerivedComponent = {
const updatedscreen = {
name: "newTextBox",
inherits: "./customComponents/textbox",
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);
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 () => {
await app.patch("/_builder/api/testApp/derivedcomponent", {
await app.patch("/_builder/api/testApp/screen", {
oldname: "newTextBox", newname: "anotherSubFolder/newTextBox"
}).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 () => {
await app.delete("/_builder/api/testApp/derivedcomponent/anotherSubFolder/newTextBox")
await app.delete("/_builder/api/testApp/screen/anotherSubFolder/newTextBox")
.expect(statusCodes.OK);
const componentFile = "./appPackages/testApp/components/anotherSubFolder/newTextBox.json";

View File

@ -37,10 +37,10 @@ module.exports.getPackageForBuilder = async (config, appname) => {
pages,
rootComponents: await getRootComponents(appPath, pages),
components: await getComponents(appPath, pages),
derivedComponents: keyBy("name")(
await fetchDerivedComponents(appPath))
screens: keyBy("name")(
await fetchscreens(appPath))
});
}
@ -60,7 +60,7 @@ module.exports.getApps = async (config, master) => {
const componentPath = (appPath, name) =>
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 compPath = componentPath(appPath, component.name);
await ensureDir(dirname(compPath));
@ -70,7 +70,7 @@ module.exports.saveDerivedComponent = async (config, appname, component) => {
{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 oldComponentPath = componentPath(
@ -85,7 +85,7 @@ module.exports.renameDerivedComponent = async (config, appname, oldName, newName
newComponentPath);
}
module.exports.deleteDerivedComponent = async (config, appname, name) => {
module.exports.deleteScreen = async (config, appname, name) => {
const appPath = appPackageFolder(config, appname);
const componentFile = componentPath(appPath, name);
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;
if(!lib) {
@ -131,7 +131,7 @@ const getRootComponents = async (appPath, pages ,lib) => {
return {components, generators};
}
const fetchDerivedComponents = async (appPath, relativePath = "") => {
const fetchscreens = async (appPath, relativePath = "") => {
const currentDir = join(appPath, "components", relativePath);
@ -159,7 +159,7 @@ const fetchDerivedComponents = async (appPath, relativePath = "") => {
components.push(component);
} else {
const childComponents = await fetchDerivedComponents(
const childComponents = await fetchscreens(
appPath, join(relativePath, item)
);
@ -172,4 +172,4 @@ const fetchDerivedComponents = async (appPath, relativePath = "") => {
return components;
}
module.exports.getRootComponents = getRootComponents;
module.exports.getComponents = getComponents;

View File

@ -33,7 +33,6 @@
"description": "an html <button />",
"props": {
"contentText": { "type": "string", "default": "Button" },
"contentComponent": "component",
"className": {"type": "string", "default": "default"},
"disabled": "bool",
"onClick": "event",
@ -71,8 +70,7 @@
"formControls": {
"type":"array",
"elementDefinition": {
"label": "string",
"control":"component"
"label": "string"
}
}
},
@ -136,18 +134,11 @@
"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"]
@ -162,7 +153,6 @@
"children": {
"type":"array",
"elementDefinition": {
"component":"component",
"gridColumnStart":"string",
"gridColumnEnd":"string",
"gridRowStart":"string",
@ -220,7 +210,6 @@
"description": "A stylable div with a component inside",
"props" : {
"text": "string",
"component": "component",
"containerClass": "string",
"background": "string",
"border": "string",
@ -260,8 +249,7 @@
"items": {
"type": "array",
"elementDefinition" : {
"title": "string",
"component": "component"
"title": "string"
}
},
"selectedItem":"string",
@ -300,13 +288,11 @@
"children": {
"type":"array",
"elementDefinition": {
"component":"component",
"className": "string"
}
},
"className":"string",
"data": "state",
"dataItemComponent": "component",
"onLoad": "event"
},
"tags": ["div", "container", "layout"]
@ -370,16 +356,5 @@
"className":"string"
},
"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": []
}
}

View File

@ -25338,7 +25338,7 @@ var app = (function () {
const bb = (bindings, context) => ({
hydrateComponent: _initialiseComponent(context, true),
appendComponent: _initialiseComponent(context, false),
appendChildren: _initialiseComponent(context, false),
insertComponent: (props, htmlElement, anchor, context) =>
_initialiseComponent(context, false)(props, htmlElement, context, anchor),
store,

View File

@ -43,18 +43,18 @@ $: {
let index = 0;
for(let child of _bb.props.children) {
if(child.className) {
_bb.hydrateComponent(
child.component,
_bb.hydrateChildren(
child.children,
staticHtmlElements[index]);
} else {
const anchor = getStaticAnchor(index);
if(!anchor) {
_bb.appendComponent(
child.component,
_bb.appendChildren(
child.children,
rootDiv);
} else {
_bb.insertComponent(
child.component,
_bb.insertChildren(
child.children,
rootDiv,
anchor);
}
@ -76,7 +76,7 @@ $: {
if(hasData()) {
let index = 0;
for(let dataItem of data) {
_bb.appendComponent(
_bb.appendChildren(
_bb.props.dataItemComponent,
rootDiv,
dataItem

View File

@ -16,7 +16,7 @@ $ : {
if(_bb && htmlElements) {
for(let el in htmlElements) {
_bb.hydrateComponent(
_bb.hydrateChildren(
_bb.props.formControls[el].control,
htmlElements[el]
);

View File

@ -22,7 +22,7 @@ let isInitilised = false;
$ : {
if(!isInitilised && _bb && htmlElements && Object.keys(htmlElements).length > 0) {
for(let el in htmlElements) {
_bb.hydrateComponent(
_bb.hydrateChildren(
_bb.props.children[el].component,
htmlElements[el]
);

View File

@ -32,10 +32,10 @@ $: {
}
if(result) {
currentComponent = _bb.hydrateComponent(
currentComponent = _bb.hydrateChildren(
_bb.props.thenComponent,element);
} else if(elseComponent && elseComponent._component) {
currentComponent = _bb.hydrateComponent(
currentComponent = _bb.hydrateChildren(
_bb.props.elseComponent,element);
}

View File

@ -53,8 +53,8 @@ $: {
const onSelectItem = (index) => () => {
selectedIndex = index;
if(!components[index]) {
const comp = _bb.hydrateComponent(
_bb.props.items[index].component, componentElements[index]);
const comp = _bb.hydrateChildren(
_bb.props.items[index].children, componentElements[index]);
components[index] = comp;
}
}

View File

@ -2,7 +2,7 @@
import {buildStyle} from "./buildStyle";
import cssVars from "./cssVars";
export let component="";
export let children="";
export let text="";
export let containerClass="";
export let background="";
@ -36,8 +36,8 @@ $: {
cursor: onClick ? "pointer" : "none"
});
if(_bb && component && componentElement && !componentInitialised) {
_bb.hydrateComponent(_bb.props.component, componentElement);
if(_bb && children && children.length > 0 && componentElement && !componentInitialised) {
_bb.hydrateChildren(_bb.props.children, componentElement);
componentInitialised = true;
}
@ -60,7 +60,7 @@ const clickHandler = () => {
use:cssVars={styleVars}
bind:this={componentElement}
on:click={clickHandler}>
{component && component._component ? "" : text}
{children && children.length === 0 ? "" : text}
</div>
<style>

View File

@ -43,7 +43,7 @@ $: {
}
for(let el in staticHtmlElements) {
staticComponents[el] = _bb.hydrateComponent(
staticComponents[el] = _bb.hydrateChildren(
_bb.props.children[el].control,
staticHtmlElements[el]
);
@ -61,7 +61,7 @@ $: {
if(hasData()) {
let index = 0;
for(let d in dataBoundElements) {
_bb.hydrateComponent(
_bb.hydrateChildren(
_bb.props.dataItemComponent,
dataBoundElements[d],
data[parseInt(d)]

View File

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

View File

@ -38,7 +38,7 @@ const createClasses = (classes) => {
$:{
if(_bb && contentComponentContainer && contentComponent._component)
_bb.hydrateComponent(_bb.props.contentComponent, contentComponentContainer);
_bb.hydrateChildren(_bb.props.contentComponent, contentComponentContainer);
cssVariables = {
hoverColor, hoverBorder,