From dc9b1a2a8cb1bf3a7648bbbd5753f2ce49b8f2d6 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 4 Nov 2021 11:30:43 +0000 Subject: [PATCH] Add button to table with search block and support defining multiple settings sections in component manifest entries --- .../builder/src/builderStore/dataBinding.js | 14 +-- .../src/builderStore/store/frontend.js | 14 +-- .../builder/src/builderStore/storeUtils.js | 19 ++++ .../ComponentSettingsSection.svelte | 40 ++++++- packages/client/manifest.json | 83 +++++++++----- .../client/src/components/app/Button.svelte | 3 + .../app/blocks/TableWithSearch.svelte | 106 ++++++++++++------ packages/client/src/utils/buttonActions.js | 8 +- packages/client/src/utils/componentProps.js | 16 +-- 9 files changed, 218 insertions(+), 85 deletions(-) diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index a19646e6fd..9003b3e06c 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -4,6 +4,7 @@ import { findAllMatchingComponents, findComponent, findComponentPath, + getComponentSettings, } from "./storeUtils" import { store } from "builderStore" import { queries as queriesStores, tables as tablesStore } from "stores/backend" @@ -82,13 +83,10 @@ export const getActionProviderComponents = (asset, componentId, actionType) => { * Gets a datasource object for a certain data provider component */ export const getDatasourceForProvider = (asset, component) => { - const def = store.actions.components.getDefinition(component?._component) - if (!def) { - return null - } + const settings = getComponentSettings(component._component) // If this component has a dataProvider setting, go up the stack and use it - const dataProviderSetting = def.settings.find(setting => { + const dataProviderSetting = settings.find(setting => { return setting.type === "dataProvider" }) if (dataProviderSetting) { @@ -100,7 +98,7 @@ export const getDatasourceForProvider = (asset, component) => { // Extract datasource from component instance const validSettingTypes = ["dataSource", "table", "schema"] - const datasourceSetting = def.settings.find(setting => { + const datasourceSetting = settings.find(setting => { return validSettingTypes.includes(setting.type) }) if (!datasourceSetting) { @@ -374,8 +372,8 @@ const buildFormSchema = component => { if (!component) { return schema } - const def = store.actions.components.getDefinition(component._component) - const fieldSetting = def?.settings?.find( + const settings = getComponentSettings(component._component) + const fieldSetting = settings.find( setting => setting.key === "field" && setting.type.startsWith("field/") ) if (fieldSetting && component.field) { diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 9110aa1430..1a5c4523e3 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -25,6 +25,7 @@ import { findClosestMatchingComponent, findAllMatchingComponents, findComponent, + getComponentSettings, } from "../storeUtils" import { uuid } from "../uuid" import { removeBindings } from "../dataBinding" @@ -368,14 +369,13 @@ export const getFrontendStore = () => { } // Generate default props + const settings = getComponentSettings(componentName) let props = { ...presetProps } - if (definition.settings) { - definition.settings.forEach(setting => { - if (setting.defaultValue !== undefined) { - props[setting.key] = setting.defaultValue - } - }) - } + settings.forEach(setting => { + if (setting.defaultValue !== undefined) { + props[setting.key] = setting.defaultValue + } + }) // Add any extra properties the component needs let extras = {} diff --git a/packages/builder/src/builderStore/storeUtils.js b/packages/builder/src/builderStore/storeUtils.js index 6d0f0beab0..e25949000f 100644 --- a/packages/builder/src/builderStore/storeUtils.js +++ b/packages/builder/src/builderStore/storeUtils.js @@ -1,3 +1,5 @@ +import { store } from "./index" + /** * Recursively searches for a specific component ID */ @@ -123,3 +125,20 @@ const searchComponentTree = (rootComponent, matchComponent) => { } return null } + +/** + * Searches a component's definition for a setting matching a certin predicate. + */ +export const getComponentSettings = componentType => { + const def = store.actions.components.getDefinition(componentType) + if (!def) { + return [] + } + let settings = def.settings?.filter(setting => !setting.section) ?? [] + def.settings + ?.filter(setting => setting.section) + .forEach(section => { + settings = settings.concat(section.settings || []) + }) + return settings +} diff --git a/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte b/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte index f047e9316b..f7123eb9e3 100644 --- a/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte @@ -22,6 +22,8 @@ ] $: settings = componentDefinition?.settings ?? [] + $: generalSettings = settings.filter(setting => !setting.section) + $: sections = settings.filter(setting => setting.section) $: isLayout = assetInstance && assetInstance.favicon $: assetDefinition = isLayout ? layoutDefinition : screenDefinition @@ -70,8 +72,8 @@ onChange={val => updateProp("_instanceName", val)} /> {/if} - {#if settings && settings.length > 0} - {#each settings as setting (setting.key)} + {#if generalSettings.length} + {#each generalSettings as setting (setting.key)} {#if canRenderControl(setting)} - {@html componentDefinition?.info} + {@html componentDefinition.info} {/if} +{#each sections as section (section.name)} + + {#each section.settings as setting (setting.key)} + {#if canRenderControl(setting)} + updateProp(setting.key, val)} + props={{ + options: setting.options || [], + placeholder: setting.placeholder || null, + min: setting.min || null, + max: setting.max || null, + }} + {bindings} + {componentDefinition} + /> + {/if} + {/each} + {#if section?.info} +
+ {@html section.info} +
+ {/if} +
+{/each} + diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 11aa033c1d..655f2f32e5 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -161,9 +161,15 @@ const confirmTextMap = { */ export const enrichButtonActions = (actions, context) => { // Prevent button actions in the builder preview - if (get(builderStore).inBuilder) { + if (!actions || get(builderStore).inBuilder) { return () => {} } + + // If this is a function then it has already been enriched + if (typeof actions === "function") { + return actions + } + const handlers = actions.map(def => handlerMap[def["##eventHandlerType"]]) return async () => { for (let i = 0; i < handlers.length; i++) { diff --git a/packages/client/src/utils/componentProps.js b/packages/client/src/utils/componentProps.js index a181d1ea4d..d600fdef04 100644 --- a/packages/client/src/utils/componentProps.js +++ b/packages/client/src/utils/componentProps.js @@ -36,17 +36,19 @@ export const enrichProps = (props, context) => { let enrichedProps = enrichDataBindings(props, totalContext) // Enrich click actions if they exist - if (enrichedProps.onClick) { - enrichedProps.onClick = enrichButtonActions( - enrichedProps.onClick, - totalContext - ) - } + Object.keys(enrichedProps).forEach(prop => { + if (prop?.toLowerCase().includes("onclick")) { + enrichedProps[prop] = enrichButtonActions( + enrichedProps[prop], + totalContext + ) + } + }) // Enrich any click actions in conditions if (enrichedProps._conditions) { enrichedProps._conditions.forEach(condition => { - if (condition.setting === "onClick") { + if (condition.setting?.toLowerCase().includes("onclick")) { condition.settingValue = enrichButtonActions( condition.settingValue, totalContext