component ComboBox { props { size = "default" color = "primary" label = "ComboBox" name = "" value = "" options = [] groups = [] placeholder = "Search or select an option" searchPlaceholder = "Start typing…" clearable = true allowCustomValue = false disabled = false required = false invalid = false validationMessage = "" helpText = "" loading = false loadingLabel = "Loading suggestions…" emptyLabel = "No matching options" clearLabel = "Clear value" toggleLabel = "Toggle suggestions" searchMode = "contains" searchFields = "label,description" minSearchLength = 0 searchResultLimit = 0 optionTemplate = "default" defaultOpen = false closeOnSelect = true fixed = false placement = "bottom" autocomplete = "off" remote = false remoteUrl = "" remoteQueryParam = "q" remoteDebounce = 250 remoteAutoLoad = true infinite = false hasMore = false page = 1 loadMoreLabel = "Load more" 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 selectedValue = value state activeIndex = -1 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 < 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()) } function matchingOptions(list) { return list.filter((option) => matches(option)) } function visibleOptions(list) { if (searchResultLimit > 0) { return matchingOptions(list).slice(0, searchResultLimit) } return matchingOptions(list) } function flatVisibleOptions() { return visibleOptions(allOptions()) } function isSelected(option) { return selectedValue === option.value } function selectedOptions() { return allOptions().filter((option) => isSelected(option)) } function selectedText() { return selectedOptions().length ? selectedOptions()[0].label : selectedValue } function inputText() { return query || selectedText() } 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 } function canSelect(option) { return !option.disabled } function chooseOption(option) { if (!canSelect(option)) { return } selectedValue = option.value query = option.label activeIndex = optionIndex(option) if (closeOnSelect) { open = false } } function updateQuery(event) { query = event.target.value selectedValue = allowCustomValue ? event.target.value : "" open = true activeIndex = flatVisibleOptions().length ? 0 : -1 } function clearValue(event) { if (event) { event.stopPropagation() } query = "" selectedValue = "" activeIndex = -1 open = false } function openDropdown() { if (!disabled) { open = true activeIndex = flatVisibleOptions().length ? 0 : -1 } } function closeDropdown() { open = false } function toggle() { if (open) { closeDropdown() } else { openDropdown() } } function setValue(nextValue) { selectedValue = nextValue query = "" } 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() 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 } } function optionIndex(option) { return flatVisibleOptions().findIndex((item) => item.value === option.value) } } view {
} }