mirror of https://github.com/Budibase/budibase.git
344 changed files with 249889 additions and 186275 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
@ -1 +1,103 @@ |
|||
window['##BUDIBASE_APPDEFINITION##'] = {"hierarchy":{"name":"root","type":"root","children":[{"name":"customer","type":"record","fields":[{"name":"name","type":"string","typeOptions":{"maxLength":1000,"values":null,"allowDeclaredValuesOnly":false},"label":"name","getInitialValue":"default","getUndefinedValue":"default"}],"children":[{"name":"invoiceyooo","type":"record","fields":[{"name":"amount","type":"number","typeOptions":{"minValue":99999999999,"maxValue":99999999999,"decimalPlaces":2},"label":"amount","getInitialValue":"default","getUndefinedValue":"default"}],"children":[],"validationRules":[],"nodeId":2,"indexes":[],"allidsShardFactor":1,"collectionName":"invoices","isSingle":false}],"validationRules":[],"nodeId":1,"indexes":[{"name":"customer_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":5}],"allidsShardFactor":64,"collectionName":"customers","isSingle":false}],"pathMaps":[],"indexes":[{"name":"Yeo index","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[1],"nodeId":4},{"name":"everyones_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":6}],"nodeId":0},"componentLibraries":["budibase-standard-components"],"appRootPath":"/testApp2","props":{}} |
|||
window["##BUDIBASE_APPDEFINITION##"] = { |
|||
hierarchy: { |
|||
name: "root", |
|||
type: "root", |
|||
children: [ |
|||
{ |
|||
name: "customer", |
|||
type: "record", |
|||
fields: [ |
|||
{ |
|||
name: "name", |
|||
type: "string", |
|||
typeOptions: { |
|||
maxLength: 1000, |
|||
values: null, |
|||
allowDeclaredValuesOnly: false, |
|||
}, |
|||
label: "name", |
|||
getInitialValue: "default", |
|||
getUndefinedValue: "default", |
|||
}, |
|||
], |
|||
children: [ |
|||
{ |
|||
name: "invoiceyooo", |
|||
type: "record", |
|||
fields: [ |
|||
{ |
|||
name: "amount", |
|||
type: "number", |
|||
typeOptions: { |
|||
minValue: 99999999999, |
|||
maxValue: 99999999999, |
|||
decimalPlaces: 2, |
|||
}, |
|||
label: "amount", |
|||
getInitialValue: "default", |
|||
getUndefinedValue: "default", |
|||
}, |
|||
], |
|||
children: [], |
|||
validationRules: [], |
|||
nodeId: 2, |
|||
indexes: [], |
|||
allidsShardFactor: 1, |
|||
collectionName: "invoices", |
|||
isSingle: false, |
|||
}, |
|||
], |
|||
validationRules: [], |
|||
nodeId: 1, |
|||
indexes: [ |
|||
{ |
|||
name: "customer_invoices", |
|||
type: "index", |
|||
map: "return {...record};", |
|||
filter: "", |
|||
indexType: "ancestor", |
|||
getShardName: "", |
|||
getSortKey: "record.id", |
|||
aggregateGroups: [], |
|||
allowedRecordNodeIds: [2], |
|||
nodeId: 5, |
|||
}, |
|||
], |
|||
allidsShardFactor: 64, |
|||
collectionName: "customers", |
|||
isSingle: false, |
|||
}, |
|||
], |
|||
pathMaps: [], |
|||
indexes: [ |
|||
{ |
|||
name: "Yeo index", |
|||
type: "index", |
|||
map: "return {...record};", |
|||
filter: "", |
|||
indexType: "ancestor", |
|||
getShardName: "", |
|||
getSortKey: "record.id", |
|||
aggregateGroups: [], |
|||
allowedRecordNodeIds: [1], |
|||
nodeId: 4, |
|||
}, |
|||
{ |
|||
name: "everyones_invoices", |
|||
type: "index", |
|||
map: "return {...record};", |
|||
filter: "", |
|||
indexType: "ancestor", |
|||
getShardName: "", |
|||
getSortKey: "record.id", |
|||
aggregateGroups: [], |
|||
allowedRecordNodeIds: [2], |
|||
nodeId: 6, |
|||
}, |
|||
], |
|||
nodeId: 0, |
|||
}, |
|||
componentLibraries: ["budibase-standard-components"], |
|||
appRootPath: "/testApp2", |
|||
props: {}, |
|||
} |
|||
|
|||
@ -1,20 +1,20 @@ |
|||
import svelte from 'rollup-plugin-svelte'; |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import svelte from "rollup-plugin-svelte" |
|||
import resolve from "rollup-plugin-node-resolve" |
|||
|
|||
export default { |
|||
input: 'src/index.js', |
|||
output: [ |
|||
{ |
|||
file: "dist/index.js", |
|||
format: 'esm', |
|||
name:"budibaseStandardComponents", |
|||
sourcemap: "inline" |
|||
} |
|||
], |
|||
plugins: [ |
|||
svelte({ |
|||
hydratable:true |
|||
}), |
|||
resolve() |
|||
] |
|||
}; |
|||
input: "src/index.js", |
|||
output: [ |
|||
{ |
|||
file: "dist/index.js", |
|||
format: "esm", |
|||
name: "budibaseStandardComponents", |
|||
sourcemap: "inline", |
|||
}, |
|||
], |
|||
plugins: [ |
|||
svelte({ |
|||
hydratable: true, |
|||
}), |
|||
resolve(), |
|||
], |
|||
} |
|||
|
|||
@ -1,16 +1,14 @@ |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import resolve from "rollup-plugin-node-resolve" |
|||
|
|||
export default { |
|||
input: 'src/generators.js', |
|||
output: [ |
|||
{ |
|||
file: "dist/generators.js", |
|||
format: 'esm', |
|||
name:"budibaseStandardComponents", |
|||
sourcemap: "inline" |
|||
} |
|||
], |
|||
plugins: [ |
|||
resolve() |
|||
] |
|||
}; |
|||
input: "src/generators.js", |
|||
output: [ |
|||
{ |
|||
file: "dist/generators.js", |
|||
format: "esm", |
|||
name: "budibaseStandardComponents", |
|||
sourcemap: "inline", |
|||
}, |
|||
], |
|||
plugins: [resolve()], |
|||
} |
|||
|
|||
@ -1,89 +1,131 @@ |
|||
import svelte from 'rollup-plugin-svelte'; |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import commonjs from 'rollup-plugin-commonjs'; |
|||
import livereload from 'rollup-plugin-livereload'; |
|||
import { terser } from 'rollup-plugin-terser'; |
|||
import json from 'rollup-plugin-json'; |
|||
import svelte from "rollup-plugin-svelte" |
|||
import resolve from "rollup-plugin-node-resolve" |
|||
import commonjs from "rollup-plugin-commonjs" |
|||
import livereload from "rollup-plugin-livereload" |
|||
import { terser } from "rollup-plugin-terser" |
|||
import json from "rollup-plugin-json" |
|||
|
|||
const production = !process.env.ROLLUP_WATCH; |
|||
const production = !process.env.ROLLUP_WATCH |
|||
|
|||
const lodash_fp_exports = [ |
|||
"find", "isUndefined", "split", |
|||
"last", "union", "reduce", "isObject", |
|||
"cloneDeep", "some", "isArray", "map", |
|||
"filter", "keys", "isFunction", "isEmpty", |
|||
"countBy", "join", "includes", "flatten", |
|||
"constant", "first", "intersection", "take", |
|||
"has", "mapValues", "isString", "isBoolean", |
|||
"isNull", "isNumber", "isObjectLike", "isDate", |
|||
"clone", "values", "keyBy", "isNaN", |
|||
"isInteger", "toNumber"]; |
|||
"find", |
|||
"isUndefined", |
|||
"split", |
|||
"last", |
|||
"union", |
|||
"reduce", |
|||
"isObject", |
|||
"cloneDeep", |
|||
"some", |
|||
"isArray", |
|||
"map", |
|||
"filter", |
|||
"keys", |
|||
"isFunction", |
|||
"isEmpty", |
|||
"countBy", |
|||
"join", |
|||
"includes", |
|||
"flatten", |
|||
"constant", |
|||
"first", |
|||
"intersection", |
|||
"take", |
|||
"has", |
|||
"mapValues", |
|||
"isString", |
|||
"isBoolean", |
|||
"isNull", |
|||
"isNumber", |
|||
"isObjectLike", |
|||
"isDate", |
|||
"clone", |
|||
"values", |
|||
"keyBy", |
|||
"isNaN", |
|||
"isInteger", |
|||
"toNumber", |
|||
] |
|||
|
|||
const lodash_exports = [ |
|||
"flow", "head", |
|||
"tail", "findIndex", "startsWith", |
|||
"dropRight", "takeRight", |
|||
"trim", "split", "replace", |
|||
"merge", "assign"]; |
|||
"flow", |
|||
"head", |
|||
"tail", |
|||
"findIndex", |
|||
"startsWith", |
|||
"dropRight", |
|||
"takeRight", |
|||
"trim", |
|||
"split", |
|||
"replace", |
|||
"merge", |
|||
"assign", |
|||
] |
|||
|
|||
const coreExternal = [ |
|||
"lodash", "lodash/fp", "date-fns", |
|||
"lunr", "safe-buffer", "shortid", |
|||
"@nx-js/compiler-util" |
|||
]; |
|||
"lodash", |
|||
"lodash/fp", |
|||
"date-fns", |
|||
"lunr", |
|||
"safe-buffer", |
|||
"shortid", |
|||
"@nx-js/compiler-util", |
|||
] |
|||
|
|||
export default { |
|||
input: 'src/Test/testMain.js', |
|||
output: { |
|||
sourcemap: true, |
|||
format: 'iife', |
|||
name: 'app', |
|||
file: 'public/bundle.js' |
|||
}, |
|||
plugins: [ |
|||
svelte({ |
|||
// enable run-time checks when not in production
|
|||
dev: !production, |
|||
// we'll extract any component CSS out into
|
|||
// a separate file — better for performance
|
|||
css: css => { |
|||
css.write('public/bundle.css'); |
|||
}, |
|||
|
|||
hydratable:true |
|||
}), |
|||
input: "src/Test/testMain.js", |
|||
output: { |
|||
sourcemap: true, |
|||
format: "iife", |
|||
name: "app", |
|||
file: "public/bundle.js", |
|||
}, |
|||
plugins: [ |
|||
svelte({ |
|||
// enable run-time checks when not in production
|
|||
dev: !production, |
|||
// we'll extract any component CSS out into
|
|||
// a separate file — better for performance
|
|||
css: css => { |
|||
css.write("public/bundle.css") |
|||
}, |
|||
|
|||
// If you have external dependencies installed from
|
|||
// npm, you'll most likely need these plugins. In
|
|||
// some cases you'll need additional configuration —
|
|||
// consult the documentation for details:
|
|||
// https://github.com/rollup/rollup-plugin-commonjs
|
|||
resolve({ |
|||
browser: true, |
|||
dedupe: importee => { |
|||
return importee === 'svelte' |
|||
|| importee.startsWith('svelte/') |
|||
|| coreExternal.includes(importee); |
|||
} |
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
"lodash":lodash_exports, |
|||
"shortid": ["generate"] |
|||
} |
|||
}), |
|||
json(), |
|||
hydratable: true, |
|||
}), |
|||
|
|||
// Watch the `public` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload('public'), |
|||
// If you have external dependencies installed from
|
|||
// npm, you'll most likely need these plugins. In
|
|||
// some cases you'll need additional configuration —
|
|||
// consult the documentation for details:
|
|||
// https://github.com/rollup/rollup-plugin-commonjs
|
|||
resolve({ |
|||
browser: true, |
|||
dedupe: importee => { |
|||
return ( |
|||
importee === "svelte" || |
|||
importee.startsWith("svelte/") || |
|||
coreExternal.includes(importee) |
|||
) |
|||
}, |
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
lodash: lodash_exports, |
|||
shortid: ["generate"], |
|||
}, |
|||
}), |
|||
json(), |
|||
|
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser() |
|||
], |
|||
watch: { |
|||
clearScreen: false |
|||
} |
|||
}; |
|||
// Watch the `public` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload("public"), |
|||
|
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser(), |
|||
], |
|||
watch: { |
|||
clearScreen: false, |
|||
}, |
|||
} |
|||
|
|||
@ -1,61 +1,92 @@ |
|||
const { readdir, stat, copyFile } = require("fs-extra"); |
|||
const { constants } = require("fs"); |
|||
const { join, basename } = require("path"); |
|||
|
|||
const packagesFolder = ".."; |
|||
|
|||
const jsFile = dir => join(dir, "index.js"); |
|||
const generatorsFile = dir => join(dir, "generators.js"); |
|||
const jsMapFile = dir => join(dir, "index.js.map"); |
|||
const sourceJs = jsFile("dist"); |
|||
const sourceJsMap = jsMapFile("dist"); |
|||
const componentsFile = "components.json"; |
|||
const sourceGenerators = generatorsFile("dist"); |
|||
|
|||
const appPackages = join(packagesFolder, "server", "appPackages"); |
|||
|
|||
const publicMain = appName => join(appPackages, appName, "public", "main", "lib", "node_modules", "@budibase", "bootstrap-components"); |
|||
const publicUnauth = appName => join(appPackages, appName, "public", "unauthenticated", "lib", "node_modules", "@budibase", "bootstrap-components"); |
|||
const nodeModulesDist = appName => join(appPackages, appName, "node_modules", "@budibase", "bootstrap-components", "dist"); |
|||
const nodeModules = appName => join(appPackages, appName, "node_modules", "@budibase", "bootstrap-components"); |
|||
|
|||
(async () => { |
|||
|
|||
const apps = await readdir(appPackages); |
|||
|
|||
const copySource = file => async toDir => { |
|||
const dest = join(toDir, basename(file)); |
|||
try { |
|||
await copyFile(file, dest, constants.COPYFILE_FICLONE); |
|||
console.log(`COPIED ${file} to ${dest}`); |
|||
} catch(e) { |
|||
console.log(`COPY FAILED ${file} to ${dest}: ${e}`); |
|||
} |
|||
} |
|||
|
|||
const copySourceJs = copySource(sourceJs); |
|||
const copySourceJsMap = copySource(sourceJsMap); |
|||
const copyGenerators = copySource(sourceGenerators); |
|||
const copyComponentsJson = copySource(componentsFile); |
|||
|
|||
const { readdir, stat, copyFile } = require("fs-extra") |
|||
const { constants } = require("fs") |
|||
const { join, basename } = require("path") |
|||
|
|||
for(let app of apps) { |
|||
if(!(await stat(join(appPackages, app))).isDirectory()) continue; |
|||
const packagesFolder = ".." |
|||
|
|||
await copySourceJs(nodeModulesDist(app)); |
|||
await copySourceJsMap(nodeModulesDist(app)); |
|||
await copyGenerators(nodeModulesDist(app)); |
|||
const jsFile = dir => join(dir, "index.js") |
|||
const generatorsFile = dir => join(dir, "generators.js") |
|||
const jsMapFile = dir => join(dir, "index.js.map") |
|||
const sourceJs = jsFile("dist") |
|||
const sourceJsMap = jsMapFile("dist") |
|||
const componentsFile = "components.json" |
|||
const sourceGenerators = generatorsFile("dist") |
|||
|
|||
await copyComponentsJson(nodeModules(app)); |
|||
const appPackages = join(packagesFolder, "server", "appPackages") |
|||
|
|||
await copySourceJs(join(publicMain(app), "dist")); |
|||
await copySourceJsMap(join(publicMain(app), "dist")); |
|||
await copyGenerators(join(publicMain(app), "dist")); |
|||
const publicMain = appName => |
|||
join( |
|||
appPackages, |
|||
appName, |
|||
"public", |
|||
"main", |
|||
"lib", |
|||
"node_modules", |
|||
"@budibase", |
|||
"bootstrap-components" |
|||
) |
|||
const publicUnauth = appName => |
|||
join( |
|||
appPackages, |
|||
appName, |
|||
"public", |
|||
"unauthenticated", |
|||
"lib", |
|||
"node_modules", |
|||
"@budibase", |
|||
"bootstrap-components" |
|||
) |
|||
const nodeModulesDist = appName => |
|||
join( |
|||
appPackages, |
|||
appName, |
|||
"node_modules", |
|||
"@budibase", |
|||
"bootstrap-components", |
|||
"dist" |
|||
) |
|||
const nodeModules = appName => |
|||
join( |
|||
appPackages, |
|||
appName, |
|||
"node_modules", |
|||
"@budibase", |
|||
"bootstrap-components" |
|||
) |
|||
|
|||
;(async () => { |
|||
const apps = await readdir(appPackages) |
|||
|
|||
await copySourceJs(join(publicUnauth(app), "dist")); |
|||
await copySourceJsMap(join(publicUnauth(app), "dist")); |
|||
await copyGenerators(join(publicUnauth(app), "dist")); |
|||
const copySource = file => async toDir => { |
|||
const dest = join(toDir, basename(file)) |
|||
try { |
|||
await copyFile(file, dest, constants.COPYFILE_FICLONE) |
|||
console.log(`COPIED ${file} to ${dest}`) |
|||
} catch (e) { |
|||
console.log(`COPY FAILED ${file} to ${dest}: ${e}`) |
|||
} |
|||
} |
|||
|
|||
const copySourceJs = copySource(sourceJs) |
|||
const copySourceJsMap = copySource(sourceJsMap) |
|||
const copyGenerators = copySource(sourceGenerators) |
|||
const copyComponentsJson = copySource(componentsFile) |
|||
|
|||
for (let app of apps) { |
|||
if (!(await stat(join(appPackages, app))).isDirectory()) continue |
|||
|
|||
await copySourceJs(nodeModulesDist(app)) |
|||
await copySourceJsMap(nodeModulesDist(app)) |
|||
await copyGenerators(nodeModulesDist(app)) |
|||
|
|||
await copyComponentsJson(nodeModules(app)) |
|||
|
|||
await copySourceJs(join(publicMain(app), "dist")) |
|||
await copySourceJsMap(join(publicMain(app), "dist")) |
|||
await copyGenerators(join(publicMain(app), "dist")) |
|||
|
|||
})(); |
|||
await copySourceJs(join(publicUnauth(app), "dist")) |
|||
await copySourceJsMap(join(publicUnauth(app), "dist")) |
|||
await copyGenerators(join(publicUnauth(app), "dist")) |
|||
} |
|||
})() |
|||
|
|||
@ -1,46 +1,43 @@ |
|||
import { writable } from "svelte/store"; |
|||
import Login from "../Login.svelte"; |
|||
import Grid from "../Grid.svelte"; |
|||
import Form from "../Form.svelte"; |
|||
import Textbox from "../Textbox.svelte"; |
|||
import Text from "../Text.svelte"; |
|||
import Nav from "../Nav.svelte"; |
|||
import Panel from "../Panel.svelte"; |
|||
import StackPanel from "../StackPanel.svelte"; |
|||
import Table from "../Table.svelte"; |
|||
import Button from "../Button.svelte"; |
|||
import { createApp } from "@budibase/client/src/createApp"; |
|||
import Login from "../Login.svelte" |
|||
import Grid from "../Grid.svelte" |
|||
import Form from "../Form.svelte" |
|||
import Textbox from "../Textbox.svelte" |
|||
import Text from "../Text.svelte" |
|||
import Nav from "../Nav.svelte" |
|||
import Panel from "../Panel.svelte" |
|||
import StackPanel from "../StackPanel.svelte" |
|||
import Table from "../Table.svelte" |
|||
import Button from "../Button.svelte" |
|||
import { createApp } from "@budibase/client/src/createApp" |
|||
|
|||
export default async () => { |
|||
export default async () => { |
|||
const componentLibraries = { |
|||
components: { |
|||
login: Login, |
|||
grid: Grid, |
|||
form: Form, |
|||
textbox: Textbox, |
|||
text: Text, |
|||
nav: Nav, |
|||
panel: Panel, |
|||
table: Table, |
|||
stackpanel: StackPanel, |
|||
button: Button, |
|||
}, |
|||
} |
|||
|
|||
const componentLibraries = { |
|||
components : { |
|||
login : Login, |
|||
grid : Grid, |
|||
form : Form, |
|||
textbox : Textbox, |
|||
text: Text, |
|||
nav: Nav, |
|||
panel: Panel, |
|||
table: Table, |
|||
stackpanel: StackPanel, |
|||
button: Button |
|||
} |
|||
} |
|||
const appDef = { hierarchy: {}, actions: {} } |
|||
const user = { name: "yeo", permissions: [] } |
|||
|
|||
const appDef = {hierarchy:{}, actions:{}}; |
|||
const user = {name:"yeo", permissions:[]}; |
|||
|
|||
var app = createApp(componentLibraries, appDef, user); |
|||
app.store.update(s => { |
|||
s.people = [ |
|||
{name:"bob", address: "123 Main Street", status: "Open"}, |
|||
{name:"poppy", address: "456 Side Road", status: "Closed"}, |
|||
{name:"Oscar", address: "678 Dodgy Alley", status: "Open"}, |
|||
]; |
|||
return s; |
|||
}) |
|||
var app = createApp(componentLibraries, appDef, user) |
|||
app.store.update(s => { |
|||
s.people = [ |
|||
{ name: "bob", address: "123 Main Street", status: "Open" }, |
|||
{ name: "poppy", address: "456 Side Road", status: "Closed" }, |
|||
{ name: "Oscar", address: "678 Dodgy Alley", status: "Open" }, |
|||
] |
|||
return s |
|||
}) |
|||
|
|||
return app; |
|||
|
|||
} |
|||
return app |
|||
} |
|||
|
|||
@ -1,253 +1,247 @@ |
|||
|
|||
export const props = { |
|||
login: { _component: "components/login" }, |
|||
|
|||
login: { _component:"components/login" }, |
|||
|
|||
form: { |
|||
_component: "components/form", |
|||
formControls: [ |
|||
{ |
|||
control: { |
|||
_component: "components/textbox" |
|||
}, |
|||
label:"First Name" |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/textbox" |
|||
}, |
|||
label:"Last Name" |
|||
} |
|||
] |
|||
}, |
|||
form: { |
|||
_component: "components/form", |
|||
formControls: [ |
|||
{ |
|||
control: { |
|||
_component: "components/textbox", |
|||
}, |
|||
label: "First Name", |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/textbox", |
|||
}, |
|||
label: "Last Name", |
|||
}, |
|||
], |
|||
}, |
|||
|
|||
nav: { |
|||
_component: "components/nav", |
|||
navBarBackground: "red", |
|||
navBarBorder: "1px solid maroon", |
|||
navBarColor: "black", |
|||
selectedItemBackground: "maroon", |
|||
selectedItemColor: "white", |
|||
selectedItemBorder: "green", |
|||
itemHoverBackground: "yellow", |
|||
itemHoverColor: "pink", |
|||
items: [ |
|||
{ |
|||
title: "People", |
|||
component: { |
|||
_component: "components/panel", |
|||
text:"People Panel", |
|||
padding: "40px", |
|||
border: "2px solid pink", |
|||
background: "mistyrose" |
|||
nav: { |
|||
_component: "components/nav", |
|||
navBarBackground: "red", |
|||
navBarBorder: "1px solid maroon", |
|||
navBarColor: "black", |
|||
selectedItemBackground: "maroon", |
|||
selectedItemColor: "white", |
|||
selectedItemBorder: "green", |
|||
itemHoverBackground: "yellow", |
|||
itemHoverColor: "pink", |
|||
items: [ |
|||
{ |
|||
title: "People", |
|||
component: { |
|||
_component: "components/panel", |
|||
text: "People Panel", |
|||
padding: "40px", |
|||
border: "2px solid pink", |
|||
background: "mistyrose", |
|||
}, |
|||
}, |
|||
{ |
|||
title: "Animals", |
|||
component: { |
|||
_component: "components/panel", |
|||
text: "Animals Panel", |
|||
padding: "40px", |
|||
border: "2px solid green", |
|||
background: "azure", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
|
|||
} |
|||
}, |
|||
{ |
|||
title: "Animals", |
|||
component: { |
|||
_component: "components/panel", |
|||
text:"Animals Panel", |
|||
padding: "40px", |
|||
border: "2px solid green", |
|||
background: "azure" |
|||
} |
|||
} |
|||
] |
|||
table: { |
|||
_component: "components/table", |
|||
columns: [ |
|||
{ |
|||
title: { |
|||
"##bbstate": "NameColumnName", |
|||
"##bbsource": "store", |
|||
"##bbstatefallback": "Name", |
|||
}, |
|||
value: { |
|||
"##bbstate": "name", |
|||
"##bbsource": "context", |
|||
}, |
|||
}, |
|||
{ |
|||
title: "Address", |
|||
value: { |
|||
"##bbstate": "address", |
|||
"##bbsource": "context", |
|||
}, |
|||
}, |
|||
{ |
|||
title: "Status", |
|||
value: { |
|||
"##bbstate": "status", |
|||
"##bbsource": "context", |
|||
}, |
|||
}, |
|||
], |
|||
data: { |
|||
"##bbstate": "people", |
|||
}, |
|||
onRowClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "NameColumnName", |
|||
value: { |
|||
"##bbstate": "name", |
|||
"##bbsource": "context", |
|||
"##bbstatefallback": "balls to that", |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
tableClass: "table-default", |
|||
theadClass: "thead-default", |
|||
tbodyClass: "tbody-default", |
|||
trClass: "tr-default", |
|||
thClass: "th-default", |
|||
}, |
|||
|
|||
table: { |
|||
_component:"components/table", |
|||
columns: [ |
|||
{ |
|||
title: { |
|||
"##bbstate":"NameColumnName", |
|||
"##bbsource":"store", |
|||
"##bbstatefallback": "Name" |
|||
}, |
|||
value: { |
|||
"##bbstate":"name", |
|||
"##bbsource":"context" |
|||
} |
|||
}, |
|||
{ |
|||
title: "Address", |
|||
value: { |
|||
"##bbstate":"address", |
|||
"##bbsource":"context" |
|||
} |
|||
}, |
|||
{ |
|||
title: "Status", |
|||
value: { |
|||
"##bbstate":"status", |
|||
"##bbsource":"context" |
|||
} |
|||
} |
|||
], |
|||
data: { |
|||
"##bbstate":"people" |
|||
}, |
|||
onRowClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "NameColumnName", |
|||
value: { |
|||
"##bbstate":"name", |
|||
"##bbsource":"context", |
|||
"##bbstatefallback": "balls to that" |
|||
} |
|||
} |
|||
} |
|||
], |
|||
tableClass: "table-default", |
|||
theadClass: "thead-default", |
|||
tbodyClass: "tbody-default", |
|||
trClass: "tr-default", |
|||
thClass: "th-default" |
|||
|
|||
grid: { |
|||
_component: "components/grid", |
|||
gridTemplateColumns: "[left] auto [center] auto [right] auto", |
|||
gridTemplateRows: "[top] auto [middle] auto [bottom] auto", |
|||
children: [ |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "1", |
|||
background: "blue", |
|||
textAlign: "center", |
|||
color: "white", |
|||
}, |
|||
gridColumn: "left", |
|||
gridRow: "top", |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "2", |
|||
background: "red", |
|||
textAlign: "center", |
|||
color: "white", |
|||
padding: "10px", |
|||
}, |
|||
gridColumn: "center", |
|||
gridRow: "middle", |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "3", |
|||
background: "yellow", |
|||
textAlign: "center", |
|||
color: "black", |
|||
}, |
|||
gridColumn: "right", |
|||
gridRow: "bottom", |
|||
}, |
|||
], |
|||
}, |
|||
boundStackPanel: { |
|||
_component: "components/stackpanel", |
|||
direction: "horizontal", |
|||
children: [ |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "STATIC", |
|||
}, |
|||
}, |
|||
], |
|||
data: { |
|||
"##bbstate": "people", |
|||
}, |
|||
|
|||
grid: { |
|||
_component: "components/grid", |
|||
gridTemplateColumns: "[left] auto [center] auto [right] auto", |
|||
gridTemplateRows: "[top] auto [middle] auto [bottom] auto", |
|||
children : [ |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "1", |
|||
background: "blue", |
|||
textAlign:"center", |
|||
color: "white" |
|||
}, |
|||
gridColumn: "left", |
|||
gridRow: "top" |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "2", |
|||
background: "red", |
|||
textAlign:"center", |
|||
color: "white", |
|||
padding: "10px" |
|||
}, |
|||
gridColumn: "center", |
|||
gridRow: "middle" |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "3", |
|||
background: "yellow", |
|||
textAlign:"center", |
|||
color: "black" |
|||
}, |
|||
gridColumn: "right", |
|||
gridRow: "bottom" |
|||
} |
|||
] |
|||
dataItemComponent: { |
|||
_component: "components/panel", |
|||
text: { |
|||
"##bbstate": "name", |
|||
"##bbsource": "context", |
|||
"##bbstatefallback": "balls to that", |
|||
}, |
|||
padding: "10px", |
|||
border: "5px solid black", |
|||
margin: "10px", |
|||
hoverColor: "white", |
|||
hoverBackground: "black", |
|||
height: "200px", |
|||
weight: "200px", |
|||
}, |
|||
boundStackPanel: { |
|||
_component: "components/stackpanel", |
|||
direction: "horizontal", |
|||
children: [ |
|||
}, |
|||
hiddenNav: { |
|||
_component: "components/stackpanel", |
|||
children: [ |
|||
{ |
|||
control: { |
|||
_component: "components/button", |
|||
contentText: "Peep", |
|||
onClick: [ |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "STATIC" |
|||
} |
|||
} |
|||
], |
|||
data: { |
|||
"##bbstate":"people" |
|||
}, |
|||
dataItemComponent: { |
|||
_component: "components/panel", |
|||
text: { |
|||
"##bbstate":"name", |
|||
"##bbsource":"context", |
|||
"##bbstatefallback": "balls to that" |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "selected", |
|||
value: "People", |
|||
}, |
|||
}, |
|||
padding: "10px", |
|||
border: "5px solid black", |
|||
margin: "10px", |
|||
hoverColor: "white", |
|||
hoverBackground: "black", |
|||
height:"200px", |
|||
weight:"200px" |
|||
} |
|||
}, |
|||
hiddenNav: { |
|||
_component: "components/stackpanel", |
|||
children: [ |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/button", |
|||
contentText: "Ani", |
|||
onClick: [ |
|||
{ |
|||
control:{ |
|||
_component: "components/button", |
|||
contentText: "Peep", |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "selected", |
|||
value: "People" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "selected", |
|||
value: "Animals", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/nav", |
|||
hideNavBar: true, |
|||
selectedItem: { |
|||
"##bbstate": "selected", |
|||
"##bbsource": "store", |
|||
"##bbstatefallback": "Animals", |
|||
}, |
|||
items: [ |
|||
{ |
|||
control:{ |
|||
_component: "components/button", |
|||
contentText: "Ani", |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "selected", |
|||
value: "Animals" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
title: "People", |
|||
component: { |
|||
_component: "components/panel", |
|||
text: "People Panel", |
|||
padding: "40px", |
|||
border: "2px solid pink", |
|||
background: "mistyrose", |
|||
}, |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/nav", |
|||
hideNavBar: true, |
|||
selectedItem: { |
|||
"##bbstate":"selected", |
|||
"##bbsource":"store", |
|||
"##bbstatefallback": "Animals" |
|||
}, |
|||
items: [ |
|||
{ |
|||
title: "People", |
|||
component: { |
|||
_component: "components/panel", |
|||
text:"People Panel", |
|||
padding: "40px", |
|||
border: "2px solid pink", |
|||
background: "mistyrose" |
|||
|
|||
} |
|||
}, |
|||
{ |
|||
title: "Animals", |
|||
component: { |
|||
_component: "components/panel", |
|||
text:"Animals Panel", |
|||
padding: "40px", |
|||
border: "2px solid green", |
|||
background: "azure" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
|||
|
|||
} |
|||
title: "Animals", |
|||
component: { |
|||
_component: "components/panel", |
|||
text: "Animals Panel", |
|||
padding: "40px", |
|||
border: "2px solid green", |
|||
background: "azure", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
} |
|||
|
|||
@ -1,7 +1,7 @@ |
|||
import App from './TestApp.svelte'; |
|||
import App from "./TestApp.svelte" |
|||
|
|||
const app = new App({ |
|||
target: document.body, |
|||
}); |
|||
target: document.body, |
|||
}) |
|||
|
|||
export default app; |
|||
export default app |
|||
|
|||
@ -1,9 +1,9 @@ |
|||
export const buildStyle = (styles) => { |
|||
let str = ""; |
|||
for(let s in styles) { |
|||
if(styles[s]) { |
|||
str += `${s}: ${styles[s]}; ` |
|||
} |
|||
export const buildStyle = styles => { |
|||
let str = "" |
|||
for (let s in styles) { |
|||
if (styles[s]) { |
|||
str += `${s}: ${styles[s]}; ` |
|||
} |
|||
return str; |
|||
} |
|||
} |
|||
return str |
|||
} |
|||
|
|||
@ -1,21 +1,19 @@ |
|||
// https://github.com/kaisermann/svelte-css-vars
|
|||
|
|||
export default (node, props) => { |
|||
Object.entries(props).forEach(([key, value]) => { |
|||
node.style.setProperty(`--${key}`, value); |
|||
}); |
|||
|
|||
return { |
|||
update(new_props) { |
|||
Object.entries(new_props).forEach(([key, value]) => { |
|||
node.style.setProperty(`--${key}`, value); |
|||
delete props[key]; |
|||
}); |
|||
|
|||
Object.keys(props).forEach(name => |
|||
node.style.removeProperty(`--${name}`), |
|||
); |
|||
props = new_props; |
|||
}, |
|||
}; |
|||
}; |
|||
Object.entries(props).forEach(([key, value]) => { |
|||
node.style.setProperty(`--${key}`, value) |
|||
}) |
|||
|
|||
return { |
|||
update(new_props) { |
|||
Object.entries(new_props).forEach(([key, value]) => { |
|||
node.style.setProperty(`--${key}`, value) |
|||
delete props[key] |
|||
}) |
|||
|
|||
Object.keys(props).forEach(name => node.style.removeProperty(`--${name}`)) |
|||
props = new_props |
|||
}, |
|||
} |
|||
} |
|||
|
|||
@ -1 +1 @@ |
|||
export const emptyProps = () => ({_component:""}); |
|||
export const emptyProps = () => ({ _component: "" }) |
|||
|
|||
@ -1,4 +1,4 @@ |
|||
export { forms } from "./generators/formsGenerator"; |
|||
export { indexTables } from "./generators/indexTablesGenerator"; |
|||
export { app } from "./generators/appGenerator"; |
|||
export { recordHomePageComponents as recordHomepages } from "./generators/recordHomePageGenerator"; |
|||
export { forms } from "./generators/formsGenerator" |
|||
export { indexTables } from "./generators/indexTablesGenerator" |
|||
export { app } from "./generators/appGenerator" |
|||
export { recordHomePageComponents as recordHomepages } from "./generators/recordHomePageGenerator" |
|||
|
|||
@ -1,18 +1,18 @@ |
|||
export const buttons = () => [ |
|||
{ |
|||
name: "common/Primary Button", |
|||
description: "Bootstrap primary button ", |
|||
inherits: "@budibase/standard-components/button", |
|||
props: { |
|||
className: "btn btn-primary" |
|||
} |
|||
{ |
|||
name: "common/Primary Button", |
|||
description: "Bootstrap primary button ", |
|||
inherits: "@budibase/standard-components/button", |
|||
props: { |
|||
className: "btn btn-primary", |
|||
}, |
|||
{ |
|||
name: "common/Default Button", |
|||
description: "Bootstrap default button", |
|||
inherits: "@budibase/standard-components/button", |
|||
props: { |
|||
className: "btn btn-secondary" |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
name: "common/Default Button", |
|||
description: "Bootstrap default button", |
|||
inherits: "@budibase/standard-components/button", |
|||
props: { |
|||
className: "btn btn-secondary", |
|||
}, |
|||
}, |
|||
] |
|||
|
|||
@ -1,123 +1,128 @@ |
|||
import {buttons} from "./buttonGenerators"; |
|||
import { buttons } from "./buttonGenerators" |
|||
|
|||
export const forms = ({records, indexes, helpers}) => |
|||
[ |
|||
...records.map(root), |
|||
...buttons({records, indexes, helpers}) |
|||
]; |
|||
export const forms = ({ records, indexes, helpers }) => [ |
|||
...records.map(root), |
|||
...buttons({ records, indexes, helpers }), |
|||
] |
|||
|
|||
export const formName = record => `${record.name}/${record.name} Form`; |
|||
export const formName = record => `${record.name}/${record.name} Form` |
|||
|
|||
const root = record => ({ |
|||
name: formName(record), |
|||
description: `Control for creating/updating '${record.nodeKey()}' `, |
|||
inherits: "@budibase/standard-components/div", |
|||
props: { |
|||
className:"p-1", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/h3", |
|||
text: `Edit ${record.name}`, |
|||
} |
|||
}, |
|||
form(record), |
|||
saveCancelButtons(record) |
|||
] |
|||
} |
|||
}) |
|||
name: formName(record), |
|||
description: `Control for creating/updating '${record.nodeKey()}' `, |
|||
inherits: "@budibase/standard-components/div", |
|||
props: { |
|||
className: "p-1", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/h3", |
|||
text: `Edit ${record.name}`, |
|||
}, |
|||
}, |
|||
form(record), |
|||
saveCancelButtons(record), |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
const form = record => ({ |
|||
component: { |
|||
_component: "@budibase/standard-components/form", |
|||
formControls: |
|||
record.fields.map(f => formControl(record, f)) |
|||
} |
|||
component: { |
|||
_component: "@budibase/standard-components/form", |
|||
formControls: record.fields.map(f => formControl(record, f)), |
|||
}, |
|||
}) |
|||
|
|||
const formControl = (record, field) => { |
|||
if(field.type === "string" && field.typeOptions.values && field.typeOptions.values.length > 0) { |
|||
return ({ |
|||
control: { |
|||
_component: "@budibase/standard-components/select", |
|||
options: field.typeOptions.values.map(v => ({id:v, value:v})), |
|||
value: { |
|||
"##bbstate":`${record.name}.${field.name}`, |
|||
"##bbsource":"store" |
|||
}, |
|||
className: "form-control" |
|||
}, |
|||
label: field.label |
|||
}); |
|||
} else { |
|||
return ({ |
|||
control: { |
|||
_component: "@budibase/standard-components/input", |
|||
value: { |
|||
"##bbstate":`${record.name}.${field.name}`, |
|||
"##bbsource":"store" |
|||
}, |
|||
className: "form-control", |
|||
type: field.type === "string" ? "text" |
|||
: field.type === "datetime" ? "date" |
|||
: field.type === "number" ? "number" |
|||
: "text" |
|||
}, |
|||
label: field.label |
|||
}); |
|||
if ( |
|||
field.type === "string" && |
|||
field.typeOptions.values && |
|||
field.typeOptions.values.length > 0 |
|||
) { |
|||
return { |
|||
control: { |
|||
_component: "@budibase/standard-components/select", |
|||
options: field.typeOptions.values.map(v => ({ id: v, value: v })), |
|||
value: { |
|||
"##bbstate": `${record.name}.${field.name}`, |
|||
"##bbsource": "store", |
|||
}, |
|||
className: "form-control", |
|||
}, |
|||
label: field.label, |
|||
} |
|||
} else { |
|||
return { |
|||
control: { |
|||
_component: "@budibase/standard-components/input", |
|||
value: { |
|||
"##bbstate": `${record.name}.${field.name}`, |
|||
"##bbsource": "store", |
|||
}, |
|||
className: "form-control", |
|||
type: |
|||
field.type === "string" |
|||
? "text" |
|||
: field.type === "datetime" |
|||
? "date" |
|||
: field.type === "number" |
|||
? "number" |
|||
: "text", |
|||
}, |
|||
label: field.label, |
|||
} |
|||
} |
|||
} |
|||
|
|||
const saveCancelButtons = (record) => ({ |
|||
component: { |
|||
_component: "@budibase/standard-components/stackpanel", |
|||
direction: "horizontal", |
|||
children: [ |
|||
paddedPanelForButton({ |
|||
_component: "common/Primary Button", |
|||
contentText: `Save ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Save Record", |
|||
parameters: { |
|||
statePath: `${record.name}`, |
|||
} |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "" |
|||
} |
|||
} |
|||
] |
|||
}), |
|||
paddedPanelForButton({ |
|||
_component: "common/Default Button", |
|||
contentText: `Cancel`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "" |
|||
} |
|||
} |
|||
] |
|||
}) |
|||
] |
|||
} |
|||
const saveCancelButtons = record => ({ |
|||
component: { |
|||
_component: "@budibase/standard-components/stackpanel", |
|||
direction: "horizontal", |
|||
children: [ |
|||
paddedPanelForButton({ |
|||
_component: "common/Primary Button", |
|||
contentText: `Save ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Save Record", |
|||
parameters: { |
|||
statePath: `${record.name}`, |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "", |
|||
}, |
|||
}, |
|||
], |
|||
}), |
|||
paddedPanelForButton({ |
|||
_component: "common/Default Button", |
|||
contentText: `Cancel`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "", |
|||
}, |
|||
}, |
|||
], |
|||
}), |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
const paddedPanelForButton = (button) => ({ |
|||
control: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group", |
|||
children: [ |
|||
{ |
|||
component: button |
|||
} |
|||
] |
|||
} |
|||
}); |
|||
|
|||
const paddedPanelForButton = button => ({ |
|||
control: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group", |
|||
children: [ |
|||
{ |
|||
component: button, |
|||
}, |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
@ -1,15 +1,14 @@ |
|||
export const getRecordPath = (record) => { |
|||
export const getRecordPath = () => { |
|||
const parts = [] |
|||
|
|||
const parts = []; |
|||
|
|||
const add = (current) => { |
|||
parts.push(current.name); |
|||
if(current.parent().type === "root") { |
|||
return; |
|||
} |
|||
|
|||
add(current.parent()); |
|||
const add = current => { |
|||
parts.push(current.name) |
|||
if (current.parent().type === "root") { |
|||
return |
|||
} |
|||
|
|||
return parts.reverse().join("/"); |
|||
} |
|||
add(current.parent()) |
|||
} |
|||
|
|||
return parts.reverse().join("/") |
|||
} |
|||
|
|||
@ -1,54 +1,53 @@ |
|||
import { getRecordPath } from "./getRecordPath"; |
|||
import { getRecordPath } from "./getRecordPath" |
|||
|
|||
export const indexTables = ({indexes, helpers}) => |
|||
indexes.map(i => indexTable(i, helpers)); |
|||
export const indexTables = ({ indexes, helpers }) => |
|||
indexes.map(i => indexTable(i, helpers)) |
|||
|
|||
const excludedColumns = ["id", "isNew", "key", "type", "sortKey"]; |
|||
const excludedColumns = ["id", "isNew", "key", "type", "sortKey"] |
|||
|
|||
export const indexTableProps = (index, helpers) => ({ |
|||
data: { |
|||
"##bbstate":index.nodeKey(), |
|||
"##bbsource":"store" |
|||
data: { |
|||
"##bbstate": index.nodeKey(), |
|||
"##bbsource": "store", |
|||
}, |
|||
tableClass: "table table-hover", |
|||
theadClass: "thead-dark", |
|||
columns: helpers |
|||
.indexSchema(index) |
|||
.filter(c => !excludedColumns.includes(c.name)) |
|||
.map(column), |
|||
onRowClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `selectedrow_${index.name}`, |
|||
value: { |
|||
"##bbstate": "key", |
|||
"##bbsource": "event", |
|||
}, |
|||
}, |
|||
}, |
|||
tableClass: "table table-hover", |
|||
theadClass: "thead-dark", |
|||
columns: helpers |
|||
.indexSchema(index) |
|||
.filter(c => !excludedColumns.includes(c.name)) |
|||
.map(column), |
|||
onRowClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `selectedrow_${index.name}`, |
|||
value: { |
|||
"##bbstate": "key", |
|||
"##bbsource": "event" |
|||
} |
|||
}, |
|||
} |
|||
] |
|||
}); |
|||
], |
|||
}) |
|||
|
|||
export const getIndexTableName = (index, record) => { |
|||
record = record |
|||
|| index.parent().type === "record" ? index.parent() : null; |
|||
|
|||
return (record |
|||
? `${getRecordPath(record)}/${index.name} Table` |
|||
: `${index.name} Table`); |
|||
record = record || index.parent().type === "record" ? index.parent() : null |
|||
|
|||
return record |
|||
? `${getRecordPath(record)}/${index.name} Table` |
|||
: `${index.name} Table` |
|||
} |
|||
|
|||
const indexTable = (index, helpers) => ({ |
|||
name: getIndexTableName(index), |
|||
inherits: "@budibase/standard-components/table", |
|||
props: indexTableProps(index, helpers) |
|||
}); |
|||
name: getIndexTableName(index), |
|||
inherits: "@budibase/standard-components/table", |
|||
props: indexTableProps(index, helpers), |
|||
}) |
|||
|
|||
const column = (col) => ({ |
|||
title: col.name, |
|||
value: { |
|||
"##bbstate": col.name, |
|||
"##bbsource":"context" |
|||
} |
|||
}) |
|||
const column = col => ({ |
|||
title: col.name, |
|||
value: { |
|||
"##bbstate": col.name, |
|||
"##bbsource": "context", |
|||
}, |
|||
}) |
|||
|
|||
@ -1,190 +1,183 @@ |
|||
import { |
|||
getIndexTableName, indexTables |
|||
} from "./indexTablesGenerator"; |
|||
import { getIndexTableName, indexTables } from "./indexTablesGenerator" |
|||
|
|||
import { |
|||
buttons |
|||
} from "./buttonGenerators"; |
|||
import { buttons } from "./buttonGenerators" |
|||
|
|||
export const recordHomePageComponents = ({indexes, records, helpers}) => |
|||
[ |
|||
...recordHomepages({indexes, records}) |
|||
.map(component), |
|||
export const recordHomePageComponents = ({ indexes, records, helpers }) => [ |
|||
...recordHomepages({ indexes, records }).map(component), |
|||
|
|||
...recordHomepages({indexes, records}) |
|||
.map(homePageButtons), |
|||
|
|||
...indexTables({indexes, records, helpers}), |
|||
...recordHomepages({ indexes, records }).map(homePageButtons), |
|||
|
|||
...buttons({indexes, buttons, helpers}) |
|||
] |
|||
...indexTables({ indexes, records, helpers }), |
|||
|
|||
...buttons({ indexes, buttons, helpers }), |
|||
] |
|||
|
|||
const findIndexForRecord = (indexes, record) => { |
|||
const forRecord = indexes.filter(i => i.allowedRecordNodeIds.includes(record.nodeId)); |
|||
if(forRecord.length === 0) return; |
|||
if(forRecord.length === 1) return forRecord[0]; |
|||
const noMap = forRecord.filter(i => !i.filter || !i.filter.trim()); |
|||
if(noMap.length === 0) forRecord[0]; |
|||
return noMap[0]; |
|||
const forRecord = indexes.filter(i => |
|||
i.allowedRecordNodeIds.includes(record.nodeId) |
|||
) |
|||
if (forRecord.length === 0) return |
|||
if (forRecord.length === 1) return forRecord[0] |
|||
const noMap = forRecord.filter(i => !i.filter || !i.filter.trim()) |
|||
if (noMap.length === 0) forRecord[0] |
|||
return noMap[0] |
|||
} |
|||
|
|||
export const recordHomepages = ({indexes, records}) => |
|||
records.filter(r => r.parent().type === "root") |
|||
.map(r =>({ |
|||
record:r, |
|||
index:findIndexForRecord(indexes, r) |
|||
})) |
|||
.filter(r => r.index); |
|||
export const recordHomepages = ({ indexes, records }) => |
|||
records |
|||
.filter(r => r.parent().type === "root") |
|||
.map(r => ({ |
|||
record: r, |
|||
index: findIndexForRecord(indexes, r), |
|||
})) |
|||
.filter(r => r.index) |
|||
|
|||
export const homepageComponentName = record => |
|||
`${record.name}/${record.name} homepage` |
|||
|
|||
export const homepageComponentName = (record) => |
|||
`${record.name}/${record.name} homepage`; |
|||
const component = ({ record, index }) => ({ |
|||
inherits: "@budibase/standard-components/div", |
|||
name: homepageComponentName(record), |
|||
props: { |
|||
className: "d-flex flex-column h-100", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: `${record.name}/homepage buttons`, |
|||
}, |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: getIndexTableName(index), |
|||
}, |
|||
className: "flex-gow-1 overflow-auto", |
|||
}, |
|||
], |
|||
onLoad: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "", |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "List Records", |
|||
parameters: { |
|||
statePath: index.nodeKey(), |
|||
indexKey: index.nodeKey(), |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
const component = ({record, index}) => ({ |
|||
inherits: "@budibase/standard-components/div", |
|||
name: homepageComponentName(record), |
|||
props: { |
|||
className: "d-flex flex-column h-100", |
|||
children: [ |
|||
const homePageButtons = ({ index, record }) => ({ |
|||
inherits: "@budibase/standard-components/div", |
|||
name: `${record.name}/homepage buttons`, |
|||
props: { |
|||
className: "btn-toolbar mt-4 mb-2", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group mr-3", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: `${record.name}/homepage buttons`, |
|||
} |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Create ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Get New Record", |
|||
parameters: { |
|||
statePath: record.name, |
|||
collectionKey: `/${record.collectionName}`, |
|||
childRecordType: record.name, |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "true", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: getIndexTableName(index) |
|||
}, |
|||
className: "flex-gow-1 overflow-auto" |
|||
} |
|||
], |
|||
onLoad: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "" |
|||
} |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Refresh`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "List Records", |
|||
parameters: { |
|||
statePath: index.nodeKey(), |
|||
indexKey: index.nodeKey(), |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "List Records", |
|||
parameters: { |
|||
statePath: index.nodeKey(), |
|||
indexKey: index.nodeKey() |
|||
} |
|||
} |
|||
] |
|||
} |
|||
|
|||
}); |
|||
|
|||
const homePageButtons = ({index, record}) => ({ |
|||
inherits: "@budibase/standard-components/div", |
|||
name: `${record.name}/homepage buttons`, |
|||
props: { |
|||
className: "btn-toolbar mt-4 mb-2", |
|||
children: [ |
|||
{ |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/if", |
|||
condition: `$store.selectedrow_${index.name} && $store.selectedrow_${index.name}.length > 0`, |
|||
thenComponent: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group mr-3", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Create ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Get New Record", |
|||
parameters: { |
|||
statePath: record.name, |
|||
collectionKey: `/${record.collectionName}`, |
|||
childRecordType: record.name |
|||
} |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "true" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
_component: "common/Default Button", |
|||
contentText: `Edit ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Load Record", |
|||
parameters: { |
|||
statePath: record.name, |
|||
recordKey: { |
|||
"##bbstate": `selectedrow_${index.name}`, |
|||
"##source": "store", |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Refresh`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "List Records", |
|||
parameters: { |
|||
statePath: index.nodeKey(), |
|||
indexKey: index.nodeKey() |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "true", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/if", |
|||
condition: `$store.selectedrow_${index.name} && $store.selectedrow_${index.name}.length > 0`, |
|||
thenComponent: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Edit ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Load Record", |
|||
parameters: { |
|||
statePath: record.name, |
|||
recordKey: { |
|||
"##bbstate" : `selectedrow_${index.name}`, |
|||
"##source": "store" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "true" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Delete ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Delete Record", |
|||
parameters: { |
|||
recordKey: { |
|||
"##bbstate" : `selectedrow_${index.name}`, |
|||
"##source": "store" |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}) |
|||
_component: "common/Default Button", |
|||
contentText: `Delete ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Delete Record", |
|||
parameters: { |
|||
recordKey: { |
|||
"##bbstate": `selectedrow_${index.name}`, |
|||
"##source": "store", |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
@ -1,36 +1,32 @@ |
|||
import { |
|||
recordHomepages, |
|||
homepageComponentName, |
|||
recordHomePageComponents |
|||
} from "./recordHomePageGenerator"; |
|||
import { formName, forms } from "./formsGenerator"; |
|||
import { |
|||
recordHomepages, |
|||
homepageComponentName, |
|||
recordHomePageComponents, |
|||
} from "./recordHomePageGenerator" |
|||
import { formName, forms } from "./formsGenerator" |
|||
|
|||
export const selectNavContent = ({indexes, records, helpers}) => |
|||
[ |
|||
...recordHomepages({indexes, records}) |
|||
.map(component), |
|||
export const selectNavContent = ({ indexes, records, helpers }) => [ |
|||
...recordHomepages({ indexes, records }).map(component), |
|||
|
|||
...recordHomePageComponents({indexes, records, helpers}), |
|||
|
|||
...forms({indexes, records, helpers}) |
|||
|
|||
] |
|||
...recordHomePageComponents({ indexes, records, helpers }), |
|||
|
|||
...forms({ indexes, records, helpers }), |
|||
] |
|||
|
|||
export const navContentComponentName = record => |
|||
`${record.name}/${record.name} Nav Content`; |
|||
`${record.name}/${record.name} Nav Content` |
|||
|
|||
const component = ({record, index}) => ({ |
|||
inherits: "@budibase/standard-components/if", |
|||
description: `the component that gets displayed when the ${record.collectionName} nav is selected`, |
|||
name: navContentComponentName(record), |
|||
props: { |
|||
condition: `$store.isEditing${record.name}`, |
|||
thenComponent: { |
|||
_component: formName(record) |
|||
}, |
|||
elseComponent: { |
|||
_component: homepageComponentName(record) |
|||
} |
|||
} |
|||
}); |
|||
const component = ({ record }) => ({ |
|||
inherits: "@budibase/standard-components/if", |
|||
description: `the component that gets displayed when the ${record.collectionName} nav is selected`, |
|||
name: navContentComponentName(record), |
|||
props: { |
|||
condition: `$store.isEditing${record.name}`, |
|||
thenComponent: { |
|||
_component: formName(record), |
|||
}, |
|||
elseComponent: { |
|||
_component: homepageComponentName(record), |
|||
}, |
|||
}, |
|||
}) |
|||
|
|||
@ -1,3 +1,2 @@ |
|||
export {default as form} from "./Form.svelte"; |
|||
export {default as nav} from "./Nav.svelte"; |
|||
|
|||
export { default as form } from "./Form.svelte" |
|||
export { default as nav } from "./Nav.svelte" |
|||
|
|||
@ -1,12 +1,13 @@ |
|||
module.exports = ({ |
|||
"presets": ["@babel/preset-env"], |
|||
"sourceMaps": "inline", |
|||
"retainLines": true, |
|||
"plugins": [ |
|||
["@babel/plugin-transform-runtime", |
|||
{ |
|||
"regenerator": true |
|||
} |
|||
] |
|||
] |
|||
}); |
|||
module.exports = { |
|||
presets: ["@babel/preset-env"], |
|||
sourceMaps: "inline", |
|||
retainLines: true, |
|||
plugins: [ |
|||
[ |
|||
"@babel/plugin-transform-runtime", |
|||
{ |
|||
regenerator: true, |
|||
}, |
|||
], |
|||
], |
|||
} |
|||
|
|||
@ -1,8 +1,8 @@ |
|||
const ncp = require('ncp').ncp; |
|||
|
|||
ncp("./dist", "../server/builder", function (err) { |
|||
if (err) { |
|||
return console.error(err); |
|||
} |
|||
console.log('Copied dist folder to ../server/builder'); |
|||
}) |
|||
const ncp = require("ncp").ncp |
|||
|
|||
ncp("./dist", "../server/builder", function(err) { |
|||
if (err) { |
|||
return console.error(err) |
|||
} |
|||
console.log("Copied dist folder to ../server/builder") |
|||
}) |
|||
|
|||
@ -1,126 +1,218 @@ |
|||
import svelte from 'rollup-plugin-svelte'; |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import commonjs from 'rollup-plugin-commonjs'; |
|||
import url from 'rollup-plugin-url'; |
|||
import livereload from 'rollup-plugin-livereload'; |
|||
import { terser } from 'rollup-plugin-terser'; |
|||
import builtins from 'rollup-plugin-node-builtins'; |
|||
import nodeglobals from 'rollup-plugin-node-globals'; |
|||
import copy from 'rollup-plugin-copy'; |
|||
import browsersync from "rollup-plugin-browsersync"; |
|||
import proxy from "http-proxy-middleware"; |
|||
import svelte from "rollup-plugin-svelte" |
|||
import resolve from "rollup-plugin-node-resolve" |
|||
import commonjs from "rollup-plugin-commonjs" |
|||
import url from "rollup-plugin-url" |
|||
import livereload from "rollup-plugin-livereload" |
|||
import { terser } from "rollup-plugin-terser" |
|||
import builtins from "rollup-plugin-node-builtins" |
|||
import nodeglobals from "rollup-plugin-node-globals" |
|||
import copy from "rollup-plugin-copy" |
|||
import browsersync from "rollup-plugin-browsersync" |
|||
import proxy from "http-proxy-middleware" |
|||
|
|||
const target = 'http://localhost:4001'; |
|||
const _builderProxy = proxy('/_builder', { |
|||
target: "http://localhost:3000", |
|||
pathRewrite: { '^/_builder': '' } |
|||
}); |
|||
const target = "http://localhost:4001" |
|||
const _builderProxy = proxy("/_builder", { |
|||
target: "http://localhost:3000", |
|||
pathRewrite: { "^/_builder": "" }, |
|||
}) |
|||
|
|||
const apiProxy = proxy(['/_builder/api/**', '/_builder/**/componentlibrary', '/_builder/**/componentlibraryGenerators'], { |
|||
target, |
|||
logLevel: "debug", |
|||
changeOrigin: true, |
|||
cookieDomainRewrite: true, |
|||
onProxyReq(proxyReq) { |
|||
if (proxyReq.getHeader("origin")) { |
|||
proxyReq.setHeader("origin", target) |
|||
} |
|||
} |
|||
}); |
|||
const apiProxy = proxy( |
|||
[ |
|||
"/_builder/api/**", |
|||
"/_builder/**/componentlibrary", |
|||
"/_builder/**/componentlibraryGenerators", |
|||
], |
|||
{ |
|||
target, |
|||
logLevel: "debug", |
|||
changeOrigin: true, |
|||
cookieDomainRewrite: true, |
|||
onProxyReq(proxyReq) { |
|||
if (proxyReq.getHeader("origin")) { |
|||
proxyReq.setHeader("origin", target) |
|||
} |
|||
}, |
|||
} |
|||
) |
|||
|
|||
const production = !process.env.ROLLUP_WATCH; |
|||
const production = !process.env.ROLLUP_WATCH |
|||
|
|||
const lodash_fp_exports = ["union", "reduce", "isUndefined", "cloneDeep", "split", "some", "map", "filter", "isEmpty", "countBy", "includes", "last", "find", "constant", |
|||
"take", "first", "intersection", "mapValues", "isNull", "has", "isInteger", "isNumber", "isString", "isBoolean", "isDate", "isArray", "isObject", "clone", "values", "keyBy", "isNaN", |
|||
"keys", "orderBy", "concat", "reverse", "difference", "merge", "flatten", "each", "pull", "join", "defaultCase", "uniqBy", "every", "uniqWith", "isFunction", "groupBy", |
|||
"differenceBy", "intersectionBy", "isEqual", "max", "sortBy", "assign", "uniq", "trimChars", "trimCharsStart", "isObjectLike", "flattenDeep", "indexOf", "isPlainObject", |
|||
"toNumber", "takeRight", "toPairs"]; |
|||
const lodash_fp_exports = [ |
|||
"union", |
|||
"reduce", |
|||
"isUndefined", |
|||
"cloneDeep", |
|||
"split", |
|||
"some", |
|||
"map", |
|||
"filter", |
|||
"isEmpty", |
|||
"countBy", |
|||
"includes", |
|||
"last", |
|||
"find", |
|||
"constant", |
|||
"take", |
|||
"first", |
|||
"intersection", |
|||
"mapValues", |
|||
"isNull", |
|||
"has", |
|||
"isInteger", |
|||
"isNumber", |
|||
"isString", |
|||
"isBoolean", |
|||
"isDate", |
|||
"isArray", |
|||
"isObject", |
|||
"clone", |
|||
"values", |
|||
"keyBy", |
|||
"isNaN", |
|||
"keys", |
|||
"orderBy", |
|||
"concat", |
|||
"reverse", |
|||
"difference", |
|||
"merge", |
|||
"flatten", |
|||
"each", |
|||
"pull", |
|||
"join", |
|||
"defaultCase", |
|||
"uniqBy", |
|||
"every", |
|||
"uniqWith", |
|||
"isFunction", |
|||
"groupBy", |
|||
"differenceBy", |
|||
"intersectionBy", |
|||
"isEqual", |
|||
"max", |
|||
"sortBy", |
|||
"assign", |
|||
"uniq", |
|||
"trimChars", |
|||
"trimCharsStart", |
|||
"isObjectLike", |
|||
"flattenDeep", |
|||
"indexOf", |
|||
"isPlainObject", |
|||
"toNumber", |
|||
"takeRight", |
|||
"toPairs", |
|||
] |
|||
|
|||
const lodash_exports = ["flow", "join", "replace", "trim", "dropRight", "takeRight", "head", "reduce", |
|||
"tail", "startsWith", "findIndex", "merge", |
|||
"assign", "each", "find", "orderBy", "union"]; |
|||
const lodash_exports = [ |
|||
"flow", |
|||
"join", |
|||
"replace", |
|||
"trim", |
|||
"dropRight", |
|||
"takeRight", |
|||
"head", |
|||
"reduce", |
|||
"tail", |
|||
"startsWith", |
|||
"findIndex", |
|||
"merge", |
|||
"assign", |
|||
"each", |
|||
"find", |
|||
"orderBy", |
|||
"union", |
|||
] |
|||
|
|||
const outputpath = "../server/builder"; |
|||
const outputpath = "../server/builder" |
|||
|
|||
const coreExternal = [ |
|||
"lodash", "lodash/fp", "date-fns", |
|||
"lunr", "safe-buffer", "shortid", |
|||
"@nx-js/compiler-util" |
|||
]; |
|||
"lodash", |
|||
"lodash/fp", |
|||
"date-fns", |
|||
"lunr", |
|||
"safe-buffer", |
|||
"shortid", |
|||
"@nx-js/compiler-util", |
|||
] |
|||
|
|||
export default { |
|||
input: 'src/main.js', |
|||
output: { |
|||
sourcemap: true, |
|||
format: 'iife', |
|||
name: 'app', |
|||
file: `${outputpath}/bundle.js` |
|||
}, |
|||
plugins: [ |
|||
copy({ |
|||
targets: [ |
|||
{ src: 'src/index.html', dest: outputpath }, |
|||
{ src: 'src/favicon.png', dest: outputpath }, |
|||
{ src: 'src/assets', dest: outputpath }, |
|||
{ src: 'node_modules/@budibase/client/dist/budibase-client.esm.mjs', dest: outputpath }, |
|||
] |
|||
}), |
|||
input: "src/main.js", |
|||
output: { |
|||
sourcemap: true, |
|||
format: "iife", |
|||
name: "app", |
|||
file: `${outputpath}/bundle.js`, |
|||
}, |
|||
plugins: [ |
|||
copy({ |
|||
targets: [ |
|||
{ src: "src/index.html", dest: outputpath }, |
|||
{ src: "src/favicon.png", dest: outputpath }, |
|||
{ src: "src/assets", dest: outputpath }, |
|||
{ |
|||
src: "node_modules/@budibase/client/dist/budibase-client.esm.mjs", |
|||
dest: outputpath, |
|||
}, |
|||
], |
|||
}), |
|||
|
|||
svelte({ |
|||
// enable run-time checks when not in production
|
|||
dev: !production, |
|||
include: 'src/**/*.svelte', |
|||
// we'll extract any component CSS out into
|
|||
// a separate file — better for performance
|
|||
css: css => { |
|||
css.write(`${outputpath}/bundle.css`); |
|||
} |
|||
}), |
|||
svelte({ |
|||
// enable run-time checks when not in production
|
|||
dev: !production, |
|||
include: "src/**/*.svelte", |
|||
// we'll extract any component CSS out into
|
|||
// a separate file — better for performance
|
|||
css: css => { |
|||
css.write(`${outputpath}/bundle.css`) |
|||
}, |
|||
}), |
|||
|
|||
resolve({ |
|||
browser: true, |
|||
dedupe: importee => { |
|||
return importee === 'svelte' |
|||
|| importee.startsWith('svelte/') |
|||
|| coreExternal.includes(importee); |
|||
} |
|||
resolve({ |
|||
browser: true, |
|||
dedupe: importee => { |
|||
return ( |
|||
importee === "svelte" || |
|||
importee.startsWith("svelte/") || |
|||
coreExternal.includes(importee) |
|||
) |
|||
}, |
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
lodash: lodash_exports, |
|||
shortid: ["generate"], |
|||
}, |
|||
}), |
|||
url({ |
|||
limit: 0, |
|||
include: ["**/*.woff2", "**/*.png"], |
|||
fileName: "[dirname][name][extname]", |
|||
emitFiles: true, |
|||
}), |
|||
url({ |
|||
limit: 0, |
|||
include: ["**/*.css"], |
|||
fileName: "[name][extname]", |
|||
emitFiles: true, |
|||
}), |
|||
builtins(), |
|||
nodeglobals(), |
|||
|
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
"lodash": lodash_exports, |
|||
"shortid": ["generate"] |
|||
} |
|||
}), |
|||
url({ |
|||
limit: 0, |
|||
include: ["**/*.woff2", "**/*.png"], |
|||
fileName: "[dirname][name][extname]", |
|||
emitFiles: true |
|||
}), |
|||
url({ |
|||
limit: 0, |
|||
include: ["**/*.css"], |
|||
fileName: "[name][extname]", |
|||
emitFiles: true |
|||
}), |
|||
builtins(), |
|||
nodeglobals(), |
|||
// Watch the `dist` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload(outputpath), |
|||
!production && |
|||
browsersync({ |
|||
server: outputpath, |
|||
middleware: [apiProxy, _builderProxy], |
|||
}), |
|||
|
|||
// Watch the `dist` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload(outputpath), |
|||
!production && browsersync({ |
|||
server: outputpath, |
|||
middleware: [apiProxy, _builderProxy] |
|||
}), |
|||
|
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser() |
|||
], |
|||
watch: { |
|||
clearScreen: false |
|||
} |
|||
}; |
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser(), |
|||
], |
|||
watch: { |
|||
clearScreen: false, |
|||
}, |
|||
} |
|||
|
|||
@ -1,17 +1,20 @@ |
|||
const apiCall = (method) => (url, body) => |
|||
fetch(url, { |
|||
method: method, |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
body: body && JSON.stringify(body), |
|||
}); |
|||
const apiCall = method => (url, body) => |
|||
fetch(url, { |
|||
method: method, |
|||
headers: { |
|||
"Content-Type": "application/json", |
|||
}, |
|||
body: body && JSON.stringify(body), |
|||
}) |
|||
|
|||
const post = apiCall("POST"); |
|||
const get = apiCall("GET"); |
|||
const patch = apiCall("PATCH"); |
|||
const del = apiCall("DELETE"); |
|||
const post = apiCall("POST") |
|||
const get = apiCall("GET") |
|||
const patch = apiCall("PATCH") |
|||
const del = apiCall("DELETE") |
|||
|
|||
export default { |
|||
post, get, patch, delete:del |
|||
}; |
|||
post, |
|||
get, |
|||
patch, |
|||
delete: del, |
|||
} |
|||
|
|||
@ -1,35 +1,33 @@ |
|||
const buildCodeForSingleScreen = screen => { |
|||
let code = "" |
|||
const walkProps = props => { |
|||
if (props._code && props._code.trim().length > 0) { |
|||
code += buildComponentCode(props) |
|||
} |
|||
|
|||
if (!props._children) return |
|||
|
|||
const buildCodeForSingleScreen = (screen) => { |
|||
let code = ""; |
|||
const walkProps = (props) => { |
|||
if(props._code && props._code.trim().length > 0) { |
|||
code += buildComponentCode(props) |
|||
} |
|||
|
|||
if(!props._children) return; |
|||
|
|||
for(let child of props._children) { |
|||
walkProps(child); |
|||
} |
|||
for (let child of props._children) { |
|||
walkProps(child) |
|||
} |
|||
} |
|||
|
|||
walkProps(screen.props); |
|||
walkProps(screen.props) |
|||
|
|||
return code; |
|||
return code |
|||
} |
|||
|
|||
export const buildCodeForScreens = screens => { |
|||
let allfunctions = ""; |
|||
for(let screen of screens) { |
|||
allfunctions += buildCodeForSingleScreen(screen); |
|||
} |
|||
|
|||
return (`return ({ ${allfunctions} });`); |
|||
let allfunctions = "" |
|||
for (let screen of screens) { |
|||
allfunctions += buildCodeForSingleScreen(screen) |
|||
} |
|||
|
|||
return `return ({ ${allfunctions} });` |
|||
} |
|||
|
|||
const buildComponentCode = (componentProps) => |
|||
`"${componentProps._id}" : (render, context) => {
|
|||
const buildComponentCode = componentProps => |
|||
`"${componentProps._id}" : (render, context) => {
|
|||
${componentProps._code} |
|||
}, |
|||
`;
|
|||
` |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
import {createNewHierarchy} from "../common/core"; |
|||
import { createNewHierarchy } from "../common/core" |
|||
|
|||
export const createPackage = (packageInfo, store) => { |
|||
packageInfo.createNewPackage(""); |
|||
const root = createNewHierarchy(); |
|||
store.importAppDefinition({ |
|||
hierarchy:root, |
|||
actions:[], |
|||
triggers:[], |
|||
accessLevels: {version:0, levels:[]} |
|||
}); |
|||
}; |
|||
packageInfo.createNewPackage("") |
|||
const root = createNewHierarchy() |
|||
store.importAppDefinition({ |
|||
hierarchy: root, |
|||
actions: [], |
|||
triggers: [], |
|||
accessLevels: { version: 0, levels: [] }, |
|||
}) |
|||
} |
|||
|
|||
@ -1,118 +1,113 @@ |
|||
import { filter, map, reduce, toPairs } from "lodash/fp"; |
|||
import { pipe } from "../common/core"; |
|||
import { filter, map, reduce, toPairs } from "lodash/fp" |
|||
import { pipe } from "../common/core" |
|||
|
|||
const self = n => n; |
|||
const join_with = delimiter => a => a.join(delimiter); |
|||
const empty_string_to_unset = s => s.length ? s : "0"; |
|||
const add_suffix = suffix => s => s + suffix; |
|||
const self = n => n |
|||
const join_with = delimiter => a => a.join(delimiter) |
|||
const empty_string_to_unset = s => (s.length ? s : "0") |
|||
const add_suffix = suffix => s => s + suffix |
|||
|
|||
export const make_margin = (values) => pipe(values, [ |
|||
map(empty_string_to_unset), |
|||
map(add_suffix('px')), |
|||
join_with(' ') |
|||
]); |
|||
|
|||
const tap = message => x => { |
|||
console.log(x); |
|||
return x; |
|||
} |
|||
export const make_margin = values => |
|||
pipe(values, [ |
|||
map(empty_string_to_unset), |
|||
map(add_suffix("px")), |
|||
join_with(" "), |
|||
]) |
|||
|
|||
const css_map = { |
|||
templaterows: { |
|||
name: 'grid-template-columns', |
|||
generate: self |
|||
}, |
|||
templatecolumns: { |
|||
name: 'grid-template-rows', |
|||
generate: self |
|||
}, |
|||
gridarea: { |
|||
name: 'grid-area', |
|||
generate: make_margin |
|||
}, |
|||
gap: { |
|||
name: 'grid-gap', |
|||
generate: n => `${n}px` |
|||
}, |
|||
columnstart: { |
|||
name: 'grid-column-start', |
|||
generate: self |
|||
}, |
|||
columnend: { |
|||
name: 'grid-column-end', |
|||
generate: self |
|||
}, |
|||
rowstart: { |
|||
name: 'grid-row-start', |
|||
generate: self |
|||
}, |
|||
rowend: { |
|||
name: 'grid-row-end', |
|||
generate: self |
|||
}, |
|||
padding: { |
|||
name: 'padding', |
|||
generate: make_margin |
|||
}, |
|||
margin: { |
|||
name: 'margin', |
|||
generate: make_margin |
|||
}, |
|||
zindex: { |
|||
name: 'z-index', |
|||
generate: self |
|||
} |
|||
templaterows: { |
|||
name: "grid-template-columns", |
|||
generate: self, |
|||
}, |
|||
templatecolumns: { |
|||
name: "grid-template-rows", |
|||
generate: self, |
|||
}, |
|||
gridarea: { |
|||
name: "grid-area", |
|||
generate: make_margin, |
|||
}, |
|||
gap: { |
|||
name: "grid-gap", |
|||
generate: n => `${n}px`, |
|||
}, |
|||
columnstart: { |
|||
name: "grid-column-start", |
|||
generate: self, |
|||
}, |
|||
columnend: { |
|||
name: "grid-column-end", |
|||
generate: self, |
|||
}, |
|||
rowstart: { |
|||
name: "grid-row-start", |
|||
generate: self, |
|||
}, |
|||
rowend: { |
|||
name: "grid-row-end", |
|||
generate: self, |
|||
}, |
|||
padding: { |
|||
name: "padding", |
|||
generate: make_margin, |
|||
}, |
|||
margin: { |
|||
name: "margin", |
|||
generate: make_margin, |
|||
}, |
|||
zindex: { |
|||
name: "z-index", |
|||
generate: self, |
|||
}, |
|||
} |
|||
|
|||
export const generate_rule = ([name, values]) => |
|||
`${css_map[name].name}: ${css_map[name].generate(values)};` |
|||
`${css_map[name].name}: ${css_map[name].generate(values)};` |
|||
|
|||
const handle_grid = (acc, [name, value]) => { |
|||
let tmp = []; |
|||
let tmp = [] |
|||
|
|||
if (name === 'row' || name === 'column') { |
|||
if (value[0]) tmp.push([`${name}start`, value[0]]); |
|||
if (value[1]) tmp.push([`${name}end`, value[1]]); |
|||
return acc.concat(tmp) |
|||
} |
|||
if (name === "row" || name === "column") { |
|||
if (value[0]) tmp.push([`${name}start`, value[0]]) |
|||
if (value[1]) tmp.push([`${name}end`, value[1]]) |
|||
return acc.concat(tmp) |
|||
} |
|||
|
|||
return acc.concat([[name, value]]); |
|||
return acc.concat([[name, value]]) |
|||
} |
|||
|
|||
const object_to_css_string = [ |
|||
toPairs, |
|||
reduce(handle_grid, []), |
|||
filter(v => Array.isArray(v[1]) ? v[1].some(s => s.length) : v[1].length), |
|||
map(generate_rule), |
|||
join_with('\n'), |
|||
]; |
|||
toPairs, |
|||
reduce(handle_grid, []), |
|||
filter(v => (Array.isArray(v[1]) ? v[1].some(s => s.length) : v[1].length)), |
|||
map(generate_rule), |
|||
join_with("\n"), |
|||
] |
|||
|
|||
export const generate_css = ({ layout, position }) => { |
|||
let _layout = pipe(layout, object_to_css_string); |
|||
_layout = _layout.length ? _layout + "\ndisplay: grid;" : _layout; |
|||
let _layout = pipe(layout, object_to_css_string) |
|||
_layout = _layout.length ? _layout + "\ndisplay: grid;" : _layout |
|||
|
|||
return { |
|||
layout: _layout, |
|||
position: pipe(position, object_to_css_string) |
|||
} |
|||
return { |
|||
layout: _layout, |
|||
position: pipe(position, object_to_css_string), |
|||
} |
|||
} |
|||
|
|||
const apply_class = (id, name, styles) => `.${name}-${id} {\n${styles}\n}`; |
|||
|
|||
export const generate_screen_css = (component_array) => { |
|||
let styles = ""; |
|||
const apply_class = (id, name, styles) => `.${name}-${id} {\n${styles}\n}` |
|||
|
|||
for (let i = 0; i < component_array.length; i += 1) { |
|||
const { _styles, _id, _children } = component_array[i]; |
|||
const { layout, position } = generate_css(_styles); |
|||
export const generate_screen_css = component_array => { |
|||
let styles = "" |
|||
|
|||
styles += apply_class(_id, 'pos', position) + "\n"; |
|||
styles += apply_class(_id, 'lay', layout) + "\n"; |
|||
for (let i = 0; i < component_array.length; i += 1) { |
|||
const { _styles, _id, _children } = component_array[i] |
|||
const { layout, position } = generate_css(_styles) |
|||
|
|||
if (_children && _children.length) { |
|||
styles += generate_screen_css(_children) + "\n"; |
|||
} |
|||
styles += apply_class(_id, "pos", position) + "\n" |
|||
styles += apply_class(_id, "lay", layout) + "\n" |
|||
|
|||
} |
|||
return styles.trim(); |
|||
if (_children && _children.length) { |
|||
styles += generate_screen_css(_children) + "\n" |
|||
} |
|||
} |
|||
return styles.trim() |
|||
} |
|||
|
|||
@ -1,29 +1,27 @@ |
|||
import {createPackage} from "./createPackage"; |
|||
import getStore from "./store"; |
|||
import { createPackage } from "./createPackage" |
|||
import getStore from "./store" |
|||
|
|||
export const store = getStore(); |
|||
export const store = getStore() |
|||
|
|||
export const createNewPackage = () => |
|||
createPackage(packageInfo, store); |
|||
export const createNewPackage = () => createPackage(packageInfo, store) |
|||
|
|||
export const initialise = async () => { |
|||
try { |
|||
setupRouter(store); |
|||
await store.initialise(); |
|||
} catch(err) { |
|||
console.log(err); |
|||
} |
|||
|
|||
} |
|||
try { |
|||
setupRouter(store) |
|||
await store.initialise() |
|||
} catch (err) { |
|||
console.log(err) |
|||
} |
|||
} |
|||
|
|||
const setupRouter = (writable) => { |
|||
const pushState = history.pushState; |
|||
history.pushState = () => { |
|||
pushState.apply(history, arguments); |
|||
//fireEvents('pushState', arguments);
|
|||
writable.initialise(); |
|||
} |
|||
window.addEventListener('hashchange',()=>{ |
|||
writable.initialise(); |
|||
}) |
|||
const setupRouter = writable => { |
|||
const pushState = history.pushState |
|||
history.pushState = () => { |
|||
pushState.apply(history, arguments) |
|||
//fireEvents('pushState', arguments);
|
|||
writable.initialise() |
|||
} |
|||
window.addEventListener("hashchange", () => { |
|||
writable.initialise() |
|||
}) |
|||
} |
|||
|
|||
@ -1,50 +1,47 @@ |
|||
import { map } from "lodash/fp"; |
|||
import { map } from "lodash/fp" |
|||
|
|||
export const loadLibs = async (appName, appPackage) => { |
|||
const allLibraries = {} |
|||
for (let lib of appPackage.pages.componentLibraries) { |
|||
const libModule = await import(makeLibraryUrl(appName, lib)) |
|||
allLibraries[lib] = libModule |
|||
} |
|||
|
|||
const allLibraries = {}; |
|||
for(let lib of appPackage.pages.componentLibraries) { |
|||
const libModule = await import(makeLibraryUrl(appName, lib)); |
|||
allLibraries[lib] = libModule; |
|||
} |
|||
|
|||
return allLibraries; |
|||
return allLibraries |
|||
} |
|||
|
|||
export const loadGeneratorLibs = async (appName, appPackage) => { |
|||
const allGeneratorLibs = {} |
|||
for (let lib of appPackage.pages.componentLibraries) { |
|||
const generatorModule = await import(makeGeneratorLibraryUrl(appName, lib)) |
|||
allGeneratorLibs[lib] = generatorModule |
|||
} |
|||
|
|||
const allGeneratorLibs = {}; |
|||
for(let lib of appPackage.pages.componentLibraries) { |
|||
const generatorModule = await import(makeGeneratorLibraryUrl(appName, lib)); |
|||
allGeneratorLibs[lib] = generatorModule; |
|||
} |
|||
|
|||
return allGeneratorLibs; |
|||
return allGeneratorLibs |
|||
} |
|||
|
|||
export const loadLibUrls = (appName, appPackage) => { |
|||
const allLibraries = [] |
|||
for (let lib of appPackage.pages.componentLibraries) { |
|||
const libUrl = makeLibraryUrl(appName, lib) |
|||
allLibraries.push({ libName: lib, importPath: libUrl }) |
|||
} |
|||
|
|||
const allLibraries = []; |
|||
for(let lib of appPackage.pages.componentLibraries) { |
|||
const libUrl = makeLibraryUrl(appName, lib); |
|||
allLibraries.push({libName:lib, importPath:libUrl}); |
|||
} |
|||
|
|||
return allLibraries; |
|||
return allLibraries |
|||
} |
|||
|
|||
export const loadLib = async (appName, lib, allLibs) => { |
|||
allLibs[lib] = await import(makeLibraryUrl(appName, lib)); |
|||
return allLibs; |
|||
allLibs[lib] = await import(makeLibraryUrl(appName, lib)) |
|||
return allLibs |
|||
} |
|||
|
|||
export const loadGeneratorLib = async (appName, lib, allGeneratorLibs) => { |
|||
allGeneratorLibs[lib] = await import(makeGeneratorLibraryUrl(appName, lib)); |
|||
return allGeneratorLibs; |
|||
allGeneratorLibs[lib] = await import(makeGeneratorLibraryUrl(appName, lib)) |
|||
return allGeneratorLibs |
|||
} |
|||
|
|||
export const makeLibraryUrl = (appName, lib) => |
|||
`/_builder/${appName}/componentlibrary?lib=${encodeURI(lib)}` |
|||
export const makeLibraryUrl = (appName, lib) => |
|||
`/_builder/${appName}/componentlibrary?lib=${encodeURI(lib)}` |
|||
|
|||
export const makeGeneratorLibraryUrl = (appName, lib) => |
|||
`/_builder/${appName}/componentlibraryGenerators?lib=${encodeURI(lib)}` |
|||
export const makeGeneratorLibraryUrl = (appName, lib) => |
|||
`/_builder/${appName}/componentlibraryGenerators?lib=${encodeURI(lib)}` |
|||
|
|||
File diff suppressed because it is too large
@ -1,799 +0,0 @@ |
|||
import { |
|||
hierarchy as hierarchyFunctions, |
|||
} from "../../../core/src"; |
|||
import { |
|||
filter, cloneDeep, sortBy, |
|||
map, last, keys, concat, keyBy, |
|||
find, isEmpty, values, |
|||
} from "lodash/fp"; |
|||
import { |
|||
pipe, getNode, validate, |
|||
constructHierarchy, templateApi |
|||
} from "../common/core"; |
|||
import { writable } from "svelte/store"; |
|||
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject" |
|||
import { buildPropsHierarchy } from "../userInterface/pagesParsing/buildPropsHierarchy" |
|||
import api from "./api"; |
|||
import { isRootComponent, getExactComponent } from "../userInterface/pagesParsing/searchComponents"; |
|||
import { rename } from "../userInterface/pagesParsing/renameScreen"; |
|||
import { |
|||
getNewComponentInfo, getScreenInfo, |
|||
} from "../userInterface/pagesParsing/createProps"; |
|||
import { |
|||
loadLibs, loadLibUrls, loadGeneratorLibs |
|||
} from "./loadComponentLibraries"; |
|||
<<<<<<< HEAD |
|||
import { buildCodeForScreens } from "./buildCodeForScreens"; |
|||
======= |
|||
import { uuid } from './uuid'; |
|||
import { generate_screen_css } from './generate_css'; |
|||
>>>>>>> master |
|||
|
|||
let appname = ""; |
|||
|
|||
export const getStore = () => { |
|||
|
|||
const initial = { |
|||
apps: [], |
|||
appname: "", |
|||
hierarchy: {}, |
|||
actions: [], |
|||
triggers: [], |
|||
pages: defaultPagesObject(), |
|||
mainUi: {}, |
|||
unauthenticatedUi: {}, |
|||
components: [], |
|||
currentFrontEndItem: null, |
|||
currentComponentInfo: null, |
|||
currentFrontEndType: "none", |
|||
currentPageName: "", |
|||
currentComponentProps: null, |
|||
currentNodeIsNew: false, |
|||
errors: [], |
|||
activeNav: "database", |
|||
isBackend: true, |
|||
hasAppPackage: false, |
|||
accessLevels: { version: 0, levels: [] }, |
|||
currentNode: null, |
|||
libraries: null, |
|||
showSettings: false, |
|||
useAnalytics: true, |
|||
}; |
|||
|
|||
const store = writable(initial); |
|||
|
|||
store.initialise = initialise(store, initial); |
|||
store.newChildRecord = newRecord(store, false); |
|||
store.newRootRecord = newRecord(store, true); |
|||
store.selectExistingNode = selectExistingNode(store); |
|||
store.newChildIndex = newIndex(store, false); |
|||
store.newRootIndex = newIndex(store, true); |
|||
store.saveCurrentNode = saveCurrentNode(store); |
|||
store.importAppDefinition = importAppDefinition(store); |
|||
store.deleteCurrentNode = deleteCurrentNode(store); |
|||
store.saveField = saveField(store); |
|||
store.deleteField = deleteField(store); |
|||
store.saveAction = saveAction(store); |
|||
store.deleteAction = deleteAction(store); |
|||
store.saveTrigger = saveTrigger(store); |
|||
store.deleteTrigger = deleteTrigger(store); |
|||
store.saveLevel = saveLevel(store); |
|||
store.deleteLevel = deleteLevel(store); |
|||
store.setActiveNav = setActiveNav(store); |
|||
store.saveScreen = saveScreen(store); |
|||
store.refreshComponents = refreshComponents(store); |
|||
store.addComponentLibrary = addComponentLibrary(store); |
|||
store.renameScreen = renameScreen(store); |
|||
store.deleteScreen = deleteScreen(store); |
|||
store.setCurrentScreen = setCurrentScreen(store); |
|||
store.setCurrentPage = setCurrentPage(store); |
|||
store.createScreen = createScreen(store); |
|||
store.removeComponentLibrary = removeComponentLibrary(store); |
|||
store.addStylesheet = addStylesheet(store); |
|||
store.removeStylesheet = removeStylesheet(store); |
|||
store.savePage = savePage(store); |
|||
store.showFrontend = showFrontend(store); |
|||
store.showBackend = showBackend(store); |
|||
store.showSettings = showSettings(store); |
|||
store.useAnalytics = useAnalytics(store); |
|||
store.createGeneratedComponents = createGeneratedComponents(store); |
|||
store.addChildComponent = addChildComponent(store); |
|||
store.selectComponent = selectComponent(store); |
|||
store.setComponentProp = setComponentProp(store); |
|||
store.setComponentStyle = setComponentStyle(store); |
|||
store.setComponentCode = setComponentCode(store); |
|||
return store; |
|||
} |
|||
|
|||
export default getStore; |
|||
|
|||
const initialise = (store, initial) => async () => { |
|||
|
|||
appname = window.location.hash |
|||
? last(window.location.hash.substr(1).split("/")) |
|||
: ""; |
|||
|
|||
if (!appname) { |
|||
initial.apps = await api.get(`/_builder/api/apps`).then(r => r.json()); |
|||
initial.hasAppPackage = false; |
|||
store.set(initial); |
|||
return initial; |
|||
} |
|||
|
|||
const pkg = await api.get(`/_builder/api/${appname}/appPackage`) |
|||
.then(r => r.json()); |
|||
|
|||
initial.libraries = await loadLibs(appname, pkg); |
|||
initial.generatorLibraries = await loadGeneratorLibs(appname, pkg); |
|||
initial.loadLibraryUrls = () => loadLibUrls(appname, pkg); |
|||
initial.appname = appname; |
|||
initial.pages = pkg.pages; |
|||
initial.hasAppPackage = true; |
|||
initial.hierarchy = pkg.appDefinition.hierarchy; |
|||
initial.accessLevels = pkg.accessLevels; |
|||
initial.screens = values(pkg.screens); |
|||
initial.generators = generatorsArray(pkg.components.generators); |
|||
initial.components = values(pkg.components.components); |
|||
initial.actions = values(pkg.appDefinition.actions); |
|||
initial.triggers = pkg.appDefinition.triggers; |
|||
|
|||
if (!!initial.hierarchy && !isEmpty(initial.hierarchy)) { |
|||
initial.hierarchy = constructHierarchy(initial.hierarchy); |
|||
const shadowHierarchy = createShadowHierarchy(initial.hierarchy); |
|||
if (initial.currentNode !== null) |
|||
initial.currentNode = getNode( |
|||
shadowHierarchy, initial.currentNode.nodeId |
|||
); |
|||
} |
|||
|
|||
store.set(initial); |
|||
return initial; |
|||
} |
|||
|
|||
const generatorsArray = generators => |
|||
pipe(generators, [ |
|||
keys, |
|||
filter(k => k !== "_lib"), |
|||
map(k => generators[k]) |
|||
]); |
|||
|
|||
|
|||
const showSettings = store => show => { |
|||
store.update(s => { |
|||
s.showSettings = !s.showSettings; |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const useAnalytics = store => useAnalytics => { |
|||
store.update(s => { |
|||
s.useAnalytics = !s.useAnalytics; |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const showBackend = store => () => { |
|||
store.update(s => { |
|||
s.isBackend = true; |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const showFrontend = store => () => { |
|||
store.update(s => { |
|||
s.isBackend = false; |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const newRecord = (store, useRoot) => () => { |
|||
store.update(s => { |
|||
s.currentNodeIsNew = true; |
|||
const shadowHierarchy = createShadowHierarchy(s.hierarchy); |
|||
parent = useRoot ? shadowHierarchy |
|||
: getNode( |
|||
shadowHierarchy, |
|||
s.currentNode.nodeId); |
|||
s.errors = []; |
|||
s.currentNode = templateApi(shadowHierarchy) |
|||
.getNewRecordTemplate(parent, "", true); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
|
|||
const selectExistingNode = (store) => (nodeId) => { |
|||
store.update(s => { |
|||
const shadowHierarchy = createShadowHierarchy(s.hierarchy); |
|||
s.currentNode = getNode( |
|||
shadowHierarchy, nodeId |
|||
); |
|||
s.currentNodeIsNew = false; |
|||
s.errors = []; |
|||
s.activeNav = "database"; |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const newIndex = (store, useRoot) => () => { |
|||
store.update(s => { |
|||
s.currentNodeIsNew = true; |
|||
s.errors = []; |
|||
const shadowHierarchy = createShadowHierarchy(s.hierarchy); |
|||
parent = useRoot ? shadowHierarchy |
|||
: getNode( |
|||
shadowHierarchy, |
|||
s.currentNode.nodeId); |
|||
|
|||
s.currentNode = templateApi(shadowHierarchy) |
|||
.getNewIndexTemplate(parent); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const saveCurrentNode = (store) => () => { |
|||
store.update(s => { |
|||
|
|||
const errors = validate.node(s.currentNode); |
|||
s.errors = errors; |
|||
if (errors.length > 0) { |
|||
return s; |
|||
} |
|||
|
|||
const parentNode = getNode( |
|||
s.hierarchy, s.currentNode.parent().nodeId); |
|||
|
|||
const existingNode = getNode( |
|||
s.hierarchy, s.currentNode.nodeId); |
|||
|
|||
let index = parentNode.children.length; |
|||
if (!!existingNode) { |
|||
// remove existing
|
|||
index = existingNode.parent().children.indexOf(existingNode); |
|||
existingNode.parent().children = pipe(existingNode.parent().children, [ |
|||
filter(c => c.nodeId !== existingNode.nodeId) |
|||
]); |
|||
} |
|||
|
|||
// should add node into existing hierarchy
|
|||
const cloned = cloneDeep(s.currentNode); |
|||
templateApi(s.hierarchy).constructNode( |
|||
parentNode, |
|||
cloned |
|||
); |
|||
|
|||
const newIndexOfchild = child => { |
|||
if (child === cloned) return index; |
|||
const currentIndex = parentNode.children.indexOf(child); |
|||
return currentIndex >= index ? currentIndex + 1 : currentIndex; |
|||
} |
|||
|
|||
parentNode.children = pipe(parentNode.children, [ |
|||
sortBy(newIndexOfchild) |
|||
]); |
|||
|
|||
if (!existingNode && s.currentNode.type === "record") { |
|||
const defaultIndex = templateApi(s.hierarchy) |
|||
.getNewIndexTemplate(cloned.parent()); |
|||
defaultIndex.name = `all_${cloned.collectionName}`; |
|||
defaultIndex.allowedRecordNodeIds = [cloned.nodeId]; |
|||
} |
|||
|
|||
s.currentNodeIsNew = false; |
|||
|
|||
savePackage(store, s); |
|||
|
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const importAppDefinition = store => appDefinition => { |
|||
store.update(s => { |
|||
s.hierarchy = appDefinition.hierarchy; |
|||
s.currentNode = appDefinition.hierarchy.children.length > 0 |
|||
? appDefinition.hierarchy.children[0] |
|||
: null; |
|||
s.actions = appDefinition.actions; |
|||
s.triggers = appDefinition.triggers; |
|||
s.currentNodeIsNew = false; |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const deleteCurrentNode = store => () => { |
|||
store.update(s => { |
|||
const nodeToDelete = getNode(s.hierarchy, s.currentNode.nodeId); |
|||
s.currentNode = hierarchyFunctions.isRoot(nodeToDelete.parent()) |
|||
? find(n => n != s.currentNode) |
|||
(s.hierarchy.children) |
|||
: nodeToDelete.parent(); |
|||
if (hierarchyFunctions.isRecord(nodeToDelete)) { |
|||
nodeToDelete.parent().children = filter(c => c.nodeId !== nodeToDelete.nodeId) |
|||
(nodeToDelete.parent().children); |
|||
} else { |
|||
nodeToDelete.parent().indexes = filter(c => c.nodeId !== nodeToDelete.nodeId) |
|||
(nodeToDelete.parent().indexes); |
|||
} |
|||
s.errors = []; |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const saveField = databaseStore => (field) => { |
|||
databaseStore.update(db => { |
|||
db.currentNode.fields = filter(f => f.name !== field.name) |
|||
(db.currentNode.fields); |
|||
|
|||
templateApi(db.hierarchy).addField(db.currentNode, field); |
|||
return db; |
|||
}); |
|||
} |
|||
|
|||
|
|||
const deleteField = databaseStore => field => { |
|||
databaseStore.update(db => { |
|||
db.currentNode.fields = filter(f => f.name !== field.name) |
|||
(db.currentNode.fields); |
|||
|
|||
return db; |
|||
}); |
|||
} |
|||
|
|||
|
|||
const saveAction = store => (newAction, isNew, oldAction = null) => { |
|||
store.update(s => { |
|||
|
|||
const existingAction = isNew |
|||
? null |
|||
: find(a => a.name === oldAction.name)(s.actions); |
|||
|
|||
if (existingAction) { |
|||
s.actions = pipe(s.actions, [ |
|||
map(a => a === existingAction ? newAction : a) |
|||
]); |
|||
} else { |
|||
s.actions.push(newAction); |
|||
} |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const deleteAction = store => action => { |
|||
store.update(s => { |
|||
s.actions = filter(a => a.name !== action.name)(s.actions); |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const saveTrigger = store => (newTrigger, isNew, oldTrigger = null) => { |
|||
store.update(s => { |
|||
|
|||
const existingTrigger = isNew |
|||
? null |
|||
: find(a => a.name === oldTrigger.name)(s.triggers); |
|||
|
|||
if (existingTrigger) { |
|||
s.triggers = pipe(s.triggers, [ |
|||
map(a => a === existingTrigger ? newTrigger : a) |
|||
]); |
|||
} else { |
|||
s.triggers.push(newTrigger); |
|||
} |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const deleteTrigger = store => trigger => { |
|||
store.update(s => { |
|||
s.triggers = filter(t => t.name !== trigger.name)(s.triggers); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const incrementAccessLevelsVersion = (s) => |
|||
s.accessLevels.version = (s.accessLevels.version || 0) + 1; |
|||
|
|||
const saveLevel = store => (newLevel, isNew, oldLevel = null) => { |
|||
store.update(s => { |
|||
|
|||
const levels = s.accessLevels.levels; |
|||
|
|||
const existingLevel = isNew |
|||
? null |
|||
: find(a => a.name === oldLevel.name)(levels); |
|||
|
|||
if (existingLevel) { |
|||
s.accessLevels.levels = pipe(levels, [ |
|||
map(a => a === existingLevel ? newLevel : a) |
|||
]); |
|||
} else { |
|||
s.accessLevels.levels.push(newLevel); |
|||
} |
|||
|
|||
incrementAccessLevelsVersion(s); |
|||
|
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const deleteLevel = store => level => { |
|||
store.update(s => { |
|||
s.accessLevels.levels = filter(t => t.name !== level.name)(s.accessLevels.levels); |
|||
incrementAccessLevelsVersion(s); |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const setActiveNav = store => navName => { |
|||
store.update(s => { |
|||
s.activeNav = navName; |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const createShadowHierarchy = hierarchy => |
|||
constructHierarchy(JSON.parse(JSON.stringify(hierarchy))); |
|||
|
|||
const saveScreen = store => (screen) => { |
|||
store.update(s => { |
|||
return _saveScreen(store, s, screen); |
|||
}) |
|||
}; |
|||
|
|||
const _saveScreen = (store, s, screen) => { |
|||
const screens = pipe(s.screens, [ |
|||
filter(c => c.name !== screen.name), |
|||
concat([screen]) |
|||
]); |
|||
|
|||
s.screens = screens; |
|||
s.currentFrontEndItem = screen; |
|||
s.currentComponentInfo = getScreenInfo( |
|||
s.components, screen); |
|||
|
|||
api.post(`/_builder/api/${s.appname}/screen`, screen) |
|||
.then(() => savePackage(store, s)); |
|||
|
|||
return s; |
|||
} |
|||
|
|||
const _save = (appname, screen, store, s) => |
|||
api.post(`/_builder/api/${appname}/screen`, screen) |
|||
.then(() => savePackage(store, s)); |
|||
|
|||
const createScreen = store => (screenName, layoutComponentName) => { |
|||
store.update(s => { |
|||
const newComponentInfo = getNewComponentInfo( |
|||
s.components, layoutComponentName, screenName); |
|||
|
|||
s.currentFrontEndItem = newComponentInfo.component; |
|||
s.currentComponentInfo = newComponentInfo; |
|||
s.currentFrontEndType = "screen"; |
|||
|
|||
return _saveScreen(store, s, newComponentInfo.component); |
|||
}); |
|||
}; |
|||
|
|||
const createGeneratedComponents = store => components => { |
|||
store.update(s => { |
|||
s.components = [...s.components, ...components]; |
|||
s.screens = [...s.screens, ...components]; |
|||
|
|||
const doCreate = async () => { |
|||
for (let c of components) { |
|||
await api.post(`/_builder/api/${s.appname}/screen`, c); |
|||
} |
|||
|
|||
await savePackage(store, s); |
|||
} |
|||
|
|||
doCreate(); |
|||
|
|||
return s; |
|||
}); |
|||
}; |
|||
|
|||
const deleteScreen = store => name => { |
|||
store.update(s => { |
|||
|
|||
const components = pipe(s.components, [ |
|||
filter(c => c.name !== name) |
|||
]); |
|||
|
|||
const screens = pipe(s.screens, [ |
|||
filter(c => c.name !== name) |
|||
]); |
|||
|
|||
s.components = components; |
|||
s.screens = screens; |
|||
if (s.currentFrontEndItem.name === name) { |
|||
s.currentFrontEndItem = null; |
|||
s.currentFrontEndType = ""; |
|||
} |
|||
|
|||
api.delete(`/_builder/api/${s.appname}/screen/${name}`); |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const renameScreen = store => (oldname, newname) => { |
|||
store.update(s => { |
|||
|
|||
const { |
|||
screens, pages, error, changedScreens |
|||
} = rename(s.pages, s.screens, oldname, newname); |
|||
|
|||
if (error) { |
|||
// should really do something with this
|
|||
return s; |
|||
} |
|||
|
|||
s.screens = screens; |
|||
s.pages = pages; |
|||
if (s.currentFrontEndItem.name === oldname) |
|||
s.currentFrontEndItem.name = newname; |
|||
|
|||
const saveAllChanged = async () => { |
|||
for (let screenName of changedScreens) { |
|||
const changedScreen |
|||
= getExactComponent(screens, screenName); |
|||
await api.post(`/_builder/api/${s.appname}/screen`, changedScreen); |
|||
} |
|||
} |
|||
|
|||
api.patch(`/_builder/api/${s.appname}/screen`, { |
|||
oldname, newname |
|||
}) |
|||
.then(() => saveAllChanged()) |
|||
.then(() => { |
|||
savePackage(store, s); |
|||
}); |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const savePage = store => async page => { |
|||
store.update(s => { |
|||
if (s.currentFrontEndType !== "page" || !s.currentPageName) { |
|||
return s; |
|||
} |
|||
|
|||
s.pages[s.currentPageName] = page; |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const addComponentLibrary = store => async lib => { |
|||
|
|||
const response = |
|||
await api.get(`/_builder/api/${appname}/componentlibrary?lib=${encodeURI(lib)}`, undefined, false); |
|||
|
|||
const success = response.status === 200; |
|||
|
|||
const error = response.status === 404 |
|||
? `Could not find library ${lib}` |
|||
: success |
|||
? "" |
|||
: response.statusText; |
|||
|
|||
const components = success |
|||
? await response.json() |
|||
: []; |
|||
|
|||
store.update(s => { |
|||
if (success) { |
|||
|
|||
const componentsArray = []; |
|||
for (let c in components) { |
|||
componentsArray.push(components[c]); |
|||
} |
|||
|
|||
s.components = pipe(s.components, [ |
|||
filter(c => !c.name.startsWith(`${lib}/`)), |
|||
concat(componentsArray) |
|||
]); |
|||
|
|||
s.pages.componentLibraries.push(lib); |
|||
savePackage(store, s); |
|||
} |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const removeComponentLibrary = store => lib => { |
|||
store.update(s => { |
|||
|
|||
|
|||
s.pages.componentLibraries = filter(l => l !== lib)( |
|||
s.pages.componentLibraries); |
|||
savePackage(store, s); |
|||
|
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const addStylesheet = store => stylesheet => { |
|||
store.update(s => { |
|||
s.pages.stylesheets.push(stylesheet); |
|||
savePackage(store, s); |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const removeStylesheet = store => stylesheet => { |
|||
store.update(s => { |
|||
s.pages.stylesheets = filter(s => s !== stylesheet)(s.pages.stylesheets); |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const refreshComponents = store => async () => { |
|||
|
|||
const componentsAndGenerators = |
|||
await api.get(`/_builder/api/${db.appname}/components`).then(r => r.json()); |
|||
|
|||
const components = pipe(componentsAndGenerators.components, [ |
|||
keys, |
|||
map(k => ({ ...componentsAndGenerators[k], name: k })) |
|||
]); |
|||
|
|||
store.update(s => { |
|||
s.components = pipe(s.components, [ |
|||
filter(c => !isRootComponent(c)), |
|||
concat(components) |
|||
]); |
|||
s.generators = componentsAndGenerators.generators; |
|||
return s; |
|||
}); |
|||
}; |
|||
|
|||
const savePackage = (store, s) => { |
|||
|
|||
const appDefinition = { |
|||
hierarchy: s.hierarchy, |
|||
triggers: s.triggers, |
|||
actions: keyBy("name")(s.actions), |
|||
props: { |
|||
main: buildPropsHierarchy( |
|||
s.components, |
|||
s.screens, |
|||
s.pages.main.appBody), |
|||
unauthenticated: buildPropsHierarchy( |
|||
s.components, |
|||
s.screens, |
|||
s.pages.unauthenticated.appBody) |
|||
}, |
|||
uiFunctions: buildCodeForScreens(s.screens) |
|||
}; |
|||
|
|||
const data = { |
|||
appDefinition, |
|||
accessLevels: s.accessLevels, |
|||
pages: s.pages, |
|||
} |
|||
|
|||
return api.post(`/_builder/api/${s.appname}/appPackage`, data); |
|||
} |
|||
|
|||
const setCurrentScreen = store => screenName => { |
|||
store.update(s => { |
|||
const screen = getExactComponent(s.screens, screenName); |
|||
s.currentFrontEndItem = screen; |
|||
s.currentFrontEndType = "screen"; |
|||
s.currentComponentInfo = getScreenInfo(s.components, screen); |
|||
setCurrentScreenFunctions(s); |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const setCurrentPage = store => pageName => { |
|||
store.update(s => { |
|||
s.currentFrontEndType = "page"; |
|||
s.currentPageName = pageName; |
|||
setCurrentScreenFunctions(s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const addChildComponent = store => component => { |
|||
|
|||
store.update(s => { |
|||
const newComponent = getNewComponentInfo( |
|||
s.components, component); |
|||
|
|||
let children = s.currentComponentInfo.component ? |
|||
s.currentComponentInfo.component.props._children : |
|||
s.currentComponentInfo._children; |
|||
|
|||
const component_definition = Object.assign( |
|||
cloneDeep(newComponent.fullProps), { |
|||
_component: component, |
|||
_styles: { position: {}, layout: {} }, |
|||
_id: uuid() |
|||
}) |
|||
|
|||
if (children) { |
|||
if (s.currentComponentInfo.component) { |
|||
s.currentComponentInfo.component.props._children = children.concat(component_definition); |
|||
} else { |
|||
s.currentComponentInfo._children = children.concat(component_definition) |
|||
} |
|||
} else { |
|||
if (s.currentComponentInfo.component) { |
|||
s.currentComponentInfo.component.props._children = [component_definition]; |
|||
} else { |
|||
s.currentComponentInfo._children = [component_definition] |
|||
} |
|||
} |
|||
|
|||
_saveScreen(store, s, s.currentFrontEndItem); |
|||
|
|||
_saveScreen(store, s, s.currentFrontEndItem); |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const selectComponent = store => component => { |
|||
store.update(s => { |
|||
s.currentComponentInfo = component; |
|||
return s; |
|||
}) |
|||
|
|||
} |
|||
|
|||
const setComponentProp = store => (name, value) => { |
|||
store.update(s => { |
|||
const current_component = s.currentComponentInfo; |
|||
s.currentComponentInfo[name] = value; |
|||
_saveScreen(store, s, s.currentFrontEndItem); |
|||
s.currentComponentInfo = current_component; |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const setComponentStyle = store => (type, name, value) => { |
|||
store.update(s => { |
|||
if (!s.currentComponentInfo._styles) { |
|||
s.currentComponentInfo._styles = {}; |
|||
} |
|||
s.currentComponentInfo._styles[type][name] = value; |
|||
s.currentFrontEndItem._css = generate_screen_css(s.currentFrontEndItem.props._children) |
|||
|
|||
// save without messing with the store
|
|||
_save(s.appname, s.currentFrontEndItem, store, s) |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const setComponentCode = store => (code) => { |
|||
store.update(s => { |
|||
s.currentComponentInfo._code = code; |
|||
|
|||
setCurrentScreenFunctions(s); |
|||
// save without messing with the store
|
|||
_save(s.appname, s.currentFrontEndItem, store, s) |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const setCurrentScreenFunctions = (s) => { |
|||
s.currentScreenFunctions = |
|||
s.currentFrontEndItem === "screen" |
|||
? buildCodeForScreens([s.currentFrontEndItem]) |
|||
: "({});"; |
|||
} |
|||
@ -1,7 +1,7 @@ |
|||
export function uuid() { |
|||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { |
|||
const r = (Math.random() * 16) | 0, |
|||
v = c == 'x' ? r : (r & 0x3) | 0x8; |
|||
return v.toString(16); |
|||
}); |
|||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => { |
|||
const r = (Math.random() * 16) | 0, |
|||
v = c == "x" ? r : (r & 0x3) | 0x8 |
|||
return v.toString(16) |
|||
}) |
|||
} |
|||
|
|||
@ -1,9 +1,9 @@ |
|||
export { default as LayoutIcon } from './Layout.svelte'; |
|||
export { default as PaintIcon } from './Paint.svelte'; |
|||
export { default as TerminalIcon } from './Terminal.svelte'; |
|||
export { default as InputIcon } from './Input.svelte'; |
|||
export { default as ImageIcon } from './Image.svelte'; |
|||
export { default as ArrowDownIcon } from './ArrowDown.svelte'; |
|||
export { default as CircleIndicator } from './CircleIndicator.svelte'; |
|||
export { default as PencilIcon } from './Pencil.svelte'; |
|||
export { default as EventsIcon } from './Events.svelte'; |
|||
export { default as LayoutIcon } from "./Layout.svelte" |
|||
export { default as PaintIcon } from "./Paint.svelte" |
|||
export { default as TerminalIcon } from "./Terminal.svelte" |
|||
export { default as InputIcon } from "./Input.svelte" |
|||
export { default as ImageIcon } from "./Image.svelte" |
|||
export { default as ArrowDownIcon } from "./ArrowDown.svelte" |
|||
export { default as CircleIndicator } from "./CircleIndicator.svelte" |
|||
export { default as PencilIcon } from "./Pencil.svelte" |
|||
export { default as EventsIcon } from "./Events.svelte" |
|||
|
|||
@ -1,12 +0,0 @@ |
|||
export { default as LayoutIcon } from './Layout.svelte'; |
|||
export { default as PaintIcon } from './Paint.svelte'; |
|||
export { default as TerminalIcon } from './Terminal.svelte'; |
|||
export { default as InputIcon } from './Input.svelte'; |
|||
export { default as ImageIcon } from './Image.svelte'; |
|||
export { default as ArrowDownIcon } from './ArrowDown.svelte'; |
|||
<<<<<<< HEAD |
|||
export { default as CircleIndicator } from './CircleIndicator.svelte'; |
|||
======= |
|||
export { default as EventsIcon } from './Events.svelte'; |
|||
export { default as PencilIcon } from './Pencil.svelte'; |
|||
>>>>>>> master |
|||
@ -1,29 +1,28 @@ |
|||
import { |
|||
isString |
|||
} from "lodash/fp"; |
|||
import { isString } from "lodash/fp" |
|||
|
|||
import { |
|||
BB_STATE_BINDINGPATH, BB_STATE_FALLBACK, |
|||
BB_STATE_BINDINGSOURCE |
|||
} from "@budibase/client/src/state/isState"; |
|||
BB_STATE_BINDINGPATH, |
|||
BB_STATE_FALLBACK, |
|||
BB_STATE_BINDINGSOURCE, |
|||
} from "@budibase/client/src/state/isState" |
|||
|
|||
export const isBinding = value => |
|||
!isString(value) |
|||
&& value |
|||
&& isString(value[BB_STATE_BINDINGPATH]) |
|||
&& value[BB_STATE_BINDINGPATH].length > 0; |
|||
export const isBinding = value => |
|||
!isString(value) && |
|||
value && |
|||
isString(value[BB_STATE_BINDINGPATH]) && |
|||
value[BB_STATE_BINDINGPATH].length > 0 |
|||
|
|||
export const setBinding = ({path, fallback, source}, binding={} ) => { |
|||
if(isNonEmptyString(path)) binding[BB_STATE_BINDINGPATH] = path; |
|||
if(isNonEmptyString(fallback)) binding[BB_STATE_FALLBACK] = fallback; |
|||
binding[BB_STATE_BINDINGSOURCE] = source || "store"; |
|||
return binding |
|||
export const setBinding = ({ path, fallback, source }, binding = {}) => { |
|||
if (isNonEmptyString(path)) binding[BB_STATE_BINDINGPATH] = path |
|||
if (isNonEmptyString(fallback)) binding[BB_STATE_FALLBACK] = fallback |
|||
binding[BB_STATE_BINDINGSOURCE] = source || "store" |
|||
return binding |
|||
} |
|||
|
|||
export const getBinding = binding => ({ |
|||
path: binding[BB_STATE_BINDINGPATH] || "", |
|||
fallback: binding[BB_STATE_FALLBACK] || "", |
|||
source: binding[BB_STATE_BINDINGSOURCE] || "store" |
|||
}); |
|||
path: binding[BB_STATE_BINDINGPATH] || "", |
|||
fallback: binding[BB_STATE_FALLBACK] || "", |
|||
source: binding[BB_STATE_BINDINGSOURCE] || "store", |
|||
}) |
|||
|
|||
const isNonEmptyString = s => isString(s) && s.length > 0; |
|||
const isNonEmptyString = s => isString(s) && s.length > 0 |
|||
|
|||
@ -1,96 +1,108 @@ |
|||
import {hierarchy as hierarchyFunctions, |
|||
common, getTemplateApi, getAuthApi } from "../../../core/src"; |
|||
import {find, filter, includes, keyBy, some, |
|||
flatten, map} from "lodash/fp"; |
|||
|
|||
import { |
|||
generateSchema |
|||
} from "../../../core/src/indexing/indexSchemaCreator"; |
|||
hierarchy as hierarchyFunctions, |
|||
common, |
|||
getTemplateApi, |
|||
getAuthApi, |
|||
} from "../../../core/src" |
|||
import { find, filter, includes, keyBy, some, flatten, map } from "lodash/fp" |
|||
|
|||
import { generateSchema } from "../../../core/src/indexing/indexSchemaCreator" |
|||
|
|||
export { userWithFullAccess } from "../../../core/src/index"; |
|||
export { userWithFullAccess } from "../../../core/src/index" |
|||
|
|||
export const pipe = common.$; |
|||
export const pipe = common.$ |
|||
|
|||
export const events = common.eventsList; |
|||
export const events = common.eventsList |
|||
|
|||
export const getNode = (hierarchy, nodeId) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
find(n => n.nodeId === nodeId || n.nodeKey() === nodeId) |
|||
]); |
|||
export const getNode = (hierarchy, nodeId) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
find(n => n.nodeId === nodeId || n.nodeKey() === nodeId), |
|||
]) |
|||
|
|||
export const constructHierarchy = node => { |
|||
if(!node) return node; |
|||
return templateApi(node).constructHierarchy(node); |
|||
if (!node) return node |
|||
return templateApi(node).constructHierarchy(node) |
|||
} |
|||
|
|||
export const createNewHierarchy = () => { |
|||
return templateApi().getNewRootLevel(); |
|||
return templateApi().getNewRootLevel() |
|||
} |
|||
|
|||
export const templateApi = hierarchy => getTemplateApi({hierarchy}) |
|||
export const authApi = (hierarchy, actions) => getAuthApi({ |
|||
hierarchy, actions: keyBy("name")(actions), publish:()=>{}}) |
|||
export const templateApi = hierarchy => getTemplateApi({ hierarchy }) |
|||
export const authApi = (hierarchy, actions) => |
|||
getAuthApi({ |
|||
hierarchy, |
|||
actions: keyBy("name")(actions), |
|||
publish: () => {}, |
|||
}) |
|||
|
|||
export const allTypes = templateApi({}).allTypes; |
|||
export const allTypes = templateApi({}).allTypes |
|||
|
|||
export const validate = { |
|||
all: templateApi({}).validateAll, |
|||
node: templateApi({}).validateNode, |
|||
field: templateApi({}).validateField |
|||
}; |
|||
all: templateApi({}).validateAll, |
|||
node: templateApi({}).validateNode, |
|||
field: templateApi({}).validateField, |
|||
} |
|||
|
|||
export const getPotentialReverseReferenceIndexes = (hierarchy, refIndex) => { |
|||
const res = pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(n => hierarchyFunctions.isAncestor(refIndex)(n) |
|||
|| hierarchyFunctions.isAncestor(refIndex)(n.parent())), |
|||
map(n => n.indexes), |
|||
flatten, |
|||
filter(hierarchyFunctions.isReferenceIndex) |
|||
]); |
|||
|
|||
return res; |
|||
const res = pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter( |
|||
n => |
|||
hierarchyFunctions.isAncestor(refIndex)(n) || |
|||
hierarchyFunctions.isAncestor(refIndex)(n.parent()) |
|||
), |
|||
map(n => n.indexes), |
|||
flatten, |
|||
filter(hierarchyFunctions.isReferenceIndex), |
|||
]) |
|||
|
|||
return res |
|||
} |
|||
|
|||
export const getPotentialReferenceIndexes = (hierarchy, record) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isAncestorIndex), |
|||
filter(i => hierarchyFunctions.isAncestor(record)(i.parent()) |
|||
|| i.parent().nodeId === record.parent().nodeId |
|||
|| hierarchyFunctions.isRoot(i.parent())) |
|||
]); |
|||
|
|||
export const getDefaultTypeOptions = type => |
|||
!type ? {} : allTypes[type].getDefaultOptions(); |
|||
|
|||
export const getNewAction = () => templateApi({}).createAction(); |
|||
export const getNewTrigger = () => templateApi({}).createTrigger(); |
|||
|
|||
export const validateActions = actions => templateApi({}).validateActions(actions); |
|||
export const validateTriggers = (triggers, actions) => templateApi({}).validateTriggers(triggers, actions); |
|||
|
|||
export const generateFullPermissions = (hierarchy, actions) => |
|||
authApi(hierarchy,actions).generateFullPermissions(); |
|||
|
|||
export const getNewAccessLevel = () => |
|||
authApi().getNewAccessLevel(); |
|||
|
|||
export const validateAccessLevels = (hierarchy, actions, accessLevels) => |
|||
authApi(hierarchy, actions).validateAccessLevels(accessLevels); |
|||
|
|||
export const getIndexNodes = (hierarchy) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isIndex) |
|||
]); |
|||
|
|||
export const getRecordNodes = (hierarchy) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isRecord) |
|||
]); |
|||
|
|||
export const getIndexSchema = hierarchy => index => |
|||
generateSchema(hierarchy, index); |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isAncestorIndex), |
|||
filter( |
|||
i => |
|||
hierarchyFunctions.isAncestor(record)(i.parent()) || |
|||
i.parent().nodeId === record.parent().nodeId || |
|||
hierarchyFunctions.isRoot(i.parent()) |
|||
), |
|||
]) |
|||
|
|||
export const getDefaultTypeOptions = type => |
|||
!type ? {} : allTypes[type].getDefaultOptions() |
|||
|
|||
export const getNewAction = () => templateApi({}).createAction() |
|||
export const getNewTrigger = () => templateApi({}).createTrigger() |
|||
|
|||
export const validateActions = actions => |
|||
templateApi({}).validateActions(actions) |
|||
export const validateTriggers = (triggers, actions) => |
|||
templateApi({}).validateTriggers(triggers, actions) |
|||
|
|||
export const generateFullPermissions = (hierarchy, actions) => |
|||
authApi(hierarchy, actions).generateFullPermissions() |
|||
|
|||
export const getNewAccessLevel = () => authApi().getNewAccessLevel() |
|||
|
|||
export const validateAccessLevels = (hierarchy, actions, accessLevels) => |
|||
authApi(hierarchy, actions).validateAccessLevels(accessLevels) |
|||
|
|||
export const getIndexNodes = hierarchy => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isIndex), |
|||
]) |
|||
|
|||
export const getRecordNodes = hierarchy => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isRecord), |
|||
]) |
|||
|
|||
export const getIndexSchema = hierarchy => index => |
|||
generateSchema(hierarchy, index) |
|||
|
|||
@ -1,26 +1,21 @@ |
|||
import { |
|||
eventHandlers |
|||
} from "../../../client/src/state/eventHandlers"; |
|||
import {writable} from "svelte/store"; |
|||
export { |
|||
EVENT_TYPE_MEMBER_NAME |
|||
} from "../../../client/src/state/eventHandlers"; |
|||
import { |
|||
createCoreApi |
|||
} from "../../../client/src/core"; |
|||
import { eventHandlers } from "../../../client/src/state/eventHandlers" |
|||
import { writable } from "svelte/store" |
|||
export { EVENT_TYPE_MEMBER_NAME } from "../../../client/src/state/eventHandlers" |
|||
import { createCoreApi } from "../../../client/src/core" |
|||
|
|||
export const allHandlers = (appDefinition, user) => { |
|||
export const allHandlers = (appDefinition, user) => { |
|||
const coreApi = createCoreApi(appDefinition, user) |
|||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy( |
|||
appDefinition.hierarchy |
|||
) |
|||
const store = writable({ |
|||
_bbuser: user, |
|||
}) |
|||
|
|||
const coreApi = createCoreApi(appDefinition, user); |
|||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(appDefinition.hierarchy); |
|||
const store = writable({ |
|||
_bbuser: user |
|||
}); |
|||
|
|||
const handlersObj = eventHandlers(store, coreApi); |
|||
const handlersArray = []; |
|||
for(let key in handlersObj) { |
|||
handlersArray.push({name:key, ...handlersObj[key]}); |
|||
} |
|||
return handlersArray; |
|||
} |
|||
const handlersObj = eventHandlers(store, coreApi) |
|||
const handlersArray = [] |
|||
for (let key in handlersObj) { |
|||
handlersArray.push({ name: key, ...handlersObj[key] }) |
|||
} |
|||
return handlersArray |
|||
} |
|||
|
|||
@ -1,3 +1,4 @@ |
|||
import feather from "feather-icons"; |
|||
const getIcon = (icon, size) => feather.icons[icon].toSvg({height:size||"16", width:size||"16"}); |
|||
export default getIcon; |
|||
import feather from "feather-icons" |
|||
const getIcon = (icon, size) => |
|||
feather.icons[icon].toSvg({ height: size || "16", width: size || "16" }) |
|||
export default getIcon |
|||
|
|||
@ -1,20 +1,18 @@ |
|||
import App from "./App.svelte"; |
|||
import "./global.css"; |
|||
import "./fonts.css"; |
|||
import "/assets/roboto-v20-latin-ext_latin-300"; |
|||
import "/assets/roboto-v20-latin-ext_latin-400"; |
|||
import "/assets/roboto-v20-latin-ext_latin-500"; |
|||
import "/assets/roboto-v20-latin-ext_latin-700"; |
|||
import "/assets/roboto-v20-latin-ext_latin-900"; |
|||
import "/assets/budibase-logo.png"; |
|||
import "/assets/budibase-logo-only.png"; |
|||
import "uikit/dist/css/uikit.min.css"; |
|||
import "uikit/dist/js/uikit.min.js"; |
|||
import "codemirror/lib/codemirror.css"; |
|||
import 'codemirror/theme/monokai.css'; |
|||
import App from "./App.svelte" |
|||
import "./global.css" |
|||
import "./fonts.css" |
|||
import "/assets/roboto-v20-latin-ext_latin-300" |
|||
import "/assets/roboto-v20-latin-ext_latin-400" |
|||
import "/assets/roboto-v20-latin-ext_latin-500" |
|||
import "/assets/roboto-v20-latin-ext_latin-700" |
|||
import "/assets/roboto-v20-latin-ext_latin-900" |
|||
import "/assets/budibase-logo.png" |
|||
import "/assets/budibase-logo-only.png" |
|||
import "uikit/dist/css/uikit.min.css" |
|||
import "uikit/dist/js/uikit.min.js" |
|||
import "codemirror/lib/codemirror.css" |
|||
import "codemirror/theme/monokai.css" |
|||
|
|||
const app = new App({ |
|||
target: document.getElementById("app") |
|||
}); |
|||
|
|||
|
|||
target: document.getElementById("app"), |
|||
}) |
|||
|
|||
@ -1,149 +0,0 @@ |
|||
<script> |
|||
import PropsView from "./PropsView.svelte"; |
|||
import { store } from "../builderStore"; |
|||
import IconButton from "../common/IconButton.svelte"; |
|||
<<<<<<< HEAD |
|||
import { LayoutIcon, PaintIcon, TerminalIcon, CircleIndicator } from '../common/Icons/'; |
|||
======= |
|||
import { LayoutIcon, PaintIcon, TerminalIcon, EventsIcon } from '../common/Icons/'; |
|||
>>>>>>> master |
|||
import CodeEditor from './CodeEditor.svelte'; |
|||
import LayoutEditor from './LayoutEditor.svelte'; |
|||
import EventsEditor from "./EventsEditor"; |
|||
|
|||
let current_view = 'props'; |
|||
let codeEditor; |
|||
|
|||
$: component = $store.currentComponentInfo; |
|||
$: originalName = component.name; |
|||
$: name = component.name; |
|||
$: description = component.description; |
|||
$: componentInfo = $store.currentComponentInfo; |
|||
$: components = $store.components; |
|||
|
|||
const onPropChanged = store.setComponentProp; |
|||
const onStyleChanged = store.setComponentStyle; |
|||
|
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<ul> |
|||
<li> |
|||
<button class:selected={current_view === 'props'} on:click={() => current_view = 'props'}> |
|||
<PaintIcon /> |
|||
</button> |
|||
</li> |
|||
<li> |
|||
<button class:selected={current_view === 'layout'} on:click={() => current_view = 'layout'}> |
|||
<LayoutIcon /> |
|||
</button> |
|||
</li> |
|||
<li> |
|||
<button class:selected={current_view === 'code'} on:click={() => codeEditor && codeEditor.show()}> |
|||
{#if componentInfo._code && componentInfo._code.trim().length > 0} |
|||
<div class="button-indicator"> |
|||
<CircleIndicator /> |
|||
</div> |
|||
{/if} |
|||
<TerminalIcon /> |
|||
</button> |
|||
</li> |
|||
<li> |
|||
<button class:selected={current_view === 'events'} on:click={() => current_view = 'events'}> |
|||
<EventsIcon /> |
|||
</button> |
|||
</li> |
|||
</ul> |
|||
|
|||
{#if !componentInfo.component} |
|||
<div class="component-props-container"> |
|||
|
|||
{#if current_view === 'props'} |
|||
<PropsView {componentInfo} {components} {onPropChanged} /> |
|||
{:else if current_view === 'layout'} |
|||
<LayoutEditor {onStyleChanged} {componentInfo}/> |
|||
<<<<<<< HEAD |
|||
======= |
|||
{:else if current_view === 'events'} |
|||
<EventsEditor {componentInfo} {components} {onPropChanged} /> |
|||
{:else} |
|||
<CodeEditor /> |
|||
>>>>>>> master |
|||
{/if} |
|||
|
|||
<CodeEditor |
|||
bind:this={codeEditor} |
|||
code={$store.currentComponentInfo._code} |
|||
onCodeChanged={store.setComponentCode} /> |
|||
|
|||
</div> |
|||
{:else} |
|||
<h1> This is a screen, this will be dealt with later</h1> |
|||
{/if} |
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
} |
|||
|
|||
.title > div:nth-child(1) { |
|||
grid-column-start: name; |
|||
color: var(--secondary100); |
|||
} |
|||
|
|||
.title > div:nth-child(2) { |
|||
grid-column-start: actions; |
|||
} |
|||
|
|||
.component-props-container { |
|||
margin-top: 10px; |
|||
flex: 1 1 auto; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
ul { |
|||
list-style: none; |
|||
display: flex; |
|||
padding: 0; |
|||
} |
|||
|
|||
li { |
|||
margin-right: 20px; |
|||
background: none; |
|||
border-radius: 5px; |
|||
width: 48px; |
|||
height: 48px; |
|||
} |
|||
|
|||
li button { |
|||
width: 100%; |
|||
height: 100%; |
|||
background: none; |
|||
border: none; |
|||
border-radius: 5px; |
|||
padding: 12px; |
|||
outline: none; |
|||
cursor: pointer; |
|||
position: relative; |
|||
} |
|||
|
|||
.selected { |
|||
color: var(--button-text); |
|||
background: var(--background-button)!important; |
|||
} |
|||
|
|||
.button-indicator { |
|||
position: absolute; |
|||
top: 6px; |
|||
right: 10px; |
|||
color: var(--button-text); |
|||
} |
|||
|
|||
</style> |
|||
@ -1 +1 @@ |
|||
export { default } from "./EventsEditor.svelte"; |
|||
export { default } from "./EventsEditor.svelte" |
|||
|
|||
@ -1,74 +0,0 @@ |
|||
<script> |
|||
import { some, includes, filter } from "lodash/fp"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import PropControl from "./PropControl.svelte"; |
|||
import IconButton from "../common/IconButton.svelte"; |
|||
|
|||
export let componentInfo; |
|||
export let onPropChanged = () => {}; |
|||
export let components; |
|||
|
|||
let errors = []; |
|||
let props = {}; |
|||
|
|||
<<<<<<< HEAD |
|||
const props_to_ignore = ['_component','_children', '_layout', '_code', '_id']; |
|||
======= |
|||
const props_to_ignore = ['_component','_children', '_styles', '_id']; |
|||
>>>>>>> master |
|||
|
|||
$: propDefs = componentInfo && Object.entries(componentInfo).filter(([name])=> !props_to_ignore.includes(name)); |
|||
|
|||
function find_type(prop_name) { |
|||
if(!componentInfo._component) return; |
|||
return components.find(({name}) => name === componentInfo._component).props[prop_name]; |
|||
} |
|||
|
|||
let setProp = (name, value) => { |
|||
onPropChanged(name, value); |
|||
} |
|||
|
|||
const fieldHasError = (propName) => |
|||
some(e => e.propName === propName)(errors); |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<form class="uk-form-stacked form-root"> |
|||
{#each propDefs as [prop_name, prop_value], index} |
|||
|
|||
<div class="prop-container"> |
|||
|
|||
<PropControl {setProp} |
|||
{prop_name} |
|||
{prop_value} |
|||
prop_type={find_type(prop_name)} |
|||
{index} |
|||
disabled={false} /> |
|||
|
|||
</div> |
|||
|
|||
{/each} |
|||
|
|||
</form> |
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
.root { |
|||
font-size:10pt; |
|||
width: 100%; |
|||
} |
|||
|
|||
.form-root { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.prop-container { |
|||
flex: 1 1 auto; |
|||
min-width: 250px; |
|||
} |
|||
</style> |
|||
@ -1,60 +1,57 @@ |
|||
import { |
|||
getComponentInfo, createProps, getInstanceProps |
|||
} from "./createProps"; |
|||
import { getComponentInfo, createProps, getInstanceProps } from "./createProps" |
|||
|
|||
export const buildPropsHierarchy = (components, screens, baseComponent) => { |
|||
|
|||
const allComponents = [...components, ...screens]; |
|||
|
|||
const buildProps = (componentDefinition, derivedFromProps) => { |
|||
|
|||
const {props} = createProps(componentDefinition, derivedFromProps); |
|||
const propsDefinition = componentDefinition.props; |
|||
props._component = componentDefinition.name; |
|||
for(let propName in props) { |
|||
if(propName === "_component") continue; |
|||
|
|||
const propDef = propsDefinition[propName]; |
|||
if(!propDef) continue; |
|||
if(propName === "_children") { |
|||
|
|||
const childrenProps = props[propName]; |
|||
|
|||
if(!childrenProps |
|||
|| childrenProps.length === 0) { |
|||
continue; |
|||
} |
|||
|
|||
props[propName] = []; |
|||
|
|||
for(let child of childrenProps) { |
|||
const propComponentInfo = getComponentInfo( |
|||
allComponents, child._component); |
|||
|
|||
const subComponentInstanceProps = getInstanceProps( |
|||
propComponentInfo, |
|||
child |
|||
); |
|||
|
|||
props[propName].push( |
|||
buildProps( |
|||
propComponentInfo.rootComponent.name, |
|||
propComponentInfo.propsDefinition, |
|||
subComponentInstanceProps)); |
|||
} |
|||
} |
|||
const allComponents = [...components, ...screens] |
|||
|
|||
const buildProps = (componentDefinition, derivedFromProps) => { |
|||
const { props } = createProps(componentDefinition, derivedFromProps) |
|||
const propsDefinition = componentDefinition.props |
|||
props._component = componentDefinition.name |
|||
for (let propName in props) { |
|||
if (propName === "_component") continue |
|||
|
|||
const propDef = propsDefinition[propName] |
|||
if (!propDef) continue |
|||
if (propName === "_children") { |
|||
const childrenProps = props[propName] |
|||
|
|||
if (!childrenProps || childrenProps.length === 0) { |
|||
continue |
|||
} |
|||
|
|||
return props; |
|||
|
|||
props[propName] = [] |
|||
|
|||
for (let child of childrenProps) { |
|||
const propComponentInfo = getComponentInfo( |
|||
allComponents, |
|||
child._component |
|||
) |
|||
|
|||
const subComponentInstanceProps = getInstanceProps( |
|||
propComponentInfo, |
|||
child |
|||
) |
|||
|
|||
props[propName].push( |
|||
buildProps( |
|||
propComponentInfo.rootComponent.name, |
|||
propComponentInfo.propsDefinition, |
|||
subComponentInstanceProps |
|||
) |
|||
) |
|||
} |
|||
} |
|||
} |
|||
|
|||
if(!baseComponent) return {}; |
|||
return props |
|||
} |
|||
|
|||
const baseComponentInfo = getComponentInfo(allComponents, baseComponent); |
|||
if (!baseComponent) return {} |
|||
|
|||
return buildProps( |
|||
baseComponentInfo.rootComponent, |
|||
baseComponentInfo.fullProps); |
|||
const baseComponentInfo = getComponentInfo(allComponents, baseComponent) |
|||
|
|||
} |
|||
return buildProps( |
|||
baseComponentInfo.rootComponent, |
|||
baseComponentInfo.fullProps |
|||
) |
|||
} |
|||
|
|||
@ -1,17 +1,16 @@ |
|||
export const defaultPagesObject = () => ({ |
|||
main: { |
|||
index: { |
|||
_component : "./components/indexHtml" |
|||
}, |
|||
appBody: "bbapp.main.json" |
|||
main: { |
|||
index: { |
|||
_component: "./components/indexHtml", |
|||
}, |
|||
unauthenticated: { |
|||
index: { |
|||
_component : "./components/indexHtml" |
|||
}, |
|||
appBody: "bbapp.unauthenticated.json" |
|||
appBody: "bbapp.main.json", |
|||
}, |
|||
unauthenticated: { |
|||
index: { |
|||
_component: "./components/indexHtml", |
|||
}, |
|||
componentLibraries: [], |
|||
stylesheets: [] |
|||
|
|||
}); |
|||
appBody: "bbapp.unauthenticated.json", |
|||
}, |
|||
componentLibraries: [], |
|||
stylesheets: [], |
|||
}) |
|||
|
|||
@ -1,89 +1,80 @@ |
|||
import { splitName } from "./splitRootComponentName"; |
|||
import { |
|||
find, filter, cloneDeep, isPlainObject, |
|||
isArray |
|||
} from "lodash/fp"; |
|||
import { isRootComponent } from "./searchComponents"; |
|||
import { splitName } from "./splitRootComponentName" |
|||
import { find, filter, cloneDeep, isPlainObject, isArray } from "lodash/fp" |
|||
import { isRootComponent } from "./searchComponents" |
|||
|
|||
export const libraryDependencies = (components, lib) => { |
|||
|
|||
const componentDependsOnLibrary = comp => { |
|||
if(isRootComponent(comp)) { |
|||
const {libName} = splitName(component.name); |
|||
return (libName === lib); |
|||
} |
|||
return componentDependsOnLibrary( |
|||
find(c => c.name === comp.props._component)( |
|||
components) |
|||
); |
|||
const componentDependsOnLibrary = comp => { |
|||
if (isRootComponent(comp)) { |
|||
const { libName } = splitName(component.name) |
|||
return libName === lib |
|||
} |
|||
|
|||
return filter(c => !isRootComponent(c) |
|||
&& componentDependsOnLibrary(c))( |
|||
components |
|||
); |
|||
return componentDependsOnLibrary( |
|||
find(c => c.name === comp.props._component)(components) |
|||
) |
|||
} |
|||
|
|||
return filter(c => !isRootComponent(c) && componentDependsOnLibrary(c))( |
|||
components |
|||
) |
|||
} |
|||
|
|||
export const componentDependencies = (pages, screens, components, dependsOn) => { |
|||
|
|||
const allComponents = [ |
|||
...cloneDeep(components), |
|||
...cloneDeep(screens)]; |
|||
|
|||
pages = cloneDeep(pages); |
|||
const dependantComponents = []; |
|||
const dependantPages = []; |
|||
|
|||
const traverseProps = (props) => { |
|||
|
|||
if(props._component && props._component === dependsOn.name) { |
|||
return true; |
|||
} |
|||
export const componentDependencies = ( |
|||
pages, |
|||
screens, |
|||
components, |
|||
dependsOn |
|||
) => { |
|||
const allComponents = [...cloneDeep(components), ...cloneDeep(screens)] |
|||
|
|||
pages = cloneDeep(pages) |
|||
const dependantComponents = [] |
|||
const dependantPages = [] |
|||
|
|||
const traverseProps = props => { |
|||
if (props._component && props._component === dependsOn.name) { |
|||
return true |
|||
} |
|||
|
|||
for(let propName in props) { |
|||
const prop = props[propName]; |
|||
if(isPlainObject(prop) && prop._component) { |
|||
if(traverseProps(prop)) return true; |
|||
} |
|||
if(isArray(prop)) { |
|||
for(let element of prop) { |
|||
if(traverseProps(element)) return true; |
|||
} |
|||
} |
|||
for (let propName in props) { |
|||
const prop = props[propName] |
|||
if (isPlainObject(prop) && prop._component) { |
|||
if (traverseProps(prop)) return true |
|||
} |
|||
if (isArray(prop)) { |
|||
for (let element of prop) { |
|||
if (traverseProps(element)) return true |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
for(let component of allComponents) { |
|||
|
|||
if(isRootComponent(component)) { |
|||
continue; |
|||
} |
|||
|
|||
if(component.name === dependsOn.name) { |
|||
continue; |
|||
} |
|||
for (let component of allComponents) { |
|||
if (isRootComponent(component)) { |
|||
continue |
|||
} |
|||
|
|||
if(component.props._component === dependsOn.name) { |
|||
dependantComponents.push(component); |
|||
continue; |
|||
} |
|||
|
|||
if(traverseProps(component.props)) { |
|||
dependantComponents.push(component); |
|||
} |
|||
if (component.name === dependsOn.name) { |
|||
continue |
|||
} |
|||
|
|||
if (component.props._component === dependsOn.name) { |
|||
dependantComponents.push(component) |
|||
continue |
|||
} |
|||
|
|||
for(let pageName in pages) { |
|||
const page = pages[pageName]; |
|||
if(page.appBody === dependsOn.name) { |
|||
dependantPages.push(pageName); |
|||
} |
|||
if (traverseProps(component.props)) { |
|||
dependantComponents.push(component) |
|||
} |
|||
} |
|||
|
|||
return {dependantComponents, dependantPages}; |
|||
for (let pageName in pages) { |
|||
const page = pages[pageName] |
|||
if (page.appBody === dependsOn.name) { |
|||
dependantPages.push(pageName) |
|||
} |
|||
} |
|||
|
|||
} |
|||
return { dependantComponents, dependantPages } |
|||
} |
|||
|
|||
@ -1,10 +1,10 @@ |
|||
import { isRootComponent } from "./searchComponents"; |
|||
import { find } from "lodash/fp"; |
|||
import { isRootComponent } from "./searchComponents" |
|||
import { find } from "lodash/fp" |
|||
|
|||
export const getRootComponent = (componentName, components) => { |
|||
const component = find(c => c.name === componentName)(components); |
|||
const component = find(c => c.name === componentName)(components) |
|||
|
|||
if(isRootComponent(component)) return component; |
|||
if (isRootComponent(component)) return component |
|||
|
|||
return getRootComponent(component.props._component, components); |
|||
} |
|||
return getRootComponent(component.props._component, components) |
|||
} |
|||
|
|||
@ -1,71 +1,64 @@ |
|||
import { |
|||
isPlainObject, isArray, cloneDeep |
|||
} from "lodash/fp"; |
|||
import { |
|||
getExactComponent |
|||
} from "./searchComponents"; |
|||
import { isPlainObject, isArray, cloneDeep } from "lodash/fp" |
|||
import { getExactComponent } from "./searchComponents" |
|||
|
|||
export const rename = (pages, screens, oldname, newname) => { |
|||
pages = cloneDeep(pages) |
|||
screens = cloneDeep(screens) |
|||
const changedScreens = [] |
|||
|
|||
pages = cloneDeep(pages); |
|||
screens = cloneDeep(screens); |
|||
const changedScreens = []; |
|||
|
|||
const existingWithNewName = getExactComponent(screens, newname); |
|||
if(existingWithNewName) return { |
|||
components: screens, pages, error: "Component by that name already exists" |
|||
}; |
|||
|
|||
const traverseProps = (props) => { |
|||
let hasEdited = false; |
|||
if(props._component && props._component === oldname) { |
|||
props._component = newname; |
|||
hasEdited = true; |
|||
} |
|||
|
|||
for(let propName in props) { |
|||
const prop = props[propName]; |
|||
if(isPlainObject(prop) && prop._component) { |
|||
hasEdited = traverseProps(prop) || hasEdited; |
|||
} |
|||
if(isArray(prop)) { |
|||
for(let element of prop) { |
|||
hasEdited = traverseProps(element) || hasEdited; |
|||
} |
|||
} |
|||
} |
|||
return hasEdited; |
|||
const existingWithNewName = getExactComponent(screens, newname) |
|||
if (existingWithNewName) |
|||
return { |
|||
components: screens, |
|||
pages, |
|||
error: "Component by that name already exists", |
|||
} |
|||
|
|||
const traverseProps = props => { |
|||
let hasEdited = false |
|||
if (props._component && props._component === oldname) { |
|||
props._component = newname |
|||
hasEdited = true |
|||
} |
|||
|
|||
for(let screen of screens) { |
|||
|
|||
let hasEdited = false; |
|||
|
|||
if(screen.name === oldname) { |
|||
screen.name = newname; |
|||
hasEdited = true; |
|||
for (let propName in props) { |
|||
const prop = props[propName] |
|||
if (isPlainObject(prop) && prop._component) { |
|||
hasEdited = traverseProps(prop) || hasEdited |
|||
} |
|||
if (isArray(prop)) { |
|||
for (let element of prop) { |
|||
hasEdited = traverseProps(element) || hasEdited |
|||
} |
|||
} |
|||
} |
|||
return hasEdited |
|||
} |
|||
|
|||
if(screen.props._component === oldname) { |
|||
screen.props._component = newname; |
|||
hasEdited = true; |
|||
} |
|||
|
|||
hasEdited = traverseProps(screen.props) || hasEdited; |
|||
for (let screen of screens) { |
|||
let hasEdited = false |
|||
|
|||
if(hasEdited && screen.name !== newname) |
|||
changedScreens.push(screen.name); |
|||
if (screen.name === oldname) { |
|||
screen.name = newname |
|||
hasEdited = true |
|||
} |
|||
|
|||
for(let pageName in pages) { |
|||
const page = pages[pageName]; |
|||
if(page.appBody === oldname) { |
|||
page.appBody = newname; |
|||
} |
|||
if (screen.props._component === oldname) { |
|||
screen.props._component = newname |
|||
hasEdited = true |
|||
} |
|||
|
|||
return {screens, pages, changedScreens}; |
|||
hasEdited = traverseProps(screen.props) || hasEdited |
|||
|
|||
if (hasEdited && screen.name !== newname) changedScreens.push(screen.name) |
|||
} |
|||
|
|||
for (let pageName in pages) { |
|||
const page = pages[pageName] |
|||
if (page.appBody === oldname) { |
|||
page.appBody = newname |
|||
} |
|||
} |
|||
|
|||
} |
|||
return { screens, pages, changedScreens } |
|||
} |
|||
|
|||
@ -1,67 +1,47 @@ |
|||
import {pipe} from "../../common/core"; |
|||
import { pipe } from "../../common/core" |
|||
|
|||
import { |
|||
find, |
|||
isUndefined, |
|||
filter, |
|||
some, |
|||
includes, |
|||
has |
|||
} from "lodash/fp"; |
|||
import { find, isUndefined, filter, some, includes, has } from "lodash/fp" |
|||
|
|||
const normalString = s => (s||"").trim().toLowerCase(); |
|||
const normalString = s => (s || "").trim().toLowerCase() |
|||
|
|||
export const isRootComponent = c => |
|||
isComponent(c) && isUndefined(c.props._component); |
|||
export const isRootComponent = c => |
|||
isComponent(c) && isUndefined(c.props._component) |
|||
|
|||
export const isComponent = c => { |
|||
const hasProp = (n) => !isUndefined(c[n]); |
|||
return hasProp("name") && hasProp("props"); |
|||
const hasProp = n => !isUndefined(c[n]) |
|||
return hasProp("name") && hasProp("props") |
|||
} |
|||
|
|||
export const searchAllComponents = (components, phrase) => { |
|||
const hasPhrase = (...vals) => |
|||
pipe(vals, [some(v => includes(normalString(phrase))(normalString(v)))]) |
|||
|
|||
const hasPhrase = (...vals) => |
|||
pipe(vals, [ |
|||
some(v => includes(normalString(phrase))(normalString(v))) |
|||
]); |
|||
const componentMatches = c => { |
|||
if (hasPhrase(c.name, ...(c.tags || []))) return true |
|||
|
|||
const componentMatches = c => { |
|||
if(hasPhrase(c.name, ...(c.tags || []))) return true; |
|||
if (isRootComponent(c)) return false |
|||
|
|||
if(isRootComponent(c)) return false; |
|||
|
|||
const parent = getExactComponent( |
|||
components, |
|||
c.props._component); |
|||
const parent = getExactComponent(components, c.props._component) |
|||
|
|||
return componentMatches(parent); |
|||
} |
|||
return componentMatches(parent) |
|||
} |
|||
|
|||
return filter(componentMatches)(components); |
|||
} |
|||
return filter(componentMatches)(components) |
|||
} |
|||
|
|||
export const getExactComponent = (components, name) => { |
|||
|
|||
const stringEquals = (s1, s2) => |
|||
normalString(s1) === normalString(s2); |
|||
|
|||
return pipe(components,[ |
|||
find(c => stringEquals(c.name, name)) |
|||
]); |
|||
} |
|||
const stringEquals = (s1, s2) => normalString(s1) === normalString(s2) |
|||
|
|||
export const getAncestorProps = (components, name, found=[]) => { |
|||
const thisComponent = getExactComponent( |
|||
components, name); |
|||
return pipe(components, [find(c => stringEquals(c.name, name))]) |
|||
} |
|||
|
|||
if(isRootComponent(thisComponent)) |
|||
return [thisComponent.props, ...found]; |
|||
export const getAncestorProps = (components, name, found = []) => { |
|||
const thisComponent = getExactComponent(components, name) |
|||
|
|||
return getAncestorProps( |
|||
components, |
|||
thisComponent.props._component, |
|||
[{...thisComponent.props}, |
|||
...found]); |
|||
if (isRootComponent(thisComponent)) return [thisComponent.props, ...found] |
|||
|
|||
return getAncestorProps(components, thisComponent.props._component, [ |
|||
{ ...thisComponent.props }, |
|||
...found, |
|||
]) |
|||
} |
|||
|
|||
@ -1,18 +1,14 @@ |
|||
import { |
|||
split, |
|||
last |
|||
} from "lodash/fp"; |
|||
import { split, last } from "lodash/fp" |
|||
|
|||
import { pipe } from "../../common/core"; |
|||
import { pipe } from "../../common/core" |
|||
|
|||
export const splitName = fullname => { |
|||
const componentName = pipe(fullname, [ |
|||
split("/"), |
|||
last |
|||
]); |
|||
const componentName = pipe(fullname, [split("/"), last]) |
|||
|
|||
const libName =fullname.substring( |
|||
0, fullname.length - componentName.length - 1); |
|||
const libName = fullname.substring( |
|||
0, |
|||
fullname.length - componentName.length - 1 |
|||
) |
|||
|
|||
return {libName, componentName}; |
|||
} |
|||
return { libName, componentName } |
|||
} |
|||
|
|||
@ -1,89 +1,86 @@ |
|||
import { |
|||
isString, |
|||
isBoolean, |
|||
isNumber, |
|||
isArray, |
|||
isObjectLike, |
|||
isPlainObject, |
|||
every, |
|||
isUndefined |
|||
} from "lodash/fp"; |
|||
isString, |
|||
isBoolean, |
|||
isNumber, |
|||
isArray, |
|||
isObjectLike, |
|||
isPlainObject, |
|||
every, |
|||
isUndefined, |
|||
} from "lodash/fp" |
|||
|
|||
import { |
|||
EVENT_TYPE_MEMBER_NAME |
|||
} from "../../common/eventHandlers"; |
|||
import { EVENT_TYPE_MEMBER_NAME } from "../../common/eventHandlers" |
|||
|
|||
import { |
|||
isBound, BB_STATE_BINDINGPATH |
|||
} from "@budibase/client/src/state/isState"; |
|||
isBound, |
|||
BB_STATE_BINDINGPATH, |
|||
} from "@budibase/client/src/state/isState" |
|||
|
|||
const defaultDef = typeName => () => ({ |
|||
type: typeName, |
|||
required:false, |
|||
default:types[typeName].default(), |
|||
options: typeName === "options" ? [] : undefined |
|||
}); |
|||
type: typeName, |
|||
required: false, |
|||
default: types[typeName].default(), |
|||
options: typeName === "options" ? [] : undefined, |
|||
}) |
|||
|
|||
const propType = (defaultValue, isOfType, defaultDefinition) => ({ |
|||
isOfType, default:defaultValue, defaultDefinition |
|||
}); |
|||
isOfType, |
|||
default: defaultValue, |
|||
defaultDefinition, |
|||
}) |
|||
|
|||
const expandSingleProp = propDef => { |
|||
const p = isString(propDef) |
|||
? types[propDef].defaultDefinition() |
|||
: propDef; |
|||
|
|||
if(!isString(propDef)) { |
|||
const def = types[propDef.type].defaultDefinition(); |
|||
for(let p in def) { |
|||
if(propDef[p] === undefined) { |
|||
propDef[p] = def[p]; |
|||
} |
|||
} |
|||
const p = isString(propDef) ? types[propDef].defaultDefinition() : propDef |
|||
|
|||
if (!isString(propDef)) { |
|||
const def = types[propDef.type].defaultDefinition() |
|||
for (let p in def) { |
|||
if (propDef[p] === undefined) { |
|||
propDef[p] = def[p] |
|||
} |
|||
} |
|||
} |
|||
|
|||
return p; |
|||
return p |
|||
} |
|||
|
|||
export const expandComponentDefinition = componentDefinition => { |
|||
const expandedProps = {}; |
|||
const expandedComponent = {...componentDefinition}; |
|||
const expandedProps = {} |
|||
const expandedComponent = { ...componentDefinition } |
|||
|
|||
for(let p in componentDefinition.props) { |
|||
expandedProps[p] = expandSingleProp( |
|||
componentDefinition.props[p]); |
|||
} |
|||
for (let p in componentDefinition.props) { |
|||
expandedProps[p] = expandSingleProp(componentDefinition.props[p]) |
|||
} |
|||
|
|||
expandedComponent.props = expandedProps; |
|||
expandedComponent.props = expandedProps |
|||
|
|||
if(expandedComponent.children !== false) { |
|||
expandedComponent.children = true; |
|||
} |
|||
if (expandedComponent.children !== false) { |
|||
expandedComponent.children = true |
|||
} |
|||
|
|||
return expandedComponent; |
|||
return expandedComponent |
|||
} |
|||
|
|||
const isComponent = isObjectLike; |
|||
const isEvent = e => |
|||
isPlainObject(e) |
|||
&& isString(e[EVENT_TYPE_MEMBER_NAME]) |
|||
&& isPlainObject(e.parameters); |
|||
const isComponent = isObjectLike |
|||
const isEvent = e => |
|||
isPlainObject(e) && |
|||
isString(e[EVENT_TYPE_MEMBER_NAME]) && |
|||
isPlainObject(e.parameters) |
|||
|
|||
const isEventList = e => |
|||
isArray(e) && every(isEvent)(e); |
|||
const isEventList = e => isArray(e) && every(isEvent)(e) |
|||
|
|||
const emptyState = () => { |
|||
const s = {}; |
|||
s[BB_STATE_BINDINGPATH] = ""; |
|||
return s; |
|||
const s = {} |
|||
s[BB_STATE_BINDINGPATH] = "" |
|||
return s |
|||
} |
|||
|
|||
export const types = { |
|||
string: propType(() => "", isString, defaultDef("string")), |
|||
bool: propType(() => false, isBoolean, defaultDef("bool")), |
|||
number: propType(() => 0, isNumber, defaultDef("number")), |
|||
options: propType(() => "", isString, defaultDef("options")), |
|||
asset: propType(() => "", isString, defaultDef("asset")), |
|||
event: propType(() => [], isEventList, defaultDef("event")), |
|||
state: propType(() => emptyState(), isBound, defaultDef("state")) |
|||
}; |
|||
string: propType(() => "", isString, defaultDef("string")), |
|||
bool: propType(() => false, isBoolean, defaultDef("bool")), |
|||
number: propType(() => 0, isNumber, defaultDef("number")), |
|||
options: propType(() => "", isString, defaultDef("options")), |
|||
asset: propType(() => "", isString, defaultDef("asset")), |
|||
event: propType(() => [], isEventList, defaultDef("event")), |
|||
state: propType(() => emptyState(), isBound, defaultDef("state")), |
|||
} |
|||
|
|||
@ -1,31 +1,25 @@ |
|||
import { componentsAndScreens } from "./testData"; |
|||
import { |
|||
find |
|||
} from "lodash/fp"; |
|||
import { buildPropsHierarchy } from "../src/userInterface/pagesParsing/buildPropsHierarchy"; |
|||
import { componentsAndScreens } from "./testData" |
|||
import { find } from "lodash/fp" |
|||
import { buildPropsHierarchy } from "../src/userInterface/pagesParsing/buildPropsHierarchy" |
|||
|
|||
describe("buildPropsHierarchy", () => { |
|||
it("should build a complex component children", () => { |
|||
const { components, screens } = componentsAndScreens() |
|||
|
|||
const allprops = buildPropsHierarchy(components, screens, "ButtonGroup") |
|||
|
|||
it("should build a complex component children", () => { |
|||
expect(allprops._component).toEqual("budibase-components/div") |
|||
|
|||
const {components, screens} = componentsAndScreens(); |
|||
const primaryButtonProps = () => ({ |
|||
_component: "budibase-components/Button", |
|||
}) |
|||
|
|||
const allprops = buildPropsHierarchy( |
|||
components, screens, "ButtonGroup"); |
|||
const button1 = primaryButtonProps() |
|||
button1.contentText = "Button 1" |
|||
expect(allprops._children[0]).toEqual(button1) |
|||
|
|||
expect(allprops._component).toEqual("budibase-components/div"); |
|||
|
|||
const primaryButtonProps = () => ({ |
|||
_component: "budibase-components/Button" |
|||
}); |
|||
|
|||
const button1 = primaryButtonProps(); |
|||
button1.contentText = "Button 1"; |
|||
expect(allprops._children[0]).toEqual(button1); |
|||
|
|||
const button2 = primaryButtonProps(); |
|||
button2.contentText = "Button 2"; |
|||
expect(allprops._children[1]).toEqual(button2) |
|||
}); |
|||
}); |
|||
const button2 = primaryButtonProps() |
|||
button2.contentText = "Button 2" |
|||
expect(allprops._children[1]).toEqual(button2) |
|||
}) |
|||
}) |
|||
|
|||
@ -1,52 +1,55 @@ |
|||
import { |
|||
componentDependencies |
|||
} from "../src/userInterface/pagesParsing/findDependencies"; |
|||
import { componentsAndScreens } from "./testData"; |
|||
import { componentDependencies } from "../src/userInterface/pagesParsing/findDependencies" |
|||
import { componentsAndScreens } from "./testData" |
|||
import { some, find } from "lodash/fp" |
|||
|
|||
describe("component dependencies", () => { |
|||
|
|||
const contains = (result, name) => |
|||
some(c => c.name === name)(result); |
|||
|
|||
const get = (all, name) => |
|||
find(c => c.name === name)(all); |
|||
|
|||
it("should include component that inheirts", () => { |
|||
|
|||
const {components, screens} = componentsAndScreens(); |
|||
|
|||
const result = componentDependencies( |
|||
{}, screens, components, |
|||
get([...components, ...screens], "budibase-components/TextBox")); |
|||
|
|||
expect(contains(result.dependantComponents, "common/SmallTextbox")).toBe(true); |
|||
|
|||
}); |
|||
|
|||
it("should include component that nests", () => { |
|||
const {components, screens} = componentsAndScreens(); |
|||
|
|||
const result = componentDependencies( |
|||
{}, screens, components, |
|||
get([...components, ...screens], "budibase-components/Button")); |
|||
|
|||
expect(contains(result.dependantComponents, "ButtonGroup")).toBe(true); |
|||
|
|||
}); |
|||
|
|||
it("should include components n page apbody", () => { |
|||
const {components, screens} = componentsAndScreens(); |
|||
const pages = { |
|||
main: { |
|||
appBody: "PrimaryButton" |
|||
} |
|||
}; |
|||
|
|||
const result = componentDependencies( |
|||
pages, screens, components, |
|||
get([...components, ...screens], "PrimaryButton")); |
|||
|
|||
expect(result.dependantPages).toEqual(["main"]); |
|||
}); |
|||
}) |
|||
const contains = (result, name) => some(c => c.name === name)(result) |
|||
|
|||
const get = (all, name) => find(c => c.name === name)(all) |
|||
|
|||
it("should include component that inheirts", () => { |
|||
const { components, screens } = componentsAndScreens() |
|||
|
|||
const result = componentDependencies( |
|||
{}, |
|||
screens, |
|||
components, |
|||
get([...components, ...screens], "budibase-components/TextBox") |
|||
) |
|||
|
|||
expect(contains(result.dependantComponents, "common/SmallTextbox")).toBe( |
|||
true |
|||
) |
|||
}) |
|||
|
|||
it("should include component that nests", () => { |
|||
const { components, screens } = componentsAndScreens() |
|||
|
|||
const result = componentDependencies( |
|||
{}, |
|||
screens, |
|||
components, |
|||
get([...components, ...screens], "budibase-components/Button") |
|||
) |
|||
|
|||
expect(contains(result.dependantComponents, "ButtonGroup")).toBe(true) |
|||
}) |
|||
|
|||
it("should include components n page apbody", () => { |
|||
const { components, screens } = componentsAndScreens() |
|||
const pages = { |
|||
main: { |
|||
appBody: "PrimaryButton", |
|||
}, |
|||
} |
|||
|
|||
const result = componentDependencies( |
|||
pages, |
|||
screens, |
|||
components, |
|||
get([...components, ...screens], "PrimaryButton") |
|||
) |
|||
|
|||
expect(result.dependantPages).toEqual(["main"]) |
|||
}) |
|||
}) |
|||
|
|||
@ -1,232 +1,212 @@ |
|||
import { |
|||
createProps, |
|||
} from "../src/userInterface/pagesParsing/createProps"; |
|||
import { |
|||
keys, some |
|||
} from "lodash/fp"; |
|||
import { |
|||
BB_STATE_BINDINGPATH |
|||
} from "@budibase/client/src/state/isState"; |
|||
import { createProps } from "../src/userInterface/pagesParsing/createProps" |
|||
import { keys, some } from "lodash/fp" |
|||
import { BB_STATE_BINDINGPATH } from "@budibase/client/src/state/isState" |
|||
|
|||
describe("createDefaultProps", () => { |
|||
const getcomponent = () => ({ |
|||
name: "some_component", |
|||
props: { |
|||
fieldName: { type: "string", default: "something" }, |
|||
}, |
|||
}) |
|||
|
|||
const getcomponent = () => ({ |
|||
name:"some_component", |
|||
props: { |
|||
fieldName: {type:"string", default:"something"} |
|||
} |
|||
}); |
|||
it("should create a object with single string value, when default string field set", () => { |
|||
const { props, errors } = createProps(getcomponent()) |
|||
|
|||
it("should create a object with single string value, when default string field set", () => { |
|||
|
|||
const { props, errors } = createProps(getcomponent()); |
|||
expect(errors).toEqual([]) |
|||
expect(props.fieldName).toBeDefined() |
|||
expect(props.fieldName).toBe("something") |
|||
expect(keys(props).length).toBe(3) |
|||
}) |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props.fieldName).toBeDefined(); |
|||
expect(props.fieldName).toBe("something"); |
|||
expect(keys(props).length).toBe(3); |
|||
}); |
|||
it("should set component name", () => { |
|||
const { props, errors } = createProps(getcomponent()) |
|||
|
|||
it("should set component name", () => { |
|||
expect(errors).toEqual([]) |
|||
expect(props._component).toBe("some_component") |
|||
}) |
|||
|
|||
const { props, errors } = createProps(getcomponent()); |
|||
it("should return error when component name not supplied", () => { |
|||
const comp = getcomponent() |
|||
comp.name = "" |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props._component).toBe("some_component"); |
|||
}); |
|||
const { errors } = createProps(comp) |
|||
|
|||
it("should return error when component name not supplied", () => { |
|||
const comp = getcomponent(); |
|||
comp.name = ""; |
|||
expect(errors.length).toEqual(1) |
|||
}) |
|||
|
|||
const { errors } = createProps(comp); |
|||
it("should create a object with single blank string value, when no default", () => { |
|||
const comp = getcomponent() |
|||
comp.props.fieldName = { type: "string" } |
|||
|
|||
expect(errors.length).toEqual(1); |
|||
}); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
it("should create a object with single blank string value, when no default", () => { |
|||
const comp = getcomponent(); |
|||
comp.props.fieldName = {type:"string"}; |
|||
expect(errors).toEqual([]) |
|||
expect(props.fieldName).toBeDefined() |
|||
expect(props.fieldName).toBe("") |
|||
}) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
it("should create a object with single blank string value, when prop definition is 'string' ", () => { |
|||
const comp = getcomponent() |
|||
comp.props.fieldName = "string" |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props.fieldName).toBeDefined(); |
|||
expect(props.fieldName).toBe(""); |
|||
}); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
it("should create a object with single blank string value, when prop definition is 'string' ", () => { |
|||
const comp = getcomponent(); |
|||
comp.props.fieldName = "string"; |
|||
expect(errors).toEqual([]) |
|||
expect(props.fieldName).toBeDefined() |
|||
expect(props.fieldName).toBe("") |
|||
}) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
it("should create a object with single fals value, when prop definition is 'bool' ", () => { |
|||
const comp = getcomponent() |
|||
comp.props.isVisible = "bool" |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props.fieldName).toBeDefined(); |
|||
expect(props.fieldName).toBe(""); |
|||
}); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
it("should create a object with single fals value, when prop definition is 'bool' ", () => { |
|||
const comp = getcomponent(); |
|||
comp.props.isVisible = "bool"; |
|||
expect(errors).toEqual([]) |
|||
expect(props.isVisible).toBeDefined() |
|||
expect(props.isVisible).toBe(false) |
|||
}) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
it("should create a object with single 0 value, when prop definition is 'number' ", () => { |
|||
const comp = getcomponent() |
|||
comp.props.width = "number" |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props.isVisible).toBeDefined(); |
|||
expect(props.isVisible).toBe(false); |
|||
}); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
it("should create a object with single 0 value, when prop definition is 'number' ", () => { |
|||
|
|||
const comp = getcomponent(); |
|||
comp.props.width = "number"; |
|||
expect(errors).toEqual([]) |
|||
expect(props.width).toBeDefined() |
|||
expect(props.width).toBe(0) |
|||
}) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
it("should create a object with empty _children array, when children===true ", () => { |
|||
const comp = getcomponent() |
|||
comp.children = true |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props.width).toBeDefined(); |
|||
expect(props.width).toBe(0); |
|||
}); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
it("should create a object with empty _children array, when children===true ", () => { |
|||
const comp = getcomponent(); |
|||
comp.children = true; |
|||
expect(errors).toEqual([]) |
|||
expect(props._children).toBeDefined() |
|||
expect(props._children).toEqual([]) |
|||
}) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
it("should create a object without _children array, when children===false ", () => { |
|||
const comp = getcomponent() |
|||
comp.children = false |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props._children).toBeDefined(); |
|||
expect(props._children).toEqual([]); |
|||
}); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
it("should create a object without _children array, when children===false ", () => { |
|||
const comp = getcomponent(); |
|||
comp.children = false; |
|||
expect(errors).toEqual([]) |
|||
expect(props._children).not.toBeDefined() |
|||
}) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
it("should create a object with single empty array, when prop definition is 'event' ", () => { |
|||
const comp = getcomponent() |
|||
comp.props.onClick = "event" |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props._children).not.toBeDefined(); |
|||
}); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
it("should create a object with single empty array, when prop definition is 'event' ", () => { |
|||
expect(errors).toEqual([]) |
|||
expect(props.onClick).toBeDefined() |
|||
expect(props.onClick).toEqual([]) |
|||
}) |
|||
|
|||
const comp = getcomponent(); |
|||
comp.props.onClick = "event"; |
|||
it("should create a object with empty state when prop def is 'state' ", () => { |
|||
const comp = getcomponent() |
|||
comp.props.data = "state" |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props.onClick).toBeDefined(); |
|||
expect(props.onClick).toEqual([]); |
|||
}); |
|||
expect(errors).toEqual([]) |
|||
expect(props.data[BB_STATE_BINDINGPATH]).toBeDefined() |
|||
expect(props.data[BB_STATE_BINDINGPATH]).toBe("") |
|||
}) |
|||
|
|||
it("should create a object with empty state when prop def is 'state' ", () => { |
|||
it("should create a object children array when children == true ", () => { |
|||
const comp = getcomponent() |
|||
comp.children = true |
|||
|
|||
const comp = getcomponent(); |
|||
comp.props.data = "state"; |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
expect(errors).toEqual([]) |
|||
expect(props._children).toBeDefined() |
|||
expect(props._children).toEqual([]) |
|||
}) |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props.data[BB_STATE_BINDINGPATH]).toBeDefined(); |
|||
expect(props.data[BB_STATE_BINDINGPATH]).toBe(""); |
|||
}); |
|||
it("should create a _children array when children not defined ", () => { |
|||
const comp = getcomponent() |
|||
|
|||
it("should create a object children array when children == true ", () => { |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
const comp = getcomponent(); |
|||
comp.children = true; |
|||
expect(errors).toEqual([]) |
|||
expect(props._children).toBeDefined() |
|||
expect(props._children).toEqual([]) |
|||
}) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
it("should not create _children array when children=false ", () => { |
|||
const comp = getcomponent() |
|||
comp.children = false |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props._children).toBeDefined(); |
|||
expect(props._children).toEqual([]); |
|||
}); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
it("should create a _children array when children not defined ", () => { |
|||
expect(errors).toEqual([]) |
|||
expect(props._children).not.toBeDefined() |
|||
}) |
|||
|
|||
const comp = getcomponent(); |
|||
it("should create an object with multiple prop names", () => { |
|||
const comp = getcomponent() |
|||
comp.props.fieldName = "string" |
|||
comp.props.fieldLength = { type: "number", default: 500 } |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
const { props, errors } = createProps(comp) |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props._children).toBeDefined(); |
|||
expect(props._children).toEqual([]); |
|||
}); |
|||
expect(errors).toEqual([]) |
|||
expect(props.fieldName).toBeDefined() |
|||
expect(props.fieldName).toBe("") |
|||
expect(props.fieldLength).toBeDefined() |
|||
expect(props.fieldLength).toBe(500) |
|||
}) |
|||
|
|||
it("should not create _children array when children=false ", () => { |
|||
it("should return error when invalid type", () => { |
|||
const comp = getcomponent() |
|||
comp.props.fieldName = "invalid type name" |
|||
comp.props.fieldLength = { type: "invalid type name " } |
|||
|
|||
const comp = getcomponent(); |
|||
comp.children = false; |
|||
const { errors } = createProps(comp) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
expect(errors.length).toBe(2) |
|||
expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy() |
|||
expect(some(e => e.propName === "fieldLength")(errors)).toBeTruthy() |
|||
}) |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props._children).not.toBeDefined(); |
|||
}); |
|||
it("should return error default value is not of declared type", () => { |
|||
const comp = getcomponent() |
|||
comp.props.fieldName = { type: "string", default: 1 } |
|||
|
|||
it("should create an object with multiple prop names", () => { |
|||
|
|||
const comp = getcomponent(); |
|||
comp.props.fieldName = "string"; |
|||
comp.props.fieldLength = { type: "number", default: 500 }; |
|||
const { errors } = createProps(comp) |
|||
|
|||
const { props, errors } = createProps(comp); |
|||
expect(errors.length).toBe(1) |
|||
expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy() |
|||
}) |
|||
|
|||
expect(errors).toEqual([]); |
|||
expect(props.fieldName).toBeDefined(); |
|||
expect(props.fieldName).toBe(""); |
|||
expect(props.fieldLength).toBeDefined(); |
|||
expect(props.fieldLength).toBe(500); |
|||
}) |
|||
it("should merge in derived props", () => { |
|||
const propDef = { |
|||
fieldName: "string", |
|||
fieldLength: { type: "number", default: 500 }, |
|||
} |
|||
|
|||
it("should return error when invalid type", () => { |
|||
const comp = getcomponent(); |
|||
comp.props.fieldName = "invalid type name"; |
|||
comp.props.fieldLength = { type: "invalid type name "}; |
|||
const comp = getcomponent() |
|||
comp.props.fieldName = "string" |
|||
comp.props.fieldLength = { type: "number", default: 500 } |
|||
|
|||
const { errors } = createProps(comp); |
|||
const derivedFrom = { |
|||
fieldName: "surname", |
|||
} |
|||
|
|||
expect(errors.length).toBe(2); |
|||
expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy(); |
|||
expect(some(e => e.propName === "fieldLength")(errors)).toBeTruthy(); |
|||
}); |
|||
|
|||
it("should return error default value is not of declared type", () => { |
|||
|
|||
const comp = getcomponent(); |
|||
comp.props.fieldName = {type:"string", default: 1} |
|||
|
|||
|
|||
const { errors } = createProps(comp); |
|||
|
|||
expect(errors.length).toBe(1); |
|||
expect(some(e => e.propName === "fieldName")(errors)).toBeTruthy(); |
|||
}); |
|||
|
|||
it("should merge in derived props", () => { |
|||
const propDef = { |
|||
fieldName: "string", |
|||
fieldLength: { type: "number", default: 500} |
|||
}; |
|||
|
|||
const comp = getcomponent(); |
|||
comp.props.fieldName = "string"; |
|||
comp.props.fieldLength = { type: "number", default: 500}; |
|||
|
|||
const derivedFrom = { |
|||
fieldName: "surname" |
|||
}; |
|||
|
|||
const { props, errors } = createProps(comp, derivedFrom); |
|||
|
|||
expect(errors.length).toBe(0); |
|||
expect(props.fieldName).toBe("surname"); |
|||
expect(props.fieldLength).toBe(500); |
|||
|
|||
}); |
|||
const { props, errors } = createProps(comp, derivedFrom) |
|||
|
|||
expect(errors.length).toBe(0) |
|||
expect(props.fieldName).toBe("surname") |
|||
expect(props.fieldLength).toBe(500) |
|||
}) |
|||
}) |
|||
|
|||
@ -1,50 +1,45 @@ |
|||
import { expandComponentDefinition } from "../src/userInterface/pagesParsing/types"; |
|||
import { expandComponentDefinition } from "../src/userInterface/pagesParsing/types" |
|||
|
|||
const componentDef = () => ({ |
|||
name: "comp", |
|||
props: { |
|||
label: "string", |
|||
width: {type:"number"}, |
|||
color: {type:"string", required:true}, |
|||
} |
|||
name: "comp", |
|||
props: { |
|||
label: "string", |
|||
width: { type: "number" }, |
|||
color: { type: "string", required: true }, |
|||
}, |
|||
}) |
|||
|
|||
describe("expandPropDefintion", () => { |
|||
|
|||
it("should expand property defined as string, into default for that type", () => { |
|||
|
|||
const result = expandComponentDefinition(componentDef()); |
|||
|
|||
expect(result.props.label.type).toBe("string"); |
|||
expect(result.props.label.required).toBe(false); |
|||
}); |
|||
|
|||
it("should add members to property defined as object, when members do not exist", () => { |
|||
|
|||
const result = expandComponentDefinition(componentDef()); |
|||
expect(result.props.width.required).toBe(false); |
|||
}); |
|||
|
|||
it("should not override existing memebers", () => { |
|||
|
|||
const result = expandComponentDefinition(componentDef()); |
|||
expect(result.props.color.required).toBe(true); |
|||
}); |
|||
|
|||
it("should set children=true when not included", () => { |
|||
const result = expandComponentDefinition(componentDef()); |
|||
expect(result.children).toBe(true); |
|||
}); |
|||
|
|||
it("should not change children when specified", () => { |
|||
const c = componentDef(); |
|||
c.children = false; |
|||
const result = expandComponentDefinition(c); |
|||
expect(result.children).toBe(false); |
|||
|
|||
c.children = true; |
|||
const result2 = expandComponentDefinition(c); |
|||
expect(result2.children).toBe(true); |
|||
}); |
|||
|
|||
}) |
|||
it("should expand property defined as string, into default for that type", () => { |
|||
const result = expandComponentDefinition(componentDef()) |
|||
|
|||
expect(result.props.label.type).toBe("string") |
|||
expect(result.props.label.required).toBe(false) |
|||
}) |
|||
|
|||
it("should add members to property defined as object, when members do not exist", () => { |
|||
const result = expandComponentDefinition(componentDef()) |
|||
expect(result.props.width.required).toBe(false) |
|||
}) |
|||
|
|||
it("should not override existing memebers", () => { |
|||
const result = expandComponentDefinition(componentDef()) |
|||
expect(result.props.color.required).toBe(true) |
|||
}) |
|||
|
|||
it("should set children=true when not included", () => { |
|||
const result = expandComponentDefinition(componentDef()) |
|||
expect(result.children).toBe(true) |
|||
}) |
|||
|
|||
it("should not change children when specified", () => { |
|||
const c = componentDef() |
|||
c.children = false |
|||
const result = expandComponentDefinition(c) |
|||
expect(result.children).toBe(false) |
|||
|
|||
c.children = true |
|||
const result2 = expandComponentDefinition(c) |
|||
expect(result2.children).toBe(true) |
|||
}) |
|||
}) |
|||
|
|||
@ -1,96 +1,91 @@ |
|||
import { |
|||
getInstanceProps, |
|||
getScreenInfo , |
|||
getComponentInfo |
|||
} from "../src/userInterface/pagesParsing/createProps"; |
|||
import { |
|||
keys, some, find |
|||
} from "lodash/fp"; |
|||
import { componentsAndScreens } from "./testData"; |
|||
|
|||
|
|||
getInstanceProps, |
|||
getScreenInfo, |
|||
getComponentInfo, |
|||
} from "../src/userInterface/pagesParsing/createProps" |
|||
import { keys, some, find } from "lodash/fp" |
|||
import { componentsAndScreens } from "./testData" |
|||
|
|||
describe("getComponentInfo", () => { |
|||
|
|||
it("should return default props for root component", () => { |
|||
const result = getComponentInfo( |
|||
componentsAndScreens().components, |
|||
"budibase-components/TextBox"); |
|||
|
|||
expect(result.errors).toEqual([]); |
|||
expect(result.fullProps).toEqual({ |
|||
_component: "budibase-components/TextBox", |
|||
size: "", |
|||
isPassword: false, |
|||
placeholder: "", |
|||
label:"" |
|||
}); |
|||
}); |
|||
|
|||
it("getInstanceProps should set supplied props on top of default props", () => { |
|||
const result = getInstanceProps( |
|||
getComponentInfo( |
|||
componentsAndScreens().components, |
|||
"budibase-components/TextBox"), |
|||
{size:"small"}); |
|||
|
|||
expect(result).toEqual({ |
|||
_component: "budibase-components/TextBox", |
|||
size: "small", |
|||
isPassword: false, |
|||
placeholder: "", |
|||
label:"" |
|||
}); |
|||
|
|||
}); |
|||
}); |
|||
it("should return default props for root component", () => { |
|||
const result = getComponentInfo( |
|||
componentsAndScreens().components, |
|||
"budibase-components/TextBox" |
|||
) |
|||
|
|||
expect(result.errors).toEqual([]) |
|||
expect(result.fullProps).toEqual({ |
|||
_component: "budibase-components/TextBox", |
|||
size: "", |
|||
isPassword: false, |
|||
placeholder: "", |
|||
label: "", |
|||
}) |
|||
}) |
|||
|
|||
it("getInstanceProps should set supplied props on top of default props", () => { |
|||
const result = getInstanceProps( |
|||
getComponentInfo( |
|||
componentsAndScreens().components, |
|||
"budibase-components/TextBox" |
|||
), |
|||
{ size: "small" } |
|||
) |
|||
|
|||
expect(result).toEqual({ |
|||
_component: "budibase-components/TextBox", |
|||
size: "small", |
|||
isPassword: false, |
|||
placeholder: "", |
|||
label: "", |
|||
}) |
|||
}) |
|||
}) |
|||
|
|||
describe("getScreenInfo", () => { |
|||
|
|||
const getScreen = (screens, name) => |
|||
find(s => s.name === name)(screens); |
|||
|
|||
it("should return correct props for screen", () => { |
|||
const {components, screens} = componentsAndScreens(); |
|||
const result = getScreenInfo( |
|||
components, |
|||
getScreen(screens, "common/SmallTextbox")); |
|||
|
|||
expect(result.errors).toEqual([]); |
|||
expect(result.fullProps).toEqual({ |
|||
_component: "common/SmallTextbox", |
|||
size: "small", |
|||
isPassword: false, |
|||
placeholder: "", |
|||
label:"" |
|||
}); |
|||
}); |
|||
|
|||
it("should return correct props for twice derived component", () => { |
|||
const {components, screens} = componentsAndScreens(); |
|||
const result = getScreenInfo( |
|||
components, |
|||
getScreen(screens, "common/PasswordBox")); |
|||
|
|||
expect(result.errors).toEqual([]); |
|||
expect(result.fullProps).toEqual({ |
|||
_component: "common/PasswordBox", |
|||
size: "small", |
|||
isPassword: true, |
|||
placeholder: "", |
|||
label:"" |
|||
}); |
|||
}); |
|||
|
|||
|
|||
it("should list unset props as those that are only defined in root", () => { |
|||
const {components, screens} = componentsAndScreens(); |
|||
const result = getScreenInfo( |
|||
components, |
|||
getScreen(screens, "common/PasswordBox")); |
|||
|
|||
expect(result.unsetProps).toEqual([ |
|||
"placeholder", "label"]); |
|||
}); |
|||
|
|||
}) |
|||
const getScreen = (screens, name) => find(s => s.name === name)(screens) |
|||
|
|||
it("should return correct props for screen", () => { |
|||
const { components, screens } = componentsAndScreens() |
|||
const result = getScreenInfo( |
|||
components, |
|||
getScreen(screens, "common/SmallTextbox") |
|||
) |
|||
|
|||
expect(result.errors).toEqual([]) |
|||
expect(result.fullProps).toEqual({ |
|||
_component: "common/SmallTextbox", |
|||
size: "small", |
|||
isPassword: false, |
|||
placeholder: "", |
|||
label: "", |
|||
}) |
|||
}) |
|||
|
|||
it("should return correct props for twice derived component", () => { |
|||
const { components, screens } = componentsAndScreens() |
|||
const result = getScreenInfo( |
|||
components, |
|||
getScreen(screens, "common/PasswordBox") |
|||
) |
|||
|
|||
expect(result.errors).toEqual([]) |
|||
expect(result.fullProps).toEqual({ |
|||
_component: "common/PasswordBox", |
|||
size: "small", |
|||
isPassword: true, |
|||
placeholder: "", |
|||
label: "", |
|||
}) |
|||
}) |
|||
|
|||
it("should list unset props as those that are only defined in root", () => { |
|||
const { components, screens } = componentsAndScreens() |
|||
const result = getScreenInfo( |
|||
components, |
|||
getScreen(screens, "common/PasswordBox") |
|||
) |
|||
|
|||
expect(result.unsetProps).toEqual(["placeholder", "label"]) |
|||
}) |
|||
}) |
|||
|
|||
@ -1,102 +1,101 @@ |
|||
|
|||
export const componentsAndScreens = () => ({ |
|||
components: [ |
|||
{ |
|||
name: "budibase-components/TextBox", |
|||
tags: ["Text", "input"], |
|||
children: false, |
|||
props: { |
|||
size: {type:"options", options:["small", "medium", "large"]}, |
|||
isPassword: "bool", |
|||
placeholder: "string", |
|||
label:"string" |
|||
} |
|||
}, |
|||
{ |
|||
name: "budibase-components/Button", |
|||
tags: ["input"], |
|||
children: true, |
|||
props: { |
|||
size: {type:"options", options:["small", "medium", "large"]}, |
|||
css: "string", |
|||
contentText: "string" |
|||
} |
|||
}, |
|||
{ |
|||
name: "budibase-components/div", |
|||
tags: ["input"], |
|||
props: { |
|||
width: "number", |
|||
} |
|||
}, |
|||
{ |
|||
name:"budibase-components/RecordView", |
|||
tags: ["record"], |
|||
props: { |
|||
data: "state" |
|||
} |
|||
} |
|||
], |
|||
screens: [ |
|||
{ |
|||
name: "common/SmallTextbox", |
|||
props: { |
|||
_component: "budibase-components/TextBox", |
|||
size: "small" |
|||
} |
|||
}, |
|||
|
|||
{ |
|||
name: "common/PasswordBox", |
|||
tags: ["mask"], |
|||
props: { |
|||
_component: "budibase-components/TextBox", |
|||
isPassword: true, |
|||
size: "small" |
|||
} |
|||
}, |
|||
components: [ |
|||
{ |
|||
name: "budibase-components/TextBox", |
|||
tags: ["Text", "input"], |
|||
children: false, |
|||
props: { |
|||
size: { type: "options", options: ["small", "medium", "large"] }, |
|||
isPassword: "bool", |
|||
placeholder: "string", |
|||
label: "string", |
|||
}, |
|||
}, |
|||
{ |
|||
name: "budibase-components/Button", |
|||
tags: ["input"], |
|||
children: true, |
|||
props: { |
|||
size: { type: "options", options: ["small", "medium", "large"] }, |
|||
css: "string", |
|||
contentText: "string", |
|||
}, |
|||
}, |
|||
{ |
|||
name: "budibase-components/div", |
|||
tags: ["input"], |
|||
props: { |
|||
width: "number", |
|||
}, |
|||
}, |
|||
{ |
|||
name: "budibase-components/RecordView", |
|||
tags: ["record"], |
|||
props: { |
|||
data: "state", |
|||
}, |
|||
}, |
|||
], |
|||
screens: [ |
|||
{ |
|||
name: "common/SmallTextbox", |
|||
props: { |
|||
_component: "budibase-components/TextBox", |
|||
size: "small", |
|||
}, |
|||
}, |
|||
|
|||
{ |
|||
name: "common/PasswordBox", |
|||
tags: ["mask"], |
|||
props: { |
|||
_component: "budibase-components/TextBox", |
|||
isPassword: true, |
|||
size: "small", |
|||
}, |
|||
}, |
|||
|
|||
{ |
|||
name: "PrimaryButton", |
|||
props: { |
|||
_component: "budibase-components/Button", |
|||
css: "btn-primary", |
|||
}, |
|||
}, |
|||
|
|||
{ |
|||
name:"PrimaryButton", |
|||
props: { |
|||
_component:"budibase-components/Button", |
|||
css:"btn-primary" |
|||
} |
|||
}, |
|||
{ |
|||
name: "ButtonGroup", |
|||
props: { |
|||
_component: "budibase-components/div", |
|||
width: 100, |
|||
_children: [ |
|||
{ |
|||
_component: "budibase-components/Button", |
|||
contentText: "Button 1", |
|||
}, |
|||
{ |
|||
_component: "budibase-components/Button", |
|||
contentText: "Button 2", |
|||
}, |
|||
{ |
|||
_component: "budibase-components/TextBox", |
|||
isPassword: true, |
|||
size: "small", |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
|
|||
{ |
|||
name:"ButtonGroup", |
|||
props: { |
|||
_component:"budibase-components/div", |
|||
width: 100, |
|||
_children: [ |
|||
{ |
|||
_component: "budibase-components/Button", |
|||
contentText: "Button 1" |
|||
}, |
|||
{ |
|||
_component: "budibase-components/Button", |
|||
contentText: "Button 2" |
|||
}, |
|||
{ |
|||
_component: "budibase-components/TextBox", |
|||
isPassword: true, |
|||
size: "small" |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
|
|||
{ |
|||
name:"Field", |
|||
props: { |
|||
_component:"budibase-components/div", |
|||
_children:[ |
|||
{ |
|||
_component: "common/SmallTextbox" |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
] |
|||
}); |
|||
{ |
|||
name: "Field", |
|||
props: { |
|||
_component: "budibase-components/div", |
|||
_children: [ |
|||
{ |
|||
_component: "common/SmallTextbox", |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
], |
|||
}) |
|||
|
|||
@ -1,105 +1,95 @@ |
|||
import { |
|||
validatePages, |
|||
validatePage |
|||
} from "../src/userInterface/pagesParsing/validatePages"; |
|||
import { |
|||
validatePages, |
|||
validatePage, |
|||
} from "../src/userInterface/pagesParsing/validatePages" |
|||
|
|||
const validPages = () => ({ |
|||
"main" : { |
|||
"index" : { |
|||
"title": "My Cool App" |
|||
}, |
|||
"appBody" : "./main.app.json" |
|||
main: { |
|||
index: { |
|||
title: "My Cool App", |
|||
}, |
|||
"unauthenticated" : { |
|||
"index" : { |
|||
"title": "My Cool App - Login" |
|||
}, |
|||
"appBody" : "./unauthenticated.app.json" |
|||
appBody: "./main.app.json", |
|||
}, |
|||
unauthenticated: { |
|||
index: { |
|||
title: "My Cool App - Login", |
|||
}, |
|||
"componentLibraries": ["./myComponents"] |
|||
}); |
|||
|
|||
const getComponent = name => ({ |
|||
testIndexHtml : { |
|||
name: "testIndexHtml", |
|||
props: { |
|||
title: "string", |
|||
} |
|||
} |
|||
}[name]) |
|||
appBody: "./unauthenticated.app.json", |
|||
}, |
|||
componentLibraries: ["./myComponents"], |
|||
}) |
|||
|
|||
const getComponent = name => |
|||
({ |
|||
testIndexHtml: { |
|||
name: "testIndexHtml", |
|||
props: { |
|||
title: "string", |
|||
}, |
|||
}, |
|||
}[name]) |
|||
|
|||
describe("validate single page", () => { |
|||
it("should return no errors when page is valid", () => { |
|||
const errors = validatePage(validPages().main, getComponent) |
|||
|
|||
it("should return no errors when page is valid", () => { |
|||
const errors = validatePage( |
|||
validPages().main, |
|||
getComponent); |
|||
|
|||
expect(errors).toEqual([]); |
|||
}); |
|||
|
|||
it("should return error when index is not set, or set incorrectly", () => { |
|||
let page = validPages().main; |
|||
delete page.index; |
|||
expect(validatePage(page, getComponent).length).toEqual(1); |
|||
}); |
|||
expect(errors).toEqual([]) |
|||
}) |
|||
|
|||
it("should return error when appBody is not set, or set incorrectly", () => { |
|||
let page = validPages().main; |
|||
delete page.appBody; |
|||
expect(validatePage(page, getComponent).length).toEqual(1); |
|||
it("should return error when index is not set, or set incorrectly", () => { |
|||
let page = validPages().main |
|||
delete page.index |
|||
expect(validatePage(page, getComponent).length).toEqual(1) |
|||
}) |
|||
|
|||
page.appBody = true; // not a string
|
|||
expect(validatePage(page, getComponent).length).toEqual(1); |
|||
it("should return error when appBody is not set, or set incorrectly", () => { |
|||
let page = validPages().main |
|||
delete page.appBody |
|||
expect(validatePage(page, getComponent).length).toEqual(1) |
|||
|
|||
page.appBody = "something.js"; // not a json
|
|||
expect(validatePage(page, getComponent).length).toEqual(1); |
|||
page.appBody = true // not a string
|
|||
expect(validatePage(page, getComponent).length).toEqual(1) |
|||
|
|||
}); |
|||
|
|||
}); |
|||
page.appBody = "something.js" // not a json
|
|||
expect(validatePage(page, getComponent).length).toEqual(1) |
|||
}) |
|||
}) |
|||
|
|||
describe("validate pages", () => { |
|||
|
|||
it("should return no errors when pages are valid", () => { |
|||
const errors = validatePages( |
|||
validPages(), |
|||
getComponent); |
|||
|
|||
expect(errors).toEqual([]); |
|||
}); |
|||
|
|||
it("should return error when component libraries not set", () => { |
|||
const pages = validPages(); |
|||
|
|||
delete pages.componentLibraries; |
|||
let errors = validatePages(pages, getComponent); |
|||
expect(errors.length).toBe(1); |
|||
|
|||
pages.componentLibraries = []; |
|||
errors = validatePages(pages, getComponent); |
|||
expect(errors.length).toBe(1); |
|||
|
|||
}); |
|||
|
|||
it("should return error when no main or unauthenticated page", () => { |
|||
|
|||
let pages = validPages(); |
|||
delete pages.main; |
|||
let errors = validatePages(pages, getComponent); |
|||
expect(errors.length).toBe(1); |
|||
|
|||
pages = validPages(); |
|||
delete pages.unauthenticated; |
|||
errors = validatePages(pages, getComponent); |
|||
expect(errors.length).toBe(1); |
|||
|
|||
}); |
|||
|
|||
it("should return error when page is invalid", () => { |
|||
const pages = validPages(); |
|||
delete pages.main.index; |
|||
const errors = validatePages(pages, getComponent); |
|||
expect(errors.length).toBe(1); |
|||
}); |
|||
}); |
|||
it("should return no errors when pages are valid", () => { |
|||
const errors = validatePages(validPages(), getComponent) |
|||
|
|||
expect(errors).toEqual([]) |
|||
}) |
|||
|
|||
it("should return error when component libraries not set", () => { |
|||
const pages = validPages() |
|||
|
|||
delete pages.componentLibraries |
|||
let errors = validatePages(pages, getComponent) |
|||
expect(errors.length).toBe(1) |
|||
|
|||
pages.componentLibraries = [] |
|||
errors = validatePages(pages, getComponent) |
|||
expect(errors.length).toBe(1) |
|||
}) |
|||
|
|||
it("should return error when no main or unauthenticated page", () => { |
|||
let pages = validPages() |
|||
delete pages.main |
|||
let errors = validatePages(pages, getComponent) |
|||
expect(errors.length).toBe(1) |
|||
|
|||
pages = validPages() |
|||
delete pages.unauthenticated |
|||
errors = validatePages(pages, getComponent) |
|||
expect(errors.length).toBe(1) |
|||
}) |
|||
|
|||
it("should return error when page is invalid", () => { |
|||
const pages = validPages() |
|||
delete pages.main.index |
|||
const errors = validatePages(pages, getComponent) |
|||
expect(errors.length).toBe(1) |
|||
}) |
|||
}) |
|||
|
|||
@ -1,21 +1,21 @@ |
|||
const yargs = require("yargs"); |
|||
const chalk = require("chalk"); |
|||
const yargs = require("yargs") |
|||
const chalk = require("chalk") |
|||
|
|||
module.exports = () => { |
|||
yargs |
|||
.scriptName("budi") |
|||
.usage('$0 <cmd> [args]') |
|||
.usage("$0 <cmd> [args]") |
|||
.command(require("./commands/init")) |
|||
.command(require("./commands/new")) |
|||
.command(require("./commands/run")) |
|||
.command(require("./commands/instance")) |
|||
.fail((msg, err) => { |
|||
if(err) { |
|||
console.log(chalk.red(err.message)); |
|||
console.log(chalk.gray(err.toString())); |
|||
if (err) { |
|||
console.log(chalk.red(err.message)) |
|||
console.log(chalk.gray(err.toString())) |
|||
} else { |
|||
console.log(chalk.red(msg)); |
|||
console.log(chalk.red(msg)) |
|||
} |
|||
}) |
|||
.help().argv; |
|||
} |
|||
.help().argv |
|||
} |
|||
|
|||
@ -1,104 +1,99 @@ |
|||
const inquirer = require("inquirer"); |
|||
const { mkdir, exists, copy } = require("fs-extra"); |
|||
const chalk = require("chalk"); |
|||
const { serverFileName, getAppContext } = require("../../common"); |
|||
const passwordQuestion = require("@inquirer/password"); |
|||
const createMasterDb = require("@budibase/server/initialise/createMasterDb"); |
|||
const { resolve } = require("path"); |
|||
const localDatastore = require("@budibase/datastores/datastores/local"); |
|||
|
|||
module.exports = (opts) => { |
|||
run(opts); |
|||
const inquirer = require("inquirer") |
|||
const { mkdir, exists, copy } = require("fs-extra") |
|||
const chalk = require("chalk") |
|||
const { serverFileName, getAppContext } = require("../../common") |
|||
const passwordQuestion = require("@inquirer/password") |
|||
const createMasterDb = require("@budibase/server/initialise/createMasterDb") |
|||
const { resolve } = require("path") |
|||
const localDatastore = require("@budibase/datastores/datastores/local") |
|||
|
|||
module.exports = opts => { |
|||
run(opts) |
|||
} |
|||
|
|||
const run = async (opts) => { |
|||
try { |
|||
await prompts(opts); |
|||
await createDevConfig(opts); |
|||
await createAppsDir(opts); |
|||
await createDataFolder(opts); |
|||
await initialiseDatabase(opts); |
|||
console.log(chalk.green("Budibase successfully initialised.")); |
|||
} catch (error) { |
|||
console.error(`Error initialising Budibase: ${error.message}`) |
|||
} |
|||
const run = async opts => { |
|||
try { |
|||
await prompts(opts) |
|||
await createDevConfig(opts) |
|||
await createAppsDir(opts) |
|||
await createDataFolder(opts) |
|||
await initialiseDatabase(opts) |
|||
console.log(chalk.green("Budibase successfully initialised.")) |
|||
} catch (error) { |
|||
console.error(`Error initialising Budibase: ${error.message}`) |
|||
} |
|||
} |
|||
|
|||
const prompts = async (opts) => { |
|||
|
|||
const questions = [ |
|||
{ |
|||
type: "input", |
|||
name: "username", |
|||
message: "Username for Admin: ", |
|||
validate: function(value) { |
|||
return !!value || "Please enter a username" |
|||
} |
|||
} |
|||
] |
|||
|
|||
if(!opts.username) { |
|||
const answers = await inquirer.prompt(questions); |
|||
opts.username = answers.username; |
|||
} |
|||
|
|||
if(!opts.password) { |
|||
|
|||
const password = await passwordQuestion({ |
|||
message: "Password for Admin: ", mask: "*" |
|||
}); |
|||
const passwordConfirm = await passwordQuestion({ |
|||
message: "Confirm Password: ", mask: "*" |
|||
}); |
|||
|
|||
if(password !== passwordConfirm) |
|||
throw new Exception("Passwords do not match!"); |
|||
|
|||
|
|||
opts.password = password; |
|||
} |
|||
const prompts = async opts => { |
|||
const questions = [ |
|||
{ |
|||
type: "input", |
|||
name: "username", |
|||
message: "Username for Admin: ", |
|||
validate: function(value) { |
|||
return !!value || "Please enter a username" |
|||
}, |
|||
}, |
|||
] |
|||
|
|||
if (!opts.username) { |
|||
const answers = await inquirer.prompt(questions) |
|||
opts.username = answers.username |
|||
} |
|||
|
|||
if (!opts.password) { |
|||
const password = await passwordQuestion({ |
|||
message: "Password for Admin: ", |
|||
mask: "*", |
|||
}) |
|||
const passwordConfirm = await passwordQuestion({ |
|||
message: "Confirm Password: ", |
|||
mask: "*", |
|||
}) |
|||
|
|||
if (password !== passwordConfirm) |
|||
throw new Exception("Passwords do not match!") |
|||
|
|||
opts.password = password |
|||
} |
|||
} |
|||
|
|||
const createAppsDir = async (opts) => { |
|||
if(!await exists(opts.configJson.latestPackagesFolder)) { |
|||
await mkdir(opts.configJson.latestPackagesFolder); |
|||
} |
|||
const createAppsDir = async opts => { |
|||
if (!(await exists(opts.configJson.latestPackagesFolder))) { |
|||
await mkdir(opts.configJson.latestPackagesFolder) |
|||
} |
|||
} |
|||
|
|||
const createDataFolder = async (opts) => { |
|||
|
|||
const dataPath = opts.configJson.datastoreConfig.rootPath; |
|||
const createDataFolder = async opts => { |
|||
const dataPath = opts.configJson.datastoreConfig.rootPath |
|||
|
|||
if(await exists(dataPath)) { |
|||
const err = `The path ${opts.datapath} already exists - has budibase already been initialised? Remove the directory to try again.`; |
|||
throw new Error(err); |
|||
} |
|||
if (await exists(dataPath)) { |
|||
const err = `The path ${opts.datapath} already exists - has budibase already been initialised? Remove the directory to try again.` |
|||
throw new Error(err) |
|||
} |
|||
|
|||
await mkdir(dataPath); |
|||
await mkdir(dataPath) |
|||
} |
|||
|
|||
const createDevConfig = async (opts) => { |
|||
|
|||
const configTemplateFile = `config.${opts.config}.js`; |
|||
const destConfigFile = "./config.js"; |
|||
|
|||
if(await exists(destConfigFile)) { |
|||
console.log(chalk.yellow("Config file already exists (config.js) - keeping your existing config")); |
|||
} else { |
|||
const srcConfig = serverFileName(configTemplateFile); |
|||
await copy(srcConfig, destConfigFile); |
|||
} |
|||
opts.configJson = require(resolve("./config.js"))(); |
|||
const createDevConfig = async opts => { |
|||
const configTemplateFile = `config.${opts.config}.js` |
|||
const destConfigFile = "./config.js" |
|||
|
|||
if (await exists(destConfigFile)) { |
|||
console.log( |
|||
chalk.yellow( |
|||
"Config file already exists (config.js) - keeping your existing config" |
|||
) |
|||
) |
|||
} else { |
|||
const srcConfig = serverFileName(configTemplateFile) |
|||
await copy(srcConfig, destConfigFile) |
|||
} |
|||
opts.configJson = require(resolve("./config.js"))() |
|||
} |
|||
|
|||
const initialiseDatabase = async (opts) => { |
|||
|
|||
const appContext = await getAppContext({masterIsCreated:false}); |
|||
const initialiseDatabase = async opts => { |
|||
const appContext = await getAppContext({ masterIsCreated: false }) |
|||
|
|||
await createMasterDb( |
|||
appContext, |
|||
localDatastore, |
|||
opts.username, |
|||
opts.password); |
|||
|
|||
} |
|||
await createMasterDb(appContext, localDatastore, opts.username, opts.password) |
|||
} |
|||
|
|||
@ -1,104 +1,116 @@ |
|||
const inquirer = require("inquirer"); |
|||
const { readJSON } = require("fs-extra"); |
|||
const { join } = require("path"); |
|||
const chalk = require("chalk"); |
|||
const fp = require("lodash/fp"); |
|||
const { getAppContext } = require("../../common"); |
|||
const passwordQuestion = require("@inquirer/password"); |
|||
|
|||
module.exports = (opts) => { |
|||
run(opts); |
|||
const inquirer = require("inquirer") |
|||
const { readJSON } = require("fs-extra") |
|||
const { join } = require("path") |
|||
const chalk = require("chalk") |
|||
const fp = require("lodash/fp") |
|||
const { getAppContext } = require("../../common") |
|||
const passwordQuestion = require("@inquirer/password") |
|||
|
|||
module.exports = opts => { |
|||
run(opts) |
|||
} |
|||
|
|||
const run = async (opts) => { |
|||
try { |
|||
const appContext = await getAppContext({configName: opts.config, masterIsCreated:true}); |
|||
opts.appContext = appContext; |
|||
opts.datapath = "./.data"; |
|||
await fetchUserLevels(opts); |
|||
await prompts(opts); |
|||
await createInstance(opts); |
|||
console.log(chalk.green(`Budibase instance created for app ${opts.appname}.`)) |
|||
} catch (error) { |
|||
console.error(chalk.red(`Error creating instance of app ${opts.appname}: ${error.message}`)) |
|||
} |
|||
|
|||
const run = async opts => { |
|||
try { |
|||
const appContext = await getAppContext({ |
|||
configName: opts.config, |
|||
masterIsCreated: true, |
|||
}) |
|||
opts.appContext = appContext |
|||
opts.datapath = "./.data" |
|||
await fetchUserLevels(opts) |
|||
await prompts(opts) |
|||
await createInstance(opts) |
|||
console.log( |
|||
chalk.green(`Budibase instance created for app ${opts.appname}.`) |
|||
) |
|||
} catch (error) { |
|||
console.error( |
|||
chalk.red( |
|||
`Error creating instance of app ${opts.appname}: ${error.message}` |
|||
) |
|||
) |
|||
} |
|||
} |
|||
|
|||
const fetchUserLevels = async (opts) => { |
|||
const accessLevels = await readJSON( |
|||
join( |
|||
opts.appContext.config.latestPackagesFolder || ".", |
|||
opts.appname, |
|||
"access_levels.json") |
|||
); |
|||
const fetchUserLevels = async opts => { |
|||
const accessLevels = await readJSON( |
|||
join( |
|||
opts.appContext.config.latestPackagesFolder || ".", |
|||
opts.appname, |
|||
"access_levels.json" |
|||
) |
|||
) |
|||
|
|||
if(accessLevels.levels.length === 0) |
|||
throw new Error("No access levels. Use the builder to create one"); |
|||
if (accessLevels.levels.length === 0) |
|||
throw new Error("No access levels. Use the builder to create one") |
|||
|
|||
opts.accessLevels = accessLevels.levels; |
|||
opts.accessLevels = accessLevels.levels |
|||
} |
|||
|
|||
const prompts = async (opts) => { |
|||
|
|||
const questions = [ |
|||
{ |
|||
type: "input", |
|||
name: "username", |
|||
message: "Username: ", |
|||
validate: function(value) { |
|||
return !!value || "Please enter a username" |
|||
} |
|||
} |
|||
] |
|||
|
|||
if(opts.accessLevels.length === 1) { |
|||
opts.userAccessLevel = opts.accessLevels[0].name; |
|||
} else { |
|||
questions.push({ |
|||
type: "input", |
|||
name: "userAccessLevel", |
|||
message: `Access Level [${fp.join(", ")(opts.accessLevels.map(l => l.name))}]: `, |
|||
choices: opts.accessLevels.map(l => l.name) |
|||
}); |
|||
} |
|||
|
|||
const answers = await inquirer.prompt(questions); |
|||
const password = await passwordQuestion({ |
|||
message: "Password for Admin: ", mask: "*" |
|||
}); |
|||
const passwordConfirm = await passwordQuestion({ |
|||
message: "Confirm Password: ", mask: "*" |
|||
}); |
|||
|
|||
if(password !== passwordConfirm) |
|||
throw new Error("Passwords do not match!"); |
|||
|
|||
opts.username = answers.username; |
|||
opts.password = password; |
|||
if(opts.accessLevels.length > 1) { |
|||
opts.userAccessLevel = answers.userAccessLevel; |
|||
} |
|||
const prompts = async opts => { |
|||
const questions = [ |
|||
{ |
|||
type: "input", |
|||
name: "username", |
|||
message: "Username: ", |
|||
validate: function(value) { |
|||
return !!value || "Please enter a username" |
|||
}, |
|||
}, |
|||
] |
|||
|
|||
if (opts.accessLevels.length === 1) { |
|||
opts.userAccessLevel = opts.accessLevels[0].name |
|||
} else { |
|||
questions.push({ |
|||
type: "input", |
|||
name: "userAccessLevel", |
|||
message: `Access Level [${fp.join(", ")( |
|||
opts.accessLevels.map(l => l.name) |
|||
)}]: `,
|
|||
choices: opts.accessLevels.map(l => l.name), |
|||
}) |
|||
} |
|||
|
|||
const answers = await inquirer.prompt(questions) |
|||
const password = await passwordQuestion({ |
|||
message: "Password for Admin: ", |
|||
mask: "*", |
|||
}) |
|||
const passwordConfirm = await passwordQuestion({ |
|||
message: "Confirm Password: ", |
|||
mask: "*", |
|||
}) |
|||
|
|||
if (password !== passwordConfirm) throw new Error("Passwords do not match!") |
|||
|
|||
opts.username = answers.username |
|||
opts.password = password |
|||
if (opts.accessLevels.length > 1) { |
|||
opts.userAccessLevel = answers.userAccessLevel |
|||
} |
|||
} |
|||
|
|||
|
|||
const createInstance = async (opts) => { |
|||
|
|||
const bb = opts.appContext.master.bbMaster; |
|||
|
|||
const app = await opts.appContext.master.getApplication(opts.appname); |
|||
const instance = bb.recordApi.getNew(`${app.key}/instances`, "instance"); |
|||
instance.name = "dev instance"; |
|||
instance.active = true; |
|||
instance.version = {key:""}; |
|||
|
|||
const user = bb.authApi.getNewUser(); |
|||
user.accessLevels.push(opts.userAccessLevel); |
|||
user.name = opts.username; |
|||
|
|||
await bb.recordApi.save(instance); |
|||
const savedInstance = await bb.recordApi.load(instance.key); |
|||
await opts.appContext.master.createAppUser( |
|||
opts.appname, savedInstance, user, opts.password); |
|||
|
|||
} |
|||
const createInstance = async opts => { |
|||
const bb = opts.appContext.master.bbMaster |
|||
|
|||
const app = await opts.appContext.master.getApplication(opts.appname) |
|||
const instance = bb.recordApi.getNew(`${app.key}/instances`, "instance") |
|||
instance.name = "dev instance" |
|||
instance.active = true |
|||
instance.version = { key: "" } |
|||
|
|||
const user = bb.authApi.getNewUser() |
|||
user.accessLevels.push(opts.userAccessLevel) |
|||
user.name = opts.username |
|||
|
|||
await bb.recordApi.save(instance) |
|||
const savedInstance = await bb.recordApi.load(instance.key) |
|||
await opts.appContext.master.createAppUser( |
|||
opts.appname, |
|||
savedInstance, |
|||
user, |
|||
opts.password |
|||
) |
|||
} |
|||
|
|||
@ -1,2 +1 @@ |
|||
|
|||
module.exports = (config) => ({}) |
|||
module.exports = config => ({}) |
|||
|
|||
@ -1,59 +1,61 @@ |
|||
const { getAppContext } = require("../../common"); |
|||
const { |
|||
getMasterApisWithFullAccess |
|||
} = require("@budibase/server/utilities/budibaseApi"); |
|||
const { copy, readJSON, writeJSON, remove, exists } = require("fs-extra"); |
|||
const { resolve, join } = require("path"); |
|||
const thisPackageJson = require("../../../package.json"); |
|||
const chalk = require("chalk"); |
|||
const {exec} = require('child_process'); |
|||
|
|||
module.exports = (opts) => { |
|||
run(opts); |
|||
console.log(chalk.green(`Budibase app ${opts.name} created!`)); |
|||
const { getAppContext } = require("../../common") |
|||
const { |
|||
getMasterApisWithFullAccess, |
|||
} = require("@budibase/server/utilities/budibaseApi") |
|||
const { copy, readJSON, writeJSON, remove, exists } = require("fs-extra") |
|||
const { resolve, join } = require("path") |
|||
const thisPackageJson = require("../../../package.json") |
|||
const chalk = require("chalk") |
|||
const { exec } = require("child_process") |
|||
|
|||
module.exports = opts => { |
|||
run(opts) |
|||
console.log(chalk.green(`Budibase app ${opts.name} created!`)) |
|||
} |
|||
|
|||
const run = async (opts) => { |
|||
const context = await getAppContext({configName:opts.config, masterIsCreated:true}); |
|||
opts.config = context.config; |
|||
const bb = await getMasterApisWithFullAccess(context); |
|||
const run = async opts => { |
|||
const context = await getAppContext({ |
|||
configName: opts.config, |
|||
masterIsCreated: true, |
|||
}) |
|||
opts.config = context.config |
|||
const bb = await getMasterApisWithFullAccess(context) |
|||
|
|||
const app = bb.recordApi.getNew("/applications", "application"); |
|||
app.name = opts.name; |
|||
const app = bb.recordApi.getNew("/applications", "application") |
|||
app.name = opts.name |
|||
|
|||
await bb.recordApi.save(app); |
|||
await createEmptyAppPackage(opts); |
|||
await bb.recordApi.save(app) |
|||
await createEmptyAppPackage(opts) |
|||
|
|||
exec(`cd ${join(opts.config.latestPackagesFolder, opts.name)} && npm install`); |
|||
exec(`cd ${join(opts.config.latestPackagesFolder, opts.name)} && npm install`) |
|||
} |
|||
|
|||
const createEmptyAppPackage = async (opts) => { |
|||
const templateFolder = resolve( |
|||
__dirname, "appPackageTemplate"); |
|||
const createEmptyAppPackage = async opts => { |
|||
const templateFolder = resolve(__dirname, "appPackageTemplate") |
|||
|
|||
const appsFolder = opts.config.latestPackagesFolder || "."; |
|||
const destinationFolder = resolve(appsFolder, opts.name); |
|||
const appsFolder = opts.config.latestPackagesFolder || "." |
|||
const destinationFolder = resolve(appsFolder, opts.name) |
|||
|
|||
if(await exists(destinationFolder)) return; |
|||
if (await exists(destinationFolder)) return |
|||
|
|||
await copy(templateFolder, destinationFolder); |
|||
await copy(templateFolder, destinationFolder) |
|||
|
|||
const packageJsonPath = join(appsFolder, opts.name, "package.json"); |
|||
const packageJson = await readJSON(packageJsonPath); |
|||
const packageJsonPath = join(appsFolder, opts.name, "package.json") |
|||
const packageJson = await readJSON(packageJsonPath) |
|||
|
|||
packageJson.name = opts.name; |
|||
packageJson.dependencies["@budibase/standard-components"] = `^${thisPackageJson.version}`; |
|||
packageJson.name = opts.name |
|||
packageJson.dependencies[ |
|||
"@budibase/standard-components" |
|||
] = `^${thisPackageJson.version}` |
|||
|
|||
await writeJSON(packageJsonPath, packageJson); |
|||
await writeJSON(packageJsonPath, packageJson) |
|||
|
|||
const removePlaceholder = async (...args) => { |
|||
await remove(join(destinationFolder, ...args, "placeholder")); |
|||
} |
|||
const removePlaceholder = async (...args) => { |
|||
await remove(join(destinationFolder, ...args, "placeholder")) |
|||
} |
|||
|
|||
await removePlaceholder("components"); |
|||
await removePlaceholder("public", "shared"); |
|||
await removePlaceholder("public", "main"); |
|||
await removePlaceholder("public", "unauthenticated"); |
|||
|
|||
|
|||
} |
|||
await removePlaceholder("components") |
|||
await removePlaceholder("public", "shared") |
|||
await removePlaceholder("public", "main") |
|||
await removePlaceholder("public", "unauthenticated") |
|||
} |
|||
|
|||
@ -1,25 +1,19 @@ |
|||
const { resolve, join } = require("path"); |
|||
const { cwd } = require("process"); |
|||
const buildAppContext = require("@budibase/server/initialise/buildAppContext"); |
|||
const { resolve, join } = require("path") |
|||
const { cwd } = require("process") |
|||
const buildAppContext = require("@budibase/server/initialise/buildAppContext") |
|||
|
|||
module.exports.serverFileName = relativePath => |
|||
resolve(__dirname, |
|||
"..", |
|||
"node_modules", |
|||
"@budibase", |
|||
"server", |
|||
relativePath); |
|||
module.exports.serverFileName = relativePath => |
|||
resolve(__dirname, "..", "node_modules", "@budibase", "server", relativePath) |
|||
|
|||
module.exports.getAppContext = async ({configName, masterIsCreated}) => { |
|||
|
|||
if(configName) { |
|||
if(!configName.endsWith(".js")) { |
|||
configName = `config.${configName}.js`; |
|||
} |
|||
} else { |
|||
configName = "config.js"; |
|||
module.exports.getAppContext = async ({ configName, masterIsCreated }) => { |
|||
if (configName) { |
|||
if (!configName.endsWith(".js")) { |
|||
configName = `config.${configName}.js` |
|||
} |
|||
} else { |
|||
configName = "config.js" |
|||
} |
|||
|
|||
const config = require(resolve(cwd(), configName))(); |
|||
return await buildAppContext(config, masterIsCreated); |
|||
} |
|||
const config = require(resolve(cwd(), configName))() |
|||
return await buildAppContext(config, masterIsCreated) |
|||
} |
|||
|
|||
@ -1,12 +1,13 @@ |
|||
module.exports = ({ |
|||
"presets": ["@babel/preset-env"], |
|||
"sourceMaps": "inline", |
|||
"retainLines": true, |
|||
"plugins": [ |
|||
["@babel/plugin-transform-runtime", |
|||
{ |
|||
"regenerator": true |
|||
} |
|||
] |
|||
] |
|||
}); |
|||
module.exports = { |
|||
presets: ["@babel/preset-env"], |
|||
sourceMaps: "inline", |
|||
retainLines: true, |
|||
plugins: [ |
|||
[ |
|||
"@babel/plugin-transform-runtime", |
|||
{ |
|||
regenerator: true, |
|||
}, |
|||
], |
|||
], |
|||
} |
|||
|
|||
@ -1,70 +1,111 @@ |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import commonjs from 'rollup-plugin-commonjs'; |
|||
import builtins from 'rollup-plugin-node-builtins'; |
|||
import nodeglobals from 'rollup-plugin-node-globals'; |
|||
import { terser } from 'rollup-plugin-terser'; |
|||
import resolve from "rollup-plugin-node-resolve" |
|||
import commonjs from "rollup-plugin-commonjs" |
|||
import builtins from "rollup-plugin-node-builtins" |
|||
import nodeglobals from "rollup-plugin-node-globals" |
|||
import { terser } from "rollup-plugin-terser" |
|||
|
|||
const lodash_fp_exports = [ |
|||
"find", "isUndefined", "split", "max", |
|||
"last", "union", "reduce", "isObject", |
|||
"cloneDeep", "some", "isArray", "map", |
|||
"filter", "keys", "isFunction", "isEmpty", |
|||
"countBy", "join", "includes", "flatten", |
|||
"constant", "first", "intersection", "take", |
|||
"has", "mapValues", "isString", "isBoolean", |
|||
"isNull", "isNumber", "isObjectLike", "isDate", |
|||
"clone", "values", "keyBy", "isNaN", |
|||
"isInteger", "toNumber"]; |
|||
"find", |
|||
"isUndefined", |
|||
"split", |
|||
"max", |
|||
"last", |
|||
"union", |
|||
"reduce", |
|||
"isObject", |
|||
"cloneDeep", |
|||
"some", |
|||
"isArray", |
|||
"map", |
|||
"filter", |
|||
"keys", |
|||
"isFunction", |
|||
"isEmpty", |
|||
"countBy", |
|||
"join", |
|||
"includes", |
|||
"flatten", |
|||
"constant", |
|||
"first", |
|||
"intersection", |
|||
"take", |
|||
"has", |
|||
"mapValues", |
|||
"isString", |
|||
"isBoolean", |
|||
"isNull", |
|||
"isNumber", |
|||
"isObjectLike", |
|||
"isDate", |
|||
"clone", |
|||
"values", |
|||
"keyBy", |
|||
"isNaN", |
|||
"isInteger", |
|||
"toNumber", |
|||
] |
|||
|
|||
const lodash_exports = [ |
|||
"flow", "head", "find", "each", |
|||
"tail", "findIndex", "startsWith", |
|||
"dropRight", "takeRight", |
|||
"trim", "split", "replace", |
|||
"merge", "assign"]; |
|||
"flow", |
|||
"head", |
|||
"find", |
|||
"each", |
|||
"tail", |
|||
"findIndex", |
|||
"startsWith", |
|||
"dropRight", |
|||
"takeRight", |
|||
"trim", |
|||
"split", |
|||
"replace", |
|||
"merge", |
|||
"assign", |
|||
] |
|||
|
|||
const coreExternal = [ |
|||
"lodash", "lodash/fp", |
|||
"lunr", "safe-buffer", "shortid", |
|||
"@nx-js/compiler-util" |
|||
]; |
|||
"lodash", |
|||
"lodash/fp", |
|||
"lunr", |
|||
"safe-buffer", |
|||
"shortid", |
|||
"@nx-js/compiler-util", |
|||
] |
|||
|
|||
export default { |
|||
input: 'src/index.js', |
|||
output: [ |
|||
{ |
|||
sourcemap: true, |
|||
format: 'iife', |
|||
name: 'app', |
|||
file: `./dist/budibase-client.js` |
|||
}, |
|||
{ |
|||
file: 'dist/budibase-client.esm.mjs', |
|||
format: 'esm', |
|||
sourcemap: "inline" |
|||
} |
|||
], |
|||
plugins: [ |
|||
|
|||
resolve({ |
|||
preferBuiltins:true, |
|||
browser:true, |
|||
dedupe: importee => { |
|||
return coreExternal.includes(importee); |
|||
} |
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
"lodash":lodash_exports, |
|||
"shortid": ["generate"] |
|||
} |
|||
}), |
|||
builtins(), |
|||
nodeglobals(), |
|||
//terser()
|
|||
], |
|||
watch: { |
|||
clearScreen: false |
|||
} |
|||
}; |
|||
input: "src/index.js", |
|||
output: [ |
|||
{ |
|||
sourcemap: true, |
|||
format: "iife", |
|||
name: "app", |
|||
file: `./dist/budibase-client.js`, |
|||
}, |
|||
{ |
|||
file: "dist/budibase-client.esm.mjs", |
|||
format: "esm", |
|||
sourcemap: "inline", |
|||
}, |
|||
], |
|||
plugins: [ |
|||
resolve({ |
|||
preferBuiltins: true, |
|||
browser: true, |
|||
dedupe: importee => { |
|||
return coreExternal.includes(importee) |
|||
}, |
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
lodash: lodash_exports, |
|||
shortid: ["generate"], |
|||
}, |
|||
}), |
|||
builtins(), |
|||
nodeglobals(), |
|||
//terser()
|
|||
], |
|||
watch: { |
|||
clearScreen: false, |
|||
}, |
|||
} |
|||
|
|||
@ -1,53 +1,52 @@ |
|||
const { readdir, stat, copyFile } = require("fs-extra"); |
|||
const { constants } = require("fs"); |
|||
const { join, basename } = require("path"); |
|||
|
|||
const packagesFolder = ".."; |
|||
|
|||
const jsFile = dir => join(dir, "budibase-client.js"); |
|||
const jsMapFile = dir => join(dir, "budibase-client.js.map"); |
|||
const sourceJs = jsFile("dist"); |
|||
const sourceJsMap = jsMapFile("dist"); |
|||
|
|||
const appPackages = join(packagesFolder, "server", "appPackages"); |
|||
|
|||
const publicMain = appName => join(appPackages, appName, "public", "main"); |
|||
const publicUnauth = appName => join(appPackages, appName, "public", "unauthenticated"); |
|||
const nodeModules = appName => join(appPackages, appName, "node_modules", "@budibase", "client", "dist"); |
|||
|
|||
(async () => { |
|||
|
|||
const apps = await readdir(appPackages); |
|||
|
|||
const copySource = file => async toDir => { |
|||
const dest = join(toDir, basename(file)); |
|||
try { |
|||
await copyFile(file, dest, constants.COPYFILE_FICLONE); |
|||
console.log(`COPIED ${file} to ${dest}`); |
|||
} catch(e) { |
|||
console.log(`COPY FAILED ${file} to ${dest}: ${e}`); |
|||
} |
|||
const { readdir, stat, copyFile } = require("fs-extra") |
|||
const { constants } = require("fs") |
|||
const { join, basename } = require("path") |
|||
|
|||
const packagesFolder = ".." |
|||
|
|||
const jsFile = dir => join(dir, "budibase-client.js") |
|||
const jsMapFile = dir => join(dir, "budibase-client.js.map") |
|||
const sourceJs = jsFile("dist") |
|||
const sourceJsMap = jsMapFile("dist") |
|||
|
|||
const appPackages = join(packagesFolder, "server", "appPackages") |
|||
|
|||
const publicMain = appName => join(appPackages, appName, "public", "main") |
|||
const publicUnauth = appName => |
|||
join(appPackages, appName, "public", "unauthenticated") |
|||
const nodeModules = appName => |
|||
join(appPackages, appName, "node_modules", "@budibase", "client", "dist") |
|||
|
|||
;(async () => { |
|||
const apps = await readdir(appPackages) |
|||
|
|||
const copySource = file => async toDir => { |
|||
const dest = join(toDir, basename(file)) |
|||
try { |
|||
await copyFile(file, dest, constants.COPYFILE_FICLONE) |
|||
console.log(`COPIED ${file} to ${dest}`) |
|||
} catch (e) { |
|||
console.log(`COPY FAILED ${file} to ${dest}: ${e}`) |
|||
} |
|||
} |
|||
|
|||
const copySourceJs = copySource(sourceJs); |
|||
const copySourceJsMap = copySource(sourceJsMap); |
|||
|
|||
const copySourceJs = copySource(sourceJs) |
|||
const copySourceJsMap = copySource(sourceJsMap) |
|||
|
|||
for(let app of apps) { |
|||
if(!(await stat(join(appPackages, app))).isDirectory()) continue; |
|||
for (let app of apps) { |
|||
if (!(await stat(join(appPackages, app))).isDirectory()) continue |
|||
|
|||
await copySourceJs(nodeModules(app)); |
|||
await copySourceJsMap(nodeModules(app)); |
|||
await copySourceJs(nodeModules(app)) |
|||
await copySourceJsMap(nodeModules(app)) |
|||
|
|||
await copySourceJs(publicMain(app)); |
|||
await copySourceJsMap(publicMain(app)); |
|||
await copySourceJs(publicMain(app)) |
|||
await copySourceJsMap(publicMain(app)) |
|||
|
|||
await copySourceJs(publicUnauth(app)); |
|||
await copySourceJsMap(publicUnauth(app)); |
|||
|
|||
await copySource( |
|||
join("dist", "budibase-client.esm.mjs"))( |
|||
join(packagesFolder, "server", "builder" )); |
|||
} |
|||
await copySourceJs(publicUnauth(app)) |
|||
await copySourceJsMap(publicUnauth(app)) |
|||
|
|||
})(); |
|||
await copySource(join("dist", "budibase-client.esm.mjs"))( |
|||
join(packagesFolder, "server", "builder") |
|||
) |
|||
} |
|||
})() |
|||
|
|||
@ -1,24 +1,22 @@ |
|||
export const USER_STATE_PATH = "_bbuser" |
|||
|
|||
export const USER_STATE_PATH = "_bbuser"; |
|||
export const authenticate = api => async ({ username, password }) => { |
|||
if (!username) { |
|||
api.error("Authenticate: username not set") |
|||
return |
|||
} |
|||
|
|||
export const authenticate = (api) => async ({username, password}) => { |
|||
if (!password) { |
|||
api.error("Authenticate: password not set") |
|||
return |
|||
} |
|||
|
|||
if(!username) { |
|||
api.error("Authenticate: username not set"); |
|||
return; |
|||
} |
|||
|
|||
if(!password) { |
|||
api.error("Authenticate: password not set"); |
|||
return; |
|||
} |
|||
const user = await api.post({ |
|||
url: "/api/authenticate", |
|||
body: { username, password }, |
|||
}) |
|||
|
|||
const user = await api.post({ |
|||
url:"/api/authenticate", |
|||
body : {username, password} |
|||
}); |
|||
|
|||
// set user even if error - so it is defined at least
|
|||
api.setState(USER_STATE_PATH, user); |
|||
localStorage.setItem("budibase:user", JSON.stringify(user)); |
|||
} |
|||
// set user even if error - so it is defined at least
|
|||
api.setState(USER_STATE_PATH, user) |
|||
localStorage.setItem("budibase:user", JSON.stringify(user)) |
|||
} |
|||
|
|||
@ -1,20 +1,19 @@ |
|||
import {trimSlash} from "../common/trimSlash"; |
|||
import { trimSlash } from "../common/trimSlash" |
|||
|
|||
export const listRecords = api => async ({indexKey, statePath}) => { |
|||
if(!indexKey) { |
|||
api.error("Load Record: record key not set"); |
|||
return; |
|||
} |
|||
|
|||
if(!statePath) { |
|||
api.error("Load Record: state path not set"); |
|||
return; |
|||
} |
|||
export const listRecords = api => async ({ indexKey, statePath }) => { |
|||
if (!indexKey) { |
|||
api.error("Load Record: record key not set") |
|||
return |
|||
} |
|||
|
|||
const records = await api.get({ |
|||
url:`/api/listRecords/${trimSlash(indexKey)}` |
|||
}); |
|||
if (!statePath) { |
|||
api.error("Load Record: state path not set") |
|||
return |
|||
} |
|||
|
|||
if(api.isSuccess(records)) |
|||
api.setState(statePath, records); |
|||
} |
|||
const records = await api.get({ |
|||
url: `/api/listRecords/${trimSlash(indexKey)}`, |
|||
}) |
|||
|
|||
if (api.isSuccess(records)) api.setState(statePath, records) |
|||
} |
|||
|
|||
@ -1,21 +1,19 @@ |
|||
import {trimSlash} from "../common/trimSlash"; |
|||
import { trimSlash } from "../common/trimSlash" |
|||
|
|||
export const loadRecord = (api) => async ({recordKey, statePath}) => { |
|||
export const loadRecord = api => async ({ recordKey, statePath }) => { |
|||
if (!recordKey) { |
|||
api.error("Load Record: record key not set") |
|||
return |
|||
} |
|||
|
|||
if(!recordKey) { |
|||
api.error("Load Record: record key not set"); |
|||
return; |
|||
} |
|||
|
|||
if(!statePath) { |
|||
api.error("Load Record: state path not set"); |
|||
return; |
|||
} |
|||
if (!statePath) { |
|||
api.error("Load Record: state path not set") |
|||
return |
|||
} |
|||
|
|||
const record = await api.get({ |
|||
url:`/api/record/${trimSlash(recordKey)}` |
|||
}); |
|||
const record = await api.get({ |
|||
url: `/api/record/${trimSlash(recordKey)}`, |
|||
}) |
|||
|
|||
if(api.isSuccess(record)) |
|||
api.setState(statePath, record); |
|||
} |
|||
if (api.isSuccess(record)) api.setState(statePath, record) |
|||
} |
|||
|
|||
@ -1,29 +1,29 @@ |
|||
import {trimSlash} from "../common/trimSlash"; |
|||
import { trimSlash } from "../common/trimSlash" |
|||
|
|||
export const saveRecord = (api) => async ({statePath}) => { |
|||
|
|||
if(!statePath) { |
|||
api.error("Load Record: state path not set"); |
|||
return; |
|||
} |
|||
export const saveRecord = api => async ({ statePath }) => { |
|||
if (!statePath) { |
|||
api.error("Load Record: state path not set") |
|||
return |
|||
} |
|||
|
|||
const recordtoSave = api.getState(statePath); |
|||
const recordtoSave = api.getState(statePath) |
|||
|
|||
if(!recordtoSave) { |
|||
api.error(`there is no record in state: ${statePath}`); |
|||
return; |
|||
} |
|||
if (!recordtoSave) { |
|||
api.error(`there is no record in state: ${statePath}`) |
|||
return |
|||
} |
|||
|
|||
if(!recordtoSave.key) { |
|||
api.error(`item in state does not appear to be a record - it has no key (${statePath})`); |
|||
return; |
|||
} |
|||
if (!recordtoSave.key) { |
|||
api.error( |
|||
`item in state does not appear to be a record - it has no key (${statePath})` |
|||
) |
|||
return |
|||
} |
|||
|
|||
const savedRecord = await api.post({ |
|||
url:`/api/record/${trimSlash(recordtoSave.key)}`, |
|||
body: recordtoSave |
|||
}); |
|||
const savedRecord = await api.post({ |
|||
url: `/api/record/${trimSlash(recordtoSave.key)}`, |
|||
body: recordtoSave, |
|||
}) |
|||
|
|||
if(api.isSuccess(savedRecord)) |
|||
api.setState(statePath, savedRecord); |
|||
} |
|||
if (api.isSuccess(savedRecord)) api.setState(statePath, savedRecord) |
|||
} |
|||
|
|||
@ -1 +1 @@ |
|||
export const trimSlash = (str) => str.replace(/^\/+|\/+$/g, ''); |
|||
export const trimSlash = str => str.replace(/^\/+|\/+$/g, "") |
|||
|
|||
@ -1,5 +1 @@ |
|||
|
|||
export { |
|||
$ |
|||
} from "../../../core/src/common"; |
|||
|
|||
export { $ } from "../../../core/src/common" |
|||
|
|||
@ -1,14 +1,12 @@ |
|||
|
|||
|
|||
export const createCoreApp = (appDefinition, user) => { |
|||
const app = { |
|||
datastore: null, |
|||
crypto:null, |
|||
publish: () => {}, |
|||
hierarchy: appDefinition.hierarchy, |
|||
actions: appDefinition.actions, |
|||
user |
|||
}; |
|||
const app = { |
|||
datastore: null, |
|||
crypto: null, |
|||
publish: () => {}, |
|||
hierarchy: appDefinition.hierarchy, |
|||
actions: appDefinition.actions, |
|||
user, |
|||
} |
|||
|
|||
return app; |
|||
} |
|||
return app |
|||
} |
|||
|
|||
@ -1,24 +1,18 @@ |
|||
import { createCoreApp } from "./createCoreApp" |
|||
import { |
|||
getNew, getNewChild |
|||
} from "../../../core/src/recordApi/getNew"; |
|||
import { |
|||
constructHierarchy |
|||
} from "../../../core/src/templateApi/createNodes"; |
|||
import { getNew, getNewChild } from "../../../core/src/recordApi/getNew" |
|||
import { constructHierarchy } from "../../../core/src/templateApi/createNodes" |
|||
|
|||
export const createCoreApi = (appDefinition, user) => { |
|||
|
|||
const app = createCoreApp(appDefinition, user); |
|||
const app = createCoreApp(appDefinition, user) |
|||
|
|||
return { |
|||
recordApi: { |
|||
getNew: getNew(app), |
|||
getNewChild: getNewChild(app) |
|||
}, |
|||
return { |
|||
recordApi: { |
|||
getNew: getNew(app), |
|||
getNewChild: getNewChild(app), |
|||
}, |
|||
|
|||
templateApi: { |
|||
constructHierarchy |
|||
} |
|||
} |
|||
|
|||
} |
|||
templateApi: { |
|||
constructHierarchy, |
|||
}, |
|||
} |
|||
} |
|||
|
|||
@ -1,82 +1,99 @@ |
|||
import {writable} from "svelte/store"; |
|||
import { createCoreApi } from "./core"; |
|||
import { getStateOrValue } from "./state/getState"; |
|||
import { setState, setStateFromBinding } from "./state/setState"; |
|||
import { trimSlash } from "./common/trimSlash"; |
|||
import { isBound } from "./state/isState"; |
|||
import { _initialiseChildren } from "./render/initialiseChildren"; |
|||
import { createTreeNode } from "./render/renderComponent"; |
|||
import { writable } from "svelte/store" |
|||
import { createCoreApi } from "./core" |
|||
import { getStateOrValue } from "./state/getState" |
|||
import { setState, setStateFromBinding } from "./state/setState" |
|||
import { trimSlash } from "./common/trimSlash" |
|||
import { isBound } from "./state/isState" |
|||
import { _initialiseChildren } from "./render/initialiseChildren" |
|||
import { createTreeNode } from "./render/renderComponent" |
|||
|
|||
export const createApp = (document, componentLibraries, appDefinition, user, uiFunctions) => { |
|||
|
|||
const coreApi = createCoreApi(appDefinition, user); |
|||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(appDefinition.hierarchy); |
|||
const store = writable({ |
|||
_bbuser: user |
|||
}); |
|||
export const createApp = ( |
|||
document, |
|||
componentLibraries, |
|||
appDefinition, |
|||
user, |
|||
uiFunctions |
|||
) => { |
|||
const coreApi = createCoreApi(appDefinition, user) |
|||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy( |
|||
appDefinition.hierarchy |
|||
) |
|||
const store = writable({ |
|||
_bbuser: user, |
|||
}) |
|||
|
|||
let globalState = null |
|||
store.subscribe(s => { |
|||
globalState = s |
|||
}) |
|||
|
|||
let globalState = null; |
|||
store.subscribe(s => { |
|||
globalState = s; |
|||
}); |
|||
const relativeUrl = url => |
|||
appDefinition.appRootPath |
|||
? appDefinition.appRootPath + "/" + trimSlash(url) |
|||
: url |
|||
|
|||
const relativeUrl = (url) => |
|||
appDefinition.appRootPath |
|||
? appDefinition.appRootPath + "/" + trimSlash(url) |
|||
: url; |
|||
const apiCall = method => (url, body) => |
|||
fetch(relativeUrl(url), { |
|||
method: method, |
|||
headers: { |
|||
"Content-Type": "application/json", |
|||
}, |
|||
body: body && JSON.stringify(body), |
|||
}) |
|||
|
|||
const api = { |
|||
post: apiCall("POST"), |
|||
get: apiCall("GET"), |
|||
patch: apiCall("PATCH"), |
|||
delete: apiCall("DELETE"), |
|||
} |
|||
|
|||
const apiCall = (method) => (url, body) => |
|||
fetch(relativeUrl(url), { |
|||
method: method, |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
body: body && JSON.stringify(body), |
|||
}); |
|||
const safeCallEvent = (event, context) => { |
|||
const isFunction = obj => |
|||
!!(obj && obj.constructor && obj.call && obj.apply) |
|||
|
|||
const api = { |
|||
post: apiCall("POST"), |
|||
get: apiCall("GET"), |
|||
patch: apiCall("PATCH"), |
|||
delete: apiCall("DELETE") |
|||
}; |
|||
if (isFunction(event)) event(context) |
|||
} |
|||
|
|||
const safeCallEvent = (event, context) => { |
|||
|
|||
const isFunction = (obj) => |
|||
!!(obj && obj.constructor && obj.call && obj.apply); |
|||
const initialiseChildrenParams = (hydrate, treeNode) => ({ |
|||
bb, |
|||
coreApi, |
|||
store, |
|||
document, |
|||
componentLibraries, |
|||
appDefinition, |
|||
hydrate, |
|||
uiFunctions, |
|||
treeNode, |
|||
}) |
|||
|
|||
if(isFunction(event)) event(context); |
|||
} |
|||
|
|||
const initialiseChildrenParams = (hydrate, treeNode) => ({ |
|||
bb, coreApi, store, document, |
|||
componentLibraries, appDefinition, |
|||
hydrate, uiFunctions, treeNode |
|||
}); |
|||
|
|||
const bb = (treeNode, componentProps) => ({ |
|||
hydrateChildren: _initialiseChildren(initialiseChildrenParams(true, treeNode)), |
|||
appendChildren: _initialiseChildren(initialiseChildrenParams(false, treeNode)), |
|||
insertChildren: (props, htmlElement, anchor) => |
|||
_initialiseChildren(initialiseChildrenParams(false, treeNode)) |
|||
(props, htmlElement, anchor), |
|||
context: treeNode.context, |
|||
props: componentProps, |
|||
call:safeCallEvent, |
|||
setStateFromBinding: (binding, value) => setStateFromBinding(store, binding, value), |
|||
setState: (path, value) => setState(store, path, value), |
|||
getStateOrValue: (prop, currentContext) => |
|||
getStateOrValue(globalState, prop, currentContext), |
|||
store, |
|||
relativeUrl, |
|||
api, |
|||
isBound, |
|||
parent |
|||
}); |
|||
|
|||
return bb(createTreeNode()); |
|||
const bb = (treeNode, componentProps) => ({ |
|||
hydrateChildren: _initialiseChildren( |
|||
initialiseChildrenParams(true, treeNode) |
|||
), |
|||
appendChildren: _initialiseChildren( |
|||
initialiseChildrenParams(false, treeNode) |
|||
), |
|||
insertChildren: (props, htmlElement, anchor) => |
|||
_initialiseChildren(initialiseChildrenParams(false, treeNode))( |
|||
props, |
|||
htmlElement, |
|||
anchor |
|||
), |
|||
context: treeNode.context, |
|||
props: componentProps, |
|||
call: safeCallEvent, |
|||
setStateFromBinding: (binding, value) => |
|||
setStateFromBinding(store, binding, value), |
|||
setState: (path, value) => setState(store, path, value), |
|||
getStateOrValue: (prop, currentContext) => |
|||
getStateOrValue(globalState, prop, currentContext), |
|||
store, |
|||
relativeUrl, |
|||
api, |
|||
isBound, |
|||
parent, |
|||
}) |
|||
|
|||
return bb(createTreeNode()) |
|||
} |
|||
|
|||
@ -1,55 +1,59 @@ |
|||
import { createApp } from "./createApp"; |
|||
import { trimSlash } from "./common/trimSlash"; |
|||
import { createApp } from "./createApp" |
|||
import { trimSlash } from "./common/trimSlash" |
|||
|
|||
export const loadBudibase = async ({ |
|||
componentLibraries, props, |
|||
window, localStorage, uiFunctions }) => { |
|||
|
|||
const appDefinition = window["##BUDIBASE_APPDEFINITION##"]; |
|||
const uiFunctionsFromWindow = window["##BUDIBASE_APPDEFINITION##"]; |
|||
uiFunctions = uiFunctionsFromWindow || uiFunctions; |
|||
|
|||
const userFromStorage = localStorage.getItem("budibase:user") |
|||
|
|||
const user = userFromStorage ? JSON.parse(userFromStorage) : { |
|||
componentLibraries, |
|||
props, |
|||
window, |
|||
localStorage, |
|||
uiFunctions, |
|||
}) => { |
|||
const appDefinition = window["##BUDIBASE_APPDEFINITION##"] |
|||
const uiFunctionsFromWindow = window["##BUDIBASE_APPDEFINITION##"] |
|||
uiFunctions = uiFunctionsFromWindow || uiFunctions |
|||
|
|||
const userFromStorage = localStorage.getItem("budibase:user") |
|||
|
|||
const user = userFromStorage |
|||
? JSON.parse(userFromStorage) |
|||
: { |
|||
name: "annonymous", |
|||
permissions : [], |
|||
isUser:false, |
|||
temp:false |
|||
}; |
|||
|
|||
if(!componentLibraries) { |
|||
|
|||
const rootPath = appDefinition.appRootPath === "" |
|||
? "" |
|||
: "/" + trimSlash(appDefinition.appRootPath); |
|||
const componentLibraryUrl = (lib) => rootPath + "/" + trimSlash(lib) |
|||
componentLibraries = {}; |
|||
|
|||
for(let lib of appDefinition.componentLibraries) { |
|||
componentLibraries[lib.libName] = await import( |
|||
componentLibraryUrl(lib.importPath)); |
|||
} |
|||
|
|||
permissions: [], |
|||
isUser: false, |
|||
temp: false, |
|||
} |
|||
|
|||
if (!componentLibraries) { |
|||
const rootPath = |
|||
appDefinition.appRootPath === "" |
|||
? "" |
|||
: "/" + trimSlash(appDefinition.appRootPath) |
|||
const componentLibraryUrl = lib => rootPath + "/" + trimSlash(lib) |
|||
componentLibraries = {} |
|||
|
|||
for (let lib of appDefinition.componentLibraries) { |
|||
componentLibraries[lib.libName] = await import( |
|||
componentLibraryUrl(lib.importPath) |
|||
) |
|||
} |
|||
|
|||
if(!props) { |
|||
props = appDefinition.props; |
|||
} |
|||
|
|||
const app = createApp( |
|||
window.document, |
|||
componentLibraries, |
|||
appDefinition, |
|||
user, |
|||
uiFunctions || {}); |
|||
app.hydrateChildren( |
|||
[props], |
|||
window.document.body); |
|||
|
|||
return app; |
|||
}; |
|||
|
|||
if(window) { |
|||
window.loadBudibase = loadBudibase; |
|||
} |
|||
} |
|||
|
|||
if (!props) { |
|||
props = appDefinition.props |
|||
} |
|||
|
|||
const app = createApp( |
|||
window.document, |
|||
componentLibraries, |
|||
appDefinition, |
|||
user, |
|||
uiFunctions || {} |
|||
) |
|||
app.hydrateChildren([props], window.document.body) |
|||
|
|||
return app |
|||
} |
|||
|
|||
if (window) { |
|||
window.loadBudibase = loadBudibase |
|||
} |
|||
|
|||
@ -1,72 +1,80 @@ |
|||
import { |
|||
setupBinding |
|||
} from "../state/stateBinding"; |
|||
import { |
|||
split, |
|||
last |
|||
} from "lodash/fp"; |
|||
import { $ } from "../core/common"; |
|||
import { renderComponent } from "./renderComponent"; |
|||
import { setupBinding } from "../state/stateBinding" |
|||
import { split, last } from "lodash/fp" |
|||
import { $ } from "../core/common" |
|||
import { renderComponent } from "./renderComponent" |
|||
|
|||
export const _initialiseChildren = (initialiseOpts) => |
|||
(childrenProps, htmlElement, anchor=null) => { |
|||
export const _initialiseChildren = initialiseOpts => ( |
|||
childrenProps, |
|||
htmlElement, |
|||
anchor = null |
|||
) => { |
|||
const { |
|||
uiFunctions, |
|||
bb, |
|||
coreApi, |
|||
store, |
|||
componentLibraries, |
|||
treeNode, |
|||
appDefinition, |
|||
document, |
|||
hydrate, |
|||
} = initialiseOpts |
|||
|
|||
const { uiFunctions, bb, coreApi, |
|||
store, componentLibraries, treeNode, |
|||
appDefinition, document, hydrate } = initialiseOpts; |
|||
for (let childNode of treeNode.children) { |
|||
if (childNode.unsubscribe) childNode.unsubscribe() |
|||
if (childNode.component) childNode.component.$destroy() |
|||
} |
|||
|
|||
for(let childNode of treeNode.children) { |
|||
if(childNode.unsubscribe) |
|||
childNode.unsubscribe(); |
|||
if(childNode.component) |
|||
childNode.component.$destroy(); |
|||
if (hydrate) { |
|||
while (htmlElement.firstChild) { |
|||
htmlElement.removeChild(htmlElement.firstChild) |
|||
} |
|||
} |
|||
|
|||
if(hydrate) { |
|||
while (htmlElement.firstChild) { |
|||
htmlElement.removeChild(htmlElement.firstChild); |
|||
} |
|||
} |
|||
|
|||
htmlElement.classList.add(`lay-${treeNode.props._id}`) |
|||
htmlElement.classList.add(`lay-${treeNode.props._id}`) |
|||
|
|||
const renderedComponents = [] |
|||
for (let childProps of childrenProps) { |
|||
const { componentName, libName } = splitName(childProps._component) |
|||
|
|||
if (!componentName || !libName) return |
|||
|
|||
const { initialProps, bind } = setupBinding( |
|||
store, |
|||
childProps, |
|||
coreApi, |
|||
appDefinition.appRootPath |
|||
) |
|||
|
|||
const renderedComponents = []; |
|||
for(let childProps of childrenProps) { |
|||
|
|||
const {componentName, libName} = splitName(childProps._component); |
|||
const componentConstructor = componentLibraries[libName][componentName] |
|||
|
|||
if(!componentName || !libName) return; |
|||
|
|||
const {initialProps, bind} = setupBinding( |
|||
store, childProps, coreApi, |
|||
appDefinition.appRootPath); |
|||
|
|||
const componentConstructor = componentLibraries[libName][componentName]; |
|||
const renderedComponentsThisIteration = renderComponent({ |
|||
props: childProps, |
|||
parentNode: treeNode, |
|||
componentConstructor, |
|||
uiFunctions, |
|||
htmlElement, |
|||
anchor, |
|||
initialProps, |
|||
bb, |
|||
}) |
|||
|
|||
const renderedComponentsThisIteration = renderComponent({ |
|||
props: childProps, |
|||
parentNode: treeNode, |
|||
componentConstructor,uiFunctions, |
|||
htmlElement, anchor, initialProps, |
|||
bb}); |
|||
|
|||
for(let comp of renderedComponentsThisIteration) { |
|||
comp.unsubscribe = bind(comp.component); |
|||
renderedComponents.push(comp); |
|||
} |
|||
for (let comp of renderedComponentsThisIteration) { |
|||
comp.unsubscribe = bind(comp.component) |
|||
renderedComponents.push(comp) |
|||
} |
|||
} |
|||
|
|||
return renderedComponents; |
|||
return renderedComponents |
|||
} |
|||
|
|||
const splitName = fullname => { |
|||
const componentName = $(fullname, [ |
|||
split("/"), |
|||
last |
|||
]); |
|||
const componentName = $(fullname, [split("/"), last]) |
|||
|
|||
const libName =fullname.substring( |
|||
0, fullname.length - componentName.length - 1); |
|||
const libName = fullname.substring( |
|||
0, |
|||
fullname.length - componentName.length - 1 |
|||
) |
|||
|
|||
return {libName, componentName}; |
|||
return { libName, componentName } |
|||
} |
|||
|
|||
@ -1,66 +1,64 @@ |
|||
|
|||
export const renderComponent = ({ |
|||
componentConstructor, uiFunctions, |
|||
htmlElement, anchor, props, |
|||
initialProps, bb, |
|||
parentNode}) => { |
|||
componentConstructor, |
|||
uiFunctions, |
|||
htmlElement, |
|||
anchor, |
|||
props, |
|||
initialProps, |
|||
bb, |
|||
parentNode, |
|||
}) => { |
|||
const func = initialProps._id ? uiFunctions[initialProps._id] : undefined |
|||
|
|||
const func = initialProps._id |
|||
? uiFunctions[initialProps._id] |
|||
: undefined; |
|||
const parentContext = (parentNode && parentNode.context) || {} |
|||
|
|||
const parentContext = (parentNode && parentNode.context) || {}; |
|||
|
|||
let renderedNodes = []; |
|||
const render = (context) => { |
|||
|
|||
let componentContext = parentContext; |
|||
if(context) { |
|||
componentContext = {...componentContext}; |
|||
componentContext.$parent = parentContext; |
|||
} |
|||
let renderedNodes = [] |
|||
const render = context => { |
|||
let componentContext = parentContext |
|||
if (context) { |
|||
componentContext = { ...componentContext } |
|||
componentContext.$parent = parentContext |
|||
} |
|||
|
|||
const thisNode = createTreeNode(); |
|||
thisNode.context = componentContext; |
|||
thisNode.parentNode = parentNode; |
|||
thisNode.props = props; |
|||
const thisNode = createTreeNode() |
|||
thisNode.context = componentContext |
|||
thisNode.parentNode = parentNode |
|||
thisNode.props = props |
|||
|
|||
parentNode.children.push(thisNode); |
|||
renderedNodes.push(thisNode); |
|||
parentNode.children.push(thisNode) |
|||
renderedNodes.push(thisNode) |
|||
|
|||
initialProps._bb = bb(thisNode, props); |
|||
initialProps._bb = bb(thisNode, props) |
|||
|
|||
thisNode.component = new componentConstructor({ |
|||
target: htmlElement, |
|||
props: initialProps, |
|||
hydrate:false, |
|||
anchor |
|||
}); |
|||
thisNode.component = new componentConstructor({ |
|||
target: htmlElement, |
|||
props: initialProps, |
|||
hydrate: false, |
|||
anchor, |
|||
}) |
|||
|
|||
thisNode.rootElement = htmlElement.children[ |
|||
htmlElement.children.length - 1]; |
|||
|
|||
if (initialProps._id) { |
|||
thisNode.rootElement.classList.add(`pos-${initialProps._id}`) |
|||
} |
|||
} |
|||
thisNode.rootElement = htmlElement.children[htmlElement.children.length - 1] |
|||
|
|||
if(func) { |
|||
func(render, parentContext); |
|||
} else { |
|||
render(); |
|||
if (initialProps._id) { |
|||
thisNode.rootElement.classList.add(`pos-${initialProps._id}`) |
|||
} |
|||
} |
|||
|
|||
return renderedNodes; |
|||
if (func) { |
|||
func(render, parentContext) |
|||
} else { |
|||
render() |
|||
} |
|||
|
|||
return renderedNodes |
|||
} |
|||
|
|||
export const createTreeNode = () => ({ |
|||
context: {}, |
|||
props: {}, |
|||
rootElement: null, |
|||
parentNode: null, |
|||
children: [], |
|||
component: null, |
|||
unsubscribe: () => {} |
|||
}); |
|||
|
|||
context: {}, |
|||
props: {}, |
|||
rootElement: null, |
|||
parentNode: null, |
|||
children: [], |
|||
component: null, |
|||
unsubscribe: () => {}, |
|||
}) |
|||
|
|||
@ -1,63 +1,71 @@ |
|||
import { ERROR } from "./standardState"; |
|||
|
|||
export const getNewChildRecordToState = (coreApi, setState) => |
|||
({recordKey, collectionName,childRecordType,statePath}) => { |
|||
const error = errorHandler(setState); |
|||
try { |
|||
if(!recordKey) { |
|||
error("getNewChild > recordKey not set"); |
|||
return; |
|||
} |
|||
|
|||
if(!collectionName) { |
|||
error("getNewChild > collectionName not set"); |
|||
return; |
|||
} |
|||
|
|||
if(!childRecordType) { |
|||
error("getNewChild > childRecordType not set"); |
|||
return; |
|||
} |
|||
|
|||
if(!statePath) { |
|||
error("getNewChild > statePath not set"); |
|||
return; |
|||
} |
|||
|
|||
const rec = coreApi.recordApi.getNewChild(recordKey, collectionName, childRecordType); |
|||
setState(statePath, rec); |
|||
import { ERROR } from "./standardState" |
|||
|
|||
export const getNewChildRecordToState = (coreApi, setState) => ({ |
|||
recordKey, |
|||
collectionName, |
|||
childRecordType, |
|||
statePath, |
|||
}) => { |
|||
const error = errorHandler(setState) |
|||
try { |
|||
if (!recordKey) { |
|||
error("getNewChild > recordKey not set") |
|||
return |
|||
} |
|||
catch(e) { |
|||
error(e.message); |
|||
|
|||
if (!collectionName) { |
|||
error("getNewChild > collectionName not set") |
|||
return |
|||
} |
|||
} |
|||
|
|||
if (!childRecordType) { |
|||
error("getNewChild > childRecordType not set") |
|||
return |
|||
} |
|||
|
|||
export const getNewRecordToState = (coreApi, setState) => |
|||
({collectionKey,childRecordType,statePath}) => { |
|||
const error = errorHandler(setState); |
|||
try { |
|||
if(!collectionKey) { |
|||
error("getNewChild > collectionKey not set"); |
|||
return; |
|||
} |
|||
if (!statePath) { |
|||
error("getNewChild > statePath not set") |
|||
return |
|||
} |
|||
|
|||
if(!childRecordType) { |
|||
error("getNewChild > childRecordType not set"); |
|||
return; |
|||
} |
|||
const rec = coreApi.recordApi.getNewChild( |
|||
recordKey, |
|||
collectionName, |
|||
childRecordType |
|||
) |
|||
setState(statePath, rec) |
|||
} catch (e) { |
|||
error(e.message) |
|||
} |
|||
} |
|||
|
|||
if(!statePath) { |
|||
error("getNewChild > statePath not set"); |
|||
return; |
|||
} |
|||
export const getNewRecordToState = (coreApi, setState) => ({ |
|||
collectionKey, |
|||
childRecordType, |
|||
statePath, |
|||
}) => { |
|||
const error = errorHandler(setState) |
|||
try { |
|||
if (!collectionKey) { |
|||
error("getNewChild > collectionKey not set") |
|||
return |
|||
} |
|||
|
|||
const rec = coreApi.recordApi.getNew(collectionKey, childRecordType); |
|||
setState(statePath, rec); |
|||
if (!childRecordType) { |
|||
error("getNewChild > childRecordType not set") |
|||
return |
|||
} |
|||
catch(e) { |
|||
error(e.message); |
|||
|
|||
if (!statePath) { |
|||
error("getNewChild > statePath not set") |
|||
return |
|||
} |
|||
|
|||
const rec = coreApi.recordApi.getNew(collectionKey, childRecordType) |
|||
setState(statePath, rec) |
|||
} catch (e) { |
|||
error(e.message) |
|||
} |
|||
} |
|||
|
|||
const errorHandler = setState => message => setState(ERROR, message); |
|||
const errorHandler = setState => message => setState(ERROR, message) |
|||
|
|||
@ -1,56 +1,54 @@ |
|||
import { setState } from "./setState"; |
|||
import { getState } from "./getState"; |
|||
import { |
|||
isArray, isUndefined |
|||
} from "lodash/fp"; |
|||
|
|||
import { createApi } from "../api"; |
|||
import { |
|||
getNewChildRecordToState, getNewRecordToState |
|||
} from "./coreHandlers"; |
|||
|
|||
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"; |
|||
|
|||
export const eventHandlers = (store,coreApi,rootPath) => { |
|||
|
|||
const handler = (parameters, execute) => ({ |
|||
execute, parameters |
|||
}); |
|||
|
|||
const setStateWithStore = (path, value) => setState(store, path, value); |
|||
|
|||
let currentState; |
|||
store.subscribe(s => { |
|||
currentState = s; |
|||
}); |
|||
|
|||
const api = createApi({ |
|||
rootPath:rootPath, |
|||
setState: setStateWithStore, |
|||
getState: (path, fallback) => getState(currentState, path, fallback) |
|||
}); |
|||
|
|||
const setStateHandler = ({path, value}) => setState(store, path, value); |
|||
|
|||
return { |
|||
"Set State": handler(["path", "value"], setStateHandler), |
|||
"Load Record": handler(["recordKey", "statePath"], api.loadRecord), |
|||
"List Records": handler(["indexKey", "statePath"], api.listRecords), |
|||
"Save Record": handler(["statePath"], api.saveRecord), |
|||
|
|||
"Get New Child Record": handler( |
|||
["recordKey", "collectionName", "childRecordType", "statePath"], |
|||
getNewChildRecordToState(coreApi, setStateWithStore)), |
|||
|
|||
"Get New Record": handler( |
|||
["collectionKey", "childRecordType", "statePath"], |
|||
getNewRecordToState(coreApi, setStateWithStore)), |
|||
|
|||
"Authenticate": handler(["username", "password"], api.authenticate) |
|||
}; |
|||
}; |
|||
|
|||
export const isEventType = prop => |
|||
isArray(prop) |
|||
&& prop.length > 0 |
|||
&& !isUndefined(prop[0][EVENT_TYPE_MEMBER_NAME]); |
|||
import { setState } from "./setState" |
|||
import { getState } from "./getState" |
|||
import { isArray, isUndefined } from "lodash/fp" |
|||
|
|||
import { createApi } from "../api" |
|||
import { getNewChildRecordToState, getNewRecordToState } from "./coreHandlers" |
|||
|
|||
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType" |
|||
|
|||
export const eventHandlers = (store, coreApi, rootPath) => { |
|||
const handler = (parameters, execute) => ({ |
|||
execute, |
|||
parameters, |
|||
}) |
|||
|
|||
const setStateWithStore = (path, value) => setState(store, path, value) |
|||
|
|||
let currentState |
|||
store.subscribe(s => { |
|||
currentState = s |
|||
}) |
|||
|
|||
const api = createApi({ |
|||
rootPath: rootPath, |
|||
setState: setStateWithStore, |
|||
getState: (path, fallback) => getState(currentState, path, fallback), |
|||
}) |
|||
|
|||
const setStateHandler = ({ path, value }) => setState(store, path, value) |
|||
|
|||
return { |
|||
"Set State": handler(["path", "value"], setStateHandler), |
|||
"Load Record": handler(["recordKey", "statePath"], api.loadRecord), |
|||
"List Records": handler(["indexKey", "statePath"], api.listRecords), |
|||
"Save Record": handler(["statePath"], api.saveRecord), |
|||
|
|||
"Get New Child Record": handler( |
|||
["recordKey", "collectionName", "childRecordType", "statePath"], |
|||
getNewChildRecordToState(coreApi, setStateWithStore) |
|||
), |
|||
|
|||
"Get New Record": handler( |
|||
["collectionKey", "childRecordType", "statePath"], |
|||
getNewRecordToState(coreApi, setStateWithStore) |
|||
), |
|||
|
|||
Authenticate: handler(["username", "password"], api.authenticate), |
|||
} |
|||
} |
|||
|
|||
export const isEventType = prop => |
|||
isArray(prop) && |
|||
prop.length > 0 && |
|||
!isUndefined(prop[0][EVENT_TYPE_MEMBER_NAME]) |
|||
|
|||
@ -1,67 +1,59 @@ |
|||
import { isUndefined, isObject } from "lodash/fp" |
|||
import { |
|||
isUndefined, |
|||
isObject |
|||
} from "lodash/fp"; |
|||
import { |
|||
isBound,BB_STATE_BINDINGPATH, BB_STATE_FALLBACK, takeStateFromStore |
|||
} from "./isState"; |
|||
isBound, |
|||
BB_STATE_BINDINGPATH, |
|||
BB_STATE_FALLBACK, |
|||
takeStateFromStore, |
|||
} from "./isState" |
|||
|
|||
export const getState = (s, path, fallback) => { |
|||
if (!s) return fallback |
|||
if (!path || path.length === 0) return fallback |
|||
|
|||
if(!s) return fallback; |
|||
if(!path || path.length === 0) return fallback; |
|||
if (path === "$") return s |
|||
|
|||
if(path === "$") return s; |
|||
const pathParts = path.split(".") |
|||
const safeGetPath = (obj, currentPartIndex = 0) => { |
|||
const currentKey = pathParts[currentPartIndex] |
|||
|
|||
const pathParts = path.split("."); |
|||
const safeGetPath = (obj, currentPartIndex=0) => { |
|||
|
|||
const currentKey = pathParts[currentPartIndex]; |
|||
|
|||
if(pathParts.length - 1 == currentPartIndex) { |
|||
const value = obj[currentKey]; |
|||
if(isUndefined(value)) |
|||
return fallback; |
|||
else |
|||
return value; |
|||
} |
|||
|
|||
if(obj[currentKey] === null |
|||
|| obj[currentKey] === undefined |
|||
|| !isObject(obj[currentKey])) { |
|||
|
|||
return fallback; |
|||
} |
|||
|
|||
return safeGetPath(obj[currentKey], currentPartIndex + 1); |
|||
if (pathParts.length - 1 == currentPartIndex) { |
|||
const value = obj[currentKey] |
|||
if (isUndefined(value)) return fallback |
|||
else return value |
|||
} |
|||
|
|||
if ( |
|||
obj[currentKey] === null || |
|||
obj[currentKey] === undefined || |
|||
!isObject(obj[currentKey]) |
|||
) { |
|||
return fallback |
|||
} |
|||
|
|||
return safeGetPath(obj[currentKey], currentPartIndex + 1) |
|||
} |
|||
|
|||
return safeGetPath(s); |
|||
return safeGetPath(s) |
|||
} |
|||
|
|||
export const getStateOrValue = (globalState, prop, currentContext) => { |
|||
|
|||
if(!prop) return prop; |
|||
|
|||
if(isBound(prop)) { |
|||
if (!prop) return prop |
|||
|
|||
const stateToUse = takeStateFromStore(prop) |
|||
? globalState |
|||
: currentContext; |
|||
if (isBound(prop)) { |
|||
const stateToUse = takeStateFromStore(prop) ? globalState : currentContext |
|||
|
|||
return getState(stateToUse, prop[BB_STATE_BINDINGPATH], prop[BB_STATE_FALLBACK]); |
|||
} |
|||
return getState( |
|||
stateToUse, |
|||
prop[BB_STATE_BINDINGPATH], |
|||
prop[BB_STATE_FALLBACK] |
|||
) |
|||
} |
|||
|
|||
if(prop.path && prop.source) { |
|||
const stateToUse = prop.source === "store" |
|||
? globalState |
|||
: currentContext; |
|||
if (prop.path && prop.source) { |
|||
const stateToUse = prop.source === "store" ? globalState : currentContext |
|||
|
|||
return getState(stateToUse, prop.path, prop.fallback); |
|||
} |
|||
return getState(stateToUse, prop.path, prop.fallback) |
|||
} |
|||
|
|||
return prop; |
|||
|
|||
} |
|||
return prop |
|||
} |
|||
|
|||
@ -1,17 +1,16 @@ |
|||
export const BB_STATE_BINDINGPATH = "##bbstate"; |
|||
export const BB_STATE_BINDINGSOURCE = "##bbsource"; |
|||
export const BB_STATE_FALLBACK = "##bbstatefallback"; |
|||
export const BB_STATE_BINDINGPATH = "##bbstate" |
|||
export const BB_STATE_BINDINGSOURCE = "##bbsource" |
|||
export const BB_STATE_FALLBACK = "##bbstatefallback" |
|||
|
|||
export const isBound = (prop) => |
|||
prop !== undefined |
|||
&& prop[BB_STATE_BINDINGPATH] !== undefined; |
|||
|
|||
export const takeStateFromStore = (prop) => |
|||
prop[BB_STATE_BINDINGSOURCE] === undefined |
|||
|| prop[BB_STATE_BINDINGSOURCE] === "store"; |
|||
export const isBound = prop => |
|||
prop !== undefined && prop[BB_STATE_BINDINGPATH] !== undefined |
|||
|
|||
export const takeStateFromContext = (prop) => |
|||
prop[BB_STATE_BINDINGSOURCE] === "context"; |
|||
export const takeStateFromStore = prop => |
|||
prop[BB_STATE_BINDINGSOURCE] === undefined || |
|||
prop[BB_STATE_BINDINGSOURCE] === "store" |
|||
|
|||
export const takeStateFromEventParameters = (prop) => |
|||
prop[BB_STATE_BINDINGSOURCE] === "event"; |
|||
export const takeStateFromContext = prop => |
|||
prop[BB_STATE_BINDINGSOURCE] === "context" |
|||
|
|||
export const takeStateFromEventParameters = prop => |
|||
prop[BB_STATE_BINDINGSOURCE] === "event" |
|||
|
|||
@ -1,38 +1,34 @@ |
|||
import { |
|||
isObject |
|||
} from "lodash/fp"; |
|||
import { BB_STATE_BINDINGPATH } from "./isState"; |
|||
import { isObject } from "lodash/fp" |
|||
import { BB_STATE_BINDINGPATH } from "./isState" |
|||
|
|||
export const setState = (store, path, value) => { |
|||
if (!path || path.length === 0) return |
|||
|
|||
if(!path || path.length === 0) return; |
|||
const pathParts = path.split(".") |
|||
const safeSetPath = (obj, currentPartIndex = 0) => { |
|||
const currentKey = pathParts[currentPartIndex] |
|||
|
|||
const pathParts = path.split("."); |
|||
const safeSetPath = (obj, currentPartIndex=0) => { |
|||
|
|||
const currentKey = pathParts[currentPartIndex]; |
|||
|
|||
if(pathParts.length - 1 == currentPartIndex) { |
|||
obj[currentKey] = value; |
|||
return; |
|||
} |
|||
|
|||
if(obj[currentKey] === null |
|||
|| obj[currentKey] === undefined |
|||
|| !isObject(obj[currentKey])) { |
|||
|
|||
obj[currentKey] = {}; |
|||
} |
|||
|
|||
safeSetPath(obj[currentKey], currentPartIndex + 1); |
|||
if (pathParts.length - 1 == currentPartIndex) { |
|||
obj[currentKey] = value |
|||
return |
|||
} |
|||
|
|||
if ( |
|||
obj[currentKey] === null || |
|||
obj[currentKey] === undefined || |
|||
!isObject(obj[currentKey]) |
|||
) { |
|||
obj[currentKey] = {} |
|||
} |
|||
|
|||
store.update(s => { |
|||
safeSetPath(s); |
|||
return s; |
|||
}); |
|||
}; |
|||
safeSetPath(obj[currentKey], currentPartIndex + 1) |
|||
} |
|||
|
|||
store.update(s => { |
|||
safeSetPath(s) |
|||
return s |
|||
}) |
|||
} |
|||
|
|||
export const setStateFromBinding = (store, binding, value) => |
|||
setState(store, binding[BB_STATE_BINDINGPATH], value); |
|||
export const setStateFromBinding = (store, binding, value) => |
|||
setState(store, binding[BB_STATE_BINDINGPATH], value) |
|||
|
|||
@ -1,2 +1 @@ |
|||
|
|||
export const ERROR = "##error_message"; |
|||
export const ERROR = "##error_message" |
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue