335 lines
9.5 KiB
Svelte
335 lines
9.5 KiB
Svelte
<script>
|
|
import { getContext } from "svelte"
|
|
import { get } from "svelte/store"
|
|
import { generate } from "shortid"
|
|
import Block from "components/Block.svelte"
|
|
import BlockComponent from "components/BlockComponent.svelte"
|
|
import { makePropSafe as safe } from "@budibase/string-templates"
|
|
import { enrichSearchColumns, enrichFilter } from "utils/blocks.js"
|
|
import { Utils } from "@budibase/frontend-core"
|
|
|
|
export let title
|
|
export let dataSource
|
|
export let searchColumns
|
|
export let filter
|
|
export let sortColumn
|
|
export let sortOrder
|
|
export let paginate
|
|
export let tableColumns
|
|
export let rowCount
|
|
export let quiet
|
|
export let compact
|
|
export let size
|
|
export let allowSelectRows
|
|
export let clickBehaviour
|
|
export let onClick
|
|
export let showTitleButton
|
|
export let titleButtonText
|
|
export let titleButtonClickBehaviour
|
|
export let onClickTitleButton
|
|
export let noRowsMessage
|
|
export let sidePanelFields
|
|
export let sidePanelShowDelete
|
|
export let sidePanelSaveLabel
|
|
export let sidePanelDeleteLabel
|
|
export let notificationOverride
|
|
|
|
const { fetchDatasourceSchema, API, generateGoldenSample } = getContext("sdk")
|
|
const component = getContext("component")
|
|
const context = getContext("context")
|
|
const stateKey = `ID_${generate()}`
|
|
|
|
let formId
|
|
let dataProviderId
|
|
let detailsFormBlockId
|
|
let detailsSidePanelId
|
|
let newRowSidePanelId
|
|
let schema
|
|
let primaryDisplay
|
|
let enrichedSearchColumns
|
|
let schemaLoaded = false
|
|
|
|
$: deleteLabel = setDeleteLabel(sidePanelDeleteLabel, sidePanelShowDelete)
|
|
$: isDSPlus = dataSource?.type === "table" || dataSource?.type === "viewV2"
|
|
$: fetchSchema(dataSource)
|
|
$: enrichSearchColumns(searchColumns, schema).then(
|
|
val => (enrichedSearchColumns = val)
|
|
)
|
|
$: enrichedFilter = enrichFilter(filter, enrichedSearchColumns, formId)
|
|
$: editTitle = getEditTitle(detailsFormBlockId, primaryDisplay)
|
|
$: normalFields = getNormalFields(schema)
|
|
$: rowClickActions =
|
|
clickBehaviour === "actions" || !isDSPlus
|
|
? onClick
|
|
: [
|
|
{
|
|
id: 0,
|
|
"##eventHandlerType": "Update State",
|
|
parameters: {
|
|
key: stateKey,
|
|
type: "set",
|
|
persist: false,
|
|
value: `{{ ${safe("eventContext")}.${safe("row")}._id }}`,
|
|
},
|
|
},
|
|
{
|
|
id: 1,
|
|
"##eventHandlerType": "Open Side Panel",
|
|
parameters: {
|
|
id: detailsSidePanelId,
|
|
},
|
|
},
|
|
]
|
|
$: buttonClickActions =
|
|
titleButtonClickBehaviour === "actions" || !isDSPlus
|
|
? onClickTitleButton
|
|
: [
|
|
{
|
|
id: 0,
|
|
"##eventHandlerType": "Open Side Panel",
|
|
parameters: {
|
|
id: newRowSidePanelId,
|
|
},
|
|
},
|
|
]
|
|
|
|
// Provide additional data context for live binding eval
|
|
export const getAdditionalDataContext = () => {
|
|
const rows = get(context)[dataProviderId]?.rows
|
|
const goldenRow = generateGoldenSample(rows)
|
|
return {
|
|
eventContext: {
|
|
row: goldenRow,
|
|
},
|
|
}
|
|
}
|
|
|
|
const setDeleteLabel = sidePanelDeleteLabel => {
|
|
// Accommodate old config to ensure delete button does not reappear
|
|
let labelText = sidePanelShowDelete === false ? "" : sidePanelDeleteLabel
|
|
|
|
// Empty text is considered hidden.
|
|
if (labelText?.trim() === "") {
|
|
return ""
|
|
}
|
|
|
|
// Default to "Delete" if the value is unset
|
|
return labelText || "Delete"
|
|
}
|
|
|
|
// Load the datasource schema so we can determine column types
|
|
const fetchSchema = async dataSource => {
|
|
if (dataSource?.type === "table") {
|
|
const definition = await API.fetchTableDefinition(dataSource?.tableId)
|
|
schema = definition.schema
|
|
primaryDisplay = definition.primaryDisplay
|
|
} else if (dataSource) {
|
|
schema = await fetchDatasourceSchema(dataSource, {
|
|
enrichRelationships: true,
|
|
})
|
|
}
|
|
schemaLoaded = true
|
|
}
|
|
|
|
const getNormalFields = schema => {
|
|
if (!schema) {
|
|
return []
|
|
}
|
|
return Object.entries(schema)
|
|
.filter(entry => {
|
|
return !entry[1].autocolumn
|
|
})
|
|
.map(entry => entry[0])
|
|
}
|
|
|
|
const getEditTitle = (detailsFormBlockId, primaryDisplay) => {
|
|
if (!primaryDisplay || !detailsFormBlockId) {
|
|
return "Edit"
|
|
}
|
|
const prefix = safe(detailsFormBlockId + "-repeater")
|
|
const binding = `${prefix}.${safe(primaryDisplay)}`
|
|
return `{{#if ${binding}}}{{${binding}}}{{else}}Details{{/if}}`
|
|
}
|
|
</script>
|
|
|
|
{#if schemaLoaded}
|
|
<Block>
|
|
<BlockComponent
|
|
type="form"
|
|
bind:id={formId}
|
|
props={{
|
|
dataSource,
|
|
disableSchemaValidation: true,
|
|
editAutoColumns: true,
|
|
size,
|
|
}}
|
|
>
|
|
{#if title || enrichedSearchColumns?.length || showTitleButton}
|
|
<BlockComponent
|
|
type="container"
|
|
props={{
|
|
direction: "row",
|
|
hAlign: "stretch",
|
|
vAlign: "middle",
|
|
gap: "M",
|
|
wrap: true,
|
|
}}
|
|
styles={{
|
|
normal: {
|
|
"margin-bottom": "20px",
|
|
},
|
|
}}
|
|
order={0}
|
|
>
|
|
<BlockComponent
|
|
type="heading"
|
|
props={{
|
|
text: title,
|
|
}}
|
|
order={0}
|
|
/>
|
|
<BlockComponent
|
|
type="container"
|
|
props={{
|
|
direction: "row",
|
|
hAlign: "left",
|
|
vAlign: "center",
|
|
gap: "M",
|
|
wrap: true,
|
|
}}
|
|
order={1}
|
|
>
|
|
{#if enrichedSearchColumns?.length}
|
|
{#each enrichedSearchColumns as column, idx (column.name)}
|
|
<BlockComponent
|
|
type={column.componentType}
|
|
props={{
|
|
field: column.name,
|
|
placeholder: column.name,
|
|
text: column.name,
|
|
autoWidth: true,
|
|
}}
|
|
styles={{
|
|
normal: {
|
|
width: "192px",
|
|
},
|
|
}}
|
|
order={idx}
|
|
/>
|
|
{/each}
|
|
{/if}
|
|
{#if showTitleButton}
|
|
<BlockComponent
|
|
type="button"
|
|
props={{
|
|
onClick: buttonClickActions,
|
|
text: titleButtonText,
|
|
type: "cta",
|
|
}}
|
|
order={enrichedSearchColumns?.length ?? 0}
|
|
/>
|
|
{/if}
|
|
</BlockComponent>
|
|
</BlockComponent>
|
|
{/if}
|
|
<BlockComponent
|
|
type="dataprovider"
|
|
bind:id={dataProviderId}
|
|
props={{
|
|
dataSource,
|
|
filter: enrichedFilter,
|
|
sortColumn: sortColumn || primaryDisplay,
|
|
sortOrder,
|
|
paginate,
|
|
limit: rowCount,
|
|
}}
|
|
context="provider"
|
|
order={1}
|
|
>
|
|
<BlockComponent
|
|
type="table"
|
|
context="table"
|
|
props={{
|
|
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
|
|
columns: tableColumns,
|
|
rowCount,
|
|
quiet,
|
|
compact,
|
|
allowSelectRows,
|
|
size,
|
|
onClick: rowClickActions,
|
|
noRowsMessage: noRowsMessage || "No rows found",
|
|
}}
|
|
/>
|
|
</BlockComponent>
|
|
{#if clickBehaviour === "details"}
|
|
<BlockComponent
|
|
name="Details side panel"
|
|
type="sidepanel"
|
|
bind:id={detailsSidePanelId}
|
|
context="details-side-panel"
|
|
order={2}
|
|
>
|
|
<BlockComponent
|
|
name="Details form block"
|
|
type="formblock"
|
|
bind:id={detailsFormBlockId}
|
|
context="form-edit"
|
|
props={{
|
|
dataSource,
|
|
buttonPosition: "top",
|
|
buttons: Utils.buildFormBlockButtonConfig({
|
|
_id: $component.id + "-form-edit",
|
|
showDeleteButton: deleteLabel !== "",
|
|
showSaveButton: true,
|
|
saveButtonLabel: sidePanelSaveLabel || "Save",
|
|
deleteButtonLabel: deleteLabel,
|
|
notificationOverride,
|
|
actionType: "Update",
|
|
dataSource,
|
|
}),
|
|
actionType: "Update",
|
|
rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`,
|
|
fields: sidePanelFields || normalFields,
|
|
title: editTitle,
|
|
labelPosition: "left",
|
|
}}
|
|
/>
|
|
</BlockComponent>
|
|
{/if}
|
|
{#if showTitleButton && titleButtonClickBehaviour === "new"}
|
|
<BlockComponent
|
|
name="New row side panel"
|
|
type="sidepanel"
|
|
bind:id={newRowSidePanelId}
|
|
context="new-side-panel"
|
|
order={3}
|
|
>
|
|
<BlockComponent
|
|
name="New row form block"
|
|
type="formblock"
|
|
context="form-new"
|
|
props={{
|
|
dataSource,
|
|
buttonPosition: "top",
|
|
buttons: Utils.buildFormBlockButtonConfig({
|
|
_id: $component.id + "-form-new",
|
|
showDeleteButton: false,
|
|
showSaveButton: true,
|
|
saveButtonLabel: "Save",
|
|
notificationOverride,
|
|
actionType: "Create",
|
|
dataSource,
|
|
}),
|
|
actionType: "Create",
|
|
fields: sidePanelFields || normalFields,
|
|
title: "Create Row",
|
|
labelPosition: "left",
|
|
}}
|
|
/>
|
|
</BlockComponent>
|
|
{/if}
|
|
</BlockComponent>
|
|
</Block>
|
|
{/if}
|