add multiselect novelties to <AppSelect />, harmonize, clean <AppSelect />

This commit is contained in:
Clement Zhang
2024-11-15 10:09:14 +01:00
parent 1367296de0
commit 88d75cc584
6 changed files with 95 additions and 116 deletions
@@ -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)]
@@ -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
@@ -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>('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)
}
</script>
@@ -215,7 +193,7 @@
<option value="" disabled selected>{resolvedConfig.placeholder}</option>
{/if}
{#each options as item (item.value)}
<option value={item.value} selected={item.value === value}>{item.label}</option>
<option value={item.value} selected={item.value === selectedValue}>{item.label}</option>
{/each}
</select>
{: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}
@@ -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 }
})
}
@@ -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',
@@ -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++) {