import * as _floating_ui_dom from '@floating-ui/dom'; import { ReferenceElement } from '@floating-ui/dom'; import * as _angular_core from '@angular/core'; import { InjectionToken, Signal, Provider } from '@angular/core'; import { FormValueControl } from '@angular/forms/signals'; import { WritingDirection, CollectionHandle, VetoableNativeEvent, VetoableEvent } from 'forty-cdk/core'; import { FloatingSide, FloatingAlign, AnchoredFormValueControlBase, AnchoredPositioningSeedDefaults } from 'forty-cdk/core-overlay'; import * as forty_cdk_combobox from 'forty-cdk/combobox'; /** * Why the combobox closed. Mirrors the menu / select vocabulary so consumers * can switch on the reason regardless of overlay flavor. */ type ForComboboxCloseReason = 'escape' | 'pointerDownOutside' | 'focusOutside' | 'select' | 'tab' | 'programmatic'; /** * Autocomplete mode applied to the input. Mirrors the * [WAI-ARIA combobox autocomplete property](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/#wai-ariaroles,states,andproperties). * * - `'none'`: input acts as a free-text query; no completion is performed. * - `'list'`: the listbox shows filtered options; the input value reflects * the user's typed query verbatim. * - `'inline'`: the rest of the first matching option is auto-completed * into the input as selected text; the listbox does not auto-open. Because * the popup never opens, the default `@if (open())` anatomy renders no * options and the label cache stays cold — inline completion only kicks in * after the options have rendered once (the popup was opened some other way, * e.g. ArrowDown / `openOnFocus`). Use `'both'` when a popup is acceptable. * - `'both'`: combines `'list'` and `'inline'` — listbox opens with the * filtered options *and* the first match auto-completes inline. */ type ForComboboxAutocomplete = 'none' | 'list' | 'inline' | 'both'; /** * Where the auto-highlight seed lands when the listbox opens. `'first'` / `'last'` * bias to the natural extreme (e.g. ArrowDown / ArrowUp on the trigger). `'selected'` * — used by the picker trigger's plain open — seeds the committed selection, falling * back to the first enabled option when there is no selection or it is filtered out. */ type ForComboboxInitialFocus = 'first' | 'last' | 'selected'; interface ForComboboxOptionHandle extends CollectionHandle { /** * Narrowed from {@link CollectionHandle}'s `Node`: the root scrolls the * highlighted option into view. */ readonly host: HTMLElement; readonly id: Signal; readonly value: Signal; readonly label: Signal; readonly disabled: Signal; /** * Index in the consumer's source array. Required when virtualizing so the * directive can fold off-screen options into the snapshot keyed by * absolute position. Optional otherwise — when absent the snapshot falls * back to DOM order. */ readonly posInSet?: Signal; } interface ForComboboxChipHandle extends CollectionHandle { /** * Narrowed from {@link CollectionHandle}'s `Node`: chip navigation moves DOM * focus between hosts. */ readonly host: HTMLElement; readonly value: Signal; } /** * A registered `[forComboboxAction]` — a non-selecting action affordance * (`role="button"`) pinned inside the popup. Tracked in a collection separate * from options / chips so it never touches `value` / `options()` / * `aria-setsize`. */ interface ForComboboxActionHandle extends CollectionHandle { /** * Narrowed from {@link CollectionHandle}'s `Node`: the action Tab ring moves * DOM focus onto the host. */ readonly host: HTMLElement; readonly id: Signal; readonly disabled: Signal; } /** * Coordination contract owned by `[forCombobox]` — the surface a consumer * reads and drives. Advanced consumers inject the token to read the selection, * the query and the open state, and to move them through the root's guards * (`activate` / `removeValue` / `clear` / `openOverlay` / `closeOverlay`). The * wiring the library's own pieces read off the root is not part * of it. * * The value model is always an array — single mode (`multiple=false`, * default) keeps 0 or 1 element, multi mode keeps any number. This mirrors * `[forListbox]` / `[forSelect]` so consumers learn one selection contract * across the whole library. * * Generic over the option value type `T` (default `string`). When a * consumer binds object items the directive infers `T` from `[(value)]` and * the per-piece signatures specialize accordingly. Items are compared via * the consumer-provided `compareWith` and rendered as labels via * `itemToStringLabel`; the form's hidden inputs serialize via * `itemToFormValue`. */ interface ForComboboxContext { /** * The typed query, as a read-only signal. Mutate it through `clear` or the * root's `[(query)]` binding — a direct write would skip the * inline-completion / open-on-query side-effects. */ readonly query: Signal; /** * The current selection, as a read-only signal. Mutate it through the guarded * methods (`activate` / `removeValue` / `clear`) or the root's `[(value)]` * binding — a direct write would bypass the disabled / readonly guards. */ readonly value: Signal; /** * Whether the listbox is open, as a read-only signal. Mutate it through * `toggle` / `openOverlay` / `closeOverlay` or the root's `[(open)]` binding. */ readonly open: Signal; readonly multiple: Signal; /** * The combobox's effective disabled — its own `disabled` input OR'd with a * surrounding disabled `[forFieldset]`. Input, options, clear, and chip pieces * read this so a disabled combobox (or fieldset) is inert and exposes * `aria-disabled`. */ readonly effectiveDisabled: Signal; readonly readonly: Signal; readonly required: Signal; readonly invalid: Signal; readonly pending: Signal; readonly dir: Signal; /** * Registers the element the listbox is positioned against, instead of the * input. The declarative `[forComboboxAnchor]` covers the common case; call * this directly when the anchor element is only reachable imperatively — it * lives in an ancestor component's template, so a directive placed on it would * resolve DI outside this root. At most one anchor may be registered per * `[forCombobox]`; a second one throws. */ registerAnchor(el: HTMLElement): void; /** * Unregisters the positioning anchor, restoring the input fallback. * Reference-based, so an anchor torn down inside `@if` unwinds cleanly. */ unregisterAnchor(el: HTMLElement): void; readonly options: Signal[]>; /** * Selected entries paired with their resolved label (from the option * cache) — convenient for rendering chips with `@for`. Falls back to * `itemToStringLabel(value)` when no matching option is registered (and * to the raw string when `T` is `string`). */ readonly selected: Signal; /** Id of the currently active option (drives `aria-activedescendant` on the input). */ readonly activeId: Signal; /** True when `value` includes `v` per the active equality function. */ isSelected(value: T): boolean; /** * Activate by handle. Single mode replaces + closes + commits label. Multi * mode toggles in/out + stays open + (when `commitOnSelect`) clears the * query so the user can search the next item. No-op on disabled / readonly. */ activate(handle: ForComboboxOptionHandle): void; /** Remove a value from `value()`. Used by chip-remove and Backspace heuristics. */ removeValue(value: T): void; /** * Clear value and (optionally) query. Used by `[forComboboxClear]` and * by the Backspace-on-empty-input heuristic. The query is reset only * when `clearQuery` is true. */ clear(clearQuery?: boolean): void; toggle(): void; openOverlay(initialFocus?: ForComboboxInitialFocus): void; closeOverlay(reason: ForComboboxCloseReason): void; } /** * The combobox's piece-coordination surface: everything the library's own * pieces read off the root that a consumer has no call to touch — the * positioning mirrors `[forComboboxContent]` feeds to floating-ui, the ids the * ARIA wiring points at, the element slots, the label caches behind chip and * inline-completion rendering, the navigation and activation cursors, and the * outside-interaction emit forwarders `injectOverlayShell` drives. * * **Not** part of {@link ForComboboxContext} and never exported * from `public-api.ts`: these are the members a refactor of the anatomy moves, * so freezing them at 1.0 would freeze the anatomy with them. */ interface ComboboxPieceContext { readonly autocompleteMode: Signal; readonly openOnFocus: Signal; readonly openOnQuery: Signal; readonly commitOnSelect: Signal; readonly clearOnQueryChange: Signal; readonly dismissible: Signal; /** * Whether focus returns to the `[forComboboxTrigger]` on close (picker * anatomy). Ignored in the editable anatomy, where focus never left the * input. Default `true`. */ readonly returnFocus: Signal; readonly side: Signal; readonly align: Signal; readonly sideOffset: Signal; readonly alignOffset: Signal; readonly avoidCollisions: Signal; readonly collisionPadding: Signal; readonly sticky: Signal<'partial' | 'always' | false>; readonly hideWhenDetached: Signal; readonly clipUntilPositioned: Signal; readonly loop: Signal; readonly inputId: Signal; readonly contentId: Signal; /** * Id of the `[forComboboxList]` listbox surface (picker anatomy). The input's * `aria-controls` points here when a list is registered; without one it falls * back to {@link contentId} (the editable anatomy where content itself is the * listbox). */ readonly listId: Signal; /** * Id of the element carrying `role="listbox"` — {@link listId} when a * `[forComboboxList]` is registered, otherwise {@link contentId}. The input * targets this with `aria-controls`. */ readonly listboxId: Signal; readonly ariaLabel: Signal; /** * Element floating-ui anchors the listbox against. Prefers an optional * `[forComboboxAnchor]` when registered, otherwise falls back to the input. * Decoupled from `input` so the input keeps driving `aria-controls`, * `aria-activedescendant`, keyboard interaction, and its outside-pointer * exemption regardless of where the listbox paints. */ readonly anchor: Signal; readonly input: Signal; /** * The optional `[forComboboxTrigger]` button (picker anatomy). When present * it is the default positioning anchor (after an explicit `[forComboboxAnchor]`) * and the element focus returns to on close. `null` in the editable anatomy. */ readonly trigger: Signal; readonly content: Signal; /** * The optional `[forComboboxList]` listbox surface (picker anatomy). When * registered, `[forComboboxContent]` drops its `role="listbox"` semantics and * becomes a neutral popup surface; the list carries the listbox role and owns * the options. `null` in the editable anatomy. */ readonly list: Signal; /** True when a `[forComboboxList]` is registered (picker anatomy). */ readonly hasList: Signal; /** Multi-mode chip collection. Order follows DOM (= `value()` order in practice). */ readonly chips: Signal[]>; /** * Non-selecting action collection (`[forComboboxAction]`), kept separate from * `options` so an action never appears in `value()`, `aria-setsize`, or * `aria-posinset`. Order follows DOM. */ readonly actions: Signal; /** * True when at least one registered action is enabled. Gates the input's * Tab-into-actions behavior: with no enabled action, Tab keeps its default * "close the listbox and let Tab flow on" semantics. */ readonly hasEnabledActions: Signal; /** * Move DOM focus within the input↔actions ring (model A). The ring is * `[input, ...enabledActions]` in DOM order and wraps in both directions, so * focus cycles among the input and the pinned actions without ever leaving * (or dismissing) the open popup — Escape / outside-pointer remain the way * out. `fromActionId === null` means the move originates from the input; pass * the action's own id when moving from an action. A stale or disabled * `fromActionId` (e.g. an action disabled while it held focus) is resolved * against the full action collection, stepping to the nearest enabled * neighbor in the requested direction rather than snapping to the input. The * ring omits the input slot when no `[forComboboxInput]` is registered, so * focus cycles among the enabled actions instead of stranding. No-op when no * action is enabled. */ moveActionFocus(fromActionId: string | null, direction: 'next' | 'prev'): void; /** Compare two items for equality. Defaults to `===`; overridden for object values. */ readonly compareWith: Signal<(a: T, b: T) => boolean>; /** Render an item as a string label. Drives chip labels and `commitOnSelect` writes into the input. */ readonly itemToStringLabel: Signal<(item: T) => string>; /** Serialize an item for the hidden input's `value` attribute. */ readonly itemToFormValue: Signal<(item: T) => string>; /** * Scroll the current activedescendant option into view. Called by * `[forComboboxContent]` from the positioner's first-resolved-position hook so * the open-time auto-highlight seed survives the content portal move (which * resets `scrollTop`) and lands after the surface is sized. No-op while * virtualizing. */ scrollActiveOptionIntoView(): void; /** * Cached entries for the currently selected values, in selection order. * Consumed by the chip label resolution and the root's `selected` fallback, * both of which must keep resolving a selected value's label after its option * leaves the rendered set — including across a query rebuild that no longer * contains it. A selected value whose option was never observed is absent * rather than represented; the caller falls back to `itemToStringLabel`. */ selectedEntries(): readonly { id: string; value: T; label: string; disabled: boolean; }[]; /** * Cached entries inline-autocomplete matches against in the input directive. * Non-virtualized: the most recent non-empty option window, so an option * removed from the source stops being offered as a completion. Virtualized: * that window overlaid with the navigator's position map, so completion still * matches options scrolled out of view. Entries carry `disabled` so completion * skips disabled options. */ completionEntries(): readonly { id: string; value: T; label: string; disabled: boolean; }[]; /** * Total number of options in the consumer's source array. Used for * `aria-setsize` and for navigation past the visible window when * virtualizing. Falls back to `options().length` when undefined. */ readonly totalCount: Signal; /** Inclusive-exclusive [start, end) range of options currently rendered when virtualizing. */ readonly visibleRange: Signal; /** True when `id` is the activedescendant. */ isActive(id: string): boolean; /** * Whether a pointer-suppression window is currently open. Opened whenever the * directive scrolls the active option into view during keyboard navigation, * so a synthetic `pointermove` fired because the scroll slid a different * option under a stationary cursor does not hijack the activedescendant. * Options consult this from their hover handler and skip the move while it * returns `true`. */ isPointerSuppressed(): boolean; /** Move the activedescendant to the first / last / next / prev enabled option. */ navigate(direction: 'next' | 'prev' | 'first' | 'last'): void; /** Activate the option currently marked as activedescendant (Enter from the input). */ activateActive(): boolean; /** Set the typed query. Emits inline completion / openOnQuery side-effects via the input directive. */ setQueryFromInput(query: string): void; /** Where focus should land after the listbox opens. The input directive sets this before flipping `open`. */ readonly initialFocus: Signal; /** * The reason of the most recent close (or `null` before any close / after a * fresh open). `[forComboboxContent]` reads this so a `'tab'` close skips the * return-focus move — Tab has already advanced focus and re-focusing the * trigger would steal it back. Only meaningful in the picker anatomy. */ readonly lastCloseReason: Signal; /** * Fires the `(autoFocusOnOpen)` output and returns whether the consumer * vetoed (called `preventDefault()`). The picker anatomy moves focus into the * input on open; a veto skips that imperative move. Editable anatomy never * calls this (focus never moves). */ emitAutoFocusOnOpen(): boolean; /** * Fires the `(autoFocusOnClose)` output and returns whether the consumer * vetoed. The picker anatomy returns focus to the trigger on close; a veto * skips it. */ emitAutoFocusOnClose(): boolean; /** * Escape is consumer-owned and routed through the input directive (focus * stays in the input), so it is invoked directly with the raw * `KeyboardEvent` rather than through the dismissible layer. */ emitEscapeKeyDown(event: KeyboardEvent): void; /** * Outside-interaction emit forwarders. The specific and composite channels * observe the same veto, so `preventDefault()` from either one suppresses the * close that otherwise follows. */ emitPointerDownOutside(veto: VetoableNativeEvent): void; emitFocusOutside(veto: VetoableNativeEvent): void; emitInteractOutside(veto: VetoableNativeEvent): void; /** Implicit close requested by the shell after an un-vetoed outside interaction. */ requestClose(reason: 'pointerDownOutside' | 'focusOutside'): void; } /** * DI token for the combobox's coordination surface, provided by `[forCombobox]`. * Publicly typed as the read surface {@link ForComboboxContext}, which is the whole of * what the token promises a consumer. The pieces read the same token at an internal type * that adds the registration protocol, so a wrapper re-providing it must alias it to the * root: `{ provide: FOR_COMBOBOX_CONTEXT, useExisting: MyCombobox }`, where `MyCombobox` * extends `ForCombobox`. A value that merely satisfies the declared type resolves too, and * is rejected in dev mode by the first piece to reach the protocol. * * `ForCombobox`'s generic does NOT flow to this token: an `InjectionToken` * is a single runtime instance, so it is published at `ForComboboxContext`. * `injectComboboxContext()` re-applies `T` with an `as unknown as` cast, and * each piece (input, option, chip) relies on consumer discipline — the * `[forComboboxOption][value]` and the root `[(value)]` must be the same `T`. * There is no clean fix without abandoning the token pattern; the contract is * the consumer's to honor. Object identity is reconciled at runtime via * `compareWith`, which bounds the practical blast radius of a mismatch. */ declare const FOR_COMBOBOX_CONTEXT: InjectionToken>; /** * The combobox's piece-registration protocol: how the input, the optional * anchor / trigger / list, the options, the chips and the actions wire * themselves into the `[forCombobox]` root, plus the two cursors the pieces set * (the activedescendant and the next open's initial-focus target). * * **Not** part of {@link ForComboboxContext} and never exported * from `public-api.ts`. It is the code most likely to be refactored, so a * consumer must not be able to name — let alone call — it. */ interface ComboboxRegistrationContext { /** Registers the `[forComboboxInput]` element. */ registerInput(el: HTMLInputElement): void; /** Unregisters the input element. Reference-based. */ unregisterInput(el: HTMLInputElement): void; /** Registers the optional `[forComboboxTrigger]` button (picker anatomy). */ registerTrigger(el: HTMLElement): void; /** Unregisters the trigger button. Reference-based. */ unregisterTrigger(el: HTMLElement): void; /** Registers the `[forComboboxContent]` popup surface. */ registerContent(el: HTMLElement): void; /** Unregisters the popup surface. Reference-based. */ unregisterContent(el: HTMLElement): void; /** Registers the optional `[forComboboxList]` listbox surface (picker anatomy). */ registerList(el: HTMLElement): void; /** Unregisters the listbox surface. Reference-based. */ unregisterList(el: HTMLElement): void; /** Registers an option so it joins the navigable collection in DOM order. */ registerOption(handle: ForComboboxOptionHandle): void; /** Unregisters an option. Reference-based. */ unregisterOption(handle: ForComboboxOptionHandle): void; /** Registers a multi-mode chip. Order follows DOM. */ registerChip(handle: ForComboboxChipHandle): void; /** Unregisters a chip. Reference-based. */ unregisterChip(handle: ForComboboxChipHandle): void; /** Registers a non-selecting `[forComboboxAction]`, kept out of the option collection. */ registerAction(handle: ForComboboxActionHandle): void; /** Unregisters an action. Reference-based. */ unregisterAction(handle: ForComboboxActionHandle): void; /** Set the activedescendant directly. Used by options on pointer-move and by the input on inline-completion seed. */ setActiveId(id: string | null): void; /** Set where focus lands after the next open. The input directive calls it before flipping `open`. */ setInitialFocus(target: ForComboboxInitialFocus): void; } /** * The combobox's internal coordination surface: everything * {@link ForComboboxContext} publishes plus the {@link ComboboxPieceContext} * members and the {@link ComboboxRegistrationContext} protocol. * * Never exported from `public-api.ts`. It is the type the pieces read * {@link FOR_COMBOBOX_CONTEXT} at, so a consumer who injects that token gets the * read surface while the pieces get the wiring protocol. `ForCombobox` declares * the members neither interface publishes TS-`private`, which keeps them out of * the emitted `.d.ts` while `useExisting` still satisfies this contract at * runtime. */ interface ComboboxContext extends ForComboboxContext, ComboboxPieceContext, ComboboxRegistrationContext { } /** * Headless implementation of the [WAI-ARIA combobox with listbox popup pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/). * Implements `FormValueControl` from `@angular/forms/signals` * for `[formField]` auto-wiring. * * Generic over the option value type `T` (default `string`). When the * consumer binds object items the directive infers `T` from `[(value)]` * and per-piece signatures (`[forComboboxOption][value]`, * `[forComboboxChip][value]`) specialize accordingly. Object identity is * resolved by the consumer-supplied `[compareWith]` and labels by * `[itemToStringLabel]`; the hidden inputs serialize via * `[itemToFormValue]` (defaults to `JSON.stringify` for non-strings). * * Selection is always modeled as `readonly T[]`: * - In single mode (`multiple=false`, default), the array has 0 or 1 * element and option activation closes the listbox. * - In multi mode, option activation toggles in/out and the listbox stays * open. Selected entries are typically rendered as chips inside * `[forComboboxChips]` next to the input. * * The visible input ("query") and the form value are separate two-way * bindable models — the consumer keeps them in sync via filtering / * display logic, and the primitive only commits to `value` when an option * is explicitly activated. * * Filtering is **always** the consumer's responsibility — the primitive is * headless and doesn't filter the registered options. Render the filtered * subset with `@for` and the registry tracks them automatically. */ declare class ForCombobox extends AnchoredFormValueControlBase implements FormValueControl, ForComboboxContext { #private; protected get positioningDefaults(): AnchoredPositioningSeedDefaults; /** * Two-way bindable. Visible input text. The `model()` change emitter * (`(queryChange)`) fires only on internal mutations (option activation * commit, `clear()`, multi-mode select reset, picker-anatomy reset on * close), never on consumer writes via `[(query)]`. */ readonly query: _angular_core.ModelSignal; /** * Two-way bindable. Selected option values. Single mode (`multiple=false`) * keeps 0 or 1 element; multi mode keeps any number. The `model()` change * emitter (`(valueChange)`) fires only on internal selection changes, * never on consumer writes via `[(value)]`. */ readonly value: _angular_core.ModelSignal; /** * Compare two items for equality. Defaults to `===`, which is the * correct identity for primitive `T` (e.g. strings, numbers). Override * when binding object items so the directive can locate selected / * removed entries by id (or any other stable key) instead of by * reference: `[compareWith]="(a, b) => a.id === b.id"`. */ readonly compareWith: _angular_core.InputSignal<(a: T, b: T) => boolean>; /** * Render an item as a string label. Defaults to `String(item)`, which is * identity for strings. Drives the visible input text after activation * (when `commitOnSelect`) and the chip label fallback in multi mode. * Override when binding object items so the directive can fall back to * a meaningful label without relying on the option cache being warm: * `[itemToStringLabel]="(it) => it.name"`. */ readonly itemToStringLabel: _angular_core.InputSignal<(item: T) => string>; /** * Serialize an item for the hidden input that participates in native * form submission. Defaults to identity for strings and to * `JSON.stringify` for non-string items so the primitive works out of * the box round-tripping objects. Override to emit a specific wire * format — typically a per-item id — when the backend expects that: * `[itemToFormValue]="(it) => it.id"`. */ readonly itemToFormValue: _angular_core.InputSignal<(item: T) => string>; /** * Two-way bindable. Whether the listbox is currently shown. Internal * transitions: input typing (when `openOnQuery`), focus (when * `openOnFocus`), ArrowDown / ArrowUp, Escape, outside dismissal, * single-mode option activation. */ readonly open: _angular_core.ModelSignal; /** * Whether several options can be selected. In multi mode activation toggles an option and the * listbox stays open; single mode keeps `value` at 0 or 1 element and closes on select. */ readonly multiple: _angular_core.InputSignalWithTransform; /** * Autocomplete mode applied to the input. Mirrors the * [WAI-ARIA `aria-autocomplete` property](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/#wai-ariaroles,states,andproperties) * and drives whether the listbox auto-opens on query and whether the * input gets inline-completed with the first match. Renamed from * `autocomplete` so consumers don't conflate it with the native HTML * `autocomplete` attribute (which the directive forces to `"off"`). * * Pure `'inline'` never opens the popup (per APG), so in the default * `@if (open())` anatomy no `[forComboboxOption]` renders and the label * cache starts cold — a first keystroke into a never-opened inline combobox * completes against nothing. Inline completion only works once the options * have rendered at least once (the user opened the popup via ArrowDown / * `openOnFocus`, warming the cache). Prefer `'both'` when a popup is * acceptable, or keep the options mounted, if completion must work from the * very first keystroke. */ readonly autocompleteMode: _angular_core.InputSignal; /** Open the listbox when the input gains focus. Off by default — opening on query / arrow keys is the standard ecosystem behavior. */ readonly openOnFocus: _angular_core.InputSignalWithTransform; /** Open the listbox when the user starts typing. On by default. Only honored when `autocompleteMode` includes a listbox (`'list'` or `'both'`). */ readonly openOnQuery: _angular_core.InputSignalWithTransform; /** * In single mode, copy the activated option's label into `query`. In * multi mode, instead **clear** the query so the user can search the * next item. On by default in both. Set `false` to leave `query` * untouched on activation in either mode. * * Governs the **editable anatomy** only. In the picker anatomy (a * `[forComboboxTrigger]` is registered) the in-panel input is a transient * filter, not the value display: the single-mode label copy is always * skipped and `query` resets to `''` on close regardless of this flag. */ readonly commitOnSelect: _angular_core.InputSignalWithTransform; /** When the user edits the query, automatically clear the committed `value`. Off by default — most apps want the value preserved across query edits. Single-mode only. */ readonly clearOnQueryChange: _angular_core.InputSignalWithTransform; /** * Auto-highlight the first enabled option whenever the listbox is open * and no activedescendant is set (e.g. after the consumer's filter * removed the previously-active option). On by default. Set `false` for * "user must arrow before anything is highlighted" behavior. */ readonly autoHighlight: _angular_core.InputSignalWithTransform; /** * Writing direction. Drives chip-cluster keyboard navigation (ArrowLeft / * ArrowRight semantics swap in RTL so they follow the visual order, not DOM * order) and the default `align` of the listbox (anchors to the right edge * of the input in RTL). When unset (default `null`), the inherited ambient * direction is resolved from the nearest ancestor carrying a `dir` attribute * (or ``), defaulting to `'ltr'`. An explicit `[dir]` always wins * and the resolved value is reflected to the host `dir` attribute. */ readonly _dirInput: _angular_core.InputSignal; readonly dir: _angular_core.Signal; /** * Whether arrow navigation wraps past the first / last enabled option. */ readonly loop: _angular_core.InputSignalWithTransform; /** When true (default), Escape, pointer-down outside, and focus outside close the listbox. */ readonly dismissible: _angular_core.InputSignalWithTransform; /** * When true (default), focus returns to the `[forComboboxTrigger]` on close. * Only relevant in the picker anatomy (a trigger is registered) — in the * editable anatomy focus never leaves the input, so there is nothing to * return. */ readonly returnFocus: _angular_core.InputSignalWithTransform; /** Manual `aria-label` on the listbox (`[forComboboxList]`, or `[forComboboxContent]` in the editable anatomy) when the input isn't a meaningful name. */ readonly ariaLabel: _angular_core.InputSignal; /** * Total number of options in the consumer's source array. Set when wiring * up a virtualized listbox (only the visible window is rendered) so the * directive can reflect `aria-setsize` and walk the snapshot for * navigation past the rendered range. Defaults to `undefined`, in which * case the directive falls back to `options().length` (the live registry). */ readonly totalCount: _angular_core.InputSignalWithTransform; /** * Inclusive-exclusive `[start, end)` range of options currently rendered * in the DOM. Used by `navigate()` to translate "move to absolute * position N" into either an in-window highlight update or a request to * the consumer to scroll N into view (`(scrollToIndex)`). When * `undefined` (default), navigation assumes every option in the snapshot * is rendered — appropriate for non-virtualized lists. */ readonly visibleRange: _angular_core.InputSignal; /** * Optional virtualized-only seam that tells the directive the source dataset * changed **without** a `totalCount` transition — a same-length re-sort or * refresh (e.g. sorting a 1000-row list). Bind any value that changes on such * a refresh (a version counter, the array reference, a sort-key string); when * it changes the position snapshot rebuilds from empty so navigation never * resolves against a stale off-window entry. Leave unset (default) when the * dataset only ever changes length. Equivalent to calling * {@link ForCombobox.invalidateSnapshot} imperatively. */ readonly dataVersion: _angular_core.InputSignal; /** * Emitted when keyboard navigation needs to land on an option whose * absolute index falls outside `visibleRange()`. Wire this to the * consumer's virtualizer (`scrollToIndex(idx)` on `@tanstack/virtual`, * `virtua`, etc.); once the option mounts, the directive seeds * `aria-activedescendant` automatically. */ readonly scrollToIndex: _angular_core.OutputEmitterRef; /** Emitted before Escape closes the listbox. Call `preventDefault()` to keep it open. */ readonly escapeKeyDown: _angular_core.OutputEmitterRef>; /** Emitted before an outside pointer-down closes the listbox. Vetoable with `preventDefault()`. */ readonly pointerDownOutside: _angular_core.OutputEmitterRef>; /** Emitted before focus leaving the surface closes the listbox. Vetoable with `preventDefault()`. */ readonly focusOutside: _angular_core.OutputEmitterRef>; /** * Emitted alongside {@link pointerDownOutside} and {@link focusOutside} for consumers that do not * care which one occurred. A `preventDefault()` on either channel suppresses the close. */ readonly interactOutside: _angular_core.OutputEmitterRef>; /** * _(picker anatomy only)_ Fires just before focus moves into the input on * open. Call `preventDefault()` on the emitted veto to skip the imperative * focus move. Only emitted when a `[forComboboxTrigger]` is registered — the * editable anatomy keeps focus in the input the whole time and has no move to * veto. */ readonly autoFocusOnOpen: _angular_core.OutputEmitterRef; /** * _(picker anatomy only)_ Fires just before focus returns to the trigger on * close. Call `preventDefault()` on the veto to suppress the return-focus. */ readonly autoFocusOnClose: _angular_core.OutputEmitterRef; readonly inputId: _angular_core.WritableSignal; readonly contentId: _angular_core.Signal; readonly listId: _angular_core.WritableSignal; readonly input: _angular_core.Signal; readonly trigger: _angular_core.Signal; /** * Element floating-ui anchors the listbox against. Resolution order: * explicit `[forComboboxAnchor]` → `[forComboboxTrigger]` (picker anatomy) → * the input (editable anatomy fallback, so existing comboboxes keep their * behavior). Decoupled from `input` so the input keeps driving * `aria-controls`, `aria-activedescendant`, keyboard interaction, and its * dismissal exemption regardless of where the listbox paints. */ readonly anchor: _angular_core.Signal<_floating_ui_dom.ReferenceElement | null>; readonly content: _angular_core.Signal; readonly list: _angular_core.Signal; /** True once a `[forComboboxList]` has registered (picker anatomy). */ readonly hasList: _angular_core.Signal; /** * Id of the element carrying `role="listbox"`: the list when one is * registered (picker anatomy), otherwise the content surface (editable * anatomy). The input targets this with `aria-controls`. */ readonly listboxId: _angular_core.Signal; readonly options: _angular_core.Signal[]>; readonly chips: _angular_core.Signal[]>; readonly actions: _angular_core.Signal; readonly hasEnabledActions: _angular_core.Signal; private readonly initialFocus; private readonly lastCloseReason; readonly activeId: _angular_core.Signal; /** * Force the virtualized position snapshot to rebuild from empty on the next * fold, discarding stale off-window entries. Call after a same-length dataset * refresh (a re-sort / reload that keeps `totalCount` unchanged) when you * cannot express the change through the reactive `[dataVersion]` input. No-op * when the combobox is not virtualized (`totalCount` unset). */ invalidateSnapshot(): void; readonly selected: _angular_core.Signal; /** * Read-only single-select convenience view of {@link value}. Returns the * sole selected item when exactly one is selected, otherwise `null` (empty * selection, or multiple selections in `multiple` mode). Lets single-select * consumers read `selectedItem()` instead of unwrapping `value()[0]`. The * array-backed `value` model remains the source of truth and the * `FormValueControl` contract; this is a derived accessor. Distinct from * {@link selected}, which pairs every selected value with its resolved * label for chip rendering. */ readonly selectedItem: _angular_core.Signal; protected fieldLabelledElement(): HTMLElement | null; protected fieldLabelledElementId(): string; /** * Move focus to the `role="combobox"` input, implementing * `FormValueControl.focus` from `@angular/forms/signals`. Without this override * Signal Forms would focus the host `[forCombobox]` wrapper — which carries no * focusable role — so focus-on-error would silently go nowhere. No-op when * disabled or before the input has registered. */ focus(options?: FocusOptions): void; constructor(); private registerInput; private unregisterInput; /** See {@link ForComboboxContext.registerAnchor}. */ registerAnchor(el: HTMLElement): void; /** See {@link ForComboboxContext.unregisterAnchor}. */ unregisterAnchor(el: HTMLElement): void; private registerTrigger; private unregisterTrigger; private registerContent; private unregisterContent; private registerList; private unregisterList; private registerOption; private unregisterOption; private registerChip; private unregisterChip; private registerAction; private unregisterAction; private moveActionFocus; isSelected(v: T): boolean; private isActive; private isPointerSuppressed; activate(handle: ForComboboxOptionHandle): void; removeValue(v: T): void; private activateActive; private navigate; private setQueryFromInput; private setActiveId; /** * Scroll the current activedescendant option into view. Driven from * `[forComboboxContent]`'s positioner first-resolved-position hook * (`onFirstPosition`) — the only moment both prerequisites hold: the content * has been portaled to `document.body` (which resets the scroll container's * `scrollTop` to 0, wiping the seed scroll the auto-highlight bridge applied * during change detection) and `@floating-ui/dom`'s `size` middleware has * constrained the surface to its `max-height` (so it is actually scrollable). * * Re-applies the scroll unconditionally — the bridge already recorded this id * as positioned, but the portal move invalidated the real scroll position, so * the usual "already positioned" guard must not short-circuit here. Fires once * per open (the positioner hook is per-open, not per-run, so a side flip while * open never re-fires it and yanks the user's scroll back), so a later hover * never scrolls. No-op while virtualizing: the navigator owns the virtualized * scroll and the indexed seed is intentionally passive. */ private scrollActiveOptionIntoView; private selectedEntries; private completionEntries; clear(clearQuery?: boolean): void; private setInitialFocus; toggle(): void; /** * Opens the listbox, seeding where the auto-highlight lands. Idempotent: an * open on a listbox that is already open re-arms nothing, so an ArrowDown that * finds the surface mounted leaves the current activedescendant where the * user navigated it. */ openOverlay(initialFocus?: ForComboboxInitialFocus): void; /** * Closes the listbox, recording `reason` as the {@link lastCloseReason} the * content reads. Idempotent, unlike the shared machine's own close: this is a * consumer-facing method, so a `closeOverlay` on an already-closed listbox must * not overwrite the previous reason (a `'tab'` close has to survive so the * content skips its return-focus) nor re-run the close side effects. */ closeOverlay(reason: ForComboboxCloseReason): void; /** Fire `(autoFocusOnOpen)` and report whether the consumer vetoed the focus move. */ private emitAutoFocusOnOpen; /** Fire `(autoFocusOnClose)` and report whether the consumer vetoed the return-focus. */ private emitAutoFocusOnClose; private emitEscapeKeyDown; /** * Outside-interaction emit forwarders. The shared `#pendingOutsideVeto` * reuse between the specific outside channels and the composite * `interactOutside` lives in `injectOverlayShell`; these only fire the * matching output with the veto the shell built. */ private emitPointerDownOutside; private emitFocusOutside; private emitInteractOutside; /** * Implicit close requested by the shell after an un-vetoed outside * interaction. The shared machine's own open guard keeps a stale event from * clobbering the previous close reason. * * The touch mark sits **ahead** of that guard rather than on the * machine's `onDismiss` hook, which is where `[forSelect]` / `[forTimePicker]` * put theirs: an outside interaction is this control's blur, so it marks the * combobox touched exactly like {@link onFocusOut} does — whether or not the * listbox was still open when the shell's event arrived. Wiring the hook * instead would additionally mark touched on Escape, which leaves focus in the * input and therefore blurs nothing. */ private requestClose; protected onFocusOut(event: FocusEvent): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration, "[forCombobox]", ["forCombobox"], { "query": { "alias": "query"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; "itemToStringLabel": { "alias": "itemToStringLabel"; "required": false; "isSignal": true; }; "itemToFormValue": { "alias": "itemToFormValue"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "autocompleteMode": { "alias": "autocompleteMode"; "required": false; "isSignal": true; }; "openOnFocus": { "alias": "openOnFocus"; "required": false; "isSignal": true; }; "openOnQuery": { "alias": "openOnQuery"; "required": false; "isSignal": true; }; "commitOnSelect": { "alias": "commitOnSelect"; "required": false; "isSignal": true; }; "clearOnQueryChange": { "alias": "clearOnQueryChange"; "required": false; "isSignal": true; }; "autoHighlight": { "alias": "autoHighlight"; "required": false; "isSignal": true; }; "_dirInput": { "alias": "dir"; "required": false; "isSignal": true; }; "loop": { "alias": "loop"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; "returnFocus": { "alias": "returnFocus"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "totalCount": { "alias": "totalCount"; "required": false; "isSignal": true; }; "visibleRange": { "alias": "visibleRange"; "required": false; "isSignal": true; }; "dataVersion": { "alias": "dataVersion"; "required": false; "isSignal": true; }; }, { "query": "queryChange"; "value": "valueChange"; "open": "openChange"; "scrollToIndex": "scrollToIndex"; "escapeKeyDown": "escapeKeyDown"; "pointerDownOutside": "pointerDownOutside"; "focusOutside": "focusOutside"; "interactOutside": "interactOutside"; "autoFocusOnOpen": "autoFocusOnOpen"; "autoFocusOnClose": "autoFocusOnClose"; }, never, never, true, never>; } /** * Optional positioning anchor. When present, `[forComboboxContent]` is * positioned against this element instead of `[forComboboxInput]` — useful * when the input lives inside a decorated field box (padding, prefix icon, * clear button, chip cluster) and the listbox should match the visible field * rather than the inner ``. * * Only positioning changes: the input still owns `aria-controls`, * `aria-expanded`, `aria-activedescendant`, keyboard interaction, and its * exemption from outside-pointer dismissal. If no anchor is registered the * listbox falls back to anchoring against the input, so existing usages are * unaffected. * * At most one `[forComboboxAnchor]` may be registered per `[forCombobox]`; a * second one throws. * * ```html *
*
* * * *
* @if (open()) { *
* } *
* ``` */ declare class ForComboboxAnchor { #private; constructor(); static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Button that opens the listbox and keeps showing the committed selection * (label + icon) while the search input lives **inside** the panel — the * "combobox with trigger" / picker anatomy. Apply on * a real ` *
* @for (item of filtered(); track item.id) { *
{{ item.name }}
* } *
* * ``` */ declare class ForComboboxAction { #private; protected readonly buttonType: _angular_core.Signal; /** * Disable the action. A disabled action drops out of the focus ring (its * `tabindex` is removed and keyboard navigation skips it), reflects * `aria-disabled="true"` + `data-disabled=""`, and ignores activation. */ readonly disabled: _angular_core.InputSignalWithTransform; /** * Fired on click / Enter / Space. Purely a side-effect hook — it **never** * mutates `[(value)]`, so the form model and `options()` are untouched. The * consumer decides what happens (create an item, open a dialog, …) and whether * to close the popup afterwards. */ readonly activate: _angular_core.OutputEmitterRef; /** Stable id for the host, used as the ring key. Adopts a consumer-set static `id`. */ readonly id: _angular_core.Signal; /** True while the action holds DOM focus — reflected as `data-highlighted`. */ protected readonly focused: _angular_core.WritableSignal; constructor(); protected onClick(): void; protected onKeyDown(event: KeyboardEvent): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Live-region slot for async-filtering feedback (loading, result count, * "no matches", error messages). Apply on a `
` inside the listbox or * next to the input. The directive sets `role="status"` so messages projected * as content are announced to screen readers when they change. * * The role is the piece's **single** live-region channel — it already implies `aria-live="polite"` * and `aria-atomic="true"`, so neither attribute is emitted beside it. * * The directive is **content-driven** and picks no message of its own. Project whatever the * consumer wants and use the exposed `count` signal to interpolate the option count when relevant. * * Place it inside `[forComboboxContent]` but **outside** the `[forComboboxList]` that owns the * options: content carries `role="listbox"` in the editable anatomy, so wrapping the options in a * list keeps this `role="status"` region a sibling of the listbox rather than an invalid child. * * ```html *
* * @if (open()) { *
*
* @if (loading()) { * Searching… * } @else if (status.count() === 0) { * No matches. * } @else { * {{ status.count() }} results. * } *
*
* @for (it of filtered(); track it.id) { *
{{ it.label }}
* } *
*
* } *
* ``` * * For an empty-only slot that auto-hides when there are options, use * `[forComboboxEmpty]`. `[forComboboxStatus]` stays mounted regardless * so transitions like "loading → 5 results" are announced as a single * change to the same live region. */ declare class ForComboboxStatus { #private; /** * Number of currently registered options. Reflects the live size of the * filtered listbox so the consumer can interpolate `{{ status.count() }}` * inside the live region. */ readonly count: _angular_core.Signal; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Optional clear button. Apply on a ` * * } * *
* … * * ``` * * Carries `role="group"` with `aria-label` (default `'Selected items'`, * override via `[ariaLabel]`) so screen readers announce the chip cluster as * a single unit. The directive itself doesn't manage focus or selection — the * chips and the input own that — but its presence groups them for * assistive tech. */ declare class ForComboboxChips { #private; protected readonly ctx: ComboboxContext; /** * Accessible name for the chip cluster, exposed as `role="group"`'s * `aria-label` so screen readers announce the selected chips as a single * unit. Defaults to the scope's `chipsAriaLabel` (`'Selected items'` unless * overridden via `provideForComboboxDefaults`); set `[ariaLabel]` to * override per-instance, or `null` to drop the attribute. */ readonly ariaLabel: _angular_core.InputSignal; protected readonly resolvedAriaLabel: _angular_core.Signal; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * One chip representing a selected value in multi mode. Apply on a * `` (or any inline-block element) inside `[forComboboxChips]`. * The chip is **out of the Tab cycle** (`tabindex="-1"`) by design: * the user reaches it via the input's Backspace heuristic (Backspace on * an empty input focuses the last chip), then navigates between chips * with ArrowLeft / ArrowRight or removes them with Backspace / Delete. * * Keyboard while the chip has focus (LTR; the ArrowLeft/Right roles swap * in RTL so they always follow visual order): * - **ArrowLeft** — focus the previous chip; bounces if first. * - **ArrowRight** — focus the next chip; if at the last, focus the input. * - **Backspace / Delete** — remove this chip + focus the previous chip, * or the next chip when there is no previous one (so removing the first * chip lands on the new first chip), falling back to the input only when * the removed chip was the last one standing. * - **Escape** — dismiss the open popup via the consumer's `(escapeKeyDown)` * (a veto keeps it open), then return focus to the input; with the popup * already closed it simply returns focus to the input. * * Click on the chip body (excluding the remove button) just focuses the * chip — useful as an alternative to the Backspace path. */ declare class ForComboboxChip { #private; protected readonly ctx: ComboboxContext; /** * The value this chip represents — must match an entry in * `[forCombobox][(value)]` per the parent's `[compareWith]`. * Generic over `T` (default `string`); inferred from the binding * (`[value]="someObject"` specializes `T`). */ readonly value: _angular_core.InputSignal; /** * `data-value` reflection — for string `T` this is the value verbatim * (unchanged from the pre-generic behaviour); for object `T` it uses * the parent's `itemToFormValue` so the attribute carries the same * wire format as the hidden inputs (typically JSON or a per-item id). */ protected readonly dataValue: _angular_core.Signal; /** Resolved label of the underlying option, used by `[forComboboxChipRemove]` for its `aria-label`. */ readonly label: _angular_core.Signal; constructor(); protected onKeyDown(event: KeyboardEvent): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration, "[forComboboxChip]", ["forComboboxChip"], { "value": { "alias": "value"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>; } /** * Remove button inside a `[forComboboxChip]`. Apply on a * `