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,
});
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
| Option | Type | Default | Description |
|---|---|---|---|
options | Array | null | Flat or grouped option list. Omit to read from a native <select> or to use loadOptions. |
multiple | boolean | false | Allow multiple selections (renders tags). |
searchable | boolean | false | Show a search input inside the menu. |
clearable | boolean | false | Show a clear button when there is a value. |
placeholder | string | "Select…" | Control placeholder text. |
maxSelections | number | Infinity | Cap on multi-select count. |
virtualized | boolean | true | Window the list for large datasets. |
itemHeight | number | 38 | Row height (px) used by virtualization. |
maxListHeight | number | 300 | Max menu height (px) for the popover. |
loadOptions | () => Promise | null | Async loader run on first open. |
renderOption | fn | null | Custom option renderer; returns HTML or a Node. |
renderValue | fn | null | Custom renderer for the selected value/tag. |
searchPlaceholder | string | "Search…" | Placeholder for the search input. |
noResultsText | string | "No results" | Empty-state text. |
loadingText | string | "Loading…" | Async loading text. |
touchUi | "auto"|boolean | "auto" | Force or disable the touch sheet. |
theme | "auto"|"light"|"dark" | "auto" | Token theme mode. |
onChange / onOpen / onClose | function | — | Lifecycle callbacks. |
Methods
| Method | Returns | Description |
|---|---|---|
getValue() | string | string[] | null | An array in multiple mode, otherwise a single value or null. |
setValue(value, silent?) | this | Select one or more values; true skips the change event. |
setOptions(options) | this | Replace the option list; prunes selections that no longer exist. |
open() / close() | void | Toggle the menu. |
setTheme(mode) | this | Switch theme at runtime. |
on(event, fn) | disposer | Subscribe to events. |
destroy() | void | Restore 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
Progressive enhancement
Point the picker at a native <select> and its options, groups, and selected state are adopted automatically.
Accessibility notes
- The control is
role="combobox"witharia-haspopup="listbox"; the menu isrole="listbox"withrole="option"rows. - Active option tracking uses
aria-activedescendant; selection usesaria-selected. - Type-ahead, arrow navigation,
Home/End,Enterto toggle, andBackspaceto remove the last tag. - On touch the menu is a focus-trapped modal sheet. See Accessibility.
Common mistakes
- Mutating
optionsin place. CallsetOptions(next)so the list re-flattens and stale selections are pruned. - Disabling virtualization for big lists. Keep
virtualized: truefor large datasets; turning it off renders every row. - Expecting a string in multiple mode.
getValue()returns an array whenmultipleis set. - Wrong
itemHeight. It must match the rendered row height or scroll math drifts.