2020-05-07 14:33:25 +02:00
|
|
|
import { split, last, compose } from "lodash/fp"
|
2020-02-18 13:29:38 +01:00
|
|
|
import { prepareRenderComponent } from "./prepareRenderComponent"
|
2020-02-10 16:51:09 +01:00
|
|
|
import { isScreenSlot } from "./builtinComponents"
|
2020-02-18 13:29:38 +01:00
|
|
|
import deepEqual from "deep-equal"
|
2020-01-28 15:14:53 +01:00
|
|
|
|
2020-02-14 12:51:45 +01:00
|
|
|
export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
2020-02-03 10:24:25 +01:00
|
|
|
const {
|
|
|
|
componentLibraries,
|
|
|
|
treeNode,
|
2020-02-10 16:51:09 +01:00
|
|
|
onScreenSlotRendered,
|
2020-02-18 13:29:38 +01:00
|
|
|
setupState,
|
2020-02-03 10:24:25 +01:00
|
|
|
} = initialiseOpts
|
2020-01-28 15:14:53 +01:00
|
|
|
|
2020-02-14 12:51:45 +01:00
|
|
|
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
|
2020-02-14 12:51:45 +01:00
|
|
|
|
|
|
|
if (!force && treeNode.children.length > 0) return treeNode.children
|
|
|
|
|
2020-02-03 10:24:25 +01:00
|
|
|
for (let childNode of treeNode.children) {
|
2020-02-10 16:51:09 +01:00
|
|
|
childNode.destroy()
|
2020-02-03 10:24:25 +01:00
|
|
|
}
|
2020-01-30 00:01:14 +01:00
|
|
|
|
2020-02-26 16:57:48 +01:00
|
|
|
if (!htmlElement) return
|
|
|
|
|
2020-02-03 10:24:25 +01:00
|
|
|
if (hydrate) {
|
|
|
|
while (htmlElement.firstChild) {
|
|
|
|
htmlElement.removeChild(htmlElement.firstChild)
|
2020-01-30 00:01:14 +01:00
|
|
|
}
|
2020-02-03 10:24:25 +01:00
|
|
|
}
|
2020-01-28 15:14:53 +01:00
|
|
|
|
2020-07-06 15:21:55 +02:00
|
|
|
const contextArray = Array.isArray(context) ? context : [context]
|
|
|
|
|
2020-02-18 13:29:38 +01:00
|
|
|
const childNodes = []
|
2020-02-03 10:24:25 +01:00
|
|
|
|
2020-07-06 15:21:55 +02:00
|
|
|
for (let context of contextArray) {
|
|
|
|
for (let childProps of treeNode.props._children) {
|
|
|
|
const { componentName, libName } = splitName(childProps._component)
|
2020-02-03 10:24:25 +01:00
|
|
|
|
2020-07-06 15:21:55 +02:00
|
|
|
if (!componentName || !libName) return
|
2020-01-28 15:14:53 +01:00
|
|
|
|
2020-07-06 15:21:55 +02:00
|
|
|
const ComponentConstructor = componentLibraries[libName][componentName]
|
2020-01-28 15:14:53 +01:00
|
|
|
|
2020-07-06 15:21:55 +02:00
|
|
|
const prepareNodes = ctx => {
|
|
|
|
const childNodesThisIteration = prepareRenderComponent({
|
|
|
|
props: childProps,
|
|
|
|
parentNode: treeNode,
|
|
|
|
ComponentConstructor,
|
|
|
|
htmlElement,
|
|
|
|
anchor,
|
|
|
|
context: ctx,
|
|
|
|
})
|
2020-06-03 00:26:06 +02:00
|
|
|
|
2020-07-06 15:21:55 +02:00
|
|
|
for (let childNode of childNodesThisIteration) {
|
|
|
|
childNodes.push(childNode)
|
|
|
|
}
|
2020-06-03 00:26:06 +02:00
|
|
|
}
|
2020-07-06 15:21:55 +02:00
|
|
|
|
2020-06-03 00:26:06 +02:00
|
|
|
prepareNodes(context)
|
2020-02-10 16:51:09 +01:00
|
|
|
}
|
2020-02-18 13:29:38 +01:00
|
|
|
}
|
2020-02-10 16:51:09 +01:00
|
|
|
|
2020-02-18 13:29:38 +01:00
|
|
|
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)
|
2020-02-03 10:24:25 +01:00
|
|
|
}
|
2020-01-28 15:14:53 +01:00
|
|
|
|
2020-02-18 13:29:38 +01:00
|
|
|
treeNode.children = childNodes
|
2020-02-14 12:51:45 +01:00
|
|
|
|
2020-02-18 13:29:38 +01:00
|
|
|
return childNodes
|
2020-01-28 15:14:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const splitName = fullname => {
|
2020-05-07 11:53:34 +02:00
|
|
|
const getComponentName = compose(last, split("/"))
|
|
|
|
|
|
|
|
const componentName = getComponentName(fullname)
|
2020-01-28 15:14:53 +01:00
|
|
|
|
2020-02-03 10:24:25 +01:00
|
|
|
const libName = fullname.substring(
|
|
|
|
0,
|
|
|
|
fullname.length - componentName.length - 1
|
|
|
|
)
|
2020-01-28 15:14:53 +01:00
|
|
|
|
2020-02-03 10:24:25 +01:00
|
|
|
return { libName, componentName }
|
2020-01-30 00:01:14 +01:00
|
|
|
}
|
2020-02-18 13:29:38 +01:00
|
|
|
|
|
|
|
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++) {
|
2020-07-07 11:48:24 +02:00
|
|
|
isEqual =
|
|
|
|
deepEqual(children1[i].context, children2[i].context) &&
|
|
|
|
areTreeNodesEqual(children1[i].children, children2[i].children)
|
2020-02-18 13:29:38 +01:00
|
|
|
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
|
|
|
|
}
|