forked from tsai/budibase
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##"]; |
|||
|
|||
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 |
|||
}.logo-container.svelte-1s3350l>img.svelte-1s3350l{max-width:100%}.login-button-container.svelte-1s3350l{text-align:right;margin-top: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%} |
|||
.form-root.svelte-h706oz{display:grid;grid-template-columns:[label] auto [control] auto} |
|||
.current.svelte-cgpppc{height:100%;width:100%} |
|||
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} |
|||
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} |
|||
#current_component.svelte-1xqz9vm{height:100%;width:100%} |
|||
.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 |
|||
}.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} |
|||
.root.svelte-10kw8to{display:grid} |
|||
.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} |
|||
.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%} |
|||
.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 */ |
|||
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> |
|||
|
|||
export let containerClass = ""; |
|||
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> |
|||
|
|||
<div class="form-root {containerClass}"> |
|||
{#each formControls as formControl} |
|||
<formControl /> |
|||
{:else} |
|||
<slot/> |
|||
{#each formControls as child, index} |
|||
<div class="label">{labels[index]}</div> |
|||
<div class="control" |
|||
bind:this={htmlElements[index]}> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
<style> |
|||
.form-root { |
|||
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> |
|||
@ -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