@trackunit/react-form-components 1.25.2 → 1.25.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-form-components",
3
- "version": "1.25.2",
3
+ "version": "1.25.3",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -15,11 +15,11 @@
15
15
  "zod": "^3.25.76",
16
16
  "tailwind-merge": "^2.0.0",
17
17
  "@trackunit/css-class-variance-utilities": "1.13.1",
18
- "@trackunit/react-components": "1.24.1",
18
+ "@trackunit/react-components": "1.24.2",
19
19
  "@trackunit/ui-icons": "1.13.1",
20
20
  "@trackunit/shared-utils": "1.15.1",
21
21
  "@trackunit/ui-design-tokens": "1.13.1",
22
- "@trackunit/i18n-library-translation": "1.21.1",
22
+ "@trackunit/i18n-library-translation": "1.21.2",
23
23
  "string-ts": "^2.0.0",
24
24
  "es-toolkit": "^1.39.10"
25
25
  },
@@ -19,7 +19,7 @@ export declare const cvaInput: (props?: ({
19
19
  *! Relies on the specific grid layout of cvaInput ☝️
20
20
  */
21
21
  export declare const cvaInputItemPlacementManager: (props?: ({
22
- position?: "span" | "before" | "after" | null | undefined;
22
+ position?: "before" | "after" | "span" | null | undefined;
23
23
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
24
24
  export declare const cvaAccessoriesContainer: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
25
25
  /**
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export declare const cvaCheckbox: (props?: ({
6
6
  invalid?: boolean | null | undefined;
7
- state?: "indeterminate" | "selected" | "deselected" | null | undefined;
7
+ state?: "selected" | "deselected" | "indeterminate" | null | undefined;
8
8
  disabled?: boolean | null | undefined;
9
9
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
10
10
  /**
@@ -1,5 +1,7 @@
1
- import { type ReactElement } from "react";
1
+ import { type FocusEvent, type ReactElement } from "react";
2
2
  import { BaseInputProps } from "../../BaseInput/BaseInput";
3
+ import { type DateFieldPickerCloseReason } from "./utils/useDatePickerController";
4
+ export type { DateFieldPickerCloseReason } from "./utils/useDatePickerController";
3
5
  type BaseInputExposedProps = Omit<BaseInputProps, "defaultValue" | "min" | "max" | "value">;
4
6
  export interface DateBaseInputProps extends BaseInputExposedProps {
5
7
  /**
@@ -18,6 +20,42 @@ export interface DateBaseInputProps extends BaseInputExposedProps {
18
20
  * A value with type Date added
19
21
  */
20
22
  value?: Date | string | number;
23
+ /**
24
+ * Optional override for the BCP-47 locale used for placeholder, display formatting, and
25
+ * locale-aware parsing of typed input.
26
+ *
27
+ * When omitted (recommended for production), the locale resolves via `useDateFieldLocale`,
28
+ * which prefers the browser's region-qualified `navigator.language` and falls back to the
29
+ * user-selected Manager UI language. Pass an explicit value (e.g. `"en-GB"`, `"da-DK"`,
30
+ * `"ja-JP"`) only for deliberate overrides — for example, embedded reports that require a
31
+ * fixed format, or Storybook/locale showcase stories.
32
+ */
33
+ locale?: string;
34
+ /**
35
+ * Called when the calendar popover closes, regardless of how it was dismissed.
36
+ *
37
+ * Receives a synthetic `FocusEvent` whose `target.value` is the canonical `YYYY-MM-DD` value
38
+ * currently committed to the input (or `""` when empty). The `reason` argument distinguishes
39
+ * Apply / Clear / Cancel from the new "outside" auto-apply, so consumers can opt out of
40
+ * server-side validation when the user explicitly cancelled.
41
+ *
42
+ * Use this as a middle ground between `onChange` (fires per keystroke / per pick) and
43
+ * `onBlur` (fires when the input itself loses focus, which may be much later or never if the
44
+ * user only interacts with the popover).
45
+ */
46
+ onPickerClose?: (event: FocusEvent<HTMLInputElement>, reason: DateFieldPickerCloseReason) => void;
47
+ /**
48
+ * When `true`, the calendar popover opens automatically the moment the input gains focus
49
+ * (for example, by tabbing into the field). The popover trigger (calendar icon, Space, or
50
+ * Enter on the icon) continues to work as before.
51
+ *
52
+ * Focus events that originate from the popover closing — Floating UI's `returnFocus`
53
+ * placing focus back on the input after Cancel/Apply/Escape/outside-press — are ignored,
54
+ * so the picker does not loop open immediately after closing.
55
+ *
56
+ * @default false
57
+ */
58
+ openOnFocus?: boolean;
21
59
  }
22
60
  /**
23
61
  * A wrapper around BaseInput with a pop-up day picker using the same calendar UI as DayPicker.
@@ -26,5 +64,4 @@ export interface DateBaseInputProps extends BaseInputExposedProps {
26
64
  *
27
65
  * NOTE: If shown with a label, please use the `DateField` component instead.
28
66
  */
29
- export declare const DateBaseInput: ({ min, max, defaultValue, value, ref, onChange, suffix: suffixProp, "data-testid": dataTestId, ...rest }: DateBaseInputProps) => ReactElement;
30
- export {};
67
+ export declare const DateBaseInput: ({ min, max, defaultValue, value, ref, onChange, onBlur, onPickerClose, openOnFocus, locale: localeProp, suffix: suffixProp, "data-testid": dataTestId, ...rest }: DateBaseInputProps) => ReactElement;
@@ -0,0 +1,34 @@
1
+ import { type ReactElement } from "react";
2
+ type CalendarView = "month" | "year" | "century" | "decade";
3
+ export interface DateBaseInputPickerContentProps {
4
+ /** Currently staged date inside the open popover (not yet committed to the input). */
5
+ pendingDate: Date | null;
6
+ /** Currently committed date held by the input. Used as a fallback for the calendar's active month. */
7
+ selectedDate: Date | undefined;
8
+ /** Disables individual day tiles based on the current `[min, max]` range. */
9
+ tileDisabled: (info: {
10
+ date: Date;
11
+ view: CalendarView;
12
+ }) => boolean;
13
+ /** Closes the surrounding popover. Provided by `<PopoverContent>` render prop. */
14
+ closePopover: () => void;
15
+ /** Fires when the user picks a new day in the calendar. */
16
+ onCalendarChange: (next: Date | null) => void;
17
+ /** Fires when the user clicks the Clear action. */
18
+ onClear: () => void;
19
+ /** Fires when the user clicks the Cancel action. */
20
+ onCancel: () => void;
21
+ /** Fires when the user clicks the Apply action. */
22
+ onApply: () => void;
23
+ /** Stages and commits the given date in one step (used for Enter on a focused day tile). */
24
+ applyDate: (next: Date | null) => void;
25
+ "data-testid"?: string;
26
+ }
27
+ /**
28
+ * Renders the calendar + Clear/Cancel/Apply buttons inside the DateBaseInput popover.
29
+ *
30
+ * Action callbacks are responsible for parent state changes only — this component handles
31
+ * closing the popover after each action completes.
32
+ */
33
+ export declare const DateBaseInputPickerContent: ({ pendingDate, selectedDate, tileDisabled, closePopover, onCalendarChange, onClear, onCancel, onApply, applyDate, "data-testid": dataTestId, }: DateBaseInputPickerContentProps) => ReactElement;
34
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Parses a heterogeneous date input (Date, ISO string, locale-formatted string, or epoch number)
3
+ * into a `Date`. Returns `undefined` when the input is empty or unparseable.
4
+ */
5
+ export declare function parseToDate(v: Date | string | number | undefined, locale: string): Date | undefined;
6
+ /** Formats a `Date` as a canonical `YYYY-MM-DD` ISO date string, or `""` when undefined. */
7
+ export declare function formatToInputString(d: Date | undefined): string;
8
+ /** Returns the epoch millis at the start of `d`'s local calendar day. */
9
+ export declare function startOfDayMs(d: Date): number;
10
+ /** Clamp a date to `[minDate, maxDate]`; returns the same date if in range or no bounds. */
11
+ export declare function clampToRange(date: Date | null | undefined, minDate: Date | undefined, maxDate: Date | undefined): Date | null;
@@ -0,0 +1,39 @@
1
+ import { type ChangeEvent, type FocusEvent, type Ref, type RefObject } from "react";
2
+ export interface UseCanonicalInputEmitterInput {
3
+ /** External ref forwarded by the consumer. Forwarded to the underlying `<input>`. */
4
+ ref: Ref<HTMLInputElement> | undefined;
5
+ /** Consumer's `onChange`. Invoked with synthetic events whose `target.value` is canonical. */
6
+ onChange: ((event: ChangeEvent<HTMLInputElement>) => void) | undefined;
7
+ /** Consumer's `onBlur`. Invoked with synthetic events whose `target.value` is canonical. */
8
+ onBlur: ((event: FocusEvent<HTMLInputElement>) => void) | undefined;
9
+ /** The locale-formatted display string the underlying `<input>` should always show. */
10
+ resolvedValue: string;
11
+ }
12
+ export interface CanonicalInputEmitter {
13
+ /** Internal ref to the underlying `<input>` element. */
14
+ inputRef: RefObject<HTMLInputElement | null>;
15
+ /** Ref-callback to attach to the underlying `<input>`; also forwards to the external `ref`. */
16
+ setInputRef: (node: HTMLInputElement | null) => void;
17
+ /** Builds (without emitting) a synthetic change event with a given value. */
18
+ createInputChangeEvent: (value: string, sourceInput: HTMLInputElement | null) => ChangeEvent<HTMLInputElement>;
19
+ /** Builds (without emitting) a synthetic blur event with a given value. */
20
+ createInputBlurEvent: (value: string, sourceInput: HTMLInputElement | null) => FocusEvent<HTMLInputElement>;
21
+ /** Mutates the input DOM value to `canonicalValue` and emits a synthetic change event. */
22
+ emitCanonicalValueChange: (canonicalValue: string) => void;
23
+ /**
24
+ * Mutates the input DOM value to `canonicalValue`, emits a synthetic blur event, then
25
+ * resyncs the DOM value back to `resolvedValue` (the locale-formatted display value).
26
+ */
27
+ emitCanonicalValueBlur: (canonicalValue: string) => void;
28
+ }
29
+ /**
30
+ * Owns the underlying `<input>` ref, ref forwarding, the DOM-value sync layout effect, and the
31
+ * synthetic change/blur emitters used to surface the canonical (ISO) value to consumers while
32
+ * the displayed input value remains in the locale-specific format.
33
+ *
34
+ * The dual format is the reason this hook exists at all: the rendered `value` is locale-formatted
35
+ * (e.g. `15/03/2025`) but `e.target.value` on `onChange`/`onBlur` should be canonical ISO
36
+ * (`2025-03-15`). The synthetic events carry the canonical value, and the layout effect ensures
37
+ * the DOM value is reconciled back to the display string on the next paint.
38
+ */
39
+ export declare const useCanonicalInputEmitter: ({ ref, onChange, onBlur, resolvedValue, }: UseCanonicalInputEmitterInput) => CanonicalInputEmitter;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Reason the calendar popover closed. Surfaced via `notifyPickerClose` so consumers (e.g.
3
+ * server-side validation) can decide whether to act on the close event:
4
+ * - `"apply"`: user clicked the Apply button.
5
+ * - `"clear"`: user clicked the Clear button.
6
+ * - `"cancel"`: user clicked the Cancel button (selection discarded).
7
+ * - `"outside"`: user clicked/tapped outside the popover or pressed Escape — selection auto-applied.
8
+ */
9
+ export type DateFieldPickerCloseReason = "apply" | "clear" | "cancel" | "outside";
10
+ type CalendarView = "month" | "year" | "century" | "decade";
11
+ export interface UseDatePickerControllerInput {
12
+ /** Currently committed date held by the input (the source of truth shown in the field). */
13
+ selectedDate: Date | undefined;
14
+ /** Lower bound for the calendar (already parsed). */
15
+ minDate: Date | undefined;
16
+ /** Upper bound for the calendar (already parsed). */
17
+ maxDate: Date | undefined;
18
+ /** Called when the user commits a new canonical (ISO `YYYY-MM-DD` or empty) value. */
19
+ commitValue: (canonicalValue: string) => void;
20
+ /** Called whenever the popover closes, with the canonical value at close time and the reason. */
21
+ notifyPickerClose: (canonicalValue: string, reason: DateFieldPickerCloseReason) => void;
22
+ }
23
+ export interface DatePickerController {
24
+ /** Currently staged date inside the open popover (not yet committed to the input). */
25
+ pendingDate: Date | null;
26
+ /** Disables individual day tiles based on the current `[min, max]` range. */
27
+ tileDisabled: (info: {
28
+ date: Date;
29
+ view: CalendarView;
30
+ }) => boolean;
31
+ /** Stages a new date as the user navigates the calendar. */
32
+ onCalendarChange: (next: Date | null) => void;
33
+ /** Clears the field and notifies picker-close with reason `"clear"`. */
34
+ onClear: () => void;
35
+ /** Discards the staged date and notifies picker-close with reason `"cancel"`. */
36
+ onCancel: () => void;
37
+ /** Commits the staged date (clamped to bounds) and notifies picker-close with reason `"apply"`. */
38
+ onApply: () => void;
39
+ /**
40
+ * Stages and commits the given date in one step (clamped to bounds), and notifies
41
+ * picker-close with reason `"apply"`. Used for keyboard-driven selection (Enter on a
42
+ * day tile) where the staged value must be applied without an extra Apply click.
43
+ */
44
+ applyDate: (next: Date | null) => void;
45
+ /**
46
+ * Reacts to the popover open/close lifecycle. On open: stages the currently selected date.
47
+ * On close: auto-applies the staged date (only commits if it differs from current) and
48
+ * notifies picker-close with reason `"outside"`.
49
+ */
50
+ onPopoverOpenStateChange: (open: boolean) => void;
51
+ }
52
+ /**
53
+ * Picker state machine for `DateBaseInput`. Owns the staged date, the popover's
54
+ * "was-open" tracking, the tile-disabled predicate, and all four user-driven action handlers
55
+ * plus the auto-apply behaviour for outside dismissals.
56
+ */
57
+ export declare const useDatePickerController: ({ selectedDate, minDate, maxDate, commitValue, notifyPickerClose, }: UseDatePickerControllerInput) => DatePickerController;
58
+ export {};
@@ -55,6 +55,29 @@ export interface DateFieldProps extends DateBaseInputProps, FormGroupExposedProp
55
55
  * );
56
56
  * };
57
57
  * ```
58
+ * @example Server-side validation when the calendar popover closes
59
+ * ```tsx
60
+ * import { DateField } from "@trackunit/react-form-components";
61
+ *
62
+ * const ValidatedField = () => (
63
+ * <DateField
64
+ * label="Start date"
65
+ * onChange={(e) => setLocalValue(e.target.value)}
66
+ * onPickerClose={(e, reason) => {
67
+ * if (reason === "cancel") return;
68
+ * validateOnServer(e.target.value);
69
+ * }}
70
+ * />
71
+ * );
72
+ * ```
73
+ * @example Open the picker automatically when the field is focused (e.g. via Tab)
74
+ * ```tsx
75
+ * import { DateField } from "@trackunit/react-form-components";
76
+ *
77
+ * const QuickPickField = () => (
78
+ * <DateField label="Start date" openOnFocus />
79
+ * );
80
+ * ```
58
81
  */
59
82
  export declare const DateField: {
60
83
  ({ label, id, tip, helpText, errorMessage, helpAddon, isInvalid, className, defaultValue, "data-testid": dataTestId, ref, required, ...rest }: DateFieldProps): ReactElement;
package/src/index.d.ts CHANGED
@@ -54,7 +54,7 @@ export * from "./components/UploadField/UploadField";
54
54
  export * from "./components/UploadInput/UploadInput";
55
55
  export * from "./components/UrlField/UrlField";
56
56
  export * from "./utilities/emailUtils";
57
- export * from "./utilities/parseDateFieldValue";
57
+ export { dateToISODateUTC, parseDateFieldValue, toISODateStringUTC } from "./utilities/parseDateFieldValue";
58
58
  export * from "./utilities/useCreateInputChangeEvent";
59
59
  export * from "./utilities/useGetPhoneValidationRules";
60
60
  export * from "./utilities/usePhoneInput";
@@ -14,8 +14,8 @@ export declare const translations: TranslationResource<TranslationKeys>;
14
14
  /**
15
15
  * Local useTranslation for this specific library
16
16
  */
17
- export declare const useTranslation: () => [TransForLibs<"baseInput.copyAction.toolTip" | "clearIndicator.icon.tooltip.clearAll" | "colorField.error.INVALID_HEX_CODE" | "colorField.error.REQUIRED" | "colorField.tooltip" | "dateField.actions.apply" | "dateField.actions.cancel" | "dateField.actions.clear" | "dateField.placeholder" | "dropzone.input.title" | "dropzone.label.default" | "emailField.error.INVALID_EMAIL" | "emailField.error.REQUIRED" | "field.notEditable.tooltip" | "field.required.asterisk.tooltip" | "numberField.error.GREATER_THAN" | "numberField.error.INVALID_NUMBER" | "numberField.error.LESS_THAN" | "numberField.error.NOT_IN_BETWEEN" | "numberField.error.REQUIRED" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined" | "schedule.label.active" | "schedule.label.allDay" | "schedule.label.day" | "search.placeholder" | "select.loadingMessage" | "select.noOptionsMessage" | "urlField.error.INVALID_URL" | "urlField.error.REQUIRED">, import("i18next").i18n, boolean] & {
18
- t: TransForLibs<"baseInput.copyAction.toolTip" | "clearIndicator.icon.tooltip.clearAll" | "colorField.error.INVALID_HEX_CODE" | "colorField.error.REQUIRED" | "colorField.tooltip" | "dateField.actions.apply" | "dateField.actions.cancel" | "dateField.actions.clear" | "dateField.placeholder" | "dropzone.input.title" | "dropzone.label.default" | "emailField.error.INVALID_EMAIL" | "emailField.error.REQUIRED" | "field.notEditable.tooltip" | "field.required.asterisk.tooltip" | "numberField.error.GREATER_THAN" | "numberField.error.INVALID_NUMBER" | "numberField.error.LESS_THAN" | "numberField.error.NOT_IN_BETWEEN" | "numberField.error.REQUIRED" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined" | "schedule.label.active" | "schedule.label.allDay" | "schedule.label.day" | "search.placeholder" | "select.loadingMessage" | "select.noOptionsMessage" | "urlField.error.INVALID_URL" | "urlField.error.REQUIRED">;
17
+ export declare const useTranslation: () => [TransForLibs<"baseInput.copyAction.toolTip" | "clearIndicator.icon.tooltip.clearAll" | "colorField.error.INVALID_HEX_CODE" | "colorField.error.REQUIRED" | "colorField.tooltip" | "dateField.actions.apply" | "dateField.actions.cancel" | "dateField.actions.clear" | "dateField.openPicker.ariaLabel" | "dropzone.input.title" | "dropzone.label.default" | "emailField.error.INVALID_EMAIL" | "emailField.error.REQUIRED" | "field.notEditable.tooltip" | "field.required.asterisk.tooltip" | "numberField.error.GREATER_THAN" | "numberField.error.INVALID_NUMBER" | "numberField.error.LESS_THAN" | "numberField.error.NOT_IN_BETWEEN" | "numberField.error.REQUIRED" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined" | "schedule.label.active" | "schedule.label.allDay" | "schedule.label.day" | "search.placeholder" | "select.loadingMessage" | "select.noOptionsMessage" | "urlField.error.INVALID_URL" | "urlField.error.REQUIRED">, import("i18next").i18n, boolean] & {
18
+ t: TransForLibs<"baseInput.copyAction.toolTip" | "clearIndicator.icon.tooltip.clearAll" | "colorField.error.INVALID_HEX_CODE" | "colorField.error.REQUIRED" | "colorField.tooltip" | "dateField.actions.apply" | "dateField.actions.cancel" | "dateField.actions.clear" | "dateField.openPicker.ariaLabel" | "dropzone.input.title" | "dropzone.label.default" | "emailField.error.INVALID_EMAIL" | "emailField.error.REQUIRED" | "field.notEditable.tooltip" | "field.required.asterisk.tooltip" | "numberField.error.GREATER_THAN" | "numberField.error.INVALID_NUMBER" | "numberField.error.LESS_THAN" | "numberField.error.NOT_IN_BETWEEN" | "numberField.error.REQUIRED" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined" | "schedule.label.active" | "schedule.label.allDay" | "schedule.label.day" | "search.placeholder" | "select.loadingMessage" | "select.noOptionsMessage" | "urlField.error.INVALID_URL" | "urlField.error.REQUIRED">;
19
19
  i18n: import("i18next").i18n;
20
20
  ready: boolean;
21
21
  };
@@ -7,6 +7,35 @@
7
7
  * @returns {Date | null} - Date at local midnight for the given day, or null if value is empty or invalid
8
8
  */
9
9
  export declare function parseDateFieldValue(value: string): Date | null;
10
+ /**
11
+ * Normalises a locale tag for use with `Intl.DateTimeFormat`.
12
+ *
13
+ * Filters out tags that are not real Intl locales (the empty string and i18next's `cimode`
14
+ * key-debug pseudo-language, surfaced as "Developer" in `LanguageSelection`) and replaces them
15
+ * with the browser locale. Region-qualified and bare-language Intl tags are returned unchanged
16
+ * so that the caller (`useDateFieldLocale`, callers passing a manual `locale` prop, etc.) keeps
17
+ * full control of the policy decision.
18
+ *
19
+ * Note: prior versions of this function additionally rewrote bare `"en"` to the browser locale
20
+ * so that UK browsers would not see US-style dates when the Manager UI language was English.
21
+ * That policy now lives in `useDateFieldLocale` (browser-first), so this function no longer
22
+ * special-cases `"en"`.
23
+ */
24
+ export declare function resolveDateFieldLocale(locale: string | undefined): string;
25
+ /**
26
+ * Returns the locale-aligned placeholder shown in the DateField. Result is memoized per locale —
27
+ * see `datePlaceholderByLocale` above.
28
+ */
29
+ export declare function getDateFieldPlaceholder(locale: string): string;
30
+ /**
31
+ * Formats a Date for display in the DateField while keeping event payloads canonical.
32
+ */
33
+ export declare function formatDateFieldValueForLocale(value: Date, locale: string): string;
34
+ /**
35
+ * Parses localized or quick-typed DateField input into a Date.
36
+ * Supports canonical YYYY-MM-DD, locale-ordered quick typing (e.g. DDMMYYYY), and YYYYMMDD.
37
+ */
38
+ export declare function parseDateFieldDisplayValue(value: string, locale: string): Date | null;
10
39
  /**
11
40
  * Converts a YYYY-MM-DD string (e.g. from DateField) to an ISO string at UTC midnight for that calendar day.
12
41
  * Use this when storing or sending date-only values so that the calendar day is preserved regardless of
@@ -1,4 +1,4 @@
1
- import { type ChangeEvent } from "react";
1
+ import { type ChangeEvent, type FocusEvent } from "react";
2
2
  /**
3
3
  * Returns a stable function that builds a synthetic change event for an input with a given value.
4
4
  * Use when calling onChange from code (e.g. clear, apply, clamped value) so consumers
@@ -9,3 +9,13 @@ import { type ChangeEvent } from "react";
9
9
  * onChange?.(createInputChangeEvent(str, inputRef.current));
10
10
  */
11
11
  export declare const useCreateInputChangeEvent: () => ((value: string, sourceInput: HTMLInputElement | null) => ChangeEvent<HTMLInputElement>);
12
+ /**
13
+ * Returns a stable function that builds a synthetic blur (FocusEvent) for an input with a given value.
14
+ * Use when calling onBlur from code (e.g. when a typed-in value is normalized on blur) so consumers
15
+ * that use `onBlur={e => doSomething(e.target.value)}` still receive the expected shape.
16
+ *
17
+ * @example
18
+ * const createInputBlurEvent = useCreateInputBlurEvent();
19
+ * onBlur?.(createInputBlurEvent(canonicalValue, inputRef.current));
20
+ */
21
+ export declare const useCreateInputBlurEvent: () => ((value: string, sourceInput: HTMLInputElement | null) => FocusEvent<HTMLInputElement>);
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Resolves the locale used by `DateField` for placeholder, parsing, and display formatting.
3
+ *
4
+ * Date format is a *regional* preference (e.g. `dd/mm/yyyy` vs `mm/dd/yyyy`), independent of the
5
+ * UI translation language. We therefore prefer `navigator.language` — a region-qualified browser
6
+ * locale (`en-GB`, `en-US`, `fr-CA`, `da-DK`, …) is the user's explicit regional preference and
7
+ * wins over the user-selected Manager UI language. The selected language is only used as a
8
+ * fallback when the browser locale is missing or has no region.
9
+ *
10
+ * Examples (selected | browser → resolved):
11
+ * - `en` | `en-GB` → `en-GB` (UK customers see `dd/mm/yyyy`)
12
+ * - `en` | `en-US` → `en-US` (US customers see `mm/dd/yyyy`)
13
+ * - `da` | `en-US` → `en-US` (browser region wins for date format)
14
+ * - `da` | `da-DK` → `da-DK` (`dd.mm.yyyy`, dotted separator is the native Danish format)
15
+ * - `en` | `en` (no region) → falls through to `resolveDateFieldLocale`, which keeps `en`
16
+ * - `cimode` | `en-US` → `en-US` (browser wins; i18next's key-debug pseudo-language is non-Intl)
17
+ * - `cimode` | `en` → `en` (`resolveDateFieldLocale` filters `cimode` out and returns the browser locale)
18
+ *
19
+ * Note: this hook reads `localStorage` and `navigator.language` synchronously and does not
20
+ * subscribe to changes. Re-renders on same-tab language change rely on the consuming component
21
+ * also using `useTranslation` (which re-renders on `i18next.languageChanged`). Cross-tab sync
22
+ * is not supported.
23
+ */
24
+ export declare const useDateFieldLocale: () => string;