From be9443561def441f53dcd6e76a70b11e57ba19d6 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Wed, 29 Jan 2020 23:01:14 +0000 Subject: [PATCH] Control flow - Client library foundations (#72) * removed binding references to array type * refactored initialiseChildren into seperate file * render function, with code blocks - tested simple cases * few mores tests for control flow * md components - getting TestApp to work * new render wrapper - bug fix * client: providing access to component root elements --- packages/client/src/createApp.js | 79 +++++-------------- packages/client/src/index.js | 7 +- .../client/src/render/initialiseChildren.js | 51 ++++++------ packages/client/src/render/renderComponent.js | 49 ++++++++---- packages/client/tests/testAppDef.js | 2 +- 5 files changed, 85 insertions(+), 103 deletions(-) diff --git a/packages/client/src/createApp.js b/packages/client/src/createApp.js index accad9f51e..bddbb8999d 100644 --- a/packages/client/src/createApp.js +++ b/packages/client/src/createApp.js @@ -5,8 +5,9 @@ import { setState, setStateFromBinding } from "./state/setState"; import { trimSlash } from "./common/trimSlash"; import { isBound } from "./state/isState"; import { _initialiseChildren } from "./render/initialiseChildren"; +import { createTreeNode } from "./render/renderComponent"; -export const createApp = (componentLibraries, appDefinition, user, uiFunctions) => { +export const createApp = (document, componentLibraries, appDefinition, user, uiFunctions) => { const coreApi = createCoreApi(appDefinition, user); appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(appDefinition.hierarchy); @@ -50,74 +51,32 @@ export const createApp = (componentLibraries, appDefinition, user, uiFunctions) if(isFunction(event)) event(context); } - const initialiseChildrenParams = (parentContext, hydrate) => ({ - bb, coreApi, store, + const initialiseChildrenParams = (hydrate, treeNode) => ({ + bb, coreApi, store, document, componentLibraries, appDefinition, - parentContext, hydrate, uiFunctions + hydrate, uiFunctions, treeNode }); - const bb = (context, props) => ({ - hydrateChildren: _initialiseChildren(initialiseChildrenParams(context, true)), - appendChildren: _initialiseChildren(initialiseChildrenParams(context, false)), - insertChildren: (props, htmlElement, anchor, context) => - _initialiseChildren(initialiseChildrenParams(context, false)) - (props, htmlElement, context, anchor), - store, - relativeUrl, - api, + const bb = (treeNode, componentProps) => ({ + hydrateChildren: _initialiseChildren(initialiseChildrenParams(true, treeNode)), + appendChildren: _initialiseChildren(initialiseChildrenParams(false, treeNode)), + insertChildren: (props, htmlElement, anchor) => + _initialiseChildren(initialiseChildrenParams(false, treeNode)) + (props, htmlElement, anchor), + context: treeNode.context, + props: componentProps, call:safeCallEvent, - isBound, setStateFromBinding: (binding, value) => setStateFromBinding(store, binding, value), setState: (path, value) => setState(store, path, value), getStateOrValue: (prop, currentContext) => getStateOrValue(globalState, prop, currentContext), - context, - props + store, + relativeUrl, + api, + isBound, + parent }); - return bb(); + return bb(createTreeNode()); } - -const buildBindings = (boundProps, boundArrays, contextBoundProps) => { - const bindings = {}; - if(boundProps && boundProps.length > 0) { - for(let p of boundProps) { - bindings[p.propName] = { - path: p.path, - fallback: p.fallback, - source: p.source - } - } - } - - if(contextBoundProps && contextBoundProps.length > 0) { - for(let p of contextBoundProps) { - bindings[p.propName] = { - path: p.path, - fallback: p.fallback, - source: p.source - } - } - } - - if(boundArrays && boundArrays.length > 0) { - for(let a of boundArrays) { - const arrayOfBindings = []; - - for(let b of a.arrayOfBindings) { - arrayOfBindings.push( - buildBindings( - b.boundProps, - b.boundArrays, - b.contextBoundProps) - ); - } - - bindings[a.propName] = arrayOfBindings; - } - } - - return bindings; -} - diff --git a/packages/client/src/index.js b/packages/client/src/index.js index aba4906680..7114cde443 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -35,16 +35,17 @@ export const loadBudibase = async ({ props = appDefinition.props; } - const _app = createApp( + const app = createApp( + window.document, componentLibraries, appDefinition, user, uiFunctions || {}); - _app.hydrateChildren( + app.hydrateChildren( [props], window.document.body); - return _app; + return app; }; if(window) { diff --git a/packages/client/src/render/initialiseChildren.js b/packages/client/src/render/initialiseChildren.js index 50a0052642..2fef49fa45 100644 --- a/packages/client/src/render/initialiseChildren.js +++ b/packages/client/src/render/initialiseChildren.js @@ -9,20 +9,26 @@ import { $ } from "../core/common"; import { renderComponent } from "./renderComponent"; export const _initialiseChildren = (initialiseOpts) => - (childrenProps, htmlElement, context, anchor=null) => { + (childrenProps, htmlElement, anchor=null) => { const { uiFunctions, bb, coreApi, - store, componentLibraries, - appDefinition, parentContext, hydrate } = initialiseOpts; - - const childComponents = []; + store, componentLibraries, treeNode, + appDefinition, document, hydrate } = initialiseOpts; + + for(let childNode of treeNode.children) { + if(childNode.unsubscribe) + childNode.unsubscribe(); + if(childNode.component) + childNode.component.$destroy(); + } if(hydrate) { while (htmlElement.firstChild) { htmlElement.removeChild(htmlElement.firstChild); } } - + + const renderedComponents = []; for(let childProps of childrenProps) { const {componentName, libName} = splitName(childProps._component); @@ -30,28 +36,25 @@ export const _initialiseChildren = (initialiseOpts) => if(!componentName || !libName) return; const {initialProps, bind} = setupBinding( - store, childProps, coreApi, - context || parentContext, appDefinition.appRootPath); - - /// here needs to go inside renderComponent ??? - const componentProps = { - ...initialProps, - _bb:bb(context || parentContext, childProps) - }; - + store, childProps, coreApi, + appDefinition.appRootPath); + const componentConstructor = componentLibraries[libName][componentName]; - const {component} = renderComponent({ + const renderedComponentsThisIteration = renderComponent({ + props: childProps, + parentNode: treeNode, componentConstructor,uiFunctions, - htmlElement, anchor, - parentContext, componentProps}); - - - bind(component); - childComponents.push(component); + htmlElement, anchor, initialProps, + bb, document}); + + for(let comp of renderedComponentsThisIteration) { + comp.unsubscribe = bind(comp.component); + renderedComponents.push(comp); + } } - return childComponents; + return renderedComponents; } const splitName = fullname => { @@ -64,4 +67,4 @@ const splitName = fullname => { 0, fullname.length - componentName.length - 1); return {libName, componentName}; -} \ No newline at end of file +} diff --git a/packages/client/src/render/renderComponent.js b/packages/client/src/render/renderComponent.js index 3ed66ac517..00d656915e 100644 --- a/packages/client/src/render/renderComponent.js +++ b/packages/client/src/render/renderComponent.js @@ -1,30 +1,43 @@ export const renderComponent = ({ componentConstructor, uiFunctions, - htmlElement, anchor, parentContext, - componentProps}) => { + htmlElement, anchor, props, + initialProps, bb, document, + parentNode}) => { - const func = componentProps._id - ? uiFunctions[componentProps._id] + const func = initialProps._id + ? uiFunctions[initialProps._id] : undefined; + + const parentContext = (parentNode && parentNode.context) || {}; - let component; - let componentContext; + let renderedNodes = []; const render = (context) => { + let componentContext = parentContext; if(context) { componentContext = {...componentContext}; componentContext.$parent = parentContext; - } else { - componentContext = parentContext; } - component = new componentConstructor({ + const thisNode = createTreeNode(); + thisNode.context = componentContext; + thisNode.parentNode = parentNode; + + parentNode.children.push(thisNode); + renderedNodes.push(thisNode); + + initialProps._bb = bb(thisNode, props); + + thisNode.component = new componentConstructor({ target: htmlElement, - props: componentProps, + props: initialProps, hydrate:false, anchor - }); + }); + + thisNode.rootElement = htmlElement.children[ + htmlElement.children.length - 1]; } if(func) { @@ -33,9 +46,15 @@ export const renderComponent = ({ render(); } - return ({ - context: componentContext, - component - }); + return renderedNodes; } +export const createTreeNode = () => ({ + context: {}, + rootElement: null, + parentNode: null, + children: [], + component: null, + unsubscribe: () => {} +}); + diff --git a/packages/client/tests/testAppDef.js b/packages/client/tests/testAppDef.js index e028730890..bcaa4cae17 100644 --- a/packages/client/tests/testAppDef.js +++ b/packages/client/tests/testAppDef.js @@ -70,7 +70,7 @@ const maketestlib = (window) => ({ node.removeChild(c); } const components = currentProps._bb.appendChildren(currentProps._children, node); - childNodes = components.map(c => c._element); + childNodes = components.map(c => c.component._element); } else { currentProps._bb.hydrateChildren(currentProps._children, node); }