import * as _angular_core from '@angular/core'; import { InjectionToken, Signal, Provider } from '@angular/core'; import { FormValueControl } from '@angular/forms/signals'; import { WritingDirection, FormUiControlBase } from 'forty-cdk/core'; import * as forty_cdk_slider from 'forty-cdk/slider'; /** * One thumb registered with `ForSlider`. The handle exposes the host element * (so the root can focus it) and the live `index` signal so reordering or * adding/removing thumbs works without re-registration. */ interface ForSliderThumbHandle { readonly host: HTMLElement; readonly index: Signal; } /** * The reachable value range for one thumb: the slider's `[min, max]` narrowed * by the adjacent thumbs and the `minStepsBetweenThumbs` gap, with both ends * rounded to the step's decimal precision. `max` is never below `min` — an * over-constrained configuration (a gap wider than the room the neighbors * leave, or `min > max`) collapses the range to a single point rather than * inverting it, so it is always safe to emit as `aria-valuemin` / * `aria-valuemax`. */ interface ForSliderThumbBounds { readonly min: number; readonly max: number; } /** * Coordination contract owned by `ForSlider`. Track and thumb pieces inject * this to read configuration, push value updates, and start drag flows. */ interface ForSliderContext { readonly minValue: Signal; readonly maxValue: Signal; readonly step: Signal; readonly stepMultiplier: Signal; readonly orientation: Signal<'horizontal' | 'vertical'>; readonly dir: Signal; /** * The slider's effective disabled — its own `disabled` input OR'd with a * surrounding disabled `[forFieldset]`. Thumb, track, and range pieces read * this so a disabled slider (or fieldset) is inert and exposes `aria-disabled`. */ readonly effectiveDisabled: Signal; readonly readonly: Signal; readonly minStepsBetweenThumbs: Signal; readonly inverted: Signal; readonly value: Signal; /** Per-thumb position as fraction `[0, 1]`, already inverted-aware. */ readonly fractions: Signal; /** * Per-thumb reachable range, index-aligned with `value()`. The single source * of truth for both the neighbor + `minStepsBetweenThumbs` clamp applied by * {@link ForSliderContext.setValueAt} and the `aria-valuemin` / * `aria-valuemax` each `[forSliderThumb]` reports. */ readonly thumbBounds: Signal; /** Range start fraction `[0, 1]` for `[forSliderRange]`. */ readonly rangeStart: Signal; /** Range end fraction `[0, 1]` for `[forSliderRange]`. */ readonly rangeEnd: Signal; /** * Set the value for a specific thumb. Snaps to step, clamps to `[min, max]`, * and respects `minStepsBetweenThumbs` against neighbors. No-op while * `disabled` or `readonly`. */ setValueAt(index: number, raw: number): void; /** * Bump a thumb by `step` (or by `step × stepMultiplier` when `large` is * true) in the requested direction, resolved against `orientation`, `dir`, * and `inverted`. */ bumpAt(index: number, key: SliderArrowKey, large: boolean): void; /** Set thumb to absolute `min` or `max` extreme (Home / End). */ setExtreme(index: number, which: 'min' | 'max'): void; /** * Map a pointer's client coordinates to a value in `[min, max]`, * respecting orientation, direction, and inversion. */ pointerToValue(clientX: number, clientY: number): number; /** Index of the thumb whose value is closest to `target`. */ nearestThumbIndex(target: number): number; setTrack(el: HTMLElement | null): void; trackElement(): HTMLElement | null; /** * Trailing-edge hook for value-changing interactions. Emits `(valueCommit)` * with the final value array if the running interaction has mutated the * value, then resets the internal flag. No-op otherwise. Pointer drags call * this on pointerup with no argument. Thumbs call it on keyup of a navigation * key, passing their own `thumbIndex` so only the thumb that armed the * pending commit can commit it (a keyup on a different thumb is a no-op). */ commitInteraction(thumbIndex?: number): void; registerThumb(handle: ForSliderThumbHandle): void; unregisterThumb(handle: ForSliderThumbHandle): void; } type SliderArrowKey = 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight'; declare const FOR_SLIDER_CONTEXT: InjectionToken; /** * Headless implementation of the [WAI-ARIA Slider pattern](https://www.w3.org/WAI/ARIA/apg/patterns/slider/) * (single thumb) and [Slider (Multi-Thumb)](https://www.w3.org/WAI/ARIA/apg/patterns/slider-multi-thumb/) * (range / N thumbs). Implements `FormValueControl` from * `@angular/forms/signals` for `[formField]` auto-wiring. * * Selection is always modeled as `readonly number[]`: * - 1 entry → single-thumb slider. * - 2 entries → range slider. * - N entries → multi-thumb slider. * * Values are kept clamped to `[min, max]` and snapped to `step` increments. * In multi-thumb mode they're constrained between neighbors so thumbs can't * cross (`minStepsBetweenThumbs` forces a minimum gap, in step units). * * The `model()` change emitter (`(valueChange)`) fires only on internal * updates (drag, keyboard, track click), never on consumer writes via * `[(value)]` — observe transitions without binding back. * * For trailing-edge work (network calls, undo entries) bind `(valueCommit)` * instead — it fires once at the end of an interaction with the final value * array, never per drag step. * * A read-only slider is reflected on this `role="group"` root as the boolean * `data-readonly` styling hook only. `aria-readonly` is not a supported * property of `role="group"`, so the ARIA announcement lives on each * `[forSliderThumb]` — `role="slider"` does support it. * * A required slider carries the boolean `data-required` hook on this same * root. `aria-required` is supported on neither `role="group"` nor * `role="slider"`, so it has no ARIA channel anywhere in the composition. */ declare class ForSlider extends FormUiControlBase implements Omit, 'min' | 'max'>, ForSliderContext { #private; /** * Two-way bindable. Selected values, one per thumb. Single-thumb sliders * keep one entry, range sliders two, multi-thumb N. Values must be sorted * ascending for multi-thumb; thumb-by-thumb clamping keeps the order * during interaction. Defaults to `[0]`; set an explicit `[(value)]` * when `min` is non-zero. */ readonly value: _angular_core.ModelSignal; /** * Minimum value. Falls back to `0`. A slider's bounds are scalar * by the ARIA slider pattern, but since Signal Forms v22 `FormUiControl.min` * is typed `NonNullable` — an array for this control — so `min` and * `max` are excluded from the `FormValueControl` `implements` clause, and * the transform widens the write type to satisfy the `[formField]` template * type-check (which pushes `readonly number[] | undefined`). The form never * produces a `min`/`max` state for an array-valued field at runtime (the * `min()`/`max()` validators only apply to numeric fields), so non-number * writes normalize to `undefined`. */ readonly min: _angular_core.InputSignalWithTransform; /** * Maximum value. Falls back to `100`. Excluded from the * `FormValueControl` `implements` clause and write-type-widened — see * {@link min}. */ readonly max: _angular_core.InputSignalWithTransform; /** * Increment values snap to. Values live on the `min` ± k·`step` grid: arrow * keys move to the next grid point in the direction of travel, so a value * that starts off the grid snaps onto it instead of taking an oversized first * jump. Fractional steps (e.g. `0.1`) are supported: the snapped value is * rounded to the step's decimal precision so float noise (`0.1 * 3`) can't * spuriously emit `valueCommit` or leak into `aria-valuenow`. */ readonly step: _angular_core.InputSignal; /** * Multiplier applied to `step` for `PageUp` / `PageDown`. Defaults to the * value from `provideForSliderDefaults` for the surrounding scope (10), so a * `step` of `1` pages by `10` and a `step` of `0.1` pages by `1`. It applies * only from a value already on the `min` ± k·`step` grid — from an off-grid * value the key lands on the adjacent grid point instead, matching the * platform `stepUp()` / `stepDown()` rule. */ readonly stepMultiplier: _angular_core.InputSignal; /** Effective minimum (defaults `0` when input is unset). Exposed to children via context. */ readonly minValue: _angular_core.Signal; /** Effective maximum (defaults `100`). Exposed to children via context. */ readonly maxValue: _angular_core.Signal; /** * Axis the thumb travels along. Reflected as `data-orientation`. */ readonly orientation: _angular_core.InputSignal<"horizontal" | "vertical">; /** * Writing direction. 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. * The resolved value is reflected to the host `dir` attribute and flips the * horizontal increase direction in RTL. */ readonly _dirInput: _angular_core.InputSignal; readonly dir: _angular_core.Signal; /** * Visual inversion. Flips the mapping between value and screen position * (e.g. horizontal LTR with `inverted=true`: max sits on the left). * Keyboard semantics still target "toward max" with ArrowUp / ArrowRight * (LTR), independent of visual flip. */ readonly inverted: _angular_core.InputSignalWithTransform; /** * Multi-thumb only: minimum gap between adjacent thumbs, in step units. * Default `0` (touch but never cross — non-passing per APG). */ readonly minStepsBetweenThumbs: _angular_core.InputSignal; /** * Emitted at the end of a value-changing interaction — pointerup / * pointercancel after a drag, or keyup after one or more keyboard * adjustments — with the final value array. Use it to defer expensive work * (network calls, * history entries) to the trailing edge of the interaction instead of * running it per step. Stays silent when nothing changed (e.g. press + * release without movement, or a non-navigation key on a focused thumb). */ readonly valueCommit: _angular_core.OutputEmitterRef; readonly fractions: _angular_core.Signal; /** * Per-thumb reachable range — `[min, max]` narrowed by the adjacent thumbs * and the `minStepsBetweenThumbs` gap, rounded to the step's decimal * precision. Drives both the clamp applied on every value write and the * `aria-valuemin` / `aria-valuemax` each thumb reports, so assistive tech * never announces a range the thumb cannot reach. An over-constrained * configuration collapses the range to a single point instead of inverting * it. */ readonly thumbBounds: _angular_core.Signal; /** * Range start fraction. Visual semantics: in single-thumb mode the range * always grows from the min edge to the thumb (`0 → fraction[0]`); in * multi-thumb mode it spans `min(values) → max(values)`. `inverted` is * already baked into the fractions, so consumers can paint blindly. */ readonly rangeStart: _angular_core.Signal; readonly rangeEnd: _angular_core.Signal; constructor(); setValueAt(index: number, raw: number): void; bumpAt(index: number, key: SliderArrowKey, large: boolean): void; setExtreme(index: number, which: 'min' | 'max'): void; pointerToValue(clientX: number, clientY: number): number; nearestThumbIndex(target: number): number; setTrack(el: HTMLElement | null): void; trackElement(): HTMLElement | null; /** * Emit `valueCommit` if the running interaction mutated the value at least * once, and clear the flag. Pointer drags call this on pointerup with no * argument (the drag's own pointerup is already scoped to one thumb). Thumbs * call it on keyup of a navigation key, passing their own `thumbIndex` so a * keyup on a thumb that did not arm the pending commit (e.g. a second thumb * focused and released without moving) cannot mis-attribute or steal another * thumb's pending commit — only the thumb that armed it commits. */ commitInteraction(thumbIndex?: number): void; /** * Move focus to a thumb, implementing `FormUiControl.focus` from * `@angular/forms/signals`. Without this override Signal Forms would focus * the host `role="group"` wrapper — which is not focusable and carries no * keyboard map — so focus-on-error would silently go nowhere. Targets the * first registered thumb (the roving-tabindex entry point); no-op when the * slider is disabled or has no thumbs. */ focus(options?: FocusOptions): void; registerThumb(handle: ForSliderThumbHandle): void; unregisterThumb(handle: ForSliderThumbHandle): void; protected onFocusOut(event: FocusEvent): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * The clickable track surface. Registers its element with `ForSlider` so the * root's pointer-drag session can map pointer coordinates to a value: a press * on bare track jumps the nearest thumb to the clicked position, focuses it, * and starts a drag, while a press on a thumb drags that specific thumb. * * Reflects `data-orientation` and `data-disabled` so consumers can paint * the track from CSS. Position the visible track however you want — this * directive just owns the bounding-rect for the math. */ declare class ForSliderTrack { #private; protected readonly ctx: forty_cdk_slider.ForSliderContext; constructor(); static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Optional decorative band between the slider's lowest and highest thumbs * (or `0 → thumb` for single-thumb sliders). The directive doesn't paint * anything — it exposes start / end fractions as CSS variables and `data-*` * so the consumer can size and position it from styles. * * Custom properties on the host: * - `--for-slider-range-start` — fraction `[0, 1]`. * - `--for-slider-range-end` — fraction `[0, 1]`. * - `--for-slider-range-size` — `end - start`, useful for `width` / `height`. * * The fractions already account for `inverted`, so painting can be blind. * Combine with `data-orientation` to pick the right CSS axis. */ declare class ForSliderRange { protected readonly ctx: forty_cdk_slider.ForSliderContext; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * One slider thumb. Apply on any focusable element (commonly `` / * `
` with the directive's auto-injected `tabindex`). The directive * sets `role="slider"`, the live `aria-value*` attributes, and keyboard * handling. Pointer drag is coordinated by the parent `[forSlider]`, whose * root-hosted pointer session drags this thumb when the press lands on it. * * `[index]` is the 0-based position in the parent slider's `value()` array * — pass the loop index when rendering N thumbs: * * ```html * @for (v of value(); let i = $index; track i) { * * } * ``` * * Custom property exposed: * - `--for-slider-thumb-position` — fraction `[0, 1]` already accounting * for `inverted`. Use it via e.g. * `inset-inline-start: calc(var(--for-slider-thumb-position) * 100%)`. */ declare class ForSliderThumb { #private; protected readonly ctx: forty_cdk_slider.ForSliderContext; /** Position in the parent slider's `value()` array (0-based). */ readonly index: _angular_core.InputSignalWithTransform; /** * Optional accessible name for this thumb (e.g. "Minimum price", * "Maximum price"), emitted as `aria-label`. Defaults to `null`, so no * attribute is emitted when unset. A consumer-set **static** `aria-label` * on the host wins over this input. When the name already exists as a * visible element in the DOM, write a native `aria-labelledby` on the host * instead — the directive never touches that attribute. */ readonly ariaLabel: _angular_core.InputSignal; protected readonly resolvedAriaLabel: _angular_core.Signal; /** * Optional human-readable value override (e.g. "$1,200" instead of "1200"). * Mirrored as `aria-valuetext` only when non-empty; when omitted, no * `aria-valuetext` is emitted so assistive tech reads `aria-valuenow`. */ readonly valueText: _angular_core.InputSignal; protected readonly tabindex: _angular_core.Signal<0 | -1>; /** * `touch-action` for the thumb: capture the slider's own axis so a finger * drag along it can't be stolen by page scrolling (which would fire * `pointercancel` mid-drag and can commit a mid-drag value), while freeing * the perpendicular axis for scrolling. A horizontal slider drags along x * (`pan-y`); a vertical slider drags along y (`pan-x`). Suppressed while the * slider is disabled or readonly (no drag to protect). */ protected readonly touchAction: _angular_core.Signal; protected readonly currentValue: _angular_core.Signal; protected readonly fraction: _angular_core.Signal; protected readonly ariaValueNow: _angular_core.Signal; protected readonly ariaValueText: _angular_core.Signal; protected readonly ariaValueMin: _angular_core.Signal; protected readonly ariaValueMax: _angular_core.Signal; constructor(); protected onKeyDown(event: KeyboardEvent): void; protected onKeyUp(event: KeyboardEvent): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Defaults inherited by descendant sliders in the surrounding injector * scope. Configure with `provideForSliderDefaults` either at the * application root or in any component's `providers` array; partial * overrides merge with the parent scope. */ interface ForSliderDefaults { /** * Multiplier applied to `step` for `PageUp` / `PageDown`. Defaults to `10`, * so a step of `1` pages by `10`. */ stepMultiplier: number; } /** Token holding the resolved slider defaults for the current scope. */ declare const FOR_SLIDER_DEFAULTS: _angular_core.InjectionToken; /** * Configures forty-cdk slider defaults for this injector scope. Partial * overrides inherit unspecified keys from the parent scope (or library * defaults at the root). */ declare function provideForSliderDefaults(defaults?: Partial): Provider[]; /** * Exact public names of every `ForSlider` 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[mySlider]', * template: '', * hostDirectives: [ * { * directive: ForSlider, * inputs: [...FOR_SLIDER_HOST_DIRECTIVE_INPUTS], * outputs: [...FOR_SLIDER_HOST_DIRECTIVE_OUTPUTS], * }, * ], * }) * export class MySlider {} * ``` */ declare const FOR_SLIDER_HOST_DIRECTIVE_INPUTS: readonly ["value", "dir", "dirty", "disabled", "errors", "invalid", "inverted", "max", "min", "minStepsBetweenThumbs", "name", "orientation", "pending", "readonly", "required", "step", "stepMultiplier", "touched"]; /** * Exact public names of every `ForSlider` output, the Signal Forms `touch` output * included. Spread it into the `outputs` array of the same `hostDirectives` entry as * {@link FOR_SLIDER_HOST_DIRECTIVE_INPUTS}. */ declare const FOR_SLIDER_HOST_DIRECTIVE_OUTPUTS: readonly ["valueChange", "valueCommit", "touchedChange", "touch"]; export { FOR_SLIDER_CONTEXT, FOR_SLIDER_DEFAULTS, FOR_SLIDER_HOST_DIRECTIVE_INPUTS, FOR_SLIDER_HOST_DIRECTIVE_OUTPUTS, ForSlider, ForSliderRange, ForSliderThumb, ForSliderTrack, provideForSliderDefaults }; export type { ForSliderContext, ForSliderDefaults, ForSliderThumbBounds, ForSliderThumbHandle, SliderArrowKey };