On this pageFunctions
Ui/Combobox
/**
* Pairs the single-select combobox's `view` and `update` (and programmatic
* helpers) behind a single Item-typed entry point. See `Listbox.create`
* for the rationale; the combobox factory follows the same shape with
* `selectItem` taking both `item` and `displayText`. `selectItem` emits
* `Selected({ value })` with the input resting on `displayText`; what the
* selection becomes is the parent's fold to decide.
*/
<Item extends string = string>(): Bundle<Item>/** Creates an initial single-select combobox model from a config. Defaults to closed with no active item and an empty input. */
(config: InitConfig): {
activationTrigger: "Pointer" | "Keyboard"
animation: Animation.Model
id: string
immediate: boolean
inputValue: string
isAnimated: boolean
isModal: boolean
isOpen: boolean
maybeActiveItemIndex: Option<number>
maybeLastPointerPosition: Option<{
screenX: number
screenY: number
}>
nullable: boolean
selectInputOnFocus: boolean
}/**
* Returns the bare DOM id of the combobox input, derived from the
* combobox's base id. Use this to associate an external label with the
* input via a native `<label for={Combobox.inputId(id)}>` or an
* `aria-labelledby` reference. Mirrors `inputSelector`, which returns the
* CSS selector form (`#${id}-input`) rather than the bare id.
*/
(id: string): string/** Schema for the activation trigger: whether the user interacted via mouse or keyboard. */
type ActivationTrigger = Literals<readonly ["Pointer", "Keyboard"]>/**
* Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field.
*
* The Combobox emits a `Selected({ value })` OutMessage on commit.
* Consumers pattern-match this in their `GotComboboxMessage` handler:
* single-select stores the value, multi-select toggles the value's
* membership. `restingInputValue` is the text the input returns to on
* close (the selection's display text for single-select, empty for
* multi-select). Everything here except the selection itself; each
* variant composes its own selection field on top.
*/
type BaseViewInputsCommon = Readonly<{
anchor: AnchorConfig
ariaLabel: string
ariaLabelledBy: string
attributes: ReadonlyArray<ChildAttribute>
backdropAttributes: ReadonlyArray<ChildAttribute>
backdropClassName: string
buttonAttributes: ReadonlyArray<ChildAttribute>
buttonClassName: string
buttonContent: Html
className: string
formName: string
groupAttributes: ReadonlyArray<ChildAttribute>
groupClassName: string
groupToHeading: (groupKey: string) => GroupHeading | undefined
inputAttributes: ReadonlyArray<ChildAttribute>
inputClassName: string
inputPlaceholder: string
inputWrapperAttributes: ReadonlyArray<ChildAttribute>
inputWrapperClassName: string
isDisabled: boolean
isInvalid: boolean
isItemDisabled: (item: Item, index: number) => boolean
isReadOnly: boolean
itemGroupKey: (item: Item, index: number) => string
items: ReadonlyArray<Item>
itemsAttributes: ReadonlyArray<ChildAttribute>
itemsClassName: string
itemsScrollAttributes: ReadonlyArray<ChildAttribute>
itemsScrollClassName: string
itemToConfig: (item: Item, context: Readonly<{
isActive: boolean
isDisabled: boolean
isReadOnly: boolean
isSelected: boolean
}>) => ItemConfig
itemToDisplayText: (item: Item, index: number) => string
itemToValue: (item: Item, index: number) => Item
openOnFocus: boolean
restingInputValue: string
separatorAttributes: ReadonlyArray<ChildAttribute>
separatorClassName: string
}>/**
* The `view`, `update`, and programmatic helpers that `Combobox.create`
* returns, bound to one `Item` type. Name it to annotate a value that
* holds a created bundle, such as a field on a config object or a
* function parameter that takes the bundle rather than calling `create`
* itself.
*/
type Bundle = Readonly<{
close: (model: Model, restingInputValue: string) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Item>>]
open: (model: Model) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Item>>]
selectItem: (model: Model, item: Item, displayText: string) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Item>>]
update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Item>>]
view: SubmodelView<Model, Message, ViewInputs<Item>>
}>/** Configuration for a group heading rendered above a group of items. */
type GroupHeading = Readonly<{
className: string
content: Html
}>/** Configuration for creating a single-select combobox model with `init`. `isAnimated` enables CSS transition coordination (default `false`). `isModal` locks page scroll and inerts other elements when open (default `false`). */
type InitConfig = BaseInitConfig/** Configuration for an individual combobox item's appearance. */
type ItemConfig = Readonly<{
className: string
content: Html
}>/**
* Generic over `Value extends string` so consumers who create the combobox
* via `Combobox.create<MyUnion>()` receive `value: MyUnion` in the
* `Selected` OutMessage from the factory's `update`, instead of
* `value: string`. Defaults to `string`.
*/
type OutMessage = Selected<Value> | ClearedSelection/** Sent when the user activates an item. Carries the neutral fact that the item was activated; the parent owns the selection and decides what it means (single-select stores the value, nullable single-select toggles it, multi-select toggles the value's membership). Generic over `Value extends string`: the runtime schema stores `value: string`, but the type-level OutMessage exposes `value: Value` so consumers who supply `items: ReadonlyArray<MyUnion>` receive `value: MyUnion` from the factory's `update` without casting. */
type Selected = Readonly<{
_tag: "Selected"
value: Value
}>/** Per-render view inputs passed to the view via `h.submodel`'s `viewInputs` field. */
type ViewInputs = BaseViewInputsCommon<Item> & Readonly<{
maybeSelectedValue: Option.Option<Item>
}>/** Sent when an item is highlighted via arrow keys or mouse hover. Includes activation trigger and optional immediate selection info. */
const ActivatedItem: CallableTaggedStruct<"ActivatedItem", {
activationTrigger: Literals<readonly ["Pointer", "Keyboard"]>
index: Number
maybeImmediateSelection: Option<Struct<{
item: String
}>>
}>/**
* The anchor-positioning Mount this Combobox renders on its items panel.
* The panel is always anchored to the input wrapper via Floating UI and
* portaled to the document body (opt out of portaling with
* `anchor.portal: false`), so it escapes ancestor stacking contexts and
* overflow clipping. The Mount also installs the `pointerdown`-cancelling
* capture listener that prevents input blur on item presses. Exposed so
* Scene tests can call
* `Scene.Mount.resolve(AnchorCombobox, CompletedAnchorCombobox())`.
*/
const AnchorCombobox: MountDefinitionWithArgs<"AnchorCombobox", {
anchor: Struct<{
gap: optional<Number>
isPlacementLocked: optional<Boolean>
offset: optional<Number>
padding: optional<Union<readonly [
Number,
Struct<{
bottom: optionalKey<Number>
left: optionalKey<Number>
right: optionalKey<Number>
top: optionalKey<Number>
}>
]>>
placement: optional<Literals<readonly ["top", "right", "bottom", "left", "top-start", "top-end", "right-start", "right-end", "bottom-start", "bottom-end", "left-start", "left-end"]>>
portal: optional<Boolean>
}>
buttonId: String
}, {
_tag: "CompletedAnchorCombobox"
}>/**
* The Mount this Combobox renders to install a `pointerdown`-cancelling
* capture listener that prevents blur on item presses. Exposed so Scene
* tests can call
* `Scene.Mount.resolve(AttachComboboxPreventBlur, CompletedAttachComboboxPreventBlur())`.
*/
const AttachComboboxPreventBlur: MountDefinitionNoArgs<"AttachComboboxPreventBlur", {
_tag: "CompletedAttachComboboxPreventBlur"
}>/**
* The Mount this Combobox renders to install the input's select-on-focus
* behavior. Exposed so Scene tests can call
* `Scene.Mount.resolve(AttachComboboxSelectOnFocus, CompletedAttachComboboxSelectOnFocus())`.
*/
const AttachComboboxSelectOnFocus: MountDefinitionNoArgs<"AttachComboboxSelectOnFocus", {
_tag: "CompletedAttachComboboxSelectOnFocus"
}>/** Sent when the combobox input loses focus. `restingInputValue` is what the input returns to on close (the parent-owned selection's display text, or empty), computed by the view from `ViewInputs.restingInputValue`. `isClearable` carries whether this close may emit `ClearedSelection`, which a read-only combobox denies. */
const BlurredInput: CallableTaggedStruct<"BlurredInput", {
isClearable: Boolean
restingInputValue: String
}>/** Sent when a nullable combobox closes with an empty input, meaning the user cleared it. The parent clears the selection it owns. */
const ClearedSelection: CallableTaggedStruct<"ClearedSelection", {}>/** Programmatically clicks the active combobox item's DOM element. */
const ClickItem: CommandDefinitionWithArgs<"ClickItem", {
id: String
index: Number
}, Effect<{
_tag: "CompletedClickItem"
}, never, never>>/** Sent when the combobox closes via Escape key or backdrop click. `restingInputValue` is what the input returns to on close (the parent-owned selection's display text, or empty), computed by the view from `ViewInputs.restingInputValue`. `isClearable` carries whether this close may emit `ClearedSelection`, which a read-only combobox denies; the view holds `isReadOnly` and the update does not. */
const Closed: CallableTaggedStruct<"Closed", {
isClearable: Boolean
restingInputValue: String
}>/** Sent when the items panel mounts and Floating UI has positioned it. Update no-ops; surfaces the positioning side effect for DevTools. */
const CompletedAnchorCombobox: CallableTaggedStruct<"CompletedAnchorCombobox", {}>/** Sent when the items panel mounts and the capture-phase pointerdown listener is attached (with or without anchor). Update no-ops; surfaces the listener-attach side effect for DevTools. */
const CompletedAttachComboboxPreventBlur: CallableTaggedStruct<"CompletedAttachComboboxPreventBlur", {}>/** Sent when the input mounts and the focus listener that auto-selects on focus is attached. Update no-ops; surfaces the listener-attach side effect for DevTools. */
const CompletedAttachComboboxSelectOnFocus: CallableTaggedStruct<"CompletedAttachComboboxSelectOnFocus", {}>/** Sent when the programmatic item click command completes. */
const CompletedClickItem: CallableTaggedStruct<"CompletedClickItem", {}>/** Sent when the focus-input command completes. */
const CompletedFocusInput: CallableTaggedStruct<"CompletedFocusInput", {}>/** Sent when the inert-others command completes. */
const CompletedInertOthers: CallableTaggedStruct<"CompletedInertOthers", {}>/** Sent when the scroll lock command completes. */
const CompletedLockScroll: CallableTaggedStruct<"CompletedLockScroll", {}>/** Sent when the combobox backdrop mounts and is portaled to the document body. Update no-ops; surfaces the portal side effect for DevTools. */
const CompletedPortalComboboxBackdrop: CallableTaggedStruct<"CompletedPortalComboboxBackdrop", {}>/** Sent when the restore-inert command completes. */
const CompletedRestoreInert: CallableTaggedStruct<"CompletedRestoreInert", {}>/** Sent when the scroll-into-view command completes after keyboard activation. */
const CompletedScrollIntoView: CallableTaggedStruct<"CompletedScrollIntoView", {}>/** Sent when the scroll unlock command completes. */
const CompletedUnlockScroll: CallableTaggedStruct<"CompletedUnlockScroll", {}>/** Sent when the mouse leaves an enabled item. */
const DeactivatedItem: CallableTaggedStruct<"DeactivatedItem", {}>/** Detects whether the combobox input wrapper moved or the leave animation ended. Whichever comes first; both outcomes signal the Animation submodel that leave is complete. */
const DetectMovementOrAnimationEnd: CommandDefinitionWithArgs<"DetectMovementOrAnimationEnd", {
id: String
}, Effect<{
_tag: "GotAnimationMessage"
message: {
_tag: "Showed"
} | {
_tag: "Hid"
} | {
_tag: "CompletedWaitForPaint"
} | {
_tag: "EndedAnimation"
}
}, never, never>>/** Moves focus to the combobox input after selection or close. */
const FocusInput: CommandDefinitionWithArgs<"FocusInput", {
id: String
}, Effect<{
_tag: "CompletedFocusInput"
}, never, never>>/** Wraps an Animation submodel message for delegation. */
const GotAnimationMessage: CallableTaggedStruct<"GotAnimationMessage", {
message: Union<[CallableTaggedStruct<"Showed", {}>, CallableTaggedStruct<"Hid", {}>, CallableTaggedStruct<"CompletedWaitForPaint", {}>, CallableTaggedStruct<"EndedAnimation", {}>]>
}>/** Marks all elements outside the combobox as inert for modal behavior. */
const InertOthers: CommandDefinitionWithArgs<"InertOthers", {
id: String
}, Effect<{
_tag: "CompletedInertOthers"
}, never, never>>/** Prevents page scrolling while the combobox popup is open in modal mode. */
const LockScroll: CommandDefinitionNoArgs<"LockScroll", Effect<{
_tag: "CompletedLockScroll"
}, never, never>>/** Union of all messages the combobox component can produce. */
const Message: S.Union<[typeof Opened, typeof Closed, typeof BlurredInput, typeof ActivatedItem, typeof DeactivatedItem, typeof SelectedItem, typeof MovedPointerOverItem, typeof RequestedItemClick, typeof SuppressedItemCommit, typeof CompletedLockScroll, typeof CompletedUnlockScroll, typeof CompletedInertOthers, typeof CompletedRestoreInert, typeof CompletedFocusInput, typeof CompletedScrollIntoView, typeof CompletedClickItem, typeof CompletedAnchorCombobox, typeof CompletedAttachComboboxPreventBlur, typeof CompletedAttachComboboxSelectOnFocus, typeof CompletedPortalComboboxBackdrop, typeof GotAnimationMessage, typeof UpdatedInputValue, typeof PressedToggleButton]>/** Schema for the single-select combobox's private interaction state (open/closed status, active item, activation trigger, typed input value). The selection is owned by the parent and passed in via `ViewInputs.maybeSelectedValue`. */
const Model: Struct<{
activationTrigger: Literals<readonly ["Pointer", "Keyboard"]>
animation: Struct<{
id: String
isShowing: Boolean
transitionState: Literals<readonly ["Idle", "EnterStart", "EnterAnimating", "LeaveStart", "LeaveAnimating"]>
}>
id: String
immediate: Boolean
inputValue: String
isAnimated: Boolean
isModal: Boolean
isOpen: Boolean
maybeActiveItemIndex: Option<Number>
maybeLastPointerPosition: Option<Struct<{
screenX: Number
screenY: Number
}>>
nullable: Boolean
selectInputOnFocus: Boolean
}>/** Sent when the pointer moves over a combobox item. */
const MovedPointerOverItem: CallableTaggedStruct<"MovedPointerOverItem", {
index: Number
screenX: Number
screenY: Number
}>/** Sent when the combobox popup opens. Contains an optional initial active item index. */
const Opened: CallableTaggedStruct<"Opened", {
maybeActiveItemIndex: Option<Number>
}>/** Union of out-messages the combobox component can produce. The parent folds `Selected` into the selection it owns and clears that selection on `ClearedSelection`. */
const OutMessage: Union<readonly [
CallableTaggedStruct<"Selected", {
value: String
}>,
CallableTaggedStruct<"ClearedSelection", {}>
]>/**
* The backdrop-portaling Mount this Combobox renders. Exposed so Scene tests can
* call `Scene.Mount.resolve(PortalComboboxBackdrop, CompletedPortalComboboxBackdrop())` to
* acknowledge the mount produced by the rendered backdrop.
*/
const PortalComboboxBackdrop: MountDefinitionNoArgs<"PortalComboboxBackdrop", {
_tag: "CompletedPortalComboboxBackdrop"
}>/** Sent when the optional toggle button is clicked. `restingInputValue` is what the input returns to when the press closes the combobox (the parent-owned selection's display text, or empty), computed by the view from `ViewInputs.restingInputValue`. `isClearable` carries whether a close from this press may emit `ClearedSelection`, which a read-only combobox denies. */
const PressedToggleButton: CallableTaggedStruct<"PressedToggleButton", {
isClearable: Boolean
restingInputValue: String
}>/** Sent when Enter or Space is pressed on the active item, triggering a programmatic click. */
const RequestedItemClick: CallableTaggedStruct<"RequestedItemClick", {
index: Number
}>/** Removes the inert attribute from elements outside the combobox. */
const RestoreInert: CommandDefinitionWithArgs<"RestoreInert", {
id: String
}, Effect<{
_tag: "CompletedRestoreInert"
}, never, never>>/** Scrolls the active combobox item into view after keyboard navigation. */
const ScrollIntoView: CommandDefinitionWithArgs<"ScrollIntoView", {
id: String
index: Number
}, Effect<{
_tag: "CompletedScrollIntoView"
}, never, never>>/** Sent when the user activates an item. Carries the neutral fact that the item was activated; the parent owns the selection and decides what it means (single-select stores the value, nullable single-select toggles it, multi-select toggles the value's membership). Generic over `Value extends string`: the runtime schema stores `value: string`, but the type-level OutMessage exposes `value: Value` so consumers who supply `items: ReadonlyArray<MyUnion>` receive `value: MyUnion` from the factory's `update` without casting. */
const Selected: CallableTaggedStruct<"Selected", {
value: String
}>/** Sent when an item is selected via Enter or click. `displayText` is the item's resting input text, and `wasSelected` reports whether the item was already in the parent-owned selection when activated, so nullable deselect logic works without the Model knowing the selection. */
const SelectedItem: CallableTaggedStruct<"SelectedItem", {
displayText: String
item: String
wasSelected: Boolean
}>/** Sent when Enter is pressed on the active item of a read-only combobox. Update no-ops; the Message exists so the keydown handler returns `Option.some` and calls `preventDefault`, which stops a surrounding form from submitting, and so the keypress stays visible for DevTools. */
const SuppressedItemCommit: CallableTaggedStruct<"SuppressedItemCommit", {}>/** Re-enables page scrolling after the combobox popup closes. */
const UnlockScroll: CommandDefinitionNoArgs<"UnlockScroll", Effect<{
_tag: "CompletedUnlockScroll"
}, never, never>>