forked from tsai/budibase
54 changed files with 1405 additions and 26290 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,8 @@ |
|||
{ |
|||
"javascript.format.enable": false, |
|||
"svelte.plugin.svelte.format.enable": false, |
|||
"html.format.enable": false, |
|||
"json.format.enable": false, |
|||
"editor.trimAutoWhitespace": false, |
|||
"sass.format.deleteWhitespace": false |
|||
} |
|||
|
After Width: | Height: | Size: 400 B |
|
After Width: | Height: | Size: 339 B |
|
After Width: | Height: | Size: 280 B |
|
After Width: | Height: | Size: 415 B |
|
After Width: | Height: | Size: 346 B |
@ -0,0 +1,5 @@ |
|||
export { default as LayoutIcon } from './Layout.svelte'; |
|||
export { default as PaintIcon } from './Paint.svelte'; |
|||
export { default as TerminalIcon } from './Terminal.svelte'; |
|||
export { default as InputIcon } from './Input.svelte'; |
|||
export { default as ImageIcon } from './Image.svelte'; |
|||
@ -0,0 +1,54 @@ |
|||
<script> |
|||
export let meta = []; |
|||
export let size = ''; |
|||
export let values = []; |
|||
</script> |
|||
|
|||
<div class="inputs {size}"> |
|||
{#each meta as { placeholder }, i} |
|||
<input type="number" placeholder="{placeholder}" bind:value={values[i]}/> |
|||
{/each} |
|||
</div> |
|||
|
|||
<style> |
|||
.inputs { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
} |
|||
|
|||
input { |
|||
width: 83px; |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
color: #163057; |
|||
opacity: 0.7; |
|||
padding: 5px 10px; |
|||
box-sizing: border-box; |
|||
border: 1px solid #DBDBDB; |
|||
border-radius: 2px; |
|||
outline: none; |
|||
} |
|||
|
|||
input[type=number]::-webkit-inner-spin-button, |
|||
input[type=number]::-webkit-outer-spin-button { |
|||
-webkit-appearance: none; |
|||
-moz-appearance: none; |
|||
appearance: none; |
|||
margin: 0; |
|||
} |
|||
|
|||
.small > input { |
|||
width: 38px; |
|||
height: 38px; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
text-align: center; |
|||
padding: 0; |
|||
} |
|||
|
|||
.small > input::placeholder { |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,119 @@ |
|||
<script> |
|||
let snippets = []; |
|||
let current_snippet = 0; |
|||
let snippet_text = '' |
|||
let id = 0; |
|||
|
|||
function save_snippet() { |
|||
if (!snippet_text) return; |
|||
|
|||
const index = snippets.findIndex(({ id }) => current_snippet === id); |
|||
|
|||
if (index > -1) { |
|||
snippets[index].snippet = snippet_text; |
|||
} else { |
|||
snippets = snippets.concat({ snippet: snippet_text , id: id }); |
|||
} |
|||
snippet_text = ''; |
|||
current_snippet = ++id; |
|||
} |
|||
|
|||
function edit_snippet(id) { |
|||
const { snippet, id: _id } = snippets.find(({ id:_id }) => _id === id); |
|||
current_snippet = id |
|||
snippet_text = snippet; |
|||
} |
|||
</script> |
|||
|
|||
<h3>Code</h3> |
|||
|
|||
<p>Use the code box below to add snippets of javascript to enhance your webapp</p> |
|||
|
|||
<div class="editor"> |
|||
<textarea class="code" bind:value={snippet_text} /> |
|||
<button on:click={save_snippet}>Save</button> |
|||
</div> |
|||
|
|||
<div class="snippets"> |
|||
<h3>Snippets added</h3> |
|||
{#each snippets as { id, snippet } } |
|||
<div class="snippet"> |
|||
<pre class="code">{snippet}</pre> |
|||
<button on:click={() => edit_snippet(id)}>Edit</button> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
<style> |
|||
h3 { |
|||
text-transform: uppercase; |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
color: #8997ab; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
p { |
|||
font-size: 12px; |
|||
color: #333; |
|||
margin-top: 0; |
|||
} |
|||
|
|||
.editor { |
|||
position: relative; |
|||
} |
|||
|
|||
.code { |
|||
width: 100%; |
|||
outline: none; |
|||
border: none; |
|||
background: #173157; |
|||
border-radius: 5px; |
|||
box-sizing: border-box; |
|||
white-space: pre; |
|||
color: #eee; |
|||
padding: 10px; |
|||
font-family: monospace; |
|||
overflow-y: scroll; |
|||
} |
|||
|
|||
.editor textarea { |
|||
resize: none; |
|||
height: 150px; |
|||
} |
|||
|
|||
button { |
|||
position: absolute; |
|||
box-shadow: 0 0 black; |
|||
color: #eee; |
|||
right: 5px; |
|||
bottom: 10px; |
|||
background: none; |
|||
border: none; |
|||
text-transform: uppercase; |
|||
font-size: 9px; |
|||
font-weight: 600; |
|||
outline: none; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.snippets { |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
.snippet { |
|||
position: relative; |
|||
margin-top: 5px; |
|||
} |
|||
|
|||
.snippet pre { |
|||
background: #f9f9f9; |
|||
color: #333; |
|||
max-height: 150px; |
|||
} |
|||
|
|||
.snippet button { |
|||
color: #ccc; |
|||
} |
|||
|
|||
</style> |
|||
@ -0,0 +1,150 @@ |
|||
<script> |
|||
|
|||
import PropsView from "./PropsView.svelte"; |
|||
import { store } from "../builderStore"; |
|||
import { isRootComponent } from "./pagesParsing/searchComponents"; |
|||
import IconButton from "../common/IconButton.svelte"; |
|||
import Textbox from "../common/Textbox.svelte"; |
|||
import { pipe } from "../common/core"; |
|||
import { |
|||
getScreenInfo |
|||
} from "./pagesParsing/createProps"; |
|||
import { LayoutIcon, PaintIcon, TerminalIcon } from '../common/Icons/'; |
|||
import CodeEditor from './CodeEditor.svelte'; |
|||
import LayoutEditor from './LayoutEditor.svelte'; |
|||
|
|||
import { |
|||
cloneDeep, |
|||
join, |
|||
split, |
|||
map, |
|||
keys, |
|||
isUndefined, |
|||
last |
|||
} from "lodash/fp"; |
|||
import { assign } from "lodash"; |
|||
|
|||
let component; |
|||
let name = ""; |
|||
let description = ""; |
|||
let tagsString = ""; |
|||
let nameInvalid = ""; |
|||
let componentInfo = {}; |
|||
let modalElement |
|||
let propsValidationErrors = []; |
|||
let originalName=""; |
|||
let components; |
|||
let ignoreStore = false; |
|||
|
|||
// $: shortName = last(name.split("/")); |
|||
|
|||
store.subscribe(s => { |
|||
if(ignoreStore) return; |
|||
component = s.currentComponentInfo; |
|||
if(!component) return; |
|||
originalName = component.name; |
|||
name = component.name; |
|||
description = component.description; |
|||
tagsString = join(", ")(component.tags); |
|||
componentInfo = s.currentComponentInfo; |
|||
components = s.components; |
|||
}); |
|||
|
|||
const onPropsChanged = store.updateComponentProp; |
|||
|
|||
let current_view = 'props'; |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<ul> |
|||
<li> |
|||
<button class:selected={current_view === 'props'} on:click={() => current_view = 'props'}> |
|||
<PaintIcon /> |
|||
</button> |
|||
</li> |
|||
<li> |
|||
<button class:selected={current_view === 'layout'} on:click={() => current_view = 'layout'}> |
|||
<LayoutIcon /> |
|||
</button> |
|||
</li> |
|||
<li> |
|||
<button class:selected={current_view === 'code'} on:click={() => current_view = 'code'}> |
|||
<TerminalIcon /> |
|||
</button> |
|||
</li> |
|||
</ul> |
|||
|
|||
{#if !componentInfo.component} |
|||
<div class="component-props-container"> |
|||
|
|||
{#if current_view === 'props'} |
|||
<PropsView {componentInfo} {components} {onPropsChanged} /> |
|||
{:else if current_view === 'layout'} |
|||
<LayoutEditor /> |
|||
{:else} |
|||
<CodeEditor /> |
|||
{/if} |
|||
|
|||
</div> |
|||
{:else} |
|||
<h1> This is a screen, this will be dealt with later</h1> |
|||
{/if} |
|||
|
|||
</div> |
|||
|
|||
|
|||
<style> |
|||
|
|||
.root { |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
} |
|||
|
|||
.title > div:nth-child(1) { |
|||
grid-column-start: name; |
|||
color: var(--secondary100); |
|||
} |
|||
|
|||
.title > div:nth-child(2) { |
|||
grid-column-start: actions; |
|||
} |
|||
|
|||
.component-props-container { |
|||
margin-top: 10px; |
|||
flex: 1 1 auto; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
ul { |
|||
list-style: none; |
|||
display: flex; |
|||
padding: 0; |
|||
} |
|||
|
|||
li { |
|||
margin-right: 20px; |
|||
background: none; |
|||
border-radius: 5px; |
|||
width: 48px; |
|||
height: 48px; |
|||
} |
|||
|
|||
li button { |
|||
width: 100%; |
|||
height: 100%; |
|||
background: none; |
|||
border: none; |
|||
border-radius: 5px; |
|||
padding: 12px; |
|||
outline: none; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.selected { |
|||
color: var(--button-text); |
|||
background: var(--background-button)!important; |
|||
} |
|||
|
|||
</style> |
|||
@ -0,0 +1,24 @@ |
|||
<script> |
|||
import { last } from "lodash/fp"; |
|||
import { pipe } from "../common/core"; |
|||
|
|||
export let components = []; |
|||
export let onSelect = () => {}; |
|||
|
|||
const capitalise = s => s.substring(0,1).toUpperCase() + s.substring(1); |
|||
const get_name = s => last(s.split('/')); |
|||
const get_capitalised_name = name => pipe(name, [get_name,capitalise]); |
|||
</script> |
|||
|
|||
{#each components as component} |
|||
<ul> |
|||
<li on:click|stopPropagation={() => onSelect(component)}> |
|||
{get_capitalised_name(component._component)} |
|||
|
|||
{#if component._children} |
|||
<svelte:self components={component._children}/> |
|||
{/if} |
|||
</li> |
|||
|
|||
</ul> |
|||
{/each} |
|||
@ -0,0 +1,116 @@ |
|||
<script> |
|||
import InputGroup from '../common/Inputs/InputGroup.svelte'; |
|||
|
|||
let grid_values = ['', '', '', '']; |
|||
let column_values = ['', '']; |
|||
let row_values = ['', '']; |
|||
let gap_values = ['']; |
|||
let margin_values = ['', '', '', '']; |
|||
let padding_values = ['', '', '', '']; |
|||
let zindex_values = ['']; |
|||
|
|||
const tbrl = [ |
|||
{ placeholder: 'T' }, |
|||
{ placeholder: 'R' }, |
|||
{ placeholder: 'B' }, |
|||
{ placeholder: 'L' } |
|||
]; |
|||
|
|||
const se = [ |
|||
{ placeholder: 'START' }, |
|||
{ placeholder: 'END' }, |
|||
] |
|||
|
|||
const single = [{ placeholder: '' }]; |
|||
</script> |
|||
|
|||
|
|||
<h3>Layout</h3> |
|||
|
|||
<h4>Positioning</h4> |
|||
|
|||
<div class="layout-pos"> |
|||
<div class="grid"> |
|||
<h5>Grid Area:</h5> |
|||
<InputGroup meta={tbrl} bind:values={grid_values} size="small"/> |
|||
</div> |
|||
|
|||
<div class="grid"> |
|||
<h5>Column:</h5> |
|||
<InputGroup meta={se} bind:values={column_values} /> |
|||
</div> |
|||
|
|||
<div class="grid"> |
|||
<h5>Row:</h5> |
|||
<InputGroup meta={se} bind:values={row_values} /> |
|||
</div> |
|||
|
|||
<div class="grid"> |
|||
<h5>Gap:</h5> |
|||
<InputGroup meta={single} bind:values={gap_values} /> |
|||
</div> |
|||
</div> |
|||
|
|||
<h4>Spacing</h4> |
|||
|
|||
<div class="layout-spacing"> |
|||
<div class="grid"> |
|||
<h5>Margin:</h5> |
|||
<InputGroup meta={tbrl} bind:values={margin_values} size="small"/> |
|||
</div> |
|||
|
|||
<div class="grid"> |
|||
<h5>Padding:</h5> |
|||
<InputGroup meta={tbrl} bind:values={padding_values} size="small"/> |
|||
</div> |
|||
</div> |
|||
|
|||
<h4>Z-Index</h4> |
|||
|
|||
<div class="layout-layer"> |
|||
<div class="grid"> |
|||
<h5>Z-Index:</h5> |
|||
<InputGroup meta={single} bind:values={zindex_values}/> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
h3 { |
|||
text-transform: uppercase; |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
color: #8997ab; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
h4 { |
|||
text-transform: uppercase; |
|||
font-size: 10px; |
|||
font-weight: 700; |
|||
color: #163057; |
|||
opacity: 0.3; |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
h5 { |
|||
font-size: 12px; |
|||
font-weight: 700; |
|||
color: #163057; |
|||
opacity: 0.6; |
|||
padding-top: 12px; |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
div > div { |
|||
display: grid; |
|||
grid-template-rows: 1fr; |
|||
grid-gap: 10px; |
|||
height: 40px; |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
.grid { |
|||
grid-template-columns: 70px 1fr; |
|||
} |
|||
|
|||
</style> |
|||
@ -1,143 +0,0 @@ |
|||
import { types } from "./types"; |
|||
import { |
|||
createProps, arrayElementComponentName |
|||
} from "./createProps"; |
|||
import { isString } from "util"; |
|||
import { |
|||
includes, filter, map, keys, |
|||
flatten, flattenDeep, each, |
|||
indexOf, isUndefined |
|||
} from "lodash/fp"; |
|||
import { common } from "../../../../core/src"; |
|||
import { |
|||
isBinding |
|||
} from "../../common/binding"; |
|||
|
|||
const pipe = common.$; |
|||
|
|||
const makeError = (errors, propName, stack) => (message) => |
|||
errors.push({ |
|||
stack, |
|||
propName, |
|||
error:message}); |
|||
|
|||
export const recursivelyValidate = (rootProps, getComponent, stack=[]) => { |
|||
|
|||
if(!rootProps._component) { |
|||
const errs = []; |
|||
makeError(errs, "_component", stack)("Component is not set"); |
|||
return errs; |
|||
// this would break everything else anyway
|
|||
} |
|||
|
|||
const componentDef = getComponent( |
|||
rootProps._component); |
|||
|
|||
|
|||
const errors = validateProps( |
|||
componentDef, |
|||
rootProps, |
|||
stack, |
|||
true); |
|||
|
|||
const validateChildren = (_props, _stack) => |
|||
!_props._children |
|||
? [] |
|||
: pipe(_props._children, [ |
|||
map(child => recursivelyValidate( |
|||
child, |
|||
getComponent, |
|||
[..._stack, _props._children.indexOf(child)])) |
|||
]); |
|||
|
|||
const childErrors = validateChildren( |
|||
rootProps, stack); |
|||
|
|||
return flattenDeep([errors, ...childErrors]); |
|||
} |
|||
|
|||
const expandPropDef = propDef => |
|||
isString(propDef) |
|||
? types[propDef].defaultDefinition() |
|||
: propDef; |
|||
|
|||
|
|||
|
|||
export const validateProps = (componentDefinition, props, stack=[], isFinal=true) => { |
|||
|
|||
const errors = []; |
|||
|
|||
if(isFinal && !props._component) { |
|||
makeError(errors, "_component", stack)("Component is not set"); |
|||
return errors; |
|||
// this would break everything else anyway
|
|||
} |
|||
|
|||
const propsDefinition = componentDefinition.props; |
|||
|
|||
for(let propDefName in props) { |
|||
|
|||
if(propDefName === "_component") continue; |
|||
if(propDefName === "_children") continue; |
|||
if(propDefName === "_layout") continue; |
|||
|
|||
const propDef = expandPropDef(propsDefinition[propDefName]); |
|||
|
|||
const type = types[propDef.type]; |
|||
|
|||
const error = makeError(errors, propDefName, stack); |
|||
|
|||
const propValue = props[propDefName]; |
|||
|
|||
// component declarations dont need to define al props.
|
|||
if(!isFinal && isUndefined(propValue)) continue; |
|||
|
|||
if(isFinal && propDef.required && propValue) { |
|||
error(`Property ${propDefName} is required`); |
|||
continue; |
|||
} |
|||
|
|||
if(isBinding(propValue)) { |
|||
if(propDef.type === "event") { |
|||
error(`Cannot apply binding to type ${propDef.type}`); |
|||
continue; |
|||
} |
|||
} |
|||
else if(!type.isOfType(propValue)) { |
|||
error(`Property ${propDefName} is not of type ${propDef.type}. Actual value ${propValue}`) |
|||
continue; |
|||
} |
|||
|
|||
|
|||
if(propDef.type === "options" |
|||
&& propValue |
|||
&& !isBinding(propValue) |
|||
&& !includes(propValue)(propDef.options)) { |
|||
error(`Property ${propDefName} is not one of allowed options. Acutal value is ${propValue}`); |
|||
} |
|||
|
|||
} |
|||
|
|||
return errors; |
|||
} |
|||
|
|||
export const validateComponentDefinition = (componentDefinition) => { |
|||
const { errors } = createProps(componentDefinition); |
|||
|
|||
const propDefinitions = expandPropDef(componentDefinition.props); |
|||
|
|||
pipe(propDefinitions, [ |
|||
keys, |
|||
map(k => ({ |
|||
propDef:propDefinitions[k], |
|||
propName:k |
|||
})), |
|||
filter(d => d.propDef.type === "options" |
|||
&& (!d.propDef.options || d.propDef.options.length === 0)), |
|||
each(d => makeError(errors, d.propName)(`${d.propName} does not have any options`)) |
|||
]); |
|||
|
|||
return errors; |
|||
|
|||
} |
|||
|
|||
@ -1,232 +0,0 @@ |
|||
import { |
|||
validateComponentDefinition, |
|||
validateProps, |
|||
recursivelyValidate |
|||
} from "../src/userInterface/pagesParsing/validateProps"; |
|||
import { createProps } from "../src/userInterface/pagesParsing/createProps"; |
|||
import { |
|||
setBinding |
|||
} from "../src/common/binding"; |
|||
|
|||
// not that allot of this functionality is covered
|
|||
// in createDefaultProps - as validate props uses that.
|
|||
|
|||
describe("validateComponentDefinition", () => { |
|||
|
|||
|
|||
it("should return error when no options for options field", () => { |
|||
|
|||
const compDef = { |
|||
name:"some_component", |
|||
props: { |
|||
size: { |
|||
type: "options", |
|||
options: [] |
|||
} |
|||
} |
|||
}; |
|||
|
|||
const errors = validateComponentDefinition(compDef); |
|||
|
|||
expect(errors.length).toEqual(1); |
|||
expect(errors[0].propName).toBe("size"); |
|||
|
|||
}); |
|||
|
|||
it("should not return error when options field has options", () => { |
|||
|
|||
const compDef = { |
|||
name: "some_component", |
|||
props: { |
|||
size: { |
|||
type: "options", |
|||
options: ["small", "medium", "large"] |
|||
} |
|||
} |
|||
}; |
|||
|
|||
const errors = validateComponentDefinition(compDef); |
|||
|
|||
expect(errors).toEqual([]); |
|||
|
|||
}); |
|||
|
|||
}); |
|||
|
|||
const validComponentDef = { |
|||
name: "some_component", |
|||
props: { |
|||
size: { |
|||
type: "options", |
|||
options: ["small", "medium", "large"], |
|||
default:"medium" |
|||
}, |
|||
rowCount : "number" |
|||
} |
|||
}; |
|||
|
|||
const childComponentDef = { |
|||
name: "child_component", |
|||
props: { |
|||
width: "number", |
|||
units: { |
|||
type: "string", |
|||
default: "px" |
|||
} |
|||
} |
|||
}; |
|||
|
|||
const validProps = () => { |
|||
|
|||
const { props } = createProps(validComponentDef); |
|||
props._children.push( |
|||
createProps(childComponentDef)); |
|||
return props; |
|||
} |
|||
|
|||
describe("validateProps", () => { |
|||
|
|||
it("should have no errors with a big list of valid props", () => { |
|||
|
|||
const errors = validateProps(validComponentDef, validProps(), [], true); |
|||
expect(errors).toEqual([]); |
|||
|
|||
}); |
|||
|
|||
it("should return error with invalid value", () => { |
|||
|
|||
const props = validProps(); |
|||
props.rowCount = "1"; |
|||
const errors = validateProps(validComponentDef, props, [], true); |
|||
expect(errors.length).toEqual(1); |
|||
expect(errors[0].propName).toBe("rowCount"); |
|||
|
|||
}); |
|||
|
|||
it("should return error with invalid option", () => { |
|||
|
|||
const props = validProps(); |
|||
props.size = "really_small"; |
|||
const errors = validateProps(validComponentDef, props, [], true); |
|||
expect(errors.length).toEqual(1); |
|||
expect(errors[0].propName).toBe("size"); |
|||
|
|||
}); |
|||
|
|||
it("should not return error when has binding", () => { |
|||
const props = validProps(); |
|||
props._children[0].width = setBinding({path:"some_path"}); |
|||
props.size = setBinding({path:"other path", fallback:"small"}); |
|||
const errors = validateProps(validComponentDef, props, [], true); |
|||
expect(errors.length).toEqual(0); |
|||
}); |
|||
}); |
|||
|
|||
describe("recursivelyValidateProps", () => { |
|||
|
|||
const rootComponent = { |
|||
name: "rootComponent", |
|||
children: true, |
|||
props: { |
|||
width: "number" |
|||
} |
|||
}; |
|||
|
|||
const todoListComponent = { |
|||
name: "todoListComponent", |
|||
props:{ |
|||
showTitle: "bool" |
|||
} |
|||
}; |
|||
|
|||
const headerComponent = { |
|||
name: "headerComponent", |
|||
props: { |
|||
text: "string" |
|||
} |
|||
}; |
|||
|
|||
const iconComponent = { |
|||
name: "iconComponent", |
|||
props: { |
|||
iconName: "string" |
|||
} |
|||
}; |
|||
|
|||
const navItemComponent = { |
|||
name: "navItemComponent", |
|||
props: { |
|||
text: "string" |
|||
} |
|||
}; |
|||
|
|||
const getComponent = name => ({ |
|||
rootComponent, |
|||
todoListComponent, |
|||
headerComponent, |
|||
iconComponent, |
|||
navItemComponent |
|||
})[name]; |
|||
|
|||
const rootProps = () => ({ |
|||
_component: "rootComponent", |
|||
width: 100, |
|||
_children: [{ |
|||
_component: "todoListComponent", |
|||
showTitle: true, |
|||
_children : [ |
|||
{ |
|||
_component: "navItemComponent", |
|||
text: "todos" |
|||
}, |
|||
{ |
|||
_component: "headerComponent", |
|||
text: "Your todo list" |
|||
}, |
|||
{ |
|||
_component: "iconComponent", |
|||
iconName: "fa fa-list" |
|||
}, |
|||
{ |
|||
_component: "iconComponent", |
|||
iconName:"fa fa-cog" |
|||
} |
|||
] |
|||
}] |
|||
}); |
|||
|
|||
it("should return no errors for valid structure", () => { |
|||
const result = recursivelyValidate( |
|||
rootProps(), |
|||
getComponent); |
|||
|
|||
expect(result).toEqual([]); |
|||
}); |
|||
|
|||
it("should return error on root component", () => { |
|||
const root = rootProps(); |
|||
root.width = "yeeeoooo"; |
|||
const result = recursivelyValidate(root, getComponent); |
|||
expect(result.length).toBe(1); |
|||
expect(result[0].propName).toBe("width"); |
|||
}); |
|||
|
|||
it("should return error on first nested child component", () => { |
|||
const root = rootProps(); |
|||
root._children[0].showTitle = "yeeeoooo"; |
|||
const result = recursivelyValidate(root, getComponent); |
|||
expect(result.length).toBe(1); |
|||
expect(result[0].stack).toEqual([0]); |
|||
expect(result[0].propName).toBe("showTitle"); |
|||
}); |
|||
|
|||
it("should return error on second nested child component", () => { |
|||
const root = rootProps(); |
|||
root._children[0]._children[0].text = false; |
|||
const result = recursivelyValidate(root, getComponent); |
|||
expect(result.length).toBe(1); |
|||
expect(result[0].stack).toEqual([0,0]); |
|||
expect(result[0].propName).toBe("text"); |
|||
}); |
|||
|
|||
}); |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,12 +1,20 @@ |
|||
{ |
|||
"_lib": "./dist/index.js", |
|||
"h1": { |
|||
"name": "H1", |
|||
"description": "An HTML H1 tag", |
|||
"props" : { |
|||
"text": "string", |
|||
"className":"string" |
|||
}, |
|||
"tags": [] |
|||
} |
|||
} |
|||
"_lib": "./dist/index.js", |
|||
"h1": { |
|||
"name": "H1", |
|||
"description": "An HTML H1 tag", |
|||
"props": { |
|||
"text": "string", |
|||
"className": "string" |
|||
}, |
|||
"tags": [] |
|||
}, |
|||
"button": { |
|||
"name": "Button", |
|||
"description": "A button", |
|||
"props": { |
|||
"raised": "bool" |
|||
}, |
|||
"tags": [] |
|||
} |
|||
} |
|||
|
|||
File diff suppressed because one or more lines are too long
@ -1,20 +1,37 @@ |
|||
import svelte from 'rollup-plugin-svelte'; |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import svelte from "rollup-plugin-svelte"; |
|||
import postcss from "rollup-plugin-postcss"; |
|||
import resolve from "rollup-plugin-node-resolve"; |
|||
import path from "path"; |
|||
|
|||
const postcssOptions = () => ({ |
|||
extensions: [".scss", ".sass"], |
|||
extract: false, |
|||
minimize: true, |
|||
use: [ |
|||
[ |
|||
"sass", |
|||
{ |
|||
includePaths: ["./node_modules"] |
|||
} |
|||
] |
|||
] |
|||
}); |
|||
|
|||
export default { |
|||
input: 'src/index.js', |
|||
output: [ |
|||
{ |
|||
file: "dist/index.js", |
|||
format: 'esm', |
|||
name:"budibaseStandardComponents", |
|||
sourcemap: "inline" |
|||
} |
|||
], |
|||
plugins: [ |
|||
svelte({ |
|||
hydratable:true |
|||
}), |
|||
resolve() |
|||
] |
|||
input: "src/index.js", |
|||
output: [ |
|||
{ |
|||
file: "dist/index.js", |
|||
format: "esm", |
|||
name: "budibaseStandardComponents", |
|||
sourcemap: "inline" |
|||
} |
|||
], |
|||
plugins: [ |
|||
svelte({ |
|||
hydratable: true |
|||
}), |
|||
resolve(), |
|||
postcss(postcssOptions()) |
|||
] |
|||
}; |
|||
|
|||
@ -1,93 +1,149 @@ |
|||
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'; |
|||
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"; |
|||
import alias from "rollup-plugin-alias"; |
|||
import path from "path"; |
|||
|
|||
const aliases = { |
|||
resolve: [".js", ".svelte"], |
|||
entries: [ |
|||
{ find: "@BBMD", replacement: path.resolve(__dirname, "dist/index.js") } |
|||
] |
|||
}; |
|||
|
|||
const production = !process.env.ROLLUP_WATCH; |
|||
|
|||
const lodash_fp_exports = [ |
|||
"find", "isUndefined", "split", "max", |
|||
"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"]; |
|||
"find", |
|||
"isUndefined", |
|||
"split", |
|||
"max", |
|||
"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", "find","each", |
|||
"tail", "findIndex", "startsWith", |
|||
"dropRight", "takeRight", |
|||
"trim", "split", "replace", |
|||
"merge", "assign"]; |
|||
"flow", |
|||
"head", |
|||
"find", |
|||
"each", |
|||
"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", "bcryptjs" |
|||
"lodash", |
|||
"lodash/fp", |
|||
"date-fns", |
|||
"lunr", |
|||
"safe-buffer", |
|||
"shortid", |
|||
"@nx-js/compiler-util", |
|||
"bcryptjs" |
|||
]; |
|||
|
|||
export default { |
|||
input: 'src/Test/testMain.js', |
|||
output: { |
|||
sourcemap: true, |
|||
format: 'iife', |
|||
name: 'app', |
|||
file: 'public/bundle.js', |
|||
globals: { |
|||
"crypto": "crypto" |
|||
} |
|||
}, |
|||
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 |
|||
}), |
|||
input: "src/Test/testMain.js", |
|||
output: { |
|||
sourcemap: true, |
|||
format: "iife", |
|||
name: "app", |
|||
file: "public/bundle.js", |
|||
globals: { |
|||
crypto: "crypto" |
|||
} |
|||
}, |
|||
plugins: [ |
|||
alias(aliases), |
|||
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); |
|||
}, |
|||
preferBuiltins: true |
|||
}), |
|||
commonjs({ |
|||
namedExports: { |
|||
"lodash/fp": lodash_fp_exports, |
|||
"lodash":lodash_exports, |
|||
"shortid": ["generate"] |
|||
} |
|||
}), |
|||
json(), |
|||
// 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) |
|||
); |
|||
}, |
|||
preferBuiltins: true |
|||
}), |
|||
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'), |
|||
// 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 |
|||
} |
|||
// If we're building for production (npm run build
|
|||
// instead of npm run dev), minify
|
|||
production && terser() |
|||
], |
|||
watch: { |
|||
clearScreen: false |
|||
} |
|||
}; |
|||
|
|||
@ -0,0 +1,12 @@ |
|||
<script> |
|||
import "@material/button/mdc-button.scss"; |
|||
export let raised = false; |
|||
|
|||
let c = raised ? "mdc-button mdc-button--raised" : "mdc-button"; |
|||
</script> |
|||
|
|||
<button class={c}> |
|||
<div class="mdc-button__ripple" /> |
|||
|
|||
<span class="mdc-button__label">Button</span> |
|||
</button> |
|||
@ -1,40 +1,35 @@ |
|||
<script> |
|||
import createApp from "./createApp"; |
|||
import { props } from "./props"; |
|||
import createApp from "./createApp"; |
|||
import { props } from "./props"; |
|||
|
|||
let _bb; |
|||
let _bb; |
|||
|
|||
const _appPromise = createApp(); |
|||
_appPromise.then(a => _bb = a); |
|||
const _appPromise = createApp(); |
|||
_appPromise.then(a => (_bb = a)); |
|||
|
|||
const testProps = props.justAnH1; |
|||
const testProps = props.justAnH1; |
|||
const button = props.button; |
|||
|
|||
let currentComponent; |
|||
let currentComponent; |
|||
|
|||
$: { |
|||
if(_bb && currentComponent) { |
|||
_bb.hydrateChildren([testProps], currentComponent); |
|||
$: { |
|||
if (_bb && currentComponent) { |
|||
_bb.hydrateChildren([testProps, button], currentComponent); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
</script> |
|||
|
|||
{#await _appPromise} |
|||
loading |
|||
{:then _bb} |
|||
|
|||
<div id="current_component" bind:this={currentComponent}> |
|||
</div> |
|||
|
|||
{/await} |
|||
|
|||
|
|||
<style> |
|||
#current_component { |
|||
#current_component { |
|||
height: 100%; |
|||
width: 100%; |
|||
} |
|||
} |
|||
</style> |
|||
|
|||
{#await _appPromise} |
|||
loading |
|||
{:then _bb} |
|||
|
|||
<div id="current_component" bind:this={currentComponent} /> |
|||
|
|||
{/await} |
|||
|
|||
@ -1,10 +1,12 @@ |
|||
|
|||
export const props = { |
|||
|
|||
justAnH1 : { |
|||
_component:"@budibase/materialdesign-components/h1", |
|||
_children: [], |
|||
text: "This is a Header" |
|||
} |
|||
|
|||
} |
|||
justAnH1: { |
|||
_component: "@budibase/materialdesign-components/h1", |
|||
_children: [], |
|||
text: "This is a Header" |
|||
}, |
|||
button: { |
|||
_component: "@budibase/materialdesign-components/button", |
|||
_children: [], |
|||
raised: true |
|||
} |
|||
}; |
|||
|
|||
@ -1,3 +1,4 @@ |
|||
import h1 from "../H1.svelte"; |
|||
import { button } from "@BBMD"; |
|||
|
|||
export default {h1}; |
|||
export default { h1, button }; |
|||
|
|||
@ -1,2 +1,3 @@ |
|||
export {default as h1} from "./H1.svelte"; |
|||
export { default as h1 } from "./H1.svelte"; |
|||
export { default as button } from "./Button.svelte"; |
|||
|
|||
|
|||
@ -1,3 +1,8 @@ |
|||
myapps/ |
|||
config.js |
|||
<<<<<<< HEAD |
|||
/builder/* |
|||
!/builder/assets/ |
|||
======= |
|||
builder/ |
|||
>>>>>>> ee5a4e8c962b29242152cbbd8065d8f3ccf65eaf |
|||
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue