refactor client library
This commit is contained in:
parent
7a3b368399
commit
e648dc80e8
|
@ -155,7 +155,6 @@ const createScreen = store => (screenName, route, layoutComponentName) => {
|
|||
description: "",
|
||||
url: "",
|
||||
_css: "",
|
||||
uiFunctions: "",
|
||||
props: createProps(rootComponent).props,
|
||||
}
|
||||
|
||||
|
@ -281,7 +280,6 @@ const _savePage = async s => {
|
|||
const page = s.pages[s.currentPageName]
|
||||
await api.post(`/_builder/api/${s.appId}/pages/${s.currentPageName}`, {
|
||||
page: { componentLibraries: s.pages.componentLibraries, ...page },
|
||||
uiFunctions: s.currentPageFunctions,
|
||||
screens: page._screens,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -116,8 +116,7 @@
|
|||
stylesheetLinks,
|
||||
selectedComponentType,
|
||||
selectedComponentId,
|
||||
frontendDefinition: JSON.stringify(frontendDefinition),
|
||||
currentPageFunctions: $store.currentPageFunctions,
|
||||
frontendDefinition: JSON.stringify(frontendDefinition)
|
||||
})} />
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,6 @@ export default ({
|
|||
</style>
|
||||
<script>
|
||||
window["##BUDIBASE_FRONTEND_DEFINITION##"] = ${frontendDefinition};
|
||||
window["##BUDIBASE_FRONTEND_FUNCTIONS##"] = ${currentPageFunctions};
|
||||
|
||||
import('/_builder/budibase-client.esm.mjs')
|
||||
.then(module => {
|
||||
|
|
|
@ -32,6 +32,11 @@
|
|||
type="number"
|
||||
class="budibase__input"
|
||||
bind:value={workflowBlock.args[parameter]} />
|
||||
{:else if type === 'longText'}
|
||||
<textarea
|
||||
type="text"
|
||||
class="budibase__input"
|
||||
bind:value={workflowBlock.args[parameter]} />
|
||||
{:else if type === 'model'}
|
||||
<select
|
||||
class="budibase__input"
|
||||
|
|
|
@ -66,8 +66,8 @@ const ACTION = {
|
|||
params: {
|
||||
to: "string",
|
||||
from: "string",
|
||||
subject: "string",
|
||||
text: "string",
|
||||
subject: "longText",
|
||||
text: "longText",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -16,6 +16,5 @@
|
|||
},
|
||||
"_code": ""
|
||||
},
|
||||
"_css": "",
|
||||
"uiFunctions": ""
|
||||
"_css": ""
|
||||
}
|
||||
|
|
|
@ -16,6 +16,5 @@
|
|||
},
|
||||
"_code": ""
|
||||
},
|
||||
"_css": "",
|
||||
"uiFunctions": ""
|
||||
"_css": ""
|
||||
}
|
||||
|
|
|
@ -16,6 +16,5 @@
|
|||
},
|
||||
"_code": ""
|
||||
},
|
||||
"_css": "",
|
||||
"uiFunctions": ""
|
||||
"_css": ""
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
"deep-equal": "^2.0.1",
|
||||
"lodash": "^4.17.15",
|
||||
"lunr": "^2.3.5",
|
||||
"mustache": "^4.0.1",
|
||||
"regexparam": "^1.3.0",
|
||||
"shortid": "^2.2.8",
|
||||
"svelte": "^3.9.2"
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import { loadRecord } from "./loadRecord"
|
||||
import { listRecords } from "./listRecords"
|
||||
import { authenticate } from "./authenticate"
|
||||
import { saveRecord } from "./saveRecord"
|
||||
import { triggerWorkflow } from "./workflow"
|
||||
|
||||
export const createApi = ({ rootPath = "", setState, getState }) => {
|
||||
|
@ -60,10 +57,7 @@ export const createApi = ({ rootPath = "", setState, getState }) => {
|
|||
}
|
||||
|
||||
return {
|
||||
loadRecord: loadRecord(apiOpts),
|
||||
listRecords: listRecords(apiOpts),
|
||||
authenticate: authenticate(apiOpts),
|
||||
saveRecord: saveRecord(apiOpts),
|
||||
triggerWorkflow: triggerWorkflow(apiOpts),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
import { trimSlash } from "../common/trimSlash"
|
||||
|
||||
export const listRecords = api => async ({ indexKey, statePath }) => {
|
||||
if (!indexKey) {
|
||||
api.error("Load Record: record key not set")
|
||||
return
|
||||
}
|
||||
|
||||
if (!statePath) {
|
||||
api.error("Load Record: state path not set")
|
||||
return
|
||||
}
|
||||
|
||||
const records = await api.get({
|
||||
url: `/api/listRecords/${trimSlash(indexKey)}`,
|
||||
})
|
||||
|
||||
if (api.isSuccess(records)) api.setState(statePath, records)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import { trimSlash } from "../common/trimSlash"
|
||||
|
||||
export const loadRecord = api => async ({ recordKey, statePath }) => {
|
||||
if (!recordKey) {
|
||||
api.error("Load Record: record key not set")
|
||||
return
|
||||
}
|
||||
|
||||
if (!statePath) {
|
||||
api.error("Load Record: state path not set")
|
||||
return
|
||||
}
|
||||
|
||||
const record = await api.get({
|
||||
url: `/api/record/${trimSlash(recordKey)}`,
|
||||
})
|
||||
|
||||
if (api.isSuccess(record)) api.setState(statePath, record)
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import { trimSlash } from "../common/trimSlash"
|
||||
|
||||
export const saveRecord = api => async ({ statePath }) => {
|
||||
if (!statePath) {
|
||||
api.error("Load Record: state path not set")
|
||||
return
|
||||
}
|
||||
|
||||
const recordtoSave = api.getState(statePath)
|
||||
|
||||
if (!recordtoSave) {
|
||||
api.error(`there is no record in state: ${statePath}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (!recordtoSave.key) {
|
||||
api.error(
|
||||
`item in state does not appear to be a record - it has no key (${statePath})`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const savedRecord = await api.post({
|
||||
url: `/api/record/${trimSlash(recordtoSave.key)}`,
|
||||
body: recordtoSave,
|
||||
})
|
||||
|
||||
if (api.isSuccess(savedRecord)) api.setState(statePath, savedRecord)
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import get from "lodash/fp/get"
|
||||
import mustache from "mustache";
|
||||
|
||||
/**
|
||||
* The workflow orchestrator is a class responsible for executing workflows.
|
||||
|
@ -41,23 +42,28 @@ export const clientStrategy = {
|
|||
for (let arg in args) {
|
||||
const argValue = args[arg]
|
||||
// Means that it's bound to state or workflow context
|
||||
if (argValue.startsWith("$")) {
|
||||
// if value is bound to workflow context.
|
||||
if (argValue.startsWith("$context")) {
|
||||
const path = argValue.replace("$context.", "")
|
||||
// pass in the value from context
|
||||
mappedArgs[arg] = get(path, this.context)
|
||||
}
|
||||
|
||||
// if the value is bound to state
|
||||
if (argValue.startsWith("$state")) {
|
||||
const path = argValue.replace("$state.", "")
|
||||
// pass in the value from state
|
||||
// TODO: not working
|
||||
mappedArgs[arg] = api.getState(path)
|
||||
}
|
||||
}
|
||||
mappedArgs[arg] = mustache.render(argValue, {
|
||||
context: this.context,
|
||||
state: api.getState()
|
||||
});
|
||||
}
|
||||
// if (argValue.startsWith("$")) {
|
||||
// // if value is bound to workflow context.
|
||||
// if (argValue.startsWith("$context")) {
|
||||
// const path = argValue.replace("$context.", "")
|
||||
// // pass in the value from context
|
||||
// mappedArgs[arg] = get(path, this.context)
|
||||
// }
|
||||
|
||||
// // if the value is bound to state
|
||||
// if (argValue.startsWith("$state")) {
|
||||
// const path = argValue.replace("$state.", "")
|
||||
// // pass in the value from state
|
||||
// // TODO: not working
|
||||
// mappedArgs[arg] = api.getState(path)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
console.log(mappedArgs)
|
||||
|
||||
|
|
|
@ -4,13 +4,12 @@ import { createTreeNode } from "./render/prepareRenderComponent"
|
|||
import { screenRouter } from "./render/screenRouter"
|
||||
import { createStateManager } from "./state/stateManager"
|
||||
|
||||
export const createApp = (
|
||||
export const createApp = ({
|
||||
componentLibraries,
|
||||
frontendDefinition,
|
||||
user,
|
||||
uiFunctions,
|
||||
window
|
||||
) => {
|
||||
}) => {
|
||||
let routeTo
|
||||
let currentUrl
|
||||
let screenStateManager
|
||||
|
@ -21,7 +20,6 @@ export const createApp = (
|
|||
store,
|
||||
frontendDefinition,
|
||||
componentLibraries,
|
||||
uiFunctions,
|
||||
onScreenSlotRendered: () => {},
|
||||
routeTo,
|
||||
appRootPath: frontendDefinition.appRootPath,
|
||||
|
@ -53,7 +51,6 @@ export const createApp = (
|
|||
const attachChildrenParams = stateManager => {
|
||||
const getInitialiseParams = treeNode => ({
|
||||
componentLibraries,
|
||||
uiFunctions,
|
||||
treeNode,
|
||||
onScreenSlotRendered,
|
||||
setupState: stateManager.setup,
|
||||
|
@ -68,7 +65,6 @@ export const createApp = (
|
|||
store: writable({ _bbuser: user }),
|
||||
frontendDefinition,
|
||||
componentLibraries,
|
||||
uiFunctions,
|
||||
onScreenSlotRendered,
|
||||
appRootPath: frontendDefinition.appRootPath,
|
||||
// seems weird, but the routeTo variable may not be available at this point
|
||||
|
|
|
@ -10,7 +10,6 @@ export const loadBudibase = async opts => {
|
|||
// const _localStorage = (opts && opts.localStorage) || localStorage
|
||||
|
||||
const frontendDefinition = _window["##BUDIBASE_FRONTEND_DEFINITION##"]
|
||||
const uiFunctions = _window["##BUDIBASE_FRONTEND_FUNCTIONS##"]
|
||||
|
||||
// TODO: update
|
||||
const user = {}
|
||||
|
@ -36,14 +35,12 @@ export const loadBudibase = async opts => {
|
|||
pageStore,
|
||||
routeTo,
|
||||
rootNode,
|
||||
} = createApp(
|
||||
componentLibraryModules,
|
||||
} = createApp({
|
||||
componentLibraries: componentLibraryModules,
|
||||
frontendDefinition,
|
||||
user,
|
||||
uiFunctions || {},
|
||||
_window,
|
||||
rootNode
|
||||
)
|
||||
window
|
||||
})
|
||||
|
||||
const route = _window.location
|
||||
? _window.location.pathname.replace(frontendDefinition.appRootPath, "")
|
||||
|
|
|
@ -5,7 +5,6 @@ import deepEqual from "deep-equal"
|
|||
|
||||
export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
||||
const {
|
||||
uiFunctions,
|
||||
componentLibraries,
|
||||
treeNode,
|
||||
onScreenSlotRendered,
|
||||
|
@ -31,8 +30,6 @@ export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
|||
}
|
||||
}
|
||||
|
||||
// htmlElement.classList.add(`lay-${treeNode.props._id}`)
|
||||
|
||||
const childNodes = []
|
||||
for (let childProps of treeNode.props._children) {
|
||||
const { componentName, libName } = splitName(childProps._component)
|
||||
|
@ -45,7 +42,6 @@ export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
|||
props: childProps,
|
||||
parentNode: treeNode,
|
||||
ComponentConstructor,
|
||||
uiFunctions,
|
||||
htmlElement,
|
||||
anchor,
|
||||
getCurrentState,
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
export const prepareRenderComponent = ({
|
||||
ComponentConstructor,
|
||||
uiFunctions,
|
||||
htmlElement,
|
||||
anchor,
|
||||
props,
|
||||
parentNode,
|
||||
getCurrentState,
|
||||
}) => {
|
||||
const func = props._id ? uiFunctions[props._id] : undefined
|
||||
|
||||
const parentContext = (parentNode && parentNode.context) || {}
|
||||
|
||||
let nodesToRender = []
|
||||
|
@ -42,13 +39,7 @@ export const prepareRenderComponent = ({
|
|||
}
|
||||
}
|
||||
|
||||
if (func) {
|
||||
const state = getCurrentState()
|
||||
const routeParams = state["##routeParams"]
|
||||
func(createNodeAndRender, parentContext, getCurrentState(), routeParams)
|
||||
} else {
|
||||
createNodeAndRender()
|
||||
}
|
||||
createNodeAndRender()
|
||||
|
||||
return nodesToRender
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { getStateOrValue } from "./getState"
|
||||
// import { getStateOrValue } from "./getState"
|
||||
import { setState, setStateFromBinding } from "./setState"
|
||||
import { trimSlash } from "../common/trimSlash"
|
||||
import { isBound } from "./parseBinding"
|
||||
|
@ -10,7 +10,6 @@ export const bbFactory = ({
|
|||
getCurrentState,
|
||||
frontendDefinition,
|
||||
componentLibraries,
|
||||
uiFunctions,
|
||||
onScreenSlotRendered,
|
||||
}) => {
|
||||
const relativeUrl = url => {
|
||||
|
@ -41,17 +40,9 @@ export const bbFactory = ({
|
|||
delete: apiCall("DELETE"),
|
||||
}
|
||||
|
||||
const safeCallEvent = (event, context) => {
|
||||
const isFunction = obj =>
|
||||
!!(obj && obj.constructor && obj.call && obj.apply)
|
||||
|
||||
if (isFunction(event)) event(context)
|
||||
}
|
||||
|
||||
return (treeNode, setupState) => {
|
||||
const attachParams = {
|
||||
componentLibraries,
|
||||
uiFunctions,
|
||||
treeNode,
|
||||
onScreenSlotRendered,
|
||||
setupState,
|
||||
|
@ -62,12 +53,12 @@ export const bbFactory = ({
|
|||
attachChildren: attachChildren(attachParams),
|
||||
context: treeNode.context,
|
||||
props: treeNode.props,
|
||||
call: safeCallEvent,
|
||||
call: (event, context) => event(context),
|
||||
setStateFromBinding: (binding, value) =>
|
||||
setStateFromBinding(store, binding, value),
|
||||
setState: (path, value) => setState(store, path, value),
|
||||
getStateOrValue: (prop, currentContext) =>
|
||||
getStateOrValue(getCurrentState(), prop, currentContext),
|
||||
// getStateOrValue: (prop, currentContext) =>
|
||||
// getStateOrValue(getCurrentState(), prop, currentContext),
|
||||
getContext: getContext(treeNode),
|
||||
setContext: setContext(treeNode),
|
||||
store: store,
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
import { ERROR } from "./standardState"
|
||||
|
||||
export const getNewChildRecordToState = (coreApi, setState) => ({
|
||||
recordKey,
|
||||
collectionName,
|
||||
childRecordType,
|
||||
statePath,
|
||||
}) => {
|
||||
const error = errorHandler(setState)
|
||||
try {
|
||||
if (!recordKey) {
|
||||
error("getNewChild > recordKey not set")
|
||||
return
|
||||
}
|
||||
|
||||
if (!collectionName) {
|
||||
error("getNewChild > collectionName not set")
|
||||
return
|
||||
}
|
||||
|
||||
if (!childRecordType) {
|
||||
error("getNewChild > childRecordType not set")
|
||||
return
|
||||
}
|
||||
|
||||
if (!statePath) {
|
||||
error("getNewChild > statePath not set")
|
||||
return
|
||||
}
|
||||
|
||||
const rec = coreApi.recordApi.getNewChild(
|
||||
recordKey,
|
||||
collectionName,
|
||||
childRecordType
|
||||
)
|
||||
setState(statePath, rec)
|
||||
} catch (e) {
|
||||
error(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
export const getNewRecordToState = (coreApi, setState) => ({
|
||||
collectionKey,
|
||||
childRecordType,
|
||||
statePath,
|
||||
}) => {
|
||||
const error = errorHandler(setState)
|
||||
try {
|
||||
if (!collectionKey) {
|
||||
error("getNewChild > collectionKey not set")
|
||||
return
|
||||
}
|
||||
|
||||
if (!childRecordType) {
|
||||
error("getNewChild > childRecordType not set")
|
||||
return
|
||||
}
|
||||
|
||||
if (!statePath) {
|
||||
error("getNewChild > statePath not set")
|
||||
return
|
||||
}
|
||||
|
||||
const rec = coreApi.recordApi.getNew(collectionKey, childRecordType)
|
||||
setState(statePath, rec)
|
||||
} catch (e) {
|
||||
error(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
const errorHandler = setState => message => setState("##error_message", message)
|
|
@ -22,7 +22,7 @@ export const eventHandlers = (store, rootPath, routeTo) => {
|
|||
const api = createApi({
|
||||
rootPath,
|
||||
setState: setStateWithStore,
|
||||
getState: (path, fallback) => getState(currentState, path, fallback),
|
||||
getState: (path, fallback) => getState(currentState, path, fallback)
|
||||
})
|
||||
|
||||
const setStateHandler = ({ path, value }) => setState(store, path, value)
|
||||
|
|
|
@ -1,46 +1,49 @@
|
|||
import { isUndefined, isObject } from "lodash/fp"
|
||||
import { parseBinding, isStoreBinding } from "./parseBinding"
|
||||
// import { isUndefined, isObject } from "lodash/fp"
|
||||
import getOr from "lodash/fp/getOr";
|
||||
// import { parseBinding, isStoreBinding } from "./parseBinding"
|
||||
|
||||
export const getState = (s, path, fallback) => {
|
||||
if (!s) return fallback
|
||||
export const getState = (state, path, fallback) => {
|
||||
if (!state) return fallback
|
||||
if (!path || path.length === 0) return fallback
|
||||
|
||||
if (path === "$") return s
|
||||
return getOr(fallback, path, state);
|
||||
|
||||
const pathParts = path.split(".")
|
||||
const safeGetPath = (obj, currentPartIndex = 0) => {
|
||||
const currentKey = pathParts[currentPartIndex]
|
||||
// if (path === "$") return state
|
||||
|
||||
if (pathParts.length - 1 == currentPartIndex) {
|
||||
const value = obj[currentKey]
|
||||
if (isUndefined(value)) return fallback
|
||||
else return value
|
||||
}
|
||||
// const pathParts = path.split(".")
|
||||
// const safeGetPath = (obj, currentPartIndex = 0) => {
|
||||
// const currentKey = pathParts[currentPartIndex]
|
||||
|
||||
if (
|
||||
obj[currentKey] === null ||
|
||||
obj[currentKey] === undefined ||
|
||||
!isObject(obj[currentKey])
|
||||
) {
|
||||
return fallback
|
||||
}
|
||||
// if (pathParts.length - 1 == currentPartIndex) {
|
||||
// const value = obj[currentKey]
|
||||
// if (isUndefined(value)) return fallback
|
||||
// else return value
|
||||
// }
|
||||
|
||||
return safeGetPath(obj[currentKey], currentPartIndex + 1)
|
||||
}
|
||||
// if (
|
||||
// obj[currentKey] === null ||
|
||||
// obj[currentKey] === undefined ||
|
||||
// !isObject(obj[currentKey])
|
||||
// ) {
|
||||
// return fallback
|
||||
// }
|
||||
|
||||
return safeGetPath(s)
|
||||
// return safeGetPath(obj[currentKey], currentPartIndex + 1)
|
||||
// }
|
||||
|
||||
// return safeGetPath(state)
|
||||
}
|
||||
|
||||
export const getStateOrValue = (globalState, prop, currentContext) => {
|
||||
if (!prop) return prop
|
||||
// export const getStateOrValue = (globalState, prop, currentContext) => {
|
||||
// if (!prop) return prop
|
||||
|
||||
const binding = parseBinding(prop)
|
||||
// const binding = parseBinding(prop)
|
||||
|
||||
if (binding) {
|
||||
const stateToUse = isStoreBinding(binding) ? globalState : currentContext
|
||||
// if (binding) {
|
||||
// const stateToUse = isStoreBinding(binding) ? globalState : currentContext
|
||||
|
||||
return getState(stateToUse, binding.path, binding.fallback)
|
||||
}
|
||||
// return getState(stateToUse, binding.path, binding.fallback)
|
||||
// }
|
||||
|
||||
return prop
|
||||
}
|
||||
// return prop
|
||||
// }
|
||||
|
|
|
@ -36,7 +36,7 @@ export const parseBinding = prop => {
|
|||
export const isStoreBinding = binding => binding && binding.source === "store"
|
||||
export const isContextBinding = binding =>
|
||||
binding && binding.source === "context"
|
||||
export const isEventBinding = binding => binding && binding.source === "event"
|
||||
// export const isEventBinding = binding => binding && binding.source === "event"
|
||||
|
||||
const hasBindingObject = prop =>
|
||||
typeof prop === "object" && prop[BB_STATE_BINDINGPATH] !== undefined
|
||||
|
|
|
@ -1,32 +1,34 @@
|
|||
import { isObject } from "lodash/fp"
|
||||
// import isObject from "lodash/fp/isObject"
|
||||
import set from "lodash/fp/set";
|
||||
import { parseBinding } from "./parseBinding"
|
||||
|
||||
export const setState = (store, path, value) => {
|
||||
if (!path || path.length === 0) return
|
||||
|
||||
const pathParts = path.split(".")
|
||||
// const pathParts = path.split(".")
|
||||
|
||||
const safeSetPath = (state, currentPartIndex = 0) => {
|
||||
const currentKey = pathParts[currentPartIndex]
|
||||
// const safeSetPath = (state, currentPartIndex = 0) => {
|
||||
// const currentKey = pathParts[currentPartIndex]
|
||||
|
||||
if (pathParts.length - 1 == currentPartIndex) {
|
||||
state[currentKey] = value
|
||||
return
|
||||
}
|
||||
// if (pathParts.length - 1 == currentPartIndex) {
|
||||
// state[currentKey] = value
|
||||
// return
|
||||
// }
|
||||
|
||||
if (
|
||||
state[currentKey] === null ||
|
||||
state[currentKey] === undefined ||
|
||||
!isObject(state[currentKey])
|
||||
) {
|
||||
state[currentKey] = {}
|
||||
}
|
||||
// if (
|
||||
// state[currentKey] === null ||
|
||||
// state[currentKey] === undefined ||
|
||||
// !isObject(state[currentKey])
|
||||
// ) {
|
||||
// state[currentKey] = {}
|
||||
// }
|
||||
|
||||
safeSetPath(state[currentKey], currentPartIndex + 1)
|
||||
}
|
||||
// safeSetPath(state[currentKey], currentPartIndex + 1)
|
||||
// }
|
||||
|
||||
store.update(state => {
|
||||
safeSetPath(state)
|
||||
// safeSetPath(state)
|
||||
state = set(path, value, state);
|
||||
return state
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
import { bbFactory } from "./bbComponentApi"
|
||||
import { getState } from "./getState"
|
||||
import { attachChildren } from "../render/attachChildren"
|
||||
import mustache from "mustache"
|
||||
|
||||
import { parseBinding } from "./parseBinding"
|
||||
|
||||
|
@ -18,14 +19,14 @@ const isMetaProp = propName =>
|
|||
propName === "_id" ||
|
||||
propName === "_style" ||
|
||||
propName === "_code" ||
|
||||
propName === "_codeMeta"
|
||||
propName === "_codeMeta" ||
|
||||
propName === "_styles"
|
||||
|
||||
export const createStateManager = ({
|
||||
store,
|
||||
appRootPath,
|
||||
frontendDefinition,
|
||||
componentLibraries,
|
||||
uiFunctions,
|
||||
onScreenSlotRendered,
|
||||
routeTo,
|
||||
}) => {
|
||||
|
@ -48,7 +49,6 @@ export const createStateManager = ({
|
|||
getCurrentState,
|
||||
frontendDefinition,
|
||||
componentLibraries,
|
||||
uiFunctions,
|
||||
onScreenSlotRendered,
|
||||
})
|
||||
|
||||
|
@ -60,7 +60,6 @@ export const createStateManager = ({
|
|||
getCurrentState,
|
||||
nodesWithCodeBoundChildren,
|
||||
nodesBoundByProps,
|
||||
uiFunctions,
|
||||
componentLibraries,
|
||||
onScreenSlotRendered,
|
||||
setupState: setup,
|
||||
|
@ -79,13 +78,12 @@ const onStoreStateUpdated = ({
|
|||
setCurrentState,
|
||||
getCurrentState,
|
||||
nodesWithCodeBoundChildren,
|
||||
nodesBoundByProps,
|
||||
uiFunctions,
|
||||
// nodesBoundByProps,
|
||||
componentLibraries,
|
||||
onScreenSlotRendered,
|
||||
setupState,
|
||||
}) => s => {
|
||||
setCurrentState(s)
|
||||
}) => state => {
|
||||
setCurrentState(state)
|
||||
|
||||
// the original array gets changed by components' destroy()
|
||||
// so we make a clone and check if they are still in the original
|
||||
|
@ -93,7 +91,6 @@ const onStoreStateUpdated = ({
|
|||
for (let node of nodesWithBoundChildren_clone) {
|
||||
if (!nodesWithCodeBoundChildren.includes(node)) continue
|
||||
attachChildren({
|
||||
uiFunctions,
|
||||
componentLibraries,
|
||||
treeNode: node,
|
||||
onScreenSlotRendered,
|
||||
|
@ -102,9 +99,9 @@ const onStoreStateUpdated = ({
|
|||
})(node.rootElement, { hydrate: true, force: true })
|
||||
}
|
||||
|
||||
for (let node of nodesBoundByProps) {
|
||||
setNodeState(s, node)
|
||||
}
|
||||
// for (let node of nodesBoundByProps) {
|
||||
// setNodeState(state, node)
|
||||
// }
|
||||
}
|
||||
|
||||
const _registerBindings = (nodesBoundByProps, nodesWithCodeBoundChildren) => (
|
||||
|
@ -172,29 +169,36 @@ const _setup = (
|
|||
|
||||
const propValue = props[propName]
|
||||
|
||||
const binding = parseBinding(propValue)
|
||||
const isBound = !!binding
|
||||
// const binding = parseBinding(propValue)
|
||||
// const isBound = !!binding
|
||||
|
||||
if (isBound) binding.propName = propName
|
||||
|
||||
if (isBound && binding.source === "state") {
|
||||
storeBoundProps.push(binding)
|
||||
|
||||
initialProps[propName] = !currentStoreState
|
||||
? binding.fallback
|
||||
: getState(
|
||||
currentStoreState,
|
||||
binding.path,
|
||||
binding.fallback,
|
||||
binding.source
|
||||
)
|
||||
if (typeof propValue === "string") {
|
||||
initialProps[propName] = mustache.render(propValue, {
|
||||
state: currentStoreState,
|
||||
context
|
||||
})
|
||||
}
|
||||
|
||||
if (isBound && binding.source === "context") {
|
||||
initialProps[propName] = !context
|
||||
? propValue
|
||||
: getState(context, binding.path, binding.fallback, binding.source)
|
||||
}
|
||||
// if (isBound) binding.propName = propName
|
||||
|
||||
// if (isBound && binding.source === "state") {
|
||||
// storeBoundProps.push(binding)
|
||||
|
||||
// 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)
|
||||
// }
|
||||
|
||||
if (isEventType(propValue)) {
|
||||
const handlersInfos = []
|
||||
|
@ -203,33 +207,38 @@ const _setup = (
|
|||
handlerType: event[EVENT_TYPE_MEMBER_NAME],
|
||||
parameters: event.parameters,
|
||||
}
|
||||
|
||||
const resolvedParams = {}
|
||||
for (let paramName in handlerInfo.parameters) {
|
||||
const paramValue = handlerInfo.parameters[paramName]
|
||||
const paramBinding = parseBinding(paramValue)
|
||||
if (!paramBinding) {
|
||||
resolvedParams[paramName] = () => paramValue
|
||||
continue
|
||||
}
|
||||
resolvedParams[paramName] = () => mustache.render(paramValue, {
|
||||
state: getCurrentState(),
|
||||
context,
|
||||
})
|
||||
// const paramBinding = parseBinding(paramValue)
|
||||
// if (!paramBinding) {
|
||||
// resolvedParams[paramName] = () => paramValue
|
||||
// continue
|
||||
// }
|
||||
|
||||
let paramValueSource
|
||||
// let paramValueSource
|
||||
|
||||
if (paramBinding.source === "context") paramValueSource = context
|
||||
if (paramBinding.source === "state")
|
||||
paramValueSource = getCurrentState()
|
||||
if (paramBinding.source === "context") paramValueSource = context
|
||||
// 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)
|
||||
// // The new dynamic event parameter bound to the relevant source
|
||||
// resolvedParams[paramName] = () =>
|
||||
// getState(paramValueSource, paramBinding.path, paramBinding.fallback)
|
||||
}
|
||||
|
||||
handlerInfo.parameters = resolvedParams
|
||||
handlersInfos.push(handlerInfo)
|
||||
}
|
||||
|
||||
if (handlersInfos.length === 0) initialProps[propName] = doNothing
|
||||
else {
|
||||
if (handlersInfos.length === 0) {
|
||||
initialProps[propName] = doNothing
|
||||
} else {
|
||||
initialProps[propName] = async context => {
|
||||
for (let handlerInfo of handlersInfos) {
|
||||
const handler = makeHandler(handlerTypes, handlerInfo)
|
||||
|
|
|
@ -1,283 +0,0 @@
|
|||
import {
|
||||
isEventType,
|
||||
eventHandlers,
|
||||
EVENT_TYPE_MEMBER_NAME,
|
||||
} from "./eventHandlers"
|
||||
import { bbFactory } from "./bbComponentApi"
|
||||
import { getState } from "./getState"
|
||||
import { attachChildren } from "../render/attachChildren"
|
||||
|
||||
import { parseBinding } from "./parseBinding"
|
||||
|
||||
const doNothing = () => {}
|
||||
doNothing.isPlaceholder = true
|
||||
|
||||
const isMetaProp = propName =>
|
||||
propName === "_component" ||
|
||||
propName === "_children" ||
|
||||
propName === "_id" ||
|
||||
propName === "_style" ||
|
||||
propName === "_code" ||
|
||||
propName === "_codeMeta"
|
||||
|
||||
export const createStateManager = ({
|
||||
store,
|
||||
coreApi,
|
||||
rootPath,
|
||||
frontendDefinition,
|
||||
componentLibraries,
|
||||
uiFunctions,
|
||||
onScreenSlotRendered,
|
||||
routeTo,
|
||||
}) => {
|
||||
let handlerTypes = eventHandlers(store, coreApi, rootPath, 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
|
||||
)
|
||||
const bb = bbFactory({
|
||||
store,
|
||||
getCurrentState,
|
||||
frontendDefinition,
|
||||
componentLibraries,
|
||||
uiFunctions,
|
||||
onScreenSlotRendered,
|
||||
})
|
||||
|
||||
const setup = _setup(handlerTypes, getCurrentState, registerBindings, bb)
|
||||
|
||||
const unsubscribe = store.subscribe(
|
||||
onStoreStateUpdated({
|
||||
setCurrentState: s => (currentState = s),
|
||||
getCurrentState,
|
||||
nodesWithCodeBoundChildren,
|
||||
nodesBoundByProps,
|
||||
uiFunctions,
|
||||
componentLibraries,
|
||||
onScreenSlotRendered,
|
||||
setupState: setup,
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
setup,
|
||||
destroy: () => unsubscribe(),
|
||||
getCurrentState,
|
||||
store,
|
||||
}
|
||||
}
|
||||
|
||||
const onStoreStateUpdated = ({
|
||||
setCurrentState,
|
||||
getCurrentState,
|
||||
nodesWithCodeBoundChildren,
|
||||
nodesBoundByProps,
|
||||
uiFunctions,
|
||||
componentLibraries,
|
||||
onScreenSlotRendered,
|
||||
setupState,
|
||||
}) => s => {
|
||||
setCurrentState(s)
|
||||
|
||||
// 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({
|
||||
uiFunctions,
|
||||
componentLibraries,
|
||||
treeNode: node,
|
||||
onScreenSlotRendered,
|
||||
setupState,
|
||||
getCurrentState,
|
||||
})(node.rootElement, { hydrate: true, force: true })
|
||||
}
|
||||
|
||||
for (let node of nodesBoundByProps) {
|
||||
setNodeState(s, node)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a components event handler parameters to state, context or the event itself.
|
||||
* @param {Array} eventHandlerProp - event handler array from component definition
|
||||
*/
|
||||
function bindComponentEventHandlers(
|
||||
eventHandlerProp,
|
||||
context,
|
||||
getCurrentState
|
||||
) {
|
||||
const boundEventHandlers = []
|
||||
for (let event of eventHandlerProp) {
|
||||
const boundEventHandler = {
|
||||
handlerType: event[EVENT_TYPE_MEMBER_NAME],
|
||||
parameters: event.parameters,
|
||||
}
|
||||
|
||||
const boundParameters = {}
|
||||
for (let paramName in boundEventHandler.parameters) {
|
||||
const paramValue = boundEventHandler.parameters[paramName]
|
||||
const paramBinding = parseBinding(paramValue)
|
||||
if (!paramBinding) {
|
||||
boundParameters[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
|
||||
boundParameters[paramName] = eventContext =>
|
||||
getState(
|
||||
paramBinding.source === "event" ? eventContext : paramValueSource,
|
||||
paramBinding.path,
|
||||
paramBinding.fallback
|
||||
)
|
||||
}
|
||||
|
||||
boundEventHandler.parameters = boundParameters
|
||||
boundEventHandlers.push(boundEventHandlers)
|
||||
|
||||
return boundEventHandlers
|
||||
}
|
||||
}
|
||||
|
||||
const _setup = (
|
||||
handlerTypes,
|
||||
getCurrentState,
|
||||
registerBindings,
|
||||
bb
|
||||
) => node => {
|
||||
const props = node.props
|
||||
const context = node.context || {}
|
||||
const initialProps = { ...props }
|
||||
const storeBoundProps = []
|
||||
const currentStoreState = getCurrentState()
|
||||
|
||||
for (let propName in props) {
|
||||
if (isMetaProp(propName)) continue
|
||||
|
||||
const propValue = props[propName]
|
||||
|
||||
const binding = parseBinding(propValue)
|
||||
const isBound = !!binding
|
||||
|
||||
if (isBound) binding.propName = propName
|
||||
|
||||
if (isBound && binding.source === "state") {
|
||||
storeBoundProps.push(binding)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if (isEventType(propValue)) {
|
||||
const boundEventHandlers = bindComponentEventHandlers(
|
||||
propValue,
|
||||
context,
|
||||
getCurrentState
|
||||
)
|
||||
|
||||
if (boundEventHandlers.length === 0) {
|
||||
initialProps[propName] = doNothing
|
||||
} else {
|
||||
initialProps[propName] = async context => {
|
||||
for (let handlerInfo of boundEventHandlers) {
|
||||
const handler = makeHandler(handlerTypes, handlerInfo)
|
||||
await handler(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerBindings(node, storeBoundProps)
|
||||
|
||||
const setup = _setup(handlerTypes, getCurrentState, registerBindings, bb)
|
||||
initialProps._bb = bb(node, setup)
|
||||
|
||||
return initialProps
|
||||
}
|
||||
|
||||
const makeHandler = (handlerTypes, handlerInfo) => {
|
||||
const handlerType = handlerTypes[handlerInfo.handlerType]
|
||||
return context => {
|
||||
const parameters = {}
|
||||
for (let paramName in handlerInfo.parameters) {
|
||||
parameters[paramName] = handlerInfo.parameters[paramName](context)
|
||||
}
|
||||
handlerType.execute(parameters)
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ export const load = async (page, screens, url, appRootPath) => {
|
|||
autoAssignIds(s.props)
|
||||
}
|
||||
setAppDef(dom.window, page, screens)
|
||||
addWindowGlobals(dom.window, page, screens, appRootPath, uiFunctions, {
|
||||
addWindowGlobals(dom.window, page, screens, appRootPath, {
|
||||
hierarchy: {},
|
||||
actions: [],
|
||||
triggers: [],
|
||||
|
@ -27,13 +27,12 @@ export const load = async (page, screens, url, appRootPath) => {
|
|||
return { dom, app }
|
||||
}
|
||||
|
||||
const addWindowGlobals = (window, page, screens, appRootPath, uiFunctions) => {
|
||||
const addWindowGlobals = (window, page, screens, appRootPath) => {
|
||||
window["##BUDIBASE_FRONTEND_DEFINITION##"] = {
|
||||
page,
|
||||
screens,
|
||||
appRootPath,
|
||||
}
|
||||
window["##BUDIBASE_FRONTEND_FUNCTIONS##"] = uiFunctions
|
||||
}
|
||||
|
||||
export const makePage = props => ({ props })
|
||||
|
@ -183,29 +182,4 @@ const maketestlib = window => ({
|
|||
set(opts.props)
|
||||
opts.target.appendChild(node)
|
||||
},
|
||||
})
|
||||
|
||||
const uiFunctions = {
|
||||
never_render: () => {},
|
||||
|
||||
always_render: render => {
|
||||
render()
|
||||
},
|
||||
|
||||
three_clones: render => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
render()
|
||||
}
|
||||
},
|
||||
|
||||
with_context: render => {
|
||||
render({ testKey: "test value" })
|
||||
},
|
||||
|
||||
n_clones_based_on_store: (render, _, state) => {
|
||||
const n = state.componentCount || 0
|
||||
for (let i = 0; i < n; i++) {
|
||||
render({ index: `index_${i}` })
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
|
@ -103,7 +103,6 @@ const buildFrontendAppDefinition = async (config, appId, pageName, pkg) => {
|
|||
filename,
|
||||
`
|
||||
window['##BUDIBASE_FRONTEND_DEFINITION##'] = ${clientUiDefinition};
|
||||
window['##BUDIBASE_FRONTEND_FUNCTIONS##'] = ${pkg.uiFunctions};
|
||||
`
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue