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