1
0
Fork 0
mirror of synced 2024-06-14 08:24:48 +12:00

component library modules and definitions refactor - moved to backend. More routing and middleware reorganisation

This commit is contained in:
Martin McKeaveney 2020-05-02 15:29:10 +01:00
parent c733b5bee8
commit abd6da7b11
113 changed files with 1207 additions and 42930 deletions

View file

@ -1,5 +0,0 @@
.DS_Store
node_modules
yarn.lock
package-lock.json
dist/index.js

View file

@ -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.

View file

@ -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

View file

@ -1,36 +0,0 @@
{
"name": "@budibase/bootstrap-components",
"svelte": "src/index.svelte",
"main": "dist/index.js",
"module": "dist/index.js",
"scripts": {
"build": "rollup -c",
"prepublishOnly": "npm run build",
"testbuild": "rollup -w -c rollup.testconfig.js",
"dev": "run-p start:dev testbuild",
"start:dev": "sirv public --single --dev",
"publishdev": "yarn build && node ./scripts/publishDev.js"
},
"devDependencies": {
"@budibase/client": "^0.0.32",
"fs-extra": "^8.1.0",
"lodash": "^4.17.15",
"npm-run-all": "^4.1.5",
"rollup": "^1.11.0",
"rollup-plugin-commonjs": "^10.0.2",
"rollup-plugin-json": "^4.0.0",
"rollup-plugin-livereload": "^1.0.1",
"rollup-plugin-node-resolve": "^5.0.0",
"rollup-plugin-svelte": "^5.0.0",
"rollup-plugin-terser": "^5.1.1",
"shortid": "^2.2.15",
"sirv-cli": "^0.4.4",
"svelte": "^3.12.1"
},
"keywords": [
"svelte"
],
"version": "0.0.32",
"license": "MIT",
"gitHead": "b1f4f90927d9e494e513220ef060af28d2d42455"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -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 */

View file

@ -1,30 +0,0 @@
{
"version": 3,
"file": "bundle.css",
"sources": [
"..\\src\\Test\\TestApp.svelte",
"..\\src\\Grid.svelte",
"..\\src\\Login.svelte",
"..\\src\\Form.svelte",
"..\\src\\Nav.svelte",
"..\\src\\Textbox.svelte",
"..\\src\\Panel.svelte",
"..\\src\\StackPanel.svelte",
"..\\src\\Table.svelte",
"..\\src\\Button.svelte"
],
"sourcesContent": [
"<script>\nimport createApp from \"./createApp\";\nimport { props } from \"./props\";\n\nlet _bb;\n\nconst _appPromise = createApp();\n_appPromise.then(a => _bb = a);\n\nconst testProps = props.hiddenNav;\n\nlet currentComponent;\n\n$: {\n if(_bb && currentComponent) {\n _bb.initialiseComponent(testProps, currentComponent);\n }\n}\n\n\n\n</script>\n\n{#await _appPromise}\nloading\n{:then _bb}\n\n<div id=\"current_component\" bind:this={currentComponent}>\n</div>\n\n{/await}\n\n\n<style>\n#current_component {\n height: 100%;\n width: 100%;\n}\n</style>\n\n",
"<script>\r\nimport { onMount } from 'svelte'\r\nimport {buildStyle} from \"./buildStyle\";\r\n\r\nexport let gridTemplateRows =\"\";\r\nexport let gridTemplateColumns =\"\";\r\nexport let children = [];\r\nexport let width = \"auto\";\r\nexport let height = \"auto\";\r\nexport let containerClass=\"\";\r\nexport let itemContainerClass=\"\";\r\n\r\n/*\"gridColumnStart\":\"string\",\r\n\"gridColumnEnd\":\"string\",\r\n\"gridRowStart\":\"string\",\r\n\"gridRowEnd\":\"string\"*/\r\n\r\n\r\nexport let _bb;\r\n\r\nlet style=\"\";\r\nlet htmlElements = {};\r\n\r\n$ : {\r\n if(_bb && htmlElements) {\r\n for(let el in htmlElements) {\r\n _bb.initialiseComponent(\r\n children[el].control,\r\n htmlElements[el]\r\n );\r\n }\r\n }\r\n}\r\n\r\nconst childStyle = child => \r\n buildStyle({\r\n \"grid-column-start\": child.gridColumnStart,\r\n \"grid-column-end\": child.gridColumnEnd,\r\n \"grid-column\": child.gridColumn,\r\n \"grid-row-start\": child.gridRowStart,\r\n \"grid-row-end\": child.gridRowStart,\r\n \"grid-row\": child.gridRow,\r\n });\r\n\r\n</script>\r\n\r\n<div class=\"root {containerClass}\"\r\n style=\"width: {width}; height: {height}; grid-template-columns: {gridTemplateColumns}; grid-template-rows: {gridTemplateRows};\">\r\n {#each children as child, index}\r\n <div class=\"{itemContainerClass}\"\r\n style={childStyle(child)}\r\n bind:this={htmlElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n display: grid;\r\n}\r\n\r\n</style>",
"<script>\n\nimport Textbox from \"./Textbox.svelte\";\nimport Form from \"./Form.svelte\";\nimport Button from \"./Button.svelte\";\n\nexport let usernameLabel = \"Username\";\nexport let passwordLabel = \"Password\";\nexport let loginButtonLabel = \"Login\";\nexport let loginRedirect = \"\";\nexport let logo = \"\";\nexport let buttonClass = \"\";\nexport let inputClass=\"\"\n\nexport let _bb;\n\nlet username = \"\";\nlet password = \"\";\nlet busy = false;\nlet incorrect = false;\nlet _logo = \"\";\nlet _buttonClass = \"\";\nlet _inputClass = \"\";\n\n$: {\n _logo = _bb.relativeUrl(logo);\n _buttonClass = buttonClass || \"default-button\";\n _inputClass = inputClass || \"default-input\";\n}\n\nconst login = () => {\n busy = true;\n _bb.api.post(\"/api/authenticate\", {username, password})\n .then(r => {\n busy = false;\n if(r.status === 200) {\n return r.json();\n } else {\n incorrect = true;\n return;\n }\n })\n .then(user => {\n if(user) {\n localStorage.setItem(\"budibase:user\", user);\n location.reload();\n }\n })\n}\n\n</script>\n\n<div class=\"root\">\n\n <div class=\"content\">\n\n {#if _logo}\n <div class=\"logo-container\">\n <img src={_logo} alt=\"logo\"/>\n </div>\n {/if}\n\n <div class=\"form-root\">\n <div class=\"label\">\n {usernameLabel}\n </div>\n <div class=\"control\">\n <input bind:value={username} type=\"text\" class={_inputClass}/>\n </div>\n <div class=\"label\">\n {passwordLabel}\n </div>\n <div class=\"control\">\n <input bind:value={password} type=\"password\" class={_inputClass}/>\n </div>\n </div>\n\n <div class=\"login-button-container\">\n <button disabled={busy} \n on:click={login}\n class={_buttonClass}>\n {loginButtonLabel}\n </button>\n </div>\n\n {#if incorrect}\n <div class=\"incorrect-details-panel\">\n Incorrect username or password\n </div>\n {/if}\n\n </div>\n\n</div>\n\n<style>\n\n.root {\n height: 100%;\n display:grid;\n grid-template-columns: [left] 1fr [middle] auto [right] 1fr;\n grid-template-rows: [top] 1fr [center] auto [bottom] 1fr;\n}\n\n.content {\n grid-column-start: middle;\n grid-row-start: center;\n width: 400px;\n}\n\n.logo-container {\n margin-bottom: 20px\n}\n\n.logo-container > img {\n max-width: 100%;\n}\n\n.login-button-container {\n text-align: right;\n margin-top: 20px;\n}\n\n.incorrect-details-panel {\n margin-top: 30px;\n padding: 10px;\n border-style: solid;\n border-width: 1px;\n border-color: maroon;\n border-radius: 1px;\n text-align: center;\n color: maroon;\n background-color: mistyrose;\n}\n\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n\n.default-input {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default-button {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default-button:active {\n\tbackground-color: #ddd;\n}\n\n.default-button:focus {\n\tborder-color: #666;\n}\n\n</style>",
"<script>\nexport let containerClass = \"\";\nexport let formControls = [];\n\nexport let _bb;\n\nlet htmlElements = {};\nlet labels = {};\n\n$ : {\n let cIndex = 0;\n for(let c of formControls) {\n labels[cIndex] = c.label;\n cIndex++;\n }\n\n if(_bb && htmlElements) {\n for(let el in htmlElements) {\n _bb.initialiseComponent(\n formControls[el].control,\n htmlElements[el]\n );\n }\n }\n}\n\n</script>\n\n<div class=\"form-root {containerClass}\">\n {#each formControls as child, index}\n <div class=\"label\">{labels[index]}</div>\n <div class=\"control\"\n bind:this={htmlElements[index]}>\n </div>\n {/each}\n</div>\n\n<style>\n.form-root {\n display: grid;\n grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/\n}\n\n.label {\n grid-column-start: label;\n padding: 5px 10px;\n vertical-align: middle;\n}\n.control {\n grid-column-start: control;\n padding: 5px 10px;\n}\n.overflow {\n grid-column-start: overflow;\n}\n.full-width {\n width: 100%;\n}\n</style>",
"<script>\r\nimport cssVars from \"./cssVars\";\r\n\r\nexport let navBarBackground = \"\";\r\nexport let navBarBorder=\"\";\r\nexport let navBarColor=\"\";\r\nexport let selectedItemBackground=\"\";\r\nexport let selectedItemColor=\"\";\r\nexport let selectedItemBorder=\"\";\r\nexport let itemHoverBackground=\"\";\r\nexport let itemHoverColor=\"\";\r\nexport let items = [];\r\nexport let hideNavBar=false;\r\nexport let selectedItem=\"\";\r\n\r\nexport let _bb;\r\n\r\nlet selectedIndex = -1;\r\nlet styleVars={};\r\nlet components = {};\r\nlet componentElements = {}\r\n\r\n\r\nconst hasComponentElements = () => \r\n Object.getOwnPropertyNames(componentElements).length > 0;\r\n\r\n$: {\r\n styleVars = {\r\n navBarBackground, navBarBorder,\r\n navBarColor, selectedItemBackground,\r\n selectedItemColor, selectedItemBorder,\r\n itemHoverBackground, itemHoverColor\r\n };\r\n\r\n if(items && items.length > 0 && hasComponentElements()) {\r\n const currentSelectedItem = selectedIndex > 0\r\n ? items[selectedIndex].title\r\n : \"\";\r\n if(selectedItem && currentSelectedItem !== selectedItem) {\r\n let i=0;\r\n for(let item of items) {\r\n if(item.title === selectedItem) {\r\n onSelectItem(i)();\r\n }\r\n i++;\r\n }\r\n } else if(!currentSelectedItem) {\r\n onSelectItem(0);\r\n }\r\n }\r\n}\r\n\r\nconst onSelectItem = (index) => () => {\r\n selectedIndex = index;\r\n if(!components[index]) {\r\n const comp = _bb.initialiseComponent(\r\n items[index].component, componentElements[index]);\r\n components[index] = comp; \r\n }\r\n}\r\n\r\n\r\n</script>\r\n\r\n<div class=\"root\" use:cssVars={styleVars}>\r\n {#if !hideNavBar}\r\n <div class=\"navbar\">\r\n {#each items as navItem, index}\r\n <div class=\"navitem\"\r\n on:click={onSelectItem(index)}\r\n class:selected={selectedIndex === index}>\r\n {navItem.title}\r\n </div>\r\n {/each}\r\n </div>\r\n {/if}\r\n {#each items as navItem, index}\r\n\r\n <div class=\"content\"\r\n bind:this={componentElements[index]}>\r\n </div>\r\n {/each}\r\n</div>\r\n\r\n<style>\r\n\r\n.root {\r\n height: 100%;\r\n width:100%;\r\n grid-template-columns: [navbar] auto [content] 1fr;\r\n display: grid;\r\n}\r\n\r\n.navbar {\r\n grid-column: navbar;\r\n background: var(--navBarBackground);\r\n border: var(--navBarBorder);\r\n color: var(--navBarColor);\r\n}\r\n\r\n.navitem {\r\n padding: 10px 17px;\r\n cursor: pointer;\r\n}\r\n\r\n.navitem:hover {\r\n background: var(--itemHoverBackground);\r\n color: var(--itemHoverColor);\r\n}\r\n\r\n.navitem.selected {\r\n background: var(--selectedItemBackground);\r\n border: var(--selectedItemBorder);\r\n color: var(--selectedItemColor);\r\n}\r\n\r\n.content {\r\n grid-column: content;\r\n}\r\n\r\n</style>\r\n\r\n",
"<script>\n\nexport let value=\"\";\nexport let hideValue = false;\nexport let className = \"default\";\n\nexport let _bb;\n\nlet actualValue = \"\";\n$: {\n\tif(_bb && value._isstate) {\n\t\t_bb.store.subscribe(s => {\n\t\t\tactualValue = _bb.store.getValue(s, value);\n\t\t});\n\t}\n}\n\nconst onchange = (ev) => {\n\tif(_bb && value._isstate) {\n\t\t_bb.store.setValue(value, ev.target.value);\n\t} else if(!value._isstate) {\n\t\tactualValue = ev.target.value;\n\t}\n}\n\n</script>\n\n{#if hideValue}\n<input class={className} \n\t type=\"password\" \n\t value={actualValue} on:change/>\n{:else}\n<input class={className} type=\"text\" value={actualValue}/>\n{/if}\n\n<style>\n.default {\n width: 100%;\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n border-radius: 2px;\n width: 100%;\n}\n\n.default:disabled {\n\tcolor: #ccc;\n}\n\n</style>",
"<script>\r\nimport {buildStyle} from \"./buildStyle\";\r\nimport cssVars from \"./cssVars\";\r\n\r\nexport let component=\"\";\r\nexport let text=\"\";\r\nexport let containerClass=\"\";\r\nexport let background=\"\";\r\nexport let border=\"\";\r\nexport let borderRadius=\"\";\r\nexport let font=\"\";\r\nexport let display=\"\";\r\nexport let textAlign=\"\";\r\nexport let color=\"\";\r\nexport let padding=\"\";\r\nexport let margin=\"\";\r\nexport let hoverBackground=\"\";\r\nexport let hoverColor=\"\";\r\nexport let onClick;\r\nexport let height;\r\nexport let width;\r\n\r\nexport let _bb;\r\n\r\nlet styleVars;\r\nlet style=\"\";\r\nlet componentElement;\r\n\r\n$: {\r\n style=buildStyle({\r\n border, background, font, margin,\r\n padding, display, color, height, width,\r\n \"text-align\": textAlign,\r\n \"border-radius\":borderRadius,\r\n cursor: onClick ? \"pointer\" : \"none\"\r\n });\r\n\r\n if(_bb && component) {\r\n _bb.initialiseComponent(component, componentElement);\r\n }\r\n\r\n styleVars = {\r\n hoverBackground:hoverBackground || background, \r\n hoverColor:hoverColor || color\r\n }\r\n}\r\n\r\nconst clickHandler = () => {\r\n if(onClick) onClick();\r\n}\r\n\r\n</script>\r\n\r\n<div class=\"{containerClass} panel\" \r\n style={style}\r\n use:cssVars={styleVars}\r\n this:bind={componentElement}\r\n on:click={clickHandler}>\r\n {component && component._component ? \"\" : text}\r\n</div>\r\n\r\n<style>\r\n\r\n.panel:hover {\r\n background: var(--hoverBackground);\r\n color: var(--hoverColor);\r\n\r\n}\r\n\r\n</style>\r\n",
"<script>\n\nimport { emptyProps } from \"./emptyProps\";\n\nexport let direction = \"horizontal\";\nexport let children = [];\nexport let width = \"auto\";\nexport let height = \"auto\";\nexport let containerClass=\"\";\nexport let itemContainerClass=\"\";\nexport let onLoad;\n\nexport let data=[];\nexport let dataItemComponent;\n\nexport let _bb;\n\nlet staticHtmlElements = {};\nlet staticComponents = {};\nlet dataBoundElements = {};\nlet dataBoundComponents = {};\n\nlet onLoadCalled = false;\n\nconst hasDataBoundComponents = () => \n Object.getOwnPropertyNames(dataBoundComponents).length > 0;\n\nconst hasData = () => \n Array.isArray(data) && data.length > 0;\n\nconst hasStaticComponents = () => {\n return Object.getOwnPropertyNames(staticComponents).length > 0;\n}\n\n$: {\n\n if(staticHtmlElements) {\n if(hasStaticComponents()) {\n for(let c in staticComponents) {\n staticComponents[c].$destroy();\n }\n staticComponents = {};\n }\n\n for(let el in staticHtmlElements) {\n staticComponents[el] = _bb.initialiseComponent(\n children[el].control,\n staticHtmlElements[el]\n );\n }\n }\n \n\n if(hasDataBoundComponents()) {\n for(let c in dataBoundComponents) {\n dataBoundComponents[c].$destroy();\n }\n dataBoundComponents = {};\n }\n\n if(hasData()) {\n let index = 0;\n for(let d in dataBoundElements) {\n _bb.initialiseComponent(\n dataItemComponent,\n dataBoundElements[d],\n data[parseInt(d)]\n );\n }\n }\n\n if(!onLoadCalled && onLoad && !onLoad.isPlaceholder) {\n onLoad();\n onLoadCalled = true;\n }\n}\n\n\n</script>\n\n<div class=\"root {containerClass}\"\n style=\"width: {width}; height: {height}\">\n\n {#if children}\n {#each children as child, index}\n <div class={direction}>\n <div class=\"{itemContainerClass}\"\n bind:this={staticHtmlElements[index]}>\n </div>\n </div>\n {/each}\n {/if}\n\n {#if data && data.length > 0}\n {#each data as child, index}\n <div class={direction}>\n <div class=\"{itemContainerClass}\"\n bind:this={dataBoundElements[index]}>\n </div>\n </div>\n {/each}\n {/if}\n</div>\n\n<style>\n\n.horizontal {\n display:inline-block;\n}\n\n.vertical {\n display: block;\n}\n\n</style>",
"<script>\r\n\r\nexport let columns=[];\r\nexport let data=\"\";\r\nexport let tableClass=\"\";\r\nexport let theadClass=\"\";\r\nexport let tbodyClass=\"\";\r\nexport let trClass=\"\";\r\nexport let thClass=\"\";\r\nexport let onRowClick;\r\n\r\nexport let _bb;\r\n\r\nconst rowClickHandler = (row) => () => {\r\n onRowClick(row);\r\n}\r\n\r\n</script>\r\n\r\n <table class={tableClass}>\r\n <thead class={theadClass}>\r\n <tr class={trClass}>\r\n {#each columns as col}\r\n <th class={thClass}>{col.title}</th>\r\n {/each}\r\n </tr>\r\n </thead>\r\n <tbody class={tbodyClass}>\r\n {#each data as row}\r\n <tr class={trClass}\r\n on:click={rowClickHandler(row)} >\r\n {#each columns as col}\r\n <th class={thClass}>{_bb.getStateOrValue(col.value, row)}</th>\r\n {/each}\r\n </tr>\r\n {/each}\r\n </tbody>\r\n</table> \r\n\r\n<style>\r\n\r\n.table-default {\r\n width: 100%;\r\n margin-bottom: 1rem;\r\n color: #212529;\r\n border-collapse: collapse;\r\n}\r\n\r\n.table-default .thead-default .th-default {\r\n vertical-align: bottom;\r\n border-bottom: 2px solid #dee2e6;\r\n font-weight: bold;\r\n}\r\n\r\n.table-default .th-default {\r\n padding: .75rem;\r\n vertical-align: top;\r\n border-top: 1px solid #dee2e6;\r\n font-weight: normal;\r\n}\r\n\r\n.th-default {\r\n text-align: inherit;\r\n}\r\n\r\n.table-default .tbody-default .tr-default:hover {\r\n color: #212529;\r\n background-color: rgba(0,0,0,.075);\r\n cursor: pointer;\r\n}\r\n\r\n</style>",
"<script>\nexport let className = \"default\";\nexport let disabled = false;\nexport let contentText;\nexport let contentComponent;\nexport let onClick = () => {};\n\nexport let _bb;\nlet contentComponentContainer;\n\n$:{\n\tif(_bb && contentComponentContainer && contentComponent._component)\n\t\t_bb.initialiseComponent(contentComponent, contentComponentContainer);\n}\n\n\nconst clickHandler = () => {\n\tif(onClick) onClick();\n}\n\n</script>\n\n\n<button class={className} {disabled} on:click={clickHandler}>\n {#if contentComponent && contentComponent._component}\n\t<div bind:this={contentComponentContainer}>\n\t</div>\n {:else if contentText}\n {contentText}\n {:else}\n <slot />\n {/if}\n</button>\n\n\n<style>\n\n.default {\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: 0.4em;\n\tmargin: 0 0 0.5em 0;\n\tbox-sizing: border-box;\n\tborder: 1px solid #ccc;\n\tborder-radius: 2px;\n\tcolor: #333;\n\tbackground-color: #f4f4f4;\n\toutline: none;\n}\n\n.default:active {\n\tbackground-color: #ddd;\n}\n\n.default:focus {\n\tborder-color: #666;\n}\n\n</style>"
],
"names": [],
"mappings": "AAkCA,kBAAkB,eAAC,CAAC,AAChB,MAAM,CAAE,IAAI,CACZ,KAAK,CAAE,IAAI,AACf,CAAC;ACqBD,KAAK,eAAC,CAAC,AACH,OAAO,CAAE,IAAI,AACjB,CAAC;ACqCD,KAAK,cAAC,CAAC,AACH,MAAM,CAAE,IAAI,CACZ,QAAQ,IAAI,CACZ,qBAAqB,CAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAC3D,kBAAkB,CAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,AAC5D,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,MAAM,CACzB,cAAc,CAAE,MAAM,CACtB,KAAK,CAAE,KAAK,AAChB,CAAC,AAED,eAAe,cAAC,CAAC,AACb,aAAa,CAAE,IAAI;AACvB,CAAC,AAED,6BAAe,CAAG,GAAG,cAAC,CAAC,AACnB,SAAS,CAAE,IAAI,AACnB,CAAC,AAED,uBAAuB,cAAC,CAAC,AACrB,UAAU,CAAE,KAAK,CACjB,UAAU,CAAE,IAAI,AACpB,CAAC,AAED,wBAAwB,cAAC,CAAC,AACtB,UAAU,CAAE,IAAI,CAChB,OAAO,CAAE,IAAI,CACb,YAAY,CAAE,KAAK,CACnB,YAAY,CAAE,GAAG,CACjB,YAAY,CAAE,MAAM,CACpB,aAAa,CAAE,GAAG,CAClB,UAAU,CAAE,MAAM,CAClB,KAAK,CAAE,MAAM,CACb,gBAAgB,CAAE,SAAS,AAC/B,CAAC,AAED,UAAU,cAAC,CAAC,AACR,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,AACrD,CAAC,AAED,MAAM,cAAC,CAAC,AACJ,iBAAiB,CAAE,KAAK,CACxB,OAAO,CAAE,GAAG,CAAC,IAAI,CACjB,cAAc,CAAE,MAAM,AAC1B,CAAC,AACD,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,OAAO,CAC1B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AAED,cAAc,cAAC,CAAC,AACf,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACnB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,eAAe,cAAC,CAAC,AAChB,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACtB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,CACX,gBAAgB,CAAE,OAAO,CACzB,OAAO,CAAE,IAAI,AACd,CAAC,AAED,6BAAe,OAAO,AAAC,CAAC,AACvB,gBAAgB,CAAE,IAAI,AACvB,CAAC,AAED,6BAAe,MAAM,AAAC,CAAC,AACtB,YAAY,CAAE,IAAI,AACnB,CAAC;AC9ID,UAAU,cAAC,CAAC,AACR,OAAO,CAAE,IAAI,CACb,qBAAqB,CAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,AACrD,CAAC,AAED,MAAM,cAAC,CAAC,AACJ,iBAAiB,CAAE,KAAK,CACxB,OAAO,CAAE,GAAG,CAAC,IAAI,CACjB,cAAc,CAAE,MAAM,AAC1B,CAAC,AACD,QAAQ,cAAC,CAAC,AACN,iBAAiB,CAAE,OAAO,CAC1B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AACD,SAAS,cAAC,CAAC,AACP,iBAAiB,CAAE,QAAQ,AAC/B,CAAC,AACD,WAAW,cAAC,CAAC,AACT,KAAK,CAAE,IAAI,AACf,CAAC;AC6BD,KAAK,cAAC,CAAC,AACH,MAAM,CAAE,IAAI,CACZ,MAAM,IAAI,CACV,qBAAqB,CAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAClD,OAAO,CAAE,IAAI,AACjB,CAAC,AAED,OAAO,cAAC,CAAC,AACL,WAAW,CAAE,MAAM,CACnB,UAAU,CAAE,IAAI,kBAAkB,CAAC,CACnC,MAAM,CAAE,IAAI,cAAc,CAAC,CAC3B,KAAK,CAAE,IAAI,aAAa,CAAC,AAC7B,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,OAAO,CAAE,IAAI,CAAC,IAAI,CAClB,MAAM,CAAE,OAAO,AACnB,CAAC,AAED,sBAAQ,MAAM,AAAC,CAAC,AACZ,UAAU,CAAE,IAAI,qBAAqB,CAAC,CACtC,KAAK,CAAE,IAAI,gBAAgB,CAAC,AAChC,CAAC,AAED,QAAQ,SAAS,cAAC,CAAC,AACf,UAAU,CAAE,IAAI,wBAAwB,CAAC,CACzC,MAAM,CAAE,IAAI,oBAAoB,CAAC,CACjC,KAAK,CAAE,IAAI,mBAAmB,CAAC,AACnC,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,WAAW,CAAE,OAAO,AACxB,CAAC;AClFD,QAAQ,eAAC,CAAC,AACN,KAAK,CAAE,IAAI,CACd,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACnB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,uBAAQ,SAAS,AAAC,CAAC,AAClB,KAAK,CAAE,IAAI,AACZ,CAAC;ACaD,oBAAM,MAAM,AAAC,CAAC,AACV,UAAU,CAAE,IAAI,iBAAiB,CAAC,CAClC,KAAK,CAAE,IAAI,YAAY,CAAC,AAE5B,CAAC;ACuCD,WAAW,cAAC,CAAC,AACT,QAAQ,YAAY,AACxB,CAAC,AAED,SAAS,cAAC,CAAC,AACP,OAAO,CAAE,KAAK,AAClB,CAAC;ACvED,cAAc,cAAC,CAAC,AACZ,KAAK,CAAE,IAAI,CACX,aAAa,CAAE,IAAI,CACnB,KAAK,CAAE,OAAO,CACd,eAAe,CAAE,QAAQ,AAC7B,CAAC,AAED,4BAAc,CAAC,cAAc,CAAC,WAAW,cAAC,CAAC,AACvC,cAAc,CAAE,MAAM,CACtB,aAAa,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAChC,WAAW,CAAE,IAAI,AACrB,CAAC,AAED,4BAAc,CAAC,WAAW,cAAC,CAAC,AACxB,OAAO,CAAE,MAAM,CACf,cAAc,CAAE,GAAG,CACnB,UAAU,CAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAC7B,WAAW,CAAE,MAAM,AACvB,CAAC,AAED,WAAW,cAAC,CAAC,AACT,UAAU,CAAE,OAAO,AACvB,CAAC,AAED,4BAAc,CAAC,cAAc,CAAC,yBAAW,MAAM,AAAC,CAAC,AAC7C,KAAK,CAAE,OAAO,CACd,gBAAgB,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAClC,MAAM,CAAE,OAAO,AACnB,CAAC;AChCD,QAAQ,eAAC,CAAC,AACT,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,OAAO,CAClB,OAAO,CAAE,KAAK,CACd,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CACnB,UAAU,CAAE,UAAU,CACtB,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CACtB,aAAa,CAAE,GAAG,CAClB,KAAK,CAAE,IAAI,CACX,gBAAgB,CAAE,OAAO,CACzB,OAAO,CAAE,IAAI,AACd,CAAC,AAED,uBAAQ,OAAO,AAAC,CAAC,AAChB,gBAAgB,CAAE,IAAI,AACvB,CAAC,AAED,uBAAQ,MAAM,AAAC,CAAC,AACf,YAAY,CAAE,IAAI,AACnB,CAAC"
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,103 +0,0 @@
window["##BUDIBASE_APPDEFINITION##"] = {
hierarchy: {
name: "root",
type: "root",
children: [
{
name: "customer",
type: "record",
fields: [
{
name: "name",
type: "string",
typeOptions: {
maxLength: 1000,
values: null,
allowDeclaredValuesOnly: false,
},
label: "name",
getInitialValue: "default",
getUndefinedValue: "default",
},
],
children: [
{
name: "invoiceyooo",
type: "record",
fields: [
{
name: "amount",
type: "number",
typeOptions: {
minValue: 99999999999,
maxValue: 99999999999,
decimalPlaces: 2,
},
label: "amount",
getInitialValue: "default",
getUndefinedValue: "default",
},
],
children: [],
validationRules: [],
nodeId: 2,
indexes: [],
allidsShardFactor: 1,
collectionName: "invoices",
isSingle: false,
},
],
validationRules: [],
nodeId: 1,
indexes: [
{
name: "customer_invoices",
type: "index",
map: "return {...record};",
filter: "",
indexType: "ancestor",
getShardName: "",
getSortKey: "record.id",
aggregateGroups: [],
allowedModelNodeIds: [2],
nodeId: 5,
},
],
allidsShardFactor: 64,
collectionName: "customers",
isSingle: false,
},
],
pathMaps: [],
indexes: [
{
name: "Yeo index",
type: "index",
map: "return {...record};",
filter: "",
indexType: "ancestor",
getShardName: "",
getSortKey: "record.id",
aggregateGroups: [],
allowedModelNodeIds: [1],
nodeId: 4,
},
{
name: "everyones_invoices",
type: "index",
map: "return {...record};",
filter: "",
indexType: "ancestor",
getShardName: "",
getSortKey: "record.id",
aggregateGroups: [],
allowedModelNodeIds: [2],
nodeId: 6,
},
],
nodeId: 0,
},
componentLibraries: ["budibase-standard-components"],
appRootPath: "/testApp2",
props: {},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -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;
}

View file

@ -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>

View file

@ -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(),
],
}

View file

@ -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,
},
}

View file

@ -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"))
}
})()

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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
}

View file

@ -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",
},
},
],
},
},
],
},
}

View file

@ -1,7 +0,0 @@
import App from "./TestApp.svelte"
const app = new App({
target: document.body,
})
export default app

View file

@ -1,9 +0,0 @@
export const buildStyle = styles => {
let str = ""
for (let s in styles) {
if (styles[s]) {
str += `${s}: ${styles[s]}; `
}
}
return str
}

View file

@ -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
},
}
}

View file

@ -1 +0,0 @@
export const emptyProps = () => ({ _component: "" })

View file

@ -1,2 +0,0 @@
export { default as form } from "./Form.svelte"
export { default as nav } from "./Nav.svelte"

View file

@ -1,4 +1,5 @@
import { filter, map, reduce, toPairs, pipe } from "lodash/fp"
import { pipe } from "components/common/core";
import { filter, map, reduce, toPairs } from "lodash/fp"
const self = n => n
const join_with = delimiter => a => a.join(delimiter)

View file

@ -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 = {}
for (let lib of libsFromPages(appPackage.pages)) {
console.log(libModule);
const libModule = await import(makeLibraryUrl(appId, lib))
allLibraries[lib] = libModule
for (let libraryName of application.componentLibraries) {
const LIBRARY_URL = `/${application._id}/componentlibrary?library=${libraryName}`;
const libraryModule = await import(LIBRARY_URL)
allLibraries[libraryName] = libraryModule
}
return allLibraries
}
export const loadLibUrls = (appId, appPackage) => {
const allLibraries = []
for (let lib of libsFromPages(appPackage.pages)) {
const libUrl = makeLibraryUrl(appId, lib)
allLibraries.push({ libName: lib, importPath: libUrl })
}
// export const loadLibUrls = (appId, appPackage) => {
// const allLibraries = []
// for (let lib of libsFromPages(appPackage.pages)) {
// const libUrl = makeLibraryUrl(appId, lib)
// allLibraries.push({ libName: lib, importPath: libUrl })
// }
return allLibraries
}
// return allLibraries
// }
export const loadLib = async (appId, lib, allLibs) => {
allLibs[lib] = await import(makeLibraryUrl(appId, lib))
return allLibs
}
// export const loadLib = async (appId, lib, allLibs) => {
// allLibs[lib] = await import(makeLibraryUrl(appId, lib))
// return allLibs
// }
export const makeLibraryUrl = (appId, lib) =>
`/_builder/${appId}/componentlibrary?lib=${encodeURI(lib)}`
// export const makeLibraryUrl = (appId, lib) =>
// `/_builder/${appId}/componentlibrary?lib=${encodeURI(lib)}`
export const libsFromPages = pages =>
pipe(pages, [values, map(p => p.componentLibraries), flatten, uniq])
// export const libsFromPages = pages =>
// pipe(pages, [values, map(p => p.componentLibraries), flatten, uniq])
export const libUrlsForPreview = (appPackage, pageName) => {
const resolve = path => {
let file = appPackage.components.libraryPaths[path]
if (file.startsWith("./")) file = file.substring(2)
if (file.startsWith("/")) file = file.substring(1)
// export const libUrlsForPreview = (appPackage, pageName) => {
// const resolve = path => {
// let file = appPackage.components.libraryPaths[path]
// if (file.startsWith("./")) file = file.substring(2)
// if (file.startsWith("/")) file = file.substring(1)
let newPath = path
// let newPath = path
if (!newPath.startsWith("./") && !newPath.startsWith("/")) {
newPath = `/node_modules/${path}`
}
// if (!newPath.startsWith("./") && !newPath.startsWith("/")) {
// newPath = `/node_modules/${path}`
// }
return {
importPath: `/lib${newPath}/${file}`,
libName: path,
}
}
// return {
// importPath: `/lib${newPath}/${file}`,
// libName: path,
// }
// }
return pipe([appPackage.pages[pageName]], [libsFromPages, map(resolve)])
}
// return pipe([appPackage.pages[pageName]], [libsFromPages, map(resolve)])
// }

View file

@ -1,12 +1,5 @@
import { writable } from "svelte/store"
import api from "../api"
import { find } from "lodash/fp"
import {
getNode,
constructHierarchy,
} from "components/common/core"
import backendActions from "../../actions/backend";
export const getBackendUiStore = () => {
const INITIAL_BACKEND_UI_STATE = {

View file

@ -1,19 +1,23 @@
import { filter, cloneDeep, last, concat, isEmpty, values } from "lodash/fp"
import { pipe, getNode, constructHierarchy } from "components/common/core"
import { filter, cloneDeep, values } from "lodash/fp"
import { pipe } from "components/common/core"
import * as backendStoreActions from "./backend"
import { writable } from "svelte/store"
import { defaultPagesObject } from "components/userInterface/pagesParsing/defaultPagesObject"
import api from "../api"
import {
DEFAULT_PAGES_OBJECT
} from "../../constants";
import { getExactComponent } from "components/userInterface/pagesParsing/searchComponents"
import { rename } from "components/userInterface/pagesParsing/renameScreen"
import {
getNewScreen,
// getNewScreen,
createProps,
makePropsSafe,
getBuiltin,
} from "components/userInterface/pagesParsing/createProps"
import { expandComponentDefinition } from "components/userInterface/pagesParsing/types"
import { loadLibs, libUrlsForPreview } from "../loadComponentLibraries"
import {
fetchComponentLibModules,
fetchComponentLibDefinitions
} from "../loadComponentLibraries"
import { buildCodeForScreens } from "../buildCodeForScreens"
import { generate_screen_css } from "../generate_css"
import { insertCodeMetadata } from "../insertCodeMetadata"
@ -24,7 +28,7 @@ export const getStore = () => {
apps: [],
appname: "",
hierarchy: {},
pages: defaultPagesObject(),
pages: DEFAULT_PAGES_OBJECT,
mainUi: {},
unauthenticatedUi: {},
components: [],
@ -35,8 +39,6 @@ export const getStore = () => {
currentComponentProps: null,
errors: [],
hasAppPackage: false,
// accessLevels: { version: 0, levels: [] },
// currentNode: null,
libraries: null,
appId: ""
}
@ -52,7 +54,7 @@ export const getStore = () => {
// store.deleteAction = backendStoreActions.deleteAction(store)
// store.saveTrigger = backendStoreActions.saveTrigger(store)
// store.deleteTrigger = backendStoreActions.deleteTrigger(store)
store.importAppDefinition = importAppDefinition(store)
// store.importAppDefinition = importAppDefinition(store)
store.saveScreen = saveScreen(store)
store.renameScreen = renameScreen(store)
@ -60,11 +62,9 @@ export const getStore = () => {
store.setCurrentScreen = setCurrentScreen(store)
store.setCurrentPage = setCurrentPage(store)
store.createScreen = createScreen(store)
// store.removeComponentLibrary = removeComponentLibrary(store)
store.addStylesheet = addStylesheet(store)
store.removeStylesheet = removeStylesheet(store)
store.savePage = savePage(store)
store.createGeneratedComponents = createGeneratedComponents(store)
store.addChildComponent = addChildComponent(store)
store.selectComponent = selectComponent(store)
store.setComponentProp = setComponentProp(store)
@ -102,7 +102,9 @@ const setPackage = (store, initial) => async (pkg) => {
},
}
initial.libraries = await loadLibs(pkg.application._id, pkg)
initial.libraries = await fetchComponentLibModules(pkg.application)
// TODO: Rename to componentDefinitions
initial.components = await fetchComponentLibDefinitions(pkg.clientId, pkg.application._id);
initial.loadLibraryUrls = pageName => {
const libs = libUrlsForPreview(pkg, pageName)
return libs
@ -111,40 +113,20 @@ const setPackage = (store, initial) => async (pkg) => {
initial.appId = pkg.application._id
initial.pages = pkg.pages
initial.hasAppPackage = true
initial.hierarchy = pkg.appDefinition.hierarchy
initial.accessLevels = pkg.accessLevels
initial.screens = values(pkg.screens)
initial.components = values(pkg.components.components).map(
expandComponentDefinition
)
initial.templates = pkg.components.templates
initial.builtins = [getBuiltin("##builtin/screenslot")]
initial.actions = values(pkg.appDefinition.actions)
initial.triggers = pkg.appDefinition.triggers
initial.appInstances = pkg.application.instances
initial.appId = pkg.application._id
store.set(initial)
console.log(initial)
return initial
}
const importAppDefinition = store => appDefinition => {
store.update(s => {
s.hierarchy = appDefinition.hierarchy
s.currentNode =
appDefinition.hierarchy.children.length > 0
? appDefinition.hierarchy.children[0]
: null
s.actions = appDefinition.actions
s.triggers = appDefinition.triggers
s.currentNodeIsNew = false
return s
})
}
const saveScreen = store => screen => {
store.update(s => {
return _saveScreen(store, s, screen)
store.update(state => {
return _saveScreen(store, state, screen)
})
}
@ -166,10 +148,7 @@ const _saveScreen = async (store, s, screen) => {
innerState.screens = screens
innerState.currentPreviewItem = screen
const safeProps = makePropsSafe(
getComponentDefinition(
innerState.components,
screen.props._component
),
innerState.components[screen.props._component],
screen.props
)
innerState.currentComponentInfo = safeProps
@ -192,21 +171,27 @@ const _saveScreenApi = (screen, s) =>
.then(() => _savePage(s))
const createScreen = store => (screenName, route, layoutComponentName) => {
store.update(s => {
const newScreen = getNewScreen(
s.components,
layoutComponentName,
screenName
)
store.update(state => {
console.log(layoutComponentName);
const rootComponent = state.components[layoutComponentName]
const newScreen = {
name: screenName || "",
description: "",
url: "",
_css: "",
uiFunctions: "",
props: createProps(rootComponent).props,
}
newScreen.route = route
s.currentPreviewItem = newScreen
s.currentComponentInfo = newScreen.props
s.currentFrontEndType = "screen"
state.currentPreviewItem = newScreen
state.currentComponentInfo = newScreen.props
state.currentFrontEndType = "screen"
_saveScreen(store, s, newScreen)
_saveScreen(store, state, newScreen)
return s
return state
})
}
@ -219,7 +204,7 @@ const setCurrentScreen = store => screenName => {
s.currentView = "detail"
const safeProps = makePropsSafe(
getComponentDefinition(s.components, screen.props._component),
s.components[screen.props._component],
screen.props
)
screen.props = safeProps
@ -229,25 +214,6 @@ const setCurrentScreen = store => screenName => {
})
}
const createGeneratedComponents = store => components => {
store.update(s => {
s.components = [...s.components, ...components]
s.screens = [...s.screens, ...components]
const doCreate = async () => {
for (let c of components) {
await api.post(`/_builder/api/${s.appId}/screen`, c)
}
await _savePage(s)
}
doCreate()
return s
})
}
const deleteScreen = store => name => {
store.update(s => {
const components = pipe(s.components, [filter(c => c.name !== name)])
@ -319,46 +285,6 @@ const savePage = store => async page => {
})
}
// const addComponentLibrary = store => async lib => {
// const response = await api.get(
// `/_builder/api/${s.appId}/componentlibrary?lib=${encodeURI(lib)}`,
// undefined,
// false
// )
// const success = response.status === 200
// const components = success ? await response.json() : []
// store.update(s => {
// if (success) {
// const componentsArray = []
// for (let c in components) {
// componentsArray.push(expandComponentDefinition(components[c]))
// }
// s.components = pipe(s.components, [
// filter(c => !c.name.startsWith(`${lib}/`)),
// concat(componentsArray),
// ])
// s.pages.componentLibraries.push(lib)
// _savePage(s)
// }
// return s
// })
// }
// const removeComponentLibrary = store => lib => {
// store.update(state => {
// state.pages.componentLibraries = state.pages.componentLibraries.filter(l => l !== lib);
// _savePage(state);
// return state;
// })
// }
const addStylesheet = store => stylesheet => {
store.update(s => {
s.pages.stylesheets.push(stylesheet)
@ -386,42 +312,44 @@ const _savePage = async s => {
}
const setCurrentPage = store => pageName => {
store.update(s => {
const current_screens = s.pages[pageName]._screens
store.update(state => {
const current_screens = state.pages[pageName]._screens
s.currentFrontEndType = "page"
s.currentPageName = pageName
s.screens = Array.isArray(current_screens)
const currentPage = state.pages[pageName];
state.currentFrontEndType = "page"
state.currentPageName = pageName
state.screens = Array.isArray(current_screens)
? current_screens
: Object.values(current_screens)
const safeProps = makePropsSafe(
getComponentDefinition(s.components, s.pages[pageName].props._component),
s.pages[pageName].props
state.components[currentPage.props._component],
currentPage.props
)
s.currentComponentInfo = safeProps
s.pages[pageName].props = safeProps
s.currentPreviewItem = s.pages[pageName]
s.currentPreviewItem._css = generate_screen_css([
s.currentPreviewItem.props,
state.currentComponentInfo = safeProps
currentPage.props = safeProps
state.currentPreviewItem = state.pages[pageName]
state.currentPreviewItem._css = generate_screen_css([
state.currentPreviewItem.props,
])
for (let screen of s.screens) {
for (let screen of state.screens) {
screen._css = generate_screen_css([screen.props])
}
setCurrentPageFunctions(s)
return s
setCurrentPageFunctions(state)
return state
})
}
const getComponentDefinition = (components, name) =>
components.find(c => c.name === name)
// const getComponentDefinition = (components, name) => components.find(c => c.name === name)
/**
* @param {string} componentToAdd - name of the component to add to the application
* @param {string} presetName - name of the component preset if defined
*/
const addChildComponent = store => (componentToAdd, presetName) => {
// componentToAdd looks like: @budibase/standard-components/container
store.update(state => {
function findSlot(component_array) {
for (let i = 0; i < component_array.length; i += 1) {
@ -442,7 +370,7 @@ const addChildComponent = store => (componentToAdd, presetName) => {
const component = componentToAdd.startsWith("##")
? getBuiltin(componentToAdd)
: state.components.find(({ name }) => name === componentToAdd)
: state.components[componentToAdd]
const presetProps = presetName ? component.presets[presetName] : {}
const newComponent = createProps(component, presetProps)
@ -488,7 +416,7 @@ const selectComponent = store => component => {
store.update(state => {
const componentDef = component._component.startsWith("##")
? component
: state.components.find(c => c.name === component._component)
: state.components[component._component]
state.currentComponentInfo = makePropsSafe(componentDef, component)
state.currentView = "component"
return state
@ -508,28 +436,28 @@ const setComponentProp = store => (name, value) => {
}
const setComponentStyle = store => (type, name, value) => {
store.update(s => {
if (!s.currentComponentInfo._styles) {
s.currentComponentInfo._styles = {}
store.update(state => {
if (!state.currentComponentInfo._styles) {
state.currentComponentInfo._styles = {}
}
s.currentComponentInfo._styles[type][name] = value
s.currentPreviewItem._css = generate_screen_css([
s.currentPreviewItem.props,
state.currentComponentInfo._styles[type][name] = value
state.currentPreviewItem._css = generate_screen_css([
state.currentPreviewItem.props,
])
// save without messing with the store
_saveCurrentPreviewItem(s)
return s
_saveCurrentPreviewItem(state)
return state
})
}
const setComponentCode = store => code => {
store.update(s => {
s.currentComponentInfo._code = code
store.update(state => {
state.currentComponentInfo._code = code
setCurrentPageFunctions(s)
setCurrentPageFunctions(state)
// save without messing with the store
_saveScreenApi(s.currentPreviewItem, s)
_saveScreenApi(state.currentPreviewItem, state)
return s
})
@ -543,31 +471,31 @@ const setCurrentPageFunctions = s => {
const buildPageCode = (screens, page) => buildCodeForScreens([page, ...screens])
const setScreenType = store => type => {
store.update(s => {
s.currentFrontEndType = type
store.update(state => {
state.currentFrontEndType = type
const pageOrScreen =
type === "page"
? s.pages[s.currentPageName]
: s.pages[s.currentPageName]._screens[0]
? state.pages[state.currentPageName]
: state.pages[state.currentPageName]._screens[0]
s.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null
s.currentPreviewItem = pageOrScreen
return s
state.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null
state.currentPreviewItem = pageOrScreen
return state
})
}
const deleteComponent = store => component => {
store.update(s => {
const parent = getParent(s.currentPreviewItem.props, component)
const deleteComponent = store => componentName => {
store.update(state => {
const parent = getParent(state.currentPreviewItem.props, componentName)
if (parent) {
parent._children = parent._children.filter(c => c !== component)
parent._children = parent._children.filter(component => component !== componentName)
}
_saveCurrentPreviewItem(s)
_saveCurrentPreviewItem(state)
return s
return state
})
}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -14,7 +14,6 @@
takeRight,
} from "lodash/fp"
import Select from "components/common/Select.svelte"
import { getIndexSchema } from "components/common/core"
import ActionButton from "components/common/ActionButton.svelte"
import TablePagination from "./TablePagination.svelte"
import { DeleteRecordModal, CreateEditRecordModal } from "./modals"

View file

@ -9,8 +9,7 @@
import api from "builderStore/api"
import { store, backendUiStore } from "builderStore"
import { common, hierarchy } from "../../../../../../../core/src/"
import { getNode } from "components/common/core"
import { templateApi, pipe, validate } from "components/common/core"
import { pipe } from "components/common/core"
import ErrorsBox from "components/common/ErrorsBox.svelte"
export let model = { schema: {} }

View file

@ -10,10 +10,6 @@
import ActionButton from "components/common/ActionButton.svelte"
import DatePicker from "components/common/DatePicker.svelte"
import { keys, cloneDeep } from "lodash/fp"
import {
allTypes,
validate,
} from "components/common/core"
const FIELD_TYPES = ["string", "number", "boolean"]

View file

@ -4,11 +4,6 @@
import { compose, map, get, flatten } from "lodash/fp"
import ActionButton from "components/common/ActionButton.svelte"
import Select from "components/common/Select.svelte"
import {
getNewRecord,
joinKey,
getExactNodeForKey,
} from "components/common/core"
import RecordFieldControl from "./RecordFieldControl.svelte"
import * as api from "../api"
import ErrorsBox from "components/common/ErrorsBox.svelte"

View file

@ -2,7 +2,6 @@
import { getContext, onMount } from "svelte"
import { store, backendUiStore } from "builderStore"
import HierarchyRow from "./HierarchyRow.svelte"
import DropdownButton from "components/common/DropdownButton.svelte"
import NavItem from "./NavItem.svelte"
import getIcon from "components/common/icon"
import api from "builderStore/api"

View file

@ -32,7 +32,7 @@
$: screensExist = $store.currentPreviewItem._screens && $store.currentPreviewItem._screens.length > 0
$: frontendDefinition = {
componentLibraries: $store.loadLibraryUrls($store.currentPageName),
libraries: $store.libraries,
page: $store.currentPreviewItem,
screens: screensExist ? $store.currentPreviewItem._screens : [{
name: "Screen Placeholder",

View file

@ -0,0 +1 @@
export { default } from "./CurrentItemPreview.svelte";

View file

@ -29,23 +29,6 @@
const categories = components.categories
let selectedCategory = categories[0]
// const onTemplateChosen = template => {
// selectedComponent = null
// const { componentName, libName } = splitName(template.name)
// const templateOptions = {
// records: getRecordNodes(hierarchy),
// indexes: getIndexNodes(hierarchy),
// helpers: {
// indexSchema: getIndexSchema(hierarchy),
// },
// }
// templateInstances = libraryModules[libName][componentName](templateOptions)
// if (!templateInstances || templateInstances.length === 0) return
// selectedTemplateInstance = templateInstances[0].name
// selectTemplateDialog.show()
// }
const onComponentChosen = component => {
if (component.template) {
// onTemplateChosen(component.template)
@ -55,28 +38,26 @@
}
}
const onTemplateInstanceChosen = () => {
selectedComponent = null
const instance = templateInstances.find(
i => i.name === selectedTemplateInstance
)
store.addTemplatedComponent(instance.props)
toggleTab()
}
// const onTemplateInstanceChosen = () => {
// selectedComponent = null
// const instance = templateInstances.find(
// i => i.name === selectedTemplateInstance
// )
// store.addTemplatedComponent(instance.props)
// toggleTab()
// }
$: templatesByComponent = groupBy(t => t.component)($store.templates)
$: hierarchy = $store.hierarchy
$: libraryModules = $store.libraries
$: standaloneTemplates = pipe(
templatesByComponent,
[
values,
flatten,
filter(t => !$store.components.some(c => c.name === t.component)),
map(t => ({ name: splitName(t.component).componentName, template: t })),
uniqBy(t => t.name),
]
)
// $: templatesByComponent = groupBy(t => t.component)($store.templates)
// $: standaloneTemplates = pipe(
// templatesByComponent,
// [
// values,
// flatten,
// filter(t => !$store.components.some(c => c.name === t.component)),
// map(t => ({ name: splitName(t.component).componentName, template: t })),
// uniqBy(t => t.name),
// ]
// )
</script>
<div class="root">
@ -93,12 +74,11 @@
<Tab
list={selectedCategory}
on:selectItem={e => onComponentChosen(e.detail)}
{onTemplateChosen}
{toggleTab} />
</div>
</div>
<ConfirmDialog
<!-- <ConfirmDialog
bind:this={selectTemplateDialog}
title="Choose Template"
onCancel={() => (selectedComponent = null)}
@ -115,7 +95,7 @@
</label>
</div>
{/each}
</ConfirmDialog>
</ConfirmDialog> -->
<style>
.tabs {

View file

@ -33,8 +33,6 @@
const isComponentSelected = (current, comp) => current === comp
const isFolderSelected = (current, folder) => isInSubfolder(current, folder)
$: _screens = pipe(
screens,
[map(c => ({ component: c, title: lastPartOfName(c) })), sortBy("title")]

View file

@ -1,5 +1,6 @@
<script>
import { last, pipe } from "lodash/fp"
import { last } from "lodash/fp"
import { pipe } from "components/common/core";
import {
XCircleIcon,
ChevronUpIcon,

View file

@ -33,9 +33,7 @@
let selectedEvent = null
$: {
const componentDefinition = components.find(
c => c.name === component._component
)
const componentDefinition = components[component._component];
events = Object.keys(componentDefinition.props)
.filter(
propName => componentDefinition.props[propName].type === EVENT_TYPE

View file

@ -26,20 +26,13 @@
let saveAttempted = false
$: layoutComponents = pipe(
$store.components,
[
filter(c => c.container),
map(c => ({ name: c.name, ...splitName(c.name) })),
]
)
$: layoutComponents = Object.values($store.components).filter(componentDefinition => componentDefinition.container)
$: layoutComponent = layoutComponent
? find(c => c.name === layoutComponent.name)(layoutComponents)
? layoutComponents.find(component => component._component === layoutComponent._component)
: layoutComponents[0]
$: screens = $store.screens
$: route = !route && screens.length === 0 ? "*" : route
$: route = !route && $store.screens.length === 0 ? "*" : route
const save = () => {
saveAttempted = true
@ -53,7 +46,7 @@
if (!isValid) return
store.createScreen(name, route, layoutComponent.name)
store.createScreen(name, route, layoutComponent._component)
dialog.hide()
}
@ -62,21 +55,16 @@
}
const screenNameExists = name => {
return some(s => {
return s.name.toLowerCase() === name.toLowerCase()
})(screens)
return $store.screens.some(screen => screen.name.toLowerCase() === name.toLowerCase());
}
const routeNameExists = route => {
return some(s => {
return s.route.toLowerCase() === route.toLowerCase()
})(screens)
return $store.screens.some(screen => screen.route.toLowerCase() === route.toLowerCase());
}
const routeChanged = event => {
const r = event.target.value
if (!r.startsWith("/")) {
route = "/" + r
if (!event.target.value.startsWith("/")) {
route = "/" + event.target.value
}
}
</script>
@ -100,7 +88,7 @@
</div>
<div class="uk-margin">
<label class="uk-form-label">Route (Url)</label>
<label class="uk-form-label">Route (URL)</label>
<div class="uk-form-controls">
<input
class="uk-input uk-form-small"
@ -117,8 +105,8 @@
class="uk-select uk-form-small"
bind:value={layoutComponent}
class:uk-form-danger={saveAttempted && !layoutComponent}>
{#each layoutComponents as comp}
<option value={comp}>{comp.componentName} - {comp.libName}</option>
{#each layoutComponents as { _component, name }}
<option value={_component}>{name}</option>
{/each}
</select>
</div>

View file

@ -2,7 +2,7 @@
// import { tick } from "svelte"
import ComponentsHierarchyChildren from "./ComponentsHierarchyChildren.svelte"
import { last, sortBy, map, trimCharsStart, trimChars, join } from "lodash/fp"
import { last, sortBy, map, trimCharsStart, trimChars, join, compose } from "lodash/fp"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { pipe } from "components/common/core"
import { store } from "builderStore"
@ -15,28 +15,15 @@
const joinPath = join("/")
const normalizedName = name =>
pipe(
name,
[
trimCharsStart("./"),
trimCharsStart("~/"),
trimCharsStart("../"),
trimChars(" "),
]
)
const lastPartOfName = c =>
c && last(c.name ? c.name.split("/") : c._component.split("/"))
const isComponentSelected = (current, comp) => current === comp
const isFolderSelected = (current, folder) => isInSubfolder(current, folder)
$: _layout = pipe(
layout,
[c => ({ component: c, title: lastPartOfName(c) })]
)
$: _layout = {
component: layout,
title: lastPartOfName(layout)
}
const isScreenSelected = component =>
component.component &&
@ -45,7 +32,6 @@
const confirmDeleteComponent = async component => {
componentToDelete = component
// await tick()
confirmDeleteDialog.show()
}
</script>

View file

@ -7,16 +7,15 @@
import { pipe } from "components/common/core"
import { filter, find, concat } from "lodash/fp"
const notSeletedComponent = { name: "(none selected)" }
const notSelectedComponent = { name: "(none selected)" }
$: page = $store.pages[$store.currentPageName]
$: title = page.index.title
$: components = pipe(
$store.components,
[filter(store => !isRootComponent($store)), concat([notSeletedComponent])]
[filter(store => !isRootComponent($store)), concat([notSelectedComponent])]
)
$: entryComponent =
find(c => c.name === page.appBody)(components) || notSeletedComponent
$: entryComponent = components[page.appBody] || notSelectedComponent
const save = () => {
if (!title || !entryComponent || entryComponent === notSeletedComponent)

View file

@ -12,10 +12,7 @@
let errors = []
const props_to_ignore = ["_component", "_children", "_styles", "_code", "_id"]
$: componentDef =
component &&
components &&
components.find(({ name }) => name === component._component)
$: componentDef = components[component._component]
let setProp = (name, value) => {
onPropChanged(name, value)

View file

@ -6,7 +6,7 @@
import { store } from "builderStore"
import IconButton from "components/common/IconButton.svelte"
import NewScreen from "./NewScreen.svelte"
import CurrentItemPreview from "./CurrentItemPreview.svelte"
import AppPreview from "./AppPreview"
import PageView from "./PageView.svelte"
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
@ -71,7 +71,7 @@
</div>
<div class="preview-pane">
<CurrentItemPreview />
<AppPreview />
</div>
{#if $store.currentFrontEndType === 'screen' || $store.currentFrontEndType === 'page'}

View file

@ -1,5 +1,5 @@
import { isString, isUndefined } from "lodash/fp"
import { types } from "./types"
import { TYPE_MAP } from "./types"
import { assign } from "lodash"
import { uuid } from "builderStore/uuid"
@ -12,17 +12,17 @@ export const getBuiltin = name => {
}
}
export const getNewScreen = (components, rootComponentName, name) => {
const rootComponent = components.find(c => c.name === rootComponentName)
return {
name: name || "",
description: "",
url: "",
_css: "",
uiFunctions: "",
props: createProps(rootComponent).props,
}
}
// export const getNewScreen = (components, rootComponentName, name) => {
// const rootComponent = components[rootComponentName]
// return {
// name: name || "",
// description: "",
// url: "",
// _css: "",
// uiFunctions: "",
// props: createProps(rootComponent).props,
// }
// }
/**
* @param {object} componentDefinition - component definition from a component library
@ -33,9 +33,9 @@ export const createProps = (componentDefinition, derivedFromProps) => {
const errorOccurred = (propName, error) => errors.push({ propName, error })
const props = {
_component: componentDefinition.name,
_styles: { position: {}, layout: {} },
_id: uuid(),
_component: componentDefinition._component,
_styles: { position: {}, layout: {} },
_code: "",
}
@ -44,11 +44,14 @@ export const createProps = (componentDefinition, derivedFromProps) => {
if (!componentDefinition.name)
errorOccurred("_component", "Component name not supplied")
const propsDef = componentDefinition.props
for (let propDef in propsDef) {
const parsedPropDef = parsePropDef(propsDef[propDef])
if (parsedPropDef.error) errorOccurred(propDef, parsedPropDef.error)
else props[propDef] = parsedPropDef
for (let propName in componentDefinition.props) {
const parsedPropDef = parsePropDef(componentDefinition.props[propName])
if (parsedPropDef.error) {
errors.push({ propName, error: parsedPropDef.error })
} else {
props[propName] = parsedPropDef
}
}
if (derivedFromProps) {
@ -88,20 +91,18 @@ const parsePropDef = propDef => {
const error = message => ({ error: message, propDef })
if (isString(propDef)) {
if (!types[propDef]) return error(`Do not recognise type ${propDef}`)
if (!TYPE_MAP[propDef]) return error(`Type ${propDef} is not recognised.`)
return types[propDef].default()
return TYPE_MAP[propDef].default
}
if (!propDef.type) return error("Property Definition must declare a type")
const type = TYPE_MAP[propDef.type]
if (!type) return error(`Type ${propDef.type} is not recognised.`)
const type = types[propDef.type]
if (!type) return error(`Do not recognise type ${propDef.type}`)
// if (isUndefined(propDef.default)) return type.default(propDef)
if (isUndefined(propDef.default)) return type.default(propDef)
if (!type.isOfType(propDef.default))
return error(`${propDef.default} is not of type ${type}`)
// if (!type.isOfType(propDef.default))
// return error(`${propDef.default} is not of type ${type}`)
return propDef.default
}

View file

@ -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 }
}

View file

@ -1,4 +1,5 @@
import { find, isUndefined, filter, some, includes, pipe } from "lodash/fp"
import { find, isUndefined, filter, some, includes } from "lodash/fp"
import { pipe } from "components/common/core";
const normalString = s => (s || "").trim().toLowerCase()

View file

@ -1,4 +1,5 @@
import { split, last, pipe } from "lodash/fp"
import { split, last } from "lodash/fp"
import { pipe } from "components/common/core";
export const splitName = fullname => {
const componentName = pipe(fullname, [split("/"), last])

View file

@ -14,70 +14,86 @@ import {
BB_STATE_BINDINGPATH,
} from "@budibase/client/src/state/parseBinding"
const defaultDef = typeName => () => ({
type: typeName,
required: false,
default: types[typeName].default(),
options: typeName === "options" ? [] : undefined,
})
// const defaultDef = typeName => () => ({
// type: typeName,
// required: false,
// default: types[typeName].default,
// options: typeName === "options" ? [] : undefined,
// })
const propType = (defaultValue, isOfType, defaultDefinition) => ({
isOfType,
default: defaultValue,
defaultDefinition,
})
// const propType = (defaultValue, isOfType, defaultDefinition) => ({
// isOfType,
// default: defaultValue,
// defaultDefinition,
// })
const expandSingleProp = propDef => {
const p = isString(propDef) ? types[propDef].defaultDefinition() : propDef
// const expandSingleProp = propDef => {
// const p = isString(propDef) ? types[propDef].defaultDefinition() : propDef
if (!isString(propDef)) {
const def = types[propDef.type].defaultDefinition()
for (let p in def) {
if (propDef[p] === undefined) {
propDef[p] = def[p]
}
}
// if (!isString(propDef)) {
// const def = types[propDef.type].defaultDefinition()
// for (let p in def) {
// if (propDef[p] === undefined) {
// propDef[p] = def[p]
// }
// }
// }
// return p
// }
// export const expandComponentDefinition = componentDefinition => {
// const expandedProps = {}
// const expandedComponent = { ...componentDefinition }
// for (let p in componentDefinition.props) {
// expandedProps[p] = expandSingleProp(componentDefinition.props[p])
// }
// expandedComponent.props = expandedProps
// if (expandedComponent.children !== false) {
// expandedComponent.children = true
// }
// return expandedComponent
// }
// const isEvent = e =>
// isPlainObject(e) &&
// isString(e[EVENT_TYPE_MEMBER_NAME]) &&
// isPlainObject(e.parameters)
// const isEventList = e => isArray(e) && every(isEvent)(e)
// // const EMPTY_STATE = () => ({ [BB_STATE_BINDINGPATH]: "" });
// export const types = {
// string: propType("", isString, defaultDef("string")),
// bool: propType(false, isBoolean, defaultDef("bool")),
// number: propType(0, isNumber, defaultDef("number")),
// options: propType("", isString, defaultDef("options")),
// asset: propType("", isString, defaultDef("asset")),
// event: propType([], isEventList, defaultDef("event")),
// // state: propType(EMPTY_STATE, isBound, defaultDef("state")),
// }
export const TYPE_MAP = {
string: {
default: ""
},
bool: {
default: false
},
number: {
default: 0
},
options: {
default: [],
options: []
},
event: {
default: [],
}
return p
}
export const expandComponentDefinition = componentDefinition => {
const expandedProps = {}
const expandedComponent = { ...componentDefinition }
for (let p in componentDefinition.props) {
expandedProps[p] = expandSingleProp(componentDefinition.props[p])
}
expandedComponent.props = expandedProps
if (expandedComponent.children !== false) {
expandedComponent.children = true
}
return expandedComponent
}
const isEvent = e =>
isPlainObject(e) &&
isString(e[EVENT_TYPE_MEMBER_NAME]) &&
isPlainObject(e.parameters)
const isEventList = e => isArray(e) && every(isEvent)(e)
const emptyState = () => {
const s = {}
s[BB_STATE_BINDINGPATH] = ""
return s
}
export const types = {
string: propType(() => "", isString, defaultDef("string")),
bool: propType(() => false, isBoolean, defaultDef("bool")),
number: propType(() => 0, isNumber, defaultDef("number")),
options: propType(() => "", isString, defaultDef("options")),
asset: propType(() => "", isString, defaultDef("asset")),
event: propType(() => [], isEventList, defaultDef("event")),
state: propType(() => emptyState(), isBound, defaultDef("state")),
}
};

View file

@ -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]
}

View file

@ -1,4 +1,4 @@
export const defaultPagesObject = () => ({
export const DEFAULT_PAGES_OBJECT = {
main: {
_props: {},
_screens: {},
@ -17,4 +17,4 @@ export const defaultPagesObject = () => ({
},
componentLibraries: [],
stylesheets: [],
})
}

View file

@ -71,6 +71,10 @@ html, body {
width:100%;
}
#app {
height: 100%;
}
h1 {
font-family: var(--fontblack);
font-size: 36pt;

View file

@ -10,6 +10,8 @@
// Get Package and set store
export let application
let ready = false
let promise = getPackage()
async function getPackage() {
@ -71,6 +73,8 @@
<div />
{:then}
<slot />
{:catch error}
<p>Something went wrong: {error.message}</p>
{/await}
</div>
@ -94,13 +98,6 @@
align-items: center;
}
.content {
flex: 1 1 auto;
width: 100%;
height: 100px;
overflow: hidden;
}
.content > div {
height: 100%;
width: 100%;

View file

@ -4,11 +4,8 @@
import { onMount } from "svelte"
$: instances = $store.appInstances
$: views = $store.hierarchy.indexes
async function selectDatabase(database) {
backendUiStore.actions.records.select(null)
backendUiStore.actions.views.select(views[0])
backendUiStore.actions.database.select(database)
}

View file

@ -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)
})
})

View file

@ -20,7 +20,6 @@
"author": "Budibase",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@budibase/datastores": "^0.0.32",
"@budibase/server": "^0.0.32",
"@inquirer/password": "^0.0.6-alpha.0",
"chalk": "^2.4.2",

View file

@ -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": ""
}

View file

@ -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": ""
}

View file

@ -6,7 +6,6 @@ const { copy, readJSON, writeJSON, remove, exists } = require("fs-extra")
const { resolve, join } = require("path")
const chalk = require("chalk")
const { exec } = require("child_process")
const glob = require("glob");
module.exports = opts => {
run(opts)

View file

@ -1,27 +1,20 @@
const { resolve, join } = require("path")
const { cwd } = require("process")
const { homedir } = require("os")
const buildAppContext = require("@budibase/server/initialise/buildAppContext")
module.exports.serverFileName = relativePath =>
resolve(__dirname, "..", "node_modules", "@budibase", "server", relativePath)
module.exports.getAppContext = async ({ configName, masterIsCreated }) => {
if (configName) {
if (!configName.endsWith(".js")) {
configName = `config.${configName}.js`
}
} else {
configName = "config.js"
}
// module.exports.getAppContext = async ({ configName, masterIsCreated }) => {
// if (configName) {
// if (!configName.endsWith(".js")) {
// configName = `config.${configName}.js`
// }
// } else {
// configName = "config.js"
// }
const config = require(resolve(cwd(), configName))()
return await buildAppContext(config, masterIsCreated)
}
// const config = require(resolve(cwd(), configName))()
// return await buildAppContext(config, masterIsCreated)
// }
module.exports.xPlatHomeDir = dir => {
if (dir.startsWith("~")) {
dir = join(homedir(), dir.substring(1))
}
return dir
}
module.exports.xPlatHomeDir = dir => dir.startsWith("~") ? join(homedir(), dir.substring(1)) : dir;

File diff suppressed because it is too large Load diff

View file

@ -8,15 +8,14 @@ import { createStateManager } from "./state/stateManager"
export const createApp = (
componentLibraries,
frontendDefinition,
backendDefinition,
user,
uiFunctions,
window
) => {
const coreApi = createCoreApi(backendDefinition, user)
backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
backendDefinition.hierarchy
)
// const coreApi = createCoreApi(backendDefinition, user)
// backendDefinition.hierarchy = coreApi.templateApi.constructHierarchy(
// backendDefinition.hierarchy
// )
let routeTo
let currentUrl
@ -26,7 +25,6 @@ export const createApp = (
const onScreenSelected = (screen, store, url) => {
const stateManager = createStateManager({
store,
coreApi,
frontendDefinition,
componentLibraries,
uiFunctions,
@ -74,7 +72,6 @@ export const createApp = (
let rootTreeNode
const pageStateManager = createStateManager({
store: writable({ _bbuser: user }),
coreApi,
frontendDefinition,
componentLibraries,
uiFunctions,

View file

@ -27,7 +27,7 @@ export const loadBudibase = async opts => {
temp: false,
}
const { appRootPath } = frontendDefinition;
let { appRootPath } = frontendDefinition;
appRootPath = appRootPath === "" ? "" : "/" + trimSlash(appRootPath)
// if (!componentLibraries) componentLibraries = {};
@ -58,7 +58,6 @@ export const loadBudibase = async opts => {
} = createApp(
componentLibraries,
frontendDefinition,
backendDefinition,
user,
uiFunctions || {},
_window,

View file

@ -7,7 +7,7 @@ import { getNewChildRecordToState, getNewRecordToState } from "./coreHandlers"
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"
export const eventHandlers = (store, coreApi, rootPath, routeTo) => {
export const eventHandlers = (store, rootPath, routeTo) => {
const handler = (parameters, execute) => ({
execute,
parameters,
@ -34,15 +34,15 @@ export const eventHandlers = (store, coreApi, rootPath, routeTo) => {
"List Records": handler(["indexKey", "statePath"], api.listRecords),
"Save Record": handler(["statePath"], api.saveRecord),
"Get New Child Record": handler(
["recordKey", "collectionName", "childRecordType", "statePath"],
getNewChildRecordToState(coreApi, setStateWithStore)
),
// "Get New Child Record": handler(
// ["recordKey", "collectionName", "childRecordType", "statePath"],
// getNewChildRecordToState(coreApi, setStateWithStore)
// ),
"Get New Record": handler(
["collectionKey", "childRecordType", "statePath"],
getNewRecordToState(coreApi, setStateWithStore)
),
// "Get New Record": handler(
// ["collectionKey", "childRecordType", "statePath"],
// getNewRecordToState(coreApi, setStateWithStore)
// ),
"Navigate To": handler(["url"], param => routeTo(param && param.url)),

View file

@ -22,7 +22,6 @@ const isMetaProp = propName =>
export const createStateManager = ({
store,
coreApi,
appRootPath,
frontendDefinition,
componentLibraries,
@ -30,7 +29,7 @@ export const createStateManager = ({
onScreenSlotRendered,
routeTo,
}) => {
let handlerTypes = eventHandlers(store, coreApi, appRootPath, routeTo)
let handlerTypes = eventHandlers(store, appRootPath, routeTo)
let currentState
// any nodes that have props that are bound to the store

View file

@ -1,5 +0,0 @@
{
"presets": ["@babel/env"],
"sourceMaps": "inline",
"retainLines": true
}

View file

@ -1 +0,0 @@
/node_modules/

View file

@ -1,5 +0,0 @@
*
!datastores/*
!index.js
!config.js
!config.template.js

View file

@ -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"
]
}
]
}

View file

@ -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

View file

@ -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

View file

@ -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: "",
}
}

View file

@ -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",
}

View file

@ -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,
}
}

View file

@ -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))

View file

@ -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"
}

View file

@ -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)
})
}
}

View file

@ -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)

View file

@ -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",
})
}

View file

@ -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
}
}

View file

@ -1,61 +0,0 @@
import { getAppApis, getTemplateApi, setupDatastore } from "@budibase/core"
const addField = templateApi => type => (record, name) => {
const field = templateApi.getNewField(type)
field.name = name
field.type = type
field.label = name
templateApi.addField(record, field)
}
export default async datastore => {
datastore = setupDatastore(datastore)
const templateApi = await getTemplateApi(datastore)
const addStringField = addField(templateApi)("string")
const addDateField = addField(templateApi)("datetime")
const addBoolField = addField(templateApi)("bool")
const root = templateApi.getNewRootLevel()
const clients = templateApi.getNewCollectionTemplate(root)
clients.name = "clients"
const client = templateApi.getNewModelTemplate(clients)
client.name = "client"
addStringField(client, "FamilyName")
addStringField(client, "Address1")
addStringField(client, "Address2")
addStringField(client, "Address3")
addStringField(client, "Address4")
addStringField(client, "Postcode")
addDateField(client, "CreatedDate")
const children = templateApi.getNewCollectionTemplate(client)
children.name = "children"
const child = templateApi.getNewModelTemplate(children)
child.name = "child"
addStringField(child, "FirstName")
addStringField(child, "Surname")
addDateField(child, "DateOfBirth")
addBoolField(child, "Current")
const contacts = templateApi.getNewCollectionTemplate(client)
contacts.name = "contacts"
const contact = templateApi.getNewModelTemplate(contacts)
contact.name = "contact"
addStringField(contact, "Name")
addStringField(contact, "relationship")
addStringField(contact, "phone1")
addStringField(contact, "phone2")
addBoolField(contact, "active")
await templateApi.saveApplicationHeirarchy(root)
const apis = await getAppApis(datastore)
await apis.collectionApi.initialiseAll()
return apis
}

File diff suppressed because it is too large Load diff

View file

@ -24,6 +24,10 @@ exports.create = async function(ctx) {
const { id, rev } = await clientDb.post({
type: "app",
instances: [],
componentLibraries: [
"@budibase/standard-components",
"@budibase/materialdesign-components"
],
...ctx.request.body,
});

View file

@ -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;
};

View file

@ -1,5 +1,6 @@
const send = require("koa-send");
const { resolve } = require("path")
const { homedir } = require("os");
// either serve the builder or serve the actual app index.html
const builderPath = resolve(process.cwd(), "builder")
@ -16,5 +17,29 @@ exports.serveApp = async function(ctx) {
}
exports.serveComponentLibrary = async function(ctx) {
await send(ctx, "/index.html", { root: builderPath })
// TODO: update to run wherever budi is run
const componentLibraryPath = resolve(
homedir(),
".budibase",
ctx.params.appId,
"node_modules",
decodeURI(ctx.query.library),
"dist"
);
await send(ctx, "/index.js", { root: componentLibraryPath })
}
exports.serveComponentDefinitions = async function(ctx) {
// TODO: update to run wherever budi is run
const componentLibraryPath = resolve(
homedir(),
".budibase",
ctx.params.appId,
"node_modules",
decodeURI(ctx.query.library),
"dist"
);
await send(ctx, "/index.js", { root: componentLibraryPath })
}

View file

@ -1,16 +1,15 @@
const Router = require("@koa/router")
const session = require("../middleware/session")
const StatusCodes = require("../utilities/statusCodes")
const compress = require("koa-compress");
const zlib = require("zlib");
const { resolve } = require("path")
const { homedir } = require("os")
const send = require("koa-send")
const routeHandlers = require("../middleware/routeHandlers")
const {
componentLibraryInfo,
} = require("../utilities/builder")
const {
componentRoutes,
appsRoutes,
// componentRoutes,
pageRoutes,
userRoutes,
authenticatedRoutes
@ -24,11 +23,21 @@ const applicationRoutes = require("./routes/neo/application");
const modelsRoutes = require("./routes/neo/model");
const viewsRoutes = require("./routes/neo/view");
const staticRoutes = require("./routes/neo/static");
const componentRoutes = require("./routes/neo/component");
module.exports = app => {
const router = new Router()
router
.use(compress({
threshold: 2048,
gzip: {
flush: zlib.Z_SYNC_FLUSH
},
deflate: {
flush: zlib.Z_SYNC_FLUSH,
}
}))
// .use(session(app))
.use(async (ctx, next) => {
// TODO: temp dev middleware
@ -107,14 +116,14 @@ module.exports = app => {
// .get("/_builder", async ctx => {
// await send(ctx, "/index.html", { root: builderPath })
// })
.get("/_builder/:appname/componentlibrary", async ctx => {
const info = await componentLibraryInfo(
ctx.config,
ctx.params.appname,
ctx.query.lib
)
await send(ctx, info.components._lib || "index.js", { root: info.libDir })
})
// .get("/_builder/:appname/componentlibrary", async ctx => {
// const info = await componentLibraryInfo(
// ctx.config,
// ctx.params.appname,
// ctx.query.lib
// )
// await send(ctx, info.components._lib || "index.js", { root: info.libDir })
// })
// .get("/_builder/*", async (ctx, next) => {
// const path = ctx.path.replace("/_builder", "")
@ -149,8 +158,8 @@ module.exports = app => {
router.use(userRoutes.allowedMethods());
// router.use(appsRoutes.routes())
// router.use(appsRoutes.allowedMethods());
router.use(componentRoutes.routes());
router.use(componentRoutes.allowedMethods());
// router.use(componentRoutes.routes());
// router.use(componentRoutes.allowedMethods());
router.use(pageRoutes.routes());
router.use(pageRoutes.allowedMethods());
@ -167,6 +176,9 @@ module.exports = app => {
router.use(applicationRoutes.routes());
router.use(applicationRoutes.allowedMethods());
router.use(componentRoutes.routes());
router.use(componentRoutes.allowedMethods());
router.use(clientRoutes.routes());
router.use(clientRoutes.allowedMethods());
@ -188,8 +200,8 @@ module.exports = app => {
// .get("/_builder/instance/:appname/:instanceid/*", routeHandlers.appDefault)
router.use(authenticatedRoutes.routes());
router.use(authenticatedRoutes.allowedMethods());
// router.use(authenticatedRoutes.routes());
// router.use(authenticatedRoutes.allowedMethods());
return router
}

View file

@ -26,15 +26,15 @@ const router = Router();
// routeHandlers.changeMyPassword
// )
router.post(
"/:appname/api/executeAction/:actionname",
routeHandlers.executeAction
)
// router.post(
// "/:appname/api/executeAction/:actionname",
// routeHandlers.executeAction
// )
router.post(
"/_builder/instance/:appname/:instanceid/api/executeAction/:actionname",
routeHandlers.executeAction
)
// router.post(
// "/_builder/instance/:appname/:instanceid/api/executeAction/:actionname",
// routeHandlers.executeAction
// )
// router.post("/:appname/api/createUser", routeHandlers.createUser)

View file

@ -2,21 +2,20 @@ const Router = require("@koa/router");
const send = require("koa-send")
const StatusCodes = require("../../utilities/statusCodes")
const {
getComponentDefinitions,
componentLibraryInfo,
} = require("../../utilities/builder")
const router = Router();
router.get("/_builder/:appname/componentlibrary", async ctx => {
const info = await componentLibraryInfo(
ctx.config,
ctx.params.appname,
ctx.query.lib
)
await send(ctx, info.components._lib || "index.js", { root: info.libDir })
})
// router.get("/_builder/:appname/componentlibrary", async ctx => {
// const info = await componentLibraryInfo(
// ctx.config,
// ctx.params.appname,
// ctx.query.lib
// )
// await send(ctx, info.components._lib || "index.js", { root: info.libDir })
// })
// router.get("/_builder/api/:appname/components", async ctx => {
// try {
@ -35,14 +34,14 @@ router.get("/_builder/:appname/componentlibrary", async ctx => {
// }
// })
router.get("/_builder/api/:appname/componentlibrary", async ctx => {
const info = await componentLibraryInfo(
ctx.config,
ctx.params.appname,
ctx.query.lib ? decodeURI(ctx.query.lib) : ""
)
ctx.body = info.components
ctx.response.status = StatusCodes.OK
})
// router.get("/_builder/api/:appname/componentlibrary", async ctx => {
// const info = await componentLibraryInfo(
// ctx.config,
// ctx.params.appname,
// ctx.query.lib ? decodeURI(ctx.query.lib) : ""
// )
// ctx.body = info.components
// ctx.response.status = StatusCodes.OK
// })
module.exports = router

View file

@ -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;

View file

@ -9,6 +9,7 @@ router
await next();
})
.get("/_builder/:file*", controller.serveBuilder)
.get("/:appName", controller.serveApp);
.get("/_app/:appId", controller.serveApp)
.get("/:appId/componentlibrary", controller.serveComponentLibrary);
module.exports = router;

View file

@ -27,6 +27,7 @@
"jsonwebtoken": "^8.5.1",
"koa": "^2.7.0",
"koa-body": "^4.1.0",
"koa-compress": "^4.0.1",
"koa-logger": "^3.2.1",
"koa-send": "^5.0.0",
"koa-session": "^5.12.0",
@ -37,7 +38,8 @@
"squirrelly": "^7.5.0",
"tar-fs": "^2.0.0",
"uuid": "^3.3.2",
"yargs": "^13.2.4"
"yargs": "^13.2.4",
"zlib": "^1.0.5"
},
"devDependencies": {
"@jest/test-sequencer": "^24.8.0",

Some files were not shown because too many files have changed in this diff Show more