import SelectStyles from "../styles/SelectStyles.wrn" component ComboBox { outputs { search(payload: { value?: string | number | boolean | null; values?: Array; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) select(payload: { value?: string | number | boolean | null; values?: Array; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) change(payload: { value?: string | number | boolean | null; values?: Array; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) clear(payload: { value?: string | number | boolean | null; values?: Array; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) open(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) close(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) load(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object }) error(payload: { error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string) } props { size: string = "default" color: string = "primary" label: string = "ComboBox" name: string = "" value: string = "" options: unknown[] = [] groups: unknown[] = [] placeholder: string = "Search or select an option" searchPlaceholder: string = "Start typing…" clearable: boolean = true allowCustomValue: boolean = false disabled: boolean = false required: boolean = false invalid: boolean = false validationMessage: string = "" helpText: string = "" loading: boolean = false loadingLabel: string = "Loading suggestions…" emptyLabel: string = "No matching options" clearLabel: string = "Clear value" toggleLabel: string = "Toggle suggestions" searchMode: string = "contains" searchFields: string = "label,description" minSearchLength: number = 0 searchResultLimit: number = 0 optionTemplate: string = "default" defaultOpen: boolean = false closeOnSelect: boolean = true fixed: boolean = false placement: string = "bottom" autocomplete: string = "off" remote: boolean = false remoteUrl: string = "" remoteQueryParam: string = "q" remoteDebounce: number = 250 remoteAutoLoad: boolean = true infinite: boolean = false hasMore: boolean = false page: number = 1 loadMoreLabel: string = "Load more" class: string = "" } state open = defaultOpen state query: string = "" state selectedValue = value state activeIndex: number = -1 functions { shared function allOptions() { return [...groups.flatMap((group) => group.options || []), ...options] } shared function searchableText(option) { return ((option.label || "") + " " + (option.description || "")).toLowerCase() } shared function matches(option) { if (!query || query.length < minSearchLength) { return true } if (searchMode === "startsWith") { return searchableText(option).startsWith(query.toLowerCase()) } if (searchMode === "exact") { return searchableText(option) === query.toLowerCase() } return searchableText(option).includes(query.toLowerCase()) } shared function matchingOptions(list) { return list.filter((option) => matches(option)) } shared function visibleOptions(list) { if (searchResultLimit > 0) { return matchingOptions(list).slice(0, searchResultLimit) } return matchingOptions(list) } shared function flatVisibleOptions() { return visibleOptions(allOptions()) } shared function isSelected(option) { return selectedValue === option.value } shared function selectedOptions() { return allOptions().filter((option) => isSelected(option)) } shared function selectedText() { return selectedOptions().length ? selectedOptions()[0].label : selectedValue } shared function inputText() { return query || selectedText() } shared function shouldShowDropdown() { if (!open) { return false } if (loading) { return true } if (remote && query.length !== 0 && query.length < minSearchLength) { return true } if (flatVisibleOptions().length) { return true } return infinite && hasMore } shared function canSelect(option) { return !option.disabled } shared function chooseOption(option) { if (!canSelect(option)) { return } selectedValue = option.value query = option.label activeIndex = optionIndex(option) if (closeOnSelect) { open = false } } client function updateQuery(event) { query = event.target.value selectedValue = allowCustomValue ? event.target.value : "" open = true activeIndex = flatVisibleOptions().length ? 0 : -1 } client function clearValue(event) { if (event) { event.stopPropagation() } query = "" selectedValue = "" activeIndex = -1 open = false } shared function openDropdown() { if (!disabled) { open = true activeIndex = flatVisibleOptions().length ? 0 : -1 } } shared function closeDropdown() { open = false } shared function toggle() { if (open) { closeDropdown() } else { openDropdown() } } shared function setValue(nextValue) { selectedValue = nextValue query = "" } shared function moveActive(direction) { if (!flatVisibleOptions().length) { return } activeIndex = (activeIndex + direction + flatVisibleOptions().length) % flatVisibleOptions().length } client function handleKeydown(event) { if (disabled) { return } if (event.key === "ArrowDown") { event.preventDefault() openDropdown() moveActive(1) } else if (event.key === "ArrowUp") { event.preventDefault() openDropdown() moveActive(-1) } else if (event.key === "Enter" && open && activeIndex >= 0) { event.preventDefault() chooseOption(flatVisibleOptions()[activeIndex]) } else if (event.key === "Escape") { closeDropdown() } else if (event.key === "Home" && open) { event.preventDefault() activeIndex = 0 } else if (event.key === "End" && open) { event.preventDefault() activeIndex = flatVisibleOptions().length - 1 } } shared function optionIndex(option) { return flatVisibleOptions().findIndex((item) => item.value === option.value) } } view {
Enter at least {minSearchLength} characters.
{loadingLabel}
{#each groups as group} {#if visibleOptions(group.options || []).length}
{group.label}
{#each group.options || [] as option} {/each}
{/if} {/each} {#each options as option} {/each}
{#if !allowCustomValue} {/if} {#if helpText && !validationMessage}{helpText}{/if} {validationMessage}