Form Block Improvements (#10404)
* Form Block Improvements * PR Fixes * PR feedback
This commit is contained in:
parent
c08db11859
commit
0c38124f6a
|
@ -21,6 +21,7 @@ import DrawerBindableCombobox from "components/common/bindings/DrawerBindableCom
|
||||||
import ColumnEditor from "./controls/ColumnEditor/ColumnEditor.svelte"
|
import ColumnEditor from "./controls/ColumnEditor/ColumnEditor.svelte"
|
||||||
import BasicColumnEditor from "./controls/ColumnEditor/BasicColumnEditor.svelte"
|
import BasicColumnEditor from "./controls/ColumnEditor/BasicColumnEditor.svelte"
|
||||||
import BarButtonList from "./controls/BarButtonList.svelte"
|
import BarButtonList from "./controls/BarButtonList.svelte"
|
||||||
|
import FieldConfiguration from "./controls/FieldConfiguration/FieldConfiguration.svelte"
|
||||||
|
|
||||||
const componentMap = {
|
const componentMap = {
|
||||||
text: DrawerBindableCombobox,
|
text: DrawerBindableCombobox,
|
||||||
|
@ -43,6 +44,7 @@ const componentMap = {
|
||||||
section: SectionSelect,
|
section: SectionSelect,
|
||||||
filter: FilterEditor,
|
filter: FilterEditor,
|
||||||
url: URLSelect,
|
url: URLSelect,
|
||||||
|
fieldConfiguration: FieldConfiguration,
|
||||||
columns: ColumnEditor,
|
columns: ColumnEditor,
|
||||||
"columns/basic": BasicColumnEditor,
|
"columns/basic": BasicColumnEditor,
|
||||||
"field/sortable": SortableFieldSelect,
|
"field/sortable": SortableFieldSelect,
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
<script>
|
||||||
|
import { Button, ActionButton, Drawer } from "@budibase/bbui"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
import ColumnDrawer from "./ColumnDrawer.svelte"
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
import {
|
||||||
|
getDatasourceForProvider,
|
||||||
|
getSchemaForDatasource,
|
||||||
|
} from "builderStore/dataBinding"
|
||||||
|
import { currentAsset } from "builderStore"
|
||||||
|
import { getFields } from "helpers/searchFields"
|
||||||
|
|
||||||
|
export let componentInstance
|
||||||
|
export let value = []
|
||||||
|
export let allowCellEditing = true
|
||||||
|
export let subject = "Table"
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let drawer
|
||||||
|
let boundValue
|
||||||
|
|
||||||
|
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
|
$: schema = getSchema($currentAsset, datasource)
|
||||||
|
$: options = allowCellEditing
|
||||||
|
? Object.keys(schema || {})
|
||||||
|
: enrichedSchemaFields?.map(field => field.name)
|
||||||
|
$: sanitisedValue = getValidColumns(value, options)
|
||||||
|
$: updateBoundValue(sanitisedValue)
|
||||||
|
$: enrichedSchemaFields = getFields(Object.values(schema || {}), {
|
||||||
|
allowLinks: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const getSchema = (asset, datasource) => {
|
||||||
|
const schema = getSchemaForDatasource(asset, datasource).schema
|
||||||
|
|
||||||
|
// Don't show ID and rev in tables
|
||||||
|
if (schema) {
|
||||||
|
delete schema._id
|
||||||
|
delete schema._rev
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateBoundValue = value => {
|
||||||
|
boundValue = cloneDeep(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getValidColumns = (columns, options) => {
|
||||||
|
if (!Array.isArray(columns) || !columns.length) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
// We need to account for legacy configs which would just be an array
|
||||||
|
// of strings
|
||||||
|
if (typeof columns[0] === "string") {
|
||||||
|
columns = columns.map(col => ({
|
||||||
|
name: col,
|
||||||
|
displayName: col,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return columns.filter(column => {
|
||||||
|
return options.includes(column.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
updateBoundValue(sanitisedValue)
|
||||||
|
drawer.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
dispatch("change", getValidColumns(boundValue, options))
|
||||||
|
drawer.hide()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ActionButton on:click={open}>Configure columns</ActionButton>
|
||||||
|
<Drawer bind:this={drawer} title="{subject} Columns">
|
||||||
|
<svelte:fragment slot="description">
|
||||||
|
Configure the columns in your {subject.toLowerCase()}.
|
||||||
|
</svelte:fragment>
|
||||||
|
<Button cta slot="buttons" on:click={save}>Save</Button>
|
||||||
|
<ColumnDrawer
|
||||||
|
slot="body"
|
||||||
|
bind:columns={boundValue}
|
||||||
|
{options}
|
||||||
|
{schema}
|
||||||
|
{allowCellEditing}
|
||||||
|
/>
|
||||||
|
</Drawer>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<script>
|
||||||
|
import { DrawerContent, Drawer, Button, Icon } from "@budibase/bbui"
|
||||||
|
import ValidationDrawer from "components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte"
|
||||||
|
export let column
|
||||||
|
export let type
|
||||||
|
|
||||||
|
let drawer
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Icon name="Settings" hoverable size="S" on:click={drawer.show} />
|
||||||
|
<Drawer bind:this={drawer} title="Field Validation">
|
||||||
|
<svelte:fragment slot="description">
|
||||||
|
"{column.name}" field validation
|
||||||
|
</svelte:fragment>
|
||||||
|
<Button cta slot="buttons" on:click={drawer.hide}>Save</Button>
|
||||||
|
<DrawerContent slot="body">
|
||||||
|
<div class="container">
|
||||||
|
<ValidationDrawer
|
||||||
|
slot="body"
|
||||||
|
bind:rules={column.validation}
|
||||||
|
fieldName={column.name}
|
||||||
|
{type}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
|
@ -0,0 +1,202 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
DrawerContent,
|
||||||
|
Layout,
|
||||||
|
Select,
|
||||||
|
Label,
|
||||||
|
Body,
|
||||||
|
Input,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
import { flip } from "svelte/animate"
|
||||||
|
import { dndzone } from "svelte-dnd-action"
|
||||||
|
import { generate } from "shortid"
|
||||||
|
import CellEditor from "./CellEditor.svelte"
|
||||||
|
|
||||||
|
export let columns = []
|
||||||
|
export let options = []
|
||||||
|
export let schema = {}
|
||||||
|
|
||||||
|
const flipDurationMs = 150
|
||||||
|
let dragDisabled = true
|
||||||
|
|
||||||
|
$: unselectedColumns = getUnselectedColumns(options, columns)
|
||||||
|
$: columns.forEach(column => {
|
||||||
|
if (!column.id) {
|
||||||
|
column.id = generate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const getUnselectedColumns = (allColumns, selectedColumns) => {
|
||||||
|
let optionsObj = {}
|
||||||
|
allColumns.forEach(option => {
|
||||||
|
optionsObj[option] = true
|
||||||
|
})
|
||||||
|
selectedColumns?.forEach(column => {
|
||||||
|
delete optionsObj[column.name]
|
||||||
|
})
|
||||||
|
return Object.keys(optionsObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRemainingColumnOptions = selectedColumn => {
|
||||||
|
if (!selectedColumn || unselectedColumns.includes(selectedColumn)) {
|
||||||
|
return unselectedColumns
|
||||||
|
}
|
||||||
|
return [selectedColumn, ...unselectedColumns]
|
||||||
|
}
|
||||||
|
|
||||||
|
const addColumn = () => {
|
||||||
|
columns = [...columns, {}]
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeColumn = id => {
|
||||||
|
columns = columns.filter(column => column.id !== id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateColumnOrder = e => {
|
||||||
|
columns = e.detail.items
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFinalize = e => {
|
||||||
|
updateColumnOrder(e)
|
||||||
|
dragDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const addAllColumns = () => {
|
||||||
|
let newColumns = columns || []
|
||||||
|
options.forEach(field => {
|
||||||
|
const fieldSchema = schema[field]
|
||||||
|
const hasCol = columns && columns.findIndex(x => x.name === field) !== -1
|
||||||
|
if (!fieldSchema?.autocolumn && !hasCol) {
|
||||||
|
newColumns.push({
|
||||||
|
name: field,
|
||||||
|
displayName: field,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
columns = newColumns
|
||||||
|
}
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
columns = []
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFieldType = column => {
|
||||||
|
return `validation/${schema[column.name]?.type}`
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DrawerContent>
|
||||||
|
<div class="container">
|
||||||
|
<Layout noPadding gap="S">
|
||||||
|
{#if columns?.length}
|
||||||
|
<Layout noPadding gap="XS">
|
||||||
|
<div class="column">
|
||||||
|
<div />
|
||||||
|
<Label size="L">Column</Label>
|
||||||
|
<Label size="L">Label</Label>
|
||||||
|
<div />
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="columns"
|
||||||
|
use:dndzone={{
|
||||||
|
items: columns,
|
||||||
|
flipDurationMs,
|
||||||
|
dropTargetStyle: { outline: "none" },
|
||||||
|
dragDisabled,
|
||||||
|
}}
|
||||||
|
on:finalize={handleFinalize}
|
||||||
|
on:consider={updateColumnOrder}
|
||||||
|
>
|
||||||
|
{#each columns as column (column.id)}
|
||||||
|
<div class="column" animate:flip={{ duration: flipDurationMs }}>
|
||||||
|
<div
|
||||||
|
class="handle"
|
||||||
|
aria-label="drag-handle"
|
||||||
|
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"}
|
||||||
|
on:mousedown={() => (dragDisabled = false)}
|
||||||
|
>
|
||||||
|
<Icon name="DragHandle" size="XL" />
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
bind:value={column.name}
|
||||||
|
placeholder="Column"
|
||||||
|
options={getRemainingColumnOptions(column.name)}
|
||||||
|
on:change={e => (column.displayName = e.detail)}
|
||||||
|
/>
|
||||||
|
<Input bind:value={column.displayName} placeholder="Label" />
|
||||||
|
<CellEditor type={getFieldType(column)} bind:column />
|
||||||
|
<Icon
|
||||||
|
name="Close"
|
||||||
|
hoverable
|
||||||
|
size="S"
|
||||||
|
on:click={() => removeColumn(column.id)}
|
||||||
|
disabled={columns.length === 1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
{:else}
|
||||||
|
<div class="column">
|
||||||
|
<div class="wide">
|
||||||
|
<Body size="S">Add columns to be included in your form below.</Body>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="column">
|
||||||
|
<div class="buttons wide">
|
||||||
|
<Button secondary icon="Add" on:click={addColumn}>Add column</Button>
|
||||||
|
<Button secondary quiet on:click={addAllColumns}>
|
||||||
|
Add all columns
|
||||||
|
</Button>
|
||||||
|
{#if columns?.length}
|
||||||
|
<Button secondary quiet on:click={reset}>Reset columns</Button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.columns {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.column {
|
||||||
|
gap: var(--spacing-l);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 20px 1fr 1fr 16px 16px;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: var(--border-radius-s);
|
||||||
|
transition: background-color ease-in-out 130ms;
|
||||||
|
}
|
||||||
|
.column:hover {
|
||||||
|
background-color: var(--spectrum-global-color-gray-100);
|
||||||
|
}
|
||||||
|
.handle {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
.wide {
|
||||||
|
grid-column: 2 / -1;
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,89 @@
|
||||||
|
<script>
|
||||||
|
import { Button, ActionButton, Drawer } from "@budibase/bbui"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
import ColumnDrawer from "./ColumnDrawer.svelte"
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
import {
|
||||||
|
getDatasourceForProvider,
|
||||||
|
getSchemaForDatasource,
|
||||||
|
} from "builderStore/dataBinding"
|
||||||
|
import { currentAsset } from "builderStore"
|
||||||
|
import { getFields } from "helpers/searchFields"
|
||||||
|
|
||||||
|
export let componentInstance
|
||||||
|
export let value = []
|
||||||
|
|
||||||
|
const convertOldColumnFormat = oldColumns => {
|
||||||
|
if (typeof oldColumns?.[0] === "string") {
|
||||||
|
value = oldColumns.map(field => ({ name: field, displayName: field }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: convertOldColumnFormat(value)
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let drawer
|
||||||
|
let boundValue
|
||||||
|
|
||||||
|
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
|
$: schema = getSchema($currentAsset, datasource)
|
||||||
|
$: options = Object.keys(schema || {})
|
||||||
|
$: sanitisedValue = getValidColumns(value, options)
|
||||||
|
$: updateBoundValue(sanitisedValue)
|
||||||
|
$: enrichedSchemaFields = getFields(Object.values(schema || {}), {
|
||||||
|
allowLinks: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const getSchema = (asset, datasource) => {
|
||||||
|
const schema = getSchemaForDatasource(asset, datasource).schema
|
||||||
|
|
||||||
|
// Don't show ID and rev in tables
|
||||||
|
if (schema) {
|
||||||
|
delete schema._id
|
||||||
|
delete schema._rev
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateBoundValue = value => {
|
||||||
|
boundValue = cloneDeep(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getValidColumns = (columns, options) => {
|
||||||
|
if (!Array.isArray(columns) || !columns.length) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
// We need to account for legacy configs which would just be an array
|
||||||
|
// of strings
|
||||||
|
if (typeof columns[0] === "string") {
|
||||||
|
columns = columns.map(col => ({
|
||||||
|
name: col,
|
||||||
|
displayName: col,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return columns.filter(column => {
|
||||||
|
return options.includes(column.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
updateBoundValue(sanitisedValue)
|
||||||
|
drawer.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
dispatch("change", getValidColumns(boundValue, options))
|
||||||
|
drawer.hide()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ActionButton on:click={open}>Configure fields</ActionButton>
|
||||||
|
<Drawer bind:this={drawer} title="Form Fields">
|
||||||
|
<svelte:fragment slot="description">
|
||||||
|
Configure the fields in your form.
|
||||||
|
</svelte:fragment>
|
||||||
|
<Button cta slot="buttons" on:click={save}>Save</Button>
|
||||||
|
<ColumnDrawer slot="body" bind:columns={boundValue} {options} {schema} />
|
||||||
|
</Drawer>
|
|
@ -16,6 +16,7 @@
|
||||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||||
import { generate } from "shortid"
|
import { generate } from "shortid"
|
||||||
|
|
||||||
|
export let fieldName = null
|
||||||
export let rules = []
|
export let rules = []
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
export let type
|
export let type
|
||||||
|
@ -124,7 +125,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
$: dataSourceSchema = getDataSourceSchema($currentAsset, $selectedComponent)
|
$: dataSourceSchema = getDataSourceSchema($currentAsset, $selectedComponent)
|
||||||
$: field = $selectedComponent?.field
|
$: field = fieldName || $selectedComponent?.field
|
||||||
$: schemaRules = parseRulesFromSchema(field, dataSourceSchema || {})
|
$: schemaRules = parseRulesFromSchema(field, dataSourceSchema || {})
|
||||||
$: fieldType = type?.split("/")[1] || "string"
|
$: fieldType = type?.split("/")[1] || "string"
|
||||||
$: constraintOptions = getConstraintsForType(fieldType)
|
$: constraintOptions = getConstraintsForType(fieldType)
|
||||||
|
@ -140,8 +141,12 @@
|
||||||
const formParent = findClosestMatchingComponent(
|
const formParent = findClosestMatchingComponent(
|
||||||
asset.props,
|
asset.props,
|
||||||
component._id,
|
component._id,
|
||||||
component => component._component.endsWith("/form")
|
component =>
|
||||||
|
component._component.endsWith("/form") ||
|
||||||
|
component._component.endsWith("/formblock") ||
|
||||||
|
component._component.endsWith("/tableblock")
|
||||||
)
|
)
|
||||||
|
|
||||||
return getSchemaForDatasource(asset, formParent?.dataSource)
|
return getSchemaForDatasource(asset, formParent?.dataSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4435,6 +4435,48 @@
|
||||||
"key": "row"
|
"key": "row"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Fields",
|
||||||
|
"type": "fieldConfiguration",
|
||||||
|
"key": "sidePanelFields",
|
||||||
|
"nested": true,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "clickBehaviour",
|
||||||
|
"value": "details"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Show delete",
|
||||||
|
"type": "boolean",
|
||||||
|
"key": "sidePanelShowDelete",
|
||||||
|
"nested": true,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "clickBehaviour",
|
||||||
|
"value": "details"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Save label",
|
||||||
|
"type": "text",
|
||||||
|
"key": "sidePanelSaveLabel",
|
||||||
|
"defaultValue": "Save",
|
||||||
|
"nested": true,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "clickBehaviour",
|
||||||
|
"value": "details"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Delete label",
|
||||||
|
"type": "text",
|
||||||
|
"key": "sidePanelDeleteLabel",
|
||||||
|
"defaultValue": "Delete",
|
||||||
|
"nested": true,
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "clickBehaviour",
|
||||||
|
"value": "details"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -4979,7 +5021,7 @@
|
||||||
"name": "Fields",
|
"name": "Fields",
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"type": "multifield",
|
"type": "fieldConfiguration",
|
||||||
"label": "Fields",
|
"label": "Fields",
|
||||||
"key": "fields",
|
"key": "fields",
|
||||||
"selectAllFields": true
|
"selectAllFields": true
|
||||||
|
@ -5028,6 +5070,17 @@
|
||||||
"invert": true
|
"invert": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"key": "saveButtonLabel",
|
||||||
|
"label": "Save button label",
|
||||||
|
"nested": true,
|
||||||
|
"defaultValue": "Save",
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "showSaveButton",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Allow delete",
|
"label": "Allow delete",
|
||||||
|
@ -5038,6 +5091,17 @@
|
||||||
"value": "Update"
|
"value": "Update"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"key": "deleteButtonLabel",
|
||||||
|
"label": "Delete button label",
|
||||||
|
"nested": true,
|
||||||
|
"defaultValue": "Delete",
|
||||||
|
"dependsOn": {
|
||||||
|
"setting": "showDeleteButton",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "url",
|
"type": "url",
|
||||||
"label": "Navigate after button press",
|
"label": "Navigate after button press",
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
export let titleButtonClickBehaviour
|
export let titleButtonClickBehaviour
|
||||||
export let onClickTitleButton
|
export let onClickTitleButton
|
||||||
export let noRowsMessage
|
export let noRowsMessage
|
||||||
|
export let sidePanelFields
|
||||||
|
export let sidePanelShowDelete
|
||||||
|
export let sidePanelSaveLabel
|
||||||
|
export let sidePanelDeleteLabel
|
||||||
|
|
||||||
const { fetchDatasourceSchema, API } = getContext("sdk")
|
const { fetchDatasourceSchema, API } = getContext("sdk")
|
||||||
const stateKey = `ID_${generate()}`
|
const stateKey = `ID_${generate()}`
|
||||||
|
@ -241,10 +245,12 @@
|
||||||
props={{
|
props={{
|
||||||
dataSource,
|
dataSource,
|
||||||
showSaveButton: true,
|
showSaveButton: true,
|
||||||
showDeleteButton: true,
|
showDeleteButton: sidePanelShowDelete,
|
||||||
|
saveButtonLabel: sidePanelSaveLabel,
|
||||||
|
deleteButtonLabel: sidePanelDeleteLabel,
|
||||||
actionType: "Update",
|
actionType: "Update",
|
||||||
rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`,
|
rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`,
|
||||||
fields: normalFields,
|
fields: sidePanelFields || normalFields,
|
||||||
title: editTitle,
|
title: editTitle,
|
||||||
labelPosition: "left",
|
labelPosition: "left",
|
||||||
}}
|
}}
|
||||||
|
@ -266,8 +272,9 @@
|
||||||
dataSource,
|
dataSource,
|
||||||
showSaveButton: true,
|
showSaveButton: true,
|
||||||
showDeleteButton: false,
|
showDeleteButton: false,
|
||||||
|
saveButtonLabel: sidePanelSaveLabel,
|
||||||
actionType: "Create",
|
actionType: "Create",
|
||||||
fields: normalFields,
|
fields: sidePanelFields || normalFields,
|
||||||
title: "Create Row",
|
title: "Create Row",
|
||||||
labelPosition: "left",
|
labelPosition: "left",
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
export let fields
|
export let fields
|
||||||
export let labelPosition
|
export let labelPosition
|
||||||
export let title
|
export let title
|
||||||
|
export let saveButtonLabel
|
||||||
|
export let deleteButtonLabel
|
||||||
export let showSaveButton
|
export let showSaveButton
|
||||||
export let showDeleteButton
|
export let showDeleteButton
|
||||||
export let rowId
|
export let rowId
|
||||||
|
@ -20,10 +22,40 @@
|
||||||
|
|
||||||
const { fetchDatasourceSchema } = getContext("sdk")
|
const { fetchDatasourceSchema } = getContext("sdk")
|
||||||
|
|
||||||
|
const convertOldFieldFormat = fields => {
|
||||||
|
if (typeof fields?.[0] === "string") {
|
||||||
|
return fields.map(field => ({ name: field, displayName: field }))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDefaultFields = (fields, schema) => {
|
||||||
|
if (schema && (!fields || fields.length === 0)) {
|
||||||
|
const defaultFields = []
|
||||||
|
|
||||||
|
Object.values(schema).forEach(field => {
|
||||||
|
if (field.autocolumn) return
|
||||||
|
|
||||||
|
defaultFields.push({
|
||||||
|
name: field.name,
|
||||||
|
displayName: field.name,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return defaultFields
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
let schema
|
let schema
|
||||||
let providerId
|
let providerId
|
||||||
let repeaterId
|
let repeaterId
|
||||||
|
|
||||||
|
$: formattedFields = convertOldFieldFormat(fields)
|
||||||
|
$: fieldsOrDefault = getDefaultFields(formattedFields, schema)
|
||||||
|
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: dataProvider = `{{ literal ${safe(providerId)} }}`
|
$: dataProvider = `{{ literal ${safe(providerId)} }}`
|
||||||
$: filter = [
|
$: filter = [
|
||||||
|
@ -46,9 +78,11 @@
|
||||||
actionType,
|
actionType,
|
||||||
size,
|
size,
|
||||||
disabled,
|
disabled,
|
||||||
fields,
|
fields: fieldsOrDefault,
|
||||||
labelPosition,
|
labelPosition,
|
||||||
title,
|
title,
|
||||||
|
saveButtonLabel,
|
||||||
|
deleteButtonLabel,
|
||||||
showSaveButton,
|
showSaveButton,
|
||||||
showDeleteButton,
|
showDeleteButton,
|
||||||
schema,
|
schema,
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
export let fields
|
export let fields
|
||||||
export let labelPosition
|
export let labelPosition
|
||||||
export let title
|
export let title
|
||||||
|
export let saveButtonLabel
|
||||||
|
export let deleteButtonLabel
|
||||||
export let showSaveButton
|
export let showSaveButton
|
||||||
export let showDeleteButton
|
export let showDeleteButton
|
||||||
export let schema
|
export let schema
|
||||||
|
@ -33,6 +35,12 @@
|
||||||
let formId
|
let formId
|
||||||
|
|
||||||
$: onSave = [
|
$: onSave = [
|
||||||
|
{
|
||||||
|
"##eventHandlerType": "Validate Form",
|
||||||
|
parameters: {
|
||||||
|
componentId: formId,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"##eventHandlerType": "Save Row",
|
"##eventHandlerType": "Save Row",
|
||||||
parameters: {
|
parameters: {
|
||||||
|
@ -163,7 +171,7 @@
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type="button"
|
type="button"
|
||||||
props={{
|
props={{
|
||||||
text: "Delete",
|
text: deleteButtonLabel || "Delete",
|
||||||
onClick: onDelete,
|
onClick: onDelete,
|
||||||
quiet: true,
|
quiet: true,
|
||||||
type: "secondary",
|
type: "secondary",
|
||||||
|
@ -175,7 +183,7 @@
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type="button"
|
type="button"
|
||||||
props={{
|
props={{
|
||||||
text: "Save",
|
text: saveButtonLabel || "Save",
|
||||||
onClick: onSave,
|
onClick: onSave,
|
||||||
type: "cta",
|
type: "cta",
|
||||||
}}
|
}}
|
||||||
|
@ -188,14 +196,14 @@
|
||||||
{/if}
|
{/if}
|
||||||
<BlockComponent type="fieldgroup" props={{ labelPosition }} order={1}>
|
<BlockComponent type="fieldgroup" props={{ labelPosition }} order={1}>
|
||||||
{#each fields as field, idx}
|
{#each fields as field, idx}
|
||||||
{#if getComponentForField(field)}
|
{#if getComponentForField(field.name)}
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type={getComponentForField(field)}
|
type={getComponentForField(field.name)}
|
||||||
props={{
|
props={{
|
||||||
field,
|
validation: field.validation,
|
||||||
label: field,
|
field: field.name,
|
||||||
placeholder: field,
|
label: field.displayName,
|
||||||
disabled,
|
placeholder: field.displayName,
|
||||||
}}
|
}}
|
||||||
order={idx}
|
order={idx}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue