Components

SelectPicker

A dependency-free <select> replacement: single or multi-select, searchable, grouped, virtualized for large datasets, and async-loadable — portal rendered and fully accessible.

Overview

Give SelectPicker an options array, or point it at an existing native <select> to enhance it in place. It builds a custom combobox control, hides the source element, and keeps a hidden form value in sync so submissions work unchanged.

new UISuite.SelectPicker("#country", {
  options: [
    { label: "United States", value: "us" },
    { label: "Greece", value: "gr" },
  ],
  searchable: true,
  clearable: true,
});
single · clearable

Features

  • Single and multiple selection with removable tag chips.
  • Built-in search with group-aware filtering.
  • <optgroup>-style grouping.
  • Virtualized rendering for thousands of options.
  • Async option loading on first open.
  • Progressive enhancement of native <select> elements.
  • Custom option/value rendering hooks.

Option shape

Options are flat or grouped. Each option supports label, value, disabled, and selected.

// Flat
[{ label: "Japan", value: "jp" }, { label: "Spain", value: "es", disabled: true }]

// Grouped
[
  { group: "Europe", options: [{ label: "Greece", value: "gr" }] },
  { group: "Asia",   options: [{ label: "Japan",  value: "jp" }] },
]

Options

OptionTypeDefaultDescription
optionsArraynullFlat or grouped option list. Omit to read from a native <select> or to use loadOptions.
multiplebooleanfalseAllow multiple selections (renders tags).
searchablebooleanfalseShow a search input inside the menu.
clearablebooleanfalseShow a clear button when there is a value.
placeholderstring"Select…"Control placeholder text.
maxSelectionsnumberInfinityCap on multi-select count.
virtualizedbooleantrueWindow the list for large datasets.
itemHeightnumber38Row height (px) used by virtualization.
maxListHeightnumber300Max menu height (px) for the popover.
loadOptions() => PromisenullAsync loader run on first open.
renderOptionfnnullCustom option renderer; returns HTML or a Node.
renderValuefnnullCustom renderer for the selected value/tag.
searchPlaceholderstring"Search…"Placeholder for the search input.
noResultsTextstring"No results"Empty-state text.
loadingTextstring"Loading…"Async loading text.
touchUi"auto"|boolean"auto"Force or disable the touch sheet.
theme"auto"|"light"|"dark""auto"Token theme mode.
onChange / onOpen / onClosefunctionLifecycle callbacks.

Methods

MethodReturnsDescription
getValue()string | string[] | nullAn array in multiple mode, otherwise a single value or null.
setValue(value, silent?)thisSelect one or more values; true skips the change event.
setOptions(options)thisReplace the option list; prunes selections that no longer exist.
open() / close()voidToggle the menu.
setTheme(mode)thisSwitch theme at runtime.
on(event, fn)disposerSubscribe to events.
destroy()voidRestore the source element and remove all listeners.

Events

change, open, and close are available via on() and option callbacks. The source element also fires a native change event and a dt:change CustomEvent with the value in detail — so framework bindings work without extra glue.

States & variants

multiple · tags · maxSelections: 3
searchable · optgroups
searchable · virtualized
loadOptions() on first open

Progressive enhancement

Point the picker at a native <select> and its options, groups, and selected state are adopted automatically.

enhanced in place

Accessibility notes

  • The control is role="combobox" with aria-haspopup="listbox"; the menu is role="listbox" with role="option" rows.
  • Active option tracking uses aria-activedescendant; selection uses aria-selected.
  • Type-ahead, arrow navigation, Home/End, Enter to toggle, and Backspace to remove the last tag.
  • On touch the menu is a focus-trapped modal sheet. See Accessibility.

Common mistakes

  • Mutating options in place. Call setOptions(next) so the list re-flattens and stale selections are pruned.
  • Disabling virtualization for big lists. Keep virtualized: true for large datasets; turning it off renders every row.
  • Expecting a string in multiple mode. getValue() returns an array when multiple is set.
  • Wrong itemHeight. It must match the rendered row height or scroll math drifts.