@servicetitan/anvil2 1.42.2 → 1.43.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 (53) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/{Combobox-D2aSaDkz.js → Combobox-C7ANSGIu.js} +2 -2
  3. package/dist/{Combobox-D2aSaDkz.js.map → Combobox-C7ANSGIu.js.map} +1 -1
  4. package/dist/Combobox.js +1 -1
  5. package/dist/{DateField-D28_sa7P.js → DateField-_TqCdcYv.js} +4 -3
  6. package/dist/{DateField-D28_sa7P.js.map → DateField-_TqCdcYv.js.map} +1 -1
  7. package/dist/DateField.js +1 -1
  8. package/dist/{DateFieldRange--oSGfjYa.js → DateFieldRange-Dk8WA52F.js} +2 -2
  9. package/dist/{DateFieldRange--oSGfjYa.js.map → DateFieldRange-Dk8WA52F.js.map} +1 -1
  10. package/dist/DateFieldRange.js +1 -1
  11. package/dist/{DateFieldSingle-0a8Bk7Yj.js → DateFieldSingle-D3xD8YZk.js} +2 -2
  12. package/dist/{DateFieldSingle-0a8Bk7Yj.js.map → DateFieldSingle-D3xD8YZk.js.map} +1 -1
  13. package/dist/DateFieldSingle.js +1 -1
  14. package/dist/{DateFieldYearless-DCv9WJdu.js → DateFieldYearless-BxkCSNk5.js} +2 -2
  15. package/dist/{DateFieldYearless-DCv9WJdu.js.map → DateFieldYearless-BxkCSNk5.js.map} +1 -1
  16. package/dist/DateFieldYearless-CAUHW6Ow-EUWxJ0OY.js +1431 -0
  17. package/dist/DateFieldYearless-CAUHW6Ow-EUWxJ0OY.js.map +1 -0
  18. package/dist/DateFieldYearless.js +1 -1
  19. package/dist/{Page-KN0DLtcf.js → Page-CxB5N9dR.js} +36 -36
  20. package/dist/{Page-KN0DLtcf.js.map → Page-CxB5N9dR.js.map} +1 -1
  21. package/dist/Page.css +72 -72
  22. package/dist/Page.js +1 -1
  23. package/dist/{Popover-B1HaUjGI.js → Popover-BPiqdyu1.js} +2 -2
  24. package/dist/{Popover-B1HaUjGI.js.map → Popover-BPiqdyu1.js.map} +1 -1
  25. package/dist/{Popover-CU2cGVD8-FWJOuFRj.js → Popover-CU2cGVD8-Casl3vM1.js} +2 -2
  26. package/dist/{Popover-CU2cGVD8-FWJOuFRj.js.map → Popover-CU2cGVD8-Casl3vM1.js.map} +1 -1
  27. package/dist/Popover.js +1 -1
  28. package/dist/TimeField-DRHLRqN3.js +913 -0
  29. package/dist/TimeField-DRHLRqN3.js.map +1 -0
  30. package/dist/TimeField.css +10 -0
  31. package/dist/TimeField.d.ts +2 -0
  32. package/dist/TimeField.js +2 -0
  33. package/dist/TimeField.js.map +1 -0
  34. package/dist/{Toolbar-BznMJKGJ.js → Toolbar-CSWhVSUM.js} +2 -2
  35. package/dist/{Toolbar-BznMJKGJ.js.map → Toolbar-CSWhVSUM.js.map} +1 -1
  36. package/dist/Toolbar.js +1 -1
  37. package/dist/assets/icons/st/gnav_field_pro_active.svg +1 -1
  38. package/dist/assets/icons/st/gnav_field_pro_inactive.svg +1 -1
  39. package/dist/components/TimeField/TimeField.d.ts +53 -0
  40. package/dist/components/TimeField/index.d.ts +1 -0
  41. package/dist/components/index.d.ts +1 -0
  42. package/dist/event-BEJFimi3.js +6 -0
  43. package/dist/event-BEJFimi3.js.map +1 -0
  44. package/dist/index.js +9 -8
  45. package/dist/index.js.map +1 -1
  46. package/dist/usePopoverCloseDelayWorkaround-BZcjPkvT-BZcjPkvT.js +18 -0
  47. package/dist/usePopoverCloseDelayWorkaround-BZcjPkvT-BZcjPkvT.js.map +1 -0
  48. package/dist/{DateFieldYearless-C3_oGDr3-5meexzZO.js → usePopoverSupport-B9Lsqryr-DhZHMoNb.js} +470 -1429
  49. package/dist/usePopoverSupport-B9Lsqryr-DhZHMoNb.js.map +1 -0
  50. package/package.json +4 -4
  51. package/dist/DateFieldYearless-C3_oGDr3-5meexzZO.js.map +0 -1
  52. package/dist/usePopoverCloseDelayWorkaround-BhhG-xEB-hfJZaXHC.js +0 -21
  53. package/dist/usePopoverCloseDelayWorkaround-BhhG-xEB-hfJZaXHC.js.map +0 -1
@@ -0,0 +1,1431 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { u as usePopoverSupport, a as useMaskito, m as maskitoDateRangeOptionsGenerator, b as maskitoWithPlaceholder, c as maskitoParseDate, d as maskitoDateOptionsGenerator } from './usePopoverSupport-B9Lsqryr-DhZHMoNb.js';
3
+ import { T as TextField } from './TextField-CRTh0gL_-D2CjcYXX.js';
4
+ import { useRef, useState, useMemo, forwardRef, useCallback, useEffect, useImperativeHandle } from 'react';
5
+ import { u as useMergeRefs$1 } from './floating-ui.react-BFNinq1w.js';
6
+ import { D as DateTime, f as Calendar } from './Calendar-DD5kmVd3-CBGTR11R.js';
7
+ import { I as Icon } from './Icon-B6HmlQiR-BxQkO3X5.js';
8
+ import { S as SvgEvent } from './event-BEJFimi3.js';
9
+ import { P as Popover } from './Popover-CU2cGVD8-Casl3vM1.js';
10
+ import { u as useOptionallyControlledState } from './useOptionallyControlledState-DAv5LXXh-DAv5LXXh.js';
11
+ import { u as useMergeRefs } from './useMergeRefs-Bde85AWI-Bde85AWI.js';
12
+ import { u as usePopoverCloseDelayWorkaround } from './usePopoverCloseDelayWorkaround-BZcjPkvT-BZcjPkvT.js';
13
+ import { u as useKeyboardFocusables } from './ProgressBar-BRvB-bD4-DppwBrFg.js';
14
+
15
+ function usePrevious(value) {
16
+ const ref = useRef();
17
+ useEffect(() => {
18
+ ref.current = value;
19
+ }, [value]);
20
+ return ref.current;
21
+ }
22
+
23
+ const makeZeroShortcutPreprocessor = (templateString, separator) => {
24
+ const zeroShortcutPreprocessor = ({ elementState, data }, actionType) => {
25
+ if (actionType === "insert" && // the user is inserting a character
26
+ data === separator && // the user typed the separator character
27
+ elementState.selection[0] === elementState.selection[1]) {
28
+ const selectionIndex = elementState.selection[0];
29
+ const separatorPositions = templateString.split("").map((char, index) => char === separator ? index : null).filter((position) => position !== null).filter((position) => position > selectionIndex);
30
+ const noRemainingSegments = !separatorPositions.length;
31
+ const nothingEnteredYet = !elementState.value.length;
32
+ const previousCharacterIsNotADigit = !/^\d$/.test(
33
+ elementState.value.slice(-1)
34
+ );
35
+ const currentCharacterIsSeparator = templateString[selectionIndex] === separator;
36
+ if (noRemainingSegments || nothingEnteredYet || previousCharacterIsNotADigit || currentCharacterIsSeparator) {
37
+ return { elementState, data };
38
+ }
39
+ const firstIndexOfSegment = Math.max(
40
+ elementState.value.lastIndexOf(separator) + 1,
41
+ elementState.value.lastIndexOf(" ") + 1,
42
+ 0
43
+ );
44
+ const lastIndexOfSegment = separatorPositions[0];
45
+ const digitsCurrentlyInSegment = elementState.value.slice(firstIndexOfSegment);
46
+ const targetNumberOfDigitsInSegment = templateString.slice(
47
+ firstIndexOfSegment,
48
+ lastIndexOfSegment
49
+ ).length;
50
+ const newSegment = digitsCurrentlyInSegment.padStart(
51
+ targetNumberOfDigitsInSegment,
52
+ "0"
53
+ );
54
+ const newValue = `${elementState.value.slice(0, firstIndexOfSegment)}${newSegment}`;
55
+ return {
56
+ elementState: {
57
+ value: newValue + data,
58
+ selection: [newValue.length, newValue.length]
59
+ },
60
+ data
61
+ };
62
+ }
63
+ return { elementState, data };
64
+ };
65
+ return zeroShortcutPreprocessor;
66
+ };
67
+
68
+ const datePlaceholderMask$1 = ({
69
+ mode,
70
+ separator = "/",
71
+ placeholder
72
+ }) => {
73
+ const dateOptions = maskitoDateOptionsGenerator({
74
+ mode,
75
+ separator
76
+ });
77
+ const { plugins, removePlaceholder, ...placeholderOptions } = maskitoWithPlaceholder(placeholder);
78
+ const datePlaceholderMask2 = {
79
+ ...dateOptions,
80
+ plugins: plugins.concat(dateOptions.plugins || []),
81
+ preprocessors: [
82
+ ...placeholderOptions.preprocessors,
83
+ ...dateOptions.preprocessors,
84
+ makeZeroShortcutPreprocessor(mode, separator)
85
+ ],
86
+ postprocessors: [
87
+ ...dateOptions.postprocessors,
88
+ ...placeholderOptions.postprocessors
89
+ ]
90
+ };
91
+ return { options: datePlaceholderMask2, removePlaceholder };
92
+ };
93
+
94
+ const DateModeToFormatMap = {
95
+ "mm/dd/yyyy": "MM/dd/yyyy",
96
+ "dd/mm/yyyy": "dd/MM/yyyy",
97
+ "yyyy/mm/dd": "yyyy/MM/dd"
98
+ };
99
+ const DateModeToPlaceholderMap = {
100
+ "dd/mm/yyyy": "__/__/____",
101
+ "mm/dd/yyyy": "__/__/____",
102
+ "yyyy/mm/dd": "____/__/__"
103
+ };
104
+
105
+ const MaskedDateInput = forwardRef(
106
+ ({
107
+ onChange,
108
+ mode = "mm/dd/yyyy",
109
+ lastValidDate,
110
+ disableHint = false,
111
+ ...props
112
+ }, ref) => {
113
+ const placeholder = DateModeToPlaceholderMap[mode];
114
+ const [inputValue, setInputValue] = useState(placeholder);
115
+ const { options, removePlaceholder } = datePlaceholderMask$1({
116
+ mode,
117
+ placeholder
118
+ });
119
+ const maskedInputRef = useMaskito({ options });
120
+ const inputRef = useRef(null);
121
+ const combinedRef = useMergeRefs$1([maskedInputRef, inputRef, ref]);
122
+ const previousDateRef = useRef(null);
123
+ const previousMode = usePrevious(mode);
124
+ useEffect(() => {
125
+ if (mode !== previousMode) {
126
+ setInputValue(
127
+ (oldInputValue) => swapMode$2(oldInputValue, previousMode ?? mode, mode)
128
+ );
129
+ }
130
+ }, [mode, previousMode]);
131
+ useEffect(() => {
132
+ if (lastValidDate === void 0) return;
133
+ if (lastValidDate === previousDateRef.current) return;
134
+ if (!lastValidDate?.equals(previousDateRef.current ?? DateTime.now())) {
135
+ setInputValue(
136
+ lastValidDate?.toFormat(DateModeToFormatMap[mode]) ?? placeholder
137
+ );
138
+ previousDateRef.current = lastValidDate;
139
+ }
140
+ }, [lastValidDate, mode, placeholder]);
141
+ const currentParsedData = useMemo(() => {
142
+ return parseInputValue$1(inputValue, mode, removePlaceholder);
143
+ }, [inputValue, mode, removePlaceholder]);
144
+ const handleChange = (event) => {
145
+ setInputValue(event.target.value);
146
+ const { date, isInputValid, isInputEmpty } = parseInputValue$1(
147
+ event.target.value,
148
+ mode,
149
+ removePlaceholder
150
+ );
151
+ const isValueDifferent = isInputValid !== currentParsedData.isInputValid || // The input has changed validity
152
+ isInputEmpty !== currentParsedData.isInputEmpty || // The input has changed emptiness
153
+ date === null !== (lastValidDate === null) || // The input has changed from empty to non-empty or vice versa
154
+ date !== null && lastValidDate !== null && !date.equals(lastValidDate);
155
+ if (!isValueDifferent) return;
156
+ onChange?.({
157
+ date: date ?? lastValidDate ?? null,
158
+ isInputValid,
159
+ isInputEmpty
160
+ });
161
+ };
162
+ useImperativeHandle(ref, () => {
163
+ const input = inputRef.current;
164
+ if (!input) return null;
165
+ return Object.assign(input, {
166
+ setDate: (date) => {
167
+ setInputValue(
168
+ date?.toFormat(DateModeToFormatMap[mode]) ?? placeholder
169
+ );
170
+ }
171
+ });
172
+ }, [mode, placeholder]);
173
+ return /* @__PURE__ */ jsx(
174
+ TextField,
175
+ {
176
+ ref: combinedRef,
177
+ "data-date": lastValidDate?.toISODate() ?? "",
178
+ "data-input-valid": currentParsedData.isInputValid,
179
+ "data-input-empty": currentParsedData.isInputEmpty,
180
+ ...props,
181
+ showCounter: false,
182
+ value: inputValue,
183
+ onChange: handleChange,
184
+ prefix: /* @__PURE__ */ jsx(Icon, { svg: SvgEvent }),
185
+ hint: disableHint ? void 0 : `Format: ${mode}`
186
+ }
187
+ );
188
+ }
189
+ );
190
+ MaskedDateInput.displayName = "MaskedDateInput";
191
+ function parseInputValue$1(value, mode, removePlaceholder) {
192
+ const valueMinusPlaceholder = removePlaceholder(value);
193
+ const jsDate = maskitoParseDate(valueMinusPlaceholder, { mode });
194
+ const luxonDate = jsDate ? DateTime.fromJSDate(jsDate, { zone: "utc" }) : null;
195
+ return {
196
+ date: luxonDate,
197
+ isInputValid: !!luxonDate,
198
+ isInputEmpty: valueMinusPlaceholder === ""
199
+ };
200
+ }
201
+ function swapMode$2(inputString, previousMode, mode) {
202
+ const { day, month, year } = divideSegments$1(inputString, previousMode);
203
+ return orderSegmentsByMode(day, month, year, mode);
204
+ }
205
+ function divideSegments$1(value, mode) {
206
+ const [segment1, segment2, segment3] = value.split("/");
207
+ if (mode === "dd/mm/yyyy") {
208
+ return { day: segment1, month: segment2, year: segment3 };
209
+ } else if (mode === "mm/dd/yyyy") {
210
+ return { day: segment2, month: segment1, year: segment3 };
211
+ } else if (mode === "yyyy/mm/dd") {
212
+ return { day: segment3, month: segment2, year: segment1 };
213
+ }
214
+ return { day: "__", month: "__", year: "____" };
215
+ }
216
+ function orderSegmentsByMode(day, month, year, mode) {
217
+ if (mode === "dd/mm/yyyy") {
218
+ return `${day}/${month}/${year}`;
219
+ } else if (mode === "mm/dd/yyyy") {
220
+ return `${month}/${day}/${year}`;
221
+ } else if (mode === "yyyy/mm/dd") {
222
+ return `${year}/${month}/${day}`;
223
+ } else {
224
+ return "";
225
+ }
226
+ }
227
+
228
+ function containsRelatedTarget(event) {
229
+ if (event.currentTarget instanceof HTMLElement && event.relatedTarget instanceof HTMLElement) {
230
+ return event.currentTarget.contains(event.relatedTarget);
231
+ }
232
+ return false;
233
+ }
234
+ function useFocusWithin({
235
+ onBlur,
236
+ onFocus
237
+ } = {}) {
238
+ const [focused, setFocused] = useState(false);
239
+ const focusedRef = useRef(false);
240
+ const elementRef = useRef(null);
241
+ const _setFocused = useCallback((value) => {
242
+ setFocused(value);
243
+ focusedRef.current = value;
244
+ }, []);
245
+ const handleFocusIn = useCallback(
246
+ (event) => {
247
+ if (!focusedRef.current) {
248
+ _setFocused(true);
249
+ onFocus?.(event);
250
+ }
251
+ },
252
+ // eslint-disable-next-line react-hooks/exhaustive-deps
253
+ [onFocus]
254
+ );
255
+ const handleFocusOut = useCallback(
256
+ (event) => {
257
+ if (focusedRef.current && !containsRelatedTarget(event)) {
258
+ _setFocused(false);
259
+ onBlur?.(event);
260
+ }
261
+ },
262
+ // eslint-disable-next-line react-hooks/exhaustive-deps
263
+ [onBlur]
264
+ );
265
+ useEffect(() => {
266
+ const element = elementRef.current;
267
+ if (!element) {
268
+ return;
269
+ }
270
+ element.addEventListener("focusin", handleFocusIn);
271
+ element.addEventListener("focusout", handleFocusOut);
272
+ return () => {
273
+ element.removeEventListener("focusin", handleFocusIn);
274
+ element.removeEventListener("focusout", handleFocusOut);
275
+ };
276
+ }, [handleFocusIn, handleFocusOut]);
277
+ return { ref: elementRef, focused };
278
+ }
279
+
280
+ function convertStringToDate(v) {
281
+ if (v === void 0 || v === null) {
282
+ return v;
283
+ }
284
+ const date = DateTime.fromISO(v, { setZone: true, zone: "utc" }).startOf(
285
+ "day"
286
+ );
287
+ if (date.isValid) {
288
+ return date;
289
+ }
290
+ return null;
291
+ }
292
+ function validateDate({
293
+ date,
294
+ constraints
295
+ }) {
296
+ const { required, unavailable, minDate, maxDate } = constraints;
297
+ if (!date) {
298
+ return required ? false : true;
299
+ }
300
+ if (unavailable?.dates?.some((d) => d.equals(date))) {
301
+ return false;
302
+ }
303
+ if (unavailable?.daysOfWeek?.includes(date.weekday)) {
304
+ return false;
305
+ }
306
+ if (minDate && date < minDate) {
307
+ return false;
308
+ }
309
+ if (maxDate && date > maxDate) {
310
+ return false;
311
+ }
312
+ return true;
313
+ }
314
+ function validateYearlessDate({
315
+ value,
316
+ constraints
317
+ }) {
318
+ const { required, unavailable, minDate, maxDate } = constraints;
319
+ if (!value || !value?.day && !value?.month) {
320
+ return required ? false : true;
321
+ }
322
+ if (value.day === null || value.month === null) {
323
+ return false;
324
+ }
325
+ if (unavailable?.dates?.some(
326
+ (d) => d.day === value.day && d.month === value.month
327
+ )) {
328
+ return false;
329
+ }
330
+ const minDateMonth = minDate?.month ?? 1;
331
+ const minDateDay = minDate?.day ?? 1;
332
+ const maxDateMonth = maxDate?.month ?? 12;
333
+ const maxDateDay = maxDate?.day ?? 31;
334
+ if (value.month < minDateMonth || // Earlier month
335
+ value.month === minDateMonth && value.day < minDateDay) {
336
+ return false;
337
+ }
338
+ if (value.month > maxDateMonth || // Later month
339
+ value.month === maxDateMonth && value.day > maxDateDay) {
340
+ return false;
341
+ }
342
+ return true;
343
+ }
344
+
345
+ function useDateFieldSingleConversion({
346
+ value,
347
+ defaultValue,
348
+ minDate,
349
+ maxDate,
350
+ unavailable,
351
+ onChange
352
+ }) {
353
+ const normalizedValue = useMemo(() => convertStringToDate(value), [value]);
354
+ const normalizedDefaultValue = useMemo(
355
+ () => convertStringToDate(defaultValue),
356
+ [defaultValue]
357
+ );
358
+ const normalizedMinDate = useMemo(
359
+ () => convertStringToDate(minDate),
360
+ [minDate]
361
+ );
362
+ const normalizedMaxDate = useMemo(
363
+ () => convertStringToDate(maxDate),
364
+ [maxDate]
365
+ );
366
+ const normalizedUnavailableDates = useMemo(
367
+ () => unavailable?.dates?.map((d) => convertStringToDate(d)).filter((d) => d !== null && d !== void 0),
368
+ [unavailable?.dates]
369
+ );
370
+ const handleChange = useCallback(
371
+ (change) => {
372
+ onChange?.({
373
+ ...change,
374
+ date: change.date?.toISODate() ?? null
375
+ });
376
+ },
377
+ [onChange]
378
+ );
379
+ return {
380
+ value: normalizedValue,
381
+ defaultValue: normalizedDefaultValue,
382
+ minDate: normalizedMinDate,
383
+ maxDate: normalizedMaxDate,
384
+ unavailable: {
385
+ dates: normalizedUnavailableDates,
386
+ daysOfWeek: unavailable?.daysOfWeek
387
+ },
388
+ onChange: handleChange
389
+ };
390
+ }
391
+
392
+ function DateFieldSingleCalendar({
393
+ onKeyDown,
394
+ value,
395
+ onSelection,
396
+ minDate,
397
+ maxDate,
398
+ unavailable,
399
+ ...rest
400
+ }) {
401
+ const handleCalendarSelection = (data) => {
402
+ if (data.value) {
403
+ const date = DateTime.fromISO(data.value, { zone: "utc" });
404
+ onSelection(date);
405
+ }
406
+ };
407
+ return /* @__PURE__ */ jsx(
408
+ Calendar,
409
+ {
410
+ ...rest,
411
+ range: false,
412
+ onKeyDown,
413
+ defaultFocusedDate: value?.toISODate() || DateTime.now().toISODate(),
414
+ value: value?.toISODate() || void 0,
415
+ onSelection: handleCalendarSelection,
416
+ defaultTimeZone: "UTC",
417
+ minDate: minDate?.toISODate() ?? void 0,
418
+ maxDate: maxDate?.toISODate() ?? void 0,
419
+ unavailable: unavailable ? {
420
+ dates: unavailable.dates?.map((d) => d.toISODate() ?? ""),
421
+ daysOfWeek: unavailable.daysOfWeek
422
+ } : void 0,
423
+ _disableAutofocus: true
424
+ }
425
+ );
426
+ }
427
+
428
+ const useDateFieldOrchestration = ({
429
+ inputRef,
430
+ calendarDefaultOpen,
431
+ popoverContentRef,
432
+ disableCalendar = false
433
+ }) => {
434
+ const documentRef = useRef(document.body);
435
+ const [calendarOpen, setCalendarOpen] = useState(calendarDefaultOpen);
436
+ const { focusables } = useKeyboardFocusables(documentRef, {
437
+ observeChange: true
438
+ });
439
+ const { focusables: popoverFocusables } = useKeyboardFocusables(
440
+ popoverContentRef,
441
+ {
442
+ observeChange: true
443
+ }
444
+ );
445
+ const pageFocusables = focusables?.filter(
446
+ (item) => !popoverFocusables?.includes(item)
447
+ );
448
+ const handleCalendarKeyDown = (event) => {
449
+ if (event.key === "Escape") {
450
+ inputRef.current?.focus();
451
+ }
452
+ };
453
+ const focusToCalendar = () => {
454
+ if (popoverContentRef.current) {
455
+ const currentFocusable = popoverContentRef.current.querySelectorAll('[tabindex = "0"]')[0];
456
+ if (currentFocusable) {
457
+ currentFocusable.focus();
458
+ }
459
+ }
460
+ };
461
+ const handleInputKeyDown = (ev) => {
462
+ if (disableCalendar) {
463
+ return;
464
+ }
465
+ let currentFocusIndex = 0;
466
+ switch (ev.key) {
467
+ case "Escape":
468
+ setCalendarOpen(false);
469
+ break;
470
+ case "Tab":
471
+ if (!calendarOpen || !pageFocusables?.length) {
472
+ break;
473
+ }
474
+ ev.preventDefault();
475
+ currentFocusIndex = pageFocusables.indexOf(inputRef.current) || 0;
476
+ setCalendarOpen(false);
477
+ if (ev.shiftKey) {
478
+ if (currentFocusIndex === 0) {
479
+ requestAnimationFrame(
480
+ () => pageFocusables[pageFocusables.length - 1].focus()
481
+ );
482
+ } else {
483
+ requestAnimationFrame(
484
+ () => pageFocusables[currentFocusIndex - 1].focus()
485
+ );
486
+ }
487
+ break;
488
+ }
489
+ if (pageFocusables.length > currentFocusIndex + 1) {
490
+ requestAnimationFrame(
491
+ () => pageFocusables[currentFocusIndex + 1].focus()
492
+ );
493
+ } else {
494
+ requestAnimationFrame(() => pageFocusables[0].focus());
495
+ }
496
+ break;
497
+ case "ArrowDown":
498
+ if (!calendarOpen) {
499
+ setCalendarOpen(true);
500
+ setTimeout(focusToCalendar, 200);
501
+ } else {
502
+ focusToCalendar();
503
+ }
504
+ }
505
+ };
506
+ return {
507
+ calendarOpen,
508
+ setCalendarOpen,
509
+ handleCalendarKeyDown,
510
+ handleInputKeyDown
511
+ };
512
+ };
513
+
514
+ function useDateFieldSingleState({
515
+ valueProp,
516
+ defaultValueProp,
517
+ inputRef,
518
+ onChange
519
+ }) {
520
+ const [value, setValue] = useOptionallyControlledState({
521
+ controlledValue: valueProp,
522
+ defaultValue: defaultValueProp
523
+ });
524
+ const setSharedValue = (date) => {
525
+ inputRef.current?.setDate(date);
526
+ setValue(date);
527
+ };
528
+ const handleInputChange = (change) => {
529
+ const date = change.isInputEmpty ? null : change.date?.startOf("day") ?? null;
530
+ onChange?.({
531
+ date,
532
+ isInputValid: change.isInputValid,
533
+ isInputEmpty: change.isInputEmpty
534
+ });
535
+ if (change.isInputValid) {
536
+ setSharedValue(change.isInputEmpty ? null : change.date);
537
+ }
538
+ if (change.isInputEmpty) {
539
+ setSharedValue(null);
540
+ return;
541
+ }
542
+ if (change.date) {
543
+ setSharedValue(change.date);
544
+ }
545
+ };
546
+ const handleCalendarSelection = (date) => {
547
+ setSharedValue(date);
548
+ onChange?.({
549
+ date,
550
+ isInputValid: true,
551
+ isInputEmpty: false
552
+ });
553
+ };
554
+ return {
555
+ value,
556
+ setValue: setSharedValue,
557
+ handleInputChange,
558
+ handleCalendarSelection
559
+ };
560
+ }
561
+
562
+ const DateFieldSingle = ({
563
+ onFocus,
564
+ onBlur,
565
+ disableCalendar,
566
+ required,
567
+ mode,
568
+ value: valueProp,
569
+ defaultValue: defaultValueProp,
570
+ minDate: minDateProp,
571
+ maxDate: maxDateProp,
572
+ unavailable: unavailableProp,
573
+ onChange: onChangeProp,
574
+ ...rest
575
+ }) => {
576
+ const inputRef = useRef(null);
577
+ const {
578
+ value: normalizedValue,
579
+ defaultValue: normalizedDefaultValue,
580
+ minDate,
581
+ maxDate,
582
+ unavailable,
583
+ onChange
584
+ } = useDateFieldSingleConversion({
585
+ value: valueProp,
586
+ defaultValue: defaultValueProp,
587
+ onChange: onChangeProp,
588
+ minDate: minDateProp,
589
+ maxDate: maxDateProp,
590
+ unavailable: unavailableProp
591
+ });
592
+ const handleChange = (change) => {
593
+ onChange?.({
594
+ ...change,
595
+ isDateValid: validateDate({
596
+ date: change.date,
597
+ constraints: {
598
+ required,
599
+ unavailable,
600
+ minDate: minDate ?? void 0,
601
+ maxDate: maxDate ?? void 0
602
+ }
603
+ })
604
+ });
605
+ };
606
+ const { value, handleInputChange, handleCalendarSelection } = useDateFieldSingleState({
607
+ valueProp: normalizedValue,
608
+ defaultValueProp: normalizedDefaultValue,
609
+ inputRef,
610
+ onChange: handleChange
611
+ });
612
+ const { ref: wrapperRef } = useFocusWithin({
613
+ onBlur,
614
+ onFocus
615
+ });
616
+ const [popoverTriggerRef, setPopoverTriggerRef] = useState();
617
+ const popoverContentRef = useRef(null);
618
+ const combinedRef = useMergeRefs([popoverTriggerRef, inputRef]);
619
+ const {
620
+ calendarOpen,
621
+ setCalendarOpen,
622
+ handleCalendarKeyDown,
623
+ handleInputKeyDown
624
+ } = useDateFieldOrchestration({
625
+ inputRef,
626
+ calendarDefaultOpen: false,
627
+ popoverContentRef,
628
+ disableCalendar
629
+ });
630
+ const popoverSupported = usePopoverSupport();
631
+ const shouldShowCalendar = usePopoverCloseDelayWorkaround(calendarOpen);
632
+ const currentValidity = useMemo(
633
+ () => validateDate({
634
+ date: value,
635
+ constraints: {
636
+ required,
637
+ unavailable,
638
+ minDate: minDate ?? void 0,
639
+ maxDate: maxDate ?? void 0
640
+ }
641
+ }),
642
+ [value, required, unavailable, minDate, maxDate]
643
+ );
644
+ const justTheField = /* @__PURE__ */ jsx(
645
+ MaskedDateInput,
646
+ {
647
+ ...rest,
648
+ mode,
649
+ ref: combinedRef,
650
+ onChange: handleInputChange,
651
+ onKeyDown: handleInputKeyDown,
652
+ onClick: () => setCalendarOpen(true),
653
+ lastValidDate: value,
654
+ required,
655
+ autoComplete: "off",
656
+ "data-date-valid": currentValidity
657
+ }
658
+ );
659
+ if (disableCalendar) {
660
+ return justTheField;
661
+ }
662
+ if (!popoverSupported) {
663
+ return justTheField;
664
+ }
665
+ return /* @__PURE__ */ jsx("div", { ref: wrapperRef, children: /* @__PURE__ */ jsxs(
666
+ Popover,
667
+ {
668
+ open: calendarOpen,
669
+ modal: true,
670
+ placement: "bottom-start",
671
+ disableFlipFallback: true,
672
+ disableTriggerFocus: true,
673
+ onClose: () => setCalendarOpen(false),
674
+ disableAutoUpdate: true,
675
+ onClickOutside: () => setCalendarOpen(false),
676
+ children: [
677
+ /* @__PURE__ */ jsx(Popover.Trigger, { children: ({ ref: iRef }) => {
678
+ setPopoverTriggerRef(iRef);
679
+ return justTheField;
680
+ } }),
681
+ /* @__PURE__ */ jsx(Popover.Content, { ref: popoverContentRef, "data-testid": "calendar-popover", children: shouldShowCalendar && /* @__PURE__ */ jsx(
682
+ DateFieldSingleCalendar,
683
+ {
684
+ onKeyDown: handleCalendarKeyDown,
685
+ value,
686
+ onSelection: handleCalendarSelection,
687
+ minDate,
688
+ maxDate,
689
+ unavailable
690
+ }
691
+ ) })
692
+ ]
693
+ }
694
+ ) });
695
+ };
696
+ DateFieldSingle.displayName = "DateFieldSingle";
697
+
698
+ const datePlaceholderMask = ({
699
+ mode,
700
+ dateSeparator = "/",
701
+ rangeSeparator = " - ",
702
+ placeholder
703
+ }) => {
704
+ const dateRangeOptions = maskitoDateRangeOptionsGenerator({
705
+ mode,
706
+ dateSeparator,
707
+ rangeSeparator
708
+ });
709
+ const { plugins, removePlaceholder, ...placeholderOptions } = maskitoWithPlaceholder(placeholder);
710
+ const datePlaceholderMask2 = {
711
+ ...dateRangeOptions,
712
+ plugins: plugins.concat(dateRangeOptions.plugins || []),
713
+ preprocessors: [
714
+ ...placeholderOptions.preprocessors,
715
+ ...dateRangeOptions.preprocessors,
716
+ makeZeroShortcutPreprocessor(placeholder, dateSeparator)
717
+ ],
718
+ postprocessors: [
719
+ // NOTE this is super fragile. If Maskito maintainers change the order of the post-processors, this will break.
720
+ // The last postprocessor is the date swap postprocessor, which we don't want to run.
721
+ // A unit test is added to ensure this doesn't break on a dependency update.
722
+ ...dateRangeOptions.postprocessors.slice(0, -1),
723
+ ...placeholderOptions.postprocessors
724
+ ]
725
+ };
726
+ return { options: datePlaceholderMask2, removePlaceholder };
727
+ };
728
+
729
+ const RANGE_SEPARATOR = " - ";
730
+ const MaskedDateRangeInput = forwardRef(
731
+ ({
732
+ onChange,
733
+ mode = "mm/dd/yyyy",
734
+ startDate,
735
+ endDate,
736
+ disableHint = false,
737
+ ...props
738
+ }, ref) => {
739
+ const halfPlaceholder = DateModeToPlaceholderMap[mode];
740
+ const fullPlaceholder = `${halfPlaceholder}${RANGE_SEPARATOR}${halfPlaceholder}`;
741
+ const [inputValue, setInputValue] = useState(fullPlaceholder);
742
+ const { options, removePlaceholder } = datePlaceholderMask({
743
+ mode,
744
+ placeholder: fullPlaceholder,
745
+ dateSeparator: "/",
746
+ rangeSeparator: RANGE_SEPARATOR
747
+ });
748
+ const maskedInputRef = useMaskito({ options });
749
+ const inputRef = useRef(null);
750
+ const combinedRef = useMergeRefs$1([maskedInputRef, inputRef, ref]);
751
+ const previousStartDate = usePrevious(startDate);
752
+ const previousEndDate = usePrevious(endDate);
753
+ const previousMode = usePrevious(mode);
754
+ useEffect(() => {
755
+ if (mode !== previousMode) {
756
+ setInputValue(
757
+ (previousInputValue) => swapMode$1(previousInputValue, previousMode ?? mode, mode)
758
+ );
759
+ }
760
+ }, [mode, fullPlaceholder, previousMode]);
761
+ useEffect(() => {
762
+ if (startDate === void 0 || endDate === void 0) return;
763
+ if (startDate === previousStartDate && endDate === previousEndDate)
764
+ return;
765
+ if (
766
+ // plus one just represents a date that is guaranteed to be different.
767
+ startDate?.equals(previousStartDate ?? startDate?.plus({ days: 1 })) && (endDate?.equals(previousEndDate ?? endDate?.plus({ days: 1 })) || endDate === previousEndDate)
768
+ )
769
+ return;
770
+ const startDateString = startDate?.toFormat(DateModeToFormatMap[mode]) ?? halfPlaceholder;
771
+ const endDateString = endDate?.toFormat(DateModeToFormatMap[mode]) ?? halfPlaceholder;
772
+ const newInputValue = `${startDateString}${RANGE_SEPARATOR}${endDateString}`;
773
+ setInputValue(newInputValue);
774
+ }, [
775
+ startDate,
776
+ endDate,
777
+ mode,
778
+ halfPlaceholder,
779
+ previousStartDate,
780
+ previousEndDate
781
+ ]);
782
+ const currentParsedData = useMemo(() => {
783
+ return parseRangeInputValue(inputValue, mode, removePlaceholder);
784
+ }, [inputValue, mode, removePlaceholder]);
785
+ const handleChange = (event) => {
786
+ setInputValue(event.target.value);
787
+ const {
788
+ startDate: parsedStartDate,
789
+ endDate: parsedEndDate,
790
+ isInputValid,
791
+ isInputEmpty,
792
+ isHalfEmpty
793
+ } = parseRangeInputValue(event.target.value, mode, removePlaceholder);
794
+ const isValueDifferent = isInputValid !== currentParsedData.isInputValid || // The input has changed validity
795
+ isInputEmpty !== currentParsedData.isInputEmpty || // The input has changed emptiness
796
+ parsedStartDate === null !== (startDate === null) || // The start date has changed from empty to non-empty or vice versa
797
+ parsedEndDate === null !== (endDate === null) || // The end date has changed from empty to non-empty or vice versa
798
+ parsedStartDate !== null && startDate !== null && !parsedStartDate.equals(startDate) || // The start date has changed
799
+ parsedEndDate !== null && endDate !== null && !parsedEndDate.equals(endDate);
800
+ if (!isValueDifferent) return;
801
+ onChange?.({
802
+ startDate: isInputEmpty ? null : parsedStartDate ?? startDate ?? null,
803
+ endDate: isInputEmpty || isHalfEmpty ? null : parsedEndDate ?? endDate ?? null,
804
+ isInputValid,
805
+ isInputEmpty
806
+ });
807
+ };
808
+ useImperativeHandle(ref, () => {
809
+ const input = inputRef.current;
810
+ if (!input) return null;
811
+ return Object.assign(input, {
812
+ setDateRange: (startDate2, endDate2) => {
813
+ const startDateString = startDate2?.toFormat(
814
+ DateModeToFormatMap[mode]
815
+ );
816
+ const endDateString = endDate2?.toFormat(DateModeToFormatMap[mode]);
817
+ const newInputValue = `${startDateString ?? halfPlaceholder}${RANGE_SEPARATOR}${endDateString ?? halfPlaceholder}`;
818
+ setInputValue(newInputValue);
819
+ }
820
+ });
821
+ }, [mode, halfPlaceholder]);
822
+ return /* @__PURE__ */ jsx(
823
+ TextField,
824
+ {
825
+ ref: combinedRef,
826
+ "data-start-date": startDate?.toISODate() ?? "",
827
+ "data-end-date": endDate?.toISODate() ?? "",
828
+ "data-input-valid": currentParsedData.isInputValid,
829
+ "data-input-empty": currentParsedData.isInputEmpty,
830
+ ...props,
831
+ showCounter: false,
832
+ value: inputValue,
833
+ onChange: handleChange,
834
+ prefix: /* @__PURE__ */ jsx(Icon, { svg: SvgEvent }),
835
+ hint: disableHint ? void 0 : `Format: ${mode}`
836
+ }
837
+ );
838
+ }
839
+ );
840
+ MaskedDateRangeInput.displayName = "MaskedDateRangeInput";
841
+ function parseRangeInputValue(value, mode, removePlaceholder) {
842
+ const valueMinusPlaceholder = removePlaceholder(value);
843
+ const [startDate, endDate] = valueMinusPlaceholder.split(RANGE_SEPARATOR);
844
+ const startJsDate = maskitoParseDate(startDate, { mode });
845
+ const endJsDate = endDate ? maskitoParseDate(endDate, { mode }) : null;
846
+ const startLuxonDate = startJsDate ? DateTime.fromJSDate(startJsDate, { zone: "utc" }) : null;
847
+ const endLuxonDate = endJsDate ? DateTime.fromJSDate(endJsDate, { zone: "utc" }) : null;
848
+ return {
849
+ startDate: startLuxonDate,
850
+ endDate: endLuxonDate,
851
+ isInputValid: !!(startLuxonDate && endLuxonDate),
852
+ // input valid if both dates are filled
853
+ isInputEmpty: valueMinusPlaceholder === "",
854
+ // input empty if nothing is typed
855
+ isHalfEmpty: endDate === void 0
856
+ };
857
+ }
858
+ function swapMode$1(inputString, previousMode, mode) {
859
+ const halves = inputString.split(RANGE_SEPARATOR);
860
+ const segments = halves.map((half) => half.split("/"));
861
+ let startDay, startMonth, startYear, endDay, endMonth, endYear;
862
+ if (previousMode === "mm/dd/yyyy") {
863
+ startDay = segments[0][1];
864
+ startMonth = segments[0][0];
865
+ startYear = segments[0][2];
866
+ endDay = segments[1][1];
867
+ endMonth = segments[1][0];
868
+ endYear = segments[1][2];
869
+ }
870
+ if (previousMode === "dd/mm/yyyy") {
871
+ startDay = segments[0][0];
872
+ startMonth = segments[0][1];
873
+ startYear = segments[0][2];
874
+ endDay = segments[1][0];
875
+ endMonth = segments[1][1];
876
+ endYear = segments[1][2];
877
+ }
878
+ if (previousMode === "yyyy/mm/dd") {
879
+ startDay = segments[0][2];
880
+ startMonth = segments[0][1];
881
+ startYear = segments[0][0];
882
+ endDay = segments[1][2];
883
+ endMonth = segments[1][1];
884
+ endYear = segments[1][0];
885
+ }
886
+ if (mode === "mm/dd/yyyy") {
887
+ return `${startMonth}/${startDay}/${startYear}${RANGE_SEPARATOR}${endMonth}/${endDay}/${endYear}`;
888
+ }
889
+ if (mode === "dd/mm/yyyy") {
890
+ return `${startDay}/${startMonth}/${startYear}${RANGE_SEPARATOR}${endDay}/${endMonth}/${endYear}`;
891
+ }
892
+ if (mode === "yyyy/mm/dd") {
893
+ return `${startYear}/${startMonth}/${startDay}${RANGE_SEPARATOR}${endYear}/${endMonth}/${endDay}`;
894
+ }
895
+ return inputString;
896
+ }
897
+
898
+ const useDateFieldRangeConversion = (props) => {
899
+ const { value, defaultValue, minDate, maxDate, unavailable, onChange } = props;
900
+ const normalizedValue = useMemo(() => {
901
+ if (value === null || value === void 0) return value;
902
+ return {
903
+ startDate: convertStringToDate(value.startDate) ?? null,
904
+ endDate: convertStringToDate(value.endDate) ?? null
905
+ };
906
+ }, [value]);
907
+ const normalizedDefaultValue = useMemo(() => {
908
+ if (defaultValue === null || defaultValue === void 0)
909
+ return defaultValue;
910
+ return {
911
+ startDate: convertStringToDate(defaultValue.startDate) ?? null,
912
+ endDate: convertStringToDate(defaultValue.endDate) ?? null
913
+ };
914
+ }, [defaultValue]);
915
+ const normalizedMinDate = useMemo(
916
+ () => convertStringToDate(minDate),
917
+ [minDate]
918
+ );
919
+ const normalizedMaxDate = useMemo(
920
+ () => convertStringToDate(maxDate),
921
+ [maxDate]
922
+ );
923
+ const normalizedUnavailableDates = useMemo(() => {
924
+ return unavailable?.dates?.map((date) => convertStringToDate(date)).filter((date) => date !== null && date !== void 0);
925
+ }, [unavailable?.dates]);
926
+ const handleChange = useCallback(
927
+ (change) => {
928
+ onChange?.({
929
+ ...change,
930
+ startDate: change.startDate?.toISODate() ?? null,
931
+ endDate: change.endDate?.toISODate() ?? null
932
+ });
933
+ },
934
+ [onChange]
935
+ );
936
+ return {
937
+ value: normalizedValue,
938
+ defaultValue: normalizedDefaultValue,
939
+ minDate: normalizedMinDate,
940
+ maxDate: normalizedMaxDate,
941
+ unavailable: {
942
+ dates: normalizedUnavailableDates,
943
+ daysOfWeek: unavailable?.daysOfWeek
944
+ },
945
+ onChange: handleChange
946
+ };
947
+ };
948
+
949
+ function useDateFieldRangeState({
950
+ valueProp,
951
+ defaultValueProp,
952
+ inputRef,
953
+ onChange
954
+ }) {
955
+ const [startDate, setStartDate] = useOptionallyControlledState({
956
+ controlledValue: valueProp !== void 0 ? valueProp?.startDate : void 0,
957
+ defaultValue: defaultValueProp !== void 0 ? defaultValueProp?.startDate : void 0
958
+ });
959
+ const [endDate, setEndDate] = useOptionallyControlledState({
960
+ controlledValue: valueProp !== void 0 ? valueProp?.endDate : void 0,
961
+ defaultValue: defaultValueProp !== void 0 ? defaultValueProp?.endDate : void 0
962
+ });
963
+ const setSharedValue = (value) => {
964
+ inputRef.current?.setDateRange(value.startDate, value.endDate);
965
+ setStartDate(value.startDate);
966
+ setEndDate(value.endDate);
967
+ };
968
+ const handleInputChange = (change) => {
969
+ const range = change.isInputEmpty ? null : {
970
+ startDate: change.startDate?.startOf("day") ?? null,
971
+ endDate: change.endDate?.startOf("day") ?? null
972
+ };
973
+ setStartDate(range?.startDate ?? null);
974
+ setEndDate(range?.endDate ?? null);
975
+ onChange?.({
976
+ startDate: range?.startDate ?? null,
977
+ endDate: range?.endDate ?? null,
978
+ isInputValid: change.isInputValid,
979
+ isInputEmpty: change.isInputEmpty
980
+ });
981
+ };
982
+ const handleCalendarSelection = ({
983
+ startDate: startDate2,
984
+ endDate: endDate2
985
+ }) => {
986
+ setSharedValue({ startDate: startDate2, endDate: endDate2 });
987
+ onChange?.({
988
+ startDate: startDate2,
989
+ endDate: endDate2,
990
+ isInputValid: true,
991
+ isInputEmpty: false
992
+ });
993
+ };
994
+ return {
995
+ startDate,
996
+ endDate,
997
+ setStartDate,
998
+ setEndDate,
999
+ handleInputChange,
1000
+ handleCalendarSelection
1001
+ };
1002
+ }
1003
+
1004
+ const DateFieldRangeCalendar = ({
1005
+ startDate,
1006
+ endDate,
1007
+ onKeyDown,
1008
+ onSelection,
1009
+ minDate,
1010
+ maxDate,
1011
+ unavailable
1012
+ }) => {
1013
+ const previousStartDate = usePrevious(startDate);
1014
+ const previousEndDate = usePrevious(endDate);
1015
+ const handleCalendarSelection = (data) => {
1016
+ if (!data.value) return;
1017
+ const calStartDate = data.value.start ? DateTime.fromISO(data.value.start, { zone: "utc" }) : null;
1018
+ const calEndDate = data.value.end ? DateTime.fromISO(data.value.end, { zone: "utc" }) : null;
1019
+ onSelection({
1020
+ startDate: calStartDate,
1021
+ endDate: calEndDate
1022
+ });
1023
+ };
1024
+ const defaultFocusedDate = useMemo(() => {
1025
+ if (!startDate && !endDate) return DateTime.now().toISODate();
1026
+ if (!startDate) return endDate?.toISODate();
1027
+ if (!endDate) return startDate?.toISODate();
1028
+ if (endDate && !previousEndDate?.equals(endDate)) {
1029
+ return endDate.toISODate();
1030
+ } else if (startDate && !previousStartDate?.equals(startDate)) {
1031
+ return startDate.toISODate();
1032
+ }
1033
+ if (endDate) return endDate.toISODate();
1034
+ if (startDate) return startDate.toISODate();
1035
+ return DateTime.now().toISODate();
1036
+ }, [previousStartDate, previousEndDate, startDate, endDate]);
1037
+ return /* @__PURE__ */ jsx(
1038
+ Calendar,
1039
+ {
1040
+ range: true,
1041
+ onKeyDown,
1042
+ defaultFocusedDate,
1043
+ value: {
1044
+ start: startDate?.toISODate() || void 0,
1045
+ end: endDate?.toISODate() || void 0
1046
+ },
1047
+ onSelection: handleCalendarSelection,
1048
+ defaultTimeZone: "UTC",
1049
+ minDate: minDate?.toISODate() ?? void 0,
1050
+ maxDate: maxDate?.toISODate() ?? void 0,
1051
+ unavailable: unavailable ? {
1052
+ dates: unavailable.dates?.map((d) => d.toISODate() ?? ""),
1053
+ daysOfWeek: unavailable.daysOfWeek
1054
+ } : void 0,
1055
+ _disableAutofocus: true
1056
+ },
1057
+ `${startDate?.toISODate()}-${endDate?.toISODate()}`
1058
+ );
1059
+ };
1060
+
1061
+ const DateFieldRange = ({
1062
+ onFocus,
1063
+ onBlur,
1064
+ disableCalendar,
1065
+ required,
1066
+ mode,
1067
+ value: valueProp,
1068
+ defaultValue: defaultValueProp,
1069
+ minDate: minDateProp,
1070
+ maxDate: maxDateProp,
1071
+ unavailable: unavailableProp,
1072
+ onChange: onChangeProp,
1073
+ ...rest
1074
+ }) => {
1075
+ const { ref: wrapperRef } = useFocusWithin({
1076
+ onBlur,
1077
+ onFocus
1078
+ });
1079
+ const { value, defaultValue, minDate, maxDate, unavailable, onChange } = useDateFieldRangeConversion({
1080
+ value: valueProp,
1081
+ defaultValue: defaultValueProp,
1082
+ minDate: minDateProp,
1083
+ maxDate: maxDateProp,
1084
+ unavailable: unavailableProp,
1085
+ onChange: onChangeProp
1086
+ });
1087
+ const inputRef = useRef(null);
1088
+ const [popoverTriggerRef, setPopoverTriggerRef] = useState();
1089
+ const popoverContentRef = useRef(null);
1090
+ const combinedRef = useMergeRefs([popoverTriggerRef, inputRef]);
1091
+ const popoverSupported = usePopoverSupport();
1092
+ const handleChange = (change) => {
1093
+ const sharedConstraints = {
1094
+ unavailable,
1095
+ minDate: minDate ?? void 0,
1096
+ maxDate: maxDate ?? void 0
1097
+ };
1098
+ onChange?.({
1099
+ startDate: change.startDate?.startOf("day") ?? null,
1100
+ endDate: change.endDate?.startOf("day") ?? null,
1101
+ isInputValid: change.isInputValid,
1102
+ isInputEmpty: change.isInputEmpty,
1103
+ isDateRangeValid: validateDateRange({
1104
+ required,
1105
+ startDate: change.startDate?.startOf("day") ?? null,
1106
+ endDate: change.endDate?.startOf("day") ?? null,
1107
+ startDateConstraints: sharedConstraints,
1108
+ endDateConstraints: sharedConstraints
1109
+ })
1110
+ });
1111
+ };
1112
+ const { startDate, endDate, handleInputChange, handleCalendarSelection } = useDateFieldRangeState({
1113
+ valueProp: value,
1114
+ defaultValueProp: defaultValue,
1115
+ inputRef,
1116
+ onChange: handleChange
1117
+ });
1118
+ const {
1119
+ calendarOpen,
1120
+ setCalendarOpen,
1121
+ handleCalendarKeyDown,
1122
+ handleInputKeyDown
1123
+ } = useDateFieldOrchestration({
1124
+ inputRef,
1125
+ calendarDefaultOpen: false,
1126
+ popoverContentRef,
1127
+ disableCalendar
1128
+ });
1129
+ const shouldShowCalendar = usePopoverCloseDelayWorkaround(calendarOpen);
1130
+ const currentValidity = useMemo(() => {
1131
+ return validateDateRange({
1132
+ required,
1133
+ startDate,
1134
+ endDate,
1135
+ startDateConstraints: {
1136
+ unavailable,
1137
+ minDate: minDate ?? void 0,
1138
+ maxDate: maxDate ?? void 0
1139
+ },
1140
+ endDateConstraints: {
1141
+ unavailable,
1142
+ minDate: minDate ?? void 0,
1143
+ maxDate: maxDate ?? void 0
1144
+ }
1145
+ });
1146
+ }, [required, startDate, endDate, minDate, maxDate, unavailable]);
1147
+ const justTheField = /* @__PURE__ */ jsx(
1148
+ MaskedDateRangeInput,
1149
+ {
1150
+ ...rest,
1151
+ mode,
1152
+ ref: combinedRef,
1153
+ startDate: startDate ?? null,
1154
+ endDate: endDate ?? null,
1155
+ onChange: handleInputChange,
1156
+ disableHint: rest.disableHint,
1157
+ onKeyDown: handleInputKeyDown,
1158
+ onClick: () => setCalendarOpen(true),
1159
+ required,
1160
+ autoComplete: "off",
1161
+ "data-date-range-valid": currentValidity
1162
+ }
1163
+ );
1164
+ if (disableCalendar) {
1165
+ return justTheField;
1166
+ }
1167
+ if (!popoverSupported) {
1168
+ return justTheField;
1169
+ }
1170
+ return /* @__PURE__ */ jsx("div", { ref: wrapperRef, children: /* @__PURE__ */ jsxs(
1171
+ Popover,
1172
+ {
1173
+ open: calendarOpen,
1174
+ modal: true,
1175
+ placement: "bottom-start",
1176
+ disableFlipFallback: true,
1177
+ disableTriggerFocus: true,
1178
+ onClose: () => setCalendarOpen(false),
1179
+ disableAutoUpdate: true,
1180
+ onOutsidePress: () => setCalendarOpen(false),
1181
+ children: [
1182
+ /* @__PURE__ */ jsx(Popover.Trigger, { children: ({ ref: iRef }) => {
1183
+ setPopoverTriggerRef(iRef);
1184
+ return justTheField;
1185
+ } }),
1186
+ /* @__PURE__ */ jsx(Popover.Content, { ref: popoverContentRef, "data-testid": "calendar-popover", children: shouldShowCalendar && /* @__PURE__ */ jsx(
1187
+ DateFieldRangeCalendar,
1188
+ {
1189
+ startDate,
1190
+ endDate,
1191
+ onKeyDown: handleCalendarKeyDown,
1192
+ onSelection: handleCalendarSelection,
1193
+ minDate,
1194
+ maxDate,
1195
+ unavailable
1196
+ }
1197
+ ) })
1198
+ ]
1199
+ }
1200
+ ) });
1201
+ };
1202
+ DateFieldRange.displayName = "DateFieldRange";
1203
+ function validateDateRange({
1204
+ required,
1205
+ startDate,
1206
+ endDate,
1207
+ startDateConstraints,
1208
+ endDateConstraints
1209
+ }) {
1210
+ if (!required && !startDate && !endDate) return true;
1211
+ return validateDate({
1212
+ date: startDate,
1213
+ constraints: { ...startDateConstraints, required: true }
1214
+ }) && validateDate({
1215
+ date: endDate,
1216
+ constraints: { ...endDateConstraints, required: true }
1217
+ }) && (!startDate || !endDate || startDate <= endDate);
1218
+ }
1219
+
1220
+ const yearlessDatePlaceholderMask = ({
1221
+ mode,
1222
+ separator = "/",
1223
+ placeholder
1224
+ }) => {
1225
+ const dateOptions = maskitoDateOptionsGenerator({
1226
+ mode,
1227
+ separator
1228
+ });
1229
+ const { plugins, removePlaceholder, ...placeholderOptions } = maskitoWithPlaceholder(placeholder);
1230
+ const datePlaceholderMask = {
1231
+ ...dateOptions,
1232
+ plugins: plugins.concat(dateOptions.plugins || []),
1233
+ preprocessors: [
1234
+ ...placeholderOptions.preprocessors,
1235
+ ...dateOptions.preprocessors,
1236
+ makeZeroShortcutPreprocessor(mode, separator)
1237
+ ],
1238
+ postprocessors: [
1239
+ ...dateOptions.postprocessors,
1240
+ ...placeholderOptions.postprocessors
1241
+ ]
1242
+ };
1243
+ return { options: datePlaceholderMask, removePlaceholder };
1244
+ };
1245
+
1246
+ const MaskedYearlessDateInput = forwardRef(({ onChange, mode = "mm/dd", value, disableHint = false, ...props }, ref) => {
1247
+ const placeholder = "__/__";
1248
+ const [inputValue, setInputValue] = useState(placeholder);
1249
+ const { options, removePlaceholder } = yearlessDatePlaceholderMask({
1250
+ mode,
1251
+ placeholder
1252
+ });
1253
+ const maskedInputRef = useMaskito({ options });
1254
+ const inputRef = useRef(null);
1255
+ const combinedRef = useMergeRefs$1([maskedInputRef, inputRef, ref]);
1256
+ const previousValue = usePrevious(value);
1257
+ const previousMode = usePrevious(mode);
1258
+ useEffect(() => {
1259
+ if (mode !== previousMode) {
1260
+ setInputValue(
1261
+ (oldInputValue) => swapMode(oldInputValue, previousMode ?? mode, mode)
1262
+ );
1263
+ }
1264
+ }, [mode, previousMode]);
1265
+ useEffect(() => {
1266
+ if (previousValue?.day !== value?.day || previousValue?.month !== value?.month) {
1267
+ if (value?.day && value?.month) {
1268
+ setInputValue(stringifyYearlessDate(value.day, value.month, mode));
1269
+ } else if (previousValue !== null && previousValue !== void 0 && (value === null || value?.day === null && value?.month === null)) {
1270
+ setInputValue(placeholder);
1271
+ } else {
1272
+ return;
1273
+ }
1274
+ }
1275
+ }, [value, mode, previousValue]);
1276
+ const currentParsedData = useMemo(() => {
1277
+ return parseInputValue(inputValue, mode, removePlaceholder);
1278
+ }, [inputValue, mode, removePlaceholder]);
1279
+ const handleChange = (event) => {
1280
+ setInputValue(event.target.value);
1281
+ const {
1282
+ value: v,
1283
+ isInputValid,
1284
+ isInputEmpty
1285
+ } = parseInputValue(event.target.value, mode, removePlaceholder);
1286
+ const isDateDifferent = v === null !== (currentParsedData.value === null) || v?.day !== currentParsedData.value?.day || v?.month !== currentParsedData.value?.month;
1287
+ if (!isDateDifferent) {
1288
+ return;
1289
+ }
1290
+ onChange?.({
1291
+ event,
1292
+ value: v ?? currentParsedData.value ?? null,
1293
+ isInputValid,
1294
+ isInputEmpty
1295
+ });
1296
+ };
1297
+ useImperativeHandle(ref, () => {
1298
+ const input = inputRef.current;
1299
+ if (!input) return null;
1300
+ return Object.assign(input, {
1301
+ setValue: (value2) => {
1302
+ if (!value2) {
1303
+ setInputValue(placeholder);
1304
+ return;
1305
+ }
1306
+ setInputValue(
1307
+ stringifyYearlessDate(value2.day ?? 1, value2.month ?? 1, mode)
1308
+ );
1309
+ }
1310
+ });
1311
+ }, [mode, placeholder]);
1312
+ return /* @__PURE__ */ jsx(
1313
+ TextField,
1314
+ {
1315
+ ref: combinedRef,
1316
+ "data-month-value": value?.month,
1317
+ "data-day-value": value?.day,
1318
+ "data-input-valid": currentParsedData.isInputValid,
1319
+ "data-input-empty": currentParsedData.isInputEmpty,
1320
+ ...props,
1321
+ showCounter: false,
1322
+ value: inputValue,
1323
+ onChange: handleChange,
1324
+ hint: disableHint ? void 0 : `Format: ${mode}`
1325
+ }
1326
+ );
1327
+ });
1328
+ MaskedYearlessDateInput.displayName = "MaskedYearlessDateInput";
1329
+ function parseInputValue(value, mode, removePlaceholder) {
1330
+ const valueMinusPlaceholder = removePlaceholder(value);
1331
+ const [segment1, segment2] = valueMinusPlaceholder.split("/");
1332
+ const incompleteFirstSegment = segment1?.length !== 2;
1333
+ const incompleteSecondSegment = segment2?.length !== 2;
1334
+ if (mode === "dd/mm") {
1335
+ return {
1336
+ value: {
1337
+ day: incompleteFirstSegment ? null : parseInt(segment1),
1338
+ month: incompleteSecondSegment ? null : parseInt(segment2)
1339
+ },
1340
+ isInputValid: !incompleteFirstSegment && !incompleteSecondSegment,
1341
+ isInputEmpty: valueMinusPlaceholder === ""
1342
+ };
1343
+ }
1344
+ return {
1345
+ value: {
1346
+ day: incompleteSecondSegment ? null : parseInt(segment2),
1347
+ month: incompleteFirstSegment ? null : parseInt(segment1)
1348
+ },
1349
+ isInputValid: !incompleteFirstSegment && !incompleteSecondSegment,
1350
+ isInputEmpty: valueMinusPlaceholder === ""
1351
+ };
1352
+ }
1353
+ function swapMode(inputString, previousMode, mode) {
1354
+ const { day, month } = divideSegments(inputString, previousMode);
1355
+ return stringifyYearlessDate(day, month, mode);
1356
+ }
1357
+ function divideSegments(value, mode) {
1358
+ const [segment1, segment2] = value.split("/");
1359
+ if (mode === "dd/mm") {
1360
+ return { day: segment1, month: segment2 };
1361
+ }
1362
+ return { day: segment2, month: segment1 };
1363
+ }
1364
+ function stringifyYearlessDate(day, month, mode) {
1365
+ const dd = day.toString().padStart(2, "0");
1366
+ const mm = month.toString().padStart(2, "0");
1367
+ if (mode === "dd/mm") {
1368
+ return `${dd}/${mm}`;
1369
+ }
1370
+ return `${mm}/${dd}`;
1371
+ }
1372
+
1373
+ const DateFieldYearless = ({
1374
+ value: valueProp,
1375
+ defaultValue: defaultValueProp,
1376
+ onChange,
1377
+ minDate,
1378
+ maxDate,
1379
+ unavailable,
1380
+ required,
1381
+ ...restProps
1382
+ }) => {
1383
+ const [value, setValue] = useOptionallyControlledState({
1384
+ controlledValue: valueProp,
1385
+ defaultValue: defaultValueProp
1386
+ });
1387
+ const currentValidity = useMemo(
1388
+ () => validateYearlessDate({
1389
+ value: value ?? null,
1390
+ constraints: {
1391
+ required,
1392
+ unavailable,
1393
+ minDate,
1394
+ maxDate
1395
+ }
1396
+ }),
1397
+ [value, required, unavailable, minDate, maxDate]
1398
+ );
1399
+ const handleInputChange = (change) => {
1400
+ const { event, value: value2, ...restChange } = change;
1401
+ setValue(value2);
1402
+ return onChange?.({
1403
+ ...restChange,
1404
+ value: value2,
1405
+ isValid: validateYearlessDate({
1406
+ value: value2,
1407
+ constraints: {
1408
+ required,
1409
+ unavailable,
1410
+ minDate,
1411
+ maxDate
1412
+ }
1413
+ })
1414
+ });
1415
+ };
1416
+ return /* @__PURE__ */ jsx(
1417
+ MaskedYearlessDateInput,
1418
+ {
1419
+ required,
1420
+ ...restProps,
1421
+ autoComplete: "off",
1422
+ onChange: handleInputChange,
1423
+ value,
1424
+ "data-valid": currentValidity
1425
+ }
1426
+ );
1427
+ };
1428
+ DateFieldYearless.displayName = "DateFieldYearless";
1429
+
1430
+ export { DateFieldRange as D, DateFieldSingle as a, DateFieldYearless as b };
1431
+ //# sourceMappingURL=DateFieldYearless-CAUHW6Ow-EUWxJ0OY.js.map