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 6ad3df2e7f
commit 808d2d0f73
7 changed files with 125 additions and 91 deletions

View File

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

View File

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

View File

@ -234,7 +234,7 @@
<ProgressCircle />
</div>
{:else}
{#if !$component.children}
{#if $component.emptyState}
<Placeholder />
{:else}
<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>
import { getContext } from "svelte"
import { BlockBuilder } from "./block-builder"
import Component from "components/Component.svelte"
import Block from "./Block.svelte"
import BlockComponent from "./BlockComponent.svelte"
// Common props
export let searchColumns
export let dataSource
// Data provider props
export let searchColumns
export let filter
export let sortColumn
export let sortOrder
export let paginate
// Table props
export let tableColumns
export let showAutoColumns
export let rowCount
export let quiet
export let size
const { styleable } = getContext("sdk")
const component = getContext("component")
let formId
let dataProviderId
let instance
$: {
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",
})
$: enrichedFilter = enrichFilter(filter, searchColumns, formId)
const enrichFilter = (filter, searchColumns, formId) => {
let enrichedFilter = [...(filter || [])]
searchColumns?.forEach(column => {
const field = builder.createComponent("stringfield", {
field: column,
placeholder: column,
label: column,
})
searchContainer.addChild(field)
newFilter.push({
enrichedFilter.push({
field: column,
operator: "equal",
type: "string",
valueType: "Binding",
value: `{{ [${form.id}].[${column}] }}`,
value: `{{ [${formId}].[${column}] }}`,
})
})
form.addChild(searchContainer)
return enrichedFilter
}
</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,
filter: newFilter,
filter: enrichedFilter,
sortColumn,
sortOrder,
paginate,
limit: rowCount,
})
const table = builder.createComponent("table", {
dataProvider: `{{ literal [${dataProvider.id}] }}`,
}}
>
<BlockComponent
type="table"
props={{
dataProvider: `{{ literal [${dataProviderId}] }}`,
columns: tableColumns,
showAutoColumns,
rowCount,
quiet,
size,
})
}}
/>
</BlockComponent>
</BlockComponent>
</Block>
dataProvider.addChild(table)
form.addChild(dataProvider)
instance = form.build()
console.log("new instance")
<style>
.search {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
margin-bottom: 20px;
gap: 10px;
}
</script>
<div use:styleable={$component.styles}>
<Component {instance} />
</div>
</style>

View File

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