From d5da75c031b7ac560d4e6d2801937462ddb8eb9d Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 21 Nov 2024 10:39:55 +0100 Subject: [PATCH] feat: allow labeled values in app multiselect --- .../components/inputs/AppMultiSelectV2.svelte | 72 +++++++--- .../apps/components/inputs/AppSelect.svelte | 14 +- .../components/inputs/AppSelectStep.svelte | 21 ++- .../apps/editor/component/components.ts | 16 +-- .../ArrayStaticInputEditor.svelte | 132 ++++++++++-------- .../settingsPanel/ComponentPanel.svelte | 4 +- .../inputEditor/StaticInputEditor.svelte | 6 + frontend/src/lib/components/apps/inputType.ts | 2 +- .../propertyPicker/ObjectViewer.svelte | 2 +- 9 files changed, 168 insertions(+), 101 deletions(-) diff --git a/frontend/src/lib/components/apps/components/inputs/AppMultiSelectV2.svelte b/frontend/src/lib/components/apps/components/inputs/AppMultiSelectV2.svelte index 261fea0317..d0c0394877 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppMultiSelectV2.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppMultiSelectV2.svelte @@ -16,6 +16,7 @@ import { offset, flip, shift } from 'svelte-floating-ui/dom' import ResolveStyle from '../helpers/ResolveStyle.svelte' import MultiSelect from '$lib/components/multiselect/MultiSelect.svelte' + import { deepEqual } from 'fast-equals' export let id: string export let configuration: RichConfigurations @@ -30,7 +31,7 @@ const { app, worldStore, selectedComponent, componentControl } = getContext('AppViewerContext') - let items: string[] + let items: (string | { value: string; label: any })[] = [] const resolvedConfig = initConfig( components['multiselectcomponent'].initialData.configuration, @@ -41,12 +42,33 @@ result: [] as string[] }) - let value: string[] | undefined = [...new Set(outputs?.result.peak())] as string[] + let selectedItems: (string | { value: string; label: any })[] | undefined = [ + ...new Set(outputs?.result.peak()) + ] as string[] + + function setResultsFromSelectedItems() { + outputs?.result.set([ + ...(selectedItems?.map((item) => { + if (typeof item == 'object' && item.value != undefined && item.label != undefined) { + return item?.value ?? `NOT_STRING` + } else if (typeof item == 'string') { + return item + } else if (typeof item == 'object' && item.label != undefined) { + return item.label + } else { + return 'NOT_STRING' + } + }) ?? []) + ]) + } $componentControl[id] = { setValue(nvalue: string[]) { - value = [...new Set(nvalue)] - outputs?.result.set([...(value ?? [])]) + if (Array.isArray(nvalue)) { + setSelectedItemsFromValues(nvalue) + } else { + console.error('Invalid value for multiselect component, expected array', nvalue) + } } } @@ -54,21 +76,33 @@ function handleItems() { if (Array.isArray(resolvedConfig.items)) { - items = resolvedConfig.items?.map((label) => { - return typeof label === 'string' ? label : `NOT_STRING` + items = resolvedConfig.items?.map((item) => { + if (typeof item == 'object' && item.value != undefined && item.label != undefined) { + return item + } + return typeof item === 'string' ? item : `NOT_STRING` }) } } - $: resolvedConfig.defaultItems && handleDefaultItems() + $: resolvedConfig.defaultItems && setSelectedItemsFromValues(resolvedConfig.defaultItems) - function handleDefaultItems() { - if (Array.isArray(resolvedConfig.defaultItems)) { - const nvalue = resolvedConfig.defaultItems?.map((label) => { - return typeof label === 'string' ? label : `NOT_STRING` - }) - value = [...new Set(nvalue)] - outputs?.result.set([...(value ?? [])]) + function setSelectedItemsFromValues(values: any[]) { + if (Array.isArray(values)) { + const nvalue = values + .map((value) => { + return ( + items.find((item) => { + if (typeof item == 'object' && item.value != undefined && item.label != undefined) { + return deepEqual(item.value, value) + } + return item == value + }) ?? (typeof value == 'string' ? value : undefined) + ) + }) + .filter((item) => item != undefined) + selectedItems = [...new Set(nvalue)] + setResultsFromSelectedItems() } } @@ -143,7 +177,7 @@ use:floatingRef bind:clientWidth={w} > - {#if !value || Array.isArray(value)} + {#if !selectedItems || Array.isArray(selectedItems)} { @@ -181,7 +215,7 @@ e.target?.['parentElement']?.dispatchEvent(newe) }} > - {option} + {typeof option == 'object' ? option?.label ?? 'NO_LABEL' : option} @@ -197,7 +231,7 @@ {:else} - Value {value} is not an array + Value {selectedItems} is not an array {/if} diff --git a/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte b/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte index 27f01e973e..6d7abcaf55 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte @@ -92,7 +92,12 @@ function handleItems() { listItems = Array.isArray(resolvedConfig.items) ? resolvedConfig.items.map((item) => { - if (!item || typeof item !== 'object') { + if (typeof item == 'string') { + return { + label: item, + value: JSON.stringify(item) + } + } else if (!item || typeof item !== 'object') { console.error('Select component items should be an array of objects') return { label: 'not object', @@ -113,7 +118,12 @@ if (resolvedConfig.defaultValue !== undefined) { rawValue = resolvedConfig.defaultValue } else if (listItems.length > 0 && resolvedConfig?.preselectFirst) { - rawValue = resolvedConfig.items[0].value + let firstItem = resolvedConfig.items[0] + if (typeof firstItem === 'string') { + rawValue = firstItem + } else { + rawValue = resolvedConfig.items[0].value + } } if (rawValue !== undefined && rawValue !== null) { value = JSON.stringify(rawValue) diff --git a/frontend/src/lib/components/apps/components/inputs/AppSelectStep.svelte b/frontend/src/lib/components/apps/components/inputs/AppSelectStep.svelte index f829de71e6..b6fb3a6e3c 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppSelectStep.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppSelectStep.svelte @@ -37,10 +37,11 @@ $componentControl[id] = { setValue(nvalue: string) { selected = nvalue - selectedIndex = resolvedConfig.items.findIndex((item) => item.value === nvalue) + selectedIndex = resolvedConfig.items.findIndex((item) => getValue(item) === nvalue) }, setTab(index) { - selected = resolvedConfig.items?.[index]?.value + let item = resolvedConfig.items?.[index] + selected = getValue(item) selectedIndex = index } } @@ -48,11 +49,11 @@ function setDefaultValue() { if (resolvedConfig.defaultValue != undefined) { selectedIndex = resolvedConfig.items.findIndex( - (item) => item.value === resolvedConfig.defaultValue + (item) => getValue(item) === resolvedConfig.defaultValue ) } if (selectedIndex === -1 || resolvedConfig.defaultValue == undefined) { - selected = resolvedConfig.items[0].value + selected = getValue(resolvedConfig.items[0]) } else if (resolvedConfig.defaultValue) { selected = resolvedConfig.items[selectedIndex].value } @@ -72,6 +73,13 @@ } } + function getValue(item: string | { label: string; value: string }) { + return typeof item == 'string' ? item : item.value + } + function getLabel(item: string | { label: string; value: string }) { + return typeof item == 'string' ? item : item.label + } + let css = initCss($app.css?.selectstepcomponent, customCss) $: selected && handleSelection(selected) @@ -106,14 +114,15 @@ >
item.label)} + tabs={(resolvedConfig?.items ?? []).map((item) => getLabel(item))} hasValidations={false} allowStepNavigation={true} {selectedIndex} on:click={(e) => { const index = e.detail.index selectedIndex = index - outputs?.result.set(resolvedConfig?.items[index].value) + let item = resolvedConfig?.items[index] + outputs?.result.set(getValue(item)) }} />
diff --git a/frontend/src/lib/components/apps/editor/component/components.ts b/frontend/src/lib/components/apps/editor/component/components.ts index 34b6faca78..93bb1abd73 100644 --- a/frontend/src/lib/components/apps/editor/component/components.ts +++ b/frontend/src/lib/components/apps/editor/component/components.ts @@ -2199,13 +2199,12 @@ This is a paragraph. items: { type: 'static', fieldType: 'array', - subFieldType: 'text', - value: ['Foo', 'Bar'] + subFieldType: 'labeledselect', + value: ['Foo', 'Bar'] as (string | { label: string; value: string })[] } as StaticAppInput, defaultItems: { type: 'static', - fieldType: 'array', - subFieldType: 'text', + fieldType: 'object', value: [] } as StaticAppInput, placeholder: { @@ -3384,7 +3383,7 @@ See date-fns format for more information. By default, it is 'dd.MM.yyyy HH:mm' value: [ { value: 'foo', label: 'Foo' }, { value: 'bar', label: 'Bar' } - ] + ] as (string | { label: string; value: string })[] } as StaticAppInput, defaultValue: { @@ -3419,14 +3418,11 @@ See date-fns format for more information. By default, it is 'dd.MM.yyyy HH:mm' type: 'static', fieldType: 'array', subFieldType: 'labeledselect', - value: [ - { value: 'foo', label: 'Foo' }, - { value: 'bar', label: 'Bar' } - ] + value: ['Foo', 'Bar'] as (string | { label: string; value: string })[] } as StaticAppInput, defaultValue: { type: 'static', - value: undefined as { value: string; label: string } | undefined, + value: undefined as any, fieldType: 'object' } } diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte index 093b2a49a6..98b8ad64d6 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte @@ -218,33 +218,43 @@ } let raw: boolean = false - // let mounted = false - // $: if (componentInput.value && mounted) { - // const newItems = (Array.isArray(componentInput.value) ? componentInput.value : []) - // .filter((x) => x != undefined) - // .map((item, index) => { - // return { value: item, id: generateRandomString() } - // }) - - // if ( - // JSON.stringify(newItems.map((i) => i.value)) !== JSON.stringify(items.map((i) => i.value)) - // ) { - // items = newItems - // } - // } - - // onMount(() => { - // mounted = true - // }) + let refreshCount = 0
{#if Array.isArray(items) && componentInput.value} -
+
{pluralize(items.length, 'item')}
- - {#if subFieldType === 'ag-grid' || subFieldType === 'table-column'} + {#if subFieldType == 'labeledselect'} + { + if (e.detail) { + items = items.map((item) => { + if (typeof item.value === 'string') { + return { ...item, value: { label: item.value, value: item.value } } + } else { + return item + } + }) + } else { + items = items.map((item) => { + if (typeof item.value === 'object' && item.value.hasOwnProperty('label')) { + return { ...item, value: item.value.value } + } else { + return { ...item, value: JSON.stringify(item.value) } + } + }) + } + refreshCount += 1 + }} + checked={items.some((x) => typeof x.value != 'string')} + /> + {:else if subFieldType === 'ag-grid' || subFieldType === 'table-column'} {/if}
-
- {#each items as item, index (item.id)} -
- + {#key refreshCount} +
+ {#each items as item, index (item.id)} +
+ -
-
- deleteElementByType(index)} - /> -
- -
- -
- +
+
+ deleteElementByType(index)} + /> +
+ +
+ +
+ +
+ {#if subFieldType !== 'db-explorer'} + + {/if}
- {#if subFieldType !== 'db-explorer'} - - {/if}
-
- {/each} -
+ {/each} +
+ {/key} {/if} {#if subFieldType === 'db-explorer'} {#if componentInput.loading} diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte index 1376437c4d..b8726bb850 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/ComponentPanel.svelte @@ -484,7 +484,7 @@ Show
-
+
Full height
{#if componentSettings?.item?.[12]?.fullHeight !== undefined}
-
+
Copy:
diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte index 1b5f2ee5a5..d234f5880b 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte @@ -152,6 +152,12 @@ : undefined} showSchemaExplorer /> + {:else if fieldType == 'labeledselect' && typeof componentInput.value == 'string'} + {:else}
| (AppInputSpec<'array', string[], 'select'> & StaticOptions) | AppInputSpec<'array', object[], 'labeledresource'> - | AppInputSpec<'array', object[], 'labeledselect'> + | AppInputSpec<'array', (object | string)[], 'labeledselect'> | AppInputSpec<'labeledselect', object> | AppInputSpec<'labeledresource', object> | AppInputSpec<'array', object[], 'tab-select'> diff --git a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte index 1e905f6822..87ab307034 100644 --- a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte +++ b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte @@ -73,7 +73,7 @@ dispatch('select', fullKey) } - $: keyLimit = isArray ? 1 : 100 + $: keyLimit = isArray ? 5 : 100 $: fullyCollapsed = keys.length > 1 && collapsed