component AdvancedSelect { props { size = "default" color = "primary" label = "Advanced Select" name = "" value = "" values = [] options = [] groups = [] placeholder = "Select an option" placeholderIcon = "" searchPlaceholder = "Search options…" multiple = false searchable = true defaultOpen = false clearable = true allowEmpty = true tags = false disabled = false required = false invalid = false validationMessage = "" helpText = "" loading = false loadingLabel = "Loading options…" emptyLabel = "No options found" selectedOptionsLabel = "Selected options" clearLabel = "Clear selection" createLabel = "Create" loadMoreLabel = "Load more" searchMode = "contains" searchFields = "label,description" minSearchLength = 0 searchResultLimit = 0 maxSelections = 0 showCounter = false counterTemplate = "{selected} selected" optionTemplate = "default" selectedTemplate = "default" closeOnSelect = true scrollToSelected = true fixed = false placement = "bottom" remote = false remoteUrl = "" remoteQueryParam = "q" remoteDebounce = 250 remoteAutoLoad = true infinite = false hasMore = false page = 1 class = "" @event search = function @event select = function @event change = function @event clear = function @event open = function @event close = function @event load = function @event error = function } state open = defaultOpen state query = "" state activeIndex = -1 state selectedValue = value state selectedValues = values functions { function allOptions() { return [...groups.flatMap((group) => group.options || []), ...options] } function searchableText(option) { return ((option.label || "") + " " + (option.description || "")).toLowerCase() } function matches(option) { if (!query || query.length < Number(minSearchLength || 0)) { 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()) } function matchingOptions(list) { return list.filter((option) => matches(option)) } function visibleOptions(list) { if (searchResultLimit > 0) { return matchingOptions(list).slice(0, Number(searchResultLimit)) } return matchingOptions(list) } function flatVisibleOptions() { return visibleOptions(allOptions()) } function isSelected(option) { return ( multiple ? selectedValues.includes(option.value) : selectedValue === option.value ) } function selectedOptions() { return allOptions().filter((option) => isSelected(option)) } function selectedCount() { return multiple ? selectedValues.length : selectedValue ? 1 : 0 } function counterText() { return ( maxSelections ? selectedCount() + " / " + maxSelections + " selected" : selectedCount() + " selected" ) } function selectedText() { return ( multiple ? selectedValues.join(", ") : selectedOptions().length ? selectedOptions()[0].label : "" ) } function triggerText() { return selectedCount() ? selectedText() : placeholder } function canSelect(option) { if (option.disabled) { return false } if (!multiple || isSelected(option)) { return true } if (maxSelections <= 0) { return true } return selectedCount() < maxSelections } function chooseSingle(option) { if (!canSelect(option)) { return } selectedValue = option.value query = "" if (closeOnSelect) { open = false } } function chooseMultiple(option) { if (!canSelect(option)) { return } if (isSelected(option)) { selectedValues = selectedValues.filter((item) => item !== option.value) } else { selectedValues = selectedValues.concat([option.value]) } query = "" } function chooseOption(option) { if (multiple) { chooseMultiple(option) return } chooseSingle(option) } function clearSelection(event) { event.stopPropagation() selectedValue = "" selectedValues = [] query = "" open = false } function toggle() { if (disabled) { return; }; open = !open; activeIndex = open && flatVisibleOptions().length ? 0 : -1; } function moveActive(direction) { if (!flatVisibleOptions().length) { return } activeIndex = (activeIndex + direction + flatVisibleOptions().length) % flatVisibleOptions().length } function handleKeydown(event) { if (disabled) { return } if (event.key === "ArrowDown") { event.preventDefault() if (!open) { open = true } moveActive(1) } else if (event.key === "ArrowUp") { event.preventDefault() if (!open) { open = true } moveActive(-1) } else if (event.key === "Enter" || event.key === " ") { event.preventDefault() if (!open) { open = true } else if (activeIndex >= 0) { chooseOption(flatVisibleOptions()[activeIndex]) } } else if (event.key === "Escape") { open = false } else if (event.key === "Home" && open) { event.preventDefault() activeIndex = 0 } else if (event.key === "End" && open) { event.preventDefault() activeIndex = flatVisibleOptions().length - 1 } } function optionIndex(option) { return flatVisibleOptions().findIndex((item) => item.value === option.value) } } view {
{counterText()}
{#if searchable} {/if}
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 !loading && !flatVisibleOptions().length}
{emptyLabel}
{/if}
{#if tags && query && !allOptions().some((option) => option.label.toLowerCase() === query.toLowerCase())} {/if}
{#if helpText && !validationMessage}{helpText}{/if} {validationMessage}
} }