@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.
- package/CHANGELOG.md +13 -0
- package/dist/{Combobox-D2aSaDkz.js → Combobox-C7ANSGIu.js} +2 -2
- package/dist/{Combobox-D2aSaDkz.js.map → Combobox-C7ANSGIu.js.map} +1 -1
- package/dist/Combobox.js +1 -1
- package/dist/{DateField-D28_sa7P.js → DateField-_TqCdcYv.js} +4 -3
- package/dist/{DateField-D28_sa7P.js.map → DateField-_TqCdcYv.js.map} +1 -1
- package/dist/DateField.js +1 -1
- package/dist/{DateFieldRange--oSGfjYa.js → DateFieldRange-Dk8WA52F.js} +2 -2
- package/dist/{DateFieldRange--oSGfjYa.js.map → DateFieldRange-Dk8WA52F.js.map} +1 -1
- package/dist/DateFieldRange.js +1 -1
- package/dist/{DateFieldSingle-0a8Bk7Yj.js → DateFieldSingle-D3xD8YZk.js} +2 -2
- package/dist/{DateFieldSingle-0a8Bk7Yj.js.map → DateFieldSingle-D3xD8YZk.js.map} +1 -1
- package/dist/DateFieldSingle.js +1 -1
- package/dist/{DateFieldYearless-DCv9WJdu.js → DateFieldYearless-BxkCSNk5.js} +2 -2
- package/dist/{DateFieldYearless-DCv9WJdu.js.map → DateFieldYearless-BxkCSNk5.js.map} +1 -1
- package/dist/DateFieldYearless-CAUHW6Ow-EUWxJ0OY.js +1431 -0
- package/dist/DateFieldYearless-CAUHW6Ow-EUWxJ0OY.js.map +1 -0
- package/dist/DateFieldYearless.js +1 -1
- package/dist/{Page-KN0DLtcf.js → Page-CxB5N9dR.js} +36 -36
- package/dist/{Page-KN0DLtcf.js.map → Page-CxB5N9dR.js.map} +1 -1
- package/dist/Page.css +72 -72
- package/dist/Page.js +1 -1
- package/dist/{Popover-B1HaUjGI.js → Popover-BPiqdyu1.js} +2 -2
- package/dist/{Popover-B1HaUjGI.js.map → Popover-BPiqdyu1.js.map} +1 -1
- package/dist/{Popover-CU2cGVD8-FWJOuFRj.js → Popover-CU2cGVD8-Casl3vM1.js} +2 -2
- package/dist/{Popover-CU2cGVD8-FWJOuFRj.js.map → Popover-CU2cGVD8-Casl3vM1.js.map} +1 -1
- package/dist/Popover.js +1 -1
- package/dist/TimeField-DRHLRqN3.js +913 -0
- package/dist/TimeField-DRHLRqN3.js.map +1 -0
- package/dist/TimeField.css +10 -0
- package/dist/TimeField.d.ts +2 -0
- package/dist/TimeField.js +2 -0
- package/dist/TimeField.js.map +1 -0
- package/dist/{Toolbar-BznMJKGJ.js → Toolbar-CSWhVSUM.js} +2 -2
- package/dist/{Toolbar-BznMJKGJ.js.map → Toolbar-CSWhVSUM.js.map} +1 -1
- package/dist/Toolbar.js +1 -1
- package/dist/assets/icons/st/gnav_field_pro_active.svg +1 -1
- package/dist/assets/icons/st/gnav_field_pro_inactive.svg +1 -1
- package/dist/components/TimeField/TimeField.d.ts +53 -0
- package/dist/components/TimeField/index.d.ts +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/event-BEJFimi3.js +6 -0
- package/dist/event-BEJFimi3.js.map +1 -0
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/usePopoverCloseDelayWorkaround-BZcjPkvT-BZcjPkvT.js +18 -0
- package/dist/usePopoverCloseDelayWorkaround-BZcjPkvT-BZcjPkvT.js.map +1 -0
- package/dist/{DateFieldYearless-C3_oGDr3-5meexzZO.js → usePopoverSupport-B9Lsqryr-DhZHMoNb.js} +470 -1429
- package/dist/usePopoverSupport-B9Lsqryr-DhZHMoNb.js.map +1 -0
- package/package.json +4 -4
- package/dist/DateFieldYearless-C3_oGDr3-5meexzZO.js.map +0 -1
- package/dist/usePopoverCloseDelayWorkaround-BhhG-xEB-hfJZaXHC.js +0 -21
- 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
|