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)
Component Value source Input form One-line positioning oas-selectMust come from the option set Button-triggered; no always-visible input The default form of constrained choice (multiple/create/remote full suite) oas-comboboxMust come from the option set Input is the control; typing only filters Always-visible-input constrained choice (single + multiple) oas-auto-completeAny free text Input is the control Suggestion input (value need not come from options) combobox keeps the pure-filtering semantics of "value always from options": multiple is built in (
multiple, mirroring theoas-selectmultiple contract); no allow-create (useoas-select allow-createoroas-dynamic-tagsfor creation,oas-auto-completefor 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
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
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
| Attribute | Description | Type | Default |
|---|---|---|---|
autocomplete | Passed through to the inner native input (falls back to off) | — | — |
clearable | Clearable (shows a clear button when a value exists; clearing dispatches oas-clear) | boolean | — |
disabled | Disabled (no input, no dropdown) | boolean | — |
filterable | Filter labels in real time while typing (filterable="false" disables local filtering) | string | true |
item-height | Virtual-scroll fixed row height (px, with virtual, default 36) | string | 36 |
loading | Loading placeholder (dropdown shows "Loading…") | boolean | — |
max-count | Multi-select limit (only with multiple): once reached, unselected options are greyed out and blocked with oas-exceed-limit; selected options can still be removed | string | — |
max-tag-count | Tag 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 default | number | — |
multiple | Multi-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 | — |
name | Form 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 | — |
open | Controlled 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 dropdown | boolean | — |
options | Options, JSON array [{ label, value, disabled?, group? }] (group is the group title, same contract as oas-select) | Option[] | string | [] |
placeholder | Placeholder text | — | — |
readonly | Readonly (focusable & copyable, no value change; focus and keyboard never open the dropdown) | boolean | — |
required | Required marker (drives the native valueMissing validation chain; not passed through to the inner control) | boolean | — |
size | Size tier: small / medium / large (default medium, follows the nearest config-provider injection) | string | medium |
status | Validation status: success / warning / error (error syncs aria-invalid, drivable by oas-form-item validation) | string | — |
value | Current value (controlled, the selected option's option.value; a JSON array string under multiple) | string | string[] | — |
virtual | Virtual 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)
| Property | Description | Type | Default |
|---|---|---|---|
filter | Custom filter function (property channel, (option, query) => boolean) | ((option: Option, query: string) => boolean) | null | — |
value | Current value: single is the selected value string (falls back to the typed draft when nothing is selected); multiple is the array of selected values | string | string[] | — |
Events
| Event | Description |
|---|---|
oas-change | Selection/clear/removal change; single detail: { value }; multiple detail: { value: string[], options: Array<Option|null> } (mirrors the oas-select multiple contract) |
oas-clear | Clear button clicked; single detail: { value } (value before clearing); multiple detail: { value: string[] } (array before clearing) |
oas-exceed-limit | Selection attempt beyond the multiple max-count limit, detail: { value, max } |
oas-input | Filter keyword typed, detail: { value } |
oas-open-change | Open state transition (both controlled setAttribute and internal toggles dispatch), detail: { open } |
CSS Variables
| CSS Variable | Default |
|---|---|
--oas-glass-blur | none |
--oas-glass-ring | transparent |
Keyboard: Enter / focus to open, ↑/↓ to move the highlight, Enter to select, Esc to close and revert.