@mercury-fx/core 2.4.0

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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -0
  3. package/dist/cx.d.ts +3 -0
  4. package/dist/date.d.ts +7 -0
  5. package/dist/index.d.ts +32 -0
  6. package/dist/internal/arrays.d.ts +95 -0
  7. package/dist/internal/attrs.d.ts +33 -0
  8. package/dist/internal/clamp.d.ts +4 -0
  9. package/dist/internal/create-id.d.ts +8 -0
  10. package/dist/internal/css-escape.d.ts +7 -0
  11. package/dist/internal/date-time/calendar/use-calendar.d.ts +63 -0
  12. package/dist/internal/date-time/field/helpers.d.ts +86 -0
  13. package/dist/internal/date-time/field/parts.d.ts +8 -0
  14. package/dist/internal/date-time/field/segments.d.ts +60 -0
  15. package/dist/internal/date-time/field/time-helpers.d.ts +82 -0
  16. package/dist/internal/date-time/field/types.d.ts +25 -0
  17. package/dist/internal/date-time/field/use-date-field.d.ts +61 -0
  18. package/dist/internal/date-time/formatter.d.ts +114 -0
  19. package/dist/internal/date-time/placeholders.d.ts +8 -0
  20. package/dist/internal/date-time/time-value.d.ts +11 -0
  21. package/dist/internal/date-time/utils.d.ts +76 -0
  22. package/dist/internal/debounce.d.ts +4 -0
  23. package/dist/internal/dom.d.ts +7 -0
  24. package/dist/internal/elements.d.ts +2 -0
  25. package/dist/internal/focus.d.ts +5 -0
  26. package/dist/internal/get-directional-keys.d.ts +21 -0
  27. package/dist/internal/is.d.ts +25 -0
  28. package/dist/internal/kbd-constants.d.ts +42 -0
  29. package/dist/internal/kbd.d.ts +1 -0
  30. package/dist/internal/locale.d.ts +6 -0
  31. package/dist/internal/math.d.ts +1 -0
  32. package/dist/internal/noop.d.ts +4 -0
  33. package/dist/internal/should-enable-focus-trap.d.ts +4 -0
  34. package/dist/internal/types.d.ts +14 -0
  35. package/dist/internal/use-anchored-position.d.ts +45 -0
  36. package/dist/internal/use-arrow-navigation.d.ts +62 -0
  37. package/dist/internal/use-dismiss.d.ts +31 -0
  38. package/dist/internal/use-focus-trap.d.ts +31 -0
  39. package/dist/internal/use-id.d.ts +6 -0
  40. package/dist/internal/warn.d.ts +1 -0
  41. package/dist/mercury-core.js +865 -0
  42. package/dist/mercury-core.js.map +1 -0
  43. package/dist/shared/date/types.d.ts +100 -0
  44. package/dist/shared/index.d.ts +36 -0
  45. package/dist/shared/types.d.ts +13 -0
  46. package/dist/types.d.ts +31 -0
  47. package/dist/utils/after-sleep.d.ts +4 -0
  48. package/dist/utils/clsx.d.ts +7 -0
  49. package/dist/utils/dom.d.ts +14 -0
  50. package/dist/utils/event-list.d.ts +1 -0
  51. package/dist/utils/events.d.ts +13 -0
  52. package/dist/utils/execute-callbacks.d.ts +7 -0
  53. package/dist/utils/is.d.ts +4 -0
  54. package/dist/utils/merge-props.d.ts +22 -0
  55. package/dist/utils/strings.d.ts +3 -0
  56. package/dist/utils/style-to-css.d.ts +9 -0
  57. package/dist/utils/style.d.ts +2 -0
  58. package/package.json +51 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jonnify <mercury@jonnify.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # @mercury-fx/core
2
+
3
+ The UI-free foundation of the **Mercury** design system: a class utility (`cx`),
4
+ locale-aware date formatters, headless behaviour hooks, and the shared type kit.
5
+ No React components, no styles.
6
+
7
+ Most consumers get this package **transitively** through
8
+ [`@mercury-fx/ui`](https://www.npmjs.com/package/@mercury-fx/ui) — install it
9
+ directly only if you want the bare utilities.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm i @mercury-fx/core react # react is a peer dependency
15
+ ```
16
+
17
+ ## Use
18
+
19
+ ```ts
20
+ import { cx, createFormatter } from "@mercury-fx/core";
21
+
22
+ cx("btn", isActive && "btn--active"); // conditional classNames
23
+ const fmt = createFormatter("en-US"); // locale-aware Intl wrapper
24
+ ```
25
+
26
+ Ships as a single ES-module bundle (`dist/mercury-core.js`) with type
27
+ declarations. `react` and `@internationalized/date` are kept external (a peer and
28
+ a runtime dependency, respectively).
29
+
30
+ ## License
31
+
32
+ MIT
package/dist/cx.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type ClassValue = string | false | null | undefined;
2
+ /** Tiny classNames join — filters falsy, joins with spaces. */
3
+ export declare function cx(...parts: ClassValue[]): string;
package/dist/date.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `@mercury-fx/ui` date utilities — the locale-aware formatters that back the date
3
+ * field / calendar components, surfaced for app and `@mercury-fx/effector` use.
4
+ * Pure (no React, no Effector); for reactive store wiring see
5
+ * `@mercury-fx/effector`'s `createFormatterModel`.
6
+ */
7
+ export { createFormatter, createTimeFormatter, type Formatter, type TimeFormatter, type FormatterOptions, type TimeFormatterOptions, type MonthFormat, type YearFormat, type DayPeriodValue, type MaybeReadable, type Readable, } from './internal/date-time/formatter';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @mercury-fx/core — Mercury's UI-free foundation.
3
+ *
4
+ * The layer that sits below `@mercury-fx/ui` and `@mercury-fx/effector`: the class
5
+ * utility, the locale-aware date formatters, and (in `internal/`, `shared/`,
6
+ * `utils/`, `types`) the headless hooks and shared types the components build
7
+ * on. No React components, no styles. Published as a bundled ES module
8
+ * (`dist/mercury-core.js`) with type declarations; the apps resolve it from
9
+ * source via a vite/tsconfig alias in dev.
10
+ *
11
+ * This barrel surfaces what crosses the package boundary today: `cx` (consumed
12
+ * by every component) and the `date` formatters (re-exported by `@mercury-fx/ui`
13
+ * for app and `@mercury-fx/effector` use). The deeper foundation lives here as
14
+ * files and is surfaced as consumers need it.
15
+ */
16
+ export { cx } from './cx';
17
+ export type { ClassValue } from './cx';
18
+ export * from './date';
19
+ export { useDateField } from './internal/date-time/field/use-date-field';
20
+ export type { UseDateFieldOptions, UseDateFieldReturn } from './internal/date-time/field/use-date-field';
21
+ export type { DateValue } from '@internationalized/date';
22
+ export { CalendarDate, parseDate } from '@internationalized/date';
23
+ export { useCalendar } from './internal/date-time/calendar/use-calendar';
24
+ export type { UseCalendarOptions, UseCalendarReturn, CalendarCell, CalendarCellProps, CalendarNavProps } from './internal/date-time/calendar/use-calendar';
25
+ export { useFocusTrap } from './internal/use-focus-trap';
26
+ export type { UseFocusTrapOptions } from './internal/use-focus-trap';
27
+ export { useDismiss } from './internal/use-dismiss';
28
+ export type { UseDismissOptions } from './internal/use-dismiss';
29
+ export { useAnchoredPosition } from './internal/use-anchored-position';
30
+ export type { UseAnchoredPositionOptions, UseAnchoredPositionReturn, AnchoredPlacement, AnchoredAlign, AnchoredPoint, } from './internal/use-anchored-position';
31
+ export { useId } from './internal/use-id';
32
+ export { useArrowNavigation } from './internal/use-arrow-navigation';
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Checks if two arrays are equal by comparing their values.
3
+ */
4
+ export declare function arraysAreEqual<T extends Array<unknown>>(arr1: T, arr2: T): boolean;
5
+ /**
6
+ * Splits an array into chunks of a given size.
7
+ * @param arr The array to split.
8
+ * @param size The size of each chunk.
9
+ * @returns An array of arrays, where each sub-array has `size` elements from the original array.
10
+ * @example ```ts
11
+ * const arr = [1, 2, 3, 4, 5, 6, 7, 8];
12
+ * const chunks = chunk(arr, 3);
13
+ * // chunks = [[1, 2, 3], [4, 5, 6], [7, 8]]
14
+ * ```
15
+ */
16
+ export declare function chunk<T>(arr: T[], size: number): T[][];
17
+ /**
18
+ * Checks if the given index is valid for the given array.
19
+ *
20
+ * @param index - The index to check
21
+ * @param arr - The array to check
22
+ */
23
+ export declare function isValidIndex(index: number, arr: unknown[]): boolean;
24
+ /**
25
+ * Returns the array element after the given index, or undefined for out-of-bounds or empty arrays.
26
+ * @param array the array.
27
+ * @param index the index of the current element.
28
+ * @param loop loop to the beginning of the array if the next index is out of bounds?
29
+ */
30
+ /**
31
+ * Returns the array element after the given index, or undefined for out-of-bounds or empty arrays.
32
+ * For single-element arrays, returns the element if the index is 0.
33
+ * @param array the array.
34
+ * @param index the index of the current element.
35
+ * @param loop loop to the beginning of the array if the next index is out of bounds?
36
+ */
37
+ export declare function next<T>(array: T[], index: number, loop?: boolean): T | undefined;
38
+ /**
39
+ * Returns the array element prior to the given index, or undefined for out-of-bounds or empty arrays.
40
+ * For single-element arrays, returns the element if the index is 0.
41
+ * @param array the array.
42
+ * @param index the index of the current element.
43
+ * @param loop loop to the end of the array if the previous index is out of bounds?
44
+ */
45
+ export declare function prev<T>(array: T[], index: number, loop?: boolean): T | undefined;
46
+ /**
47
+ * Returns the element some number after the given index. If the target index is out of bounds:
48
+ * - If looping is disabled, the first or last element will be returned.
49
+ * - If looping is enabled, it will wrap around the array.
50
+ * Returns undefined for empty arrays or out-of-bounds initial indices.
51
+ * @param array the array.
52
+ * @param index the index of the current element.
53
+ * @param increment the number of elements to move forward (can be negative).
54
+ * @param loop loop around the array if the target index is out of bounds?
55
+ */
56
+ export declare function forward<T>(array: T[], index: number, increment: number, loop?: boolean): T | undefined;
57
+ /**
58
+ * Returns the element some number before the given index. If the target index is out of bounds:
59
+ * - If looping is disabled, the first or last element will be returned.
60
+ * - If looping is enabled, it will wrap around the array.
61
+ * Returns undefined for empty arrays or out-of-bounds initial indices.
62
+ * @param array the array.
63
+ * @param index the index of the current element.
64
+ * @param decrement the number of elements to move backward (can be negative).
65
+ * @param loop loop around the array if the target index is out of bounds?
66
+ */
67
+ export declare function backward<T>(array: T[], index: number, decrement: number, loop?: boolean): T | undefined;
68
+ /**
69
+ * Finds the next matching item from a list of values based on a search string.
70
+ *
71
+ * This function handles several special cases in typeahead behavior:
72
+ *
73
+ * 1. Space handling: When a search string ends with a space, it handles it specially:
74
+ * - If there's only one match for the text before the space, it ignores the space
75
+ * - If there are multiple matches and the current match already starts with the search prefix
76
+ * followed by a space, it keeps the current match (doesn't change selection on space)
77
+ * - Only after typing characters beyond the space will it move to a more specific match
78
+ *
79
+ * 2. Repeated character handling: If a search consists of repeated characters (e.g., "aaa"),
80
+ * it treats it as a single character for matching purposes
81
+ *
82
+ * 3. Cycling behavior: The function wraps around the values array starting from the current match
83
+ * to find the next appropriate match, creating a cycling selection behavior
84
+ *
85
+ * @param values - Array of string values to search through (e.g., the text content of menu items)
86
+ * @param search - The current search string typed by the user
87
+ * @param currentMatch - The currently selected/matched item, if any
88
+ * @returns The next matching value that should be selected, or undefined if no match is found
89
+ */
90
+ export declare function getNextMatch(values: string[], search: string, currentMatch?: string): string | undefined;
91
+ /**
92
+ * Wraps an array around itself at a given start index
93
+ * Example: `wrapArray(['a', 'b', 'c', 'd'], 2) === ['c', 'd', 'a', 'b']`
94
+ */
95
+ export declare function wrapArray<T>(array: T[], startIndex: number): T[];
@@ -0,0 +1,33 @@
1
+ export declare function boolToStr(condition: boolean): "true" | "false";
2
+ export declare function boolToStrTrueOrUndef(condition: boolean): "true" | undefined;
3
+ export declare function boolToEmptyStrOrUndef(condition: boolean): "" | undefined;
4
+ export declare function boolToTrueOrUndef(condition: boolean): true | undefined;
5
+ export declare function getDataOpenClosed(condition: boolean): "open" | "closed";
6
+ export declare function getDataChecked(condition: boolean): "checked" | "unchecked";
7
+ export type TransitionState = "starting" | "ending" | "idle" | undefined;
8
+ export declare function getDataTransitionAttrs(state: TransitionState): {
9
+ "data-starting-style"?: "";
10
+ "data-ending-style"?: "";
11
+ };
12
+ export declare function getAriaChecked(checked: boolean, indeterminate: boolean): "true" | "false" | "mixed";
13
+ export type BitsAttrsConfig<T extends readonly string[]> = {
14
+ component: string;
15
+ parts: T;
16
+ getVariant?: () => string | null;
17
+ };
18
+ export type CreateBitsAttrsReturn<T extends readonly string[]> = {
19
+ [K in T[number]]: string;
20
+ } & {
21
+ selector: (part: T[number]) => string;
22
+ getAttr: (part: T[number], variant?: string) => string;
23
+ };
24
+ export declare class BitsAttrs<T extends readonly string[]> {
25
+ #private;
26
+ attrs: Record<T[number], string>;
27
+ constructor(config: BitsAttrsConfig<T>);
28
+ getAttr(part: T[number], variantOverride?: string): string;
29
+ selector(part: T[number], variantOverride?: string): string;
30
+ }
31
+ export declare function createBitsAttrs<const T extends readonly string[]>(config: Omit<BitsAttrsConfig<T>, "parts"> & {
32
+ parts: T;
33
+ }): CreateBitsAttrsReturn<T>;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Clamps a number between a minimum and maximum value.
3
+ */
4
+ export declare function clamp(n: number, min: number, max: number): number;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Creates a unique ID for a given uid and optional prefix.
3
+ *
4
+ * @param uid - the uid generated by $props.id()
5
+ * @param prefix - optional prefix to use for the id (defaults to "bits")
6
+ */
7
+ export declare function createId(uid: string): string;
8
+ export declare function createId(prefix: string, uid: string): string;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * https://github.com/mathiasbynens/CSS.escape
3
+ *
4
+ * @param value - The value to escape for use as a CSS identifier
5
+ * @returns The escaped CSS identifier string
6
+ */
7
+ export declare function cssEscape(value: string): string;
@@ -0,0 +1,63 @@
1
+ import { DateValue } from '@internationalized/date';
2
+ /** Options for {@link useCalendar}. */
3
+ export type UseCalendarOptions = {
4
+ /** Controlled selected day. */
5
+ value?: DateValue;
6
+ /** Uncontrolled initial selected day. */
7
+ defaultValue?: DateValue;
8
+ /** Fires when a day is chosen. */
9
+ onChange?: (value: DateValue) => void;
10
+ /** BCP-47 locale for the month/weekday labels + week ordering. Default "en". */
11
+ locale?: string;
12
+ /** First column of the week, 0=Sun .. 6=Sat. Default 0. */
13
+ firstDayOfWeek?: number;
14
+ };
15
+ /** Props spread onto a single day cell. */
16
+ export interface CalendarCellProps {
17
+ role: "gridcell";
18
+ "aria-selected": boolean;
19
+ "aria-label": string;
20
+ tabIndex: number;
21
+ onClick: () => void;
22
+ }
23
+ /** One rendered day in the month grid. */
24
+ export interface CalendarCell {
25
+ date: DateValue;
26
+ label: string;
27
+ isSelected: boolean;
28
+ isToday: boolean;
29
+ isOutsideMonth: boolean;
30
+ cellProps: CalendarCellProps;
31
+ }
32
+ /** Props for a prev/next month navigation control. */
33
+ export interface CalendarNavProps {
34
+ onClick: () => void;
35
+ "aria-label": string;
36
+ }
37
+ /** Return shape of {@link useCalendar}. */
38
+ export interface UseCalendarReturn {
39
+ /** Localized "month year" header, e.g. "March 2024". */
40
+ monthLabel: string;
41
+ /** Seven localized weekday headers, in `firstDayOfWeek` order. */
42
+ weekdays: string[];
43
+ /** The 42 grid cells (6 weeks) for the visible month. */
44
+ cells: CalendarCell[];
45
+ /** Props for the grid container. */
46
+ gridProps: {
47
+ role: "grid";
48
+ "aria-label": string;
49
+ };
50
+ /** The current selected value (controlled or uncontrolled). */
51
+ selected: DateValue | undefined;
52
+ /** Props for the previous-month control. */
53
+ prevButtonProps: CalendarNavProps;
54
+ /** Props for the next-month control. */
55
+ nextButtonProps: CalendarNavProps;
56
+ }
57
+ /**
58
+ * Headless month-grid calendar composable. Composes the owned `internal/date-time`
59
+ * machinery (locale formatter, week alignment, value conversion) over
60
+ * `@internationalized/date` values; the only new logic is the grid assembly and
61
+ * month paging. Presentational-ready: spread the cell/nav prop kits.
62
+ */
63
+ export declare function useCalendar(options?: UseCalendarOptions): UseCalendarReturn;
@@ -0,0 +1,86 @@
1
+ import { DateValue } from '@internationalized/date';
2
+ import { Formatter } from '../../..internal/date-time/formatter.js';
3
+ import { DateAndTimeSegmentObj, DateSegmentPart, EditableSegmentPart, SegmentContentObj, SegmentPart, SegmentStateMap, SegmentValueObj } from './types.js';
4
+ import { Granularity, HourCycle } from '../../..shared/date/types.js';
5
+ export declare function initializeSegmentValues(granularity: Granularity): SegmentValueObj;
6
+ type SharedContentProps = {
7
+ granularity: Granularity;
8
+ dateRef: DateValue;
9
+ formatter: Formatter;
10
+ hideTimeZone: boolean;
11
+ hourCycle: HourCycle | undefined;
12
+ };
13
+ type CreateContentObjProps = SharedContentProps & {
14
+ segmentValues: SegmentValueObj;
15
+ locale: string;
16
+ };
17
+ type CreateContentProps = CreateContentObjProps;
18
+ export declare function createContent(props: CreateContentProps): {
19
+ obj: SegmentContentObj;
20
+ arr: {
21
+ part: SegmentPart;
22
+ value: string;
23
+ }[];
24
+ };
25
+ export declare function initSegmentStates(): SegmentStateMap;
26
+ export declare function initSegmentIds(): any;
27
+ export declare function isDateSegmentPart(part: unknown): part is DateSegmentPart;
28
+ export declare function isSegmentPart(part: string): part is EditableSegmentPart;
29
+ export declare function isAnySegmentPart(part: unknown): part is SegmentPart;
30
+ type GetValueFromSegments = {
31
+ segmentObj: SegmentValueObj;
32
+ fieldNode: HTMLElement | null;
33
+ dateRef: DateValue;
34
+ };
35
+ export declare function getValueFromSegments(props: GetValueFromSegments): DateValue;
36
+ /**
37
+ * Check if all the segments being used have been filled.
38
+ * We use this to determine when we should set the value
39
+ * store of the date field(s).
40
+ *
41
+ * @param segmentValues - The current `SegmentValueObj`
42
+ * @param fieldNode - The id of the date field
43
+ */
44
+ export declare function areAllSegmentsFilled(segmentValues: SegmentValueObj, fieldNode: HTMLElement | null): boolean;
45
+ /**
46
+ * Determines if the provided object is a valid `DateAndTimeSegmentObj`
47
+ * by checking if it has the correct keys and values for each key.
48
+ */
49
+ export declare function isDateAndTimeSegmentObj(obj: unknown): obj is DateAndTimeSegmentObj;
50
+ /**
51
+ * Infer the granularity to use based on the
52
+ * value and granularity props.
53
+ */
54
+ export declare function inferGranularity(value: DateValue, granularity: Granularity | undefined): Granularity;
55
+ export declare function isAcceptableSegmentKey(key: string): boolean;
56
+ /**
57
+ * Determines if the element with the provided id is the first focusable
58
+ * segment in the date field with the provided fieldId.
59
+ *
60
+ * @param id - The id of the element to check if it's the first segment
61
+ * @param fieldNode - The id of the date field associated with the segment
62
+ */
63
+ export declare function isFirstSegment(id: string, fieldNode: HTMLElement | null): boolean;
64
+ type SetDescriptionProps = {
65
+ id: string;
66
+ formatter: Formatter;
67
+ value: DateValue;
68
+ doc: Document;
69
+ };
70
+ /**
71
+ * Creates or updates a description element for a date field
72
+ * which enables screen readers to read the date field's value.
73
+ *
74
+ * This element is hidden from view, and is portalled to the body
75
+ * so it can be associated via `aria-describedby` and read by
76
+ * screen readers as the user interacts with the date field.
77
+ */
78
+ export declare function setDescription(props: SetDescriptionProps): void;
79
+ /**
80
+ * Removes the description element for the date field with
81
+ * the provided ID. This function should be called when the
82
+ * date field is unmounted.
83
+ */
84
+ export declare function removeDescriptionElement(id: string, doc: Document): void;
85
+ export declare function getDefaultHourCycle(locale: string): 12 | 24;
86
+ export {};
@@ -0,0 +1,8 @@
1
+ export declare const DATE_SEGMENT_PARTS: readonly ["day", "month", "year"];
2
+ export declare const EDITABLE_TIME_SEGMENT_PARTS: readonly ["hour", "minute", "second", "dayPeriod"];
3
+ export declare const NON_EDITABLE_SEGMENT_PARTS: readonly ["literal", "timeZoneName"];
4
+ export declare const EDITABLE_SEGMENT_PARTS: readonly ["day", "month", "year", "hour", "minute", "second", "dayPeriod"];
5
+ export declare const ALL_SEGMENT_PARTS: readonly ["day", "month", "year", "hour", "minute", "second", "dayPeriod", "literal", "timeZoneName"];
6
+ export declare const ALL_TIME_SEGMENT_PARTS: readonly ["hour", "minute", "second", "dayPeriod", "literal", "timeZoneName"];
7
+ export declare const ALL_EXCEPT_LITERAL_PARTS: ("day" | "month" | "year" | "hour" | "minute" | "second" | "dayPeriod" | "timeZoneName")[];
8
+ export declare const ALL_TIME_EXCEPT_LITERAL_PARTS: ("hour" | "minute" | "second" | "dayPeriod" | "timeZoneName")[];
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Handles segment navigation based on the provided keyboard event and field ID.
3
+ *
4
+ * @param e - The keyboard event
5
+ * @param fieldNode - The ID of the field we're navigating within
6
+ */
7
+ export declare function handleSegmentNavigation(e: KeyboardEvent, fieldNode: HTMLElement | null): void;
8
+ export declare function handleTimeSegmentNavigation(e: KeyboardEvent, fieldNode: HTMLElement | null): void;
9
+ /**
10
+ * Retrieves the next segment in the list of segments relative to the provided node.
11
+ *
12
+ * @param node - The node we're starting from
13
+ * @param segments - The list of candidate segments to navigate through
14
+ */
15
+ export declare function getNextSegment(node: HTMLElement, segments: HTMLElement[]): HTMLElement | null | undefined;
16
+ /**
17
+ * Retrieves the previous segment in the list of segments relative to the provided node.
18
+ *
19
+ * @param node - The node we're starting from
20
+ * @param segments - The list of candidate segments to navigate through
21
+ */
22
+ export declare function getPrevSegment(node: HTMLElement, segments: HTMLElement[]): HTMLElement | null | undefined;
23
+ /**
24
+ * Retrieves an object containing the next and previous segments relative to the current node.
25
+ *
26
+ * @param startingNode - The node we're starting from
27
+ * @param fieldNode - The ID of the field we're navigating within
28
+ */
29
+ export declare function getPrevNextSegments(startingNode: HTMLElement, fieldNode: HTMLElement | null): {
30
+ next: HTMLElement | null | undefined;
31
+ prev: HTMLElement | null | undefined;
32
+ };
33
+ export declare function getPrevNextTimeSegments(startingNode: HTMLElement, fieldNode: HTMLElement | null): {
34
+ next: HTMLElement | null | undefined;
35
+ prev: HTMLElement | null | undefined;
36
+ };
37
+ /**
38
+ * Shifts the focus to the next segment in the list of segments
39
+ * within the field identified by the provided ID.
40
+ */
41
+ export declare function moveToNextSegment(e: KeyboardEvent, fieldNode: HTMLElement | null): void;
42
+ export declare function moveToNextTimeSegment(e: KeyboardEvent, fieldNode: HTMLElement | null): void;
43
+ export declare function moveToPrevTimeSegment(e: KeyboardEvent, fieldNode: HTMLElement | null): void;
44
+ /**
45
+ * Shifts the focus to the previous segment in the list of segments
46
+ * within the field identified by the provided ID. If this is the first
47
+ * segment, focus will not be shifted.
48
+ */
49
+ export declare function moveToPrevSegment(e: KeyboardEvent, fieldNode: HTMLElement | null): void;
50
+ export declare function isSegmentNavigationKey(key: string): boolean;
51
+ /**
52
+ * Retrieves all the interactive segments within the field identified by the provided ID.
53
+ */
54
+ export declare function getSegments(fieldNode: HTMLElement | null): HTMLElement[];
55
+ export declare function getTimeSegments(fieldNode: HTMLElement | null): HTMLElement[];
56
+ export declare function getFirstTimeSegment(fieldNode: HTMLElement | null): HTMLElement | undefined;
57
+ /**
58
+ * Get the first interactive segment within the field identified by the provided ID.
59
+ */
60
+ export declare function getFirstSegment(fieldNode: HTMLElement | null): HTMLElement | undefined;
@@ -0,0 +1,82 @@
1
+ import { EditableTimeSegmentPart, HourCycle, TimeGranularity, TimeSegmentContentObj, TimeSegmentStateMap, TimeSegmentValueObj, TimeValue } from '../../..shared/date/types.js';
2
+ import { Time } from '@internationalized/date';
3
+ import { TimeFormatter } from '../../..internal/date-time/formatter.js';
4
+ import { TimeSegmentPart } from './types.js';
5
+ export declare function initializeSegmentValues(): TimeSegmentValueObj;
6
+ type SharedTimeContentProps = {
7
+ granularity: TimeGranularity;
8
+ timeRef: TimeValue;
9
+ formatter: TimeFormatter;
10
+ hideTimeZone: boolean;
11
+ hourCycle: HourCycle | undefined;
12
+ };
13
+ type CreateTimeContentObjProps = SharedTimeContentProps & {
14
+ segmentValues: TimeSegmentValueObj;
15
+ locale: string;
16
+ };
17
+ type CreateTimeContentProps = CreateTimeContentObjProps;
18
+ export declare function createTimeContent(props: CreateTimeContentProps): {
19
+ obj: TimeSegmentContentObj;
20
+ arr: {
21
+ part: TimeSegmentPart;
22
+ value: string;
23
+ }[];
24
+ };
25
+ export declare function initTimeSegmentStates(): TimeSegmentStateMap;
26
+ export declare function initTimeSegmentIds(): any;
27
+ export declare function isEditableTimeSegmentPart(part: unknown): part is EditableTimeSegmentPart;
28
+ export declare function isAnyTimeSegmentPart(part: unknown): part is TimeSegmentPart;
29
+ type GetTimeValueFromSegments<T extends TimeValue = Time> = {
30
+ segmentObj: TimeSegmentValueObj;
31
+ fieldNode: HTMLElement | null;
32
+ timeRef: T;
33
+ };
34
+ export declare function getTimeValueFromSegments<T extends TimeValue = Time>(props: GetTimeValueFromSegments<T>): T;
35
+ /**
36
+ * Check if all the segments being used have been filled.
37
+ * We use this to determine when we should set the value
38
+ * store of the date field(s).
39
+ *
40
+ * @param segmentValues - The current `SegmentValueObj`
41
+ * @param fieldNode - The id of the date field
42
+ */
43
+ export declare function areAllTimeSegmentsFilled(segmentValues: TimeSegmentValueObj, fieldNode: HTMLElement | null): boolean;
44
+ /**
45
+ * Infer the granularity to use based on the
46
+ * value and granularity props.
47
+ */
48
+ export declare function inferTimeGranularity(granularity: TimeGranularity | undefined): TimeGranularity;
49
+ /**
50
+ * Determines if the element with the provided id is the first focusable
51
+ * segment in the date field with the provided fieldId.
52
+ *
53
+ * @param id - The id of the element to check if it's the first segment
54
+ * @param fieldNode - The id of the date field associated with the segment
55
+ */
56
+ export declare function isFirstTimeSegment(id: string, fieldNode: HTMLElement | null): boolean;
57
+ type SetTimeDescriptionProps = {
58
+ id: string;
59
+ formatter: TimeFormatter;
60
+ value: TimeValue;
61
+ doc: Document;
62
+ };
63
+ /**
64
+ * Creates or updates a description element for a date field
65
+ * which enables screen readers to read the date field's value.
66
+ *
67
+ * This element is hidden from view, and is portalled to the body
68
+ * so it can be associated via `aria-describedby` and read by
69
+ * screen readers as the user interacts with the date field.
70
+ */
71
+ export declare function setTimeDescription(props: SetTimeDescriptionProps): void;
72
+ /**
73
+ * Removes the description element for the date field with
74
+ * the provided ID. This function should be called when the
75
+ * date field is unmounted.
76
+ */
77
+ export declare function removeTimeDescriptionElement(id: string, doc: Document): void;
78
+ export { convertTimeValueToDateValue } from '../../..internal/date-time/time-value.js';
79
+ export declare function convertTimeValueToTime(time: TimeValue): Time;
80
+ export declare function isTimeBefore(timeToCompare: Time, referenceTime: Time): boolean;
81
+ export declare function isTimeAfter(timeToCompare: Time, referenceTime: Time): boolean;
82
+ export declare function getISOTimeValue(time: TimeValue): string;
@@ -0,0 +1,25 @@
1
+ import { DATE_SEGMENT_PARTS, EDITABLE_SEGMENT_PARTS, NON_EDITABLE_SEGMENT_PARTS, EDITABLE_TIME_SEGMENT_PARTS } from './parts.js';
2
+ export type DateSegmentPart = (typeof DATE_SEGMENT_PARTS)[number];
3
+ export type TimeSegmentPart = (typeof EDITABLE_TIME_SEGMENT_PARTS)[number];
4
+ export type EditableSegmentPart = (typeof EDITABLE_SEGMENT_PARTS)[number];
5
+ export type NonEditableSegmentPart = (typeof NON_EDITABLE_SEGMENT_PARTS)[number];
6
+ export type SegmentPart = EditableSegmentPart | NonEditableSegmentPart;
7
+ export type AnyExceptLiteral = Exclude<SegmentPart, "literal">;
8
+ export type DayPeriod = "AM" | "PM" | null;
9
+ export type DateSegmentObj = {
10
+ [K in DateSegmentPart]: string | null;
11
+ };
12
+ export type TimeSegmentObj = {
13
+ [K in TimeSegmentPart]: K extends "dayPeriod" ? DayPeriod : string | null;
14
+ };
15
+ export type DateAndTimeSegmentObj = DateSegmentObj & TimeSegmentObj;
16
+ export type SegmentValueObj = DateSegmentObj | DateAndTimeSegmentObj;
17
+ export type SegmentContentObj = Record<EditableSegmentPart, string>;
18
+ export type SegmentState = {
19
+ lastKeyZero: boolean;
20
+ hasLeftFocus: boolean;
21
+ updating: string | null;
22
+ };
23
+ export type SegmentStateMap = {
24
+ [K in EditableSegmentPart]: SegmentState;
25
+ };
@@ -0,0 +1,61 @@
1
+ import { KeyboardEvent, RefObject } from 'react';
2
+ import { DateValue } from '@internationalized/date';
3
+ /** Options for {@link useDateField}. */
4
+ export type UseDateFieldOptions = {
5
+ /** Controlled value. When set, the hook reflects this value and reports edits via `onChange`. */
6
+ value?: DateValue;
7
+ /** Uncontrolled initial value — seeds the segments on first render. */
8
+ defaultValue?: DateValue;
9
+ /** Called with the reconstructed value when every editable segment is filled, or `undefined` otherwise. */
10
+ onChange?: (v: DateValue | undefined) => void;
11
+ /** BCP-47 locale that orders the segments and renders the literals. Defaults to `"en"`. */
12
+ locale?: string;
13
+ /** Granularity of the field. Date-only for this wave. */
14
+ granularity?: "day";
15
+ };
16
+ /** One rendered slot of the date field — an editable part or a literal separator. */
17
+ export interface DateSegment {
18
+ part: string;
19
+ value: string;
20
+ }
21
+ /** Props for the field container element. */
22
+ export interface FieldProps {
23
+ ref: RefObject<HTMLElement | null>;
24
+ role: "group";
25
+ }
26
+ /** Props for an editable segment (spinbutton) — spread onto the part element. */
27
+ export interface EditableSegmentProps {
28
+ "data-segment": string;
29
+ role: "spinbutton";
30
+ inputMode: "numeric";
31
+ tabIndex: number;
32
+ "aria-label": string;
33
+ "aria-valuenow"?: number;
34
+ "aria-valuemin": number;
35
+ "aria-valuemax": number;
36
+ onKeyDown: (e: KeyboardEvent<HTMLElement>) => void;
37
+ }
38
+ /** Props for a literal (separator) segment. */
39
+ export interface LiteralSegmentProps {
40
+ "data-segment": string;
41
+ }
42
+ /** The two segment shapes spread by {@link UseDateFieldReturn.segmentProps}. */
43
+ export type SegmentProps = EditableSegmentProps | LiteralSegmentProps;
44
+ /** Return shape of {@link useDateField}. */
45
+ export interface UseDateFieldReturn {
46
+ /** Ordered render list — editable parts and literal separators, in locale order. */
47
+ segments: DateSegment[];
48
+ /** Props for the field container (carries the ref the machinery queries). */
49
+ fieldProps: FieldProps;
50
+ /** Props builder for a single segment, keyed by its part name. */
51
+ segmentProps: (part: string) => SegmentProps;
52
+ /** The current value — controlled value when set, else the reconstruction when all parts are filled. */
53
+ value: DateValue | undefined;
54
+ }
55
+ /**
56
+ * Headless date-field composable. Composes the owned `internal/date-time` machinery
57
+ * (segment seeding, locale formatter, content layout, value reconstruction, caret
58
+ * navigation) into a presentational-ready prop kit. The keyboard reducer is the only
59
+ * new logic; no native date math is hand-rolled.
60
+ */
61
+ export declare function useDateField(options?: UseDateFieldOptions): UseDateFieldReturn;