mirror of https://github.com/Budibase/budibase.git
32 changed files with 21056 additions and 933 deletions
@ -0,0 +1,46 @@ |
|||||
|
import { |
||||
|
split, |
||||
|
last |
||||
|
} from "lodash/fp"; |
||||
|
import {writable} from "svelte/store"; |
||||
|
import { $ } from "./core/common"; |
||||
|
import { bindComponent } from "./stateBinding"; |
||||
|
|
||||
|
export const createApp = componentLibraries => { |
||||
|
|
||||
|
const initialiseComponent = (props, htmlElement) => { |
||||
|
|
||||
|
const {componentName, libName} = splitName(props._component); |
||||
|
|
||||
|
const component = new (componentLibraries[libName][componentName])({ |
||||
|
target: htmlElement, |
||||
|
props: {...props, _app} |
||||
|
}); |
||||
|
|
||||
|
bindComponent(store, component); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const store = writable({}); |
||||
|
|
||||
|
const _app = { |
||||
|
initialiseComponent, |
||||
|
store |
||||
|
}; |
||||
|
|
||||
|
return _app; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
const splitName = fullname => { |
||||
|
const componentName = $(fullname, [ |
||||
|
split("/"), |
||||
|
last |
||||
|
]); |
||||
|
|
||||
|
const libName =fullname.substring( |
||||
|
0, fullname.length - componentName.length - 1); |
||||
|
|
||||
|
return {libName, componentName}; |
||||
|
} |
||||
@ -1,5 +1,25 @@ |
|||||
import { initialise } from "./initialise"; |
import { createApp } from "./createApp"; |
||||
|
|
||||
const appDefinition = window["##BUDIBASE_APPDEFINITION##"]; |
const appDefinition = window["##BUDIBASE_APPDEFINITION##"]; |
||||
|
|
||||
initialise(window.document, appDefinition); |
const componentLibraryUrl = (lib) => "./" + trimSlash(lib) |
||||
|
|
||||
|
const trimSlash = (str) => str.replace(/^\/+|\/+$/g, ''); |
||||
|
|
||||
|
(async () => { |
||||
|
|
||||
|
const componentLibraries = {}; |
||||
|
|
||||
|
for(let lib of appDefinition.componentLibraries) { |
||||
|
componentLibraries[lib.libName] = await import( |
||||
|
componentLibraryUrl(lib.importPath)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const _app = createApp(componentLibraries); |
||||
|
|
||||
|
_app.initialiseComponent( |
||||
|
props, |
||||
|
document.body); |
||||
|
|
||||
|
})(); |
||||
@ -1,22 +0,0 @@ |
|||||
import { writable } from "svelte/store"; |
|
||||
import { initialiseComponent } from "./initialiseComponent"; |
|
||||
|
|
||||
export const initialise = async (document, appDefinition) => { |
|
||||
|
|
||||
const componentLibraries = {}; |
|
||||
|
|
||||
for(let lib of appDefinition.componentLibraries) { |
|
||||
componentLibraries[lib.libName] = await import( |
|
||||
componentLibraryUrl(lib.importPath)); |
|
||||
} |
|
||||
|
|
||||
const store = writable({}); |
|
||||
|
|
||||
initialiseComponent(componentLibraries, store)( |
|
||||
appDefinition.props, |
|
||||
document.body); |
|
||||
} |
|
||||
const componentLibraryUrl = (lib) => "./" + trimSlash(lib) |
|
||||
|
|
||||
const trimSlash = (str) => str.replace(/^\/+|\/+$/g, ''); |
|
||||
|
|
||||
@ -1,36 +0,0 @@ |
|||||
import { |
|
||||
find, |
|
||||
isUndefined, |
|
||||
split, |
|
||||
last |
|
||||
} from "lodash/fp"; |
|
||||
|
|
||||
import { $ } from "./core/common"; |
|
||||
|
|
||||
export const initialiseComponent = (componentLibraries, store) => (props, htmlElement) => { |
|
||||
|
|
||||
const _app = { |
|
||||
initialiseComponent: initialiseComponent(componentLibraries, store), |
|
||||
store |
|
||||
}; |
|
||||
|
|
||||
const {componentName, libName} = splitName(props._component); |
|
||||
|
|
||||
new (componentLibraries[libName][componentName])({ |
|
||||
target: htmlElement, |
|
||||
props: {...props, _app} |
|
||||
}); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
const splitName = fullname => { |
|
||||
const componentName = $(fullname, [ |
|
||||
split("/"), |
|
||||
last |
|
||||
]); |
|
||||
|
|
||||
const libName =fullname.substring( |
|
||||
0, fullname.length - componentName.length - 1); |
|
||||
|
|
||||
return {libName, componentName}; |
|
||||
} |
|
||||
@ -0,0 +1,59 @@ |
|||||
|
|
||||
|
export const BB_STATE_INDICATOR = "##bbstate"; |
||||
|
export const BB_STATE_FALLBACK = "##bbstatefallback"; |
||||
|
|
||||
|
export const bindComponent = (store, component) => { |
||||
|
|
||||
|
const newProps = {...component.props}; |
||||
|
const boundProps = []; |
||||
|
|
||||
|
for(let propName in component.props) { |
||||
|
const val = newProps[propName]; |
||||
|
|
||||
|
if(!isState(val)) continue; |
||||
|
|
||||
|
const binding = stateBinding(val); |
||||
|
const fallback = stateFallback(val); |
||||
|
|
||||
|
boundProps.push({ |
||||
|
stateBinding:binding, |
||||
|
fallback, propName |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
if(boundProps.length === 0) return; |
||||
|
|
||||
|
const unsubscribe = store.subscribe(s => { |
||||
|
const newProps = {...component.props}; |
||||
|
|
||||
|
for(let boundProp of boundProps) { |
||||
|
const val = boundValueFromStore( |
||||
|
s, |
||||
|
boundProp.stateBinding, |
||||
|
boundProp.fallback); |
||||
|
|
||||
|
if(val === undefined && newProps[boundProp.propName] !== undefined) { |
||||
|
delete newProps[boundProp.propName]; |
||||
|
} |
||||
|
|
||||
|
if(val !== undefined) { |
||||
|
newProps[boundProp.propName] = val; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
component.$set(newProps); |
||||
|
}); |
||||
|
|
||||
|
return unsubscribe; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const isState = (prop) => prop[BB_STATE_INDICATOR] !== undefined; |
||||
|
const stateBinding = (prop) => prop[BB_STATE_INDICATOR]; |
||||
|
const stateFallback = (prop) => prop[BB_STATE_FALLBACK]; |
||||
|
const boundValueFromStore = (s, binding, fallback) => { |
||||
|
const value = s[binding]; |
||||
|
if(value === undefined) return fallback; |
||||
|
return value; |
||||
|
} |
||||
@ -1,9 +1,9 @@ |
|||||
.root.svelte-1s3350l{height:100%;display:grid;grid-template-columns:[left] 1fr [middle] auto [right] 1fr;grid-template-rows:[top] 1fr [center] auto [bottom] 1fr}.content.svelte-1s3350l{grid-column-start:middle;grid-row-start:center;width:400px}.logo-container.svelte-1s3350l{margin-bottom:20px |
#current_component.svelte-1xqz9vm{height:100%;width:100%} |
||||
}.logo-container.svelte-1s3350l>img.svelte-1s3350l{max-width:100%}.login-button-container.svelte-1s3350l{text-align:right;margin-top:20px} |
.root.svelte-1oto99m{height:100%;display:grid;grid-template-columns:[left] 1fr [middle] auto [right] 1fr;grid-template-rows:[top] 1fr [center] auto [bottom] 1fr}.content.svelte-1oto99m{grid-column-start:middle;grid-row-start:center;width:400px}.logo-container.svelte-1oto99m{margin-bottom:20px |
||||
.label.svelte-98bu7e{grid-column-start:label;padding:5px 10px;vertical-align:middle}.control.svelte-98bu7e{grid-column-start:control;padding:5px 10px}.overflow.svelte-98bu7e{grid-column-start:overflow}.full-width.svelte-98bu7e{width:100%} |
}.logo-container.svelte-1oto99m>img.svelte-1oto99m{max-width:100%}.login-button-container.svelte-1oto99m{text-align:right;margin-top:20px}.incorrect-details-panel.svelte-1oto99m{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-1oto99m{display:grid;grid-template-columns:[label] auto [control] 1fr}.label.svelte-1oto99m{grid-column-start:label;padding:5px 10px;vertical-align:middle}.control.svelte-1oto99m{grid-column-start:control;padding:5px 10px} |
||||
.form-root.svelte-h706oz{display:grid;grid-template-columns:[label] auto [control] auto} |
.root.svelte-10kw8to{display:grid} |
||||
.current.svelte-cgpppc{height:100%;width:100%} |
.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} |
||||
input.svelte-bmvn6x{width:100%}input.svelte-bmvn6x{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%}input.svelte-bmvn6x:disabled{color:#ccc} |
.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%} |
||||
button.svelte-19ku4ig{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}button.svelte-19ku4ig{color:#333;background-color:#f4f4f4;outline:none}button.svelte-19ku4ig:active{background-color:#ddd}button.svelte-19ku4ig:focus{border-color:#666} |
.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 */ |
/*# sourceMappingURL=bundle.css.map */ |
||||
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||||
|
window['##BUDIBASE_APPDEFINITION##'] = {"hierarchy":{"name":"root","type":"root","children":[{"name":"customer","type":"record","fields":[{"name":"name","type":"string","typeOptions":{"maxLength":1000,"values":null,"allowDeclaredValuesOnly":false},"label":"name","getInitialValue":"default","getUndefinedValue":"default"}],"children":[{"name":"invoiceyooo","type":"record","fields":[{"name":"amount","type":"number","typeOptions":{"minValue":99999999999,"maxValue":99999999999,"decimalPlaces":2},"label":"amount","getInitialValue":"default","getUndefinedValue":"default"}],"children":[],"validationRules":[],"nodeId":2,"indexes":[],"allidsShardFactor":1,"collectionName":"invoices","isSingle":false}],"validationRules":[],"nodeId":1,"indexes":[{"name":"customer_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":5}],"allidsShardFactor":64,"collectionName":"customers","isSingle":false}],"pathMaps":[],"indexes":[{"name":"Yeo index","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[1],"nodeId":4},{"name":"everyones_invoices","type":"index","map":"return {...record};","filter":"","indexType":"ancestor","getShardName":"","getSortKey":"record.id","aggregateGroups":[],"allowedRecordNodeIds":[2],"nodeId":6}],"nodeId":0},"componentLibraries":["budibase-standard-components"],"appRootPath":"/testApp2","props":{}} |
||||
@ -1,21 +1,59 @@ |
|||||
<script> |
<script> |
||||
|
|
||||
export let containerClass = ""; |
export let containerClass = ""; |
||||
export let formControls = []; |
export let formControls = []; |
||||
|
|
||||
|
export let _app; |
||||
|
|
||||
|
let htmlElements = {}; |
||||
|
let labels = {}; |
||||
|
|
||||
|
$ : { |
||||
|
let cIndex = 0; |
||||
|
for(let c of formControls) { |
||||
|
labels[cIndex] = c.label; |
||||
|
cIndex++; |
||||
|
} |
||||
|
|
||||
|
if(_app && htmlElements) { |
||||
|
for(let el in htmlElements) { |
||||
|
_app.initialiseComponent( |
||||
|
formControls[el].control, |
||||
|
htmlElements[el] |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
</script> |
</script> |
||||
|
|
||||
<div class="form-root {containerClass}"> |
<div class="form-root {containerClass}"> |
||||
{#each formControls as formControl} |
{#each formControls as child, index} |
||||
<formControl /> |
<div class="label">{labels[index]}</div> |
||||
{:else} |
<div class="control" |
||||
<slot/> |
bind:this={htmlElements[index]}> |
||||
|
</div> |
||||
{/each} |
{/each} |
||||
</div> |
</div> |
||||
|
|
||||
<style> |
<style> |
||||
.form-root { |
.form-root { |
||||
display: grid; |
display: grid; |
||||
grid-template-columns: [label] auto [control] auto; /* [overflow] auto;*/ |
grid-template-columns: [label] auto [control] 1fr; /* [overflow] auto;*/ |
||||
|
} |
||||
|
|
||||
|
.label { |
||||
|
grid-column-start: label; |
||||
|
padding: 5px 10px; |
||||
|
vertical-align: middle; |
||||
|
} |
||||
|
.control { |
||||
|
grid-column-start: control; |
||||
|
padding: 5px 10px; |
||||
|
} |
||||
|
.overflow { |
||||
|
grid-column-start: overflow; |
||||
|
} |
||||
|
.full-width { |
||||
|
width: 100%; |
||||
} |
} |
||||
</style> |
</style> |
||||
@ -1,47 +0,0 @@ |
|||||
<script> |
|
||||
|
|
||||
export let containerClass = ""; |
|
||||
export let labelContainerClass = ""; |
|
||||
export let controlContainerClass = ""; |
|
||||
export let label = ""; |
|
||||
export let control; |
|
||||
export let overflowControl; |
|
||||
export let fullWidth = false; |
|
||||
|
|
||||
</script> |
|
||||
|
|
||||
<div class="label {labelContainerClass}">{label}</div> |
|
||||
<div class="control {controlContainerClass}" class:full-width={fullWidth}> |
|
||||
{#if control} |
|
||||
<control /> |
|
||||
{:else} |
|
||||
<slot /> |
|
||||
{/if} |
|
||||
|
|
||||
</div> |
|
||||
<!--div class="overflow"> |
|
||||
{#if overflowControl} |
|
||||
<overflowControl /> |
|
||||
{:else} |
|
||||
<slot name="overflow" /> |
|
||||
{/if} |
|
||||
</div>--> |
|
||||
|
|
||||
<style> |
|
||||
|
|
||||
.label { |
|
||||
grid-column-start: label; |
|
||||
padding: 5px 10px; |
|
||||
vertical-align: middle; |
|
||||
} |
|
||||
.control { |
|
||||
grid-column-start: control; |
|
||||
padding: 5px 10px; |
|
||||
} |
|
||||
.overflow { |
|
||||
grid-column-start: overflow; |
|
||||
} |
|
||||
.full-width { |
|
||||
width: 100%; |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,63 @@ |
|||||
|
<script> |
||||
|
import { onMount } from 'svelte' |
||||
|
import {buildStyle} from "./buildStyle"; |
||||
|
|
||||
|
export let gridTemplateRows =""; |
||||
|
export let gridTemplateColumns =""; |
||||
|
export let children = []; |
||||
|
export let width = "auto"; |
||||
|
export let height = "auto"; |
||||
|
export let containerClass=""; |
||||
|
export let itemContainerClass=""; |
||||
|
|
||||
|
/*"gridColumnStart":"string", |
||||
|
"gridColumnEnd":"string", |
||||
|
"gridRowStart":"string", |
||||
|
"gridRowEnd":"string"*/ |
||||
|
|
||||
|
|
||||
|
export let _app; |
||||
|
|
||||
|
let style=""; |
||||
|
let htmlElements = {}; |
||||
|
|
||||
|
$ : { |
||||
|
if(_app && htmlElements) { |
||||
|
for(let el in htmlElements) { |
||||
|
_app.initialiseComponent( |
||||
|
children[el].control, |
||||
|
htmlElements[el] |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const childStyle = child => |
||||
|
buildStyle({ |
||||
|
"grid-column-start": child.gridColumnStart, |
||||
|
"grid-column-end": child.gridColumnEnd, |
||||
|
"grid-column": child.gridColumn, |
||||
|
"grid-row-start": child.gridRowStart, |
||||
|
"grid-row-end": child.gridRowStart, |
||||
|
"grid-row": child.gridRow, |
||||
|
}); |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<div class="root {containerClass}" |
||||
|
style="width: {width}; height: {height}; grid-template-columns: {gridTemplateColumns}; grid-template-rows: {gridTemplateRows};"> |
||||
|
{#each children as child, index} |
||||
|
<div class="{itemContainerClass}" |
||||
|
style={childStyle(child)} |
||||
|
bind:this={htmlElements[index]}> |
||||
|
</div> |
||||
|
{/each} |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
|
||||
|
.root { |
||||
|
display: grid; |
||||
|
} |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,40 @@ |
|||||
|
import { writable } from "svelte/store"; |
||||
|
import Login from "../Login.svelte"; |
||||
|
import Grid from "../Grid.svelte"; |
||||
|
import Form from "../Form.svelte"; |
||||
|
import Textbox from "../Textbox.svelte"; |
||||
|
import Text from "../Text.svelte"; |
||||
|
import { createApp } from "@budibase/client/src/createApp"; |
||||
|
|
||||
|
export default async () => { |
||||
|
|
||||
|
const componentLibraries = { |
||||
|
components : { |
||||
|
login : Login, |
||||
|
grid : Grid, |
||||
|
form : Form, |
||||
|
textbox : Textbox, |
||||
|
text: Text |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return createApp(componentLibraries); |
||||
|
|
||||
|
const initialiseComponent = (props, htmlElement) => { |
||||
|
|
||||
|
new (components[props._component])({ |
||||
|
target: htmlElement, |
||||
|
props: {...props, _app} |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const store = writable({}); |
||||
|
|
||||
|
const _app = { |
||||
|
initialiseComponent, |
||||
|
store |
||||
|
}; |
||||
|
|
||||
|
return _app; |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
|
||||
|
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" |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
|
||||
|
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" |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
<script> |
||||
|
import {buildStyle} from "./buildStyle"; |
||||
|
|
||||
|
export let value=""; |
||||
|
export let containerClass=""; |
||||
|
|
||||
|
export let background=""; |
||||
|
export let border=""; |
||||
|
export let font=""; |
||||
|
export let display=""; |
||||
|
export let textAlign=""; |
||||
|
export let color=""; |
||||
|
export let padding=""; |
||||
|
|
||||
|
export let _app; |
||||
|
|
||||
|
let style=""; |
||||
|
|
||||
|
|
||||
|
$: { |
||||
|
style=buildStyle({ |
||||
|
border, background, font, |
||||
|
padding, display, color, |
||||
|
"text-align": textAlign |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<div class={containerClass} |
||||
|
style={style}> |
||||
|
{value} |
||||
|
</div> |
||||
@ -0,0 +1,22 @@ |
|||||
|
|
||||
|
const apiCall = (method) => (url, body) => |
||||
|
fetch(url, { |
||||
|
method: method, |
||||
|
headers: { |
||||
|
'Content-Type': 'application/json', |
||||
|
}, |
||||
|
body: body && JSON.stringify(body), |
||||
|
}); |
||||
|
|
||||
|
export const post = apiCall("POST"); |
||||
|
export const get = apiCall("GET"); |
||||
|
export const patch = apiCall("PATCH"); |
||||
|
export const del = apiCall("DELETE"); |
||||
|
|
||||
|
export const authenticate = (username, password) => post("./api/authenticate", { |
||||
|
username, password |
||||
|
}); |
||||
|
|
||||
|
export default { |
||||
|
post, get, patch, delete:del |
||||
|
}; |
||||
@ -0,0 +1,9 @@ |
|||||
|
export const buildStyle = (styles) => { |
||||
|
let str = ""; |
||||
|
for(let s in styles) { |
||||
|
if(styles[s]) { |
||||
|
str += `${s}: ${styles[s]}; ` |
||||
|
} |
||||
|
} |
||||
|
return str; |
||||
|
} |
||||
Loading…
Reference in new issue