Add generic block structure with support for writing blocks like normal svelte components rather than a JSON builder

This commit is contained in:
Andrew Kingston 2021-11-02 08:45:27 +00:00
parent e9dd47b562
commit e6ffccaff7
7 changed files with 125 additions and 91 deletions

View File

@ -2576,7 +2576,6 @@
] ]
}, },
"tablewithsearch": { "tablewithsearch": {
"block": true,
"name": "Table with search", "name": "Table with search",
"icon": "Table", "icon": "Table",
"styles": ["size"], "styles": ["size"],

View File

@ -40,6 +40,7 @@
// Get contexts // Get contexts
const context = getContext("context") const context = getContext("context")
const insideScreenslot = !!getContext("screenslot") const insideScreenslot = !!getContext("screenslot")
const insideBlock = !!getContext("block")
// Create component context // Create component context
const componentStore = writable({}) const componentStore = writable({})
@ -53,7 +54,8 @@
$: name = instance._instanceName $: name = instance._instanceName
$: interactive = $: interactive =
$builderStore.inBuilder && $builderStore.inBuilder &&
($builderStore.previewType === "layout" || insideScreenslot) ($builderStore.previewType === "layout" || insideScreenslot) &&
!insideBlock
$: empty = interactive && !children.length && definition?.hasChildren $: empty = interactive && !children.length && definition?.hasChildren
$: emptyState = empty && definition?.showEmptyState !== false $: emptyState = empty && definition?.showEmptyState !== false
$: rawProps = getRawProps(instance) $: rawProps = getRawProps(instance)
@ -197,6 +199,8 @@
{/each} {/each}
{:else if emptyState} {:else if emptyState}
<Placeholder /> <Placeholder />
{:else if insideBlock}
<slot />
{/if} {/if}
</svelte:component> </svelte:component>
</div> </div>

View File

@ -234,7 +234,7 @@
<ProgressCircle /> <ProgressCircle />
</div> </div>
{:else} {:else}
{#if !$component.children} {#if $component.emptyState}
<Placeholder /> <Placeholder />
{:else} {:else}
<slot /> <slot />

View File

@ -0,0 +1,14 @@
<script>
import { getContext, setContext } from "svelte"
const component = getContext("component")
const { styleable } = getContext("sdk")
setContext("block", {
id: $component.id,
})
</script>
<div use:styleable={$component.styles}>
<slot />
</div>

View File

@ -0,0 +1,33 @@
<script>
import { getContext } from "svelte"
import { generate } from "shortid"
import Component from "components/Component.svelte"
export let type
export let props
export let styles
export let id
const block = getContext("block")
const rand = generate()
$: id = block.id + rand
$: instance = createInstance(type, props, id)
const createInstance = (type, props, id) => {
return {
_component: `@budibase/standard-components/${type}`,
_id: id,
_styles: {
normal: {
...styles,
},
},
...props,
}
}
</script>
<Component {instance}>
<slot />
</Component>

View File

@ -1,99 +1,89 @@
<script> <script>
import { getContext } from "svelte" import Block from "./Block.svelte"
import { BlockBuilder } from "./block-builder" import BlockComponent from "./BlockComponent.svelte"
import Component from "components/Component.svelte"
// Common props
export let searchColumns
export let dataSource export let dataSource
export let searchColumns
// Data provider props
export let filter export let filter
export let sortColumn export let sortColumn
export let sortOrder export let sortOrder
export let paginate export let paginate
// Table props
export let tableColumns export let tableColumns
export let showAutoColumns export let showAutoColumns
export let rowCount export let rowCount
export let quiet export let quiet
export let size export let size
const { styleable } = getContext("sdk") let formId
const component = getContext("component") let dataProviderId
let instance $: enrichedFilter = enrichFilter(filter, searchColumns, formId)
$: {
const builder = new BlockBuilder($component)
const form = builder.createComponent("form", {
dataSource,
})
let newFilter = [...(filter || [])]
// Add search bar if required
if (searchColumns?.length) {
const searchContainer = builder
.createComponent("container", {
direction: "row",
hAlign: "right",
vAlign: "middle",
gap: "M",
wrap: true,
})
.setStyles({
"margin-bottom": "20px",
})
const enrichFilter = (filter, searchColumns, formId) => {
let enrichedFilter = [...(filter || [])]
searchColumns?.forEach(column => { searchColumns?.forEach(column => {
const field = builder.createComponent("stringfield", { enrichedFilter.push({
field: column,
placeholder: column,
label: column,
})
searchContainer.addChild(field)
newFilter.push({
field: column, field: column,
operator: "equal", operator: "equal",
type: "string", type: "string",
valueType: "Binding", valueType: "Binding",
value: `{{ [${form.id}].[${column}] }}`, value: `{{ [${formId}].[${column}] }}`,
}) })
}) })
return enrichedFilter
form.addChild(searchContainer)
} }
</script>
const dataProvider = builder.createComponent("dataprovider", { <Block>
<BlockComponent type="form" bind:id={formId} props={{ dataSource }}>
{#if searchColumns?.length}
<div class="search">
{#each searchColumns as column}
<BlockComponent
type="stringfield"
props={{
field: column,
placeholder: column,
label: column,
}}
/>
{/each}
</div>
{/if}
<BlockComponent
type="dataprovider"
bind:id={dataProviderId}
props={{
dataSource, dataSource,
filter: newFilter, filter: enrichedFilter,
sortColumn, sortColumn,
sortOrder, sortOrder,
paginate, paginate,
limit: rowCount, limit: rowCount,
}) }}
>
const table = builder.createComponent("table", { <BlockComponent
dataProvider: `{{ literal [${dataProvider.id}] }}`, type="table"
props={{
dataProvider: `{{ literal [${dataProviderId}] }}`,
columns: tableColumns, columns: tableColumns,
showAutoColumns, showAutoColumns,
rowCount, rowCount,
quiet, quiet,
size, size,
}) }}
/>
</BlockComponent>
</BlockComponent>
</Block>
dataProvider.addChild(table) <style>
.search {
form.addChild(dataProvider) display: flex;
instance = form.build() flex-direction: row;
console.log("new instance") justify-content: flex-end;
align-items: center;
margin-bottom: 20px;
gap: 10px;
} }
</script> </style>
<div use:styleable={$component.styles}>
<Component {instance} />
</div>

View File

@ -64,9 +64,7 @@ export const styleable = (node, styles = {}) => {
// Handler to select a component in the builder when clicking it in the // Handler to select a component in the builder when clicking it in the
// builder preview // builder preview
selectComponent = event => { selectComponent = event => {
if (newStyles.interactive) {
builderStore.actions.selectComponent(componentId) builderStore.actions.selectComponent(componentId)
}
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
return false return false
@ -77,7 +75,7 @@ export const styleable = (node, styles = {}) => {
node.addEventListener("mouseout", applyNormalStyles) node.addEventListener("mouseout", applyNormalStyles)
// Add builder preview click listener // Add builder preview click listener
if (get(builderStore).inBuilder) { if (newStyles.interactive) {
node.addEventListener("click", selectComponent, false) node.addEventListener("click", selectComponent, false)
} }
@ -89,12 +87,8 @@ export const styleable = (node, styles = {}) => {
const removeListeners = () => { const removeListeners = () => {
node.removeEventListener("mouseover", applyHoverStyles) node.removeEventListener("mouseover", applyHoverStyles)
node.removeEventListener("mouseout", applyNormalStyles) node.removeEventListener("mouseout", applyNormalStyles)
// Remove builder preview click listener
if (get(builderStore).inBuilder) {
node.removeEventListener("click", selectComponent) node.removeEventListener("click", selectComponent)
} }
}
// Apply initial styles // Apply initial styles
setupStyles(styles) setupStyles(styles)