component library modules and definitions refactor - moved to backend. More routing and middleware reorganisation
This commit is contained in:
parent
3564fec064
commit
5b4bce8b8f
|
@ -1,5 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
yarn.lock
|
|
||||||
package-lock.json
|
|
||||||
dist/index.js
|
|
|
@ -1,33 +0,0 @@
|
||||||
*Psst — looking for an app template? Go here --> [sveltejs/template](https://github.com/sveltejs/template)*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# component-template
|
|
||||||
|
|
||||||
A base for building shareable Svelte components. Clone it with [degit](https://github.com/Rich-Harris/degit):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx degit sveltejs/component-template my-new-component
|
|
||||||
cd my-new-component
|
|
||||||
npm install # or yarn
|
|
||||||
```
|
|
||||||
|
|
||||||
Your component's source code lives in `src/index.html`.
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
* [ ] some firm opinions about the best way to test components
|
|
||||||
* [ ] update `degit` so that it automates some of the setup work
|
|
||||||
|
|
||||||
|
|
||||||
## Setting up
|
|
||||||
|
|
||||||
* Run `npm init` (or `yarn init`)
|
|
||||||
* Replace this README with your own
|
|
||||||
|
|
||||||
|
|
||||||
## Consuming components
|
|
||||||
|
|
||||||
Your package.json has a `"svelte"` field pointing to `src/index.html`, which allows Svelte apps to import the source code directly, if they are using a bundler plugin like [rollup-plugin-svelte](https://github.com/rollup/rollup-plugin-svelte) or [svelte-loader](https://github.com/sveltejs/svelte-loader) (where [`resolve.mainFields`](https://webpack.js.org/configuration/resolve/#resolve-mainfields) in your webpack config includes `"svelte"`). **This is recommended.**
|
|
||||||
|
|
||||||
For everyone else, `npm run build` will bundle your component's source code into a plain JavaScript module (`index.mjs`) and a UMD script (`index.js`). This will happen automatically when you publish your component to npm, courtesy of the `prepublishOnly` hook in package.json.
|
|
|
@ -1,47 +0,0 @@
|
||||||
{
|
|
||||||
"_lib": "./dist/index.js",
|
|
||||||
"form" : {
|
|
||||||
"importPath": "Form",
|
|
||||||
"name": "Form",
|
|
||||||
"description": "A form - allgned fields with labels",
|
|
||||||
"props" : {
|
|
||||||
"containerClass": "string",
|
|
||||||
"formControls": {
|
|
||||||
"type":"array",
|
|
||||||
"elementDefinition": {
|
|
||||||
"label": "string",
|
|
||||||
"control":"component",
|
|
||||||
"controlPosition": {
|
|
||||||
"type":"options",
|
|
||||||
"options": ["Before Label","After Label"],
|
|
||||||
"default": "After Label"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": ["form"]
|
|
||||||
},
|
|
||||||
"nav": {
|
|
||||||
"importPath": "Nav",
|
|
||||||
"name": "Nav",
|
|
||||||
"description": "A nav - a side bar of buttons that control the currently active component",
|
|
||||||
"props" : {
|
|
||||||
"items": {
|
|
||||||
"type": "array",
|
|
||||||
"elementDefinition" : {
|
|
||||||
"title": "string",
|
|
||||||
"component": "component"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"selectedItem":"string",
|
|
||||||
"pills":"bool",
|
|
||||||
"orientation":{"type":"options", "options": ["horizontal", "vertical"]},
|
|
||||||
"alignment":{"type":"options", "options": ["start", "center", "end"]},
|
|
||||||
"fill":"bool",
|
|
||||||
"hideNavBar":"bool",
|
|
||||||
"className": "string"
|
|
||||||
|
|
||||||
},
|
|
||||||
"tags": ["nav", "navigation", "sidebar"]
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
|
@ -1,36 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@budibase/bootstrap-components",
|
|
||||||
"svelte": "src/index.svelte",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"module": "dist/index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "rollup -c",
|
|
||||||
"prepublishOnly": "npm run build",
|
|
||||||
"testbuild": "rollup -w -c rollup.testconfig.js",
|
|
||||||
"dev": "run-p start:dev testbuild",
|
|
||||||
"start:dev": "sirv public --single --dev",
|
|
||||||
"publishdev": "yarn build && node ./scripts/publishDev.js"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@budibase/client": "^0.0.32",
|
|
||||||
"fs-extra": "^8.1.0",
|
|
||||||
"lodash": "^4.17.15",
|
|
||||||
"npm-run-all": "^4.1.5",
|
|
||||||
"rollup": "^1.11.0",
|
|
||||||
"rollup-plugin-commonjs": "^10.0.2",
|
|
||||||
"rollup-plugin-json": "^4.0.0",
|
|
||||||
"rollup-plugin-livereload": "^1.0.1",
|
|
||||||
"rollup-plugin-node-resolve": "^5.0.0",
|
|
||||||
"rollup-plugin-svelte": "^5.0.0",
|
|
||||||
"rollup-plugin-terser": "^5.1.1",
|
|
||||||
"shortid": "^2.2.15",
|
|
||||||
"sirv-cli": "^0.4.4",
|
|
||||||
"svelte": "^3.12.1"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"svelte"
|
|
||||||
],
|
|
||||||
"version": "0.0.32",
|
|
||||||
"license": "MIT",
|
|
||||||
"gitHead": "b1f4f90927d9e494e513220ef060af28d2d42455"
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
|
@ -1,13 +0,0 @@
|
||||||
#current_component.svelte-1xqz9vm{height:100%;width:100%}
|
|
||||||
.root.svelte-10kw8to{display:grid}
|
|
||||||
.root.svelte-crnq0a{height:100%;display:grid;grid-template-columns:[left] 1fr [middle] auto [right] 1fr;grid-template-rows:[top] 1fr [center] auto [bottom] 1fr}.content.svelte-crnq0a{grid-column-start:middle;grid-row-start:center;width:400px}.logo-container.svelte-crnq0a{margin-bottom:20px
|
|
||||||
}.logo-container.svelte-crnq0a>img.svelte-crnq0a{max-width:100%}.login-button-container.svelte-crnq0a{text-align:right;margin-top:20px}.incorrect-details-panel.svelte-crnq0a{margin-top:30px;padding:10px;border-style:solid;border-width:1px;border-color:maroon;border-radius:1px;text-align:center;color:maroon;background-color:mistyrose}.form-root.svelte-crnq0a{display:grid;grid-template-columns:[label] auto [control] 1fr}.label.svelte-crnq0a{grid-column-start:label;padding:5px 10px;vertical-align:middle}.control.svelte-crnq0a{grid-column-start:control;padding:5px 10px}.default-input.svelte-crnq0a{font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;width:100%}.default-button.svelte-crnq0a{font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;color:#333;background-color:#f4f4f4;outline:none}.default-button.svelte-crnq0a:active{background-color:#ddd}.default-button.svelte-crnq0a:focus{border-color:#666}
|
|
||||||
.form-root.svelte-m9d6ue{display:grid;grid-template-columns:[label] auto [control] 1fr}.label.svelte-m9d6ue{grid-column-start:label;padding:5px 10px;vertical-align:middle}.control.svelte-m9d6ue{grid-column-start:control;padding:5px 10px}.overflow.svelte-m9d6ue{grid-column-start:overflow}.full-width.svelte-m9d6ue{width:100%}
|
|
||||||
.root.svelte-aihwli{height:100%;width:100%;grid-template-columns:[navbar] auto [content] 1fr;display:grid}.navbar.svelte-aihwli{grid-column:navbar;background:var(--navBarBackground);border:var(--navBarBorder);color:var(--navBarColor)}.navitem.svelte-aihwli{padding:10px 17px;cursor:pointer}.navitem.svelte-aihwli:hover{background:var(--itemHoverBackground);color:var(--itemHoverColor)}.navitem.selected.svelte-aihwli{background:var(--selectedItemBackground);border:var(--selectedItemBorder);color:var(--selectedItemColor)}.content.svelte-aihwli{grid-column:content}
|
|
||||||
.default.svelte-1ec4wqj{width:100%;font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;width:100%}.default.svelte-1ec4wqj:disabled{color:#ccc}
|
|
||||||
.panel.svelte-6yfcjx:hover{background:var(--hoverBackground);color:var(--hoverColor)}
|
|
||||||
.horizontal.svelte-osi0db{display:inline-block}.vertical.svelte-osi0db{display:block}
|
|
||||||
.table-default.svelte-h8rqk6{width:100%;margin-bottom:1rem;color:#212529;border-collapse:collapse}.table-default.svelte-h8rqk6 .thead-default .th-default.svelte-h8rqk6{vertical-align:bottom;border-bottom:2px solid #dee2e6;font-weight:bold}.table-default.svelte-h8rqk6 .th-default.svelte-h8rqk6{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6;font-weight:normal}.th-default.svelte-h8rqk6{text-align:inherit}.table-default.svelte-h8rqk6 .tbody-default .tr-default.svelte-h8rqk6:hover{color:#212529;background-color:rgba(0,0,0,.075);cursor:pointer}
|
|
||||||
.default.svelte-1q8lga0{font-family:inherit;font-size:inherit;padding:0.4em;margin:0 0 0.5em 0;box-sizing:border-box;border:1px solid #ccc;border-radius:2px;color:#333;background-color:#f4f4f4;outline:none}.default.svelte-1q8lga0:active{background-color:#ddd}.default.svelte-1q8lga0:focus{border-color:#666}
|
|
||||||
|
|
||||||
/*# sourceMappingURL=bundle.css.map */
|
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"file": "bundle.css",
|
|
||||||
"sources": [
|
|
||||||
"..\\src\\Test\\TestApp.svelte",
|
|
||||||
"..\\src\\Grid.svelte",
|
|
||||||
"..\\src\\Login.svelte",
|
|
||||||
"..\\src\\Form.svelte",
|
|
||||||
"..\\src\\Nav.svelte",
|
|
||||||
"..\\src\\Textbox.svelte",
|
|
||||||
"..\\src\\Panel.svelte",
|
|
||||||
"..\\src\\StackPanel.svelte",
|
|
||||||
"..\\src\\Table.svelte",
|
|
||||||
"..\\src\\Button.svelte"
|
|
||||||
],
|
|
||||||
"sourcesContent": [
|
|
||||||
"<script>\nimport createApp from \"./createApp\";\nimport { props } from \"./props\";\n\nlet _bb;\n\nconst _appPromise = createApp();\n_appPromise.then(a => _bb = a);\n\nconst testProps = props.hiddenNav;\n\nlet currentComponent;\n\n$: {\n if(_bb && currentComponent) {\n _bb.initialiseComponent(testProps, currentComponent);\n }\n}\n\n\n\n</script>\n\n{#await _appPromise}\nloading\n{:then _bb}\n\n<div id=\"current_component\" bind:this={currentComponent}>\n</div>\n\n{/await}\n\n\n<style>\n#current_component {\n height: 100%;\n width: 100%;\n}\n</style>\n\n",
|
|
||||||
"<script>\r\nimport { onMount } from 'svelte'\r\nimport {buildStyle} from \"./buildStyle\";\r\n\r\nexport let gridTemplateRows =\"\";\r\nexport let gridTemplateColumns =\"\";\r\nexport let children = [];\r\nexport let width = \"auto\";\r\nexport let height = \"auto\";\r\nexport let containerClass=\"\";\r\nexport let itemContainerClass=\"\";\r\n\r\n/*\"gridColumnStart\":\"string\",\r\n\"gridColumnEnd\":\"string\",\r\n\"gridRowStart\":\"string\",\r\n\"gridRowEnd\":\"string\"*/\r\n\r\n\r\nexport let _bb;\r\n\r\nlet style=\"\";\r\nlet htmlElements = {};\r\n\r\n$ : {\r\n if(_bb && htmlElements) {\r\n for(let el in htmlElements) {\r\n _bb.initialiseComponent(\r\n children[el].control,\r\n htmlElements[el]\r\n );\r\n }\r\n }\r\n}\r\n\r\nconst childStyle = child => \r\n buildStyle({\r\n \"grid-column-start\": child.gridColumnStart,\r\n \"grid-column-end\": child.gridColumnEnd,\r\n \"grid-column\": child.gridColumn,\r\n \"grid-row-start\": child.gridRowStart,\r\n \"grid-row-end\": child.gridRowStart,\r\n \"grid-row\": child.gridRow,\r\n });\r\n\r\n</script>\r\n\r\n<div class=\"root {containerClass}\"\r\n style=\"width: {width}; height: {height}; grid-template-columns: {gridTemplateColumns}; grid-template-rows: {gridTemplateRows};\">\r\n {#each children as child, index}\r\n <div class=\"{itemContainerClass}\"\r\n style={childStyle(child)}\r\n bind:this={htmlElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n display: grid;\r\n}\r\n\r\n</style>",
|
|
||||||
"<script>\n\nimport Textbox from \"./Textbox.svelte\";\nimport Form from \"./Form.svelte\";\nimport Button from \"./Button.svelte\";\n\nexport let usernameLabel = \"Username\";\nexport let passwordLabel = \"Password\";\nexport let loginButtonLabel = \"Login\";\nexport let loginRedirect = \"\";\nexport let logo = \"\";\nexport let buttonClass = \"\";\nexport let inputClass=\"\"\n\nexport let _bb;\n\nlet username = \"\";\nlet password = \"\";\nlet busy = false;\nlet incorrect = false;\nlet _logo = \"\";\nlet _buttonClass = \"\";\nlet _inputClass = \"\";\n\n$: {\n _logo = _bb.relativeUrl(logo);\n _buttonClass = buttonClass || \"default-button\";\n _inputClass = inputClass || \"default-input\";\n}\n\nconst login = () => {\n busy = true;\n _bb.api.post(\"/api/authenticate\", {username, password})\n .then(r => {\n busy = false;\n if(r.status === 200) {\n return r.json();\n } else {\n incorrect = true;\n return;\n }\n })\n .then(user => {\n if(user) {\n localStorage.setItem(\"budibase:user\", user);\n location.reload();\n }\n })\n}\n\n</script>\n\n<div class=\"root\">\n\n <div class=\"content\">\n\n {#if _logo}\n <div class=\"logo-container\">\n <img src={_logo} alt=\"logo\"/>\n </div>\n {/if}\n\n <div class=\"form-root\">\n <div class=\"label\">\n {usernameLabel}\n </div>\n <div class=\"control\">\n <input bind:value={username} type=\"text\" class={_inputClass}/>\n </div>\n <div class=\"label\">\n {passwordLabel}\n </div>\n <div class=\"control\">\n <input bind:value={password} type=\"password\" class={_inputClass}/>\n </div>\n </div>\n\n <div class=\"login-button-container\">\n <button disabled={busy} \n on:click={login}\n class={_buttonClass}>\n {loginButtonLabel}\n </button>\n </div>\n\n {#if incorrect}\n <div class=\"incorrect-details-panel\">\n Incorrect username or password\n </div>\n {/if}\n\n </div>\n\n</div>\n\n<style>\n\n.root {\n height: 100%;\n display:grid;\n grid-template-columns: [left] 1fr [middle] auto [right] 1fr;\n grid-template-rows: [top] 1fr [center] auto [bottom] 1fr;\n}\n\n.content {\n grid-column-start: middle;\n grid-row-start: center;\n width: 400px;\n}\n\n.logo-container {\n margin-bottom: 20px\n}\n\n.logo-container > img {\n max-width: 100%;\n}\n\n.login-button-container {\n text-align: right;\n margin-top: 20px;\n}\n\n.incorrect-details-panel {\n margin-top: 30px;\n padding: 10px;\n border-style: solid;\n border-width: 1px;\n border-color: maroon;\n border-radius: 1px;\n text-align: center;\n color: maroon;\n background-color: mistyrose;\n}\n\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n\n.default-input {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default-button {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default-button:active {\n\tbackground-color: #ddd;\n}\n\n.default-button:focus {\n\tborder-color: #666;\n}\n\n</style>",
|
|
||||||
"<script>\nexport let containerClass = \"\";\nexport let formControls = [];\n\nexport let _bb;\n\nlet htmlElements = {};\nlet labels = {};\n\n$ : {\n let cIndex = 0;\n for(let c of formControls) {\n labels[cIndex] = c.label;\n cIndex++;\n }\n\n if(_bb && htmlElements) {\n for(let el in htmlElements) {\n _bb.initialiseComponent(\n formControls[el].control,\n htmlElements[el]\n );\n }\n }\n}\n\n</script>\n\n<div class=\"form-root {containerClass}\">\n {#each formControls as child, index}\n <div class=\"label\">{labels[index]}</div>\n <div class=\"control\"\n bind:this={htmlElements[index]}>\n </div>\n {/each}\n</div>\n\n<style>\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n.overflow {\n grid-column-start: overflow;\n}\n.full-width {\n width: 100%;\n}\n</style>",
|
|
||||||
"<script>\r\nimport cssVars from \"./cssVars\";\r\n\r\nexport let navBarBackground = \"\";\r\nexport let navBarBorder=\"\";\r\nexport let navBarColor=\"\";\r\nexport let selectedItemBackground=\"\";\r\nexport let selectedItemColor=\"\";\r\nexport let selectedItemBorder=\"\";\r\nexport let itemHoverBackground=\"\";\r\nexport let itemHoverColor=\"\";\r\nexport let items = [];\r\nexport let hideNavBar=false;\r\nexport let selectedItem=\"\";\r\n\r\nexport let _bb;\r\n\r\nlet selectedIndex = -1;\r\nlet styleVars={};\r\nlet components = {};\r\nlet componentElements = {}\r\n\r\n\r\nconst hasComponentElements = () => \r\n Object.getOwnPropertyNames(componentElements).length > 0;\r\n\r\n$: {\r\n styleVars = {\r\n navBarBackground, navBarBorder,\r\n navBarColor, selectedItemBackground,\r\n selectedItemColor, selectedItemBorder,\r\n itemHoverBackground, itemHoverColor\r\n };\r\n\r\n if(items && items.length > 0 && hasComponentElements()) {\r\n const currentSelectedItem = selectedIndex > 0\r\n ? items[selectedIndex].title\r\n : \"\";\r\n if(selectedItem && currentSelectedItem !== selectedItem) {\r\n let i=0;\r\n for(let item of items) {\r\n if(item.title === selectedItem) {\r\n onSelectItem(i)();\r\n }\r\n i++;\r\n }\r\n } else if(!currentSelectedItem) {\r\n onSelectItem(0);\r\n }\r\n }\r\n}\r\n\r\nconst onSelectItem = (index) => () => {\r\n selectedIndex = index;\r\n if(!components[index]) {\r\n const comp = _bb.initialiseComponent(\r\n items[index].component, componentElements[index]);\r\n components[index] = comp; \r\n }\r\n}\r\n\r\n\r\n</script>\r\n\r\n<div class=\"root\" use:cssVars={styleVars}>\r\n {#if !hideNavBar}\r\n <div class=\"navbar\">\r\n {#each items as navItem, index}\r\n <div class=\"navitem\"\r\n on:click={onSelectItem(index)}\r\n class:selected={selectedIndex === index}>\r\n {navItem.title}\r\n </div>\r\n {/each}\r\n </div>\r\n {/if}\r\n {#each items as navItem, index}\r\n\r\n <div class=\"content\"\r\n bind:this={componentElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n height: 100%;\r\n width:100%;\r\n grid-template-columns: [navbar] auto [content] 1fr;\r\n display: grid;\r\n}\r\n\r\n.navbar {\r\n grid-column: navbar;\r\n background: var(--navBarBackground);\r\n border: var(--navBarBorder);\r\n color: var(--navBarColor);\r\n}\r\n\r\n.navitem {\r\n padding: 10px 17px;\r\n cursor: pointer;\r\n}\r\n\r\n.navitem:hover {\r\n background: var(--itemHoverBackground);\r\n color: var(--itemHoverColor);\r\n}\r\n\r\n.navitem.selected {\r\n background: var(--selectedItemBackground);\r\n border: var(--selectedItemBorder);\r\n color: var(--selectedItemColor);\r\n}\r\n\r\n.content {\r\n grid-column: content;\r\n}\r\n\r\n</style>\r\n\r\n",
|
|
||||||
"<script>\n\nexport let value=\"\";\nexport let hideValue = false;\nexport let className = \"default\";\n\nexport let _bb;\n\nlet actualValue = \"\";\n$: {\n\tif(_bb && value._isstate) {\n\t\t_bb.store.subscribe(s => {\n\t\t\tactualValue = _bb.store.getValue(s, value);\n\t\t});\n\t}\n}\n\nconst onchange = (ev) => {\n\tif(_bb && value._isstate) {\n\t\t_bb.store.setValue(value, ev.target.value);\n\t} else if(!value._isstate) {\n\t\tactualValue = ev.target.value;\n\t}\n}\n\n</script>\n\n{#if hideValue}\n<input class={className} \n\t type=\"password\" \n\t value={actualValue} on:change/>\n{:else}\n<input class={className} type=\"text\" value={actualValue}/>\n{/if}\n\n<style>\n.default {\n width: 100%;\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default:disabled {\n\tcolor: #ccc;\n}\n\n</style>",
|
|
||||||
"<script>\r\nimport {buildStyle} from \"./buildStyle\";\r\nimport cssVars from \"./cssVars\";\r\n\r\nexport let component=\"\";\r\nexport let text=\"\";\r\nexport let containerClass=\"\";\r\nexport let background=\"\";\r\nexport let border=\"\";\r\nexport let borderRadius=\"\";\r\nexport let font=\"\";\r\nexport let display=\"\";\r\nexport let textAlign=\"\";\r\nexport let color=\"\";\r\nexport let padding=\"\";\r\nexport let margin=\"\";\r\nexport let hoverBackground=\"\";\r\nexport let hoverColor=\"\";\r\nexport let onClick;\r\nexport let height;\r\nexport let width;\r\n\r\nexport let _bb;\r\n\r\nlet styleVars;\r\nlet style=\"\";\r\nlet componentElement;\r\n\r\n$: {\r\n style=buildStyle({\r\n border, background, font, margin,\r\n padding, display, color, height, width,\r\n \"text-align\": textAlign,\r\n \"border-radius\":borderRadius,\r\n cursor: onClick ? \"pointer\" : \"none\"\r\n });\r\n\r\n if(_bb && component) {\r\n _bb.initialiseComponent(component, componentElement);\r\n }\r\n\r\n styleVars = {\r\n hoverBackground:hoverBackground || background, \r\n hoverColor:hoverColor || color\r\n }\r\n}\r\n\r\nconst clickHandler = () => {\r\n if(onClick) onClick();\r\n}\r\n\r\n</script>\r\n\r\n<div class=\"{containerClass} panel\" \r\n style={style}\r\n use:cssVars={styleVars}\r\n this:bind={componentElement}\r\n on:click={clickHandler}>\r\n {component && component._component ? \"\" : text}\r\n</div>\r\n\r\n<style>\r\n\r\n.panel:hover {\r\n background: var(--hoverBackground);\r\n color: var(--hoverColor);\r\n\r\n}\r\n\r\n</style>\r\n",
|
|
||||||
"<script>\n\nimport { emptyProps } from \"./emptyProps\";\n\nexport let direction = \"horizontal\";\nexport let children = [];\nexport let width = \"auto\";\nexport let height = \"auto\";\nexport let containerClass=\"\";\nexport let itemContainerClass=\"\";\nexport let onLoad;\n\nexport let data=[];\nexport let dataItemComponent;\n\nexport let _bb;\n\nlet staticHtmlElements = {};\nlet staticComponents = {};\nlet dataBoundElements = {};\nlet dataBoundComponents = {};\n\nlet onLoadCalled = false;\n\nconst hasDataBoundComponents = () => \n Object.getOwnPropertyNames(dataBoundComponents).length > 0;\n\nconst hasData = () => \n Array.isArray(data) && data.length > 0;\n\nconst hasStaticComponents = () => {\n return Object.getOwnPropertyNames(staticComponents).length > 0;\n}\n\n$: {\n\n if(staticHtmlElements) {\n if(hasStaticComponents()) {\n for(let c in staticComponents) {\n staticComponents[c].$destroy();\n }\n staticComponents = {};\n }\n\n for(let el in staticHtmlElements) {\n staticComponents[el] = _bb.initialiseComponent(\n children[el].control,\n staticHtmlElements[el]\n );\n }\n }\n \n\n if(hasDataBoundComponents()) {\n for(let c in dataBoundComponents) {\n dataBoundComponents[c].$destroy();\n }\n dataBoundComponents = {};\n }\n\n if(hasData()) {\n let index = 0;\n for(let d in dataBoundElements) {\n _bb.initialiseComponent(\n dataItemComponent,\n dataBoundElements[d],\n data[parseInt(d)]\n );\n }\n }\n\n if(!onLoadCalled && onLoad && !onLoad.isPlaceholder) {\n onLoad();\n onLoadCalled = true;\n }\n}\n\n\n</script>\n\n<div class=\"root {containerClass}\"\n style=\"width: {width}; height: {height}\">\n\n {#if children}\n {#each children as child, index}\n <div class={direction}>\n <div class=\"{itemContainerClass}\"\n bind:this={staticHtmlElements[index]}>\n </div>\n </div>\n {/each}\n {/if}\n\n {#if data && data.length > 0}\n {#each data as child, index}\n <div class={direction}>\n <div class=\"{itemContainerClass}\"\n bind:this={dataBoundElements[index]}>\n </div>\n </div>\n {/each}\n {/if}\n</div>\n\n<style>\n\n.horizontal {\n display:inline-block;\n}\n\n.vertical {\n display: block;\n}\n\n</style>",
|
|
||||||
"<script>\r\n\r\nexport let columns=[];\r\nexport let data=\"\";\r\nexport let tableClass=\"\";\r\nexport let theadClass=\"\";\r\nexport let tbodyClass=\"\";\r\nexport let trClass=\"\";\r\nexport let thClass=\"\";\r\nexport let onRowClick;\r\n\r\nexport let _bb;\r\n\r\nconst rowClickHandler = (row) => () => {\r\n onRowClick(row);\r\n}\r\n\r\n</script>\r\n\r\n <table class={tableClass}>\r\n <thead class={theadClass}>\r\n <tr class={trClass}>\r\n {#each columns as col}\r\n <th class={thClass}>{col.title}</th>\r\n {/each}\r\n </tr>\r\n </thead>\r\n <tbody class={tbodyClass}>\r\n {#each data as row}\r\n <tr class={trClass}\r\n on:click={rowClickHandler(row)} >\r\n {#each columns as col}\r\n <th class={thClass}>{_bb.getStateOrValue(col.value, row)}</th>\r\n {/each}\r\n </tr>\r\n {/each}\r\n </tbody>\r\n</table> \r\n\r\n<style>\r\n\r\n.table-default {\r\n width: 100%;\r\n margin-bottom: 1rem;\r\n color: #212529;\r\n border-collapse: collapse;\r\n}\r\n\r\n.table-default .thead-default .th-default {\r\n vertical-align: bottom;\r\n border-bottom: 2px solid #dee2e6;\r\n font-weight: bold;\r\n}\r\n\r\n.table-default .th-default {\r\n padding: .75rem;\r\n vertical-align: top;\r\n border-top: 1px solid #dee2e6;\r\n font-weight: normal;\r\n}\r\n\r\n.th-default {\r\n text-align: inherit;\r\n}\r\n\r\n.table-default .tbody-default .tr-default:hover {\r\n color: #212529;\r\n background-color: rgba(0,0,0,.075);\r\n cursor: pointer;\r\n}\r\n\r\n</style>",
|
|
||||||
"<script>\nexport let className = \"default\";\nexport let disabled = false;\nexport let contentText;\nexport let contentComponent;\nexport let onClick = () => {};\n\nexport let _bb;\nlet contentComponentContainer;\n\n$:{\n\tif(_bb && contentComponentContainer && contentComponent._component)\n\t\t_bb.initialiseComponent(contentComponent, contentComponentContainer);\n}\n\n\nconst clickHandler = () => {\n\tif(onClick) onClick();\n}\n\n</script>\n\n\n<button class={className} {disabled} on:click={clickHandler}>\n {#if contentComponent && contentComponent._component}\n\t<div bind:this={contentComponentContainer}>\n\t</div>\n {:else if contentText}\n {contentText}\n {:else}\n <slot />\n {/if}\n</button>\n\n\n<style>\n\n.default {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default:active {\n\tbackground-color: #ddd;\n}\n\n.default:focus {\n\tborder-color: #666;\n}\n\n</style>"
|
|
||||||
],
|
|
||||||
"names": [],
|
|
||||||
"mappings": "AAkCA,kBAAkB,eAAC,CAAC,AAChB,MAAM,CAAE,IAAI,CACZ,KAAK,CAAE,IAAI,AACf,CAAC;ACqBD,KAAK,eAAC,CAAC,AACH,OAAO,CAAE,IAAI,AACjB,CAAC;ACqCD,KAAK,cAAC,CAAC,AACH,MAAM,CAAE,IAAI,CACZ,QAAQ,IAAI,CACZ,qBAAqB,CAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAC3D,kBAAkB,CAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,AAC5D,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,MAAM,CACzB,cAAc,CAAE,MAAM,CACtB,KAAK,CAAE,KAAK,AAChB,CAAC,AAED,eAAe,cAAC,CAAC,AACb,aAAa,CAAE,IAAI;AACvB,CAAC,AAED,6BAAe,CAAG,GAAG,cAAC,CAAC,AACnB,SAAS,CAAE,IAAI,AACnB,CAAC,AAED,uBAAuB,cAAC,CAAC,AACrB,UAAU,CAAE,KAAK,CACjB,UAAU,CAAE,IAAI,AACpB,CAAC,AAED,wBAAwB,cAAC,CAAC,AACtB,UAAU,CAAE,IAAI,CAChB,OAAO,CAAE,IAAI,CACb,YAAY,CAAE,KAAK,CACnB,YAAY,CAAE,GAAG,CACjB,YAAY,CAAE,MAAM,CACpB,aAAa,CAAE,GAAG,CAClB,UAAU,CAAE,MAAM,CAClB,KAAK,CAAE,MAAM,CACb,gBAAgB,CAAE,SAAS,AAC/B,CAAC,AAED,UAAU,cAAC,CAAC,AACR,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,AACrD,CAAC,AAED,MAAM,cAAC,CAAC,AACJ,iBAAiB,CAAE,KAAK,CACxB,OAAO,CAAE,GAAG,CAAC,IAAI,CACjB,cAAc,CAAE,MAAM,AAC1B,CAAC,AACD,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,OAAO,CAC1B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AAED,cAAc,cAAC,CAAC,AACf,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACnB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,eAAe,cAAC,CAAC,AAChB,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACtB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,CACX,gBAAgB,CAAE,OAAO,CACzB,OAAO,CAAE,IAAI,AACd,CAAC,AAED,6BAAe,OAAO,AAAC,CAAC,AACvB,gBAAgB,CAAE,IAAI,AACvB,CAAC,AAED,6BAAe,MAAM,AAAC,CAAC,AACtB,YAAY,CAAE,IAAI,AACnB,CAAC;AC9ID,UAAU,cAAC,CAAC,AACR,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,AACrD,CAAC,AAED,MAAM,cAAC,CAAC,AACJ,iBAAiB,CAAE,KAAK,CACxB,OAAO,CAAE,GAAG,CAAC,IAAI,CACjB,cAAc,CAAE,MAAM,AAC1B,CAAC,AACD,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,OAAO,CAC1B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AACD,SAAS,cAAC,CAAC,AACP,iBAAiB,CAAE,QAAQ,AAC/B,CAAC,AACD,WAAW,cAAC,CAAC,AACT,KAAK,CAAE,IAAI,AACf,CAAC;AC6BD,KAAK,cAAC,CAAC,AACH,MAAM,CAAE,IAAI,CACZ,MAAM,IAAI,CACV,qBAAqB,CAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAClD,OAAO,CAAE,IAAI,AACjB,CAAC,AAED,OAAO,cAAC,CAAC,AACL,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,IAAI,kBAAkB,CAAC,CACnC,MAAM,CAAE,IAAI,cAAc,CAAC,CAC3B,KAAK,CAAE,IAAI,aAAa,CAAC,AAC7B,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,OAAO,CAAE,IAAI,CAAC,IAAI,CAClB,MAAM,CAAE,OAAO,AACnB,CAAC,AAED,sBAAQ,MAAM,AAAC,CAAC,AACZ,UAAU,CAAE,IAAI,qBAAqB,CAAC,CACtC,KAAK,CAAE,IAAI,gBAAgB,CAAC,AAChC,CAAC,AAED,QAAQ,SAAS,cAAC,CAAC,AACf,UAAU,CAAE,IAAI,wBAAwB,CAAC,CACzC,MAAM,CAAE,IAAI,oBAAoB,CAAC,CACjC,KAAK,CAAE,IAAI,mBAAmB,CAAC,AACnC,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,WAAW,CAAE,OAAO,AACxB,CAAC;AClFD,QAAQ,eAAC,CAAC,AACN,KAAK,CAAE,IAAI,CACd,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACnB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,uBAAQ,SAAS,AAAC,CAAC,AAClB,KAAK,CAAE,IAAI,AACZ,CAAC;ACaD,oBAAM,MAAM,AAAC,CAAC,AACV,UAAU,CAAE,IAAI,iBAAiB,CAAC,CAClC,KAAK,CAAE,IAAI,YAAY,CAAC,AAE5B,CAAC;ACuCD,WAAW,cAAC,CAAC,AACT,QAAQ,YAAY,AACxB,CAAC,AAED,SAAS,cAAC,CAAC,AACP,OAAO,CAAE,KAAK,AAClB,CAAC;ACvED,cAAc,cAAC,CAAC,AACZ,KAAK,CAAE,IAAI,CACX,aAAa,CAAE,IAAI,CACnB,KAAK,CAAE,OAAO,CACd,eAAe,CAAE,QAAQ,AAC7B,CAAC,AAED,4BAAc,CAAC,cAAc,CAAC,WAAW,cAAC,CAAC,AACvC,cAAc,CAAE,MAAM,CACtB,aAAa,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAChC,WAAW,CAAE,IAAI,AACrB,CAAC,AAED,4BAAc,CAAC,WAAW,cAAC,CAAC,AACxB,OAAO,CAAE,MAAM,CACf,cAAc,CAAE,GAAG,CACnB,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAC7B,WAAW,CAAE,MAAM,AACvB,CAAC,AAED,WAAW,cAAC,CAAC,AACT,UAAU,CAAE,OAAO,AACvB,CAAC,AAED,4BAAc,CAAC,cAAc,CAAC,yBAAW,MAAM,AAAC,CAAC,AAC7C,KAAK,CAAE,OAAO,CACd,gBAAgB,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAClC,MAAM,CAAE,OAAO,AACnB,CAAC;AChCD,QAAQ,eAAC,CAAC,AACT,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACtB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,CACX,gBAAgB,CAAE,OAAO,CACzB,OAAO,CAAE,IAAI,AACd,CAAC,AAED,uBAAQ,OAAO,AAAC,CAAC,AAChB,gBAAgB,CAAE,IAAI,AACvB,CAAC,AAED,uBAAQ,MAAM,AAAC,CAAC,AACf,YAAY,CAAE,IAAI,AACnB,CAAC"
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,103 +0,0 @@
|
||||||
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: [],
|
|
||||||
allowedModelNodeIds: [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: [],
|
|
||||||
allowedModelNodeIds: [1],
|
|
||||||
nodeId: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "everyones_invoices",
|
|
||||||
type: "index",
|
|
||||||
map: "return {...record};",
|
|
||||||
filter: "",
|
|
||||||
indexType: "ancestor",
|
|
||||||
getShardName: "",
|
|
||||||
getSortKey: "record.id",
|
|
||||||
aggregateGroups: [],
|
|
||||||
allowedModelNodeIds: [2],
|
|
||||||
nodeId: 6,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
nodeId: 0,
|
|
||||||
},
|
|
||||||
componentLibraries: ["budibase-standard-components"],
|
|
||||||
appRootPath: "/testApp2",
|
|
||||||
props: {},
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.1 KiB |
|
@ -1,62 +0,0 @@
|
||||||
html, body {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: #333;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: rgb(0,100,200);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: rgb(0,80,160);
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, button, select, textarea {
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
padding: 0.4em;
|
|
||||||
margin: 0 0 0.5em 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:disabled {
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="range"] {
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
color: #333;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:active {
|
|
||||||
background-color: #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:focus {
|
|
||||||
border-color: #666;
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset='utf8'>
|
|
||||||
<meta name='viewport' content='width=device-width'>
|
|
||||||
|
|
||||||
<title>Svelte app</title>
|
|
||||||
|
|
||||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
|
||||||
<link rel='stylesheet' href='/global.css'>
|
|
||||||
<link rel='stylesheet' href='/bundle.css'>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<script src='/bundle.js'></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,20 +0,0 @@
|
||||||
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(),
|
|
||||||
],
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
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 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",
|
|
||||||
]
|
|
||||||
|
|
||||||
const lodash_exports = [
|
|
||||||
"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",
|
|
||||||
]
|
|
||||||
|
|
||||||
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,
|
|
||||||
}),
|
|
||||||
|
|
||||||
// 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(),
|
|
||||||
|
|
||||||
// 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,86 +0,0 @@
|
||||||
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 jsMapFile = dir => join(dir, "index.js.map")
|
|
||||||
const sourceJs = jsFile("dist")
|
|
||||||
const sourceJsMap = jsMapFile("dist")
|
|
||||||
const componentsFile = "components.json"
|
|
||||||
|
|
||||||
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 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 copyComponentsJson(nodeModules(app))
|
|
||||||
|
|
||||||
await copySourceJs(join(publicMain(app), "dist"))
|
|
||||||
await copySourceJsMap(join(publicMain(app), "dist"))
|
|
||||||
|
|
||||||
await copySourceJs(join(publicUnauth(app), "dist"))
|
|
||||||
await copySourceJsMap(join(publicUnauth(app), "dist"))
|
|
||||||
}
|
|
||||||
})()
|
|
|
@ -1,44 +0,0 @@
|
||||||
<script>
|
|
||||||
export let formControls = []
|
|
||||||
|
|
||||||
export let _bb
|
|
||||||
|
|
||||||
let htmlElements = {}
|
|
||||||
let labelElements = {}
|
|
||||||
let labels = {}
|
|
||||||
let isInitialised = false
|
|
||||||
|
|
||||||
$: {
|
|
||||||
if (_bb && htmlElements && !isInitialised) {
|
|
||||||
let cIndex = 0
|
|
||||||
for (let c of formControls) {
|
|
||||||
labels[cIndex] = c.label
|
|
||||||
cIndex++
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let el in htmlElements) {
|
|
||||||
if (formControls[el].control.controlPosition === "Before Label") {
|
|
||||||
_bb.insertChildren(
|
|
||||||
_bb.props.formControls[el].control,
|
|
||||||
htmlElements[el],
|
|
||||||
htmlElements[el].childNodes.find(n => n.tagName === "LABEL")
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_bb.appendChildren(
|
|
||||||
_bb.props.formControls[el].control,
|
|
||||||
htmlElements[el]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isInitialised = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<form>
|
|
||||||
{#each formControls as child, idx}
|
|
||||||
<div class="form-group" bind:this={htmlElements[idx]}>
|
|
||||||
<label>{labels[idx]}</label>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</form>
|
|
|
@ -1,119 +0,0 @@
|
||||||
<script>
|
|
||||||
export let items = []
|
|
||||||
export let hideNavBar = false
|
|
||||||
export let selectedItem = ""
|
|
||||||
export let orientation = "horizontal" // horizontal, verical
|
|
||||||
export let alignment = "start" // start, center, end
|
|
||||||
export let pills = false
|
|
||||||
export let fill = false
|
|
||||||
export let className = ""
|
|
||||||
export let _bb
|
|
||||||
|
|
||||||
let selectedIndex = -1
|
|
||||||
let styleVars = {}
|
|
||||||
let components = {}
|
|
||||||
let componentElement
|
|
||||||
let orientationClass = ""
|
|
||||||
let navClasses = ""
|
|
||||||
let currentComponent
|
|
||||||
let _selectedItem = ""
|
|
||||||
|
|
||||||
const hasComponentElements = () =>
|
|
||||||
Object.getOwnPropertyNames(componentElements).length > 0
|
|
||||||
|
|
||||||
const getSelectedItemByIndex = index => (index >= 0 ? items[index].title : "")
|
|
||||||
|
|
||||||
$: {
|
|
||||||
let _navClasses = ""
|
|
||||||
|
|
||||||
if (orientation === "vertical") {
|
|
||||||
_navClasses += " flex-column"
|
|
||||||
} else {
|
|
||||||
_navClasses += ` justify-content-${alignment}`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pills) _navClasses += " nav-pills"
|
|
||||||
|
|
||||||
if (fill) _navClasses += " nav-fill nav-justified"
|
|
||||||
|
|
||||||
navClasses = _navClasses
|
|
||||||
|
|
||||||
if (items && componentElement) {
|
|
||||||
const currentSelectedItem = getSelectedItemByIndex(selectedIndex)
|
|
||||||
|
|
||||||
if (selectedItem && currentSelectedItem !== selectedItem) {
|
|
||||||
let i = 0
|
|
||||||
for (let item of items) {
|
|
||||||
if (item.title === selectedItem) {
|
|
||||||
SelectItem(i)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
} else if (!selectedItem) {
|
|
||||||
SelectItem(-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SelectItem = index => {
|
|
||||||
selectedIndex = index
|
|
||||||
const newSelectedItem = getSelectedItemByIndex(index)
|
|
||||||
if (newSelectedItem !== selectedItem) {
|
|
||||||
selectedItem = newSelectedItem
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentComponent) {
|
|
||||||
try {
|
|
||||||
currentComponent.$destroy()
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= 0)
|
|
||||||
currentComponent = _bb.hydrateChildren(
|
|
||||||
_bb.props.items[index].component,
|
|
||||||
componentElement
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSelectItemClicked = index => () => {
|
|
||||||
if (_bb.props.selectedItem) {
|
|
||||||
// binding - call state, which should SelectItem(..)
|
|
||||||
const selectedItemBinding = _bb.props.selectedItem
|
|
||||||
_bb.setStateFromBinding(
|
|
||||||
selectedItemBinding,
|
|
||||||
getSelectedItemByIndex(index)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// no binding - call this
|
|
||||||
SelectItem(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root {className}">
|
|
||||||
{#if !hideNavBar}
|
|
||||||
<ul class="nav {navClasses}">
|
|
||||||
{#each items as navItem, index}
|
|
||||||
<li class="nav-item">
|
|
||||||
<button
|
|
||||||
class="nav-link btn btn-link"
|
|
||||||
on:click={onSelectItemClicked(index)}
|
|
||||||
class:disabled={navItem.disabled}
|
|
||||||
class:active={selectedIndex === index}>
|
|
||||||
{navItem.title}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
{#each items as navItem, index}
|
|
||||||
<div bind:this={componentElement} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,34 +0,0 @@
|
||||||
<script>
|
|
||||||
import createApp from "./createApp"
|
|
||||||
import { props } from "./props"
|
|
||||||
|
|
||||||
let _bb
|
|
||||||
|
|
||||||
const _appPromise = createApp()
|
|
||||||
_appPromise.then(a => (_bb = a))
|
|
||||||
|
|
||||||
const testProps = props.hiddenNav
|
|
||||||
|
|
||||||
let currentComponent
|
|
||||||
|
|
||||||
$: {
|
|
||||||
if (_bb && currentComponent) {
|
|
||||||
_bb.hydrateChildren(testProps, currentComponent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#await _appPromise}
|
|
||||||
loading
|
|
||||||
{:then _bb}
|
|
||||||
|
|
||||||
<div id="current_component" bind:this={currentComponent} />
|
|
||||||
|
|
||||||
{/await}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
#current_component {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,43 +0,0 @@
|
||||||
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 () => {
|
|
||||||
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: [] }
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,247 +0,0 @@
|
||||||
export const props = {
|
|
||||||
login: { _component: "components/login" },
|
|
||||||
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
hiddenNav: {
|
|
||||||
_component: "components/stackpanel",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
control: {
|
|
||||||
_component: "components/button",
|
|
||||||
contentText: "Peep",
|
|
||||||
onClick: [
|
|
||||||
{
|
|
||||||
"##eventHandlerType": "Set State",
|
|
||||||
parameters: {
|
|
||||||
path: "selected",
|
|
||||||
value: "People",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
control: {
|
|
||||||
_component: "components/button",
|
|
||||||
contentText: "Ani",
|
|
||||||
onClick: [
|
|
||||||
{
|
|
||||||
"##eventHandlerType": "Set State",
|
|
||||||
parameters: {
|
|
||||||
path: "selected",
|
|
||||||
value: "Animals",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
import App from "./TestApp.svelte"
|
|
||||||
|
|
||||||
const app = new App({
|
|
||||||
target: document.body,
|
|
||||||
})
|
|
||||||
|
|
||||||
export default app
|
|
|
@ -1,9 +0,0 @@
|
||||||
export const buildStyle = styles => {
|
|
||||||
let str = ""
|
|
||||||
for (let s in styles) {
|
|
||||||
if (styles[s]) {
|
|
||||||
str += `${s}: ${styles[s]}; `
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// 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
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
export const emptyProps = () => ({ _component: "" })
|
|
|
@ -1,2 +0,0 @@
|
||||||
export { default as form } from "./Form.svelte"
|
|
||||||
export { default as nav } from "./Nav.svelte"
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { filter, map, reduce, toPairs, pipe } from "lodash/fp"
|
import { pipe } from "components/common/core";
|
||||||
|
import { filter, map, reduce, toPairs } from "lodash/fp"
|
||||||
|
|
||||||
const self = n => n
|
const self = n => n
|
||||||
const join_with = delimiter => a => a.join(delimiter)
|
const join_with = delimiter => a => a.join(delimiter)
|
||||||
|
|
|
@ -1,56 +1,73 @@
|
||||||
import { flatten, values, uniq, map } from "lodash/fp"
|
/**
|
||||||
import { pipe } from "components/common/core"
|
* Fetches the definitions for component library components. This includes
|
||||||
|
* their props and other metadata from components.json.
|
||||||
|
* @param {string} clientId - ID of the current client
|
||||||
|
* @param {string} appId - ID of the currently running app
|
||||||
|
*/
|
||||||
|
export const fetchComponentLibDefinitions = async (clientId, appId) => {
|
||||||
|
const LIB_DEFINITION_URL = `/${clientId}/${appId}/components/definitions`;
|
||||||
|
try {
|
||||||
|
const libDefinitionResponse = await fetch(LIB_DEFINITION_URL);
|
||||||
|
return await libDefinitionResponse.json();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error fetching component definitions for ${appId}`, err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const loadLibs = async (appId, appPackage) => {
|
/**
|
||||||
|
* Loads the JavaScript files for app component libraries and returns a map of their modules.
|
||||||
|
* @param {object} application - package definition
|
||||||
|
*/
|
||||||
|
export const fetchComponentLibModules = async application => {
|
||||||
const allLibraries = {}
|
const allLibraries = {}
|
||||||
|
|
||||||
for (let lib of libsFromPages(appPackage.pages)) {
|
for (let libraryName of application.componentLibraries) {
|
||||||
console.log(libModule);
|
const LIBRARY_URL = `/${application._id}/componentlibrary?library=${libraryName}`;
|
||||||
const libModule = await import(makeLibraryUrl(appId, lib))
|
const libraryModule = await import(LIBRARY_URL)
|
||||||
allLibraries[lib] = libModule
|
allLibraries[libraryName] = libraryModule
|
||||||
}
|
}
|
||||||
|
|
||||||
return allLibraries
|
return allLibraries
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadLibUrls = (appId, appPackage) => {
|
// export const loadLibUrls = (appId, appPackage) => {
|
||||||
const allLibraries = []
|
// const allLibraries = []
|
||||||
for (let lib of libsFromPages(appPackage.pages)) {
|
// for (let lib of libsFromPages(appPackage.pages)) {
|
||||||
const libUrl = makeLibraryUrl(appId, lib)
|
// const libUrl = makeLibraryUrl(appId, lib)
|
||||||
allLibraries.push({ libName: lib, importPath: libUrl })
|
// allLibraries.push({ libName: lib, importPath: libUrl })
|
||||||
}
|
// }
|
||||||
|
|
||||||
return allLibraries
|
// return allLibraries
|
||||||
}
|
// }
|
||||||
|
|
||||||
export const loadLib = async (appId, lib, allLibs) => {
|
// export const loadLib = async (appId, lib, allLibs) => {
|
||||||
allLibs[lib] = await import(makeLibraryUrl(appId, lib))
|
// allLibs[lib] = await import(makeLibraryUrl(appId, lib))
|
||||||
return allLibs
|
// return allLibs
|
||||||
}
|
// }
|
||||||
|
|
||||||
export const makeLibraryUrl = (appId, lib) =>
|
// export const makeLibraryUrl = (appId, lib) =>
|
||||||
`/_builder/${appId}/componentlibrary?lib=${encodeURI(lib)}`
|
// `/_builder/${appId}/componentlibrary?lib=${encodeURI(lib)}`
|
||||||
|
|
||||||
export const libsFromPages = pages =>
|
// export const libsFromPages = pages =>
|
||||||
pipe(pages, [values, map(p => p.componentLibraries), flatten, uniq])
|
// pipe(pages, [values, map(p => p.componentLibraries), flatten, uniq])
|
||||||
|
|
||||||
export const libUrlsForPreview = (appPackage, pageName) => {
|
// export const libUrlsForPreview = (appPackage, pageName) => {
|
||||||
const resolve = path => {
|
// const resolve = path => {
|
||||||
let file = appPackage.components.libraryPaths[path]
|
// let file = appPackage.components.libraryPaths[path]
|
||||||
if (file.startsWith("./")) file = file.substring(2)
|
// if (file.startsWith("./")) file = file.substring(2)
|
||||||
if (file.startsWith("/")) file = file.substring(1)
|
// if (file.startsWith("/")) file = file.substring(1)
|
||||||
|
|
||||||
let newPath = path
|
// let newPath = path
|
||||||
|
|
||||||
if (!newPath.startsWith("./") && !newPath.startsWith("/")) {
|
// if (!newPath.startsWith("./") && !newPath.startsWith("/")) {
|
||||||
newPath = `/node_modules/${path}`
|
// newPath = `/node_modules/${path}`
|
||||||
}
|
// }
|
||||||
|
|
||||||
return {
|
// return {
|
||||||
importPath: `/lib${newPath}/${file}`,
|
// importPath: `/lib${newPath}/${file}`,
|
||||||
libName: path,
|
// libName: path,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return pipe([appPackage.pages[pageName]], [libsFromPages, map(resolve)])
|
// return pipe([appPackage.pages[pageName]], [libsFromPages, map(resolve)])
|
||||||
}
|
// }
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import api from "../api"
|
import api from "../api"
|
||||||
import { find } from "lodash/fp"
|
|
||||||
import {
|
|
||||||
getNode,
|
|
||||||
constructHierarchy,
|
|
||||||
} from "components/common/core"
|
|
||||||
|
|
||||||
import backendActions from "../../actions/backend";
|
|
||||||
|
|
||||||
export const getBackendUiStore = () => {
|
export const getBackendUiStore = () => {
|
||||||
const INITIAL_BACKEND_UI_STATE = {
|
const INITIAL_BACKEND_UI_STATE = {
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
import { filter, cloneDeep, last, concat, isEmpty, values } from "lodash/fp"
|
import { filter, cloneDeep, values } from "lodash/fp"
|
||||||
import { pipe, getNode, constructHierarchy } from "components/common/core"
|
import { pipe } from "components/common/core"
|
||||||
import * as backendStoreActions from "./backend"
|
import * as backendStoreActions from "./backend"
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import { defaultPagesObject } from "components/userInterface/pagesParsing/defaultPagesObject"
|
|
||||||
import api from "../api"
|
import api from "../api"
|
||||||
|
import {
|
||||||
|
DEFAULT_PAGES_OBJECT
|
||||||
|
} from "../../constants";
|
||||||
import { getExactComponent } from "components/userInterface/pagesParsing/searchComponents"
|
import { getExactComponent } from "components/userInterface/pagesParsing/searchComponents"
|
||||||
import { rename } from "components/userInterface/pagesParsing/renameScreen"
|
import { rename } from "components/userInterface/pagesParsing/renameScreen"
|
||||||
import {
|
import {
|
||||||
getNewScreen,
|
// getNewScreen,
|
||||||
createProps,
|
createProps,
|
||||||
makePropsSafe,
|
makePropsSafe,
|
||||||
getBuiltin,
|
getBuiltin,
|
||||||
} from "components/userInterface/pagesParsing/createProps"
|
} from "components/userInterface/pagesParsing/createProps"
|
||||||
import { expandComponentDefinition } from "components/userInterface/pagesParsing/types"
|
import {
|
||||||
import { loadLibs, libUrlsForPreview } from "../loadComponentLibraries"
|
fetchComponentLibModules,
|
||||||
|
fetchComponentLibDefinitions
|
||||||
|
} from "../loadComponentLibraries"
|
||||||
import { buildCodeForScreens } from "../buildCodeForScreens"
|
import { buildCodeForScreens } from "../buildCodeForScreens"
|
||||||
import { generate_screen_css } from "../generate_css"
|
import { generate_screen_css } from "../generate_css"
|
||||||
import { insertCodeMetadata } from "../insertCodeMetadata"
|
import { insertCodeMetadata } from "../insertCodeMetadata"
|
||||||
|
@ -24,7 +28,7 @@ export const getStore = () => {
|
||||||
apps: [],
|
apps: [],
|
||||||
appname: "",
|
appname: "",
|
||||||
hierarchy: {},
|
hierarchy: {},
|
||||||
pages: defaultPagesObject(),
|
pages: DEFAULT_PAGES_OBJECT,
|
||||||
mainUi: {},
|
mainUi: {},
|
||||||
unauthenticatedUi: {},
|
unauthenticatedUi: {},
|
||||||
components: [],
|
components: [],
|
||||||
|
@ -35,8 +39,6 @@ export const getStore = () => {
|
||||||
currentComponentProps: null,
|
currentComponentProps: null,
|
||||||
errors: [],
|
errors: [],
|
||||||
hasAppPackage: false,
|
hasAppPackage: false,
|
||||||
// accessLevels: { version: 0, levels: [] },
|
|
||||||
// currentNode: null,
|
|
||||||
libraries: null,
|
libraries: null,
|
||||||
appId: ""
|
appId: ""
|
||||||
}
|
}
|
||||||
|
@ -52,7 +54,7 @@ export const getStore = () => {
|
||||||
// store.deleteAction = backendStoreActions.deleteAction(store)
|
// store.deleteAction = backendStoreActions.deleteAction(store)
|
||||||
// store.saveTrigger = backendStoreActions.saveTrigger(store)
|
// store.saveTrigger = backendStoreActions.saveTrigger(store)
|
||||||
// store.deleteTrigger = backendStoreActions.deleteTrigger(store)
|
// store.deleteTrigger = backendStoreActions.deleteTrigger(store)
|
||||||
store.importAppDefinition = importAppDefinition(store)
|
// store.importAppDefinition = importAppDefinition(store)
|
||||||
|
|
||||||
store.saveScreen = saveScreen(store)
|
store.saveScreen = saveScreen(store)
|
||||||
store.renameScreen = renameScreen(store)
|
store.renameScreen = renameScreen(store)
|
||||||
|
@ -60,11 +62,9 @@ export const getStore = () => {
|
||||||
store.setCurrentScreen = setCurrentScreen(store)
|
store.setCurrentScreen = setCurrentScreen(store)
|
||||||
store.setCurrentPage = setCurrentPage(store)
|
store.setCurrentPage = setCurrentPage(store)
|
||||||
store.createScreen = createScreen(store)
|
store.createScreen = createScreen(store)
|
||||||
// store.removeComponentLibrary = removeComponentLibrary(store)
|
|
||||||
store.addStylesheet = addStylesheet(store)
|
store.addStylesheet = addStylesheet(store)
|
||||||
store.removeStylesheet = removeStylesheet(store)
|
store.removeStylesheet = removeStylesheet(store)
|
||||||
store.savePage = savePage(store)
|
store.savePage = savePage(store)
|
||||||
store.createGeneratedComponents = createGeneratedComponents(store)
|
|
||||||
store.addChildComponent = addChildComponent(store)
|
store.addChildComponent = addChildComponent(store)
|
||||||
store.selectComponent = selectComponent(store)
|
store.selectComponent = selectComponent(store)
|
||||||
store.setComponentProp = setComponentProp(store)
|
store.setComponentProp = setComponentProp(store)
|
||||||
|
@ -102,7 +102,9 @@ const setPackage = (store, initial) => async (pkg) => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
initial.libraries = await loadLibs(pkg.application._id, pkg)
|
initial.libraries = await fetchComponentLibModules(pkg.application)
|
||||||
|
// TODO: Rename to componentDefinitions
|
||||||
|
initial.components = await fetchComponentLibDefinitions(pkg.clientId, pkg.application._id);
|
||||||
initial.loadLibraryUrls = pageName => {
|
initial.loadLibraryUrls = pageName => {
|
||||||
const libs = libUrlsForPreview(pkg, pageName)
|
const libs = libUrlsForPreview(pkg, pageName)
|
||||||
return libs
|
return libs
|
||||||
|
@ -111,40 +113,20 @@ const setPackage = (store, initial) => async (pkg) => {
|
||||||
initial.appId = pkg.application._id
|
initial.appId = pkg.application._id
|
||||||
initial.pages = pkg.pages
|
initial.pages = pkg.pages
|
||||||
initial.hasAppPackage = true
|
initial.hasAppPackage = true
|
||||||
initial.hierarchy = pkg.appDefinition.hierarchy
|
|
||||||
initial.accessLevels = pkg.accessLevels
|
|
||||||
initial.screens = values(pkg.screens)
|
initial.screens = values(pkg.screens)
|
||||||
initial.components = values(pkg.components.components).map(
|
|
||||||
expandComponentDefinition
|
|
||||||
)
|
|
||||||
initial.templates = pkg.components.templates
|
initial.templates = pkg.components.templates
|
||||||
initial.builtins = [getBuiltin("##builtin/screenslot")]
|
initial.builtins = [getBuiltin("##builtin/screenslot")]
|
||||||
initial.actions = values(pkg.appDefinition.actions)
|
|
||||||
initial.triggers = pkg.appDefinition.triggers
|
|
||||||
initial.appInstances = pkg.application.instances
|
initial.appInstances = pkg.application.instances
|
||||||
initial.appId = pkg.application._id
|
initial.appId = pkg.application._id
|
||||||
|
|
||||||
store.set(initial)
|
store.set(initial)
|
||||||
|
console.log(initial)
|
||||||
return initial
|
return initial
|
||||||
}
|
}
|
||||||
|
|
||||||
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 saveScreen = store => screen => {
|
const saveScreen = store => screen => {
|
||||||
store.update(s => {
|
store.update(state => {
|
||||||
return _saveScreen(store, s, screen)
|
return _saveScreen(store, state, screen)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,10 +148,7 @@ const _saveScreen = async (store, s, screen) => {
|
||||||
innerState.screens = screens
|
innerState.screens = screens
|
||||||
innerState.currentPreviewItem = screen
|
innerState.currentPreviewItem = screen
|
||||||
const safeProps = makePropsSafe(
|
const safeProps = makePropsSafe(
|
||||||
getComponentDefinition(
|
innerState.components[screen.props._component],
|
||||||
innerState.components,
|
|
||||||
screen.props._component
|
|
||||||
),
|
|
||||||
screen.props
|
screen.props
|
||||||
)
|
)
|
||||||
innerState.currentComponentInfo = safeProps
|
innerState.currentComponentInfo = safeProps
|
||||||
|
@ -192,21 +171,27 @@ const _saveScreenApi = (screen, s) =>
|
||||||
.then(() => _savePage(s))
|
.then(() => _savePage(s))
|
||||||
|
|
||||||
const createScreen = store => (screenName, route, layoutComponentName) => {
|
const createScreen = store => (screenName, route, layoutComponentName) => {
|
||||||
store.update(s => {
|
store.update(state => {
|
||||||
const newScreen = getNewScreen(
|
console.log(layoutComponentName);
|
||||||
s.components,
|
const rootComponent = state.components[layoutComponentName]
|
||||||
layoutComponentName,
|
|
||||||
screenName
|
const newScreen = {
|
||||||
)
|
name: screenName || "",
|
||||||
|
description: "",
|
||||||
|
url: "",
|
||||||
|
_css: "",
|
||||||
|
uiFunctions: "",
|
||||||
|
props: createProps(rootComponent).props,
|
||||||
|
}
|
||||||
|
|
||||||
newScreen.route = route
|
newScreen.route = route
|
||||||
s.currentPreviewItem = newScreen
|
state.currentPreviewItem = newScreen
|
||||||
s.currentComponentInfo = newScreen.props
|
state.currentComponentInfo = newScreen.props
|
||||||
s.currentFrontEndType = "screen"
|
state.currentFrontEndType = "screen"
|
||||||
|
|
||||||
_saveScreen(store, s, newScreen)
|
_saveScreen(store, state, newScreen)
|
||||||
|
|
||||||
return s
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +204,7 @@ const setCurrentScreen = store => screenName => {
|
||||||
s.currentView = "detail"
|
s.currentView = "detail"
|
||||||
|
|
||||||
const safeProps = makePropsSafe(
|
const safeProps = makePropsSafe(
|
||||||
getComponentDefinition(s.components, screen.props._component),
|
s.components[screen.props._component],
|
||||||
screen.props
|
screen.props
|
||||||
)
|
)
|
||||||
screen.props = safeProps
|
screen.props = safeProps
|
||||||
|
@ -229,25 +214,6 @@ const setCurrentScreen = store => screenName => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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.appId}/screen`, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
await _savePage(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
doCreate()
|
|
||||||
|
|
||||||
return s
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteScreen = store => name => {
|
const deleteScreen = store => name => {
|
||||||
store.update(s => {
|
store.update(s => {
|
||||||
const components = pipe(s.components, [filter(c => c.name !== name)])
|
const components = pipe(s.components, [filter(c => c.name !== name)])
|
||||||
|
@ -319,46 +285,6 @@ const savePage = store => async page => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// const addComponentLibrary = store => async lib => {
|
|
||||||
// const response = await api.get(
|
|
||||||
// `/_builder/api/${s.appId}/componentlibrary?lib=${encodeURI(lib)}`,
|
|
||||||
// undefined,
|
|
||||||
// false
|
|
||||||
// )
|
|
||||||
|
|
||||||
// const success = response.status === 200
|
|
||||||
|
|
||||||
// const components = success ? await response.json() : []
|
|
||||||
|
|
||||||
// store.update(s => {
|
|
||||||
// if (success) {
|
|
||||||
// const componentsArray = []
|
|
||||||
// for (let c in components) {
|
|
||||||
// componentsArray.push(expandComponentDefinition(components[c]))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// s.components = pipe(s.components, [
|
|
||||||
// filter(c => !c.name.startsWith(`${lib}/`)),
|
|
||||||
// concat(componentsArray),
|
|
||||||
// ])
|
|
||||||
|
|
||||||
// s.pages.componentLibraries.push(lib)
|
|
||||||
// _savePage(s)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return s
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const removeComponentLibrary = store => lib => {
|
|
||||||
// store.update(state => {
|
|
||||||
// state.pages.componentLibraries = state.pages.componentLibraries.filter(l => l !== lib);
|
|
||||||
// _savePage(state);
|
|
||||||
|
|
||||||
// return state;
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
const addStylesheet = store => stylesheet => {
|
const addStylesheet = store => stylesheet => {
|
||||||
store.update(s => {
|
store.update(s => {
|
||||||
s.pages.stylesheets.push(stylesheet)
|
s.pages.stylesheets.push(stylesheet)
|
||||||
|
@ -386,42 +312,44 @@ const _savePage = async s => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const setCurrentPage = store => pageName => {
|
const setCurrentPage = store => pageName => {
|
||||||
store.update(s => {
|
store.update(state => {
|
||||||
const current_screens = s.pages[pageName]._screens
|
const current_screens = state.pages[pageName]._screens
|
||||||
|
|
||||||
s.currentFrontEndType = "page"
|
const currentPage = state.pages[pageName];
|
||||||
s.currentPageName = pageName
|
|
||||||
s.screens = Array.isArray(current_screens)
|
state.currentFrontEndType = "page"
|
||||||
|
state.currentPageName = pageName
|
||||||
|
state.screens = Array.isArray(current_screens)
|
||||||
? current_screens
|
? current_screens
|
||||||
: Object.values(current_screens)
|
: Object.values(current_screens)
|
||||||
const safeProps = makePropsSafe(
|
const safeProps = makePropsSafe(
|
||||||
getComponentDefinition(s.components, s.pages[pageName].props._component),
|
state.components[currentPage.props._component],
|
||||||
s.pages[pageName].props
|
currentPage.props
|
||||||
)
|
)
|
||||||
s.currentComponentInfo = safeProps
|
state.currentComponentInfo = safeProps
|
||||||
s.pages[pageName].props = safeProps
|
currentPage.props = safeProps
|
||||||
s.currentPreviewItem = s.pages[pageName]
|
state.currentPreviewItem = state.pages[pageName]
|
||||||
s.currentPreviewItem._css = generate_screen_css([
|
state.currentPreviewItem._css = generate_screen_css([
|
||||||
s.currentPreviewItem.props,
|
state.currentPreviewItem.props,
|
||||||
])
|
])
|
||||||
|
|
||||||
for (let screen of s.screens) {
|
for (let screen of state.screens) {
|
||||||
screen._css = generate_screen_css([screen.props])
|
screen._css = generate_screen_css([screen.props])
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentPageFunctions(s)
|
setCurrentPageFunctions(state)
|
||||||
return s
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getComponentDefinition = (components, name) =>
|
// const getComponentDefinition = (components, name) => components.find(c => c.name === name)
|
||||||
components.find(c => c.name === name)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} componentToAdd - name of the component to add to the application
|
* @param {string} componentToAdd - name of the component to add to the application
|
||||||
* @param {string} presetName - name of the component preset if defined
|
* @param {string} presetName - name of the component preset if defined
|
||||||
*/
|
*/
|
||||||
const addChildComponent = store => (componentToAdd, presetName) => {
|
const addChildComponent = store => (componentToAdd, presetName) => {
|
||||||
|
// componentToAdd looks like: @budibase/standard-components/container
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
function findSlot(component_array) {
|
function findSlot(component_array) {
|
||||||
for (let i = 0; i < component_array.length; i += 1) {
|
for (let i = 0; i < component_array.length; i += 1) {
|
||||||
|
@ -442,7 +370,7 @@ const addChildComponent = store => (componentToAdd, presetName) => {
|
||||||
|
|
||||||
const component = componentToAdd.startsWith("##")
|
const component = componentToAdd.startsWith("##")
|
||||||
? getBuiltin(componentToAdd)
|
? getBuiltin(componentToAdd)
|
||||||
: state.components.find(({ name }) => name === componentToAdd)
|
: state.components[componentToAdd]
|
||||||
|
|
||||||
const presetProps = presetName ? component.presets[presetName] : {}
|
const presetProps = presetName ? component.presets[presetName] : {}
|
||||||
const newComponent = createProps(component, presetProps)
|
const newComponent = createProps(component, presetProps)
|
||||||
|
@ -488,7 +416,7 @@ const selectComponent = store => component => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const componentDef = component._component.startsWith("##")
|
const componentDef = component._component.startsWith("##")
|
||||||
? component
|
? component
|
||||||
: state.components.find(c => c.name === component._component)
|
: state.components[component._component]
|
||||||
state.currentComponentInfo = makePropsSafe(componentDef, component)
|
state.currentComponentInfo = makePropsSafe(componentDef, component)
|
||||||
state.currentView = "component"
|
state.currentView = "component"
|
||||||
return state
|
return state
|
||||||
|
@ -508,28 +436,28 @@ const setComponentProp = store => (name, value) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const setComponentStyle = store => (type, name, value) => {
|
const setComponentStyle = store => (type, name, value) => {
|
||||||
store.update(s => {
|
store.update(state => {
|
||||||
if (!s.currentComponentInfo._styles) {
|
if (!state.currentComponentInfo._styles) {
|
||||||
s.currentComponentInfo._styles = {}
|
state.currentComponentInfo._styles = {}
|
||||||
}
|
}
|
||||||
s.currentComponentInfo._styles[type][name] = value
|
state.currentComponentInfo._styles[type][name] = value
|
||||||
s.currentPreviewItem._css = generate_screen_css([
|
state.currentPreviewItem._css = generate_screen_css([
|
||||||
s.currentPreviewItem.props,
|
state.currentPreviewItem.props,
|
||||||
])
|
])
|
||||||
|
|
||||||
// save without messing with the store
|
// save without messing with the store
|
||||||
_saveCurrentPreviewItem(s)
|
_saveCurrentPreviewItem(state)
|
||||||
return s
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const setComponentCode = store => code => {
|
const setComponentCode = store => code => {
|
||||||
store.update(s => {
|
store.update(state => {
|
||||||
s.currentComponentInfo._code = code
|
state.currentComponentInfo._code = code
|
||||||
|
|
||||||
setCurrentPageFunctions(s)
|
setCurrentPageFunctions(state)
|
||||||
// save without messing with the store
|
// save without messing with the store
|
||||||
_saveScreenApi(s.currentPreviewItem, s)
|
_saveScreenApi(state.currentPreviewItem, state)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
|
@ -543,31 +471,31 @@ const setCurrentPageFunctions = s => {
|
||||||
const buildPageCode = (screens, page) => buildCodeForScreens([page, ...screens])
|
const buildPageCode = (screens, page) => buildCodeForScreens([page, ...screens])
|
||||||
|
|
||||||
const setScreenType = store => type => {
|
const setScreenType = store => type => {
|
||||||
store.update(s => {
|
store.update(state => {
|
||||||
s.currentFrontEndType = type
|
state.currentFrontEndType = type
|
||||||
|
|
||||||
const pageOrScreen =
|
const pageOrScreen =
|
||||||
type === "page"
|
type === "page"
|
||||||
? s.pages[s.currentPageName]
|
? state.pages[state.currentPageName]
|
||||||
: s.pages[s.currentPageName]._screens[0]
|
: state.pages[state.currentPageName]._screens[0]
|
||||||
|
|
||||||
s.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null
|
state.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null
|
||||||
s.currentPreviewItem = pageOrScreen
|
state.currentPreviewItem = pageOrScreen
|
||||||
return s
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteComponent = store => component => {
|
const deleteComponent = store => componentName => {
|
||||||
store.update(s => {
|
store.update(state => {
|
||||||
const parent = getParent(s.currentPreviewItem.props, component)
|
const parent = getParent(state.currentPreviewItem.props, componentName)
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
parent._children = parent._children.filter(c => c !== component)
|
parent._children = parent._children.filter(component => component !== componentName)
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveCurrentPreviewItem(s)
|
_saveCurrentPreviewItem(state)
|
||||||
|
|
||||||
return s
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
<script>
|
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import Button from "components/common/Button.svelte"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
|
||||||
import { cloneDeep, filter, keys, map, isUndefined } from "lodash/fp"
|
|
||||||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
|
||||||
import { validateActions, pipe } from "components/common/core"
|
|
||||||
import getIcon from "components/common/icon"
|
|
||||||
|
|
||||||
export let action
|
|
||||||
export let onFinished = action => {}
|
|
||||||
export let allActions
|
|
||||||
|
|
||||||
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)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const save = () => {
|
|
||||||
const newActionsList = [
|
|
||||||
...pipe(allActions, [filter(a => a !== action)]),
|
|
||||||
clonedAction,
|
|
||||||
]
|
|
||||||
|
|
||||||
errors = pipe(newActionsList, [validateActions, map(e => e.error)])
|
|
||||||
|
|
||||||
if (errors.length === 0) onFinished(clonedAction)
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancel = () => {
|
|
||||||
onFinished()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
|
|
||||||
<ErrorsBox {errors} />
|
|
||||||
|
|
||||||
<form on:submit|preventDefault 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} />
|
|
||||||
<ActionButton primary on:click={addNewOption}>Add</ActionButton>
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 10px">
|
|
||||||
{#each initialOptions as option}
|
|
||||||
<span class="option-container">
|
|
||||||
{option.key} : {option.value}
|
|
||||||
<span
|
|
||||||
style="font-size:10pt; cursor: pointer"
|
|
||||||
on:click={() => removeOption(option)}>
|
|
||||||
{@html getIcon('trash-2')}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-modal-footer uk-text-right">
|
|
||||||
<ButtonGroup>
|
|
||||||
<ActionButton primary grouped on:click={save}>Save</ActionButton>
|
|
||||||
<ActionButton alert grouped on:click={cancel}>Cancel</ActionButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
padding: 2rem;
|
|
||||||
border-radius: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uk-grid-small {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.option-container {
|
|
||||||
border-style: dotted;
|
|
||||||
border-width: 1px;
|
|
||||||
border-color: var(--primary75);
|
|
||||||
padding: 3px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,108 +0,0 @@
|
||||||
<script>
|
|
||||||
import getIcon from "components/common/icon"
|
|
||||||
import { store } from "builderStore"
|
|
||||||
import Button from "components/common/Button.svelte"
|
|
||||||
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
|
||||||
import ActionView from "./ActionView.svelte"
|
|
||||||
import Modal from "components/common/Modal.svelte"
|
|
||||||
import { pipe } from "components/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)
|
|
||||||
} else {
|
|
||||||
onActionCancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h3 class="budibase__title--3">Actions</h3>
|
|
||||||
|
|
||||||
{#if actionsArray}
|
|
||||||
<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 />
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{#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>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{:else}(no actions added){/if}
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title={editingActionIsNew ? 'Create Action' : 'Edit Action'}
|
|
||||||
bind:isOpen={isEditing}>
|
|
||||||
{#if isEditing}
|
|
||||||
<ActionView
|
|
||||||
action={editingAction}
|
|
||||||
allActions={$store.actions}
|
|
||||||
onFinished={actionEditingFinished}
|
|
||||||
isNew={editingActionIsNew} />
|
|
||||||
{/if}
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.edit-button {
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--secondary25);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:hover .edit-button {
|
|
||||||
color: var(--secondary75);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-content {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,135 +0,0 @@
|
||||||
<script>
|
|
||||||
import getIcon from "components/common/icon"
|
|
||||||
import { store } from "builderStore"
|
|
||||||
import Button from "components/common/Button.svelte"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
|
||||||
import Actions from "./Actions.svelte"
|
|
||||||
import Triggers from "./Triggers.svelte"
|
|
||||||
import { getNewAction, getNewTrigger } from "components/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)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
<div class="actions-header">
|
|
||||||
<ButtonGroup>
|
|
||||||
<ActionButton color="secondary" grouped on:click={newAction}>
|
|
||||||
Create New Action
|
|
||||||
</ActionButton>
|
|
||||||
<ActionButton color="tertiary" grouped on:click={newTrigger}>
|
|
||||||
Create New Trigger
|
|
||||||
</ActionButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="node-view">
|
|
||||||
<Actions
|
|
||||||
{editingActionIsNew}
|
|
||||||
{editingAction}
|
|
||||||
{onActionEdit}
|
|
||||||
{onActionDelete}
|
|
||||||
{onActionSave}
|
|
||||||
{onActionCancel} />
|
|
||||||
|
|
||||||
<Triggers
|
|
||||||
{editingTriggerIsNew}
|
|
||||||
{editingTrigger}
|
|
||||||
{onTriggerEdit}
|
|
||||||
{onTriggerDelete}
|
|
||||||
{onTriggerSave}
|
|
||||||
{onTriggerCancel} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions-header {
|
|
||||||
flex: 0 1 auto;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-view {
|
|
||||||
overflow-y: auto;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,74 +0,0 @@
|
||||||
<script>
|
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import Button from "components/common/Button.svelte"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import Dropdown from "components/common/Dropdown.svelte"
|
|
||||||
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
|
||||||
import CodeArea from "components/common/CodeArea.svelte"
|
|
||||||
import { cloneDeep, filter, keys, some, map, isUndefined } from "lodash/fp"
|
|
||||||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
|
||||||
import { validateTriggers, pipe, events } from "components/common/core"
|
|
||||||
import getIcon from "components/common/icon"
|
|
||||||
|
|
||||||
export let trigger
|
|
||||||
export let onFinished = action => {}
|
|
||||||
export let allTriggers
|
|
||||||
export let allActions
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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" />
|
|
||||||
|
|
||||||
<form on:submit|preventDefault 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} />
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="uk-modal-footer uk-text-right">
|
|
||||||
<ButtonGroup>
|
|
||||||
<ActionButton primary on:click={save}>Save</ActionButton>
|
|
||||||
<ActionButton alert on:click={cancel}>Cancel</ActionButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,93 +0,0 @@
|
||||||
<script>
|
|
||||||
import { store } from "builderStore"
|
|
||||||
import getIcon from "components/common/icon"
|
|
||||||
import Button from "components/common/Button.svelte"
|
|
||||||
import Modal from "components/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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h3 class="budibase__title--3">Triggers</h3>
|
|
||||||
|
|
||||||
{#if $store.triggers}
|
|
||||||
<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 />
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{#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>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{:else}(no triggers added){/if}
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title={editingTriggerIsNew ? 'Create Trigger' : 'Edit Trigger'}
|
|
||||||
onClosed={() => (isEditing = false)}
|
|
||||||
bind:isOpen={isEditing}>
|
|
||||||
{#if isEditing}
|
|
||||||
<TriggerView
|
|
||||||
trigger={editingTrigger}
|
|
||||||
allActions={$store.actions}
|
|
||||||
allTriggers={$store.triggers}
|
|
||||||
onFinished={triggerEditingFinished}
|
|
||||||
isNew={editingTriggerIsNew} />
|
|
||||||
{/if}
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.edit-button {
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--secondary25);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin: 3rem 0rem 0rem 0rem;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-content {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:hover .edit-button {
|
|
||||||
color: var(--secondary75);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,67 +0,0 @@
|
||||||
<script>
|
|
||||||
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="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="budibase__nav-item" on:click={action.onclick}>
|
|
||||||
{action.label}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.dropdown-background {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.root {
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-content {
|
|
||||||
position: absolute;
|
|
||||||
background-color: var(--white);
|
|
||||||
min-width: 160px;
|
|
||||||
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) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-row {
|
|
||||||
padding: 7px 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-row:hover {
|
|
||||||
background-color: var(--primary100);
|
|
||||||
color: var(--white);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -14,7 +14,6 @@
|
||||||
takeRight,
|
takeRight,
|
||||||
} from "lodash/fp"
|
} from "lodash/fp"
|
||||||
import Select from "components/common/Select.svelte"
|
import Select from "components/common/Select.svelte"
|
||||||
import { getIndexSchema } from "components/common/core"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
import TablePagination from "./TablePagination.svelte"
|
import TablePagination from "./TablePagination.svelte"
|
||||||
import { DeleteRecordModal, CreateEditRecordModal } from "./modals"
|
import { DeleteRecordModal, CreateEditRecordModal } from "./modals"
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import { common, hierarchy } from "../../../../../../../core/src/"
|
import { common, hierarchy } from "../../../../../../../core/src/"
|
||||||
import { getNode } from "components/common/core"
|
import { pipe } from "components/common/core"
|
||||||
import { templateApi, pipe, validate } from "components/common/core"
|
|
||||||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
||||||
|
|
||||||
export let model = { schema: {} }
|
export let model = { schema: {} }
|
||||||
|
|
|
@ -10,10 +10,6 @@
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
import DatePicker from "components/common/DatePicker.svelte"
|
import DatePicker from "components/common/DatePicker.svelte"
|
||||||
import { keys, cloneDeep } from "lodash/fp"
|
import { keys, cloneDeep } from "lodash/fp"
|
||||||
import {
|
|
||||||
allTypes,
|
|
||||||
validate,
|
|
||||||
} from "components/common/core"
|
|
||||||
|
|
||||||
const FIELD_TYPES = ["string", "number", "boolean"]
|
const FIELD_TYPES = ["string", "number", "boolean"]
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,6 @@
|
||||||
import { compose, map, get, flatten } from "lodash/fp"
|
import { compose, map, get, flatten } from "lodash/fp"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
import Select from "components/common/Select.svelte"
|
import Select from "components/common/Select.svelte"
|
||||||
import {
|
|
||||||
getNewRecord,
|
|
||||||
joinKey,
|
|
||||||
getExactNodeForKey,
|
|
||||||
} from "components/common/core"
|
|
||||||
import RecordFieldControl from "./RecordFieldControl.svelte"
|
import RecordFieldControl from "./RecordFieldControl.svelte"
|
||||||
import * as api from "../api"
|
import * as api from "../api"
|
||||||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import { getContext, onMount } from "svelte"
|
import { getContext, onMount } from "svelte"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import HierarchyRow from "./HierarchyRow.svelte"
|
import HierarchyRow from "./HierarchyRow.svelte"
|
||||||
import DropdownButton from "components/common/DropdownButton.svelte"
|
|
||||||
import NavItem from "./NavItem.svelte"
|
import NavItem from "./NavItem.svelte"
|
||||||
import getIcon from "components/common/icon"
|
import getIcon from "components/common/icon"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
$: screensExist = $store.currentPreviewItem._screens && $store.currentPreviewItem._screens.length > 0
|
$: screensExist = $store.currentPreviewItem._screens && $store.currentPreviewItem._screens.length > 0
|
||||||
|
|
||||||
$: frontendDefinition = {
|
$: frontendDefinition = {
|
||||||
componentLibraries: $store.loadLibraryUrls($store.currentPageName),
|
libraries: $store.libraries,
|
||||||
page: $store.currentPreviewItem,
|
page: $store.currentPreviewItem,
|
||||||
screens: screensExist ? $store.currentPreviewItem._screens : [{
|
screens: screensExist ? $store.currentPreviewItem._screens : [{
|
||||||
name: "Screen Placeholder",
|
name: "Screen Placeholder",
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from "./CurrentItemPreview.svelte";
|
|
@ -29,23 +29,6 @@
|
||||||
const categories = components.categories
|
const categories = components.categories
|
||||||
let selectedCategory = categories[0]
|
let selectedCategory = categories[0]
|
||||||
|
|
||||||
// const onTemplateChosen = template => {
|
|
||||||
// selectedComponent = null
|
|
||||||
// const { componentName, libName } = splitName(template.name)
|
|
||||||
// const templateOptions = {
|
|
||||||
// records: getRecordNodes(hierarchy),
|
|
||||||
// indexes: getIndexNodes(hierarchy),
|
|
||||||
// helpers: {
|
|
||||||
// indexSchema: getIndexSchema(hierarchy),
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
|
|
||||||
// templateInstances = libraryModules[libName][componentName](templateOptions)
|
|
||||||
// if (!templateInstances || templateInstances.length === 0) return
|
|
||||||
// selectedTemplateInstance = templateInstances[0].name
|
|
||||||
// selectTemplateDialog.show()
|
|
||||||
// }
|
|
||||||
|
|
||||||
const onComponentChosen = component => {
|
const onComponentChosen = component => {
|
||||||
if (component.template) {
|
if (component.template) {
|
||||||
// onTemplateChosen(component.template)
|
// onTemplateChosen(component.template)
|
||||||
|
@ -55,28 +38,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onTemplateInstanceChosen = () => {
|
// const onTemplateInstanceChosen = () => {
|
||||||
selectedComponent = null
|
// selectedComponent = null
|
||||||
const instance = templateInstances.find(
|
// const instance = templateInstances.find(
|
||||||
i => i.name === selectedTemplateInstance
|
// i => i.name === selectedTemplateInstance
|
||||||
)
|
// )
|
||||||
store.addTemplatedComponent(instance.props)
|
// store.addTemplatedComponent(instance.props)
|
||||||
toggleTab()
|
// toggleTab()
|
||||||
}
|
// }
|
||||||
|
|
||||||
$: templatesByComponent = groupBy(t => t.component)($store.templates)
|
// $: templatesByComponent = groupBy(t => t.component)($store.templates)
|
||||||
$: hierarchy = $store.hierarchy
|
// $: standaloneTemplates = pipe(
|
||||||
$: libraryModules = $store.libraries
|
// templatesByComponent,
|
||||||
$: standaloneTemplates = pipe(
|
// [
|
||||||
templatesByComponent,
|
// values,
|
||||||
[
|
// flatten,
|
||||||
values,
|
// filter(t => !$store.components.some(c => c.name === t.component)),
|
||||||
flatten,
|
// map(t => ({ name: splitName(t.component).componentName, template: t })),
|
||||||
filter(t => !$store.components.some(c => c.name === t.component)),
|
// uniqBy(t => t.name),
|
||||||
map(t => ({ name: splitName(t.component).componentName, template: t })),
|
// ]
|
||||||
uniqBy(t => t.name),
|
// )
|
||||||
]
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
|
@ -93,12 +74,11 @@
|
||||||
<Tab
|
<Tab
|
||||||
list={selectedCategory}
|
list={selectedCategory}
|
||||||
on:selectItem={e => onComponentChosen(e.detail)}
|
on:selectItem={e => onComponentChosen(e.detail)}
|
||||||
{onTemplateChosen}
|
|
||||||
{toggleTab} />
|
{toggleTab} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ConfirmDialog
|
<!-- <ConfirmDialog
|
||||||
bind:this={selectTemplateDialog}
|
bind:this={selectTemplateDialog}
|
||||||
title="Choose Template"
|
title="Choose Template"
|
||||||
onCancel={() => (selectedComponent = null)}
|
onCancel={() => (selectedComponent = null)}
|
||||||
|
@ -115,7 +95,7 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</ConfirmDialog>
|
</ConfirmDialog> -->
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.tabs {
|
.tabs {
|
||||||
|
|
|
@ -33,8 +33,6 @@
|
||||||
|
|
||||||
const isComponentSelected = (current, comp) => current === comp
|
const isComponentSelected = (current, comp) => current === comp
|
||||||
|
|
||||||
const isFolderSelected = (current, folder) => isInSubfolder(current, folder)
|
|
||||||
|
|
||||||
$: _screens = pipe(
|
$: _screens = pipe(
|
||||||
screens,
|
screens,
|
||||||
[map(c => ({ component: c, title: lastPartOfName(c) })), sortBy("title")]
|
[map(c => ({ component: c, title: lastPartOfName(c) })), sortBy("title")]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { last, pipe } from "lodash/fp"
|
import { last } from "lodash/fp"
|
||||||
|
import { pipe } from "components/common/core";
|
||||||
import {
|
import {
|
||||||
XCircleIcon,
|
XCircleIcon,
|
||||||
ChevronUpIcon,
|
ChevronUpIcon,
|
||||||
|
|
|
@ -33,9 +33,7 @@
|
||||||
let selectedEvent = null
|
let selectedEvent = null
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
const componentDefinition = components.find(
|
const componentDefinition = components[component._component];
|
||||||
c => c.name === component._component
|
|
||||||
)
|
|
||||||
events = Object.keys(componentDefinition.props)
|
events = Object.keys(componentDefinition.props)
|
||||||
.filter(
|
.filter(
|
||||||
propName => componentDefinition.props[propName].type === EVENT_TYPE
|
propName => componentDefinition.props[propName].type === EVENT_TYPE
|
||||||
|
|
|
@ -26,20 +26,13 @@
|
||||||
|
|
||||||
let saveAttempted = false
|
let saveAttempted = false
|
||||||
|
|
||||||
$: layoutComponents = pipe(
|
$: layoutComponents = Object.values($store.components).filter(componentDefinition => componentDefinition.container)
|
||||||
$store.components,
|
|
||||||
[
|
|
||||||
filter(c => c.container),
|
|
||||||
map(c => ({ name: c.name, ...splitName(c.name) })),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
$: layoutComponent = layoutComponent
|
$: layoutComponent = layoutComponent
|
||||||
? find(c => c.name === layoutComponent.name)(layoutComponents)
|
? layoutComponents.find(component => component._component === layoutComponent._component)
|
||||||
: layoutComponents[0]
|
: layoutComponents[0]
|
||||||
|
|
||||||
$: screens = $store.screens
|
$: route = !route && $store.screens.length === 0 ? "*" : route
|
||||||
$: route = !route && screens.length === 0 ? "*" : route
|
|
||||||
|
|
||||||
const save = () => {
|
const save = () => {
|
||||||
saveAttempted = true
|
saveAttempted = true
|
||||||
|
@ -53,7 +46,7 @@
|
||||||
|
|
||||||
if (!isValid) return
|
if (!isValid) return
|
||||||
|
|
||||||
store.createScreen(name, route, layoutComponent.name)
|
store.createScreen(name, route, layoutComponent._component)
|
||||||
dialog.hide()
|
dialog.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,21 +55,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const screenNameExists = name => {
|
const screenNameExists = name => {
|
||||||
return some(s => {
|
return $store.screens.some(screen => screen.name.toLowerCase() === name.toLowerCase());
|
||||||
return s.name.toLowerCase() === name.toLowerCase()
|
|
||||||
})(screens)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const routeNameExists = route => {
|
const routeNameExists = route => {
|
||||||
return some(s => {
|
return $store.screens.some(screen => screen.route.toLowerCase() === route.toLowerCase());
|
||||||
return s.route.toLowerCase() === route.toLowerCase()
|
|
||||||
})(screens)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const routeChanged = event => {
|
const routeChanged = event => {
|
||||||
const r = event.target.value
|
if (!event.target.value.startsWith("/")) {
|
||||||
if (!r.startsWith("/")) {
|
route = "/" + event.target.value
|
||||||
route = "/" + r
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -100,7 +88,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
<label class="uk-form-label">Route (Url)</label>
|
<label class="uk-form-label">Route (URL)</label>
|
||||||
<div class="uk-form-controls">
|
<div class="uk-form-controls">
|
||||||
<input
|
<input
|
||||||
class="uk-input uk-form-small"
|
class="uk-input uk-form-small"
|
||||||
|
@ -117,8 +105,8 @@
|
||||||
class="uk-select uk-form-small"
|
class="uk-select uk-form-small"
|
||||||
bind:value={layoutComponent}
|
bind:value={layoutComponent}
|
||||||
class:uk-form-danger={saveAttempted && !layoutComponent}>
|
class:uk-form-danger={saveAttempted && !layoutComponent}>
|
||||||
{#each layoutComponents as comp}
|
{#each layoutComponents as { _component, name }}
|
||||||
<option value={comp}>{comp.componentName} - {comp.libName}</option>
|
<option value={_component}>{name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// import { tick } from "svelte"
|
// import { tick } from "svelte"
|
||||||
import ComponentsHierarchyChildren from "./ComponentsHierarchyChildren.svelte"
|
import ComponentsHierarchyChildren from "./ComponentsHierarchyChildren.svelte"
|
||||||
|
|
||||||
import { last, sortBy, map, trimCharsStart, trimChars, join } from "lodash/fp"
|
import { last, sortBy, map, trimCharsStart, trimChars, join, compose } from "lodash/fp"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import { pipe } from "components/common/core"
|
import { pipe } from "components/common/core"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
|
@ -15,28 +15,15 @@
|
||||||
|
|
||||||
const joinPath = join("/")
|
const joinPath = join("/")
|
||||||
|
|
||||||
const normalizedName = name =>
|
|
||||||
pipe(
|
|
||||||
name,
|
|
||||||
[
|
|
||||||
trimCharsStart("./"),
|
|
||||||
trimCharsStart("~/"),
|
|
||||||
trimCharsStart("../"),
|
|
||||||
trimChars(" "),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
const lastPartOfName = c =>
|
const lastPartOfName = c =>
|
||||||
c && last(c.name ? c.name.split("/") : c._component.split("/"))
|
c && last(c.name ? c.name.split("/") : c._component.split("/"))
|
||||||
|
|
||||||
const isComponentSelected = (current, comp) => current === comp
|
const isComponentSelected = (current, comp) => current === comp
|
||||||
|
|
||||||
const isFolderSelected = (current, folder) => isInSubfolder(current, folder)
|
$: _layout = {
|
||||||
|
component: layout,
|
||||||
$: _layout = pipe(
|
title: lastPartOfName(layout)
|
||||||
layout,
|
}
|
||||||
[c => ({ component: c, title: lastPartOfName(c) })]
|
|
||||||
)
|
|
||||||
|
|
||||||
const isScreenSelected = component =>
|
const isScreenSelected = component =>
|
||||||
component.component &&
|
component.component &&
|
||||||
|
@ -45,7 +32,6 @@
|
||||||
|
|
||||||
const confirmDeleteComponent = async component => {
|
const confirmDeleteComponent = async component => {
|
||||||
componentToDelete = component
|
componentToDelete = component
|
||||||
// await tick()
|
|
||||||
confirmDeleteDialog.show()
|
confirmDeleteDialog.show()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -7,16 +7,15 @@
|
||||||
import { pipe } from "components/common/core"
|
import { pipe } from "components/common/core"
|
||||||
import { filter, find, concat } from "lodash/fp"
|
import { filter, find, concat } from "lodash/fp"
|
||||||
|
|
||||||
const notSeletedComponent = { name: "(none selected)" }
|
const notSelectedComponent = { name: "(none selected)" }
|
||||||
|
|
||||||
$: page = $store.pages[$store.currentPageName]
|
$: page = $store.pages[$store.currentPageName]
|
||||||
$: title = page.index.title
|
$: title = page.index.title
|
||||||
$: components = pipe(
|
$: components = pipe(
|
||||||
$store.components,
|
$store.components,
|
||||||
[filter(store => !isRootComponent($store)), concat([notSeletedComponent])]
|
[filter(store => !isRootComponent($store)), concat([notSelectedComponent])]
|
||||||
)
|
)
|
||||||
$: entryComponent =
|
$: entryComponent = components[page.appBody] || notSelectedComponent
|
||||||
find(c => c.name === page.appBody)(components) || notSeletedComponent
|
|
||||||
|
|
||||||
const save = () => {
|
const save = () => {
|
||||||
if (!title || !entryComponent || entryComponent === notSeletedComponent)
|
if (!title || !entryComponent || entryComponent === notSeletedComponent)
|
||||||
|
|
|
@ -12,10 +12,7 @@
|
||||||
let errors = []
|
let errors = []
|
||||||
const props_to_ignore = ["_component", "_children", "_styles", "_code", "_id"]
|
const props_to_ignore = ["_component", "_children", "_styles", "_code", "_id"]
|
||||||
|
|
||||||
$: componentDef =
|
$: componentDef = components[component._component]
|
||||||
component &&
|
|
||||||
components &&
|
|
||||||
components.find(({ name }) => name === component._component)
|
|
||||||
|
|
||||||
let setProp = (name, value) => {
|
let setProp = (name, value) => {
|
||||||
onPropChanged(name, value)
|
onPropChanged(name, value)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import IconButton from "components/common/IconButton.svelte"
|
import IconButton from "components/common/IconButton.svelte"
|
||||||
import NewScreen from "./NewScreen.svelte"
|
import NewScreen from "./NewScreen.svelte"
|
||||||
import CurrentItemPreview from "./CurrentItemPreview.svelte"
|
import AppPreview from "./AppPreview"
|
||||||
import PageView from "./PageView.svelte"
|
import PageView from "./PageView.svelte"
|
||||||
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte"
|
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="preview-pane">
|
<div class="preview-pane">
|
||||||
<CurrentItemPreview />
|
<AppPreview />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $store.currentFrontEndType === 'screen' || $store.currentFrontEndType === 'page'}
|
{#if $store.currentFrontEndType === 'screen' || $store.currentFrontEndType === 'page'}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { isString, isUndefined } from "lodash/fp"
|
import { isString, isUndefined } from "lodash/fp"
|
||||||
import { types } from "./types"
|
import { TYPE_MAP } from "./types"
|
||||||
import { assign } from "lodash"
|
import { assign } from "lodash"
|
||||||
import { uuid } from "builderStore/uuid"
|
import { uuid } from "builderStore/uuid"
|
||||||
|
|
||||||
|
@ -12,17 +12,17 @@ export const getBuiltin = name => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getNewScreen = (components, rootComponentName, name) => {
|
// export const getNewScreen = (components, rootComponentName, name) => {
|
||||||
const rootComponent = components.find(c => c.name === rootComponentName)
|
// const rootComponent = components[rootComponentName]
|
||||||
return {
|
// return {
|
||||||
name: name || "",
|
// name: name || "",
|
||||||
description: "",
|
// description: "",
|
||||||
url: "",
|
// url: "",
|
||||||
_css: "",
|
// _css: "",
|
||||||
uiFunctions: "",
|
// uiFunctions: "",
|
||||||
props: createProps(rootComponent).props,
|
// props: createProps(rootComponent).props,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {object} componentDefinition - component definition from a component library
|
* @param {object} componentDefinition - component definition from a component library
|
||||||
|
@ -33,9 +33,9 @@ export const createProps = (componentDefinition, derivedFromProps) => {
|
||||||
const errorOccurred = (propName, error) => errors.push({ propName, error })
|
const errorOccurred = (propName, error) => errors.push({ propName, error })
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
_component: componentDefinition.name,
|
|
||||||
_styles: { position: {}, layout: {} },
|
|
||||||
_id: uuid(),
|
_id: uuid(),
|
||||||
|
_component: componentDefinition._component,
|
||||||
|
_styles: { position: {}, layout: {} },
|
||||||
_code: "",
|
_code: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,11 +44,14 @@ export const createProps = (componentDefinition, derivedFromProps) => {
|
||||||
if (!componentDefinition.name)
|
if (!componentDefinition.name)
|
||||||
errorOccurred("_component", "Component name not supplied")
|
errorOccurred("_component", "Component name not supplied")
|
||||||
|
|
||||||
const propsDef = componentDefinition.props
|
for (let propName in componentDefinition.props) {
|
||||||
for (let propDef in propsDef) {
|
const parsedPropDef = parsePropDef(componentDefinition.props[propName])
|
||||||
const parsedPropDef = parsePropDef(propsDef[propDef])
|
|
||||||
if (parsedPropDef.error) errorOccurred(propDef, parsedPropDef.error)
|
if (parsedPropDef.error) {
|
||||||
else props[propDef] = parsedPropDef
|
errors.push({ propName, error: parsedPropDef.error })
|
||||||
|
} else {
|
||||||
|
props[propName] = parsedPropDef
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (derivedFromProps) {
|
if (derivedFromProps) {
|
||||||
|
@ -88,20 +91,18 @@ const parsePropDef = propDef => {
|
||||||
const error = message => ({ error: message, propDef })
|
const error = message => ({ error: message, propDef })
|
||||||
|
|
||||||
if (isString(propDef)) {
|
if (isString(propDef)) {
|
||||||
if (!types[propDef]) return error(`Do not recognise type ${propDef}`)
|
if (!TYPE_MAP[propDef]) return error(`Type ${propDef} is not recognised.`)
|
||||||
|
|
||||||
return types[propDef].default()
|
return TYPE_MAP[propDef].default
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!propDef.type) return error("Property Definition must declare a type")
|
const type = TYPE_MAP[propDef.type]
|
||||||
|
if (!type) return error(`Type ${propDef.type} is not recognised.`)
|
||||||
|
|
||||||
const type = types[propDef.type]
|
// if (isUndefined(propDef.default)) return type.default(propDef)
|
||||||
if (!type) return error(`Do not recognise type ${propDef.type}`)
|
|
||||||
|
|
||||||
if (isUndefined(propDef.default)) return type.default(propDef)
|
// if (!type.isOfType(propDef.default))
|
||||||
|
// return error(`${propDef.default} is not of type ${type}`)
|
||||||
if (!type.isOfType(propDef.default))
|
|
||||||
return error(`${propDef.default} is not of type ${type}`)
|
|
||||||
|
|
||||||
return propDef.default
|
return propDef.default
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
import { splitName } from "./splitRootComponentName"
|
|
||||||
import { find, filter, cloneDeep, isPlainObject, isArray } from "lodash/fp"
|
|
||||||
import { isRootComponent } from "./searchComponents"
|
|
||||||
|
|
||||||
export const libraryDependencies = (components, lib) => {
|
|
||||||
const componentDependsOnLibrary = comp => {
|
|
||||||
if (isRootComponent(comp)) {
|
|
||||||
const { libName } = splitName(comp.name)
|
|
||||||
return libName === lib
|
|
||||||
}
|
|
||||||
return componentDependsOnLibrary(
|
|
||||||
find(c => c.name === comp.props._component)(components)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return filter(c => !isRootComponent(c) && componentDependsOnLibrary(c))(
|
|
||||||
components
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const componentDependencies = (
|
|
||||||
pages,
|
|
||||||
screens,
|
|
||||||
components,
|
|
||||||
dependsOn
|
|
||||||
) => {
|
|
||||||
const allComponents = [...cloneDeep(components), ...cloneDeep(screens)]
|
|
||||||
|
|
||||||
pages = cloneDeep(pages)
|
|
||||||
const dependantComponents = []
|
|
||||||
const dependantPages = []
|
|
||||||
|
|
||||||
const traverseProps = props => {
|
|
||||||
if (props._component && props._component === dependsOn.name) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let propName in props) {
|
|
||||||
const prop = props[propName]
|
|
||||||
if (isPlainObject(prop) && prop._component) {
|
|
||||||
if (traverseProps(prop)) return true
|
|
||||||
}
|
|
||||||
if (isArray(prop)) {
|
|
||||||
for (let element of prop) {
|
|
||||||
if (traverseProps(element)) return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let component of allComponents) {
|
|
||||||
if (isRootComponent(component)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component.name === dependsOn.name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component.props._component === dependsOn.name) {
|
|
||||||
dependantComponents.push(component)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (traverseProps(component.props)) {
|
|
||||||
dependantComponents.push(component)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let pageName in pages) {
|
|
||||||
const page = pages[pageName]
|
|
||||||
if (page.appBody === dependsOn.name) {
|
|
||||||
dependantPages.push(pageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { dependantComponents, dependantPages }
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { find, isUndefined, filter, some, includes, pipe } from "lodash/fp"
|
import { find, isUndefined, filter, some, includes } from "lodash/fp"
|
||||||
|
import { pipe } from "components/common/core";
|
||||||
|
|
||||||
const normalString = s => (s || "").trim().toLowerCase()
|
const normalString = s => (s || "").trim().toLowerCase()
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { split, last, pipe } from "lodash/fp"
|
import { split, last } from "lodash/fp"
|
||||||
|
import { pipe } from "components/common/core";
|
||||||
|
|
||||||
export const splitName = fullname => {
|
export const splitName = fullname => {
|
||||||
const componentName = pipe(fullname, [split("/"), last])
|
const componentName = pipe(fullname, [split("/"), last])
|
||||||
|
|
|
@ -14,70 +14,86 @@ import {
|
||||||
BB_STATE_BINDINGPATH,
|
BB_STATE_BINDINGPATH,
|
||||||
} from "@budibase/client/src/state/parseBinding"
|
} from "@budibase/client/src/state/parseBinding"
|
||||||
|
|
||||||
const defaultDef = typeName => () => ({
|
// const defaultDef = typeName => () => ({
|
||||||
type: typeName,
|
// type: typeName,
|
||||||
required: false,
|
// required: false,
|
||||||
default: types[typeName].default(),
|
// default: types[typeName].default,
|
||||||
options: typeName === "options" ? [] : undefined,
|
// options: typeName === "options" ? [] : undefined,
|
||||||
})
|
// })
|
||||||
|
|
||||||
const propType = (defaultValue, isOfType, defaultDefinition) => ({
|
// const propType = (defaultValue, isOfType, defaultDefinition) => ({
|
||||||
isOfType,
|
// isOfType,
|
||||||
default: defaultValue,
|
// default: defaultValue,
|
||||||
defaultDefinition,
|
// defaultDefinition,
|
||||||
})
|
// })
|
||||||
|
|
||||||
const expandSingleProp = propDef => {
|
// const expandSingleProp = propDef => {
|
||||||
const p = isString(propDef) ? types[propDef].defaultDefinition() : propDef
|
// const p = isString(propDef) ? types[propDef].defaultDefinition() : propDef
|
||||||
|
|
||||||
if (!isString(propDef)) {
|
// if (!isString(propDef)) {
|
||||||
const def = types[propDef.type].defaultDefinition()
|
// const def = types[propDef.type].defaultDefinition()
|
||||||
for (let p in def) {
|
// for (let p in def) {
|
||||||
if (propDef[p] === undefined) {
|
// if (propDef[p] === undefined) {
|
||||||
propDef[p] = def[p]
|
// propDef[p] = def[p]
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return p
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export const expandComponentDefinition = componentDefinition => {
|
||||||
|
// const expandedProps = {}
|
||||||
|
// const expandedComponent = { ...componentDefinition }
|
||||||
|
|
||||||
|
// for (let p in componentDefinition.props) {
|
||||||
|
// expandedProps[p] = expandSingleProp(componentDefinition.props[p])
|
||||||
|
// }
|
||||||
|
|
||||||
|
// expandedComponent.props = expandedProps
|
||||||
|
|
||||||
|
// if (expandedComponent.children !== false) {
|
||||||
|
// expandedComponent.children = true
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return expandedComponent
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const isEvent = e =>
|
||||||
|
// isPlainObject(e) &&
|
||||||
|
// isString(e[EVENT_TYPE_MEMBER_NAME]) &&
|
||||||
|
// isPlainObject(e.parameters)
|
||||||
|
|
||||||
|
// const isEventList = e => isArray(e) && every(isEvent)(e)
|
||||||
|
|
||||||
|
// // const EMPTY_STATE = () => ({ [BB_STATE_BINDINGPATH]: "" });
|
||||||
|
|
||||||
|
// export const types = {
|
||||||
|
// string: propType("", isString, defaultDef("string")),
|
||||||
|
// bool: propType(false, isBoolean, defaultDef("bool")),
|
||||||
|
// number: propType(0, isNumber, defaultDef("number")),
|
||||||
|
// options: propType("", isString, defaultDef("options")),
|
||||||
|
// asset: propType("", isString, defaultDef("asset")),
|
||||||
|
// event: propType([], isEventList, defaultDef("event")),
|
||||||
|
// // state: propType(EMPTY_STATE, isBound, defaultDef("state")),
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
export const TYPE_MAP = {
|
||||||
|
string: {
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
bool: {
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
number: {
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
default: [],
|
||||||
|
options: []
|
||||||
|
},
|
||||||
|
event: {
|
||||||
|
default: [],
|
||||||
}
|
}
|
||||||
|
};
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
export const expandComponentDefinition = componentDefinition => {
|
|
||||||
const expandedProps = {}
|
|
||||||
const expandedComponent = { ...componentDefinition }
|
|
||||||
|
|
||||||
for (let p in componentDefinition.props) {
|
|
||||||
expandedProps[p] = expandSingleProp(componentDefinition.props[p])
|
|
||||||
}
|
|
||||||
|
|
||||||
expandedComponent.props = expandedProps
|
|
||||||
|
|
||||||
if (expandedComponent.children !== false) {
|
|
||||||
expandedComponent.children = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return expandedComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
const isEvent = e =>
|
|
||||||
isPlainObject(e) &&
|
|
||||||
isString(e[EVENT_TYPE_MEMBER_NAME]) &&
|
|
||||||
isPlainObject(e.parameters)
|
|
||||||
|
|
||||||
const isEventList = e => isArray(e) && every(isEvent)(e)
|
|
||||||
|
|
||||||
const emptyState = () => {
|
|
||||||
const s = {}
|
|
||||||
s[BB_STATE_BINDINGPATH] = ""
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
export const types = {
|
|
||||||
string: propType(() => "", isString, defaultDef("string")),
|
|
||||||
bool: propType(() => false, isBoolean, defaultDef("bool")),
|
|
||||||
number: propType(() => 0, isNumber, defaultDef("number")),
|
|
||||||
options: propType(() => "", isString, defaultDef("options")),
|
|
||||||
asset: propType(() => "", isString, defaultDef("asset")),
|
|
||||||
event: propType(() => [], isEventList, defaultDef("event")),
|
|
||||||
state: propType(() => emptyState(), isBound, defaultDef("state")),
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
import { isString, keys, flatten, isArray, map, filter } from "lodash/fp"
|
|
||||||
import { common } from "../../../../../core/src"
|
|
||||||
const pipe = common.$
|
|
||||||
|
|
||||||
export const validatePage = page => {
|
|
||||||
const errors = []
|
|
||||||
const error = message => errors.push(message)
|
|
||||||
|
|
||||||
const noIndex = !page.index
|
|
||||||
if (noIndex) {
|
|
||||||
error("Page does not define an index member")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!page.appBody ||
|
|
||||||
!isString(page.appBody) ||
|
|
||||||
!page.appBody.endsWith(".json")
|
|
||||||
) {
|
|
||||||
error("App body must be set to a valid JSON file")
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Commenting this for now
|
|
||||||
* index is a load of static members just now, but maybe useful
|
|
||||||
for pageLayout props (which is just a pipe dream at time of writing)
|
|
||||||
const indexHtmlErrors = noIndex
|
|
||||||
? []
|
|
||||||
: pipe(
|
|
||||||
recursivelyValidate(page.index, getComponent), [
|
|
||||||
map(e => `Index.html: ${e.error}`)
|
|
||||||
]);
|
|
||||||
*/
|
|
||||||
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
export const validatePages = (pages, getComponent) => {
|
|
||||||
let errors = []
|
|
||||||
const error = message => errors.push(message)
|
|
||||||
|
|
||||||
if (!pages.main) {
|
|
||||||
error("must have a 'main' page")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pages.unauthenticated) {
|
|
||||||
error("must have a 'unauthenticated' (login) page")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!pages.componentLibraries ||
|
|
||||||
!isArray(pages.componentLibraries) ||
|
|
||||||
pages.componentLibraries.length === 0
|
|
||||||
) {
|
|
||||||
error("componentLibraries must be set to a non-empty array of strings")
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageErrors = pipe(pages, [
|
|
||||||
keys,
|
|
||||||
filter(k => k !== "componentLibraries"),
|
|
||||||
map(k => validatePage(pages[k], getComponent)),
|
|
||||||
flatten,
|
|
||||||
])
|
|
||||||
|
|
||||||
return [...errors, ...pageErrors]
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const defaultPagesObject = () => ({
|
export const DEFAULT_PAGES_OBJECT = {
|
||||||
main: {
|
main: {
|
||||||
_props: {},
|
_props: {},
|
||||||
_screens: {},
|
_screens: {},
|
||||||
|
@ -17,4 +17,4 @@ export const defaultPagesObject = () => ({
|
||||||
},
|
},
|
||||||
componentLibraries: [],
|
componentLibraries: [],
|
||||||
stylesheets: [],
|
stylesheets: [],
|
||||||
})
|
}
|
|
@ -71,6 +71,10 @@ html, body {
|
||||||
width:100%;
|
width:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-family: var(--fontblack);
|
font-family: var(--fontblack);
|
||||||
font-size: 36pt;
|
font-size: 36pt;
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
// Get Package and set store
|
// Get Package and set store
|
||||||
export let application
|
export let application
|
||||||
|
|
||||||
|
let ready = false
|
||||||
|
|
||||||
let promise = getPackage()
|
let promise = getPackage()
|
||||||
|
|
||||||
async function getPackage() {
|
async function getPackage() {
|
||||||
|
@ -71,6 +73,8 @@
|
||||||
<div />
|
<div />
|
||||||
{:then}
|
{:then}
|
||||||
<slot />
|
<slot />
|
||||||
|
{:catch error}
|
||||||
|
<p>Something went wrong: {error.message}</p>
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -94,13 +98,6 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
width: 100%;
|
|
||||||
height: 100px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content > div {
|
.content > div {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -4,11 +4,8 @@
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
$: instances = $store.appInstances
|
$: instances = $store.appInstances
|
||||||
$: views = $store.hierarchy.indexes
|
|
||||||
|
|
||||||
async function selectDatabase(database) {
|
async function selectDatabase(database) {
|
||||||
backendUiStore.actions.records.select(null)
|
|
||||||
backendUiStore.actions.views.select(views[0])
|
|
||||||
backendUiStore.actions.database.select(database)
|
backendUiStore.actions.database.select(database)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
import {
|
|
||||||
validatePages,
|
|
||||||
validatePage,
|
|
||||||
} from "../src/components/userInterface/pagesParsing/validatePages"
|
|
||||||
|
|
||||||
const validPages = () => ({
|
|
||||||
main: {
|
|
||||||
index: {
|
|
||||||
title: "My Cool App",
|
|
||||||
},
|
|
||||||
appBody: "./main.app.json",
|
|
||||||
},
|
|
||||||
unauthenticated: {
|
|
||||||
index: {
|
|
||||||
title: "My Cool App - Login",
|
|
||||||
},
|
|
||||||
appBody: "./unauthenticated.app.json",
|
|
||||||
},
|
|
||||||
componentLibraries: ["./myComponents"],
|
|
||||||
})
|
|
||||||
|
|
||||||
const getComponent = name =>
|
|
||||||
({
|
|
||||||
testIndexHtml: {
|
|
||||||
name: "testIndexHtml",
|
|
||||||
props: {
|
|
||||||
title: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}[name])
|
|
||||||
|
|
||||||
describe("validate single page", () => {
|
|
||||||
it("should return no errors when page is valid", () => {
|
|
||||||
const errors = validatePage(validPages().main, getComponent)
|
|
||||||
|
|
||||||
expect(errors).toEqual([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return error when index is not set, or set incorrectly", () => {
|
|
||||||
let page = validPages().main
|
|
||||||
delete page.index
|
|
||||||
expect(validatePage(page, getComponent).length).toEqual(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return error when appBody is not set, or set incorrectly", () => {
|
|
||||||
let page = validPages().main
|
|
||||||
delete page.appBody
|
|
||||||
expect(validatePage(page, getComponent).length).toEqual(1)
|
|
||||||
|
|
||||||
page.appBody = true // not a string
|
|
||||||
expect(validatePage(page, getComponent).length).toEqual(1)
|
|
||||||
|
|
||||||
page.appBody = "something.js" // not a json
|
|
||||||
expect(validatePage(page, getComponent).length).toEqual(1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("validate pages", () => {
|
|
||||||
it("should return no errors when pages are valid", () => {
|
|
||||||
const errors = validatePages(validPages(), getComponent)
|
|
||||||
|
|
||||||
expect(errors).toEqual([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return error when component libraries not set", () => {
|
|
||||||
const pages = validPages()
|
|
||||||
|
|
||||||
delete pages.componentLibraries
|
|
||||||
let errors = validatePages(pages, getComponent)
|
|
||||||
expect(errors.length).toBe(1)
|
|
||||||
|
|
||||||
pages.componentLibraries = []
|
|
||||||
errors = validatePages(pages, getComponent)
|
|
||||||
expect(errors.length).toBe(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return error when no main or unauthenticated page", () => {
|
|
||||||
let pages = validPages()
|
|
||||||
delete pages.main
|
|
||||||
let errors = validatePages(pages, getComponent)
|
|
||||||
expect(errors.length).toBe(1)
|
|
||||||
|
|
||||||
pages = validPages()
|
|
||||||
delete pages.unauthenticated
|
|
||||||
errors = validatePages(pages, getComponent)
|
|
||||||
expect(errors.length).toBe(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return error when page is invalid", () => {
|
|
||||||
const pages = validPages()
|
|
||||||
delete pages.main.index
|
|
||||||
const errors = validatePages(pages, getComponent)
|
|
||||||
expect(errors.length).toBe(1)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -20,7 +20,6 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/datastores": "^0.0.32",
|
|
||||||
"@budibase/server": "^0.0.32",
|
"@budibase/server": "^0.0.32",
|
||||||
"@inquirer/password": "^0.0.6-alpha.0",
|
"@inquirer/password": "^0.0.6-alpha.0",
|
||||||
"chalk": "^2.4.2",
|
"chalk": "^2.4.2",
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"title": "Test App",
|
||||||
|
"favicon": "./_shared/favicon.png",
|
||||||
|
"stylesheets": [],
|
||||||
|
"componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"],
|
||||||
|
"props" : {
|
||||||
|
"_component": "@budibase/standard-components/container",
|
||||||
|
"_children": [],
|
||||||
|
"_id": 0,
|
||||||
|
"type": "div",
|
||||||
|
"_styles": {
|
||||||
|
"layout": {},
|
||||||
|
"position": {}
|
||||||
|
},
|
||||||
|
"_code": ""
|
||||||
|
},
|
||||||
|
"_css": "",
|
||||||
|
"uiFunctions": ""
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"title": "Test App",
|
||||||
|
"favicon": "./_shared/favicon.png",
|
||||||
|
"stylesheets": [],
|
||||||
|
"componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"],
|
||||||
|
"props" : {
|
||||||
|
"_component": "@budibase/standard-components/container",
|
||||||
|
"_children": [],
|
||||||
|
"_id": 1,
|
||||||
|
"type": "div",
|
||||||
|
"_styles": {
|
||||||
|
"layout": {},
|
||||||
|
"position": {}
|
||||||
|
},
|
||||||
|
"_code": ""
|
||||||
|
},
|
||||||
|
"_css": "",
|
||||||
|
"uiFunctions": ""
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ const { copy, readJSON, writeJSON, remove, exists } = require("fs-extra")
|
||||||
const { resolve, join } = require("path")
|
const { resolve, join } = require("path")
|
||||||
const chalk = require("chalk")
|
const chalk = require("chalk")
|
||||||
const { exec } = require("child_process")
|
const { exec } = require("child_process")
|
||||||
const glob = require("glob");
|
|
||||||
|
|
||||||
module.exports = opts => {
|
module.exports = opts => {
|
||||||
run(opts)
|
run(opts)
|
||||||
|
|
|
@ -1,27 +1,20 @@
|
||||||
const { resolve, join } = require("path")
|
const { resolve, join } = require("path")
|
||||||
const { cwd } = require("process")
|
|
||||||
const { homedir } = require("os")
|
const { homedir } = require("os")
|
||||||
const buildAppContext = require("@budibase/server/initialise/buildAppContext")
|
|
||||||
|
|
||||||
module.exports.serverFileName = relativePath =>
|
module.exports.serverFileName = relativePath =>
|
||||||
resolve(__dirname, "..", "node_modules", "@budibase", "server", relativePath)
|
resolve(__dirname, "..", "node_modules", "@budibase", "server", relativePath)
|
||||||
|
|
||||||
module.exports.getAppContext = async ({ configName, masterIsCreated }) => {
|
// module.exports.getAppContext = async ({ configName, masterIsCreated }) => {
|
||||||
if (configName) {
|
// if (configName) {
|
||||||
if (!configName.endsWith(".js")) {
|
// if (!configName.endsWith(".js")) {
|
||||||
configName = `config.${configName}.js`
|
// configName = `config.${configName}.js`
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
configName = "config.js"
|
// configName = "config.js"
|
||||||
}
|
// }
|
||||||
|
|
||||||
const config = require(resolve(cwd(), configName))()
|
// const config = require(resolve(cwd(), configName))()
|
||||||
return await buildAppContext(config, masterIsCreated)
|
// return await buildAppContext(config, masterIsCreated)
|
||||||
}
|
// }
|
||||||
|
|
||||||
module.exports.xPlatHomeDir = dir => {
|
module.exports.xPlatHomeDir = dir => dir.startsWith("~") ? join(homedir(), dir.substring(1)) : dir;
|
||||||
if (dir.startsWith("~")) {
|
|
||||||
dir = join(homedir(), dir.substring(1))
|
|
||||||
}
|
|
||||||
return dir
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,15 +8,14 @@ import { createStateManager } from "./state/stateManager"
|
||||||
export const createApp = (
|
export const createApp = (
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
frontendDefinition,
|
frontendDefinition,
|
||||||
backendDefinition,
|
|
||||||
user,
|
user,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
window
|
window
|
||||||
) => {
|
) => {
|
||||||
const coreApi = createCoreApi(backendDefinition, user)
|
// const coreApi = createCoreApi(backendDefinition, user)
|
||||||
backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
// backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
|
||||||
backendDefinition.hierarchy
|
// backendDefinition.hierarchy
|
||||||
)
|
// )
|
||||||
|
|
||||||
let routeTo
|
let routeTo
|
||||||
let currentUrl
|
let currentUrl
|
||||||
|
@ -26,7 +25,6 @@ export const createApp = (
|
||||||
const onScreenSelected = (screen, store, url) => {
|
const onScreenSelected = (screen, store, url) => {
|
||||||
const stateManager = createStateManager({
|
const stateManager = createStateManager({
|
||||||
store,
|
store,
|
||||||
coreApi,
|
|
||||||
frontendDefinition,
|
frontendDefinition,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
|
@ -74,7 +72,6 @@ export const createApp = (
|
||||||
let rootTreeNode
|
let rootTreeNode
|
||||||
const pageStateManager = createStateManager({
|
const pageStateManager = createStateManager({
|
||||||
store: writable({ _bbuser: user }),
|
store: writable({ _bbuser: user }),
|
||||||
coreApi,
|
|
||||||
frontendDefinition,
|
frontendDefinition,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
uiFunctions,
|
uiFunctions,
|
||||||
|
|
|
@ -27,7 +27,7 @@ export const loadBudibase = async opts => {
|
||||||
temp: false,
|
temp: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
const { appRootPath } = frontendDefinition;
|
let { appRootPath } = frontendDefinition;
|
||||||
appRootPath = appRootPath === "" ? "" : "/" + trimSlash(appRootPath)
|
appRootPath = appRootPath === "" ? "" : "/" + trimSlash(appRootPath)
|
||||||
|
|
||||||
// if (!componentLibraries) componentLibraries = {};
|
// if (!componentLibraries) componentLibraries = {};
|
||||||
|
@ -58,7 +58,6 @@ export const loadBudibase = async opts => {
|
||||||
} = createApp(
|
} = createApp(
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
frontendDefinition,
|
frontendDefinition,
|
||||||
backendDefinition,
|
|
||||||
user,
|
user,
|
||||||
uiFunctions || {},
|
uiFunctions || {},
|
||||||
_window,
|
_window,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { getNewChildRecordToState, getNewRecordToState } from "./coreHandlers"
|
||||||
|
|
||||||
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"
|
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"
|
||||||
|
|
||||||
export const eventHandlers = (store, coreApi, rootPath, routeTo) => {
|
export const eventHandlers = (store, rootPath, routeTo) => {
|
||||||
const handler = (parameters, execute) => ({
|
const handler = (parameters, execute) => ({
|
||||||
execute,
|
execute,
|
||||||
parameters,
|
parameters,
|
||||||
|
@ -34,15 +34,15 @@ export const eventHandlers = (store, coreApi, rootPath, routeTo) => {
|
||||||
"List Records": handler(["indexKey", "statePath"], api.listRecords),
|
"List Records": handler(["indexKey", "statePath"], api.listRecords),
|
||||||
"Save Record": handler(["statePath"], api.saveRecord),
|
"Save Record": handler(["statePath"], api.saveRecord),
|
||||||
|
|
||||||
"Get New Child Record": handler(
|
// "Get New Child Record": handler(
|
||||||
["recordKey", "collectionName", "childRecordType", "statePath"],
|
// ["recordKey", "collectionName", "childRecordType", "statePath"],
|
||||||
getNewChildRecordToState(coreApi, setStateWithStore)
|
// getNewChildRecordToState(coreApi, setStateWithStore)
|
||||||
),
|
// ),
|
||||||
|
|
||||||
"Get New Record": handler(
|
// "Get New Record": handler(
|
||||||
["collectionKey", "childRecordType", "statePath"],
|
// ["collectionKey", "childRecordType", "statePath"],
|
||||||
getNewRecordToState(coreApi, setStateWithStore)
|
// getNewRecordToState(coreApi, setStateWithStore)
|
||||||
),
|
// ),
|
||||||
|
|
||||||
"Navigate To": handler(["url"], param => routeTo(param && param.url)),
|
"Navigate To": handler(["url"], param => routeTo(param && param.url)),
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ const isMetaProp = propName =>
|
||||||
|
|
||||||
export const createStateManager = ({
|
export const createStateManager = ({
|
||||||
store,
|
store,
|
||||||
coreApi,
|
|
||||||
appRootPath,
|
appRootPath,
|
||||||
frontendDefinition,
|
frontendDefinition,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
|
@ -30,7 +29,7 @@ export const createStateManager = ({
|
||||||
onScreenSlotRendered,
|
onScreenSlotRendered,
|
||||||
routeTo,
|
routeTo,
|
||||||
}) => {
|
}) => {
|
||||||
let handlerTypes = eventHandlers(store, coreApi, appRootPath, routeTo)
|
let handlerTypes = eventHandlers(store, appRootPath, routeTo)
|
||||||
let currentState
|
let currentState
|
||||||
|
|
||||||
// any nodes that have props that are bound to the store
|
// any nodes that have props that are bound to the store
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"presets": ["@babel/env"],
|
|
||||||
"sourceMaps": "inline",
|
|
||||||
"retainLines": true
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
/node_modules/
|
|
|
@ -1,5 +0,0 @@
|
||||||
*
|
|
||||||
!datastores/*
|
|
||||||
!index.js
|
|
||||||
!config.js
|
|
||||||
!config.template.js
|
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "debug memory",
|
|
||||||
"program": "${workspaceFolder}/index.js",
|
|
||||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/babel-node",
|
|
||||||
"runtimeArgs": ["--nolazy"],
|
|
||||||
"args":["memory"],
|
|
||||||
"skipFiles": [
|
|
||||||
"<node_internals>/**/*.js"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
import fs from "fs"
|
|
||||||
import { join } from "path"
|
|
||||||
import { promisify } from "es6-promisify"
|
|
||||||
import _rimraf from "rimraf"
|
|
||||||
|
|
||||||
const mkdir = promisify(fs.mkdir)
|
|
||||||
const rimraf = promisify(_rimraf)
|
|
||||||
|
|
||||||
const getConfig = async () => {
|
|
||||||
const config = {
|
|
||||||
local: {
|
|
||||||
root: "./output/local/files",
|
|
||||||
},
|
|
||||||
memory: {
|
|
||||||
root: "./output/memory",
|
|
||||||
},
|
|
||||||
azure: {
|
|
||||||
root: "./output/azure",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
await rimraf("./output")
|
|
||||||
await mkdir("./output")
|
|
||||||
|
|
||||||
for (let type in config) {
|
|
||||||
await mkdir(join("output", type))
|
|
||||||
}
|
|
||||||
|
|
||||||
await mkdir("./output/local/files")
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getConfig
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { mkdir } from "fs"
|
|
||||||
import { join } from "path"
|
|
||||||
import { promisify } from "es6-promisify"
|
|
||||||
|
|
||||||
const mkdirp = promisify(mkdir)
|
|
||||||
|
|
||||||
const getConfig = async () => {
|
|
||||||
const config = {
|
|
||||||
local: {
|
|
||||||
root: "./output/local/files",
|
|
||||||
},
|
|
||||||
memory: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
await mkdirp("./output")
|
|
||||||
|
|
||||||
for (let type in config) {
|
|
||||||
await mkdirp(join("output", type))
|
|
||||||
}
|
|
||||||
|
|
||||||
await mkdirp("./output/local/files")
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getConfig
|
|
|
@ -1,81 +0,0 @@
|
||||||
import {
|
|
||||||
SharedKeyCredential,
|
|
||||||
BlockBlobURL,
|
|
||||||
BlobURL,
|
|
||||||
ContainerURL,
|
|
||||||
ServiceURL,
|
|
||||||
StorageURL,
|
|
||||||
Aborter,
|
|
||||||
} from "@azure/storage-blob"
|
|
||||||
|
|
||||||
export const createFile = ({ containerUrl }) => async (key, content) => {
|
|
||||||
const blobURL = BlobURL.fromContainerURL(containerUrl, key)
|
|
||||||
const blockBlobURL = BlockBlobURL.fromBlobURL(blobURL)
|
|
||||||
await blockBlobURL.upload(Aborter.none, content, content.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const updateFile = opts => async (path, content) =>
|
|
||||||
createFile(opts)(path, content)
|
|
||||||
|
|
||||||
export const loadFile = ({ containerUrl }) => async (key, content) => {
|
|
||||||
const blobURL = BlobURL.fromContainerURL(containerUrl, key)
|
|
||||||
|
|
||||||
const downloadBlockBlobResponse = await blobURL.download(Aborter.none, 0)
|
|
||||||
|
|
||||||
return downloadBlockBlobResponse.readableStreamBody
|
|
||||||
.read(content.length)
|
|
||||||
.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
export const exists = ({ containerUrl }) => async key => {
|
|
||||||
const blobURL = BlobURL.fromContainerURL(containerUrl, key)
|
|
||||||
const getPropsResponse = await blobURL.getProperties()
|
|
||||||
return getPropsResponse._response.StatusCode === 200
|
|
||||||
}
|
|
||||||
|
|
||||||
export const deleteFile = ({ containerURL }) => async key => {
|
|
||||||
const blobURL = BlobURL.fromContainerURL(containerURL, key)
|
|
||||||
await blobURL.delete(Aborter.none)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createContainer = ({ containerUrl }) => async () =>
|
|
||||||
await containerUrl.create(Aborter.none)
|
|
||||||
|
|
||||||
export const deleteContainer = ({ containerUrl }) => async () =>
|
|
||||||
await containerUrl.delete(Aborter.none)
|
|
||||||
|
|
||||||
const initialise = opts => {
|
|
||||||
const sharedKeyCredential = new SharedKeyCredential(
|
|
||||||
opts.account,
|
|
||||||
opts.accountKey
|
|
||||||
)
|
|
||||||
|
|
||||||
const pipeline = StorageURL.newPipeline(sharedKeyCredential)
|
|
||||||
|
|
||||||
const serviceURL = new ServiceURL(
|
|
||||||
`https://${opts.account}.blob.core.windows.net`,
|
|
||||||
pipeline
|
|
||||||
)
|
|
||||||
|
|
||||||
const containerURL = ContainerURL.fromServiceURL(
|
|
||||||
serviceURL,
|
|
||||||
opts.containerName
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
containerURL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default opts => {
|
|
||||||
const access = initialise(opts)
|
|
||||||
return {
|
|
||||||
createFile: createFile(access),
|
|
||||||
updateFile: updateFile(access),
|
|
||||||
loadFile: loadFile(access),
|
|
||||||
exists: exists(access),
|
|
||||||
|
|
||||||
datastoreType: "azure-blob-storage",
|
|
||||||
datastoreDescription: "",
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
const {
|
|
||||||
access,
|
|
||||||
mkdir,
|
|
||||||
remove,
|
|
||||||
unlink,
|
|
||||||
readdir,
|
|
||||||
rename,
|
|
||||||
stat,
|
|
||||||
readFile,
|
|
||||||
writeFile,
|
|
||||||
} = require("fs-extra")
|
|
||||||
const { join } = require("path")
|
|
||||||
const { createReadStream, createWriteStream } = require("fs")
|
|
||||||
|
|
||||||
const _writeFile = (path, content, overwrite) =>
|
|
||||||
writeFile(path, content, {
|
|
||||||
encoding: "utf8",
|
|
||||||
flag: overwrite ? "w" : "wx",
|
|
||||||
})
|
|
||||||
|
|
||||||
const updateFile = root => async (path, file) =>
|
|
||||||
await _writeFile(join(root, path), file, true)
|
|
||||||
|
|
||||||
const createFile = root => async (path, file) =>
|
|
||||||
await _writeFile(join(root, path), file, false)
|
|
||||||
|
|
||||||
const loadFile = root => async path => await readFile(join(root, path), "utf8")
|
|
||||||
|
|
||||||
const exists = root => async path => {
|
|
||||||
try {
|
|
||||||
await access(join(root, path))
|
|
||||||
} catch (e) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
const createFolder = root => async path => await mkdir(join(root, path))
|
|
||||||
|
|
||||||
const deleteFile = root => async path => await unlink(join(root, path))
|
|
||||||
|
|
||||||
module.exports.deleteFile = deleteFile
|
|
||||||
|
|
||||||
const deleteFolder = root => async path => await remove(join(root, path))
|
|
||||||
|
|
||||||
const readableFileStream = root => async path =>
|
|
||||||
createReadStream(join(root, path))
|
|
||||||
|
|
||||||
const writableFileStream = root => path =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
const stream = createWriteStream(join(root, path), "utf8")
|
|
||||||
stream.on("open", () => resolve(stream))
|
|
||||||
stream.on("error", reject)
|
|
||||||
})
|
|
||||||
|
|
||||||
const getFolderContents = root => async path => await readdir(join(root, path))
|
|
||||||
|
|
||||||
const renameFile = root => async (oldPath, newPath) =>
|
|
||||||
await rename(join(root, oldPath), join(root, newPath))
|
|
||||||
|
|
||||||
const getFileSize = root => async path => (await stat(join(root, path))).size
|
|
||||||
|
|
||||||
const datastoreFolder = (applicationId, instanceId) =>
|
|
||||||
applicationId === "master" ? "master" : `app.${applicationId}.${instanceId}`
|
|
||||||
|
|
||||||
const createEmptyDb = rootConfig => async (applicationId, instanceId) => {
|
|
||||||
const folder = datastoreFolder(applicationId, instanceId)
|
|
||||||
const dbRootConfig = getDbRootConfig(rootConfig, applicationId, instanceId)
|
|
||||||
await createFolder(dbRootConfig)(folder)
|
|
||||||
return folder
|
|
||||||
}
|
|
||||||
|
|
||||||
const getDatastoreConfig = rootConfig => (applicationId, instanceId) =>
|
|
||||||
join(rootConfig.rootPath, datastoreFolder(applicationId, instanceId))
|
|
||||||
|
|
||||||
const getMasterDbRootConfig = rootConfig => () => rootConfig.rootPath
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const getInstanceDbRootConfig = rootConfig => (_applicationId, _instanceId) =>
|
|
||||||
rootConfig.rootPath
|
|
||||||
const getDbRootConfig = (rootConfig, applicationId, instanceId) =>
|
|
||||||
applicationId === "master"
|
|
||||||
? getMasterDbRootConfig(rootConfig)()
|
|
||||||
: getInstanceDbRootConfig(rootConfig)(applicationId, instanceId)
|
|
||||||
|
|
||||||
module.exports.databaseManager = rootConfig => ({
|
|
||||||
createEmptyDb: createEmptyDb(rootConfig),
|
|
||||||
getDatastoreConfig: getDatastoreConfig(rootConfig),
|
|
||||||
getMasterDbRootConfig: getMasterDbRootConfig(rootConfig),
|
|
||||||
getInstanceDbRootConfig: getInstanceDbRootConfig(rootConfig),
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports.getDatastore = rootFolderPath => ({
|
|
||||||
createFile: createFile(rootFolderPath),
|
|
||||||
updateFile: updateFile(rootFolderPath),
|
|
||||||
loadFile: loadFile(rootFolderPath),
|
|
||||||
exists: exists(rootFolderPath),
|
|
||||||
deleteFile: deleteFile(rootFolderPath),
|
|
||||||
createFolder: createFolder(rootFolderPath),
|
|
||||||
deleteFolder: deleteFolder(rootFolderPath),
|
|
||||||
readableFileStream: readableFileStream(rootFolderPath),
|
|
||||||
writableFileStream: writableFileStream(rootFolderPath),
|
|
||||||
renameFile: renameFile(rootFolderPath),
|
|
||||||
getFolderContents: getFolderContents(rootFolderPath),
|
|
||||||
getFileSize: getFileSize(rootFolderPath),
|
|
||||||
createEmptyDb: createEmptyDb(rootFolderPath),
|
|
||||||
datastoreType: "local",
|
|
||||||
datastoreDescription: rootFolderPath,
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports.configParameters = {
|
|
||||||
rootPath: "Root Data Folder",
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
import { isUndefined, has } from "lodash"
|
|
||||||
import { take } from "lodash/fp"
|
|
||||||
import { Readable, Writable } from "readable-stream"
|
|
||||||
import { Buffer } from "safe-buffer"
|
|
||||||
import { splitKey, joinKey, $ } from "../src/common"
|
|
||||||
import { getLastPartInKey } from "../src/templateApi/heirarchy"
|
|
||||||
|
|
||||||
const folderMarker = "OH-YES-ITSA-FOLDER-"
|
|
||||||
const isFolder = val => val.includes(folderMarker)
|
|
||||||
|
|
||||||
const getParentFolderKey = key =>
|
|
||||||
$(key, [splitKey, take(splitKey(key).length - 1), joinKey])
|
|
||||||
|
|
||||||
const getParentFolder = (data, key) => {
|
|
||||||
const parentKey = getParentFolderKey(key)
|
|
||||||
if (data[parentKey] === undefined)
|
|
||||||
throw new Error(
|
|
||||||
"Parent folder for " + key + " does not exist (" + parentKey + ")"
|
|
||||||
)
|
|
||||||
return JSON.parse(data[parentKey])
|
|
||||||
}
|
|
||||||
|
|
||||||
const addItemToParentFolder = (data, path) => {
|
|
||||||
if (getParentFolderKey(path) === "/") return
|
|
||||||
const parentFolder = getParentFolder(data, path)
|
|
||||||
parentFolder.items.push(getLastPartInKey(path))
|
|
||||||
data[getParentFolderKey(path)] = JSON.stringify(parentFolder)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createFile = data => async (path, content) => {
|
|
||||||
if (await exists(data)(path)) {
|
|
||||||
throw new Error(path + " already exists")
|
|
||||||
}
|
|
||||||
addItemToParentFolder(data, path)
|
|
||||||
data[path] = content
|
|
||||||
}
|
|
||||||
export const updateFile = data => async (path, content) => {
|
|
||||||
// putting this check in to force use of create
|
|
||||||
if (!(await exists(data)(path)))
|
|
||||||
throw new Error("cannot update " + path + " - does not exist")
|
|
||||||
data[path] = content
|
|
||||||
}
|
|
||||||
|
|
||||||
export const writableFileStream = data => async path => {
|
|
||||||
//if(!await exists(data)(path)) throw new Error("cannot write stream to " + path + " - does not exist");
|
|
||||||
const stream = Writable()
|
|
||||||
stream._write = (chunk, encoding, done) => {
|
|
||||||
data[path] = data[path] === undefined ? [] : data[path]
|
|
||||||
data[path] = [...data[path], ...chunk]
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
return stream
|
|
||||||
}
|
|
||||||
|
|
||||||
export const readableFileStream = data => async path => {
|
|
||||||
if (!(await exists(data)(path)))
|
|
||||||
throw new Error("cannot read stream from " + path + " - does not exist")
|
|
||||||
const s = new Readable()
|
|
||||||
s._read = () => {
|
|
||||||
s.push(Buffer.from(data[path]))
|
|
||||||
s.push(null)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
export const renameFile = data => async (oldKey, newKey) => {
|
|
||||||
if (!(await exists(data)(oldKey)))
|
|
||||||
throw new Error("cannot rename path: " + oldKey + " ... does not exist")
|
|
||||||
if (await exists(data)(newKey))
|
|
||||||
throw new Error("cannot rename path: " + newKey + " ... already exists")
|
|
||||||
data[newKey] = data[oldKey]
|
|
||||||
delete data[oldKey]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadFile = data => async path => {
|
|
||||||
const result = data[path]
|
|
||||||
if (isUndefined(result))
|
|
||||||
throw new Error("Load failed - path " + path + " does not exist")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
export const exists = data => async path => has(data, path)
|
|
||||||
export const deleteFile = data => async path => {
|
|
||||||
if (!(await exists(data)(path)))
|
|
||||||
throw new Error("Cannot delete file, path " + path + " does not exist")
|
|
||||||
if (isFolder(data[path]))
|
|
||||||
throw new Error("DeleteFile: Path " + path + " is a folder, not a file")
|
|
||||||
const parentFolder = getParentFolder(data, path)
|
|
||||||
parentFolder.items = parentFolder.items.filter(
|
|
||||||
i => i !== getLastPartInKey(path)
|
|
||||||
)
|
|
||||||
data[getParentFolderKey(path)] = JSON.stringify(parentFolder)
|
|
||||||
delete data[path]
|
|
||||||
}
|
|
||||||
export const createFolder = data => async path => {
|
|
||||||
if (await exists(data)(path))
|
|
||||||
throw new Error("Cannot create folder, path " + path + " already exists")
|
|
||||||
addItemToParentFolder(data, path)
|
|
||||||
data[path] = JSON.stringify({ folderMarker, items: [] })
|
|
||||||
}
|
|
||||||
export const deleteFolder = data => async path => {
|
|
||||||
if (!(await exists(data)(path)))
|
|
||||||
throw new Error("Cannot delete folder, path " + path + " does not exist")
|
|
||||||
if (!isFolder(data[path]))
|
|
||||||
throw new Error("DeleteFolder: Path " + path + " is not a folder")
|
|
||||||
delete data[path]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getFolderContents = data => async folderPath => {
|
|
||||||
if (!isFolder(data[folderPath]))
|
|
||||||
throw new Error("Not a folder: " + folderPath)
|
|
||||||
if (!(await exists(data)(folderPath)))
|
|
||||||
throw new Error("Folder does not exist: " + folderPath)
|
|
||||||
return JSON.parse(data[folderPath]).items
|
|
||||||
}
|
|
||||||
|
|
||||||
export default data => {
|
|
||||||
return {
|
|
||||||
createFile: createFile(data),
|
|
||||||
updateFile: updateFile(data),
|
|
||||||
loadFile: loadFile(data),
|
|
||||||
exists: exists(data),
|
|
||||||
deleteFile: deleteFile(data),
|
|
||||||
createFolder: createFolder(data),
|
|
||||||
deleteFolder: deleteFolder(data),
|
|
||||||
readableFileStream: readableFileStream(data),
|
|
||||||
writableFileStream: writableFileStream(data),
|
|
||||||
renameFile: renameFile(data),
|
|
||||||
getFolderContents: getFolderContents(data),
|
|
||||||
datastoreType: "memory",
|
|
||||||
datastoreDescription: "",
|
|
||||||
data,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
import local from "./datastores/local"
|
|
||||||
import azureBlob from "./datastores/azure-blob"
|
|
||||||
import memory from "./datastores/memory"
|
|
||||||
import getConfig from "./config"
|
|
||||||
import tests from "./tests"
|
|
||||||
|
|
||||||
const initialise = async () => {
|
|
||||||
const type = process.argv[2]
|
|
||||||
const config = (await getConfig())[type]
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "local":
|
|
||||||
return { datastore: local(config.root), config }
|
|
||||||
case "memory":
|
|
||||||
return { datastore: memory(config), config }
|
|
||||||
case "azure":
|
|
||||||
return { datastore: azureBlob(config), config }
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initialise()
|
|
||||||
.then(init => {
|
|
||||||
return tests(init.datastore, init.config)
|
|
||||||
})
|
|
||||||
.then(() => console.log("done"))
|
|
||||||
.catch(e => console.log(e))
|
|
|
@ -1,39 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@budibase/datastores",
|
|
||||||
"version": "0.0.32",
|
|
||||||
"description": "implementations of all the datastores... azureblob, local etc",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"memory": "npx babel-node index.js memory",
|
|
||||||
"local": "npx babel-node index.js local",
|
|
||||||
"azure": "npx babel-node index.js azure"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+ssh://git@gitlab.com/budibase-source/budibase-datastores.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"budibase"
|
|
||||||
],
|
|
||||||
"author": "Michael Shanks",
|
|
||||||
"license": "AGPL-3.0",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://gitlab.com/budibase-source/budibase-datastores/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://gitlab.com/budibase-source/budibase-datastores#README",
|
|
||||||
"dependencies": {
|
|
||||||
"@azure/storage-blob": "^10.1.0-preview",
|
|
||||||
"@babel/cli": "^7.1.2",
|
|
||||||
"@babel/core": "^7.1.2",
|
|
||||||
"@babel/node": "^7.0.0",
|
|
||||||
"@babel/preset-env": "^7.1.0",
|
|
||||||
"@budibase/core": "^0.0.32",
|
|
||||||
"es6-promisify": "^6.0.1",
|
|
||||||
"fs-extra": "^8.1.0",
|
|
||||||
"lodash": "^4.17.13",
|
|
||||||
"p-limit": "^2.0.0",
|
|
||||||
"papaparse": "^4.6.1",
|
|
||||||
"rimraf": "^2.6.2"
|
|
||||||
},
|
|
||||||
"gitHead": "b1f4f90927d9e494e513220ef060af28d2d42455"
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { eventsList } from "@budibase/core"
|
|
||||||
import { filter, union, has, map } from "lodash/fp"
|
|
||||||
|
|
||||||
const allEventsOfType = type => filter(e => e.endsWith(`:${type}`))(eventsList)
|
|
||||||
|
|
||||||
const hasRecord = has("record")
|
|
||||||
|
|
||||||
export const register = (app, logTimeElapsed, eventNamespaces = []) => {
|
|
||||||
const onCompleteEvents =
|
|
||||||
eventNamespaces.length === 0
|
|
||||||
? allEventsOfType("onComplete")
|
|
||||||
: map(e => `${e}:onComplete`)(eventNamespaces)
|
|
||||||
|
|
||||||
const onErrorEvents =
|
|
||||||
eventNamespaces.length === 0
|
|
||||||
? allEventsOfType("onError")
|
|
||||||
: map(e => `${e}:onError`)(eventNamespaces)
|
|
||||||
|
|
||||||
for (let ev of union(onCompleteEvents)(onErrorEvents)) {
|
|
||||||
app.subscribe(ev, (_, ctx) => {
|
|
||||||
const info = hasRecord(ctx) ? ctx.record.type() : ""
|
|
||||||
|
|
||||||
logTimeElapsed(ev, ctx.elapsed, info)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { map } from "lodash"
|
|
||||||
|
|
||||||
export const action = (name, run, iterator = iterateActionTimes(1)) => ({
|
|
||||||
name,
|
|
||||||
run,
|
|
||||||
iterator,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const iterateActionTimes = times => run =>
|
|
||||||
map([...Array(times).keys()], run)
|
|
||||||
|
|
||||||
export const iterateCollection = getCollection => run =>
|
|
||||||
map(getCollection(), run)
|
|
|
@ -1,60 +0,0 @@
|
||||||
import setup from "./setup"
|
|
||||||
import records from "./records"
|
|
||||||
import { register } from "./diagnosticPlugin"
|
|
||||||
import pLimit from "p-limit"
|
|
||||||
import papa from "papaparse"
|
|
||||||
import { writeFileSync } from "fs"
|
|
||||||
|
|
||||||
const limit = pLimit(1)
|
|
||||||
|
|
||||||
const iterateActions = async (apis, getIterator) => {
|
|
||||||
const iterator = getIterator(apis)
|
|
||||||
let result = iterator()
|
|
||||||
while (!result.done) {
|
|
||||||
try {
|
|
||||||
const runPromises = result.action.iterator(i =>
|
|
||||||
limit(() => result.action.run(i))
|
|
||||||
)
|
|
||||||
|
|
||||||
await Promise.all(runPromises)
|
|
||||||
result = iterator()
|
|
||||||
} catch (e) {
|
|
||||||
e.message = `FAILED: ${result.action.name}: ${e.message}`
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async (datastore, config) => {
|
|
||||||
const apis = await setup(datastore)
|
|
||||||
|
|
||||||
const diagnostics = []
|
|
||||||
let currentRecordCount = 0
|
|
||||||
|
|
||||||
register(
|
|
||||||
apis,
|
|
||||||
(ev, elapsed, info) => {
|
|
||||||
if (ev === "recordApi:save:onComplete") {
|
|
||||||
currentRecordCount++
|
|
||||||
} else if (ev === "recordApi:delete:onComplete") {
|
|
||||||
currentRecordCount--
|
|
||||||
}
|
|
||||||
diagnostics.push({ method: ev, elapsed, info, count: currentRecordCount })
|
|
||||||
console.log(`${ev} ${info} ${elapsed / 1000} s`)
|
|
||||||
},
|
|
||||||
[
|
|
||||||
"recordApi:save",
|
|
||||||
"recordApi:load",
|
|
||||||
"viewApi:listItems",
|
|
||||||
"recordApi:delete",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
await iterateActions(apis, records)
|
|
||||||
|
|
||||||
const diagnosticscsv = papa.unparse(diagnostics)
|
|
||||||
|
|
||||||
writeFileSync(config.root + "\\results.csv", diagnosticscsv, {
|
|
||||||
encoding: "utf8",
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
import { action, iterateActionTimes } from "./helpers"
|
|
||||||
import { isUndefined, union } from "lodash"
|
|
||||||
|
|
||||||
const createClient = (apis, getState) => async i => {
|
|
||||||
const client = apis.recordApi.getNew("/clients", "client")
|
|
||||||
client.FamilyName = "Humperdink"
|
|
||||||
client.Address1 = `${i} Mainitucetts Avenue`
|
|
||||||
client.Address2 = "Longerton Road South"
|
|
||||||
client.Address3 = "Chalico City"
|
|
||||||
client.Address4 = "Northern Humranistan"
|
|
||||||
client.Postcode = "BY71 5FR"
|
|
||||||
client.CreatedDate = new Date()
|
|
||||||
|
|
||||||
const state = getState()
|
|
||||||
if (isUndefined(state.clientKeys)) state.clientKeys = []
|
|
||||||
state.clientKeys.push(client.key())
|
|
||||||
|
|
||||||
await apis.recordApi.save(client)
|
|
||||||
|
|
||||||
return client.key()
|
|
||||||
}
|
|
||||||
|
|
||||||
const listClients = (apis, getState) => async () => {
|
|
||||||
const clients = await apis.viewApi.listItems("/clients/default")
|
|
||||||
const state = getState()
|
|
||||||
if (state.clientKeys.length !== clients.length) {
|
|
||||||
throw new Error(
|
|
||||||
"list CLients, expected " +
|
|
||||||
state.clientKeys.length.toString() +
|
|
||||||
" clients, actual " +
|
|
||||||
clients.length.toString()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default apis => {
|
|
||||||
const state = {}
|
|
||||||
const getState = () => state
|
|
||||||
|
|
||||||
const noOfRecords = 10000
|
|
||||||
const recordsPerIteration = 10
|
|
||||||
const noOfIterations = noOfRecords / recordsPerIteration
|
|
||||||
|
|
||||||
const actionsInOneIteration = () => [
|
|
||||||
action(
|
|
||||||
"Create client",
|
|
||||||
createClient(apis, getState),
|
|
||||||
iterateActionTimes(recordsPerIteration)
|
|
||||||
),
|
|
||||||
|
|
||||||
action("List Clients", listClients(apis, getState)),
|
|
||||||
]
|
|
||||||
|
|
||||||
let actions = []
|
|
||||||
for (let index = 0; index < noOfIterations; index++) {
|
|
||||||
actions = union(actions, actionsInOneIteration())
|
|
||||||
}
|
|
||||||
|
|
||||||
let actionIndex = 0
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (actionIndex == actions.length) {
|
|
||||||
return { done: true }
|
|
||||||
}
|
|
||||||
const result = { action: actions[actionIndex], done: false }
|
|
||||||
actionIndex++
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
import { getAppApis, getTemplateApi, setupDatastore } from "@budibase/core"
|
|
||||||
|
|
||||||
const addField = templateApi => type => (record, name) => {
|
|
||||||
const field = templateApi.getNewField(type)
|
|
||||||
field.name = name
|
|
||||||
field.type = type
|
|
||||||
field.label = name
|
|
||||||
templateApi.addField(record, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async datastore => {
|
|
||||||
datastore = setupDatastore(datastore)
|
|
||||||
const templateApi = await getTemplateApi(datastore)
|
|
||||||
const addStringField = addField(templateApi)("string")
|
|
||||||
const addDateField = addField(templateApi)("datetime")
|
|
||||||
const addBoolField = addField(templateApi)("bool")
|
|
||||||
|
|
||||||
const root = templateApi.getNewRootLevel()
|
|
||||||
|
|
||||||
const clients = templateApi.getNewCollectionTemplate(root)
|
|
||||||
clients.name = "clients"
|
|
||||||
|
|
||||||
const client = templateApi.getNewModelTemplate(clients)
|
|
||||||
client.name = "client"
|
|
||||||
addStringField(client, "FamilyName")
|
|
||||||
addStringField(client, "Address1")
|
|
||||||
addStringField(client, "Address2")
|
|
||||||
addStringField(client, "Address3")
|
|
||||||
addStringField(client, "Address4")
|
|
||||||
addStringField(client, "Postcode")
|
|
||||||
addDateField(client, "CreatedDate")
|
|
||||||
|
|
||||||
const children = templateApi.getNewCollectionTemplate(client)
|
|
||||||
children.name = "children"
|
|
||||||
|
|
||||||
const child = templateApi.getNewModelTemplate(children)
|
|
||||||
child.name = "child"
|
|
||||||
addStringField(child, "FirstName")
|
|
||||||
addStringField(child, "Surname")
|
|
||||||
addDateField(child, "DateOfBirth")
|
|
||||||
addBoolField(child, "Current")
|
|
||||||
|
|
||||||
const contacts = templateApi.getNewCollectionTemplate(client)
|
|
||||||
contacts.name = "contacts"
|
|
||||||
|
|
||||||
const contact = templateApi.getNewModelTemplate(contacts)
|
|
||||||
contact.name = "contact"
|
|
||||||
addStringField(contact, "Name")
|
|
||||||
addStringField(contact, "relationship")
|
|
||||||
addStringField(contact, "phone1")
|
|
||||||
addStringField(contact, "phone2")
|
|
||||||
addBoolField(contact, "active")
|
|
||||||
|
|
||||||
await templateApi.saveApplicationHeirarchy(root)
|
|
||||||
|
|
||||||
const apis = await getAppApis(datastore)
|
|
||||||
|
|
||||||
await apis.collectionApi.initialiseAll()
|
|
||||||
|
|
||||||
return apis
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -24,6 +24,10 @@ exports.create = async function(ctx) {
|
||||||
const { id, rev } = await clientDb.post({
|
const { id, rev } = await clientDb.post({
|
||||||
type: "app",
|
type: "app",
|
||||||
instances: [],
|
instances: [],
|
||||||
|
componentLibraries: [
|
||||||
|
"@budibase/standard-components",
|
||||||
|
"@budibase/materialdesign-components"
|
||||||
|
],
|
||||||
...ctx.request.body,
|
...ctx.request.body,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
const CouchDB = require("../../db");
|
||||||
|
const { homedir } = require("os");
|
||||||
|
const { resolve, join } = require("path");
|
||||||
|
|
||||||
|
exports.fetchAppComponentDefinitions = async function(ctx) {
|
||||||
|
const db = new CouchDB(`client-${ctx.params.clientId}`);
|
||||||
|
const app = await db.get(ctx.params.appId)
|
||||||
|
|
||||||
|
const componentDefinitions = app.componentLibraries.reduce((acc, componentLibrary) => {
|
||||||
|
const appDirectory = resolve(homedir(), ".budibase", ctx.params.appId, "node_modules");
|
||||||
|
const componentJson = require(join(appDirectory, componentLibrary, "components.json"));
|
||||||
|
|
||||||
|
const result = {};
|
||||||
|
|
||||||
|
// map over the components.json and add the library identifier as a key
|
||||||
|
// button -> @budibase/standard-components/button
|
||||||
|
for (key in componentJson) {
|
||||||
|
const fullComponentName = `${componentLibrary}/${key}`;
|
||||||
|
result[fullComponentName] = {
|
||||||
|
_component: fullComponentName,
|
||||||
|
...componentJson[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
...result
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
ctx.body = componentDefinitions;
|
||||||
|
};
|
|
@ -1,5 +1,6 @@
|
||||||
const send = require("koa-send");
|
const send = require("koa-send");
|
||||||
const { resolve } = require("path")
|
const { resolve } = require("path")
|
||||||
|
const { homedir } = require("os");
|
||||||
|
|
||||||
// either serve the builder or serve the actual app index.html
|
// either serve the builder or serve the actual app index.html
|
||||||
const builderPath = resolve(process.cwd(), "builder")
|
const builderPath = resolve(process.cwd(), "builder")
|
||||||
|
@ -16,5 +17,29 @@ exports.serveApp = async function(ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.serveComponentLibrary = async function(ctx) {
|
exports.serveComponentLibrary = async function(ctx) {
|
||||||
await send(ctx, "/index.html", { root: builderPath })
|
// TODO: update to run wherever budi is run
|
||||||
|
const componentLibraryPath = resolve(
|
||||||
|
homedir(),
|
||||||
|
".budibase",
|
||||||
|
ctx.params.appId,
|
||||||
|
"node_modules",
|
||||||
|
decodeURI(ctx.query.library),
|
||||||
|
"dist"
|
||||||
|
);
|
||||||
|
|
||||||
|
await send(ctx, "/index.js", { root: componentLibraryPath })
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.serveComponentDefinitions = async function(ctx) {
|
||||||
|
// TODO: update to run wherever budi is run
|
||||||
|
const componentLibraryPath = resolve(
|
||||||
|
homedir(),
|
||||||
|
".budibase",
|
||||||
|
ctx.params.appId,
|
||||||
|
"node_modules",
|
||||||
|
decodeURI(ctx.query.library),
|
||||||
|
"dist"
|
||||||
|
);
|
||||||
|
|
||||||
|
await send(ctx, "/index.js", { root: componentLibraryPath })
|
||||||
}
|
}
|
|
@ -1,16 +1,15 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const session = require("../middleware/session")
|
const session = require("../middleware/session")
|
||||||
const StatusCodes = require("../utilities/statusCodes")
|
const compress = require("koa-compress");
|
||||||
|
const zlib = require("zlib");
|
||||||
const { resolve } = require("path")
|
const { resolve } = require("path")
|
||||||
const { homedir } = require("os")
|
const { homedir } = require("os")
|
||||||
const send = require("koa-send")
|
const send = require("koa-send")
|
||||||
const routeHandlers = require("../middleware/routeHandlers")
|
|
||||||
const {
|
const {
|
||||||
componentLibraryInfo,
|
componentLibraryInfo,
|
||||||
} = require("../utilities/builder")
|
} = require("../utilities/builder")
|
||||||
const {
|
const {
|
||||||
componentRoutes,
|
// componentRoutes,
|
||||||
appsRoutes,
|
|
||||||
pageRoutes,
|
pageRoutes,
|
||||||
userRoutes,
|
userRoutes,
|
||||||
authenticatedRoutes
|
authenticatedRoutes
|
||||||
|
@ -24,11 +23,21 @@ const applicationRoutes = require("./routes/neo/application");
|
||||||
const modelsRoutes = require("./routes/neo/model");
|
const modelsRoutes = require("./routes/neo/model");
|
||||||
const viewsRoutes = require("./routes/neo/view");
|
const viewsRoutes = require("./routes/neo/view");
|
||||||
const staticRoutes = require("./routes/neo/static");
|
const staticRoutes = require("./routes/neo/static");
|
||||||
|
const componentRoutes = require("./routes/neo/component");
|
||||||
|
|
||||||
module.exports = app => {
|
module.exports = app => {
|
||||||
const router = new Router()
|
const router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
|
.use(compress({
|
||||||
|
threshold: 2048,
|
||||||
|
gzip: {
|
||||||
|
flush: zlib.Z_SYNC_FLUSH
|
||||||
|
},
|
||||||
|
deflate: {
|
||||||
|
flush: zlib.Z_SYNC_FLUSH,
|
||||||
|
}
|
||||||
|
}))
|
||||||
// .use(session(app))
|
// .use(session(app))
|
||||||
.use(async (ctx, next) => {
|
.use(async (ctx, next) => {
|
||||||
// TODO: temp dev middleware
|
// TODO: temp dev middleware
|
||||||
|
@ -107,14 +116,14 @@ module.exports = app => {
|
||||||
// .get("/_builder", async ctx => {
|
// .get("/_builder", async ctx => {
|
||||||
// await send(ctx, "/index.html", { root: builderPath })
|
// await send(ctx, "/index.html", { root: builderPath })
|
||||||
// })
|
// })
|
||||||
.get("/_builder/:appname/componentlibrary", async ctx => {
|
// .get("/_builder/:appname/componentlibrary", async ctx => {
|
||||||
const info = await componentLibraryInfo(
|
// const info = await componentLibraryInfo(
|
||||||
ctx.config,
|
// ctx.config,
|
||||||
ctx.params.appname,
|
// ctx.params.appname,
|
||||||
ctx.query.lib
|
// ctx.query.lib
|
||||||
)
|
// )
|
||||||
await send(ctx, info.components._lib || "index.js", { root: info.libDir })
|
// await send(ctx, info.components._lib || "index.js", { root: info.libDir })
|
||||||
})
|
// })
|
||||||
// .get("/_builder/*", async (ctx, next) => {
|
// .get("/_builder/*", async (ctx, next) => {
|
||||||
// const path = ctx.path.replace("/_builder", "")
|
// const path = ctx.path.replace("/_builder", "")
|
||||||
|
|
||||||
|
@ -149,8 +158,8 @@ module.exports = app => {
|
||||||
router.use(userRoutes.allowedMethods());
|
router.use(userRoutes.allowedMethods());
|
||||||
// router.use(appsRoutes.routes())
|
// router.use(appsRoutes.routes())
|
||||||
// router.use(appsRoutes.allowedMethods());
|
// router.use(appsRoutes.allowedMethods());
|
||||||
router.use(componentRoutes.routes());
|
// router.use(componentRoutes.routes());
|
||||||
router.use(componentRoutes.allowedMethods());
|
// router.use(componentRoutes.allowedMethods());
|
||||||
router.use(pageRoutes.routes());
|
router.use(pageRoutes.routes());
|
||||||
router.use(pageRoutes.allowedMethods());
|
router.use(pageRoutes.allowedMethods());
|
||||||
|
|
||||||
|
@ -167,6 +176,9 @@ module.exports = app => {
|
||||||
router.use(applicationRoutes.routes());
|
router.use(applicationRoutes.routes());
|
||||||
router.use(applicationRoutes.allowedMethods());
|
router.use(applicationRoutes.allowedMethods());
|
||||||
|
|
||||||
|
router.use(componentRoutes.routes());
|
||||||
|
router.use(componentRoutes.allowedMethods());
|
||||||
|
|
||||||
router.use(clientRoutes.routes());
|
router.use(clientRoutes.routes());
|
||||||
router.use(clientRoutes.allowedMethods());
|
router.use(clientRoutes.allowedMethods());
|
||||||
|
|
||||||
|
@ -188,8 +200,8 @@ module.exports = app => {
|
||||||
// .get("/_builder/instance/:appname/:instanceid/*", routeHandlers.appDefault)
|
// .get("/_builder/instance/:appname/:instanceid/*", routeHandlers.appDefault)
|
||||||
|
|
||||||
|
|
||||||
router.use(authenticatedRoutes.routes());
|
// router.use(authenticatedRoutes.routes());
|
||||||
router.use(authenticatedRoutes.allowedMethods());
|
// router.use(authenticatedRoutes.allowedMethods());
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,15 @@ const router = Router();
|
||||||
// routeHandlers.changeMyPassword
|
// routeHandlers.changeMyPassword
|
||||||
// )
|
// )
|
||||||
|
|
||||||
router.post(
|
// router.post(
|
||||||
"/:appname/api/executeAction/:actionname",
|
// "/:appname/api/executeAction/:actionname",
|
||||||
routeHandlers.executeAction
|
// routeHandlers.executeAction
|
||||||
)
|
// )
|
||||||
|
|
||||||
router.post(
|
// router.post(
|
||||||
"/_builder/instance/:appname/:instanceid/api/executeAction/:actionname",
|
// "/_builder/instance/:appname/:instanceid/api/executeAction/:actionname",
|
||||||
routeHandlers.executeAction
|
// routeHandlers.executeAction
|
||||||
)
|
// )
|
||||||
|
|
||||||
// router.post("/:appname/api/createUser", routeHandlers.createUser)
|
// router.post("/:appname/api/createUser", routeHandlers.createUser)
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,20 @@ const Router = require("@koa/router");
|
||||||
const send = require("koa-send")
|
const send = require("koa-send")
|
||||||
const StatusCodes = require("../../utilities/statusCodes")
|
const StatusCodes = require("../../utilities/statusCodes")
|
||||||
const {
|
const {
|
||||||
getComponentDefinitions,
|
|
||||||
componentLibraryInfo,
|
componentLibraryInfo,
|
||||||
} = require("../../utilities/builder")
|
} = require("../../utilities/builder")
|
||||||
|
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/_builder/:appname/componentlibrary", async ctx => {
|
// router.get("/_builder/:appname/componentlibrary", async ctx => {
|
||||||
const info = await componentLibraryInfo(
|
// const info = await componentLibraryInfo(
|
||||||
ctx.config,
|
// ctx.config,
|
||||||
ctx.params.appname,
|
// ctx.params.appname,
|
||||||
ctx.query.lib
|
// ctx.query.lib
|
||||||
)
|
// )
|
||||||
await send(ctx, info.components._lib || "index.js", { root: info.libDir })
|
// await send(ctx, info.components._lib || "index.js", { root: info.libDir })
|
||||||
})
|
// })
|
||||||
|
|
||||||
// router.get("/_builder/api/:appname/components", async ctx => {
|
// router.get("/_builder/api/:appname/components", async ctx => {
|
||||||
// try {
|
// try {
|
||||||
|
@ -35,14 +34,14 @@ router.get("/_builder/:appname/componentlibrary", async ctx => {
|
||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
|
|
||||||
router.get("/_builder/api/:appname/componentlibrary", async ctx => {
|
// router.get("/_builder/api/:appname/componentlibrary", async ctx => {
|
||||||
const info = await componentLibraryInfo(
|
// const info = await componentLibraryInfo(
|
||||||
ctx.config,
|
// ctx.config,
|
||||||
ctx.params.appname,
|
// ctx.params.appname,
|
||||||
ctx.query.lib ? decodeURI(ctx.query.lib) : ""
|
// ctx.query.lib ? decodeURI(ctx.query.lib) : ""
|
||||||
)
|
// )
|
||||||
ctx.body = info.components
|
// ctx.body = info.components
|
||||||
ctx.response.status = StatusCodes.OK
|
// ctx.response.status = StatusCodes.OK
|
||||||
})
|
// })
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
|
@ -0,0 +1,9 @@
|
||||||
|
const Router = require("@koa/router");
|
||||||
|
const controller = require("../../controllers/component");
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router
|
||||||
|
.get("/:clientId/:appId/components/definitions", controller.fetchAppComponentDefinitions);
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -9,6 +9,7 @@ router
|
||||||
await next();
|
await next();
|
||||||
})
|
})
|
||||||
.get("/_builder/:file*", controller.serveBuilder)
|
.get("/_builder/:file*", controller.serveBuilder)
|
||||||
.get("/:appName", controller.serveApp);
|
.get("/_app/:appId", controller.serveApp)
|
||||||
|
.get("/:appId/componentlibrary", controller.serveComponentLibrary);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
|
@ -27,6 +27,7 @@
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"koa": "^2.7.0",
|
"koa": "^2.7.0",
|
||||||
"koa-body": "^4.1.0",
|
"koa-body": "^4.1.0",
|
||||||
|
"koa-compress": "^4.0.1",
|
||||||
"koa-logger": "^3.2.1",
|
"koa-logger": "^3.2.1",
|
||||||
"koa-send": "^5.0.0",
|
"koa-send": "^5.0.0",
|
||||||
"koa-session": "^5.12.0",
|
"koa-session": "^5.12.0",
|
||||||
|
@ -37,7 +38,8 @@
|
||||||
"squirrelly": "^7.5.0",
|
"squirrelly": "^7.5.0",
|
||||||
"tar-fs": "^2.0.0",
|
"tar-fs": "^2.0.0",
|
||||||
"uuid": "^3.3.2",
|
"uuid": "^3.3.2",
|
||||||
"yargs": "^13.2.4"
|
"yargs": "^13.2.4",
|
||||||
|
"zlib": "^1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/test-sequencer": "^24.8.0",
|
"@jest/test-sequencer": "^24.8.0",
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue