data components

This commit is contained in:
Martin McKeaveney 2020-05-07 22:15:09 +01:00
parent 96a1bc52de
commit db69673a9d
21 changed files with 292 additions and 150 deletions

View File

@ -46,10 +46,4 @@
background: #fafafa;
border-radius: 0.5rem;
}
select {
width: 100%;
}
option {
padding: 10px;
}
</style>

View File

@ -1,6 +1,5 @@
<script>
import PropsView from "./PropsView.svelte"
import StateBindingControl from "./StateBindingControl.svelte"
import { store } from "builderStore"
import IconButton from "components/common/IconButton.svelte"
import {
@ -30,7 +29,6 @@
? getProps($store.currentPreviewItem, ["name", "favicon"])
: getProps($store.currentPreviewItem, ["name", "description", "route"])
const onPropChanged = store.setComponentProp
const onStyleChanged = store.setComponentStyle
function getProps(obj, keys) {
@ -89,14 +87,14 @@
on:input={({ target }) => store.setMetadataProp(k, target.value)} />
</div>
{/each}
<PropsView {component} {components} {onPropChanged} />
<PropsView {component} {components} />
{:else}
<PropsView {component} {components} {onPropChanged} />
<PropsView {component} {components} />
{/if}
{:else if current_view === 'layout'}
<LayoutEditor {onStyleChanged} {component} />
{:else if current_view === 'events'}
<EventsEditor {component} {components} {onPropChanged} />
<EventsEditor {component} {components} />
{/if}
<CodeEditor

View File

@ -1,4 +1,5 @@
<script>
import { store } from "builderStore";
import Modal from "../../common/Modal.svelte"
import HandlerSelector from "./HandlerSelector.svelte"
import IconButton from "../../common/IconButton.svelte"
@ -14,7 +15,6 @@
export let eventOptions = []
export let open
export let onClose
export let onPropChanged
let eventType = ""
let draftEventHandler = { parameters: [] }
@ -52,12 +52,12 @@
}
const deleteEvent = () => {
onPropChanged(eventType, [])
store.setComponentProp(eventType, [])
closeModal()
}
const saveEventData = () => {
onPropChanged(eventType, eventData.handlers)
store.setComponentProp(eventType, eventData.handlers)
closeModal()
}
</script>

View File

@ -25,7 +25,6 @@
export const EVENT_TYPE = "event"
export let component
export let onPropChanged = () => {}
export let components
let modalOpen = false
@ -74,7 +73,6 @@
</form>
</div>
<EventEditorModal
{onPropChanged}
open={modalOpen}
onClose={closeModal}
eventOptions={events}

View File

@ -3,7 +3,6 @@
import PlusButton from "components/common/PlusButton.svelte"
import Select from "components/common/Select.svelte"
import StateBindingCascader from "./StateBindingCascader.svelte"
import StateBindingControl from "../StateBindingControl.svelte"
import { find, map, keys, reduce, keyBy } from "lodash/fp"
import { pipe } from "components/common/core"
import {

View File

@ -3,7 +3,6 @@
import PlusButton from "components/common/PlusButton.svelte"
import Select from "components/common/Select.svelte"
import Input from "components/common/Input.svelte"
import StateBindingControl from "../StateBindingControl.svelte"
import { find, map, keys, reduce, keyBy } from "lodash/fp"
import { pipe } from "components/common/core"
import {

View File

@ -1,18 +1,14 @@
<script>
import { store } from "builderStore";
import Checkbox from "../common/Checkbox.svelte"
import Textbox from "../common/Textbox.svelte"
import Dropdown from "../common/Dropdown.svelte"
import StateBindingControl from "./StateBindingControl.svelte"
export let setProp = () => {}
export let index
export let prop_name
export let prop_value
export let prop_definition = {}
const setComponentProp = props => {
setProp(propDef.____name, props)
}
</script>
<div class="root">
@ -23,7 +19,7 @@
type={prop_definition.type || prop_definition}
options={prop_definition.options}
styleBindingProperty={prop_definition.styleBindingProperty}
onChanged={v => setProp(prop_name, v)} />
onChanged={v => store.setComponentProp(prop_name, v)} />
{/if}
</div>

View File

@ -6,17 +6,12 @@
import IconButton from "../common/IconButton.svelte"
export let component
export let onPropChanged = () => {}
export let components
let errors = []
const props_to_ignore = ["_component", "_children", "_styles", "_code", "_id"]
$: componentDef = components[component._component]
let setProp = (name, value) => {
onPropChanged(name, value)
}
</script>
<div class="root">
@ -27,7 +22,6 @@
{#if prop_def !== 'event'}
<div class="prop-container">
<PropControl
{setProp}
{prop_name}
prop_value={component[prop_name]}
prop_definition={prop_def}

View File

@ -13,6 +13,7 @@
export let styleBindingProperty = ""
$: bindOptionToStyle = !!styleBindingProperty
</script>
<div class="unbound-container">
@ -26,10 +27,12 @@
{:else if type === 'models'}
<select
class="uk-select uk-form-small"
{value}
on:change={ev => onChanged(ev.target.value)}>
bind:value
on:change={() => {
onChanged(value)
}}>
{#each $backendUiStore.models || [] as option}
<option value={`all_${option._id}`}>{option.name}</option>
<option value={option}>{option.name}</option>
{/each}
</select>
{:else if type === 'options' || type === 'models'}

View File

@ -189,6 +189,22 @@ export default {
commonProps: {},
children: [],
},
{
name: "Chart",
_component: "@budibase/standard-components/datachart",
description: "Shiny chart",
icon: "ri-bar-chart-line",
commonProps: {},
children: [],
},
{
name: "List",
_component: "@budibase/standard-components/datalist",
description: "Shiny list",
icon: "ri-file-list-line",
commonProps: {},
children: [],
},
],
},
],

View File

@ -29,9 +29,12 @@ export const loadBudibase = async opts => {
// let { appRootPath } = frontendDefinition;
// appRootPath = appRootPath === "" ? "" : "/" + trimSlash(appRootPatl)
const componentLibraryModules = opts.componentLibraries || {}
const componentLibraryModules = opts && opts.componentLibraries || {}
const libraries = frontendDefinition.libraries || []
const libraries = frontendDefinition.libraries || [
"@budibase/materialdesign-components",
"@budibase/standard-components"
]
for (let library of libraries) {
// fetch the JavaScript for the component libraries from the server

View File

@ -45,7 +45,10 @@ exports.save = async function(ctx) {
// record: record,
// })
ctx.body = record
ctx.body = {
...record,
...response
}
ctx.status = 200
ctx.message = `${model.name} created successfully`
}

View File

@ -276,6 +276,45 @@
"model": "models"
}
},
"datalist": {
"description": "A configurable data list that attaches to your backend models.",
"data": true,
"props": {
"model": "models",
"layout": {
"type": "options",
"default": "list",
"options": [
"list",
"grid"
]
}
}
},
"datachart": {
"description": "shiny chart",
"data": true,
"props": {
"model": "models",
"type": {
"type": "options",
"default": "column2d",
"options": [
"column3d",
"line",
"area2d",
"bar2d",
"bar3d",
"pie2d",
"pie3d",
"doughnut2d",
"doughnut3d",
"pareto2d",
"pareto3d"
]
}
}
},
"link": {
"description": "an HTML anchor <a> tag",
"props": {

View File

@ -14,6 +14,7 @@
"devDependencies": {
"@budibase/client": "^0.0.32",
"@nx-js/compiler-util": "^2.0.0",
"@rollup/plugin-commonjs": "^11.1.0",
"bcryptjs": "2.4.3",
"fs-extra": "^8.1.0",
"lodash": "^4.17.15",
@ -34,5 +35,9 @@
],
"version": "0.0.32",
"license": "MIT",
"gitHead": "b1f4f90927d9e494e513220ef060af28d2d42455"
"gitHead": "b1f4f90927d9e494e513220ef060af28d2d42455",
"dependencies": {
"fusioncharts": "^3.15.1-sr.1",
"svelte-fusioncharts": "^1.0.0"
}
}

View File

@ -1,5 +1,6 @@
import svelte from "rollup-plugin-svelte"
import resolve from "rollup-plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
export default {
input: "src/index.js",
@ -15,6 +16,9 @@ export default {
svelte({
hydratable: true,
}),
resolve(),
resolve({
browser: true,
}),
commonjs()
],
}

View File

@ -1,22 +1,38 @@
<script>
import { onMount } from "svelte"
import { onMount } from "svelte";
import FusionCharts from "fusioncharts";
import Charts from "fusioncharts/fusioncharts.charts";
import FusionTheme from "fusioncharts/themes/fusioncharts.theme.fusion";
import SvelteFC, {fcRoot} from 'svelte-fusioncharts';
fcRoot(FusionCharts, Charts, FusionTheme);
export let _bb
export let onLoad
export let _instanceId
export let model
export let type = "column2d"
let cssVariables
let headers = []
let data = []
let store = _bb.store
$: chartConfigs = {
type,
width: '600',
height: '400',
dataFormat: 'json',
dataSource: {
data: $store[model._id] || []
}
}
async function fetchData() {
const FETCH_RECORDS_URL = `/api/${_instanceId}/${model}/records`
const FETCH_RECORDS_URL = `/api/${_instanceId}/all_${model._id}/records`
const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) {
const json = await response.json()
data = json
store.update(state => {
state[model._id] = json
return state
})
} else {
throw new Error("Failed to fetch records.", response)
}
@ -25,75 +41,9 @@
onMount(async () => {
await fetchData()
})
</script>
<!-- This prop was in the old one -->
<!-- use:cssVars={cssVariables} -->
<table class="uk-table">
<thead>
<tr>
{#each headers as header}
<th>{header}</th>
{/each}
</tr>
</thead>
<tbody>
{#each data as row}
<tr>
{#each headers as header}
{#if row[header]}
<td>{row[header]}</td>
{/if}
{/each}
</tr>
{/each}
</tbody>
</table>
<!-- <button
bind:this={theButton}
use:cssVars={cssVariables}
class="{className}
{customClasses}"
disabled={disabled || false}
on:click={clickHandler}
style={buttonStyles}>
{#if !_bb.props._children || _bb.props._children.length === 0}
{contentText}
{/if}
</button> -->
<style>
table {
border: 1px solid #ccc;
background: #fff;
border-radius: 3px;
border-collapse: collapse;
}
thead {
background: #f9f9f9;
border: 1px solid #ccc;
}
thead th {
color: var(--button-text);
text-transform: capitalize;
font-weight: 500;
font-size: 14px;
text-rendering: optimizeLegibility;
letter-spacing: 1px;
}
tbody tr {
border-bottom: 1px solid #ccc;
transition: 0.3s background-color;
color: var(--secondary100);
font-size: 14px;
}
tbody tr:hover {
background: #fafafa;
}
</style>
<div id="container">
<SvelteFC {...chartConfigs} />
</div>

View File

@ -2,26 +2,73 @@
import { onMount } from "svelte"
export let _bb
export let _viewName
export let _instanceId
export let model
let username
let password
let newModel = {
modelId: model._id
}
let store = _bb.store
$: fields = Object.keys(model.schema)
async function save() {
const SAVE_RECORD_URL = `/api/${_instanceId}/records`
const response = await _bb.api.post(SAVE_RECORD_URL, newModel);
const json = await response.json();
store.update(state => {
state[model._id] = [
...state[model._id],
json
]
return state
});
}
const handleInput = field => event => {
let value
if (event.target.type === "checkbox") {
value = event.target.checked
newModel[field] = value
return
}
if (event.target.type === "number") {
value = parseInt(event.target.value)
newModel[field] = value
return
}
value = event.target.value
newModel[field] = value
}
</script>
<form class="uk-form">
<form class="uk-form" on:submit|preventDefault>
<h4>{model.name}</h4>
<div>
<div class="uk-margin">
<label class="uk-form-label" for="form-stacked-text">Username</label>
<input class="uk-input" type="text" bind:value={username} />
</div>
<div class="uk-margin">
<label class="uk-form-label" for="form-stacked-text">Password</label>
<input class="uk-input" type="password" bind:value={password} />
</div>
{#each fields as field}
<div class="uk-margin">
<label class="form-label" for="form-stacked-text">{field}</label>
<input
class="uk-input"
type={model.schema[field].type === "string" ? "text" : model.schema[field].type}
on:change={handleInput(field)}
/>
</div>
{/each}
</div>
<button on:click={save}>
SAVE
</button>
</form>
<style>
.form-label {
font-weight: bold;
}
</style>

View File

@ -0,0 +1,87 @@
<script>
import { onMount } from "svelte"
export let _bb
export let _instanceId
export let model
export let layout = "list"
let headers = []
let store = _bb.store
async function fetchData() {
const FETCH_RECORDS_URL = `/api/${_instanceId}/all_${model._id}/records`
const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) {
const json = await response.json()
store.update(state => {
state[model._id] = json
return state
});
} else {
throw new Error("Failed to fetch records.", response)
}
}
$: data = $store[model._id] || []
onMount(async () => {
await fetchData()
})
</script>
<section
class:grid={layout === "grid"}
class:list={layout === "list"}
>
{#each data as data}
<div class="data-card">
<ul>
{#each Object.keys(data) as key}
<li>
<span class="data-key">
{key}:
</span>
<span class="data-value">
{data[key]}
</span>
</li>
{/each}
</ul>
</div>
{/each}
</section>
<style>
.list {
display: flex;
flex-direction: column;
font-family: Roboto;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
ul {
list-style-type: none;
}
li {
margin: 5px 0 5px 0;
}
.data-card {
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
}
.data-key {
font-weight: bold;
font-size: 20px;
text-transform: capitalize;
}
</style>

View File

@ -0,0 +1,17 @@
<script>
export let _bb
export let model
let searchValue = ""
function search() {
const SEARCH_URL =
_bb.api.get(``);
}
</script>
<div>
<input type="text" bind:value={searchValue}>
<button on:click={search}>Search</button>
</div>

View File

@ -1,38 +1,38 @@
<script>
import { onMount } from "svelte"
// import { cssVars, createClasses } from "./cssVars"
// import { buildStyle } from "./buildStyle"
export let _bb
export let onLoad
export let _instanceId
export let model
let cssVariables
let headers = []
let data = []
let store = _bb.store
async function fetchData() {
const FETCH_RECORDS_URL = `/api/${_instanceId}/${model}/records`
const FETCH_RECORDS_URL = `/api/${_instanceId}/all_${model._id}/records`
const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) {
const json = await response.json()
data = json
headers = Object.keys(data[0]).filter(key => !key.startsWith("_"))
store.update(state => {
state[model._id] = json
return state
});
headers = Object.keys(json[0]).filter(key => !key.startsWith("_"))
} else {
throw new Error("Failed to fetch records.", response)
}
}
$: data = $store[model._id] || []
onMount(async () => {
await fetchData()
})
</script>
<!-- This prop was in the old one -->
<!-- use:cssVars={cssVariables} -->
<table class="uk-table">
<thead>
<tr>
@ -54,19 +54,6 @@
</tbody>
</table>
<!-- <button
bind:this={theButton}
use:cssVars={cssVariables}
class="{className}
{customClasses}"
disabled={disabled || false}
on:click={clickHandler}
style={buttonStyles}>
{#if !_bb.props._children || _bb.props._children.length === 0}
{contentText}
{/if}
</button> -->
<style>
table {
border: 1px solid #ccc;

View File

@ -16,3 +16,6 @@ export { default as icon } from "./Icon.svelte"
export { default as Navigation } from "./Navigation.svelte"
export { default as datatable } from "./DataTable.svelte"
export { default as dataform } from "./DataForm.svelte"
export { default as datachart } from "./DataChart.svelte"
export { default as datalist } from "./DataList.svelte"
export { default as datasearch } from "./DataSearch.svelte"