2020-02-18 13:29:38 +01:00
|
|
|
import {
|
|
|
|
isEventType,
|
|
|
|
eventHandlers,
|
|
|
|
EVENT_TYPE_MEMBER_NAME,
|
|
|
|
} from "./eventHandlers"
|
|
|
|
import { bbFactory } from "./bbComponentApi"
|
2020-05-29 17:06:23 +02:00
|
|
|
import { createTreeNode } from "../render/prepareRenderComponent"
|
2020-02-18 13:29:38 +01:00
|
|
|
import { getState } from "./getState"
|
|
|
|
import { attachChildren } from "../render/attachChildren"
|
2020-05-29 15:06:10 +02:00
|
|
|
import mustache from "mustache"
|
2020-05-30 01:14:41 +02:00
|
|
|
import { appStore } from "./store";
|
2020-02-18 13:29:38 +01:00
|
|
|
|
2020-02-21 15:44:48 +01:00
|
|
|
import { parseBinding } from "./parseBinding"
|
2020-02-18 13:29:38 +01:00
|
|
|
|
|
|
|
const doNothing = () => {}
|
|
|
|
doNothing.isPlaceholder = true
|
|
|
|
|
|
|
|
const isMetaProp = propName =>
|
|
|
|
propName === "_component" ||
|
|
|
|
propName === "_children" ||
|
|
|
|
propName === "_id" ||
|
|
|
|
propName === "_style" ||
|
|
|
|
propName === "_code" ||
|
2020-05-29 15:06:10 +02:00
|
|
|
propName === "_codeMeta" ||
|
|
|
|
propName === "_styles"
|
2020-02-18 13:29:38 +01:00
|
|
|
|
|
|
|
export const createStateManager = ({
|
2020-05-30 01:14:41 +02:00
|
|
|
// store,
|
2020-02-24 12:15:08 +01:00
|
|
|
appRootPath,
|
2020-02-18 13:29:38 +01:00
|
|
|
frontendDefinition,
|
|
|
|
componentLibraries,
|
|
|
|
onScreenSlotRendered,
|
2020-02-21 15:44:48 +01:00
|
|
|
routeTo,
|
2020-02-18 13:29:38 +01:00
|
|
|
}) => {
|
2020-05-30 01:14:41 +02:00
|
|
|
let handlerTypes = eventHandlers(appStore, appRootPath, routeTo)
|
2020-02-18 13:29:38 +01:00
|
|
|
let currentState
|
|
|
|
|
|
|
|
// any nodes that have props that are bound to the store
|
2020-05-29 17:06:23 +02:00
|
|
|
// let nodesBoundByProps = []
|
2020-02-18 13:29:38 +01:00
|
|
|
|
|
|
|
// any node whose children depend on code, that uses the store
|
2020-05-29 17:06:23 +02:00
|
|
|
// let nodesWithCodeBoundChildren = []
|
2020-02-18 13:29:38 +01:00
|
|
|
|
|
|
|
const getCurrentState = () => currentState
|
2020-05-29 17:06:23 +02:00
|
|
|
// const registerBindings = _registerBindings(
|
|
|
|
// nodesBoundByProps,
|
|
|
|
// nodesWithCodeBoundChildren
|
|
|
|
// )
|
2020-05-30 01:14:41 +02:00
|
|
|
|
2020-02-18 13:29:38 +01:00
|
|
|
const bb = bbFactory({
|
2020-05-30 01:14:41 +02:00
|
|
|
store: appStore,
|
2020-02-18 13:29:38 +01:00
|
|
|
getCurrentState,
|
|
|
|
frontendDefinition,
|
|
|
|
componentLibraries,
|
|
|
|
onScreenSlotRendered,
|
|
|
|
})
|
|
|
|
|
2020-05-30 01:14:41 +02:00
|
|
|
const setup = _setup({ handlerTypes, getCurrentState, bb, store: appStore })
|
|
|
|
|
|
|
|
// TODO: remove
|
|
|
|
const unsubscribe = appStore.subscribe(state => {
|
|
|
|
console.log("store updated", state);
|
|
|
|
return state;
|
|
|
|
});
|
2020-02-18 13:29:38 +01:00
|
|
|
|
2020-05-30 01:14:41 +02:00
|
|
|
// const unsubscribe = store.subscribe(
|
|
|
|
// onStoreStateUpdated({
|
|
|
|
// setCurrentState: state => (currentState = state),
|
|
|
|
// getCurrentState,
|
|
|
|
// // nodesWithCodeBoundChildren,
|
|
|
|
// // nodesBoundByProps,
|
|
|
|
// componentLibraries,
|
|
|
|
// onScreenSlotRendered,
|
|
|
|
// setupState: setup,
|
|
|
|
// })
|
|
|
|
// )
|
2020-02-18 13:29:38 +01:00
|
|
|
|
|
|
|
return {
|
|
|
|
setup,
|
|
|
|
destroy: () => unsubscribe(),
|
|
|
|
getCurrentState,
|
2020-05-30 01:14:41 +02:00
|
|
|
store: appStore,
|
2020-02-18 13:29:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const onStoreStateUpdated = ({
|
|
|
|
setCurrentState,
|
|
|
|
getCurrentState,
|
|
|
|
componentLibraries,
|
|
|
|
onScreenSlotRendered,
|
2020-05-30 01:14:41 +02:00
|
|
|
setupState
|
2020-05-29 15:06:10 +02:00
|
|
|
}) => state => {
|
2020-05-30 01:14:41 +02:00
|
|
|
// fire the state update event to re-render anything bound to this
|
|
|
|
// setCurrentState(state)
|
|
|
|
|
|
|
|
// setCurrentState(state)
|
|
|
|
// attachChildren({
|
|
|
|
// componentLibraries,
|
|
|
|
// treeNode: createTreeNode(),
|
|
|
|
// onScreenSlotRendered,
|
|
|
|
// setupState,
|
|
|
|
// getCurrentState,
|
|
|
|
// })(document.querySelector("#app"), { hydrate: true, force: true })
|
2020-05-29 17:06:23 +02:00
|
|
|
|
|
|
|
// // the original array gets changed by components' destroy()
|
|
|
|
// // so we make a clone and check if they are still in the original
|
|
|
|
// const nodesWithBoundChildren_clone = [...nodesWithCodeBoundChildren]
|
|
|
|
// for (let node of nodesWithBoundChildren_clone) {
|
|
|
|
// if (!nodesWithCodeBoundChildren.includes(node)) continue
|
|
|
|
// attachChildren({
|
|
|
|
// componentLibraries,
|
|
|
|
// treeNode: node,
|
|
|
|
// onScreenSlotRendered,
|
|
|
|
// setupState,
|
|
|
|
// getCurrentState,
|
|
|
|
// })(node.rootElement, { hydrate: true, force: true })
|
2020-05-29 15:06:10 +02:00
|
|
|
// }
|
2020-02-18 13:29:38 +01:00
|
|
|
}
|
|
|
|
|
2020-05-29 17:06:23 +02:00
|
|
|
// const _registerBindings = (nodesBoundByProps, nodesWithCodeBoundChildren) => (
|
|
|
|
// node,
|
|
|
|
// bindings
|
|
|
|
// ) => {
|
|
|
|
// if (bindings.length > 0) {
|
|
|
|
// node.bindings = bindings
|
|
|
|
// nodesBoundByProps.push(node)
|
|
|
|
// const onDestroy = () => {
|
|
|
|
// nodesBoundByProps = nodesBoundByProps.filter(n => n === node)
|
|
|
|
// node.onDestroy = node.onDestroy.filter(d => d === onDestroy)
|
|
|
|
// }
|
|
|
|
// node.onDestroy.push(onDestroy)
|
|
|
|
// }
|
|
|
|
// if (
|
|
|
|
// node.props._children &&
|
|
|
|
// node.props._children.filter(c => c._codeMeta && c._codeMeta.dependsOnStore)
|
|
|
|
// .length > 0
|
|
|
|
// ) {
|
|
|
|
// nodesWithCodeBoundChildren.push(node)
|
|
|
|
// const onDestroy = () => {
|
|
|
|
// nodesWithCodeBoundChildren = nodesWithCodeBoundChildren.filter(
|
|
|
|
// n => n === node
|
|
|
|
// )
|
|
|
|
// node.onDestroy = node.onDestroy.filter(d => d === onDestroy)
|
|
|
|
// }
|
|
|
|
// node.onDestroy.push(onDestroy)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// const setNodeState = (storeState, node) => {
|
|
|
|
// if (!node.component) return
|
|
|
|
// const newProps = { ...node.bindings.initialProps }
|
|
|
|
|
|
|
|
// for (let binding of node.bindings) {
|
|
|
|
// const val = getState(storeState, binding.path, binding.fallback)
|
|
|
|
|
|
|
|
// if (val === undefined && newProps[binding.propName] !== undefined) {
|
|
|
|
// delete newProps[binding.propName]
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (val !== undefined) {
|
|
|
|
// newProps[binding.propName] = val
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// node.component.$set(newProps)
|
|
|
|
// }
|
2020-02-18 13:29:38 +01:00
|
|
|
|
2020-05-30 01:14:41 +02:00
|
|
|
const _setup = ({
|
2020-02-18 13:29:38 +01:00
|
|
|
handlerTypes,
|
|
|
|
getCurrentState,
|
2020-05-30 01:14:41 +02:00
|
|
|
bb,
|
|
|
|
store
|
|
|
|
}) => node => {
|
2020-02-18 13:29:38 +01:00
|
|
|
const props = node.props
|
|
|
|
const context = node.context || {}
|
|
|
|
const initialProps = { ...props }
|
2020-05-29 17:06:23 +02:00
|
|
|
// const storeBoundProps = []
|
2020-02-18 13:29:38 +01:00
|
|
|
const currentStoreState = getCurrentState()
|
|
|
|
|
2020-05-30 01:14:41 +02:00
|
|
|
console.log("node", node);
|
|
|
|
|
|
|
|
// console.log("node", node);
|
|
|
|
// console.log("nodeComponent", node.component);
|
|
|
|
|
2020-02-18 13:29:38 +01:00
|
|
|
for (let propName in props) {
|
|
|
|
if (isMetaProp(propName)) continue
|
|
|
|
|
2020-02-21 19:02:02 +01:00
|
|
|
const propValue = props[propName]
|
2020-02-18 13:29:38 +01:00
|
|
|
|
2020-05-29 15:06:10 +02:00
|
|
|
// const binding = parseBinding(propValue)
|
2020-05-30 01:14:41 +02:00
|
|
|
// TODO: better binding stuff
|
|
|
|
const isBound = typeof propValue === "string" && propValue.startsWith("{{");
|
2020-02-21 19:02:02 +01:00
|
|
|
|
2020-05-30 01:14:41 +02:00
|
|
|
if (isBound) {
|
2020-05-29 15:06:10 +02:00
|
|
|
initialProps[propName] = mustache.render(propValue, {
|
|
|
|
state: currentStoreState,
|
|
|
|
context
|
|
|
|
})
|
2020-05-30 01:14:41 +02:00
|
|
|
|
|
|
|
if (!node.stateBound) {
|
|
|
|
node.stateBound = true
|
|
|
|
}
|
2020-05-29 15:06:10 +02:00
|
|
|
}
|
2020-02-18 13:29:38 +01:00
|
|
|
|
2020-05-29 15:06:10 +02:00
|
|
|
// if (isBound) binding.propName = propName
|
2020-02-18 13:29:38 +01:00
|
|
|
|
2020-05-29 15:06:10 +02:00
|
|
|
// if (isBound && binding.source === "state") {
|
|
|
|
// storeBoundProps.push(binding)
|
2020-02-23 23:07:28 +01:00
|
|
|
|
2020-05-29 15:06:10 +02:00
|
|
|
// initialProps[propName] = !currentStoreState
|
|
|
|
// ? binding.fallback
|
|
|
|
// : getState(
|
|
|
|
// currentStoreState,
|
|
|
|
// binding.path,
|
|
|
|
// binding.fallback,
|
|
|
|
// binding.source
|
|
|
|
// )
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (isBound && binding.source === "context") {
|
|
|
|
// initialProps[propName] = !context
|
|
|
|
// ? propValue
|
|
|
|
// : getState(context, binding.path, binding.fallback, binding.source)
|
|
|
|
// }
|
2020-02-23 23:07:28 +01:00
|
|
|
|
2020-02-21 19:02:02 +01:00
|
|
|
if (isEventType(propValue)) {
|
2020-02-18 13:29:38 +01:00
|
|
|
const handlersInfos = []
|
2020-02-21 19:02:02 +01:00
|
|
|
for (let event of propValue) {
|
2020-02-18 13:29:38 +01:00
|
|
|
const handlerInfo = {
|
2020-02-21 19:02:02 +01:00
|
|
|
handlerType: event[EVENT_TYPE_MEMBER_NAME],
|
|
|
|
parameters: event.parameters,
|
2020-02-18 13:29:38 +01:00
|
|
|
}
|
2020-05-29 15:06:10 +02:00
|
|
|
|
2020-02-18 13:29:38 +01:00
|
|
|
const resolvedParams = {}
|
|
|
|
for (let paramName in handlerInfo.parameters) {
|
|
|
|
const paramValue = handlerInfo.parameters[paramName]
|
2020-05-29 15:06:10 +02:00
|
|
|
resolvedParams[paramName] = () => mustache.render(paramValue, {
|
|
|
|
state: getCurrentState(),
|
|
|
|
context,
|
|
|
|
})
|
|
|
|
// const paramBinding = parseBinding(paramValue)
|
|
|
|
// if (!paramBinding) {
|
|
|
|
// resolvedParams[paramName] = () => paramValue
|
|
|
|
// continue
|
|
|
|
// }
|
|
|
|
|
|
|
|
// let paramValueSource
|
|
|
|
|
|
|
|
// if (paramBinding.source === "context") paramValueSource = context
|
|
|
|
// if (paramBinding.source === "state")
|
|
|
|
// paramValueSource = getCurrentState()
|
|
|
|
|
|
|
|
// // The new dynamic event parameter bound to the relevant source
|
|
|
|
// resolvedParams[paramName] = () =>
|
|
|
|
// getState(paramValueSource, paramBinding.path, paramBinding.fallback)
|
2020-02-18 13:29:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
handlerInfo.parameters = resolvedParams
|
|
|
|
handlersInfos.push(handlerInfo)
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:06:10 +02:00
|
|
|
if (handlersInfos.length === 0) {
|
|
|
|
initialProps[propName] = doNothing
|
|
|
|
} else {
|
2020-02-18 13:29:38 +01:00
|
|
|
initialProps[propName] = async context => {
|
|
|
|
for (let handlerInfo of handlersInfos) {
|
|
|
|
const handler = makeHandler(handlerTypes, handlerInfo)
|
|
|
|
await handler(context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-29 17:06:23 +02:00
|
|
|
// registerBindings(node, storeBoundProps)
|
2020-02-18 13:29:38 +01:00
|
|
|
|
2020-05-30 01:14:41 +02:00
|
|
|
const setup = _setup({ handlerTypes, getCurrentState, bb, store })
|
2020-02-18 13:29:38 +01:00
|
|
|
initialProps._bb = bb(node, setup)
|
|
|
|
|
|
|
|
return initialProps
|
|
|
|
}
|
|
|
|
|
|
|
|
const makeHandler = (handlerTypes, handlerInfo) => {
|
|
|
|
const handlerType = handlerTypes[handlerInfo.handlerType]
|
2020-02-24 17:04:13 +01:00
|
|
|
return async context => {
|
2020-02-18 13:29:38 +01:00
|
|
|
const parameters = {}
|
2020-02-21 19:02:02 +01:00
|
|
|
for (let paramName in handlerInfo.parameters) {
|
|
|
|
parameters[paramName] = handlerInfo.parameters[paramName](context)
|
2020-02-18 13:29:38 +01:00
|
|
|
}
|
2020-02-24 17:04:13 +01:00
|
|
|
await handlerType.execute(parameters)
|
2020-02-18 13:29:38 +01:00
|
|
|
}
|
2020-02-23 23:07:28 +01:00
|
|
|
}
|