Merge pull request #3355 from Budibase/repeater-array
Data block + array fields as data sources
This commit is contained in:
commit
3db13562d6
|
@ -207,11 +207,11 @@ const getProviderContextBindings = (asset, dataProviders) => {
|
||||||
const keys = Object.keys(schema).sort()
|
const keys = Object.keys(schema).sort()
|
||||||
|
|
||||||
// Generate safe unique runtime prefix
|
// Generate safe unique runtime prefix
|
||||||
let runtimeId = component._id
|
let providerId = component._id
|
||||||
if (runtimeSuffix) {
|
if (runtimeSuffix) {
|
||||||
runtimeId += `-${runtimeSuffix}`
|
providerId += `-${runtimeSuffix}`
|
||||||
}
|
}
|
||||||
const safeComponentId = makePropSafe(runtimeId)
|
const safeComponentId = makePropSafe(providerId)
|
||||||
|
|
||||||
// Create bindable properties for each schema field
|
// Create bindable properties for each schema field
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
|
@ -235,7 +235,7 @@ const getProviderContextBindings = (asset, dataProviders) => {
|
||||||
// Field schema and provider are required to construct relationship
|
// Field schema and provider are required to construct relationship
|
||||||
// datasource options, based on bindable properties
|
// datasource options, based on bindable properties
|
||||||
fieldSchema,
|
fieldSchema,
|
||||||
providerId: component._id,
|
providerId,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -333,8 +333,11 @@ const getUrlBindings = asset => {
|
||||||
*/
|
*/
|
||||||
export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
|
export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
|
||||||
let schema, table
|
let schema, table
|
||||||
|
|
||||||
if (datasource) {
|
if (datasource) {
|
||||||
const { type } = datasource
|
const { type } = datasource
|
||||||
|
|
||||||
|
// Determine the source table from the datasource type
|
||||||
if (type === "provider") {
|
if (type === "provider") {
|
||||||
const component = findComponent(asset.props, datasource.providerId)
|
const component = findComponent(asset.props, datasource.providerId)
|
||||||
const source = getDatasourceForProvider(asset, component)
|
const source = getDatasourceForProvider(asset, component)
|
||||||
|
@ -342,11 +345,32 @@ export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
|
||||||
} else if (type === "query") {
|
} else if (type === "query") {
|
||||||
const queries = get(queriesStores).list
|
const queries = get(queriesStores).list
|
||||||
table = queries.find(query => query._id === datasource._id)
|
table = queries.find(query => query._id === datasource._id)
|
||||||
|
} else if (type === "field") {
|
||||||
|
table = { name: datasource.fieldName }
|
||||||
|
const { fieldType } = datasource
|
||||||
|
if (fieldType === "attachment") {
|
||||||
|
schema = {
|
||||||
|
url: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (fieldType === "array") {
|
||||||
|
schema = {
|
||||||
|
value: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const tables = get(tablesStore).list
|
const tables = get(tablesStore).list
|
||||||
table = tables.find(table => table._id === datasource.tableId)
|
table = tables.find(table => table._id === datasource.tableId)
|
||||||
}
|
}
|
||||||
if (table) {
|
|
||||||
|
// Determine the schema from the table if not already determined
|
||||||
|
if (table && !schema) {
|
||||||
if (type === "view") {
|
if (type === "view") {
|
||||||
schema = cloneDeep(table.views?.[datasource.name]?.schema)
|
schema = cloneDeep(table.views?.[datasource.name]?.schema)
|
||||||
} else if (type === "query" && isForm) {
|
} else if (type === "query" && isForm) {
|
||||||
|
@ -525,7 +549,7 @@ function bindingReplacement(bindableProperties, textWithBindings, convertTo) {
|
||||||
* {{ literal [componentId] }}
|
* {{ literal [componentId] }}
|
||||||
*/
|
*/
|
||||||
function extractLiteralHandlebarsID(value) {
|
function extractLiteralHandlebarsID(value) {
|
||||||
return value?.match(/{{\s*literal[\s[]+([a-fA-F0-9]+)[\s\]]*}}/)?.[1]
|
return value?.match(/{{\s*literal\s*\[+([^\]]+)].*}}/)?.[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
"icon": "Article",
|
"icon": "Article",
|
||||||
"children": [
|
"children": [
|
||||||
"tableblock",
|
"tableblock",
|
||||||
"cardsblock"
|
"cardsblock",
|
||||||
|
"repeaterblock"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"section",
|
"section",
|
||||||
|
|
|
@ -11,10 +11,7 @@
|
||||||
const getValue = component => `{{ literal ${makePropSafe(component._id)} }}`
|
const getValue = component => `{{ literal ${makePropSafe(component._id)} }}`
|
||||||
|
|
||||||
$: path = findComponentPath($currentAsset.props, $store.selectedComponentId)
|
$: path = findComponentPath($currentAsset.props, $store.selectedComponentId)
|
||||||
$: providers = path.filter(
|
$: providers = path.filter(c => c._component?.endsWith("/dataprovider"))
|
||||||
component =>
|
|
||||||
component._component === "@budibase/standard-components/dataprovider"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set initial value to closest data provider
|
// Set initial value to closest data provider
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|
|
@ -20,16 +20,18 @@
|
||||||
import { notifications } from "@budibase/bbui"
|
import { notifications } from "@budibase/bbui"
|
||||||
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
||||||
import IntegrationQueryEditor from "components/integration/index.svelte"
|
import IntegrationQueryEditor from "components/integration/index.svelte"
|
||||||
|
import { makePropSafe as safe } from "@budibase/string-templates"
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
let anchorRight, dropdownRight
|
|
||||||
let drawer
|
|
||||||
|
|
||||||
export let value = {}
|
export let value = {}
|
||||||
export let otherSources
|
export let otherSources
|
||||||
export let showAllQueries
|
export let showAllQueries
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
const arrayTypes = ["attachment", "array"]
|
||||||
|
let anchorRight, dropdownRight
|
||||||
|
let drawer
|
||||||
|
|
||||||
$: text = value?.label ?? "Choose an option"
|
$: text = value?.label ?? "Choose an option"
|
||||||
$: tables = $tablesStore.list.map(m => ({
|
$: tables = $tablesStore.list.map(m => ({
|
||||||
label: m.name,
|
label: m.name,
|
||||||
|
@ -54,8 +56,6 @@
|
||||||
name: query.name,
|
name: query.name,
|
||||||
tableId: query._id,
|
tableId: query._id,
|
||||||
...query,
|
...query,
|
||||||
schema: query.schema,
|
|
||||||
parameters: query.parameters,
|
|
||||||
type: "query",
|
type: "query",
|
||||||
}))
|
}))
|
||||||
$: dataProviders = getDataProviderComponents(
|
$: dataProviders = getDataProviderComponents(
|
||||||
|
@ -65,29 +65,40 @@
|
||||||
label: provider._instanceName,
|
label: provider._instanceName,
|
||||||
name: provider._instanceName,
|
name: provider._instanceName,
|
||||||
providerId: provider._id,
|
providerId: provider._id,
|
||||||
value: `{{ literal [${provider._id}] }}`,
|
value: `{{ literal ${safe(provider._id)} }}`,
|
||||||
type: "provider",
|
type: "provider",
|
||||||
schema: provider.schema,
|
|
||||||
}))
|
|
||||||
$: queryBindableProperties = bindings.map(property => ({
|
|
||||||
...property,
|
|
||||||
category: property.type === "instance" ? "Component" : "Table",
|
|
||||||
label: property.readableBinding,
|
|
||||||
path: property.readableBinding,
|
|
||||||
}))
|
}))
|
||||||
$: links = bindings
|
$: links = bindings
|
||||||
.filter(x => x.fieldSchema?.type === "link")
|
.filter(x => x.fieldSchema?.type === "link")
|
||||||
.map(property => {
|
.map(binding => {
|
||||||
|
const { providerId, readableBinding, fieldSchema } = binding || {}
|
||||||
|
const { name, tableId } = fieldSchema || {}
|
||||||
|
const safeProviderId = safe(providerId)
|
||||||
return {
|
return {
|
||||||
providerId: property.providerId,
|
providerId,
|
||||||
label: property.readableBinding,
|
label: readableBinding,
|
||||||
fieldName: property.fieldSchema.name,
|
fieldName: name,
|
||||||
tableId: property.fieldSchema.tableId,
|
tableId,
|
||||||
type: "link",
|
type: "link",
|
||||||
// These properties will be enriched by the client library and provide
|
// These properties will be enriched by the client library and provide
|
||||||
// details of the parent row of the relationship field, from context
|
// details of the parent row of the relationship field, from context
|
||||||
rowId: `{{ ${property.providerId}._id }}`,
|
rowId: `{{ ${safeProviderId}.${safe("_id")} }}`,
|
||||||
rowTableId: `{{ ${property.providerId}.tableId }}`,
|
rowTableId: `{{ ${safeProviderId}.${safe("tableId")} }}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$: fields = bindings
|
||||||
|
.filter(x => arrayTypes.includes(x.fieldSchema?.type))
|
||||||
|
.map(binding => {
|
||||||
|
const { providerId, readableBinding, runtimeBinding } = binding
|
||||||
|
const { name, type, tableId } = binding.fieldSchema
|
||||||
|
return {
|
||||||
|
providerId,
|
||||||
|
label: readableBinding,
|
||||||
|
fieldName: name,
|
||||||
|
fieldType: type,
|
||||||
|
tableId,
|
||||||
|
type: "field",
|
||||||
|
value: `{{ literal ${runtimeBinding} }}`,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -102,6 +113,14 @@
|
||||||
).source
|
).source
|
||||||
return $integrations[source].query[query.queryVerb]
|
return $integrations[source].query[query.queryVerb]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getQueryParams = query => {
|
||||||
|
return $queriesStore.list.find(q => q._id === query?._id)?.parameters || []
|
||||||
|
}
|
||||||
|
|
||||||
|
const getQueryDatasource = query => {
|
||||||
|
return $datasources.list.find(ds => ds._id === query?.datasourceId)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container" bind:this={anchorRight}>
|
<div class="container" bind:this={anchorRight}>
|
||||||
|
@ -127,11 +146,10 @@
|
||||||
</Button>
|
</Button>
|
||||||
<DrawerContent slot="body">
|
<DrawerContent slot="body">
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
{#if value.parameters.length > 0}
|
{#if getQueryParams(value._id).length > 0}
|
||||||
<ParameterBuilder
|
<ParameterBuilder
|
||||||
bind:customParams={value.queryParams}
|
bind:customParams={value.queryParams}
|
||||||
parameters={queries.find(query => query._id === value._id)
|
parameters={getQueryParams(value)}
|
||||||
.parameters}
|
|
||||||
{bindings}
|
{bindings}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -139,9 +157,7 @@
|
||||||
height={200}
|
height={200}
|
||||||
query={value}
|
query={value}
|
||||||
schema={fetchQueryDefinition(value)}
|
schema={fetchQueryDefinition(value)}
|
||||||
datasource={$datasources.list.find(
|
datasource={getQueryDatasource(value)}
|
||||||
ds => ds._id === value.datasourceId
|
|
||||||
)}
|
|
||||||
editable={false}
|
editable={false}
|
||||||
/>
|
/>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
@ -159,52 +175,71 @@
|
||||||
<li on:click={() => handleSelected(table)}>{table.label}</li>
|
<li on:click={() => handleSelected(table)}>{table.label}</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
<Divider size="S" />
|
{#if views?.length}
|
||||||
<div class="title">
|
<Divider size="S" />
|
||||||
<Heading size="XS">Views</Heading>
|
<div class="title">
|
||||||
</div>
|
<Heading size="XS">Views</Heading>
|
||||||
<ul>
|
</div>
|
||||||
{#each views as view}
|
<ul>
|
||||||
<li on:click={() => handleSelected(view)}>{view.label}</li>
|
{#each views as view}
|
||||||
{/each}
|
<li on:click={() => handleSelected(view)}>{view.label}</li>
|
||||||
</ul>
|
{/each}
|
||||||
<Divider size="S" />
|
</ul>
|
||||||
<div class="title">
|
{/if}
|
||||||
<Heading size="XS">Relationships</Heading>
|
{#if queries?.length}
|
||||||
</div>
|
<Divider size="S" />
|
||||||
<ul>
|
<div class="title">
|
||||||
{#each links as link}
|
<Heading size="XS">Queries</Heading>
|
||||||
<li on:click={() => handleSelected(link)}>{link.label}</li>
|
</div>
|
||||||
{/each}
|
<ul>
|
||||||
</ul>
|
{#each queries as query}
|
||||||
<Divider size="S" />
|
<li
|
||||||
<div class="title">
|
class:selected={value === query}
|
||||||
<Heading size="XS">Queries</Heading>
|
on:click={() => handleSelected(query)}
|
||||||
</div>
|
>
|
||||||
<ul>
|
{query.label}
|
||||||
{#each queries as query}
|
</li>
|
||||||
<li
|
{/each}
|
||||||
class:selected={value === query}
|
</ul>
|
||||||
on:click={() => handleSelected(query)}
|
{/if}
|
||||||
>
|
{#if links?.length}
|
||||||
{query.label}
|
<Divider size="S" />
|
||||||
</li>
|
<div class="title">
|
||||||
{/each}
|
<Heading size="XS">Relationships</Heading>
|
||||||
</ul>
|
</div>
|
||||||
<Divider size="S" />
|
<ul>
|
||||||
<div class="title">
|
{#each links as link}
|
||||||
<Heading size="XS">Data Providers</Heading>
|
<li on:click={() => handleSelected(link)}>{link.label}</li>
|
||||||
</div>
|
{/each}
|
||||||
<ul>
|
</ul>
|
||||||
{#each dataProviders as provider}
|
{/if}
|
||||||
<li
|
{#if fields?.length}
|
||||||
class:selected={value === provider}
|
<Divider size="S" />
|
||||||
on:click={() => handleSelected(provider)}
|
<div class="title">
|
||||||
>
|
<Heading size="XS">Fields</Heading>
|
||||||
{provider.label}
|
</div>
|
||||||
</li>
|
<ul>
|
||||||
{/each}
|
{#each fields as field}
|
||||||
</ul>
|
<li on:click={() => handleSelected(field)}>{field.label}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
{#if dataProviders?.length}
|
||||||
|
<Divider size="S" />
|
||||||
|
<div class="title">
|
||||||
|
<Heading size="XS">Data Providers</Heading>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
{#each dataProviders as provider}
|
||||||
|
<li
|
||||||
|
class:selected={value === provider}
|
||||||
|
on:click={() => handleSelected(provider)}
|
||||||
|
>
|
||||||
|
{provider.label}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
{#if otherSources?.length}
|
{#if otherSources?.length}
|
||||||
<Divider size="S" />
|
<Divider size="S" />
|
||||||
<div class="title">
|
<div class="title">
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
} from "builderStore/dataBinding"
|
} from "builderStore/dataBinding"
|
||||||
|
|
||||||
export let label = ""
|
export let label = ""
|
||||||
export let bindable = true
|
|
||||||
export let componentInstance = {}
|
export let componentInstance = {}
|
||||||
export let control = null
|
export let control = null
|
||||||
export let key = ""
|
export let key = ""
|
||||||
|
|
|
@ -2933,5 +2933,203 @@
|
||||||
"type": "schema",
|
"type": "schema",
|
||||||
"suffix": "repeater"
|
"suffix": "repeater"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"repeaterblock": {
|
||||||
|
"name": "Repeater block",
|
||||||
|
"icon": "ViewList",
|
||||||
|
"illegalChildren": ["section"],
|
||||||
|
"hasChildren": true,
|
||||||
|
"showSettingsBar": true,
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"type": "dataSource",
|
||||||
|
"label": "Data",
|
||||||
|
"key": "dataSource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "filter",
|
||||||
|
"label": "Filtering",
|
||||||
|
"key": "filter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"label": "Sort Column",
|
||||||
|
"key": "sortColumn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Sort Order",
|
||||||
|
"key": "sortOrder",
|
||||||
|
"options": ["Ascending", "Descending"],
|
||||||
|
"defaultValue": "Descending"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"label": "Limit",
|
||||||
|
"key": "limit",
|
||||||
|
"defaultValue": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Paginate",
|
||||||
|
"key": "paginate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": true,
|
||||||
|
"name": "Layout settings",
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Empty Text",
|
||||||
|
"key": "noRowsMessage",
|
||||||
|
"defaultValue": "No rows found"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Direction",
|
||||||
|
"key": "direction",
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Column",
|
||||||
|
"value": "column",
|
||||||
|
"barIcon": "ViewRow",
|
||||||
|
"barTitle": "Column layout"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Row",
|
||||||
|
"value": "row",
|
||||||
|
"barIcon": "ViewColumn",
|
||||||
|
"barTitle": "Row layout"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"defaultValue": "column"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Horiz. Align",
|
||||||
|
"key": "hAlign",
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Left",
|
||||||
|
"value": "left",
|
||||||
|
"barIcon": "AlignLeft",
|
||||||
|
"barTitle": "Align left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Center",
|
||||||
|
"value": "center",
|
||||||
|
"barIcon": "AlignCenter",
|
||||||
|
"barTitle": "Align center"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Right",
|
||||||
|
"value": "right",
|
||||||
|
"barIcon": "AlignRight",
|
||||||
|
"barTitle": "Align right"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Stretch",
|
||||||
|
"value": "stretch",
|
||||||
|
"barIcon": "MoveLeftRight",
|
||||||
|
"barTitle": "Align stretched horizontally"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"defaultValue": "stretch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Vert. Align",
|
||||||
|
"key": "vAlign",
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "buttons",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Top",
|
||||||
|
"value": "top",
|
||||||
|
"barIcon": "AlignTop",
|
||||||
|
"barTitle": "Align top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Middle",
|
||||||
|
"value": "middle",
|
||||||
|
"barIcon": "AlignMiddle",
|
||||||
|
"barTitle": "Align middle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Bottom",
|
||||||
|
"value": "bottom",
|
||||||
|
"barIcon": "AlignBottom",
|
||||||
|
"barTitle": "Align bottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Stretch",
|
||||||
|
"value": "stretch",
|
||||||
|
"barIcon": "MoveUpDown",
|
||||||
|
"barTitle": "Align stretched vertically"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"defaultValue": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"label": "Gap",
|
||||||
|
"key": "gap",
|
||||||
|
"showInBar": true,
|
||||||
|
"barStyle": "picker",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "None",
|
||||||
|
"value": "N"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Small",
|
||||||
|
"value": "S"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Medium",
|
||||||
|
"value": "M"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Large",
|
||||||
|
"value": "L"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"defaultValue": "M"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"context": [
|
||||||
|
{
|
||||||
|
"type": "static",
|
||||||
|
"suffix": "provider",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"label": "Rows",
|
||||||
|
"key": "rows"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Rows Length",
|
||||||
|
"key": "rowsLength"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Schema",
|
||||||
|
"key": "schema"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Page Number",
|
||||||
|
"key": "pageNumber"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "schema",
|
||||||
|
"suffix": "repeater"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,26 @@ export const fetchDatasourceSchema = async dataSource => {
|
||||||
return dataSource.value?.schema
|
return dataSource.value?.schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Field sources have their schema statically defined
|
||||||
|
if (type === "field") {
|
||||||
|
if (dataSource.fieldType === "attachment") {
|
||||||
|
return {
|
||||||
|
url: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (dataSource.fieldType === "array") {
|
||||||
|
return {
|
||||||
|
value: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tables, views and links can be fetched by table ID
|
// Tables, views and links can be fetched by table ID
|
||||||
if (
|
if (
|
||||||
(type === "table" || type === "view" || type === "link") &&
|
(type === "table" || type === "view" || type === "link") &&
|
||||||
|
|
|
@ -30,6 +30,6 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Component {instance}>
|
<Component {instance} isBlock>
|
||||||
<slot />
|
<slot />
|
||||||
</Component>
|
</Component>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
export let instance = {}
|
export let instance = {}
|
||||||
export let isLayout = false
|
export let isLayout = false
|
||||||
export let isScreen = false
|
export let isScreen = false
|
||||||
|
export let isBlock = false
|
||||||
|
|
||||||
// The enriched component settings
|
// The enriched component settings
|
||||||
let enrichedSettings
|
let enrichedSettings
|
||||||
|
@ -44,7 +45,6 @@
|
||||||
// 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({})
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
$: interactive =
|
$: interactive =
|
||||||
$builderStore.inBuilder &&
|
$builderStore.inBuilder &&
|
||||||
($builderStore.previewType === "layout" || insideScreenslot) &&
|
($builderStore.previewType === "layout" || insideScreenslot) &&
|
||||||
!insideBlock
|
!isBlock
|
||||||
$: draggable = interactive && !isLayout && !isScreen
|
$: draggable = interactive && !isLayout && !isScreen
|
||||||
$: droppable = interactive && !isLayout && !isScreen
|
$: droppable = interactive && !isLayout && !isScreen
|
||||||
|
|
||||||
|
@ -262,6 +262,7 @@
|
||||||
class:droppable
|
class:droppable
|
||||||
class:empty
|
class:empty
|
||||||
class:interactive
|
class:interactive
|
||||||
|
class:block={isBlock}
|
||||||
data-id={id}
|
data-id={id}
|
||||||
data-name={name}
|
data-name={name}
|
||||||
>
|
>
|
||||||
|
@ -272,7 +273,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
{:else if emptyState}
|
{:else if emptyState}
|
||||||
<Placeholder />
|
<Placeholder />
|
||||||
{:else if insideBlock}
|
{:else if isBlock}
|
||||||
<slot />
|
<slot />
|
||||||
{/if}
|
{/if}
|
||||||
</svelte:component>
|
</svelte:component>
|
||||||
|
|
|
@ -183,7 +183,16 @@
|
||||||
} else if (dataSource?.type === "provider") {
|
} else if (dataSource?.type === "provider") {
|
||||||
// For providers referencing another provider, just use the rows it
|
// For providers referencing another provider, just use the rows it
|
||||||
// provides
|
// provides
|
||||||
allRows = dataSource?.value?.rows ?? []
|
allRows = dataSource?.value?.rows || []
|
||||||
|
} else if (dataSource?.type === "field") {
|
||||||
|
// Field sources will be available from context.
|
||||||
|
// Enrich non object elements into object to ensure a valid schema.
|
||||||
|
const data = dataSource?.value || []
|
||||||
|
if (Array.isArray(data) && data[0] && typeof data[0] !== "object") {
|
||||||
|
allRows = data.map(value => ({ value }))
|
||||||
|
} else {
|
||||||
|
allRows = data
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// For other data sources like queries or views, fetch all rows from the
|
// For other data sources like queries or views, fetch all rows from the
|
||||||
// server
|
// server
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import Block from "components/Block.svelte"
|
import Block from "components/Block.svelte"
|
||||||
import BlockComponent from "components/BlockComponent.svelte"
|
import BlockComponent from "components/BlockComponent.svelte"
|
||||||
import { Heading } from "@budibase/bbui"
|
import { Heading } from "@budibase/bbui"
|
||||||
|
import { makePropSafe as safe } from "@budibase/string-templates"
|
||||||
|
|
||||||
export let title
|
export let title
|
||||||
export let dataSource
|
export let dataSource
|
||||||
|
@ -103,7 +104,7 @@
|
||||||
}
|
}
|
||||||
const col = linkColumn || "_id"
|
const col = linkColumn || "_id"
|
||||||
const split = url.split("/:")
|
const split = url.split("/:")
|
||||||
return `${split[0]}/{{ [${repeaterId}].[${col}] }}`
|
return `${split[0]}/{{ ${safe(repeaterId)}.${safe(col)} }}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the datasource schema on mount so we can determine column types
|
// Load the datasource schema on mount so we can determine column types
|
||||||
|
@ -171,7 +172,7 @@
|
||||||
bind:id={repeaterId}
|
bind:id={repeaterId}
|
||||||
context="repeater"
|
context="repeater"
|
||||||
props={{
|
props={{
|
||||||
dataProvider: `{{ literal [${dataProviderId}] }}`,
|
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
|
||||||
direction: "row",
|
direction: "row",
|
||||||
hAlign: "stretch",
|
hAlign: "stretch",
|
||||||
vAlign: "top",
|
vAlign: "top",
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<script>
|
||||||
|
import BlockComponent from "components/BlockComponent.svelte"
|
||||||
|
import Block from "components/Block.svelte"
|
||||||
|
import Placeholder from "components/app/Placeholder.svelte"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
import { makePropSafe as safe } from "@budibase/string-templates"
|
||||||
|
|
||||||
|
export let dataSource
|
||||||
|
export let filter
|
||||||
|
export let sortColumn
|
||||||
|
export let sortOrder
|
||||||
|
export let limit
|
||||||
|
export let paginate
|
||||||
|
export let noRowsMessage
|
||||||
|
export let direction
|
||||||
|
export let hAlign
|
||||||
|
export let vAlign
|
||||||
|
export let gap
|
||||||
|
|
||||||
|
let providerId
|
||||||
|
|
||||||
|
const component = getContext("component")
|
||||||
|
const { styleable } = getContext("sdk")
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Block>
|
||||||
|
<div use:styleable={$component.styles}>
|
||||||
|
<BlockComponent
|
||||||
|
type="dataprovider"
|
||||||
|
context="provider"
|
||||||
|
bind:id={providerId}
|
||||||
|
props={{
|
||||||
|
dataSource,
|
||||||
|
filter,
|
||||||
|
sortColumn,
|
||||||
|
sortOrder,
|
||||||
|
limit,
|
||||||
|
paginate,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if $component.empty}
|
||||||
|
<Placeholder text={$component.name} />
|
||||||
|
{:else}
|
||||||
|
<BlockComponent
|
||||||
|
type="repeater"
|
||||||
|
context="repeater"
|
||||||
|
props={{
|
||||||
|
dataProvider: `{{ literal ${safe(providerId)} }}`,
|
||||||
|
noRowsMessage,
|
||||||
|
direction,
|
||||||
|
hAlign,
|
||||||
|
vAlign,
|
||||||
|
gap,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</BlockComponent>
|
||||||
|
{/if}
|
||||||
|
</BlockComponent>
|
||||||
|
</div>
|
||||||
|
</Block>
|
|
@ -3,6 +3,7 @@
|
||||||
import Block from "components/Block.svelte"
|
import Block from "components/Block.svelte"
|
||||||
import BlockComponent from "components/BlockComponent.svelte"
|
import BlockComponent from "components/BlockComponent.svelte"
|
||||||
import { Heading } from "@budibase/bbui"
|
import { Heading } from "@budibase/bbui"
|
||||||
|
import { makePropSafe as safe } from "@budibase/string-templates"
|
||||||
|
|
||||||
export let title
|
export let title
|
||||||
export let dataSource
|
export let dataSource
|
||||||
|
@ -61,7 +62,7 @@
|
||||||
operator: column.type === "string" ? "string" : "equal",
|
operator: column.type === "string" ? "string" : "equal",
|
||||||
type: "string",
|
type: "string",
|
||||||
valueType: "Binding",
|
valueType: "Binding",
|
||||||
value: `{{ [${formId}].[${column.name}] }}`,
|
value: `{{ ${safe(formId)}.${safe(column.name)} }}`,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return enrichedFilter
|
return enrichedFilter
|
||||||
|
@ -147,7 +148,7 @@
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type="table"
|
type="table"
|
||||||
props={{
|
props={{
|
||||||
dataProvider: `{{ literal [${dataProviderId}] }}`,
|
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
|
||||||
columns: tableColumns,
|
columns: tableColumns,
|
||||||
showAutoColumns,
|
showAutoColumns,
|
||||||
rowCount,
|
rowCount,
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export { default as tableblock } from "./TableBlock.svelte"
|
export { default as tableblock } from "./TableBlock.svelte"
|
||||||
export { default as cardsblock } from "./CardsBlock.svelte"
|
export { default as cardsblock } from "./CardsBlock.svelte"
|
||||||
|
export { default as repeaterblock } from "./RepeaterBlock.svelte"
|
||||||
|
|
|
@ -147,7 +147,7 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const element = e.target.closest(".component")
|
const element = e.target.closest(".component:not(.block)")
|
||||||
if (
|
if (
|
||||||
element &&
|
element &&
|
||||||
element.classList.contains("droppable") &&
|
element.classList.contains("droppable") &&
|
||||||
|
|
|
@ -17,7 +17,19 @@
|
||||||
|
|
||||||
$: definition = $builderStore.selectedComponentDefinition
|
$: definition = $builderStore.selectedComponentDefinition
|
||||||
$: showBar = definition?.showSettingsBar && !$builderStore.isDragging
|
$: showBar = definition?.showSettingsBar && !$builderStore.isDragging
|
||||||
$: settings = definition?.settings?.filter(setting => setting.showInBar) ?? []
|
$: settings = getBarSettings(definition)
|
||||||
|
|
||||||
|
const getBarSettings = definition => {
|
||||||
|
let allSettings = []
|
||||||
|
definition?.settings?.forEach(setting => {
|
||||||
|
if (setting.section) {
|
||||||
|
allSettings = allSettings.concat(setting.settings || [])
|
||||||
|
} else {
|
||||||
|
allSettings.push(setting)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return allSettings.filter(setting => setting.showInBar)
|
||||||
|
}
|
||||||
|
|
||||||
const updatePosition = () => {
|
const updatePosition = () => {
|
||||||
if (!showBar) {
|
if (!showBar) {
|
||||||
|
|
|
@ -46,6 +46,9 @@ const HELPERS = [
|
||||||
}),
|
}),
|
||||||
// adds a note for post-processor
|
// adds a note for post-processor
|
||||||
new Helper(HelperFunctionNames.LITERAL, value => {
|
new Helper(HelperFunctionNames.LITERAL, value => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
const type = typeof value
|
const type = typeof value
|
||||||
const outputVal = type === "object" ? JSON.stringify(value) : value
|
const outputVal = type === "object" ? JSON.stringify(value) : value
|
||||||
return `{{${LITERAL_MARKER} ${type}-${outputVal}}}`
|
return `{{${LITERAL_MARKER} ${type}-${outputVal}}}`
|
||||||
|
|
Loading…
Reference in New Issue