forked from tsai/budibase
113 changed files with 1252 additions and 42975 deletions
@ -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" |
|
||||
} |
|
||||
|
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
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: {}, |
|
||||
} |
|
||||
|
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,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,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> |
|
||||
@ -0,0 +1 @@ |
|||||
|
export { default } from "./CurrentItemPreview.svelte"; |
||||
@ -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,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,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) |
|
||||
}) |
|
||||
}) |
|
||||
@ -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": "" |
||||
|
} |
||||
@ -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
@ -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
@ -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; |
||||
|
}; |
||||
@ -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; |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue