Skip to content

Combobox ​

A filterable combobox whose input is the control: an always-visible editable input shows the selected label, typing filters options in real time, and value takes option.value on selection. Multi-select is built in via multiple.

Choosing among the three (select / combobox / auto-complete)

ComponentValue sourceInput formOne-line positioning
oas-selectMust come from the option setButton-triggered; no always-visible inputThe default form of constrained choice (multiple/create/remote full suite)
oas-comboboxMust come from the option setInput is the control; typing only filtersAlways-visible-input constrained choice (single + multiple)
oas-auto-completeAny free textInput is the controlSuggestion input (value need not come from options)

combobox keeps the pure-filtering semantics of "value always from options": multiple is built in (multiple, mirroring the oas-select multiple contract); no allow-create (use oas-select allow-create or oas-dynamic-tags for creation, oas-auto-complete for free text) — no duplication with existing capabilities in the library.

Basic Usage ​

Basic usage

Click/focus to open the dropdown; typing filters in real time (filterable is enabled by default, substring-matching the label); ↑/↓ move the highlight, Enter selects, Esc closes.

Preset Value (controlled) ​

Preset value (controlled)

value is a controlled property: it is written back after selection; externally changing the property also immediately reflects in the input's label.

Externally Controlled Value ​

External controlled value

Set to AppleClear value

In controlled mode, the host can drive the component display via the value attribute at any time:

Groups ​

Groups (group)

Options with a group field render group titles (not selectable) with indented options; ↑/↓ navigate continuously across groups (same JSON contract as oas-select).

Custom Filter Function ​

Custom filtering (el.filter)

el.filter = (option, query) => boolean is a JS property channel (attributes cannot carry functions): it takes over local filtering (e.g. pinyin initials, remote matching); set to null to restore the default label-substring filter; not involved when filterable="false".

Size & Status ​

Sizes (size)

Status (status)

size supports small / medium / large (follows the nearest config-provider injection); status supports success / warning / error (error syncs aria-invalid, drivable by oas-form-item validation).

Readonly ​

Readonly

readonly allows focus & copy but no value change; focus and keyboard no longer open the dropdown.

Controlled Open ​

Controlled open + oas-open-change

Toggle open

The open attribute is the controlled open state (internal open/close writes back to it); every transition dispatches oas-open-change (detail.open boolean) — linked scenarios (opening via external buttons/shortcuts) are implemented by the host driving the attribute.

Large Data (Virtual Scroll) ​

10k-option virtual scroll

With virtual, only the visible window renders (reusing oas-virtual-list window computation) — 10k options scroll smoothly; item-height adjusts the fixed height (default 36). Keyboard ↑/↓ navigation auto-follows the highlighted item and aria-activedescendant keeps pointing at a visible item; options with group automatically fall back to full rendering.

Filtering & Blur Boundaries ​

Input filtering + blur fallback

If you type without selecting and then blur/press Esc, the input falls back to the currently selected label (non-destructive by default — the selected value is not lost) — combobox values always come from options; for free text use oas-auto-complete.

Not Filterable ​

Filtering disabled (filterable="false")

With filterable="false", typing no longer filters options; select only via keyboard ↑/↓ + Enter or mouse.

Clearable ​

Clearable

When a value is selected, a clear button appears; clicking clears value and dispatches oas-clear and oas-change.

Multiple ​

Set multiple to enter multi-select mode (mirroring the oas-select multiple contract): value becomes a JSON array string, selected options render as removable tags (click × to remove one); after each pick the panel stays open and the input clears its filter for continuous selection; typing only filters (free-text semantics are off in multiple mode — blur discards the filter and never touches the selection). oas-change detail is { value: string[], options: Array<Option|null> }; clearable clears all and dispatches oas-clear (detail holds the pre-clear array).

Multiple (multiple + tags)

Multi-select Limit & Tag Collapse (max-count / max-tag-count) ​

max-count caps how many options can be selected (multiple only): once reached, unselected options are greyed out in the dropdown (aria-disabled) and clicks/keyboard picks are blocked with oas-exceed-limit (detail: { value, max }); selected options can still be removed. When max-tag-count is explicitly set, the selected tags are laid out on one line and the overflow collapses into +N (hover title lists the folded labels); without it tags wrap and the control grows in height.

Multi-select limit (max-count=3) + tag collapse (max-tag-count=2)

Presets and external control: value='["apple","banana"]' drives the tags directly; reading el.value returns the selected array and writing an array (or a single value) applies immediately without dispatching events. Form submission uses the native FormData "same-name multiple entries" semantics (one same-name entry per selected value); required validates "at least one selected".

Loading ​

Loading

While loading, the dropdown shows a loading placeholder (the host sets it during remote data requests).

Empty State ​

No options (empty)

When options are empty, the dropdown shows the empty state; when filtering yields no match, the no-match state is shown.

Disabled ​

Disabled

Events ​

Event output

Listen to oas-input (filter keyword), oas-change (selection), oas-clear (clear), oas-open-change (open transitions):

Mobile form (bottom sheet) ​

On touch screens (coarse pointer) or narrow viewports (<768px), the option list is automatically hosted by an oas-bottom-sheet bottom sheet: rises from the viewport bottom + backdrop + drag handle swipe-down to close (backdrop click / Esc also close), with bottom safe-area inset; the input stays in place and focusing it still pops the list inside the sheet for selection; the desktop form keeps the original floating dropdown. Inside the sheet, option rows are lifted to a 44px touch target (--oas-touch-target-min).

required ​

required (native validation chain)

required drives the native validation chain (form-associated): when unfilled, checkValidity() returns false (valueMissing) and native form submission is blocked; it recovers to :valid once filled.

Programmatic read/write of the current value goes through the public value property: el.value reads the selected value (value attribute) first, falling back to the in-progress draft when nothing is selected (FormData semantics); el.value = x writes the controlled value attribute and reflects the option label in the input (label/value separation) without dispatching events.

autocomplete Passthrough ​

autocomplete passthrough

autocomplete passes through to the inner native input (defaults to off: combobox draws its own dropdown, so browser autofill is disabled by default). Inside a native form with a name, the browser uses this to decide whether to offer historical entries.

API ​

oas-combobox ​

Attributes ​

AttributeDescriptionTypeDefault
autocompletePassed through to the inner native input (falls back to off)——
clearableClearable (shows a clear button when a value exists; clearing dispatches oas-clear)boolean—
disabledDisabled (no input, no dropdown)boolean—
filterableFilter labels in real time while typing (filterable="false" disables local filtering)stringtrue
item-heightVirtual-scroll fixed row height (px, with virtual, default 36)string36
loadingLoading placeholder (dropdown shows "Loading…")boolean—
max-countMulti-select limit (only with multiple): once reached, unselected options are greyed out and blocked with oas-exceed-limit; selected options can still be removedstring—
max-tag-countTag collapse count (only with multiple): when explicitly set, tags are laid out on one line and the overflow collapses into +N (title lists the folded labels); without it tags wrap by defaultnumber—
multipleMulti-select mode (mirrors oas-select multiple semantics: the value is a JSON array, selected options render as removable tags, the panel stays open after each pick for continuous selection; typing only filters, free-text semantics are off)boolean—
nameForm field name (key for native FormData submission; also the key of same-name multiple entries in multiple mode; the browser skips submission when name is absent)string—
openControlled open state (internal open/close writes back to the attribute; every transition dispatches oas-open-change; forced closed under readonly/disabled); in the mobile form (touch / viewport <768px) the open panel is hosted by an oas-bottom-sheet bottom sheet (swipe-down/backdrop/Esc to close); desktop keeps the floating dropdownboolean—
optionsOptions, JSON array [{ label, value, disabled?, group? }] (group is the group title, same contract as oas-select)Option[] | string[]
placeholderPlaceholder text——
readonlyReadonly (focusable & copyable, no value change; focus and keyboard never open the dropdown)boolean—
requiredRequired marker (drives the native valueMissing validation chain; not passed through to the inner control)boolean—
sizeSize tier: small / medium / large (default medium, follows the nearest config-provider injection)stringmedium
statusValidation status: success / warning / error (error syncs aria-invalid, drivable by oas-form-item validation)string—
valueCurrent value (controlled, the selected option's option.value; a JSON array string under multiple)string | string[]—
virtualVirtual scroll (reuses oas-virtual-list to render only the visible window; options with group fall back to full rendering)boolean—

Property (JS property; see each row for attribute reflection) ​

PropertyDescriptionTypeDefault
filterCustom filter function (property channel, (option, query) => boolean)((option: Option, query: string) => boolean) | null—
valueCurrent value: single is the selected value string (falls back to the typed draft when nothing is selected); multiple is the array of selected valuesstring | string[]—

Events ​

EventDescription
oas-changeSelection/clear/removal change; single detail: { value }; multiple detail: { value: string[], options: Array<Option|null> } (mirrors the oas-select multiple contract)
oas-clearClear button clicked; single detail: { value } (value before clearing); multiple detail: { value: string[] } (array before clearing)
oas-exceed-limitSelection attempt beyond the multiple max-count limit, detail: { value, max }
oas-inputFilter keyword typed, detail: { value }
oas-open-changeOpen state transition (both controlled setAttribute and internal toggles dispatch), detail: { open }

CSS Variables ​

CSS VariableDefault
--oas-glass-blurnone
--oas-glass-ringtransparent

Keyboard: Enter / focus to open, ↑/↓ to move the highlight, Enter to select, Esc to close and revert.

Released under the MIT OR Apache-2.0 License.