Builder saves backend and front end seperately (#88)
* refactoring server for screens & page layout restructure * Disable API calls, UI placeholders. * buildPropsHierarchy is gone & screen has url * Recent changes. * router * router * updated git-ignore to reinclude server/utilities/builder * modified cli - budi new create new file structure * Fix uuid import. * prettier fixes * prettier fixes * prettier fixes * page/screen restructure.. broken tests * all tests passing at last * screen routing tests * Working screen editor and preview. * Render page previews to the screen. * Key input lists to ensure new array references when updating styles. * Ensure the iframe html and body fills the container. * Save screens via the API. * Get all save APIs almost working. * Write pages.json to disk. * Use correct API endpoint for saving styles. * Differentiate between saving properties of screens and pages. * Add required fields to default pages layouts. * Add _css default property to newly created screens. * Add default code property. * page layout / screens - app output * backend and fronend save seperately Co-authored-by: pngwn <pnda007@gmail.com>
This commit is contained in:
parent
8a80d8801a
commit
34b957f331
|
@ -295,7 +295,7 @@ const saveCurrentNode = store => () => {
|
||||||
|
|
||||||
s.currentNodeIsNew = false
|
s.currentNodeIsNew = false
|
||||||
|
|
||||||
savePackage(store, s)
|
saveBackend(s)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
|
@ -331,7 +331,7 @@ const deleteCurrentNode = store => () => {
|
||||||
)(nodeToDelete.parent().indexes)
|
)(nodeToDelete.parent().indexes)
|
||||||
}
|
}
|
||||||
s.errors = []
|
s.errors = []
|
||||||
savePackage(store, s)
|
saveBackend(s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -370,7 +370,7 @@ const saveAction = store => (newAction, isNew, oldAction = null) => {
|
||||||
} else {
|
} else {
|
||||||
s.actions.push(newAction)
|
s.actions.push(newAction)
|
||||||
}
|
}
|
||||||
savePackage(store, s)
|
saveBackend(s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -378,7 +378,7 @@ const saveAction = store => (newAction, isNew, oldAction = null) => {
|
||||||
const deleteAction = store => action => {
|
const deleteAction = store => action => {
|
||||||
store.update(s => {
|
store.update(s => {
|
||||||
s.actions = filter(a => a.name !== action.name)(s.actions)
|
s.actions = filter(a => a.name !== action.name)(s.actions)
|
||||||
savePackage(store, s)
|
saveBackend(s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -396,7 +396,7 @@ const saveTrigger = store => (newTrigger, isNew, oldTrigger = null) => {
|
||||||
} else {
|
} else {
|
||||||
s.triggers.push(newTrigger)
|
s.triggers.push(newTrigger)
|
||||||
}
|
}
|
||||||
savePackage(store, s)
|
saveBackend(s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -429,7 +429,7 @@ const saveLevel = store => (newLevel, isNew, oldLevel = null) => {
|
||||||
|
|
||||||
incrementAccessLevelsVersion(s)
|
incrementAccessLevelsVersion(s)
|
||||||
|
|
||||||
savePackage(store, s)
|
saveBackend(s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -440,7 +440,7 @@ const deleteLevel = store => level => {
|
||||||
s.accessLevels.levels
|
s.accessLevels.levels
|
||||||
)
|
)
|
||||||
incrementAccessLevelsVersion(s)
|
incrementAccessLevelsVersion(s)
|
||||||
savePackage(store, s)
|
saveBackend(s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -477,18 +477,18 @@ const _saveScreen = (store, s, screen) => {
|
||||||
`/_builder/api/${s.appname}/pages/${s.currentPageName}/screen`,
|
`/_builder/api/${s.appname}/pages/${s.currentPageName}/screen`,
|
||||||
screen
|
screen
|
||||||
)
|
)
|
||||||
.then(() => savePackage(store, s))
|
.then(() => _savePage(s))
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
const _save = (appname, screen, store, s) =>
|
const _saveScreenApi = (screen, s) =>
|
||||||
api
|
api
|
||||||
.post(
|
.post(
|
||||||
`/_builder/api/${s.appname}/pages/${s.currentPageName}/screen`,
|
`/_builder/api/${s.appname}/pages/${s.currentPageName}/screen`,
|
||||||
screen
|
screen
|
||||||
)
|
)
|
||||||
.then(() => savePackage(store, s))
|
.then(() => _savePage(s))
|
||||||
|
|
||||||
const createScreen = store => (screenName, route, layoutComponentName) => {
|
const createScreen = store => (screenName, route, layoutComponentName) => {
|
||||||
store.update(s => {
|
store.update(s => {
|
||||||
|
@ -530,7 +530,7 @@ const createGeneratedComponents = store => components => {
|
||||||
await api.post(`/_builder/api/${s.appname}/screen`, c)
|
await api.post(`/_builder/api/${s.appname}/screen`, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
await savePackage(store, s)
|
await _savePage(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
doCreate()
|
doCreate()
|
||||||
|
@ -591,7 +591,7 @@ const renameScreen = store => (oldname, newname) => {
|
||||||
})
|
})
|
||||||
.then(() => saveAllChanged())
|
.then(() => saveAllChanged())
|
||||||
.then(() => {
|
.then(() => {
|
||||||
savePackage(store, s)
|
_savePage(s)
|
||||||
})
|
})
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
@ -605,7 +605,7 @@ const savePage = store => async page => {
|
||||||
}
|
}
|
||||||
|
|
||||||
s.pages[s.currentPageName] = page
|
s.pages[s.currentPageName] = page
|
||||||
savePackage(store, s)
|
_savePage(s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -634,7 +634,7 @@ const addComponentLibrary = store => async lib => {
|
||||||
])
|
])
|
||||||
|
|
||||||
s.pages.componentLibraries.push(lib)
|
s.pages.componentLibraries.push(lib)
|
||||||
savePackage(store, s)
|
_savePage(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
@ -646,7 +646,7 @@ const removeComponentLibrary = store => lib => {
|
||||||
s.pages.componentLibraries = filter(l => l !== lib)(
|
s.pages.componentLibraries = filter(l => l !== lib)(
|
||||||
s.pages.componentLibraries
|
s.pages.componentLibraries
|
||||||
)
|
)
|
||||||
savePackage(store, s)
|
_savePage(s)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
|
@ -655,7 +655,7 @@ const removeComponentLibrary = store => lib => {
|
||||||
const addStylesheet = store => stylesheet => {
|
const addStylesheet = store => stylesheet => {
|
||||||
store.update(s => {
|
store.update(s => {
|
||||||
s.pages.stylesheets.push(stylesheet)
|
s.pages.stylesheets.push(stylesheet)
|
||||||
savePackage(store, s)
|
_savePage(s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -663,14 +663,14 @@ const addStylesheet = store => stylesheet => {
|
||||||
const removeStylesheet = store => stylesheet => {
|
const removeStylesheet = store => stylesheet => {
|
||||||
store.update(s => {
|
store.update(s => {
|
||||||
s.pages.stylesheets = filter(s => s !== stylesheet)(s.pages.stylesheets)
|
s.pages.stylesheets = filter(s => s !== stylesheet)(s.pages.stylesheets)
|
||||||
savePackage(store, s)
|
_savePage(s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const refreshComponents = store => async () => {
|
const refreshComponents = store => async () => {
|
||||||
const componentsAndGenerators = await api
|
const componentsAndGenerators = await api
|
||||||
.get(`/_builder/api/${db.appname}/components`)
|
.get(`/_builder/api/${appname}/components`)
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
|
|
||||||
const components = pipe(componentsAndGenerators.components, [
|
const components = pipe(componentsAndGenerators.components, [
|
||||||
|
@ -688,20 +688,24 @@ const refreshComponents = store => async () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const savePackage = async (store, s) => {
|
const _savePage = async s => {
|
||||||
const page = s.pages[s.currentPageName]
|
const page = s.pages[s.currentPageName]
|
||||||
|
|
||||||
await api.post(`/_builder/api/${appname}/pages/${s.currentPageName}`, {
|
await api.post(`/_builder/api/${appname}/pages/${s.currentPageName}`, {
|
||||||
|
page: { componentLibraries: s.pages.componentLibraries, ...page },
|
||||||
|
uiFunctions: "{'1234':() => 'test return'}",
|
||||||
|
screens: page.screens,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveBackend = async s => {
|
||||||
|
await api.post(`/_builder/api/${appname}/backend`, {
|
||||||
appDefinition: {
|
appDefinition: {
|
||||||
hierarchy: s.hierarchy,
|
hierarchy: s.hierarchy,
|
||||||
actions: s.actions,
|
actions: s.actions,
|
||||||
triggers: s.triggers,
|
triggers: s.triggers,
|
||||||
},
|
},
|
||||||
accessLevels: s.accessLevels,
|
accessLevels: s.accessLevels,
|
||||||
page: { componentLibraries: s.pages.componentLibraries, ...page },
|
|
||||||
uiFunctions: "{'1234':() => 'test return'}",
|
|
||||||
props: page.props,
|
|
||||||
screens: page.screens,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,7 +735,7 @@ const addChildComponent = store => componentName => {
|
||||||
newComponent.props
|
newComponent.props
|
||||||
)
|
)
|
||||||
|
|
||||||
savePackage(store, s)
|
_savePage(s)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
|
@ -750,7 +754,7 @@ const setComponentProp = store => (name, value) => {
|
||||||
s.currentComponentInfo[name] = value
|
s.currentComponentInfo[name] = value
|
||||||
|
|
||||||
s.currentFrontEndType === "page"
|
s.currentFrontEndType === "page"
|
||||||
? savePackage(store, s, s.currentPreviewItem)
|
? _savePage(s, s.currentPreviewItem)
|
||||||
: _saveScreen(store, s, s.currentPreviewItem)
|
: _saveScreen(store, s, s.currentPreviewItem)
|
||||||
|
|
||||||
s.currentComponentInfo = current_component
|
s.currentComponentInfo = current_component
|
||||||
|
@ -770,8 +774,8 @@ const setComponentStyle = store => (type, name, value) => {
|
||||||
|
|
||||||
// save without messing with the store
|
// save without messing with the store
|
||||||
s.currentFrontEndType === "page"
|
s.currentFrontEndType === "page"
|
||||||
? savePackage(store, s, s.currentPreviewItem)
|
? _savePage(s)
|
||||||
: _save(s.appname, s.currentPreviewItem, store, s)
|
: _saveScreenApi(s.currentPreviewItem, s)
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -782,7 +786,7 @@ const setComponentCode = store => code => {
|
||||||
|
|
||||||
setCurrentScreenFunctions(s)
|
setCurrentScreenFunctions(s)
|
||||||
// save without messing with the store
|
// save without messing with the store
|
||||||
_save(s.appname, s.currentPreviewItem, store, s)
|
_saveScreenApi(s.currentPreviewItem, s)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
|
|
|
@ -29,14 +29,17 @@
|
||||||
[map(s => `<link rel="stylesheet" href="${s}"/>`), join("\n")]
|
[map(s => `<link rel="stylesheet" href="${s}"/>`), join("\n")]
|
||||||
)
|
)
|
||||||
|
|
||||||
$: appDefinition = {
|
$: frontendDefinition = {
|
||||||
componentLibraries: $store.loadLibraryUrls(),
|
componentLibraries: $store.loadLibraryUrls(),
|
||||||
props:
|
page: $store.currentPreviewItem,
|
||||||
$store.currentPreviewItem &&
|
screens: [],
|
||||||
transform_component($store.currentPreviewItem, true),
|
|
||||||
hierarchy: $store.hierarchy,
|
|
||||||
appRootPath: "",
|
appRootPath: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: backendDefinition = {
|
||||||
|
hierarchy: $store.hierarchy,
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="component-container">
|
<div class="component-container">
|
||||||
|
@ -55,8 +58,9 @@
|
||||||
}
|
}
|
||||||
<\/style>
|
<\/style>
|
||||||
<\script>
|
<\script>
|
||||||
window["##BUDIBASE_APPDEFINITION##"] = ${JSON.stringify(appDefinition)};
|
window["##BUDIBASE_FRONTEND_DEFINITION##"] = ${JSON.stringify(frontendDefinition)};
|
||||||
window["##BUDIBASE_UIFUNCTIONS"] = ${$store.currentScreenFunctions};
|
window["##BUDIBASE_BACKEND_DEFINITION##"] = ${JSON.stringify(backendDefinition)};
|
||||||
|
window["##BUDIBASE_FRONTEND_FUNCTIONS##"] = ${$store.currentScreenFunctions};
|
||||||
|
|
||||||
import('/_builder/budibase-client.esm.mjs')
|
import('/_builder/budibase-client.esm.mjs')
|
||||||
.then(module => {
|
.then(module => {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
export const createCoreApp = (appDefinition, user) => {
|
export const createCoreApp = (backendDefinition, user) => {
|
||||||
const app = {
|
const app = {
|
||||||
datastore: null,
|
datastore: null,
|
||||||
crypto: null,
|
crypto: null,
|
||||||
publish: () => {},
|
publish: () => {},
|
||||||
hierarchy: appDefinition.hierarchy,
|
hierarchy: backendDefinition.hierarchy,
|
||||||
actions: appDefinition.actions,
|
actions: backendDefinition.actions,
|
||||||
user,
|
user,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { createCoreApp } from "./createCoreApp"
|
||||||
import { getNew, getNewChild } from "../../../core/src/recordApi/getNew"
|
import { getNew, getNewChild } from "../../../core/src/recordApi/getNew"
|
||||||
import { constructHierarchy } from "../../../core/src/templateApi/createNodes"
|
import { constructHierarchy } from "../../../core/src/templateApi/createNodes"
|
||||||
|
|
||||||
export const createCoreApi = (appDefinition, user) => {
|
export const createCoreApi = (backendDefinition, user) => {
|
||||||
const app = createCoreApp(appDefinition, user)
|
const app = createCoreApp(backendDefinition, user)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recordApi: {
|
recordApi: {
|
||||||
|
|
|
@ -11,22 +11,23 @@ import { screenRouter } from "./render/screenRouter"
|
||||||
export const createApp = (
|
export const createApp = (
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
screens
|
screens
|
||||||
) => {
|
) => {
|
||||||
const coreApi = createCoreApi(appDefinition, user)
|
const coreApi = createCoreApi(backendDefinition, user)
|
||||||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
||||||
appDefinition.hierarchy
|
backendDefinition.hierarchy
|
||||||
)
|
)
|
||||||
const pageStore = writable({
|
const pageStore = writable({
|
||||||
_bbuser: user,
|
_bbuser: user,
|
||||||
})
|
})
|
||||||
|
|
||||||
const relativeUrl = url =>
|
const relativeUrl = url =>
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
? appDefinition.appRootPath + "/" + trimSlash(url)
|
? frontendDefinition.appRootPath + "/" + trimSlash(url)
|
||||||
: url
|
: url
|
||||||
|
|
||||||
const apiCall = method => (url, body) =>
|
const apiCall = method => (url, body) =>
|
||||||
|
@ -89,7 +90,7 @@ export const createApp = (
|
||||||
store,
|
store,
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
hydrate,
|
hydrate,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
treeNode,
|
treeNode,
|
||||||
|
|
|
@ -10,8 +10,9 @@ export const loadBudibase = async ({
|
||||||
localStorage,
|
localStorage,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
}) => {
|
}) => {
|
||||||
const appDefinition = window["##BUDIBASE_APPDEFINITION##"]
|
const backendDefinition = window["##BUDIBASE_BACKEND_DEFINITION##"]
|
||||||
const uiFunctionsFromWindow = window["##BUDIBASE_UIFUNCTIONS##"]
|
const frontendDefinition = window["##BUDIBASE_FRONTEND_DEFINITION##"]
|
||||||
|
const uiFunctionsFromWindow = window["##BUDIBASE_FRONTEND_FUNCTIONS##"]
|
||||||
uiFunctions = uiFunctionsFromWindow || uiFunctions
|
uiFunctions = uiFunctionsFromWindow || uiFunctions
|
||||||
|
|
||||||
const userFromStorage = localStorage.getItem("budibase:user")
|
const userFromStorage = localStorage.getItem("budibase:user")
|
||||||
|
@ -26,16 +27,16 @@ export const loadBudibase = async ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootPath =
|
const rootPath =
|
||||||
appDefinition.appRootPath === ""
|
frontendDefinition.appRootPath === ""
|
||||||
? ""
|
? ""
|
||||||
: "/" + trimSlash(appDefinition.appRootPath)
|
: "/" + trimSlash(frontendDefinition.appRootPath)
|
||||||
|
|
||||||
if (!componentLibraries) {
|
if (!componentLibraries) {
|
||||||
|
|
||||||
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib)
|
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib)
|
||||||
componentLibraries = {}
|
componentLibraries = {}
|
||||||
|
|
||||||
for (let lib of appDefinition.componentLibraries) {
|
for (let lib of frontendDefinition.componentLibraries) {
|
||||||
componentLibraries[lib.libName] = await import(
|
componentLibraries[lib.libName] = await import(
|
||||||
componentLibraryUrl(lib.importPath)
|
componentLibraryUrl(lib.importPath)
|
||||||
)
|
)
|
||||||
|
@ -45,17 +46,18 @@ export const loadBudibase = async ({
|
||||||
componentLibraries[builtinLibName] = builtins(window)
|
componentLibraries[builtinLibName] = builtins(window)
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
page = appDefinition.page
|
page = frontendDefinition.page
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!screens) {
|
if (!screens) {
|
||||||
screens = appDefinition.screens
|
screens = frontendDefinition.screens
|
||||||
}
|
}
|
||||||
|
|
||||||
const { initialisePage, screenStore, pageStore, routeTo, rootNode } = createApp(
|
const { initialisePage, screenStore, pageStore, routeTo, rootNode } = createApp(
|
||||||
window.document,
|
window.document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions || {},
|
uiFunctions || {},
|
||||||
screens
|
screens
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const initialiseChildren = initialiseOpts => (
|
||||||
store,
|
store,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
treeNode,
|
treeNode,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
hydrate,
|
hydrate,
|
||||||
onScreenSlotRendered,
|
onScreenSlotRendered,
|
||||||
} = initialiseOpts
|
} = initialiseOpts
|
||||||
|
@ -43,7 +43,7 @@ export const initialiseChildren = initialiseOpts => (
|
||||||
store,
|
store,
|
||||||
childProps,
|
childProps,
|
||||||
coreApi,
|
coreApi,
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
)
|
)
|
||||||
|
|
||||||
const componentConstructor = componentLibraries[libName][componentName]
|
const componentConstructor = componentLibraries[libName][componentName]
|
||||||
|
|
|
@ -51,13 +51,16 @@ const autoAssignIds = (props, count = 0) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const setAppDef = (window, page, screens) => {
|
const setAppDef = (window, page, screens) => {
|
||||||
window["##BUDIBASE_APPDEFINITION##"] = {
|
window["##BUDIBASE_FRONTEND_DEFINITION##"] = {
|
||||||
componentLibraries: [],
|
componentLibraries: [],
|
||||||
page,
|
page,
|
||||||
screens,
|
screens,
|
||||||
hierarchy: {},
|
|
||||||
appRootPath: "",
|
appRootPath: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window["##BUDIBASE_BACKEND_DEFINITION##"] = {
|
||||||
|
hierarchy: {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const allLibs = window => ({
|
const allLibs = window => ({
|
||||||
|
|
|
@ -2,9 +2,61 @@ var app = (function (exports) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function noop() { }
|
function noop() { }
|
||||||
|
function run(fn) {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
function run_all(fns) {
|
||||||
|
fns.forEach(run);
|
||||||
|
}
|
||||||
function safe_not_equal(a, b) {
|
function safe_not_equal(a, b) {
|
||||||
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
||||||
}
|
}
|
||||||
|
function destroy_component(component, detaching) {
|
||||||
|
if (component.$$.fragment) {
|
||||||
|
run_all(component.$$.on_destroy);
|
||||||
|
component.$$.fragment.d(detaching);
|
||||||
|
// TODO null out other refs, including component.$$ (but need to
|
||||||
|
// preserve final state?)
|
||||||
|
component.$$.on_destroy = component.$$.fragment = null;
|
||||||
|
component.$$.ctx = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let SvelteElement;
|
||||||
|
if (typeof HTMLElement !== 'undefined') {
|
||||||
|
SvelteElement = class extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
for (const key in this.$$.slotted) {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
this.appendChild(this.$$.slotted[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributeChangedCallback(attr, _oldValue, newValue) {
|
||||||
|
this[attr] = newValue;
|
||||||
|
}
|
||||||
|
$destroy() {
|
||||||
|
destroy_component(this, 1);
|
||||||
|
this.$destroy = noop;
|
||||||
|
}
|
||||||
|
$on(type, callback) {
|
||||||
|
// TODO should this delegate to addEventListener?
|
||||||
|
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
|
||||||
|
callbacks.push(callback);
|
||||||
|
return () => {
|
||||||
|
const index = callbacks.indexOf(callback);
|
||||||
|
if (index !== -1)
|
||||||
|
callbacks.splice(index, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$set() {
|
||||||
|
// overridden by instance, if it has props
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const subscriber_queue = [];
|
const subscriber_queue = [];
|
||||||
/**
|
/**
|
||||||
|
@ -58,13 +110,13 @@ var app = (function (exports) {
|
||||||
return { set, update, subscribe };
|
return { set, update, subscribe };
|
||||||
}
|
}
|
||||||
|
|
||||||
const createCoreApp = (appDefinition, user) => {
|
const createCoreApp = (backendDefinition, user) => {
|
||||||
const app = {
|
const app = {
|
||||||
datastore: null,
|
datastore: null,
|
||||||
crypto: null,
|
crypto: null,
|
||||||
publish: () => {},
|
publish: () => {},
|
||||||
hierarchy: appDefinition.hierarchy,
|
hierarchy: backendDefinition.hierarchy,
|
||||||
actions: appDefinition.actions,
|
actions: backendDefinition.actions,
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1349,17 +1401,48 @@ var app = (function (exports) {
|
||||||
|
|
||||||
var randomByteBrowser = randomByte;
|
var randomByteBrowser = randomByte;
|
||||||
|
|
||||||
var format_browser = function (random, alphabet, size) {
|
/**
|
||||||
|
* Secure random string generator with custom alphabet.
|
||||||
|
*
|
||||||
|
* Alphabet must contain 256 symbols or less. Otherwise, the generator
|
||||||
|
* will not be secure.
|
||||||
|
*
|
||||||
|
* @param {generator} random The random bytes generator.
|
||||||
|
* @param {string} alphabet Symbols to be used in new random string.
|
||||||
|
* @param {size} size The number of symbols in new random string.
|
||||||
|
*
|
||||||
|
* @return {string} Random string.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const format = require('nanoid/format')
|
||||||
|
*
|
||||||
|
* function random (size) {
|
||||||
|
* const result = []
|
||||||
|
* for (let i = 0; i < size; i++) {
|
||||||
|
* result.push(randomByte())
|
||||||
|
* }
|
||||||
|
* return result
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* format(random, "abcdef", 5) //=> "fbaef"
|
||||||
|
*
|
||||||
|
* @name format
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
var format = function (random, alphabet, size) {
|
||||||
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
||||||
var step = -~(1.6 * mask * size / alphabet.length);
|
var step = Math.ceil(1.6 * mask * size / alphabet.length);
|
||||||
var id = '';
|
size = +size;
|
||||||
|
|
||||||
|
var id = '';
|
||||||
while (true) {
|
while (true) {
|
||||||
var i = step;
|
var bytes = random(step);
|
||||||
var bytes = random(i);
|
for (var i = 0; i < step; i++) {
|
||||||
while (i--) {
|
var byte = bytes[i] & mask;
|
||||||
id += alphabet[bytes[i] & mask] || '';
|
if (alphabet[byte]) {
|
||||||
if (id.length === +size) return id
|
id += alphabet[byte];
|
||||||
|
if (id.length === size) return id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1371,7 +1454,7 @@ var app = (function (exports) {
|
||||||
var str = '';
|
var str = '';
|
||||||
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
str = str + format_browser(randomByteBrowser, alphabet_1.get(), 1);
|
str = str + format(randomByteBrowser, alphabet_1.get(), 1);
|
||||||
done = number < (Math.pow(16, loopCounter + 1 ) );
|
done = number < (Math.pow(16, loopCounter + 1 ) );
|
||||||
loopCounter++;
|
loopCounter++;
|
||||||
}
|
}
|
||||||
|
@ -21567,8 +21650,8 @@ var app = (function (exports) {
|
||||||
return node
|
return node
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCoreApi = (appDefinition, user) => {
|
const createCoreApi = (backendDefinition, user) => {
|
||||||
const app = createCoreApp(appDefinition, user);
|
const app = createCoreApp(backendDefinition, user);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recordApi: {
|
recordApi: {
|
||||||
|
@ -22186,10 +22269,46 @@ var app = (function (exports) {
|
||||||
parentNode: null,
|
parentNode: null,
|
||||||
children: [],
|
children: [],
|
||||||
component: null,
|
component: null,
|
||||||
unsubscribe: () => { },
|
unsubscribe: () => {},
|
||||||
|
get destroy() {
|
||||||
|
const node = this;
|
||||||
|
return () => {
|
||||||
|
if (node.unsubscribe) node.unsubscribe();
|
||||||
|
if (node.component && node.component.$destroy) node.component.$destroy();
|
||||||
|
if (node.children) {
|
||||||
|
for (let child of node.children) {
|
||||||
|
child.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const _initialiseChildren = initialiseOpts => (
|
const screenSlotComponent = window => {
|
||||||
|
return function(opts) {
|
||||||
|
const node = window.document.createElement("DIV");
|
||||||
|
const $set = props => {
|
||||||
|
props._bb.hydrateChildren(props._children, node);
|
||||||
|
};
|
||||||
|
const $destroy = () => {
|
||||||
|
if (opts.target && node) opts.target.removeChild(node);
|
||||||
|
};
|
||||||
|
this.$set = $set;
|
||||||
|
this.$destroy = $destroy;
|
||||||
|
opts.target.appendChild(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const builtinLibName = "##builtin";
|
||||||
|
|
||||||
|
const isScreenSlot = componentName =>
|
||||||
|
componentName === "##builtin/screenslot";
|
||||||
|
|
||||||
|
const builtins = window => ({
|
||||||
|
screenslot: screenSlotComponent(window),
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialiseChildren = initialiseOpts => (
|
||||||
childrenProps,
|
childrenProps,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor = null
|
anchor = null
|
||||||
|
@ -22201,14 +22320,13 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
treeNode,
|
treeNode,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
document,
|
|
||||||
hydrate,
|
hydrate,
|
||||||
|
onScreenSlotRendered,
|
||||||
} = initialiseOpts;
|
} = initialiseOpts;
|
||||||
|
|
||||||
for (let childNode of treeNode.children) {
|
for (let childNode of treeNode.children) {
|
||||||
if (childNode.unsubscribe) childNode.unsubscribe();
|
childNode.destroy();
|
||||||
if (childNode.component) childNode.component.$destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hydrate) {
|
if (hydrate) {
|
||||||
|
@ -22229,7 +22347,7 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
childProps,
|
childProps,
|
||||||
coreApi,
|
coreApi,
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
);
|
);
|
||||||
|
|
||||||
const componentConstructor = componentLibraries[libName][componentName];
|
const componentConstructor = componentLibraries[libName][componentName];
|
||||||
|
@ -22245,6 +22363,15 @@ var app = (function (exports) {
|
||||||
bb,
|
bb,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
onScreenSlotRendered &&
|
||||||
|
isScreenSlot(childProps._component) &&
|
||||||
|
renderedComponentsThisIteration.length > 0
|
||||||
|
) {
|
||||||
|
// assuming there is only ever one screen slot
|
||||||
|
onScreenSlotRendered(renderedComponentsThisIteration[0]);
|
||||||
|
}
|
||||||
|
|
||||||
for (let comp of renderedComponentsThisIteration) {
|
for (let comp of renderedComponentsThisIteration) {
|
||||||
comp.unsubscribe = bind(comp.component);
|
comp.unsubscribe = bind(comp.component);
|
||||||
renderedComponents.push(comp);
|
renderedComponents.push(comp);
|
||||||
|
@ -22265,29 +22392,123 @@ var app = (function (exports) {
|
||||||
return { libName, componentName }
|
return { libName, componentName }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function regexparam (str, loose) {
|
||||||
|
if (str instanceof RegExp) return { keys:false, pattern:str };
|
||||||
|
var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/');
|
||||||
|
arr[0] || arr.shift();
|
||||||
|
|
||||||
|
while (tmp = arr.shift()) {
|
||||||
|
c = tmp[0];
|
||||||
|
if (c === '*') {
|
||||||
|
keys.push('wild');
|
||||||
|
pattern += '/(.*)';
|
||||||
|
} else if (c === ':') {
|
||||||
|
o = tmp.indexOf('?', 1);
|
||||||
|
ext = tmp.indexOf('.', 1);
|
||||||
|
keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) );
|
||||||
|
pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)';
|
||||||
|
if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext);
|
||||||
|
} else {
|
||||||
|
pattern += '/' + tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
keys: keys,
|
||||||
|
pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const screenRouter = (screens, onScreenSelected) => {
|
||||||
|
const routes = screens.map(s => s.route);
|
||||||
|
let fallback = routes.findIndex(([p]) => p === "*");
|
||||||
|
if (fallback < 0) fallback = 0;
|
||||||
|
|
||||||
|
let current;
|
||||||
|
|
||||||
|
function route(url) {
|
||||||
|
const _url = url.state || url;
|
||||||
|
current = routes.findIndex(
|
||||||
|
p => p !== "*" && new RegExp("^" + p + "$").test(_url)
|
||||||
|
);
|
||||||
|
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (current === -1) {
|
||||||
|
routes.forEach(([p], i) => {
|
||||||
|
const pm = regexparam(p);
|
||||||
|
const matches = pm.pattern.exec(_url);
|
||||||
|
|
||||||
|
if (!matches) return
|
||||||
|
|
||||||
|
let j = 0;
|
||||||
|
while (j < pm.keys.length) {
|
||||||
|
params[pm.keys[j]] = matches[++j] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const storeInitial = {};
|
||||||
|
const store = writable(storeInitial);
|
||||||
|
|
||||||
|
if (current !== -1) {
|
||||||
|
onScreenSelected(screens[current], store, _url);
|
||||||
|
} else if (fallback) {
|
||||||
|
onScreenSelected(screens[fallback], store, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
!url.state && history.pushState(_url, null, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function click(e) {
|
||||||
|
const x = e.target.closest("a");
|
||||||
|
const y = x && x.getAttribute("href");
|
||||||
|
|
||||||
|
if (
|
||||||
|
e.ctrlKey ||
|
||||||
|
e.metaKey ||
|
||||||
|
e.altKey ||
|
||||||
|
e.shiftKey ||
|
||||||
|
e.button ||
|
||||||
|
e.defaultPrevented
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!y || x.target || x.host !== location.host) return
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
route(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("popstate", route);
|
||||||
|
addEventListener("pushstate", route);
|
||||||
|
addEventListener("click", click);
|
||||||
|
|
||||||
|
return route
|
||||||
|
};
|
||||||
|
|
||||||
const createApp = (
|
const createApp = (
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions
|
uiFunctions,
|
||||||
|
screens
|
||||||
) => {
|
) => {
|
||||||
const coreApi = createCoreApi(appDefinition, user);
|
const coreApi = createCoreApi(backendDefinition, user);
|
||||||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
||||||
appDefinition.hierarchy
|
backendDefinition.hierarchy
|
||||||
);
|
);
|
||||||
const store = writable({
|
const pageStore = writable({
|
||||||
_bbuser: user,
|
_bbuser: user,
|
||||||
});
|
});
|
||||||
|
|
||||||
let globalState = null;
|
|
||||||
store.subscribe(s => {
|
|
||||||
globalState = s;
|
|
||||||
});
|
|
||||||
|
|
||||||
const relativeUrl = url =>
|
const relativeUrl = url =>
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
? appDefinition.appRootPath + "/" + trimSlash(url)
|
? frontendDefinition.appRootPath + "/" + trimSlash(url)
|
||||||
: url;
|
: url;
|
||||||
|
|
||||||
const apiCall = method => (url, body) =>
|
const apiCall = method => (url, body) =>
|
||||||
|
@ -22313,27 +22534,60 @@ var app = (function (exports) {
|
||||||
if (isFunction(event)) event(context);
|
if (isFunction(event)) event(context);
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialiseChildrenParams = (hydrate, treeNode) => ({
|
let routeTo;
|
||||||
bb,
|
let currentScreenStore;
|
||||||
|
let currentScreenUbsubscribe;
|
||||||
|
let currentUrl;
|
||||||
|
|
||||||
|
const onScreenSlotRendered = screenSlotNode => {
|
||||||
|
const onScreenSelected = (screen, store, url) => {
|
||||||
|
const { getInitialiseParams, unsubscribe } = initialiseChildrenParams(
|
||||||
|
store
|
||||||
|
);
|
||||||
|
const initialiseChildParams = getInitialiseParams(true, screenSlotNode);
|
||||||
|
initialiseChildren(initialiseChildParams)(
|
||||||
|
[screen.props],
|
||||||
|
screenSlotNode.rootElement
|
||||||
|
);
|
||||||
|
if (currentScreenUbsubscribe) currentScreenUbsubscribe();
|
||||||
|
currentScreenUbsubscribe = unsubscribe;
|
||||||
|
currentScreenStore = store;
|
||||||
|
currentUrl = url;
|
||||||
|
};
|
||||||
|
|
||||||
|
routeTo = screenRouter(screens, onScreenSelected);
|
||||||
|
routeTo(currentUrl || window.location.pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialiseChildrenParams = store => {
|
||||||
|
let currentState = null;
|
||||||
|
const unsubscribe = store.subscribe(s => {
|
||||||
|
currentState = s;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getInitialiseParams = (hydrate, treeNode) => ({
|
||||||
|
bb: getBbClientApi,
|
||||||
coreApi,
|
coreApi,
|
||||||
store,
|
store,
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
hydrate,
|
hydrate,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
treeNode,
|
treeNode,
|
||||||
|
onScreenSlotRendered,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bb = (treeNode, componentProps) => ({
|
const getBbClientApi = (treeNode, componentProps) => {
|
||||||
hydrateChildren: _initialiseChildren(
|
return {
|
||||||
initialiseChildrenParams(true, treeNode)
|
hydrateChildren: initialiseChildren(
|
||||||
|
getInitialiseParams(true, treeNode)
|
||||||
),
|
),
|
||||||
appendChildren: _initialiseChildren(
|
appendChildren: initialiseChildren(
|
||||||
initialiseChildrenParams(false, treeNode)
|
getInitialiseParams(false, treeNode)
|
||||||
),
|
),
|
||||||
insertChildren: (props, htmlElement, anchor) =>
|
insertChildren: (props, htmlElement, anchor) =>
|
||||||
_initialiseChildren(initialiseChildrenParams(false, treeNode))(
|
initialiseChildren(getInitialiseParams(false, treeNode))(
|
||||||
props,
|
props,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor
|
anchor
|
||||||
|
@ -22345,26 +22599,50 @@ var app = (function (exports) {
|
||||||
setStateFromBinding(store, binding, value),
|
setStateFromBinding(store, binding, value),
|
||||||
setState: (path, value) => setState(store, path, value),
|
setState: (path, value) => setState(store, path, value),
|
||||||
getStateOrValue: (prop, currentContext) =>
|
getStateOrValue: (prop, currentContext) =>
|
||||||
getStateOrValue(globalState, prop, currentContext),
|
getStateOrValue(currentState, prop, currentContext),
|
||||||
store,
|
store,
|
||||||
relativeUrl,
|
relativeUrl,
|
||||||
api,
|
api,
|
||||||
isBound,
|
isBound,
|
||||||
parent,
|
parent,
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
return { getInitialiseParams, unsubscribe }
|
||||||
|
};
|
||||||
|
|
||||||
return bb(createTreeNode())
|
let rootTreeNode;
|
||||||
|
|
||||||
|
const initialisePage = (page, target, urlPath) => {
|
||||||
|
currentUrl = urlPath;
|
||||||
|
|
||||||
|
rootTreeNode = createTreeNode();
|
||||||
|
const { getInitialiseParams } = initialiseChildrenParams(pageStore);
|
||||||
|
const initChildParams = getInitialiseParams(true, rootTreeNode);
|
||||||
|
|
||||||
|
initialiseChildren(initChildParams)([page.props], target);
|
||||||
|
|
||||||
|
return rootTreeNode
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
initialisePage,
|
||||||
|
screenStore: () => currentScreenStore,
|
||||||
|
pageStore: () => pageStore,
|
||||||
|
routeTo: () => routeTo,
|
||||||
|
rootNode: () => rootTreeNode,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadBudibase = async ({
|
const loadBudibase = async ({
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
props,
|
page,
|
||||||
|
screens,
|
||||||
window,
|
window,
|
||||||
localStorage,
|
localStorage,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
}) => {
|
}) => {
|
||||||
const appDefinition = window["##BUDIBASE_APPDEFINITION##"];
|
const backendDefinition = window["##BUDIBASE_BACKEND_DEFINITION##"];
|
||||||
const uiFunctionsFromWindow = window["##BUDIBASE_APPDEFINITION##"];
|
const frontendDefinition = window["##BUDIBASE_FRONTEND_DEFINITION##"];
|
||||||
|
const uiFunctionsFromWindow = window["##BUDIBASE_FRONTEND_FUNCTIONS##"];
|
||||||
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
||||||
|
|
||||||
const userFromStorage = localStorage.getItem("budibase:user");
|
const userFromStorage = localStorage.getItem("budibase:user");
|
||||||
|
@ -22378,35 +22656,54 @@ var app = (function (exports) {
|
||||||
temp: false,
|
temp: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!componentLibraries) {
|
|
||||||
const rootPath =
|
const rootPath =
|
||||||
appDefinition.appRootPath === ""
|
frontendDefinition.appRootPath === ""
|
||||||
? ""
|
? ""
|
||||||
: "/" + trimSlash(appDefinition.appRootPath);
|
: "/" + trimSlash(frontendDefinition.appRootPath);
|
||||||
|
|
||||||
|
if (!componentLibraries) {
|
||||||
|
|
||||||
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
||||||
componentLibraries = {};
|
componentLibraries = {};
|
||||||
|
|
||||||
for (let lib of appDefinition.componentLibraries) {
|
for (let lib of frontendDefinition.componentLibraries) {
|
||||||
componentLibraries[lib.libName] = await import(
|
componentLibraries[lib.libName] = await import(
|
||||||
componentLibraryUrl(lib.importPath)
|
componentLibraryUrl(lib.importPath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props) {
|
componentLibraries[builtinLibName] = builtins(window);
|
||||||
props = appDefinition.props;
|
|
||||||
|
if (!page) {
|
||||||
|
page = frontendDefinition.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = createApp(
|
if (!screens) {
|
||||||
|
screens = frontendDefinition.screens;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { initialisePage, screenStore, pageStore, routeTo, rootNode } = createApp(
|
||||||
window.document,
|
window.document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions || {}
|
uiFunctions || {},
|
||||||
|
screens
|
||||||
);
|
);
|
||||||
app.hydrateChildren([props], window.document.body);
|
|
||||||
|
|
||||||
return app
|
const route = window.location
|
||||||
|
? window.location.pathname.replace(rootPath, "")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
rootNode: initialisePage(page, window.document.body, route),
|
||||||
|
screenStore,
|
||||||
|
pageStore,
|
||||||
|
routeTo,
|
||||||
|
rootNode
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,7 +15,8 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<script src='/_master/clientAppDefinition.js'></script>
|
<script src='/_master/clientFrontendDefinition.js'></script>
|
||||||
|
<script src='/_master/clientBackendDefinition.js'></script>
|
||||||
<script src='/_master/budibase-client.js'></script>
|
<script src='/_master/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
loadBudibase();
|
loadBudibase();
|
||||||
|
|
|
@ -2,9 +2,61 @@ var app = (function (exports) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function noop() { }
|
function noop() { }
|
||||||
|
function run(fn) {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
function run_all(fns) {
|
||||||
|
fns.forEach(run);
|
||||||
|
}
|
||||||
function safe_not_equal(a, b) {
|
function safe_not_equal(a, b) {
|
||||||
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
||||||
}
|
}
|
||||||
|
function destroy_component(component, detaching) {
|
||||||
|
if (component.$$.fragment) {
|
||||||
|
run_all(component.$$.on_destroy);
|
||||||
|
component.$$.fragment.d(detaching);
|
||||||
|
// TODO null out other refs, including component.$$ (but need to
|
||||||
|
// preserve final state?)
|
||||||
|
component.$$.on_destroy = component.$$.fragment = null;
|
||||||
|
component.$$.ctx = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let SvelteElement;
|
||||||
|
if (typeof HTMLElement !== 'undefined') {
|
||||||
|
SvelteElement = class extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
for (const key in this.$$.slotted) {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
this.appendChild(this.$$.slotted[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributeChangedCallback(attr, _oldValue, newValue) {
|
||||||
|
this[attr] = newValue;
|
||||||
|
}
|
||||||
|
$destroy() {
|
||||||
|
destroy_component(this, 1);
|
||||||
|
this.$destroy = noop;
|
||||||
|
}
|
||||||
|
$on(type, callback) {
|
||||||
|
// TODO should this delegate to addEventListener?
|
||||||
|
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
|
||||||
|
callbacks.push(callback);
|
||||||
|
return () => {
|
||||||
|
const index = callbacks.indexOf(callback);
|
||||||
|
if (index !== -1)
|
||||||
|
callbacks.splice(index, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$set() {
|
||||||
|
// overridden by instance, if it has props
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const subscriber_queue = [];
|
const subscriber_queue = [];
|
||||||
/**
|
/**
|
||||||
|
@ -58,13 +110,13 @@ var app = (function (exports) {
|
||||||
return { set, update, subscribe };
|
return { set, update, subscribe };
|
||||||
}
|
}
|
||||||
|
|
||||||
const createCoreApp = (appDefinition, user) => {
|
const createCoreApp = (backendDefinition, user) => {
|
||||||
const app = {
|
const app = {
|
||||||
datastore: null,
|
datastore: null,
|
||||||
crypto: null,
|
crypto: null,
|
||||||
publish: () => {},
|
publish: () => {},
|
||||||
hierarchy: appDefinition.hierarchy,
|
hierarchy: backendDefinition.hierarchy,
|
||||||
actions: appDefinition.actions,
|
actions: backendDefinition.actions,
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1349,17 +1401,48 @@ var app = (function (exports) {
|
||||||
|
|
||||||
var randomByteBrowser = randomByte;
|
var randomByteBrowser = randomByte;
|
||||||
|
|
||||||
var format_browser = function (random, alphabet, size) {
|
/**
|
||||||
|
* Secure random string generator with custom alphabet.
|
||||||
|
*
|
||||||
|
* Alphabet must contain 256 symbols or less. Otherwise, the generator
|
||||||
|
* will not be secure.
|
||||||
|
*
|
||||||
|
* @param {generator} random The random bytes generator.
|
||||||
|
* @param {string} alphabet Symbols to be used in new random string.
|
||||||
|
* @param {size} size The number of symbols in new random string.
|
||||||
|
*
|
||||||
|
* @return {string} Random string.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const format = require('nanoid/format')
|
||||||
|
*
|
||||||
|
* function random (size) {
|
||||||
|
* const result = []
|
||||||
|
* for (let i = 0; i < size; i++) {
|
||||||
|
* result.push(randomByte())
|
||||||
|
* }
|
||||||
|
* return result
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* format(random, "abcdef", 5) //=> "fbaef"
|
||||||
|
*
|
||||||
|
* @name format
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
var format = function (random, alphabet, size) {
|
||||||
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
||||||
var step = -~(1.6 * mask * size / alphabet.length);
|
var step = Math.ceil(1.6 * mask * size / alphabet.length);
|
||||||
var id = '';
|
size = +size;
|
||||||
|
|
||||||
|
var id = '';
|
||||||
while (true) {
|
while (true) {
|
||||||
var i = step;
|
var bytes = random(step);
|
||||||
var bytes = random(i);
|
for (var i = 0; i < step; i++) {
|
||||||
while (i--) {
|
var byte = bytes[i] & mask;
|
||||||
id += alphabet[bytes[i] & mask] || '';
|
if (alphabet[byte]) {
|
||||||
if (id.length === +size) return id
|
id += alphabet[byte];
|
||||||
|
if (id.length === size) return id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1371,7 +1454,7 @@ var app = (function (exports) {
|
||||||
var str = '';
|
var str = '';
|
||||||
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
str = str + format_browser(randomByteBrowser, alphabet_1.get(), 1);
|
str = str + format(randomByteBrowser, alphabet_1.get(), 1);
|
||||||
done = number < (Math.pow(16, loopCounter + 1 ) );
|
done = number < (Math.pow(16, loopCounter + 1 ) );
|
||||||
loopCounter++;
|
loopCounter++;
|
||||||
}
|
}
|
||||||
|
@ -21567,8 +21650,8 @@ var app = (function (exports) {
|
||||||
return node
|
return node
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCoreApi = (appDefinition, user) => {
|
const createCoreApi = (backendDefinition, user) => {
|
||||||
const app = createCoreApp(appDefinition, user);
|
const app = createCoreApp(backendDefinition, user);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recordApi: {
|
recordApi: {
|
||||||
|
@ -22186,10 +22269,46 @@ var app = (function (exports) {
|
||||||
parentNode: null,
|
parentNode: null,
|
||||||
children: [],
|
children: [],
|
||||||
component: null,
|
component: null,
|
||||||
unsubscribe: () => { },
|
unsubscribe: () => {},
|
||||||
|
get destroy() {
|
||||||
|
const node = this;
|
||||||
|
return () => {
|
||||||
|
if (node.unsubscribe) node.unsubscribe();
|
||||||
|
if (node.component && node.component.$destroy) node.component.$destroy();
|
||||||
|
if (node.children) {
|
||||||
|
for (let child of node.children) {
|
||||||
|
child.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const _initialiseChildren = initialiseOpts => (
|
const screenSlotComponent = window => {
|
||||||
|
return function(opts) {
|
||||||
|
const node = window.document.createElement("DIV");
|
||||||
|
const $set = props => {
|
||||||
|
props._bb.hydrateChildren(props._children, node);
|
||||||
|
};
|
||||||
|
const $destroy = () => {
|
||||||
|
if (opts.target && node) opts.target.removeChild(node);
|
||||||
|
};
|
||||||
|
this.$set = $set;
|
||||||
|
this.$destroy = $destroy;
|
||||||
|
opts.target.appendChild(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const builtinLibName = "##builtin";
|
||||||
|
|
||||||
|
const isScreenSlot = componentName =>
|
||||||
|
componentName === "##builtin/screenslot";
|
||||||
|
|
||||||
|
const builtins = window => ({
|
||||||
|
screenslot: screenSlotComponent(window),
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialiseChildren = initialiseOpts => (
|
||||||
childrenProps,
|
childrenProps,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor = null
|
anchor = null
|
||||||
|
@ -22201,14 +22320,13 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
treeNode,
|
treeNode,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
document,
|
|
||||||
hydrate,
|
hydrate,
|
||||||
|
onScreenSlotRendered,
|
||||||
} = initialiseOpts;
|
} = initialiseOpts;
|
||||||
|
|
||||||
for (let childNode of treeNode.children) {
|
for (let childNode of treeNode.children) {
|
||||||
if (childNode.unsubscribe) childNode.unsubscribe();
|
childNode.destroy();
|
||||||
if (childNode.component) childNode.component.$destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hydrate) {
|
if (hydrate) {
|
||||||
|
@ -22229,7 +22347,7 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
childProps,
|
childProps,
|
||||||
coreApi,
|
coreApi,
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
);
|
);
|
||||||
|
|
||||||
const componentConstructor = componentLibraries[libName][componentName];
|
const componentConstructor = componentLibraries[libName][componentName];
|
||||||
|
@ -22245,6 +22363,15 @@ var app = (function (exports) {
|
||||||
bb,
|
bb,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
onScreenSlotRendered &&
|
||||||
|
isScreenSlot(childProps._component) &&
|
||||||
|
renderedComponentsThisIteration.length > 0
|
||||||
|
) {
|
||||||
|
// assuming there is only ever one screen slot
|
||||||
|
onScreenSlotRendered(renderedComponentsThisIteration[0]);
|
||||||
|
}
|
||||||
|
|
||||||
for (let comp of renderedComponentsThisIteration) {
|
for (let comp of renderedComponentsThisIteration) {
|
||||||
comp.unsubscribe = bind(comp.component);
|
comp.unsubscribe = bind(comp.component);
|
||||||
renderedComponents.push(comp);
|
renderedComponents.push(comp);
|
||||||
|
@ -22265,29 +22392,123 @@ var app = (function (exports) {
|
||||||
return { libName, componentName }
|
return { libName, componentName }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function regexparam (str, loose) {
|
||||||
|
if (str instanceof RegExp) return { keys:false, pattern:str };
|
||||||
|
var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/');
|
||||||
|
arr[0] || arr.shift();
|
||||||
|
|
||||||
|
while (tmp = arr.shift()) {
|
||||||
|
c = tmp[0];
|
||||||
|
if (c === '*') {
|
||||||
|
keys.push('wild');
|
||||||
|
pattern += '/(.*)';
|
||||||
|
} else if (c === ':') {
|
||||||
|
o = tmp.indexOf('?', 1);
|
||||||
|
ext = tmp.indexOf('.', 1);
|
||||||
|
keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) );
|
||||||
|
pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)';
|
||||||
|
if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext);
|
||||||
|
} else {
|
||||||
|
pattern += '/' + tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
keys: keys,
|
||||||
|
pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const screenRouter = (screens, onScreenSelected) => {
|
||||||
|
const routes = screens.map(s => s.route);
|
||||||
|
let fallback = routes.findIndex(([p]) => p === "*");
|
||||||
|
if (fallback < 0) fallback = 0;
|
||||||
|
|
||||||
|
let current;
|
||||||
|
|
||||||
|
function route(url) {
|
||||||
|
const _url = url.state || url;
|
||||||
|
current = routes.findIndex(
|
||||||
|
p => p !== "*" && new RegExp("^" + p + "$").test(_url)
|
||||||
|
);
|
||||||
|
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (current === -1) {
|
||||||
|
routes.forEach(([p], i) => {
|
||||||
|
const pm = regexparam(p);
|
||||||
|
const matches = pm.pattern.exec(_url);
|
||||||
|
|
||||||
|
if (!matches) return
|
||||||
|
|
||||||
|
let j = 0;
|
||||||
|
while (j < pm.keys.length) {
|
||||||
|
params[pm.keys[j]] = matches[++j] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const storeInitial = {};
|
||||||
|
const store = writable(storeInitial);
|
||||||
|
|
||||||
|
if (current !== -1) {
|
||||||
|
onScreenSelected(screens[current], store, _url);
|
||||||
|
} else if (fallback) {
|
||||||
|
onScreenSelected(screens[fallback], store, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
!url.state && history.pushState(_url, null, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function click(e) {
|
||||||
|
const x = e.target.closest("a");
|
||||||
|
const y = x && x.getAttribute("href");
|
||||||
|
|
||||||
|
if (
|
||||||
|
e.ctrlKey ||
|
||||||
|
e.metaKey ||
|
||||||
|
e.altKey ||
|
||||||
|
e.shiftKey ||
|
||||||
|
e.button ||
|
||||||
|
e.defaultPrevented
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!y || x.target || x.host !== location.host) return
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
route(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("popstate", route);
|
||||||
|
addEventListener("pushstate", route);
|
||||||
|
addEventListener("click", click);
|
||||||
|
|
||||||
|
return route
|
||||||
|
};
|
||||||
|
|
||||||
const createApp = (
|
const createApp = (
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions
|
uiFunctions,
|
||||||
|
screens
|
||||||
) => {
|
) => {
|
||||||
const coreApi = createCoreApi(appDefinition, user);
|
const coreApi = createCoreApi(backendDefinition, user);
|
||||||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
||||||
appDefinition.hierarchy
|
backendDefinition.hierarchy
|
||||||
);
|
);
|
||||||
const store = writable({
|
const pageStore = writable({
|
||||||
_bbuser: user,
|
_bbuser: user,
|
||||||
});
|
});
|
||||||
|
|
||||||
let globalState = null;
|
|
||||||
store.subscribe(s => {
|
|
||||||
globalState = s;
|
|
||||||
});
|
|
||||||
|
|
||||||
const relativeUrl = url =>
|
const relativeUrl = url =>
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
? appDefinition.appRootPath + "/" + trimSlash(url)
|
? frontendDefinition.appRootPath + "/" + trimSlash(url)
|
||||||
: url;
|
: url;
|
||||||
|
|
||||||
const apiCall = method => (url, body) =>
|
const apiCall = method => (url, body) =>
|
||||||
|
@ -22313,27 +22534,60 @@ var app = (function (exports) {
|
||||||
if (isFunction(event)) event(context);
|
if (isFunction(event)) event(context);
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialiseChildrenParams = (hydrate, treeNode) => ({
|
let routeTo;
|
||||||
bb,
|
let currentScreenStore;
|
||||||
|
let currentScreenUbsubscribe;
|
||||||
|
let currentUrl;
|
||||||
|
|
||||||
|
const onScreenSlotRendered = screenSlotNode => {
|
||||||
|
const onScreenSelected = (screen, store, url) => {
|
||||||
|
const { getInitialiseParams, unsubscribe } = initialiseChildrenParams(
|
||||||
|
store
|
||||||
|
);
|
||||||
|
const initialiseChildParams = getInitialiseParams(true, screenSlotNode);
|
||||||
|
initialiseChildren(initialiseChildParams)(
|
||||||
|
[screen.props],
|
||||||
|
screenSlotNode.rootElement
|
||||||
|
);
|
||||||
|
if (currentScreenUbsubscribe) currentScreenUbsubscribe();
|
||||||
|
currentScreenUbsubscribe = unsubscribe;
|
||||||
|
currentScreenStore = store;
|
||||||
|
currentUrl = url;
|
||||||
|
};
|
||||||
|
|
||||||
|
routeTo = screenRouter(screens, onScreenSelected);
|
||||||
|
routeTo(currentUrl || window.location.pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialiseChildrenParams = store => {
|
||||||
|
let currentState = null;
|
||||||
|
const unsubscribe = store.subscribe(s => {
|
||||||
|
currentState = s;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getInitialiseParams = (hydrate, treeNode) => ({
|
||||||
|
bb: getBbClientApi,
|
||||||
coreApi,
|
coreApi,
|
||||||
store,
|
store,
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
hydrate,
|
hydrate,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
treeNode,
|
treeNode,
|
||||||
|
onScreenSlotRendered,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bb = (treeNode, componentProps) => ({
|
const getBbClientApi = (treeNode, componentProps) => {
|
||||||
hydrateChildren: _initialiseChildren(
|
return {
|
||||||
initialiseChildrenParams(true, treeNode)
|
hydrateChildren: initialiseChildren(
|
||||||
|
getInitialiseParams(true, treeNode)
|
||||||
),
|
),
|
||||||
appendChildren: _initialiseChildren(
|
appendChildren: initialiseChildren(
|
||||||
initialiseChildrenParams(false, treeNode)
|
getInitialiseParams(false, treeNode)
|
||||||
),
|
),
|
||||||
insertChildren: (props, htmlElement, anchor) =>
|
insertChildren: (props, htmlElement, anchor) =>
|
||||||
_initialiseChildren(initialiseChildrenParams(false, treeNode))(
|
initialiseChildren(getInitialiseParams(false, treeNode))(
|
||||||
props,
|
props,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor
|
anchor
|
||||||
|
@ -22345,26 +22599,50 @@ var app = (function (exports) {
|
||||||
setStateFromBinding(store, binding, value),
|
setStateFromBinding(store, binding, value),
|
||||||
setState: (path, value) => setState(store, path, value),
|
setState: (path, value) => setState(store, path, value),
|
||||||
getStateOrValue: (prop, currentContext) =>
|
getStateOrValue: (prop, currentContext) =>
|
||||||
getStateOrValue(globalState, prop, currentContext),
|
getStateOrValue(currentState, prop, currentContext),
|
||||||
store,
|
store,
|
||||||
relativeUrl,
|
relativeUrl,
|
||||||
api,
|
api,
|
||||||
isBound,
|
isBound,
|
||||||
parent,
|
parent,
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
return { getInitialiseParams, unsubscribe }
|
||||||
|
};
|
||||||
|
|
||||||
return bb(createTreeNode())
|
let rootTreeNode;
|
||||||
|
|
||||||
|
const initialisePage = (page, target, urlPath) => {
|
||||||
|
currentUrl = urlPath;
|
||||||
|
|
||||||
|
rootTreeNode = createTreeNode();
|
||||||
|
const { getInitialiseParams } = initialiseChildrenParams(pageStore);
|
||||||
|
const initChildParams = getInitialiseParams(true, rootTreeNode);
|
||||||
|
|
||||||
|
initialiseChildren(initChildParams)([page.props], target);
|
||||||
|
|
||||||
|
return rootTreeNode
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
initialisePage,
|
||||||
|
screenStore: () => currentScreenStore,
|
||||||
|
pageStore: () => pageStore,
|
||||||
|
routeTo: () => routeTo,
|
||||||
|
rootNode: () => rootTreeNode,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadBudibase = async ({
|
const loadBudibase = async ({
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
props,
|
page,
|
||||||
|
screens,
|
||||||
window,
|
window,
|
||||||
localStorage,
|
localStorage,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
}) => {
|
}) => {
|
||||||
const appDefinition = window["##BUDIBASE_APPDEFINITION##"];
|
const backendDefinition = window["##BUDIBASE_BACKEND_DEFINITION##"];
|
||||||
const uiFunctionsFromWindow = window["##BUDIBASE_APPDEFINITION##"];
|
const frontendDefinition = window["##BUDIBASE_FRONTEND_DEFINITION##"];
|
||||||
|
const uiFunctionsFromWindow = window["##BUDIBASE_FRONTEND_FUNCTIONS##"];
|
||||||
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
||||||
|
|
||||||
const userFromStorage = localStorage.getItem("budibase:user");
|
const userFromStorage = localStorage.getItem("budibase:user");
|
||||||
|
@ -22378,35 +22656,54 @@ var app = (function (exports) {
|
||||||
temp: false,
|
temp: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!componentLibraries) {
|
|
||||||
const rootPath =
|
const rootPath =
|
||||||
appDefinition.appRootPath === ""
|
frontendDefinition.appRootPath === ""
|
||||||
? ""
|
? ""
|
||||||
: "/" + trimSlash(appDefinition.appRootPath);
|
: "/" + trimSlash(frontendDefinition.appRootPath);
|
||||||
|
|
||||||
|
if (!componentLibraries) {
|
||||||
|
|
||||||
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
||||||
componentLibraries = {};
|
componentLibraries = {};
|
||||||
|
|
||||||
for (let lib of appDefinition.componentLibraries) {
|
for (let lib of frontendDefinition.componentLibraries) {
|
||||||
componentLibraries[lib.libName] = await import(
|
componentLibraries[lib.libName] = await import(
|
||||||
componentLibraryUrl(lib.importPath)
|
componentLibraryUrl(lib.importPath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props) {
|
componentLibraries[builtinLibName] = builtins(window);
|
||||||
props = appDefinition.props;
|
|
||||||
|
if (!page) {
|
||||||
|
page = frontendDefinition.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = createApp(
|
if (!screens) {
|
||||||
|
screens = frontendDefinition.screens;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { initialisePage, screenStore, pageStore, routeTo, rootNode } = createApp(
|
||||||
window.document,
|
window.document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions || {}
|
uiFunctions || {},
|
||||||
|
screens
|
||||||
);
|
);
|
||||||
app.hydrateChildren([props], window.document.body);
|
|
||||||
|
|
||||||
return app
|
const route = window.location
|
||||||
|
? window.location.pathname.replace(rootPath, "")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
rootNode: initialisePage(page, window.document.body, route),
|
||||||
|
screenStore,
|
||||||
|
pageStore,
|
||||||
|
routeTo,
|
||||||
|
rootNode
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,7 +15,8 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<script src='/_master/clientAppDefinition.js'></script>
|
<script src='/_master/clientFrontendDefinition.js'></script>
|
||||||
|
<script src='/_master/clientBackendDefinition.js'></script>
|
||||||
<script src='/_master/budibase-client.js'></script>
|
<script src='/_master/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
loadBudibase();
|
loadBudibase();
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -29,7 +29,8 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src='/clientAppDefinition.js'></script>
|
<script src='/_master/clientFrontendDefinition.js'></script>
|
||||||
|
<script src='/_master/clientBackendDefinition.js'></script>
|
||||||
<script src='/budibase-client.js'></script>
|
<script src='/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
loadBudibase();
|
loadBudibase();
|
||||||
|
|
|
@ -2,9 +2,61 @@ var app = (function (exports) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function noop() { }
|
function noop() { }
|
||||||
|
function run(fn) {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
function run_all(fns) {
|
||||||
|
fns.forEach(run);
|
||||||
|
}
|
||||||
function safe_not_equal(a, b) {
|
function safe_not_equal(a, b) {
|
||||||
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
||||||
}
|
}
|
||||||
|
function destroy_component(component, detaching) {
|
||||||
|
if (component.$$.fragment) {
|
||||||
|
run_all(component.$$.on_destroy);
|
||||||
|
component.$$.fragment.d(detaching);
|
||||||
|
// TODO null out other refs, including component.$$ (but need to
|
||||||
|
// preserve final state?)
|
||||||
|
component.$$.on_destroy = component.$$.fragment = null;
|
||||||
|
component.$$.ctx = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let SvelteElement;
|
||||||
|
if (typeof HTMLElement !== 'undefined') {
|
||||||
|
SvelteElement = class extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
for (const key in this.$$.slotted) {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
this.appendChild(this.$$.slotted[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributeChangedCallback(attr, _oldValue, newValue) {
|
||||||
|
this[attr] = newValue;
|
||||||
|
}
|
||||||
|
$destroy() {
|
||||||
|
destroy_component(this, 1);
|
||||||
|
this.$destroy = noop;
|
||||||
|
}
|
||||||
|
$on(type, callback) {
|
||||||
|
// TODO should this delegate to addEventListener?
|
||||||
|
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
|
||||||
|
callbacks.push(callback);
|
||||||
|
return () => {
|
||||||
|
const index = callbacks.indexOf(callback);
|
||||||
|
if (index !== -1)
|
||||||
|
callbacks.splice(index, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$set() {
|
||||||
|
// overridden by instance, if it has props
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const subscriber_queue = [];
|
const subscriber_queue = [];
|
||||||
/**
|
/**
|
||||||
|
@ -58,13 +110,13 @@ var app = (function (exports) {
|
||||||
return { set, update, subscribe };
|
return { set, update, subscribe };
|
||||||
}
|
}
|
||||||
|
|
||||||
const createCoreApp = (appDefinition, user) => {
|
const createCoreApp = (backendDefinition, user) => {
|
||||||
const app = {
|
const app = {
|
||||||
datastore: null,
|
datastore: null,
|
||||||
crypto: null,
|
crypto: null,
|
||||||
publish: () => {},
|
publish: () => {},
|
||||||
hierarchy: appDefinition.hierarchy,
|
hierarchy: backendDefinition.hierarchy,
|
||||||
actions: appDefinition.actions,
|
actions: backendDefinition.actions,
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1349,17 +1401,48 @@ var app = (function (exports) {
|
||||||
|
|
||||||
var randomByteBrowser = randomByte;
|
var randomByteBrowser = randomByte;
|
||||||
|
|
||||||
var format_browser = function (random, alphabet, size) {
|
/**
|
||||||
|
* Secure random string generator with custom alphabet.
|
||||||
|
*
|
||||||
|
* Alphabet must contain 256 symbols or less. Otherwise, the generator
|
||||||
|
* will not be secure.
|
||||||
|
*
|
||||||
|
* @param {generator} random The random bytes generator.
|
||||||
|
* @param {string} alphabet Symbols to be used in new random string.
|
||||||
|
* @param {size} size The number of symbols in new random string.
|
||||||
|
*
|
||||||
|
* @return {string} Random string.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const format = require('nanoid/format')
|
||||||
|
*
|
||||||
|
* function random (size) {
|
||||||
|
* const result = []
|
||||||
|
* for (let i = 0; i < size; i++) {
|
||||||
|
* result.push(randomByte())
|
||||||
|
* }
|
||||||
|
* return result
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* format(random, "abcdef", 5) //=> "fbaef"
|
||||||
|
*
|
||||||
|
* @name format
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
var format = function (random, alphabet, size) {
|
||||||
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
||||||
var step = -~(1.6 * mask * size / alphabet.length);
|
var step = Math.ceil(1.6 * mask * size / alphabet.length);
|
||||||
var id = '';
|
size = +size;
|
||||||
|
|
||||||
|
var id = '';
|
||||||
while (true) {
|
while (true) {
|
||||||
var i = step;
|
var bytes = random(step);
|
||||||
var bytes = random(i);
|
for (var i = 0; i < step; i++) {
|
||||||
while (i--) {
|
var byte = bytes[i] & mask;
|
||||||
id += alphabet[bytes[i] & mask] || '';
|
if (alphabet[byte]) {
|
||||||
if (id.length === +size) return id
|
id += alphabet[byte];
|
||||||
|
if (id.length === size) return id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1371,7 +1454,7 @@ var app = (function (exports) {
|
||||||
var str = '';
|
var str = '';
|
||||||
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
str = str + format_browser(randomByteBrowser, alphabet_1.get(), 1);
|
str = str + format(randomByteBrowser, alphabet_1.get(), 1);
|
||||||
done = number < (Math.pow(16, loopCounter + 1 ) );
|
done = number < (Math.pow(16, loopCounter + 1 ) );
|
||||||
loopCounter++;
|
loopCounter++;
|
||||||
}
|
}
|
||||||
|
@ -21567,8 +21650,8 @@ var app = (function (exports) {
|
||||||
return node
|
return node
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCoreApi = (appDefinition, user) => {
|
const createCoreApi = (backendDefinition, user) => {
|
||||||
const app = createCoreApp(appDefinition, user);
|
const app = createCoreApp(backendDefinition, user);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recordApi: {
|
recordApi: {
|
||||||
|
@ -22186,10 +22269,46 @@ var app = (function (exports) {
|
||||||
parentNode: null,
|
parentNode: null,
|
||||||
children: [],
|
children: [],
|
||||||
component: null,
|
component: null,
|
||||||
unsubscribe: () => { },
|
unsubscribe: () => {},
|
||||||
|
get destroy() {
|
||||||
|
const node = this;
|
||||||
|
return () => {
|
||||||
|
if (node.unsubscribe) node.unsubscribe();
|
||||||
|
if (node.component && node.component.$destroy) node.component.$destroy();
|
||||||
|
if (node.children) {
|
||||||
|
for (let child of node.children) {
|
||||||
|
child.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const _initialiseChildren = initialiseOpts => (
|
const screenSlotComponent = window => {
|
||||||
|
return function(opts) {
|
||||||
|
const node = window.document.createElement("DIV");
|
||||||
|
const $set = props => {
|
||||||
|
props._bb.hydrateChildren(props._children, node);
|
||||||
|
};
|
||||||
|
const $destroy = () => {
|
||||||
|
if (opts.target && node) opts.target.removeChild(node);
|
||||||
|
};
|
||||||
|
this.$set = $set;
|
||||||
|
this.$destroy = $destroy;
|
||||||
|
opts.target.appendChild(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const builtinLibName = "##builtin";
|
||||||
|
|
||||||
|
const isScreenSlot = componentName =>
|
||||||
|
componentName === "##builtin/screenslot";
|
||||||
|
|
||||||
|
const builtins = window => ({
|
||||||
|
screenslot: screenSlotComponent(window),
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialiseChildren = initialiseOpts => (
|
||||||
childrenProps,
|
childrenProps,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor = null
|
anchor = null
|
||||||
|
@ -22201,14 +22320,13 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
treeNode,
|
treeNode,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
document,
|
|
||||||
hydrate,
|
hydrate,
|
||||||
|
onScreenSlotRendered,
|
||||||
} = initialiseOpts;
|
} = initialiseOpts;
|
||||||
|
|
||||||
for (let childNode of treeNode.children) {
|
for (let childNode of treeNode.children) {
|
||||||
if (childNode.unsubscribe) childNode.unsubscribe();
|
childNode.destroy();
|
||||||
if (childNode.component) childNode.component.$destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hydrate) {
|
if (hydrate) {
|
||||||
|
@ -22229,7 +22347,7 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
childProps,
|
childProps,
|
||||||
coreApi,
|
coreApi,
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
);
|
);
|
||||||
|
|
||||||
const componentConstructor = componentLibraries[libName][componentName];
|
const componentConstructor = componentLibraries[libName][componentName];
|
||||||
|
@ -22245,6 +22363,15 @@ var app = (function (exports) {
|
||||||
bb,
|
bb,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
onScreenSlotRendered &&
|
||||||
|
isScreenSlot(childProps._component) &&
|
||||||
|
renderedComponentsThisIteration.length > 0
|
||||||
|
) {
|
||||||
|
// assuming there is only ever one screen slot
|
||||||
|
onScreenSlotRendered(renderedComponentsThisIteration[0]);
|
||||||
|
}
|
||||||
|
|
||||||
for (let comp of renderedComponentsThisIteration) {
|
for (let comp of renderedComponentsThisIteration) {
|
||||||
comp.unsubscribe = bind(comp.component);
|
comp.unsubscribe = bind(comp.component);
|
||||||
renderedComponents.push(comp);
|
renderedComponents.push(comp);
|
||||||
|
@ -22265,29 +22392,123 @@ var app = (function (exports) {
|
||||||
return { libName, componentName }
|
return { libName, componentName }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function regexparam (str, loose) {
|
||||||
|
if (str instanceof RegExp) return { keys:false, pattern:str };
|
||||||
|
var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/');
|
||||||
|
arr[0] || arr.shift();
|
||||||
|
|
||||||
|
while (tmp = arr.shift()) {
|
||||||
|
c = tmp[0];
|
||||||
|
if (c === '*') {
|
||||||
|
keys.push('wild');
|
||||||
|
pattern += '/(.*)';
|
||||||
|
} else if (c === ':') {
|
||||||
|
o = tmp.indexOf('?', 1);
|
||||||
|
ext = tmp.indexOf('.', 1);
|
||||||
|
keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) );
|
||||||
|
pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)';
|
||||||
|
if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext);
|
||||||
|
} else {
|
||||||
|
pattern += '/' + tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
keys: keys,
|
||||||
|
pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const screenRouter = (screens, onScreenSelected) => {
|
||||||
|
const routes = screens.map(s => s.route);
|
||||||
|
let fallback = routes.findIndex(([p]) => p === "*");
|
||||||
|
if (fallback < 0) fallback = 0;
|
||||||
|
|
||||||
|
let current;
|
||||||
|
|
||||||
|
function route(url) {
|
||||||
|
const _url = url.state || url;
|
||||||
|
current = routes.findIndex(
|
||||||
|
p => p !== "*" && new RegExp("^" + p + "$").test(_url)
|
||||||
|
);
|
||||||
|
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (current === -1) {
|
||||||
|
routes.forEach(([p], i) => {
|
||||||
|
const pm = regexparam(p);
|
||||||
|
const matches = pm.pattern.exec(_url);
|
||||||
|
|
||||||
|
if (!matches) return
|
||||||
|
|
||||||
|
let j = 0;
|
||||||
|
while (j < pm.keys.length) {
|
||||||
|
params[pm.keys[j]] = matches[++j] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const storeInitial = {};
|
||||||
|
const store = writable(storeInitial);
|
||||||
|
|
||||||
|
if (current !== -1) {
|
||||||
|
onScreenSelected(screens[current], store, _url);
|
||||||
|
} else if (fallback) {
|
||||||
|
onScreenSelected(screens[fallback], store, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
!url.state && history.pushState(_url, null, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function click(e) {
|
||||||
|
const x = e.target.closest("a");
|
||||||
|
const y = x && x.getAttribute("href");
|
||||||
|
|
||||||
|
if (
|
||||||
|
e.ctrlKey ||
|
||||||
|
e.metaKey ||
|
||||||
|
e.altKey ||
|
||||||
|
e.shiftKey ||
|
||||||
|
e.button ||
|
||||||
|
e.defaultPrevented
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!y || x.target || x.host !== location.host) return
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
route(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("popstate", route);
|
||||||
|
addEventListener("pushstate", route);
|
||||||
|
addEventListener("click", click);
|
||||||
|
|
||||||
|
return route
|
||||||
|
};
|
||||||
|
|
||||||
const createApp = (
|
const createApp = (
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions
|
uiFunctions,
|
||||||
|
screens
|
||||||
) => {
|
) => {
|
||||||
const coreApi = createCoreApi(appDefinition, user);
|
const coreApi = createCoreApi(backendDefinition, user);
|
||||||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
||||||
appDefinition.hierarchy
|
backendDefinition.hierarchy
|
||||||
);
|
);
|
||||||
const store = writable({
|
const pageStore = writable({
|
||||||
_bbuser: user,
|
_bbuser: user,
|
||||||
});
|
});
|
||||||
|
|
||||||
let globalState = null;
|
|
||||||
store.subscribe(s => {
|
|
||||||
globalState = s;
|
|
||||||
});
|
|
||||||
|
|
||||||
const relativeUrl = url =>
|
const relativeUrl = url =>
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
? appDefinition.appRootPath + "/" + trimSlash(url)
|
? frontendDefinition.appRootPath + "/" + trimSlash(url)
|
||||||
: url;
|
: url;
|
||||||
|
|
||||||
const apiCall = method => (url, body) =>
|
const apiCall = method => (url, body) =>
|
||||||
|
@ -22313,27 +22534,60 @@ var app = (function (exports) {
|
||||||
if (isFunction(event)) event(context);
|
if (isFunction(event)) event(context);
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialiseChildrenParams = (hydrate, treeNode) => ({
|
let routeTo;
|
||||||
bb,
|
let currentScreenStore;
|
||||||
|
let currentScreenUbsubscribe;
|
||||||
|
let currentUrl;
|
||||||
|
|
||||||
|
const onScreenSlotRendered = screenSlotNode => {
|
||||||
|
const onScreenSelected = (screen, store, url) => {
|
||||||
|
const { getInitialiseParams, unsubscribe } = initialiseChildrenParams(
|
||||||
|
store
|
||||||
|
);
|
||||||
|
const initialiseChildParams = getInitialiseParams(true, screenSlotNode);
|
||||||
|
initialiseChildren(initialiseChildParams)(
|
||||||
|
[screen.props],
|
||||||
|
screenSlotNode.rootElement
|
||||||
|
);
|
||||||
|
if (currentScreenUbsubscribe) currentScreenUbsubscribe();
|
||||||
|
currentScreenUbsubscribe = unsubscribe;
|
||||||
|
currentScreenStore = store;
|
||||||
|
currentUrl = url;
|
||||||
|
};
|
||||||
|
|
||||||
|
routeTo = screenRouter(screens, onScreenSelected);
|
||||||
|
routeTo(currentUrl || window.location.pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialiseChildrenParams = store => {
|
||||||
|
let currentState = null;
|
||||||
|
const unsubscribe = store.subscribe(s => {
|
||||||
|
currentState = s;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getInitialiseParams = (hydrate, treeNode) => ({
|
||||||
|
bb: getBbClientApi,
|
||||||
coreApi,
|
coreApi,
|
||||||
store,
|
store,
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
hydrate,
|
hydrate,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
treeNode,
|
treeNode,
|
||||||
|
onScreenSlotRendered,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bb = (treeNode, componentProps) => ({
|
const getBbClientApi = (treeNode, componentProps) => {
|
||||||
hydrateChildren: _initialiseChildren(
|
return {
|
||||||
initialiseChildrenParams(true, treeNode)
|
hydrateChildren: initialiseChildren(
|
||||||
|
getInitialiseParams(true, treeNode)
|
||||||
),
|
),
|
||||||
appendChildren: _initialiseChildren(
|
appendChildren: initialiseChildren(
|
||||||
initialiseChildrenParams(false, treeNode)
|
getInitialiseParams(false, treeNode)
|
||||||
),
|
),
|
||||||
insertChildren: (props, htmlElement, anchor) =>
|
insertChildren: (props, htmlElement, anchor) =>
|
||||||
_initialiseChildren(initialiseChildrenParams(false, treeNode))(
|
initialiseChildren(getInitialiseParams(false, treeNode))(
|
||||||
props,
|
props,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor
|
anchor
|
||||||
|
@ -22345,26 +22599,50 @@ var app = (function (exports) {
|
||||||
setStateFromBinding(store, binding, value),
|
setStateFromBinding(store, binding, value),
|
||||||
setState: (path, value) => setState(store, path, value),
|
setState: (path, value) => setState(store, path, value),
|
||||||
getStateOrValue: (prop, currentContext) =>
|
getStateOrValue: (prop, currentContext) =>
|
||||||
getStateOrValue(globalState, prop, currentContext),
|
getStateOrValue(currentState, prop, currentContext),
|
||||||
store,
|
store,
|
||||||
relativeUrl,
|
relativeUrl,
|
||||||
api,
|
api,
|
||||||
isBound,
|
isBound,
|
||||||
parent,
|
parent,
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
return { getInitialiseParams, unsubscribe }
|
||||||
|
};
|
||||||
|
|
||||||
return bb(createTreeNode())
|
let rootTreeNode;
|
||||||
|
|
||||||
|
const initialisePage = (page, target, urlPath) => {
|
||||||
|
currentUrl = urlPath;
|
||||||
|
|
||||||
|
rootTreeNode = createTreeNode();
|
||||||
|
const { getInitialiseParams } = initialiseChildrenParams(pageStore);
|
||||||
|
const initChildParams = getInitialiseParams(true, rootTreeNode);
|
||||||
|
|
||||||
|
initialiseChildren(initChildParams)([page.props], target);
|
||||||
|
|
||||||
|
return rootTreeNode
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
initialisePage,
|
||||||
|
screenStore: () => currentScreenStore,
|
||||||
|
pageStore: () => pageStore,
|
||||||
|
routeTo: () => routeTo,
|
||||||
|
rootNode: () => rootTreeNode,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadBudibase = async ({
|
const loadBudibase = async ({
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
props,
|
page,
|
||||||
|
screens,
|
||||||
window,
|
window,
|
||||||
localStorage,
|
localStorage,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
}) => {
|
}) => {
|
||||||
const appDefinition = window["##BUDIBASE_APPDEFINITION##"];
|
const backendDefinition = window["##BUDIBASE_BACKEND_DEFINITION##"];
|
||||||
const uiFunctionsFromWindow = window["##BUDIBASE_APPDEFINITION##"];
|
const frontendDefinition = window["##BUDIBASE_FRONTEND_DEFINITION##"];
|
||||||
|
const uiFunctionsFromWindow = window["##BUDIBASE_FRONTEND_FUNCTIONS##"];
|
||||||
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
||||||
|
|
||||||
const userFromStorage = localStorage.getItem("budibase:user");
|
const userFromStorage = localStorage.getItem("budibase:user");
|
||||||
|
@ -22378,35 +22656,54 @@ var app = (function (exports) {
|
||||||
temp: false,
|
temp: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!componentLibraries) {
|
|
||||||
const rootPath =
|
const rootPath =
|
||||||
appDefinition.appRootPath === ""
|
frontendDefinition.appRootPath === ""
|
||||||
? ""
|
? ""
|
||||||
: "/" + trimSlash(appDefinition.appRootPath);
|
: "/" + trimSlash(frontendDefinition.appRootPath);
|
||||||
|
|
||||||
|
if (!componentLibraries) {
|
||||||
|
|
||||||
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
||||||
componentLibraries = {};
|
componentLibraries = {};
|
||||||
|
|
||||||
for (let lib of appDefinition.componentLibraries) {
|
for (let lib of frontendDefinition.componentLibraries) {
|
||||||
componentLibraries[lib.libName] = await import(
|
componentLibraries[lib.libName] = await import(
|
||||||
componentLibraryUrl(lib.importPath)
|
componentLibraryUrl(lib.importPath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props) {
|
componentLibraries[builtinLibName] = builtins(window);
|
||||||
props = appDefinition.props;
|
|
||||||
|
if (!page) {
|
||||||
|
page = frontendDefinition.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = createApp(
|
if (!screens) {
|
||||||
|
screens = frontendDefinition.screens;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { initialisePage, screenStore, pageStore, routeTo, rootNode } = createApp(
|
||||||
window.document,
|
window.document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions || {}
|
uiFunctions || {},
|
||||||
|
screens
|
||||||
);
|
);
|
||||||
app.hydrateChildren([props], window.document.body);
|
|
||||||
|
|
||||||
return app
|
const route = window.location
|
||||||
|
? window.location.pathname.replace(rootPath, "")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
rootNode: initialisePage(page, window.document.body, route),
|
||||||
|
screenStore,
|
||||||
|
pageStore,
|
||||||
|
routeTo,
|
||||||
|
rootNode
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -17,7 +17,8 @@
|
||||||
<link rel='stylesheet' href='https://css-r-us.com/myawesomestyles.css'>
|
<link rel='stylesheet' href='https://css-r-us.com/myawesomestyles.css'>
|
||||||
<link rel='stylesheet' href='///local.css'>
|
<link rel='stylesheet' href='///local.css'>
|
||||||
|
|
||||||
<script src='/clientAppDefinition.js'></script>
|
<script src='/_master/clientFrontendDefinition.js'></script>
|
||||||
|
<script src='/_master/clientBackendDefinition.js'></script>
|
||||||
<script src='/budibase-client.js'></script>
|
<script src='/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
loadBudibase();
|
loadBudibase();
|
||||||
|
|
|
@ -2,9 +2,61 @@ var app = (function (exports) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function noop() { }
|
function noop() { }
|
||||||
|
function run(fn) {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
function run_all(fns) {
|
||||||
|
fns.forEach(run);
|
||||||
|
}
|
||||||
function safe_not_equal(a, b) {
|
function safe_not_equal(a, b) {
|
||||||
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
||||||
}
|
}
|
||||||
|
function destroy_component(component, detaching) {
|
||||||
|
if (component.$$.fragment) {
|
||||||
|
run_all(component.$$.on_destroy);
|
||||||
|
component.$$.fragment.d(detaching);
|
||||||
|
// TODO null out other refs, including component.$$ (but need to
|
||||||
|
// preserve final state?)
|
||||||
|
component.$$.on_destroy = component.$$.fragment = null;
|
||||||
|
component.$$.ctx = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let SvelteElement;
|
||||||
|
if (typeof HTMLElement !== 'undefined') {
|
||||||
|
SvelteElement = class extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
for (const key in this.$$.slotted) {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
this.appendChild(this.$$.slotted[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributeChangedCallback(attr, _oldValue, newValue) {
|
||||||
|
this[attr] = newValue;
|
||||||
|
}
|
||||||
|
$destroy() {
|
||||||
|
destroy_component(this, 1);
|
||||||
|
this.$destroy = noop;
|
||||||
|
}
|
||||||
|
$on(type, callback) {
|
||||||
|
// TODO should this delegate to addEventListener?
|
||||||
|
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
|
||||||
|
callbacks.push(callback);
|
||||||
|
return () => {
|
||||||
|
const index = callbacks.indexOf(callback);
|
||||||
|
if (index !== -1)
|
||||||
|
callbacks.splice(index, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$set() {
|
||||||
|
// overridden by instance, if it has props
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const subscriber_queue = [];
|
const subscriber_queue = [];
|
||||||
/**
|
/**
|
||||||
|
@ -58,13 +110,13 @@ var app = (function (exports) {
|
||||||
return { set, update, subscribe };
|
return { set, update, subscribe };
|
||||||
}
|
}
|
||||||
|
|
||||||
const createCoreApp = (appDefinition, user) => {
|
const createCoreApp = (backendDefinition, user) => {
|
||||||
const app = {
|
const app = {
|
||||||
datastore: null,
|
datastore: null,
|
||||||
crypto: null,
|
crypto: null,
|
||||||
publish: () => {},
|
publish: () => {},
|
||||||
hierarchy: appDefinition.hierarchy,
|
hierarchy: backendDefinition.hierarchy,
|
||||||
actions: appDefinition.actions,
|
actions: backendDefinition.actions,
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1349,17 +1401,48 @@ var app = (function (exports) {
|
||||||
|
|
||||||
var randomByteBrowser = randomByte;
|
var randomByteBrowser = randomByte;
|
||||||
|
|
||||||
var format_browser = function (random, alphabet, size) {
|
/**
|
||||||
|
* Secure random string generator with custom alphabet.
|
||||||
|
*
|
||||||
|
* Alphabet must contain 256 symbols or less. Otherwise, the generator
|
||||||
|
* will not be secure.
|
||||||
|
*
|
||||||
|
* @param {generator} random The random bytes generator.
|
||||||
|
* @param {string} alphabet Symbols to be used in new random string.
|
||||||
|
* @param {size} size The number of symbols in new random string.
|
||||||
|
*
|
||||||
|
* @return {string} Random string.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const format = require('nanoid/format')
|
||||||
|
*
|
||||||
|
* function random (size) {
|
||||||
|
* const result = []
|
||||||
|
* for (let i = 0; i < size; i++) {
|
||||||
|
* result.push(randomByte())
|
||||||
|
* }
|
||||||
|
* return result
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* format(random, "abcdef", 5) //=> "fbaef"
|
||||||
|
*
|
||||||
|
* @name format
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
var format = function (random, alphabet, size) {
|
||||||
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
||||||
var step = -~(1.6 * mask * size / alphabet.length);
|
var step = Math.ceil(1.6 * mask * size / alphabet.length);
|
||||||
var id = '';
|
size = +size;
|
||||||
|
|
||||||
|
var id = '';
|
||||||
while (true) {
|
while (true) {
|
||||||
var i = step;
|
var bytes = random(step);
|
||||||
var bytes = random(i);
|
for (var i = 0; i < step; i++) {
|
||||||
while (i--) {
|
var byte = bytes[i] & mask;
|
||||||
id += alphabet[bytes[i] & mask] || '';
|
if (alphabet[byte]) {
|
||||||
if (id.length === +size) return id
|
id += alphabet[byte];
|
||||||
|
if (id.length === size) return id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1371,7 +1454,7 @@ var app = (function (exports) {
|
||||||
var str = '';
|
var str = '';
|
||||||
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
str = str + format_browser(randomByteBrowser, alphabet_1.get(), 1);
|
str = str + format(randomByteBrowser, alphabet_1.get(), 1);
|
||||||
done = number < (Math.pow(16, loopCounter + 1 ) );
|
done = number < (Math.pow(16, loopCounter + 1 ) );
|
||||||
loopCounter++;
|
loopCounter++;
|
||||||
}
|
}
|
||||||
|
@ -21567,8 +21650,8 @@ var app = (function (exports) {
|
||||||
return node
|
return node
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCoreApi = (appDefinition, user) => {
|
const createCoreApi = (backendDefinition, user) => {
|
||||||
const app = createCoreApp(appDefinition, user);
|
const app = createCoreApp(backendDefinition, user);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recordApi: {
|
recordApi: {
|
||||||
|
@ -22186,10 +22269,46 @@ var app = (function (exports) {
|
||||||
parentNode: null,
|
parentNode: null,
|
||||||
children: [],
|
children: [],
|
||||||
component: null,
|
component: null,
|
||||||
unsubscribe: () => { },
|
unsubscribe: () => {},
|
||||||
|
get destroy() {
|
||||||
|
const node = this;
|
||||||
|
return () => {
|
||||||
|
if (node.unsubscribe) node.unsubscribe();
|
||||||
|
if (node.component && node.component.$destroy) node.component.$destroy();
|
||||||
|
if (node.children) {
|
||||||
|
for (let child of node.children) {
|
||||||
|
child.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const _initialiseChildren = initialiseOpts => (
|
const screenSlotComponent = window => {
|
||||||
|
return function(opts) {
|
||||||
|
const node = window.document.createElement("DIV");
|
||||||
|
const $set = props => {
|
||||||
|
props._bb.hydrateChildren(props._children, node);
|
||||||
|
};
|
||||||
|
const $destroy = () => {
|
||||||
|
if (opts.target && node) opts.target.removeChild(node);
|
||||||
|
};
|
||||||
|
this.$set = $set;
|
||||||
|
this.$destroy = $destroy;
|
||||||
|
opts.target.appendChild(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const builtinLibName = "##builtin";
|
||||||
|
|
||||||
|
const isScreenSlot = componentName =>
|
||||||
|
componentName === "##builtin/screenslot";
|
||||||
|
|
||||||
|
const builtins = window => ({
|
||||||
|
screenslot: screenSlotComponent(window),
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialiseChildren = initialiseOpts => (
|
||||||
childrenProps,
|
childrenProps,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor = null
|
anchor = null
|
||||||
|
@ -22201,14 +22320,13 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
treeNode,
|
treeNode,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
document,
|
|
||||||
hydrate,
|
hydrate,
|
||||||
|
onScreenSlotRendered,
|
||||||
} = initialiseOpts;
|
} = initialiseOpts;
|
||||||
|
|
||||||
for (let childNode of treeNode.children) {
|
for (let childNode of treeNode.children) {
|
||||||
if (childNode.unsubscribe) childNode.unsubscribe();
|
childNode.destroy();
|
||||||
if (childNode.component) childNode.component.$destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hydrate) {
|
if (hydrate) {
|
||||||
|
@ -22229,7 +22347,7 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
childProps,
|
childProps,
|
||||||
coreApi,
|
coreApi,
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
);
|
);
|
||||||
|
|
||||||
const componentConstructor = componentLibraries[libName][componentName];
|
const componentConstructor = componentLibraries[libName][componentName];
|
||||||
|
@ -22245,6 +22363,15 @@ var app = (function (exports) {
|
||||||
bb,
|
bb,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
onScreenSlotRendered &&
|
||||||
|
isScreenSlot(childProps._component) &&
|
||||||
|
renderedComponentsThisIteration.length > 0
|
||||||
|
) {
|
||||||
|
// assuming there is only ever one screen slot
|
||||||
|
onScreenSlotRendered(renderedComponentsThisIteration[0]);
|
||||||
|
}
|
||||||
|
|
||||||
for (let comp of renderedComponentsThisIteration) {
|
for (let comp of renderedComponentsThisIteration) {
|
||||||
comp.unsubscribe = bind(comp.component);
|
comp.unsubscribe = bind(comp.component);
|
||||||
renderedComponents.push(comp);
|
renderedComponents.push(comp);
|
||||||
|
@ -22265,29 +22392,123 @@ var app = (function (exports) {
|
||||||
return { libName, componentName }
|
return { libName, componentName }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function regexparam (str, loose) {
|
||||||
|
if (str instanceof RegExp) return { keys:false, pattern:str };
|
||||||
|
var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/');
|
||||||
|
arr[0] || arr.shift();
|
||||||
|
|
||||||
|
while (tmp = arr.shift()) {
|
||||||
|
c = tmp[0];
|
||||||
|
if (c === '*') {
|
||||||
|
keys.push('wild');
|
||||||
|
pattern += '/(.*)';
|
||||||
|
} else if (c === ':') {
|
||||||
|
o = tmp.indexOf('?', 1);
|
||||||
|
ext = tmp.indexOf('.', 1);
|
||||||
|
keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) );
|
||||||
|
pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)';
|
||||||
|
if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext);
|
||||||
|
} else {
|
||||||
|
pattern += '/' + tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
keys: keys,
|
||||||
|
pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const screenRouter = (screens, onScreenSelected) => {
|
||||||
|
const routes = screens.map(s => s.route);
|
||||||
|
let fallback = routes.findIndex(([p]) => p === "*");
|
||||||
|
if (fallback < 0) fallback = 0;
|
||||||
|
|
||||||
|
let current;
|
||||||
|
|
||||||
|
function route(url) {
|
||||||
|
const _url = url.state || url;
|
||||||
|
current = routes.findIndex(
|
||||||
|
p => p !== "*" && new RegExp("^" + p + "$").test(_url)
|
||||||
|
);
|
||||||
|
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (current === -1) {
|
||||||
|
routes.forEach(([p], i) => {
|
||||||
|
const pm = regexparam(p);
|
||||||
|
const matches = pm.pattern.exec(_url);
|
||||||
|
|
||||||
|
if (!matches) return
|
||||||
|
|
||||||
|
let j = 0;
|
||||||
|
while (j < pm.keys.length) {
|
||||||
|
params[pm.keys[j]] = matches[++j] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const storeInitial = {};
|
||||||
|
const store = writable(storeInitial);
|
||||||
|
|
||||||
|
if (current !== -1) {
|
||||||
|
onScreenSelected(screens[current], store, _url);
|
||||||
|
} else if (fallback) {
|
||||||
|
onScreenSelected(screens[fallback], store, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
!url.state && history.pushState(_url, null, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function click(e) {
|
||||||
|
const x = e.target.closest("a");
|
||||||
|
const y = x && x.getAttribute("href");
|
||||||
|
|
||||||
|
if (
|
||||||
|
e.ctrlKey ||
|
||||||
|
e.metaKey ||
|
||||||
|
e.altKey ||
|
||||||
|
e.shiftKey ||
|
||||||
|
e.button ||
|
||||||
|
e.defaultPrevented
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!y || x.target || x.host !== location.host) return
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
route(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("popstate", route);
|
||||||
|
addEventListener("pushstate", route);
|
||||||
|
addEventListener("click", click);
|
||||||
|
|
||||||
|
return route
|
||||||
|
};
|
||||||
|
|
||||||
const createApp = (
|
const createApp = (
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions
|
uiFunctions,
|
||||||
|
screens
|
||||||
) => {
|
) => {
|
||||||
const coreApi = createCoreApi(appDefinition, user);
|
const coreApi = createCoreApi(backendDefinition, user);
|
||||||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
||||||
appDefinition.hierarchy
|
backendDefinition.hierarchy
|
||||||
);
|
);
|
||||||
const store = writable({
|
const pageStore = writable({
|
||||||
_bbuser: user,
|
_bbuser: user,
|
||||||
});
|
});
|
||||||
|
|
||||||
let globalState = null;
|
|
||||||
store.subscribe(s => {
|
|
||||||
globalState = s;
|
|
||||||
});
|
|
||||||
|
|
||||||
const relativeUrl = url =>
|
const relativeUrl = url =>
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
? appDefinition.appRootPath + "/" + trimSlash(url)
|
? frontendDefinition.appRootPath + "/" + trimSlash(url)
|
||||||
: url;
|
: url;
|
||||||
|
|
||||||
const apiCall = method => (url, body) =>
|
const apiCall = method => (url, body) =>
|
||||||
|
@ -22313,27 +22534,60 @@ var app = (function (exports) {
|
||||||
if (isFunction(event)) event(context);
|
if (isFunction(event)) event(context);
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialiseChildrenParams = (hydrate, treeNode) => ({
|
let routeTo;
|
||||||
bb,
|
let currentScreenStore;
|
||||||
|
let currentScreenUbsubscribe;
|
||||||
|
let currentUrl;
|
||||||
|
|
||||||
|
const onScreenSlotRendered = screenSlotNode => {
|
||||||
|
const onScreenSelected = (screen, store, url) => {
|
||||||
|
const { getInitialiseParams, unsubscribe } = initialiseChildrenParams(
|
||||||
|
store
|
||||||
|
);
|
||||||
|
const initialiseChildParams = getInitialiseParams(true, screenSlotNode);
|
||||||
|
initialiseChildren(initialiseChildParams)(
|
||||||
|
[screen.props],
|
||||||
|
screenSlotNode.rootElement
|
||||||
|
);
|
||||||
|
if (currentScreenUbsubscribe) currentScreenUbsubscribe();
|
||||||
|
currentScreenUbsubscribe = unsubscribe;
|
||||||
|
currentScreenStore = store;
|
||||||
|
currentUrl = url;
|
||||||
|
};
|
||||||
|
|
||||||
|
routeTo = screenRouter(screens, onScreenSelected);
|
||||||
|
routeTo(currentUrl || window.location.pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialiseChildrenParams = store => {
|
||||||
|
let currentState = null;
|
||||||
|
const unsubscribe = store.subscribe(s => {
|
||||||
|
currentState = s;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getInitialiseParams = (hydrate, treeNode) => ({
|
||||||
|
bb: getBbClientApi,
|
||||||
coreApi,
|
coreApi,
|
||||||
store,
|
store,
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
hydrate,
|
hydrate,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
treeNode,
|
treeNode,
|
||||||
|
onScreenSlotRendered,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bb = (treeNode, componentProps) => ({
|
const getBbClientApi = (treeNode, componentProps) => {
|
||||||
hydrateChildren: _initialiseChildren(
|
return {
|
||||||
initialiseChildrenParams(true, treeNode)
|
hydrateChildren: initialiseChildren(
|
||||||
|
getInitialiseParams(true, treeNode)
|
||||||
),
|
),
|
||||||
appendChildren: _initialiseChildren(
|
appendChildren: initialiseChildren(
|
||||||
initialiseChildrenParams(false, treeNode)
|
getInitialiseParams(false, treeNode)
|
||||||
),
|
),
|
||||||
insertChildren: (props, htmlElement, anchor) =>
|
insertChildren: (props, htmlElement, anchor) =>
|
||||||
_initialiseChildren(initialiseChildrenParams(false, treeNode))(
|
initialiseChildren(getInitialiseParams(false, treeNode))(
|
||||||
props,
|
props,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor
|
anchor
|
||||||
|
@ -22345,26 +22599,50 @@ var app = (function (exports) {
|
||||||
setStateFromBinding(store, binding, value),
|
setStateFromBinding(store, binding, value),
|
||||||
setState: (path, value) => setState(store, path, value),
|
setState: (path, value) => setState(store, path, value),
|
||||||
getStateOrValue: (prop, currentContext) =>
|
getStateOrValue: (prop, currentContext) =>
|
||||||
getStateOrValue(globalState, prop, currentContext),
|
getStateOrValue(currentState, prop, currentContext),
|
||||||
store,
|
store,
|
||||||
relativeUrl,
|
relativeUrl,
|
||||||
api,
|
api,
|
||||||
isBound,
|
isBound,
|
||||||
parent,
|
parent,
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
return { getInitialiseParams, unsubscribe }
|
||||||
|
};
|
||||||
|
|
||||||
return bb(createTreeNode())
|
let rootTreeNode;
|
||||||
|
|
||||||
|
const initialisePage = (page, target, urlPath) => {
|
||||||
|
currentUrl = urlPath;
|
||||||
|
|
||||||
|
rootTreeNode = createTreeNode();
|
||||||
|
const { getInitialiseParams } = initialiseChildrenParams(pageStore);
|
||||||
|
const initChildParams = getInitialiseParams(true, rootTreeNode);
|
||||||
|
|
||||||
|
initialiseChildren(initChildParams)([page.props], target);
|
||||||
|
|
||||||
|
return rootTreeNode
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
initialisePage,
|
||||||
|
screenStore: () => currentScreenStore,
|
||||||
|
pageStore: () => pageStore,
|
||||||
|
routeTo: () => routeTo,
|
||||||
|
rootNode: () => rootTreeNode,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadBudibase = async ({
|
const loadBudibase = async ({
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
props,
|
page,
|
||||||
|
screens,
|
||||||
window,
|
window,
|
||||||
localStorage,
|
localStorage,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
}) => {
|
}) => {
|
||||||
const appDefinition = window["##BUDIBASE_APPDEFINITION##"];
|
const backendDefinition = window["##BUDIBASE_BACKEND_DEFINITION##"];
|
||||||
const uiFunctionsFromWindow = window["##BUDIBASE_APPDEFINITION##"];
|
const frontendDefinition = window["##BUDIBASE_FRONTEND_DEFINITION##"];
|
||||||
|
const uiFunctionsFromWindow = window["##BUDIBASE_FRONTEND_FUNCTIONS##"];
|
||||||
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
||||||
|
|
||||||
const userFromStorage = localStorage.getItem("budibase:user");
|
const userFromStorage = localStorage.getItem("budibase:user");
|
||||||
|
@ -22378,35 +22656,54 @@ var app = (function (exports) {
|
||||||
temp: false,
|
temp: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!componentLibraries) {
|
|
||||||
const rootPath =
|
const rootPath =
|
||||||
appDefinition.appRootPath === ""
|
frontendDefinition.appRootPath === ""
|
||||||
? ""
|
? ""
|
||||||
: "/" + trimSlash(appDefinition.appRootPath);
|
: "/" + trimSlash(frontendDefinition.appRootPath);
|
||||||
|
|
||||||
|
if (!componentLibraries) {
|
||||||
|
|
||||||
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
||||||
componentLibraries = {};
|
componentLibraries = {};
|
||||||
|
|
||||||
for (let lib of appDefinition.componentLibraries) {
|
for (let lib of frontendDefinition.componentLibraries) {
|
||||||
componentLibraries[lib.libName] = await import(
|
componentLibraries[lib.libName] = await import(
|
||||||
componentLibraryUrl(lib.importPath)
|
componentLibraryUrl(lib.importPath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props) {
|
componentLibraries[builtinLibName] = builtins(window);
|
||||||
props = appDefinition.props;
|
|
||||||
|
if (!page) {
|
||||||
|
page = frontendDefinition.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = createApp(
|
if (!screens) {
|
||||||
|
screens = frontendDefinition.screens;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { initialisePage, screenStore, pageStore, routeTo, rootNode } = createApp(
|
||||||
window.document,
|
window.document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions || {}
|
uiFunctions || {},
|
||||||
|
screens
|
||||||
);
|
);
|
||||||
app.hydrateChildren([props], window.document.body);
|
|
||||||
|
|
||||||
return app
|
const route = window.location
|
||||||
|
? window.location.pathname.replace(rootPath, "")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
rootNode: initialisePage(page, window.document.body, route),
|
||||||
|
screenStore,
|
||||||
|
pageStore,
|
||||||
|
routeTo,
|
||||||
|
rootNode
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -16,7 +16,8 @@
|
||||||
|
|
||||||
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'>
|
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'>
|
||||||
|
|
||||||
<script src='/testApp2/clientAppDefinition.js'></script>
|
<script src='/_master/clientFrontendDefinition.js'></script>
|
||||||
|
<script src='/_master/clientBackendDefinition.js'></script>
|
||||||
<script src='/testApp2/budibase-client.js'></script>
|
<script src='/testApp2/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
loadBudibase();
|
loadBudibase();
|
||||||
|
|
|
@ -2,9 +2,61 @@ var app = (function (exports) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function noop() { }
|
function noop() { }
|
||||||
|
function run(fn) {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
function run_all(fns) {
|
||||||
|
fns.forEach(run);
|
||||||
|
}
|
||||||
function safe_not_equal(a, b) {
|
function safe_not_equal(a, b) {
|
||||||
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
|
||||||
}
|
}
|
||||||
|
function destroy_component(component, detaching) {
|
||||||
|
if (component.$$.fragment) {
|
||||||
|
run_all(component.$$.on_destroy);
|
||||||
|
component.$$.fragment.d(detaching);
|
||||||
|
// TODO null out other refs, including component.$$ (but need to
|
||||||
|
// preserve final state?)
|
||||||
|
component.$$.on_destroy = component.$$.fragment = null;
|
||||||
|
component.$$.ctx = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let SvelteElement;
|
||||||
|
if (typeof HTMLElement !== 'undefined') {
|
||||||
|
SvelteElement = class extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.attachShadow({ mode: 'open' });
|
||||||
|
}
|
||||||
|
connectedCallback() {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
for (const key in this.$$.slotted) {
|
||||||
|
// @ts-ignore todo: improve typings
|
||||||
|
this.appendChild(this.$$.slotted[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributeChangedCallback(attr, _oldValue, newValue) {
|
||||||
|
this[attr] = newValue;
|
||||||
|
}
|
||||||
|
$destroy() {
|
||||||
|
destroy_component(this, 1);
|
||||||
|
this.$destroy = noop;
|
||||||
|
}
|
||||||
|
$on(type, callback) {
|
||||||
|
// TODO should this delegate to addEventListener?
|
||||||
|
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
|
||||||
|
callbacks.push(callback);
|
||||||
|
return () => {
|
||||||
|
const index = callbacks.indexOf(callback);
|
||||||
|
if (index !== -1)
|
||||||
|
callbacks.splice(index, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$set() {
|
||||||
|
// overridden by instance, if it has props
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const subscriber_queue = [];
|
const subscriber_queue = [];
|
||||||
/**
|
/**
|
||||||
|
@ -58,13 +110,13 @@ var app = (function (exports) {
|
||||||
return { set, update, subscribe };
|
return { set, update, subscribe };
|
||||||
}
|
}
|
||||||
|
|
||||||
const createCoreApp = (appDefinition, user) => {
|
const createCoreApp = (backendDefinition, user) => {
|
||||||
const app = {
|
const app = {
|
||||||
datastore: null,
|
datastore: null,
|
||||||
crypto: null,
|
crypto: null,
|
||||||
publish: () => {},
|
publish: () => {},
|
||||||
hierarchy: appDefinition.hierarchy,
|
hierarchy: backendDefinition.hierarchy,
|
||||||
actions: appDefinition.actions,
|
actions: backendDefinition.actions,
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1349,17 +1401,48 @@ var app = (function (exports) {
|
||||||
|
|
||||||
var randomByteBrowser = randomByte;
|
var randomByteBrowser = randomByte;
|
||||||
|
|
||||||
var format_browser = function (random, alphabet, size) {
|
/**
|
||||||
|
* Secure random string generator with custom alphabet.
|
||||||
|
*
|
||||||
|
* Alphabet must contain 256 symbols or less. Otherwise, the generator
|
||||||
|
* will not be secure.
|
||||||
|
*
|
||||||
|
* @param {generator} random The random bytes generator.
|
||||||
|
* @param {string} alphabet Symbols to be used in new random string.
|
||||||
|
* @param {size} size The number of symbols in new random string.
|
||||||
|
*
|
||||||
|
* @return {string} Random string.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const format = require('nanoid/format')
|
||||||
|
*
|
||||||
|
* function random (size) {
|
||||||
|
* const result = []
|
||||||
|
* for (let i = 0; i < size; i++) {
|
||||||
|
* result.push(randomByte())
|
||||||
|
* }
|
||||||
|
* return result
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* format(random, "abcdef", 5) //=> "fbaef"
|
||||||
|
*
|
||||||
|
* @name format
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
var format = function (random, alphabet, size) {
|
||||||
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1;
|
||||||
var step = -~(1.6 * mask * size / alphabet.length);
|
var step = Math.ceil(1.6 * mask * size / alphabet.length);
|
||||||
var id = '';
|
size = +size;
|
||||||
|
|
||||||
|
var id = '';
|
||||||
while (true) {
|
while (true) {
|
||||||
var i = step;
|
var bytes = random(step);
|
||||||
var bytes = random(i);
|
for (var i = 0; i < step; i++) {
|
||||||
while (i--) {
|
var byte = bytes[i] & mask;
|
||||||
id += alphabet[bytes[i] & mask] || '';
|
if (alphabet[byte]) {
|
||||||
if (id.length === +size) return id
|
id += alphabet[byte];
|
||||||
|
if (id.length === size) return id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1371,7 +1454,7 @@ var app = (function (exports) {
|
||||||
var str = '';
|
var str = '';
|
||||||
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
str = str + format_browser(randomByteBrowser, alphabet_1.get(), 1);
|
str = str + format(randomByteBrowser, alphabet_1.get(), 1);
|
||||||
done = number < (Math.pow(16, loopCounter + 1 ) );
|
done = number < (Math.pow(16, loopCounter + 1 ) );
|
||||||
loopCounter++;
|
loopCounter++;
|
||||||
}
|
}
|
||||||
|
@ -21567,8 +21650,8 @@ var app = (function (exports) {
|
||||||
return node
|
return node
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCoreApi = (appDefinition, user) => {
|
const createCoreApi = (backendDefinition, user) => {
|
||||||
const app = createCoreApp(appDefinition, user);
|
const app = createCoreApp(backendDefinition, user);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recordApi: {
|
recordApi: {
|
||||||
|
@ -22186,10 +22269,46 @@ var app = (function (exports) {
|
||||||
parentNode: null,
|
parentNode: null,
|
||||||
children: [],
|
children: [],
|
||||||
component: null,
|
component: null,
|
||||||
unsubscribe: () => { },
|
unsubscribe: () => {},
|
||||||
|
get destroy() {
|
||||||
|
const node = this;
|
||||||
|
return () => {
|
||||||
|
if (node.unsubscribe) node.unsubscribe();
|
||||||
|
if (node.component && node.component.$destroy) node.component.$destroy();
|
||||||
|
if (node.children) {
|
||||||
|
for (let child of node.children) {
|
||||||
|
child.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const _initialiseChildren = initialiseOpts => (
|
const screenSlotComponent = window => {
|
||||||
|
return function(opts) {
|
||||||
|
const node = window.document.createElement("DIV");
|
||||||
|
const $set = props => {
|
||||||
|
props._bb.hydrateChildren(props._children, node);
|
||||||
|
};
|
||||||
|
const $destroy = () => {
|
||||||
|
if (opts.target && node) opts.target.removeChild(node);
|
||||||
|
};
|
||||||
|
this.$set = $set;
|
||||||
|
this.$destroy = $destroy;
|
||||||
|
opts.target.appendChild(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const builtinLibName = "##builtin";
|
||||||
|
|
||||||
|
const isScreenSlot = componentName =>
|
||||||
|
componentName === "##builtin/screenslot";
|
||||||
|
|
||||||
|
const builtins = window => ({
|
||||||
|
screenslot: screenSlotComponent(window),
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialiseChildren = initialiseOpts => (
|
||||||
childrenProps,
|
childrenProps,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor = null
|
anchor = null
|
||||||
|
@ -22201,14 +22320,13 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
treeNode,
|
treeNode,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
document,
|
|
||||||
hydrate,
|
hydrate,
|
||||||
|
onScreenSlotRendered,
|
||||||
} = initialiseOpts;
|
} = initialiseOpts;
|
||||||
|
|
||||||
for (let childNode of treeNode.children) {
|
for (let childNode of treeNode.children) {
|
||||||
if (childNode.unsubscribe) childNode.unsubscribe();
|
childNode.destroy();
|
||||||
if (childNode.component) childNode.component.$destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hydrate) {
|
if (hydrate) {
|
||||||
|
@ -22229,7 +22347,7 @@ var app = (function (exports) {
|
||||||
store,
|
store,
|
||||||
childProps,
|
childProps,
|
||||||
coreApi,
|
coreApi,
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
);
|
);
|
||||||
|
|
||||||
const componentConstructor = componentLibraries[libName][componentName];
|
const componentConstructor = componentLibraries[libName][componentName];
|
||||||
|
@ -22245,6 +22363,15 @@ var app = (function (exports) {
|
||||||
bb,
|
bb,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
onScreenSlotRendered &&
|
||||||
|
isScreenSlot(childProps._component) &&
|
||||||
|
renderedComponentsThisIteration.length > 0
|
||||||
|
) {
|
||||||
|
// assuming there is only ever one screen slot
|
||||||
|
onScreenSlotRendered(renderedComponentsThisIteration[0]);
|
||||||
|
}
|
||||||
|
|
||||||
for (let comp of renderedComponentsThisIteration) {
|
for (let comp of renderedComponentsThisIteration) {
|
||||||
comp.unsubscribe = bind(comp.component);
|
comp.unsubscribe = bind(comp.component);
|
||||||
renderedComponents.push(comp);
|
renderedComponents.push(comp);
|
||||||
|
@ -22265,29 +22392,123 @@ var app = (function (exports) {
|
||||||
return { libName, componentName }
|
return { libName, componentName }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function regexparam (str, loose) {
|
||||||
|
if (str instanceof RegExp) return { keys:false, pattern:str };
|
||||||
|
var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/');
|
||||||
|
arr[0] || arr.shift();
|
||||||
|
|
||||||
|
while (tmp = arr.shift()) {
|
||||||
|
c = tmp[0];
|
||||||
|
if (c === '*') {
|
||||||
|
keys.push('wild');
|
||||||
|
pattern += '/(.*)';
|
||||||
|
} else if (c === ':') {
|
||||||
|
o = tmp.indexOf('?', 1);
|
||||||
|
ext = tmp.indexOf('.', 1);
|
||||||
|
keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) );
|
||||||
|
pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)';
|
||||||
|
if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext);
|
||||||
|
} else {
|
||||||
|
pattern += '/' + tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
keys: keys,
|
||||||
|
pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const screenRouter = (screens, onScreenSelected) => {
|
||||||
|
const routes = screens.map(s => s.route);
|
||||||
|
let fallback = routes.findIndex(([p]) => p === "*");
|
||||||
|
if (fallback < 0) fallback = 0;
|
||||||
|
|
||||||
|
let current;
|
||||||
|
|
||||||
|
function route(url) {
|
||||||
|
const _url = url.state || url;
|
||||||
|
current = routes.findIndex(
|
||||||
|
p => p !== "*" && new RegExp("^" + p + "$").test(_url)
|
||||||
|
);
|
||||||
|
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (current === -1) {
|
||||||
|
routes.forEach(([p], i) => {
|
||||||
|
const pm = regexparam(p);
|
||||||
|
const matches = pm.pattern.exec(_url);
|
||||||
|
|
||||||
|
if (!matches) return
|
||||||
|
|
||||||
|
let j = 0;
|
||||||
|
while (j < pm.keys.length) {
|
||||||
|
params[pm.keys[j]] = matches[++j] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const storeInitial = {};
|
||||||
|
const store = writable(storeInitial);
|
||||||
|
|
||||||
|
if (current !== -1) {
|
||||||
|
onScreenSelected(screens[current], store, _url);
|
||||||
|
} else if (fallback) {
|
||||||
|
onScreenSelected(screens[fallback], store, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
!url.state && history.pushState(_url, null, _url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function click(e) {
|
||||||
|
const x = e.target.closest("a");
|
||||||
|
const y = x && x.getAttribute("href");
|
||||||
|
|
||||||
|
if (
|
||||||
|
e.ctrlKey ||
|
||||||
|
e.metaKey ||
|
||||||
|
e.altKey ||
|
||||||
|
e.shiftKey ||
|
||||||
|
e.button ||
|
||||||
|
e.defaultPrevented
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!y || x.target || x.host !== location.host) return
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
route(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("popstate", route);
|
||||||
|
addEventListener("pushstate", route);
|
||||||
|
addEventListener("click", click);
|
||||||
|
|
||||||
|
return route
|
||||||
|
};
|
||||||
|
|
||||||
const createApp = (
|
const createApp = (
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions
|
uiFunctions,
|
||||||
|
screens
|
||||||
) => {
|
) => {
|
||||||
const coreApi = createCoreApi(appDefinition, user);
|
const coreApi = createCoreApi(backendDefinition, user);
|
||||||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
||||||
appDefinition.hierarchy
|
backendDefinition.hierarchy
|
||||||
);
|
);
|
||||||
const store = writable({
|
const pageStore = writable({
|
||||||
_bbuser: user,
|
_bbuser: user,
|
||||||
});
|
});
|
||||||
|
|
||||||
let globalState = null;
|
|
||||||
store.subscribe(s => {
|
|
||||||
globalState = s;
|
|
||||||
});
|
|
||||||
|
|
||||||
const relativeUrl = url =>
|
const relativeUrl = url =>
|
||||||
appDefinition.appRootPath
|
frontendDefinition.appRootPath
|
||||||
? appDefinition.appRootPath + "/" + trimSlash(url)
|
? frontendDefinition.appRootPath + "/" + trimSlash(url)
|
||||||
: url;
|
: url;
|
||||||
|
|
||||||
const apiCall = method => (url, body) =>
|
const apiCall = method => (url, body) =>
|
||||||
|
@ -22313,27 +22534,60 @@ var app = (function (exports) {
|
||||||
if (isFunction(event)) event(context);
|
if (isFunction(event)) event(context);
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialiseChildrenParams = (hydrate, treeNode) => ({
|
let routeTo;
|
||||||
bb,
|
let currentScreenStore;
|
||||||
|
let currentScreenUbsubscribe;
|
||||||
|
let currentUrl;
|
||||||
|
|
||||||
|
const onScreenSlotRendered = screenSlotNode => {
|
||||||
|
const onScreenSelected = (screen, store, url) => {
|
||||||
|
const { getInitialiseParams, unsubscribe } = initialiseChildrenParams(
|
||||||
|
store
|
||||||
|
);
|
||||||
|
const initialiseChildParams = getInitialiseParams(true, screenSlotNode);
|
||||||
|
initialiseChildren(initialiseChildParams)(
|
||||||
|
[screen.props],
|
||||||
|
screenSlotNode.rootElement
|
||||||
|
);
|
||||||
|
if (currentScreenUbsubscribe) currentScreenUbsubscribe();
|
||||||
|
currentScreenUbsubscribe = unsubscribe;
|
||||||
|
currentScreenStore = store;
|
||||||
|
currentUrl = url;
|
||||||
|
};
|
||||||
|
|
||||||
|
routeTo = screenRouter(screens, onScreenSelected);
|
||||||
|
routeTo(currentUrl || window.location.pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialiseChildrenParams = store => {
|
||||||
|
let currentState = null;
|
||||||
|
const unsubscribe = store.subscribe(s => {
|
||||||
|
currentState = s;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getInitialiseParams = (hydrate, treeNode) => ({
|
||||||
|
bb: getBbClientApi,
|
||||||
coreApi,
|
coreApi,
|
||||||
store,
|
store,
|
||||||
document,
|
document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
hydrate,
|
hydrate,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
treeNode,
|
treeNode,
|
||||||
|
onScreenSlotRendered,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bb = (treeNode, componentProps) => ({
|
const getBbClientApi = (treeNode, componentProps) => {
|
||||||
hydrateChildren: _initialiseChildren(
|
return {
|
||||||
initialiseChildrenParams(true, treeNode)
|
hydrateChildren: initialiseChildren(
|
||||||
|
getInitialiseParams(true, treeNode)
|
||||||
),
|
),
|
||||||
appendChildren: _initialiseChildren(
|
appendChildren: initialiseChildren(
|
||||||
initialiseChildrenParams(false, treeNode)
|
getInitialiseParams(false, treeNode)
|
||||||
),
|
),
|
||||||
insertChildren: (props, htmlElement, anchor) =>
|
insertChildren: (props, htmlElement, anchor) =>
|
||||||
_initialiseChildren(initialiseChildrenParams(false, treeNode))(
|
initialiseChildren(getInitialiseParams(false, treeNode))(
|
||||||
props,
|
props,
|
||||||
htmlElement,
|
htmlElement,
|
||||||
anchor
|
anchor
|
||||||
|
@ -22345,26 +22599,50 @@ var app = (function (exports) {
|
||||||
setStateFromBinding(store, binding, value),
|
setStateFromBinding(store, binding, value),
|
||||||
setState: (path, value) => setState(store, path, value),
|
setState: (path, value) => setState(store, path, value),
|
||||||
getStateOrValue: (prop, currentContext) =>
|
getStateOrValue: (prop, currentContext) =>
|
||||||
getStateOrValue(globalState, prop, currentContext),
|
getStateOrValue(currentState, prop, currentContext),
|
||||||
store,
|
store,
|
||||||
relativeUrl,
|
relativeUrl,
|
||||||
api,
|
api,
|
||||||
isBound,
|
isBound,
|
||||||
parent,
|
parent,
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
return { getInitialiseParams, unsubscribe }
|
||||||
|
};
|
||||||
|
|
||||||
return bb(createTreeNode())
|
let rootTreeNode;
|
||||||
|
|
||||||
|
const initialisePage = (page, target, urlPath) => {
|
||||||
|
currentUrl = urlPath;
|
||||||
|
|
||||||
|
rootTreeNode = createTreeNode();
|
||||||
|
const { getInitialiseParams } = initialiseChildrenParams(pageStore);
|
||||||
|
const initChildParams = getInitialiseParams(true, rootTreeNode);
|
||||||
|
|
||||||
|
initialiseChildren(initChildParams)([page.props], target);
|
||||||
|
|
||||||
|
return rootTreeNode
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
initialisePage,
|
||||||
|
screenStore: () => currentScreenStore,
|
||||||
|
pageStore: () => pageStore,
|
||||||
|
routeTo: () => routeTo,
|
||||||
|
rootNode: () => rootTreeNode,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadBudibase = async ({
|
const loadBudibase = async ({
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
props,
|
page,
|
||||||
|
screens,
|
||||||
window,
|
window,
|
||||||
localStorage,
|
localStorage,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
}) => {
|
}) => {
|
||||||
const appDefinition = window["##BUDIBASE_APPDEFINITION##"];
|
const backendDefinition = window["##BUDIBASE_BACKEND_DEFINITION##"];
|
||||||
const uiFunctionsFromWindow = window["##BUDIBASE_APPDEFINITION##"];
|
const frontendDefinition = window["##BUDIBASE_FRONTEND_DEFINITION##"];
|
||||||
|
const uiFunctionsFromWindow = window["##BUDIBASE_FRONTEND_FUNCTIONS##"];
|
||||||
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
uiFunctions = uiFunctionsFromWindow || uiFunctions;
|
||||||
|
|
||||||
const userFromStorage = localStorage.getItem("budibase:user");
|
const userFromStorage = localStorage.getItem("budibase:user");
|
||||||
|
@ -22378,35 +22656,54 @@ var app = (function (exports) {
|
||||||
temp: false,
|
temp: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!componentLibraries) {
|
|
||||||
const rootPath =
|
const rootPath =
|
||||||
appDefinition.appRootPath === ""
|
frontendDefinition.appRootPath === ""
|
||||||
? ""
|
? ""
|
||||||
: "/" + trimSlash(appDefinition.appRootPath);
|
: "/" + trimSlash(frontendDefinition.appRootPath);
|
||||||
|
|
||||||
|
if (!componentLibraries) {
|
||||||
|
|
||||||
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib);
|
||||||
componentLibraries = {};
|
componentLibraries = {};
|
||||||
|
|
||||||
for (let lib of appDefinition.componentLibraries) {
|
for (let lib of frontendDefinition.componentLibraries) {
|
||||||
componentLibraries[lib.libName] = await import(
|
componentLibraries[lib.libName] = await import(
|
||||||
componentLibraryUrl(lib.importPath)
|
componentLibraryUrl(lib.importPath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props) {
|
componentLibraries[builtinLibName] = builtins(window);
|
||||||
props = appDefinition.props;
|
|
||||||
|
if (!page) {
|
||||||
|
page = frontendDefinition.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = createApp(
|
if (!screens) {
|
||||||
|
screens = frontendDefinition.screens;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { initialisePage, screenStore, pageStore, routeTo, rootNode } = createApp(
|
||||||
window.document,
|
window.document,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
appDefinition,
|
frontendDefinition,
|
||||||
|
backendDefinition,
|
||||||
user,
|
user,
|
||||||
uiFunctions || {}
|
uiFunctions || {},
|
||||||
|
screens
|
||||||
);
|
);
|
||||||
app.hydrateChildren([props], window.document.body);
|
|
||||||
|
|
||||||
return app
|
const route = window.location
|
||||||
|
? window.location.pathname.replace(rootPath, "")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
rootNode: initialisePage(page, window.document.body, route),
|
||||||
|
screenStore,
|
||||||
|
pageStore,
|
||||||
|
routeTo,
|
||||||
|
rootNode
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -16,7 +16,8 @@
|
||||||
|
|
||||||
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'>
|
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'>
|
||||||
|
|
||||||
<script src='/testApp2/clientAppDefinition.js'></script>
|
<script src='/_master/clientFrontendDefinition.js'></script>
|
||||||
|
<script src='/_master/clientBackendDefinition.js'></script>
|
||||||
<script src='/testApp2/budibase-client.js'></script>
|
<script src='/testApp2/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
loadBudibase();
|
loadBudibase();
|
||||||
|
|
|
@ -11,9 +11,10 @@ const {
|
||||||
saveScreen,
|
saveScreen,
|
||||||
renameScreen,
|
renameScreen,
|
||||||
deleteScreen,
|
deleteScreen,
|
||||||
savePagePackage,
|
buildPage,
|
||||||
componentLibraryInfo,
|
componentLibraryInfo,
|
||||||
listScreens,
|
listScreens,
|
||||||
|
saveBackend,
|
||||||
} = require("../utilities/builder")
|
} = require("../utilities/builder")
|
||||||
|
|
||||||
const builderPath = resolve(__dirname, "../builder")
|
const builderPath = resolve(__dirname, "../builder")
|
||||||
|
@ -179,8 +180,17 @@ module.exports = (config, app) => {
|
||||||
ctx.body = info.generators
|
ctx.body = info.generators
|
||||||
ctx.response.status = StatusCodes.OK
|
ctx.response.status = StatusCodes.OK
|
||||||
})
|
})
|
||||||
|
.post("/_builder/api/:appname/backend", async ctx => {
|
||||||
|
await saveBackend(
|
||||||
|
config,
|
||||||
|
ctx.params.appname,
|
||||||
|
ctx.request.body.appDefinition,
|
||||||
|
ctx.request.body.accessLevels
|
||||||
|
)
|
||||||
|
ctx.response.status = StatusCodes.OK
|
||||||
|
})
|
||||||
.post("/_builder/api/:appname/pages/:pageName", async ctx => {
|
.post("/_builder/api/:appname/pages/:pageName", async ctx => {
|
||||||
await savePagePackage(
|
await buildPage(
|
||||||
config,
|
config,
|
||||||
ctx.params.appname,
|
ctx.params.appname,
|
||||||
ctx.params.pageName,
|
ctx.params.pageName,
|
||||||
|
|
|
@ -8,24 +8,27 @@ const {
|
||||||
copyFile,
|
copyFile,
|
||||||
writeFile,
|
writeFile,
|
||||||
readFile,
|
readFile,
|
||||||
|
writeJSON,
|
||||||
} = require("fs-extra")
|
} = require("fs-extra")
|
||||||
const { join, resolve, dirname } = require("path")
|
const { join, resolve, dirname } = require("path")
|
||||||
const sqrl = require("squirrelly")
|
const sqrl = require("squirrelly")
|
||||||
const { convertCssToFiles } = require("./convertCssToFiles")
|
const { convertCssToFiles } = require("./convertCssToFiles")
|
||||||
|
const publicPath = require("./publicPath")
|
||||||
|
|
||||||
module.exports = async (config, appname, pkg) => {
|
module.exports = async (config, appname, pageName, pkg) => {
|
||||||
const appPath = appPackageFolder(config, appname)
|
const appPath = appPackageFolder(config, appname)
|
||||||
|
|
||||||
await convertCssToFiles(publicPath(appPath, pkg.pageName), pkg)
|
await convertCssToFiles(publicPath(appPath, pageName), pkg)
|
||||||
|
|
||||||
await buildIndexHtml(config, appname, appPath, pkg)
|
await buildIndexHtml(config, appname, pageName, appPath, pkg)
|
||||||
|
|
||||||
await buildClientAppDefinition(config, appname, pkg, appPath)
|
await buildFrontendAppDefinition(config, appname, pageName, pkg, appPath)
|
||||||
|
|
||||||
await copyClientLib(appPath, pkg.pageName)
|
await copyClientLib(appPath, pageName)
|
||||||
|
|
||||||
|
await savePageJson(appPath, pageName, pkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
const publicPath = (appPath, pageName) => join(appPath, "public", pageName)
|
|
||||||
const rootPath = (config, appname) =>
|
const rootPath = (config, appname) =>
|
||||||
config.useAppRootPath ? `/${appname}` : ""
|
config.useAppRootPath ? `/${appname}` : ""
|
||||||
|
|
||||||
|
@ -42,8 +45,8 @@ const copyClientLib = async (appPath, pageName) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildIndexHtml = async (config, appname, appPath, pkg) => {
|
const buildIndexHtml = async (config, appname, pageName, appPath, pkg) => {
|
||||||
const appPublicPath = publicPath(appPath, pkg.pageName)
|
const appPublicPath = publicPath(appPath, pageName)
|
||||||
const appRootPath = rootPath(config, appname)
|
const appRootPath = rootPath(config, appname)
|
||||||
|
|
||||||
const stylesheetUrl = s =>
|
const stylesheetUrl = s =>
|
||||||
|
@ -72,9 +75,9 @@ const buildIndexHtml = async (config, appname, appPath, pkg) => {
|
||||||
await writeFile(indexHtmlPath, indexHtml, { flag: "w+" })
|
await writeFile(indexHtmlPath, indexHtml, { flag: "w+" })
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildClientAppDefinition = async (config, appname, pkg) => {
|
const buildFrontendAppDefinition = async (config, appname, pageName, pkg) => {
|
||||||
const appPath = appPackageFolder(config, appname)
|
const appPath = appPackageFolder(config, appname)
|
||||||
const appPublicPath = publicPath(appPath, pkg.pageName)
|
const appPublicPath = publicPath(appPath, pageName)
|
||||||
const appRootPath = rootPath(config, appname)
|
const appRootPath = rootPath(config, appname)
|
||||||
|
|
||||||
const componentLibraries = []
|
const componentLibraries = []
|
||||||
|
@ -109,7 +112,7 @@ const buildClientAppDefinition = async (config, appname, pkg) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filename = join(appPublicPath, "clientAppDefinition.js")
|
const filename = join(appPublicPath, "clientFrontendDefinition.js")
|
||||||
|
|
||||||
if (pkg.page._css) {
|
if (pkg.page._css) {
|
||||||
delete pkg.page._css
|
delete pkg.page._css
|
||||||
|
@ -121,17 +124,32 @@ const buildClientAppDefinition = async (config, appname, pkg) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientAppDefObj = {
|
const clientUiDefinition = JSON.stringify({
|
||||||
hierarchy: pkg.appDefinition.hierarchy,
|
|
||||||
componentLibraries: componentLibraries,
|
componentLibraries: componentLibraries,
|
||||||
appRootPath: appRootPath,
|
appRootPath: appRootPath,
|
||||||
page: pkg.page,
|
page: pkg.page,
|
||||||
screens: pkg.screens,
|
screens: pkg.screens,
|
||||||
}
|
})
|
||||||
|
|
||||||
await writeFile(
|
await writeFile(
|
||||||
filename,
|
filename,
|
||||||
`window['##BUDIBASE_APPDEFINITION##'] = ${JSON.stringify(clientAppDefObj)};
|
`window['##BUDIBASE_FRONTEND_DEINITION##'] = ${clientUiDefinition};
|
||||||
window['##BUDIBASE_UIFUNCTIONS##'] = ${pkg.uiFunctions}`
|
window['##BUDIBASE_FRONTEND_FUNCTIONS##'] = ${pkg.uiFunctions}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const savePageJson = async (appPath, pageName, pkg) => {
|
||||||
|
const pageFile = join(appPath, "pages", pageName, "page.json")
|
||||||
|
|
||||||
|
if (pkg.page._css) {
|
||||||
|
delete pkg.page._css
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkg.page.name) {
|
||||||
|
delete pkg.page.name
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeJSON(pageFile, pkg.page, {
|
||||||
|
spaces: 2,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
const { readJSON, readdir } = require("fs-extra")
|
||||||
|
const { join } = require("path")
|
||||||
|
|
||||||
|
module.exports = async appPath => {
|
||||||
|
const pages = {}
|
||||||
|
|
||||||
|
const pageFolders = await readdir(join(appPath, "pages"))
|
||||||
|
for (let pageFolder of pageFolders) {
|
||||||
|
try {
|
||||||
|
pages[pageFolder] = await readJSON(
|
||||||
|
join(appPath, "pages", pageFolder, "page.json")
|
||||||
|
)
|
||||||
|
pages[pageFolder].name = pageFolder
|
||||||
|
} catch (_) {
|
||||||
|
// ignore error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ const {
|
||||||
readJSON,
|
readJSON,
|
||||||
writeJSON,
|
writeJSON,
|
||||||
readdir,
|
readdir,
|
||||||
stat,
|
|
||||||
ensureDir,
|
ensureDir,
|
||||||
rename,
|
rename,
|
||||||
unlink,
|
unlink,
|
||||||
|
@ -11,14 +10,18 @@ const {
|
||||||
} = require("fs-extra")
|
} = require("fs-extra")
|
||||||
const { join, dirname } = require("path")
|
const { join, dirname } = require("path")
|
||||||
const { $ } = require("@budibase/core").common
|
const { $ } = require("@budibase/core").common
|
||||||
const { keyBy, intersection, map, values, flatten } = require("lodash/fp")
|
const { intersection, map, values, flatten } = require("lodash/fp")
|
||||||
const { merge } = require("lodash")
|
const { merge } = require("lodash")
|
||||||
|
|
||||||
const { componentLibraryInfo } = require("./componentLibraryInfo")
|
const { componentLibraryInfo } = require("./componentLibraryInfo")
|
||||||
const savePagePackage = require("./savePagePackage")
|
|
||||||
const buildPage = require("./buildPage")
|
const buildPage = require("./buildPage")
|
||||||
|
const getPages = require("./getPages")
|
||||||
|
const listScreens = require("./listScreens")
|
||||||
|
const saveBackend = require("./saveBackend")
|
||||||
|
|
||||||
module.exports.savePagePackage = savePagePackage
|
module.exports.buildPage = buildPage
|
||||||
|
module.exports.listScreens = listScreens
|
||||||
|
module.exports.saveBackend = saveBackend
|
||||||
|
|
||||||
const getAppDefinition = async appPath =>
|
const getAppDefinition = async appPath =>
|
||||||
await readJSON(`${appPath}/appDefinition.json`)
|
await readJSON(`${appPath}/appDefinition.json`)
|
||||||
|
@ -45,31 +48,9 @@ module.exports.getApps = async (config, master) => {
|
||||||
return $(master.listApplications(), [map(a => a.name), intersection(dirs)])
|
return $(master.listApplications(), [map(a => a.name), intersection(dirs)])
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPages = async appPath => {
|
|
||||||
const pages = {}
|
|
||||||
|
|
||||||
const pageFolders = await readdir(join(appPath, "pages"))
|
|
||||||
for (let pageFolder of pageFolders) {
|
|
||||||
try {
|
|
||||||
pages[pageFolder] = await readJSON(
|
|
||||||
join(appPath, "pages", pageFolder, "page.json")
|
|
||||||
)
|
|
||||||
} catch (_) {
|
|
||||||
// ignore error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pages
|
|
||||||
}
|
|
||||||
|
|
||||||
const screenPath = (appPath, pageName, name) =>
|
const screenPath = (appPath, pageName, name) =>
|
||||||
join(appPath, "pages", pageName, "screens", name + ".json")
|
join(appPath, "pages", pageName, "screens", name + ".json")
|
||||||
|
|
||||||
module.exports.listScreens = async (config, appname, pagename) => {
|
|
||||||
const appPath = appPackageFolder(config, appname)
|
|
||||||
return keyBy("name")(await fetchscreens(appPath, pagename))
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.saveScreen = async (config, appname, pagename, screen) => {
|
module.exports.saveScreen = async (config, appname, pagename, screen) => {
|
||||||
const appPath = appPackageFolder(config, appname)
|
const appPath = appPackageFolder(config, appname)
|
||||||
const compPath = screenPath(appPath, pagename, screen.name)
|
const compPath = screenPath(appPath, pagename, screen.name)
|
||||||
|
@ -158,43 +139,4 @@ const getComponents = async (appPath, pages, lib) => {
|
||||||
return { components, generators }
|
return { components, generators }
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchscreens = async (appPath, pagename, relativePath = "") => {
|
|
||||||
const currentDir = join(appPath, "pages", pagename, "screens", relativePath)
|
|
||||||
|
|
||||||
const contents = await readdir(currentDir)
|
|
||||||
|
|
||||||
const screens = []
|
|
||||||
|
|
||||||
for (let item of contents) {
|
|
||||||
const itemRelativePath = join(relativePath, item)
|
|
||||||
const itemFullPath = join(currentDir, item)
|
|
||||||
const stats = await stat(itemFullPath)
|
|
||||||
|
|
||||||
if (stats.isFile()) {
|
|
||||||
if (!item.endsWith(".json")) continue
|
|
||||||
|
|
||||||
const component = await readJSON(itemFullPath)
|
|
||||||
|
|
||||||
component.name = itemRelativePath
|
|
||||||
.substring(0, itemRelativePath.length - 5)
|
|
||||||
.replace(/\\/g, "/")
|
|
||||||
|
|
||||||
component.props = component.props || {}
|
|
||||||
|
|
||||||
screens.push(component)
|
|
||||||
} else {
|
|
||||||
const childComponents = await fetchscreens(
|
|
||||||
appPath,
|
|
||||||
join(relativePath, item)
|
|
||||||
)
|
|
||||||
|
|
||||||
for (let c of childComponents) {
|
|
||||||
screens.push(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return screens
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.getComponents = getComponents
|
module.exports.getComponents = getComponents
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
{{ /if }}
|
{{ /if }}
|
||||||
|
|
||||||
|
|
||||||
<script src='{{ appRootPath }}/clientAppDefinition.js'></script>
|
<script src='{{ appRootPath }}/clientFrontendDefinition.js'></script>
|
||||||
|
<script src='{{ appRootPath }}/clientBackendDefinition.js'></script>
|
||||||
<script src='{{ appRootPath }}/budibase-client.js'></script>
|
<script src='{{ appRootPath }}/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
loadBudibase();
|
loadBudibase();
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
const { appPackageFolder } = require("../createAppPackage")
|
||||||
|
const { readJSON, readdir, stat } = require("fs-extra")
|
||||||
|
const { join } = require("path")
|
||||||
|
const { keyBy } = require("lodash/fp")
|
||||||
|
|
||||||
|
module.exports = async (config, appname, pagename) => {
|
||||||
|
const appPath = appPackageFolder(config, appname)
|
||||||
|
return keyBy("name")(await fetchscreens(appPath, pagename))
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchscreens = async (appPath, pagename, relativePath = "") => {
|
||||||
|
const currentDir = join(appPath, "pages", pagename, "screens", relativePath)
|
||||||
|
|
||||||
|
const contents = await readdir(currentDir)
|
||||||
|
|
||||||
|
const screens = []
|
||||||
|
|
||||||
|
for (let item of contents) {
|
||||||
|
const itemRelativePath = join(relativePath, item)
|
||||||
|
const itemFullPath = join(currentDir, item)
|
||||||
|
const stats = await stat(itemFullPath)
|
||||||
|
|
||||||
|
if (stats.isFile()) {
|
||||||
|
if (!item.endsWith(".json")) continue
|
||||||
|
|
||||||
|
const component = await readJSON(itemFullPath)
|
||||||
|
|
||||||
|
component.name = itemRelativePath
|
||||||
|
.substring(0, itemRelativePath.length - 5)
|
||||||
|
.replace(/\\/g, "/")
|
||||||
|
|
||||||
|
component.props = component.props || {}
|
||||||
|
|
||||||
|
screens.push(component)
|
||||||
|
} else {
|
||||||
|
const childComponents = await fetchscreens(
|
||||||
|
appPath,
|
||||||
|
join(relativePath, item)
|
||||||
|
)
|
||||||
|
|
||||||
|
for (let c of childComponents) {
|
||||||
|
screens.push(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return screens
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
const { join } = require("path")
|
||||||
|
|
||||||
|
module.exports = (appPath, pageName) => join(appPath, "public", pageName)
|
|
@ -0,0 +1,28 @@
|
||||||
|
const getPages = require("./getPages")
|
||||||
|
const { appPackageFolder } = require("../createAppPackage")
|
||||||
|
const { writeJSON, writeFile } = require("fs-extra")
|
||||||
|
const { join } = require("path")
|
||||||
|
const publicPath = require("./publicPath")
|
||||||
|
|
||||||
|
module.exports = async (config, appname, appDefinition, accessLevels) => {
|
||||||
|
const appPath = appPackageFolder(config, appname)
|
||||||
|
|
||||||
|
await writeJSON(`${appPath}/appDefinition.json`, appDefinition, {
|
||||||
|
spaces: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
await writeJSON(`${appPath}/access_levels.json`, accessLevels, {
|
||||||
|
spaces: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
const pages = await getPages(appPath)
|
||||||
|
for (let pageName in pages) {
|
||||||
|
const pagePublicPath = publicPath(appPath, pageName)
|
||||||
|
const filename = join(pagePublicPath, "clientBackendDefinition.js")
|
||||||
|
const appDefString = JSON.stringify(appDefinition)
|
||||||
|
await writeFile(
|
||||||
|
filename,
|
||||||
|
`window['##BUDIBASE_FRONTEND_DEINITION##'] = ${appDefString};`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
const { appPackageFolder } = require("../createAppPackage")
|
|
||||||
const { writeJSON } = require("fs-extra")
|
|
||||||
const { join } = require("path")
|
|
||||||
|
|
||||||
const buildPage = require("./buildPage")
|
|
||||||
|
|
||||||
module.exports = async (config, appname, pageName, pkg) => {
|
|
||||||
const appPath = appPackageFolder(config, appname)
|
|
||||||
pkg.pageName = pageName
|
|
||||||
|
|
||||||
await writeJSON(`${appPath}/appDefinition.json`, pkg.appDefinition, {
|
|
||||||
spaces: 2,
|
|
||||||
})
|
|
||||||
|
|
||||||
await writeJSON(`${appPath}/access_levels.json`, pkg.accessLevels, {
|
|
||||||
spaces: 2,
|
|
||||||
})
|
|
||||||
|
|
||||||
await buildPage(config, appname, pkg)
|
|
||||||
|
|
||||||
const pageFile = join(appPath, "pages", pageName, "page.json")
|
|
||||||
|
|
||||||
if (pkg.page._css) {
|
|
||||||
delete pkg.page._css
|
|
||||||
}
|
|
||||||
|
|
||||||
await writeJSON(pageFile, pkg.page, {
|
|
||||||
spaces: 2,
|
|
||||||
})
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue