import type * as React from 'react'; import type { PrimitivePropsWithRef } from '../utils/primitive.cjs'; import type { SlotProps } from '../utils/slot.cjs'; import type { FormErrors } from './serverErrors.cjs'; /** A plain-object mirror of the native `ValidityState` for one field, captured by {@link Form} after a validation pass. */ export interface ValiditySnapshot { badInput: boolean; customError: boolean; patternMismatch: boolean; rangeOverflow: boolean; rangeUnderflow: boolean; stepMismatch: boolean; tooLong: boolean; tooShort: boolean; typeMismatch: boolean; valueMissing: boolean; valid: boolean; } /** @internal Validity state distributed from Form to field descendants. */ export interface FormContextValue { validities: Record; /** One field's server errors, already normalised and already cleared if the user has edited it since. */ serverErrors: (name: string) => string[]; } /** When a {@link Form} refreshes a control's validity outside the submit pass. */ export type FormValidationMode = 'submit' | 'blur' | 'change'; /** Whether a {@link Form} refuses an invalid submit or only annotates it. */ export type FormValidationBehavior = 'native' | 'aria'; /** What one failing field reports through {@link FormProps.onFieldInvalid}. */ export interface FormFieldInvalidDetail { /** The `name` the failing control submits under — the key its snapshot is stored against. */ name: string; /** A plain snapshot of the control's validity flags, so the consumer can branch on the reason. */ validity: ValiditySnapshot; } /** The imperative half of the Form contract, assigned onto the `
` node the forwarded ref already carries. */ export interface FormHandle { /** Runs every control's native `checkValidity()` and every descendant `FormFieldRoot`'s own validation model, resolving to whether the whole form passed. */ validate(): Promise; } /** What {@link Form}'s forwarded ref carries: the `` plus `validate()`. */ export type FormElementHandle = HTMLFormElement & FormHandle; /** Props for {@link FormRoot}: {@link FormProps} minus the derived-anatomy-only props. */ export type FormRootProps = Omit & Pick, 'asChild'>; /** Props for the {@link Form} recipe. */ export interface FormProps extends Omit, 'onSubmit' | 'onInvalid' | 'onReset' | 'asChild'> { /** * Heading for a derived `FormErrorSummary` ahead of the fields, focused the first time it appears. * @default undefined — no summary is derived, and nothing takes focus. */ errorSummary?: string | undefined; /** * Label for a derived `FormSubmit` button, rendered after the fields. * @default undefined — no submit button is derived. */ submitLabel?: string | undefined; /** * Errors the browser cannot derive, keyed by the `name` a {@link FormField} already carries, shown by that field's catch-all `FormMessage`. * @default undefined — no server errors. */ errors?: FormErrors | undefined; /** * Fires on submit only once every field passes native constraint validation * — or, under `validationBehavior="aria"`, whether or not it does. * @default undefined — no-op if omitted. */ onSubmit?: ((event: React.SubmitEvent) => void) | undefined; /** * Fires when the native `` is reset, after the Form clears its * per-field validity snapshots and touched/dirty/error state. * @default undefined — no-op if omitted. */ onReset?: ((event: Event) => void) | undefined; /** * Fires once per failing field during the submit pass, so a consumer can react before a blocked submit returns silently. * @default undefined — no-op if omitted. */ onFieldInvalid?: ((detail: FormFieldInvalidDetail) => void) | undefined; /** * When a control's validity snapshot refreshes, and therefore when its `FormMessage`s may start showing. * @default 'change' */ validationMode?: FormValidationMode | undefined; /** * Whether a failed validation refuses the submit or only annotates it. * @default 'native' */ validationBehavior?: FormValidationBehavior | undefined; /** * Spacing rung the skin packs the form by, mirrored to `data-density`. * @default undefined — the skin's own default spacing. */ density?: 'compact' | (string & {}) | undefined; } /** @internal Field identity distributed from {@link FormField} to its descendants via context. */ export interface FormFieldContextValue { name: string; id: string; messageId: string; /** Ids of the field's currently shown messages, in mount order. */ messageIds: readonly string[]; /** Returns the id owned by the message with this token: the bare `messageId` for the first, `${messageId}-n` after. */ claimMessageId: (token: string) => string; setMessageVisible: (id: string, visible: boolean) => void; } /** Props for {@link FormFieldRoot}: {@link FormFieldProps} minus the derived-anatomy-only props. */ export type FormFieldRootProps = Omit & Pick, 'asChild'>; /** Props for {@link FormField}, a per-field grouping of label, control, and message. */ export interface FormFieldProps extends Omit, 'asChild'> { /** Field name, matched against the `name` attribute of the native input rendered inside `FormControl`. */ name: string; /** * The control's label, rendered into a derived `FormLabel` whose `htmlFor` * points at the id this field injects onto the control. * @default undefined — no label is rendered, and the control is unnamed. */ label?: React.ReactNode | undefined; /** * The catch-all validation message, rendered into a derived `FormMessage` shown whenever the field is invalid at all. * @default undefined — the message element renders with no text of its own. */ message?: React.ReactNode | undefined; } /** Props for {@link FormLabel}, a label wired to its `FormField`'s control. */ export interface FormLabelProps extends PrimitivePropsWithRef<'label'> { } /** Props for {@link FormControl}, which injects field attributes onto its single child input. */ export interface FormControlProps extends SlotProps { } /** Props for {@link FormMessage}, a conditionally-rendered validation message. */ export interface FormMessageProps extends PrimitivePropsWithRef<'span'> { /** * Which `ValidityState` flag to show this message for; omit to match any invalid state. * @default undefined — matches any invalid state. */ match?: keyof ValiditySnapshot | undefined; /** * Always render regardless of native validity, the per-message escape hatch. * @default false */ forceMatch?: boolean | undefined; } /** Props for {@link FormSubmit}, a `type="submit"` button. */ export interface FormSubmitProps extends PrimitivePropsWithRef<'button'> { } /** Props for {@link FormErrorSummary}, the GOV.UK error summary for a form's `FormFieldRoot`s. */ export interface FormErrorSummaryProps extends Omit, 'autoFocus'> { /** * Focus the summary the first time it appears. * @default true */ autoFocus?: boolean | undefined; }