diff --git a/packages/bbui/src/ActionButton/ActionButton.svelte b/packages/bbui/src/ActionButton/ActionButton.svelte index 427a98f888..0e6ec3d155 100644 --- a/packages/bbui/src/ActionButton/ActionButton.svelte +++ b/packages/bbui/src/ActionButton/ActionButton.svelte @@ -130,5 +130,6 @@ max-width: 150px; transform: translateX(-50%); text-align: center; + z-index: 1; } diff --git a/packages/bbui/src/DetailSummary/DetailSummary.svelte b/packages/bbui/src/DetailSummary/DetailSummary.svelte index e5d6fda86b..2cbb6796f3 100644 --- a/packages/bbui/src/DetailSummary/DetailSummary.svelte +++ b/packages/bbui/src/DetailSummary/DetailSummary.svelte @@ -78,7 +78,7 @@ var(--spacing-xl); } .property-panel.no-title { - padding: var(--spacing-xl); + padding-top: var(--spacing-xl); } .show { diff --git a/packages/bbui/src/Form/Field.svelte b/packages/bbui/src/Form/Field.svelte index 0c031b0235..1770438c3c 100644 --- a/packages/bbui/src/Form/Field.svelte +++ b/packages/bbui/src/Form/Field.svelte @@ -51,15 +51,13 @@ margin-top: var(--spectrum-global-dimension-size-75); align-items: center; } - .helpText :global(svg) { - width: 14px; - color: var(--grey-5); + width: 13px; + color: var(--spectrum-global-color-gray-600); margin-right: 6px; } - .helpText span { - color: var(--grey-7); + color: var(--spectrum-global-color-gray-800); font-size: var(--spectrum-global-dimension-font-size-75); } diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index d86e94aba2..52368a0723 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -465,8 +465,8 @@ const filterCategoryByContext = (component, context) => { const { _component } = component if (_component.endsWith("formblock")) { if ( - (component.actionType == "Create" && context.type === "schema") || - (component.actionType == "View" && context.type === "form") + (component.actionType === "Create" && context.type === "schema") || + (component.actionType === "View" && context.type === "form") ) { return false } @@ -474,20 +474,21 @@ const filterCategoryByContext = (component, context) => { return true } +// Enrich binding category information for certain components const getComponentBindingCategory = (component, context, def) => { let icon = def.icon let category = component._instanceName if (component._component.endsWith("formblock")) { - let contextCategorySuffix = { - form: "Fields", - schema: "Row", + if (context.type === "form") { + category = `${component._instanceName} - Fields` + icon = "Form" + } else if (context.type === "schema") { + category = `${component._instanceName} - Row` + icon = "Data" } - category = `${component._instanceName} - ${ - contextCategorySuffix[context.type] - }` - icon = context.type === "form" ? "Form" : "Data" } + return { icon, category, diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index aaa0eb0184..2d62a0667e 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -610,12 +610,12 @@ export const getFrontendStore = () => { // Use default config if the 'buttons' prop has never been initialised if (!("buttons" in enrichedComponent)) { enrichedComponent["buttons"] = - Utils.buildDynamicButtonConfig(enrichedComponent) + Utils.buildFormBlockButtonConfig(enrichedComponent) migrated = true } else if (enrichedComponent["buttons"] == null) { // Ignore legacy config if 'buttons' has been reset by 'resetOn' const { _id, actionType, dataSource } = enrichedComponent - enrichedComponent["buttons"] = Utils.buildDynamicButtonConfig({ + enrichedComponent["buttons"] = Utils.buildFormBlockButtonConfig({ _id, actionType, dataSource, @@ -1289,15 +1289,14 @@ export const getFrontendStore = () => { const settings = getComponentSettings(component._component) const updatedSetting = settings.find(setting => setting.key === name) - // Can be a single string or array of strings - const resetFields = settings.filter(setting => { - return ( + // Reset dependent fields + settings.forEach(setting => { + const needsReset = name === setting.resetOn || (Array.isArray(setting.resetOn) && setting.resetOn.includes(name)) - ) - }) - resetFields?.forEach(setting => { - component[setting.key] = null + if (needsReset) { + component[setting.key] = setting.defaultValue || null + } }) if ( diff --git a/packages/builder/src/components/design/settings/componentSettings.js b/packages/builder/src/components/design/settings/componentSettings.js index b740247294..f2d1520878 100644 --- a/packages/builder/src/components/design/settings/componentSettings.js +++ b/packages/builder/src/components/design/settings/componentSettings.js @@ -25,6 +25,8 @@ import BarButtonList from "./controls/BarButtonList.svelte" import FieldConfiguration from "./controls/FieldConfiguration/FieldConfiguration.svelte" import ButtonConfiguration from "./controls/ButtonConfiguration/ButtonConfiguration.svelte" import RelationshipFilterEditor from "./controls/RelationshipFilterEditor.svelte" +import FormStepConfiguration from "./controls/FormStepConfiguration.svelte" +import FormStepControls from "components/design/settings/controls/FormStepControls.svelte" const componentMap = { text: DrawerBindableInput, @@ -51,6 +53,8 @@ const componentMap = { url: URLSelect, fieldConfiguration: FieldConfiguration, buttonConfiguration: ButtonConfiguration, + stepConfiguration: FormStepConfiguration, + formStepControls: FormStepControls, columns: ColumnEditor, "columns/basic": BasicColumnEditor, "columns/grid": GridColumnEditor, diff --git a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte index ce91c8f7b5..63bfecf386 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte @@ -34,6 +34,9 @@ $: canAddButtons = max == null || buttonList.length < max const sanitizeValue = val => { + if (!Array.isArray(val)) { + return null + } return val?.map(button => { return button._component ? button : buildPseudoInstance(button) }) diff --git a/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte index cce11e4b17..384f9bf098 100644 --- a/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte +++ b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte @@ -13,6 +13,8 @@ export let draggable = true export let focus + let zoneType = generate() + let store = writable({ selected: null, actions: { @@ -46,6 +48,7 @@ return { id: listItemKey ? item[listItemKey] : generate(), item, + type: zoneType, } }) .filter(item => item.id) @@ -83,6 +86,8 @@ items: draggableItems, dropTargetStyle: { outline: "none" }, dragDisabled: !draggable || inactive, + type: zoneType, + dropFromOthersDisabled: true, }} on:finalize={handleFinalize} on:consider={updateRowOrder} diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte index 7f1ac1cf25..e864a4c2aa 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte @@ -14,6 +14,7 @@ import { convertOldFieldFormat, getComponentForField } from "./utils" export let componentInstance + export let bindings export let value const dispatch = createEventDispatcher() @@ -28,7 +29,9 @@ let selectAll = true - $: bindings = getBindableProperties($selectedScreen, componentInstance._id) + $: resolvedBindings = + bindings || getBindableProperties($selectedScreen, componentInstance._id) + $: actionType = componentInstance.actionType let componentBindings = [] @@ -39,7 +42,10 @@ ) } - $: datasource = getDatasourceForProvider($currentAsset, componentInstance) + $: datasource = + componentInstance.dataSource || + getDatasourceForProvider($currentAsset, componentInstance) + $: resourceId = datasource?.resourceId || datasource?.tableId $: if (!isEqual(value, cachedValue)) { @@ -179,7 +185,7 @@ listType={FieldSetting} listTypeProps={{ componentBindings, - bindings, + bindings: resolvedBindings, }} /> {/if} diff --git a/packages/builder/src/components/design/settings/controls/FormStepConfiguration.svelte b/packages/builder/src/components/design/settings/controls/FormStepConfiguration.svelte new file mode 100644 index 0000000000..bd28347e08 --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/FormStepConfiguration.svelte @@ -0,0 +1,171 @@ + + +
+ +
+ + diff --git a/packages/builder/src/components/design/settings/controls/FormStepControls.svelte b/packages/builder/src/components/design/settings/controls/FormStepControls.svelte new file mode 100644 index 0000000000..638d80945d --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/FormStepControls.svelte @@ -0,0 +1,84 @@ + + +{#if stepCount === 1} +
+ { + stepAction("addStep") + }} + > + Add Step + +
+{:else} +
+ { + stepAction("previousStep") + }} + tooltip={"Previous step"} + /> + { + stepAction("nextStep") + }} + tooltip={"Next step"} + /> + { + stepAction("removeStep") + }} + tooltip={"Remove step"} + /> + { + stepAction("addStep") + }} + tooltip={"Add step"} + /> +
+{/if} + + diff --git a/packages/builder/src/components/design/settings/controls/PropertyControl.svelte b/packages/builder/src/components/design/settings/controls/PropertyControl.svelte index a6f3d1b218..c20dd9310b 100644 --- a/packages/builder/src/components/design/settings/controls/PropertyControl.svelte +++ b/packages/builder/src/components/design/settings/controls/PropertyControl.svelte @@ -24,6 +24,7 @@ export let propertyFocus = false export let info = null export let disableBindings = false + export let wide $: nullishValue = value == null || value === "" $: allBindings = getAllBindings(bindings, componentBindings, nested) @@ -78,7 +79,7 @@
@@ -104,6 +105,7 @@ {...props} on:drawerHide on:drawerShow + on:meta />
{#if info} @@ -146,15 +148,28 @@ .control { position: relative; } - .property-control.wide .control { - grid-column: 1 / -1; - } .text { font-size: var(--spectrum-global-dimension-font-size-75); color: var(--grey-6); grid-column: 2 / 2; } + + .property-control.wide .control { + flex: 1; + } + .property-control.wide { + grid-template-columns: unset; + display: flex; + flex-direction: column; + width: 100%; + } + .property-control.wide > * { + width: 100%; + } .property-control.wide .text { grid-column: 1 / -1; } + .property-control.wide .label { + margin-bottom: -8px; + } diff --git a/packages/builder/src/components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte b/packages/builder/src/components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte index cd50f526b5..25c7651d35 100644 --- a/packages/builder/src/components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte +++ b/packages/builder/src/components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte @@ -12,7 +12,10 @@ } from "@budibase/bbui" import { currentAsset, selectedComponent } from "builderStore" import { findClosestMatchingComponent } from "builderStore/componentUtils" - import { getSchemaForDatasource } from "builderStore/dataBinding" + import { + getSchemaForDatasource, + getDatasourceForProvider, + } from "builderStore/dataBinding" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" import { generate } from "shortid" @@ -124,6 +127,12 @@ ], } + const resolveDatasource = (currentAsset, componentInstance, parent) => { + return ( + getDatasourceForProvider(currentAsset, parent || componentInstance) || {} + ) + } + $: dataSourceSchema = getDataSourceSchema($currentAsset, $selectedComponent) $: field = fieldName || $selectedComponent?.field $: schemaRules = parseRulesFromSchema(field, dataSourceSchema || {}) @@ -146,8 +155,8 @@ component._component.endsWith("/formblock") || component._component.endsWith("/tableblock") ) - - return getSchemaForDatasource(asset, formParent?.dataSource) + const dataSource = resolveDatasource(asset, component, formParent) + return getSchemaForDatasource(asset, dataSource) } const parseRulesFromSchema = (field, dataSourceSchema) => { diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte index df97561013..27f0f7bf7a 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte @@ -32,21 +32,19 @@ const generalSettings = settings.filter( setting => !setting.section && setting.tag === tag ) - const customSections = settings.filter( setting => setting.section && setting.tag === tag ) - let sections = [ - ...(generalSettings?.length - ? [ - { - name: "General", - settings: generalSettings, - }, - ] - : []), - ...(customSections || []), - ] + let sections = [] + if (generalSettings.length) { + sections.push({ + name: "General", + settings: generalSettings, + }) + } + if (customSections.length) { + sections = sections.concat(customSections) + } // Filter out settings which shouldn't be rendered sections.forEach(section => { @@ -153,6 +151,7 @@ {#if section.info}
@@ -172,6 +171,7 @@ control={getComponentForSetting(setting)} label={setting.label} labelHidden={setting.labelHidden} + wide={setting.wide} key={setting.key} value={componentInstance[setting.key]} defaultValue={setting.defaultValue} @@ -208,7 +208,7 @@ {/if} {/each} -{#if componentDefinition?.block && !tag} +{#if componentDefinition?.block && !tag && componentDefinition.ejectable !== false} diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json index dd129be11e..96e8faf93c 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json @@ -8,6 +8,7 @@ "cardsblock", "repeaterblock", "formblock", + "multistepformblock", "chartblock", "rowexplorer" ] diff --git a/packages/client/manifest.json b/packages/client/manifest.json index b59f6d0fad..1c62b90a64 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -4879,7 +4879,7 @@ }, "chartblock": { "block": true, - "name": "Chart block", + "name": "Chart Block", "icon": "GraphPie", "hasChildren": false, "settings": [ @@ -5369,7 +5369,7 @@ }, "tableblock": { "block": true, - "name": "Table block", + "name": "Table Block", "icon": "Table", "styles": ["size"], "size": { @@ -5615,7 +5615,7 @@ }, "cardsblock": { "block": true, - "name": "Cards block", + "name": "Cards Block", "icon": "PersonalizationField", "styles": ["size"], "size": { @@ -5795,7 +5795,7 @@ }, "repeaterblock": { "block": true, - "name": "Repeater block", + "name": "Repeater Block", "icon": "ViewList", "illegalChildren": ["section"], "hasChildren": true, @@ -6035,6 +6035,164 @@ } ] }, + "multistepformblock": { + "name": "Multi-step Form Block", + "icon": "AssetsAdded", + "block": true, + "hasChildren": false, + "ejectable": false, + "size": { + "width": 400, + "height": 400 + }, + "styles": ["size"], + "settings": [ + { + "type": "table", + "label": "Data", + "key": "dataSource" + }, + { + "type": "radio", + "label": "Type", + "key": "actionType", + "options": ["Create", "Update", "View"], + "defaultValue": "Create" + }, + { + "section": true, + "dependsOn": { + "setting": "actionType", + "value": "Create", + "invert": true + }, + "name": "Row ID", + "info": "How to pass a row ID using bindings", + "settings": [ + { + "type": "text", + "label": "Row ID", + "key": "rowId", + "nested": true + }, + { + "type": "text", + "label": "No rows found", + "key": "noRowsMessage", + "defaultValue": "We couldn't find a row to display", + "nested": true + } + ] + }, + { + "section": true, + "name": "Details", + "settings": [ + { + "type": "stepConfiguration", + "key": "steps", + "nested": true, + "labelHidden": true, + "resetOn": [ + "dataSource", + "actionType" + ], + "defaultValue": [ + {} + ] + } + ] + } + ], + "actions": [ + { + "type": "ValidateForm", + "suffix": "form" + }, + { + "type": "ClearForm", + "suffix": "form" + }, + { + "type": "UpdateFieldValue", + "suffix": "form" + }, + { + "type": "ScrollTo", + "suffix": "form" + }, + { + "type": "ChangeFormStep", + "suffix": "form" + } + ], + "context": [ + { + "type": "form", + "suffix": "form" + }, + { + "type": "static", + "suffix": "form", + "values": [ + { + "label": "Value", + "key": "__value", + "type": "object" + }, + { + "label": "Valid", + "key": "__valid", + "type": "boolean" + }, + { + "label": "Current Step", + "key": "__currentStep", + "type": "number" + }, + { + "label": "Current Step Valid", + "key": "__currentStepValid", + "type": "boolean" + } + ] + } + ] + }, + "multistepformblockstep": { + "name": "Multi-step Form Block Step", + "settings": [ + { + "type": "formStepControls", + "label": "Steps", + "key": "steps" + }, + { + "type": "text", + "label": "Title", + "key": "title", + "nested": true + }, + { + "type": "text", + "label": "Description", + "key": "desc", + "nested": true + }, + { + "type": "fieldConfiguration", + "key": "fields", + "nested": true + }, + { + "type": "buttonConfiguration", + "label": "Buttons", + "key": "buttons", + "wide": true, + "nested": true + } + ] + }, "formblock": { "name": "Form Block", "icon": "Form", @@ -6290,7 +6448,7 @@ } }, "gridblock": { - "name": "Grid block", + "name": "Grid Block", "icon": "Table", "styles": ["size"], "size": { diff --git a/packages/client/src/components/app/blocks/MultiStepFormblock.svelte b/packages/client/src/components/app/blocks/MultiStepFormblock.svelte new file mode 100644 index 0000000000..0573ba15b5 --- /dev/null +++ b/packages/client/src/components/app/blocks/MultiStepFormblock.svelte @@ -0,0 +1,187 @@ + + + + + {#each enrichedSteps as step, stepIdx} + + + + + + + + {#each step.fields as field, fieldIdx (`${field.field || field.name}_${stepIdx}_${fieldIdx}`)} + {#if getComponentForField(field)} + + {/if} + {/each} + + + + + {/each} + + diff --git a/packages/client/src/components/app/blocks/TableBlock.svelte b/packages/client/src/components/app/blocks/TableBlock.svelte index c8b6a07e3d..04a7134ca0 100644 --- a/packages/client/src/components/app/blocks/TableBlock.svelte +++ b/packages/client/src/components/app/blocks/TableBlock.svelte @@ -265,7 +265,7 @@ props={{ dataSource, buttonPosition: "top", - buttons: Utils.buildDynamicButtonConfig({ + buttons: Utils.buildFormBlockButtonConfig({ _id: $component.id + "-form-edit", showDeleteButton: deleteLabel !== "", showSaveButton: true, @@ -299,7 +299,7 @@ props={{ dataSource, buttonPosition: "top", - buttons: Utils.buildDynamicButtonConfig({ + buttons: Utils.buildFormBlockButtonConfig({ _id: $component.id + "-form-new", showDeleteButton: false, showSaveButton: true, diff --git a/packages/client/src/components/app/blocks/form/FormBlock.svelte b/packages/client/src/components/app/blocks/form/FormBlock.svelte index f23ecf451d..cdf1a05628 100644 --- a/packages/client/src/components/app/blocks/form/FormBlock.svelte +++ b/packages/client/src/components/app/blocks/form/FormBlock.svelte @@ -1,10 +1,8 @@ - - {#if actionType === "Create"} - - - - {:else} - - - - - - {/if} - + + + diff --git a/packages/client/src/components/app/blocks/form/FormBlockWrapper.svelte b/packages/client/src/components/app/blocks/form/FormBlockWrapper.svelte new file mode 100644 index 0000000000..26767f84b3 --- /dev/null +++ b/packages/client/src/components/app/blocks/form/FormBlockWrapper.svelte @@ -0,0 +1,64 @@ + + + + {#if actionType === "Create"} + + + + {:else} + + + + + + {/if} + diff --git a/packages/client/src/components/app/blocks/index.js b/packages/client/src/components/app/blocks/index.js index f74d2f0e12..2c8d81cf96 100644 --- a/packages/client/src/components/app/blocks/index.js +++ b/packages/client/src/components/app/blocks/index.js @@ -4,3 +4,4 @@ export { default as repeaterblock } from "./RepeaterBlock.svelte" export { default as formblock } from "./form/FormBlock.svelte" export { default as chartblock } from "./ChartBlock.svelte" export { default as rowexplorer } from "./RowExplorer.svelte" +export { default as multistepformblock } from "./MultiStepFormblock.svelte" diff --git a/packages/client/src/components/app/forms/Field.svelte b/packages/client/src/components/app/forms/Field.svelte index 22420b7430..adf5d10df7 100644 --- a/packages/client/src/components/app/forms/Field.svelte +++ b/packages/client/src/components/app/forms/Field.svelte @@ -137,21 +137,23 @@ width: 100%; } + .error :global(svg), + .helpText :global(svg) { + width: 13px; + margin-right: 6px; + } + .error { display: flex; margin-top: var(--spectrum-global-dimension-size-75); align-items: center; } - .error :global(svg) { - width: 14px; color: var( --spectrum-semantic-negative-color-default, var(--spectrum-global-color-red-500) ); - margin-right: 4px; } - .error span { color: var( --spectrum-semantic-negative-color-default, @@ -165,17 +167,14 @@ margin-top: var(--spectrum-global-dimension-size-75); align-items: center; } - .helpText :global(svg) { - width: 14px; - color: var(--grey-7); - margin-right: 6px; + color: var(--spectrum-global-color-gray-600); } - .helpText span { - color: var(--grey-5); + color: var(--spectrum-global-color-gray-800); font-size: var(--spectrum-global-dimension-font-size-75); } + .spectrum-FieldLabel--right, .spectrum-FieldLabel--left { padding-right: var(--spectrum-global-dimension-size-200); diff --git a/packages/client/src/components/app/forms/Form.svelte b/packages/client/src/components/app/forms/Form.svelte index 1a740585f3..94559ab7be 100644 --- a/packages/client/src/components/app/forms/Form.svelte +++ b/packages/client/src/components/app/forms/Form.svelte @@ -34,7 +34,7 @@ let loaded = false let schema let table - let currentStep = writable(getInitialFormStep()) + let currentStep = getContext("current-step") || writable(getInitialFormStep()) $: fetchSchema(dataSource) $: schemaKey = generateSchemaKey(schema) diff --git a/packages/client/src/components/app/forms/InnerForm.svelte b/packages/client/src/components/app/forms/InnerForm.svelte index 6ebe9de7ec..9d0db43bf4 100644 --- a/packages/client/src/components/app/forms/InnerForm.svelte +++ b/packages/client/src/components/app/forms/InnerForm.svelte @@ -423,10 +423,14 @@ } const fieldId = field.fieldState.fieldId const fieldElement = document.getElementById(fieldId) - fieldElement.focus({ preventScroll: true }) + if (fieldElement) { + fieldElement.focus({ preventScroll: true }) + } const label = document.querySelector(`label[for="${fieldId}"]`) - label.style.scrollMargin = "100px" - label.scrollIntoView({ behavior: "smooth", block: "nearest" }) + if (label) { + label.style.scrollMargin = "100px" + label.scrollIntoView({ behavior: "smooth", block: "nearest" }) + } } // Action context to pass to children diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 044c900c9d..a3cb4206c3 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -76,6 +76,8 @@ const loadBudibase = async () => { } else { dndStore.actions.reset() } + } else if (type === "builder-meta") { + builderStore.actions.setMetadata(data) } } diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index f92e6b9df9..4a9eaa8d2f 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -18,6 +18,7 @@ const createBuilderStore = () => { hiddenComponentIds: [], usedPlugins: null, eventResolvers: {}, + metadata: null, // Legacy - allow the builder to specify a layout layout: null, @@ -123,6 +124,12 @@ const createBuilderStore = () => { parentType, }) }, + setMetadata: metadata => { + store.update(state => ({ + ...state, + metadata, + })) + }, } return { ...store, diff --git a/packages/frontend-core/src/utils/utils.js b/packages/frontend-core/src/utils/utils.js index 93884c719f..65690cd535 100644 --- a/packages/frontend-core/src/utils/utils.js +++ b/packages/frontend-core/src/utils/utils.js @@ -116,7 +116,7 @@ export const domDebounce = callback => { * * @param {any} props * */ -export const buildDynamicButtonConfig = props => { +export const buildFormBlockButtonConfig = props => { const { _id, actionType, @@ -130,7 +130,6 @@ export const buildDynamicButtonConfig = props => { } = props || {} if (!_id) { - console.log("MISSING ID") return } const formId = `${_id}-form` @@ -228,7 +227,7 @@ export const buildDynamicButtonConfig = props => { }) } - if (actionType == "Update" && showDeleteButton !== false) { + if (actionType === "Update" && showDeleteButton !== false) { defaultButtons.push({ text: deleteText || "Delete", _id: Helpers.uuid(), @@ -241,3 +240,108 @@ export const buildDynamicButtonConfig = props => { return defaultButtons } + +export const buildMultiStepFormBlockDefaultProps = props => { + const { _id, stepCount, currentStep, actionType, dataSource } = props || {} + + // Sanity check + if (!_id || !stepCount) { + return + } + + const title = `Step {{ [${_id}-form].[__currentStep] }}` + const resourceId = dataSource?.resourceId + const formId = `${_id}-form` + let buttons = [] + + // Add previous step button if we aren't the first step + if (currentStep !== 0) { + buttons.push({ + _id: Helpers.uuid(), + _component: "@budibase/standard-components/button", + _instanceName: Helpers.uuid(), + text: "Back", + type: "secondary", + size: "M", + onClick: [ + { + parameters: { + type: "prev", + componentId: formId, + }, + "##eventHandlerType": "Change Form Step", + }, + ], + }) + } + + // Add a next button if we aren't the last step + if (currentStep !== stepCount - 1) { + buttons.push({ + _id: Helpers.uuid(), + _component: "@budibase/standard-components/button", + _instanceName: Helpers.uuid(), + text: "Next", + type: "cta", + size: "M", + onClick: [ + { + "##eventHandlerType": "Validate Form", + parameters: { + componentId: formId, + }, + }, + { + parameters: { + type: "next", + componentId: formId, + }, + "##eventHandlerType": "Change Form Step", + }, + ], + }) + } + + // Add save button if we are the last step + if (actionType !== "View" && currentStep === stepCount - 1) { + buttons.push({ + _id: Helpers.uuid(), + _component: "@budibase/standard-components/button", + _instanceName: Helpers.uuid(), + text: "Save", + type: "cta", + size: "M", + onClick: [ + { + "##eventHandlerType": "Validate Form", + parameters: { + componentId: formId, + }, + }, + { + "##eventHandlerType": "Save Row", + parameters: { + tableId: resourceId, + providerId: formId, + }, + }, + // Clear a create form once submitted + ...(actionType !== "Create" + ? [] + : [ + { + "##eventHandlerType": "Clear Form", + parameters: { + componentId: formId, + }, + }, + ]), + ], + }) + } + + return { + buttons, + title, + } +}