The dropdown lets users pick one item from a set of single-line text options (color, font size, shipping method…). Its style and behavior reproduce the ComboBox of WinUI 3 (Windows App SDK 2.0): geometry, state colors, dropdown item indicator bars, panel positioning and animation are all checked line by line against src/controls/dev/ComboBox/ComboBox_themeresources.xaml; key handling, text search and the editable state follow src/dxaml/xcp/dxaml/lib/ComboBox_Partial.cpp.
The interaction foundation reuses the Combobox primitive of reka-ui (ARIA combobox semantics, roving highlight, popup positioning, close on outside click); this component adds WinUI's key handling, text search, editable state and visual states.
Three value-handling conventions aligned with WinUI:
null (matches WinUI SelectedIndex = -1): without modelValue it is uncontrolled, null means nothing is selected, and clearing the selection emits null;items (matches WinUI ItemsSource), each item { value, text?, disabled? }, text defaults to String(value);selectionChangedTrigger decides when to commit: default committed — a click / Enter / text search changes the selection; with the panel open, arrow keys only move the highlight (WinUI SelectionChangedTrigger default behavior).v-model binds the selected value, items supplies the options, header is WinUI's title row (also the default accessible name), and placeholder is shown when nothing is selected.
Currently selected: — (shows the placeholder when nothing is selected)
header maps to WinUI Header (LineHeight 20, bottom margin 8, weight Normal), description maps to Description (rendered below the control and used as aria-describedby), and label takes precedence over header as the accessible name.
Font size: 14
Elements of items can be of any type: use by to specify item identity (field name or compare function) and text to specify the display and search text. The payload of selectionChanged aligns with WinUI SelectionChangedEventArgs: addedItem / removedItem / addedIndex / removedIndex (single-select, at most one of each; index is -1 when nothing is selected).
For "highlight follows the selection" behavior (WinUI SelectionChangedTrigger = Always, the feel of a font picker), pass selection-changed-trigger="always":
<FluereCombobox v-model="font" :items="FONTS" selection-changed-trigger="always" />
Key handling follows WinUI's two key maps line by line (MainKeyDown / PopupKeyDown):
| Key | Panel collapsed (non-editable) | Panel expanded |
|---|---|---|
↑ / ↓ | changes the selection directly (skips disabled items) | moves the highlight (does not change selection under committed) |
Home / End | selects first / last enabled item | highlights first / last |
Enter | opens the panel | commits the highlighted item and closes |
Space | opens the panel | equivalent to Enter |
Ctrl / Cmd + Enter | — | deselects the currently selected item |
Alt + ↑ / ↓ | opens the panel | closes the panel |
F4 | opens the panel | closes the panel |
Escape | not handled (bubbles to the parent) | closes the panel without changing the selection |
| Character keys | prefix-searches and selects the hit directly | same (the popup bubbles the character back to the control) |
Tab | focus leaves normally | closes the panel; focus leaves normally |
Text search shares WinUI's semantics: item text is first TrimStart(' ') and then matched by case-insensitive prefix match (no substring / fuzzy matching); the search string accumulates when two keystrokes are ≤ 1000ms apart and restarts otherwise; the new search string scans circularly starting from "current item + 1", and the hit directly becomes the selection. text-search-enabled="false" turns it off entirely (matches IsTextSearchEnabled). When focus is on the control and the panel is collapsed, the mouse wheel also changes the selection (WinUI only responds when focused and collapsed).
Current: —
The panel's maximum height defaults to 504px (WinUI DefaultComboBoxStyle MaxDropDownHeight, roughly 15 items); beyond that the list scrolls internally.
editable maps to WinUI IsEditable: the input box accepts a custom value, and typing does not filter the list (an editable WinUI ComboBox only does inline completion, not filtering); the panel is still opened via the arrow hotspot / F4 / Alt+↓.
UpdateEditableTextBox(selectText: true)); continued typing replaces it;Enter / commit on blur: text matching an existing item → selects that item; otherwise it emits textSubmitted and, following WinUI's default behavior, clears the selection while keeping the text;textSubmitted handled: setting true equals WinUI e.Handled = true, and the component no longer changes the selection (convenient for filling the value back after validation);Escape: rolls the text back to the current selection (WinUI CommitRevertEditableSearch(restoreValue: true));v-model:text (maps to WinUI Text).Selected: WinUI
Use the #item slot to override row content (maps to WinUI ItemTemplate); slot params are { item, index }. The selection indicator bar, padding, corner radius and state colors are still handled by the component.
Current: #0f6cbd
disabled maps to WinUI IsEnabled = false (fill / foreground / arrow / indicator bar all use the disabled slots, and it ignores pointer and keyboard); an individual option's disabled maps to ComboBoxItem.IsEnabled = false and is excluded from arrow-key selection and text search.
When placed inside a <form> with a name, it adds a hidden native <input type="hidden"> submitting name=value (an empty string when nothing is selected), matching the use of WinUI SelectedValue.
| Prop (Props) | Type | Default | Description |
|---|---|---|---|
modelValue | unknown | null | — | Current selected value (v-model); null means nothing is selected (WinUI SelectedItem) |
defaultValue | unknown | null | null | Uncontrolled initial selected value |
items | FluereComboboxItem[] | [] | Option data (WinUI ItemsSource): { value, text?, disabled? } |
open | boolean | — | Panel open state (v-model:open, WinUI IsDropDownOpen) |
defaultOpen | boolean | false | Uncontrolled initial open state |
disabled | boolean | false | Whether the control is disabled (WinUI IsEnabled) |
editable | boolean | false | Editable state (WinUI IsEditable) |
text | string | — | Editable-state text (v-model:text, WinUI Text) |
defaultText | string | '' | Uncontrolled initial text in editable state |
placeholder | string | — | Placeholder (WinUI PlaceholderText), shown only when nothing is selected |
header | string | — | Top title (WinUI Header), also the default accessible name |
label | string | — | Accessible name (takes precedence over header) |
description | string | — | Description below the control (WinUI Description), also used as aria-describedby |
name | string | — | Form submission name, paired with the hidden native input |
by | string | ((a, b) => boolean) | — | Item-identity comparison for object items |
maxDropDownHeight | number | 504 | Maximum panel height (WinUI MaxDropDownHeight) |
textSearchEnabled | boolean | true | Whether prefix search is enabled (WinUI IsTextSearchEnabled) |
selectionChangedTrigger | 'committed' | 'always' | 'committed' | Commit timing (WinUI SelectionChangedTrigger) |
dir | 'ltr' | 'rtl' | — | Reading direction |
| Event | Payload | Description |
|---|---|---|
update:modelValue | unknown | null | Selected value change (v-model) |
update:open | boolean | Panel open state change (v-model:open) |
update:text | string | Editable-state text change (v-model:text) |
selectionChanged | { addedItem, removedItem, addedIndex, removedIndex } | Corresponds to WinUI SelectionChanged |
dropDownOpened | — | Corresponds to WinUI DropDownOpened |
dropDownClosed | — | Corresponds to WinUI DropDownClosed |
textSubmitted | { text: string, handled: boolean } | Corresponds to WinUI TextSubmitted (editable state) |
| Slot (Slots) | Description |
|---|---|
item | Dropdown item content ({ item, index }), corresponds to WinUI ItemTemplate |
header | Top title row (WinUI Header / HeaderTemplate) |
description | Description below the control (WinUI Description) |
empty | Content when there are no options (WinUI has no empty-state text; if not provided an empty panel renders) |
Accessibility: the input box carries
role="combobox",aria-expanded/aria-controls/aria-activedescendant; the non-editable state addsreadonlyandaria-autocomplete="none", while the editable state usesaria-autocomplete="list"; dropdown items arerole="option"+aria-selected, and disabled items carryaria-disabled. Focus stays on the control (TabNavigation="Once"semantics); dropdown items never enter the Tab sequence.
Differences from WinUI (all are web-side approximations, recorded line by line):
- Acrylic surface: the Fluent token set has no material tokens, so the popup approximates the surface with
colorNeutralBackground1+shadow16(same as the compact NumberBox popup); the blur recipe ofAcrylicInAppFillColorDefaultBrushis not reproduced.- Popup open animation: a
clip-paththat expands from the horizontal midline of the selected item toward both the top and the bottom approximatesSplitOpenThemeAnimation(when nothing is selected it hugs the trigger-control side; 250ms /0,0,0,1) — the panel first collapses into a thin slit, then plays only after the measured selected-item position is written into a CSS variable; the close animation is omitted — reka'sPresenceunmounts content immediately in order to keeparia-hiddensemantics correct.- Arrow pressed state: the WinUI 3 Gallery arrow press is a slight press-down (not a flip), approximated here with
translateY(1px)(in viadurationFast/ back viadurationNormal).- Focus rectangle: WinUI uses
FocusStrokeColorOuter(light#E4000000/ white in dark), which maps tocolorStrokeFocus2in the token set; the other controls of the component library currently usecolorCompoundBrandStroke, while this component takes its color from the WinUI source. The focus rectangle and the 3×16 brand-color indicator bar appear only for keyboard / programmatic focus (corresponding to the distinction between WinUIFocusedandPointerFocused).PageUp/PageDown: WinUI moves the highlight by page while expanded; here the keys are only swallowed (to prevent page scrolling), without moving the highlight.- An item
valuecannot be an empty string: the reka foundation uses it to mean "clear the selection", sovalue: ''is rejected (WinUI has no such restriction); usenullor a sentinel value when empty-string semantics are needed.- When text is too long, the input box truncates it with an ellipsis (WinUI just clips it).