@ -1 +1,103 @@ |
|||
window['##BUDIBASE_APPDEFINITION##'] = {"hierarchy":{"name":"root","type":"root","children":[{"name":"customer","type":"record","fields":[{"name":"name","type":"string","typeOptions":{"maxLength":1000,"values":null,"allowDeclaredValuesOnly":false},"label":"name","getInitialValue":"default","getUndefinedValue":"default"}],"children":[{"name":"invoiceyooo","type":"record","fields":[{"name":"amount","type":"number","typeOptions":{"minValue":99999999999,"maxValue":99999999999,"decimalPlaces":2},"label":"amount","getInitialValue":"default","getUndefinedValue":"default"}],"children":[],"validationRules":[],"nodeId":2,"indexes":[],"allidsShardFactor":1,"collectionName":"invoices","isSingle":false}],"validationRules":[],"nodeId":1,"indexes":[{"name":"customer_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":5}],"allidsShardFactor":64,"collectionName":"customers","isSingle":false}],"pathMaps":[],"indexes":[{"name":"Yeo index","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[1],"nodeId":4},{"name":"everyones_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":6}],"nodeId":0},"componentLibraries":["budibase-standard-components"],"appRootPath":"/testApp2","props":{}} |
|||
window["##BUDIBASE_APPDEFINITION##"] = { |
|||
hierarchy: { |
|||
name: "root", |
|||
type: "root", |
|||
children: [ |
|||
{ |
|||
name: "customer", |
|||
type: "record", |
|||
fields: [ |
|||
{ |
|||
name: "name", |
|||
type: "string", |
|||
typeOptions: { |
|||
maxLength: 1000, |
|||
values: null, |
|||
allowDeclaredValuesOnly: false, |
|||
}, |
|||
label: "name", |
|||
getInitialValue: "default", |
|||
getUndefinedValue: "default", |
|||
}, |
|||
], |
|||
children: [ |
|||
{ |
|||
name: "invoiceyooo", |
|||
type: "record", |
|||
fields: [ |
|||
{ |
|||
name: "amount", |
|||
type: "number", |
|||
typeOptions: { |
|||
minValue: 99999999999, |
|||
maxValue: 99999999999, |
|||
decimalPlaces: 2, |
|||
}, |
|||
label: "amount", |
|||
getInitialValue: "default", |
|||
getUndefinedValue: "default", |
|||
}, |
|||
], |
|||
children: [], |
|||
validationRules: [], |
|||
nodeId: 2, |
|||
indexes: [], |
|||
allidsShardFactor: 1, |
|||
collectionName: "invoices", |
|||
isSingle: false, |
|||
}, |
|||
], |
|||
validationRules: [], |
|||
nodeId: 1, |
|||
indexes: [ |
|||
{ |
|||
name: "customer_invoices", |
|||
type: "index", |
|||
map: "return {...record};", |
|||
filter: "", |
|||
indexType: "ancestor", |
|||
getShardName: "", |
|||
getSortKey: "record.id", |
|||
aggregateGroups: [], |
|||
allowedRecordNodeIds: [2], |
|||
nodeId: 5, |
|||
}, |
|||
], |
|||
allidsShardFactor: 64, |
|||
collectionName: "customers", |
|||
isSingle: false, |
|||
}, |
|||
], |
|||
pathMaps: [], |
|||
indexes: [ |
|||
{ |
|||
name: "Yeo index", |
|||
type: "index", |
|||
map: "return {...record};", |
|||
filter: "", |
|||
indexType: "ancestor", |
|||
getShardName: "", |
|||
getSortKey: "record.id", |
|||
aggregateGroups: [], |
|||
allowedRecordNodeIds: [1], |
|||
nodeId: 4, |
|||
}, |
|||
{ |
|||
name: "everyones_invoices", |
|||
type: "index", |
|||
map: "return {...record};", |
|||
filter: "", |
|||
indexType: "ancestor", |
|||
getShardName: "", |
|||
getSortKey: "record.id", |
|||
aggregateGroups: [], |
|||
allowedRecordNodeIds: [2], |
|||
nodeId: 6, |
|||
}, |
|||
], |
|||
nodeId: 0, |
|||
}, |
|||
componentLibraries: ["budibase-standard-components"], |
|||
appRootPath: "/testApp2", |
|||
props: {}, |
|||
} |
|||
|
|||
@ -1,20 +1,20 @@ |
|||
import svelte from 'rollup-plugin-svelte'; |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import svelte from "rollup-plugin-svelte" |
|||
import resolve from "rollup-plugin-node-resolve" |
|||
|
|||
export default { |
|||
input: 'src/index.js', |
|||
output: [ |
|||
{ |
|||
file: "dist/index.js", |
|||
format: 'esm', |
|||
name:"budibaseStandardComponents", |
|||
sourcemap: "inline" |
|||
} |
|||
], |
|||
plugins: [ |
|||
svelte({ |
|||
hydratable:true |
|||
}), |
|||
resolve() |
|||
] |
|||
}; |
|||
input: "src/index.js", |
|||
output: [ |
|||
{ |
|||
file: "dist/index.js", |
|||
format: "esm", |
|||
name: "budibaseStandardComponents", |
|||
sourcemap: "inline", |
|||
}, |
|||
], |
|||
plugins: [ |
|||
svelte({ |
|||
hydratable: true, |
|||
}), |
|||
resolve(), |
|||
], |
|||
} |
|||
|
|||
@ -1,16 +1,14 @@ |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import resolve from "rollup-plugin-node-resolve" |
|||
|
|||
export default { |
|||
input: 'src/generators.js', |
|||
output: [ |
|||
{ |
|||
file: "dist/generators.js", |
|||
format: 'esm', |
|||
name:"budibaseStandardComponents", |
|||
sourcemap: "inline" |
|||
} |
|||
], |
|||
plugins: [ |
|||
resolve() |
|||
] |
|||
}; |
|||
input: "src/generators.js", |
|||
output: [ |
|||
{ |
|||
file: "dist/generators.js", |
|||
format: "esm", |
|||
name: "budibaseStandardComponents", |
|||
sourcemap: "inline", |
|||
}, |
|||
], |
|||
plugins: [resolve()], |
|||
} |
|||
|
|||
@ -1,89 +1,131 @@ |
|||
import svelte from 'rollup-plugin-svelte'; |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import commonjs from 'rollup-plugin-commonjs'; |
|||
import livereload from 'rollup-plugin-livereload'; |
|||
import { terser } from 'rollup-plugin-terser'; |
|||
import json from 'rollup-plugin-json'; |
|||
import svelte from "rollup-plugin-svelte" |
|||
import resolve from "rollup-plugin-node-resolve" |
|||
import commonjs from "rollup-plugin-commonjs" |
|||
import livereload from "rollup-plugin-livereload" |
|||
import { terser } from "rollup-plugin-terser" |
|||
import json from "rollup-plugin-json" |
|||
|
|||
const production = !process.env.ROLLUP_WATCH; |
|||
const production = !process.env.ROLLUP_WATCH |
|||
|
|||
const lodash_fp_exports = [ |
|||
"find", "isUndefined", "split", |
|||
"last", "union", "reduce", "isObject", |
|||
"cloneDeep", "some", "isArray", "map", |
|||
"filter", "keys", "isFunction", "isEmpty", |
|||
"countBy", "join", "includes", "flatten", |
|||
"constant", "first", "intersection", "take", |
|||
"has", "mapValues", "isString", "isBoolean", |
|||
"isNull", "isNumber", "isObjectLike", "isDate", |
|||
"clone", "values", "keyBy", "isNaN", |
|||
"isInteger", "toNumber"]; |
|||
"find", |
|||
"isUndefined", |
|||
"split", |
|||
"last", |
|||
"union", |
|||
"reduce", |
|||
"isObject", |
|||
"cloneDeep", |
|||
"some", |
|||
"isArray", |
|||
"map", |
|||
"filter", |
|||
"keys", |
|||
"isFunction", |
|||
"isEmpty", |
|||
"countBy", |
|||
"join", |
|||
"includes", |
|||
"flatten", |
|||
"constant", |
|||
"first", |
|||
"intersection", |
|||
"take", |
|||
"has", |
|||
"mapValues", |
|||
"isString", |
|||
"isBoolean", |
|||
"isNull", |
|||
"isNumber", |
|||
"isObjectLike", |
|||
"isDate", |
|||
"clone", |
|||
"values", |
|||
"keyBy", |
|||
"isNaN", |
|||
"isInteger", |
|||
"toNumber", |
|||
] |
|||
|
|||
const lodash_exports = [ |
|||
"flow", "head", |
|||
"tail", "findIndex", "startsWith", |
|||
"dropRight", "takeRight", |
|||
"trim", "split", "replace", |
|||
"merge", "assign"]; |
|||
"flow", |
|||
"head", |
|||
"tail", |
|||
"findIndex", |
|||
"startsWith", |
|||
"dropRight", |
|||
"takeRight", |
|||
"trim", |
|||
"split", |
|||
"replace", |
|||
"merge", |
|||
"assign", |
|||
] |
|||
|
|||
const coreExternal = [ |
|||
"lodash", "lodash/fp", "date-fns", |
|||
"lunr", "safe-buffer", "shortid", |
|||
"@nx-js/compiler-util" |
|||
]; |
|||
"lodash", |
|||
"lodash/fp", |
|||
"date-fns", |
|||
"lunr", |
|||
"safe-buffer", |
|||
"shortid", |
|||
"@nx-js/compiler-util", |
|||
] |
|||
|
|||
export default { |
|||
input: 'src/Test/testMain.js', |
|||
output: { |
|||
sourcemap: true, |
|||
format: 'iife', |
|||
name: 'app', |
|||
file: 'public/bundle.js' |
|||
}, |
|||
plugins: [ |
|||
svelte({ |
|||
// enable run-time checks when not in production
|
|||
dev: !production, |
|||
// we'll extract any component CSS out into
|
|||
// a separate file — better for performance
|
|||
css: css => { |
|||
css.write('public/bundle.css'); |
|||
}, |
|||
|
|||
hydratable:true |
|||
}), |
|||
input: "src/Test/testMain.js", |
|||
output: { |
|||
sourcemap: true, |
|||
format: "iife", |
|||
name: "app", |
|||
file: "public/bundle.js", |
|||
}, |
|||
plugins: [ |
|||
svelte({ |
|||
// enable run-time checks when not in production
|
|||
dev: !production, |
|||
// we'll extract any component CSS out into
|
|||
// a separate file — better for performance
|
|||
css: css => { |
|||
css.write("public/bundle.css") |
|||
}, |
|||
|
|||
// If you have external dependencies installed from
|
|||
// npm, you'll most likely need these plugins. In
|
|||
// some cases you'll need additional configuration —
|
|||
// consult the documentation for details:
|
|||
// https://github.com/rollup/rollup-plugin-commonjs
|
|||
resolve({ |
|||
browser: true, |
|||
dedupe: importee => { |
|||
return importee === 'svelte' |
|||
|| importee.startsWith('svelte/') |
|||
|| coreExternal.includes(importee); |
|||
} |
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
"lodash":lodash_exports, |
|||
"shortid": ["generate"] |
|||
} |
|||
}), |
|||
json(), |
|||
hydratable: true, |
|||
}), |
|||
|
|||
// Watch the `public` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload('public'), |
|||
// If you have external dependencies installed from
|
|||
// npm, you'll most likely need these plugins. In
|
|||
// some cases you'll need additional configuration —
|
|||
// consult the documentation for details:
|
|||
// https://github.com/rollup/rollup-plugin-commonjs
|
|||
resolve({ |
|||
browser: true, |
|||
dedupe: importee => { |
|||
return ( |
|||
importee === "svelte" || |
|||
importee.startsWith("svelte/") || |
|||
coreExternal.includes(importee) |
|||
) |
|||
}, |
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
lodash: lodash_exports, |
|||
shortid: ["generate"], |
|||
}, |
|||
}), |
|||
json(), |
|||
|
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser() |
|||
], |
|||
watch: { |
|||
clearScreen: false |
|||
} |
|||
}; |
|||
// Watch the `public` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload("public"), |
|||
|
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser(), |
|||
], |
|||
watch: { |
|||
clearScreen: false, |
|||
}, |
|||
} |
|||
|
|||
@ -1,61 +1,92 @@ |
|||
const { readdir, stat, copyFile } = require("fs-extra"); |
|||
const { constants } = require("fs"); |
|||
const { join, basename } = require("path"); |
|||
|
|||
const packagesFolder = ".."; |
|||
|
|||
const jsFile = dir => join(dir, "index.js"); |
|||
const generatorsFile = dir => join(dir, "generators.js"); |
|||
const jsMapFile = dir => join(dir, "index.js.map"); |
|||
const sourceJs = jsFile("dist"); |
|||
const sourceJsMap = jsMapFile("dist"); |
|||
const componentsFile = "components.json"; |
|||
const sourceGenerators = generatorsFile("dist"); |
|||
|
|||
const appPackages = join(packagesFolder, "server", "appPackages"); |
|||
|
|||
const publicMain = appName => join(appPackages, appName, "public", "main", "lib", "node_modules", "@budibase", "bootstrap-components"); |
|||
const publicUnauth = appName => join(appPackages, appName, "public", "unauthenticated", "lib", "node_modules", "@budibase", "bootstrap-components"); |
|||
const nodeModulesDist = appName => join(appPackages, appName, "node_modules", "@budibase", "bootstrap-components", "dist"); |
|||
const nodeModules = appName => join(appPackages, appName, "node_modules", "@budibase", "bootstrap-components"); |
|||
|
|||
(async () => { |
|||
|
|||
const apps = await readdir(appPackages); |
|||
|
|||
const copySource = file => async toDir => { |
|||
const dest = join(toDir, basename(file)); |
|||
try { |
|||
await copyFile(file, dest, constants.COPYFILE_FICLONE); |
|||
console.log(`COPIED ${file} to ${dest}`); |
|||
} catch(e) { |
|||
console.log(`COPY FAILED ${file} to ${dest}: ${e}`); |
|||
} |
|||
} |
|||
|
|||
const copySourceJs = copySource(sourceJs); |
|||
const copySourceJsMap = copySource(sourceJsMap); |
|||
const copyGenerators = copySource(sourceGenerators); |
|||
const copyComponentsJson = copySource(componentsFile); |
|||
|
|||
const { readdir, stat, copyFile } = require("fs-extra") |
|||
const { constants } = require("fs") |
|||
const { join, basename } = require("path") |
|||
|
|||
for(let app of apps) { |
|||
if(!(await stat(join(appPackages, app))).isDirectory()) continue; |
|||
const packagesFolder = ".." |
|||
|
|||
await copySourceJs(nodeModulesDist(app)); |
|||
await copySourceJsMap(nodeModulesDist(app)); |
|||
await copyGenerators(nodeModulesDist(app)); |
|||
const jsFile = dir => join(dir, "index.js") |
|||
const generatorsFile = dir => join(dir, "generators.js") |
|||
const jsMapFile = dir => join(dir, "index.js.map") |
|||
const sourceJs = jsFile("dist") |
|||
const sourceJsMap = jsMapFile("dist") |
|||
const componentsFile = "components.json" |
|||
const sourceGenerators = generatorsFile("dist") |
|||
|
|||
await copyComponentsJson(nodeModules(app)); |
|||
const appPackages = join(packagesFolder, "server", "appPackages") |
|||
|
|||
await copySourceJs(join(publicMain(app), "dist")); |
|||
await copySourceJsMap(join(publicMain(app), "dist")); |
|||
await copyGenerators(join(publicMain(app), "dist")); |
|||
const publicMain = appName => |
|||
join( |
|||
appPackages, |
|||
appName, |
|||
"public", |
|||
"main", |
|||
"lib", |
|||
"node_modules", |
|||
"@budibase", |
|||
"bootstrap-components" |
|||
) |
|||
const publicUnauth = appName => |
|||
join( |
|||
appPackages, |
|||
appName, |
|||
"public", |
|||
"unauthenticated", |
|||
"lib", |
|||
"node_modules", |
|||
"@budibase", |
|||
"bootstrap-components" |
|||
) |
|||
const nodeModulesDist = appName => |
|||
join( |
|||
appPackages, |
|||
appName, |
|||
"node_modules", |
|||
"@budibase", |
|||
"bootstrap-components", |
|||
"dist" |
|||
) |
|||
const nodeModules = appName => |
|||
join( |
|||
appPackages, |
|||
appName, |
|||
"node_modules", |
|||
"@budibase", |
|||
"bootstrap-components" |
|||
) |
|||
|
|||
;(async () => { |
|||
const apps = await readdir(appPackages) |
|||
|
|||
await copySourceJs(join(publicUnauth(app), "dist")); |
|||
await copySourceJsMap(join(publicUnauth(app), "dist")); |
|||
await copyGenerators(join(publicUnauth(app), "dist")); |
|||
const copySource = file => async toDir => { |
|||
const dest = join(toDir, basename(file)) |
|||
try { |
|||
await copyFile(file, dest, constants.COPYFILE_FICLONE) |
|||
console.log(`COPIED ${file} to ${dest}`) |
|||
} catch (e) { |
|||
console.log(`COPY FAILED ${file} to ${dest}: ${e}`) |
|||
} |
|||
} |
|||
|
|||
const copySourceJs = copySource(sourceJs) |
|||
const copySourceJsMap = copySource(sourceJsMap) |
|||
const copyGenerators = copySource(sourceGenerators) |
|||
const copyComponentsJson = copySource(componentsFile) |
|||
|
|||
for (let app of apps) { |
|||
if (!(await stat(join(appPackages, app))).isDirectory()) continue |
|||
|
|||
await copySourceJs(nodeModulesDist(app)) |
|||
await copySourceJsMap(nodeModulesDist(app)) |
|||
await copyGenerators(nodeModulesDist(app)) |
|||
|
|||
await copyComponentsJson(nodeModules(app)) |
|||
|
|||
await copySourceJs(join(publicMain(app), "dist")) |
|||
await copySourceJsMap(join(publicMain(app), "dist")) |
|||
await copyGenerators(join(publicMain(app), "dist")) |
|||
|
|||
})(); |
|||
await copySourceJs(join(publicUnauth(app), "dist")) |
|||
await copySourceJsMap(join(publicUnauth(app), "dist")) |
|||
await copyGenerators(join(publicUnauth(app), "dist")) |
|||
} |
|||
})() |
|||
|
|||
@ -1,46 +1,43 @@ |
|||
import { writable } from "svelte/store"; |
|||
import Login from "../Login.svelte"; |
|||
import Grid from "../Grid.svelte"; |
|||
import Form from "../Form.svelte"; |
|||
import Textbox from "../Textbox.svelte"; |
|||
import Text from "../Text.svelte"; |
|||
import Nav from "../Nav.svelte"; |
|||
import Panel from "../Panel.svelte"; |
|||
import StackPanel from "../StackPanel.svelte"; |
|||
import Table from "../Table.svelte"; |
|||
import Button from "../Button.svelte"; |
|||
import { createApp } from "@budibase/client/src/createApp"; |
|||
import Login from "../Login.svelte" |
|||
import Grid from "../Grid.svelte" |
|||
import Form from "../Form.svelte" |
|||
import Textbox from "../Textbox.svelte" |
|||
import Text from "../Text.svelte" |
|||
import Nav from "../Nav.svelte" |
|||
import Panel from "../Panel.svelte" |
|||
import StackPanel from "../StackPanel.svelte" |
|||
import Table from "../Table.svelte" |
|||
import Button from "../Button.svelte" |
|||
import { createApp } from "@budibase/client/src/createApp" |
|||
|
|||
export default async () => { |
|||
export default async () => { |
|||
const componentLibraries = { |
|||
components: { |
|||
login: Login, |
|||
grid: Grid, |
|||
form: Form, |
|||
textbox: Textbox, |
|||
text: Text, |
|||
nav: Nav, |
|||
panel: Panel, |
|||
table: Table, |
|||
stackpanel: StackPanel, |
|||
button: Button, |
|||
}, |
|||
} |
|||
|
|||
const componentLibraries = { |
|||
components : { |
|||
login : Login, |
|||
grid : Grid, |
|||
form : Form, |
|||
textbox : Textbox, |
|||
text: Text, |
|||
nav: Nav, |
|||
panel: Panel, |
|||
table: Table, |
|||
stackpanel: StackPanel, |
|||
button: Button |
|||
} |
|||
} |
|||
const appDef = { hierarchy: {}, actions: {} } |
|||
const user = { name: "yeo", permissions: [] } |
|||
|
|||
const appDef = {hierarchy:{}, actions:{}}; |
|||
const user = {name:"yeo", permissions:[]}; |
|||
|
|||
var app = createApp(componentLibraries, appDef, user); |
|||
app.store.update(s => { |
|||
s.people = [ |
|||
{name:"bob", address: "123 Main Street", status: "Open"}, |
|||
{name:"poppy", address: "456 Side Road", status: "Closed"}, |
|||
{name:"Oscar", address: "678 Dodgy Alley", status: "Open"}, |
|||
]; |
|||
return s; |
|||
}) |
|||
var app = createApp(componentLibraries, appDef, user) |
|||
app.store.update(s => { |
|||
s.people = [ |
|||
{ name: "bob", address: "123 Main Street", status: "Open" }, |
|||
{ name: "poppy", address: "456 Side Road", status: "Closed" }, |
|||
{ name: "Oscar", address: "678 Dodgy Alley", status: "Open" }, |
|||
] |
|||
return s |
|||
}) |
|||
|
|||
return app; |
|||
|
|||
} |
|||
return app |
|||
} |
|||
|
|||
@ -1,253 +1,247 @@ |
|||
|
|||
export const props = { |
|||
login: { _component: "components/login" }, |
|||
|
|||
login: { _component:"components/login" }, |
|||
|
|||
form: { |
|||
_component: "components/form", |
|||
formControls: [ |
|||
{ |
|||
control: { |
|||
_component: "components/textbox" |
|||
}, |
|||
label:"First Name" |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/textbox" |
|||
}, |
|||
label:"Last Name" |
|||
} |
|||
] |
|||
}, |
|||
form: { |
|||
_component: "components/form", |
|||
formControls: [ |
|||
{ |
|||
control: { |
|||
_component: "components/textbox", |
|||
}, |
|||
label: "First Name", |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/textbox", |
|||
}, |
|||
label: "Last Name", |
|||
}, |
|||
], |
|||
}, |
|||
|
|||
nav: { |
|||
_component: "components/nav", |
|||
navBarBackground: "red", |
|||
navBarBorder: "1px solid maroon", |
|||
navBarColor: "black", |
|||
selectedItemBackground: "maroon", |
|||
selectedItemColor: "white", |
|||
selectedItemBorder: "green", |
|||
itemHoverBackground: "yellow", |
|||
itemHoverColor: "pink", |
|||
items: [ |
|||
{ |
|||
title: "People", |
|||
component: { |
|||
_component: "components/panel", |
|||
text:"People Panel", |
|||
padding: "40px", |
|||
border: "2px solid pink", |
|||
background: "mistyrose" |
|||
nav: { |
|||
_component: "components/nav", |
|||
navBarBackground: "red", |
|||
navBarBorder: "1px solid maroon", |
|||
navBarColor: "black", |
|||
selectedItemBackground: "maroon", |
|||
selectedItemColor: "white", |
|||
selectedItemBorder: "green", |
|||
itemHoverBackground: "yellow", |
|||
itemHoverColor: "pink", |
|||
items: [ |
|||
{ |
|||
title: "People", |
|||
component: { |
|||
_component: "components/panel", |
|||
text: "People Panel", |
|||
padding: "40px", |
|||
border: "2px solid pink", |
|||
background: "mistyrose", |
|||
}, |
|||
}, |
|||
{ |
|||
title: "Animals", |
|||
component: { |
|||
_component: "components/panel", |
|||
text: "Animals Panel", |
|||
padding: "40px", |
|||
border: "2px solid green", |
|||
background: "azure", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
|
|||
} |
|||
}, |
|||
{ |
|||
title: "Animals", |
|||
component: { |
|||
_component: "components/panel", |
|||
text:"Animals Panel", |
|||
padding: "40px", |
|||
border: "2px solid green", |
|||
background: "azure" |
|||
} |
|||
} |
|||
] |
|||
table: { |
|||
_component: "components/table", |
|||
columns: [ |
|||
{ |
|||
title: { |
|||
"##bbstate": "NameColumnName", |
|||
"##bbsource": "store", |
|||
"##bbstatefallback": "Name", |
|||
}, |
|||
value: { |
|||
"##bbstate": "name", |
|||
"##bbsource": "context", |
|||
}, |
|||
}, |
|||
{ |
|||
title: "Address", |
|||
value: { |
|||
"##bbstate": "address", |
|||
"##bbsource": "context", |
|||
}, |
|||
}, |
|||
{ |
|||
title: "Status", |
|||
value: { |
|||
"##bbstate": "status", |
|||
"##bbsource": "context", |
|||
}, |
|||
}, |
|||
], |
|||
data: { |
|||
"##bbstate": "people", |
|||
}, |
|||
onRowClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "NameColumnName", |
|||
value: { |
|||
"##bbstate": "name", |
|||
"##bbsource": "context", |
|||
"##bbstatefallback": "balls to that", |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
tableClass: "table-default", |
|||
theadClass: "thead-default", |
|||
tbodyClass: "tbody-default", |
|||
trClass: "tr-default", |
|||
thClass: "th-default", |
|||
}, |
|||
|
|||
table: { |
|||
_component:"components/table", |
|||
columns: [ |
|||
{ |
|||
title: { |
|||
"##bbstate":"NameColumnName", |
|||
"##bbsource":"store", |
|||
"##bbstatefallback": "Name" |
|||
}, |
|||
value: { |
|||
"##bbstate":"name", |
|||
"##bbsource":"context" |
|||
} |
|||
}, |
|||
{ |
|||
title: "Address", |
|||
value: { |
|||
"##bbstate":"address", |
|||
"##bbsource":"context" |
|||
} |
|||
}, |
|||
{ |
|||
title: "Status", |
|||
value: { |
|||
"##bbstate":"status", |
|||
"##bbsource":"context" |
|||
} |
|||
} |
|||
], |
|||
data: { |
|||
"##bbstate":"people" |
|||
}, |
|||
onRowClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "NameColumnName", |
|||
value: { |
|||
"##bbstate":"name", |
|||
"##bbsource":"context", |
|||
"##bbstatefallback": "balls to that" |
|||
} |
|||
} |
|||
} |
|||
], |
|||
tableClass: "table-default", |
|||
theadClass: "thead-default", |
|||
tbodyClass: "tbody-default", |
|||
trClass: "tr-default", |
|||
thClass: "th-default" |
|||
|
|||
grid: { |
|||
_component: "components/grid", |
|||
gridTemplateColumns: "[left] auto [center] auto [right] auto", |
|||
gridTemplateRows: "[top] auto [middle] auto [bottom] auto", |
|||
children: [ |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "1", |
|||
background: "blue", |
|||
textAlign: "center", |
|||
color: "white", |
|||
}, |
|||
gridColumn: "left", |
|||
gridRow: "top", |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "2", |
|||
background: "red", |
|||
textAlign: "center", |
|||
color: "white", |
|||
padding: "10px", |
|||
}, |
|||
gridColumn: "center", |
|||
gridRow: "middle", |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "3", |
|||
background: "yellow", |
|||
textAlign: "center", |
|||
color: "black", |
|||
}, |
|||
gridColumn: "right", |
|||
gridRow: "bottom", |
|||
}, |
|||
], |
|||
}, |
|||
boundStackPanel: { |
|||
_component: "components/stackpanel", |
|||
direction: "horizontal", |
|||
children: [ |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "STATIC", |
|||
}, |
|||
}, |
|||
], |
|||
data: { |
|||
"##bbstate": "people", |
|||
}, |
|||
|
|||
grid: { |
|||
_component: "components/grid", |
|||
gridTemplateColumns: "[left] auto [center] auto [right] auto", |
|||
gridTemplateRows: "[top] auto [middle] auto [bottom] auto", |
|||
children : [ |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "1", |
|||
background: "blue", |
|||
textAlign:"center", |
|||
color: "white" |
|||
}, |
|||
gridColumn: "left", |
|||
gridRow: "top" |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "2", |
|||
background: "red", |
|||
textAlign:"center", |
|||
color: "white", |
|||
padding: "10px" |
|||
}, |
|||
gridColumn: "center", |
|||
gridRow: "middle" |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "3", |
|||
background: "yellow", |
|||
textAlign:"center", |
|||
color: "black" |
|||
}, |
|||
gridColumn: "right", |
|||
gridRow: "bottom" |
|||
} |
|||
] |
|||
dataItemComponent: { |
|||
_component: "components/panel", |
|||
text: { |
|||
"##bbstate": "name", |
|||
"##bbsource": "context", |
|||
"##bbstatefallback": "balls to that", |
|||
}, |
|||
padding: "10px", |
|||
border: "5px solid black", |
|||
margin: "10px", |
|||
hoverColor: "white", |
|||
hoverBackground: "black", |
|||
height: "200px", |
|||
weight: "200px", |
|||
}, |
|||
boundStackPanel: { |
|||
_component: "components/stackpanel", |
|||
direction: "horizontal", |
|||
children: [ |
|||
}, |
|||
hiddenNav: { |
|||
_component: "components/stackpanel", |
|||
children: [ |
|||
{ |
|||
control: { |
|||
_component: "components/button", |
|||
contentText: "Peep", |
|||
onClick: [ |
|||
{ |
|||
control: { |
|||
_component: "components/text", |
|||
value: "STATIC" |
|||
} |
|||
} |
|||
], |
|||
data: { |
|||
"##bbstate":"people" |
|||
}, |
|||
dataItemComponent: { |
|||
_component: "components/panel", |
|||
text: { |
|||
"##bbstate":"name", |
|||
"##bbsource":"context", |
|||
"##bbstatefallback": "balls to that" |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "selected", |
|||
value: "People", |
|||
}, |
|||
}, |
|||
padding: "10px", |
|||
border: "5px solid black", |
|||
margin: "10px", |
|||
hoverColor: "white", |
|||
hoverBackground: "black", |
|||
height:"200px", |
|||
weight:"200px" |
|||
} |
|||
}, |
|||
hiddenNav: { |
|||
_component: "components/stackpanel", |
|||
children: [ |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/button", |
|||
contentText: "Ani", |
|||
onClick: [ |
|||
{ |
|||
control:{ |
|||
_component: "components/button", |
|||
contentText: "Peep", |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "selected", |
|||
value: "People" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "selected", |
|||
value: "Animals", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/nav", |
|||
hideNavBar: true, |
|||
selectedItem: { |
|||
"##bbstate": "selected", |
|||
"##bbsource": "store", |
|||
"##bbstatefallback": "Animals", |
|||
}, |
|||
items: [ |
|||
{ |
|||
control:{ |
|||
_component: "components/button", |
|||
contentText: "Ani", |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: "selected", |
|||
value: "Animals" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
title: "People", |
|||
component: { |
|||
_component: "components/panel", |
|||
text: "People Panel", |
|||
padding: "40px", |
|||
border: "2px solid pink", |
|||
background: "mistyrose", |
|||
}, |
|||
}, |
|||
{ |
|||
control: { |
|||
_component: "components/nav", |
|||
hideNavBar: true, |
|||
selectedItem: { |
|||
"##bbstate":"selected", |
|||
"##bbsource":"store", |
|||
"##bbstatefallback": "Animals" |
|||
}, |
|||
items: [ |
|||
{ |
|||
title: "People", |
|||
component: { |
|||
_component: "components/panel", |
|||
text:"People Panel", |
|||
padding: "40px", |
|||
border: "2px solid pink", |
|||
background: "mistyrose" |
|||
|
|||
} |
|||
}, |
|||
{ |
|||
title: "Animals", |
|||
component: { |
|||
_component: "components/panel", |
|||
text:"Animals Panel", |
|||
padding: "40px", |
|||
border: "2px solid green", |
|||
background: "azure" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
|||
|
|||
} |
|||
title: "Animals", |
|||
component: { |
|||
_component: "components/panel", |
|||
text: "Animals Panel", |
|||
padding: "40px", |
|||
border: "2px solid green", |
|||
background: "azure", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
} |
|||
|
|||
@ -1,7 +1,7 @@ |
|||
import App from './TestApp.svelte'; |
|||
import App from "./TestApp.svelte" |
|||
|
|||
const app = new App({ |
|||
target: document.body, |
|||
}); |
|||
target: document.body, |
|||
}) |
|||
|
|||
export default app; |
|||
export default app |
|||
|
|||
@ -1,9 +1,9 @@ |
|||
export const buildStyle = (styles) => { |
|||
let str = ""; |
|||
for(let s in styles) { |
|||
if(styles[s]) { |
|||
str += `${s}: ${styles[s]}; ` |
|||
} |
|||
export const buildStyle = styles => { |
|||
let str = "" |
|||
for (let s in styles) { |
|||
if (styles[s]) { |
|||
str += `${s}: ${styles[s]}; ` |
|||
} |
|||
return str; |
|||
} |
|||
} |
|||
return str |
|||
} |
|||
|
|||
@ -1,21 +1,19 @@ |
|||
// https://github.com/kaisermann/svelte-css-vars
|
|||
|
|||
export default (node, props) => { |
|||
Object.entries(props).forEach(([key, value]) => { |
|||
node.style.setProperty(`--${key}`, value); |
|||
}); |
|||
|
|||
return { |
|||
update(new_props) { |
|||
Object.entries(new_props).forEach(([key, value]) => { |
|||
node.style.setProperty(`--${key}`, value); |
|||
delete props[key]; |
|||
}); |
|||
|
|||
Object.keys(props).forEach(name => |
|||
node.style.removeProperty(`--${name}`), |
|||
); |
|||
props = new_props; |
|||
}, |
|||
}; |
|||
}; |
|||
Object.entries(props).forEach(([key, value]) => { |
|||
node.style.setProperty(`--${key}`, value) |
|||
}) |
|||
|
|||
return { |
|||
update(new_props) { |
|||
Object.entries(new_props).forEach(([key, value]) => { |
|||
node.style.setProperty(`--${key}`, value) |
|||
delete props[key] |
|||
}) |
|||
|
|||
Object.keys(props).forEach(name => node.style.removeProperty(`--${name}`)) |
|||
props = new_props |
|||
}, |
|||
} |
|||
} |
|||
|
|||
@ -1 +1 @@ |
|||
export const emptyProps = () => ({_component:""}); |
|||
export const emptyProps = () => ({ _component: "" }) |
|||
|
|||
@ -1,4 +1,4 @@ |
|||
export { forms } from "./generators/formsGenerator"; |
|||
export { indexTables } from "./generators/indexTablesGenerator"; |
|||
export { app } from "./generators/appGenerator"; |
|||
export { recordHomePageComponents as recordHomepages } from "./generators/recordHomePageGenerator"; |
|||
export { forms } from "./generators/formsGenerator" |
|||
export { indexTables } from "./generators/indexTablesGenerator" |
|||
export { app } from "./generators/appGenerator" |
|||
export { recordHomePageComponents as recordHomepages } from "./generators/recordHomePageGenerator" |
|||
|
|||
@ -1,18 +1,18 @@ |
|||
export const buttons = () => [ |
|||
{ |
|||
name: "common/Primary Button", |
|||
description: "Bootstrap primary button ", |
|||
inherits: "@budibase/standard-components/button", |
|||
props: { |
|||
className: "btn btn-primary" |
|||
} |
|||
{ |
|||
name: "common/Primary Button", |
|||
description: "Bootstrap primary button ", |
|||
inherits: "@budibase/standard-components/button", |
|||
props: { |
|||
className: "btn btn-primary", |
|||
}, |
|||
{ |
|||
name: "common/Default Button", |
|||
description: "Bootstrap default button", |
|||
inherits: "@budibase/standard-components/button", |
|||
props: { |
|||
className: "btn btn-secondary" |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
name: "common/Default Button", |
|||
description: "Bootstrap default button", |
|||
inherits: "@budibase/standard-components/button", |
|||
props: { |
|||
className: "btn btn-secondary", |
|||
}, |
|||
}, |
|||
] |
|||
|
|||
@ -1,123 +1,128 @@ |
|||
import {buttons} from "./buttonGenerators"; |
|||
import { buttons } from "./buttonGenerators" |
|||
|
|||
export const forms = ({records, indexes, helpers}) => |
|||
[ |
|||
...records.map(root), |
|||
...buttons({records, indexes, helpers}) |
|||
]; |
|||
export const forms = ({ records, indexes, helpers }) => [ |
|||
...records.map(root), |
|||
...buttons({ records, indexes, helpers }), |
|||
] |
|||
|
|||
export const formName = record => `${record.name}/${record.name} Form`; |
|||
export const formName = record => `${record.name}/${record.name} Form` |
|||
|
|||
const root = record => ({ |
|||
name: formName(record), |
|||
description: `Control for creating/updating '${record.nodeKey()}' `, |
|||
inherits: "@budibase/standard-components/div", |
|||
props: { |
|||
className:"p-1", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/h3", |
|||
text: `Edit ${record.name}`, |
|||
} |
|||
}, |
|||
form(record), |
|||
saveCancelButtons(record) |
|||
] |
|||
} |
|||
}) |
|||
name: formName(record), |
|||
description: `Control for creating/updating '${record.nodeKey()}' `, |
|||
inherits: "@budibase/standard-components/div", |
|||
props: { |
|||
className: "p-1", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/h3", |
|||
text: `Edit ${record.name}`, |
|||
}, |
|||
}, |
|||
form(record), |
|||
saveCancelButtons(record), |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
const form = record => ({ |
|||
component: { |
|||
_component: "@budibase/standard-components/form", |
|||
formControls: |
|||
record.fields.map(f => formControl(record, f)) |
|||
} |
|||
component: { |
|||
_component: "@budibase/standard-components/form", |
|||
formControls: record.fields.map(f => formControl(record, f)), |
|||
}, |
|||
}) |
|||
|
|||
const formControl = (record, field) => { |
|||
if(field.type === "string" && field.typeOptions.values && field.typeOptions.values.length > 0) { |
|||
return ({ |
|||
control: { |
|||
_component: "@budibase/standard-components/select", |
|||
options: field.typeOptions.values.map(v => ({id:v, value:v})), |
|||
value: { |
|||
"##bbstate":`${record.name}.${field.name}`, |
|||
"##bbsource":"store" |
|||
}, |
|||
className: "form-control" |
|||
}, |
|||
label: field.label |
|||
}); |
|||
} else { |
|||
return ({ |
|||
control: { |
|||
_component: "@budibase/standard-components/input", |
|||
value: { |
|||
"##bbstate":`${record.name}.${field.name}`, |
|||
"##bbsource":"store" |
|||
}, |
|||
className: "form-control", |
|||
type: field.type === "string" ? "text" |
|||
: field.type === "datetime" ? "date" |
|||
: field.type === "number" ? "number" |
|||
: "text" |
|||
}, |
|||
label: field.label |
|||
}); |
|||
if ( |
|||
field.type === "string" && |
|||
field.typeOptions.values && |
|||
field.typeOptions.values.length > 0 |
|||
) { |
|||
return { |
|||
control: { |
|||
_component: "@budibase/standard-components/select", |
|||
options: field.typeOptions.values.map(v => ({ id: v, value: v })), |
|||
value: { |
|||
"##bbstate": `${record.name}.${field.name}`, |
|||
"##bbsource": "store", |
|||
}, |
|||
className: "form-control", |
|||
}, |
|||
label: field.label, |
|||
} |
|||
} else { |
|||
return { |
|||
control: { |
|||
_component: "@budibase/standard-components/input", |
|||
value: { |
|||
"##bbstate": `${record.name}.${field.name}`, |
|||
"##bbsource": "store", |
|||
}, |
|||
className: "form-control", |
|||
type: |
|||
field.type === "string" |
|||
? "text" |
|||
: field.type === "datetime" |
|||
? "date" |
|||
: field.type === "number" |
|||
? "number" |
|||
: "text", |
|||
}, |
|||
label: field.label, |
|||
} |
|||
} |
|||
} |
|||
|
|||
const saveCancelButtons = (record) => ({ |
|||
component: { |
|||
_component: "@budibase/standard-components/stackpanel", |
|||
direction: "horizontal", |
|||
children: [ |
|||
paddedPanelForButton({ |
|||
_component: "common/Primary Button", |
|||
contentText: `Save ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Save Record", |
|||
parameters: { |
|||
statePath: `${record.name}`, |
|||
} |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "" |
|||
} |
|||
} |
|||
] |
|||
}), |
|||
paddedPanelForButton({ |
|||
_component: "common/Default Button", |
|||
contentText: `Cancel`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "" |
|||
} |
|||
} |
|||
] |
|||
}) |
|||
] |
|||
} |
|||
const saveCancelButtons = record => ({ |
|||
component: { |
|||
_component: "@budibase/standard-components/stackpanel", |
|||
direction: "horizontal", |
|||
children: [ |
|||
paddedPanelForButton({ |
|||
_component: "common/Primary Button", |
|||
contentText: `Save ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Save Record", |
|||
parameters: { |
|||
statePath: `${record.name}`, |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "", |
|||
}, |
|||
}, |
|||
], |
|||
}), |
|||
paddedPanelForButton({ |
|||
_component: "common/Default Button", |
|||
contentText: `Cancel`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "", |
|||
}, |
|||
}, |
|||
], |
|||
}), |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
const paddedPanelForButton = (button) => ({ |
|||
control: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group", |
|||
children: [ |
|||
{ |
|||
component: button |
|||
} |
|||
] |
|||
} |
|||
}); |
|||
|
|||
const paddedPanelForButton = button => ({ |
|||
control: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group", |
|||
children: [ |
|||
{ |
|||
component: button, |
|||
}, |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
@ -1,15 +1,14 @@ |
|||
export const getRecordPath = (record) => { |
|||
export const getRecordPath = () => { |
|||
const parts = [] |
|||
|
|||
const parts = []; |
|||
|
|||
const add = (current) => { |
|||
parts.push(current.name); |
|||
if(current.parent().type === "root") { |
|||
return; |
|||
} |
|||
|
|||
add(current.parent()); |
|||
const add = current => { |
|||
parts.push(current.name) |
|||
if (current.parent().type === "root") { |
|||
return |
|||
} |
|||
|
|||
return parts.reverse().join("/"); |
|||
} |
|||
add(current.parent()) |
|||
} |
|||
|
|||
return parts.reverse().join("/") |
|||
} |
|||
|
|||
@ -1,54 +1,53 @@ |
|||
import { getRecordPath } from "./getRecordPath"; |
|||
import { getRecordPath } from "./getRecordPath" |
|||
|
|||
export const indexTables = ({indexes, helpers}) => |
|||
indexes.map(i => indexTable(i, helpers)); |
|||
export const indexTables = ({ indexes, helpers }) => |
|||
indexes.map(i => indexTable(i, helpers)) |
|||
|
|||
const excludedColumns = ["id", "isNew", "key", "type", "sortKey"]; |
|||
const excludedColumns = ["id", "isNew", "key", "type", "sortKey"] |
|||
|
|||
export const indexTableProps = (index, helpers) => ({ |
|||
data: { |
|||
"##bbstate":index.nodeKey(), |
|||
"##bbsource":"store" |
|||
data: { |
|||
"##bbstate": index.nodeKey(), |
|||
"##bbsource": "store", |
|||
}, |
|||
tableClass: "table table-hover", |
|||
theadClass: "thead-dark", |
|||
columns: helpers |
|||
.indexSchema(index) |
|||
.filter(c => !excludedColumns.includes(c.name)) |
|||
.map(column), |
|||
onRowClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `selectedrow_${index.name}`, |
|||
value: { |
|||
"##bbstate": "key", |
|||
"##bbsource": "event", |
|||
}, |
|||
}, |
|||
}, |
|||
tableClass: "table table-hover", |
|||
theadClass: "thead-dark", |
|||
columns: helpers |
|||
.indexSchema(index) |
|||
.filter(c => !excludedColumns.includes(c.name)) |
|||
.map(column), |
|||
onRowClick: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `selectedrow_${index.name}`, |
|||
value: { |
|||
"##bbstate": "key", |
|||
"##bbsource": "event" |
|||
} |
|||
}, |
|||
} |
|||
] |
|||
}); |
|||
], |
|||
}) |
|||
|
|||
export const getIndexTableName = (index, record) => { |
|||
record = record |
|||
|| index.parent().type === "record" ? index.parent() : null; |
|||
|
|||
return (record |
|||
? `${getRecordPath(record)}/${index.name} Table` |
|||
: `${index.name} Table`); |
|||
record = record || index.parent().type === "record" ? index.parent() : null |
|||
|
|||
return record |
|||
? `${getRecordPath(record)}/${index.name} Table` |
|||
: `${index.name} Table` |
|||
} |
|||
|
|||
const indexTable = (index, helpers) => ({ |
|||
name: getIndexTableName(index), |
|||
inherits: "@budibase/standard-components/table", |
|||
props: indexTableProps(index, helpers) |
|||
}); |
|||
name: getIndexTableName(index), |
|||
inherits: "@budibase/standard-components/table", |
|||
props: indexTableProps(index, helpers), |
|||
}) |
|||
|
|||
const column = (col) => ({ |
|||
title: col.name, |
|||
value: { |
|||
"##bbstate": col.name, |
|||
"##bbsource":"context" |
|||
} |
|||
}) |
|||
const column = col => ({ |
|||
title: col.name, |
|||
value: { |
|||
"##bbstate": col.name, |
|||
"##bbsource": "context", |
|||
}, |
|||
}) |
|||
|
|||
@ -1,190 +1,183 @@ |
|||
import { |
|||
getIndexTableName, indexTables |
|||
} from "./indexTablesGenerator"; |
|||
import { getIndexTableName, indexTables } from "./indexTablesGenerator" |
|||
|
|||
import { |
|||
buttons |
|||
} from "./buttonGenerators"; |
|||
import { buttons } from "./buttonGenerators" |
|||
|
|||
export const recordHomePageComponents = ({indexes, records, helpers}) => |
|||
[ |
|||
...recordHomepages({indexes, records}) |
|||
.map(component), |
|||
export const recordHomePageComponents = ({ indexes, records, helpers }) => [ |
|||
...recordHomepages({ indexes, records }).map(component), |
|||
|
|||
...recordHomepages({indexes, records}) |
|||
.map(homePageButtons), |
|||
|
|||
...indexTables({indexes, records, helpers}), |
|||
...recordHomepages({ indexes, records }).map(homePageButtons), |
|||
|
|||
...buttons({indexes, buttons, helpers}) |
|||
] |
|||
...indexTables({ indexes, records, helpers }), |
|||
|
|||
...buttons({ indexes, buttons, helpers }), |
|||
] |
|||
|
|||
const findIndexForRecord = (indexes, record) => { |
|||
const forRecord = indexes.filter(i => i.allowedRecordNodeIds.includes(record.nodeId)); |
|||
if(forRecord.length === 0) return; |
|||
if(forRecord.length === 1) return forRecord[0]; |
|||
const noMap = forRecord.filter(i => !i.filter || !i.filter.trim()); |
|||
if(noMap.length === 0) forRecord[0]; |
|||
return noMap[0]; |
|||
const forRecord = indexes.filter(i => |
|||
i.allowedRecordNodeIds.includes(record.nodeId) |
|||
) |
|||
if (forRecord.length === 0) return |
|||
if (forRecord.length === 1) return forRecord[0] |
|||
const noMap = forRecord.filter(i => !i.filter || !i.filter.trim()) |
|||
if (noMap.length === 0) forRecord[0] |
|||
return noMap[0] |
|||
} |
|||
|
|||
export const recordHomepages = ({indexes, records}) => |
|||
records.filter(r => r.parent().type === "root") |
|||
.map(r =>({ |
|||
record:r, |
|||
index:findIndexForRecord(indexes, r) |
|||
})) |
|||
.filter(r => r.index); |
|||
export const recordHomepages = ({ indexes, records }) => |
|||
records |
|||
.filter(r => r.parent().type === "root") |
|||
.map(r => ({ |
|||
record: r, |
|||
index: findIndexForRecord(indexes, r), |
|||
})) |
|||
.filter(r => r.index) |
|||
|
|||
export const homepageComponentName = record => |
|||
`${record.name}/${record.name} homepage` |
|||
|
|||
export const homepageComponentName = (record) => |
|||
`${record.name}/${record.name} homepage`; |
|||
const component = ({ record, index }) => ({ |
|||
inherits: "@budibase/standard-components/div", |
|||
name: homepageComponentName(record), |
|||
props: { |
|||
className: "d-flex flex-column h-100", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: `${record.name}/homepage buttons`, |
|||
}, |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: getIndexTableName(index), |
|||
}, |
|||
className: "flex-gow-1 overflow-auto", |
|||
}, |
|||
], |
|||
onLoad: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "", |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "List Records", |
|||
parameters: { |
|||
statePath: index.nodeKey(), |
|||
indexKey: index.nodeKey(), |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
const component = ({record, index}) => ({ |
|||
inherits: "@budibase/standard-components/div", |
|||
name: homepageComponentName(record), |
|||
props: { |
|||
className: "d-flex flex-column h-100", |
|||
children: [ |
|||
const homePageButtons = ({ index, record }) => ({ |
|||
inherits: "@budibase/standard-components/div", |
|||
name: `${record.name}/homepage buttons`, |
|||
props: { |
|||
className: "btn-toolbar mt-4 mb-2", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group mr-3", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: `${record.name}/homepage buttons`, |
|||
} |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Create ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Get New Record", |
|||
parameters: { |
|||
statePath: record.name, |
|||
collectionKey: `/${record.collectionName}`, |
|||
childRecordType: record.name, |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "true", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: getIndexTableName(index) |
|||
}, |
|||
className: "flex-gow-1 overflow-auto" |
|||
} |
|||
], |
|||
onLoad: [ |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "" |
|||
} |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Refresh`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "List Records", |
|||
parameters: { |
|||
statePath: index.nodeKey(), |
|||
indexKey: index.nodeKey(), |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "List Records", |
|||
parameters: { |
|||
statePath: index.nodeKey(), |
|||
indexKey: index.nodeKey() |
|||
} |
|||
} |
|||
] |
|||
} |
|||
|
|||
}); |
|||
|
|||
const homePageButtons = ({index, record}) => ({ |
|||
inherits: "@budibase/standard-components/div", |
|||
name: `${record.name}/homepage buttons`, |
|||
props: { |
|||
className: "btn-toolbar mt-4 mb-2", |
|||
children: [ |
|||
{ |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/if", |
|||
condition: `$store.selectedrow_${index.name} && $store.selectedrow_${index.name}.length > 0`, |
|||
thenComponent: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group mr-3", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Create ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Get New Record", |
|||
parameters: { |
|||
statePath: record.name, |
|||
collectionKey: `/${record.collectionName}`, |
|||
childRecordType: record.name |
|||
} |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "true" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
_component: "common/Default Button", |
|||
contentText: `Edit ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Load Record", |
|||
parameters: { |
|||
statePath: record.name, |
|||
recordKey: { |
|||
"##bbstate": `selectedrow_${index.name}`, |
|||
"##source": "store", |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Refresh`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "List Records", |
|||
parameters: { |
|||
statePath: index.nodeKey(), |
|||
indexKey: index.nodeKey() |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
}, |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "true", |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: "@budibase/standard-components/if", |
|||
condition: `$store.selectedrow_${index.name} && $store.selectedrow_${index.name}.length > 0`, |
|||
thenComponent: { |
|||
_component: "@budibase/standard-components/div", |
|||
className: "btn-group", |
|||
children: [ |
|||
{ |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Edit ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Load Record", |
|||
parameters: { |
|||
statePath: record.name, |
|||
recordKey: { |
|||
"##bbstate" : `selectedrow_${index.name}`, |
|||
"##source": "store" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"##eventHandlerType": "Set State", |
|||
parameters: { |
|||
path: `isEditing${record.name}`, |
|||
value: "true" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
component: { |
|||
_component: "common/Default Button", |
|||
contentText: `Delete ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Delete Record", |
|||
parameters: { |
|||
recordKey: { |
|||
"##bbstate" : `selectedrow_${index.name}`, |
|||
"##source": "store" |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}) |
|||
_component: "common/Default Button", |
|||
contentText: `Delete ${record.name}`, |
|||
onClick: [ |
|||
{ |
|||
"##eventHandlerType": "Delete Record", |
|||
parameters: { |
|||
recordKey: { |
|||
"##bbstate": `selectedrow_${index.name}`, |
|||
"##source": "store", |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
}) |
|||
|
|||
@ -1,36 +1,32 @@ |
|||
import { |
|||
recordHomepages, |
|||
homepageComponentName, |
|||
recordHomePageComponents |
|||
} from "./recordHomePageGenerator"; |
|||
import { formName, forms } from "./formsGenerator"; |
|||
import { |
|||
recordHomepages, |
|||
homepageComponentName, |
|||
recordHomePageComponents, |
|||
} from "./recordHomePageGenerator" |
|||
import { formName, forms } from "./formsGenerator" |
|||
|
|||
export const selectNavContent = ({indexes, records, helpers}) => |
|||
[ |
|||
...recordHomepages({indexes, records}) |
|||
.map(component), |
|||
export const selectNavContent = ({ indexes, records, helpers }) => [ |
|||
...recordHomepages({ indexes, records }).map(component), |
|||
|
|||
...recordHomePageComponents({indexes, records, helpers}), |
|||
|
|||
...forms({indexes, records, helpers}) |
|||
|
|||
] |
|||
...recordHomePageComponents({ indexes, records, helpers }), |
|||
|
|||
...forms({ indexes, records, helpers }), |
|||
] |
|||
|
|||
export const navContentComponentName = record => |
|||
`${record.name}/${record.name} Nav Content`; |
|||
`${record.name}/${record.name} Nav Content` |
|||
|
|||
const component = ({record, index}) => ({ |
|||
inherits: "@budibase/standard-components/if", |
|||
description: `the component that gets displayed when the ${record.collectionName} nav is selected`, |
|||
name: navContentComponentName(record), |
|||
props: { |
|||
condition: `$store.isEditing${record.name}`, |
|||
thenComponent: { |
|||
_component: formName(record) |
|||
}, |
|||
elseComponent: { |
|||
_component: homepageComponentName(record) |
|||
} |
|||
} |
|||
}); |
|||
const component = ({ record }) => ({ |
|||
inherits: "@budibase/standard-components/if", |
|||
description: `the component that gets displayed when the ${record.collectionName} nav is selected`, |
|||
name: navContentComponentName(record), |
|||
props: { |
|||
condition: `$store.isEditing${record.name}`, |
|||
thenComponent: { |
|||
_component: formName(record), |
|||
}, |
|||
elseComponent: { |
|||
_component: homepageComponentName(record), |
|||
}, |
|||
}, |
|||
}) |
|||
|
|||
@ -1,3 +1,2 @@ |
|||
export {default as form} from "./Form.svelte"; |
|||
export {default as nav} from "./Nav.svelte"; |
|||
|
|||
export { default as form } from "./Form.svelte" |
|||
export { default as nav } from "./Nav.svelte" |
|||
|
|||
@ -1,12 +1,13 @@ |
|||
module.exports = ({ |
|||
"presets": ["@babel/preset-env"], |
|||
"sourceMaps": "inline", |
|||
"retainLines": true, |
|||
"plugins": [ |
|||
["@babel/plugin-transform-runtime", |
|||
{ |
|||
"regenerator": true |
|||
} |
|||
] |
|||
] |
|||
}); |
|||
module.exports = { |
|||
presets: ["@babel/preset-env"], |
|||
sourceMaps: "inline", |
|||
retainLines: true, |
|||
plugins: [ |
|||
[ |
|||
"@babel/plugin-transform-runtime", |
|||
{ |
|||
regenerator: true, |
|||
}, |
|||
], |
|||
], |
|||
} |
|||
|
|||
@ -1,8 +1,8 @@ |
|||
const ncp = require('ncp').ncp; |
|||
|
|||
ncp("./dist", "../server/builder", function (err) { |
|||
if (err) { |
|||
return console.error(err); |
|||
} |
|||
console.log('Copied dist folder to ../server/builder'); |
|||
}) |
|||
const ncp = require("ncp").ncp |
|||
|
|||
ncp("./dist", "../server/builder", function(err) { |
|||
if (err) { |
|||
return console.error(err) |
|||
} |
|||
console.log("Copied dist folder to ../server/builder") |
|||
}) |
|||
|
|||
@ -1,126 +1,218 @@ |
|||
import svelte from 'rollup-plugin-svelte'; |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import commonjs from 'rollup-plugin-commonjs'; |
|||
import url from 'rollup-plugin-url'; |
|||
import livereload from 'rollup-plugin-livereload'; |
|||
import { terser } from 'rollup-plugin-terser'; |
|||
import builtins from 'rollup-plugin-node-builtins'; |
|||
import nodeglobals from 'rollup-plugin-node-globals'; |
|||
import copy from 'rollup-plugin-copy'; |
|||
import browsersync from "rollup-plugin-browsersync"; |
|||
import proxy from "http-proxy-middleware"; |
|||
import svelte from "rollup-plugin-svelte" |
|||
import resolve from "rollup-plugin-node-resolve" |
|||
import commonjs from "rollup-plugin-commonjs" |
|||
import url from "rollup-plugin-url" |
|||
import livereload from "rollup-plugin-livereload" |
|||
import { terser } from "rollup-plugin-terser" |
|||
import builtins from "rollup-plugin-node-builtins" |
|||
import nodeglobals from "rollup-plugin-node-globals" |
|||
import copy from "rollup-plugin-copy" |
|||
import browsersync from "rollup-plugin-browsersync" |
|||
import proxy from "http-proxy-middleware" |
|||
|
|||
const target = 'http://localhost:4001'; |
|||
const _builderProxy = proxy('/_builder', { |
|||
target: "http://localhost:3000", |
|||
pathRewrite: { '^/_builder': '' } |
|||
}); |
|||
const target = "http://localhost:4001" |
|||
const _builderProxy = proxy("/_builder", { |
|||
target: "http://localhost:3000", |
|||
pathRewrite: { "^/_builder": "" }, |
|||
}) |
|||
|
|||
const apiProxy = proxy(['/_builder/api/**', '/_builder/**/componentlibrary', '/_builder/**/componentlibraryGenerators'], { |
|||
target, |
|||
logLevel: "debug", |
|||
changeOrigin: true, |
|||
cookieDomainRewrite: true, |
|||
onProxyReq(proxyReq) { |
|||
if (proxyReq.getHeader("origin")) { |
|||
proxyReq.setHeader("origin", target) |
|||
} |
|||
} |
|||
}); |
|||
const apiProxy = proxy( |
|||
[ |
|||
"/_builder/api/**", |
|||
"/_builder/**/componentlibrary", |
|||
"/_builder/**/componentlibraryGenerators", |
|||
], |
|||
{ |
|||
target, |
|||
logLevel: "debug", |
|||
changeOrigin: true, |
|||
cookieDomainRewrite: true, |
|||
onProxyReq(proxyReq) { |
|||
if (proxyReq.getHeader("origin")) { |
|||
proxyReq.setHeader("origin", target) |
|||
} |
|||
}, |
|||
} |
|||
) |
|||
|
|||
const production = !process.env.ROLLUP_WATCH; |
|||
const production = !process.env.ROLLUP_WATCH |
|||
|
|||
const lodash_fp_exports = ["union", "reduce", "isUndefined", "cloneDeep", "split", "some", "map", "filter", "isEmpty", "countBy", "includes", "last", "find", "constant", |
|||
"take", "first", "intersection", "mapValues", "isNull", "has", "isInteger", "isNumber", "isString", "isBoolean", "isDate", "isArray", "isObject", "clone", "values", "keyBy", "isNaN", |
|||
"keys", "orderBy", "concat", "reverse", "difference", "merge", "flatten", "each", "pull", "join", "defaultCase", "uniqBy", "every", "uniqWith", "isFunction", "groupBy", |
|||
"differenceBy", "intersectionBy", "isEqual", "max", "sortBy", "assign", "uniq", "trimChars", "trimCharsStart", "isObjectLike", "flattenDeep", "indexOf", "isPlainObject", |
|||
"toNumber", "takeRight", "toPairs"]; |
|||
const lodash_fp_exports = [ |
|||
"union", |
|||
"reduce", |
|||
"isUndefined", |
|||
"cloneDeep", |
|||
"split", |
|||
"some", |
|||
"map", |
|||
"filter", |
|||
"isEmpty", |
|||
"countBy", |
|||
"includes", |
|||
"last", |
|||
"find", |
|||
"constant", |
|||
"take", |
|||
"first", |
|||
"intersection", |
|||
"mapValues", |
|||
"isNull", |
|||
"has", |
|||
"isInteger", |
|||
"isNumber", |
|||
"isString", |
|||
"isBoolean", |
|||
"isDate", |
|||
"isArray", |
|||
"isObject", |
|||
"clone", |
|||
"values", |
|||
"keyBy", |
|||
"isNaN", |
|||
"keys", |
|||
"orderBy", |
|||
"concat", |
|||
"reverse", |
|||
"difference", |
|||
"merge", |
|||
"flatten", |
|||
"each", |
|||
"pull", |
|||
"join", |
|||
"defaultCase", |
|||
"uniqBy", |
|||
"every", |
|||
"uniqWith", |
|||
"isFunction", |
|||
"groupBy", |
|||
"differenceBy", |
|||
"intersectionBy", |
|||
"isEqual", |
|||
"max", |
|||
"sortBy", |
|||
"assign", |
|||
"uniq", |
|||
"trimChars", |
|||
"trimCharsStart", |
|||
"isObjectLike", |
|||
"flattenDeep", |
|||
"indexOf", |
|||
"isPlainObject", |
|||
"toNumber", |
|||
"takeRight", |
|||
"toPairs", |
|||
] |
|||
|
|||
const lodash_exports = ["flow", "join", "replace", "trim", "dropRight", "takeRight", "head", "reduce", |
|||
"tail", "startsWith", "findIndex", "merge", |
|||
"assign", "each", "find", "orderBy", "union"]; |
|||
const lodash_exports = [ |
|||
"flow", |
|||
"join", |
|||
"replace", |
|||
"trim", |
|||
"dropRight", |
|||
"takeRight", |
|||
"head", |
|||
"reduce", |
|||
"tail", |
|||
"startsWith", |
|||
"findIndex", |
|||
"merge", |
|||
"assign", |
|||
"each", |
|||
"find", |
|||
"orderBy", |
|||
"union", |
|||
] |
|||
|
|||
const outputpath = "../server/builder"; |
|||
const outputpath = "../server/builder" |
|||
|
|||
const coreExternal = [ |
|||
"lodash", "lodash/fp", "date-fns", |
|||
"lunr", "safe-buffer", "shortid", |
|||
"@nx-js/compiler-util" |
|||
]; |
|||
"lodash", |
|||
"lodash/fp", |
|||
"date-fns", |
|||
"lunr", |
|||
"safe-buffer", |
|||
"shortid", |
|||
"@nx-js/compiler-util", |
|||
] |
|||
|
|||
export default { |
|||
input: 'src/main.js', |
|||
output: { |
|||
sourcemap: true, |
|||
format: 'iife', |
|||
name: 'app', |
|||
file: `${outputpath}/bundle.js` |
|||
}, |
|||
plugins: [ |
|||
copy({ |
|||
targets: [ |
|||
{ src: 'src/index.html', dest: outputpath }, |
|||
{ src: 'src/favicon.png', dest: outputpath }, |
|||
{ src: 'src/assets', dest: outputpath }, |
|||
{ src: 'node_modules/@budibase/client/dist/budibase-client.esm.mjs', dest: outputpath }, |
|||
] |
|||
}), |
|||
input: "src/main.js", |
|||
output: { |
|||
sourcemap: true, |
|||
format: "iife", |
|||
name: "app", |
|||
file: `${outputpath}/bundle.js`, |
|||
}, |
|||
plugins: [ |
|||
copy({ |
|||
targets: [ |
|||
{ src: "src/index.html", dest: outputpath }, |
|||
{ src: "src/favicon.png", dest: outputpath }, |
|||
{ src: "src/assets", dest: outputpath }, |
|||
{ |
|||
src: "node_modules/@budibase/client/dist/budibase-client.esm.mjs", |
|||
dest: outputpath, |
|||
}, |
|||
], |
|||
}), |
|||
|
|||
svelte({ |
|||
// enable run-time checks when not in production
|
|||
dev: !production, |
|||
include: 'src/**/*.svelte', |
|||
// we'll extract any component CSS out into
|
|||
// a separate file — better for performance
|
|||
css: css => { |
|||
css.write(`${outputpath}/bundle.css`); |
|||
} |
|||
}), |
|||
svelte({ |
|||
// enable run-time checks when not in production
|
|||
dev: !production, |
|||
include: "src/**/*.svelte", |
|||
// we'll extract any component CSS out into
|
|||
// a separate file — better for performance
|
|||
css: css => { |
|||
css.write(`${outputpath}/bundle.css`) |
|||
}, |
|||
}), |
|||
|
|||
resolve({ |
|||
browser: true, |
|||
dedupe: importee => { |
|||
return importee === 'svelte' |
|||
|| importee.startsWith('svelte/') |
|||
|| coreExternal.includes(importee); |
|||
} |
|||
resolve({ |
|||
browser: true, |
|||
dedupe: importee => { |
|||
return ( |
|||
importee === "svelte" || |
|||
importee.startsWith("svelte/") || |
|||
coreExternal.includes(importee) |
|||
) |
|||
}, |
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
lodash: lodash_exports, |
|||
shortid: ["generate"], |
|||
}, |
|||
}), |
|||
url({ |
|||
limit: 0, |
|||
include: ["**/*.woff2", "**/*.png"], |
|||
fileName: "[dirname][name][extname]", |
|||
emitFiles: true, |
|||
}), |
|||
url({ |
|||
limit: 0, |
|||
include: ["**/*.css"], |
|||
fileName: "[name][extname]", |
|||
emitFiles: true, |
|||
}), |
|||
builtins(), |
|||
nodeglobals(), |
|||
|
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
"lodash": lodash_exports, |
|||
"shortid": ["generate"] |
|||
} |
|||
}), |
|||
url({ |
|||
limit: 0, |
|||
include: ["**/*.woff2", "**/*.png"], |
|||
fileName: "[dirname][name][extname]", |
|||
emitFiles: true |
|||
}), |
|||
url({ |
|||
limit: 0, |
|||
include: ["**/*.css"], |
|||
fileName: "[name][extname]", |
|||
emitFiles: true |
|||
}), |
|||
builtins(), |
|||
nodeglobals(), |
|||
// Watch the `dist` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload(outputpath), |
|||
!production && |
|||
browsersync({ |
|||
server: outputpath, |
|||
middleware: [apiProxy, _builderProxy], |
|||
}), |
|||
|
|||
// Watch the `dist` directory and refresh the
|
|||
// browser on changes when not in production
|
|||
!production && livereload(outputpath), |
|||
!production && browsersync({ |
|||
server: outputpath, |
|||
middleware: [apiProxy, _builderProxy] |
|||
}), |
|||
|
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser() |
|||
], |
|||
watch: { |
|||
clearScreen: false |
|||
} |
|||
}; |
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser(), |
|||
], |
|||
watch: { |
|||
clearScreen: false, |
|||
}, |
|||
} |
|||
|
|||
@ -1,56 +1,47 @@ |
|||
<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 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"; |
|||
|
|||
import { store } from "./builderStore" |
|||
|
|||
export let navWidth = "50px" |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="nav"> |
|||
<BackendNav /> |
|||
</div> |
|||
<div class="content" |
|||
style="width: calc(100% - {navWidth}); left: {navWidth}"> |
|||
{#if $store.activeNav === "database"} |
|||
<Database /> |
|||
{:else if $store.activeNav === "actions"} |
|||
<ActionsAndTriggers /> |
|||
{:else if $store.activeNav === "access levels"} |
|||
<AccessLevels /> |
|||
<div class="content" style="width: calc(100% - {navWidth}); left: {navWidth}"> |
|||
{#if $store.activeNav === 'database'} |
|||
<Database /> |
|||
{:else if $store.activeNav === 'actions'} |
|||
<ActionsAndTriggers /> |
|||
{:else if $store.activeNav === 'access levels'} |
|||
<AccessLevels /> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
height: 100%; |
|||
display: flex; |
|||
} |
|||
|
|||
|
|||
.content { |
|||
flex: 1 1 auto; |
|||
height: 100%; |
|||
background-color: var(--white); |
|||
margin:0; |
|||
} |
|||
|
|||
.nav { |
|||
flex: 0 1 auto; |
|||
width: 300px; |
|||
height: 100%; |
|||
} |
|||
|
|||
</style> |
|||
.root { |
|||
height: 100%; |
|||
display: flex; |
|||
} |
|||
|
|||
.content { |
|||
flex: 1 1 auto; |
|||
height: 100%; |
|||
background-color: var(--white); |
|||
margin: 0; |
|||
} |
|||
|
|||
.nav { |
|||
flex: 0 1 auto; |
|||
width: 300px; |
|||
height: 100%; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,56 +1,55 @@ |
|||
<script> |
|||
import Button from "./common/Button.svelte" |
|||
import { store } from "./builderStore" |
|||
|
|||
import Button from "./common/Button.svelte" |
|||
import { store } from "./builderStore"; |
|||
|
|||
let errors = []; |
|||
|
|||
let errors = [] |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="inner"> |
|||
<img src="/_builder/assets/budibase-logo.png" class="logo" alt="budibase logo"/> |
|||
<div> |
|||
|
|||
<div> |
|||
<h4 style="margin-bottom: 20px">Choose an Application</h4> |
|||
{#each $store.apps as app} |
|||
<a href={`#/${app}`} class="app-link">{app}</a> |
|||
{/each} |
|||
</div> |
|||
</div> |
|||
<div class="inner"> |
|||
<img |
|||
src="/_builder/assets/budibase-logo.png" |
|||
class="logo" |
|||
alt="budibase logo" /> |
|||
<div> |
|||
|
|||
<div> |
|||
<h4 style="margin-bottom: 20px">Choose an Application</h4> |
|||
{#each $store.apps as app} |
|||
<a href={`#/${app}`} class="app-link">{app}</a> |
|||
{/each} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
position: fixed; |
|||
margin: 0 auto; |
|||
text-align: center; |
|||
top: 20%; |
|||
/*color: #333333; |
|||
background-color: #fdfdfd;*/ |
|||
width:100%; |
|||
} |
|||
width: 100%; |
|||
} |
|||
|
|||
.inner { |
|||
display:inline-block; |
|||
.inner { |
|||
display: inline-block; |
|||
margin: auto; |
|||
} |
|||
} |
|||
|
|||
.logo { |
|||
.logo { |
|||
width: 300px; |
|||
margin-bottom: 40px; |
|||
} |
|||
} |
|||
|
|||
.root :global(.option) { |
|||
width:250px; |
|||
} |
|||
.root :global(.option) { |
|||
width: 250px; |
|||
} |
|||
|
|||
.app-link { |
|||
.app-link { |
|||
margin-top: 10px; |
|||
display: block; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,27 +1,26 @@ |
|||
<script> |
|||
|
|||
import IconButton from "./common/IconButton.svelte"; |
|||
import { store } from "./builderStore"; |
|||
import UserInterfaceRoot from "./userInterface/UserInterfaceRoot.svelte"; |
|||
import { fade } from "svelte/transition"; |
|||
|
|||
import IconButton from "./common/IconButton.svelte" |
|||
import { store } from "./builderStore" |
|||
import UserInterfaceRoot from "./userInterface/UserInterfaceRoot.svelte" |
|||
import { fade } from "svelte/transition" |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="content uk-container"> |
|||
<div class="content uk-container"> |
|||
|
|||
<h1>Settings</h1> |
|||
<h1>Settings</h1> |
|||
|
|||
<label> |
|||
<input type="checkbox" |
|||
class="uk-checkbox" |
|||
bind:checked={$store.useAnalytics}> |
|||
Send analytics |
|||
</label> |
|||
<label> |
|||
<input |
|||
type="checkbox" |
|||
class="uk-checkbox" |
|||
bind:checked={$store.useAnalytics} /> |
|||
Send analytics |
|||
</label> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,99 +1,91 @@ |
|||
<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"; |
|||
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"; |
|||
|
|||
export let level; |
|||
export let allPermissions; |
|||
export let onFinished; |
|||
export let isNew; |
|||
export let allLevels; |
|||
export let hierarchy; |
|||
export let actions; |
|||
|
|||
let errors = []; |
|||
let clonedLevel = cloneDeep(level); |
|||
|
|||
const matchPermissions = (p1, p2) => |
|||
p1.type === p2.type |
|||
&& |
|||
((!p2.nodeKey && !p1.nodeKey) |
|||
|| p2.nodeKey === p1.nodeKey); |
|||
|
|||
const hasPermission = hasPerm => |
|||
some(p => matchPermissions(p, hasPerm)) |
|||
(clonedLevel.permissions); |
|||
|
|||
$: permissionMatrix = |
|||
map(p => ({permission:p, hasPermission: hasPermission(p)})) |
|||
(allPermissions) |
|||
|
|||
const getPermissionName = perm => |
|||
perm.nodeKey |
|||
? `${perm.type} - ${perm.nodeKey}` |
|||
: perm.type; |
|||
|
|||
const save = () => { |
|||
|
|||
const newLevels = |
|||
isNew |
|||
? [...allLevels, clonedLevel] |
|||
: [...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); |
|||
export let level |
|||
export let allPermissions |
|||
export let onFinished |
|||
export let isNew |
|||
export let allLevels |
|||
export let hierarchy |
|||
export let actions |
|||
|
|||
let errors = [] |
|||
let clonedLevel = cloneDeep(level) |
|||
|
|||
const matchPermissions = (p1, p2) => |
|||
p1.type === p2.type && |
|||
((!p2.nodeKey && !p1.nodeKey) || p2.nodeKey === p1.nodeKey) |
|||
|
|||
const hasPermission = hasPerm => |
|||
some(p => matchPermissions(p, hasPerm))(clonedLevel.permissions) |
|||
|
|||
$: permissionMatrix = map(p => ({ |
|||
permission: p, |
|||
hasPermission: hasPermission(p), |
|||
}))(allPermissions) |
|||
|
|||
const getPermissionName = perm => |
|||
perm.nodeKey ? `${perm.type} - ${perm.nodeKey}` : perm.type |
|||
|
|||
const save = () => { |
|||
const newLevels = isNew |
|||
? [...allLevels, clonedLevel] |
|||
: [...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 { |
|||
clonedLevel.permissions = filter(p => !matchPermissions(p, perm))(clonedLevel.permissions); |
|||
clonedLevel.permissions = filter(p => !matchPermissions(p, perm))( |
|||
clonedLevel.permissions |
|||
) |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<div> |
|||
|
|||
<ErrorsBox {errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
<ErrorsBox {errors} /> |
|||
|
|||
<Textbox label="Name" bind:text={clonedLevel.name} /> |
|||
<form class="uk-form-horizontal"> |
|||
|
|||
{#each permissionMatrix as permission} |
|||
<div> |
|||
<Checkbox label={getPermissionName(permission.permission)} |
|||
checked={permission.hasPermission} |
|||
on:change={permissionChanged(permission.permission)} /> |
|||
</div> |
|||
{/each} |
|||
<Textbox label="Name" bind:text={clonedLevel.name} /> |
|||
|
|||
</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"> |
|||
<Button color="primary" grouped on:click={save}>Save</Button> |
|||
<Button color="secondary" grouped on:click={() => onFinished()}>Cancel</Button> |
|||
</ButtonGroup> |
|||
</form> |
|||
|
|||
<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> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,119 +1,115 @@ |
|||
<script> |
|||
|
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import {store} from "../builderStore"; |
|||
import {generateFullPermissions, getNewAccessLevel} from "../common/core"; |
|||
import getIcon from "../common/icon"; |
|||
import AccessLevelView from "./AccessLevelView.svelte"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
|
|||
let editingLevel = null; |
|||
let editingLevelIsNew = false; |
|||
$: isEditing = (editingLevel !== null); |
|||
|
|||
let allPermissions = []; |
|||
store.subscribe(db => { |
|||
allPermissions = generateFullPermissions(db.hierarchy, db.actions); |
|||
}) |
|||
|
|||
let onLevelEdit = (level) => { |
|||
editingLevel = level; |
|||
editingLevelIsNew = false; |
|||
}; |
|||
|
|||
let onLevelCancel = () => { |
|||
editingAction = null; |
|||
}; |
|||
|
|||
let onLevelDelete = (level) => { |
|||
store.deleteLevel(level); |
|||
}; |
|||
|
|||
|
|||
let createNewLevel = () => { |
|||
editingLevelIsNew = true; |
|||
editingLevel = getNewAccessLevel(); |
|||
} |
|||
|
|||
let onEditingFinished = (level) => { |
|||
if(level) { |
|||
store.saveLevel(level, editingLevelIsNew, editingLevel); |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import { store } from "../builderStore" |
|||
import { generateFullPermissions, getNewAccessLevel } from "../common/core" |
|||
import getIcon from "../common/icon" |
|||
import AccessLevelView from "./AccessLevelView.svelte" |
|||
import Modal from "../common/Modal.svelte" |
|||
|
|||
let editingLevel = null |
|||
let editingLevelIsNew = false |
|||
$: isEditing = editingLevel !== null |
|||
|
|||
let allPermissions = [] |
|||
store.subscribe(db => { |
|||
allPermissions = generateFullPermissions(db.hierarchy, db.actions) |
|||
}) |
|||
|
|||
let onLevelEdit = level => { |
|||
editingLevel = level |
|||
editingLevelIsNew = false |
|||
} |
|||
|
|||
let onLevelCancel = () => { |
|||
editingAction = null |
|||
} |
|||
|
|||
let onLevelDelete = level => { |
|||
store.deleteLevel(level) |
|||
} |
|||
|
|||
let createNewLevel = () => { |
|||
editingLevelIsNew = true |
|||
editingLevel = getNewAccessLevel() |
|||
} |
|||
|
|||
let onEditingFinished = level => { |
|||
if (level) { |
|||
store.saveLevel(level, editingLevelIsNew, editingLevel) |
|||
} |
|||
editingLevel = null; |
|||
} |
|||
|
|||
const getPermissionsString = perms => { |
|||
return `${perms.length} / ${allPermissions.length}`; |
|||
} |
|||
editingLevel = null |
|||
} |
|||
|
|||
const getPermissionsString = perms => { |
|||
return `${perms.length} / ${allPermissions.length}` |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<ButtonGroup> |
|||
<Button grouped color="secondary" on:click={createNewLevel}>Create New Access Level</Button> |
|||
</ButtonGroup> |
|||
<ButtonGroup> |
|||
<Button grouped color="secondary" on:click={createNewLevel}> |
|||
Create New Access Level |
|||
</Button> |
|||
</ButtonGroup> |
|||
|
|||
{#if $store.accessLevels} |
|||
<table class="fields-table uk-table uk-table-small"> |
|||
<thead> |
|||
{#if $store.accessLevels} |
|||
<table class="fields-table uk-table uk-table-small"> |
|||
<thead> |
|||
<tr> |
|||
<th>Name</th> |
|||
<th>Permissions</th> |
|||
<th></th> |
|||
<th>Name</th> |
|||
<th>Permissions</th> |
|||
<th /> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
</thead> |
|||
<tbody> |
|||
{#each $store.accessLevels.levels as level} |
|||
<tr> |
|||
<td >{level.name}</td> |
|||
<td >{getPermissionsString(level.permissions)}</td> |
|||
<tr> |
|||
<td>{level.name}</td> |
|||
<td>{getPermissionsString(level.permissions)}</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onLevelEdit(level)}>{@html getIcon("edit")}</span> |
|||
<span on:click={() => onLevelDelete(level)}>{@html getIcon("trash")}</span> |
|||
<span on:click={() => onLevelEdit(level)}> |
|||
{@html getIcon('edit')} |
|||
</span> |
|||
<span on:click={() => onLevelDelete(level)}> |
|||
{@html getIcon('trash')} |
|||
</span> |
|||
</td> |
|||
</tr> |
|||
</tr> |
|||
{/each} |
|||
</tbody> |
|||
</table> |
|||
{:else} |
|||
(no actions added) |
|||
{/if} |
|||
|
|||
</tbody> |
|||
</table> |
|||
{:else}(no actions added){/if} |
|||
|
|||
<Modal bind:isOpen={isEditing}> |
|||
<Modal bind:isOpen={isEditing}> |
|||
{#if isEditing} |
|||
<AccessLevelView level={editingLevel} |
|||
allPermissions={allPermissions} |
|||
onFinished={onEditingFinished} |
|||
isNew={editingLevelIsNew} |
|||
allLevels={$store.accessLevels.levels} |
|||
hierarchy={$store.hierarchy} |
|||
actions={$store.actions} /> |
|||
{/if} |
|||
</Modal> |
|||
|
|||
<AccessLevelView |
|||
level={editingLevel} |
|||
{allPermissions} |
|||
onFinished={onEditingFinished} |
|||
isNew={editingLevelIsNew} |
|||
allLevels={$store.accessLevels.levels} |
|||
hierarchy={$store.hierarchy} |
|||
actions={$store.actions} /> |
|||
{/if} |
|||
</Modal> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
position: relative; |
|||
padding: 1.5rem; |
|||
} |
|||
|
|||
} |
|||
|
|||
.actions-header { |
|||
.actions-header { |
|||
flex: 0 1 auto; |
|||
} |
|||
} |
|||
|
|||
.node-view { |
|||
.node-view { |
|||
overflow-y: auto; |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,123 +1,134 @@ |
|||
<script> |
|||
|
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import {cloneDeep, filter, keys, |
|||
map, isUndefined} from "lodash/fp"; |
|||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
|||
import {validateActions, pipe} from "../common/core"; |
|||
import getIcon from "../common/icon"; |
|||
|
|||
export let action; |
|||
export let onFinished = (action) => {}; |
|||
export let allActions; |
|||
export let isNew = true; |
|||
|
|||
let optKey = ""; |
|||
let optValue = ""; |
|||
|
|||
let clonedAction = cloneDeep(action); |
|||
let initialOptions = pipe(action.initialOptions, [ |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import { cloneDeep, filter, keys, map, isUndefined } from "lodash/fp" |
|||
import ErrorsBox from "../common/ErrorsBox.svelte" |
|||
import { validateActions, pipe } from "../common/core" |
|||
import getIcon from "../common/icon" |
|||
|
|||
export let action |
|||
export let onFinished = action => {} |
|||
export let allActions |
|||
export let isNew = true |
|||
|
|||
let optKey = "" |
|||
let optValue = "" |
|||
|
|||
let clonedAction = cloneDeep(action) |
|||
let initialOptions = pipe(action.initialOptions, [ |
|||
keys, |
|||
map(k => ({key:k, value:action.initialOptions[k]})) |
|||
]); |
|||
let errors = []; |
|||
|
|||
const addNewOption = () => { |
|||
|
|||
if(optKey && optValue && isUndefined(clonedAction.initialOptions[optKey])) { |
|||
clonedAction.initialOptions[optKey] = optValue; |
|||
initialOptions = [...initialOptions, { |
|||
key:optKey, value: optValue |
|||
}]; |
|||
optKey = ""; |
|||
optValue = ""; |
|||
} |
|||
} |
|||
|
|||
const removeOption = (opt) => { |
|||
if(opt) { |
|||
delete clonedAction.initialOptions[opt.key] |
|||
initialOptions = pipe(initialOptions, [ |
|||
filter(o => o.key !== opt.key) |
|||
]); |
|||
map(k => ({ key: k, value: action.initialOptions[k] })), |
|||
]) |
|||
let errors = [] |
|||
|
|||
const addNewOption = () => { |
|||
if ( |
|||
optKey && |
|||
optValue && |
|||
isUndefined(clonedAction.initialOptions[optKey]) |
|||
) { |
|||
clonedAction.initialOptions[optKey] = optValue |
|||
initialOptions = [ |
|||
...initialOptions, |
|||
{ |
|||
key: optKey, |
|||
value: optValue, |
|||
}, |
|||
] |
|||
optKey = "" |
|||
optValue = "" |
|||
} |
|||
} |
|||
} |
|||
|
|||
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 = [ |
|||
...pipe(allActions ,[filter(a => a !== action)]), |
|||
clonedAction] |
|||
...pipe(allActions, [filter(a => a !== action)]), |
|||
clonedAction, |
|||
] |
|||
|
|||
errors = pipe(newActionsList ,[ |
|||
validateActions, |
|||
map(e => e.error) |
|||
]); |
|||
errors = pipe(newActionsList, [validateActions, map(e => e.error)]) |
|||
|
|||
if(errors.length === 0) |
|||
onFinished(clonedAction); |
|||
} |
|||
|
|||
const cancel = () => { |
|||
onFinished(); |
|||
} |
|||
if (errors.length === 0) onFinished(clonedAction) |
|||
} |
|||
|
|||
const cancel = () => { |
|||
onFinished() |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<ErrorsBox {errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<Textbox label="Name" bind:text={clonedAction.name} /> |
|||
<Textbox label="Behaviour Source" bind:text={clonedAction.behaviourSource} /> |
|||
<Textbox label="Behaviour" bind:text={clonedAction.behaviourName} /> |
|||
|
|||
</form> |
|||
|
|||
<div class=" uk-form-stacked" style="margin-bottom: 20px"> |
|||
<label class="uk-form-label">Default Options</label> |
|||
<div class="uk-grid-small" uk-grid> |
|||
<input class="uk-input uk-width-1-4 uk-margin-right" placeholder="key" bind:value={optKey} > |
|||
<input class="uk-input uk-width-1-4 uk-margin-right" 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> |
|||
<ErrorsBox {errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<Textbox label="Name" bind:text={clonedAction.name} /> |
|||
<Textbox |
|||
label="Behaviour Source" |
|||
bind:text={clonedAction.behaviourSource} /> |
|||
<Textbox label="Behaviour" bind:text={clonedAction.behaviourName} /> |
|||
|
|||
</form> |
|||
|
|||
<div class=" uk-form-stacked" style="margin-bottom: 20px"> |
|||
<label class="uk-form-label">Default Options</label> |
|||
<div class="uk-grid-small" uk-grid> |
|||
<input |
|||
class="uk-input uk-width-1-4 uk-margin-right" |
|||
placeholder="key" |
|||
bind:value={optKey} /> |
|||
<input |
|||
class="uk-input uk-width-1-4 uk-margin-right" |
|||
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> |
|||
|
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
|
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
padding: 2rem; |
|||
border-radius: 2rem; |
|||
} |
|||
} |
|||
|
|||
.uk-grid-small { |
|||
.uk-grid-small { |
|||
padding: 1rem; |
|||
} |
|||
} |
|||
|
|||
.option-container { |
|||
.option-container { |
|||
border-style: dotted; |
|||
border-width: 1px; |
|||
border-color: var(--primary75); |
|||
padding: 3px; |
|||
margin-right: 5px; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,112 +1,111 @@ |
|||
<script> |
|||
|
|||
import getIcon from "../common/icon"; |
|||
import {store} from "../builderStore"; |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import ActionView from "./ActionView.svelte"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
import {pipe} from "../common/core"; |
|||
import {keys, map, join} from "lodash/fp"; |
|||
|
|||
export let editingActionIsNew = false; |
|||
export let editingAction = null; |
|||
export let onActionEdit = (action) => {}; |
|||
export let onActionDelete = (action) => {}; |
|||
export let onActionSave = (action) => {}; |
|||
export let onActionCancel = () => {}; |
|||
|
|||
$: isEditing = (editingAction !== null); |
|||
|
|||
let actionsArray = []; |
|||
store.subscribe(s => { |
|||
actionsArray = pipe(s.actions, [ |
|||
keys, |
|||
map(k => s.actions[k]) |
|||
]); |
|||
}); |
|||
|
|||
let getDefaultOptionsHtml = defaultOptions => |
|||
import getIcon from "../common/icon" |
|||
import { store } from "../builderStore" |
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import ActionView from "./ActionView.svelte" |
|||
import Modal from "../common/Modal.svelte" |
|||
import { pipe } from "../common/core" |
|||
import { keys, map, join } from "lodash/fp" |
|||
|
|||
export let editingActionIsNew = false |
|||
export let editingAction = null |
|||
export let onActionEdit = action => {} |
|||
export let onActionDelete = action => {} |
|||
export let onActionSave = action => {} |
|||
export let onActionCancel = () => {} |
|||
|
|||
$: isEditing = editingAction !== null |
|||
|
|||
let actionsArray = [] |
|||
store.subscribe(s => { |
|||
actionsArray = pipe(s.actions, [keys, map(k => s.actions[k])]) |
|||
}) |
|||
|
|||
let getDefaultOptionsHtml = defaultOptions => |
|||
pipe(defaultOptions, [ |
|||
keys, |
|||
map(k => `<span style="color:var(--slate)">${k}: </span>${JSON.stringify(defaultOptions[k])}`), |
|||
join("<br>") |
|||
]); |
|||
|
|||
|
|||
let actionEditingFinished = (action) => { |
|||
|
|||
if(action) { |
|||
onActionSave(action) |
|||
keys, |
|||
map( |
|||
k => |
|||
`<span style="color:var(--slate)">${k}: </span>${JSON.stringify( |
|||
defaultOptions[k] |
|||
)}` |
|||
), |
|||
join("<br>"), |
|||
]) |
|||
|
|||
let actionEditingFinished = action => { |
|||
if (action) { |
|||
onActionSave(action) |
|||
} else { |
|||
onActionCancel(); |
|||
onActionCancel() |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<h3 class="title">Actions</h3> |
|||
|
|||
{#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> |
|||
<tr> |
|||
<th >Description</th> |
|||
<th>Behaviour Source</th> |
|||
<th>Behaviour Name</th> |
|||
<th>Default Options</th> |
|||
<th></th> |
|||
</tr> |
|||
<tr> |
|||
<th>Description</th> |
|||
<th>Behaviour Source</th> |
|||
<th>Behaviour Name</th> |
|||
<th>Default Options</th> |
|||
<th /> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{#each actionsArray as action} |
|||
{#each actionsArray as action} |
|||
<tr> |
|||
<td class="table-content">{action.name}</td> |
|||
<td class="table-content">{action.behaviourSource}</td> |
|||
<td class="table-content">{action.behaviourName}</td> |
|||
<td class="table-content">{@html getDefaultOptionsHtml(action.initialOptions)}</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onActionEdit(action)}>{@html getIcon("edit")}</span> |
|||
<span on:click={() => onActionDelete(action)}>{@html getIcon("trash")}</span> |
|||
</td> |
|||
<td class="table-content">{action.name}</td> |
|||
<td class="table-content">{action.behaviourSource}</td> |
|||
<td class="table-content">{action.behaviourName}</td> |
|||
<td class="table-content"> |
|||
{@html getDefaultOptionsHtml(action.initialOptions)} |
|||
</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onActionEdit(action)}> |
|||
{@html getIcon('edit')} |
|||
</span> |
|||
<span on:click={() => onActionDelete(action)}> |
|||
{@html getIcon('trash')} |
|||
</span> |
|||
</td> |
|||
</tr> |
|||
{/each} |
|||
{/each} |
|||
</tbody> |
|||
</table> |
|||
{:else} |
|||
(no actions added) |
|||
{/if} |
|||
|
|||
</table> |
|||
{:else}(no actions added){/if} |
|||
|
|||
<Modal bind:isOpen={isEditing}> |
|||
{#if isEditing} |
|||
<ActionView action={editingAction} |
|||
allActions={$store.actions} |
|||
onFinished={actionEditingFinished} |
|||
isNew={editingActionIsNew}/> |
|||
{/if} |
|||
{#if isEditing} |
|||
<ActionView |
|||
action={editingAction} |
|||
allActions={$store.actions} |
|||
onFinished={actionEditingFinished} |
|||
isNew={editingActionIsNew} /> |
|||
{/if} |
|||
</Modal> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.edit-button { |
|||
cursor:pointer; |
|||
.edit-button { |
|||
cursor: pointer; |
|||
color: var(--secondary25); |
|||
} |
|||
} |
|||
|
|||
tr:hover .edit-button { |
|||
tr:hover .edit-button { |
|||
color: var(--secondary75); |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
.title { |
|||
margin: 3rem 0rem 0rem 0rem; |
|||
font-weight: 700; |
|||
} |
|||
} |
|||
|
|||
.table-content { |
|||
.table-content { |
|||
font-weight: 500; |
|||
font-size: .9rem; |
|||
} |
|||
|
|||
</style> |
|||
font-size: 0.9rem; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,129 +1,130 @@ |
|||
<script> |
|||
|
|||
import getIcon from "../common/icon"; |
|||
import {store} from "../builderStore"; |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import Actions from "./Actions.svelte"; |
|||
import Triggers from "./Triggers.svelte"; |
|||
import {getNewAction, getNewTrigger} from "../common/core"; |
|||
|
|||
let editingAction = null; |
|||
let editingActionIsNew = true; |
|||
let editingTrigger = null; |
|||
let editingTriggerIsNew = true; |
|||
|
|||
let getDefaultOptionsHtml = defaultOptions => |
|||
import getIcon from "../common/icon" |
|||
import { store } from "../builderStore" |
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import Actions from "./Actions.svelte" |
|||
import Triggers from "./Triggers.svelte" |
|||
import { getNewAction, getNewTrigger } from "../common/core" |
|||
|
|||
let editingAction = null |
|||
let editingActionIsNew = true |
|||
let editingTrigger = null |
|||
let editingTriggerIsNew = true |
|||
|
|||
let getDefaultOptionsHtml = defaultOptions => |
|||
pipe(defaultOptions, [ |
|||
keys, |
|||
map(k => `<span style="color:var(--slate)">${k}: </span>${JSON.parse(typeOptions[k])}`), |
|||
join("<br>") |
|||
]); |
|||
|
|||
let onActionEdit = (action) => { |
|||
editingAction = action; |
|||
editingActionIsNew = false; |
|||
} |
|||
|
|||
let newAction = () => { |
|||
editingAction = getNewAction(); |
|||
editingActionIsNew = true; |
|||
} |
|||
|
|||
let onActionDelete = (action) => { |
|||
store.deleteAction(action); |
|||
} |
|||
|
|||
let deleteTrigger = () => {} |
|||
|
|||
let editTrigger = (trigger) => { |
|||
editingTrigger = trigger; |
|||
editingTriggerIsNew = false; |
|||
} |
|||
|
|||
let newTrigger = () => { |
|||
editingTrigger = getNewTrigger(); |
|||
editingTriggerIsNew = true; |
|||
} |
|||
|
|||
let onActionSave = action => { |
|||
store.saveAction( |
|||
action, |
|||
editingActionIsNew, |
|||
editingAction); |
|||
|
|||
editingAction = null; |
|||
} |
|||
|
|||
let onActionCancel = () => { |
|||
editingAction = null; |
|||
} |
|||
|
|||
let onTriggerSave = trigger => { |
|||
store.saveTrigger( |
|||
trigger, |
|||
editingTriggerIsNew, |
|||
editingTrigger); |
|||
|
|||
editingTrigger = null; |
|||
} |
|||
|
|||
let onTriggerCancel = () => { |
|||
editingTrigger = null; |
|||
} |
|||
|
|||
let onTriggerEdit = (trigger) => { |
|||
editingTrigger = trigger; |
|||
editingTriggerIsNew = false; |
|||
} |
|||
|
|||
|
|||
let onTriggerDelete = (trigger) => { |
|||
store.deleteTrigger(trigger); |
|||
} |
|||
|
|||
keys, |
|||
map( |
|||
k => |
|||
`<span style="color:var(--slate)">${k}: </span>${JSON.parse( |
|||
typeOptions[k] |
|||
)}` |
|||
), |
|||
join("<br>"), |
|||
]) |
|||
|
|||
let onActionEdit = action => { |
|||
editingAction = action |
|||
editingActionIsNew = false |
|||
} |
|||
|
|||
let newAction = () => { |
|||
editingAction = getNewAction() |
|||
editingActionIsNew = true |
|||
} |
|||
|
|||
let onActionDelete = action => { |
|||
store.deleteAction(action) |
|||
} |
|||
|
|||
let deleteTrigger = () => {} |
|||
|
|||
let editTrigger = trigger => { |
|||
editingTrigger = trigger |
|||
editingTriggerIsNew = false |
|||
} |
|||
|
|||
let newTrigger = () => { |
|||
editingTrigger = getNewTrigger() |
|||
editingTriggerIsNew = true |
|||
} |
|||
|
|||
let onActionSave = action => { |
|||
store.saveAction(action, editingActionIsNew, editingAction) |
|||
|
|||
editingAction = null |
|||
} |
|||
|
|||
let onActionCancel = () => { |
|||
editingAction = null |
|||
} |
|||
|
|||
let onTriggerSave = trigger => { |
|||
store.saveTrigger(trigger, editingTriggerIsNew, editingTrigger) |
|||
|
|||
editingTrigger = null |
|||
} |
|||
|
|||
let onTriggerCancel = () => { |
|||
editingTrigger = null |
|||
} |
|||
|
|||
let onTriggerEdit = trigger => { |
|||
editingTrigger = trigger |
|||
editingTriggerIsNew = false |
|||
} |
|||
|
|||
let onTriggerDelete = trigger => { |
|||
store.deleteTrigger(trigger) |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="actions-header"> |
|||
<ButtonGroup> |
|||
<Button color="secondary" |
|||
grouped |
|||
on:click={newAction}>Create New Action</Button> |
|||
<Button color="tertiary" |
|||
grouped |
|||
on:click={newTrigger}>Create New Trigger</Button> |
|||
</ButtonGroup> |
|||
</div> |
|||
|
|||
<div class="node-view"> |
|||
<Actions {editingActionIsNew} {editingAction} |
|||
{onActionEdit} {onActionDelete} {onActionSave} |
|||
{onActionCancel} /> |
|||
|
|||
<Triggers {editingTriggerIsNew} {editingTrigger} |
|||
{onTriggerEdit} {onTriggerDelete} {onTriggerSave} |
|||
{onTriggerCancel} /> |
|||
</div> |
|||
<div class="actions-header"> |
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={newAction}> |
|||
Create New Action |
|||
</Button> |
|||
<Button color="tertiary" grouped on:click={newTrigger}> |
|||
Create New Trigger |
|||
</Button> |
|||
</ButtonGroup> |
|||
</div> |
|||
|
|||
<div class="node-view"> |
|||
<Actions |
|||
{editingActionIsNew} |
|||
{editingAction} |
|||
{onActionEdit} |
|||
{onActionDelete} |
|||
{onActionSave} |
|||
{onActionCancel} /> |
|||
|
|||
<Triggers |
|||
{editingTriggerIsNew} |
|||
{editingTrigger} |
|||
{onTriggerEdit} |
|||
{onTriggerDelete} |
|||
{onTriggerSave} |
|||
{onTriggerCancel} /> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
position: relative; |
|||
padding: 1.5rem; |
|||
} |
|||
|
|||
} |
|||
|
|||
.actions-header { |
|||
.actions-header { |
|||
flex: 0 1 auto; |
|||
} |
|||
} |
|||
|
|||
.node-view { |
|||
.node-view { |
|||
overflow-y: auto; |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,68 +1,71 @@ |
|||
<script> |
|||
|
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import CodeArea from "../common/CodeArea.svelte"; |
|||
import {cloneDeep, filter, keys, some, |
|||
map, isUndefined} from "lodash/fp"; |
|||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
|||
import {validateTriggers, pipe, events} from "../common/core"; |
|||
import getIcon from "../common/icon"; |
|||
|
|||
export let trigger; |
|||
export let onFinished = (action) => {}; |
|||
export let allTriggers; |
|||
export let allActions; |
|||
export let isNew = true; |
|||
|
|||
let clonedTrigger = cloneDeep(trigger); |
|||
let errors = []; |
|||
$: actionNames = map(a => a.name)(allActions); |
|||
|
|||
let cancel = () => onFinished(); |
|||
let save = () => { |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import CodeArea from "../common/CodeArea.svelte" |
|||
import { cloneDeep, filter, keys, some, map, isUndefined } from "lodash/fp" |
|||
import ErrorsBox from "../common/ErrorsBox.svelte" |
|||
import { validateTriggers, pipe, events } from "../common/core" |
|||
import getIcon from "../common/icon" |
|||
|
|||
export let trigger |
|||
export let onFinished = action => {} |
|||
export let allTriggers |
|||
export let allActions |
|||
export let isNew = true |
|||
|
|||
let clonedTrigger = cloneDeep(trigger) |
|||
let errors = [] |
|||
$: actionNames = map(a => a.name)(allActions) |
|||
|
|||
let cancel = () => onFinished() |
|||
let save = () => { |
|||
const newTriggersList = [ |
|||
...pipe(allTriggers ,[filter(t => t !== trigger)]), |
|||
clonedTrigger] |
|||
|
|||
errors = validateTriggers(newTriggersList, allActions); |
|||
...pipe(allTriggers, [filter(t => t !== trigger)]), |
|||
clonedTrigger, |
|||
] |
|||
|
|||
const test = map(t =>(!t.actionName || some(a => a.name === t.actionName)(allActions)))(newTriggersList) |
|||
errors = validateTriggers(newTriggersList, allActions) |
|||
|
|||
if(errors.length === 0) |
|||
onFinished(clonedTrigger); |
|||
} |
|||
const test = map( |
|||
t => !t.actionName || some(a => a.name === t.actionName)(allActions) |
|||
)(newTriggersList) |
|||
|
|||
if (errors.length === 0) onFinished(clonedTrigger) |
|||
} |
|||
</script> |
|||
|
|||
<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" |
|||
options={["",...events]} |
|||
bind:selected={clonedTrigger.eventName} /> |
|||
<Dropdown label="Action" |
|||
options={["",...actionNames]} |
|||
bind:selected={clonedTrigger.actionName} /> |
|||
<CodeArea label="Condition (javascript)" |
|||
bind:text={clonedTrigger.condition} /> |
|||
<CodeArea label="Action Options Creator (javascript)" |
|||
bind:text={clonedTrigger.optionsCreator} /> |
|||
<Dropdown |
|||
label="Event" |
|||
options={['', ...events]} |
|||
bind:selected={clonedTrigger.eventName} /> |
|||
<Dropdown |
|||
label="Action" |
|||
options={['', ...actionNames]} |
|||
bind:selected={clonedTrigger.actionName} /> |
|||
<CodeArea |
|||
label="Condition (javascript)" |
|||
bind:text={clonedTrigger.condition} /> |
|||
<CodeArea |
|||
label="Action Options Creator (javascript)" |
|||
bind:text={clonedTrigger.optionsCreator} /> |
|||
|
|||
</form> |
|||
</form> |
|||
|
|||
<ButtonGroup> |
|||
<Button color="primary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
<ButtonGroup> |
|||
<Button color="primary" grouped on:click={save}>Save</Button> |
|||
<Button color="tertiary" grouped on:click={cancel}>Cancel</Button> |
|||
</ButtonGroup> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,94 +1,90 @@ |
|||
<script> |
|||
|
|||
import {store} from "../builderStore"; |
|||
import getIcon from "../common/icon"; |
|||
import Button from "../common/Button.svelte"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
import TriggerView from "./TriggerView.svelte"; |
|||
|
|||
|
|||
export let editingTrigger = null; |
|||
export let editingTriggerIsNew = true; |
|||
export let onTriggerEdit = (trigger) => {}; |
|||
export let onTriggerDelete = (trigger) => {}; |
|||
export let onTriggerSave = (trigger) => {}; |
|||
export let onTriggerCancel = () => {}; |
|||
|
|||
$: isEditing = (editingTrigger !== null); |
|||
|
|||
let triggerEditingFinished = (trigger) => { |
|||
|
|||
if(trigger) { |
|||
onTriggerSave(trigger) |
|||
import { store } from "../builderStore" |
|||
import getIcon from "../common/icon" |
|||
import Button from "../common/Button.svelte" |
|||
import Modal from "../common/Modal.svelte" |
|||
import TriggerView from "./TriggerView.svelte" |
|||
|
|||
export let editingTrigger = null |
|||
export let editingTriggerIsNew = true |
|||
export let onTriggerEdit = trigger => {} |
|||
export let onTriggerDelete = trigger => {} |
|||
export let onTriggerSave = trigger => {} |
|||
export let onTriggerCancel = () => {} |
|||
|
|||
$: isEditing = editingTrigger !== null |
|||
|
|||
let triggerEditingFinished = trigger => { |
|||
if (trigger) { |
|||
onTriggerSave(trigger) |
|||
} else { |
|||
onTriggerCancel(); |
|||
onTriggerCancel() |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<h3 class="title">Triggers</h3> |
|||
|
|||
{#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> |
|||
<tr> |
|||
<th>Event</th> |
|||
<th>Action</th> |
|||
<th>Condition</th> |
|||
<th>Create Options</th> |
|||
<th></th> |
|||
</tr> |
|||
<tr> |
|||
<th>Event</th> |
|||
<th>Action</th> |
|||
<th>Condition</th> |
|||
<th>Create Options</th> |
|||
<th /> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{#each $store.triggers as trigger} |
|||
{#each $store.triggers as trigger} |
|||
<tr> |
|||
<td class="table-content">{trigger.eventName}</td> |
|||
<td class="table-content">{trigger.actionName}</td> |
|||
<td class="table-content">{trigger.condition}</td> |
|||
<td class="table-content">{trigger.optionsCreator}</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onTriggerEdit(trigger)}>{@html getIcon("edit")}</span> |
|||
<span on:click={() => onTriggerDelete(trigger)}>{@html getIcon("trash")}</span> |
|||
</td> |
|||
<td class="table-content">{trigger.eventName}</td> |
|||
<td class="table-content">{trigger.actionName}</td> |
|||
<td class="table-content">{trigger.condition}</td> |
|||
<td class="table-content">{trigger.optionsCreator}</td> |
|||
<td class="edit-button"> |
|||
<span on:click={() => onTriggerEdit(trigger)}> |
|||
{@html getIcon('edit')} |
|||
</span> |
|||
<span on:click={() => onTriggerDelete(trigger)}> |
|||
{@html getIcon('trash')} |
|||
</span> |
|||
</td> |
|||
</tr> |
|||
{/each} |
|||
{/each} |
|||
</tbody> |
|||
</table> |
|||
{:else} |
|||
(no triggers added) |
|||
{/if} |
|||
|
|||
</table> |
|||
{:else}(no triggers added){/if} |
|||
|
|||
<Modal bind:isOpen={isEditing}> |
|||
{#if isEditing} |
|||
<TriggerView trigger={editingTrigger} |
|||
allActions={$store.actions} |
|||
allTriggers={$store.triggers} |
|||
onFinished={triggerEditingFinished} |
|||
isNew={editingTriggerIsNew}/> |
|||
{/if} |
|||
{#if isEditing} |
|||
<TriggerView |
|||
trigger={editingTrigger} |
|||
allActions={$store.actions} |
|||
allTriggers={$store.triggers} |
|||
onFinished={triggerEditingFinished} |
|||
isNew={editingTriggerIsNew} /> |
|||
{/if} |
|||
</Modal> |
|||
|
|||
<style> |
|||
|
|||
.edit-button { |
|||
cursor:pointer; |
|||
.edit-button { |
|||
cursor: pointer; |
|||
color: var(--secondary25); |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
.title { |
|||
margin: 3rem 0rem 0rem 0rem; |
|||
font-weight: 700; |
|||
} |
|||
} |
|||
|
|||
.table-content { |
|||
.table-content { |
|||
font-weight: 500; |
|||
font-size: .9rem; |
|||
} |
|||
font-size: 0.9rem; |
|||
} |
|||
|
|||
tr:hover .edit-button { |
|||
tr:hover .edit-button { |
|||
color: var(--secondary75); |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,17 +1,20 @@ |
|||
const apiCall = (method) => (url, body) => |
|||
fetch(url, { |
|||
method: method, |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
body: body && JSON.stringify(body), |
|||
}); |
|||
const apiCall = method => (url, body) => |
|||
fetch(url, { |
|||
method: method, |
|||
headers: { |
|||
"Content-Type": "application/json", |
|||
}, |
|||
body: body && JSON.stringify(body), |
|||
}) |
|||
|
|||
const post = apiCall("POST"); |
|||
const get = apiCall("GET"); |
|||
const patch = apiCall("PATCH"); |
|||
const del = apiCall("DELETE"); |
|||
const post = apiCall("POST") |
|||
const get = apiCall("GET") |
|||
const patch = apiCall("PATCH") |
|||
const del = apiCall("DELETE") |
|||
|
|||
export default { |
|||
post, get, patch, delete:del |
|||
}; |
|||
post, |
|||
get, |
|||
patch, |
|||
delete: del, |
|||
} |
|||
|
|||
@ -1,35 +1,33 @@ |
|||
const buildCodeForSingleScreen = screen => { |
|||
let code = "" |
|||
const walkProps = props => { |
|||
if (props._code && props._code.trim().length > 0) { |
|||
code += buildComponentCode(props) |
|||
} |
|||
|
|||
if (!props._children) return |
|||
|
|||
const buildCodeForSingleScreen = (screen) => { |
|||
let code = ""; |
|||
const walkProps = (props) => { |
|||
if(props._code && props._code.trim().length > 0) { |
|||
code += buildComponentCode(props) |
|||
} |
|||
|
|||
if(!props._children) return; |
|||
|
|||
for(let child of props._children) { |
|||
walkProps(child); |
|||
} |
|||
for (let child of props._children) { |
|||
walkProps(child) |
|||
} |
|||
} |
|||
|
|||
walkProps(screen.props); |
|||
walkProps(screen.props) |
|||
|
|||
return code; |
|||
return code |
|||
} |
|||
|
|||
export const buildCodeForScreens = screens => { |
|||
let allfunctions = ""; |
|||
for(let screen of screens) { |
|||
allfunctions += buildCodeForSingleScreen(screen); |
|||
} |
|||
|
|||
return (`return ({ ${allfunctions} });`); |
|||
let allfunctions = "" |
|||
for (let screen of screens) { |
|||
allfunctions += buildCodeForSingleScreen(screen) |
|||
} |
|||
|
|||
return `return ({ ${allfunctions} });` |
|||
} |
|||
|
|||
const buildComponentCode = (componentProps) => |
|||
`"${componentProps._id}" : (render, context) => {
|
|||
const buildComponentCode = componentProps => |
|||
`"${componentProps._id}" : (render, context) => {
|
|||
${componentProps._code} |
|||
}, |
|||
`;
|
|||
` |
|||
|
|||
@ -1,12 +1,12 @@ |
|||
import {createNewHierarchy} from "../common/core"; |
|||
import { createNewHierarchy } from "../common/core" |
|||
|
|||
export const createPackage = (packageInfo, store) => { |
|||
packageInfo.createNewPackage(""); |
|||
const root = createNewHierarchy(); |
|||
store.importAppDefinition({ |
|||
hierarchy:root, |
|||
actions:[], |
|||
triggers:[], |
|||
accessLevels: {version:0, levels:[]} |
|||
}); |
|||
}; |
|||
packageInfo.createNewPackage("") |
|||
const root = createNewHierarchy() |
|||
store.importAppDefinition({ |
|||
hierarchy: root, |
|||
actions: [], |
|||
triggers: [], |
|||
accessLevels: { version: 0, levels: [] }, |
|||
}) |
|||
} |
|||
|
|||
@ -1,118 +1,113 @@ |
|||
import { filter, map, reduce, toPairs } from "lodash/fp"; |
|||
import { pipe } from "../common/core"; |
|||
import { filter, map, reduce, toPairs } from "lodash/fp" |
|||
import { pipe } from "../common/core" |
|||
|
|||
const self = n => n; |
|||
const join_with = delimiter => a => a.join(delimiter); |
|||
const empty_string_to_unset = s => s.length ? s : "0"; |
|||
const add_suffix = suffix => s => s + suffix; |
|||
const self = n => n |
|||
const join_with = delimiter => a => a.join(delimiter) |
|||
const empty_string_to_unset = s => (s.length ? s : "0") |
|||
const add_suffix = suffix => s => s + suffix |
|||
|
|||
export const make_margin = (values) => pipe(values, [ |
|||
map(empty_string_to_unset), |
|||
map(add_suffix('px')), |
|||
join_with(' ') |
|||
]); |
|||
|
|||
const tap = message => x => { |
|||
console.log(x); |
|||
return x; |
|||
} |
|||
export const make_margin = values => |
|||
pipe(values, [ |
|||
map(empty_string_to_unset), |
|||
map(add_suffix("px")), |
|||
join_with(" "), |
|||
]) |
|||
|
|||
const css_map = { |
|||
templaterows: { |
|||
name: 'grid-template-columns', |
|||
generate: self |
|||
}, |
|||
templatecolumns: { |
|||
name: 'grid-template-rows', |
|||
generate: self |
|||
}, |
|||
gridarea: { |
|||
name: 'grid-area', |
|||
generate: make_margin |
|||
}, |
|||
gap: { |
|||
name: 'grid-gap', |
|||
generate: n => `${n}px` |
|||
}, |
|||
columnstart: { |
|||
name: 'grid-column-start', |
|||
generate: self |
|||
}, |
|||
columnend: { |
|||
name: 'grid-column-end', |
|||
generate: self |
|||
}, |
|||
rowstart: { |
|||
name: 'grid-row-start', |
|||
generate: self |
|||
}, |
|||
rowend: { |
|||
name: 'grid-row-end', |
|||
generate: self |
|||
}, |
|||
padding: { |
|||
name: 'padding', |
|||
generate: make_margin |
|||
}, |
|||
margin: { |
|||
name: 'margin', |
|||
generate: make_margin |
|||
}, |
|||
zindex: { |
|||
name: 'z-index', |
|||
generate: self |
|||
} |
|||
templaterows: { |
|||
name: "grid-template-columns", |
|||
generate: self, |
|||
}, |
|||
templatecolumns: { |
|||
name: "grid-template-rows", |
|||
generate: self, |
|||
}, |
|||
gridarea: { |
|||
name: "grid-area", |
|||
generate: make_margin, |
|||
}, |
|||
gap: { |
|||
name: "grid-gap", |
|||
generate: n => `${n}px`, |
|||
}, |
|||
columnstart: { |
|||
name: "grid-column-start", |
|||
generate: self, |
|||
}, |
|||
columnend: { |
|||
name: "grid-column-end", |
|||
generate: self, |
|||
}, |
|||
rowstart: { |
|||
name: "grid-row-start", |
|||
generate: self, |
|||
}, |
|||
rowend: { |
|||
name: "grid-row-end", |
|||
generate: self, |
|||
}, |
|||
padding: { |
|||
name: "padding", |
|||
generate: make_margin, |
|||
}, |
|||
margin: { |
|||
name: "margin", |
|||
generate: make_margin, |
|||
}, |
|||
zindex: { |
|||
name: "z-index", |
|||
generate: self, |
|||
}, |
|||
} |
|||
|
|||
export const generate_rule = ([name, values]) => |
|||
`${css_map[name].name}: ${css_map[name].generate(values)};` |
|||
`${css_map[name].name}: ${css_map[name].generate(values)};` |
|||
|
|||
const handle_grid = (acc, [name, value]) => { |
|||
let tmp = []; |
|||
let tmp = [] |
|||
|
|||
if (name === 'row' || name === 'column') { |
|||
if (value[0]) tmp.push([`${name}start`, value[0]]); |
|||
if (value[1]) tmp.push([`${name}end`, value[1]]); |
|||
return acc.concat(tmp) |
|||
} |
|||
if (name === "row" || name === "column") { |
|||
if (value[0]) tmp.push([`${name}start`, value[0]]) |
|||
if (value[1]) tmp.push([`${name}end`, value[1]]) |
|||
return acc.concat(tmp) |
|||
} |
|||
|
|||
return acc.concat([[name, value]]); |
|||
return acc.concat([[name, value]]) |
|||
} |
|||
|
|||
const object_to_css_string = [ |
|||
toPairs, |
|||
reduce(handle_grid, []), |
|||
filter(v => Array.isArray(v[1]) ? v[1].some(s => s.length) : v[1].length), |
|||
map(generate_rule), |
|||
join_with('\n'), |
|||
]; |
|||
toPairs, |
|||
reduce(handle_grid, []), |
|||
filter(v => (Array.isArray(v[1]) ? v[1].some(s => s.length) : v[1].length)), |
|||
map(generate_rule), |
|||
join_with("\n"), |
|||
] |
|||
|
|||
export const generate_css = ({ layout, position }) => { |
|||
let _layout = pipe(layout, object_to_css_string); |
|||
_layout = _layout.length ? _layout + "\ndisplay: grid;" : _layout; |
|||
let _layout = pipe(layout, object_to_css_string) |
|||
_layout = _layout.length ? _layout + "\ndisplay: grid;" : _layout |
|||
|
|||
return { |
|||
layout: _layout, |
|||
position: pipe(position, object_to_css_string) |
|||
} |
|||
return { |
|||
layout: _layout, |
|||
position: pipe(position, object_to_css_string), |
|||
} |
|||
} |
|||
|
|||
const apply_class = (id, name, styles) => `.${name}-${id} {\n${styles}\n}`; |
|||
|
|||
export const generate_screen_css = (component_array) => { |
|||
let styles = ""; |
|||
const apply_class = (id, name, styles) => `.${name}-${id} {\n${styles}\n}` |
|||
|
|||
for (let i = 0; i < component_array.length; i += 1) { |
|||
const { _styles, _id, _children } = component_array[i]; |
|||
const { layout, position } = generate_css(_styles); |
|||
export const generate_screen_css = component_array => { |
|||
let styles = "" |
|||
|
|||
styles += apply_class(_id, 'pos', position) + "\n"; |
|||
styles += apply_class(_id, 'lay', layout) + "\n"; |
|||
for (let i = 0; i < component_array.length; i += 1) { |
|||
const { _styles, _id, _children } = component_array[i] |
|||
const { layout, position } = generate_css(_styles) |
|||
|
|||
if (_children && _children.length) { |
|||
styles += generate_screen_css(_children) + "\n"; |
|||
} |
|||
styles += apply_class(_id, "pos", position) + "\n" |
|||
styles += apply_class(_id, "lay", layout) + "\n" |
|||
|
|||
} |
|||
return styles.trim(); |
|||
if (_children && _children.length) { |
|||
styles += generate_screen_css(_children) + "\n" |
|||
} |
|||
} |
|||
return styles.trim() |
|||
} |
|||
|
|||
@ -1,29 +1,27 @@ |
|||
import {createPackage} from "./createPackage"; |
|||
import getStore from "./store"; |
|||
import { createPackage } from "./createPackage" |
|||
import getStore from "./store" |
|||
|
|||
export const store = getStore(); |
|||
export const store = getStore() |
|||
|
|||
export const createNewPackage = () => |
|||
createPackage(packageInfo, store); |
|||
export const createNewPackage = () => createPackage(packageInfo, store) |
|||
|
|||
export const initialise = async () => { |
|||
try { |
|||
setupRouter(store); |
|||
await store.initialise(); |
|||
} catch(err) { |
|||
console.log(err); |
|||
} |
|||
|
|||
} |
|||
try { |
|||
setupRouter(store) |
|||
await store.initialise() |
|||
} catch (err) { |
|||
console.log(err) |
|||
} |
|||
} |
|||
|
|||
const setupRouter = (writable) => { |
|||
const pushState = history.pushState; |
|||
history.pushState = () => { |
|||
pushState.apply(history, arguments); |
|||
//fireEvents('pushState', arguments);
|
|||
writable.initialise(); |
|||
} |
|||
window.addEventListener('hashchange',()=>{ |
|||
writable.initialise(); |
|||
}) |
|||
const setupRouter = writable => { |
|||
const pushState = history.pushState |
|||
history.pushState = () => { |
|||
pushState.apply(history, arguments) |
|||
//fireEvents('pushState', arguments);
|
|||
writable.initialise() |
|||
} |
|||
window.addEventListener("hashchange", () => { |
|||
writable.initialise() |
|||
}) |
|||
} |
|||
|
|||
@ -1,50 +1,47 @@ |
|||
import { map } from "lodash/fp"; |
|||
import { map } from "lodash/fp" |
|||
|
|||
export const loadLibs = async (appName, appPackage) => { |
|||
const allLibraries = {} |
|||
for (let lib of appPackage.pages.componentLibraries) { |
|||
const libModule = await import(makeLibraryUrl(appName, lib)) |
|||
allLibraries[lib] = libModule |
|||
} |
|||
|
|||
const allLibraries = {}; |
|||
for(let lib of appPackage.pages.componentLibraries) { |
|||
const libModule = await import(makeLibraryUrl(appName, lib)); |
|||
allLibraries[lib] = libModule; |
|||
} |
|||
|
|||
return allLibraries; |
|||
return allLibraries |
|||
} |
|||
|
|||
export const loadGeneratorLibs = async (appName, appPackage) => { |
|||
const allGeneratorLibs = {} |
|||
for (let lib of appPackage.pages.componentLibraries) { |
|||
const generatorModule = await import(makeGeneratorLibraryUrl(appName, lib)) |
|||
allGeneratorLibs[lib] = generatorModule |
|||
} |
|||
|
|||
const allGeneratorLibs = {}; |
|||
for(let lib of appPackage.pages.componentLibraries) { |
|||
const generatorModule = await import(makeGeneratorLibraryUrl(appName, lib)); |
|||
allGeneratorLibs[lib] = generatorModule; |
|||
} |
|||
|
|||
return allGeneratorLibs; |
|||
return allGeneratorLibs |
|||
} |
|||
|
|||
export const loadLibUrls = (appName, appPackage) => { |
|||
const allLibraries = [] |
|||
for (let lib of appPackage.pages.componentLibraries) { |
|||
const libUrl = makeLibraryUrl(appName, lib) |
|||
allLibraries.push({ libName: lib, importPath: libUrl }) |
|||
} |
|||
|
|||
const allLibraries = []; |
|||
for(let lib of appPackage.pages.componentLibraries) { |
|||
const libUrl = makeLibraryUrl(appName, lib); |
|||
allLibraries.push({libName:lib, importPath:libUrl}); |
|||
} |
|||
|
|||
return allLibraries; |
|||
return allLibraries |
|||
} |
|||
|
|||
export const loadLib = async (appName, lib, allLibs) => { |
|||
allLibs[lib] = await import(makeLibraryUrl(appName, lib)); |
|||
return allLibs; |
|||
allLibs[lib] = await import(makeLibraryUrl(appName, lib)) |
|||
return allLibs |
|||
} |
|||
|
|||
export const loadGeneratorLib = async (appName, lib, allGeneratorLibs) => { |
|||
allGeneratorLibs[lib] = await import(makeGeneratorLibraryUrl(appName, lib)); |
|||
return allGeneratorLibs; |
|||
allGeneratorLibs[lib] = await import(makeGeneratorLibraryUrl(appName, lib)) |
|||
return allGeneratorLibs |
|||
} |
|||
|
|||
export const makeLibraryUrl = (appName, lib) => |
|||
`/_builder/${appName}/componentlibrary?lib=${encodeURI(lib)}` |
|||
export const makeLibraryUrl = (appName, lib) => |
|||
`/_builder/${appName}/componentlibrary?lib=${encodeURI(lib)}` |
|||
|
|||
export const makeGeneratorLibraryUrl = (appName, lib) => |
|||
`/_builder/${appName}/componentlibraryGenerators?lib=${encodeURI(lib)}` |
|||
export const makeGeneratorLibraryUrl = (appName, lib) => |
|||
`/_builder/${appName}/componentlibraryGenerators?lib=${encodeURI(lib)}` |
|||
|
|||
@ -1,799 +0,0 @@ |
|||
import { |
|||
hierarchy as hierarchyFunctions, |
|||
} from "../../../core/src"; |
|||
import { |
|||
filter, cloneDeep, sortBy, |
|||
map, last, keys, concat, keyBy, |
|||
find, isEmpty, values, |
|||
} from "lodash/fp"; |
|||
import { |
|||
pipe, getNode, validate, |
|||
constructHierarchy, templateApi |
|||
} from "../common/core"; |
|||
import { writable } from "svelte/store"; |
|||
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject" |
|||
import { buildPropsHierarchy } from "../userInterface/pagesParsing/buildPropsHierarchy" |
|||
import api from "./api"; |
|||
import { isRootComponent, getExactComponent } from "../userInterface/pagesParsing/searchComponents"; |
|||
import { rename } from "../userInterface/pagesParsing/renameScreen"; |
|||
import { |
|||
getNewComponentInfo, getScreenInfo, |
|||
} from "../userInterface/pagesParsing/createProps"; |
|||
import { |
|||
loadLibs, loadLibUrls, loadGeneratorLibs |
|||
} from "./loadComponentLibraries"; |
|||
<<<<<<< HEAD |
|||
import { buildCodeForScreens } from "./buildCodeForScreens"; |
|||
======= |
|||
import { uuid } from './uuid'; |
|||
import { generate_screen_css } from './generate_css'; |
|||
>>>>>>> master |
|||
|
|||
let appname = ""; |
|||
|
|||
export const getStore = () => { |
|||
|
|||
const initial = { |
|||
apps: [], |
|||
appname: "", |
|||
hierarchy: {}, |
|||
actions: [], |
|||
triggers: [], |
|||
pages: defaultPagesObject(), |
|||
mainUi: {}, |
|||
unauthenticatedUi: {}, |
|||
components: [], |
|||
currentFrontEndItem: null, |
|||
currentComponentInfo: null, |
|||
currentFrontEndType: "none", |
|||
currentPageName: "", |
|||
currentComponentProps: null, |
|||
currentNodeIsNew: false, |
|||
errors: [], |
|||
activeNav: "database", |
|||
isBackend: true, |
|||
hasAppPackage: false, |
|||
accessLevels: { version: 0, levels: [] }, |
|||
currentNode: null, |
|||
libraries: null, |
|||
showSettings: false, |
|||
useAnalytics: true, |
|||
}; |
|||
|
|||
const store = writable(initial); |
|||
|
|||
store.initialise = initialise(store, initial); |
|||
store.newChildRecord = newRecord(store, false); |
|||
store.newRootRecord = newRecord(store, true); |
|||
store.selectExistingNode = selectExistingNode(store); |
|||
store.newChildIndex = newIndex(store, false); |
|||
store.newRootIndex = newIndex(store, true); |
|||
store.saveCurrentNode = saveCurrentNode(store); |
|||
store.importAppDefinition = importAppDefinition(store); |
|||
store.deleteCurrentNode = deleteCurrentNode(store); |
|||
store.saveField = saveField(store); |
|||
store.deleteField = deleteField(store); |
|||
store.saveAction = saveAction(store); |
|||
store.deleteAction = deleteAction(store); |
|||
store.saveTrigger = saveTrigger(store); |
|||
store.deleteTrigger = deleteTrigger(store); |
|||
store.saveLevel = saveLevel(store); |
|||
store.deleteLevel = deleteLevel(store); |
|||
store.setActiveNav = setActiveNav(store); |
|||
store.saveScreen = saveScreen(store); |
|||
store.refreshComponents = refreshComponents(store); |
|||
store.addComponentLibrary = addComponentLibrary(store); |
|||
store.renameScreen = renameScreen(store); |
|||
store.deleteScreen = deleteScreen(store); |
|||
store.setCurrentScreen = setCurrentScreen(store); |
|||
store.setCurrentPage = setCurrentPage(store); |
|||
store.createScreen = createScreen(store); |
|||
store.removeComponentLibrary = removeComponentLibrary(store); |
|||
store.addStylesheet = addStylesheet(store); |
|||
store.removeStylesheet = removeStylesheet(store); |
|||
store.savePage = savePage(store); |
|||
store.showFrontend = showFrontend(store); |
|||
store.showBackend = showBackend(store); |
|||
store.showSettings = showSettings(store); |
|||
store.useAnalytics = useAnalytics(store); |
|||
store.createGeneratedComponents = createGeneratedComponents(store); |
|||
store.addChildComponent = addChildComponent(store); |
|||
store.selectComponent = selectComponent(store); |
|||
store.setComponentProp = setComponentProp(store); |
|||
store.setComponentStyle = setComponentStyle(store); |
|||
store.setComponentCode = setComponentCode(store); |
|||
return store; |
|||
} |
|||
|
|||
export default getStore; |
|||
|
|||
const initialise = (store, initial) => async () => { |
|||
|
|||
appname = window.location.hash |
|||
? last(window.location.hash.substr(1).split("/")) |
|||
: ""; |
|||
|
|||
if (!appname) { |
|||
initial.apps = await api.get(`/_builder/api/apps`).then(r => r.json()); |
|||
initial.hasAppPackage = false; |
|||
store.set(initial); |
|||
return initial; |
|||
} |
|||
|
|||
const pkg = await api.get(`/_builder/api/${appname}/appPackage`) |
|||
.then(r => r.json()); |
|||
|
|||
initial.libraries = await loadLibs(appname, pkg); |
|||
initial.generatorLibraries = await loadGeneratorLibs(appname, pkg); |
|||
initial.loadLibraryUrls = () => loadLibUrls(appname, pkg); |
|||
initial.appname = appname; |
|||
initial.pages = pkg.pages; |
|||
initial.hasAppPackage = true; |
|||
initial.hierarchy = pkg.appDefinition.hierarchy; |
|||
initial.accessLevels = pkg.accessLevels; |
|||
initial.screens = values(pkg.screens); |
|||
initial.generators = generatorsArray(pkg.components.generators); |
|||
initial.components = values(pkg.components.components); |
|||
initial.actions = values(pkg.appDefinition.actions); |
|||
initial.triggers = pkg.appDefinition.triggers; |
|||
|
|||
if (!!initial.hierarchy && !isEmpty(initial.hierarchy)) { |
|||
initial.hierarchy = constructHierarchy(initial.hierarchy); |
|||
const shadowHierarchy = createShadowHierarchy(initial.hierarchy); |
|||
if (initial.currentNode !== null) |
|||
initial.currentNode = getNode( |
|||
shadowHierarchy, initial.currentNode.nodeId |
|||
); |
|||
} |
|||
|
|||
store.set(initial); |
|||
return initial; |
|||
} |
|||
|
|||
const generatorsArray = generators => |
|||
pipe(generators, [ |
|||
keys, |
|||
filter(k => k !== "_lib"), |
|||
map(k => generators[k]) |
|||
]); |
|||
|
|||
|
|||
const showSettings = store => show => { |
|||
store.update(s => { |
|||
s.showSettings = !s.showSettings; |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const useAnalytics = store => useAnalytics => { |
|||
store.update(s => { |
|||
s.useAnalytics = !s.useAnalytics; |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const showBackend = store => () => { |
|||
store.update(s => { |
|||
s.isBackend = true; |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const showFrontend = store => () => { |
|||
store.update(s => { |
|||
s.isBackend = false; |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const newRecord = (store, useRoot) => () => { |
|||
store.update(s => { |
|||
s.currentNodeIsNew = true; |
|||
const shadowHierarchy = createShadowHierarchy(s.hierarchy); |
|||
parent = useRoot ? shadowHierarchy |
|||
: getNode( |
|||
shadowHierarchy, |
|||
s.currentNode.nodeId); |
|||
s.errors = []; |
|||
s.currentNode = templateApi(shadowHierarchy) |
|||
.getNewRecordTemplate(parent, "", true); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
|
|||
const selectExistingNode = (store) => (nodeId) => { |
|||
store.update(s => { |
|||
const shadowHierarchy = createShadowHierarchy(s.hierarchy); |
|||
s.currentNode = getNode( |
|||
shadowHierarchy, nodeId |
|||
); |
|||
s.currentNodeIsNew = false; |
|||
s.errors = []; |
|||
s.activeNav = "database"; |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const newIndex = (store, useRoot) => () => { |
|||
store.update(s => { |
|||
s.currentNodeIsNew = true; |
|||
s.errors = []; |
|||
const shadowHierarchy = createShadowHierarchy(s.hierarchy); |
|||
parent = useRoot ? shadowHierarchy |
|||
: getNode( |
|||
shadowHierarchy, |
|||
s.currentNode.nodeId); |
|||
|
|||
s.currentNode = templateApi(shadowHierarchy) |
|||
.getNewIndexTemplate(parent); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const saveCurrentNode = (store) => () => { |
|||
store.update(s => { |
|||
|
|||
const errors = validate.node(s.currentNode); |
|||
s.errors = errors; |
|||
if (errors.length > 0) { |
|||
return s; |
|||
} |
|||
|
|||
const parentNode = getNode( |
|||
s.hierarchy, s.currentNode.parent().nodeId); |
|||
|
|||
const existingNode = getNode( |
|||
s.hierarchy, s.currentNode.nodeId); |
|||
|
|||
let index = parentNode.children.length; |
|||
if (!!existingNode) { |
|||
// remove existing
|
|||
index = existingNode.parent().children.indexOf(existingNode); |
|||
existingNode.parent().children = pipe(existingNode.parent().children, [ |
|||
filter(c => c.nodeId !== existingNode.nodeId) |
|||
]); |
|||
} |
|||
|
|||
// should add node into existing hierarchy
|
|||
const cloned = cloneDeep(s.currentNode); |
|||
templateApi(s.hierarchy).constructNode( |
|||
parentNode, |
|||
cloned |
|||
); |
|||
|
|||
const newIndexOfchild = child => { |
|||
if (child === cloned) return index; |
|||
const currentIndex = parentNode.children.indexOf(child); |
|||
return currentIndex >= index ? currentIndex + 1 : currentIndex; |
|||
} |
|||
|
|||
parentNode.children = pipe(parentNode.children, [ |
|||
sortBy(newIndexOfchild) |
|||
]); |
|||
|
|||
if (!existingNode && s.currentNode.type === "record") { |
|||
const defaultIndex = templateApi(s.hierarchy) |
|||
.getNewIndexTemplate(cloned.parent()); |
|||
defaultIndex.name = `all_${cloned.collectionName}`; |
|||
defaultIndex.allowedRecordNodeIds = [cloned.nodeId]; |
|||
} |
|||
|
|||
s.currentNodeIsNew = false; |
|||
|
|||
savePackage(store, s); |
|||
|
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const importAppDefinition = store => appDefinition => { |
|||
store.update(s => { |
|||
s.hierarchy = appDefinition.hierarchy; |
|||
s.currentNode = appDefinition.hierarchy.children.length > 0 |
|||
? appDefinition.hierarchy.children[0] |
|||
: null; |
|||
s.actions = appDefinition.actions; |
|||
s.triggers = appDefinition.triggers; |
|||
s.currentNodeIsNew = false; |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const deleteCurrentNode = store => () => { |
|||
store.update(s => { |
|||
const nodeToDelete = getNode(s.hierarchy, s.currentNode.nodeId); |
|||
s.currentNode = hierarchyFunctions.isRoot(nodeToDelete.parent()) |
|||
? find(n => n != s.currentNode) |
|||
(s.hierarchy.children) |
|||
: nodeToDelete.parent(); |
|||
if (hierarchyFunctions.isRecord(nodeToDelete)) { |
|||
nodeToDelete.parent().children = filter(c => c.nodeId !== nodeToDelete.nodeId) |
|||
(nodeToDelete.parent().children); |
|||
} else { |
|||
nodeToDelete.parent().indexes = filter(c => c.nodeId !== nodeToDelete.nodeId) |
|||
(nodeToDelete.parent().indexes); |
|||
} |
|||
s.errors = []; |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const saveField = databaseStore => (field) => { |
|||
databaseStore.update(db => { |
|||
db.currentNode.fields = filter(f => f.name !== field.name) |
|||
(db.currentNode.fields); |
|||
|
|||
templateApi(db.hierarchy).addField(db.currentNode, field); |
|||
return db; |
|||
}); |
|||
} |
|||
|
|||
|
|||
const deleteField = databaseStore => field => { |
|||
databaseStore.update(db => { |
|||
db.currentNode.fields = filter(f => f.name !== field.name) |
|||
(db.currentNode.fields); |
|||
|
|||
return db; |
|||
}); |
|||
} |
|||
|
|||
|
|||
const saveAction = store => (newAction, isNew, oldAction = null) => { |
|||
store.update(s => { |
|||
|
|||
const existingAction = isNew |
|||
? null |
|||
: find(a => a.name === oldAction.name)(s.actions); |
|||
|
|||
if (existingAction) { |
|||
s.actions = pipe(s.actions, [ |
|||
map(a => a === existingAction ? newAction : a) |
|||
]); |
|||
} else { |
|||
s.actions.push(newAction); |
|||
} |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const deleteAction = store => action => { |
|||
store.update(s => { |
|||
s.actions = filter(a => a.name !== action.name)(s.actions); |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const saveTrigger = store => (newTrigger, isNew, oldTrigger = null) => { |
|||
store.update(s => { |
|||
|
|||
const existingTrigger = isNew |
|||
? null |
|||
: find(a => a.name === oldTrigger.name)(s.triggers); |
|||
|
|||
if (existingTrigger) { |
|||
s.triggers = pipe(s.triggers, [ |
|||
map(a => a === existingTrigger ? newTrigger : a) |
|||
]); |
|||
} else { |
|||
s.triggers.push(newTrigger); |
|||
} |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const deleteTrigger = store => trigger => { |
|||
store.update(s => { |
|||
s.triggers = filter(t => t.name !== trigger.name)(s.triggers); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const incrementAccessLevelsVersion = (s) => |
|||
s.accessLevels.version = (s.accessLevels.version || 0) + 1; |
|||
|
|||
const saveLevel = store => (newLevel, isNew, oldLevel = null) => { |
|||
store.update(s => { |
|||
|
|||
const levels = s.accessLevels.levels; |
|||
|
|||
const existingLevel = isNew |
|||
? null |
|||
: find(a => a.name === oldLevel.name)(levels); |
|||
|
|||
if (existingLevel) { |
|||
s.accessLevels.levels = pipe(levels, [ |
|||
map(a => a === existingLevel ? newLevel : a) |
|||
]); |
|||
} else { |
|||
s.accessLevels.levels.push(newLevel); |
|||
} |
|||
|
|||
incrementAccessLevelsVersion(s); |
|||
|
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const deleteLevel = store => level => { |
|||
store.update(s => { |
|||
s.accessLevels.levels = filter(t => t.name !== level.name)(s.accessLevels.levels); |
|||
incrementAccessLevelsVersion(s); |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const setActiveNav = store => navName => { |
|||
store.update(s => { |
|||
s.activeNav = navName; |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const createShadowHierarchy = hierarchy => |
|||
constructHierarchy(JSON.parse(JSON.stringify(hierarchy))); |
|||
|
|||
const saveScreen = store => (screen) => { |
|||
store.update(s => { |
|||
return _saveScreen(store, s, screen); |
|||
}) |
|||
}; |
|||
|
|||
const _saveScreen = (store, s, screen) => { |
|||
const screens = pipe(s.screens, [ |
|||
filter(c => c.name !== screen.name), |
|||
concat([screen]) |
|||
]); |
|||
|
|||
s.screens = screens; |
|||
s.currentFrontEndItem = screen; |
|||
s.currentComponentInfo = getScreenInfo( |
|||
s.components, screen); |
|||
|
|||
api.post(`/_builder/api/${s.appname}/screen`, screen) |
|||
.then(() => savePackage(store, s)); |
|||
|
|||
return s; |
|||
} |
|||
|
|||
const _save = (appname, screen, store, s) => |
|||
api.post(`/_builder/api/${appname}/screen`, screen) |
|||
.then(() => savePackage(store, s)); |
|||
|
|||
const createScreen = store => (screenName, layoutComponentName) => { |
|||
store.update(s => { |
|||
const newComponentInfo = getNewComponentInfo( |
|||
s.components, layoutComponentName, screenName); |
|||
|
|||
s.currentFrontEndItem = newComponentInfo.component; |
|||
s.currentComponentInfo = newComponentInfo; |
|||
s.currentFrontEndType = "screen"; |
|||
|
|||
return _saveScreen(store, s, newComponentInfo.component); |
|||
}); |
|||
}; |
|||
|
|||
const createGeneratedComponents = store => components => { |
|||
store.update(s => { |
|||
s.components = [...s.components, ...components]; |
|||
s.screens = [...s.screens, ...components]; |
|||
|
|||
const doCreate = async () => { |
|||
for (let c of components) { |
|||
await api.post(`/_builder/api/${s.appname}/screen`, c); |
|||
} |
|||
|
|||
await savePackage(store, s); |
|||
} |
|||
|
|||
doCreate(); |
|||
|
|||
return s; |
|||
}); |
|||
}; |
|||
|
|||
const deleteScreen = store => name => { |
|||
store.update(s => { |
|||
|
|||
const components = pipe(s.components, [ |
|||
filter(c => c.name !== name) |
|||
]); |
|||
|
|||
const screens = pipe(s.screens, [ |
|||
filter(c => c.name !== name) |
|||
]); |
|||
|
|||
s.components = components; |
|||
s.screens = screens; |
|||
if (s.currentFrontEndItem.name === name) { |
|||
s.currentFrontEndItem = null; |
|||
s.currentFrontEndType = ""; |
|||
} |
|||
|
|||
api.delete(`/_builder/api/${s.appname}/screen/${name}`); |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const renameScreen = store => (oldname, newname) => { |
|||
store.update(s => { |
|||
|
|||
const { |
|||
screens, pages, error, changedScreens |
|||
} = rename(s.pages, s.screens, oldname, newname); |
|||
|
|||
if (error) { |
|||
// should really do something with this
|
|||
return s; |
|||
} |
|||
|
|||
s.screens = screens; |
|||
s.pages = pages; |
|||
if (s.currentFrontEndItem.name === oldname) |
|||
s.currentFrontEndItem.name = newname; |
|||
|
|||
const saveAllChanged = async () => { |
|||
for (let screenName of changedScreens) { |
|||
const changedScreen |
|||
= getExactComponent(screens, screenName); |
|||
await api.post(`/_builder/api/${s.appname}/screen`, changedScreen); |
|||
} |
|||
} |
|||
|
|||
api.patch(`/_builder/api/${s.appname}/screen`, { |
|||
oldname, newname |
|||
}) |
|||
.then(() => saveAllChanged()) |
|||
.then(() => { |
|||
savePackage(store, s); |
|||
}); |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const savePage = store => async page => { |
|||
store.update(s => { |
|||
if (s.currentFrontEndType !== "page" || !s.currentPageName) { |
|||
return s; |
|||
} |
|||
|
|||
s.pages[s.currentPageName] = page; |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const addComponentLibrary = store => async lib => { |
|||
|
|||
const response = |
|||
await api.get(`/_builder/api/${appname}/componentlibrary?lib=${encodeURI(lib)}`, undefined, false); |
|||
|
|||
const success = response.status === 200; |
|||
|
|||
const error = response.status === 404 |
|||
? `Could not find library ${lib}` |
|||
: success |
|||
? "" |
|||
: response.statusText; |
|||
|
|||
const components = success |
|||
? await response.json() |
|||
: []; |
|||
|
|||
store.update(s => { |
|||
if (success) { |
|||
|
|||
const componentsArray = []; |
|||
for (let c in components) { |
|||
componentsArray.push(components[c]); |
|||
} |
|||
|
|||
s.components = pipe(s.components, [ |
|||
filter(c => !c.name.startsWith(`${lib}/`)), |
|||
concat(componentsArray) |
|||
]); |
|||
|
|||
s.pages.componentLibraries.push(lib); |
|||
savePackage(store, s); |
|||
} |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const removeComponentLibrary = store => lib => { |
|||
store.update(s => { |
|||
|
|||
|
|||
s.pages.componentLibraries = filter(l => l !== lib)( |
|||
s.pages.componentLibraries); |
|||
savePackage(store, s); |
|||
|
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const addStylesheet = store => stylesheet => { |
|||
store.update(s => { |
|||
s.pages.stylesheets.push(stylesheet); |
|||
savePackage(store, s); |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const removeStylesheet = store => stylesheet => { |
|||
store.update(s => { |
|||
s.pages.stylesheets = filter(s => s !== stylesheet)(s.pages.stylesheets); |
|||
savePackage(store, s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const refreshComponents = store => async () => { |
|||
|
|||
const componentsAndGenerators = |
|||
await api.get(`/_builder/api/${db.appname}/components`).then(r => r.json()); |
|||
|
|||
const components = pipe(componentsAndGenerators.components, [ |
|||
keys, |
|||
map(k => ({ ...componentsAndGenerators[k], name: k })) |
|||
]); |
|||
|
|||
store.update(s => { |
|||
s.components = pipe(s.components, [ |
|||
filter(c => !isRootComponent(c)), |
|||
concat(components) |
|||
]); |
|||
s.generators = componentsAndGenerators.generators; |
|||
return s; |
|||
}); |
|||
}; |
|||
|
|||
const savePackage = (store, s) => { |
|||
|
|||
const appDefinition = { |
|||
hierarchy: s.hierarchy, |
|||
triggers: s.triggers, |
|||
actions: keyBy("name")(s.actions), |
|||
props: { |
|||
main: buildPropsHierarchy( |
|||
s.components, |
|||
s.screens, |
|||
s.pages.main.appBody), |
|||
unauthenticated: buildPropsHierarchy( |
|||
s.components, |
|||
s.screens, |
|||
s.pages.unauthenticated.appBody) |
|||
}, |
|||
uiFunctions: buildCodeForScreens(s.screens) |
|||
}; |
|||
|
|||
const data = { |
|||
appDefinition, |
|||
accessLevels: s.accessLevels, |
|||
pages: s.pages, |
|||
} |
|||
|
|||
return api.post(`/_builder/api/${s.appname}/appPackage`, data); |
|||
} |
|||
|
|||
const setCurrentScreen = store => screenName => { |
|||
store.update(s => { |
|||
const screen = getExactComponent(s.screens, screenName); |
|||
s.currentFrontEndItem = screen; |
|||
s.currentFrontEndType = "screen"; |
|||
s.currentComponentInfo = getScreenInfo(s.components, screen); |
|||
setCurrentScreenFunctions(s); |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const setCurrentPage = store => pageName => { |
|||
store.update(s => { |
|||
s.currentFrontEndType = "page"; |
|||
s.currentPageName = pageName; |
|||
setCurrentScreenFunctions(s); |
|||
return s; |
|||
}); |
|||
} |
|||
|
|||
const addChildComponent = store => component => { |
|||
|
|||
store.update(s => { |
|||
const newComponent = getNewComponentInfo( |
|||
s.components, component); |
|||
|
|||
let children = s.currentComponentInfo.component ? |
|||
s.currentComponentInfo.component.props._children : |
|||
s.currentComponentInfo._children; |
|||
|
|||
const component_definition = Object.assign( |
|||
cloneDeep(newComponent.fullProps), { |
|||
_component: component, |
|||
_styles: { position: {}, layout: {} }, |
|||
_id: uuid() |
|||
}) |
|||
|
|||
if (children) { |
|||
if (s.currentComponentInfo.component) { |
|||
s.currentComponentInfo.component.props._children = children.concat(component_definition); |
|||
} else { |
|||
s.currentComponentInfo._children = children.concat(component_definition) |
|||
} |
|||
} else { |
|||
if (s.currentComponentInfo.component) { |
|||
s.currentComponentInfo.component.props._children = [component_definition]; |
|||
} else { |
|||
s.currentComponentInfo._children = [component_definition] |
|||
} |
|||
} |
|||
|
|||
_saveScreen(store, s, s.currentFrontEndItem); |
|||
|
|||
_saveScreen(store, s, s.currentFrontEndItem); |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const selectComponent = store => component => { |
|||
store.update(s => { |
|||
s.currentComponentInfo = component; |
|||
return s; |
|||
}) |
|||
|
|||
} |
|||
|
|||
const setComponentProp = store => (name, value) => { |
|||
store.update(s => { |
|||
const current_component = s.currentComponentInfo; |
|||
s.currentComponentInfo[name] = value; |
|||
_saveScreen(store, s, s.currentFrontEndItem); |
|||
s.currentComponentInfo = current_component; |
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const setComponentStyle = store => (type, name, value) => { |
|||
store.update(s => { |
|||
if (!s.currentComponentInfo._styles) { |
|||
s.currentComponentInfo._styles = {}; |
|||
} |
|||
s.currentComponentInfo._styles[type][name] = value; |
|||
s.currentFrontEndItem._css = generate_screen_css(s.currentFrontEndItem.props._children) |
|||
|
|||
// save without messing with the store
|
|||
_save(s.appname, s.currentFrontEndItem, store, s) |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const setComponentCode = store => (code) => { |
|||
store.update(s => { |
|||
s.currentComponentInfo._code = code; |
|||
|
|||
setCurrentScreenFunctions(s); |
|||
// save without messing with the store
|
|||
_save(s.appname, s.currentFrontEndItem, store, s) |
|||
|
|||
return s; |
|||
}) |
|||
} |
|||
|
|||
const setCurrentScreenFunctions = (s) => { |
|||
s.currentScreenFunctions = |
|||
s.currentFrontEndItem === "screen" |
|||
? buildCodeForScreens([s.currentFrontEndItem]) |
|||
: "({});"; |
|||
} |
|||
@ -1,7 +1,7 @@ |
|||
export function uuid() { |
|||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { |
|||
const r = (Math.random() * 16) | 0, |
|||
v = c == 'x' ? r : (r & 0x3) | 0x8; |
|||
return v.toString(16); |
|||
}); |
|||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => { |
|||
const r = (Math.random() * 16) | 0, |
|||
v = c == "x" ? r : (r & 0x3) | 0x8 |
|||
return v.toString(16) |
|||
}) |
|||
} |
|||
|
|||
@ -1,163 +1,167 @@ |
|||
<script> |
|||
export let color = "primary" |
|||
export let className = "" |
|||
export let style = "" |
|||
export let groupPosition = "" |
|||
export let grouped = false |
|||
|
|||
export let color = "primary"; |
|||
export let className = ""; |
|||
export let style = ""; |
|||
export let groupPosition = ""; |
|||
export let grouped = false; |
|||
|
|||
$: borderClass = grouped |
|||
? "" |
|||
: "border-normal"; |
|||
|
|||
$: borderClass = grouped ? "" : "border-normal" |
|||
</script> |
|||
|
|||
<button class="{color} {className} {borderClass} {grouped ? "grouped" : ""}" |
|||
style="{style}" |
|||
on:click > |
|||
<slot/> |
|||
<button |
|||
class="{color} |
|||
{className} |
|||
{borderClass} |
|||
{grouped ? 'grouped' : ''}" |
|||
{style} |
|||
on:click> |
|||
<slot /> |
|||
</button> |
|||
|
|||
<style> |
|||
|
|||
.border-normal { 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-middle { border-radius: 0; } |
|||
|
|||
button { |
|||
border-style: solid; |
|||
.border-normal { |
|||
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-middle { |
|||
border-radius: 0; |
|||
} |
|||
|
|||
button { |
|||
border-style: solid; |
|||
padding: 7.5px 15px; |
|||
cursor:pointer; |
|||
margin:5px; |
|||
cursor: pointer; |
|||
margin: 5px; |
|||
border-radius: 5px; |
|||
} |
|||
} |
|||
|
|||
/* ---- PRIMARY ----*/ |
|||
.primary { |
|||
/* ---- PRIMARY ----*/ |
|||
.primary { |
|||
background-color: var(--primary100); |
|||
border-color: var(--primary100); |
|||
color: var(--white); |
|||
} |
|||
} |
|||
|
|||
.primary:hover { |
|||
.primary:hover { |
|||
background-color: var(--primary75); |
|||
border-color: var(--primary75); |
|||
} |
|||
} |
|||
|
|||
.primary:active { |
|||
.primary:active { |
|||
background-color: var(--primarydark); |
|||
border-color: var(--primarydark); |
|||
} |
|||
} |
|||
|
|||
.primary-outline { |
|||
.primary-outline { |
|||
background-color: var(--white); |
|||
border-color: var(--primary100); |
|||
color: var(--primary100); |
|||
} |
|||
} |
|||
|
|||
.primary-outline:hover { |
|||
.primary-outline:hover { |
|||
background-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
.primary-outline:pressed { |
|||
.primary-outline:pressed { |
|||
background-color: var(--primary25); |
|||
} |
|||
} |
|||
|
|||
/* ---- secondary ----*/ |
|||
/* ---- secondary ----*/ |
|||
|
|||
.secondary { |
|||
.secondary { |
|||
background-color: var(--secondary100); |
|||
border-color: var(--secondary100); |
|||
color: var(--white); |
|||
} |
|||
} |
|||
|
|||
.secondary:hover { |
|||
.secondary:hover { |
|||
background-color: var(--secondary75); |
|||
border-color: var(--secondary75); |
|||
} |
|||
} |
|||
|
|||
.secondary:pressed { |
|||
.secondary:pressed { |
|||
background-color: var(--secondarydark); |
|||
border-color: var(--secondarydark); |
|||
} |
|||
} |
|||
|
|||
.secondary-outline { |
|||
.secondary-outline { |
|||
background-color: var(--white); |
|||
border-color: var(--secondary100); |
|||
color: var(--secondary100); |
|||
} |
|||
} |
|||
|
|||
.secondary-outline:hover { |
|||
.secondary-outline:hover { |
|||
background-color: var(--secondary10); |
|||
} |
|||
} |
|||
|
|||
.secondary-outline:pressed { |
|||
.secondary-outline:pressed { |
|||
background-color: var(--secondary25); |
|||
} |
|||
} |
|||
|
|||
|
|||
/* ---- success ----*/ |
|||
.success { |
|||
/* ---- success ----*/ |
|||
.success { |
|||
background-color: var(--success100); |
|||
border-color: var(--success100); |
|||
color: var(--white); |
|||
} |
|||
} |
|||
|
|||
.success:hover { |
|||
.success:hover { |
|||
background-color: var(--success75); |
|||
border-color: var(--success75); |
|||
} |
|||
} |
|||
|
|||
.success:pressed { |
|||
.success:pressed { |
|||
background-color: var(--successdark); |
|||
border-color: var(--successdark); |
|||
} |
|||
} |
|||
|
|||
.success-outline { |
|||
.success-outline { |
|||
background-color: var(--white); |
|||
border-color: var(--success100); |
|||
color: var(--success100); |
|||
} |
|||
} |
|||
|
|||
.success-outline:hover { |
|||
.success-outline:hover { |
|||
background-color: var(--success10); |
|||
} |
|||
} |
|||
|
|||
.success-outline:pressed { |
|||
.success-outline:pressed { |
|||
background-color: var(--success25); |
|||
} |
|||
} |
|||
|
|||
/* ---- deletion ----*/ |
|||
.deletion { |
|||
/* ---- deletion ----*/ |
|||
.deletion { |
|||
background-color: var(--deletion100); |
|||
border-color: var(--deletion100); |
|||
color: var(--white); |
|||
} |
|||
} |
|||
|
|||
.deletion:hover { |
|||
.deletion:hover { |
|||
background-color: var(--deletion75); |
|||
border-color: var(--deletion75); |
|||
} |
|||
} |
|||
|
|||
.deletion:pressed { |
|||
.deletion:pressed { |
|||
background-color: var(--deletiondark); |
|||
border-color: var(--deletiondark); |
|||
} |
|||
} |
|||
|
|||
.deletion-outline { |
|||
.deletion-outline { |
|||
background-color: var(--white); |
|||
border-color: var(--deletion100); |
|||
color: var(--deletion100); |
|||
} |
|||
} |
|||
|
|||
.deletion-outline:hover { |
|||
.deletion-outline:hover { |
|||
background-color: var(--deletion10); |
|||
} |
|||
} |
|||
|
|||
.deletion-outline:pressed { |
|||
.deletion-outline:pressed { |
|||
background-color: var(--deletion25); |
|||
} |
|||
|
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,27 +1,25 @@ |
|||
<script> |
|||
export let style=""; |
|||
export let style = "" |
|||
</script> |
|||
|
|||
<div class="root" style={style}> |
|||
<slot /> |
|||
<div class="root" {style}> |
|||
<slot /> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
display: flex; |
|||
} |
|||
} |
|||
|
|||
.root:last-child { |
|||
.root:last-child { |
|||
border-radius: 0 var(--borderradius) var(--borderradius) 0; |
|||
} |
|||
} |
|||
|
|||
.root:first-child { |
|||
border-radius: var(--borderradius) 0 0 var(--borderradius); |
|||
} |
|||
.root:first-child { |
|||
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; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,16 +1,13 @@ |
|||
<script> |
|||
|
|||
export let checked=false; |
|||
export let label=""; |
|||
|
|||
export let checked = false |
|||
export let label = "" |
|||
</script> |
|||
|
|||
<input class="uk-checkbox" type="checkbox" bind:checked on:change />{label} |
|||
<input class="uk-checkbox" type="checkbox" bind:checked on:change /> |
|||
{label} |
|||
|
|||
<style> |
|||
|
|||
input { |
|||
margin-right:7px; |
|||
} |
|||
|
|||
</style> |
|||
input { |
|||
margin-right: 7px; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,24 +1,22 @@ |
|||
<script> |
|||
// todo: use https://ace.c9.io |
|||
export let text = ""; |
|||
export let label = ""; |
|||
// todo: use https://ace.c9.io |
|||
export let text = "" |
|||
export let label = "" |
|||
</script> |
|||
|
|||
<div>{label}</div> |
|||
<textarea class="uk-textarea" bind:value={text}></textarea> |
|||
<textarea class="uk-textarea" bind:value={text} /> |
|||
|
|||
<style> |
|||
|
|||
textarea { |
|||
padding:3px; |
|||
margin-top:5px; |
|||
margin-bottom:10px; |
|||
textarea { |
|||
padding: 3px; |
|||
margin-top: 5px; |
|||
margin-bottom: 10px; |
|||
background: var(--lightslate); |
|||
color: var(--white); |
|||
font-family: 'Courier New', Courier, monospace; |
|||
width:95%; |
|||
height:100px; |
|||
font-family: "Courier New", Courier, monospace; |
|||
width: 95%; |
|||
height: 100px; |
|||
border-radius: 5px; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,17 +1,13 @@ |
|||
<script> |
|||
|
|||
export let name = ""; |
|||
|
|||
export let name = "" |
|||
</script> |
|||
|
|||
<div> |
|||
<h4>Coming Sometime: {name}</h4> |
|||
<h4>Coming Sometime: {name}</h4> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
h4 { |
|||
h4 { |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,39 +1,44 @@ |
|||
<script> |
|||
import {createEventDispatcher} from "svelte"; |
|||
|
|||
export let selected; |
|||
export let label; |
|||
export let options; |
|||
export let valueMember; |
|||
export let textMember; |
|||
export let multiple=false; |
|||
export let width = "medium"; |
|||
export let size = "small"; |
|||
|
|||
const dispatch =createEventDispatcher(); |
|||
|
|||
|
|||
import { createEventDispatcher } from "svelte" |
|||
|
|||
export let selected |
|||
export let label |
|||
export let options |
|||
export let valueMember |
|||
export let textMember |
|||
export let multiple = false |
|||
export let width = "medium" |
|||
export let size = "small" |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
</script> |
|||
|
|||
|
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label">{label}</label> |
|||
<div class="uk-form-controls"> |
|||
{#if multiple} |
|||
|
|||
<select class="uk-select uk-form-width-{width} uk-form-{size}" multiple bind:value={selected} on:change> |
|||
{#each options as option} |
|||
<option value={!valueMember ? option : valueMember(option)}>{!textMember ? option : textMember(option)}</option> |
|||
{/each} |
|||
</select> |
|||
|
|||
{:else} |
|||
|
|||
<select class="uk-select uk-form-width-{width} uk-form-{size}" bind:value={selected} on:change> |
|||
{#each options as option} |
|||
<option value={!valueMember ? option : valueMember(option)}>{!textMember ? option : textMember(option)}</option> |
|||
{/each} |
|||
</select> |
|||
{/if} |
|||
</div> |
|||
<label class="uk-form-label">{label}</label> |
|||
<div class="uk-form-controls"> |
|||
{#if multiple} |
|||
<select |
|||
class="uk-select uk-form-width-{width} uk-form-{size}" |
|||
multiple |
|||
bind:value={selected} |
|||
on:change> |
|||
{#each options as option} |
|||
<option value={!valueMember ? option : valueMember(option)}> |
|||
{!textMember ? option : textMember(option)} |
|||
</option> |
|||
{/each} |
|||
</select> |
|||
{:else} |
|||
<select |
|||
class="uk-select uk-form-width-{width} uk-form-{size}" |
|||
bind:value={selected} |
|||
on:change> |
|||
{#each options as option} |
|||
<option value={!valueMember ? option : valueMember(option)}> |
|||
{!textMember ? option : textMember(option)} |
|||
</option> |
|||
{/each} |
|||
</select> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
|
|||
@ -1,68 +1,65 @@ |
|||
<script> |
|||
|
|||
import getIcon from "./icon"; |
|||
export let iconName; |
|||
export let actions = []; // [ {label: "Action Name", onclick: () => {...} } ] |
|||
let isDroppedDown = false; |
|||
|
|||
import getIcon from "./icon" |
|||
export let iconName |
|||
export let actions = [] // [ {label: "Action Name", onclick: () => {...} } ] |
|||
let isDroppedDown = false |
|||
</script> |
|||
|
|||
<div class="root" on:click={() => (isDroppedDown = !isDroppedDown)}> |
|||
{@html getIcon(iconName)} |
|||
|
|||
<div class="root" on:click={() => isDroppedDown = !isDroppedDown}> |
|||
{@html getIcon(iconName)} |
|||
|
|||
<div class="dropdown-background" on:click|stopPropagation={() => isDroppedDown = false} style="display: {isDroppedDown ? 'block' : 'none'}"></div> |
|||
<div |
|||
class="dropdown-background" |
|||
on:click|stopPropagation={() => (isDroppedDown = false)} |
|||
style="display: {isDroppedDown ? 'block' : 'none'}" /> |
|||
|
|||
<div class="dropdown-content" style="display: {isDroppedDown ? 'inline-block' : 'none'}"> |
|||
{#each actions as action} |
|||
<div class="action-row" on:click={action.onclick}> |
|||
{action.label} |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
</div> |
|||
<div |
|||
class="dropdown-content" |
|||
style="display: {isDroppedDown ? 'inline-block' : 'none'}"> |
|||
{#each actions as action} |
|||
<div class="action-row" on:click={action.onclick}>{action.label}</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.dropdown-background { |
|||
.dropdown-background { |
|||
position: fixed; |
|||
top:0; |
|||
left:0; |
|||
width:100vw; |
|||
height:100vh; |
|||
} |
|||
top: 0; |
|||
left: 0; |
|||
width: 100vw; |
|||
height: 100vh; |
|||
} |
|||
|
|||
.root { |
|||
.root { |
|||
cursor: pointer; |
|||
z-index: 1; |
|||
} |
|||
} |
|||
|
|||
.dropdown-content { |
|||
.dropdown-content { |
|||
position: absolute; |
|||
background-color: var(--white); |
|||
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; |
|||
font-weight: normal; |
|||
border-style: solid; |
|||
border-width: 1px; |
|||
border-color: var(--secondary10); |
|||
} |
|||
} |
|||
|
|||
.dropdown-content:not(:focus) { |
|||
.dropdown-content:not(:focus) { |
|||
display: none; |
|||
} |
|||
} |
|||
|
|||
.action-row { |
|||
.action-row { |
|||
padding: 7px 10px; |
|||
cursor:pointer; |
|||
} |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.action-row:hover { |
|||
.action-row:hover { |
|||
background-color: var(--primary100); |
|||
color:var(--white); |
|||
} |
|||
|
|||
</style> |
|||
color: var(--white); |
|||
} |
|||
</style> |
|||
|
|||
@ -1,29 +1,29 @@ |
|||
<script> |
|||
export let errors = []; |
|||
export let errors = [] |
|||
|
|||
$:hasErrors = errors.length > 0; |
|||
$: hasErrors = errors.length > 0 |
|||
</script> |
|||
|
|||
{#if hasErrors} |
|||
<div class="error-container"> |
|||
<div class="error-container"> |
|||
{#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} |
|||
</div> |
|||
</div> |
|||
{/if} |
|||
|
|||
<style> |
|||
|
|||
.error-container { |
|||
padding:10px; |
|||
.error-container { |
|||
padding: 10px; |
|||
border-style: solid; |
|||
border-color: var(--deletion100); |
|||
border-radius: var(--borderradiusall); |
|||
background: var(--deletion75); |
|||
} |
|||
} |
|||
|
|||
.error-row { |
|||
.error-row { |
|||
padding: 5px 0px; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,63 +1,59 @@ |
|||
<script> |
|||
import getIcon from "./icon"; |
|||
|
|||
export let size = 18; |
|||
export let icon = ""; |
|||
export let style = ""; |
|||
export let color = "var(--secondary100)"; |
|||
export let hoverColor = "var(--secondary75)"; |
|||
export let attributes = {}; |
|||
|
|||
let currentAttributes = []; |
|||
const addAttributes = (node, attributes) => { |
|||
|
|||
const add = (_attributes) => { |
|||
const attrs = []; |
|||
for(let attr in _attributes) { |
|||
node.setAttribute(attr, _attributes[attr]); |
|||
attrs.push("uk-toggle") |
|||
} |
|||
currentAttributes = attrs; |
|||
import getIcon from "./icon" |
|||
|
|||
export let size = 18 |
|||
export let icon = "" |
|||
export let style = "" |
|||
export let color = "var(--secondary100)" |
|||
export let hoverColor = "var(--secondary75)" |
|||
export let attributes = {} |
|||
|
|||
let currentAttributes = [] |
|||
const addAttributes = (node, attributes) => { |
|||
const add = _attributes => { |
|||
const attrs = [] |
|||
for (let attr in _attributes) { |
|||
node.setAttribute(attr, _attributes[attr]) |
|||
attrs.push("uk-toggle") |
|||
} |
|||
currentAttributes = attrs |
|||
} |
|||
|
|||
add(attributes); |
|||
add(attributes) |
|||
|
|||
return { |
|||
// should implement update method |
|||
update(attributes) { |
|||
for(let attr of currentAttributes) { |
|||
node.removeAttribute(attr) |
|||
} |
|||
add(attributes); |
|||
}, |
|||
destroy() {} |
|||
// should implement update method |
|||
update(attributes) { |
|||
for (let attr of currentAttributes) { |
|||
node.removeAttribute(attr) |
|||
} |
|||
add(attributes) |
|||
}, |
|||
destroy() {}, |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<button style="{style}{style ? ";" : ""} color:{color}; --hovercolor:{hoverColor}" |
|||
on:click |
|||
use:addAttributes={attributes}> |
|||
{@html getIcon(icon, size)} |
|||
<button |
|||
style="{style}{style ? ';' : ''} color:{color}; --hovercolor:{hoverColor}" |
|||
on:click |
|||
use:addAttributes={attributes}> |
|||
{@html getIcon(icon, size)} |
|||
</button> |
|||
|
|||
|
|||
<style> |
|||
|
|||
button { |
|||
button { |
|||
border-style: none; |
|||
background-color: rgba(0,0,0,0); |
|||
background-color: rgba(0, 0, 0, 0); |
|||
cursor: pointer; |
|||
outline:none; |
|||
} |
|||
outline: none; |
|||
} |
|||
|
|||
button:hover { |
|||
button:hover { |
|||
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 PaintIcon } from './Paint.svelte'; |
|||
export { default as TerminalIcon } from './Terminal.svelte'; |
|||
export { default as InputIcon } from './Input.svelte'; |
|||
export { default as ImageIcon } from './Image.svelte'; |
|||
export { default as ArrowDownIcon } from './ArrowDown.svelte'; |
|||
export { default as CircleIndicator } from './CircleIndicator.svelte'; |
|||
export { default as PencilIcon } from './Pencil.svelte'; |
|||
export { default as EventsIcon } from './Events.svelte'; |
|||
export { default as LayoutIcon } from "./Layout.svelte" |
|||
export { default as PaintIcon } from "./Paint.svelte" |
|||
export { default as TerminalIcon } from "./Terminal.svelte" |
|||
export { default as InputIcon } from "./Input.svelte" |
|||
export { default as ImageIcon } from "./Image.svelte" |
|||
export { default as ArrowDownIcon } from "./ArrowDown.svelte" |
|||
export { default as CircleIndicator } from "./CircleIndicator.svelte" |
|||
export { default as PencilIcon } from "./Pencil.svelte" |
|||
export { default as EventsIcon } from "./Events.svelte" |
|||
|
|||
@ -1,12 +0,0 @@ |
|||
export { default as LayoutIcon } from './Layout.svelte'; |
|||
export { default as PaintIcon } from './Paint.svelte'; |
|||
export { default as TerminalIcon } from './Terminal.svelte'; |
|||
export { default as InputIcon } from './Input.svelte'; |
|||
export { default as ImageIcon } from './Image.svelte'; |
|||
export { default as ArrowDownIcon } from './ArrowDown.svelte'; |
|||
<<<<<<< HEAD |
|||
export { default as CircleIndicator } from './CircleIndicator.svelte'; |
|||
======= |
|||
export { default as EventsIcon } from './Events.svelte'; |
|||
export { default as PencilIcon } from './Pencil.svelte'; |
|||
>>>>>>> master |
|||
@ -1,35 +1,30 @@ |
|||
<script> |
|||
import {join} from "lodash/fp"; |
|||
import { join } from "lodash/fp" |
|||
|
|||
export let values; |
|||
export let label; |
|||
export let values |
|||
export let label |
|||
|
|||
const inputChanged = ev => { |
|||
const inputChanged = ev => { |
|||
try { |
|||
values = ev.target.value.split("\n"); |
|||
} catch(_) { |
|||
values = []; |
|||
values = ev.target.value.split("\n") |
|||
} catch (_) { |
|||
values = [] |
|||
} |
|||
} |
|||
|
|||
$: valuesText = join("\n")(values); |
|||
|
|||
} |
|||
|
|||
$: valuesText = join("\n")(values) |
|||
</script> |
|||
|
|||
|
|||
<div class="uk-margin"> |
|||
<label class="uk-form-label">{label}</label> |
|||
<div class="uk-form-controls"> |
|||
<textarea value={valuesText} on:change={inputChanged} ></textarea> |
|||
</div> |
|||
<label class="uk-form-label">{label}</label> |
|||
<div class="uk-form-controls"> |
|||
<textarea value={valuesText} on:change={inputChanged} /> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
textarea { |
|||
width:300px; |
|||
height:200px; |
|||
} |
|||
|
|||
</style> |
|||
textarea { |
|||
width: 300px; |
|||
height: 200px; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,29 +1,28 @@ |
|||
import { |
|||
isString |
|||
} from "lodash/fp"; |
|||
import { isString } from "lodash/fp" |
|||
|
|||
import { |
|||
BB_STATE_BINDINGPATH, BB_STATE_FALLBACK, |
|||
BB_STATE_BINDINGSOURCE |
|||
} from "@budibase/client/src/state/isState"; |
|||
BB_STATE_BINDINGPATH, |
|||
BB_STATE_FALLBACK, |
|||
BB_STATE_BINDINGSOURCE, |
|||
} from "@budibase/client/src/state/isState" |
|||
|
|||
export const isBinding = value => |
|||
!isString(value) |
|||
&& value |
|||
&& isString(value[BB_STATE_BINDINGPATH]) |
|||
&& value[BB_STATE_BINDINGPATH].length > 0; |
|||
export const isBinding = value => |
|||
!isString(value) && |
|||
value && |
|||
isString(value[BB_STATE_BINDINGPATH]) && |
|||
value[BB_STATE_BINDINGPATH].length > 0 |
|||
|
|||
export const setBinding = ({path, fallback, source}, binding={} ) => { |
|||
if(isNonEmptyString(path)) binding[BB_STATE_BINDINGPATH] = path; |
|||
if(isNonEmptyString(fallback)) binding[BB_STATE_FALLBACK] = fallback; |
|||
binding[BB_STATE_BINDINGSOURCE] = source || "store"; |
|||
return binding |
|||
export const setBinding = ({ path, fallback, source }, binding = {}) => { |
|||
if (isNonEmptyString(path)) binding[BB_STATE_BINDINGPATH] = path |
|||
if (isNonEmptyString(fallback)) binding[BB_STATE_FALLBACK] = fallback |
|||
binding[BB_STATE_BINDINGSOURCE] = source || "store" |
|||
return binding |
|||
} |
|||
|
|||
export const getBinding = binding => ({ |
|||
path: binding[BB_STATE_BINDINGPATH] || "", |
|||
fallback: binding[BB_STATE_FALLBACK] || "", |
|||
source: binding[BB_STATE_BINDINGSOURCE] || "store" |
|||
}); |
|||
path: binding[BB_STATE_BINDINGPATH] || "", |
|||
fallback: binding[BB_STATE_FALLBACK] || "", |
|||
source: binding[BB_STATE_BINDINGSOURCE] || "store", |
|||
}) |
|||
|
|||
const isNonEmptyString = s => isString(s) && s.length > 0; |
|||
const isNonEmptyString = s => isString(s) && s.length > 0 |
|||
|
|||
@ -1,96 +1,108 @@ |
|||
import {hierarchy as hierarchyFunctions, |
|||
common, getTemplateApi, getAuthApi } from "../../../core/src"; |
|||
import {find, filter, includes, keyBy, some, |
|||
flatten, map} from "lodash/fp"; |
|||
|
|||
import { |
|||
generateSchema |
|||
} from "../../../core/src/indexing/indexSchemaCreator"; |
|||
hierarchy as hierarchyFunctions, |
|||
common, |
|||
getTemplateApi, |
|||
getAuthApi, |
|||
} from "../../../core/src" |
|||
import { find, filter, includes, keyBy, some, flatten, map } from "lodash/fp" |
|||
|
|||
import { generateSchema } from "../../../core/src/indexing/indexSchemaCreator" |
|||
|
|||
export { userWithFullAccess } from "../../../core/src/index"; |
|||
export { userWithFullAccess } from "../../../core/src/index" |
|||
|
|||
export const pipe = common.$; |
|||
export const pipe = common.$ |
|||
|
|||
export const events = common.eventsList; |
|||
export const events = common.eventsList |
|||
|
|||
export const getNode = (hierarchy, nodeId) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
find(n => n.nodeId === nodeId || n.nodeKey() === nodeId) |
|||
]); |
|||
export const getNode = (hierarchy, nodeId) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
find(n => n.nodeId === nodeId || n.nodeKey() === nodeId), |
|||
]) |
|||
|
|||
export const constructHierarchy = node => { |
|||
if(!node) return node; |
|||
return templateApi(node).constructHierarchy(node); |
|||
if (!node) return node |
|||
return templateApi(node).constructHierarchy(node) |
|||
} |
|||
|
|||
export const createNewHierarchy = () => { |
|||
return templateApi().getNewRootLevel(); |
|||
return templateApi().getNewRootLevel() |
|||
} |
|||
|
|||
export const templateApi = hierarchy => getTemplateApi({hierarchy}) |
|||
export const authApi = (hierarchy, actions) => getAuthApi({ |
|||
hierarchy, actions: keyBy("name")(actions), publish:()=>{}}) |
|||
export const templateApi = hierarchy => getTemplateApi({ hierarchy }) |
|||
export const authApi = (hierarchy, actions) => |
|||
getAuthApi({ |
|||
hierarchy, |
|||
actions: keyBy("name")(actions), |
|||
publish: () => {}, |
|||
}) |
|||
|
|||
export const allTypes = templateApi({}).allTypes; |
|||
export const allTypes = templateApi({}).allTypes |
|||
|
|||
export const validate = { |
|||
all: templateApi({}).validateAll, |
|||
node: templateApi({}).validateNode, |
|||
field: templateApi({}).validateField |
|||
}; |
|||
all: templateApi({}).validateAll, |
|||
node: templateApi({}).validateNode, |
|||
field: templateApi({}).validateField, |
|||
} |
|||
|
|||
export const getPotentialReverseReferenceIndexes = (hierarchy, refIndex) => { |
|||
const res = pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(n => hierarchyFunctions.isAncestor(refIndex)(n) |
|||
|| hierarchyFunctions.isAncestor(refIndex)(n.parent())), |
|||
map(n => n.indexes), |
|||
flatten, |
|||
filter(hierarchyFunctions.isReferenceIndex) |
|||
]); |
|||
|
|||
return res; |
|||
const res = pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter( |
|||
n => |
|||
hierarchyFunctions.isAncestor(refIndex)(n) || |
|||
hierarchyFunctions.isAncestor(refIndex)(n.parent()) |
|||
), |
|||
map(n => n.indexes), |
|||
flatten, |
|||
filter(hierarchyFunctions.isReferenceIndex), |
|||
]) |
|||
|
|||
return res |
|||
} |
|||
|
|||
export const getPotentialReferenceIndexes = (hierarchy, record) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isAncestorIndex), |
|||
filter(i => hierarchyFunctions.isAncestor(record)(i.parent()) |
|||
|| i.parent().nodeId === record.parent().nodeId |
|||
|| hierarchyFunctions.isRoot(i.parent())) |
|||
]); |
|||
|
|||
export const getDefaultTypeOptions = type => |
|||
!type ? {} : allTypes[type].getDefaultOptions(); |
|||
|
|||
export const getNewAction = () => templateApi({}).createAction(); |
|||
export const getNewTrigger = () => templateApi({}).createTrigger(); |
|||
|
|||
export const validateActions = actions => templateApi({}).validateActions(actions); |
|||
export const validateTriggers = (triggers, actions) => templateApi({}).validateTriggers(triggers, actions); |
|||
|
|||
export const generateFullPermissions = (hierarchy, actions) => |
|||
authApi(hierarchy,actions).generateFullPermissions(); |
|||
|
|||
export const getNewAccessLevel = () => |
|||
authApi().getNewAccessLevel(); |
|||
|
|||
export const validateAccessLevels = (hierarchy, actions, accessLevels) => |
|||
authApi(hierarchy, actions).validateAccessLevels(accessLevels); |
|||
|
|||
export const getIndexNodes = (hierarchy) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isIndex) |
|||
]); |
|||
|
|||
export const getRecordNodes = (hierarchy) => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isRecord) |
|||
]); |
|||
|
|||
export const getIndexSchema = hierarchy => index => |
|||
generateSchema(hierarchy, index); |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isAncestorIndex), |
|||
filter( |
|||
i => |
|||
hierarchyFunctions.isAncestor(record)(i.parent()) || |
|||
i.parent().nodeId === record.parent().nodeId || |
|||
hierarchyFunctions.isRoot(i.parent()) |
|||
), |
|||
]) |
|||
|
|||
export const getDefaultTypeOptions = type => |
|||
!type ? {} : allTypes[type].getDefaultOptions() |
|||
|
|||
export const getNewAction = () => templateApi({}).createAction() |
|||
export const getNewTrigger = () => templateApi({}).createTrigger() |
|||
|
|||
export const validateActions = actions => |
|||
templateApi({}).validateActions(actions) |
|||
export const validateTriggers = (triggers, actions) => |
|||
templateApi({}).validateTriggers(triggers, actions) |
|||
|
|||
export const generateFullPermissions = (hierarchy, actions) => |
|||
authApi(hierarchy, actions).generateFullPermissions() |
|||
|
|||
export const getNewAccessLevel = () => authApi().getNewAccessLevel() |
|||
|
|||
export const validateAccessLevels = (hierarchy, actions, accessLevels) => |
|||
authApi(hierarchy, actions).validateAccessLevels(accessLevels) |
|||
|
|||
export const getIndexNodes = hierarchy => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isIndex), |
|||
]) |
|||
|
|||
export const getRecordNodes = hierarchy => |
|||
pipe(hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isRecord), |
|||
]) |
|||
|
|||
export const getIndexSchema = hierarchy => index => |
|||
generateSchema(hierarchy, index) |
|||
|
|||
@ -1,26 +1,21 @@ |
|||
import { |
|||
eventHandlers |
|||
} from "../../../client/src/state/eventHandlers"; |
|||
import {writable} from "svelte/store"; |
|||
export { |
|||
EVENT_TYPE_MEMBER_NAME |
|||
} from "../../../client/src/state/eventHandlers"; |
|||
import { |
|||
createCoreApi |
|||
} from "../../../client/src/core"; |
|||
import { eventHandlers } from "../../../client/src/state/eventHandlers" |
|||
import { writable } from "svelte/store" |
|||
export { EVENT_TYPE_MEMBER_NAME } from "../../../client/src/state/eventHandlers" |
|||
import { createCoreApi } from "../../../client/src/core" |
|||
|
|||
export const allHandlers = (appDefinition, user) => { |
|||
export const allHandlers = (appDefinition, user) => { |
|||
const coreApi = createCoreApi(appDefinition, user) |
|||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy( |
|||
appDefinition.hierarchy |
|||
) |
|||
const store = writable({ |
|||
_bbuser: user, |
|||
}) |
|||
|
|||
const coreApi = createCoreApi(appDefinition, user); |
|||
appDefinition.hierarchy = coreApi.templateApi.constructHierarchy(appDefinition.hierarchy); |
|||
const store = writable({ |
|||
_bbuser: user |
|||
}); |
|||
|
|||
const handlersObj = eventHandlers(store, coreApi); |
|||
const handlersArray = []; |
|||
for(let key in handlersObj) { |
|||
handlersArray.push({name:key, ...handlersObj[key]}); |
|||
} |
|||
return handlersArray; |
|||
} |
|||
const handlersObj = eventHandlers(store, coreApi) |
|||
const handlersArray = [] |
|||
for (let key in handlersObj) { |
|||
handlersArray.push({ name: key, ...handlersObj[key] }) |
|||
} |
|||
return handlersArray |
|||
} |
|||
|
|||
@ -1,3 +1,4 @@ |
|||
import feather from "feather-icons"; |
|||
const getIcon = (icon, size) => feather.icons[icon].toSvg({height:size||"16", width:size||"16"}); |
|||
export default getIcon; |
|||
import feather from "feather-icons" |
|||
const getIcon = (icon, size) => |
|||
feather.icons[icon].toSvg({ height: size || "16", width: size || "16" }) |
|||
export default getIcon |
|||
|
|||
@ -1,62 +1,59 @@ |
|||
<script> |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import {store} from "../builderStore"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
|||
|
|||
export let left; |
|||
let confirmDelete = false; |
|||
const openConfirmDelete = () => { |
|||
confirmDelete = true; |
|||
} |
|||
|
|||
const deleteCurrentNode = () => { |
|||
confirmDelete = false; |
|||
store.deleteCurrentNode(); |
|||
} |
|||
|
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import { store } from "../builderStore" |
|||
import Modal from "../common/Modal.svelte" |
|||
import ErrorsBox from "../common/ErrorsBox.svelte" |
|||
|
|||
export let left |
|||
let confirmDelete = false |
|||
const openConfirmDelete = () => { |
|||
confirmDelete = true |
|||
} |
|||
|
|||
const deleteCurrentNode = () => { |
|||
confirmDelete = false |
|||
store.deleteCurrentNode() |
|||
} |
|||
</script> |
|||
|
|||
<div class="root" style="left: {left}"> |
|||
|
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={store.saveCurrentNode}> |
|||
{#if $store.currentNodeIsNew} |
|||
Create |
|||
{:else} |
|||
Update |
|||
{/if} |
|||
</Button> |
|||
|
|||
{#if !$store.currentNodeIsNew} |
|||
<Button color="tertiary" grouped on:click={openConfirmDelete}> |
|||
Delete |
|||
</Button> |
|||
{/if} |
|||
</ButtonGroup> |
|||
|
|||
{#if !!$store.errors && $store.errors.length > 0} |
|||
<ButtonGroup> |
|||
<Button color="secondary" grouped on:click={store.saveCurrentNode}> |
|||
{#if $store.currentNodeIsNew}Create{:else}Update{/if} |
|||
</Button> |
|||
|
|||
{#if !$store.currentNodeIsNew} |
|||
<Button color="tertiary" grouped on:click={openConfirmDelete}> |
|||
Delete |
|||
</Button> |
|||
{/if} |
|||
</ButtonGroup> |
|||
|
|||
{#if !!$store.errors && $store.errors.length > 0} |
|||
<div style="width: 500px"> |
|||
<ErrorsBox errors={$store.errors}/> |
|||
<ErrorsBox errors={$store.errors} /> |
|||
</div> |
|||
{/if} |
|||
|
|||
<Modal bind:isOpen={confirmDelete}> |
|||
<div style="margin: 10px 0px 20px 0px">Are you sure you want to delete {$store.currentNode.name} ?</div> |
|||
<div style="float:right"> |
|||
<Button color="primary" on:click={deleteCurrentNode}>Yes</Button> |
|||
<Button color="secondary" on:click={() => confirmDelete = false}>No</Button> |
|||
</div> |
|||
</Modal> |
|||
{/if} |
|||
|
|||
<Modal bind:isOpen={confirmDelete}> |
|||
<div style="margin: 10px 0px 20px 0px"> |
|||
Are you sure you want to delete {$store.currentNode.name} ? |
|||
</div> |
|||
<div style="float:right"> |
|||
<Button color="primary" on:click={deleteCurrentNode}>Yes</Button> |
|||
<Button color="secondary" on:click={() => (confirmDelete = false)}> |
|||
No |
|||
</Button> |
|||
</div> |
|||
</Modal> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
padding: 1.5rem; |
|||
width: 100%; |
|||
align-items: right; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,20 +1,16 @@ |
|||
<script> |
|||
import RecordCard from "./RecordCard.svelte" |
|||
|
|||
import RecordCard from "./RecordCard.svelte"; |
|||
|
|||
export let record = {}; |
|||
|
|||
export let record = {} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="title">{record.name}</div> |
|||
<div class="inner"> |
|||
<div class="node-path">{record.nodeKey()}</div> |
|||
</div> |
|||
<div class="title">{record.name}</div> |
|||
<div class="inner"> |
|||
<div class="node-path">{record.nodeKey()}</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
|
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,82 +1,85 @@ |
|||
<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"; |
|||
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 hierarchyWidth = "200px"; |
|||
const defaultNewIndexActions = [ |
|||
{ |
|||
label: "New Root Index", |
|||
onclick: store.newRootIndex, |
|||
}, |
|||
] |
|||
|
|||
const defaultNewIndexActions = [{ |
|||
label:"New Root Index", |
|||
onclick: store.newRootIndex |
|||
}]; |
|||
const defaultNewRecordActions = [ |
|||
{ |
|||
label: "New Root Record", |
|||
onclick: store.newRootRecord, |
|||
}, |
|||
] |
|||
|
|||
const defaultNewRecordActions = [{ |
|||
label:"New Root Record", |
|||
onclick: store.newRootRecord |
|||
}]; |
|||
let newIndexActions = defaultNewIndexActions |
|||
let newRecordActions = defaultNewRecordActions |
|||
|
|||
let newIndexActions = defaultNewIndexActions; |
|||
let newRecordActions = defaultNewRecordActions; |
|||
|
|||
store.subscribe(db => { |
|||
if(!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
|||
newRecordActions = defaultNewRecordActions; |
|||
newIndexActions = defaultNewIndexActions; |
|||
store.subscribe(db => { |
|||
if (!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
|||
newRecordActions = defaultNewRecordActions |
|||
newIndexActions = defaultNewIndexActions |
|||
} else { |
|||
newRecordActions = [ |
|||
...defaultNewRecordActions, |
|||
{label: `New Child Record of ${db.currentNode.name}`, |
|||
onclick: store.newChildRecord} |
|||
]; |
|||
newRecordActions = [ |
|||
...defaultNewRecordActions, |
|||
{ |
|||
label: `New Child Record of ${db.currentNode.name}`, |
|||
onclick: store.newChildRecord, |
|||
}, |
|||
] |
|||
|
|||
newIndexActions = [ |
|||
...defaultNewIndexActions, |
|||
{label: `New Index on ${db.currentNode.name}`, |
|||
onclick: store.newChildIndex} |
|||
]; |
|||
newIndexActions = [ |
|||
...defaultNewIndexActions, |
|||
{ |
|||
label: `New Index on ${db.currentNode.name}`, |
|||
onclick: store.newChildIndex, |
|||
}, |
|||
] |
|||
} |
|||
}); |
|||
|
|||
|
|||
}) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="actions-header"> |
|||
{#if $store.currentNode} |
|||
<ActionsHeader left={hierarchyWidth}/> |
|||
{/if} |
|||
</div> |
|||
<div class="node-view"> |
|||
{#if !$store.currentNode} |
|||
<h1 style="margin-left: 100px">:)</h1> |
|||
{:else if $store.currentNode.type === "record"} |
|||
<RecordView /> |
|||
{:else} |
|||
<IndexView /> |
|||
{/if} |
|||
</div> |
|||
<div class="actions-header"> |
|||
{#if $store.currentNode} |
|||
<ActionsHeader left={hierarchyWidth} /> |
|||
{/if} |
|||
</div> |
|||
<div class="node-view"> |
|||
{#if !$store.currentNode} |
|||
<h1 style="margin-left: 100px">:)</h1> |
|||
{:else if $store.currentNode.type === 'record'} |
|||
<RecordView /> |
|||
{:else} |
|||
<IndexView /> |
|||
{/if} |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
position: relative; |
|||
} |
|||
} |
|||
|
|||
.actions-header { |
|||
.actions-header { |
|||
flex: 0 1 auto; |
|||
} |
|||
} |
|||
|
|||
.node-view { |
|||
.node-view { |
|||
overflow-y: auto; |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,121 +1,158 @@ |
|||
<script> |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import ButtonGroup from "../common/ButtonGroup.svelte"; |
|||
import NumberBox from "../common/NumberBox.svelte"; |
|||
import ValuesList from "../common/ValuesList.svelte"; |
|||
import ErrorsBox from "../common/ErrorsBox.svelte"; |
|||
import Checkbox from "../common/Checkbox.svelte"; |
|||
import DatePicker from "../common/DatePicker.svelte"; |
|||
import {cloneDeep, assign, keys, |
|||
isNumber, includes, map, isBoolean} from "lodash/fp"; |
|||
import {allTypes, validate, getPotentialReferenceIndexes, |
|||
getDefaultTypeOptions, getNode, |
|||
getPotentialReverseReferenceIndexes} from "../common/core"; |
|||
|
|||
export let field; |
|||
export let allFields; |
|||
export let onFinished = () => {}; |
|||
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 |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import ButtonGroup from "../common/ButtonGroup.svelte" |
|||
import NumberBox from "../common/NumberBox.svelte" |
|||
import ValuesList from "../common/ValuesList.svelte" |
|||
import ErrorsBox from "../common/ErrorsBox.svelte" |
|||
import Checkbox from "../common/Checkbox.svelte" |
|||
import DatePicker from "../common/DatePicker.svelte" |
|||
import { |
|||
cloneDeep, |
|||
assign, |
|||
keys, |
|||
isNumber, |
|||
includes, |
|||
map, |
|||
isBoolean, |
|||
} from "lodash/fp" |
|||
import { |
|||
allTypes, |
|||
validate, |
|||
getPotentialReferenceIndexes, |
|||
getDefaultTypeOptions, |
|||
getNode, |
|||
getPotentialReverseReferenceIndexes, |
|||
} from "../common/core" |
|||
|
|||
export let field |
|||
export let allFields |
|||
export let onFinished = () => {} |
|||
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 = |
|||
!selectedReverseRefIndex |
|||
$: possibleReverseReferenceIndexes = !selectedReverseRefIndex |
|||
? [] |
|||
: getPotentialReverseReferenceIndexes( |
|||
store.hierarchy, selectedReverseRefIndex); |
|||
|
|||
const typeChanged = (ev) => |
|||
clonedField.typeOptions = getDefaultTypeOptions(ev.detail); |
|||
|
|||
|
|||
const save = () => { |
|||
|
|||
errors = validate.field(allFields)(clonedField); |
|||
if(errors.length > 0) return; |
|||
field.typeOptions = cloneDeep(clonedField.typeOptions); |
|||
onFinished( |
|||
assign(field)(clonedField) |
|||
); |
|||
} |
|||
|
|||
store.hierarchy, |
|||
selectedReverseRefIndex |
|||
) |
|||
|
|||
const typeChanged = ev => |
|||
(clonedField.typeOptions = getDefaultTypeOptions(ev.detail)) |
|||
|
|||
const save = () => { |
|||
errors = validate.field(allFields)(clonedField) |
|||
if (errors.length > 0) return |
|||
field.typeOptions = cloneDeep(clonedField.typeOptions) |
|||
onFinished(assign(field)(clonedField)) |
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<ErrorsBox errors={errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<Dropdown label="Type" bind:selected={clonedField.type} options={keys(allTypes)} on:change={typeChanged} /> |
|||
|
|||
{#if isNew} |
|||
<Textbox label="Field Name" bind:text={clonedField.name} /> |
|||
{:else} |
|||
<div style="font-weight: bold">{clonedField.name}</div> |
|||
{/if} |
|||
|
|||
<Textbox label="Label" bind:text={clonedField.label} /> |
|||
|
|||
{#if clonedField.type === "string"} |
|||
<NumberBox label="Max Length" bind:value={clonedField.typeOptions.maxLength} /> |
|||
<ValuesList label="Values (options)" bind:values={clonedField.typeOptions.values} /> |
|||
<Checkbox label="Declared Values Only" bind:checked={clonedField.typeOptions.allowDeclaredValuesOnly} /> |
|||
{:else if clonedField.type === "bool"} |
|||
<Checkbox label="Allow Null" bind:checked={clonedField.typeOptions.allowNulls} /> |
|||
{:else if clonedField.type === "datetime"} |
|||
<DatePicker label="Min Value" bind:value={clonedField.typeOptions.minValue} /> |
|||
<DatePicker label="Max Value" bind:value={clonedField.typeOptions.maxValue} /> |
|||
{:else if clonedField.type === "number"} |
|||
<NumberBox label="Min Value" bind:value={clonedField.typeOptions.minValue} /> |
|||
<NumberBox label="Max Value" bind:value={clonedField.typeOptions.maxValue} /> |
|||
<NumberBox label="Decimal Places" bind:value={clonedField.typeOptions.decimalPlaces} /> |
|||
{:else if clonedField.type === "reference"} |
|||
<Dropdown label="Lookup Index" |
|||
options={possibleReferenceIndexes} |
|||
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> |
|||
<ErrorsBox {errors} /> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
|
|||
<Dropdown |
|||
label="Type" |
|||
bind:selected={clonedField.type} |
|||
options={keys(allTypes)} |
|||
on:change={typeChanged} /> |
|||
|
|||
{#if isNew} |
|||
<Textbox label="Field Name" bind:text={clonedField.name} /> |
|||
{:else} |
|||
<div style="font-weight: bold">{clonedField.name}</div> |
|||
{/if} |
|||
|
|||
<Textbox label="Label" bind:text={clonedField.label} /> |
|||
|
|||
{#if clonedField.type === 'string'} |
|||
<NumberBox |
|||
label="Max Length" |
|||
bind:value={clonedField.typeOptions.maxLength} /> |
|||
<ValuesList |
|||
label="Values (options)" |
|||
bind:values={clonedField.typeOptions.values} /> |
|||
<Checkbox |
|||
label="Declared Values Only" |
|||
bind:checked={clonedField.typeOptions.allowDeclaredValuesOnly} /> |
|||
{:else if clonedField.type === 'bool'} |
|||
<Checkbox |
|||
label="Allow Null" |
|||
bind:checked={clonedField.typeOptions.allowNulls} /> |
|||
{:else if clonedField.type === 'datetime'} |
|||
<DatePicker |
|||
label="Min Value" |
|||
bind:value={clonedField.typeOptions.minValue} /> |
|||
<DatePicker |
|||
label="Max Value" |
|||
bind:value={clonedField.typeOptions.maxValue} /> |
|||
{:else if clonedField.type === 'number'} |
|||
<NumberBox |
|||
label="Min Value" |
|||
bind:value={clonedField.typeOptions.minValue} /> |
|||
<NumberBox |
|||
label="Max Value" |
|||
bind:value={clonedField.typeOptions.maxValue} /> |
|||
<NumberBox |
|||
label="Decimal Places" |
|||
bind:value={clonedField.typeOptions.decimalPlaces} /> |
|||
{:else if clonedField.type === 'reference'} |
|||
<Dropdown |
|||
label="Lookup Index" |
|||
options={possibleReferenceIndexes} |
|||
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> |
|||
|
|||
<style> |
|||
|
|||
|
|||
|
|||
</style> |
|||
</style> |
|||
|
|||
@ -1,45 +1,42 @@ |
|||
<script> |
|||
|
|||
import {store} from "../builderStore"; |
|||
import {cloneDeep} from "lodash/fp"; |
|||
export let level = 0; |
|||
export let node; |
|||
|
|||
import { store } from "../builderStore" |
|||
import { cloneDeep } from "lodash/fp" |
|||
export let level = 0 |
|||
export let node |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="title" on:click={() => store.selectExistingNode(node.nodeId)} style="padding-left: {20 + (level * 20)}px"> |
|||
{node.name} |
|||
</div> |
|||
{#if node.children} |
|||
<div |
|||
class="title" |
|||
on:click={() => store.selectExistingNode(node.nodeId)} |
|||
style="padding-left: {20 + level * 20}px"> |
|||
{node.name} |
|||
</div> |
|||
{#if node.children} |
|||
{#each node.children as child} |
|||
<svelte:self node={child} |
|||
level={level+1}/> |
|||
<svelte:self node={child} level={level + 1} /> |
|||
{/each} |
|||
{/if} |
|||
{/if} |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
display: block; |
|||
font-size: .9rem; |
|||
font-size: 0.9rem; |
|||
width: 100%; |
|||
cursor: pointer; |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
.title { |
|||
font: var(--fontblack); |
|||
padding-top: 10px; |
|||
padding-right: 5px; |
|||
padding-bottom: 10px; |
|||
color: var(--secondary100); |
|||
} |
|||
} |
|||
|
|||
.title:hover { |
|||
.title:hover { |
|||
background-color: var(--secondary10); |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,76 +1,79 @@ |
|||
<script> |
|||
|
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import CodeArea from "../common/CodeArea.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import Dropdown from "../common/Dropdown.svelte"; |
|||
import {store} from "../builderStore"; |
|||
import {filter, some, map} from "lodash/fp"; |
|||
import {hierarchy as hierarchyFunctions, common} from "../../../core/src"; |
|||
|
|||
const pipe = common.$; |
|||
|
|||
let index; |
|||
let indexableRecords = []; |
|||
|
|||
store.subscribe($store => { |
|||
index = $store.currentNode; |
|||
indexableRecords = pipe($store.hierarchy,[ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isDecendant(index.parent())), |
|||
filter(hierarchyFunctions.isRecord), |
|||
map(n => ({ |
|||
node:n, |
|||
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds) |
|||
})) |
|||
]); |
|||
}); |
|||
|
|||
const toggleAllowedRecord = record => { |
|||
if(record.isallowed) { |
|||
index.allowedRecordNodeIds = filter(id => id !== record.node.nodeId) |
|||
(index.allowedRecordNodeIds); |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import CodeArea from "../common/CodeArea.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import { store } from "../builderStore" |
|||
import { filter, some, map } from "lodash/fp" |
|||
import { hierarchy as hierarchyFunctions, common } from "../../../core/src" |
|||
|
|||
const pipe = common.$ |
|||
|
|||
let index |
|||
let indexableRecords = [] |
|||
|
|||
store.subscribe($store => { |
|||
index = $store.currentNode |
|||
indexableRecords = pipe($store.hierarchy, [ |
|||
hierarchyFunctions.getFlattenedHierarchy, |
|||
filter(hierarchyFunctions.isDecendant(index.parent())), |
|||
filter(hierarchyFunctions.isRecord), |
|||
map(n => ({ |
|||
node: n, |
|||
isallowed: some(id => n.nodeId === id)(index.allowedRecordNodeIds), |
|||
})), |
|||
]) |
|||
}) |
|||
|
|||
const toggleAllowedRecord = record => { |
|||
if (record.isallowed) { |
|||
index.allowedRecordNodeIds = filter(id => id !== record.node.nodeId)( |
|||
index.allowedRecordNodeIds |
|||
) |
|||
} else { |
|||
index.allowedRecordNodeIds.push(record.node.nodeId); |
|||
index.allowedRecordNodeIds.push(record.node.nodeId) |
|||
} |
|||
}; |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<form class="uk-form-horizontal root"> |
|||
<Textbox bind:text={index.name} label="Name"/> |
|||
|
|||
<div class="allowed-records"> |
|||
<div>Records to Index</div> |
|||
{#each indexableRecords as rec} |
|||
<input type="checkbox" checked={rec.isallowed} on:change={() => toggleAllowedRecord(rec)}/> |
|||
<span>{rec.node.name}</span> |
|||
{/each} |
|||
</div> |
|||
<Textbox bind:text={index.name} label="Name" /> |
|||
|
|||
<div class="allowed-records"> |
|||
<div>Records to Index</div> |
|||
{#each indexableRecords as rec} |
|||
<input |
|||
type="checkbox" |
|||
checked={rec.isallowed} |
|||
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> |
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
padding: 15px; |
|||
} |
|||
} |
|||
|
|||
.allowed-records { |
|||
.allowed-records { |
|||
margin: 20px 0px; |
|||
} |
|||
|
|||
.allowed-records > span { |
|||
margin-right:30px; |
|||
} |
|||
} |
|||
|
|||
</style> |
|||
.allowed-records > span { |
|||
margin-right: 30px; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,300 +1,307 @@ |
|||
<script> |
|||
|
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import Button from "../common/Button.svelte"; |
|||
import getIcon from "../common/icon"; |
|||
import FieldView from "./FieldView.svelte"; |
|||
import Modal from "../common/Modal.svelte"; |
|||
import {map, join, filter, some, |
|||
find, keys, isDate} from "lodash/fp"; |
|||
import { store } from "../builderStore"; |
|||
import {common, hierarchy as h} from "../../../core/src"; |
|||
import {templateApi, pipe, validate} from "../common/core"; |
|||
|
|||
let record; |
|||
let getIndexAllowedRecords; |
|||
let editingField = false; |
|||
let fieldToEdit; |
|||
let isNewField = false; |
|||
let newField; |
|||
let editField; |
|||
let deleteField; |
|||
let onFinishedFieldEdit; |
|||
let editIndex; |
|||
|
|||
store.subscribe($store => { |
|||
record = $store.currentNode; |
|||
const flattened = h.getFlattenedHierarchy($store.hierarchy); |
|||
getIndexAllowedRecords = index => |
|||
pipe(index.allowedRecordNodeIds, [ |
|||
filter(id => some(n => n.nodeId === id)(flattened)), |
|||
map(id => find(n => n.nodeId === id) |
|||
(flattened).name), |
|||
join(", ") |
|||
]); |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Button from "../common/Button.svelte" |
|||
import getIcon from "../common/icon" |
|||
import FieldView from "./FieldView.svelte" |
|||
import Modal from "../common/Modal.svelte" |
|||
import { map, join, filter, some, find, keys, isDate } from "lodash/fp" |
|||
import { store } from "../builderStore" |
|||
import { common, hierarchy as h } from "../../../core/src" |
|||
import { templateApi, pipe, validate } from "../common/core" |
|||
|
|||
let record |
|||
let getIndexAllowedRecords |
|||
let editingField = false |
|||
let fieldToEdit |
|||
let isNewField = false |
|||
let newField |
|||
let editField |
|||
let deleteField |
|||
let onFinishedFieldEdit |
|||
let editIndex |
|||
|
|||
store.subscribe($store => { |
|||
record = $store.currentNode |
|||
const flattened = h.getFlattenedHierarchy($store.hierarchy) |
|||
getIndexAllowedRecords = index => |
|||
pipe(index.allowedRecordNodeIds, [ |
|||
filter(id => some(n => n.nodeId === id)(flattened)), |
|||
map(id => find(n => n.nodeId === id)(flattened).name), |
|||
join(", "), |
|||
]) |
|||
|
|||
newField = () => { |
|||
isNewField = true; |
|||
fieldToEdit = templateApi($store.hierarchy).getNewField("string"); |
|||
editingField = true; |
|||
isNewField = true |
|||
fieldToEdit = templateApi($store.hierarchy).getNewField("string") |
|||
editingField = true |
|||
} |
|||
|
|||
|
|||
onFinishedFieldEdit = (field) => { |
|||
if(field) { |
|||
store.saveField(field); |
|||
} |
|||
editingField = false; |
|||
onFinishedFieldEdit = field => { |
|||
if (field) { |
|||
store.saveField(field) |
|||
} |
|||
editingField = false |
|||
} |
|||
|
|||
editField = (field) => { |
|||
isNewField = false; |
|||
fieldToEdit = field; |
|||
editingField = true; |
|||
editField = field => { |
|||
isNewField = false |
|||
fieldToEdit = field |
|||
editingField = true |
|||
} |
|||
|
|||
deleteField = (field) => { |
|||
store.deleteField(field); |
|||
deleteField = field => { |
|||
store.deleteField(field) |
|||
} |
|||
|
|||
editIndex = index => { |
|||
store.selectExistingNode(index.nodeId); |
|||
store.selectExistingNode(index.nodeId) |
|||
} |
|||
|
|||
}) |
|||
|
|||
let getTypeOptionsValueText = value => { |
|||
if(value === Number.MAX_SAFE_INTEGER |
|||
|| value === Number.MIN_SAFE_INTEGER |
|||
|| new Date(value).getTime() === new Date(8640000000000000).getTime() |
|||
|| new Date(value).getTime() === new Date(-8640000000000000).getTime()) return "(any)"; |
|||
|
|||
if(value === null) return "(not set)"; |
|||
return value; |
|||
} |
|||
|
|||
let getTypeOptions = typeOptions => |
|||
}) |
|||
|
|||
let getTypeOptionsValueText = value => { |
|||
if ( |
|||
value === Number.MAX_SAFE_INTEGER || |
|||
value === Number.MIN_SAFE_INTEGER || |
|||
new Date(value).getTime() === new Date(8640000000000000).getTime() || |
|||
new Date(value).getTime() === new Date(-8640000000000000).getTime() |
|||
) |
|||
return "(any)" |
|||
|
|||
if (value === null) return "(not set)" |
|||
return value |
|||
} |
|||
|
|||
let getTypeOptions = typeOptions => |
|||
pipe(typeOptions, [ |
|||
keys, |
|||
map(k => `<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText(typeOptions[k])}`), |
|||
join("<br>") |
|||
]); |
|||
|
|||
const nameChanged = ev => { |
|||
const pluralName = n => `${n}s`; |
|||
if(record.collectionName === "") { |
|||
record.collectionName = pluralName(ev.target.value); |
|||
keys, |
|||
map( |
|||
k => |
|||
`<span style="color:var(--slate)">${k}: </span>${getTypeOptionsValueText( |
|||
typeOptions[k] |
|||
)}` |
|||
), |
|||
join("<br>"), |
|||
]) |
|||
|
|||
const nameChanged = ev => { |
|||
const pluralName = n => `${n}s` |
|||
if (record.collectionName === "") { |
|||
record.collectionName = pluralName(ev.target.value) |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<form class="uk-form-horizontal"> |
|||
<h3 class="settings-title"> |
|||
Settings |
|||
</h3> |
|||
|
|||
<Textbox label="Name:" bind:text={record.name} on:change={nameChanged}/> |
|||
{#if !record.isSingle} |
|||
<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) |
|||
<form class="uk-form-horizontal"> |
|||
<h3 class="settings-title">Settings</h3> |
|||
|
|||
<Textbox label="Name:" bind:text={record.name} on:change={nameChanged} /> |
|||
{#if !record.isSingle} |
|||
<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 editingField} |
|||
<Modal bind:isOpen={editingField} onClosed={() => onFinishedFieldEdit(false) }> |
|||
<FieldView field={fieldToEdit} |
|||
onFinished={onFinishedFieldEdit} |
|||
allFields={record.fields} |
|||
store={$store}/> |
|||
{#if record.fields.length > 0} |
|||
<table class="fields-table uk-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>Name</th> |
|||
<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> |
|||
{/if} |
|||
{/if} |
|||
|
|||
<h3 class="title"> |
|||
Indexes |
|||
</h3> |
|||
<h3 class="title">Indexes</h3> |
|||
|
|||
{#each record.indexes as index} |
|||
{#each record.indexes as index} |
|||
<div class="index-container"> |
|||
<div class="index-name"> |
|||
{index.name} |
|||
<span style="margin-left: 7px" on:click={() => editIndex(index)}>{@html getIcon("edit")}</span> |
|||
</div> |
|||
<div class="index-field-row"> |
|||
<span class="index-label">records indexed: </span> |
|||
<span>{getIndexAllowedRecords(index)}</span> |
|||
<span class="index-label" style="margin-left: 15px">type:</span> |
|||
<span>{index.indexType}</span> |
|||
</div> |
|||
<div class="index-name"> |
|||
{index.name} |
|||
<span style="margin-left: 7px" on:click={() => editIndex(index)}> |
|||
{@html getIcon('edit')} |
|||
</span> |
|||
</div> |
|||
<div class="index-field-row"> |
|||
<span class="index-label">records indexed:</span> |
|||
<span>{getIndexAllowedRecords(index)}</span> |
|||
<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"> |
|||
<span class="index-label">map:</span> |
|||
<code class="index-mapfilter">{index.map}</code> |
|||
<span class="index-label">filter:</span> |
|||
<code class="index-mapfilter">{index.filter}</code> |
|||
</div> |
|||
{#if index.filter} |
|||
<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. |
|||
{/if} |
|||
</div> |
|||
{/each} |
|||
{:else} |
|||
<div class="no-indexes">No indexes added.</div> |
|||
{/each} |
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
height: 100%; |
|||
padding: 2rem; |
|||
} |
|||
} |
|||
|
|||
.settings-title { |
|||
.settings-title { |
|||
font-weight: 700; |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
.title { |
|||
margin: 3rem 0rem 0rem 0rem; |
|||
font-weight: 700; |
|||
} |
|||
} |
|||
|
|||
.recordkey { |
|||
.recordkey { |
|||
font-size: 14px; |
|||
font-weight: 600; |
|||
color: var(--primary100); |
|||
} |
|||
} |
|||
|
|||
.fields-table { |
|||
.fields-table { |
|||
margin: 1rem 1rem 0rem 0rem; |
|||
border-collapse:collapse; |
|||
} |
|||
border-collapse: collapse; |
|||
} |
|||
|
|||
.add-field-button { |
|||
cursor:pointer; |
|||
} |
|||
.add-field-button { |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.edit-button { |
|||
cursor:pointer; |
|||
.edit-button { |
|||
cursor: pointer; |
|||
color: var(--secondary25); |
|||
} |
|||
} |
|||
|
|||
.edit-button:hover { |
|||
cursor:pointer; |
|||
.edit-button:hover { |
|||
cursor: pointer; |
|||
color: var(--secondary75); |
|||
} |
|||
} |
|||
|
|||
th { |
|||
text-align: left; |
|||
} |
|||
th { |
|||
text-align: left; |
|||
} |
|||
|
|||
td { |
|||
td { |
|||
padding: 1rem 5rem 1rem 0rem; |
|||
margin:0; |
|||
margin: 0; |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
} |
|||
|
|||
.field-label { |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
} |
|||
.field-label { |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
thead > tr { |
|||
thead > tr { |
|||
border-width: 0px 0px 1px 0px; |
|||
border-style: solid; |
|||
border-color: var(--secondary75); |
|||
margin-bottom: 20px; |
|||
} |
|||
} |
|||
|
|||
tbody > tr { |
|||
tbody > tr { |
|||
border-width: 0px 0px 1px 0px; |
|||
border-style: solid; |
|||
border-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
tbody > tr:hover { |
|||
tbody > tr:hover { |
|||
background-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
tbody > tr:hover .edit-button { |
|||
tbody > tr:hover .edit-button { |
|||
color: var(--secondary75); |
|||
} |
|||
} |
|||
|
|||
.index-container { |
|||
.index-container { |
|||
border-style: solid; |
|||
border-width: 0 0 1px 0; |
|||
border-color: var(--secondary25); |
|||
padding: 10px; |
|||
margin-bottom: 5px; |
|||
} |
|||
} |
|||
|
|||
.index-label { |
|||
.index-label { |
|||
color: var(--slate); |
|||
} |
|||
} |
|||
|
|||
.index-name { |
|||
.index-name { |
|||
font-weight: bold; |
|||
color: var(--primary100); |
|||
} |
|||
} |
|||
|
|||
.index-container code { |
|||
.index-container code { |
|||
margin: 0; |
|||
display: inline; |
|||
background-color: var(--primary10); |
|||
color: var(--secondary100); |
|||
padding:3px; |
|||
} |
|||
padding: 3px; |
|||
} |
|||
|
|||
.index-field-row { |
|||
.index-field-row { |
|||
margin: 1rem 0rem 0rem 0rem; |
|||
} |
|||
} |
|||
|
|||
.no-indexes { |
|||
.no-indexes { |
|||
margin: 1rem 0rem 0rem 0rem; |
|||
font-family: var(--fontnormal); |
|||
font-size: 14px; |
|||
} |
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,20 +1,18 @@ |
|||
import App from "./App.svelte"; |
|||
import "./global.css"; |
|||
import "./fonts.css"; |
|||
import "/assets/roboto-v20-latin-ext_latin-300"; |
|||
import "/assets/roboto-v20-latin-ext_latin-400"; |
|||
import "/assets/roboto-v20-latin-ext_latin-500"; |
|||
import "/assets/roboto-v20-latin-ext_latin-700"; |
|||
import "/assets/roboto-v20-latin-ext_latin-900"; |
|||
import "/assets/budibase-logo.png"; |
|||
import "/assets/budibase-logo-only.png"; |
|||
import "uikit/dist/css/uikit.min.css"; |
|||
import "uikit/dist/js/uikit.min.js"; |
|||
import "codemirror/lib/codemirror.css"; |
|||
import 'codemirror/theme/monokai.css'; |
|||
import App from "./App.svelte" |
|||
import "./global.css" |
|||
import "./fonts.css" |
|||
import "/assets/roboto-v20-latin-ext_latin-300" |
|||
import "/assets/roboto-v20-latin-ext_latin-400" |
|||
import "/assets/roboto-v20-latin-ext_latin-500" |
|||
import "/assets/roboto-v20-latin-ext_latin-700" |
|||
import "/assets/roboto-v20-latin-ext_latin-900" |
|||
import "/assets/budibase-logo.png" |
|||
import "/assets/budibase-logo-only.png" |
|||
import "uikit/dist/css/uikit.min.css" |
|||
import "uikit/dist/js/uikit.min.js" |
|||
import "codemirror/lib/codemirror.css" |
|||
import "codemirror/theme/monokai.css" |
|||
|
|||
const app = new App({ |
|||
target: document.getElementById("app") |
|||
}); |
|||
|
|||
|
|||
target: document.getElementById("app"), |
|||
}) |
|||
|
|||
@ -1,163 +1,153 @@ |
|||
<script> |
|||
|
|||
import { store } from "../builderStore"; |
|||
import HierarchyRow from "./HierarchyRow.svelte"; |
|||
import DropdownButton from "../common/DropdownButton.svelte"; |
|||
import {hierarchy as hierarchyFunctions} from "../../../core/src"; |
|||
import NavItem from "./NavItem.svelte"; |
|||
import getIcon from "../common/icon"; |
|||
|
|||
|
|||
const newRootRecord = () => { |
|||
store.newRootRecord(); |
|||
} |
|||
|
|||
const newRootIndex = () => { |
|||
store.newRootIndex(); |
|||
} |
|||
|
|||
const newChildRecord = () => { |
|||
store.newChildRecord(); |
|||
} |
|||
|
|||
const newChildIndex = () => { |
|||
store.newChildIndex(); |
|||
} |
|||
|
|||
const defaultNewChildActions = [ |
|||
import { store } from "../builderStore" |
|||
import HierarchyRow from "./HierarchyRow.svelte" |
|||
import DropdownButton from "../common/DropdownButton.svelte" |
|||
import { hierarchy as hierarchyFunctions } from "../../../core/src" |
|||
import NavItem from "./NavItem.svelte" |
|||
import getIcon from "../common/icon" |
|||
|
|||
const newRootRecord = () => { |
|||
store.newRootRecord() |
|||
} |
|||
|
|||
const newRootIndex = () => { |
|||
store.newRootIndex() |
|||
} |
|||
|
|||
const newChildRecord = () => { |
|||
store.newChildRecord() |
|||
} |
|||
|
|||
const newChildIndex = () => { |
|||
store.newChildIndex() |
|||
} |
|||
|
|||
const defaultNewChildActions = [ |
|||
{ |
|||
label:"New Root Record", |
|||
onclick: newRootRecord |
|||
label: "New Root Record", |
|||
onclick: newRootRecord, |
|||
}, |
|||
{ |
|||
label:"New Root Index", |
|||
onclick: newRootIndex |
|||
} |
|||
]; |
|||
label: "New Root Index", |
|||
onclick: newRootIndex, |
|||
}, |
|||
] |
|||
|
|||
let newChildActions = defaultNewChildActions; |
|||
let newChildActions = defaultNewChildActions |
|||
|
|||
const setActiveNav = (name) => () => { |
|||
store.setActiveNav(name); |
|||
} |
|||
const setActiveNav = name => () => { |
|||
store.setActiveNav(name) |
|||
} |
|||
|
|||
store.subscribe(db => { |
|||
if(!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
|||
newChildActions = defaultNewChildActions; |
|||
store.subscribe(db => { |
|||
if (!db.currentNode || hierarchyFunctions.isIndex(db.currentNode)) { |
|||
newChildActions = defaultNewChildActions |
|||
} else { |
|||
newChildActions = [ |
|||
{ |
|||
label:"New Root Record", |
|||
onclick: newRootRecord |
|||
}, |
|||
{ |
|||
label:"New Root Index", |
|||
onclick: newRootIndex |
|||
}, |
|||
{ |
|||
label: `New Child Record of ${db.currentNode.name}`, |
|||
onclick: newChildRecord |
|||
}, |
|||
{ |
|||
label: `New Index on ${db.currentNode.name}`, |
|||
onclick: newChildIndex |
|||
} |
|||
]; |
|||
newChildActions = [ |
|||
{ |
|||
label: "New Root Record", |
|||
onclick: newRootRecord, |
|||
}, |
|||
{ |
|||
label: "New Root Index", |
|||
onclick: newRootIndex, |
|||
}, |
|||
{ |
|||
label: `New Child Record of ${db.currentNode.name}`, |
|||
onclick: newChildRecord, |
|||
}, |
|||
{ |
|||
label: `New Index on ${db.currentNode.name}`, |
|||
onclick: newChildIndex, |
|||
}, |
|||
] |
|||
} |
|||
}); |
|||
|
|||
}) |
|||
</script> |
|||
|
|||
|
|||
<div class="items-root"> |
|||
<div class="hierarchy"> |
|||
<div class="components-list-container"> |
|||
<div class="nav-group-header"> |
|||
<div>{@html getIcon("database","18")}</div> |
|||
<div class="hierarchy-title">Database</div> |
|||
<DropdownButton iconName="plus" actions={newChildActions} /> |
|||
</div> |
|||
<div class="hierarchy"> |
|||
<div class="components-list-container"> |
|||
<div class="nav-group-header"> |
|||
<div> |
|||
{@html getIcon('database', '18')} |
|||
</div> |
|||
<div class="hierarchy-title">Database</div> |
|||
<DropdownButton iconName="plus" actions={newChildActions} /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="hierarchy-items-container"> |
|||
{#each $store.hierarchy.children as record} |
|||
<HierarchyRow node={record} |
|||
type="record" /> |
|||
{/each} |
|||
<div class="hierarchy-items-container"> |
|||
{#each $store.hierarchy.children as record} |
|||
<HierarchyRow node={record} type="record" /> |
|||
{/each} |
|||
|
|||
{#each $store.hierarchy.indexes as index} |
|||
<HierarchyRow node={index} |
|||
type="index" /> |
|||
{/each} |
|||
</div> |
|||
{#each $store.hierarchy.indexes as index} |
|||
<HierarchyRow node={index} type="index" /> |
|||
{/each} |
|||
</div> |
|||
</div> |
|||
|
|||
<NavItem name="actions" label="Actions & Triggers"/> |
|||
<NavItem name="access levels" label="User Levels"/> |
|||
<NavItem name="actions" label="Actions & Triggers" /> |
|||
<NavItem name="access levels" label="User Levels" /> |
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
|
|||
.items-root { |
|||
.items-root { |
|||
display: flex; |
|||
flex-direction: column; |
|||
max-height: 100%; |
|||
height: 100%; |
|||
background-color: var(--secondary5); |
|||
} |
|||
} |
|||
|
|||
.nav-group-header { |
|||
display:grid; |
|||
.nav-group-header { |
|||
display: grid; |
|||
grid-template-columns: [icon] auto [title] 1fr [button] auto; |
|||
padding: 2rem 1rem 0rem 1rem; |
|||
font-size: .9rem; |
|||
font-size: 0.9rem; |
|||
font-weight: bold; |
|||
} |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(1) { |
|||
padding: 0rem .7rem 0rem 0rem; |
|||
.nav-group-header > div:nth-child(1) { |
|||
padding: 0rem 0.7rem 0rem 0rem; |
|||
vertical-align: bottom; |
|||
grid-column-start: icon; |
|||
margin-right: 5px; |
|||
} |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(2) { |
|||
margin-left:5px; |
|||
.nav-group-header > div:nth-child(2) { |
|||
margin-left: 5px; |
|||
vertical-align: bottom; |
|||
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; |
|||
grid-column-start: button; |
|||
cursor: pointer; |
|||
color: var(--primary75); |
|||
} |
|||
} |
|||
|
|||
.nav-group-header>div:nth-child(3):hover { |
|||
color: var(--primary75); |
|||
} |
|||
.nav-group-header > div:nth-child(3):hover { |
|||
color: var(--primary75); |
|||
} |
|||
|
|||
.hierarchy-title { |
|||
.hierarchy-title { |
|||
flex: auto 1 1; |
|||
} |
|||
|
|||
} |
|||
|
|||
.hierarchy { |
|||
display:flex; |
|||
.hierarchy { |
|||
display: flex; |
|||
flex-direction: column; |
|||
flex: 1 0 auto; |
|||
height: 100px; |
|||
} |
|||
} |
|||
|
|||
.hierarchy-items-container { |
|||
.hierarchy-items-container { |
|||
flex: 1 1 auto; |
|||
overflow-y:auto; |
|||
} |
|||
|
|||
|
|||
</style> |
|||
overflow-y: auto; |
|||
} |
|||
</style> |
|||
|
|||
@ -1,66 +1,63 @@ |
|||
<script> |
|||
|
|||
import {store} from "../builderStore"; |
|||
import {cloneDeep} from "lodash/fp"; |
|||
import getIcon from "../common/icon"; |
|||
export let level = 0; |
|||
export let node; |
|||
export let type; |
|||
|
|||
let navActive = ""; |
|||
$:icon = type==="index" ? "list" : "file"; |
|||
|
|||
store.subscribe(s => { |
|||
if(s.currentNode) |
|||
navActive = (s.activeNav === "database" && node.nodeId === s.currentNode.nodeId |
|||
? "active" : "") |
|||
}); |
|||
|
|||
import { store } from "../builderStore" |
|||
import { cloneDeep } from "lodash/fp" |
|||
import getIcon from "../common/icon" |
|||
export let level = 0 |
|||
export let node |
|||
export let type |
|||
|
|||
let navActive = "" |
|||
$: icon = type === "index" ? "list" : "file" |
|||
|
|||
store.subscribe(s => { |
|||
if (s.currentNode) |
|||
navActive = |
|||
s.activeNav === "database" && node.nodeId === s.currentNode.nodeId |
|||
? "active" |
|||
: "" |
|||
}) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<div class="title {navActive}" on:click={() => store.selectExistingNode(node.nodeId)} style="padding-left: {20 + (level * 20)}px"> |
|||
{@html getIcon(icon, 12)} <span style="margin-left: 1rem">{node.name}</span> |
|||
</div> |
|||
{#if node.children} |
|||
{#each node.children as child} |
|||
<svelte:self node={child} |
|||
level={level+1} |
|||
type="record"/> |
|||
{/each} |
|||
{/if} |
|||
{#if node.indexes} |
|||
{#each node.indexes as index} |
|||
<svelte:self node={index} |
|||
level={level+1} |
|||
type="index"/> |
|||
{/each} |
|||
{/if} |
|||
<div |
|||
class="title {navActive}" |
|||
on:click={() => store.selectExistingNode(node.nodeId)} |
|||
style="padding-left: {20 + level * 20}px"> |
|||
{@html getIcon(icon, 12)} |
|||
<span style="margin-left: 1rem">{node.name}</span> |
|||
</div> |
|||
{#if node.children} |
|||
{#each node.children as child} |
|||
<svelte:self node={child} level={level + 1} type="record" /> |
|||
{/each} |
|||
{/if} |
|||
{#if node.indexes} |
|||
{#each node.indexes as index} |
|||
<svelte:self node={index} level={level + 1} type="index" /> |
|||
{/each} |
|||
{/if} |
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
.root { |
|||
display: block; |
|||
font-size: .9rem; |
|||
font-size: 0.9rem; |
|||
width: 100%; |
|||
cursor: pointer; |
|||
color: var(--secondary50); |
|||
font-weight: 500; |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
padding-top: .5rem; |
|||
padding-right: .5rem; |
|||
} |
|||
.title { |
|||
padding-top: 0.5rem; |
|||
padding-right: 0.5rem; |
|||
} |
|||
|
|||
.title:hover { |
|||
.title:hover { |
|||
background-color: var(--secondary10); |
|||
} |
|||
} |
|||
|
|||
.active { |
|||
.active { |
|||
background-color: var(--primary10); |
|||
} |
|||
|
|||
</style> |
|||
} |
|||
</style> |
|||
|
|||
@ -1,43 +1,35 @@ |
|||
<script> |
|||
import { store } from "../builderStore" |
|||
import getIcon from "../common/icon" |
|||
|
|||
import {store} from "../builderStore"; |
|||
import getIcon from "../common/icon"; |
|||
export let name = "" |
|||
export let label = "" |
|||
|
|||
export let name = ""; |
|||
export let label = ""; |
|||
let navActive = "" |
|||
|
|||
let navActive = ""; |
|||
|
|||
store.subscribe(db => { |
|||
navActive = (db.activeNav === name ? "active" : "") |
|||
}); |
|||
|
|||
const setActive = () => |
|||
store.setActiveNav(name); |
|||
store.subscribe(db => { |
|||
navActive = db.activeNav === name ? "active" : "" |
|||
}) |
|||
|
|||
const setActive = () => store.setActiveNav(name) |
|||
</script> |
|||
|
|||
<div class="nav-item {navActive}" on:click={setActive}> |
|||
{label} |
|||
</div> |
|||
|
|||
<div class="nav-item {navActive}" on:click={setActive}>{label}</div> |
|||
|
|||
<style> |
|||
|
|||
.nav-item { |
|||
.nav-item { |
|||
padding: 1.5rem 1rem 0rem 1rem; |
|||
font-size: .9rem; |
|||
font-size: 0.9rem; |
|||
font-weight: bold; |
|||
cursor: pointer; |
|||
flex: 0 0 auto; |
|||
} |
|||
} |
|||
|
|||
.nav-item:hover { |
|||
.nav-item:hover { |
|||
background-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
.active { |
|||
.active { |
|||
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> |
|||
import { searchAllComponents } from "./pagesParsing/searchComponents" |
|||
import { store } from "../builderStore" |
|||
|
|||
import { searchAllComponents } from "./pagesParsing/searchComponents"; |
|||
import { store } from "../builderStore"; |
|||
export let onComponentChosen = () => {} |
|||
|
|||
export let onComponentChosen = () => {}; |
|||
let phrase = "" |
|||
|
|||
let phrase = ""; |
|||
components = $store.components |
|||
|
|||
components = $store.components; |
|||
|
|||
$: filteredComponents = |
|||
!phrase |
|||
? [] |
|||
: searchAllComponents(components, phrase); |
|||
$: filteredComponents = !phrase ? [] : searchAllComponents(components, phrase) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<form class="uk-search uk-search-large"> |
|||
<span uk-search-icon></span> |
|||
<input class="uk-search-input" |
|||
type="search" |
|||
placeholder="Based on component..." |
|||
bind:value={phrase}> |
|||
</form> |
|||
|
|||
<div> |
|||
{#each filteredComponents as component} |
|||
<div class="component" on:click={() => onComponentChosen(component)}> |
|||
<div class="title">{component.name}</div> |
|||
<div class="description">{component.description}</div> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
<form class="uk-search uk-search-large"> |
|||
<span uk-search-icon /> |
|||
<input |
|||
class="uk-search-input" |
|||
type="search" |
|||
placeholder="Based on component..." |
|||
bind:value={phrase} /> |
|||
</form> |
|||
|
|||
<div> |
|||
{#each filteredComponents as component} |
|||
<div class="component" on:click={() => onComponentChosen(component)}> |
|||
<div class="title">{component.name}</div> |
|||
<div class="description">{component.description}</div> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
|
|||
.component { |
|||
padding:5px; |
|||
.component { |
|||
padding: 5px; |
|||
border-style: solid; |
|||
border-width: 0 0 1px 0; |
|||
border-color: var(--lightslate); |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
|
|||
.component:hover { |
|||
.component:hover { |
|||
background-color: var(--primary10); |
|||
} |
|||
} |
|||
|
|||
.component > .title { |
|||
.component > .title { |
|||
font-size: 13pt; |
|||
color: var(--secondary100); |
|||
} |
|||
} |
|||
|
|||
.component > .description { |
|||
.component > .description { |
|||
font-size: 10pt; |
|||
color: var(--primary75); |
|||
font-style: italic; |
|||
} |
|||
|
|||
} |
|||
</style> |
|||
|
|||