diff --git a/frontend/src/lib/components/apps/components/inputs/AppMultiSelect.svelte b/frontend/src/lib/components/apps/components/inputs/AppMultiSelect.svelte index 7b75d6391e..f6686b9a9b 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppMultiSelect.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppMultiSelect.svelte @@ -60,11 +60,11 @@ } } - $: resolvedConfig.defaultItems && handleDefaultItems() + $: resolvedConfig.defaultValues && handleDefaultValues() - function handleDefaultItems() { - if (Array.isArray(resolvedConfig.defaultItems)) { - const nvalue = resolvedConfig.defaultItems?.map((label) => { + function handleDefaultValues() { + if (Array.isArray(resolvedConfig.defaultValues)) { + const nvalue = resolvedConfig.defaultValues?.map((label) => { return typeof label === 'string' ? label : `NOT_STRING` }) value = [...new Set(nvalue)] diff --git a/frontend/src/lib/components/apps/components/inputs/AppMultiSelectV2.svelte b/frontend/src/lib/components/apps/components/inputs/AppMultiSelectV2.svelte index 9f75764e29..e5c99d4996 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppMultiSelectV2.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppMultiSelectV2.svelte @@ -16,7 +16,8 @@ import { offset, flip, shift } from 'svelte-floating-ui/dom' import ResolveStyle from '../helpers/ResolveStyle.svelte' import MultiSelect from '$lib/components/multiselect/MultiSelect.svelte' - import { isObjectOptionArray, isStringArray, type ObjectOption } from '../../../multiselect/types' + import type { ObjectOption } from '../../../multiselect/types' + import { parseConfigOptions } from './utils' export let configuration: RichConfigurations export let customCss: ComponentCustomCSS<'multiselectcomponent'> | undefined = undefined @@ -56,7 +57,9 @@ } } - $: resolvedConfig.defaultItems && parseDefaultItems() + $: if (resolvedConfig.defaultValues) { + selectOptionsByValue(resolvedConfig.defaultValues) + } $: outerDiv && portalRef && @@ -75,65 +78,14 @@ } function selectOptionsByValue(values: any[]) { + if (!Array.isArray(values)) { + outputs?.result.set([]) + } + selectedOptions = findOptionsByValue(values) outputs?.result.set([...(selectedOptions.map((option) => option.value) ?? [])]) } - function parseConfigOptions(resolvedConfigItems) { - if (!resolvedConfigItems) { - return [] - } - if (isObjectOptionArray(resolvedConfigItems)) { - return parseLabeledItems(resolvedConfigItems) - } else if (isStringArray(resolvedConfigItems)) { - return parseStringItems(resolvedConfigItems) - } - return [] - } - - function parseLabeledItems(resolvedConfigItems: ObjectOption[]) { - return resolvedConfigItems?.map((item: ObjectOption) => { - if (!item || typeof item !== 'object') { - console.error( - 'When labeled, MultiSelect component items should be an array of { label: string, value: string }.' - ) - return { - label: 'not object', - value: 'not object' - } - } - return { - label: item?.label ?? 'undefined', - value: - typeof item?.value === 'object' ? JSON.stringify(item.value) : item?.value ?? 'undefined' - } - }) - } - - function parseStringItems(resolvedConfigItems: any[]): ObjectOption[] { - return resolvedConfigItems?.map((option: string) => { - if (option === null || option === undefined || typeof option !== 'string') { - console.error( - 'When not labeled, MultiSelect component items should be an array of strings.' - ) - return { label: 'not string', value: 'not string' } - } - if (option === '') { - return { label: 'empty string', value: 'empty string' } - } - return { label: option, value: option } - }) - } - - function parseDefaultItems() { - if (!Array.isArray(resolvedConfig.defaultItems)) { - outputs?.result.set([]) - return - } - - selectOptionsByValue(resolvedConfig.defaultItems) - } - function setOuterDivStyle(outerDiv: HTMLDivElement, portalRef: HTMLDivElement, style: string) { outerDiv.setAttribute('style', style) // find ul in portalRef and set style diff --git a/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte b/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte index ce3a551131..5e953e7637 100644 --- a/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte +++ b/frontend/src/lib/components/apps/components/inputs/AppSelect.svelte @@ -16,6 +16,7 @@ import Popover from '$lib/components/Popover.svelte' import ResolveStyle from '../helpers/ResolveStyle.svelte' import type { ObjectOption } from '$lib/components/multiselect/types' + import { parseConfigOptions } from './utils' export let id: string export let configuration: RichConfigurations @@ -42,8 +43,8 @@ } = getContext('AppViewerContext') $componentControl[id] = { - setValue(nvalue: string) { - setValue(JSON.stringify(nvalue)) + setValue(newValue: any) { + setValue(newValue) } } if (controls) { @@ -60,13 +61,12 @@ }) // The library expects double quotes around the value - let value: string | undefined = noDefault - ? undefined - : outputs?.result.peak() - ? JSON.stringify(outputs?.result.peak()) - : undefined + let selectedValue: string | undefined - $: resolvedConfig.items && handleItems() + $: if (resolvedConfig.items) { + options = parseConfigOptions(resolvedConfig.items) + updateSelectedValue() + } $: resolvedConfig.defaultValue != undefined && handleDefault() let filterText = '' @@ -74,35 +74,20 @@ let previsousFilter = '' - function handleItems() { - options = Array.isArray(resolvedConfig.items) - ? resolvedConfig.items.map((item) => { - if (!item || typeof item !== 'object') { - console.error('Select component items should be an array of objects') - return { - label: 'not object', - value: 'not object' - } - } - return { - label: item?.label ?? 'undefined', - value: item?.value != undefined ? JSON.stringify(item.value) : 'undefined' - } - }) - : [] - - if (value != undefined && options.some((x) => x.value === value)) { + function updateSelectedValue() { + if (selectedValue != undefined && options.some((x) => x.value === selectedValue)) { return } - let rawValue + let newValue: string | undefined if (resolvedConfig.defaultValue !== undefined) { - rawValue = resolvedConfig.defaultValue + newValue = resolvedConfig.defaultValue } else if (options.length > 0 && resolvedConfig?.preselectFirst) { - rawValue = resolvedConfig.items[0].value + newValue = resolvedConfig.items[0].value } - if (rawValue !== undefined && rawValue !== null) { - value = JSON.stringify(rawValue) - outputs?.result.set(rawValue) + + if (newValue !== undefined && newValue !== null) { + selectedValue = newValue + outputs?.result.set(newValue) } } @@ -128,13 +113,9 @@ setValue(value) } - function setValue(nvalue: any) { - let result: any = undefined - try { - result = JSON.parse(nvalue) - } catch (_) {} - value = nvalue - outputs?.result.set(result) + function setValue(newValue: any) { + selectedValue = newValue + outputs?.result.set(newValue) if (recomputeIds) { recomputeIds.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((f) => f())) @@ -142,11 +123,11 @@ } function onClear() { - value = undefined + selectedValue = undefined outputs?.result.set(undefined, true) } - function handleFilter(e) { + function handleFilter() { if (resolvedConfig.create && filterText !== previsousFilter) { previsousFilter = filterText if (filterText.length > 0) { @@ -156,21 +137,18 @@ (item) => item.label.toString().toLowerCase() === filterText.toLowerCase() ) if (!exists) { - options = [ - ...prev, - { value: JSON.stringify(filterText), label: filterText, created: true } - ] + options = [...prev, { value: filterText, label: filterText, created: true }] } } } } function handleDefault() { - if (resolvedConfig.defaultValue != undefined) { - const nvalue = resolvedConfig.defaultValue - value = JSON.stringify(nvalue) - outputs?.result.set(nvalue) + if (!!noDefault || resolvedConfig.defaultValue !== undefined) { + return } + selectedValue = resolvedConfig.defaultValue + outputs?.result.set(resolvedConfig.defaultValue) } @@ -215,7 +193,7 @@ {/if} {#each options as item (item.value)} - + {/each} {:else} @@ -234,7 +212,7 @@ containerStyles={($darkMode ? SELECT_INPUT_DEFAULT_STYLE.containerStylesDark : SELECT_INPUT_DEFAULT_STYLE.containerStyles) + css?.input?.style} - {value} + value={selectedValue} class={css?.input?.class} placeholder={resolvedConfig.placeholder} disabled={resolvedConfig.disabled} diff --git a/frontend/src/lib/components/apps/components/inputs/utils.ts b/frontend/src/lib/components/apps/components/inputs/utils.ts new file mode 100644 index 0000000000..5dc84dc088 --- /dev/null +++ b/frontend/src/lib/components/apps/components/inputs/utils.ts @@ -0,0 +1,48 @@ +import { + type ObjectOption, + isObjectOptionArray, + isStringArray +} from '$lib/components/multiselect/types' +import type { StaticAppInput } from '../../inputType' + +export function parseConfigOptions(resolvedConfigItems: StaticAppInput) { + if (!resolvedConfigItems) { + return [] + } + if (isObjectOptionArray(resolvedConfigItems)) { + return parseLabeledItems(resolvedConfigItems) + } else if (isStringArray(resolvedConfigItems)) { + return parseStringItems(resolvedConfigItems) + } + return [] +} +export function parseLabeledItems(resolvedConfigItems: ObjectOption[]) { + return resolvedConfigItems?.map((item: ObjectOption) => { + if (!item || typeof item !== 'object') { + console.error( + 'When labeled, MultiSelect component items should be an array of { label: string, value: string }.' + ) + return { + label: 'not object', + value: 'not object' + } + } + return { + label: item?.label ?? 'undefined', + value: + typeof item?.value === 'object' ? JSON.stringify(item.value) : item?.value ?? 'undefined' + } + }) +} +export function parseStringItems(resolvedConfigItems: string[]): ObjectOption[] { + return resolvedConfigItems?.map((option: string) => { + if (option === null || option === undefined || typeof option !== 'string') { + console.error('When not labeled, MultiSelect component items should be an array of strings.') + return { label: 'not string', value: 'not string' } + } + if (option === '') { + return { label: 'empty string', value: 'empty string' } + } + return { label: option, value: option } + }) +} diff --git a/frontend/src/lib/components/apps/editor/component/components.ts b/frontend/src/lib/components/apps/editor/component/components.ts index 6ce8fb9f05..3ac23aad6c 100644 --- a/frontend/src/lib/components/apps/editor/component/components.ts +++ b/frontend/src/lib/components/apps/editor/component/components.ts @@ -2079,7 +2079,8 @@ This is a paragraph. value: [ { value: 'foo', label: 'Foo' }, { value: 'bar', label: 'Bar' } - ] + ], + hasLabeledMode: true } as StaticAppInput, create: { type: 'static', @@ -2150,7 +2151,7 @@ This is a paragraph. subFieldType: 'text', value: ['Foo', 'Bar'] } as StaticAppInput, - defaultItems: { + defaultValues: { type: 'static', fieldType: 'array', subFieldType: 'text', @@ -2203,7 +2204,7 @@ This is a paragraph. value: ['Foo', 'Bar'], hasLabeledMode: true } as StaticAppInput, - defaultItems: { + defaultValues: { type: 'static', fieldType: 'array', subFieldType: 'simplestringselect', diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte index 29bd8f9e5d..3bd716c881 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte @@ -232,7 +232,7 @@ dispatch('componentInputChange', newComponentInput) } - /** Transforms items string[] to ObjectOption[ and vice versa. Mutating items will fire handleItemsChange. */ + /** Transforms items string[] to ObjectOption] and vice versa. Mutating items will fire handleItemsChange. */ function handleLabeledChange() { if (labeled && isStringArray(items.map((item) => item.value))) { for (let i = 0; i < items.length; i++) {