budibase/packages/client/src/state/stateManager.js

293 lines
8.0 KiB
JavaScript
Raw Normal View History

import {
isEventType,
eventHandlers,
EVENT_TYPE_MEMBER_NAME,
} from "./eventHandlers"
import { bbFactory } from "./bbComponentApi"
import { createTreeNode } from "../render/prepareRenderComponent"
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";
import { parseBinding } from "./parseBinding"
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"
export const createStateManager = ({
2020-05-30 01:14:41 +02:00
// store,
appRootPath,
frontendDefinition,
componentLibraries,
onScreenSlotRendered,
routeTo,
}) => {
2020-05-30 01:14:41 +02:00
let handlerTypes = eventHandlers(appStore, appRootPath, routeTo)
let currentState
// any nodes that have props that are bound to the store
// let nodesBoundByProps = []
// any node whose children depend on code, that uses the store
// let nodesWithCodeBoundChildren = []
const getCurrentState = () => currentState
// const registerBindings = _registerBindings(
// nodesBoundByProps,
// nodesWithCodeBoundChildren
// )
2020-05-30 01:14:41 +02:00
const bb = bbFactory({
2020-05-30 01:14:41 +02:00
store: appStore,
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-05-30 01:14:41 +02:00
// const unsubscribe = store.subscribe(
// onStoreStateUpdated({
// setCurrentState: state => (currentState = state),
// getCurrentState,
// // nodesWithCodeBoundChildren,
// // nodesBoundByProps,
// componentLibraries,
// onScreenSlotRendered,
// setupState: setup,
// })
// )
return {
setup,
destroy: () => unsubscribe(),
getCurrentState,
2020-05-30 01:14:41 +02:00
store: appStore,
}
}
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 })
// // 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
// }
}
// 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-05-30 01:14:41 +02:00
const _setup = ({
handlerTypes,
getCurrentState,
2020-05-30 01:14:41 +02:00
bb,
store
}) => node => {
const props = node.props
const context = node.context || {}
const initialProps = { ...props }
// const storeBoundProps = []
const currentStoreState = getCurrentState()
2020-05-30 01:14:41 +02:00
console.log("node", node);
// console.log("node", node);
// console.log("nodeComponent", node.component);
for (let propName in props) {
if (isMetaProp(propName)) continue
2020-02-21 19:02:02 +01:00
const propValue = props[propName]
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-05-29 15:06:10 +02:00
// if (isBound) binding.propName = propName
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)) {
const handlersInfos = []
2020-02-21 19:02:02 +01:00
for (let event of propValue) {
const handlerInfo = {
2020-02-21 19:02:02 +01:00
handlerType: event[EVENT_TYPE_MEMBER_NAME],
parameters: event.parameters,
}
2020-05-29 15:06:10 +02: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)
}
handlerInfo.parameters = resolvedParams
handlersInfos.push(handlerInfo)
}
2020-05-29 15:06:10 +02:00
if (handlersInfos.length === 0) {
initialProps[propName] = doNothing
} else {
initialProps[propName] = async context => {
for (let handlerInfo of handlersInfos) {
const handler = makeHandler(handlerTypes, handlerInfo)
await handler(context)
}
}
}
}
}
// registerBindings(node, storeBoundProps)
2020-05-30 01:14:41 +02:00
const setup = _setup({ handlerTypes, getCurrentState, bb, store })
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 => {
const parameters = {}
2020-02-21 19:02:02 +01:00
for (let paramName in handlerInfo.parameters) {
parameters[paramName] = handlerInfo.parameters[paramName](context)
}
2020-02-24 17:04:13 +01:00
await handlerType.execute(parameters)
}
2020-02-23 23:07:28 +01:00
}