From 5de81c624fe72ffda1d5f2a8493f77715367742a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 6 Sep 2024 11:16:27 +0100 Subject: [PATCH] Expose refresh datasource action from form blocks and add row action button templates --- .../ButtonConfiguration.svelte | 21 ++++- .../Component/ComponentSettingsSection.svelte | 11 +++ packages/builder/src/templates/rowActions.js | 91 +++++++++++++++++++ packages/client/manifest.json | 8 ++ 4 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 packages/builder/src/templates/rowActions.js 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 db2289345f..212f96bdca 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte @@ -6,6 +6,7 @@ import { componentStore } from "stores/builder" import { getEventContextBindings } from "dataBinding" import { cloneDeep, isEqual } from "lodash/fp" + import { getRowActionButtonTemplates } from "templates/rowActions" export let componentInstance export let componentBindings @@ -19,11 +20,14 @@ let focusItem let cachedValue + let rowActionTemplates = [] + $: console.log(rowActionTemplates) + + $: getRowActionTemplates(componentInstance) $: if (!isEqual(value, cachedValue)) { cachedValue = cloneDeep(value) } - $: buttonList = sanitizeValue(cachedValue) || [] $: buttonCount = buttonList.length $: eventContextBindings = getEventContextBindings({ @@ -39,6 +43,11 @@ } $: canAddButtons = max == null || buttonList.length < max + const getRowActionTemplates = async instance => { + const templates = await getRowActionButtonTemplates({ component: instance }) + rowActionTemplates = templates + } + const sanitizeValue = val => { if (!Array.isArray(val)) { return null @@ -79,9 +88,13 @@ } const addButton = () => { - const newButton = buildPseudoInstance({ - text: `Button ${buttonCount + 1}`, - }) + // const newButton = buildPseudoInstance({ + // text: `Button ${buttonCount + 1}`, + // }) + const newButton = rowActionTemplates[0] + if (!newButton) { + return + } dispatch("change", [...buttonList, newButton]) focusItem = newButton._id } 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 d5a696c6bf..a433e5ae79 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 @@ -8,6 +8,7 @@ import InfoDisplay from "./InfoDisplay.svelte" import analytics, { Events } from "analytics" import { shouldDisplaySetting } from "@budibase/frontend-core" + import { getContext, setContext } from "svelte" export let componentDefinition export let componentInstance @@ -19,6 +20,16 @@ export let includeHidden = false export let tag + // Sometimes we render component settings using a complicated nested + // component instance technique. This results in instances with IDs that + // don't exist anywhere in the tree. Therefore we need to keep track of + // what the real component tree ID is so we can always find it. + const rootId = getContext("rootId") + if (!rootId) { + setContext("rootId", componentInstance._id) + } + componentInstance._rootId = rootId || componentInstance._id + $: sections = getSections( componentInstance, componentDefinition, diff --git a/packages/builder/src/templates/rowActions.js b/packages/builder/src/templates/rowActions.js new file mode 100644 index 0000000000..df02424f21 --- /dev/null +++ b/packages/builder/src/templates/rowActions.js @@ -0,0 +1,91 @@ +import { get } from "svelte/store" +import { getDatasourceForProvider } from "dataBinding" +import { rowActions, selectedScreen, componentStore } from "stores/builder" +import { Helpers } from "@budibase/bbui" +import { findComponent } from "helpers/components" + +export const getRowActionButtonTemplates = async ({ screen, component }) => { + if (!component) { + return [] + } + if (!screen) { + screen = get(selectedScreen) + } + const id = component._rootId + const instance = findComponent(screen?.props, id) + if (!instance) { + return [] + } + + // The row ID binding depends on what component this is. + // Therefore we need to whitelist this to only function for certain components. + const type = instance?._component + const isGridBlock = type?.endsWith("/gridblock") + const isFormBlock = + type?.endsWith("/formblock") || type?.endsWith("/multistepformblock") + if (!isGridBlock && !isFormBlock) { + return [] + } + + // Check we have a valid datasource that can contain row actions + const ds = getDatasourceForProvider(get(selectedScreen), instance) + if (ds?.type !== "table" && ds?.type !== "viewV2") { + return [] + } + const resourceId = ds.id || ds.tableId + if (!resourceId) { + return [] + } + await rowActions.refreshRowActions(resourceId) + const enabledActions = get(rowActions)[resourceId] || [] + + // Generate the row ID binding depending on the component + let rowIdBinding + if (isGridBlock) { + rowIdBinding = `{{ [${instance._id}].[_id] }}` + } else if (isFormBlock) { + rowIdBinding = `{{ [${instance._id}-repeater].[_id] }}` + } + + // Create templates + return enabledActions.map(action => { + // Create a button instance + const button = componentStore.createInstance( + `@budibase/standard-components/button`, + { + _instanceName: Helpers.uuid(), + text: action.name, + type: "primary", + } + ) + + // Row action button action + const onClick = [ + { + parameters: { + rowActionId: action.id, + resourceId, + rowId: rowIdBinding, + }, + "##eventHandlerType": "Row Action", + id: Helpers.uuid(), + }, + ] + + // For form blocks we need to manually refresh the form after running the action + if (isFormBlock) { + onClick.push({ + parameters: { + componentId: `${instance._id}-provider`, + }, + "##eventHandlerType": "Refresh Data Provider", + id: Helpers.uuid(), + }) + } + + return { + ...button, + onClick, + } + }) +} diff --git a/packages/client/manifest.json b/packages/client/manifest.json index eefe3a74b1..40d91bf628 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -6998,6 +6998,10 @@ { "type": "ChangeFormStep", "suffix": "form" + }, + { + "type": "RefreshDatasource", + "suffix": "provider" } ], "context": [ @@ -7241,6 +7245,10 @@ { "type": "ScrollTo", "suffix": "form" + }, + { + "type": "RefreshDatasource", + "suffix": "provider" } ], "context": [