import * as _angular_core from '@angular/core'; import { InjectionToken, Signal } from '@angular/core'; import { FormValueControl } from '@angular/forms/signals'; import { FormUiControlBase } from 'forty-cdk/core'; import * as forty_cdk_otp_input from 'forty-cdk/otp-input'; /** * The coordination surface a `[forOtpInput]` exposes to its `[forOtpInputSlot]` * children. Each slot reads its own state through these methods, passing its * `index`. The methods read the directive's signals internally, so calling them * inside a slot's `computed` tracks the underlying state reactively. */ interface ForOtpInputContext { /** The configured number of slots (the `length` input). */ readonly length: Signal; /** * The character displayed in slot `index` — masked when `mask` is on — or * `null` when the slot is empty. */ charAt(index: number): string | null; /** * Whether slot `index` is the active caret position (or inside the current * selection range). At most one slot is active for a collapsed caret. */ isActive(index: number): boolean; /** * Whether slot `index` should render a fake caret: it is the active, empty * slot while the input is focused with a collapsed caret. */ hasFakeCaret(index: number): boolean; } /** Injection token for the `[forOtpInput]` coordination surface the slots read. */ declare const FOR_OTP_INPUT_CONTEXT: InjectionToken; /** * Allowed-character class for an OTP / PIN input. Drives both the per-character * filter (rejected characters are dropped and fire `(reject)`) and the * derived `inputmode`. Override entirely with a custom `allowedPattern` RegExp. */ type OtpInputType = 'numeric' | 'alphanumeric' | 'alphabetic'; /** * Matches a value made up only of digits. Bind to `[allowedPattern]` for a * digit-only code; equivalent to the default `type="numeric"`. */ declare const OTP_REGEXP_ONLY_DIGITS: RegExp; /** Matches a value made up only of latin letters (`type="alphabetic"`). */ declare const OTP_REGEXP_ONLY_CHARS: RegExp; /** Matches a value made up only of digits and latin letters (`type="alphanumeric"`). */ declare const OTP_REGEXP_ONLY_DIGITS_AND_CHARS: RegExp; /** * Headless OTP / PIN input following the **single-input** model: one real * `` carries the whole code as a `string`, and the * `[forOtpInputSlot]` pieces are a pure * styling surface painted over it. There is **no** WAI-ARIA APG pattern for * OTP; this gives the cleanest screen-reader experience (one ordinary text * field, not "edit text, 1 of 6" announced N times), native mobile SMS autofill * via `autocomplete="one-time-code"`, and native paste / caret / selection. * * Apply `[forOtpInput]` on a wrapper element — it becomes a `role="group"` and * the directive injects the single visually-hidden-but-interactive `` * inside it (the consumer styles that input to overlay the slots). It * implements Angular's `FormValueControl` from `@angular/forms/signals`, * so it auto-wires with `[formField]` and auto-associates inside a `[forField]` * (label / description / error) with no extra markup. * * Because the focusable, submittable control is the injected `` and not * the `role="group"` host, `ForOtpInput` redirects `FormUiControlBase`'s * host-targeted helpers onto that input: it overrides `fieldLabelledElement()` * (so the field association lands on the input) and `fieldStateReflectionTarget()` * (so the form-state `data-*` reflect there), and reflects the OTP-specific input * attributes (`maxLength` / `inputmode` / `autocomplete` / `pattern` / `name`) * and ARIA on the input itself. * * The host gets `data-complete` (while every slot is filled) plus `data-disabled` * and `data-readonly` for CSS hooks, so the whole slot composition is stylable * from the wrapper; the real input carries `data-disabled` / `data-readonly` * plus `data-touched` / `data-dirty` / `data-pending` / `data-invalid`. * * > **`allowedPattern`, not `pattern`.** The custom allowed-character RegExp is * > named `allowedPattern` because `FormUiControl.pattern` is reserved by Signal * > Forms for an array of validation patterns the `[formField]` directive binds * > in — reusing the name would both break the `implements` contract and let the * > field overwrite the character filter. * * @example * ```html *
* @for (i of otp.slots(); track i) { *
* {{ s.char() }} * @if (s.hasFakeCaret()) { } *
* } *
* ``` */ declare class ForOtpInput extends FormUiControlBase implements FormValueControl, ForOtpInputContext { #private; /** * The current code. Required by `FormValueControl`. Two-way bindable; * its rendered length is clamped to `length()`. */ readonly value: _angular_core.ModelSignal; /** Number of characters / slots. */ readonly length: _angular_core.InputSignal; /** Allowed character class. Ignored when `allowedPattern` is set. Defaults to `'numeric'`. */ readonly type: _angular_core.InputSignal; /** * Custom allowed-character RegExp, tested per typed/pasted character; * overrides `type`. Named `allowedPattern` (not `pattern`) to avoid the * reserved `FormUiControl.pattern` member — see the class JSDoc. */ readonly allowedPattern: _angular_core.InputSignal; /** Obscure entered characters in the slots (PIN entry); `value()` stays raw. */ readonly mask: _angular_core.InputSignalWithTransform; /** Toggle `autocomplete="one-time-code"` for mobile SMS autofill. */ readonly oneTimeCode: _angular_core.InputSignalWithTransform; /** Rewrite pasted text before it fills the slots (e.g. strip separators). */ readonly pasteTransformer: _angular_core.InputSignal<((pasted: string) => string) | null>; /** * Accessible name for the group, also reflected as `aria-label` on the * injected real `` whenever no field-provided `aria-labelledby` * applies (standalone usage, or a `[forField]` without a label). Emits * `aria-label` only when truthy; a field label always wins on the input. */ readonly ariaLabel: _angular_core.InputSignal; protected readonly resolvedAriaLabel: _angular_core.Signal; /** Fires when every slot is filled (by typing or paste). */ readonly complete: _angular_core.OutputEmitterRef; /** Fires when an entered / pasted character is rejected by `type` / `allowedPattern`. */ readonly reject: _angular_core.OutputEmitterRef<{ value: string; }>; /** `true` when every slot is filled — reflected as `data-complete` on the host. */ readonly filled: _angular_core.Signal; /** The slot indices, for the consumer's `@for`. */ readonly slots: _angular_core.Signal; constructor(); /** * The injected real `` is the focusable, submittable control, so the * surrounding `[forField]` associates with it rather than the `role="group"` * host. Returns `null` until the input is created (after hydration), at which * point the field-wiring effect re-targets it. See the Select precedent. */ protected fieldLabelledElement(): HTMLElement | null; /** * The four form-state `data-*` booleans reflect onto the injected real * ``, alongside the OTP-specific `data-disabled` / `data-readonly`, * rather than the `role="group"` host. `null` until the input exists. */ protected fieldStateReflectionTarget(): HTMLElement | null; /** Move focus to the real input. Implements `FormUiControl.focus`. */ focus(options?: FocusOptions): void; /** The character in slot `index` (masked when `mask`), or `null` when empty. */ charAt(index: number): string | null; /** Whether slot `index` is the active caret position (or inside the selection). */ isActive(index: number): boolean; /** Whether slot `index` should render a fake caret. */ hasFakeCaret(index: number): boolean; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * One slot of a `[forOtpInput]` — a pure **styling surface** painted over the * single real input. Apply one per index * inside the `[forOtpInput]` wrapper, passing the 0-based `index`. It renders * nothing structural: it exposes the slot's character and active state, and * reflects boolean `data-active` / `data-empty` for CSS. * * The slot has no click handler by design — the real input overlays the slots * (the consumer styles it to fill the group), so pointer events land on the * input and native caret positioning drives which slot is active. * * @example * ```html *
* {{ s.char() }} * @if (s.hasFakeCaret()) { } *
* ``` */ declare class ForOtpInputSlot { protected readonly ctx: forty_cdk_otp_input.ForOtpInputContext; /** This slot's position (0-based). */ readonly index: _angular_core.InputSignal; /** The character in this slot, or `null` when empty (masked when `mask` is on). */ readonly char: _angular_core.Signal; /** Whether this slot is the active caret position. */ readonly active: _angular_core.Signal; /** Whether to render a fake caret in this slot. */ readonly hasFakeCaret: _angular_core.Signal; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } /** * Exact public names of every `ForOtpInput` 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[myOtpInput]', * template: '', * hostDirectives: [ * { * directive: ForOtpInput, * inputs: [...FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS], * outputs: [...FOR_OTP_INPUT_HOST_DIRECTIVE_OUTPUTS], * }, * ], * }) * export class MyOtpInput {} * ``` */ declare const FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS: readonly ["value", "allowedPattern", "ariaLabel", "dirty", "disabled", "errors", "invalid", "length", "mask", "name", "oneTimeCode", "pasteTransformer", "pending", "readonly", "required", "type", "touched"]; /** * Exact public names of every `ForOtpInput` output, the Signal Forms `touch` output * included. Spread it into the `outputs` array of the same `hostDirectives` entry as * {@link FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS}. */ declare const FOR_OTP_INPUT_HOST_DIRECTIVE_OUTPUTS: readonly ["valueChange", "touchedChange", "touch", "complete", "reject"]; export { FOR_OTP_INPUT_CONTEXT, FOR_OTP_INPUT_HOST_DIRECTIVE_INPUTS, FOR_OTP_INPUT_HOST_DIRECTIVE_OUTPUTS, ForOtpInput, ForOtpInputSlot, OTP_REGEXP_ONLY_CHARS, OTP_REGEXP_ONLY_DIGITS, OTP_REGEXP_ONLY_DIGITS_AND_CHARS }; export type { ForOtpInputContext, OtpInputType };