/** When a Field first runs its validator. See {@link FieldControllerProps.mode}. */ export type FieldMode = 'change' | 'blur' | 'submit'; /** What a validator may return: one message, several, or nothing when the value is acceptable. */ export type FieldValidationResult = string | string[] | null | undefined | void; /** A Field's validator, sync or async. */ export type FieldValidator = (value: unknown) => FieldValidationResult | Promise; /** The bubbling DOM event a Field fires whenever its snapshot changes; a Form's ErrorSummary listens for it. */ export declare const FIELD_VALIDITY_CHANGE_EVENT = "validity-change"; /** Marks a Field root, so the error summary can find failing fields without a registry. */ export declare const FIELD_ROOT_ATTRIBUTE = "data-skdx-field"; /** Marks a Field's name, used as the error-summary link text when nothing else supplies one. */ export declare const FIELD_NAME_ATTRIBUTE = "data-skdx-field-name"; /** The Field's current messages, written to the root on validity changes. */ export declare const FIELD_MESSAGE_ATTRIBUTE = "data-skdx-field-message"; /** Marks a Field's description element, so its id can be added to the control's `aria-describedby`. */ export declare const FIELD_DESCRIPTION_ATTRIBUTE = "data-skdx-field-description"; /** Marks a Field's error element, so its id can be added to `aria-describedby` and its text read by the summary. */ export declare const FIELD_ERROR_ATTRIBUTE = "data-skdx-field-error"; /** Marks a Field's character-count element, so it can be styled and found without a framework ref. */ export declare const FIELD_CHARACTER_COUNT_ATTRIBUTE = "data-skdx-field-character-count"; /** Marks a Field whose async validator is still running. */ export declare const FIELD_PENDING_ATTRIBUTE = "data-skdx-field-pending"; /** Marks the re-dispatched submit after async validators have settled. */ export declare const FIELD_RESUBMIT_ATTRIBUTE = "data-skdx-field-resubmit"; /** Marks a FormField root, allowing error summary to list both Field and FormField failures. */ export declare const FORM_FIELD_ATTRIBUTE = "data-skdx-form-field"; /** Marks a rendered FormMessage so the error summary shows the user's displayed message. */ export declare const FORM_MESSAGE_ATTRIBUTE = "data-skdx-form-message"; /** Events a Form ErrorSummary should re-scan on. */ export declare const ERROR_SUMMARY_RESCAN_EVENTS: readonly ["validity-change", "invalid", "change", "blur"]; /** Marks a Form's ErrorSummary so the engine knows whether to suppress the Field's own announcement. */ export declare const FIELD_ERROR_SUMMARY_ATTRIBUTE = "data-skdx-error-summary"; /** Delay in milliseconds before character count announces remaining count. */ export declare const CHARACTER_COUNT_ANNOUNCE_DELAY_MS = 1000; /** What a Field's character count is configured with. */ export interface FieldCharacterCountOptions { /** The soft limit, not the native `maxlength` which truncates silently. */ maxLength: number; /** * Percentage of `maxLength` before the count appears; `0` shows it immediately. * @default 0 */ threshold?: number | undefined; } /** Default count message; negative means over-limit. */ export declare function characterCountMessage(remaining: number): string; /** Whether a count has passed its threshold and should be rendered/announced. */ export declare function characterCountVisible(count: number, maxLength: number, threshold?: number): boolean; /** Joins a Field's messages into one string, separated by a period unless a message already ends in sentence punctuation. */ export declare function joinFieldMessages(messages: string[]): string; /** Fallback message when native constraint validation has no `validationMessage`. */ export declare const NATIVE_INVALID_FALLBACK_MESSAGE = "This field is invalid."; /** Marks an element as this Field's control, an escape hatch for composites in shadow DOM. */ export declare const FIELD_CONTROL_ATTRIBUTE = "data-skdx-field-control"; /** Selector to find a Field's control, matching first in document order. */ export declare const FIELD_CONTROL_SELECTOR: string; /** Marks a Field's required indicator for styling and assertion verification. */ export declare const FIELD_REQUIRED_INDICATOR_ATTRIBUTE = "data-skdx-field-required-indicator"; /** The public state of one Field, handed to the framework layer and to `validity-change` listeners. */ export interface FieldSnapshot { /** The id of the discovered control — the Label's `for`, and the error summary's link target. */ controlId: string; /** Every current message, server-injected first. Empty means valid. */ errors: string[]; /** `errors.length === 0`. */ valid: boolean; /** The control has been blurred at least once. */ touched: boolean; /** The value has been edited at least once. */ dirty: boolean; /** The control currently holds a value. */ filled: boolean; /** Length of the control's value, tracked only while a character count is configured. */ count: number; /** The discovered control's live `ValidityState`, or `null` when none; compared by identity to detect swaps. */ validity: ValidityState | null; } /** The props the framework layer forwards into the controller on every render. */ export interface FieldControllerProps { /** See {@link FieldValidator}. */ validate?: FieldValidator | undefined; /** * When validation first runs; re-validates on input after any failure. * @default 'blur' */ mode?: FieldMode | undefined; /** Server-injected errors, always shown, listed before anything `validate` produces. */ errors?: string | string[] | undefined; /** Value handed to `validate`; `undefined` reads it off the control instead. */ value?: unknown; /** Field name, used as the error-summary link text when no error element is rendered. */ name?: string | undefined; /** * Whether an answer is mandatory, reflected as native `required` or `aria-required="true"`. * @default undefined — not required. */ required?: boolean | undefined; /** * Whether the field is unavailable, applied as native `disabled` or `aria-disabled="true"`. * @default undefined — not disabled. */ disabled?: boolean | undefined; /** * Formats the character-count message; negative `remaining` means over-limit. * @default undefined — {@link characterCountMessage}'s English wording. */ getCharacterCountMessage?: ((remaining: number) => string) | undefined; /** * Fallback message when native constraint validation fails without a message. * @default undefined — 'This field is invalid.' */ invalidMessage?: string | undefined; /** * Delay in milliseconds before `change` mode validates; `0` validates every keystroke. * @default 0 — no debounce. */ validationDebounceTime?: number | undefined; } /** Everything the controller needs that the framework layer owns. */ export interface FieldControllerOptions { /** Id of the Label element, used for a group's `aria-labelledby`. */ labelId: string; /** Id of the Description element. */ descriptionId: string; /** Id of the Error element. */ errorId: string; /** Fallback id assigned to a control that has none of its own. */ controlId: string; /** True for fieldsets where Label is legend and control uses aria-labelledby. */ grouped: boolean; /** Called whenever the snapshot changes, so the framework layer can re-render. */ onSnapshotChange: (snapshot: FieldSnapshot) => void; /** Announces messages to the live region with politeness picked per message. */ announce: (message: string, politeness: 'polite' | 'assertive') => void; } /** Coerces whatever a validator (or the `errors` prop) returned into a message list. */ export declare function normalizeMessages(result: FieldValidationResult | string | string[]): string[]; /** The first control inside `root` that belongs to *this* Field rather than to a nested one. */ export declare function findFieldControl(root: Element): HTMLElement | null; /** Reads the current value from a control, checking checked state, value property, or hidden input. */ export declare function readControlValue(control: HTMLElement | null): unknown; /** Whether a value counts as entered, for `data-filled`. */ export declare function isFilled(value: unknown): boolean; /** One failing field, as listed by a Form's ErrorSummary. */ export interface FieldErrorEntry { /** The control's id — the link's `href` target. */ id: string; /** The message shown as the link text. */ message: string; } /** Returns the form or skdx-form a summary should scan, falling back to its parent. */ export declare function errorSummaryScope(root: Element): Element; /** Returns all currently-failing fields in scope, in document order. */ export declare function collectFieldErrors(scope: ParentNode): FieldErrorEntry[]; /** Runs all Fields in scope without short-circuiting, so all errors appear in ErrorSummary. */ export declare function validateFields(scope: ParentNode): Promise; /** Resets every Field's state when the form is reset, clearing messages and interaction flags. */ export declare function resetFields(scope: ParentNode): void; /** Sends focus to a failing field, or to the first control inside a group. */ export declare function focusFieldTarget(id: string, doc?: Document): void; /** Sends focus to the control an `invalid` event fired at — the visible control, when the failing one is a composite's hidden mirror input — and scrolls it into view. */ export declare function focusInvalidControl(control: HTMLElement): void; /** Owns one Field's state machine, connected to its root element and re-synced on every render. */ export declare class FieldController { private readonly options; private props; private root; private form; private control; /** The `aria-describedby` the consumer put on the control themselves, preserved across rewrites. */ private baseDescribedBy; /** The `aria-labelledby` the consumer put on the control themselves, preserved across rewrites. */ private baseLabelledBy; /** Whether this controller wrote aria-labelledby; only we may withdraw it. */ private wroteLabelledBy; /** Whether this controller wrote required/disabled; only we may withdraw them. */ private wroteRequired; private wroteDisabled; private wroteGroupDisabled; /** Bumped on every validation run; a resolution from a superseded run is discarded. */ private runToken; private pending; /** Messages from the `errors` prop, always listed first. */ private injected; /** Messages from the last `validate` run, or the native constraint-validation fallback. */ private custom; /** Set by a mounted character-count part; `null` while the Field has none. */ private characterCount; private announceTimer; /** Pending {@link FieldControllerProps.validationDebounceTime} run, cancelled by the next keystroke or a blur. */ private validationTimer; /** Whether a submit is waiting on this Field's async validator to settle. */ private awaitingSubmit; private snapshot; constructor(options: FieldControllerOptions); /** The current state. Read by the framework layer to render the parts. */ get state(): FieldSnapshot; /** Replaces the forwarded props; safe to call on every render. */ setProps(props: FieldControllerProps): void; /** Registers or clears the character count configuration. */ setCharacterCount(options: FieldCharacterCountOptions | null): void; /** Length of the control's current value; `0` when it holds something that is not text. */ private readCount; /** The count copy, in the consumer's wording when they supplied one. */ private countMessage; /** True once the value is longer than the configured soft limit. */ private isOverLimit; /** Composes errors: server errors, validation results, then over-limit message. */ private composeErrors; /** Announces the count politely one pause after the last keystroke, or assertively on over-limit. */ private scheduleCountAnnouncement; /** Attaches the controller to its rendered root element. */ connect(root: HTMLElement): void; /** Detaches every listener. */ disconnect(): void; /** Re-discovers the control and re-applies injected attributes. */ sync(): void; private applyDescribedBy; private applyLabelledBy; /** Sets required and disabled on control using native attributes or ARIA as appropriate. */ private applyConstraintState; /** A group container needs its radios disabled individually; only undo what this controller set. */ private applyGroupDisabled; /** Runs validation now, resolving to whether the field is valid. */ validate(): Promise; /** Resets the field to mount state and bumps the token to cancel any async validation run. */ reset(): void; /** Marks the field touched without validating (used by a submit pass before it validates). */ private markTouched; private applyStateAttributes; private resolveValue; /** The discovered control's live `ValidityState`, when it has one. */ private readValidity; /** Publishes (or withdraws) the "a validator is still running here" marker. */ private markPending; private nativeMessage; /** Runs validation, returning messages synchronously for sync validators. */ private runValidation; private commit; private update; private readonly handleInput; /** Runs validator with debounce in change mode, immediately in blur/submit mode. */ private scheduleValidation; /** Whether this Field's form has an ErrorSummary; queried per submit since it renders conditionally. */ private hasErrorSummary; private readonly handleFocusOut; /** Whether this submit is the re-dispatch from {@link resubmitIfSettled}, which must not re-run validators. */ private isEngineResubmit; private readonly handleSubmit; /** Re-dispatches submit once all async validators have settled, avoiding loops via FIELD_RESUBMIT_ATTRIBUTE. */ private resubmitIfSettled; }