import { ComponentPropsWithRef, FocusEvent, KeyboardEvent, ReactNode } from 'react'; import { Nullable } from '@viasat/beam-shared/utils/types'; import { ThemeTypes } from '@viasat/beam-shared/utils/constants'; import { FormValidator } from '@viasat/beam-shared/components/form'; import { InputSize } from '@viasat/beam-shared/components/input'; type DateFieldInputAttributes = Omit, 'value' | 'defaultValue' | 'onChange' | 'min' | 'max' | 'type' | 'size' | 'ref' | 'form' | 'autoComplete' | 'inputMode'>; export type SegmentType = 'day' | 'month' | 'year'; /** * Argument to `adjustSegment` / `adjustSegmentValue`. * A numeric delta (e.g. ±1, ±5) or a boundary keyword for Home/End. */ export type SegmentAdjustment = number | 'min' | 'max'; export type FocusTarget = SegmentType | 'all' | null; /** ARIA role for a segment: `spinbutton` (default) or `textbox` (iOS VoiceOver fallback post-mount). */ export type SegmentRole = 'spinbutton' | 'textbox'; /** Per-segment values. Month is 1-12 (NOT 0-11). */ export interface SegmentValues { day: number | null; month: number | null; year: number | null; } /** Per-segment raw input strings — the single source of truth. '' = empty. */ export type SegmentText = Record; export interface AppendDigitResult { /** True when the segment completed and focus should auto-advance. */ advanced: boolean; /** True when the digit was refused (invalid char, out-of-range, or full). */ rejected: boolean; } /** Shape mirrors `Intl.DateTimeFormatPart`; `getDateParts` derives this directly from `formatToParts()`. */ export type DateFieldPart = { type: SegmentType | 'literal'; value: string; }; /** * The DateField `ref`: the field's root element augmented with `clear()`. It * stays a real DOM node so it works anywhere one is expected — a `Popover` * trigger, measuring, `scrollIntoView` — while `focus()` and `blur()` become * field-aware overrides that move to the first segment and exit the field. * All three no-op when disabled; `clear()` also no-ops when read-only. */ export type DateFieldHandle = HTMLDivElement & { clear(): void; }; export interface DateFieldProps extends DateFieldInputAttributes { /** * Specify HelperText for DateField */ helperText?: Nullable; /** * Specify if DateField is a required input * @default false */ required?: boolean; /** * Specify if the DateField displays with an asterisk * @default false */ hideRequiredMarker?: boolean; /** * Specify Label for DateField */ label?: Nullable; /** * Specify error text and display error state of a DateField */ error?: boolean | string; /** * Specify if DateField displays in a read-only state * @default false */ readOnly?: boolean; /** * Specify if DateField displays in a disabled state * @default false */ disabled?: boolean; /** * Specify if DateField is fluid * @default false */ fluid?: boolean; /** * Specify the size of DateField * @default 'md' */ size?: InputSize; /** * Specify the width of DateField */ width?: string; /** * Specify the theme of the DateField. By default it inherits the theme from the parent */ theme?: ThemeTypes; /** * Specify form validation rules for DateField */ validationRules?: Array; /** * Specify the controlled date value of DateField; pass `null` to clear */ value?: Nullable; /** * Specify the initial date value of an uncontrolled DateField */ defaultValue?: Nullable; /** * Specify the handler called when the date value changes */ onChange?: (value: Nullable) => void; /** * Specify the earliest date; a touched value springs up to it on blur * (add a `validationRules` entry to block submit) */ minDate?: Date; /** * Specify the latest date; a touched value springs down to it on blur * (add a `validationRules` entry to block submit) */ maxDate?: Date; /** * Specify a localized message, announced politely to screen readers when a * touched value springs to a bound; omit for no announcement */ getClampAnnouncement?: (clampedDate: Date) => string; /** * Specify the BCP-47 locale tag driving segment order and separators * (e.g. `en-GB` → DD/MM/YYYY, `de-DE` → DD.MM.YYYY) * @default 'en-US' */ locale?: string; } /** * Internal props for the segment subcomponent. Not part of the public API. * `displayValue` / `isPlaceholder` are pre-computed from the segment string so * the segment is pure render; `value` is the parsed number for `aria-valuenow`. */ export interface DateFieldSegmentProps { type: SegmentType; /** Numeric segment value — used for `aria-valuenow`. `null` when the segment has no committed value. */ value: Nullable; /** `aria-valuemin` — the intrinsic calendar floor, not the `minDate` prop. */ valueMin: number; /** * `aria-valuemax` — the intrinsic calendar ceiling, not the `maxDate` prop: * month-aware for day (28 for a non-leap February), 12 for month, 9999 for year. */ valueMax: number; /** Pre-computed text to render inside the segment (buffer, formatted value, or placeholder). */ displayValue: string; /** True when `displayValue` IS the placeholder (drives `data-placeholder` CSS hook). */ isPlaceholder: boolean; /** Pre-computed `aria-valuetext`; month = locale name, day/year = displayValue (leading-zero mirror). */ valueText: string; /** Drives `aria-invalid` on the segment when the field is in an error/invalid state. */ invalid: boolean; /** Role for the segment: 'spinbutton' (default) or 'textbox' (iOS VoiceOver fallback post-mount). */ role: SegmentRole; isActive: boolean; disabled: boolean; readOnly: boolean; /** * Drives `aria-required`, which lives on the segments rather than the field * root: it is role-specific, and `group` does not support it while * `spinbutton` and `textbox` do. */ required: boolean; /** Fires when the segment gains focus (Tab, click, or programmatic). */ onFocus: () => void; /** Fires when focus leaves the segment; `relatedTarget` drives field-exit detection. */ onBlur: (event: FocusEvent) => void; /** Fires per typed character; multi-character input (paste, IME commit) is ignored. */ onInput: (type: SegmentType, char: string) => void; /** Fires on segment keydown; the orchestrator routes it into `useDateFieldKeyboard`. */ onKeyDown: (type: SegmentType, event: KeyboardEvent) => void; segmentRef: (element: HTMLSpanElement | null) => void; } export {};