1
0
Fork 0
mirror of synced 2024-07-04 05:50:57 +12:00
This commit is contained in:
Conor Mack 2020-01-20 12:05:20 +00:00
commit aee5051053
74 changed files with 67196 additions and 5010 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

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

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

View file

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

View file

@ -3,28 +3,18 @@
"file": "bundle.css",
"sources": [
"..\\src\\Test\\TestApp.svelte",
"..\\src\\Grid.svelte",
"..\\src\\Login.svelte",
"..\\src\\Form.svelte",
"..\\src\\Panel.svelte",
"..\\src\\Nav.svelte",
"..\\src\\Textbox.svelte",
"..\\src\\StackPanel.svelte",
"..\\src\\Button.svelte",
"..\\src\\Table.svelte"
],
"sourcesContent": [
"<script>\nimport createApp from \"./createApp\";\nimport { props } from \"./props\";\n\nlet _bb;\n\nconst _appPromise = createApp();\n_appPromise.then(a => _bb = a);\n\nconst testProps = props.table;\n\nlet currentComponent;\n\n$: {\n if(_bb && currentComponent) {\n _bb.hydrateComponent(testProps, currentComponent);\n }\n}\n\n\n\n</script>\n\n{#await _appPromise}\nloading\n{:then _bb}\n\n<div id=\"current_component\" bind:this={currentComponent}>\n</div>\n\n{/await}\n\n\n<style>\n#current_component {\n height: 100%;\n width: 100%;\n}\n</style>\n\n",
"<script>\r\nimport { onMount } from 'svelte'\r\nimport {buildStyle} from \"./buildStyle\";\r\n\r\nexport let gridTemplateRows =\"\";\r\nexport let gridTemplateColumns =\"\";\r\nexport let children = [];\r\nexport let width = \"auto\";\r\nexport let height = \"auto\";\r\nexport let containerClass=\"\";\r\nexport let itemContainerClass=\"\";\r\n\r\n/*\"gridColumnStart\":\"string\",\r\n\"gridColumnEnd\":\"string\",\r\n\"gridRowStart\":\"string\",\r\n\"gridRowEnd\":\"string\"*/\r\n\r\n\r\nexport let _bb;\r\n\r\nlet style=\"\";\r\nlet htmlElements = {};\r\n\r\n$ : {\r\n if(_bb && htmlElements) {\r\n for(let el in htmlElements) {\r\n _bb.hydrateComponent(\r\n children[el].control,\r\n htmlElements[el]\r\n );\r\n }\r\n }\r\n}\r\n\r\nconst childStyle = child => \r\n buildStyle({\r\n \"grid-column-start\": child.gridColumnStart,\r\n \"grid-column-end\": child.gridColumnEnd,\r\n \"grid-column\": child.gridColumn,\r\n \"grid-row-start\": child.gridRowStart,\r\n \"grid-row-end\": child.gridRowStart,\r\n \"grid-row\": child.gridRow,\r\n });\r\n\r\n</script>\r\n\r\n<div class=\"root {containerClass}\"\r\n style=\"width: {width}; height: {height}; grid-template-columns: {gridTemplateColumns}; grid-template-rows: {gridTemplateRows};\">\r\n {#each children as child, index}\r\n <div class=\"{itemContainerClass}\"\r\n style={childStyle(child)}\r\n bind:this={htmlElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n display: grid;\r\n}\r\n\r\n</style>",
"<script>\n\nimport Textbox from \"./Textbox.svelte\";\nimport Form from \"./Form.svelte\";\nimport Button from \"./Button.svelte\";\n\nexport let usernameLabel = \"Username\";\nexport let passwordLabel = \"Password\";\nexport let loginButtonLabel = \"Login\";\nexport let loginRedirect = \"\";\nexport let logo = \"\";\nexport let buttonClass = \"\";\nexport let inputClass=\"\"\n\nexport let _bb;\n\nlet username = \"\";\nlet password = \"\";\nlet busy = false;\nlet incorrect = false;\nlet _logo = \"\";\nlet _buttonClass = \"\";\nlet _inputClass = \"\";\n\n$: {\n _logo = _bb.relativeUrl(logo);\n _buttonClass = buttonClass || \"default-button\";\n _inputClass = inputClass || \"default-input\";\n}\n\nconst login = () => {\n busy = true;\n _bb.api.post(\"/api/authenticate\", {username, password})\n .then(r => {\n busy = false;\n if(r.status === 200) {\n return r.json();\n } else {\n incorrect = true;\n return;\n }\n })\n .then(user => {\n if(user) {\n localStorage.setItem(\"budibase:user\", JSON.stringify(user));\n location.reload();\n }\n })\n}\n\n</script>\n\n<div class=\"root\">\n\n <div class=\"content\">\n\n {#if _logo}\n <div class=\"logo-container\">\n <img src={_logo} alt=\"logo\"/>\n </div>\n {/if}\n\n <div class=\"form-root\">\n <div class=\"label\">\n {usernameLabel}\n </div>\n <div class=\"control\">\n <input bind:value={username} type=\"text\" class={_inputClass}/>\n </div>\n <div class=\"label\">\n {passwordLabel}\n </div>\n <div class=\"control\">\n <input bind:value={password} type=\"password\" class={_inputClass}/>\n </div>\n </div>\n\n <div class=\"login-button-container\">\n <button disabled={busy} \n on:click={login}\n class={_buttonClass}>\n {loginButtonLabel}\n </button>\n </div>\n\n {#if incorrect}\n <div class=\"incorrect-details-panel\">\n Incorrect username or password\n </div>\n {/if}\n\n </div>\n\n</div>\n\n<style>\n\n.root {\n height: 100%;\n display:grid;\n grid-template-columns: [left] 1fr [middle] auto [right] 1fr;\n grid-template-rows: [top] 1fr [center] auto [bottom] 1fr;\n}\n\n.content {\n grid-column-start: middle;\n grid-row-start: center;\n width: 400px;\n}\n\n.logo-container {\n margin-bottom: 20px\n}\n\n.logo-container > img {\n max-width: 100%;\n}\n\n.login-button-container {\n text-align: right;\n margin-top: 20px;\n}\n\n.incorrect-details-panel {\n margin-top: 30px;\n padding: 10px;\n border-style: solid;\n border-width: 1px;\n border-color: maroon;\n border-radius: 1px;\n text-align: center;\n color: maroon;\n background-color: mistyrose;\n}\n\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n\n.default-input {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default-button {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default-button:active {\n\tbackground-color: #ddd;\n}\n\n.default-button:focus {\n\tborder-color: #666;\n}\n\n</style>",
"<script>\nexport let containerClass = \"\";\nexport let formControls = [];\n\nexport let _bb;\n\nlet htmlElements = {};\nlet labels = {};\n\n$ : {\n let cIndex = 0;\n for(let c of formControls) {\n labels[cIndex] = c.label;\n cIndex++;\n }\n\n if(_bb && htmlElements) {\n for(let el in htmlElements) {\n _bb.hydrateComponent(\n formControls[el].control,\n htmlElements[el]\n );\n }\n }\n}\n\n</script>\n\n<div class=\"form-root {containerClass}\">\n {#each formControls as child, index}\n <div class=\"label\">{labels[index]}</div>\n <div class=\"control\"\n bind:this={htmlElements[index]}>\n </div>\n {/each}\n</div>\n\n<style>\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n.overflow {\n grid-column-start: overflow;\n}\n.full-width {\n width: 100%;\n}\n</style>",
"<script>\nimport {buildStyle} from \"./buildStyle\";\nimport cssVars from \"./cssVars\";\n\nexport let component=\"\";\nexport let text=\"\";\nexport let containerClass=\"\";\nexport let background=\"\";\nexport let border=\"\";\nexport let borderRadius=\"\";\nexport let font=\"\";\nexport let display=\"\";\nexport let textAlign=\"\";\nexport let color=\"\";\nexport let padding=\"\";\nexport let margin=\"\";\nexport let hoverBackground=\"\";\nexport let hoverColor=\"\";\nexport let onClick;\nexport let height;\nexport let width;\n\nexport let _bb;\n\nlet styleVars;\nlet style=\"\";\nlet componentElement;\nlet componentInitialised=false;\n\n$: {\n style=buildStyle({\n border, background, font, margin,\n padding, display, color, height, width,\n \"text-align\": textAlign,\n \"border-radius\":borderRadius,\n cursor: onClick ? \"pointer\" : \"none\"\n });\n\n if(_bb && component && componentElement && !componentInitialised) {\n _bb.hydrateComponent(component, componentElement);\n componentInitialised = true;\n }\n\n styleVars = {\n hoverBackground:hoverBackground || background, \n hoverColor:hoverColor || color\n }\n}\n\nconst clickHandler = () => {\n if(onClick) {\n _bb.call(onClick);\n }\n}\n\n</script>\n\n<div class=\"{containerClass} panel\" \n style={style}\n use:cssVars={styleVars}\n bind:this={componentElement}\n on:click={clickHandler}>\n {component && component._component ? \"\" : text}\n</div>\n\n<style>\n\n.panel:hover {\n background: var(--hoverBackground);\n color: var(--hoverColor);\n\n}\n\n</style>\n",
"<script>\r\nimport cssVars from \"./cssVars\";\r\n\r\nexport let navBarBackground = \"\";\r\nexport let navBarBorder=\"\";\r\nexport let navBarColor=\"\";\r\nexport let selectedItemBackground=\"\";\r\nexport let selectedItemColor=\"\";\r\nexport let selectedItemBorder=\"\";\r\nexport let itemHoverBackground=\"\";\r\nexport let itemHoverColor=\"\";\r\nexport let items = [];\r\nexport let hideNavBar=false;\r\nexport let selectedItem=\"\";\r\n\r\nexport let _bb;\r\n\r\nlet selectedIndex = -1;\r\nlet styleVars={};\r\nlet components = {};\r\nlet componentElements = {}\r\n\r\n\r\nconst hasComponentElements = () => \r\n Object.getOwnPropertyNames(componentElements).length > 0;\r\n\r\n$: {\r\n styleVars = {\r\n navBarBackground, navBarBorder,\r\n navBarColor, selectedItemBackground,\r\n selectedItemColor, selectedItemBorder,\r\n itemHoverBackground, itemHoverColor\r\n };\r\n\r\n if(items && items.length > 0 && hasComponentElements()) {\r\n const currentSelectedItem = selectedIndex > 0\r\n ? items[selectedIndex].title\r\n : \"\";\r\n if(selectedItem && currentSelectedItem !== selectedItem) {\r\n let i=0;\r\n for(let item of items) {\r\n if(item.title === selectedItem) {\r\n onSelectItem(i)();\r\n }\r\n i++;\r\n }\r\n } else if(!currentSelectedItem) {\r\n onSelectItem(0);\r\n }\r\n }\r\n}\r\n\r\nconst onSelectItem = (index) => () => {\r\n selectedIndex = index;\r\n if(!components[index]) {\r\n const comp = _bb.hydrateComponent(\r\n items[index].component, componentElements[index]);\r\n components[index] = comp; \r\n }\r\n}\r\n\r\n\r\n</script>\r\n\r\n<div class=\"root\" use:cssVars={styleVars}>\r\n {#if !hideNavBar}\r\n <div class=\"navbar\">\r\n {#each items as navItem, index}\r\n <div class=\"navitem\"\r\n on:click={onSelectItem(index)}\r\n class:selected={selectedIndex === index}>\r\n {navItem.title}\r\n </div>\r\n {/each}\r\n </div>\r\n {/if}\r\n {#each items as navItem, index}\r\n\r\n <div class=\"content\"\r\n bind:this={componentElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n height: 100%;\r\n width:100%;\r\n grid-template-columns: [navbar] auto [content] 1fr;\r\n display: grid;\r\n}\r\n\r\n.navbar {\r\n grid-column: navbar;\r\n background: var(--navBarBackground);\r\n border: var(--navBarBorder);\r\n color: var(--navBarColor);\r\n}\r\n\r\n.navitem {\r\n padding: 10px 17px;\r\n cursor: pointer;\r\n}\r\n\r\n.navitem:hover {\r\n background: var(--itemHoverBackground);\r\n color: var(--itemHoverColor);\r\n}\r\n\r\n.navitem.selected {\r\n background: var(--selectedItemBackground);\r\n border: var(--selectedItemBorder);\r\n color: var(--selectedItemColor);\r\n}\r\n\r\n.content {\r\n grid-column: content;\r\n}\r\n\r\n</style>\r\n\r\n",
"<script>\n\nexport let value=\"\";\nexport let hideValue = false;\nexport let className = \"default\";\n\nexport let _bb;\n\nlet actualValue = \"\";\n\nconst onchange = (ev) => {\n\tif(_bb) {\n\t\t_bb.setStateFromBinding(_bb.bindings.value, ev.target.value);\n\t}\n}\n\n</script>\n\n{#if hideValue}\n<input class={className} \n\t type=\"password\" \n\t value={value} \n\t on:change={onchange}/>\n{:else}\n<input class={className} \n\t type=\"text\" \n\t value={value}\n\t on:change={onchange}/>\n{/if}\n\n<style>\n.default {\n width: 100%;\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default:disabled {\n\tcolor: #ccc;\n}\n\n</style>",
"<script>\n\nimport { emptyProps } from \"./emptyProps\";\n\nexport let direction = \"horizontal\";\nexport let children = [];\nexport let width = \"auto\";\nexport let height = \"auto\";\nexport let containerClass=\"\";\nexport let itemContainerClass=\"\";\nexport let onLoad;\n\nexport let data=[];\nexport let dataItemComponent;\n\nexport let _bb;\n\nlet staticHtmlElements = {};\nlet staticComponents = {};\nlet dataBoundElements = {};\nlet dataBoundComponents = {};\n\nlet onLoadCalled = false;\n\nconst hasDataBoundComponents = () => \n Object.getOwnPropertyNames(dataBoundComponents).length > 0;\n\nconst hasData = () => \n Array.isArray(data) && data.length > 0;\n\nconst hasStaticComponents = () => {\n return Object.getOwnPropertyNames(staticComponents).length > 0;\n}\n\n$: {\n\n if(staticHtmlElements) {\n if(hasStaticComponents()) {\n for(let c in staticComponents) {\n staticComponents[c].$destroy();\n }\n staticComponents = {};\n }\n\n for(let el in staticHtmlElements) {\n staticComponents[el] = _bb.hydrateComponent(\n children[el].control,\n staticHtmlElements[el]\n );\n }\n }\n \n\n if(hasDataBoundComponents()) {\n for(let c in dataBoundComponents) {\n dataBoundComponents[c].$destroy();\n }\n dataBoundComponents = {};\n }\n\n if(hasData()) {\n let index = 0;\n for(let d in dataBoundElements) {\n _bb.hydrateComponent(\n dataItemComponent,\n dataBoundElements[d],\n data[parseInt(d)]\n );\n }\n }\n\n if(!onLoadCalled && onLoad && !onLoad.isPlaceholder) {\n _bb.call(onLoad);\n onLoadCalled = true;\n }\n}\n\n\n</script>\n\n<div class=\"root {containerClass}\"\n style=\"width: {width}; height: {height}\">\n\n {#if children}\n {#each children as child, index}\n <div class={direction}>\n <div class=\"{itemContainerClass}\"\n bind:this={staticHtmlElements[index]}>\n </div>\n </div>\n {/each}\n {/if}\n\n {#if data && data.length > 0}\n {#each data as child, index}\n <div class={direction}>\n <div class=\"{itemContainerClass}\"\n bind:this={dataBoundElements[index]}>\n </div>\n </div>\n {/each}\n {/if}\n</div>\n\n<style>\n\n.horizontal {\n display:inline-block;\n}\n\n.vertical {\n display: block;\n}\n\n</style>",
"<script>\nimport cssVars from \"./cssVars\";\nimport {buildStyle} from \"./buildStyle\";\nexport let className = \"default\";\nexport let disabled = false;\nexport let contentText;\nexport let contentComponent;\nexport let onClick;\nexport let background;\nexport let color;\nexport let border;\nexport let padding;\nexport let hoverColor;\nexport let hoverBackground;\nexport let hoverBorder;\n\nexport let _bb;\nlet contentComponentContainer;\nlet cssVariables;\nlet buttonStyles;\n\nlet customHoverColorClass;\nlet customHoverBorderClass;\nlet customHoverBackClass;\n\nlet customClasses = \"\";\n\nconst createClasses = (classes) => {\n\tlet all = \"\";\n\tfor(let cls in classes) {\n\t\tif(classes[cls]) {\n\t\t\tall = all + \" \" + cls;\n\t\t}\n\t}\n\treturn all;\n}\n\t\n\n$:{\n\tif(_bb && contentComponentContainer && contentComponent._component)\n\t\t_bb.hydrateComponent(contentComponent, contentComponentContainer);\n\n\tcssVariables = {\n\t\thoverColor, hoverBorder,\n\t\thoverBackground,\n\t\tbackground, color, border,\n\t};\n\n\tbuttonStyles = buildStyle({\n\t\tpadding\n\t});\t\n\t\n\tcustomClasses = createClasses({\n\t\thoverColor, hoverBorder, hoverBackground,\n\t\tbackground, border, color\n\t});\n\t\n}\n\n\n\n\nconst clickHandler = () => {\n\t_bb.call(onClick);\n}\n\n</script>\n\n\n<button use:cssVars={cssVariables} \n\t\tclass=\"{className} {customClasses}\" \n\t\tdisabled={disabled || false} \n\t\ton:click={clickHandler} \n\t\tstyle={buttonStyles}>\n {#if contentComponent && contentComponent._component}\n\t<div bind:this={contentComponentContainer}>\n\t</div>\n {:else if contentText}\n {contentText}\n {:else}\n <slot />\n {/if}\n</button>\n\n\n<style>\n\n.default {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default:active {\n\tbackground-color: #ddd;\n}\n\n.default:focus {\n\tborder-color: #666;\n}\n\n.border {\n\tborder: var(--border);\n}\n\n.color {\n\tcolor: var(--color);\n}\n\n.background {\n\tbackground: var(--background);\n}\n\n.hoverBorder:hover {\n\tborder: var(--hoverBorder);\n}\n\n.hoverColor:hover {\n\tcolor: var(--hoverColor);\n}\n\n.hoverBack:hover {\n\tbackground: var(--hoverBackground);\n}\n\n\n</style>",
"<script>\r\n\r\nexport let columns=[];\r\nexport let data=\"\";\r\nexport let tableClass=\"\";\r\nexport let theadClass=\"\";\r\nexport let tbodyClass=\"\";\r\nexport let trClass=\"\";\r\nexport let thClass=\"\";\r\nexport let onRowClick;\r\n\r\nexport let _bb;\r\n\r\nconst rowClickHandler = (row) => () => {\r\n _bb.call(onRowClick, row);\r\n}\r\n\r\nconst cellValue = (colIndex, row) => \r\n _bb.getStateOrValue(\r\n _bb.bindings.columns[colIndex].value\r\n , row)\r\n\r\n\r\n</script>\r\n\r\n <table class={tableClass}>\r\n <thead class={theadClass}>\r\n <tr class={trClass}>\r\n {#each columns as col}\r\n <th class={thClass}>{col.title}</th>\r\n {/each}\r\n </tr>\r\n </thead>\r\n <tbody class={tbodyClass}>\r\n {#if data}\r\n {#each data as row}\r\n <tr class={trClass}\r\n on:click={rowClickHandler(row)} >\r\n {#each columns as col, index}\r\n <th class={thClass}>{cellValue(index, row)}</th>\r\n {/each}\r\n </tr>\r\n {/each}\r\n {/if}\r\n </tbody>\r\n</table> \r\n\r\n<style>\r\n\r\n.table-default {\r\n width: 100%;\r\n margin-bottom: 1rem;\r\n color: #212529;\r\n border-collapse: collapse;\r\n}\r\n\r\n.table-default .thead-default .th-default {\r\n vertical-align: bottom;\r\n border-bottom: 2px solid #dee2e6;\r\n font-weight: bold;\r\n}\r\n\r\n.table-default .th-default {\r\n padding: .75rem;\r\n vertical-align: top;\r\n border-top: 1px solid #dee2e6;\r\n font-weight: normal;\r\n}\r\n\r\n.th-default {\r\n text-align: inherit;\r\n}\r\n\r\n.table-default .tbody-default .tr-default:hover {\r\n color: #212529;\r\n background-color: rgba(0,0,0,.075);\r\n cursor: pointer;\r\n}\r\n\r\n</style>"
"<script>\r\nimport createApp from \"./createApp\";\r\nimport { props } from \"./props\";\r\n\r\nlet _bb;\r\n\r\nconst _appPromise = createApp();\r\n_appPromise.then(a => _bb = a);\r\n\r\nconst testProps = props.divWithAFewControls;\r\n\r\nlet currentComponent;\r\n\r\n$: {\r\n if(_bb && currentComponent) {\r\n _bb.hydrateChildren(testProps._children, currentComponent);\r\n }\r\n}\r\n\r\n\r\n\r\n</script>\r\n\r\n{#await _appPromise}\r\nloading\r\n{:then _bb}\r\n\r\n<div id=\"current_component\" bind:this={currentComponent}>\r\n</div>\r\n\r\n{/await}\r\n\r\n\r\n<style>\r\n#current_component {\r\n height: 100%;\r\n width: 100%;\r\n}\r\n</style>\r\n\r\n",
"<script>\n\nimport Button from \"./Button.svelte\";\n\nexport let usernameLabel = \"Username\";\nexport let passwordLabel = \"Password\";\nexport let loginButtonLabel = \"Login\";\nexport let loginRedirect = \"\";\nexport let logo = \"\";\nexport let buttonClass = \"\";\nexport let inputClass=\"\"\n\nexport let _bb;\n\nlet username = \"\";\nlet password = \"\";\nlet busy = false;\nlet incorrect = false;\nlet _logo = \"\";\nlet _buttonClass = \"\";\nlet _inputClass = \"\";\n\n$: {\n _logo = _bb.relativeUrl(logo);\n _buttonClass = buttonClass || \"default-button\";\n _inputClass = inputClass || \"default-input\";\n}\n\nconst login = () => {\n busy = true;\n _bb.api.post(\"/api/authenticate\", {username, password})\n .then(r => {\n busy = false;\n if(r.status === 200) {\n return r.json();\n } else {\n incorrect = true;\n return;\n }\n })\n .then(user => {\n if(user) {\n localStorage.setItem(\"budibase:user\", JSON.stringify(user));\n location.reload();\n }\n })\n}\n\n</script>\n\n<div class=\"root\">\n\n <div class=\"content\">\n\n {#if _logo}\n <div class=\"logo-container\">\n <img src={_logo} alt=\"logo\"/>\n </div>\n {/if}\n\n <div class=\"form-root\">\n <div class=\"label\">\n {usernameLabel}\n </div>\n <div class=\"control\">\n <input bind:value={username} type=\"text\" class={_inputClass}/>\n </div>\n <div class=\"label\">\n {passwordLabel}\n </div>\n <div class=\"control\">\n <input bind:value={password} type=\"password\" class={_inputClass}/>\n </div>\n </div>\n\n <div class=\"login-button-container\">\n <button disabled={busy} \n on:click={login}\n class={_buttonClass}>\n {loginButtonLabel}\n </button>\n </div>\n\n {#if incorrect}\n <div class=\"incorrect-details-panel\">\n Incorrect username or password\n </div>\n {/if}\n\n </div>\n\n</div>\n\n<style>\n\n.root {\n height: 100%;\n display:grid;\n grid-template-columns: [left] 1fr [middle] auto [right] 1fr;\n grid-template-rows: [top] 1fr [center] auto [bottom] 1fr;\n}\n\n.content {\n grid-column-start: middle;\n grid-row-start: center;\n width: 400px;\n}\n\n.logo-container {\n margin-bottom: 20px\n}\n\n.logo-container > img {\n max-width: 100%;\n}\n\n.login-button-container {\n text-align: right;\n margin-top: 20px;\n}\n\n.incorrect-details-panel {\n margin-top: 30px;\n padding: 10px;\n border-style: solid;\n border-width: 1px;\n border-color: maroon;\n border-radius: 1px;\n text-align: center;\n color: maroon;\n background-color: mistyrose;\n}\n\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n\n.default-input {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default-button {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default-button:active {\n\tbackground-color: #ddd;\n}\n\n.default-button:focus {\n\tborder-color: #666;\n}\n\n</style>",
"<script>\r\nimport cssVars from \"./cssVars\";\r\n\r\nexport let navBarBackground = \"\";\r\nexport let navBarBorder=\"\";\r\nexport let navBarColor=\"\";\r\nexport let selectedItemBackground=\"\";\r\nexport let selectedItemColor=\"\";\r\nexport let selectedItemBorder=\"\";\r\nexport let itemHoverBackground=\"\";\r\nexport let itemHoverColor=\"\";\r\nexport let hideNavBar=false;\r\nexport let selectedItem=\"\";\r\n\r\nexport let _children;\r\nexport let _bb;\r\n\r\nlet selectedIndex = -1;\r\nlet styleVars={};\r\nlet components = {};\r\nlet componentElements = {}\r\n\r\n\r\nconst hasComponentElements = () => \r\n Object.getOwnPropertyNames(componentElements).length > 0;\r\n\r\n$: {\r\n styleVars = {\r\n navBarBackground, navBarBorder,\r\n navBarColor, selectedItemBackground,\r\n selectedItemColor, selectedItemBorder,\r\n itemHoverBackground, itemHoverColor\r\n };\r\n\r\n if(_children && _children.length > 0 && hasComponentElements()) {\r\n const currentSelectedItem = selectedIndex > 0\r\n ? _children[selectedIndex].title\r\n : \"\";\r\n if(selectedItem && currentSelectedItem !== selectedItem) {\r\n let i=0;\r\n for(let child of _children) {\r\n if(child.title === selectedItem) {\r\n onSelectItem(i)();\r\n }\r\n i++;\r\n }\r\n } else if(!currentSelectedItem) {\r\n onSelectItem(0);\r\n }\r\n }\r\n}\r\n\r\nconst onSelectItem = (index) => () => {\r\n selectedIndex = index;\r\n if(!components[index]) {\r\n const comp = _bb.hydrateChildren(\r\n _children[index].children, componentElements[index]);\r\n components[index] = comp; \r\n }\r\n}\r\n\r\n\r\n</script>\r\n\r\n<div class=\"root\" use:cssVars={styleVars}>\r\n {#if !hideNavBar}\r\n <div class=\"navbar\">\r\n {#each _children as navItem, index}\r\n <div class=\"navitem\"\r\n on:click={onSelectItem(index)}\r\n class:selected={selectedIndex === index}>\r\n {navItem.title}\r\n </div>\r\n {/each}\r\n </div>\r\n {/if}\r\n {#each _children as navItem, index}\r\n <div class=\"content\"\r\n bind:this={componentElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n height: 100%;\r\n width:100%;\r\n grid-template-columns: [navbar] auto [content] 1fr;\r\n display: grid;\r\n}\r\n\r\n.navbar {\r\n grid-column: navbar;\r\n background: var(--navBarBackground);\r\n border: var(--navBarBorder);\r\n color: var(--navBarColor);\r\n}\r\n\r\n.navitem {\r\n padding: 10px 17px;\r\n cursor: pointer;\r\n}\r\n\r\n.navitem:hover {\r\n background: var(--itemHoverBackground);\r\n color: var(--itemHoverColor);\r\n}\r\n\r\n.navitem.selected {\r\n background: var(--selectedItemBackground);\r\n border: var(--selectedItemBorder);\r\n color: var(--selectedItemColor);\r\n}\r\n\r\n.content {\r\n grid-column: content;\r\n}\r\n\r\n</style>\r\n\r\n",
"<script>\r\nimport cssVars from \"./cssVars\";\r\nimport {buildStyle} from \"./buildStyle\";\r\nexport let className = \"default\";\r\nexport let disabled = false;\r\nexport let contentText;\r\nexport let onClick;\r\nexport let background;\r\nexport let color;\r\nexport let border;\r\nexport let padding;\r\nexport let hoverColor;\r\nexport let hoverBackground;\r\nexport let hoverBorder;\r\nexport let _children;\r\n\r\nexport let _bb;\r\nlet theButton;\r\nlet cssVariables;\r\nlet buttonStyles;\r\n\r\nlet customHoverColorClass;\r\nlet customHoverBorderClass;\r\nlet customHoverBackClass;\r\n\r\nlet customClasses = \"\";\r\n\r\nconst createClasses = (classes) => {\r\n\tlet all = \"\";\r\n\tfor(let cls in classes) {\r\n\t\tif(classes[cls]) {\r\n\t\t\tall = all + \" \" + cls;\r\n\t\t}\r\n\t}\r\n\treturn all;\r\n}\r\n\r\n$:{\r\n\tif(_bb && theButton && _children && _children.length)\r\n\t\t_bb.hydrateChildren(_children, theButton);\r\n}\r\n\r\n$:{\r\n\tcssVariables = {\r\n\t\thoverColor, hoverBorder,\r\n\t\thoverBackground,\r\n\t\tbackground, color, border,\r\n\t};\r\n\r\n\tbuttonStyles = buildStyle({\r\n\t\tpadding\r\n\t});\t\r\n\t\r\n\tcustomClasses = createClasses({\r\n\t\thoverColor, hoverBorder, hoverBackground,\r\n\t\tbackground, border, color\r\n\t});\r\n}\r\n\r\n\r\n\r\n\r\nconst clickHandler = () => {\r\n\t_bb.call(onClick);\r\n}\r\n\r\n</script>\r\n\r\n\r\n<button bind:this={theButton}\r\n\t\tuse:cssVars={cssVariables} \r\n\t\tclass=\"{className} {customClasses}\" \r\n\t\tdisabled={disabled || false} \r\n\t\ton:click={clickHandler} \r\n\t\tstyle={buttonStyles}>\r\n {#if !_children || _children.length === 0}\r\n {contentText}\r\n {/if}\r\n</button>\r\n\r\n\r\n<style>\r\n\r\n.default {\r\n\tfont-family: inherit;\r\n\tfont-size: inherit;\r\n\tpadding: 0.4em;\r\n\tmargin: 0 0 0.5em 0;\r\n\tbox-sizing: border-box;\r\n\tborder: 1px solid #ccc;\r\n\tborder-radius: 2px;\r\n\tcolor: #333;\r\n\tbackground-color: #f4f4f4;\r\n\toutline: none;\r\n}\r\n\r\n.default:active {\r\n\tbackground-color: #ddd;\r\n}\r\n\r\n.default:focus {\r\n\tborder-color: #666;\r\n}\r\n\r\n.border {\r\n\tborder: var(--border);\r\n}\r\n\r\n.color {\r\n\tcolor: var(--color);\r\n}\r\n\r\n.background {\r\n\tbackground: var(--background);\r\n}\r\n\r\n.hoverBorder:hover {\r\n\tborder: var(--hoverBorder);\r\n}\r\n\r\n.hoverColor:hover {\r\n\tcolor: var(--hoverColor);\r\n}\r\n\r\n.hoverBack:hover {\r\n\tbackground: var(--hoverBackground);\r\n}\r\n\r\n\r\n</style>",
"<script>\r\n\r\nexport let columns=[];\r\nexport let data=\"\";\r\nexport let tableClass=\"\";\r\nexport let theadClass=\"\";\r\nexport let tbodyClass=\"\";\r\nexport let trClass=\"\";\r\nexport let thClass=\"\";\r\nexport let onRowClick;\r\n\r\nexport let _bb;\r\n\r\nconst rowClickHandler = (row) => () => {\r\n _bb.call(onRowClick, row);\r\n}\r\n\r\nconst cellValue = (colIndex, row) => {\r\n const val = _bb.getStateOrValue(\r\n _bb.props.columns[colIndex].value\r\n , row)\r\n return val;\r\n}\r\n\r\n\r\n</script>\r\n\r\n <table class={tableClass}>\r\n <thead class={theadClass}>\r\n <tr class={trClass}>\r\n {#each columns as col}\r\n <th class={thClass}>{col.title}</th>\r\n {/each}\r\n </tr>\r\n </thead>\r\n <tbody class={tbodyClass}>\r\n {#if data}\r\n {#each data as row}\r\n <tr class={trClass}\r\n on:click={rowClickHandler(row)} >\r\n {#each columns as col, index}\r\n <th class={thClass}>{cellValue(index, row)}</th>\r\n {/each}\r\n </tr>\r\n {/each}\r\n {/if}\r\n </tbody>\r\n</table> \r\n\r\n<style>\r\n\r\n.table-default {\r\n width: 100%;\r\n margin-bottom: 1rem;\r\n color: #212529;\r\n border-collapse: collapse;\r\n}\r\n\r\n.table-default .thead-default .th-default {\r\n vertical-align: bottom;\r\n border-bottom: 2px solid #dee2e6;\r\n font-weight: bold;\r\n}\r\n\r\n.table-default .th-default {\r\n padding: .75rem;\r\n vertical-align: top;\r\n border-top: 1px solid #dee2e6;\r\n font-weight: normal;\r\n}\r\n\r\n.th-default {\r\n text-align: inherit;\r\n}\r\n\r\n.table-default .tbody-default .tr-default:hover {\r\n color: #212529;\r\n background-color: rgba(0,0,0,.075);\r\n cursor: pointer;\r\n}\r\n\r\n</style>"
],
"names": [],
"mappings": "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"
"mappings": "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"
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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