import * as _angular_core from '@angular/core'; import { InjectionToken, Signal, Provider } from '@angular/core'; import { FormValueControl } from '@angular/forms/signals'; import { WritingDirection, ListNavigationAction, FormUiControlBase, RovingTabindex } from 'forty-cdk/core'; interface ForListboxOptionHandle { readonly host: HTMLElement; readonly value: Signal; readonly disabled: Signal; readonly id: Signal; readonly posInSet: Signal; } /** * Coordination contract owned by `ForListbox`. Each `ForListboxOption` * registers a handle on init so the group can react to disabled changes, * compute the first-enabled tab entry, and run typeahead matching. * * Generic over the option value type `T` (default `string` at the public * root). When a consumer binds object items the directive infers `T` from * `[(value)]` and `[forListboxOption][value]`; object identity is resolved * by the consumer-supplied `compareWith` and the form's hidden inputs * serialize via `itemToFormValue`. Option text labels are still read from the * rendered `textContent`. */ interface ForListboxContext { readonly value: Signal; readonly multiple: Signal; /** * The listbox's effective disabled — its own `disabled` input OR'd with a * surrounding disabled `[forFieldset]`. Each `ForListboxOption` ORs this into * its own `effectiveDisabled`, so a disabled listbox (or fieldset) disables * every option. */ readonly effectiveDisabled: Signal; readonly readonly: Signal; readonly orientation: Signal<'horizontal' | 'vertical'>; readonly dir: Signal; readonly selectionFollowsFocus: Signal; /** Compare two items for equality. Defaults to `===`; overridden for object values. */ readonly compareWith: Signal<(a: T, b: T) => boolean>; /** Serialize an item for the hidden input's `value` attribute. Defaults to `String(item)`. */ readonly itemToFormValue: Signal<(item: T) => string>; /** * Full source length when virtualizing, `undefined` in the roving-tabindex * path. Drives the option's `aria-setsize` / `aria-posinset` and the * option's focus-model branch. */ readonly totalCount: Signal; /** * The active option's `id` when using the activedescendant focus model, * `null` in the roving-tabindex path. Moved by keyboard navigation and by * hover alike. Options read this to compute `data-highlighted`. */ readonly activeDescendantId: Signal; isSelected(value: T): boolean; /** Toggle in multi-mode, replace in single-mode. No-op on disabled / readonly. */ activate(value: T): void; /** Move focus from `currentOption` according to `action`. May also select if `selectionFollowsFocus` is on. */ navigate(currentOption: HTMLElement, action: ListNavigationAction): void; /** * Multi-mode only. Move focus to the next/prev enabled option AND toggle its * selected state — APG "Shift+ArrowDown / Shift+ArrowUp toggles selection * while moving focus". No-op in single mode, on disabled, or when no enabled * neighbor exists. On `readonly` the focus still moves (matching * {@link navigate}); only the selection mutation is blocked. */ extendByArrow(currentOption: HTMLElement, action: 'next' | 'prev'): void; /** * Multi-mode only. APG "Shift+Space": select every enabled option from the * anchor (set on the most recent unmodified activation) up to and including * `currentOption`. Existing selection outside the range is preserved. No-op * in single mode or when the listbox is disabled / readonly. */ selectRangeToFocused(currentOption: HTMLElement): void; /** * Multi-mode only. APG "Ctrl/Cmd+A": select every enabled option. If every * enabled option is already selected, clears the selection (toggle). */ selectAll(): void; /** * Multi-mode only. APG "Ctrl+Shift+Home / Ctrl+Shift+End": select every * enabled option from `currentOption` (inclusive) to the first / last * enabled option, and move focus to that edge. */ selectFromCurrentToEdge(currentOption: HTMLElement, edge: 'first' | 'last'): void; /** * Forward a keydown to the typeahead helper. If the key is a printable * character, finds the first matching option and focuses it; returns true * to indicate the event was consumed. */ handleTypeahead(event: KeyboardEvent): boolean; /** * Pre-focus tab-stop policy: with at least one selection, the first selected * enabled option is the sole entry point; otherwise the first enabled option * in DOM order. Guarantees a single `tabindex="0"` before roving takes over. */ isFirstFocusableOption(el: HTMLElement): boolean; /** * `true` when `el` is the highlighted option in the roving-tabindex path * (reflected as `data-highlighted`): the one the pointer is over, else the * roving active option. */ isOptionHighlighted(el: HTMLElement): boolean; /** * Roving-tabindex value for `el`: `0` for the active option once roving has * taken over, `-1` otherwise. Returns `null` before any option is active so * the caller can fall back to {@link isFirstFocusableOption}. */ optionTabindex(el: HTMLElement): -1 | 0 | null; /** * Mark `el` as the roving-tabindex active option (called on option focus). * Also drops any pointer highlight, so the keyboard channel owns the highlight * again from the move that focused `el`. */ setActiveOption(el: HTMLElement): void; /** * All registered options, in DOM (rendered) order. Exposed for container-level * coordinators that compose onto the listbox — e.g. `ForListboxReorder` reads the * ordered hosts to resolve drop targets and emit reorder indices — without each * option needing a `[forDraggable]` that would fight the listbox's own roving tabindex. */ readonly options: Signal[]>; /** * Called by an option on click. In the virtualized path, moves * `aria-activedescendant` to that option and returns DOM focus to the * container. A no-op in the roving-tabindex path. */ notifyOptionClick(optionId: string): void; registerOption(handle: ForListboxOptionHandle): void; unregisterOption(handle: ForListboxOptionHandle): void; } /** * DI token for the listbox's coordination surface, provided by `[forListbox]`. * * Publicly typed as the read surface {@link ForListboxContext}, which is the whole * of what the token promises a consumer. The options read the same token at an * internal type that adds the pointer-highlight channel, so a wrapper re-providing * it must alias it to the root: `{ provide: FOR_LISTBOX_CONTEXT, useExisting: MyListbox }`, * where `MyListbox` extends `ForListbox`. A value that merely satisfies the declared * type resolves too, and is rejected in dev mode by the first piece to reach the channel. */ declare const FOR_LISTBOX_CONTEXT: InjectionToken>; /** * Headless implementation of the [WAI-ARIA Listbox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/). * 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 * `[forListboxOption][value]`; object identity is resolved by the * consumer-supplied `[compareWith]` and the hidden inputs serialize * via `[itemToFormValue]`. Option display text is read from the rendered * `textContent`, so no separate label function is needed. * * Selection is always modeled as `readonly T[]`: * - In single mode (`multiple=false`, default), the array has 0 or 1 element. * - In multi mode, any number of items can be selected. * * Single-select consumers can read the sole value through the * {@link ForListbox.selected} convenience accessor instead of unwrapping the * array. * * Keyboard supports the full APG-recommended model: arrows + Home/End for * focus movement, Space/Enter (via native ` * } * * ``` */ declare class ForListboxReorder { #private; /** * Disables reorder interactions while leaving selection / typeahead intact. Named distinctly * from the listbox's own `disabled` so the two never share a single `[disabled]` binding on the * shared host. The listbox being disabled (its own `disabled` or a surrounding `[forFieldset]`) * also disables reorder. */ readonly reorderDisabled: _angular_core.InputSignalWithTransform; /** Emitted once per committed reorder gesture with the previous / new option index. */ readonly optionReorder: _angular_core.OutputEmitterRef; protected readonly _dragging: _angular_core.WritableSignal; constructor(); static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Defaults inherited by descendant listboxes in the surrounding injector * scope. Configure with `provideForListboxDefaults` either at the * application root or in any component's `providers` array; partial * overrides merge with the parent scope. */ interface ForListboxDefaults { /** * Single-mode only: when `true`, arrow navigation also selects the * focused option. APG calls this optional and recommends caution — * leave `false` unless the UX truly benefits from selection following * focus. */ selectionFollowsFocus: boolean; /** * `[forListboxReorder]` announcement when an option is lifted for reorder. * `index` and `total` are 1-based. Override to localize. */ reorderAnnounceLift: (label: string, index: number, total: number) => string; /** * `[forListboxReorder]` announcement when the reorder drop position changes. * `index` and `total` are 1-based. Override to localize. */ reorderAnnounceMove: (label: string, index: number, total: number) => string; /** * `[forListboxReorder]` announcement on a committed reorder drop. `index` and * `total` are 1-based. Override to localize. */ reorderAnnounceDrop: (label: string, index: number, total: number) => string; /** * `[forListboxReorder]` announcement when a reorder is cancelled. Override to * localize. */ reorderAnnounceCancel: (label: string) => string; } /** Token holding the resolved listbox defaults for the current scope. */ declare const FOR_LISTBOX_DEFAULTS: _angular_core.InjectionToken; /** * Configures forty-cdk listbox defaults for this injector scope. Partial * overrides inherit unspecified keys from the parent scope (or library * defaults at the root). */ declare function provideForListboxDefaults(defaults?: Partial): Provider[]; /** * Exact public names of every `ForListbox` input, its models included. Spread it into the * `inputs` array of a `hostDirectives` entry so a wrapper component re-exposes the * primitive's full surface — the Signal Forms members `[formField]` binds among them — * without hand-maintaining the list. Always spread into an inline object literal as shown * below: the literal is what keeps the entry statically analyzable for consumers compiling * against the published package. An anti-drift spec fails when this list no longer matches * the directive's actual API. See `docs/wrapping-form-primitives.md` for both supported * wrapping patterns. * * @example * ```ts * @Component({ * selector: 'div[myListbox]', * template: '', * hostDirectives: [ * { * directive: ForListbox, * inputs: [...FOR_LISTBOX_HOST_DIRECTIVE_INPUTS], * outputs: [...FOR_LISTBOX_HOST_DIRECTIVE_OUTPUTS], * }, * ], * }) * export class MyListbox {} * ``` */ declare const FOR_LISTBOX_HOST_DIRECTIVE_INPUTS: readonly ["value", "ariaLabel", "compareWith", "dataVersion", "dir", "dirty", "disabled", "errors", "invalid", "itemToFormValue", "loop", "multiple", "name", "orientation", "pending", "readonly", "required", "selectionFollowsFocus", "totalCount", "touched", "visibleRange"]; /** * Exact public names of every `ForListbox` output, the Signal Forms `touch` output * included. Spread it into the `outputs` array of the same `hostDirectives` entry as * {@link FOR_LISTBOX_HOST_DIRECTIVE_INPUTS}. */ declare const FOR_LISTBOX_HOST_DIRECTIVE_OUTPUTS: readonly ["scrollToIndex", "touch", "touchedChange", "valueChange"]; export { FOR_LISTBOX_CONTEXT, FOR_LISTBOX_DEFAULTS, FOR_LISTBOX_HOST_DIRECTIVE_INPUTS, FOR_LISTBOX_HOST_DIRECTIVE_OUTPUTS, FOR_LISTBOX_OPTION, ForListbox, ForListboxGroup, ForListboxGroupLabel, ForListboxOption, ForListboxOptionIndicator, ForListboxReorder, provideForListboxDefaults }; export type { ForListboxContext, ForListboxDefaults, ForListboxOptionHandle, ForListboxReorderEvent };