@codecademy/gamut 68.2.3-alpha.df22d8.0 → 68.2.3-alpha.e0ecfc.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 (62) hide show
  1. package/dist/DatePicker/Calendar/CalendarBody.d.ts +3 -0
  2. package/dist/DatePicker/Calendar/CalendarBody.js +143 -0
  3. package/dist/DatePicker/Calendar/CalendarFooter.d.ts +3 -0
  4. package/dist/DatePicker/Calendar/CalendarFooter.js +57 -0
  5. package/dist/DatePicker/Calendar/CalendarHeader.d.ts +3 -0
  6. package/dist/DatePicker/Calendar/CalendarHeader.js +46 -0
  7. package/dist/DatePicker/Calendar/CalendarNavLastMonth.d.ts +3 -0
  8. package/dist/DatePicker/Calendar/CalendarNavLastMonth.js +30 -0
  9. package/dist/DatePicker/Calendar/CalendarNavNextMonth.d.ts +3 -0
  10. package/dist/DatePicker/Calendar/CalendarNavNextMonth.js +30 -0
  11. package/dist/DatePicker/Calendar/CalendarWrapper.d.ts +8 -0
  12. package/dist/DatePicker/Calendar/CalendarWrapper.js +27 -0
  13. package/dist/DatePicker/Calendar/index.d.ts +6 -0
  14. package/dist/DatePicker/Calendar/index.js +6 -0
  15. package/dist/DatePicker/Calendar/types.d.ts +77 -0
  16. package/dist/DatePicker/Calendar/types.js +1 -0
  17. package/dist/DatePicker/Calendar/utils/dateGrid.d.ts +38 -0
  18. package/dist/DatePicker/Calendar/utils/dateGrid.js +103 -0
  19. package/dist/DatePicker/Calendar/utils/elements.d.ts +18 -0
  20. package/dist/DatePicker/Calendar/utils/elements.js +116 -0
  21. package/dist/DatePicker/Calendar/utils/format.d.ts +28 -0
  22. package/dist/DatePicker/Calendar/utils/format.js +72 -0
  23. package/dist/DatePicker/Calendar/utils/keyHandler.d.ts +12 -0
  24. package/dist/DatePicker/Calendar/utils/keyHandler.js +126 -0
  25. package/dist/DatePicker/Calendar/utils/validation.d.ts +13 -0
  26. package/dist/DatePicker/Calendar/utils/validation.js +23 -0
  27. package/dist/DatePicker/DatePicker.d.ts +7 -0
  28. package/dist/DatePicker/DatePicker.js +157 -0
  29. package/dist/DatePicker/DatePickerCalendar.d.ts +11 -0
  30. package/dist/DatePicker/DatePickerCalendar.js +163 -0
  31. package/dist/DatePicker/DatePickerContext.d.ts +10 -0
  32. package/dist/DatePicker/DatePickerContext.js +18 -0
  33. package/dist/DatePicker/DatePickerInput/Segment/DatePickerInputSegment.d.ts +20 -0
  34. package/dist/DatePicker/DatePickerInput/Segment/DatePickerInputSegment.js +138 -0
  35. package/dist/DatePicker/DatePickerInput/Segment/elements.d.ts +16 -0
  36. package/dist/DatePicker/DatePickerInput/Segment/elements.js +34 -0
  37. package/dist/DatePicker/DatePickerInput/Segment/index.d.ts +4 -0
  38. package/dist/DatePicker/DatePickerInput/Segment/index.js +3 -0
  39. package/dist/DatePicker/DatePickerInput/Segment/segmentUtils.d.ts +49 -0
  40. package/dist/DatePicker/DatePickerInput/Segment/segmentUtils.js +202 -0
  41. package/dist/DatePicker/DatePickerInput/elements.d.ts +15 -0
  42. package/dist/DatePicker/DatePickerInput/elements.js +19 -0
  43. package/dist/DatePicker/DatePickerInput/index.d.ts +13 -0
  44. package/dist/DatePicker/DatePickerInput/index.js +187 -0
  45. package/dist/DatePicker/DatePickerInput/utils.d.ts +17 -0
  46. package/dist/DatePicker/DatePickerInput/utils.js +50 -0
  47. package/dist/DatePicker/index.d.ts +5 -0
  48. package/dist/DatePicker/index.js +5 -0
  49. package/dist/DatePicker/types.d.ts +86 -0
  50. package/dist/DatePicker/types.js +1 -0
  51. package/dist/DatePicker/utils/dateSelect.d.ts +19 -0
  52. package/dist/DatePicker/utils/dateSelect.js +146 -0
  53. package/dist/DatePicker/utils/locale.d.ts +38 -0
  54. package/dist/DatePicker/utils/locale.js +93 -0
  55. package/dist/DatePicker/utils/translations.d.ts +15 -0
  56. package/dist/DatePicker/utils/translations.js +10 -0
  57. package/dist/FocusTrap/index.d.ts +2 -2
  58. package/dist/PopoverContainer/PopoverContainer.js +23 -3
  59. package/dist/PopoverContainer/types.d.ts +5 -0
  60. package/dist/index.d.ts +2 -0
  61. package/dist/index.js +2 -0
  62. package/package.json +7 -6
@@ -0,0 +1,15 @@
1
+ import { StyleProps } from '@codecademy/variance';
2
+ import { conditionalStyles, inputSizeStyles } from '../../Form/styles';
3
+ interface SegmentedShellProps extends StyleProps<typeof conditionalStyles>, StyleProps<typeof inputSizeStyles> {
4
+ }
5
+ /**
6
+ * Shell uses the same style stack as `Input`. `formFieldStyles` targets `&:focus`, but the host is a
7
+ * `div` — focus is on inner spinbuttons, so we mirror `Input` focus visuals with `&:focus-within`.
8
+ */
9
+ export declare const SegmentedShell: import("@emotion/styled").StyledComponent<{
10
+ theme?: import("@emotion/react").Theme;
11
+ as?: React.ElementType;
12
+ } & import("../../Box").FlexBoxProps & Pick<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "slot" | "style" | "title" | "dir" | "children" | "className" | "aria-hidden" | "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | keyof import("react").ClassAttributes<HTMLDivElement> | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "autoCapitalize" | "autoFocus" | "contentEditable" | "contextMenu" | "draggable" | "enterKeyHint" | "hidden" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "content" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "exportparts" | "part" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDragCapture" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture"> & {
13
+ theme?: import("@emotion/react").Theme;
14
+ } & SegmentedShellProps, {}, {}>;
15
+ export {};
@@ -0,0 +1,19 @@
1
+ import _styled from "@emotion/styled/base";
2
+ import { css, theme } from '@codecademy/gamut-styles';
3
+ import { FlexBox } from '../../Box';
4
+ import { conditionalStyles, formFieldFocusStyles, formFieldStyles, inputSizeStyles } from '../../Form/styles';
5
+ /**
6
+ * Shell uses the same style stack as `Input`. `formFieldStyles` targets `&:focus`, but the host is a
7
+ * `div` — focus is on inner spinbuttons, so we mirror `Input` focus visuals with `&:focus-within`.
8
+ */
9
+ export const SegmentedShell = /*#__PURE__*/_styled(FlexBox, {
10
+ target: "ex306dz0",
11
+ label: "SegmentedShell"
12
+ })(formFieldStyles, conditionalStyles, inputSizeStyles, ({
13
+ variant
14
+ }) => css({
15
+ '&:focus-within': variant === 'error' ? {
16
+ borderColor: 'feedback-error',
17
+ boxShadow: `inset 0 0 0 1px ${theme.colors['feedback-error']}`
18
+ } : formFieldFocusStyles
19
+ }), process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9EYXRlUGlja2VyL0RhdGVQaWNrZXJJbnB1dC9lbGVtZW50cy50c3giXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBb0I4QiIsImZpbGUiOiIuLi8uLi8uLi9zcmMvRGF0ZVBpY2tlci9EYXRlUGlja2VySW5wdXQvZWxlbWVudHMudHN4Iiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3NzLCB0aGVtZSB9IGZyb20gJ0Bjb2RlY2FkZW15L2dhbXV0LXN0eWxlcyc7XG5pbXBvcnQgeyBTdHlsZVByb3BzIH0gZnJvbSAnQGNvZGVjYWRlbXkvdmFyaWFuY2UnO1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnO1xuXG5pbXBvcnQgeyBGbGV4Qm94IH0gZnJvbSAnLi4vLi4vQm94JztcbmltcG9ydCB7XG4gIGNvbmRpdGlvbmFsU3R5bGVzLFxuICBmb3JtRmllbGRGb2N1c1N0eWxlcyxcbiAgZm9ybUZpZWxkU3R5bGVzLFxuICBpbnB1dFNpemVTdHlsZXMsXG59IGZyb20gJy4uLy4uL0Zvcm0vc3R5bGVzJztcblxuaW50ZXJmYWNlIFNlZ21lbnRlZFNoZWxsUHJvcHNcbiAgZXh0ZW5kcyBTdHlsZVByb3BzPHR5cGVvZiBjb25kaXRpb25hbFN0eWxlcz4sXG4gICAgU3R5bGVQcm9wczx0eXBlb2YgaW5wdXRTaXplU3R5bGVzPiB7fVxuXG4vKipcbiAqIFNoZWxsIHVzZXMgdGhlIHNhbWUgc3R5bGUgc3RhY2sgYXMgYElucHV0YC4gYGZvcm1GaWVsZFN0eWxlc2AgdGFyZ2V0cyBgJjpmb2N1c2AsIGJ1dCB0aGUgaG9zdCBpcyBhXG4gKiBgZGl2YCDigJQgZm9jdXMgaXMgb24gaW5uZXIgc3BpbmJ1dHRvbnMsIHNvIHdlIG1pcnJvciBgSW5wdXRgIGZvY3VzIHZpc3VhbHMgd2l0aCBgJjpmb2N1cy13aXRoaW5gLlxuICovXG5leHBvcnQgY29uc3QgU2VnbWVudGVkU2hlbGwgPSBzdHlsZWQoRmxleEJveCk8U2VnbWVudGVkU2hlbGxQcm9wcz4oXG4gIGZvcm1GaWVsZFN0eWxlcyxcbiAgY29uZGl0aW9uYWxTdHlsZXMsXG4gIGlucHV0U2l6ZVN0eWxlcyxcbiAgKHsgdmFyaWFudCB9KSA9PlxuICAgIGNzcyh7XG4gICAgICAnJjpmb2N1cy13aXRoaW4nOlxuICAgICAgICB2YXJpYW50ID09PSAnZXJyb3InXG4gICAgICAgICAgPyB7XG4gICAgICAgICAgICAgIGJvcmRlckNvbG9yOiAnZmVlZGJhY2stZXJyb3InLFxuICAgICAgICAgICAgICBib3hTaGFkb3c6IGBpbnNldCAwIDAgMCAxcHggJHt0aGVtZS5jb2xvcnNbJ2ZlZWRiYWNrLWVycm9yJ119YCxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICA6IGZvcm1GaWVsZEZvY3VzU3R5bGVzLFxuICAgIH0pXG4pO1xuIl19 */");
@@ -0,0 +1,13 @@
1
+ import type { InputWrapperProps } from '../../Form/inputs/Input';
2
+ /**
3
+ * Props for DatePickerInput. When used inside DatePicker, only overrides (e.g. placeholder, label).
4
+ * In range mode, use rangePart to bind to start or end date. When outside DatePicker, pass value, onChange, etc.
5
+ */
6
+ export type DatePickerInputProps = Omit<InputWrapperProps, 'className' | 'type' | 'icon' | 'value' | 'onChange' | 'color'> & {
7
+ /** In range mode: which part of the range this input edits. Omit for single-date or combined display. */
8
+ rangePart?: 'start' | 'end';
9
+ };
10
+ export declare const DatePickerInput: import("react").ForwardRefExoticComponent<Omit<InputWrapperProps, "color" | "className" | "onChange" | "type" | "icon" | "value"> & {
11
+ /** In range mode: which part of the range this input edits. Omit for single-date or combined display. */
12
+ rangePart?: "start" | "end";
13
+ } & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,187 @@
1
+ import { MiniCalendarIcon } from '@codecademy/gamut-icons';
2
+ import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
3
+ import { FlexBox } from '../../Box';
4
+ import { FormGroup } from '../../Form/elements/FormGroup';
5
+ import { useDatePicker } from '../DatePickerContext';
6
+ import { SegmentedShell } from './elements';
7
+ import { DatePickerInputSegment, getDateSegmentsFromDate, normalizeSegmentValues, parseSegmentsToDate, SegmentLiteral } from './Segment';
8
+ import { formatDateISO8601DateOnly, getDateFieldOrder, getDateFormatLayout } from './utils';
9
+
10
+ /**
11
+ * Props for DatePickerInput. When used inside DatePicker, only overrides (e.g. placeholder, label).
12
+ * In range mode, use rangePart to bind to start or end date. When outside DatePicker, pass value, onChange, etc.
13
+ */
14
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
+ export const DatePickerInput = /*#__PURE__*/forwardRef(({
16
+ disabled,
17
+ error,
18
+ form,
19
+ label,
20
+ name,
21
+ rangePart,
22
+ size = 'base',
23
+ ...rest
24
+ }, ref) => {
25
+ const context = useDatePicker();
26
+ if (context == null) {
27
+ throw new Error('DatePickerInput must be used inside a DatePicker (it reads shared state from context).');
28
+ }
29
+ const {
30
+ mode,
31
+ startOrSelectedDate,
32
+ setSelection,
33
+ openCalendar,
34
+ focusCalendarGrid,
35
+ locale,
36
+ isCalendarOpen,
37
+ translations
38
+ } = context;
39
+ const isRange = mode === 'range';
40
+ const endDate = isRange ? context.endDate : null;
41
+ const inputID = useId();
42
+ const inputId = `datepicker-input-${inputID.replace(/:/g, '')}`;
43
+ const {
44
+ layout,
45
+ fieldOrder
46
+ } = useMemo(() => {
47
+ const layout = getDateFormatLayout(locale);
48
+ return {
49
+ layout,
50
+ fieldOrder: getDateFieldOrder(layout)
51
+ };
52
+ }, [locale]);
53
+ const firstField = fieldOrder[0];
54
+ const firstFieldId = `${inputId}-${firstField}`;
55
+ const defaultLabel = !isRange ? translations.dateLabel : rangePart === 'end' ? translations.endDateLabel : translations.startDateLabel;
56
+ const boundDate = isRange && rangePart === 'end' ? endDate : startOrSelectedDate;
57
+ const segmentsFromBound = useCallback(() => getDateSegmentsFromDate(boundDate), [boundDate]);
58
+ const [segments, setSegments] = useState(segmentsFromBound);
59
+ const parsedForHidden = parseSegmentsToDate(segments);
60
+ const hiddenValue = parsedForHidden ? formatDateISO8601DateOnly(parsedForHidden) : '';
61
+ const isInputFocusedRef = useRef(false);
62
+ const containerRef = useRef(null);
63
+ const setShellRef = useCallback(el => {
64
+ containerRef.current = el;
65
+ if (typeof ref === 'function') ref(el);else if (ref != null) ref.current = el;
66
+ }, [ref]);
67
+ useEffect(() => {
68
+ if (!isInputFocusedRef.current) {
69
+ setSegments(segmentsFromBound());
70
+ }
71
+ }, [segmentsFromBound]);
72
+ const commitParsedDate = useCallback(parsed => {
73
+ if (isRange && rangePart) {
74
+ if (rangePart === 'start') setSelection(parsed, endDate);else setSelection(startOrSelectedDate, parsed);
75
+ } else setSelection(parsed);
76
+ }, [isRange, rangePart, setSelection, endDate, startOrSelectedDate]);
77
+ const clearSelection = useCallback(() => {
78
+ if (isRange && rangePart) {
79
+ if (rangePart === 'start') setSelection(null, endDate);else setSelection(startOrSelectedDate, null);
80
+ } else setSelection(null);
81
+ }, [isRange, rangePart, setSelection, endDate, startOrSelectedDate]);
82
+ const applySegments = useCallback(next => {
83
+ const parsed = parseSegmentsToDate(next);
84
+ if (parsed) commitParsedDate(parsed);else if (!next.month && !next.day && !next.year) clearSelection();
85
+ }, [clearSelection, commitParsedDate]);
86
+ const handleContainerBlur = e => {
87
+ if (containerRef.current?.contains(e.relatedTarget)) return;
88
+ isInputFocusedRef.current = false;
89
+ setSegments(prev => {
90
+ const normalized = normalizeSegmentValues(prev);
91
+ const parsed = parseSegmentsToDate(normalized);
92
+ if (parsed) {
93
+ commitParsedDate(parsed);
94
+ return normalized;
95
+ }
96
+ if (!normalized.month && !normalized.day && !normalized.year) {
97
+ clearSelection();
98
+ return getDateSegmentsFromDate(null);
99
+ }
100
+ return segmentsFromBound();
101
+ });
102
+ };
103
+ const handleContainerFocus = () => {
104
+ isInputFocusedRef.current = true;
105
+ };
106
+ const handleSegmentFocus = () => {
107
+ handleContainerFocus();
108
+ if (isRange && rangePart) context.setActiveRangePart(rangePart);
109
+ };
110
+ const handleOpenCalendar = () => {
111
+ if (!disabled) openCalendar({
112
+ moveFocusIntoCalendar: false
113
+ });
114
+ };
115
+ const focusOrOpenCalendarGrid = () => {
116
+ if (isCalendarOpen) focusCalendarGrid();else openCalendar({
117
+ moveFocusIntoCalendar: true
118
+ });
119
+ };
120
+ return /*#__PURE__*/_jsx(FormGroup, {
121
+ htmlFor: firstFieldId,
122
+ isSoloField: true,
123
+ label: label ?? defaultLabel,
124
+ mb: 0,
125
+ pb: 0,
126
+ spacing: "tight",
127
+ width: "fit-content",
128
+ children: /*#__PURE__*/_jsxs(SegmentedShell, {
129
+ inputSize: size === 'small' ? 'small' : 'base',
130
+ ref: setShellRef,
131
+ role: "group",
132
+ variant: error ? 'error' : undefined,
133
+ width: "113px",
134
+ onBlur: handleContainerBlur,
135
+ onFocus: handleContainerFocus,
136
+ ...rest,
137
+ children: [/*#__PURE__*/_jsx(FlexBox, {
138
+ alignItems: "center",
139
+ justifyContent: "center",
140
+ children: layout.map((item, index) => {
141
+ if (item.kind === 'literal') {
142
+ return /*#__PURE__*/_jsx(SegmentLiteral, {
143
+ "aria-hidden": true
144
+ // eslint-disable-next-line react/no-array-index-key
145
+ ,
146
+ children: `${item.text}`
147
+ }, `literal-${item.text}-${index}`);
148
+ }
149
+ const idx = fieldOrder.indexOf(item.field);
150
+ const prevField = idx > 0 ? fieldOrder[idx - 1] : null;
151
+ const nextField = idx < fieldOrder.length - 1 ? fieldOrder[idx + 1] : null;
152
+ return /*#__PURE__*/_jsx(DatePickerInputSegment, {
153
+ applySegments: applySegments,
154
+ disabled: Boolean(disabled),
155
+ error: Boolean(error),
156
+ field: item.field,
157
+ focusOrOpenCalendarGrid: focusOrOpenCalendarGrid,
158
+ handleOnClick: handleOpenCalendar,
159
+ handleOnFocus: handleSegmentFocus,
160
+ nextField: nextField,
161
+ prevField: prevField,
162
+ segments: segments,
163
+ setSegments: setSegments
164
+ }, item.field);
165
+ })
166
+ }), /*#__PURE__*/_jsx("input", {
167
+ "aria-hidden": true,
168
+ form: form,
169
+ name: name,
170
+ tabIndex: -1,
171
+ type: "hidden",
172
+ value: hiddenValue
173
+ }), /*#__PURE__*/_jsx(FlexBox, {
174
+ alignItems: "center",
175
+ justifyContent: "center",
176
+ pl: 16,
177
+ pr: 8,
178
+ role: "presentation",
179
+ onClick: handleOpenCalendar,
180
+ children: /*#__PURE__*/_jsx(MiniCalendarIcon, {
181
+ "aria-hidden": true,
182
+ size: 16
183
+ })
184
+ })]
185
+ })
186
+ });
187
+ });
@@ -0,0 +1,17 @@
1
+ /** Single date field in locale order (from `Intl.DateTimeFormat#formatToParts`). */
2
+ export type DatePartKind = 'month' | 'day' | 'year';
3
+ export type DateFormatLayoutItem = {
4
+ kind: 'field';
5
+ field: DatePartKind;
6
+ } | {
7
+ kind: 'literal';
8
+ text: string;
9
+ };
10
+ /**
11
+ * Month/day/year order and literal separators for the locale (e.g. MM/DD/YYYY vs DD/MM/YYYY).
12
+ */
13
+ export declare const getDateFormatLayout: (locale: Intl.Locale) => DateFormatLayoutItem[];
14
+ /** Focus / tab order for the three fields (locale order). */
15
+ export declare const getDateFieldOrder: (layout: DateFormatLayoutItem[]) => DatePartKind[];
16
+ /** ISO 8601 date-only string for hidden form fields. */
17
+ export declare const formatDateISO8601DateOnly: (date: Date) => string;
@@ -0,0 +1,50 @@
1
+ import { stringifyLocale } from '../utils/locale';
2
+
3
+ /** Single date field in locale order (from `Intl.DateTimeFormat#formatToParts`). */
4
+
5
+ /**
6
+ * Month/day/year order and literal separators for the locale (e.g. MM/DD/YYYY vs DD/MM/YYYY).
7
+ */
8
+ export const getDateFormatLayout = locale => {
9
+ const parts = new Intl.DateTimeFormat(stringifyLocale(locale), {
10
+ year: 'numeric',
11
+ month: '2-digit',
12
+ day: '2-digit'
13
+ }).formatToParts(new Date(2025, 10, 15));
14
+ const items = [];
15
+ for (const part of parts) {
16
+ if (part.type === 'month') items.push({
17
+ kind: 'field',
18
+ field: 'month'
19
+ });else if (part.type === 'day') items.push({
20
+ kind: 'field',
21
+ field: 'day'
22
+ });else if (part.type === 'year') items.push({
23
+ kind: 'field',
24
+ field: 'year'
25
+ });else if (part.type === 'literal') items.push({
26
+ kind: 'literal',
27
+ text: part.value
28
+ });
29
+ }
30
+ return items;
31
+ };
32
+
33
+ /** Focus / tab order for the three fields (locale order). */
34
+ export const getDateFieldOrder = layout => {
35
+ const order = [];
36
+ for (const item of layout) {
37
+ if (item.kind === 'field' && !order.includes(item.field)) {
38
+ order.push(item.field);
39
+ }
40
+ }
41
+ return order.length === 3 ? order : ['month', 'day', 'year'];
42
+ };
43
+
44
+ /** ISO 8601 date-only string for hidden form fields. */
45
+ export const formatDateISO8601DateOnly = date => {
46
+ const y = date.getFullYear();
47
+ const m = date.getMonth() + 1;
48
+ const d = date.getDate();
49
+ return `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
50
+ };
@@ -0,0 +1,5 @@
1
+ export * from './DatePicker';
2
+ export * from './DatePickerContext';
3
+ export * from './DatePickerCalendar';
4
+ export * from './DatePickerInput';
5
+ export type { IsoWeekday } from './utils/locale';
@@ -0,0 +1,5 @@
1
+ export * from './DatePicker';
2
+ export * from './DatePickerContext';
3
+ export * from './DatePickerCalendar';
4
+ export * from './DatePickerInput';
5
+ export {};
@@ -0,0 +1,86 @@
1
+ import { ComponentProps } from 'react';
2
+ import { Input } from '../Form/inputs/Input';
3
+ import { CalendarBaseProps } from './Calendar/types';
4
+ import { DatePickerTranslations } from './utils/translations';
5
+ export interface DatePickerBaseProps extends Pick<CalendarBaseProps, 'locale' | 'disabledDates'> {
6
+ /** When provided, only the provider is rendered and children compose Input + Calendar. */
7
+ children?: React.ReactNode;
8
+ /** Placeholder for the input. */
9
+ placeholder?: string;
10
+ /** Override UI strings (e.g. clear button). Merged with defaults. */
11
+ translations?: DatePickerTranslations;
12
+ inputSize?: ComponentProps<typeof Input>['size'];
13
+ }
14
+ export interface DatePickerSingleProps extends DatePickerBaseProps {
15
+ mode?: 'single';
16
+ /** Controlled selected date. */
17
+ selectedDate: Date | null;
18
+ /** Called when the user selects a date. */
19
+ setSelectedDate: (date: Date | null) => void;
20
+ /** Label for the input. */
21
+ label?: string;
22
+ }
23
+ export interface DatePickerRangeProps extends DatePickerBaseProps {
24
+ mode: 'range';
25
+ /** Controlled start date. */
26
+ startDate: Date | null;
27
+ /** Controlled end date. */
28
+ endDate: Date | null;
29
+ /** Called when the user changes the start date. */
30
+ setStartDate: (date: Date | null) => void;
31
+ /** Called when the user changes the end date. */
32
+ setEndDate: (date: Date | null) => void;
33
+ /** Label for the start date input. */
34
+ startLabel?: string;
35
+ /** Label for the end date input. */
36
+ endLabel?: string;
37
+ }
38
+ export type DatePickerProps = DatePickerSingleProps | DatePickerRangeProps;
39
+ export type OpenCalendarOptions = {
40
+ /**
41
+ * When true, move DOM focus into the date grid after open (keyboard / explicit request).
42
+ * When false (default), keep focus on the input so pointer users can type (WCAG 3.2.1).
43
+ */
44
+ moveFocusIntoCalendar?: boolean;
45
+ };
46
+ export interface DatePickerBaseContextValue extends Pick<CalendarBaseProps, 'disabledDates'> {
47
+ /**
48
+ * Resolved `Intl.Locale` from the `locale` prop (or runtime default). Same instance passed to
49
+ * formatters and available for `getWeekInfo()` etc.
50
+ */
51
+ locale: Intl.Locale;
52
+ isCalendarOpen: boolean;
53
+ openCalendar: (options?: OpenCalendarOptions) => void;
54
+ /** Move focus from the input into the grid when the calendar is already open (e.g. ArrowDown). */
55
+ focusCalendarGrid: () => void;
56
+ /**
57
+ * Flips on each grid focus request so `CalendarBody` effects re-run when `focusTarget` is unchanged.
58
+ * Not a semantic true/false — only the change matters; pair with `gridFocusRequested`.
59
+ */
60
+ focusGridSignal: boolean;
61
+ /** When true, `CalendarBody` runs a one-shot move of DOM focus into the grid if it is not already there. */
62
+ gridFocusRequested: boolean;
63
+ /** Clears `gridFocusRequested` after focus has moved into the grid (or call when closing). */
64
+ clearGridFocusRequest: () => void;
65
+ closeCalendar: () => void;
66
+ calendarDialogId: string;
67
+ /** UI string overrides (e.g. clear button). */
68
+ translations: Required<DatePickerTranslations>;
69
+ /** Start date (range) or selected date (single). */
70
+ startOrSelectedDate: Date | null;
71
+ /** Set selection. Single: (date). Range: (start, end). */
72
+ setSelection: (startOrSelectedDate: Date | null, endDate?: Date | null) => void;
73
+ }
74
+ export interface DatePickerSingleContextValue extends DatePickerBaseContextValue {
75
+ mode: 'single';
76
+ }
77
+ export type ActiveRangePart = 'start' | 'end' | null;
78
+ export interface DatePickerRangeContextValue extends DatePickerBaseContextValue {
79
+ mode: 'range';
80
+ endDate: Date | null;
81
+ /** Which input is active (start/end focused); null = selection mode. */
82
+ activeRangePart: ActiveRangePart;
83
+ /** Set which input is active (e.g. when input receives focus). */
84
+ setActiveRangePart: (part: ActiveRangePart) => void;
85
+ }
86
+ export type DatePickerContextValue = DatePickerSingleContextValue | DatePickerRangeContextValue;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import type { DatePickerBaseContextValue, DatePickerProps, DatePickerRangeContextValue, DatePickerRangeProps } from '../types';
2
+ export declare const isRangeProps: (props: DatePickerProps) => props is DatePickerRangeProps;
3
+ export type RangeContainsDisabledParams = {
4
+ start: Date;
5
+ end: Date;
6
+ disabledDates: Date[];
7
+ };
8
+ /** True if any disabled date falls within [start, end] (inclusive, by calendar day). */
9
+ export declare const rangeContainsDisabled: ({ start, end, disabledDates, }: RangeContainsDisabledParams) => boolean;
10
+ export type HandleDateSelectSingleParams = {
11
+ date: Date;
12
+ selectedDate: DatePickerBaseContextValue['startOrSelectedDate'];
13
+ } & Pick<DatePickerBaseContextValue, 'setSelection'>;
14
+ export declare const handleDateSelectSingle: ({ date, selectedDate, setSelection, }: HandleDateSelectSingleParams) => void;
15
+ export type HandleDateSelectRangeParams = {
16
+ date: Date;
17
+ startDate: DatePickerRangeContextValue['startOrSelectedDate'];
18
+ } & Pick<DatePickerRangeContextValue, 'activeRangePart' | 'endDate' | 'setSelection' | 'disabledDates'>;
19
+ export declare const handleDateSelectRange: ({ date, activeRangePart, startDate, endDate, setSelection, disabledDates, }: HandleDateSelectRangeParams) => void;
@@ -0,0 +1,146 @@
1
+ import { isDateInRange, isSameDay } from '../Calendar/utils/dateGrid';
2
+ export const isRangeProps = props => props.mode === 'range';
3
+ /** True if any disabled date falls within [start, end] (inclusive, by calendar day). */
4
+ export const rangeContainsDisabled = ({
5
+ start,
6
+ end,
7
+ disabledDates
8
+ }) => {
9
+ return disabledDates.some(date => isSameDay(date, start) || isSameDay(date, end) || isDateInRange(date, start, end));
10
+ };
11
+ export const handleDateSelectSingle = ({
12
+ date,
13
+ selectedDate,
14
+ setSelection
15
+ }) => {
16
+ // If clicked date is the same as Start Date: Clear Start Date
17
+ if (selectedDate && date.getTime() === selectedDate.getTime()) {
18
+ setSelection(null);
19
+ return;
20
+ }
21
+ // If clicked date is not the same as Start Date: Set Start Date to clicked date
22
+ setSelection(date);
23
+ };
24
+ const applyRangeOrNewStart = ({
25
+ start,
26
+ end,
27
+ clickedDate,
28
+ disabledDates,
29
+ setSelection
30
+ }) => {
31
+ // if range contains disabled dates, set start date to clicked date and end date to null
32
+ if (rangeContainsDisabled({
33
+ start,
34
+ end,
35
+ disabledDates
36
+ })) {
37
+ setSelection(clickedDate, null);
38
+ } else {
39
+ setSelection(start, end);
40
+ }
41
+ };
42
+ export const handleDateSelectRange = ({
43
+ date,
44
+ activeRangePart,
45
+ startDate,
46
+ endDate,
47
+ setSelection,
48
+ disabledDates = []
49
+ }) => {
50
+ // Range mode: field targeting (start or end input was focused)
51
+ if (activeRangePart === 'start') {
52
+ if (date.getTime() === startDate?.getTime()) {
53
+ setSelection(null, endDate);
54
+ return;
55
+ }
56
+ const newEnd = endDate != null && date.getTime() <= endDate.getTime() ? endDate : null;
57
+ if (newEnd != null) {
58
+ applyRangeOrNewStart({
59
+ start: date,
60
+ end: newEnd,
61
+ clickedDate: date,
62
+ disabledDates,
63
+ setSelection
64
+ });
65
+ } else {
66
+ setSelection(date, newEnd);
67
+ }
68
+ return;
69
+ }
70
+ if (activeRangePart === 'end') {
71
+ if (date.getTime() === endDate?.getTime()) {
72
+ setSelection(startDate, null);
73
+ return;
74
+ }
75
+ const newStart = startDate != null && date.getTime() >= startDate.getTime() ? startDate : null;
76
+ if (newStart != null) {
77
+ applyRangeOrNewStart({
78
+ start: newStart,
79
+ end: date,
80
+ clickedDate: date,
81
+ disabledDates,
82
+ setSelection
83
+ });
84
+ } else {
85
+ setSelection(newStart, date);
86
+ }
87
+ return;
88
+ }
89
+
90
+ // Range selection mode (no field focused: calendar drives both)
91
+ if (startDate && endDate) {
92
+ // if start date is end date and is clicked, clears everything
93
+ if (startDate.getTime() === endDate.getTime() && date.getTime() === startDate.getTime()) {
94
+ setSelection(null, null);
95
+ return;
96
+ }
97
+ // if clicked on start date, end date becomes start date
98
+ if (date.getTime() === startDate.getTime()) {
99
+ setSelection(endDate, null);
100
+ return;
101
+ }
102
+ // if clicked on end date, clears end date and start remains
103
+ if (date.getTime() === endDate.getTime()) {
104
+ setSelection(startDate, null);
105
+ return;
106
+ }
107
+ // If clicked date > Start: Updates End Date to new date (Start remains)
108
+ if (date.getTime() > startDate.getTime()) {
109
+ applyRangeOrNewStart({
110
+ start: startDate,
111
+ end: date,
112
+ clickedDate: date,
113
+ disabledDates,
114
+ setSelection
115
+ });
116
+ return;
117
+ }
118
+ // If clicked date < Start: Updates Start Date to new date (End remains) - extends range to the left
119
+ applyRangeOrNewStart({
120
+ start: date,
121
+ end: endDate,
122
+ clickedDate: date,
123
+ disabledDates,
124
+ setSelection
125
+ });
126
+ return;
127
+ }
128
+ // Start is Set, End is Empty
129
+ if (startDate && !endDate) {
130
+ // If clicked date < Start: Restarts selection with clicked date as new Start
131
+ if (date.getTime() < startDate.getTime()) {
132
+ setSelection(date, null);
133
+ } else {
134
+ // If clicked date > Start: Sets it as End Date (if range valid)
135
+ applyRangeOrNewStart({
136
+ start: startDate,
137
+ end: date,
138
+ clickedDate: date,
139
+ disabledDates,
140
+ setSelection
141
+ });
142
+ }
143
+ return;
144
+ }
145
+ setSelection(date, null);
146
+ };
@@ -0,0 +1,38 @@
1
+ import '@formatjs/intl-locale/polyfill.js';
2
+ /**
3
+ * The runtime default locale string (user agent), matching what `Intl` uses when no locale is passed.
4
+ */
5
+ export declare const getDefaultLocaleTag: () => string;
6
+ /**
7
+ * Resolves `Intl.LocalesArgument` (or `undefined`) to a stable `Intl.Locale` instance for formatting
8
+ * and locale metadata (e.g. `getWeekInfo()` in supporting environments).
9
+ *
10
+ * - `undefined` → default runtime locale via {@link getDefaultLocaleTag}
11
+ * - `Intl.Locale` → returned as-is (no duplicate allocation)
12
+ * - `string` / `readonly string[]` → `new Intl.Locale(...)`
13
+ */
14
+ export declare const resolveLocale: (locales?: Intl.LocalesArgument) => Intl.Locale;
15
+ /**
16
+ * Memoized {@link resolveLocale} for calendar subcomponents. Pass the same `locale` prop you accept
17
+ * from `CalendarBaseProps` (optional `Intl.LocalesArgument`).
18
+ */
19
+ export declare const useResolvedLocale: (locale?: Intl.LocalesArgument) => Intl.Locale;
20
+ /**
21
+ * Convert an Intl.Locale to a string. This is necessary the Intl.DateTimeFormat constructor only accepts a string in some versions of TS.
22
+ * @param locale - The Intl.Locale to convert to a string.
23
+ * @returns The stringified locale.
24
+ */
25
+ export declare const stringifyLocale: (locale: Intl.Locale) => string;
26
+ /** ISO weekday: 1 = Monday … 7 = Sunday (matches `Intl.Locale#getWeekInfo().firstDay`). */
27
+ export type IsoWeekday = 1 | 2 | 3 | 4 | 5 | 6 | 7;
28
+ /**
29
+ * First calendar column weekday from `locale` (via `getWeekInfo()`), or explicit override.
30
+ * - `weekStartsOnOverride` — ISO weekday **1–7** (Monday … Sunday), same as `getWeekInfo().firstDay`
31
+ * - omitted → `locale.getWeekInfo().firstDay` when available, else **7** (Sunday)
32
+ */
33
+ export declare const getIsoFirstDayFromLocale: (locale: Intl.Locale, weekStartsOnOverride?: IsoWeekday) => IsoWeekday;
34
+ /**
35
+ * Hook: resolved first weekday for the calendar grid. Re-reads after mount so async polyfills
36
+ * (e.g. Firefox) can install `getWeekInfo` before the first paint in some bundles.
37
+ */
38
+ export declare const useIsoFirstWeekday: (locale: Intl.Locale, weekStartsOnOverride?: IsoWeekday) => IsoWeekday;