import * as _angular_core from '@angular/core'; import { InjectionToken, Signal, Provider } from '@angular/core'; import * as forty_cdk_select from 'forty-cdk/select'; import { FormValueControl } from '@angular/forms/signals'; import { WritingDirection, CollectionHandle, VetoableNativeEvent, VetoableEvent } from 'forty-cdk/core'; import { FloatingSide, FloatingAlign, ListboxOverlayContext, AnchoredFormValueControlBase, AnchoredPositioningSeedDefaults } from 'forty-cdk/core-overlay'; /** * Why a select requested close. Mirrors the menu primitive vocabulary so * downstream code that switches on close reasons stays consistent. */ type ForSelectCloseReason = 'escape' | 'pointerDownOutside' | 'focusOutside' | 'select' | 'tab' | 'programmatic'; /** * Where focus lands when the listbox opens. `'selected'` snaps to the first * currently-selected enabled option (matches native ``). All `side`/`align`/`*Offset`/`sticky`/ * `hideWhenDetached`/`clipUntilPositioned`/`avoidCollisions` inputs are no-ops in * `'item-aligned'` mode (only `collisionPadding` is honored). */ readonly position: Signal<'popper' | 'item-aligned'>; 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 orientation: Signal<'horizontal' | 'vertical'>; readonly selectionFollowsFocus: Signal; readonly ariaLabel: Signal; /** * The `[forSelect]` root (wrapper) element. Lets the trigger tell a focus * move to a sibling *inside* the wrapper (e.g. a clear button next to the * trigger) apart from a genuine focus leave, so `touched` isn't flipped * prematurely when focus stays within the control. */ readonly host: HTMLElement; /** 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>; /** * Host element of the first enabled, currently-selected option, or `null` * when no selection exists. Used by `position="item-aligned"` to anchor * the listbox over the trigger; falls back to the first enabled option * inside the listbox when this is `null`. */ readonly selectedOptionEl: Signal; /** * Multi-select only (APG range keyboard). Move focus to the next / previous * enabled option and toggle it in/out of the selection, without moving the * range anchor. Non-wrapping. No-op in single mode, when disabled, or in the * virtualized path. Focus still moves under `readonly`; only the selection * mutation is blocked. */ extendByArrow(currentOption: HTMLElement, action: 'next' | 'prev'): void; /** * Multi-select only (APG range keyboard, Shift+Space). Add every enabled * option between the range anchor and the focused option to the selection, * preserving any selection outside the span. Falls back to selecting just the * focused option when no anchor exists. No-op in single mode, disabled, * readonly, or the virtualized path. */ selectRangeToFocused(currentOption: HTMLElement): void; /** * Multi-select only (APG range keyboard, Ctrl+Shift+Home / End). Add every * enabled option from the focused option to the first / last option to the * selection and move focus to that edge, preserving any selection outside the * span. No-op in single mode, disabled, or the virtualized path. Focus still * moves under `readonly`; only the selection mutation is blocked. */ selectFromCurrentToEdge(currentOption: HTMLElement, edge: 'first' | 'last'): void; /** Open-state typeahead: focus the first enabled option whose text matches the buffered prefix. */ handleTypeahead(event: KeyboardEvent): void; /** * Closed-state typeahead (single mode only). Selects the first matching * option directly without opening the listbox — mirrors native ``: * - **Closed trigger** (single mode only): printable keys select the matching * option immediately without opening the listbox. * - **Open listbox**: printable keys move focus to the first matching option * (selection still requires Enter / Space / click). */ declare class ForSelect extends AnchoredFormValueControlBase implements FormValueControl, ForSelectContext { #private; protected readonly positioningDefaults: forty_cdk_select.ForSelectDefaults; /** The `[forSelect]` root element (see `SelectPieceContext.host`). */ private readonly host; /** * Two-way bindable. Selected option values. Single-mode keeps 0 or 1 * element. 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 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>; /** * 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>; /** * Resolve the display label for an item without the listbox mounted. * When set, {@link selectedLabels} (and therefore `[forSelectValue]`) * renders this for any selected value, so a pre-set object value shows * its label on first paint — before the listbox has ever been opened, in * the documented `@if (forSelect.open())` pattern. Without it, object-value * labels resolve from the rendered option `textContent`, which is only * available once the content mounts; the serialized form value is shown as * a last-resort fallback in the meantime: `[itemToLabel]="(c) => c.name"`. * * Defaults to `undefined` (string mode renders the value verbatim, so no * label function is needed). */ readonly itemToLabel: _angular_core.InputSignal<((item: T) => string) | undefined>; /** * Read-only single-select convenience view of {@link value}. Returns the * sole value when exactly one is selected (regardless of `multiple`), * otherwise `null` (zero, or 2+ selected). Lets single-select consumers * read `selected()` instead of unwrapping `value()[0]`. The array-backed * `value` model remains the source of truth and the `FormValueControl` * contract; this is a derived accessor. */ readonly selected: _angular_core.Signal; /** * Two-way bindable. Whether the listbox is currently shown. The `model()` * change emitter (`(openChange)`) fires only on internal transitions * (trigger toggle, Escape, outside dismissal, single-mode option select), * never on consumer writes via `[(open)]`. */ readonly open: _angular_core.ModelSignal; /** * When true, multiple options can be selected and option activation toggles * without closing the listbox. Single mode (default) keeps the value array at * 0 or 1 element and closes on select. * * In the default (non-virtualized) path the full APG range keyboard * (Shift+Arrow, Shift+Space, Ctrl/Cmd+A, Ctrl+Shift+Home/End) extends the * selection, matching `ForListbox`. * * That range keyboard is not supported together with virtualization * (`totalCount` set): range selection needs the full set of enabled options * across the range, which is unavailable while the list is partially * unmounted. Pressing one of those combinations on a virtualized multi-select * listbox throws in dev mode. Toggle options individually with Enter, Space, * or click, or drop `totalCount` to use the non-virtualized DOM-focus listbox. */ readonly multiple: _angular_core.InputSignalWithTransform; /** * Presentation mode. When `true`, `[forSelectContent]` mounts as a trapped / * inert / scroll-locked modal surface (routed through `_internal/modal-shell`) * instead of the default anchored popover — the batteries-included touch * presentation a consumer opts into with `[modal]="isCoarsePointer()"`. The * form-value wiring (`[(value)]`, `name`) is unchanged. * * Read once when the content mounts (the two shells are structurally * different; switching at runtime would need a remount, and the surface * mounts lazily via `@if (open())` well after `modal` settles). Every * anchored-positioning input — `position`, `side`, `align`, `sideOffset`, * `alignOffset`, `sticky`, `hideWhenDetached`, `clipUntilPositioned`, `avoidCollisions`, * `collisionPadding` — is a no-op in this mode. Default * `false` (non-breaking). The swipe / snap-point sheet is not * this mode — compose a `ForListbox` inside a `ForDrawer` for that. */ readonly modal: _angular_core.InputSignalWithTransform; /** * Positioning algorithm. * * - `'popper'` (default): standard floating-ui anchored placement using * `side` / `align` / `sideOffset` / `alignOffset`, with `flip` + `shift` * collision handling. Same path as Popover / DropdownMenu. * - `'item-aligned'`: the listbox overlays the trigger so the selected * option's vertical center aligns with the trigger's vertical center * — visually the menu "snaps over" the trigger when opened, mirroring * macOS native `` leaves the tab order the same way. It is * reflected non-destructively — the directive only removes the attribute when * it set it itself — and `data-disabled=""` stays as the styling hook. The * remaining form-control state (`aria-readonly` / `aria-required` / * `aria-invalid` / `aria-busy`) is unaffected, and the read-only state carries * its own `data-readonly=""` styling hook — `readonly` is not a valid attribute * of ` * ``` * * Reflects the parent option's `data-state` (`"checked" | "unchecked"`) so * the consumer can also style it from CSS. Visibility while unselected is * enforced with an inline `display: none` (which beats any author `display` * rule a consumer applies via a class) in addition to the `hidden` attribute * that removes it from the a11y tree. */ declare class ForSelectIndicator { protected readonly option: ForSelectOption; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Optional grouping wrapper inside a `[forSelectContent]`. Renders * `role="group"` and references the descendant `[forSelectGroupLabel]` * (if any) via `aria-labelledby`. Options inside a group still register * with the root listbox normally, so keyboard navigation traverses across * groups in DOM order without special handling. */ declare class ForSelectGroup { #private; /** The id of the registered group label (or `null` if none). */ readonly labelId: _angular_core.Signal; protected readonly labelledBy: _angular_core.Signal; /** Called by `ForSelectGroupLabel` on mount. */ registerLabel(id: string): void; /** Called by `ForSelectGroupLabel` on destroy. Idempotent. */ unregisterLabel(id: string): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Visible label for a `[forSelectGroup]`. Generates a stable id and * registers with its parent group so the group's `aria-labelledby` points * at this element. Apply on whatever heading element fits the design * (often `
` styled as a small caption). */ declare class ForSelectGroupLabel { /** Stable host id used by the parent group's `aria-labelledby`. */ readonly id: string; constructor(); static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Visual + semantic separator between options or groups inside a * `[forSelectContent]`. Carries `role="separator"` and is intentionally not * registered with the listbox's option collection, so keyboard navigation * and typeahead skip it automatically. Set `decorative` when the surrounding * options already convey the split and the line should be skipped by * assistive tech. */ declare class ForSelectSeparator { /** * Axis the separator divides along, always reflected to `data-orientation` * and reflected to `aria-orientation` only for `vertical` (`horizontal` is * the ARIA default and is omitted). `horizontal` (default) splits options * stacked vertically — the common case in a listbox; `vertical` splits * options laid out horizontally. */ readonly orientation: _angular_core.InputSignal<"horizontal" | "vertical">; /** * When true, the separator is purely visual: it gets `role="none"` and no * `aria-orientation`, so assistive tech treats the surrounding options as a * single flow. `data-orientation` is still reflected for styling. */ readonly decorative: _angular_core.InputSignalWithTransform; protected roleAttr(): 'separator' | 'none'; protected ariaOrientationAttr(): 'vertical' | null; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Defaults inherited by descendant selects in the surrounding injector * scope. Configure with `provideForSelectDefaults` either at the * application root or in any component's `providers` array; partial * overrides merge with the parent scope. */ interface ForSelectDefaults extends AnchoredPositioningSeedDefaults { /** * Side the listbox is anchored to for selects that don't override `side` * locally. Ignored under `position="item-aligned"`. Library fallback * `'bottom'`. */ side: FloatingSide; /** * Alignment along the chosen `side` for selects that don't override `align` * locally. Ignored under `position="item-aligned"`. Library fallback * `'start'`. */ align: FloatingAlign; /** * Distance (px) between the select trigger and the floating content * along the resolved `side` axis. */ sideOffset: number; /** * Padding (px) added to the viewport edges for collision-aware * positioning. Higher values keep the floating content further from * the edge when `flip` / `shift` runs. */ collisionPadding: number; } /** Token holding the resolved select defaults for the current scope. */ declare const FOR_SELECT_DEFAULTS: _angular_core.InjectionToken; /** * Configures forty-cdk select defaults for this injector scope. Partial * overrides inherit unspecified keys from the parent scope (or library * defaults at the root). */ declare function provideForSelectDefaults(defaults?: Partial): Provider[]; /** * Exact public names of every `ForSelect` 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[mySelect]', * template: '', * hostDirectives: [ * { * directive: ForSelect, * inputs: [...FOR_SELECT_HOST_DIRECTIVE_INPUTS], * outputs: [...FOR_SELECT_HOST_DIRECTIVE_OUTPUTS], * }, * ], * }) * export class MySelect {} * ``` */ declare const FOR_SELECT_HOST_DIRECTIVE_INPUTS: readonly ["value", "open", "align", "alignOffset", "ariaLabel", "avoidCollisions", "clipUntilPositioned", "collisionPadding", "compareWith", "dataVersion", "dir", "dirty", "disabled", "dismissible", "errors", "hideWhenDetached", "invalid", "itemToFormValue", "itemToLabel", "loop", "modal", "multiple", "name", "orientation", "pending", "placeholder", "position", "readonly", "required", "returnFocus", "selectedIndex", "selectionFollowsFocus", "side", "sideOffset", "sticky", "totalCount", "touched", "visibleRange"]; /** * Exact public names of every `ForSelect` output, the Signal Forms `touch` output * included. Spread it into the `outputs` array of the same `hostDirectives` entry as * {@link FOR_SELECT_HOST_DIRECTIVE_INPUTS}. */ declare const FOR_SELECT_HOST_DIRECTIVE_OUTPUTS: readonly ["valueChange", "openChange", "escapeKeyDown", "pointerDownOutside", "focusOutside", "interactOutside", "autoFocusOnOpen", "autoFocusOnClose", "scrollToIndex", "touchedChange", "touch"]; export { FOR_SELECT_CONTEXT, FOR_SELECT_DEFAULTS, FOR_SELECT_HOST_DIRECTIVE_INPUTS, FOR_SELECT_HOST_DIRECTIVE_OUTPUTS, FOR_SELECT_OPTION, ForSelect, ForSelectAnchor, ForSelectContent, ForSelectGroup, ForSelectGroupLabel, ForSelectIndicator, ForSelectOption, ForSelectSeparator, ForSelectTrigger, ForSelectValue, provideForSelectDefaults }; export type { ForSelectCloseReason, ForSelectContext, ForSelectDefaults, ForSelectInitialFocus, ForSelectOverlayFacade };