budibase/packages/client/src/render/attachChildren.js

113 lines
3.0 KiB
JavaScript
Raw Normal View History

2020-05-07 14:33:25 +02:00
import { split, last, compose } from "lodash/fp"
import { prepareRenderComponent } from "./prepareRenderComponent"
import { isScreenSlot } from "./builtinComponents"
import deepEqual from "deep-equal"
export const attachChildren = initialiseOpts => (htmlElement, options) => {
const {
componentLibraries,
treeNode,
onScreenSlotRendered,
setupState,
} = initialiseOpts
const anchor = options && options.anchor ? options.anchor : null
const force = options ? options.force : false
const hydrate = options ? options.hydrate : true
2020-06-03 00:26:06 +02:00
const context = options && options.context
if (!force && treeNode.children.length > 0) return treeNode.children
for (let childNode of treeNode.children) {
childNode.destroy()
}
if (!htmlElement) return
if (hydrate) {
while (htmlElement.firstChild) {
htmlElement.removeChild(htmlElement.firstChild)
}
}
const contextArray = Array.isArray(context) ? context : [context]
const childNodes = []
for (let context of contextArray) {
for (let childProps of treeNode.props._children) {
const { componentName, libName } = splitName(childProps._component)
if (!componentName || !libName) return
const ComponentConstructor = componentLibraries[libName][componentName]
const prepareNodes = ctx => {
const childNodesThisIteration = prepareRenderComponent({
props: childProps,
parentNode: treeNode,
ComponentConstructor,
htmlElement,
anchor,
context: ctx,
})
2020-06-03 00:26:06 +02:00
for (let childNode of childNodesThisIteration) {
childNodes.push(childNode)
}
2020-06-03 00:26:06 +02:00
}
2020-06-03 00:26:06 +02:00
prepareNodes(context)
}
}
if (areTreeNodesEqual(treeNode.children, childNodes)) return treeNode.children
for (let node of childNodes) {
const initialProps = setupState(node)
node.render(initialProps)
}
const screenSlot = childNodes.find(n => isScreenSlot(n.props._component))
if (onScreenSlotRendered && screenSlot) {
// assuming there is only ever one screen slot
onScreenSlotRendered(screenSlot)
}
treeNode.children = childNodes
return childNodes
}
const splitName = fullname => {
2020-05-07 11:53:34 +02:00
const getComponentName = compose(last, split("/"))
const componentName = getComponentName(fullname)
const libName = fullname.substring(
0,
fullname.length - componentName.length - 1
)
return { libName, componentName }
}
const areTreeNodesEqual = (children1, children2) => {
if (children1.length !== children2.length) return false
if (children1 === children2) return true
let isEqual = false
for (let i = 0; i < children1.length; i++) {
isEqual =
deepEqual(children1[i].context, children2[i].context) &&
areTreeNodesEqual(children1[i].children, children2[i].children)
if (!isEqual) return false
if (isScreenSlot(children1[i].parentNode.props._component)) {
isEqual = deepEqual(children1[i].props, children2[i].props)
}
if (!isEqual) return false
}
return true
}