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
This commit is contained in:
parent
fe9d83d08e
commit
1ec928e60a
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -9,13 +9,18 @@ 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;
|
||||
store, componentLibraries, treeNode,
|
||||
appDefinition, document, hydrate } = initialiseOpts;
|
||||
|
||||
const childComponents = [];
|
||||
for(let childNode of treeNode.children) {
|
||||
if(childNode.unsubscribe)
|
||||
childNode.unsubscribe();
|
||||
if(childNode.component)
|
||||
childNode.component.$destroy();
|
||||
}
|
||||
|
||||
if(hydrate) {
|
||||
while (htmlElement.firstChild) {
|
||||
|
@ -23,6 +28,7 @@ export const _initialiseChildren = (initialiseOpts) =>
|
|||
}
|
||||
}
|
||||
|
||||
const renderedComponents = [];
|
||||
for(let childProps of childrenProps) {
|
||||
|
||||
const {componentName, libName} = splitName(childProps._component);
|
||||
|
@ -31,27 +37,24 @@ export const _initialiseChildren = (initialiseOpts) =>
|
|||
|
||||
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)
|
||||
};
|
||||
appDefinition.appRootPath);
|
||||
|
||||
const componentConstructor = componentLibraries[libName][componentName];
|
||||
|
||||
const {component} = renderComponent({
|
||||
const renderedComponentsThisIteration = renderComponent({
|
||||
props: childProps,
|
||||
parentNode: treeNode,
|
||||
componentConstructor,uiFunctions,
|
||||
htmlElement, anchor,
|
||||
parentContext, componentProps});
|
||||
htmlElement, anchor, initialProps,
|
||||
bb, document});
|
||||
|
||||
|
||||
bind(component);
|
||||
childComponents.push(component);
|
||||
for(let comp of renderedComponentsThisIteration) {
|
||||
comp.unsubscribe = bind(comp.component);
|
||||
renderedComponents.push(comp);
|
||||
}
|
||||
}
|
||||
|
||||
return childComponents;
|
||||
return renderedComponents;
|
||||
}
|
||||
|
||||
const splitName = fullname => {
|
||||
|
|
|
@ -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;
|
||||
|
||||
let component;
|
||||
let componentContext;
|
||||
const parentContext = (parentNode && parentNode.context) || {};
|
||||
|
||||
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: () => {}
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue