@ -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,56 +1,47 @@ |
|||||
<script> |
<script> |
||||
|
import BackendNav from "./nav/BackendNav.svelte" |
||||
|
import Database from "./database/DatabaseRoot.svelte" |
||||
|
import UserInterface from "./userInterface/UserInterfaceRoot.svelte" |
||||
|
import ActionsAndTriggers from "./actionsAndTriggers/ActionsAndTriggersRoot.svelte" |
||||
|
import AccessLevels from "./accessLevels/AccessLevelsRoot.svelte" |
||||
|
import ComingSoon from "./common/ComingSoon.svelte" |
||||
|
|
||||
import BackendNav from "./nav/BackendNav.svelte"; |
import { store } from "./builderStore" |
||||
import Database from "./database/DatabaseRoot.svelte" ; |
|
||||
import UserInterface from "./userInterface/UserInterfaceRoot.svelte" ; |
|
||||
import ActionsAndTriggers from "./actionsAndTriggers/ActionsAndTriggersRoot.svelte" ; |
|
||||
import AccessLevels from "./accessLevels/AccessLevelsRoot.svelte" ; |
|
||||
import ComingSoon from "./common/ComingSoon.svelte"; |
|
||||
|
|
||||
import {store} from "./builderStore"; |
|
||||
|
|
||||
export let navWidth = "50px"; |
|
||||
|
|
||||
|
|
||||
|
export let navWidth = "50px" |
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
<div class="nav"> |
<div class="nav"> |
||||
<BackendNav /> |
<BackendNav /> |
||||
</div> |
</div> |
||||
<div class="content" |
<div class="content" style="width: calc(100% - {navWidth}); left: {navWidth}"> |
||||
style="width: calc(100% - {navWidth}); left: {navWidth}"> |
{#if $store.activeNav === 'database'} |
||||
{#if $store.activeNav === "database"} |
<Database /> |
||||
<Database /> |
{:else if $store.activeNav === 'actions'} |
||||
{:else if $store.activeNav === "actions"} |
<ActionsAndTriggers /> |
||||
<ActionsAndTriggers /> |
{:else if $store.activeNav === 'access levels'} |
||||
{:else if $store.activeNav === "access levels"} |
<AccessLevels /> |
||||
<AccessLevels /> |
|
||||
{/if} |
{/if} |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
height: 100%; |
||||
height: 100%; |
display: flex; |
||||
display: flex; |
} |
||||
} |
|
||||
|
.content { |
||||
|
flex: 1 1 auto; |
||||
.content { |
height: 100%; |
||||
flex: 1 1 auto; |
background-color: var(--white); |
||||
height: 100%; |
margin: 0; |
||||
background-color: var(--white); |
} |
||||
margin:0; |
|
||||
} |
.nav { |
||||
|
flex: 0 1 auto; |
||||
.nav { |
width: 300px; |
||||
flex: 0 1 auto; |
height: 100%; |
||||
width: 300px; |
} |
||||
height: 100%; |
</style> |
||||
} |
|
||||
|
|
||||
</style> |
|
||||
|
|||||
@ -1,56 +1,55 @@ |
|||||
<script> |
<script> |
||||
|
import Button from "./common/Button.svelte" |
||||
|
import { store } from "./builderStore" |
||||
|
|
||||
import Button from "./common/Button.svelte" |
let errors = [] |
||||
import { store } from "./builderStore"; |
|
||||
|
|
||||
let errors = []; |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
<div class="inner"> |
<div class="inner"> |
||||
<img src="/_builder/assets/budibase-logo.png" class="logo" alt="budibase logo"/> |
<img |
||||
<div> |
src="/_builder/assets/budibase-logo.png" |
||||
|
class="logo" |
||||
<div> |
alt="budibase logo" /> |
||||
<h4 style="margin-bottom: 20px">Choose an Application</h4> |
<div> |
||||
{#each $store.apps as app} |
|
||||
<a href={`#/${app}`} class="app-link">{app}</a> |
<div> |
||||
{/each} |
<h4 style="margin-bottom: 20px">Choose an Application</h4> |
||||
</div> |
{#each $store.apps as app} |
||||
</div> |
<a href={`#/${app}`} class="app-link">{app}</a> |
||||
|
{/each} |
||||
|
</div> |
||||
</div> |
</div> |
||||
|
</div> |
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
position: fixed; |
position: fixed; |
||||
margin: 0 auto; |
margin: 0 auto; |
||||
text-align: center; |
text-align: center; |
||||
top: 20%; |
top: 20%; |
||||
/*color: #333333; |
/*color: #333333; |
||||
background-color: #fdfdfd;*/ |
background-color: #fdfdfd;*/ |
||||
width:100%; |
width: 100%; |
||||
} |
} |
||||
|
|
||||
.inner { |
.inner { |
||||
display:inline-block; |
display: inline-block; |
||||
margin: auto; |
margin: auto; |
||||
} |
} |
||||
|
|
||||
.logo { |
.logo { |
||||
width: 300px; |
width: 300px; |
||||
margin-bottom: 40px; |
margin-bottom: 40px; |
||||
} |
} |
||||
|
|
||||
.root :global(.option) { |
.root :global(.option) { |
||||
width:250px; |
width: 250px; |
||||
} |
} |
||||
|
|
||||
.app-link { |
.app-link { |
||||
margin-top: 10px; |
margin-top: 10px; |
||||
display: block; |
display: block; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,27 +1,26 @@ |
|||||
<script> |
<script> |
||||
|
import IconButton from "./common/IconButton.svelte" |
||||
import IconButton from "./common/IconButton.svelte"; |
import { store } from "./builderStore" |
||||
import { store } from "./builderStore"; |
import UserInterfaceRoot from "./userInterface/UserInterfaceRoot.svelte" |
||||
import UserInterfaceRoot from "./userInterface/UserInterfaceRoot.svelte"; |
import { fade } from "svelte/transition" |
||||
import { fade } from "svelte/transition"; |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
<div class="content uk-container"> |
<div class="content uk-container"> |
||||
|
|
||||
<h1>Settings</h1> |
<h1>Settings</h1> |
||||
|
|
||||
<label> |
<label> |
||||
<input type="checkbox" |
<input |
||||
class="uk-checkbox" |
type="checkbox" |
||||
bind:checked={$store.useAnalytics}> |
class="uk-checkbox" |
||||
Send analytics |
bind:checked={$store.useAnalytics} /> |
||||
</label> |
Send analytics |
||||
|
</label> |
||||
|
|
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
|
||||
</style> |
</style> |
||||
|
|||||
@ -1,99 +1,91 @@ |
|||||
<script> |
<script> |
||||
|
import { cloneDeep, map, some, filter } from "lodash/fp" |
||||
|
import Textbox from "../common/Textbox.svelte" |
||||
|
import Checkbox from "../common/Checkbox.svelte" |
||||
|
import ButtonGroup from "../common/ButtonGroup.svelte" |
||||
|
import Button from "../common/Button.svelte" |
||||
|
import { validateAccessLevels } from "../common/core" |
||||
|
import ErrorsBox from "../common/ErrorsBox.svelte" |
||||
|
|
||||
import {cloneDeep, map, some, filter} from "lodash/fp"; |
export let level |
||||
import Textbox from "../common/Textbox.svelte"; |
export let allPermissions |
||||
import Checkbox from "../common/Checkbox.svelte"; |
export let onFinished |
||||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
export let isNew |
||||
import Button from "../common/Button.svelte"; |
export let allLevels |
||||
import {validateAccessLevels} from "../common/core"; |
export let hierarchy |
||||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
export let actions |
||||
|
|
||||
export let level; |
let errors = [] |
||||
export let allPermissions; |
let clonedLevel = cloneDeep(level) |
||||
export let onFinished; |
|
||||
export let isNew; |
const matchPermissions = (p1, p2) => |
||||
export let allLevels; |
p1.type === p2.type && |
||||
export let hierarchy; |
((!p2.nodeKey && !p1.nodeKey) || p2.nodeKey === p1.nodeKey) |
||||
export let actions; |
|
||||
|
const hasPermission = hasPerm => |
||||
let errors = []; |
some(p => matchPermissions(p, hasPerm))(clonedLevel.permissions) |
||||
let clonedLevel = cloneDeep(level); |
|
||||
|
$: permissionMatrix = map(p => ({ |
||||
const matchPermissions = (p1, p2) => |
permission: p, |
||||
p1.type === p2.type |
hasPermission: hasPermission(p), |
||||
&& |
}))(allPermissions) |
||||
((!p2.nodeKey && !p1.nodeKey) |
|
||||
|| p2.nodeKey === p1.nodeKey); |
const getPermissionName = perm => |
||||
|
perm.nodeKey ? `${perm.type} - ${perm.nodeKey}` : perm.type |
||||
const hasPermission = hasPerm => |
|
||||
some(p => matchPermissions(p, hasPerm)) |
const save = () => { |
||||
(clonedLevel.permissions); |
const newLevels = isNew |
||||
|
? [...allLevels, clonedLevel] |
||||
$: permissionMatrix = |
: [...filter(l => l.name !== level.name)(allLevels), clonedLevel] |
||||
map(p => ({permission:p, hasPermission: hasPermission(p)})) |
|
||||
(allPermissions) |
errors = validateAccessLevels(hierarchy, actions, newLevels) |
||||
|
|
||||
const getPermissionName = perm => |
if (errors.length > 0) return |
||||
perm.nodeKey |
|
||||
? `${perm.type} - ${perm.nodeKey}` |
onFinished(clonedLevel) |
||||
: perm.type; |
} |
||||
|
|
||||
const save = () => { |
const permissionChanged = perm => ev => { |
||||
|
const hasPermission = ev.target.checked |
||||
const newLevels = |
|
||||
isNew |
if (hasPermission) { |
||||
? [...allLevels, clonedLevel] |
clonedLevel.permissions.push(perm) |
||||
: [...filter(l => l.name !== level.name)(allLevels), clonedLevel]; |
|
||||
|
|
||||
errors = validateAccessLevels( |
|
||||
hierarchy, |
|
||||
actions, |
|
||||
newLevels |
|
||||
); |
|
||||
|
|
||||
if(errors.length > 0) return; |
|
||||
|
|
||||
onFinished(clonedLevel); |
|
||||
} |
|
||||
|
|
||||
const permissionChanged = perm => ev => { |
|
||||
const hasPermission = ev.target.checked; |
|
||||
|
|
||||
if(hasPermission) { |
|
||||
clonedLevel.permissions.push(perm); |
|
||||
} else { |
} else { |
||||
clonedLevel.permissions = filter(p => !matchPermissions(p, perm))(clonedLevel.permissions); |
clonedLevel.permissions = filter(p => !matchPermissions(p, perm))( |
||||
|
clonedLevel.permissions |
||||
|
) |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div> |
<div> |
||||
|
|
||||
<ErrorsBox {errors} /> |
<ErrorsBox {errors} /> |
||||
|
|
||||
<form class="uk-form-horizontal"> |
|
||||
|
|
||||
<Textbox label="Name" bind:text={clonedLevel.name} /> |
<form class="uk-form-horizontal"> |
||||
|
|
||||
{#each permissionMatrix as permission} |
<Textbox label="Name" bind:text={clonedLevel.name} /> |
||||
<div> |
|
||||
<Checkbox label={getPermissionName(permission.permission)} |
|
||||
checked={permission.hasPermission} |
|
||||
on:change={permissionChanged(permission.permission)} /> |
|
||||
</div> |
|
||||
{/each} |
|
||||
|
|
||||
</form> |
{#each permissionMatrix as permission} |
||||
|
<div> |
||||
|
<Checkbox |
||||
|
label={getPermissionName(permission.permission)} |
||||
|
checked={permission.hasPermission} |
||||
|
on:change={permissionChanged(permission.permission)} /> |
||||
|
</div> |
||||
|
{/each} |
||||
|
|
||||
<ButtonGroup style="margin-top: 10px"> |
</form> |
||||
<Button color="primary" grouped on:click={save}>Save</Button> |
|
||||
<Button color="secondary" grouped on:click={() => onFinished()}>Cancel</Button> |
|
||||
</ButtonGroup> |
|
||||
|
|
||||
|
<ButtonGroup style="margin-top: 10px"> |
||||
|
<Button color="primary" grouped on:click={save}>Save</Button> |
||||
|
<Button color="secondary" grouped on:click={() => onFinished()}> |
||||
|
Cancel |
||||
|
</Button> |
||||
|
</ButtonGroup> |
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
|
||||
</style> |
</style> |
||||
|
|||||
@ -1,119 +1,115 @@ |
|||||
<script> |
<script> |
||||
|
import ButtonGroup from "../common/ButtonGroup.svelte" |
||||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
import Button from "../common/Button.svelte" |
||||
import Button from "../common/Button.svelte"; |
import { store } from "../builderStore" |
||||
import {store} from "../builderStore"; |
import { generateFullPermissions, getNewAccessLevel } from "../common/core" |
||||
import {generateFullPermissions, getNewAccessLevel} from "../common/core"; |
import getIcon from "../common/icon" |
||||
import getIcon from "../common/icon"; |
import AccessLevelView from "./AccessLevelView.svelte" |
||||
import AccessLevelView from "./AccessLevelView.svelte"; |
import Modal from "../common/Modal.svelte" |
||||
import Modal from "../common/Modal.svelte"; |
|
||||
|
let editingLevel = null |
||||
let editingLevel = null; |
let editingLevelIsNew = false |
||||
let editingLevelIsNew = false; |
$: isEditing = editingLevel !== null |
||||
$: isEditing = (editingLevel !== null); |
|
||||
|
let allPermissions = [] |
||||
let allPermissions = []; |
store.subscribe(db => { |
||||
store.subscribe(db => { |
allPermissions = generateFullPermissions(db.hierarchy, db.actions) |
||||
allPermissions = generateFullPermissions(db.hierarchy, db.actions); |
}) |
||||
}) |
|
||||
|
let onLevelEdit = level => { |
||||
let onLevelEdit = (level) => { |
editingLevel = level |
||||
editingLevel = level; |
editingLevelIsNew = false |
||||
editingLevelIsNew = false; |
} |
||||
}; |
|
||||
|
let onLevelCancel = () => { |
||||
let onLevelCancel = () => { |
editingAction = null |
||||
editingAction = null; |
} |
||||
}; |
|
||||
|
let onLevelDelete = level => { |
||||
let onLevelDelete = (level) => { |
store.deleteLevel(level) |
||||
store.deleteLevel(level); |
} |
||||
}; |
|
||||
|
let createNewLevel = () => { |
||||
|
editingLevelIsNew = true |
||||
let createNewLevel = () => { |
editingLevel = getNewAccessLevel() |
||||
editingLevelIsNew = true; |
} |
||||
editingLevel = getNewAccessLevel(); |
|
||||
} |
let onEditingFinished = level => { |
||||
|
if (level) { |
||||
let onEditingFinished = (level) => { |
store.saveLevel(level, editingLevelIsNew, editingLevel) |
||||
if(level) { |
|
||||
store.saveLevel(level, editingLevelIsNew, editingLevel); |
|
||||
} |
} |
||||
editingLevel = null; |
editingLevel = null |
||||
} |
} |
||||
|
|
||||
const getPermissionsString = perms => { |
|
||||
return `${perms.length} / ${allPermissions.length}`; |
|
||||
} |
|
||||
|
|
||||
|
const getPermissionsString = perms => { |
||||
|
return `${perms.length} / ${allPermissions.length}` |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
|
|
||||
<ButtonGroup> |
<ButtonGroup> |
||||
<Button grouped color="secondary" on:click={createNewLevel}>Create New Access Level</Button> |
<Button grouped color="secondary" on:click={createNewLevel}> |
||||
</ButtonGroup> |
Create New Access Level |
||||
|
</Button> |
||||
|
</ButtonGroup> |
||||
|
|
||||
{#if $store.accessLevels} |
{#if $store.accessLevels} |
||||
<table class="fields-table uk-table uk-table-small"> |
<table class="fields-table uk-table uk-table-small"> |
||||
<thead> |
<thead> |
||||
<tr> |
<tr> |
||||
<th>Name</th> |
<th>Name</th> |
||||
<th>Permissions</th> |
<th>Permissions</th> |
||||
<th></th> |
<th /> |
||||
</tr> |
</tr> |
||||
</thead> |
</thead> |
||||
<tbody> |
<tbody> |
||||
{#each $store.accessLevels.levels as level} |
{#each $store.accessLevels.levels as level} |
||||
<tr> |
<tr> |
||||
<td >{level.name}</td> |
<td>{level.name}</td> |
||||
<td >{getPermissionsString(level.permissions)}</td> |
<td>{getPermissionsString(level.permissions)}</td> |
||||
<td class="edit-button"> |
<td class="edit-button"> |
||||
<span on:click={() => onLevelEdit(level)}>{@html getIcon("edit")}</span> |
<span on:click={() => onLevelEdit(level)}> |
||||
<span on:click={() => onLevelDelete(level)}>{@html getIcon("trash")}</span> |
{@html getIcon('edit')} |
||||
|
</span> |
||||
|
<span on:click={() => onLevelDelete(level)}> |
||||
|
{@html getIcon('trash')} |
||||
|
</span> |
||||
</td> |
</td> |
||||
</tr> |
</tr> |
||||
{/each} |
{/each} |
||||
</tbody> |
</tbody> |
||||
</table> |
</table> |
||||
{:else} |
{:else}(no actions added){/if} |
||||
(no actions added) |
|
||||
{/if} |
|
||||
|
|
||||
|
|
||||
<Modal bind:isOpen={isEditing}> |
<Modal bind:isOpen={isEditing}> |
||||
{#if isEditing} |
{#if isEditing} |
||||
<AccessLevelView level={editingLevel} |
<AccessLevelView |
||||
allPermissions={allPermissions} |
level={editingLevel} |
||||
onFinished={onEditingFinished} |
{allPermissions} |
||||
isNew={editingLevelIsNew} |
onFinished={onEditingFinished} |
||||
allLevels={$store.accessLevels.levels} |
isNew={editingLevelIsNew} |
||||
hierarchy={$store.hierarchy} |
allLevels={$store.accessLevels.levels} |
||||
actions={$store.actions} /> |
hierarchy={$store.hierarchy} |
||||
{/if} |
actions={$store.actions} /> |
||||
</Modal> |
{/if} |
||||
|
</Modal> |
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
height: 100%; |
height: 100%; |
||||
position: relative; |
position: relative; |
||||
padding: 1.5rem; |
padding: 1.5rem; |
||||
|
} |
||||
|
|
||||
} |
.actions-header { |
||||
|
|
||||
.actions-header { |
|
||||
flex: 0 1 auto; |
flex: 0 1 auto; |
||||
} |
} |
||||
|
|
||||
.node-view { |
.node-view { |
||||
overflow-y: auto; |
overflow-y: auto; |
||||
flex: 1 1 auto; |
flex: 1 1 auto; |
||||
} |
} |
||||
|
</style> |
||||
|
|
||||
</style> |
|
||||
|
|||||
@ -1,123 +1,134 @@ |
|||||
<script> |
<script> |
||||
|
import Textbox from "../common/Textbox.svelte" |
||||
import Textbox from "../common/Textbox.svelte"; |
import Button from "../common/Button.svelte" |
||||
import Button from "../common/Button.svelte"; |
import ButtonGroup from "../common/ButtonGroup.svelte" |
||||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
import { cloneDeep, filter, keys, map, isUndefined } from "lodash/fp" |
||||
import {cloneDeep, filter, keys, |
import ErrorsBox from "../common/ErrorsBox.svelte" |
||||
map, isUndefined} from "lodash/fp"; |
import { validateActions, pipe } from "../common/core" |
||||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
import getIcon from "../common/icon" |
||||
import {validateActions, pipe} from "../common/core"; |
|
||||
import getIcon from "../common/icon"; |
export let action |
||||
|
export let onFinished = action => {} |
||||
export let action; |
export let allActions |
||||
export let onFinished = (action) => {}; |
export let isNew = true |
||||
export let allActions; |
|
||||
export let isNew = true; |
let optKey = "" |
||||
|
let optValue = "" |
||||
let optKey = ""; |
|
||||
let optValue = ""; |
let clonedAction = cloneDeep(action) |
||||
|
let initialOptions = pipe(action.initialOptions, [ |
||||
let clonedAction = cloneDeep(action); |
|
||||
let initialOptions = pipe(action.initialOptions, [ |
|
||||
keys, |
keys, |
||||
map(k => ({key:k, value:action.initialOptions[k]})) |
map(k => ({ key: k, value: action.initialOptions[k] })), |
||||
]); |
]) |
||||
let errors = []; |
let errors = [] |
||||
|
|
||||
const addNewOption = () => { |
const addNewOption = () => { |
||||
|
if ( |
||||
if(optKey && optValue && isUndefined(clonedAction.initialOptions[optKey])) { |
optKey && |
||||
clonedAction.initialOptions[optKey] = optValue; |
optValue && |
||||
initialOptions = [...initialOptions, { |
isUndefined(clonedAction.initialOptions[optKey]) |
||||
key:optKey, value: optValue |
) { |
||||
}]; |
clonedAction.initialOptions[optKey] = optValue |
||||
optKey = ""; |
initialOptions = [ |
||||
optValue = ""; |
...initialOptions, |
||||
} |
{ |
||||
} |
key: optKey, |
||||
|
value: optValue, |
||||
const removeOption = (opt) => { |
}, |
||||
if(opt) { |
] |
||||
delete clonedAction.initialOptions[opt.key] |
optKey = "" |
||||
initialOptions = pipe(initialOptions, [ |
optValue = "" |
||||
filter(o => o.key !== opt.key) |
|
||||
]); |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
const save = () => { |
const removeOption = opt => { |
||||
|
if (opt) { |
||||
|
delete clonedAction.initialOptions[opt.key] |
||||
|
initialOptions = pipe(initialOptions, [filter(o => o.key !== opt.key)]) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const save = () => { |
||||
const newActionsList = [ |
const newActionsList = [ |
||||
...pipe(allActions ,[filter(a => a !== action)]), |
...pipe(allActions, [filter(a => a !== action)]), |
||||
clonedAction] |
clonedAction, |
||||
|
] |
||||
|
|
||||
errors = pipe(newActionsList ,[ |
errors = pipe(newActionsList, [validateActions, map(e => e.error)]) |
||||
validateActions, |
|
||||
map(e => e.error) |
|
||||
]); |
|
||||
|
|
||||
if(errors.length === 0) |
if (errors.length === 0) onFinished(clonedAction) |
||||
onFinished(clonedAction); |
} |
||||
} |
|
||||
|
|
||||
const cancel = () => { |
|
||||
onFinished(); |
|
||||
} |
|
||||
|
|
||||
|
const cancel = () => { |
||||
|
onFinished() |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
|
|
||||
<ErrorsBox {errors} /> |
<ErrorsBox {errors} /> |
||||
|
|
||||
<form class="uk-form-horizontal"> |
<form class="uk-form-horizontal"> |
||||
|
|
||||
<Textbox label="Name" bind:text={clonedAction.name} /> |
<Textbox label="Name" bind:text={clonedAction.name} /> |
||||
<Textbox label="Behaviour Source" bind:text={clonedAction.behaviourSource} /> |
<Textbox |
||||
<Textbox label="Behaviour" bind:text={clonedAction.behaviourName} /> |
label="Behaviour Source" |
||||
|
bind:text={clonedAction.behaviourSource} /> |
||||
</form> |
<Textbox label="Behaviour" bind:text={clonedAction.behaviourName} /> |
||||
|
|
||||
<div class=" uk-form-stacked" style="margin-bottom: 20px"> |
</form> |
||||
<label class="uk-form-label">Default Options</label> |
|
||||
<div class="uk-grid-small" uk-grid> |
<div class=" uk-form-stacked" style="margin-bottom: 20px"> |
||||
<input class="uk-input uk-width-1-4 uk-margin-right" placeholder="key" bind:value={optKey} > |
<label class="uk-form-label">Default Options</label> |
||||
<input class="uk-input uk-width-1-4 uk-margin-right" placeholder="value" bind:value={optValue} > |
<div class="uk-grid-small" uk-grid> |
||||
<Button color="primary-outline uk-width-1-4" on:click={addNewOption}>Add</Button> |
<input |
||||
</div> |
class="uk-input uk-width-1-4 uk-margin-right" |
||||
<div style="margin-top: 10px"> |
placeholder="key" |
||||
{#each initialOptions as option} |
bind:value={optKey} /> |
||||
<span class="option-container">{option.key} : {option.value} <span style="font-size:10pt; cursor: pointer" on:click={() => removeOption(option)}>{@html getIcon("trash-2")}</span></span> |
<input |
||||
{/each} |
class="uk-input uk-width-1-4 uk-margin-right" |
||||
</div> |
placeholder="value" |
||||
|
bind:value={optValue} /> |
||||
|
<Button color="primary-outline uk-width-1-4" on:click={addNewOption}> |
||||
|
Add |
||||
|
</Button> |
||||
|
</div> |
||||
|
<div style="margin-top: 10px"> |
||||
|
{#each initialOptions as option} |
||||
|
<span class="option-container"> |
||||
|
{option.key} : {option.value} |
||||
|
<span |
||||
|
style="font-size:10pt; cursor: pointer" |
||||
|
on:click={() => removeOption(option)}> |
||||
|
{@html getIcon('trash-2')} |
||||
|
</span> |
||||
|
</span> |
||||
|
{/each} |
||||
</div> |
</div> |
||||
|
</div> |
||||
|
|
||||
<ButtonGroup> |
<ButtonGroup> |
||||
<Button color="secondary" grouped on:click={save}>Save</Button> |
<Button color="secondary" grouped on:click={save}>Save</Button> |
||||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
||||
</ButtonGroup> |
</ButtonGroup> |
||||
|
|
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
padding: 2rem; |
padding: 2rem; |
||||
border-radius: 2rem; |
border-radius: 2rem; |
||||
} |
} |
||||
|
|
||||
.uk-grid-small { |
.uk-grid-small { |
||||
padding: 1rem; |
padding: 1rem; |
||||
} |
} |
||||
|
|
||||
.option-container { |
.option-container { |
||||
border-style: dotted; |
border-style: dotted; |
||||
border-width: 1px; |
border-width: 1px; |
||||
border-color: var(--primary75); |
border-color: var(--primary75); |
||||
padding: 3px; |
padding: 3px; |
||||
margin-right: 5px; |
margin-right: 5px; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,112 +1,111 @@ |
|||||
<script> |
<script> |
||||
|
import getIcon from "../common/icon" |
||||
import getIcon from "../common/icon"; |
import { store } from "../builderStore" |
||||
import {store} from "../builderStore"; |
import Button from "../common/Button.svelte" |
||||
import Button from "../common/Button.svelte"; |
import ButtonGroup from "../common/ButtonGroup.svelte" |
||||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
import ActionView from "./ActionView.svelte" |
||||
import ActionView from "./ActionView.svelte"; |
import Modal from "../common/Modal.svelte" |
||||
import Modal from "../common/Modal.svelte"; |
import { pipe } from "../common/core" |
||||
import {pipe} from "../common/core"; |
import { keys, map, join } from "lodash/fp" |
||||
import {keys, map, join} from "lodash/fp"; |
|
||||
|
export let editingActionIsNew = false |
||||
export let editingActionIsNew = false; |
export let editingAction = null |
||||
export let editingAction = null; |
export let onActionEdit = action => {} |
||||
export let onActionEdit = (action) => {}; |
export let onActionDelete = action => {} |
||||
export let onActionDelete = (action) => {}; |
export let onActionSave = action => {} |
||||
export let onActionSave = (action) => {}; |
export let onActionCancel = () => {} |
||||
export let onActionCancel = () => {}; |
|
||||
|
$: isEditing = editingAction !== null |
||||
$: isEditing = (editingAction !== null); |
|
||||
|
let actionsArray = [] |
||||
let actionsArray = []; |
store.subscribe(s => { |
||||
store.subscribe(s => { |
actionsArray = pipe(s.actions, [keys, map(k => s.actions[k])]) |
||||
actionsArray = pipe(s.actions, [ |
}) |
||||
keys, |
|
||||
map(k => s.actions[k]) |
let getDefaultOptionsHtml = defaultOptions => |
||||
]); |
|
||||
}); |
|
||||
|
|
||||
let getDefaultOptionsHtml = defaultOptions => |
|
||||
pipe(defaultOptions, [ |
pipe(defaultOptions, [ |
||||
keys, |
keys, |
||||
map(k => `<span style="color:var(--slate)">${k}: </span>${JSON.stringify(defaultOptions[k])}`), |
map( |
||||
join("<br>") |
k => |
||||
]); |
`<span style="color:var(--slate)">${k}: </span>${JSON.stringify( |
||||
|
defaultOptions[k] |
||||
|
)}` |
||||
let actionEditingFinished = (action) => { |
), |
||||
|
join("<br>"), |
||||
if(action) { |
]) |
||||
onActionSave(action) |
|
||||
|
let actionEditingFinished = action => { |
||||
|
if (action) { |
||||
|
onActionSave(action) |
||||
} else { |
} else { |
||||
onActionCancel(); |
onActionCancel() |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<h3 class="title">Actions</h3> |
<h3 class="title">Actions</h3> |
||||
|
|
||||
{#if actionsArray} |
{#if actionsArray} |
||||
<table class="fields-table uk-table uk-table-small uk-table-striped"> |
<table class="fields-table uk-table uk-table-small uk-table-striped"> |
||||
<thead> |
<thead> |
||||
<tr> |
<tr> |
||||
<th >Description</th> |
<th>Description</th> |
||||
<th>Behaviour Source</th> |
<th>Behaviour Source</th> |
||||
<th>Behaviour Name</th> |
<th>Behaviour Name</th> |
||||
<th>Default Options</th> |
<th>Default Options</th> |
||||
<th></th> |
<th /> |
||||
</tr> |
</tr> |
||||
</thead> |
</thead> |
||||
<tbody> |
<tbody> |
||||
{#each actionsArray as action} |
{#each actionsArray as action} |
||||
<tr> |
<tr> |
||||
<td class="table-content">{action.name}</td> |
<td class="table-content">{action.name}</td> |
||||
<td class="table-content">{action.behaviourSource}</td> |
<td class="table-content">{action.behaviourSource}</td> |
||||
<td class="table-content">{action.behaviourName}</td> |
<td class="table-content">{action.behaviourName}</td> |
||||
<td class="table-content">{@html getDefaultOptionsHtml(action.initialOptions)}</td> |
<td class="table-content"> |
||||
<td class="edit-button"> |
{@html getDefaultOptionsHtml(action.initialOptions)} |
||||
<span on:click={() => onActionEdit(action)}>{@html getIcon("edit")}</span> |
</td> |
||||
<span on:click={() => onActionDelete(action)}>{@html getIcon("trash")}</span> |
<td class="edit-button"> |
||||
</td> |
<span on:click={() => onActionEdit(action)}> |
||||
|
{@html getIcon('edit')} |
||||
|
</span> |
||||
|
<span on:click={() => onActionDelete(action)}> |
||||
|
{@html getIcon('trash')} |
||||
|
</span> |
||||
|
</td> |
||||
</tr> |
</tr> |
||||
{/each} |
{/each} |
||||
</tbody> |
</tbody> |
||||
</table> |
</table> |
||||
{:else} |
{:else}(no actions added){/if} |
||||
(no actions added) |
|
||||
{/if} |
|
||||
|
|
||||
|
|
||||
<Modal bind:isOpen={isEditing}> |
<Modal bind:isOpen={isEditing}> |
||||
{#if isEditing} |
{#if isEditing} |
||||
<ActionView action={editingAction} |
<ActionView |
||||
allActions={$store.actions} |
action={editingAction} |
||||
onFinished={actionEditingFinished} |
allActions={$store.actions} |
||||
isNew={editingActionIsNew}/> |
onFinished={actionEditingFinished} |
||||
{/if} |
isNew={editingActionIsNew} /> |
||||
|
{/if} |
||||
</Modal> |
</Modal> |
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.edit-button { |
||||
.edit-button { |
cursor: pointer; |
||||
cursor:pointer; |
|
||||
color: var(--secondary25); |
color: var(--secondary25); |
||||
} |
} |
||||
|
|
||||
tr:hover .edit-button { |
tr:hover .edit-button { |
||||
color: var(--secondary75); |
color: var(--secondary75); |
||||
} |
} |
||||
|
|
||||
.title { |
.title { |
||||
margin: 3rem 0rem 0rem 0rem; |
margin: 3rem 0rem 0rem 0rem; |
||||
font-weight: 700; |
font-weight: 700; |
||||
} |
} |
||||
|
|
||||
.table-content { |
.table-content { |
||||
font-weight: 500; |
font-weight: 500; |
||||
font-size: .9rem; |
font-size: 0.9rem; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,129 +1,130 @@ |
|||||
<script> |
<script> |
||||
|
import getIcon from "../common/icon" |
||||
import getIcon from "../common/icon"; |
import { store } from "../builderStore" |
||||
import {store} from "../builderStore"; |
import Button from "../common/Button.svelte" |
||||
import Button from "../common/Button.svelte"; |
import ButtonGroup from "../common/ButtonGroup.svelte" |
||||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
import Actions from "./Actions.svelte" |
||||
import Actions from "./Actions.svelte"; |
import Triggers from "./Triggers.svelte" |
||||
import Triggers from "./Triggers.svelte"; |
import { getNewAction, getNewTrigger } from "../common/core" |
||||
import {getNewAction, getNewTrigger} from "../common/core"; |
|
||||
|
let editingAction = null |
||||
let editingAction = null; |
let editingActionIsNew = true |
||||
let editingActionIsNew = true; |
let editingTrigger = null |
||||
let editingTrigger = null; |
let editingTriggerIsNew = true |
||||
let editingTriggerIsNew = true; |
|
||||
|
let getDefaultOptionsHtml = defaultOptions => |
||||
let getDefaultOptionsHtml = defaultOptions => |
|
||||
pipe(defaultOptions, [ |
pipe(defaultOptions, [ |
||||
keys, |
keys, |
||||
map(k => `<span style="color:var(--slate)">${k}: </span>${JSON.parse(typeOptions[k])}`), |
map( |
||||
join("<br>") |
k => |
||||
]); |
`<span style="color:var(--slate)">${k}: </span>${JSON.parse( |
||||
|
typeOptions[k] |
||||
let onActionEdit = (action) => { |
)}` |
||||
editingAction = action; |
), |
||||
editingActionIsNew = false; |
join("<br>"), |
||||
} |
]) |
||||
|
|
||||
let newAction = () => { |
let onActionEdit = action => { |
||||
editingAction = getNewAction(); |
editingAction = action |
||||
editingActionIsNew = true; |
editingActionIsNew = false |
||||
} |
} |
||||
|
|
||||
let onActionDelete = (action) => { |
let newAction = () => { |
||||
store.deleteAction(action); |
editingAction = getNewAction() |
||||
} |
editingActionIsNew = true |
||||
|
} |
||||
let deleteTrigger = () => {} |
|
||||
|
let onActionDelete = action => { |
||||
let editTrigger = (trigger) => { |
store.deleteAction(action) |
||||
editingTrigger = trigger; |
} |
||||
editingTriggerIsNew = false; |
|
||||
} |
let deleteTrigger = () => {} |
||||
|
|
||||
let newTrigger = () => { |
let editTrigger = trigger => { |
||||
editingTrigger = getNewTrigger(); |
editingTrigger = trigger |
||||
editingTriggerIsNew = true; |
editingTriggerIsNew = false |
||||
} |
} |
||||
|
|
||||
let onActionSave = action => { |
let newTrigger = () => { |
||||
store.saveAction( |
editingTrigger = getNewTrigger() |
||||
action, |
editingTriggerIsNew = true |
||||
editingActionIsNew, |
} |
||||
editingAction); |
|
||||
|
let onActionSave = action => { |
||||
editingAction = null; |
store.saveAction(action, editingActionIsNew, editingAction) |
||||
} |
|
||||
|
editingAction = null |
||||
let onActionCancel = () => { |
} |
||||
editingAction = null; |
|
||||
} |
let onActionCancel = () => { |
||||
|
editingAction = null |
||||
let onTriggerSave = trigger => { |
} |
||||
store.saveTrigger( |
|
||||
trigger, |
let onTriggerSave = trigger => { |
||||
editingTriggerIsNew, |
store.saveTrigger(trigger, editingTriggerIsNew, editingTrigger) |
||||
editingTrigger); |
|
||||
|
editingTrigger = null |
||||
editingTrigger = null; |
} |
||||
} |
|
||||
|
let onTriggerCancel = () => { |
||||
let onTriggerCancel = () => { |
editingTrigger = null |
||||
editingTrigger = null; |
} |
||||
} |
|
||||
|
let onTriggerEdit = trigger => { |
||||
let onTriggerEdit = (trigger) => { |
editingTrigger = trigger |
||||
editingTrigger = trigger; |
editingTriggerIsNew = false |
||||
editingTriggerIsNew = false; |
} |
||||
} |
|
||||
|
let onTriggerDelete = trigger => { |
||||
|
store.deleteTrigger(trigger) |
||||
let onTriggerDelete = (trigger) => { |
} |
||||
store.deleteTrigger(trigger); |
|
||||
} |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
<div class="actions-header"> |
<div class="actions-header"> |
||||
<ButtonGroup> |
<ButtonGroup> |
||||
<Button color="secondary" |
<Button color="secondary" grouped on:click={newAction}> |
||||
grouped |
Create New Action |
||||
on:click={newAction}>Create New Action</Button> |
</Button> |
||||
<Button color="tertiary" |
<Button color="tertiary" grouped on:click={newTrigger}> |
||||
grouped |
Create New Trigger |
||||
on:click={newTrigger}>Create New Trigger</Button> |
</Button> |
||||
</ButtonGroup> |
</ButtonGroup> |
||||
</div> |
</div> |
||||
|
|
||||
<div class="node-view"> |
<div class="node-view"> |
||||
<Actions {editingActionIsNew} {editingAction} |
<Actions |
||||
{onActionEdit} {onActionDelete} {onActionSave} |
{editingActionIsNew} |
||||
{onActionCancel} /> |
{editingAction} |
||||
|
{onActionEdit} |
||||
<Triggers {editingTriggerIsNew} {editingTrigger} |
{onActionDelete} |
||||
{onTriggerEdit} {onTriggerDelete} {onTriggerSave} |
{onActionSave} |
||||
{onTriggerCancel} /> |
{onActionCancel} /> |
||||
</div> |
|
||||
|
<Triggers |
||||
|
{editingTriggerIsNew} |
||||
|
{editingTrigger} |
||||
|
{onTriggerEdit} |
||||
|
{onTriggerDelete} |
||||
|
{onTriggerSave} |
||||
|
{onTriggerCancel} /> |
||||
|
</div> |
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
height: 100%; |
height: 100%; |
||||
position: relative; |
position: relative; |
||||
padding: 1.5rem; |
padding: 1.5rem; |
||||
|
} |
||||
|
|
||||
} |
.actions-header { |
||||
|
|
||||
.actions-header { |
|
||||
flex: 0 1 auto; |
flex: 0 1 auto; |
||||
} |
} |
||||
|
|
||||
.node-view { |
.node-view { |
||||
overflow-y: auto; |
overflow-y: auto; |
||||
flex: 1 1 auto; |
flex: 1 1 auto; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,68 +1,71 @@ |
|||||
<script> |
<script> |
||||
|
import Textbox from "../common/Textbox.svelte" |
||||
import Textbox from "../common/Textbox.svelte"; |
import Button from "../common/Button.svelte" |
||||
import Button from "../common/Button.svelte"; |
import Dropdown from "../common/Dropdown.svelte" |
||||
import Dropdown from "../common/Dropdown.svelte"; |
import ButtonGroup from "../common/ButtonGroup.svelte" |
||||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
import CodeArea from "../common/CodeArea.svelte" |
||||
import CodeArea from "../common/CodeArea.svelte"; |
import { cloneDeep, filter, keys, some, map, isUndefined } from "lodash/fp" |
||||
import {cloneDeep, filter, keys, some, |
import ErrorsBox from "../common/ErrorsBox.svelte" |
||||
map, isUndefined} from "lodash/fp"; |
import { validateTriggers, pipe, events } from "../common/core" |
||||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
import getIcon from "../common/icon" |
||||
import {validateTriggers, pipe, events} from "../common/core"; |
|
||||
import getIcon from "../common/icon"; |
export let trigger |
||||
|
export let onFinished = action => {} |
||||
export let trigger; |
export let allTriggers |
||||
export let onFinished = (action) => {}; |
export let allActions |
||||
export let allTriggers; |
export let isNew = true |
||||
export let allActions; |
|
||||
export let isNew = true; |
let clonedTrigger = cloneDeep(trigger) |
||||
|
let errors = [] |
||||
let clonedTrigger = cloneDeep(trigger); |
$: actionNames = map(a => a.name)(allActions) |
||||
let errors = []; |
|
||||
$: actionNames = map(a => a.name)(allActions); |
let cancel = () => onFinished() |
||||
|
let save = () => { |
||||
let cancel = () => onFinished(); |
|
||||
let save = () => { |
|
||||
const newTriggersList = [ |
const newTriggersList = [ |
||||
...pipe(allTriggers ,[filter(t => t !== trigger)]), |
...pipe(allTriggers, [filter(t => t !== trigger)]), |
||||
clonedTrigger] |
clonedTrigger, |
||||
|
] |
||||
errors = validateTriggers(newTriggersList, allActions); |
|
||||
|
|
||||
const test = map(t =>(!t.actionName || some(a => a.name === t.actionName)(allActions)))(newTriggersList) |
errors = validateTriggers(newTriggersList, allActions) |
||||
|
|
||||
if(errors.length === 0) |
const test = map( |
||||
onFinished(clonedTrigger); |
t => !t.actionName || some(a => a.name === t.actionName)(allActions) |
||||
} |
)(newTriggersList) |
||||
|
|
||||
|
if (errors.length === 0) onFinished(clonedTrigger) |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<div> |
<div> |
||||
|
|
||||
<ErrorsBox {errors} style="margin-bottom:20px"/> |
<ErrorsBox {errors} style="margin-bottom:20px" /> |
||||
|
|
||||
|
<form class="uk-form-horizontal"> |
||||
|
|
||||
<form class="uk-form-horizontal"> |
<Dropdown |
||||
|
label="Event" |
||||
<Dropdown label="Event" |
options={['', ...events]} |
||||
options={["",...events]} |
bind:selected={clonedTrigger.eventName} /> |
||||
bind:selected={clonedTrigger.eventName} /> |
<Dropdown |
||||
<Dropdown label="Action" |
label="Action" |
||||
options={["",...actionNames]} |
options={['', ...actionNames]} |
||||
bind:selected={clonedTrigger.actionName} /> |
bind:selected={clonedTrigger.actionName} /> |
||||
<CodeArea label="Condition (javascript)" |
<CodeArea |
||||
bind:text={clonedTrigger.condition} /> |
label="Condition (javascript)" |
||||
<CodeArea label="Action Options Creator (javascript)" |
bind:text={clonedTrigger.condition} /> |
||||
bind:text={clonedTrigger.optionsCreator} /> |
<CodeArea |
||||
|
label="Action Options Creator (javascript)" |
||||
|
bind:text={clonedTrigger.optionsCreator} /> |
||||
|
|
||||
</form> |
</form> |
||||
|
|
||||
<ButtonGroup> |
<ButtonGroup> |
||||
<Button color="primary" grouped on:click={save}>Save</Button> |
<Button color="primary" grouped on:click={save}>Save</Button> |
||||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
||||
</ButtonGroup> |
</ButtonGroup> |
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
|
||||
</style> |
</style> |
||||
|
|||||
@ -1,94 +1,90 @@ |
|||||
<script> |
<script> |
||||
|
import { store } from "../builderStore" |
||||
import {store} from "../builderStore"; |
import getIcon from "../common/icon" |
||||
import getIcon from "../common/icon"; |
import Button from "../common/Button.svelte" |
||||
import Button from "../common/Button.svelte"; |
import Modal from "../common/Modal.svelte" |
||||
import Modal from "../common/Modal.svelte"; |
import TriggerView from "./TriggerView.svelte" |
||||
import TriggerView from "./TriggerView.svelte"; |
|
||||
|
export let editingTrigger = null |
||||
|
export let editingTriggerIsNew = true |
||||
export let editingTrigger = null; |
export let onTriggerEdit = trigger => {} |
||||
export let editingTriggerIsNew = true; |
export let onTriggerDelete = trigger => {} |
||||
export let onTriggerEdit = (trigger) => {}; |
export let onTriggerSave = trigger => {} |
||||
export let onTriggerDelete = (trigger) => {}; |
export let onTriggerCancel = () => {} |
||||
export let onTriggerSave = (trigger) => {}; |
|
||||
export let onTriggerCancel = () => {}; |
$: isEditing = editingTrigger !== null |
||||
|
|
||||
$: isEditing = (editingTrigger !== null); |
let triggerEditingFinished = trigger => { |
||||
|
if (trigger) { |
||||
let triggerEditingFinished = (trigger) => { |
onTriggerSave(trigger) |
||||
|
|
||||
if(trigger) { |
|
||||
onTriggerSave(trigger) |
|
||||
} else { |
} else { |
||||
onTriggerCancel(); |
onTriggerCancel() |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<h3 class="title">Triggers</h3> |
<h3 class="title">Triggers</h3> |
||||
|
|
||||
{#if $store.triggers} |
{#if $store.triggers} |
||||
<table class="fields-table uk-table uk-table-small uk-table-striped"> |
<table class="fields-table uk-table uk-table-small uk-table-striped"> |
||||
<thead> |
<thead> |
||||
<tr> |
<tr> |
||||
<th>Event</th> |
<th>Event</th> |
||||
<th>Action</th> |
<th>Action</th> |
||||
<th>Condition</th> |
<th>Condition</th> |
||||
<th>Create Options</th> |
<th>Create Options</th> |
||||
<th></th> |
<th /> |
||||
</tr> |
</tr> |
||||
</thead> |
</thead> |
||||
<tbody> |
<tbody> |
||||
{#each $store.triggers as trigger} |
{#each $store.triggers as trigger} |
||||
<tr> |
<tr> |
||||
<td class="table-content">{trigger.eventName}</td> |
<td class="table-content">{trigger.eventName}</td> |
||||
<td class="table-content">{trigger.actionName}</td> |
<td class="table-content">{trigger.actionName}</td> |
||||
<td class="table-content">{trigger.condition}</td> |
<td class="table-content">{trigger.condition}</td> |
||||
<td class="table-content">{trigger.optionsCreator}</td> |
<td class="table-content">{trigger.optionsCreator}</td> |
||||
<td class="edit-button"> |
<td class="edit-button"> |
||||
<span on:click={() => onTriggerEdit(trigger)}>{@html getIcon("edit")}</span> |
<span on:click={() => onTriggerEdit(trigger)}> |
||||
<span on:click={() => onTriggerDelete(trigger)}>{@html getIcon("trash")}</span> |
{@html getIcon('edit')} |
||||
</td> |
</span> |
||||
|
<span on:click={() => onTriggerDelete(trigger)}> |
||||
|
{@html getIcon('trash')} |
||||
|
</span> |
||||
|
</td> |
||||
</tr> |
</tr> |
||||
{/each} |
{/each} |
||||
</tbody> |
</tbody> |
||||
</table> |
</table> |
||||
{:else} |
{:else}(no triggers added){/if} |
||||
(no triggers added) |
|
||||
{/if} |
|
||||
|
|
||||
|
|
||||
<Modal bind:isOpen={isEditing}> |
<Modal bind:isOpen={isEditing}> |
||||
{#if isEditing} |
{#if isEditing} |
||||
<TriggerView trigger={editingTrigger} |
<TriggerView |
||||
allActions={$store.actions} |
trigger={editingTrigger} |
||||
allTriggers={$store.triggers} |
allActions={$store.actions} |
||||
onFinished={triggerEditingFinished} |
allTriggers={$store.triggers} |
||||
isNew={editingTriggerIsNew}/> |
onFinished={triggerEditingFinished} |
||||
{/if} |
isNew={editingTriggerIsNew} /> |
||||
|
{/if} |
||||
</Modal> |
</Modal> |
||||
|
|
||||
<style> |
<style> |
||||
|
.edit-button { |
||||
.edit-button { |
cursor: pointer; |
||||
cursor:pointer; |
|
||||
color: var(--secondary25); |
color: var(--secondary25); |
||||
} |
} |
||||
|
|
||||
.title { |
.title { |
||||
margin: 3rem 0rem 0rem 0rem; |
margin: 3rem 0rem 0rem 0rem; |
||||
font-weight: 700; |
font-weight: 700; |
||||
} |
} |
||||
|
|
||||
.table-content { |
.table-content { |
||||
font-weight: 500; |
font-weight: 500; |
||||
font-size: .9rem; |
font-size: 0.9rem; |
||||
} |
} |
||||
|
|
||||
tr:hover .edit-button { |
tr:hover .edit-button { |
||||
color: var(--secondary75); |
color: var(--secondary75); |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -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)}` |
||||
|
|||||
@ -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,163 +1,167 @@ |
|||||
<script> |
<script> |
||||
|
export let color = "primary" |
||||
|
export let className = "" |
||||
|
export let style = "" |
||||
|
export let groupPosition = "" |
||||
|
export let grouped = false |
||||
|
|
||||
export let color = "primary"; |
$: borderClass = grouped ? "" : "border-normal" |
||||
export let className = ""; |
|
||||
export let style = ""; |
|
||||
export let groupPosition = ""; |
|
||||
export let grouped = false; |
|
||||
|
|
||||
$: borderClass = grouped |
|
||||
? "" |
|
||||
: "border-normal"; |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<button class="{color} {className} {borderClass} {grouped ? "grouped" : ""}" |
<button |
||||
style="{style}" |
class="{color} |
||||
on:click > |
{className} |
||||
<slot/> |
{borderClass} |
||||
|
{grouped ? 'grouped' : ''}" |
||||
|
{style} |
||||
|
on:click> |
||||
|
<slot /> |
||||
</button> |
</button> |
||||
|
|
||||
<style> |
<style> |
||||
|
.border-normal { |
||||
.border-normal { border-radius: var(--borderradiusall); } |
border-radius: var(--borderradiusall); |
||||
.border-left { border-radius: var(--borderradius) 0 0 var(--borderradius); } |
} |
||||
.border-right { border-radius: 0 var(--borderradius) var(--borderradius) 0; } |
.border-left { |
||||
.border-middle { border-radius: 0; } |
border-radius: var(--borderradius) 0 0 var(--borderradius); |
||||
|
} |
||||
button { |
.border-right { |
||||
border-style: solid; |
border-radius: 0 var(--borderradius) var(--borderradius) 0; |
||||
|
} |
||||
|
.border-middle { |
||||
|
border-radius: 0; |
||||
|
} |
||||
|
|
||||
|
button { |
||||
|
border-style: solid; |
||||
padding: 7.5px 15px; |
padding: 7.5px 15px; |
||||
cursor:pointer; |
cursor: pointer; |
||||
margin:5px; |
margin: 5px; |
||||
border-radius: 5px; |
border-radius: 5px; |
||||
} |
} |
||||
|
|
||||
/* ---- PRIMARY ----*/ |
/* ---- PRIMARY ----*/ |
||||
.primary { |
.primary { |
||||
background-color: var(--primary100); |
background-color: var(--primary100); |
||||
border-color: var(--primary100); |
border-color: var(--primary100); |
||||
color: var(--white); |
color: var(--white); |
||||
} |
} |
||||
|
|
||||
.primary:hover { |
.primary:hover { |
||||
background-color: var(--primary75); |
background-color: var(--primary75); |
||||
border-color: var(--primary75); |
border-color: var(--primary75); |
||||
} |
} |
||||
|
|
||||
.primary:active { |
.primary:active { |
||||
background-color: var(--primarydark); |
background-color: var(--primarydark); |
||||
border-color: var(--primarydark); |
border-color: var(--primarydark); |
||||
} |
} |
||||
|
|
||||
.primary-outline { |
.primary-outline { |
||||
background-color: var(--white); |
background-color: var(--white); |
||||
border-color: var(--primary100); |
border-color: var(--primary100); |
||||
color: var(--primary100); |
color: var(--primary100); |
||||
} |
} |
||||
|
|
||||
.primary-outline:hover { |
.primary-outline:hover { |
||||
background-color: var(--primary10); |
background-color: var(--primary10); |
||||
} |
} |
||||
|
|
||||
.primary-outline:pressed { |
.primary-outline:pressed { |
||||
background-color: var(--primary25); |
background-color: var(--primary25); |
||||
} |
} |
||||
|
|
||||
/* ---- secondary ----*/ |
/* ---- secondary ----*/ |
||||
|
|
||||
.secondary { |
.secondary { |
||||
background-color: var(--secondary100); |
background-color: var(--secondary100); |
||||
border-color: var(--secondary100); |
border-color: var(--secondary100); |
||||
color: var(--white); |
color: var(--white); |
||||
} |
} |
||||
|
|
||||
.secondary:hover { |
.secondary:hover { |
||||
background-color: var(--secondary75); |
background-color: var(--secondary75); |
||||
border-color: var(--secondary75); |
border-color: var(--secondary75); |
||||
} |
} |
||||
|
|
||||
.secondary:pressed { |
.secondary:pressed { |
||||
background-color: var(--secondarydark); |
background-color: var(--secondarydark); |
||||
border-color: var(--secondarydark); |
border-color: var(--secondarydark); |
||||
} |
} |
||||
|
|
||||
.secondary-outline { |
.secondary-outline { |
||||
background-color: var(--white); |
background-color: var(--white); |
||||
border-color: var(--secondary100); |
border-color: var(--secondary100); |
||||
color: var(--secondary100); |
color: var(--secondary100); |
||||
} |
} |
||||
|
|
||||
.secondary-outline:hover { |
.secondary-outline:hover { |
||||
background-color: var(--secondary10); |
background-color: var(--secondary10); |
||||
} |
} |
||||
|
|
||||
.secondary-outline:pressed { |
.secondary-outline:pressed { |
||||
background-color: var(--secondary25); |
background-color: var(--secondary25); |
||||
} |
} |
||||
|
|
||||
|
/* ---- success ----*/ |
||||
/* ---- success ----*/ |
.success { |
||||
.success { |
|
||||
background-color: var(--success100); |
background-color: var(--success100); |
||||
border-color: var(--success100); |
border-color: var(--success100); |
||||
color: var(--white); |
color: var(--white); |
||||
} |
} |
||||
|
|
||||
.success:hover { |
.success:hover { |
||||
background-color: var(--success75); |
background-color: var(--success75); |
||||
border-color: var(--success75); |
border-color: var(--success75); |
||||
} |
} |
||||
|
|
||||
.success:pressed { |
.success:pressed { |
||||
background-color: var(--successdark); |
background-color: var(--successdark); |
||||
border-color: var(--successdark); |
border-color: var(--successdark); |
||||
} |
} |
||||
|
|
||||
.success-outline { |
.success-outline { |
||||
background-color: var(--white); |
background-color: var(--white); |
||||
border-color: var(--success100); |
border-color: var(--success100); |
||||
color: var(--success100); |
color: var(--success100); |
||||
} |
} |
||||
|
|
||||
.success-outline:hover { |
.success-outline:hover { |
||||
background-color: var(--success10); |
background-color: var(--success10); |
||||
} |
} |
||||
|
|
||||
.success-outline:pressed { |
.success-outline:pressed { |
||||
background-color: var(--success25); |
background-color: var(--success25); |
||||
} |
} |
||||
|
|
||||
/* ---- deletion ----*/ |
/* ---- deletion ----*/ |
||||
.deletion { |
.deletion { |
||||
background-color: var(--deletion100); |
background-color: var(--deletion100); |
||||
border-color: var(--deletion100); |
border-color: var(--deletion100); |
||||
color: var(--white); |
color: var(--white); |
||||
} |
} |
||||
|
|
||||
.deletion:hover { |
.deletion:hover { |
||||
background-color: var(--deletion75); |
background-color: var(--deletion75); |
||||
border-color: var(--deletion75); |
border-color: var(--deletion75); |
||||
} |
} |
||||
|
|
||||
.deletion:pressed { |
.deletion:pressed { |
||||
background-color: var(--deletiondark); |
background-color: var(--deletiondark); |
||||
border-color: var(--deletiondark); |
border-color: var(--deletiondark); |
||||
} |
} |
||||
|
|
||||
.deletion-outline { |
.deletion-outline { |
||||
background-color: var(--white); |
background-color: var(--white); |
||||
border-color: var(--deletion100); |
border-color: var(--deletion100); |
||||
color: var(--deletion100); |
color: var(--deletion100); |
||||
} |
} |
||||
|
|
||||
.deletion-outline:hover { |
.deletion-outline:hover { |
||||
background-color: var(--deletion10); |
background-color: var(--deletion10); |
||||
} |
} |
||||
|
|
||||
.deletion-outline:pressed { |
.deletion-outline:pressed { |
||||
background-color: var(--deletion25); |
background-color: var(--deletion25); |
||||
} |
} |
||||
|
</style> |
||||
|
|
||||
</style> |
|
||||
|
|||||
@ -1,27 +1,25 @@ |
|||||
<script> |
<script> |
||||
export let style=""; |
export let style = "" |
||||
</script> |
</script> |
||||
|
|
||||
<div class="root" style={style}> |
<div class="root" {style}> |
||||
<slot /> |
<slot /> |
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
display: flex; |
display: flex; |
||||
} |
} |
||||
|
|
||||
.root:last-child { |
.root:last-child { |
||||
border-radius: 0 var(--borderradius) var(--borderradius) 0; |
border-radius: 0 var(--borderradius) var(--borderradius) 0; |
||||
} |
} |
||||
|
|
||||
.root:first-child { |
.root:first-child { |
||||
border-radius: var(--borderradius) 0 0 var(--borderradius); |
border-radius: var(--borderradius) 0 0 var(--borderradius); |
||||
} |
} |
||||
|
|
||||
.root:not(:first-child):not(:last-child) { |
.root:not(:first-child):not(:last-child) { |
||||
border-radius: 0; |
border-radius: 0; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,16 +1,13 @@ |
|||||
<script> |
<script> |
||||
|
export let checked = false |
||||
export let checked=false; |
export let label = "" |
||||
export let label=""; |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<input class="uk-checkbox" type="checkbox" bind:checked on:change />{label} |
<input class="uk-checkbox" type="checkbox" bind:checked on:change /> |
||||
|
{label} |
||||
|
|
||||
<style> |
<style> |
||||
|
input { |
||||
input { |
margin-right: 7px; |
||||
margin-right:7px; |
} |
||||
} |
</style> |
||||
|
|
||||
</style> |
|
||||
|
|||||
@ -1,24 +1,22 @@ |
|||||
<script> |
<script> |
||||
// todo: use https://ace.c9.io |
// todo: use https://ace.c9.io |
||||
export let text = ""; |
export let text = "" |
||||
export let label = ""; |
export let label = "" |
||||
</script> |
</script> |
||||
|
|
||||
<div>{label}</div> |
<div>{label}</div> |
||||
<textarea class="uk-textarea" bind:value={text}></textarea> |
<textarea class="uk-textarea" bind:value={text} /> |
||||
|
|
||||
<style> |
<style> |
||||
|
textarea { |
||||
textarea { |
padding: 3px; |
||||
padding:3px; |
margin-top: 5px; |
||||
margin-top:5px; |
margin-bottom: 10px; |
||||
margin-bottom:10px; |
|
||||
background: var(--lightslate); |
background: var(--lightslate); |
||||
color: var(--white); |
color: var(--white); |
||||
font-family: 'Courier New', Courier, monospace; |
font-family: "Courier New", Courier, monospace; |
||||
width:95%; |
width: 95%; |
||||
height:100px; |
height: 100px; |
||||
border-radius: 5px; |
border-radius: 5px; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,17 +1,13 @@ |
|||||
<script> |
<script> |
||||
|
export let name = "" |
||||
export let name = ""; |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div> |
<div> |
||||
<h4>Coming Sometime: {name}</h4> |
<h4>Coming Sometime: {name}</h4> |
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
h4 { |
||||
h4 { |
|
||||
margin-top: 20px; |
margin-top: 20px; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,39 +1,44 @@ |
|||||
<script> |
<script> |
||||
import {createEventDispatcher} from "svelte"; |
import { createEventDispatcher } from "svelte" |
||||
|
|
||||
export let selected; |
export let selected |
||||
export let label; |
export let label |
||||
export let options; |
export let options |
||||
export let valueMember; |
export let valueMember |
||||
export let textMember; |
export let textMember |
||||
export let multiple=false; |
export let multiple = false |
||||
export let width = "medium"; |
export let width = "medium" |
||||
export let size = "small"; |
export let size = "small" |
||||
|
|
||||
const dispatch =createEventDispatcher(); |
const dispatch = createEventDispatcher() |
||||
|
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
|
|
||||
<div class="uk-margin"> |
<div class="uk-margin"> |
||||
<label class="uk-form-label">{label}</label> |
<label class="uk-form-label">{label}</label> |
||||
<div class="uk-form-controls"> |
<div class="uk-form-controls"> |
||||
{#if multiple} |
{#if multiple} |
||||
|
<select |
||||
<select class="uk-select uk-form-width-{width} uk-form-{size}" multiple bind:value={selected} on:change> |
class="uk-select uk-form-width-{width} uk-form-{size}" |
||||
{#each options as option} |
multiple |
||||
<option value={!valueMember ? option : valueMember(option)}>{!textMember ? option : textMember(option)}</option> |
bind:value={selected} |
||||
{/each} |
on:change> |
||||
</select> |
{#each options as option} |
||||
|
<option value={!valueMember ? option : valueMember(option)}> |
||||
{:else} |
{!textMember ? option : textMember(option)} |
||||
|
</option> |
||||
<select class="uk-select uk-form-width-{width} uk-form-{size}" bind:value={selected} on:change> |
{/each} |
||||
{#each options as option} |
</select> |
||||
<option value={!valueMember ? option : valueMember(option)}>{!textMember ? option : textMember(option)}</option> |
{:else} |
||||
{/each} |
<select |
||||
</select> |
class="uk-select uk-form-width-{width} uk-form-{size}" |
||||
{/if} |
bind:value={selected} |
||||
</div> |
on:change> |
||||
|
{#each options as option} |
||||
|
<option value={!valueMember ? option : valueMember(option)}> |
||||
|
{!textMember ? option : textMember(option)} |
||||
|
</option> |
||||
|
{/each} |
||||
|
</select> |
||||
|
{/if} |
||||
|
</div> |
||||
</div> |
</div> |
||||
|
|||||
@ -1,68 +1,65 @@ |
|||||
<script> |
<script> |
||||
|
import getIcon from "./icon" |
||||
import getIcon from "./icon"; |
export let iconName |
||||
export let iconName; |
export let actions = [] // [ {label: "Action Name", onclick: () => {...} } ] |
||||
export let actions = []; // [ {label: "Action Name", onclick: () => {...} } ] |
let isDroppedDown = false |
||||
let isDroppedDown = false; |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
|
<div class="root" on:click={() => (isDroppedDown = !isDroppedDown)}> |
||||
|
{@html getIcon(iconName)} |
||||
|
|
||||
<div class="root" on:click={() => isDroppedDown = !isDroppedDown}> |
<div |
||||
{@html getIcon(iconName)} |
class="dropdown-background" |
||||
|
on:click|stopPropagation={() => (isDroppedDown = false)} |
||||
<div class="dropdown-background" on:click|stopPropagation={() => isDroppedDown = false} style="display: {isDroppedDown ? 'block' : 'none'}"></div> |
style="display: {isDroppedDown ? 'block' : 'none'}" /> |
||||
|
|
||||
<div class="dropdown-content" style="display: {isDroppedDown ? 'inline-block' : 'none'}"> |
<div |
||||
{#each actions as action} |
class="dropdown-content" |
||||
<div class="action-row" on:click={action.onclick}> |
style="display: {isDroppedDown ? 'inline-block' : 'none'}"> |
||||
{action.label} |
{#each actions as action} |
||||
</div> |
<div class="action-row" on:click={action.onclick}>{action.label}</div> |
||||
{/each} |
{/each} |
||||
</div> |
</div> |
||||
|
|
||||
</div> |
|
||||
|
|
||||
|
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
.dropdown-background { |
||||
.dropdown-background { |
|
||||
position: fixed; |
position: fixed; |
||||
top:0; |
top: 0; |
||||
left:0; |
left: 0; |
||||
width:100vw; |
width: 100vw; |
||||
height:100vh; |
height: 100vh; |
||||
} |
} |
||||
|
|
||||
.root { |
.root { |
||||
cursor: pointer; |
cursor: pointer; |
||||
z-index: 1; |
z-index: 1; |
||||
} |
} |
||||
|
|
||||
.dropdown-content { |
.dropdown-content { |
||||
position: absolute; |
position: absolute; |
||||
background-color: var(--white); |
background-color: var(--white); |
||||
min-width: 160px; |
min-width: 160px; |
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); |
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); |
||||
z-index: 1; |
z-index: 1; |
||||
font-weight: normal; |
font-weight: normal; |
||||
border-style: solid; |
border-style: solid; |
||||
border-width: 1px; |
border-width: 1px; |
||||
border-color: var(--secondary10); |
border-color: var(--secondary10); |
||||
} |
} |
||||
|
|
||||
.dropdown-content:not(:focus) { |
.dropdown-content:not(:focus) { |
||||
display: none; |
display: none; |
||||
} |
} |
||||
|
|
||||
.action-row { |
.action-row { |
||||
padding: 7px 10px; |
padding: 7px 10px; |
||||
cursor:pointer; |
cursor: pointer; |
||||
} |
} |
||||
|
|
||||
.action-row:hover { |
.action-row:hover { |
||||
background-color: var(--primary100); |
background-color: var(--primary100); |
||||
color:var(--white); |
color: var(--white); |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,29 +1,29 @@ |
|||||
<script> |
<script> |
||||
export let errors = []; |
export let errors = [] |
||||
|
|
||||
$:hasErrors = errors.length > 0; |
$: hasErrors = errors.length > 0 |
||||
</script> |
</script> |
||||
|
|
||||
{#if hasErrors} |
{#if hasErrors} |
||||
<div class="error-container"> |
<div class="error-container"> |
||||
{#each errors as error} |
{#each errors as error} |
||||
<div class="error-row">{error.field ? `${error.field}: ` : ""}{error.error}</div> |
<div class="error-row"> |
||||
|
{error.field ? `${error.field}: ` : ''}{error.error} |
||||
|
</div> |
||||
{/each} |
{/each} |
||||
</div> |
</div> |
||||
{/if} |
{/if} |
||||
|
|
||||
<style> |
<style> |
||||
|
.error-container { |
||||
.error-container { |
padding: 10px; |
||||
padding:10px; |
|
||||
border-style: solid; |
border-style: solid; |
||||
border-color: var(--deletion100); |
border-color: var(--deletion100); |
||||
border-radius: var(--borderradiusall); |
border-radius: var(--borderradiusall); |
||||
background: var(--deletion75); |
background: var(--deletion75); |
||||
} |
} |
||||
|
|
||||
.error-row { |
.error-row { |
||||
padding: 5px 0px; |
padding: 5px 0px; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,63 +1,59 @@ |
|||||
<script> |
<script> |
||||
import getIcon from "./icon"; |
import getIcon from "./icon" |
||||
|
|
||||
export let size = 18; |
export let size = 18 |
||||
export let icon = ""; |
export let icon = "" |
||||
export let style = ""; |
export let style = "" |
||||
export let color = "var(--secondary100)"; |
export let color = "var(--secondary100)" |
||||
export let hoverColor = "var(--secondary75)"; |
export let hoverColor = "var(--secondary75)" |
||||
export let attributes = {}; |
export let attributes = {} |
||||
|
|
||||
let currentAttributes = []; |
let currentAttributes = [] |
||||
const addAttributes = (node, attributes) => { |
const addAttributes = (node, attributes) => { |
||||
|
const add = _attributes => { |
||||
const add = (_attributes) => { |
const attrs = [] |
||||
const attrs = []; |
for (let attr in _attributes) { |
||||
for(let attr in _attributes) { |
node.setAttribute(attr, _attributes[attr]) |
||||
node.setAttribute(attr, _attributes[attr]); |
attrs.push("uk-toggle") |
||||
attrs.push("uk-toggle") |
} |
||||
} |
currentAttributes = attrs |
||||
currentAttributes = attrs; |
|
||||
} |
} |
||||
|
|
||||
add(attributes); |
add(attributes) |
||||
|
|
||||
return { |
return { |
||||
// should implement update method |
// should implement update method |
||||
update(attributes) { |
update(attributes) { |
||||
for(let attr of currentAttributes) { |
for (let attr of currentAttributes) { |
||||
node.removeAttribute(attr) |
node.removeAttribute(attr) |
||||
} |
} |
||||
add(attributes); |
add(attributes) |
||||
}, |
}, |
||||
destroy() {} |
destroy() {}, |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<button style="{style}{style ? ";" : ""} color:{color}; --hovercolor:{hoverColor}" |
<button |
||||
on:click |
style="{style}{style ? ';' : ''} color:{color}; --hovercolor:{hoverColor}" |
||||
use:addAttributes={attributes}> |
on:click |
||||
{@html getIcon(icon, size)} |
use:addAttributes={attributes}> |
||||
|
{@html getIcon(icon, size)} |
||||
</button> |
</button> |
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
button { |
||||
button { |
|
||||
border-style: none; |
border-style: none; |
||||
background-color: rgba(0,0,0,0); |
background-color: rgba(0, 0, 0, 0); |
||||
cursor: pointer; |
cursor: pointer; |
||||
outline:none; |
outline: none; |
||||
} |
} |
||||
|
|
||||
button:hover { |
button:hover { |
||||
color: var(--hovercolor); |
color: var(--hovercolor); |
||||
} |
} |
||||
|
|
||||
button:active { |
|
||||
outline:none; |
|
||||
} |
|
||||
|
|
||||
</style> |
button:active { |
||||
|
outline: none; |
||||
|
} |
||||
|
</style> |
||||
|
|||||
|
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 246 B |
|
Before Width: | Height: | Size: 158 B After Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 400 B After Width: | Height: | Size: 430 B |
|
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 365 B |
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 305 B |
|
Before Width: | Height: | Size: 415 B After Width: | Height: | Size: 448 B |
|
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 447 B |
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 372 B |
@ -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,35 +1,30 @@ |
|||||
<script> |
<script> |
||||
import {join} from "lodash/fp"; |
import { join } from "lodash/fp" |
||||
|
|
||||
export let values; |
export let values |
||||
export let label; |
export let label |
||||
|
|
||||
const inputChanged = ev => { |
const inputChanged = ev => { |
||||
try { |
try { |
||||
values = ev.target.value.split("\n"); |
values = ev.target.value.split("\n") |
||||
} catch(_) { |
} catch (_) { |
||||
values = []; |
values = [] |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
$: valuesText = join("\n")(values); |
|
||||
|
|
||||
|
|
||||
|
$: valuesText = join("\n")(values) |
||||
</script> |
</script> |
||||
|
|
||||
|
|
||||
<div class="uk-margin"> |
<div class="uk-margin"> |
||||
<label class="uk-form-label">{label}</label> |
<label class="uk-form-label">{label}</label> |
||||
<div class="uk-form-controls"> |
<div class="uk-form-controls"> |
||||
<textarea value={valuesText} on:change={inputChanged} ></textarea> |
<textarea value={valuesText} on:change={inputChanged} /> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
textarea { |
||||
textarea { |
width: 300px; |
||||
width:300px; |
height: 200px; |
||||
height:200px; |
} |
||||
} |
</style> |
||||
|
|
||||
</style> |
|
||||
|
|||||
@ -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,62 +1,59 @@ |
|||||
<script> |
<script> |
||||
import Button from "../common/Button.svelte"; |
import Button from "../common/Button.svelte" |
||||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
import ButtonGroup from "../common/ButtonGroup.svelte" |
||||
import {store} from "../builderStore"; |
import { store } from "../builderStore" |
||||
import Modal from "../common/Modal.svelte"; |
import Modal from "../common/Modal.svelte" |
||||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
import ErrorsBox from "../common/ErrorsBox.svelte" |
||||
|
|
||||
export let left; |
export let left |
||||
let confirmDelete = false; |
let confirmDelete = false |
||||
const openConfirmDelete = () => { |
const openConfirmDelete = () => { |
||||
confirmDelete = true; |
confirmDelete = true |
||||
} |
} |
||||
|
|
||||
const deleteCurrentNode = () => { |
const deleteCurrentNode = () => { |
||||
confirmDelete = false; |
confirmDelete = false |
||||
store.deleteCurrentNode(); |
store.deleteCurrentNode() |
||||
} |
} |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root" style="left: {left}"> |
<div class="root" style="left: {left}"> |
||||
|
|
||||
<ButtonGroup> |
<ButtonGroup> |
||||
<Button color="secondary" grouped on:click={store.saveCurrentNode}> |
<Button color="secondary" grouped on:click={store.saveCurrentNode}> |
||||
{#if $store.currentNodeIsNew} |
{#if $store.currentNodeIsNew}Create{:else}Update{/if} |
||||
Create |
</Button> |
||||
{:else} |
|
||||
Update |
{#if !$store.currentNodeIsNew} |
||||
{/if} |
<Button color="tertiary" grouped on:click={openConfirmDelete}> |
||||
</Button> |
Delete |
||||
|
</Button> |
||||
{#if !$store.currentNodeIsNew} |
{/if} |
||||
<Button color="tertiary" grouped on:click={openConfirmDelete}> |
</ButtonGroup> |
||||
Delete |
|
||||
</Button> |
{#if !!$store.errors && $store.errors.length > 0} |
||||
{/if} |
|
||||
</ButtonGroup> |
|
||||
|
|
||||
{#if !!$store.errors && $store.errors.length > 0} |
|
||||
<div style="width: 500px"> |
<div style="width: 500px"> |
||||
<ErrorsBox errors={$store.errors}/> |
<ErrorsBox errors={$store.errors} /> |
||||
</div> |
</div> |
||||
{/if} |
{/if} |
||||
|
|
||||
<Modal bind:isOpen={confirmDelete}> |
<Modal bind:isOpen={confirmDelete}> |
||||
<div style="margin: 10px 0px 20px 0px">Are you sure you want to delete {$store.currentNode.name} ?</div> |
<div style="margin: 10px 0px 20px 0px"> |
||||
<div style="float:right"> |
Are you sure you want to delete {$store.currentNode.name} ? |
||||
<Button color="primary" on:click={deleteCurrentNode}>Yes</Button> |
</div> |
||||
<Button color="secondary" on:click={() => confirmDelete = false}>No</Button> |
<div style="float:right"> |
||||
</div> |
<Button color="primary" on:click={deleteCurrentNode}>Yes</Button> |
||||
</Modal> |
<Button color="secondary" on:click={() => (confirmDelete = false)}> |
||||
|
No |
||||
|
</Button> |
||||
|
</div> |
||||
|
</Modal> |
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
padding: 1.5rem; |
padding: 1.5rem; |
||||
width: 100%; |
width: 100%; |
||||
align-items: right; |
align-items: right; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,20 +1,16 @@ |
|||||
<script> |
<script> |
||||
|
import RecordCard from "./RecordCard.svelte" |
||||
|
|
||||
import RecordCard from "./RecordCard.svelte"; |
export let record = {} |
||||
|
|
||||
export let record = {}; |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
<div class="title">{record.name}</div> |
<div class="title">{record.name}</div> |
||||
<div class="inner"> |
<div class="inner"> |
||||
<div class="node-path">{record.nodeKey()}</div> |
<div class="node-path">{record.nodeKey()}</div> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
|
||||
|
</style> |
||||
|
|
||||
</style> |
|
||||
|
|||||
@ -1,82 +1,85 @@ |
|||||
<script> |
<script> |
||||
|
import HierarchyRow from "./HierarchyRow.svelte" |
||||
|
import RecordView from "./RecordView.svelte" |
||||
|
import IndexView from "./IndexView.svelte" |
||||
|
import ActionsHeader from "./ActionsHeader.svelte" |
||||
|
import { store } from "../builderStore" |
||||
|
import getIcon from "../common/icon" |
||||
|
import DropdownButton from "../common/DropdownButton.svelte" |
||||
|
import { hierarchy as hierarchyFunctions } from "../../../core/src" |
||||
|
|
||||
import HierarchyRow from "./HierarchyRow.svelte"; |
const hierarchyWidth = "200px" |
||||
import RecordView from "./RecordView.svelte"; |
|
||||
import IndexView from "./IndexView.svelte"; |
|
||||
import ActionsHeader from "./ActionsHeader.svelte"; |
|
||||
import {store} from "../builderStore"; |
|
||||
import getIcon from "../common/icon"; |
|
||||
import DropdownButton from "../common/DropdownButton.svelte"; |
|
||||
import {hierarchy as hierarchyFunctions} from "../../../core/src"; |
|
||||
|
|
||||
const hierarchyWidth = "200px"; |
const defaultNewIndexActions = [ |
||||
|
{ |
||||
|
label: "New Root Index", |
||||
|
onclick: store.newRootIndex, |
||||
|
}, |
||||
|
] |
||||
|
|
||||
const defaultNewIndexActions = [{ |
const defaultNewRecordActions = [ |
||||
label:"New Root Index", |
{ |
||||
onclick: store.newRootIndex |
label: "New Root Record", |
||||
}]; |
onclick: store.newRootRecord, |
||||
|
}, |
||||
|
] |
||||
|
|
||||
const defaultNewRecordActions = [{ |
let newIndexActions = defaultNewIndexActions |
||||
label:"New Root Record", |
let newRecordActions = defaultNewRecordActions |
||||
onclick: store.newRootRecord |
|
||||
}]; |
|
||||
|
|
||||
let newIndexActions = defaultNewIndexActions; |
store.subscribe(db => { |
||||
let newRecordActions = defaultNewRecordActions; |
if (!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
||||
|
newRecordActions = defaultNewRecordActions |
||||
store.subscribe(db => { |
newIndexActions = defaultNewIndexActions |
||||
if(!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
|
||||
newRecordActions = defaultNewRecordActions; |
|
||||
newIndexActions = defaultNewIndexActions; |
|
||||
} else { |
} else { |
||||
newRecordActions = [ |
newRecordActions = [ |
||||
...defaultNewRecordActions, |
...defaultNewRecordActions, |
||||
{label: `New Child Record of ${db.currentNode.name}`, |
{ |
||||
onclick: store.newChildRecord} |
label: `New Child Record of ${db.currentNode.name}`, |
||||
]; |
onclick: store.newChildRecord, |
||||
|
}, |
||||
|
] |
||||
|
|
||||
newIndexActions = [ |
newIndexActions = [ |
||||
...defaultNewIndexActions, |
...defaultNewIndexActions, |
||||
{label: `New Index on ${db.currentNode.name}`, |
{ |
||||
onclick: store.newChildIndex} |
label: `New Index on ${db.currentNode.name}`, |
||||
]; |
onclick: store.newChildIndex, |
||||
|
}, |
||||
|
] |
||||
} |
} |
||||
}); |
}) |
||||
|
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
<div class="actions-header"> |
<div class="actions-header"> |
||||
{#if $store.currentNode} |
{#if $store.currentNode} |
||||
<ActionsHeader left={hierarchyWidth}/> |
<ActionsHeader left={hierarchyWidth} /> |
||||
{/if} |
{/if} |
||||
</div> |
</div> |
||||
<div class="node-view"> |
<div class="node-view"> |
||||
{#if !$store.currentNode} |
{#if !$store.currentNode} |
||||
<h1 style="margin-left: 100px">:)</h1> |
<h1 style="margin-left: 100px">:)</h1> |
||||
{:else if $store.currentNode.type === "record"} |
{:else if $store.currentNode.type === 'record'} |
||||
<RecordView /> |
<RecordView /> |
||||
{:else} |
{:else} |
||||
<IndexView /> |
<IndexView /> |
||||
{/if} |
{/if} |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
.root { |
.root { |
||||
height: 100%; |
height: 100%; |
||||
position: relative; |
position: relative; |
||||
} |
} |
||||
|
|
||||
.actions-header { |
.actions-header { |
||||
flex: 0 1 auto; |
flex: 0 1 auto; |
||||
} |
} |
||||
|
|
||||
.node-view { |
.node-view { |
||||
overflow-y: auto; |
overflow-y: auto; |
||||
flex: 1 1 auto; |
flex: 1 1 auto; |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,121 +1,158 @@ |
|||||
<script> |
<script> |
||||
import Dropdown from "../common/Dropdown.svelte"; |
import Dropdown from "../common/Dropdown.svelte" |
||||
import Textbox from "../common/Textbox.svelte"; |
import Textbox from "../common/Textbox.svelte" |
||||
import Button from "../common/Button.svelte"; |
import Button from "../common/Button.svelte" |
||||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
import ButtonGroup from "../common/ButtonGroup.svelte" |
||||
import NumberBox from "../common/NumberBox.svelte"; |
import NumberBox from "../common/NumberBox.svelte" |
||||
import ValuesList from "../common/ValuesList.svelte"; |
import ValuesList from "../common/ValuesList.svelte" |
||||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
import ErrorsBox from "../common/ErrorsBox.svelte" |
||||
import Checkbox from "../common/Checkbox.svelte"; |
import Checkbox from "../common/Checkbox.svelte" |
||||
import DatePicker from "../common/DatePicker.svelte"; |
import DatePicker from "../common/DatePicker.svelte" |
||||
import {cloneDeep, assign, keys, |
import { |
||||
isNumber, includes, map, isBoolean} from "lodash/fp"; |
cloneDeep, |
||||
import {allTypes, validate, getPotentialReferenceIndexes, |
assign, |
||||
getDefaultTypeOptions, getNode, |
keys, |
||||
getPotentialReverseReferenceIndexes} from "../common/core"; |
isNumber, |
||||
|
includes, |
||||
export let field; |
map, |
||||
export let allFields; |
isBoolean, |
||||
export let onFinished = () => {}; |
} from "lodash/fp" |
||||
export let store; |
import { |
||||
|
allTypes, |
||||
let errors = []; |
validate, |
||||
let clonedField = cloneDeep(field); |
getPotentialReferenceIndexes, |
||||
|
getDefaultTypeOptions, |
||||
$: isNew = !!field && field.name.length === 0; |
getNode, |
||||
|
getPotentialReverseReferenceIndexes, |
||||
$: possibleReferenceIndexes = getPotentialReferenceIndexes( |
} from "../common/core" |
||||
store.hierarchy, store.currentNode |
|
||||
); |
export let field |
||||
|
export let allFields |
||||
$: selectedReverseRefIndex = |
export let onFinished = () => {} |
||||
!clonedField.typeOptions.indexNodeKey |
export let store |
||||
|
|
||||
|
let errors = [] |
||||
|
let clonedField = cloneDeep(field) |
||||
|
|
||||
|
$: isNew = !!field && field.name.length === 0 |
||||
|
|
||||
|
$: possibleReferenceIndexes = getPotentialReferenceIndexes( |
||||
|
store.hierarchy, |
||||
|
store.currentNode |
||||
|
) |
||||
|
|
||||
|
$: selectedReverseRefIndex = !clonedField.typeOptions.indexNodeKey |
||||
? "" |
? "" |
||||
: getNode(store.hierarchy, clonedField.typeOptions.indexNodeKey); |
: getNode(store.hierarchy, clonedField.typeOptions.indexNodeKey) |
||||
|
|
||||
$: possibleReverseReferenceIndexes = |
$: possibleReverseReferenceIndexes = !selectedReverseRefIndex |
||||
!selectedReverseRefIndex |
|
||||
? [] |
? [] |
||||
: getPotentialReverseReferenceIndexes( |
: getPotentialReverseReferenceIndexes( |
||||
store.hierarchy, selectedReverseRefIndex); |
store.hierarchy, |
||||
|
selectedReverseRefIndex |
||||
const typeChanged = (ev) => |
) |
||||
clonedField.typeOptions = getDefaultTypeOptions(ev.detail); |
|
||||
|
const typeChanged = ev => |
||||
|
(clonedField.typeOptions = getDefaultTypeOptions(ev.detail)) |
||||
const save = () => { |
|
||||
|
const save = () => { |
||||
errors = validate.field(allFields)(clonedField); |
errors = validate.field(allFields)(clonedField) |
||||
if(errors.length > 0) return; |
if (errors.length > 0) return |
||||
field.typeOptions = cloneDeep(clonedField.typeOptions); |
field.typeOptions = cloneDeep(clonedField.typeOptions) |
||||
onFinished( |
onFinished(assign(field)(clonedField)) |
||||
assign(field)(clonedField) |
} |
||||
); |
|
||||
} |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
|
|
||||
<ErrorsBox errors={errors} /> |
<ErrorsBox {errors} /> |
||||
|
|
||||
<form class="uk-form-horizontal"> |
<form class="uk-form-horizontal"> |
||||
|
|
||||
<Dropdown label="Type" bind:selected={clonedField.type} options={keys(allTypes)} on:change={typeChanged} /> |
<Dropdown |
||||
|
label="Type" |
||||
{#if isNew} |
bind:selected={clonedField.type} |
||||
<Textbox label="Field Name" bind:text={clonedField.name} /> |
options={keys(allTypes)} |
||||
{:else} |
on:change={typeChanged} /> |
||||
<div style="font-weight: bold">{clonedField.name}</div> |
|
||||
{/if} |
{#if isNew} |
||||
|
<Textbox label="Field Name" bind:text={clonedField.name} /> |
||||
<Textbox label="Label" bind:text={clonedField.label} /> |
{:else} |
||||
|
<div style="font-weight: bold">{clonedField.name}</div> |
||||
{#if clonedField.type === "string"} |
{/if} |
||||
<NumberBox label="Max Length" bind:value={clonedField.typeOptions.maxLength} /> |
|
||||
<ValuesList label="Values (options)" bind:values={clonedField.typeOptions.values} /> |
<Textbox label="Label" bind:text={clonedField.label} /> |
||||
<Checkbox label="Declared Values Only" bind:checked={clonedField.typeOptions.allowDeclaredValuesOnly} /> |
|
||||
{:else if clonedField.type === "bool"} |
{#if clonedField.type === 'string'} |
||||
<Checkbox label="Allow Null" bind:checked={clonedField.typeOptions.allowNulls} /> |
<NumberBox |
||||
{:else if clonedField.type === "datetime"} |
label="Max Length" |
||||
<DatePicker label="Min Value" bind:value={clonedField.typeOptions.minValue} /> |
bind:value={clonedField.typeOptions.maxLength} /> |
||||
<DatePicker label="Max Value" bind:value={clonedField.typeOptions.maxValue} /> |
<ValuesList |
||||
{:else if clonedField.type === "number"} |
label="Values (options)" |
||||
<NumberBox label="Min Value" bind:value={clonedField.typeOptions.minValue} /> |
bind:values={clonedField.typeOptions.values} /> |
||||
<NumberBox label="Max Value" bind:value={clonedField.typeOptions.maxValue} /> |
<Checkbox |
||||
<NumberBox label="Decimal Places" bind:value={clonedField.typeOptions.decimalPlaces} /> |
label="Declared Values Only" |
||||
{:else if clonedField.type === "reference"} |
bind:checked={clonedField.typeOptions.allowDeclaredValuesOnly} /> |
||||
<Dropdown label="Lookup Index" |
{:else if clonedField.type === 'bool'} |
||||
options={possibleReferenceIndexes} |
<Checkbox |
||||
valueMember={n => n.nodeKey()} |
label="Allow Null" |
||||
textMember={n => n.name} |
bind:checked={clonedField.typeOptions.allowNulls} /> |
||||
bind:selected={clonedField.typeOptions.indexNodeKey} /> |
{:else if clonedField.type === 'datetime'} |
||||
|
<DatePicker |
||||
<Dropdown label="Reverse Reference Index" |
label="Min Value" |
||||
options={possibleReverseReferenceIndexes} |
bind:value={clonedField.typeOptions.minValue} /> |
||||
multiple=true |
<DatePicker |
||||
valueMember={n => n.nodeKey()} |
label="Max Value" |
||||
textMember={n => n.name} |
bind:value={clonedField.typeOptions.maxValue} /> |
||||
bind:selected={clonedField.typeOptions.reverseIndexNodeKeys} /> |
{:else if clonedField.type === 'number'} |
||||
|
<NumberBox |
||||
<Textbox label="Display Value" bind:text={clonedField.typeOptions.displayValue} /> |
label="Min Value" |
||||
|
bind:value={clonedField.typeOptions.minValue} /> |
||||
{:else if clonedField.type.startsWith("array")} |
<NumberBox |
||||
<NumberBox label="Min Length" bind:value={clonedField.typeOptions.minLength} /> |
label="Max Value" |
||||
<NumberBox label="Max Length" bind:value={clonedField.typeOptions.maxLength} /> |
bind:value={clonedField.typeOptions.maxValue} /> |
||||
{/if} |
<NumberBox |
||||
|
label="Decimal Places" |
||||
</form> |
bind:value={clonedField.typeOptions.decimalPlaces} /> |
||||
|
{:else if clonedField.type === 'reference'} |
||||
<ButtonGroup style="float: right;"> |
<Dropdown |
||||
<Button color="primary" grouped on:click={save}>Save</Button> |
label="Lookup Index" |
||||
<Button color="tertiary" grouped on:click={() => onFinished(false)}>Cancel</Button> |
options={possibleReferenceIndexes} |
||||
</ButtonGroup> |
valueMember={n => n.nodeKey()} |
||||
|
textMember={n => n.name} |
||||
|
bind:selected={clonedField.typeOptions.indexNodeKey} /> |
||||
|
|
||||
|
<Dropdown |
||||
|
label="Reverse Reference Index" |
||||
|
options={possibleReverseReferenceIndexes} |
||||
|
multiple="true" |
||||
|
valueMember={n => n.nodeKey()} |
||||
|
textMember={n => n.name} |
||||
|
bind:selected={clonedField.typeOptions.reverseIndexNodeKeys} /> |
||||
|
|
||||
|
<Textbox |
||||
|
label="Display Value" |
||||
|
bind:text={clonedField.typeOptions.displayValue} /> |
||||
|
{:else if clonedField.type.startsWith('array')} |
||||
|
<NumberBox |
||||
|
label="Min Length" |
||||
|
bind:value={clonedField.typeOptions.minLength} /> |
||||
|
<NumberBox |
||||
|
label="Max Length" |
||||
|
bind:value={clonedField.typeOptions.maxLength} /> |
||||
|
{/if} |
||||
|
|
||||
|
</form> |
||||
|
|
||||
|
<ButtonGroup style="float: right;"> |
||||
|
<Button color="primary" grouped on:click={save}>Save</Button> |
||||
|
<Button color="tertiary" grouped on:click={() => onFinished(false)}> |
||||
|
Cancel |
||||
|
</Button> |
||||
|
</ButtonGroup> |
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
|
||||
|
</style> |
||||
|
|
||||
</style> |
|
||||
|
|||||
@ -1,45 +1,42 @@ |
|||||
<script> |
<script> |
||||
|
import { store } from "../builderStore" |
||||
import {store} from "../builderStore"; |
import { cloneDeep } from "lodash/fp" |
||||
import {cloneDeep} from "lodash/fp"; |
export let level = 0 |
||||
export let level = 0; |
export let node |
||||
export let node; |
|
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
<div class="title" on:click={() => store.selectExistingNode(node.nodeId)} style="padding-left: {20 + (level * 20)}px"> |
<div |
||||
{node.name} |
class="title" |
||||
</div> |
on:click={() => store.selectExistingNode(node.nodeId)} |
||||
{#if node.children} |
style="padding-left: {20 + level * 20}px"> |
||||
|
{node.name} |
||||
|
</div> |
||||
|
{#if node.children} |
||||
{#each node.children as child} |
{#each node.children as child} |
||||
<svelte:self node={child} |
<svelte:self node={child} level={level + 1} /> |
||||
level={level+1}/> |
|
||||
{/each} |
{/each} |
||||
{/if} |
{/if} |
||||
</div> |
</div> |
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
display: block; |
display: block; |
||||
font-size: .9rem; |
font-size: 0.9rem; |
||||
width: 100%; |
width: 100%; |
||||
cursor: pointer; |
cursor: pointer; |
||||
font-weight: bold; |
font-weight: bold; |
||||
} |
} |
||||
|
|
||||
.title { |
.title { |
||||
font: var(--fontblack); |
font: var(--fontblack); |
||||
padding-top: 10px; |
padding-top: 10px; |
||||
padding-right: 5px; |
padding-right: 5px; |
||||
padding-bottom: 10px; |
padding-bottom: 10px; |
||||
color: var(--secondary100); |
color: var(--secondary100); |
||||
} |
} |
||||
|
|
||||
.title:hover { |
.title:hover { |
||||
background-color: var(--secondary10); |
background-color: var(--secondary10); |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,76 +1,79 @@ |
|||||
<script> |
<script> |
||||
|
import Textbox from "../common/Textbox.svelte" |
||||
import Textbox from "../common/Textbox.svelte"; |
import CodeArea from "../common/CodeArea.svelte" |
||||
import CodeArea from "../common/CodeArea.svelte"; |
import Button from "../common/Button.svelte" |
||||
import Button from "../common/Button.svelte"; |
import Dropdown from "../common/Dropdown.svelte" |
||||
import Dropdown from "../common/Dropdown.svelte"; |
import { store } from "../builderStore" |
||||
import {store} from "../builderStore"; |
import { filter, some, map } from "lodash/fp" |
||||
import {filter, some, map} from "lodash/fp"; |
import { hierarchy as hierarchyFunctions, common } from "../../../core/src" |
||||
import {hierarchy as hierarchyFunctions, common} from "../../../core/src"; |
|
||||
|
const pipe = common.$ |
||||
const pipe = common.$; |
|
||||
|
let index |
||||
let index; |
let indexableRecords = [] |
||||
let indexableRecords = []; |
|
||||
|
store.subscribe($store => { |
||||
store.subscribe($store => { |
index = $store.currentNode |
||||
index = $store.currentNode; |
indexableRecords = pipe($store.hierarchy, [ |
||||
indexableRecords = pipe($store.hierarchy,[ |
hierarchyFunctions.getFlattenedHierarchy, |
||||
hierarchyFunctions.getFlattenedHierarchy, |
filter(hierarchyFunctions.isDecendant(index.parent())), |
||||
filter(hierarchyFunctions.isDecendant(index.parent())), |
filter(hierarchyFunctions.isRecord), |
||||
filter(hierarchyFunctions.isRecord), |
map(n => ({ |
||||
map(n => ({ |
node: n, |
||||
node:n, |
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds), |
||||
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds) |
})), |
||||
})) |
]) |
||||
]); |
}) |
||||
}); |
|
||||
|
const toggleAllowedRecord = record => { |
||||
const toggleAllowedRecord = record => { |
if (record.isallowed) { |
||||
if(record.isallowed) { |
index.allowedRecordNodeIds = filter(id => id !== record.node.nodeId)( |
||||
index.allowedRecordNodeIds = filter(id => id !== record.node.nodeId) |
index.allowedRecordNodeIds |
||||
(index.allowedRecordNodeIds); |
) |
||||
} else { |
} else { |
||||
index.allowedRecordNodeIds.push(record.node.nodeId); |
index.allowedRecordNodeIds.push(record.node.nodeId) |
||||
} |
} |
||||
}; |
} |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<form class="uk-form-horizontal root"> |
<form class="uk-form-horizontal root"> |
||||
<Textbox bind:text={index.name} label="Name"/> |
<Textbox bind:text={index.name} label="Name" /> |
||||
|
|
||||
<div class="allowed-records"> |
<div class="allowed-records"> |
||||
<div>Records to Index</div> |
<div>Records to Index</div> |
||||
{#each indexableRecords as rec} |
{#each indexableRecords as rec} |
||||
<input type="checkbox" checked={rec.isallowed} on:change={() => toggleAllowedRecord(rec)}/> |
<input |
||||
<span>{rec.node.name}</span> |
type="checkbox" |
||||
{/each} |
checked={rec.isallowed} |
||||
</div> |
on:change={() => toggleAllowedRecord(rec)} /> |
||||
|
<span>{rec.node.name}</span> |
||||
|
{/each} |
||||
|
</div> |
||||
|
|
||||
|
<Dropdown |
||||
|
label="Index Type" |
||||
|
bind:selected={index.indexType} |
||||
|
options={['ancestor', 'reference']} /> |
||||
|
|
||||
|
<CodeArea bind:text={index.map} label="Map (javascript)" /> |
||||
|
<CodeArea bind:text={index.filter} label="Filter (javascript expression)" /> |
||||
|
<CodeArea |
||||
|
bind:text={index.getShardName} |
||||
|
label="Shard Name (javascript expression)" /> |
||||
|
|
||||
|
|
||||
<Dropdown label="Index Type" bind:selected={index.indexType} options={["ancestor", "reference"]} /> |
|
||||
|
|
||||
<CodeArea bind:text={index.map} label="Map (javascript)"/> |
|
||||
<CodeArea bind:text={index.filter} label="Filter (javascript expression)"/> |
|
||||
<CodeArea bind:text={index.getShardName} label="Shard Name (javascript expression)"/> |
|
||||
|
|
||||
|
|
||||
</form> |
</form> |
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
height: 100%; |
height: 100%; |
||||
padding: 15px; |
padding: 15px; |
||||
} |
} |
||||
|
|
||||
.allowed-records { |
.allowed-records { |
||||
margin: 20px 0px; |
margin: 20px 0px; |
||||
} |
} |
||||
|
|
||||
.allowed-records > span { |
|
||||
margin-right:30px; |
|
||||
} |
|
||||
|
|
||||
</style> |
.allowed-records > span { |
||||
|
margin-right: 30px; |
||||
|
} |
||||
|
</style> |
||||
|
|||||
@ -1,300 +1,307 @@ |
|||||
<script> |
<script> |
||||
|
import Textbox from "../common/Textbox.svelte" |
||||
import Textbox from "../common/Textbox.svelte"; |
import Button from "../common/Button.svelte" |
||||
import Button from "../common/Button.svelte"; |
import getIcon from "../common/icon" |
||||
import getIcon from "../common/icon"; |
import FieldView from "./FieldView.svelte" |
||||
import FieldView from "./FieldView.svelte"; |
import Modal from "../common/Modal.svelte" |
||||
import Modal from "../common/Modal.svelte"; |
import { map, join, filter, some, find, keys, isDate } from "lodash/fp" |
||||
import {map, join, filter, some, |
import { store } from "../builderStore" |
||||
find, keys, isDate} from "lodash/fp"; |
import { common, hierarchy as h } from "../../../core/src" |
||||
import { store } from "../builderStore"; |
import { templateApi, pipe, validate } from "../common/core" |
||||
import {common, hierarchy as h} from "../../../core/src"; |
|
||||
import {templateApi, pipe, validate} from "../common/core"; |
let record |
||||
|
let getIndexAllowedRecords |
||||
let record; |
let editingField = false |
||||
let getIndexAllowedRecords; |
let fieldToEdit |
||||
let editingField = false; |
let isNewField = false |
||||
let fieldToEdit; |
let newField |
||||
let isNewField = false; |
let editField |
||||
let newField; |
let deleteField |
||||
let editField; |
let onFinishedFieldEdit |
||||
let deleteField; |
let editIndex |
||||
let onFinishedFieldEdit; |
|
||||
let editIndex; |
store.subscribe($store => { |
||||
|
record = $store.currentNode |
||||
store.subscribe($store => { |
const flattened = h.getFlattenedHierarchy($store.hierarchy) |
||||
record = $store.currentNode; |
getIndexAllowedRecords = index => |
||||
const flattened = h.getFlattenedHierarchy($store.hierarchy); |
pipe(index.allowedRecordNodeIds, [ |
||||
getIndexAllowedRecords = index => |
filter(id => some(n => n.nodeId === id)(flattened)), |
||||
pipe(index.allowedRecordNodeIds, [ |
map(id => find(n => n.nodeId === id)(flattened).name), |
||||
filter(id => some(n => n.nodeId === id)(flattened)), |
join(", "), |
||||
map(id => find(n => n.nodeId === id) |
]) |
||||
(flattened).name), |
|
||||
join(", ") |
|
||||
]); |
|
||||
|
|
||||
newField = () => { |
newField = () => { |
||||
isNewField = true; |
isNewField = true |
||||
fieldToEdit = templateApi($store.hierarchy).getNewField("string"); |
fieldToEdit = templateApi($store.hierarchy).getNewField("string") |
||||
editingField = true; |
editingField = true |
||||
} |
} |
||||
|
|
||||
|
onFinishedFieldEdit = field => { |
||||
onFinishedFieldEdit = (field) => { |
if (field) { |
||||
if(field) { |
store.saveField(field) |
||||
store.saveField(field); |
} |
||||
} |
editingField = false |
||||
editingField = false; |
|
||||
} |
} |
||||
|
|
||||
editField = (field) => { |
editField = field => { |
||||
isNewField = false; |
isNewField = false |
||||
fieldToEdit = field; |
fieldToEdit = field |
||||
editingField = true; |
editingField = true |
||||
} |
} |
||||
|
|
||||
deleteField = (field) => { |
deleteField = field => { |
||||
store.deleteField(field); |
store.deleteField(field) |
||||
} |
} |
||||
|
|
||||
editIndex = index => { |
editIndex = index => { |
||||
store.selectExistingNode(index.nodeId); |
store.selectExistingNode(index.nodeId) |
||||
} |
} |
||||
|
}) |
||||
}) |
|
||||
|
let getTypeOptionsValueText = value => { |
||||
let getTypeOptionsValueText = value => { |
if ( |
||||
if(value === Number.MAX_SAFE_INTEGER |
value === Number.MAX_SAFE_INTEGER || |
||||
|| value === Number.MIN_SAFE_INTEGER |
value === Number.MIN_SAFE_INTEGER || |
||||
|| new Date(value).getTime() === new Date(8640000000000000).getTime() |
new Date(value).getTime() === new Date(8640000000000000).getTime() || |
||||
|| new Date(value).getTime() === new Date(-8640000000000000).getTime()) return "(any)"; |
new Date(value).getTime() === new Date(-8640000000000000).getTime() |
||||
|
) |
||||
if(value === null) return "(not set)"; |
return "(any)" |
||||
return value; |
|
||||
} |
if (value === null) return "(not set)" |
||||
|
return value |
||||
let getTypeOptions = typeOptions => |
} |
||||
|
|
||||
|
let getTypeOptions = typeOptions => |
||||
pipe(typeOptions, [ |
pipe(typeOptions, [ |
||||
keys, |
keys, |
||||
map(k => `<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText(typeOptions[k])}`), |
map( |
||||
join("<br>") |
k => |
||||
]); |
`<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText( |
||||
|
typeOptions[k] |
||||
const nameChanged = ev => { |
)}` |
||||
const pluralName = n => `${n}s`; |
), |
||||
if(record.collectionName === "") { |
join("<br>"), |
||||
record.collectionName = pluralName(ev.target.value); |
]) |
||||
|
|
||||
|
const nameChanged = ev => { |
||||
|
const pluralName = n => `${n}s` |
||||
|
if (record.collectionName === "") { |
||||
|
record.collectionName = pluralName(ev.target.value) |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
|
|
||||
<form class="uk-form-horizontal"> |
<form class="uk-form-horizontal"> |
||||
<h3 class="settings-title"> |
<h3 class="settings-title">Settings</h3> |
||||
Settings |
|
||||
</h3> |
<Textbox label="Name:" bind:text={record.name} on:change={nameChanged} /> |
||||
|
{#if !record.isSingle} |
||||
<Textbox label="Name:" bind:text={record.name} on:change={nameChanged}/> |
<Textbox label="Collection Name:" bind:text={record.collectionName} /> |
||||
{#if !record.isSingle} |
<Textbox label="Shard Factor:" bind:text={record.allidsShardFactor} /> |
||||
<Textbox label="Collection Name:" bind:text={record.collectionName} /> |
|
||||
<Textbox label="Shard Factor:" bind:text={record.allidsShardFactor} /> |
|
||||
{/if} |
|
||||
<div class="recordkey">{record.nodeKey()}</div> |
|
||||
|
|
||||
</form> |
|
||||
<h3 class="title"> |
|
||||
Fields <span class="add-field-button" on:click={newField}>{@html getIcon("plus")}</span> |
|
||||
</h3> |
|
||||
|
|
||||
{#if record.fields.length > 0} |
|
||||
<table class="fields-table uk-table"> |
|
||||
<thead> |
|
||||
<tr> |
|
||||
<th>Name</th> |
|
||||
<th>Type</th> |
|
||||
<th>Options</th> |
|
||||
<th></th> |
|
||||
</tr> |
|
||||
</thead> |
|
||||
<tbody> |
|
||||
{#each record.fields as field} |
|
||||
<tr> |
|
||||
<td > |
|
||||
<div class="field-label">{field.label}</div> |
|
||||
<div style="font-size: 0.8em; color: var(--slate)">{field.name}</div> |
|
||||
</td> |
|
||||
<td >{field.type}</td> |
|
||||
<td >{@html getTypeOptions(field.typeOptions)}</td> |
|
||||
<td> |
|
||||
<span class="edit-button" on:click={() => editField(field)}>{@html getIcon("edit")}</span> |
|
||||
<span class="edit-button" on:click={() => deleteField(field)}>{@html getIcon("trash")}</span> |
|
||||
</td> |
|
||||
</tr> |
|
||||
{/each} |
|
||||
</tbody> |
|
||||
</table> |
|
||||
{:else} |
|
||||
(no fields added) |
|
||||
{/if} |
{/if} |
||||
|
<div class="recordkey">{record.nodeKey()}</div> |
||||
|
|
||||
|
</form> |
||||
|
<h3 class="title"> |
||||
|
Fields |
||||
|
<span class="add-field-button" on:click={newField}> |
||||
|
{@html getIcon('plus')} |
||||
|
</span> |
||||
|
</h3> |
||||
|
|
||||
{#if editingField} |
{#if record.fields.length > 0} |
||||
<Modal bind:isOpen={editingField} onClosed={() => onFinishedFieldEdit(false) }> |
<table class="fields-table uk-table"> |
||||
<FieldView field={fieldToEdit} |
<thead> |
||||
onFinished={onFinishedFieldEdit} |
<tr> |
||||
allFields={record.fields} |
<th>Name</th> |
||||
store={$store}/> |
<th>Type</th> |
||||
|
<th>Options</th> |
||||
|
<th /> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
{#each record.fields as field} |
||||
|
<tr> |
||||
|
<td> |
||||
|
<div class="field-label">{field.label}</div> |
||||
|
<div style="font-size: 0.8em; color: var(--slate)"> |
||||
|
{field.name} |
||||
|
</div> |
||||
|
</td> |
||||
|
<td>{field.type}</td> |
||||
|
<td> |
||||
|
{@html getTypeOptions(field.typeOptions)} |
||||
|
</td> |
||||
|
<td> |
||||
|
<span class="edit-button" on:click={() => editField(field)}> |
||||
|
{@html getIcon('edit')} |
||||
|
</span> |
||||
|
<span class="edit-button" on:click={() => deleteField(field)}> |
||||
|
{@html getIcon('trash')} |
||||
|
</span> |
||||
|
</td> |
||||
|
</tr> |
||||
|
{/each} |
||||
|
</tbody> |
||||
|
</table> |
||||
|
{:else}(no fields added){/if} |
||||
|
|
||||
|
{#if editingField} |
||||
|
<Modal |
||||
|
bind:isOpen={editingField} |
||||
|
onClosed={() => onFinishedFieldEdit(false)}> |
||||
|
<FieldView |
||||
|
field={fieldToEdit} |
||||
|
onFinished={onFinishedFieldEdit} |
||||
|
allFields={record.fields} |
||||
|
store={$store} /> |
||||
</Modal> |
</Modal> |
||||
{/if} |
{/if} |
||||
|
|
||||
<h3 class="title"> |
<h3 class="title">Indexes</h3> |
||||
Indexes |
|
||||
</h3> |
|
||||
|
|
||||
{#each record.indexes as index} |
{#each record.indexes as index} |
||||
<div class="index-container"> |
<div class="index-container"> |
||||
<div class="index-name"> |
<div class="index-name"> |
||||
{index.name} |
{index.name} |
||||
<span style="margin-left: 7px" on:click={() => editIndex(index)}>{@html getIcon("edit")}</span> |
<span style="margin-left: 7px" on:click={() => editIndex(index)}> |
||||
</div> |
{@html getIcon('edit')} |
||||
<div class="index-field-row"> |
</span> |
||||
<span class="index-label">records indexed: </span> |
</div> |
||||
<span>{getIndexAllowedRecords(index)}</span> |
<div class="index-field-row"> |
||||
<span class="index-label" style="margin-left: 15px">type:</span> |
<span class="index-label">records indexed:</span> |
||||
<span>{index.indexType}</span> |
<span>{getIndexAllowedRecords(index)}</span> |
||||
</div> |
<span class="index-label" style="margin-left: 15px">type:</span> |
||||
|
<span>{index.indexType}</span> |
||||
|
</div> |
||||
|
<div class="index-field-row"> |
||||
|
<span class="index-label">map:</span> |
||||
|
<code class="index-mapfilter">{index.map}</code> |
||||
|
</div> |
||||
|
{#if index.filter} |
||||
<div class="index-field-row"> |
<div class="index-field-row"> |
||||
<span class="index-label">map:</span> |
<span class="index-label">filter:</span> |
||||
<code class="index-mapfilter">{index.map}</code> |
<code class="index-mapfilter">{index.filter}</code> |
||||
</div> |
</div> |
||||
{#if index.filter} |
{/if} |
||||
<div class="index-field-row"> |
|
||||
<span class="index-label">filter:</span> |
|
||||
<code class="index-mapfilter">{index.filter}</code> |
|
||||
</div> |
|
||||
{/if} |
|
||||
</div> |
|
||||
{:else} |
|
||||
<div class="no-indexes"> |
|
||||
No indexes added. |
|
||||
</div> |
</div> |
||||
{/each} |
{:else} |
||||
|
<div class="no-indexes">No indexes added.</div> |
||||
|
{/each} |
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
height: 100%; |
height: 100%; |
||||
padding: 2rem; |
padding: 2rem; |
||||
} |
} |
||||
|
|
||||
.settings-title { |
.settings-title { |
||||
font-weight: 700; |
font-weight: 700; |
||||
} |
} |
||||
|
|
||||
.title { |
.title { |
||||
margin: 3rem 0rem 0rem 0rem; |
margin: 3rem 0rem 0rem 0rem; |
||||
font-weight: 700; |
font-weight: 700; |
||||
} |
} |
||||
|
|
||||
.recordkey { |
.recordkey { |
||||
font-size: 14px; |
font-size: 14px; |
||||
font-weight: 600; |
font-weight: 600; |
||||
color: var(--primary100); |
color: var(--primary100); |
||||
} |
} |
||||
|
|
||||
.fields-table { |
.fields-table { |
||||
margin: 1rem 1rem 0rem 0rem; |
margin: 1rem 1rem 0rem 0rem; |
||||
border-collapse:collapse; |
border-collapse: collapse; |
||||
} |
} |
||||
|
|
||||
.add-field-button { |
.add-field-button { |
||||
cursor:pointer; |
cursor: pointer; |
||||
} |
} |
||||
|
|
||||
.edit-button { |
.edit-button { |
||||
cursor:pointer; |
cursor: pointer; |
||||
color: var(--secondary25); |
color: var(--secondary25); |
||||
} |
} |
||||
|
|
||||
.edit-button:hover { |
.edit-button:hover { |
||||
cursor:pointer; |
cursor: pointer; |
||||
color: var(--secondary75); |
color: var(--secondary75); |
||||
} |
} |
||||
|
|
||||
th { |
th { |
||||
text-align: left; |
text-align: left; |
||||
} |
} |
||||
|
|
||||
td { |
td { |
||||
padding: 1rem 5rem 1rem 0rem; |
padding: 1rem 5rem 1rem 0rem; |
||||
margin:0; |
margin: 0; |
||||
font-size: 14px; |
font-size: 14px; |
||||
font-weight: 500; |
font-weight: 500; |
||||
|
} |
||||
|
|
||||
} |
.field-label { |
||||
|
font-size: 14px; |
||||
.field-label { |
font-weight: 500; |
||||
font-size: 14px; |
} |
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
thead > tr { |
thead > tr { |
||||
border-width: 0px 0px 1px 0px; |
border-width: 0px 0px 1px 0px; |
||||
border-style: solid; |
border-style: solid; |
||||
border-color: var(--secondary75); |
border-color: var(--secondary75); |
||||
margin-bottom: 20px; |
margin-bottom: 20px; |
||||
} |
} |
||||
|
|
||||
tbody > tr { |
tbody > tr { |
||||
border-width: 0px 0px 1px 0px; |
border-width: 0px 0px 1px 0px; |
||||
border-style: solid; |
border-style: solid; |
||||
border-color: var(--primary10); |
border-color: var(--primary10); |
||||
} |
} |
||||
|
|
||||
tbody > tr:hover { |
tbody > tr:hover { |
||||
background-color: var(--primary10); |
background-color: var(--primary10); |
||||
} |
} |
||||
|
|
||||
tbody > tr:hover .edit-button { |
tbody > tr:hover .edit-button { |
||||
color: var(--secondary75); |
color: var(--secondary75); |
||||
} |
} |
||||
|
|
||||
.index-container { |
.index-container { |
||||
border-style: solid; |
border-style: solid; |
||||
border-width: 0 0 1px 0; |
border-width: 0 0 1px 0; |
||||
border-color: var(--secondary25); |
border-color: var(--secondary25); |
||||
padding: 10px; |
padding: 10px; |
||||
margin-bottom: 5px; |
margin-bottom: 5px; |
||||
} |
} |
||||
|
|
||||
.index-label { |
.index-label { |
||||
color: var(--slate); |
color: var(--slate); |
||||
} |
} |
||||
|
|
||||
.index-name { |
.index-name { |
||||
font-weight: bold; |
font-weight: bold; |
||||
color: var(--primary100); |
color: var(--primary100); |
||||
} |
} |
||||
|
|
||||
.index-container code { |
.index-container code { |
||||
margin: 0; |
margin: 0; |
||||
display: inline; |
display: inline; |
||||
background-color: var(--primary10); |
background-color: var(--primary10); |
||||
color: var(--secondary100); |
color: var(--secondary100); |
||||
padding:3px; |
padding: 3px; |
||||
} |
} |
||||
|
|
||||
.index-field-row { |
.index-field-row { |
||||
margin: 1rem 0rem 0rem 0rem; |
margin: 1rem 0rem 0rem 0rem; |
||||
} |
} |
||||
|
|
||||
.no-indexes { |
.no-indexes { |
||||
margin: 1rem 0rem 0rem 0rem; |
margin: 1rem 0rem 0rem 0rem; |
||||
font-family: var(--fontnormal); |
font-family: var(--fontnormal); |
||||
font-size: 14px; |
font-size: 14px; |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||
@ -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,163 +1,153 @@ |
|||||
<script> |
<script> |
||||
|
import { store } from "../builderStore" |
||||
import { store } from "../builderStore"; |
import HierarchyRow from "./HierarchyRow.svelte" |
||||
import HierarchyRow from "./HierarchyRow.svelte"; |
import DropdownButton from "../common/DropdownButton.svelte" |
||||
import DropdownButton from "../common/DropdownButton.svelte"; |
import { hierarchy as hierarchyFunctions } from "../../../core/src" |
||||
import {hierarchy as hierarchyFunctions} from "../../../core/src"; |
import NavItem from "./NavItem.svelte" |
||||
import NavItem from "./NavItem.svelte"; |
import getIcon from "../common/icon" |
||||
import getIcon from "../common/icon"; |
|
||||
|
const newRootRecord = () => { |
||||
|
store.newRootRecord() |
||||
const newRootRecord = () => { |
} |
||||
store.newRootRecord(); |
|
||||
} |
const newRootIndex = () => { |
||||
|
store.newRootIndex() |
||||
const newRootIndex = () => { |
} |
||||
store.newRootIndex(); |
|
||||
} |
const newChildRecord = () => { |
||||
|
store.newChildRecord() |
||||
const newChildRecord = () => { |
} |
||||
store.newChildRecord(); |
|
||||
} |
const newChildIndex = () => { |
||||
|
store.newChildIndex() |
||||
const newChildIndex = () => { |
} |
||||
store.newChildIndex(); |
|
||||
} |
const defaultNewChildActions = [ |
||||
|
|
||||
const defaultNewChildActions = [ |
|
||||
{ |
{ |
||||
label:"New Root Record", |
label: "New Root Record", |
||||
onclick: newRootRecord |
onclick: newRootRecord, |
||||
}, |
}, |
||||
{ |
{ |
||||
label:"New Root Index", |
label: "New Root Index", |
||||
onclick: newRootIndex |
onclick: newRootIndex, |
||||
} |
}, |
||||
]; |
] |
||||
|
|
||||
let newChildActions = defaultNewChildActions; |
let newChildActions = defaultNewChildActions |
||||
|
|
||||
const setActiveNav = (name) => () => { |
const setActiveNav = name => () => { |
||||
store.setActiveNav(name); |
store.setActiveNav(name) |
||||
} |
} |
||||
|
|
||||
store.subscribe(db => { |
store.subscribe(db => { |
||||
if(!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
if (!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
||||
newChildActions = defaultNewChildActions; |
newChildActions = defaultNewChildActions |
||||
} else { |
} else { |
||||
newChildActions = [ |
newChildActions = [ |
||||
{ |
{ |
||||
label:"New Root Record", |
label: "New Root Record", |
||||
onclick: newRootRecord |
onclick: newRootRecord, |
||||
}, |
}, |
||||
{ |
{ |
||||
label:"New Root Index", |
label: "New Root Index", |
||||
onclick: newRootIndex |
onclick: newRootIndex, |
||||
}, |
}, |
||||
{ |
{ |
||||
label: `New Child Record of ${db.currentNode.name}`, |
label: `New Child Record of ${db.currentNode.name}`, |
||||
onclick: newChildRecord |
onclick: newChildRecord, |
||||
}, |
}, |
||||
{ |
{ |
||||
label: `New Index on ${db.currentNode.name}`, |
label: `New Index on ${db.currentNode.name}`, |
||||
onclick: newChildIndex |
onclick: newChildIndex, |
||||
} |
}, |
||||
]; |
] |
||||
} |
} |
||||
}); |
}) |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
|
|
||||
<div class="items-root"> |
<div class="items-root"> |
||||
<div class="hierarchy"> |
<div class="hierarchy"> |
||||
<div class="components-list-container"> |
<div class="components-list-container"> |
||||
<div class="nav-group-header"> |
<div class="nav-group-header"> |
||||
<div>{@html getIcon("database","18")}</div> |
<div> |
||||
<div class="hierarchy-title">Database</div> |
{@html getIcon('database', '18')} |
||||
<DropdownButton iconName="plus" actions={newChildActions} /> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
|
<div class="hierarchy-title">Database</div> |
||||
|
<DropdownButton iconName="plus" actions={newChildActions} /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
<div class="hierarchy-items-container"> |
<div class="hierarchy-items-container"> |
||||
{#each $store.hierarchy.children as record} |
{#each $store.hierarchy.children as record} |
||||
<HierarchyRow node={record} |
<HierarchyRow node={record} type="record" /> |
||||
type="record" /> |
{/each} |
||||
{/each} |
|
||||
|
|
||||
{#each $store.hierarchy.indexes as index} |
{#each $store.hierarchy.indexes as index} |
||||
<HierarchyRow node={index} |
<HierarchyRow node={index} type="index" /> |
||||
type="index" /> |
{/each} |
||||
{/each} |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
|
</div> |
||||
|
|
||||
<NavItem name="actions" label="Actions & Triggers"/> |
<NavItem name="actions" label="Actions & Triggers" /> |
||||
<NavItem name="access levels" label="User Levels"/> |
<NavItem name="access levels" label="User Levels" /> |
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.items-root { |
||||
|
|
||||
.items-root { |
|
||||
display: flex; |
display: flex; |
||||
flex-direction: column; |
flex-direction: column; |
||||
max-height: 100%; |
max-height: 100%; |
||||
height: 100%; |
height: 100%; |
||||
background-color: var(--secondary5); |
background-color: var(--secondary5); |
||||
} |
} |
||||
|
|
||||
.nav-group-header { |
.nav-group-header { |
||||
display:grid; |
display: grid; |
||||
grid-template-columns: [icon] auto [title] 1fr [button] auto; |
grid-template-columns: [icon] auto [title] 1fr [button] auto; |
||||
padding: 2rem 1rem 0rem 1rem; |
padding: 2rem 1rem 0rem 1rem; |
||||
font-size: .9rem; |
font-size: 0.9rem; |
||||
font-weight: bold; |
font-weight: bold; |
||||
} |
} |
||||
|
|
||||
.nav-group-header>div:nth-child(1) { |
.nav-group-header > div:nth-child(1) { |
||||
padding: 0rem .7rem 0rem 0rem; |
padding: 0rem 0.7rem 0rem 0rem; |
||||
vertical-align: bottom; |
vertical-align: bottom; |
||||
grid-column-start: icon; |
grid-column-start: icon; |
||||
margin-right: 5px; |
margin-right: 5px; |
||||
} |
} |
||||
|
|
||||
.nav-group-header>div:nth-child(2) { |
.nav-group-header > div:nth-child(2) { |
||||
margin-left:5px; |
margin-left: 5px; |
||||
vertical-align: bottom; |
vertical-align: bottom; |
||||
grid-column-start: title; |
grid-column-start: title; |
||||
margin-top:auto; |
margin-top: auto; |
||||
} |
} |
||||
|
|
||||
.nav-group-header>div:nth-child(3) { |
.nav-group-header > div:nth-child(3) { |
||||
vertical-align: bottom; |
vertical-align: bottom; |
||||
grid-column-start: button; |
grid-column-start: button; |
||||
cursor: pointer; |
cursor: pointer; |
||||
color: var(--primary75); |
color: var(--primary75); |
||||
} |
} |
||||
|
|
||||
.nav-group-header>div:nth-child(3):hover { |
.nav-group-header > div:nth-child(3):hover { |
||||
color: var(--primary75); |
color: var(--primary75); |
||||
} |
} |
||||
|
|
||||
.hierarchy-title { |
.hierarchy-title { |
||||
flex: auto 1 1; |
flex: auto 1 1; |
||||
} |
} |
||||
|
|
||||
|
|
||||
.hierarchy { |
.hierarchy { |
||||
display:flex; |
display: flex; |
||||
flex-direction: column; |
flex-direction: column; |
||||
flex: 1 0 auto; |
flex: 1 0 auto; |
||||
height: 100px; |
height: 100px; |
||||
} |
} |
||||
|
|
||||
.hierarchy-items-container { |
.hierarchy-items-container { |
||||
flex: 1 1 auto; |
flex: 1 1 auto; |
||||
overflow-y:auto; |
overflow-y: auto; |
||||
} |
} |
||||
|
</style> |
||||
|
|
||||
</style> |
|
||||
|
|||||
@ -1,66 +1,63 @@ |
|||||
<script> |
<script> |
||||
|
import { store } from "../builderStore" |
||||
import {store} from "../builderStore"; |
import { cloneDeep } from "lodash/fp" |
||||
import {cloneDeep} from "lodash/fp"; |
import getIcon from "../common/icon" |
||||
import getIcon from "../common/icon"; |
export let level = 0 |
||||
export let level = 0; |
export let node |
||||
export let node; |
export let type |
||||
export let type; |
|
||||
|
let navActive = "" |
||||
let navActive = ""; |
$: icon = type === "index" ? "list" : "file" |
||||
$:icon = type==="index" ? "list" : "file"; |
|
||||
|
store.subscribe(s => { |
||||
store.subscribe(s => { |
if (s.currentNode) |
||||
if(s.currentNode) |
navActive = |
||||
navActive = (s.activeNav === "database" && node.nodeId === s.currentNode.nodeId |
s.activeNav === "database" && node.nodeId === s.currentNode.nodeId |
||||
? "active" : "") |
? "active" |
||||
}); |
: "" |
||||
|
}) |
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
<div class="title {navActive}" on:click={() => store.selectExistingNode(node.nodeId)} style="padding-left: {20 + (level * 20)}px"> |
<div |
||||
{@html getIcon(icon, 12)} <span style="margin-left: 1rem">{node.name}</span> |
class="title {navActive}" |
||||
</div> |
on:click={() => store.selectExistingNode(node.nodeId)} |
||||
{#if node.children} |
style="padding-left: {20 + level * 20}px"> |
||||
{#each node.children as child} |
{@html getIcon(icon, 12)} |
||||
<svelte:self node={child} |
<span style="margin-left: 1rem">{node.name}</span> |
||||
level={level+1} |
</div> |
||||
type="record"/> |
{#if node.children} |
||||
{/each} |
{#each node.children as child} |
||||
{/if} |
<svelte:self node={child} level={level + 1} type="record" /> |
||||
{#if node.indexes} |
{/each} |
||||
{#each node.indexes as index} |
{/if} |
||||
<svelte:self node={index} |
{#if node.indexes} |
||||
level={level+1} |
{#each node.indexes as index} |
||||
type="index"/> |
<svelte:self node={index} level={level + 1} type="index" /> |
||||
{/each} |
{/each} |
||||
{/if} |
{/if} |
||||
</div> |
</div> |
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.root { |
||||
.root { |
|
||||
display: block; |
display: block; |
||||
font-size: .9rem; |
font-size: 0.9rem; |
||||
width: 100%; |
width: 100%; |
||||
cursor: pointer; |
cursor: pointer; |
||||
color: var(--secondary50); |
color: var(--secondary50); |
||||
font-weight: 500; |
font-weight: 500; |
||||
} |
} |
||||
|
|
||||
.title { |
.title { |
||||
padding-top: .5rem; |
padding-top: 0.5rem; |
||||
padding-right: .5rem; |
padding-right: 0.5rem; |
||||
} |
} |
||||
|
|
||||
.title:hover { |
.title:hover { |
||||
background-color: var(--secondary10); |
background-color: var(--secondary10); |
||||
} |
} |
||||
|
|
||||
.active { |
.active { |
||||
background-color: var(--primary10); |
background-color: var(--primary10); |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -1,43 +1,35 @@ |
|||||
<script> |
<script> |
||||
|
import { store } from "../builderStore" |
||||
|
import getIcon from "../common/icon" |
||||
|
|
||||
import {store} from "../builderStore"; |
export let name = "" |
||||
import getIcon from "../common/icon"; |
export let label = "" |
||||
|
|
||||
export let name = ""; |
let navActive = "" |
||||
export let label = ""; |
|
||||
|
|
||||
let navActive = ""; |
store.subscribe(db => { |
||||
|
navActive = db.activeNav === name ? "active" : "" |
||||
store.subscribe(db => { |
}) |
||||
navActive = (db.activeNav === name ? "active" : "") |
|
||||
}); |
|
||||
|
|
||||
const setActive = () => |
|
||||
store.setActiveNav(name); |
|
||||
|
|
||||
|
const setActive = () => store.setActiveNav(name) |
||||
</script> |
</script> |
||||
|
|
||||
<div class="nav-item {navActive}" on:click={setActive}> |
<div class="nav-item {navActive}" on:click={setActive}>{label}</div> |
||||
{label} |
|
||||
</div> |
|
||||
|
|
||||
|
|
||||
<style> |
<style> |
||||
|
.nav-item { |
||||
.nav-item { |
|
||||
padding: 1.5rem 1rem 0rem 1rem; |
padding: 1.5rem 1rem 0rem 1rem; |
||||
font-size: .9rem; |
font-size: 0.9rem; |
||||
font-weight: bold; |
font-weight: bold; |
||||
cursor: pointer; |
cursor: pointer; |
||||
flex: 0 0 auto; |
flex: 0 0 auto; |
||||
} |
} |
||||
|
|
||||
.nav-item:hover { |
.nav-item:hover { |
||||
background-color: var(--primary10); |
background-color: var(--primary10); |
||||
} |
} |
||||
|
|
||||
.active { |
.active { |
||||
background-color: var(--primary10); |
background-color: var(--primary10); |
||||
} |
} |
||||
|
</style> |
||||
</style> |
|
||||
|
|||||
@ -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,64 +1,59 @@ |
|||||
<script> |
<script> |
||||
|
import { searchAllComponents } from "./pagesParsing/searchComponents" |
||||
|
import { store } from "../builderStore" |
||||
|
|
||||
import { searchAllComponents } from "./pagesParsing/searchComponents"; |
export let onComponentChosen = () => {} |
||||
import { store } from "../builderStore"; |
|
||||
|
|
||||
export let onComponentChosen = () => {}; |
let phrase = "" |
||||
|
|
||||
let phrase = ""; |
components = $store.components |
||||
|
|
||||
components = $store.components; |
$: filteredComponents = !phrase ? [] : searchAllComponents(components, phrase) |
||||
|
|
||||
$: filteredComponents = |
|
||||
!phrase |
|
||||
? [] |
|
||||
: searchAllComponents(components, phrase); |
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="root"> |
<div class="root"> |
||||
|
|
||||
<form class="uk-search uk-search-large"> |
<form class="uk-search uk-search-large"> |
||||
<span uk-search-icon></span> |
<span uk-search-icon /> |
||||
<input class="uk-search-input" |
<input |
||||
type="search" |
class="uk-search-input" |
||||
placeholder="Based on component..." |
type="search" |
||||
bind:value={phrase}> |
placeholder="Based on component..." |
||||
</form> |
bind:value={phrase} /> |
||||
|
</form> |
||||
<div> |
|
||||
{#each filteredComponents as component} |
<div> |
||||
<div class="component" on:click={() => onComponentChosen(component)}> |
{#each filteredComponents as component} |
||||
<div class="title">{component.name}</div> |
<div class="component" on:click={() => onComponentChosen(component)}> |
||||
<div class="description">{component.description}</div> |
<div class="title">{component.name}</div> |
||||
</div> |
<div class="description">{component.description}</div> |
||||
{/each} |
</div> |
||||
</div> |
{/each} |
||||
|
</div> |
||||
|
|
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
|
.component { |
||||
.component { |
padding: 5px; |
||||
padding:5px; |
|
||||
border-style: solid; |
border-style: solid; |
||||
border-width: 0 0 1px 0; |
border-width: 0 0 1px 0; |
||||
border-color: var(--lightslate); |
border-color: var(--lightslate); |
||||
cursor: pointer; |
cursor: pointer; |
||||
} |
} |
||||
|
|
||||
.component:hover { |
.component:hover { |
||||
background-color: var(--primary10); |
background-color: var(--primary10); |
||||
} |
} |
||||
|
|
||||
.component > .title { |
.component > .title { |
||||
font-size: 13pt; |
font-size: 13pt; |
||||
color: var(--secondary100); |
color: var(--secondary100); |
||||
} |
} |
||||
|
|
||||
.component > .description { |
.component > .description { |
||||
font-size: 10pt; |
font-size: 10pt; |
||||
color: var(--primary75); |
color: var(--primary75); |
||||
font-style: italic; |
font-style: italic; |
||||
} |
} |
||||
|
|
||||
</style> |
</style> |
||||
|
|||||