@servicetitan/anvil2 1.42.1 → 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 +26 -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-c4LX8hoE.js → DateFieldRange-Dk8WA52F.js} +2 -2
- package/dist/{DateFieldRange-c4LX8hoE.js.map → DateFieldRange-Dk8WA52F.js.map} +1 -1
- package/dist/DateFieldRange.js +1 -1
- package/dist/{DateFieldSingle-WfPOm0qk.js → DateFieldSingle-D3xD8YZk.js} +2 -2
- package/dist/{DateFieldSingle-WfPOm0qk.js.map → DateFieldSingle-D3xD8YZk.js.map} +1 -1
- package/dist/DateFieldSingle.js +1 -1
- package/dist/{DateFieldYearless-DC50l-ht.js → DateFieldYearless-BxkCSNk5.js} +2 -2
- package/dist/{DateFieldYearless-DC50l-ht.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/Pagination-DU_qvFRR.js +430 -0
- package/dist/Pagination-DU_qvFRR.js.map +1 -0
- package/dist/Pagination.js +1 -429
- package/dist/Pagination.js.map +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 -0
- package/dist/assets/icons/st/gnav_field_pro_inactive.svg +1 -0
- package/dist/assets/icons/st.ts +2 -0
- package/dist/components/TimeField/TimeField.d.ts +53 -0
- package/dist/components/TimeField/index.d.ts +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/event-BEJFimi3.js +6 -0
- package/dist/event-BEJFimi3.js.map +1 -0
- package/dist/index.js +10 -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-DU0z2fEA-ByR2ixI5.js → usePopoverSupport-B9Lsqryr-DhZHMoNb.js} +470 -1447
- package/dist/usePopoverSupport-B9Lsqryr-DhZHMoNb.js.map +1 -0
- package/package.json +2 -2
- package/dist/DateFieldYearless-DU0z2fEA-ByR2ixI5.js.map +0 -1
- package/dist/usePopoverCloseDelayWorkaround-BhhG-xEB-hfJZaXHC.js +0 -21
- package/dist/usePopoverCloseDelayWorkaround-BhhG-xEB-hfJZaXHC.js.map +0 -1
package/dist/{DateFieldYearless-DU0z2fEA-ByR2ixI5.js → usePopoverSupport-B9Lsqryr-DhZHMoNb.js}
RENAMED
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { T as TextField } from './TextField-CRTh0gL_-D2CjcYXX.js';
|
|
4
|
-
import { u as useMergeRefs$1 } from './floating-ui.react-BFNinq1w.js';
|
|
5
|
-
import { D as DateTime, f as Calendar } from './Calendar-DD5kmVd3-CBGTR11R.js';
|
|
6
|
-
import { I as Icon } from './Icon-B6HmlQiR-BxQkO3X5.js';
|
|
7
|
-
import { u as usePopoverCloseDelayWorkaround, S as SvgEvent } from './usePopoverCloseDelayWorkaround-BhhG-xEB-hfJZaXHC.js';
|
|
8
|
-
import { d as Popover } from './Popover-CU2cGVD8-FWJOuFRj.js';
|
|
9
|
-
import { u as useOptionallyControlledState } from './useOptionallyControlledState-DAv5LXXh-DAv5LXXh.js';
|
|
10
|
-
import { u as useMergeRefs } from './useMergeRefs-Bde85AWI-Bde85AWI.js';
|
|
11
|
-
import { s as supportsPopover, u as useKeyboardFocusables } from './ProgressBar-BRvB-bD4-DppwBrFg.js';
|
|
1
|
+
import { useState, useCallback, useRef, useLayoutEffect, useEffect } from 'react';
|
|
2
|
+
import { s as supportsPopover } from './ProgressBar-BRvB-bD4-DppwBrFg.js';
|
|
12
3
|
|
|
13
4
|
function getContentEditableSelection(element) {
|
|
14
5
|
const { anchorOffset = 0, focusOffset = 0 } = element.ownerDocument.getSelection() || {};
|
|
@@ -984,6 +975,19 @@ const DATE_SEGMENTS_MAX_VALUES = {
|
|
|
984
975
|
const DEFAULT_MIN_DATE = new Date('0001-01-01T00:00');
|
|
985
976
|
const DEFAULT_MAX_DATE = new Date('9999-12-31T23:59:59.999');
|
|
986
977
|
|
|
978
|
+
const DEFAULT_TIME_SEGMENT_MAX_VALUES = {
|
|
979
|
+
hours: 23,
|
|
980
|
+
minutes: 59,
|
|
981
|
+
seconds: 59,
|
|
982
|
+
milliseconds: 999,
|
|
983
|
+
};
|
|
984
|
+
const DEFAULT_TIME_SEGMENT_MIN_VALUES = {
|
|
985
|
+
hours: 0,
|
|
986
|
+
minutes: 0,
|
|
987
|
+
seconds: 0,
|
|
988
|
+
milliseconds: 0,
|
|
989
|
+
};
|
|
990
|
+
|
|
987
991
|
/**
|
|
988
992
|
* {@link https://unicode-table.com/en/00A0/ Non-breaking space}.
|
|
989
993
|
*/
|
|
@@ -1022,9 +1026,31 @@ const CHAR_MINUS = '\u2212';
|
|
|
1022
1026
|
* is used as prolonged sounds in Japanese.
|
|
1023
1027
|
*/
|
|
1024
1028
|
const CHAR_JP_HYPHEN = '\u30FC';
|
|
1029
|
+
/**
|
|
1030
|
+
* {@link https://symbl.cc/en/003A/ Colon}
|
|
1031
|
+
* is a punctuation mark that connects parts of a text logically.
|
|
1032
|
+
* ---
|
|
1033
|
+
* is also used as separator in time.
|
|
1034
|
+
*/
|
|
1035
|
+
const CHAR_COLON = '\u003A';
|
|
1036
|
+
/**
|
|
1037
|
+
* {@link https://symbl.cc/en/FF1A/ Full-width colon}
|
|
1038
|
+
* is a full-width punctuation mark used to separate parts of a text commonly in Japanese.
|
|
1039
|
+
*/
|
|
1040
|
+
const CHAR_JP_COLON = '\uFF1A';
|
|
1041
|
+
|
|
1042
|
+
const ANY_MERIDIEM_CHARACTER_RE = new RegExp(`[${CHAR_NO_BREAK_SPACE}APM]+$`, 'g');
|
|
1043
|
+
const ALL_MERIDIEM_CHARACTERS_RE = new RegExp(`${CHAR_NO_BREAK_SPACE}[AP]M$`, 'g');
|
|
1025
1044
|
|
|
1026
1045
|
const TIME_FIXED_CHARACTERS = [':', '.'];
|
|
1027
1046
|
|
|
1047
|
+
const TIME_SEGMENT_VALUE_LENGTHS = {
|
|
1048
|
+
hours: 2,
|
|
1049
|
+
minutes: 2,
|
|
1050
|
+
seconds: 2,
|
|
1051
|
+
milliseconds: 3,
|
|
1052
|
+
};
|
|
1053
|
+
|
|
1028
1054
|
function validateDateString({ dateString, dateModeTemplate, dateSegmentsSeparator, offset, selection: [from, to], }) {
|
|
1029
1055
|
var _a, _b;
|
|
1030
1056
|
const parsedDate = parseDateString(dateString, dateModeTemplate);
|
|
@@ -1078,6 +1104,8 @@ function validateDateString({ dateString, dateModeTemplate, dateSegmentsSeparato
|
|
|
1078
1104
|
function identity(x) {
|
|
1079
1105
|
return x;
|
|
1080
1106
|
}
|
|
1107
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
1108
|
+
function noop() { }
|
|
1081
1109
|
|
|
1082
1110
|
/**
|
|
1083
1111
|
* Copy-pasted solution from lodash
|
|
@@ -1091,6 +1119,17 @@ function escapeRegExp(str) {
|
|
|
1091
1119
|
: str;
|
|
1092
1120
|
}
|
|
1093
1121
|
|
|
1122
|
+
function findCommonBeginningSubstr(a, b) {
|
|
1123
|
+
let res = '';
|
|
1124
|
+
for (let i = 0; i < a.length; i++) {
|
|
1125
|
+
if (a[i] !== b[i]) {
|
|
1126
|
+
return res;
|
|
1127
|
+
}
|
|
1128
|
+
res += a[i];
|
|
1129
|
+
}
|
|
1130
|
+
return res;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1094
1133
|
function isEmpty(entity) {
|
|
1095
1134
|
return !entity || (typeof entity === 'object' && Object.keys(entity).length === 0);
|
|
1096
1135
|
}
|
|
@@ -1113,6 +1152,15 @@ function padWithZeroesUntilValid(segmentValue, paddedMaxValue, prefixedZeroesCou
|
|
|
1113
1152
|
return padWithZeroesUntilValid(`${valueWithoutLastChar}0`, paddedMaxValue, prefixedZeroesCount);
|
|
1114
1153
|
}
|
|
1115
1154
|
|
|
1155
|
+
/**
|
|
1156
|
+
* Replace fullwidth colon with half width colon
|
|
1157
|
+
* @param fullWidthColon full width colon
|
|
1158
|
+
* @returns processed half width colon
|
|
1159
|
+
*/
|
|
1160
|
+
function toHalfWidthColon(fullWidthColon) {
|
|
1161
|
+
return fullWidthColon.replaceAll(new RegExp(CHAR_JP_COLON, 'g'), CHAR_COLON);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1116
1164
|
/**
|
|
1117
1165
|
* Replace fullwidth numbers with half width number
|
|
1118
1166
|
* @param fullWidthNumber full width number
|
|
@@ -1122,6 +1170,22 @@ function toHalfWidthNumber(fullWidthNumber) {
|
|
|
1122
1170
|
return fullWidthNumber.replaceAll(/[0-9]/g, (s) => String.fromCharCode(s.charCodeAt(0) - 0xfee0));
|
|
1123
1171
|
}
|
|
1124
1172
|
|
|
1173
|
+
/**
|
|
1174
|
+
* Convert full width colon (:) to half width one (:)
|
|
1175
|
+
*/
|
|
1176
|
+
function createColonConvertPreprocessor() {
|
|
1177
|
+
return ({ elementState, data }) => {
|
|
1178
|
+
const { value, selection } = elementState;
|
|
1179
|
+
return {
|
|
1180
|
+
elementState: {
|
|
1181
|
+
selection,
|
|
1182
|
+
value: toHalfWidthColon(value),
|
|
1183
|
+
},
|
|
1184
|
+
data: toHalfWidthColon(data),
|
|
1185
|
+
};
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1125
1189
|
function createDateSegmentsZeroPaddingPostprocessor({ dateModeTemplate, dateSegmentSeparator, splitFn, uniteFn, }) {
|
|
1126
1190
|
return ({ value, selection }) => {
|
|
1127
1191
|
var _a;
|
|
@@ -1205,7 +1269,184 @@ function createFullWidthToHalfWidthPreprocessor() {
|
|
|
1205
1269
|
};
|
|
1206
1270
|
}
|
|
1207
1271
|
|
|
1208
|
-
|
|
1272
|
+
function createTimeMaskExpression(mode) {
|
|
1273
|
+
return Array.from(mode.replace(' AA', ''))
|
|
1274
|
+
.map((char) => (TIME_FIXED_CHARACTERS.includes(char) ? char : /\d/))
|
|
1275
|
+
.concat(mode.includes('AA') ? [CHAR_NO_BREAK_SPACE, /[AP]/i, /M/i] : []);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
function padTimeSegments(timeSegments, pad) {
|
|
1279
|
+
return Object.fromEntries(Object.entries(timeSegments).map(([segmentName, segmentValue]) => [
|
|
1280
|
+
segmentName,
|
|
1281
|
+
pad(String(segmentValue), TIME_SEGMENT_VALUE_LENGTHS[segmentName]),
|
|
1282
|
+
]));
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
function padStartTimeSegments(timeSegments) {
|
|
1286
|
+
return padTimeSegments(timeSegments, (value, length) => value.padStart(length, '0'));
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
const SEGMENT_FULL_NAME = {
|
|
1290
|
+
HH: 'hours',
|
|
1291
|
+
MM: 'minutes',
|
|
1292
|
+
SS: 'seconds',
|
|
1293
|
+
MSS: 'milliseconds',
|
|
1294
|
+
};
|
|
1295
|
+
/**
|
|
1296
|
+
* @param timeString can be with/without fixed characters
|
|
1297
|
+
*/
|
|
1298
|
+
function parseTimeString(timeString, timeMode) {
|
|
1299
|
+
const onlyDigits = timeString.replaceAll(/\D+/g, '');
|
|
1300
|
+
let offset = 0;
|
|
1301
|
+
return Object.fromEntries(timeMode
|
|
1302
|
+
.split(/\W/)
|
|
1303
|
+
.filter((segmentAbbr) => SEGMENT_FULL_NAME[segmentAbbr])
|
|
1304
|
+
.map((segmentAbbr) => {
|
|
1305
|
+
const segmentValue = onlyDigits.slice(offset, offset + segmentAbbr.length);
|
|
1306
|
+
offset += segmentAbbr.length;
|
|
1307
|
+
return [SEGMENT_FULL_NAME[segmentAbbr], segmentValue];
|
|
1308
|
+
}));
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
const LEADING_NON_DIGITS = /^\D*/;
|
|
1312
|
+
const TRAILING_NON_DIGITS = /\D*$/;
|
|
1313
|
+
function toTimeString({ hours = '', minutes = '', seconds = '', milliseconds = '', }) {
|
|
1314
|
+
return `${hours}:${minutes}:${seconds}.${milliseconds}`
|
|
1315
|
+
.replace(LEADING_NON_DIGITS, '')
|
|
1316
|
+
.replace(TRAILING_NON_DIGITS, '');
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
const TRAILING_TIME_SEGMENT_SEPARATOR_REG = new RegExp(`[${TIME_FIXED_CHARACTERS.map(escapeRegExp).join('')}]$`);
|
|
1320
|
+
/**
|
|
1321
|
+
* Pads invalid time segment with zero to make it valid.
|
|
1322
|
+
* @example 00:|00 => Type 9 (too much for the first digit of minutes) => 00:09|
|
|
1323
|
+
* @example |19:00 => Type 2 (29 - invalid value for hours) => 2|0:00
|
|
1324
|
+
*/
|
|
1325
|
+
function enrichTimeSegmentsWithZeroes({ value, selection }, { mode, timeSegmentMaxValues = DEFAULT_TIME_SEGMENT_MAX_VALUES, }) {
|
|
1326
|
+
const [from, to] = selection;
|
|
1327
|
+
const parsedTime = parseTimeString(value, mode);
|
|
1328
|
+
const possibleTimeSegments = Object.entries(parsedTime);
|
|
1329
|
+
const paddedMaxValues = padStartTimeSegments(timeSegmentMaxValues);
|
|
1330
|
+
const validatedTimeSegments = {};
|
|
1331
|
+
let paddedZeroes = 0;
|
|
1332
|
+
for (const [segmentName, segmentValue] of possibleTimeSegments) {
|
|
1333
|
+
const maxSegmentValue = paddedMaxValues[segmentName];
|
|
1334
|
+
const { validatedSegmentValue, prefixedZeroesCount } = padWithZeroesUntilValid(segmentValue, String(maxSegmentValue));
|
|
1335
|
+
paddedZeroes += prefixedZeroesCount;
|
|
1336
|
+
validatedTimeSegments[segmentName] = validatedSegmentValue;
|
|
1337
|
+
}
|
|
1338
|
+
const [leadingNonDigitCharacters = ''] = value.match(/^\D+(?=\d)/g) || []; // prefix
|
|
1339
|
+
const [trailingNonDigitCharacters = ''] = value.match(/\D+$/g) || []; // trailing segment separators / meridiem characters / postfix
|
|
1340
|
+
const validatedTimeString = leadingNonDigitCharacters +
|
|
1341
|
+
toTimeString(validatedTimeSegments) +
|
|
1342
|
+
trailingNonDigitCharacters;
|
|
1343
|
+
const addedDateSegmentSeparators = Math.max(validatedTimeString.length - value.length - paddedZeroes, 0);
|
|
1344
|
+
let newFrom = from + paddedZeroes + addedDateSegmentSeparators;
|
|
1345
|
+
let newTo = to + paddedZeroes + addedDateSegmentSeparators;
|
|
1346
|
+
if (newFrom === newTo &&
|
|
1347
|
+
paddedZeroes &&
|
|
1348
|
+
// if next character after cursor is time segment separator
|
|
1349
|
+
validatedTimeString.slice(0, newTo + 1).match(TRAILING_TIME_SEGMENT_SEPARATOR_REG)) {
|
|
1350
|
+
newFrom++;
|
|
1351
|
+
newTo++;
|
|
1352
|
+
}
|
|
1353
|
+
return {
|
|
1354
|
+
value: validatedTimeString,
|
|
1355
|
+
selection: [newFrom, newTo],
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
/**
|
|
1360
|
+
* Prevent insertion if any time segment will become invalid
|
|
1361
|
+
* (and even zero padding won't help with it).
|
|
1362
|
+
* @example 2|0:00 => Type 9 => 2|0:00
|
|
1363
|
+
*/
|
|
1364
|
+
function createInvalidTimeSegmentInsertionPreprocessor({ timeMode, timeSegmentMinValues = DEFAULT_TIME_SEGMENT_MIN_VALUES, timeSegmentMaxValues = DEFAULT_TIME_SEGMENT_MAX_VALUES, parseValue = (x) => ({ timeString: x }), }) {
|
|
1365
|
+
const invalidCharsRegExp = new RegExp(`[^\\d${TIME_FIXED_CHARACTERS.map(escapeRegExp).join('')}]+`);
|
|
1366
|
+
return ({ elementState, data }, actionType) => {
|
|
1367
|
+
if (actionType !== 'insert') {
|
|
1368
|
+
return { elementState, data };
|
|
1369
|
+
}
|
|
1370
|
+
const { value, selection } = elementState;
|
|
1371
|
+
const [from, rawTo] = selection;
|
|
1372
|
+
const newCharacters = data.replace(invalidCharsRegExp, '');
|
|
1373
|
+
const to = rawTo + newCharacters.length; // to be conformed with `overwriteMode: replace`
|
|
1374
|
+
const newPossibleValue = value.slice(0, from) + newCharacters + value.slice(to);
|
|
1375
|
+
const { timeString, restValue = '' } = parseValue(newPossibleValue);
|
|
1376
|
+
const timeSegments = Object.entries(parseTimeString(timeString, timeMode));
|
|
1377
|
+
let offset = restValue.length;
|
|
1378
|
+
for (const [segmentName, stringifiedSegmentValue] of timeSegments) {
|
|
1379
|
+
const minSegmentValue = timeSegmentMinValues[segmentName];
|
|
1380
|
+
const maxSegmentValue = timeSegmentMaxValues[segmentName];
|
|
1381
|
+
const segmentValue = Number(stringifiedSegmentValue);
|
|
1382
|
+
const lastSegmentDigitIndex = offset + TIME_SEGMENT_VALUE_LENGTHS[segmentName];
|
|
1383
|
+
if (lastSegmentDigitIndex >= from &&
|
|
1384
|
+
lastSegmentDigitIndex <= to &&
|
|
1385
|
+
segmentValue !== clamp(segmentValue, minSegmentValue, maxSegmentValue)) {
|
|
1386
|
+
return { elementState, data: '' }; // prevent insertion
|
|
1387
|
+
}
|
|
1388
|
+
offset +=
|
|
1389
|
+
stringifiedSegmentValue.length +
|
|
1390
|
+
// any time segment separator
|
|
1391
|
+
1;
|
|
1392
|
+
}
|
|
1393
|
+
return { elementState, data };
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function createMeridiemPreprocessor(timeMode) {
|
|
1398
|
+
if (!timeMode.includes('AA')) {
|
|
1399
|
+
return identity;
|
|
1400
|
+
}
|
|
1401
|
+
const mainMeridiemCharRE = /^[AP]$/gi;
|
|
1402
|
+
return ({ elementState, data }) => {
|
|
1403
|
+
const { value, selection } = elementState;
|
|
1404
|
+
const newValue = value.toUpperCase();
|
|
1405
|
+
const newData = data.toUpperCase();
|
|
1406
|
+
if (newValue.match(ALL_MERIDIEM_CHARACTERS_RE) &&
|
|
1407
|
+
newData.match(mainMeridiemCharRE)) {
|
|
1408
|
+
return {
|
|
1409
|
+
elementState: {
|
|
1410
|
+
value: newValue.replaceAll(ALL_MERIDIEM_CHARACTERS_RE, ''),
|
|
1411
|
+
selection,
|
|
1412
|
+
},
|
|
1413
|
+
data: `${newData}M`,
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
return { elementState: { selection, value: newValue }, data: newData };
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
function createMeridiemPostprocessor(timeMode) {
|
|
1420
|
+
if (!timeMode.includes('AA')) {
|
|
1421
|
+
return identity;
|
|
1422
|
+
}
|
|
1423
|
+
return ({ value, selection }, initialElementState) => {
|
|
1424
|
+
if (!value.match(ANY_MERIDIEM_CHARACTER_RE) ||
|
|
1425
|
+
value.match(ALL_MERIDIEM_CHARACTERS_RE)) {
|
|
1426
|
+
return { value, selection };
|
|
1427
|
+
}
|
|
1428
|
+
const [from, to] = selection;
|
|
1429
|
+
// any meridiem character was deleted
|
|
1430
|
+
if (initialElementState.value.match(ALL_MERIDIEM_CHARACTERS_RE)) {
|
|
1431
|
+
const newValue = value.replace(ANY_MERIDIEM_CHARACTER_RE, '');
|
|
1432
|
+
return {
|
|
1433
|
+
value: newValue,
|
|
1434
|
+
selection: [
|
|
1435
|
+
Math.min(from, newValue.length),
|
|
1436
|
+
Math.min(to, newValue.length),
|
|
1437
|
+
],
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
const fullMeridiem = `${CHAR_NO_BREAK_SPACE}${value.includes('P') ? 'P' : 'A'}M`;
|
|
1441
|
+
const newValue = value.replace(ANY_MERIDIEM_CHARACTER_RE, (x) => x !== CHAR_NO_BREAK_SPACE ? fullMeridiem : x);
|
|
1442
|
+
return {
|
|
1443
|
+
value: newValue,
|
|
1444
|
+
selection: to >= newValue.indexOf(fullMeridiem)
|
|
1445
|
+
? [newValue.length, newValue.length]
|
|
1446
|
+
: selection,
|
|
1447
|
+
};
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1209
1450
|
|
|
1210
1451
|
function raiseSegmentValueToMin(segments, fullMode) {
|
|
1211
1452
|
const segmentsLength = getDateSegmentValueLength(fullMode);
|
|
@@ -1284,6 +1525,59 @@ function normalizeDatePreprocessor({ dateModeTemplate, dateSegmentsSeparator, ra
|
|
|
1284
1525
|
};
|
|
1285
1526
|
}
|
|
1286
1527
|
|
|
1528
|
+
function maskitoPostfixPostprocessorGenerator(postfix) {
|
|
1529
|
+
const postfixRE = new RegExp(`${escapeRegExp(postfix)}$`);
|
|
1530
|
+
return postfix
|
|
1531
|
+
? ({ value, selection }, initialElementState) => {
|
|
1532
|
+
if (!value && !initialElementState.value.endsWith(postfix)) {
|
|
1533
|
+
// cases when developer wants input to be empty (programmatically)
|
|
1534
|
+
return { value, selection };
|
|
1535
|
+
}
|
|
1536
|
+
if (!value.endsWith(postfix) &&
|
|
1537
|
+
!initialElementState.value.endsWith(postfix)) {
|
|
1538
|
+
return { selection, value: value + postfix };
|
|
1539
|
+
}
|
|
1540
|
+
const initialValueBeforePostfix = initialElementState.value.replace(postfixRE, '');
|
|
1541
|
+
const postfixWasModified = initialElementState.selection[1] > initialValueBeforePostfix.length;
|
|
1542
|
+
const alreadyExistedValueBeforePostfix = findCommonBeginningSubstr(initialValueBeforePostfix, value);
|
|
1543
|
+
return {
|
|
1544
|
+
selection,
|
|
1545
|
+
value: Array.from(postfix)
|
|
1546
|
+
.reverse()
|
|
1547
|
+
.reduce((newValue, char, index) => {
|
|
1548
|
+
const i = newValue.length - 1 - index;
|
|
1549
|
+
const isInitiallyMirroredChar = alreadyExistedValueBeforePostfix[i] === char &&
|
|
1550
|
+
postfixWasModified;
|
|
1551
|
+
return newValue[i] !== char || isInitiallyMirroredChar
|
|
1552
|
+
? newValue.slice(0, i + 1) + char + newValue.slice(i + 1)
|
|
1553
|
+
: newValue;
|
|
1554
|
+
}, value),
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
: identity;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
function maskitoPrefixPostprocessorGenerator(prefix) {
|
|
1561
|
+
return prefix
|
|
1562
|
+
? ({ value, selection }, initialElementState) => {
|
|
1563
|
+
if (value.startsWith(prefix) || // already valid
|
|
1564
|
+
(!value && !initialElementState.value.startsWith(prefix)) // cases when developer wants input to be empty
|
|
1565
|
+
) {
|
|
1566
|
+
return { value, selection };
|
|
1567
|
+
}
|
|
1568
|
+
const [from, to] = selection;
|
|
1569
|
+
const prefixedValue = Array.from(prefix).reduce((modifiedValue, char, i) => modifiedValue[i] === char
|
|
1570
|
+
? modifiedValue
|
|
1571
|
+
: modifiedValue.slice(0, i) + char + modifiedValue.slice(i), value);
|
|
1572
|
+
const addedCharsCount = prefixedValue.length - value.length;
|
|
1573
|
+
return {
|
|
1574
|
+
selection: [from + addedCharsCount, to + addedCharsCount],
|
|
1575
|
+
value: prefixedValue,
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
: identity;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1287
1581
|
function createValidDatePreprocessor({ dateModeTemplate, dateSegmentsSeparator, rangeSeparator = '', }) {
|
|
1288
1582
|
return ({ elementState, data }) => {
|
|
1289
1583
|
const { value, selection } = elementState;
|
|
@@ -1390,6 +1684,108 @@ function maskitoCaretGuard(guard) {
|
|
|
1390
1684
|
});
|
|
1391
1685
|
}
|
|
1392
1686
|
|
|
1687
|
+
function createMeridiemSteppingPlugin(meridiemStartIndex) {
|
|
1688
|
+
if (meridiemStartIndex < 0) {
|
|
1689
|
+
return noop;
|
|
1690
|
+
}
|
|
1691
|
+
return (element) => {
|
|
1692
|
+
const listener = (event) => {
|
|
1693
|
+
const caretIndex = Number(element.selectionStart);
|
|
1694
|
+
const value = element.value.toUpperCase();
|
|
1695
|
+
if ((event.key !== 'ArrowUp' && event.key !== 'ArrowDown') ||
|
|
1696
|
+
caretIndex < meridiemStartIndex) {
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
event.preventDefault();
|
|
1700
|
+
// eslint-disable-next-line no-nested-ternary
|
|
1701
|
+
const meridiemMainCharacter = value.includes('A')
|
|
1702
|
+
? 'P'
|
|
1703
|
+
: value.includes('P') || event.key === 'ArrowUp'
|
|
1704
|
+
? 'A'
|
|
1705
|
+
: 'P';
|
|
1706
|
+
const newMeridiem = `${CHAR_NO_BREAK_SPACE}${meridiemMainCharacter}M`;
|
|
1707
|
+
maskitoUpdateElement(element, {
|
|
1708
|
+
value: value.length === meridiemStartIndex
|
|
1709
|
+
? value + newMeridiem
|
|
1710
|
+
: value.replace(ANY_MERIDIEM_CHARACTER_RE, newMeridiem),
|
|
1711
|
+
selection: [caretIndex, caretIndex],
|
|
1712
|
+
});
|
|
1713
|
+
};
|
|
1714
|
+
element.addEventListener('keydown', listener);
|
|
1715
|
+
return () => element.removeEventListener('keydown', listener);
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
function createTimeSegmentsSteppingPlugin({ step, fullMode, timeSegmentMaxValues, }) {
|
|
1720
|
+
const segmentsIndexes = createTimeSegmentsIndexes(fullMode);
|
|
1721
|
+
return step <= 0
|
|
1722
|
+
? noop
|
|
1723
|
+
: (element) => {
|
|
1724
|
+
const listener = (event) => {
|
|
1725
|
+
var _a;
|
|
1726
|
+
if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') {
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
event.preventDefault();
|
|
1730
|
+
const selectionStart = (_a = element.selectionStart) !== null && _a !== void 0 ? _a : 0;
|
|
1731
|
+
const activeSegment = getActiveSegment({
|
|
1732
|
+
segmentsIndexes,
|
|
1733
|
+
selectionStart,
|
|
1734
|
+
});
|
|
1735
|
+
if (!activeSegment) {
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
const updatedValue = updateSegmentValue({
|
|
1739
|
+
selection: segmentsIndexes.get(activeSegment),
|
|
1740
|
+
value: element.value,
|
|
1741
|
+
toAdd: event.key === 'ArrowUp' ? step : -step,
|
|
1742
|
+
max: timeSegmentMaxValues[activeSegment],
|
|
1743
|
+
});
|
|
1744
|
+
maskitoUpdateElement(element, {
|
|
1745
|
+
value: updatedValue,
|
|
1746
|
+
selection: [selectionStart, selectionStart],
|
|
1747
|
+
});
|
|
1748
|
+
};
|
|
1749
|
+
element.addEventListener('keydown', listener);
|
|
1750
|
+
return () => element.removeEventListener('keydown', listener);
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1753
|
+
function createTimeSegmentsIndexes(fullMode) {
|
|
1754
|
+
return new Map([
|
|
1755
|
+
['hours', getSegmentRange(fullMode, 'HH')],
|
|
1756
|
+
['milliseconds', getSegmentRange(fullMode, 'MSS')],
|
|
1757
|
+
['minutes', getSegmentRange(fullMode, 'MM')],
|
|
1758
|
+
['seconds', getSegmentRange(fullMode, 'SS')],
|
|
1759
|
+
]);
|
|
1760
|
+
}
|
|
1761
|
+
function getSegmentRange(mode, segment) {
|
|
1762
|
+
const index = mode.indexOf(segment);
|
|
1763
|
+
return index === -1 ? [-1, -1] : [index, index + segment.length];
|
|
1764
|
+
}
|
|
1765
|
+
function getActiveSegment({ segmentsIndexes, selectionStart, }) {
|
|
1766
|
+
for (const [segmentName, segmentRange] of segmentsIndexes.entries()) {
|
|
1767
|
+
const [from, to] = segmentRange;
|
|
1768
|
+
if (from <= selectionStart && selectionStart <= to) {
|
|
1769
|
+
return segmentName;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
return null;
|
|
1773
|
+
}
|
|
1774
|
+
function updateSegmentValue({ selection, value, toAdd, max, }) {
|
|
1775
|
+
const [from, to] = selection;
|
|
1776
|
+
const segmentValue = Number(value.slice(from, to).padEnd(to - from, '0'));
|
|
1777
|
+
const newSegmentValue = mod(segmentValue + toAdd, max + 1);
|
|
1778
|
+
return (value.slice(0, from) +
|
|
1779
|
+
String(newSegmentValue).padStart(to - from, '0') +
|
|
1780
|
+
value.slice(to, value.length));
|
|
1781
|
+
}
|
|
1782
|
+
function mod(value, max) {
|
|
1783
|
+
if (value < 0) {
|
|
1784
|
+
value += Math.floor(Math.abs(value) / max + 1) * max;
|
|
1785
|
+
}
|
|
1786
|
+
return value % max;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1393
1789
|
function maskitoWithPlaceholder(placeholder, focusedOnly = false) {
|
|
1394
1790
|
let lastClearValue = '';
|
|
1395
1791
|
let action = 'validation';
|
|
@@ -1652,6 +2048,66 @@ function maskitoDateRangeOptionsGenerator({ mode, min, max, minLength, maxLength
|
|
|
1652
2048
|
] });
|
|
1653
2049
|
}
|
|
1654
2050
|
|
|
2051
|
+
function maskitoTimeOptionsGenerator({ mode, timeSegmentMaxValues = {}, timeSegmentMinValues = {}, step = 0, prefix = '', postfix = '', }) {
|
|
2052
|
+
const hasMeridiem = mode.includes('AA');
|
|
2053
|
+
const enrichedTimeSegmentMaxValues = Object.assign(Object.assign(Object.assign({}, DEFAULT_TIME_SEGMENT_MAX_VALUES), (hasMeridiem ? { hours: 12 } : {})), timeSegmentMaxValues);
|
|
2054
|
+
const enrichedTimeSegmentMinValues = Object.assign(Object.assign(Object.assign({}, DEFAULT_TIME_SEGMENT_MIN_VALUES), (hasMeridiem ? { hours: 1 } : {})), timeSegmentMinValues);
|
|
2055
|
+
const maskExpression = [...prefix, ...createTimeMaskExpression(mode)];
|
|
2056
|
+
return {
|
|
2057
|
+
mask: postfix
|
|
2058
|
+
? ({ value }) => cutExpression(maskExpression, value).concat(...postfix)
|
|
2059
|
+
: maskExpression,
|
|
2060
|
+
preprocessors: [
|
|
2061
|
+
createFullWidthToHalfWidthPreprocessor(),
|
|
2062
|
+
createColonConvertPreprocessor(),
|
|
2063
|
+
createZeroPlaceholdersPreprocessor(postfix),
|
|
2064
|
+
createMeridiemPreprocessor(mode),
|
|
2065
|
+
createInvalidTimeSegmentInsertionPreprocessor({
|
|
2066
|
+
timeMode: mode,
|
|
2067
|
+
timeSegmentMinValues: enrichedTimeSegmentMinValues,
|
|
2068
|
+
timeSegmentMaxValues: enrichedTimeSegmentMaxValues,
|
|
2069
|
+
}),
|
|
2070
|
+
],
|
|
2071
|
+
postprocessors: [
|
|
2072
|
+
createMeridiemPostprocessor(mode),
|
|
2073
|
+
(elementState) => enrichTimeSegmentsWithZeroes(elementState, {
|
|
2074
|
+
mode,
|
|
2075
|
+
timeSegmentMaxValues: enrichedTimeSegmentMaxValues,
|
|
2076
|
+
}),
|
|
2077
|
+
maskitoPrefixPostprocessorGenerator(prefix),
|
|
2078
|
+
maskitoPostfixPostprocessorGenerator(postfix),
|
|
2079
|
+
],
|
|
2080
|
+
plugins: [
|
|
2081
|
+
createTimeSegmentsSteppingPlugin({
|
|
2082
|
+
fullMode: mode,
|
|
2083
|
+
step,
|
|
2084
|
+
timeSegmentMaxValues: enrichedTimeSegmentMaxValues,
|
|
2085
|
+
}),
|
|
2086
|
+
createMeridiemSteppingPlugin(mode.indexOf('AA')),
|
|
2087
|
+
],
|
|
2088
|
+
overwriteMode: 'replace',
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
/**
|
|
2092
|
+
* Without cutting, the mask expression removes postfix on the last digit deletion
|
|
2093
|
+
* ___
|
|
2094
|
+
* Case 1 (static pattern mask expression)
|
|
2095
|
+
* Mask expression is [/\d/, /\d/, ':', /\d/, /\d/, ' left']
|
|
2096
|
+
* 12:34| left => Press Backspace => 12:3|
|
|
2097
|
+
* Mask correctly removes postfix because it's fixed characters after not yet inserted 4th digit.
|
|
2098
|
+
* ___
|
|
2099
|
+
* Case 2 (dynamic pattern mask expression)
|
|
2100
|
+
* Mask expression is [/\d/, /\d/, ':', /\d/, /\d/, ' left'] & textfield contains `12:34 left`
|
|
2101
|
+
* 12:34| left => Press Backspace => Mask expression becomes [/\d/, /\d/, ':', /\d/, ' left'] => 12:3| left
|
|
2102
|
+
* Mask correctly does not remove postfix because it's trailing fixed characters
|
|
2103
|
+
* and all non-fixed characters were already inserted.
|
|
2104
|
+
*/
|
|
2105
|
+
function cutExpression(expression, value) {
|
|
2106
|
+
let digitsCount = Math.min(value.replaceAll(/\D/g, '').length, expression.filter((x) => typeof x !== 'string').length) || 1;
|
|
2107
|
+
const afterLastDigit = expression.findIndex((x) => typeof x !== 'string' && !--digitsCount) + 1;
|
|
2108
|
+
return expression.slice(0, afterLastDigit);
|
|
2109
|
+
}
|
|
2110
|
+
|
|
1655
2111
|
/**
|
|
1656
2112
|
* React adds `_valueTracker` property to every textfield elements for its internal logic with controlled inputs.
|
|
1657
2113
|
* Also, React monkey-patches `value`-setter of the native textfield elements to update state inside its `_valueTracker`.
|
|
@@ -1765,14 +2221,6 @@ const useMaskito = ({
|
|
|
1765
2221
|
return onRefChange;
|
|
1766
2222
|
};
|
|
1767
2223
|
|
|
1768
|
-
function usePrevious(value) {
|
|
1769
|
-
const ref = useRef();
|
|
1770
|
-
useEffect(() => {
|
|
1771
|
-
ref.current = value;
|
|
1772
|
-
}, [value]);
|
|
1773
|
-
return ref.current;
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
2224
|
function usePopoverSupport() {
|
|
1777
2225
|
const [popoverSupported, setPopoverSupported] = useState(
|
|
1778
2226
|
void 0
|
|
@@ -1783,1430 +2231,5 @@ function usePopoverSupport() {
|
|
|
1783
2231
|
return popoverSupported;
|
|
1784
2232
|
}
|
|
1785
2233
|
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
if (actionType === "insert" && // the user is inserting a character
|
|
1789
|
-
data === separator && // the user typed the separator character
|
|
1790
|
-
elementState.selection[0] === elementState.selection[1]) {
|
|
1791
|
-
const selectionIndex = elementState.selection[0];
|
|
1792
|
-
const separatorPositions = templateString.split("").map((char, index) => char === separator ? index : null).filter((position) => position !== null).filter((position) => position > selectionIndex);
|
|
1793
|
-
const noRemainingSegments = !separatorPositions.length;
|
|
1794
|
-
const nothingEnteredYet = !elementState.value.length;
|
|
1795
|
-
const previousCharacterIsNotADigit = !/^\d$/.test(
|
|
1796
|
-
elementState.value.slice(-1)
|
|
1797
|
-
);
|
|
1798
|
-
const currentCharacterIsSeparator = templateString[selectionIndex] === separator;
|
|
1799
|
-
if (noRemainingSegments || nothingEnteredYet || previousCharacterIsNotADigit || currentCharacterIsSeparator) {
|
|
1800
|
-
return { elementState, data };
|
|
1801
|
-
}
|
|
1802
|
-
const firstIndexOfSegment = Math.max(
|
|
1803
|
-
elementState.value.lastIndexOf(separator) + 1,
|
|
1804
|
-
elementState.value.lastIndexOf(" ") + 1,
|
|
1805
|
-
0
|
|
1806
|
-
);
|
|
1807
|
-
const lastIndexOfSegment = separatorPositions[0];
|
|
1808
|
-
const digitsCurrentlyInSegment = elementState.value.slice(firstIndexOfSegment);
|
|
1809
|
-
const targetNumberOfDigitsInSegment = templateString.slice(
|
|
1810
|
-
firstIndexOfSegment,
|
|
1811
|
-
lastIndexOfSegment
|
|
1812
|
-
).length;
|
|
1813
|
-
const newSegment = digitsCurrentlyInSegment.padStart(
|
|
1814
|
-
targetNumberOfDigitsInSegment,
|
|
1815
|
-
"0"
|
|
1816
|
-
);
|
|
1817
|
-
const newValue = `${elementState.value.slice(0, firstIndexOfSegment)}${newSegment}`;
|
|
1818
|
-
return {
|
|
1819
|
-
elementState: {
|
|
1820
|
-
value: newValue + data,
|
|
1821
|
-
selection: [newValue.length, newValue.length]
|
|
1822
|
-
},
|
|
1823
|
-
data
|
|
1824
|
-
};
|
|
1825
|
-
}
|
|
1826
|
-
return { elementState, data };
|
|
1827
|
-
};
|
|
1828
|
-
return zeroShortcutPreprocessor;
|
|
1829
|
-
};
|
|
1830
|
-
|
|
1831
|
-
const datePlaceholderMask$1 = ({
|
|
1832
|
-
mode,
|
|
1833
|
-
separator = "/",
|
|
1834
|
-
placeholder
|
|
1835
|
-
}) => {
|
|
1836
|
-
const dateOptions = maskitoDateOptionsGenerator({
|
|
1837
|
-
mode,
|
|
1838
|
-
separator
|
|
1839
|
-
});
|
|
1840
|
-
const { plugins, removePlaceholder, ...placeholderOptions } = maskitoWithPlaceholder(placeholder);
|
|
1841
|
-
const datePlaceholderMask2 = {
|
|
1842
|
-
...dateOptions,
|
|
1843
|
-
plugins: plugins.concat(dateOptions.plugins || []),
|
|
1844
|
-
preprocessors: [
|
|
1845
|
-
...placeholderOptions.preprocessors,
|
|
1846
|
-
...dateOptions.preprocessors,
|
|
1847
|
-
makeZeroShortcutPreprocessor(mode, separator)
|
|
1848
|
-
],
|
|
1849
|
-
postprocessors: [
|
|
1850
|
-
...dateOptions.postprocessors,
|
|
1851
|
-
...placeholderOptions.postprocessors
|
|
1852
|
-
]
|
|
1853
|
-
};
|
|
1854
|
-
return { options: datePlaceholderMask2, removePlaceholder };
|
|
1855
|
-
};
|
|
1856
|
-
|
|
1857
|
-
const DateModeToFormatMap = {
|
|
1858
|
-
"mm/dd/yyyy": "MM/dd/yyyy",
|
|
1859
|
-
"dd/mm/yyyy": "dd/MM/yyyy",
|
|
1860
|
-
"yyyy/mm/dd": "yyyy/MM/dd"
|
|
1861
|
-
};
|
|
1862
|
-
const DateModeToPlaceholderMap = {
|
|
1863
|
-
"dd/mm/yyyy": "__/__/____",
|
|
1864
|
-
"mm/dd/yyyy": "__/__/____",
|
|
1865
|
-
"yyyy/mm/dd": "____/__/__"
|
|
1866
|
-
};
|
|
1867
|
-
|
|
1868
|
-
const MaskedDateInput = forwardRef(
|
|
1869
|
-
({
|
|
1870
|
-
onChange,
|
|
1871
|
-
mode = "mm/dd/yyyy",
|
|
1872
|
-
lastValidDate,
|
|
1873
|
-
disableHint = false,
|
|
1874
|
-
...props
|
|
1875
|
-
}, ref) => {
|
|
1876
|
-
const placeholder = DateModeToPlaceholderMap[mode];
|
|
1877
|
-
const [inputValue, setInputValue] = useState(placeholder);
|
|
1878
|
-
const { options, removePlaceholder } = datePlaceholderMask$1({
|
|
1879
|
-
mode,
|
|
1880
|
-
placeholder
|
|
1881
|
-
});
|
|
1882
|
-
const maskedInputRef = useMaskito({ options });
|
|
1883
|
-
const inputRef = useRef(null);
|
|
1884
|
-
const combinedRef = useMergeRefs$1([maskedInputRef, inputRef, ref]);
|
|
1885
|
-
const previousDateRef = useRef(null);
|
|
1886
|
-
const previousMode = usePrevious(mode);
|
|
1887
|
-
useEffect(() => {
|
|
1888
|
-
if (mode !== previousMode) {
|
|
1889
|
-
setInputValue(
|
|
1890
|
-
(oldInputValue) => swapMode$2(oldInputValue, previousMode ?? mode, mode)
|
|
1891
|
-
);
|
|
1892
|
-
}
|
|
1893
|
-
}, [mode, previousMode]);
|
|
1894
|
-
useEffect(() => {
|
|
1895
|
-
if (lastValidDate === void 0) return;
|
|
1896
|
-
if (lastValidDate === previousDateRef.current) return;
|
|
1897
|
-
if (!lastValidDate?.equals(previousDateRef.current ?? DateTime.now())) {
|
|
1898
|
-
setInputValue(
|
|
1899
|
-
lastValidDate?.toFormat(DateModeToFormatMap[mode]) ?? placeholder
|
|
1900
|
-
);
|
|
1901
|
-
previousDateRef.current = lastValidDate;
|
|
1902
|
-
}
|
|
1903
|
-
}, [lastValidDate, mode, placeholder]);
|
|
1904
|
-
const currentParsedData = useMemo(() => {
|
|
1905
|
-
return parseInputValue$1(inputValue, mode, removePlaceholder);
|
|
1906
|
-
}, [inputValue, mode, removePlaceholder]);
|
|
1907
|
-
const handleChange = (event) => {
|
|
1908
|
-
setInputValue(event.target.value);
|
|
1909
|
-
const { date, isInputValid, isInputEmpty } = parseInputValue$1(
|
|
1910
|
-
event.target.value,
|
|
1911
|
-
mode,
|
|
1912
|
-
removePlaceholder
|
|
1913
|
-
);
|
|
1914
|
-
const isValueDifferent = isInputValid !== currentParsedData.isInputValid || // The input has changed validity
|
|
1915
|
-
isInputEmpty !== currentParsedData.isInputEmpty || // The input has changed emptiness
|
|
1916
|
-
date === null !== (lastValidDate === null) || // The input has changed from empty to non-empty or vice versa
|
|
1917
|
-
date !== null && lastValidDate !== null && !date.equals(lastValidDate);
|
|
1918
|
-
if (!isValueDifferent) return;
|
|
1919
|
-
onChange?.({
|
|
1920
|
-
date: date ?? lastValidDate ?? null,
|
|
1921
|
-
isInputValid,
|
|
1922
|
-
isInputEmpty
|
|
1923
|
-
});
|
|
1924
|
-
};
|
|
1925
|
-
useImperativeHandle(ref, () => {
|
|
1926
|
-
const input = inputRef.current;
|
|
1927
|
-
if (!input) return null;
|
|
1928
|
-
return Object.assign(input, {
|
|
1929
|
-
setDate: (date) => {
|
|
1930
|
-
setInputValue(
|
|
1931
|
-
date?.toFormat(DateModeToFormatMap[mode]) ?? placeholder
|
|
1932
|
-
);
|
|
1933
|
-
}
|
|
1934
|
-
});
|
|
1935
|
-
}, [mode, placeholder]);
|
|
1936
|
-
return /* @__PURE__ */ jsx(
|
|
1937
|
-
TextField,
|
|
1938
|
-
{
|
|
1939
|
-
ref: combinedRef,
|
|
1940
|
-
"data-date": lastValidDate?.toISODate() ?? "",
|
|
1941
|
-
"data-input-valid": currentParsedData.isInputValid,
|
|
1942
|
-
"data-input-empty": currentParsedData.isInputEmpty,
|
|
1943
|
-
...props,
|
|
1944
|
-
showCounter: false,
|
|
1945
|
-
value: inputValue,
|
|
1946
|
-
onChange: handleChange,
|
|
1947
|
-
prefix: /* @__PURE__ */ jsx(Icon, { svg: SvgEvent }),
|
|
1948
|
-
hint: disableHint ? void 0 : `Format: ${mode}`
|
|
1949
|
-
}
|
|
1950
|
-
);
|
|
1951
|
-
}
|
|
1952
|
-
);
|
|
1953
|
-
MaskedDateInput.displayName = "MaskedDateInput";
|
|
1954
|
-
function parseInputValue$1(value, mode, removePlaceholder) {
|
|
1955
|
-
const valueMinusPlaceholder = removePlaceholder(value);
|
|
1956
|
-
const jsDate = maskitoParseDate(valueMinusPlaceholder, { mode });
|
|
1957
|
-
const luxonDate = jsDate ? DateTime.fromJSDate(jsDate, { zone: "utc" }) : null;
|
|
1958
|
-
return {
|
|
1959
|
-
date: luxonDate,
|
|
1960
|
-
isInputValid: !!luxonDate,
|
|
1961
|
-
isInputEmpty: valueMinusPlaceholder === ""
|
|
1962
|
-
};
|
|
1963
|
-
}
|
|
1964
|
-
function swapMode$2(inputString, previousMode, mode) {
|
|
1965
|
-
const { day, month, year } = divideSegments$1(inputString, previousMode);
|
|
1966
|
-
return orderSegmentsByMode(day, month, year, mode);
|
|
1967
|
-
}
|
|
1968
|
-
function divideSegments$1(value, mode) {
|
|
1969
|
-
const [segment1, segment2, segment3] = value.split("/");
|
|
1970
|
-
if (mode === "dd/mm/yyyy") {
|
|
1971
|
-
return { day: segment1, month: segment2, year: segment3 };
|
|
1972
|
-
} else if (mode === "mm/dd/yyyy") {
|
|
1973
|
-
return { day: segment2, month: segment1, year: segment3 };
|
|
1974
|
-
} else if (mode === "yyyy/mm/dd") {
|
|
1975
|
-
return { day: segment3, month: segment2, year: segment1 };
|
|
1976
|
-
}
|
|
1977
|
-
return { day: "__", month: "__", year: "____" };
|
|
1978
|
-
}
|
|
1979
|
-
function orderSegmentsByMode(day, month, year, mode) {
|
|
1980
|
-
if (mode === "dd/mm/yyyy") {
|
|
1981
|
-
return `${day}/${month}/${year}`;
|
|
1982
|
-
} else if (mode === "mm/dd/yyyy") {
|
|
1983
|
-
return `${month}/${day}/${year}`;
|
|
1984
|
-
} else if (mode === "yyyy/mm/dd") {
|
|
1985
|
-
return `${year}/${month}/${day}`;
|
|
1986
|
-
} else {
|
|
1987
|
-
return "";
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
function containsRelatedTarget(event) {
|
|
1992
|
-
if (event.currentTarget instanceof HTMLElement && event.relatedTarget instanceof HTMLElement) {
|
|
1993
|
-
return event.currentTarget.contains(event.relatedTarget);
|
|
1994
|
-
}
|
|
1995
|
-
return false;
|
|
1996
|
-
}
|
|
1997
|
-
function useFocusWithin({
|
|
1998
|
-
onBlur,
|
|
1999
|
-
onFocus
|
|
2000
|
-
} = {}) {
|
|
2001
|
-
const [focused, setFocused] = useState(false);
|
|
2002
|
-
const focusedRef = useRef(false);
|
|
2003
|
-
const elementRef = useRef(null);
|
|
2004
|
-
const _setFocused = useCallback((value) => {
|
|
2005
|
-
setFocused(value);
|
|
2006
|
-
focusedRef.current = value;
|
|
2007
|
-
}, []);
|
|
2008
|
-
const handleFocusIn = useCallback(
|
|
2009
|
-
(event) => {
|
|
2010
|
-
if (!focusedRef.current) {
|
|
2011
|
-
_setFocused(true);
|
|
2012
|
-
onFocus?.(event);
|
|
2013
|
-
}
|
|
2014
|
-
},
|
|
2015
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2016
|
-
[onFocus]
|
|
2017
|
-
);
|
|
2018
|
-
const handleFocusOut = useCallback(
|
|
2019
|
-
(event) => {
|
|
2020
|
-
if (focusedRef.current && !containsRelatedTarget(event)) {
|
|
2021
|
-
_setFocused(false);
|
|
2022
|
-
onBlur?.(event);
|
|
2023
|
-
}
|
|
2024
|
-
},
|
|
2025
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2026
|
-
[onBlur]
|
|
2027
|
-
);
|
|
2028
|
-
useEffect(() => {
|
|
2029
|
-
const element = elementRef.current;
|
|
2030
|
-
if (!element) {
|
|
2031
|
-
return;
|
|
2032
|
-
}
|
|
2033
|
-
element.addEventListener("focusin", handleFocusIn);
|
|
2034
|
-
element.addEventListener("focusout", handleFocusOut);
|
|
2035
|
-
return () => {
|
|
2036
|
-
element.removeEventListener("focusin", handleFocusIn);
|
|
2037
|
-
element.removeEventListener("focusout", handleFocusOut);
|
|
2038
|
-
};
|
|
2039
|
-
}, [handleFocusIn, handleFocusOut]);
|
|
2040
|
-
return { ref: elementRef, focused };
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
function convertStringToDate(v) {
|
|
2044
|
-
if (v === void 0 || v === null) {
|
|
2045
|
-
return v;
|
|
2046
|
-
}
|
|
2047
|
-
const date = DateTime.fromISO(v, { setZone: true, zone: "utc" }).startOf(
|
|
2048
|
-
"day"
|
|
2049
|
-
);
|
|
2050
|
-
if (date.isValid) {
|
|
2051
|
-
return date;
|
|
2052
|
-
}
|
|
2053
|
-
return null;
|
|
2054
|
-
}
|
|
2055
|
-
function validateDate({
|
|
2056
|
-
date,
|
|
2057
|
-
constraints
|
|
2058
|
-
}) {
|
|
2059
|
-
const { required, unavailable, minDate, maxDate } = constraints;
|
|
2060
|
-
if (!date) {
|
|
2061
|
-
return required ? false : true;
|
|
2062
|
-
}
|
|
2063
|
-
if (unavailable?.dates?.some((d) => d.equals(date))) {
|
|
2064
|
-
return false;
|
|
2065
|
-
}
|
|
2066
|
-
if (unavailable?.daysOfWeek?.includes(date.weekday)) {
|
|
2067
|
-
return false;
|
|
2068
|
-
}
|
|
2069
|
-
if (minDate && date < minDate) {
|
|
2070
|
-
return false;
|
|
2071
|
-
}
|
|
2072
|
-
if (maxDate && date > maxDate) {
|
|
2073
|
-
return false;
|
|
2074
|
-
}
|
|
2075
|
-
return true;
|
|
2076
|
-
}
|
|
2077
|
-
function validateYearlessDate({
|
|
2078
|
-
value,
|
|
2079
|
-
constraints
|
|
2080
|
-
}) {
|
|
2081
|
-
const { required, unavailable, minDate, maxDate } = constraints;
|
|
2082
|
-
if (!value || !value?.day && !value?.month) {
|
|
2083
|
-
return required ? false : true;
|
|
2084
|
-
}
|
|
2085
|
-
if (value.day === null || value.month === null) {
|
|
2086
|
-
return false;
|
|
2087
|
-
}
|
|
2088
|
-
if (unavailable?.dates?.some(
|
|
2089
|
-
(d) => d.day === value.day && d.month === value.month
|
|
2090
|
-
)) {
|
|
2091
|
-
return false;
|
|
2092
|
-
}
|
|
2093
|
-
const minDateMonth = minDate?.month ?? 1;
|
|
2094
|
-
const minDateDay = minDate?.day ?? 1;
|
|
2095
|
-
const maxDateMonth = maxDate?.month ?? 12;
|
|
2096
|
-
const maxDateDay = maxDate?.day ?? 31;
|
|
2097
|
-
if (value.month < minDateMonth || // Earlier month
|
|
2098
|
-
value.month === minDateMonth && value.day < minDateDay) {
|
|
2099
|
-
return false;
|
|
2100
|
-
}
|
|
2101
|
-
if (value.month > maxDateMonth || // Later month
|
|
2102
|
-
value.month === maxDateMonth && value.day > maxDateDay) {
|
|
2103
|
-
return false;
|
|
2104
|
-
}
|
|
2105
|
-
return true;
|
|
2106
|
-
}
|
|
2107
|
-
|
|
2108
|
-
function useDateFieldSingleConversion({
|
|
2109
|
-
value,
|
|
2110
|
-
defaultValue,
|
|
2111
|
-
minDate,
|
|
2112
|
-
maxDate,
|
|
2113
|
-
unavailable,
|
|
2114
|
-
onChange
|
|
2115
|
-
}) {
|
|
2116
|
-
const normalizedValue = useMemo(() => convertStringToDate(value), [value]);
|
|
2117
|
-
const normalizedDefaultValue = useMemo(
|
|
2118
|
-
() => convertStringToDate(defaultValue),
|
|
2119
|
-
[defaultValue]
|
|
2120
|
-
);
|
|
2121
|
-
const normalizedMinDate = useMemo(
|
|
2122
|
-
() => convertStringToDate(minDate),
|
|
2123
|
-
[minDate]
|
|
2124
|
-
);
|
|
2125
|
-
const normalizedMaxDate = useMemo(
|
|
2126
|
-
() => convertStringToDate(maxDate),
|
|
2127
|
-
[maxDate]
|
|
2128
|
-
);
|
|
2129
|
-
const normalizedUnavailableDates = useMemo(
|
|
2130
|
-
() => unavailable?.dates?.map((d) => convertStringToDate(d)).filter((d) => d !== null && d !== void 0),
|
|
2131
|
-
[unavailable?.dates]
|
|
2132
|
-
);
|
|
2133
|
-
const handleChange = useCallback(
|
|
2134
|
-
(change) => {
|
|
2135
|
-
onChange?.({
|
|
2136
|
-
...change,
|
|
2137
|
-
date: change.date?.toISODate() ?? null
|
|
2138
|
-
});
|
|
2139
|
-
},
|
|
2140
|
-
[onChange]
|
|
2141
|
-
);
|
|
2142
|
-
return {
|
|
2143
|
-
value: normalizedValue,
|
|
2144
|
-
defaultValue: normalizedDefaultValue,
|
|
2145
|
-
minDate: normalizedMinDate,
|
|
2146
|
-
maxDate: normalizedMaxDate,
|
|
2147
|
-
unavailable: {
|
|
2148
|
-
dates: normalizedUnavailableDates,
|
|
2149
|
-
daysOfWeek: unavailable?.daysOfWeek
|
|
2150
|
-
},
|
|
2151
|
-
onChange: handleChange
|
|
2152
|
-
};
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
|
-
function DateFieldSingleCalendar({
|
|
2156
|
-
onKeyDown,
|
|
2157
|
-
value,
|
|
2158
|
-
onSelection,
|
|
2159
|
-
minDate,
|
|
2160
|
-
maxDate,
|
|
2161
|
-
unavailable,
|
|
2162
|
-
...rest
|
|
2163
|
-
}) {
|
|
2164
|
-
const handleCalendarSelection = (data) => {
|
|
2165
|
-
if (data.value) {
|
|
2166
|
-
const date = DateTime.fromISO(data.value, { zone: "utc" });
|
|
2167
|
-
onSelection(date);
|
|
2168
|
-
}
|
|
2169
|
-
};
|
|
2170
|
-
return /* @__PURE__ */ jsx(
|
|
2171
|
-
Calendar,
|
|
2172
|
-
{
|
|
2173
|
-
...rest,
|
|
2174
|
-
range: false,
|
|
2175
|
-
onKeyDown,
|
|
2176
|
-
defaultFocusedDate: value?.toISODate() || DateTime.now().toISODate(),
|
|
2177
|
-
value: value?.toISODate() || void 0,
|
|
2178
|
-
onSelection: handleCalendarSelection,
|
|
2179
|
-
defaultTimeZone: "UTC",
|
|
2180
|
-
minDate: minDate?.toISODate() ?? void 0,
|
|
2181
|
-
maxDate: maxDate?.toISODate() ?? void 0,
|
|
2182
|
-
unavailable: unavailable ? {
|
|
2183
|
-
dates: unavailable.dates?.map((d) => d.toISODate() ?? ""),
|
|
2184
|
-
daysOfWeek: unavailable.daysOfWeek
|
|
2185
|
-
} : void 0,
|
|
2186
|
-
_disableAutofocus: true
|
|
2187
|
-
}
|
|
2188
|
-
);
|
|
2189
|
-
}
|
|
2190
|
-
|
|
2191
|
-
const useDateFieldOrchestration = ({
|
|
2192
|
-
inputRef,
|
|
2193
|
-
calendarDefaultOpen,
|
|
2194
|
-
popoverContentRef,
|
|
2195
|
-
disableCalendar = false
|
|
2196
|
-
}) => {
|
|
2197
|
-
const documentRef = useRef(document.body);
|
|
2198
|
-
const [calendarOpen, setCalendarOpen] = useState(calendarDefaultOpen);
|
|
2199
|
-
const { focusables } = useKeyboardFocusables(documentRef, {
|
|
2200
|
-
observeChange: true
|
|
2201
|
-
});
|
|
2202
|
-
const { focusables: popoverFocusables } = useKeyboardFocusables(
|
|
2203
|
-
popoverContentRef,
|
|
2204
|
-
{
|
|
2205
|
-
observeChange: true
|
|
2206
|
-
}
|
|
2207
|
-
);
|
|
2208
|
-
const pageFocusables = focusables?.filter(
|
|
2209
|
-
(item) => !popoverFocusables?.includes(item)
|
|
2210
|
-
);
|
|
2211
|
-
const handleCalendarKeyDown = (event) => {
|
|
2212
|
-
if (event.key === "Escape") {
|
|
2213
|
-
inputRef.current?.focus();
|
|
2214
|
-
}
|
|
2215
|
-
};
|
|
2216
|
-
const focusToCalendar = () => {
|
|
2217
|
-
if (popoverContentRef.current) {
|
|
2218
|
-
const currentFocusable = popoverContentRef.current.querySelectorAll('[tabindex = "0"]')[0];
|
|
2219
|
-
if (currentFocusable) {
|
|
2220
|
-
currentFocusable.focus();
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
};
|
|
2224
|
-
const handleInputKeyDown = (ev) => {
|
|
2225
|
-
if (disableCalendar) {
|
|
2226
|
-
return;
|
|
2227
|
-
}
|
|
2228
|
-
let currentFocusIndex = 0;
|
|
2229
|
-
switch (ev.key) {
|
|
2230
|
-
case "Escape":
|
|
2231
|
-
setCalendarOpen(false);
|
|
2232
|
-
break;
|
|
2233
|
-
case "Tab":
|
|
2234
|
-
if (!calendarOpen || !pageFocusables?.length) {
|
|
2235
|
-
break;
|
|
2236
|
-
}
|
|
2237
|
-
ev.preventDefault();
|
|
2238
|
-
currentFocusIndex = pageFocusables.indexOf(inputRef.current) || 0;
|
|
2239
|
-
setCalendarOpen(false);
|
|
2240
|
-
if (ev.shiftKey) {
|
|
2241
|
-
if (currentFocusIndex === 0) {
|
|
2242
|
-
requestAnimationFrame(
|
|
2243
|
-
() => pageFocusables[pageFocusables.length - 1].focus()
|
|
2244
|
-
);
|
|
2245
|
-
} else {
|
|
2246
|
-
requestAnimationFrame(
|
|
2247
|
-
() => pageFocusables[currentFocusIndex - 1].focus()
|
|
2248
|
-
);
|
|
2249
|
-
}
|
|
2250
|
-
break;
|
|
2251
|
-
}
|
|
2252
|
-
if (pageFocusables.length > currentFocusIndex + 1) {
|
|
2253
|
-
requestAnimationFrame(
|
|
2254
|
-
() => pageFocusables[currentFocusIndex + 1].focus()
|
|
2255
|
-
);
|
|
2256
|
-
} else {
|
|
2257
|
-
requestAnimationFrame(() => pageFocusables[0].focus());
|
|
2258
|
-
}
|
|
2259
|
-
break;
|
|
2260
|
-
case "ArrowDown":
|
|
2261
|
-
if (!calendarOpen) {
|
|
2262
|
-
setCalendarOpen(true);
|
|
2263
|
-
setTimeout(focusToCalendar, 200);
|
|
2264
|
-
} else {
|
|
2265
|
-
focusToCalendar();
|
|
2266
|
-
}
|
|
2267
|
-
}
|
|
2268
|
-
};
|
|
2269
|
-
return {
|
|
2270
|
-
calendarOpen,
|
|
2271
|
-
setCalendarOpen,
|
|
2272
|
-
handleCalendarKeyDown,
|
|
2273
|
-
handleInputKeyDown
|
|
2274
|
-
};
|
|
2275
|
-
};
|
|
2276
|
-
|
|
2277
|
-
function useDateFieldSingleState({
|
|
2278
|
-
valueProp,
|
|
2279
|
-
defaultValueProp,
|
|
2280
|
-
inputRef,
|
|
2281
|
-
onChange
|
|
2282
|
-
}) {
|
|
2283
|
-
const [value, setValue] = useOptionallyControlledState({
|
|
2284
|
-
controlledValue: valueProp,
|
|
2285
|
-
defaultValue: defaultValueProp
|
|
2286
|
-
});
|
|
2287
|
-
const setSharedValue = (date) => {
|
|
2288
|
-
inputRef.current?.setDate(date);
|
|
2289
|
-
setValue(date);
|
|
2290
|
-
};
|
|
2291
|
-
const handleInputChange = (change) => {
|
|
2292
|
-
const date = change.isInputEmpty ? null : change.date?.startOf("day") ?? null;
|
|
2293
|
-
onChange?.({
|
|
2294
|
-
date,
|
|
2295
|
-
isInputValid: change.isInputValid,
|
|
2296
|
-
isInputEmpty: change.isInputEmpty
|
|
2297
|
-
});
|
|
2298
|
-
if (change.isInputValid) {
|
|
2299
|
-
setSharedValue(change.isInputEmpty ? null : change.date);
|
|
2300
|
-
}
|
|
2301
|
-
if (change.isInputEmpty) {
|
|
2302
|
-
setSharedValue(null);
|
|
2303
|
-
return;
|
|
2304
|
-
}
|
|
2305
|
-
if (change.date) {
|
|
2306
|
-
setSharedValue(change.date);
|
|
2307
|
-
}
|
|
2308
|
-
};
|
|
2309
|
-
const handleCalendarSelection = (date) => {
|
|
2310
|
-
setSharedValue(date);
|
|
2311
|
-
onChange?.({
|
|
2312
|
-
date,
|
|
2313
|
-
isInputValid: true,
|
|
2314
|
-
isInputEmpty: false
|
|
2315
|
-
});
|
|
2316
|
-
};
|
|
2317
|
-
return {
|
|
2318
|
-
value,
|
|
2319
|
-
setValue: setSharedValue,
|
|
2320
|
-
handleInputChange,
|
|
2321
|
-
handleCalendarSelection
|
|
2322
|
-
};
|
|
2323
|
-
}
|
|
2324
|
-
|
|
2325
|
-
const DateFieldSingle = ({
|
|
2326
|
-
onFocus,
|
|
2327
|
-
onBlur,
|
|
2328
|
-
disableCalendar,
|
|
2329
|
-
required,
|
|
2330
|
-
mode,
|
|
2331
|
-
value: valueProp,
|
|
2332
|
-
defaultValue: defaultValueProp,
|
|
2333
|
-
minDate: minDateProp,
|
|
2334
|
-
maxDate: maxDateProp,
|
|
2335
|
-
unavailable: unavailableProp,
|
|
2336
|
-
onChange: onChangeProp,
|
|
2337
|
-
...rest
|
|
2338
|
-
}) => {
|
|
2339
|
-
const inputRef = useRef(null);
|
|
2340
|
-
const {
|
|
2341
|
-
value: normalizedValue,
|
|
2342
|
-
defaultValue: normalizedDefaultValue,
|
|
2343
|
-
minDate,
|
|
2344
|
-
maxDate,
|
|
2345
|
-
unavailable,
|
|
2346
|
-
onChange
|
|
2347
|
-
} = useDateFieldSingleConversion({
|
|
2348
|
-
value: valueProp,
|
|
2349
|
-
defaultValue: defaultValueProp,
|
|
2350
|
-
onChange: onChangeProp,
|
|
2351
|
-
minDate: minDateProp,
|
|
2352
|
-
maxDate: maxDateProp,
|
|
2353
|
-
unavailable: unavailableProp
|
|
2354
|
-
});
|
|
2355
|
-
const handleChange = (change) => {
|
|
2356
|
-
onChange?.({
|
|
2357
|
-
...change,
|
|
2358
|
-
isDateValid: validateDate({
|
|
2359
|
-
date: change.date,
|
|
2360
|
-
constraints: {
|
|
2361
|
-
required,
|
|
2362
|
-
unavailable,
|
|
2363
|
-
minDate: minDate ?? void 0,
|
|
2364
|
-
maxDate: maxDate ?? void 0
|
|
2365
|
-
}
|
|
2366
|
-
})
|
|
2367
|
-
});
|
|
2368
|
-
};
|
|
2369
|
-
const { value, handleInputChange, handleCalendarSelection } = useDateFieldSingleState({
|
|
2370
|
-
valueProp: normalizedValue,
|
|
2371
|
-
defaultValueProp: normalizedDefaultValue,
|
|
2372
|
-
inputRef,
|
|
2373
|
-
onChange: handleChange
|
|
2374
|
-
});
|
|
2375
|
-
const { ref: wrapperRef } = useFocusWithin({
|
|
2376
|
-
onBlur,
|
|
2377
|
-
onFocus
|
|
2378
|
-
});
|
|
2379
|
-
const [popoverTriggerRef, setPopoverTriggerRef] = useState();
|
|
2380
|
-
const popoverContentRef = useRef(null);
|
|
2381
|
-
const combinedRef = useMergeRefs([popoverTriggerRef, inputRef]);
|
|
2382
|
-
const {
|
|
2383
|
-
calendarOpen,
|
|
2384
|
-
setCalendarOpen,
|
|
2385
|
-
handleCalendarKeyDown,
|
|
2386
|
-
handleInputKeyDown
|
|
2387
|
-
} = useDateFieldOrchestration({
|
|
2388
|
-
inputRef,
|
|
2389
|
-
calendarDefaultOpen: false,
|
|
2390
|
-
popoverContentRef,
|
|
2391
|
-
disableCalendar
|
|
2392
|
-
});
|
|
2393
|
-
const popoverSupported = usePopoverSupport();
|
|
2394
|
-
const shouldShowCalendar = usePopoverCloseDelayWorkaround(calendarOpen);
|
|
2395
|
-
const currentValidity = useMemo(
|
|
2396
|
-
() => validateDate({
|
|
2397
|
-
date: value,
|
|
2398
|
-
constraints: {
|
|
2399
|
-
required,
|
|
2400
|
-
unavailable,
|
|
2401
|
-
minDate: minDate ?? void 0,
|
|
2402
|
-
maxDate: maxDate ?? void 0
|
|
2403
|
-
}
|
|
2404
|
-
}),
|
|
2405
|
-
[value, required, unavailable, minDate, maxDate]
|
|
2406
|
-
);
|
|
2407
|
-
const justTheField = /* @__PURE__ */ jsx(
|
|
2408
|
-
MaskedDateInput,
|
|
2409
|
-
{
|
|
2410
|
-
...rest,
|
|
2411
|
-
mode,
|
|
2412
|
-
ref: combinedRef,
|
|
2413
|
-
onChange: handleInputChange,
|
|
2414
|
-
onKeyDown: handleInputKeyDown,
|
|
2415
|
-
onClick: () => setCalendarOpen(true),
|
|
2416
|
-
lastValidDate: value,
|
|
2417
|
-
required,
|
|
2418
|
-
autoComplete: "off",
|
|
2419
|
-
"data-date-valid": currentValidity
|
|
2420
|
-
}
|
|
2421
|
-
);
|
|
2422
|
-
if (disableCalendar) {
|
|
2423
|
-
return justTheField;
|
|
2424
|
-
}
|
|
2425
|
-
if (!popoverSupported) {
|
|
2426
|
-
return justTheField;
|
|
2427
|
-
}
|
|
2428
|
-
return /* @__PURE__ */ jsx("div", { ref: wrapperRef, children: /* @__PURE__ */ jsxs(
|
|
2429
|
-
Popover,
|
|
2430
|
-
{
|
|
2431
|
-
open: calendarOpen,
|
|
2432
|
-
modal: true,
|
|
2433
|
-
placement: "bottom-start",
|
|
2434
|
-
disableFlipFallback: true,
|
|
2435
|
-
disableTriggerFocus: true,
|
|
2436
|
-
onClose: () => setCalendarOpen(false),
|
|
2437
|
-
disableAutoUpdate: true,
|
|
2438
|
-
onClickOutside: () => setCalendarOpen(false),
|
|
2439
|
-
children: [
|
|
2440
|
-
/* @__PURE__ */ jsx(Popover.Trigger, { children: ({ ref: iRef }) => {
|
|
2441
|
-
setPopoverTriggerRef(iRef);
|
|
2442
|
-
return justTheField;
|
|
2443
|
-
} }),
|
|
2444
|
-
/* @__PURE__ */ jsx(Popover.Content, { ref: popoverContentRef, "data-testid": "calendar-popover", children: shouldShowCalendar && /* @__PURE__ */ jsx(
|
|
2445
|
-
DateFieldSingleCalendar,
|
|
2446
|
-
{
|
|
2447
|
-
onKeyDown: handleCalendarKeyDown,
|
|
2448
|
-
value,
|
|
2449
|
-
onSelection: handleCalendarSelection,
|
|
2450
|
-
minDate,
|
|
2451
|
-
maxDate,
|
|
2452
|
-
unavailable
|
|
2453
|
-
}
|
|
2454
|
-
) })
|
|
2455
|
-
]
|
|
2456
|
-
}
|
|
2457
|
-
) });
|
|
2458
|
-
};
|
|
2459
|
-
DateFieldSingle.displayName = "DateFieldSingle";
|
|
2460
|
-
|
|
2461
|
-
const datePlaceholderMask = ({
|
|
2462
|
-
mode,
|
|
2463
|
-
dateSeparator = "/",
|
|
2464
|
-
rangeSeparator = " - ",
|
|
2465
|
-
placeholder
|
|
2466
|
-
}) => {
|
|
2467
|
-
const dateRangeOptions = maskitoDateRangeOptionsGenerator({
|
|
2468
|
-
mode,
|
|
2469
|
-
dateSeparator,
|
|
2470
|
-
rangeSeparator
|
|
2471
|
-
});
|
|
2472
|
-
const { plugins, removePlaceholder, ...placeholderOptions } = maskitoWithPlaceholder(placeholder);
|
|
2473
|
-
const datePlaceholderMask2 = {
|
|
2474
|
-
...dateRangeOptions,
|
|
2475
|
-
plugins: plugins.concat(dateRangeOptions.plugins || []),
|
|
2476
|
-
preprocessors: [
|
|
2477
|
-
...placeholderOptions.preprocessors,
|
|
2478
|
-
...dateRangeOptions.preprocessors,
|
|
2479
|
-
makeZeroShortcutPreprocessor(placeholder, dateSeparator)
|
|
2480
|
-
],
|
|
2481
|
-
postprocessors: [
|
|
2482
|
-
// NOTE this is super fragile. If Maskito maintainers change the order of the post-processors, this will break.
|
|
2483
|
-
// The last postprocessor is the date swap postprocessor, which we don't want to run.
|
|
2484
|
-
// A unit test is added to ensure this doesn't break on a dependency update.
|
|
2485
|
-
...dateRangeOptions.postprocessors.slice(0, -1),
|
|
2486
|
-
...placeholderOptions.postprocessors
|
|
2487
|
-
]
|
|
2488
|
-
};
|
|
2489
|
-
return { options: datePlaceholderMask2, removePlaceholder };
|
|
2490
|
-
};
|
|
2491
|
-
|
|
2492
|
-
const RANGE_SEPARATOR = " - ";
|
|
2493
|
-
const MaskedDateRangeInput = forwardRef(
|
|
2494
|
-
({
|
|
2495
|
-
onChange,
|
|
2496
|
-
mode = "mm/dd/yyyy",
|
|
2497
|
-
startDate,
|
|
2498
|
-
endDate,
|
|
2499
|
-
disableHint = false,
|
|
2500
|
-
...props
|
|
2501
|
-
}, ref) => {
|
|
2502
|
-
const halfPlaceholder = DateModeToPlaceholderMap[mode];
|
|
2503
|
-
const fullPlaceholder = `${halfPlaceholder}${RANGE_SEPARATOR}${halfPlaceholder}`;
|
|
2504
|
-
const [inputValue, setInputValue] = useState(fullPlaceholder);
|
|
2505
|
-
const { options, removePlaceholder } = datePlaceholderMask({
|
|
2506
|
-
mode,
|
|
2507
|
-
placeholder: fullPlaceholder,
|
|
2508
|
-
dateSeparator: "/",
|
|
2509
|
-
rangeSeparator: RANGE_SEPARATOR
|
|
2510
|
-
});
|
|
2511
|
-
const maskedInputRef = useMaskito({ options });
|
|
2512
|
-
const inputRef = useRef(null);
|
|
2513
|
-
const combinedRef = useMergeRefs$1([maskedInputRef, inputRef, ref]);
|
|
2514
|
-
const previousStartDate = usePrevious(startDate);
|
|
2515
|
-
const previousEndDate = usePrevious(endDate);
|
|
2516
|
-
const previousMode = usePrevious(mode);
|
|
2517
|
-
useEffect(() => {
|
|
2518
|
-
if (mode !== previousMode) {
|
|
2519
|
-
setInputValue(
|
|
2520
|
-
(previousInputValue) => swapMode$1(previousInputValue, previousMode ?? mode, mode)
|
|
2521
|
-
);
|
|
2522
|
-
}
|
|
2523
|
-
}, [mode, fullPlaceholder, previousMode]);
|
|
2524
|
-
useEffect(() => {
|
|
2525
|
-
if (startDate === void 0 || endDate === void 0) return;
|
|
2526
|
-
if (startDate === previousStartDate && endDate === previousEndDate)
|
|
2527
|
-
return;
|
|
2528
|
-
if (
|
|
2529
|
-
// plus one just represents a date that is guaranteed to be different.
|
|
2530
|
-
startDate?.equals(previousStartDate ?? startDate?.plus({ days: 1 })) && (endDate?.equals(previousEndDate ?? endDate?.plus({ days: 1 })) || endDate === previousEndDate)
|
|
2531
|
-
)
|
|
2532
|
-
return;
|
|
2533
|
-
const startDateString = startDate?.toFormat(DateModeToFormatMap[mode]) ?? halfPlaceholder;
|
|
2534
|
-
const endDateString = endDate?.toFormat(DateModeToFormatMap[mode]) ?? halfPlaceholder;
|
|
2535
|
-
const newInputValue = `${startDateString}${RANGE_SEPARATOR}${endDateString}`;
|
|
2536
|
-
setInputValue(newInputValue);
|
|
2537
|
-
}, [
|
|
2538
|
-
startDate,
|
|
2539
|
-
endDate,
|
|
2540
|
-
mode,
|
|
2541
|
-
halfPlaceholder,
|
|
2542
|
-
previousStartDate,
|
|
2543
|
-
previousEndDate
|
|
2544
|
-
]);
|
|
2545
|
-
const currentParsedData = useMemo(() => {
|
|
2546
|
-
return parseRangeInputValue(inputValue, mode, removePlaceholder);
|
|
2547
|
-
}, [inputValue, mode, removePlaceholder]);
|
|
2548
|
-
const handleChange = (event) => {
|
|
2549
|
-
setInputValue(event.target.value);
|
|
2550
|
-
const {
|
|
2551
|
-
startDate: parsedStartDate,
|
|
2552
|
-
endDate: parsedEndDate,
|
|
2553
|
-
isInputValid,
|
|
2554
|
-
isInputEmpty,
|
|
2555
|
-
isHalfEmpty
|
|
2556
|
-
} = parseRangeInputValue(event.target.value, mode, removePlaceholder);
|
|
2557
|
-
const isValueDifferent = isInputValid !== currentParsedData.isInputValid || // The input has changed validity
|
|
2558
|
-
isInputEmpty !== currentParsedData.isInputEmpty || // The input has changed emptiness
|
|
2559
|
-
parsedStartDate === null !== (startDate === null) || // The start date has changed from empty to non-empty or vice versa
|
|
2560
|
-
parsedEndDate === null !== (endDate === null) || // The end date has changed from empty to non-empty or vice versa
|
|
2561
|
-
parsedStartDate !== null && startDate !== null && !parsedStartDate.equals(startDate) || // The start date has changed
|
|
2562
|
-
parsedEndDate !== null && endDate !== null && !parsedEndDate.equals(endDate);
|
|
2563
|
-
if (!isValueDifferent) return;
|
|
2564
|
-
onChange?.({
|
|
2565
|
-
startDate: isInputEmpty ? null : parsedStartDate ?? startDate ?? null,
|
|
2566
|
-
endDate: isInputEmpty || isHalfEmpty ? null : parsedEndDate ?? endDate ?? null,
|
|
2567
|
-
isInputValid,
|
|
2568
|
-
isInputEmpty
|
|
2569
|
-
});
|
|
2570
|
-
};
|
|
2571
|
-
useImperativeHandle(ref, () => {
|
|
2572
|
-
const input = inputRef.current;
|
|
2573
|
-
if (!input) return null;
|
|
2574
|
-
return Object.assign(input, {
|
|
2575
|
-
setDateRange: (startDate2, endDate2) => {
|
|
2576
|
-
const startDateString = startDate2?.toFormat(
|
|
2577
|
-
DateModeToFormatMap[mode]
|
|
2578
|
-
);
|
|
2579
|
-
const endDateString = endDate2?.toFormat(DateModeToFormatMap[mode]);
|
|
2580
|
-
const newInputValue = `${startDateString ?? halfPlaceholder}${RANGE_SEPARATOR}${endDateString ?? halfPlaceholder}`;
|
|
2581
|
-
setInputValue(newInputValue);
|
|
2582
|
-
}
|
|
2583
|
-
});
|
|
2584
|
-
}, [mode, halfPlaceholder]);
|
|
2585
|
-
return /* @__PURE__ */ jsx(
|
|
2586
|
-
TextField,
|
|
2587
|
-
{
|
|
2588
|
-
ref: combinedRef,
|
|
2589
|
-
"data-start-date": startDate?.toISODate() ?? "",
|
|
2590
|
-
"data-end-date": endDate?.toISODate() ?? "",
|
|
2591
|
-
"data-input-valid": currentParsedData.isInputValid,
|
|
2592
|
-
"data-input-empty": currentParsedData.isInputEmpty,
|
|
2593
|
-
...props,
|
|
2594
|
-
showCounter: false,
|
|
2595
|
-
value: inputValue,
|
|
2596
|
-
onChange: handleChange,
|
|
2597
|
-
prefix: /* @__PURE__ */ jsx(Icon, { svg: SvgEvent }),
|
|
2598
|
-
hint: disableHint ? void 0 : `Format: ${mode}`
|
|
2599
|
-
}
|
|
2600
|
-
);
|
|
2601
|
-
}
|
|
2602
|
-
);
|
|
2603
|
-
MaskedDateRangeInput.displayName = "MaskedDateRangeInput";
|
|
2604
|
-
function parseRangeInputValue(value, mode, removePlaceholder) {
|
|
2605
|
-
const valueMinusPlaceholder = removePlaceholder(value);
|
|
2606
|
-
const [startDate, endDate] = valueMinusPlaceholder.split(RANGE_SEPARATOR);
|
|
2607
|
-
const startJsDate = maskitoParseDate(startDate, { mode });
|
|
2608
|
-
const endJsDate = endDate ? maskitoParseDate(endDate, { mode }) : null;
|
|
2609
|
-
const startLuxonDate = startJsDate ? DateTime.fromJSDate(startJsDate, { zone: "utc" }) : null;
|
|
2610
|
-
const endLuxonDate = endJsDate ? DateTime.fromJSDate(endJsDate, { zone: "utc" }) : null;
|
|
2611
|
-
return {
|
|
2612
|
-
startDate: startLuxonDate,
|
|
2613
|
-
endDate: endLuxonDate,
|
|
2614
|
-
isInputValid: !!(startLuxonDate && endLuxonDate),
|
|
2615
|
-
// input valid if both dates are filled
|
|
2616
|
-
isInputEmpty: valueMinusPlaceholder === "",
|
|
2617
|
-
// input empty if nothing is typed
|
|
2618
|
-
isHalfEmpty: endDate === void 0
|
|
2619
|
-
};
|
|
2620
|
-
}
|
|
2621
|
-
function swapMode$1(inputString, previousMode, mode) {
|
|
2622
|
-
const halves = inputString.split(RANGE_SEPARATOR);
|
|
2623
|
-
const segments = halves.map((half) => half.split("/"));
|
|
2624
|
-
let startDay, startMonth, startYear, endDay, endMonth, endYear;
|
|
2625
|
-
if (previousMode === "mm/dd/yyyy") {
|
|
2626
|
-
startDay = segments[0][1];
|
|
2627
|
-
startMonth = segments[0][0];
|
|
2628
|
-
startYear = segments[0][2];
|
|
2629
|
-
endDay = segments[1][1];
|
|
2630
|
-
endMonth = segments[1][0];
|
|
2631
|
-
endYear = segments[1][2];
|
|
2632
|
-
}
|
|
2633
|
-
if (previousMode === "dd/mm/yyyy") {
|
|
2634
|
-
startDay = segments[0][0];
|
|
2635
|
-
startMonth = segments[0][1];
|
|
2636
|
-
startYear = segments[0][2];
|
|
2637
|
-
endDay = segments[1][0];
|
|
2638
|
-
endMonth = segments[1][1];
|
|
2639
|
-
endYear = segments[1][2];
|
|
2640
|
-
}
|
|
2641
|
-
if (previousMode === "yyyy/mm/dd") {
|
|
2642
|
-
startDay = segments[0][2];
|
|
2643
|
-
startMonth = segments[0][1];
|
|
2644
|
-
startYear = segments[0][0];
|
|
2645
|
-
endDay = segments[1][2];
|
|
2646
|
-
endMonth = segments[1][1];
|
|
2647
|
-
endYear = segments[1][0];
|
|
2648
|
-
}
|
|
2649
|
-
if (mode === "mm/dd/yyyy") {
|
|
2650
|
-
return `${startMonth}/${startDay}/${startYear}${RANGE_SEPARATOR}${endMonth}/${endDay}/${endYear}`;
|
|
2651
|
-
}
|
|
2652
|
-
if (mode === "dd/mm/yyyy") {
|
|
2653
|
-
return `${startDay}/${startMonth}/${startYear}${RANGE_SEPARATOR}${endDay}/${endMonth}/${endYear}`;
|
|
2654
|
-
}
|
|
2655
|
-
if (mode === "yyyy/mm/dd") {
|
|
2656
|
-
return `${startYear}/${startMonth}/${startDay}${RANGE_SEPARATOR}${endYear}/${endMonth}/${endDay}`;
|
|
2657
|
-
}
|
|
2658
|
-
return inputString;
|
|
2659
|
-
}
|
|
2660
|
-
|
|
2661
|
-
const useDateFieldRangeConversion = (props) => {
|
|
2662
|
-
const { value, defaultValue, minDate, maxDate, unavailable, onChange } = props;
|
|
2663
|
-
const normalizedValue = useMemo(() => {
|
|
2664
|
-
if (value === null || value === void 0) return value;
|
|
2665
|
-
return {
|
|
2666
|
-
startDate: convertStringToDate(value.startDate) ?? null,
|
|
2667
|
-
endDate: convertStringToDate(value.endDate) ?? null
|
|
2668
|
-
};
|
|
2669
|
-
}, [value]);
|
|
2670
|
-
const normalizedDefaultValue = useMemo(() => {
|
|
2671
|
-
if (defaultValue === null || defaultValue === void 0)
|
|
2672
|
-
return defaultValue;
|
|
2673
|
-
return {
|
|
2674
|
-
startDate: convertStringToDate(defaultValue.startDate) ?? null,
|
|
2675
|
-
endDate: convertStringToDate(defaultValue.endDate) ?? null
|
|
2676
|
-
};
|
|
2677
|
-
}, [defaultValue]);
|
|
2678
|
-
const normalizedMinDate = useMemo(
|
|
2679
|
-
() => convertStringToDate(minDate),
|
|
2680
|
-
[minDate]
|
|
2681
|
-
);
|
|
2682
|
-
const normalizedMaxDate = useMemo(
|
|
2683
|
-
() => convertStringToDate(maxDate),
|
|
2684
|
-
[maxDate]
|
|
2685
|
-
);
|
|
2686
|
-
const normalizedUnavailableDates = useMemo(() => {
|
|
2687
|
-
return unavailable?.dates?.map((date) => convertStringToDate(date)).filter((date) => date !== null && date !== void 0);
|
|
2688
|
-
}, [unavailable?.dates]);
|
|
2689
|
-
const handleChange = useCallback(
|
|
2690
|
-
(change) => {
|
|
2691
|
-
onChange?.({
|
|
2692
|
-
...change,
|
|
2693
|
-
startDate: change.startDate?.toISODate() ?? null,
|
|
2694
|
-
endDate: change.endDate?.toISODate() ?? null
|
|
2695
|
-
});
|
|
2696
|
-
},
|
|
2697
|
-
[onChange]
|
|
2698
|
-
);
|
|
2699
|
-
return {
|
|
2700
|
-
value: normalizedValue,
|
|
2701
|
-
defaultValue: normalizedDefaultValue,
|
|
2702
|
-
minDate: normalizedMinDate,
|
|
2703
|
-
maxDate: normalizedMaxDate,
|
|
2704
|
-
unavailable: {
|
|
2705
|
-
dates: normalizedUnavailableDates,
|
|
2706
|
-
daysOfWeek: unavailable?.daysOfWeek
|
|
2707
|
-
},
|
|
2708
|
-
onChange: handleChange
|
|
2709
|
-
};
|
|
2710
|
-
};
|
|
2711
|
-
|
|
2712
|
-
function useDateFieldRangeState({
|
|
2713
|
-
valueProp,
|
|
2714
|
-
defaultValueProp,
|
|
2715
|
-
inputRef,
|
|
2716
|
-
onChange
|
|
2717
|
-
}) {
|
|
2718
|
-
const [startDate, setStartDate] = useOptionallyControlledState({
|
|
2719
|
-
controlledValue: valueProp !== void 0 ? valueProp?.startDate : void 0,
|
|
2720
|
-
defaultValue: defaultValueProp !== void 0 ? defaultValueProp?.startDate : void 0
|
|
2721
|
-
});
|
|
2722
|
-
const [endDate, setEndDate] = useOptionallyControlledState({
|
|
2723
|
-
controlledValue: valueProp !== void 0 ? valueProp?.endDate : void 0,
|
|
2724
|
-
defaultValue: defaultValueProp !== void 0 ? defaultValueProp?.endDate : void 0
|
|
2725
|
-
});
|
|
2726
|
-
const setSharedValue = (value) => {
|
|
2727
|
-
inputRef.current?.setDateRange(value.startDate, value.endDate);
|
|
2728
|
-
setStartDate(value.startDate);
|
|
2729
|
-
setEndDate(value.endDate);
|
|
2730
|
-
};
|
|
2731
|
-
const handleInputChange = (change) => {
|
|
2732
|
-
const range = change.isInputEmpty ? null : {
|
|
2733
|
-
startDate: change.startDate?.startOf("day") ?? null,
|
|
2734
|
-
endDate: change.endDate?.startOf("day") ?? null
|
|
2735
|
-
};
|
|
2736
|
-
setStartDate(range?.startDate ?? null);
|
|
2737
|
-
setEndDate(range?.endDate ?? null);
|
|
2738
|
-
onChange?.({
|
|
2739
|
-
startDate: range?.startDate ?? null,
|
|
2740
|
-
endDate: range?.endDate ?? null,
|
|
2741
|
-
isInputValid: change.isInputValid,
|
|
2742
|
-
isInputEmpty: change.isInputEmpty
|
|
2743
|
-
});
|
|
2744
|
-
};
|
|
2745
|
-
const handleCalendarSelection = ({
|
|
2746
|
-
startDate: startDate2,
|
|
2747
|
-
endDate: endDate2
|
|
2748
|
-
}) => {
|
|
2749
|
-
setSharedValue({ startDate: startDate2, endDate: endDate2 });
|
|
2750
|
-
onChange?.({
|
|
2751
|
-
startDate: startDate2,
|
|
2752
|
-
endDate: endDate2,
|
|
2753
|
-
isInputValid: true,
|
|
2754
|
-
isInputEmpty: false
|
|
2755
|
-
});
|
|
2756
|
-
};
|
|
2757
|
-
return {
|
|
2758
|
-
startDate,
|
|
2759
|
-
endDate,
|
|
2760
|
-
setStartDate,
|
|
2761
|
-
setEndDate,
|
|
2762
|
-
handleInputChange,
|
|
2763
|
-
handleCalendarSelection
|
|
2764
|
-
};
|
|
2765
|
-
}
|
|
2766
|
-
|
|
2767
|
-
const DateFieldRangeCalendar = ({
|
|
2768
|
-
startDate,
|
|
2769
|
-
endDate,
|
|
2770
|
-
onKeyDown,
|
|
2771
|
-
onSelection,
|
|
2772
|
-
minDate,
|
|
2773
|
-
maxDate,
|
|
2774
|
-
unavailable
|
|
2775
|
-
}) => {
|
|
2776
|
-
const previousStartDate = usePrevious(startDate);
|
|
2777
|
-
const previousEndDate = usePrevious(endDate);
|
|
2778
|
-
const handleCalendarSelection = (data) => {
|
|
2779
|
-
if (!data.value) return;
|
|
2780
|
-
const calStartDate = data.value.start ? DateTime.fromISO(data.value.start, { zone: "utc" }) : null;
|
|
2781
|
-
const calEndDate = data.value.end ? DateTime.fromISO(data.value.end, { zone: "utc" }) : null;
|
|
2782
|
-
onSelection({
|
|
2783
|
-
startDate: calStartDate,
|
|
2784
|
-
endDate: calEndDate
|
|
2785
|
-
});
|
|
2786
|
-
};
|
|
2787
|
-
const defaultFocusedDate = useMemo(() => {
|
|
2788
|
-
if (!startDate && !endDate) return DateTime.now().toISODate();
|
|
2789
|
-
if (!startDate) return endDate?.toISODate();
|
|
2790
|
-
if (!endDate) return startDate?.toISODate();
|
|
2791
|
-
if (endDate && !previousEndDate?.equals(endDate)) {
|
|
2792
|
-
return endDate.toISODate();
|
|
2793
|
-
} else if (startDate && !previousStartDate?.equals(startDate)) {
|
|
2794
|
-
return startDate.toISODate();
|
|
2795
|
-
}
|
|
2796
|
-
if (endDate) return endDate.toISODate();
|
|
2797
|
-
if (startDate) return startDate.toISODate();
|
|
2798
|
-
return DateTime.now().toISODate();
|
|
2799
|
-
}, [previousStartDate, previousEndDate, startDate, endDate]);
|
|
2800
|
-
return /* @__PURE__ */ jsx(
|
|
2801
|
-
Calendar,
|
|
2802
|
-
{
|
|
2803
|
-
range: true,
|
|
2804
|
-
onKeyDown,
|
|
2805
|
-
defaultFocusedDate,
|
|
2806
|
-
value: {
|
|
2807
|
-
start: startDate?.toISODate() || void 0,
|
|
2808
|
-
end: endDate?.toISODate() || void 0
|
|
2809
|
-
},
|
|
2810
|
-
onSelection: handleCalendarSelection,
|
|
2811
|
-
defaultTimeZone: "UTC",
|
|
2812
|
-
minDate: minDate?.toISODate() ?? void 0,
|
|
2813
|
-
maxDate: maxDate?.toISODate() ?? void 0,
|
|
2814
|
-
unavailable: unavailable ? {
|
|
2815
|
-
dates: unavailable.dates?.map((d) => d.toISODate() ?? ""),
|
|
2816
|
-
daysOfWeek: unavailable.daysOfWeek
|
|
2817
|
-
} : void 0,
|
|
2818
|
-
_disableAutofocus: true
|
|
2819
|
-
},
|
|
2820
|
-
`${startDate?.toISODate()}-${endDate?.toISODate()}`
|
|
2821
|
-
);
|
|
2822
|
-
};
|
|
2823
|
-
|
|
2824
|
-
const DateFieldRange = ({
|
|
2825
|
-
onFocus,
|
|
2826
|
-
onBlur,
|
|
2827
|
-
disableCalendar,
|
|
2828
|
-
required,
|
|
2829
|
-
mode,
|
|
2830
|
-
value: valueProp,
|
|
2831
|
-
defaultValue: defaultValueProp,
|
|
2832
|
-
minDate: minDateProp,
|
|
2833
|
-
maxDate: maxDateProp,
|
|
2834
|
-
unavailable: unavailableProp,
|
|
2835
|
-
onChange: onChangeProp,
|
|
2836
|
-
...rest
|
|
2837
|
-
}) => {
|
|
2838
|
-
const { ref: wrapperRef } = useFocusWithin({
|
|
2839
|
-
onBlur,
|
|
2840
|
-
onFocus
|
|
2841
|
-
});
|
|
2842
|
-
const { value, defaultValue, minDate, maxDate, unavailable, onChange } = useDateFieldRangeConversion({
|
|
2843
|
-
value: valueProp,
|
|
2844
|
-
defaultValue: defaultValueProp,
|
|
2845
|
-
minDate: minDateProp,
|
|
2846
|
-
maxDate: maxDateProp,
|
|
2847
|
-
unavailable: unavailableProp,
|
|
2848
|
-
onChange: onChangeProp
|
|
2849
|
-
});
|
|
2850
|
-
const inputRef = useRef(null);
|
|
2851
|
-
const [popoverTriggerRef, setPopoverTriggerRef] = useState();
|
|
2852
|
-
const popoverContentRef = useRef(null);
|
|
2853
|
-
const combinedRef = useMergeRefs([popoverTriggerRef, inputRef]);
|
|
2854
|
-
const popoverSupported = usePopoverSupport();
|
|
2855
|
-
const handleChange = (change) => {
|
|
2856
|
-
const sharedConstraints = {
|
|
2857
|
-
unavailable,
|
|
2858
|
-
minDate: minDate ?? void 0,
|
|
2859
|
-
maxDate: maxDate ?? void 0
|
|
2860
|
-
};
|
|
2861
|
-
onChange?.({
|
|
2862
|
-
startDate: change.startDate?.startOf("day") ?? null,
|
|
2863
|
-
endDate: change.endDate?.startOf("day") ?? null,
|
|
2864
|
-
isInputValid: change.isInputValid,
|
|
2865
|
-
isInputEmpty: change.isInputEmpty,
|
|
2866
|
-
isDateRangeValid: validateDateRange({
|
|
2867
|
-
required,
|
|
2868
|
-
startDate: change.startDate?.startOf("day") ?? null,
|
|
2869
|
-
endDate: change.endDate?.startOf("day") ?? null,
|
|
2870
|
-
startDateConstraints: sharedConstraints,
|
|
2871
|
-
endDateConstraints: sharedConstraints
|
|
2872
|
-
})
|
|
2873
|
-
});
|
|
2874
|
-
};
|
|
2875
|
-
const { startDate, endDate, handleInputChange, handleCalendarSelection } = useDateFieldRangeState({
|
|
2876
|
-
valueProp: value,
|
|
2877
|
-
defaultValueProp: defaultValue,
|
|
2878
|
-
inputRef,
|
|
2879
|
-
onChange: handleChange
|
|
2880
|
-
});
|
|
2881
|
-
const {
|
|
2882
|
-
calendarOpen,
|
|
2883
|
-
setCalendarOpen,
|
|
2884
|
-
handleCalendarKeyDown,
|
|
2885
|
-
handleInputKeyDown
|
|
2886
|
-
} = useDateFieldOrchestration({
|
|
2887
|
-
inputRef,
|
|
2888
|
-
calendarDefaultOpen: false,
|
|
2889
|
-
popoverContentRef,
|
|
2890
|
-
disableCalendar
|
|
2891
|
-
});
|
|
2892
|
-
const shouldShowCalendar = usePopoverCloseDelayWorkaround(calendarOpen);
|
|
2893
|
-
const currentValidity = useMemo(() => {
|
|
2894
|
-
return validateDateRange({
|
|
2895
|
-
required,
|
|
2896
|
-
startDate,
|
|
2897
|
-
endDate,
|
|
2898
|
-
startDateConstraints: {
|
|
2899
|
-
unavailable,
|
|
2900
|
-
minDate: minDate ?? void 0,
|
|
2901
|
-
maxDate: maxDate ?? void 0
|
|
2902
|
-
},
|
|
2903
|
-
endDateConstraints: {
|
|
2904
|
-
unavailable,
|
|
2905
|
-
minDate: minDate ?? void 0,
|
|
2906
|
-
maxDate: maxDate ?? void 0
|
|
2907
|
-
}
|
|
2908
|
-
});
|
|
2909
|
-
}, [required, startDate, endDate, minDate, maxDate, unavailable]);
|
|
2910
|
-
const justTheField = /* @__PURE__ */ jsx(
|
|
2911
|
-
MaskedDateRangeInput,
|
|
2912
|
-
{
|
|
2913
|
-
...rest,
|
|
2914
|
-
mode,
|
|
2915
|
-
ref: combinedRef,
|
|
2916
|
-
startDate: startDate ?? null,
|
|
2917
|
-
endDate: endDate ?? null,
|
|
2918
|
-
onChange: handleInputChange,
|
|
2919
|
-
disableHint: rest.disableHint,
|
|
2920
|
-
onKeyDown: handleInputKeyDown,
|
|
2921
|
-
onClick: () => setCalendarOpen(true),
|
|
2922
|
-
required,
|
|
2923
|
-
autoComplete: "off",
|
|
2924
|
-
"data-date-range-valid": currentValidity
|
|
2925
|
-
}
|
|
2926
|
-
);
|
|
2927
|
-
if (disableCalendar) {
|
|
2928
|
-
return justTheField;
|
|
2929
|
-
}
|
|
2930
|
-
if (!popoverSupported) {
|
|
2931
|
-
return justTheField;
|
|
2932
|
-
}
|
|
2933
|
-
return /* @__PURE__ */ jsx("div", { ref: wrapperRef, children: /* @__PURE__ */ jsxs(
|
|
2934
|
-
Popover,
|
|
2935
|
-
{
|
|
2936
|
-
open: calendarOpen,
|
|
2937
|
-
modal: true,
|
|
2938
|
-
placement: "bottom-start",
|
|
2939
|
-
disableFlipFallback: true,
|
|
2940
|
-
disableTriggerFocus: true,
|
|
2941
|
-
onClose: () => setCalendarOpen(false),
|
|
2942
|
-
disableAutoUpdate: true,
|
|
2943
|
-
onOutsidePress: () => setCalendarOpen(false),
|
|
2944
|
-
children: [
|
|
2945
|
-
/* @__PURE__ */ jsx(Popover.Trigger, { children: ({ ref: iRef }) => {
|
|
2946
|
-
setPopoverTriggerRef(iRef);
|
|
2947
|
-
return justTheField;
|
|
2948
|
-
} }),
|
|
2949
|
-
/* @__PURE__ */ jsx(Popover.Content, { ref: popoverContentRef, "data-testid": "calendar-popover", children: shouldShowCalendar && /* @__PURE__ */ jsx(
|
|
2950
|
-
DateFieldRangeCalendar,
|
|
2951
|
-
{
|
|
2952
|
-
startDate,
|
|
2953
|
-
endDate,
|
|
2954
|
-
onKeyDown: handleCalendarKeyDown,
|
|
2955
|
-
onSelection: handleCalendarSelection,
|
|
2956
|
-
minDate,
|
|
2957
|
-
maxDate,
|
|
2958
|
-
unavailable
|
|
2959
|
-
}
|
|
2960
|
-
) })
|
|
2961
|
-
]
|
|
2962
|
-
}
|
|
2963
|
-
) });
|
|
2964
|
-
};
|
|
2965
|
-
DateFieldRange.displayName = "DateFieldRange";
|
|
2966
|
-
function validateDateRange({
|
|
2967
|
-
required,
|
|
2968
|
-
startDate,
|
|
2969
|
-
endDate,
|
|
2970
|
-
startDateConstraints,
|
|
2971
|
-
endDateConstraints
|
|
2972
|
-
}) {
|
|
2973
|
-
if (!required && !startDate && !endDate) return true;
|
|
2974
|
-
return validateDate({
|
|
2975
|
-
date: startDate,
|
|
2976
|
-
constraints: { ...startDateConstraints, required: true }
|
|
2977
|
-
}) && validateDate({
|
|
2978
|
-
date: endDate,
|
|
2979
|
-
constraints: { ...endDateConstraints, required: true }
|
|
2980
|
-
}) && (!startDate || !endDate || startDate <= endDate);
|
|
2981
|
-
}
|
|
2982
|
-
|
|
2983
|
-
const yearlessDatePlaceholderMask = ({
|
|
2984
|
-
mode,
|
|
2985
|
-
separator = "/",
|
|
2986
|
-
placeholder
|
|
2987
|
-
}) => {
|
|
2988
|
-
const dateOptions = maskitoDateOptionsGenerator({
|
|
2989
|
-
mode,
|
|
2990
|
-
separator
|
|
2991
|
-
});
|
|
2992
|
-
const { plugins, removePlaceholder, ...placeholderOptions } = maskitoWithPlaceholder(placeholder);
|
|
2993
|
-
const datePlaceholderMask = {
|
|
2994
|
-
...dateOptions,
|
|
2995
|
-
plugins: plugins.concat(dateOptions.plugins || []),
|
|
2996
|
-
preprocessors: [
|
|
2997
|
-
...placeholderOptions.preprocessors,
|
|
2998
|
-
...dateOptions.preprocessors,
|
|
2999
|
-
makeZeroShortcutPreprocessor(mode, separator)
|
|
3000
|
-
],
|
|
3001
|
-
postprocessors: [
|
|
3002
|
-
...dateOptions.postprocessors,
|
|
3003
|
-
...placeholderOptions.postprocessors
|
|
3004
|
-
]
|
|
3005
|
-
};
|
|
3006
|
-
return { options: datePlaceholderMask, removePlaceholder };
|
|
3007
|
-
};
|
|
3008
|
-
|
|
3009
|
-
const MaskedYearlessDateInput = forwardRef(({ onChange, mode = "mm/dd", value, disableHint = false, ...props }, ref) => {
|
|
3010
|
-
const placeholder = "__/__";
|
|
3011
|
-
const [inputValue, setInputValue] = useState(placeholder);
|
|
3012
|
-
const { options, removePlaceholder } = yearlessDatePlaceholderMask({
|
|
3013
|
-
mode,
|
|
3014
|
-
placeholder
|
|
3015
|
-
});
|
|
3016
|
-
const maskedInputRef = useMaskito({ options });
|
|
3017
|
-
const inputRef = useRef(null);
|
|
3018
|
-
const combinedRef = useMergeRefs$1([maskedInputRef, inputRef, ref]);
|
|
3019
|
-
const previousValue = usePrevious(value);
|
|
3020
|
-
const previousMode = usePrevious(mode);
|
|
3021
|
-
useEffect(() => {
|
|
3022
|
-
if (mode !== previousMode) {
|
|
3023
|
-
setInputValue(
|
|
3024
|
-
(oldInputValue) => swapMode(oldInputValue, previousMode ?? mode, mode)
|
|
3025
|
-
);
|
|
3026
|
-
}
|
|
3027
|
-
}, [mode, previousMode]);
|
|
3028
|
-
useEffect(() => {
|
|
3029
|
-
if (previousValue?.day !== value?.day || previousValue?.month !== value?.month) {
|
|
3030
|
-
if (value?.day && value?.month) {
|
|
3031
|
-
setInputValue(stringifyYearlessDate(value.day, value.month, mode));
|
|
3032
|
-
} else if (previousValue !== null && previousValue !== void 0 && (value === null || value?.day === null && value?.month === null)) {
|
|
3033
|
-
setInputValue(placeholder);
|
|
3034
|
-
} else {
|
|
3035
|
-
return;
|
|
3036
|
-
}
|
|
3037
|
-
}
|
|
3038
|
-
}, [value, mode, previousValue]);
|
|
3039
|
-
const currentParsedData = useMemo(() => {
|
|
3040
|
-
return parseInputValue(inputValue, mode, removePlaceholder);
|
|
3041
|
-
}, [inputValue, mode, removePlaceholder]);
|
|
3042
|
-
const handleChange = (event) => {
|
|
3043
|
-
setInputValue(event.target.value);
|
|
3044
|
-
const {
|
|
3045
|
-
value: v,
|
|
3046
|
-
isInputValid,
|
|
3047
|
-
isInputEmpty
|
|
3048
|
-
} = parseInputValue(event.target.value, mode, removePlaceholder);
|
|
3049
|
-
onChange?.({
|
|
3050
|
-
event,
|
|
3051
|
-
value: v ?? currentParsedData.value ?? null,
|
|
3052
|
-
isInputValid,
|
|
3053
|
-
isInputEmpty
|
|
3054
|
-
});
|
|
3055
|
-
};
|
|
3056
|
-
useImperativeHandle(ref, () => {
|
|
3057
|
-
const input = inputRef.current;
|
|
3058
|
-
if (!input) return null;
|
|
3059
|
-
return Object.assign(input, {
|
|
3060
|
-
setValue: (value2) => {
|
|
3061
|
-
if (!value2) {
|
|
3062
|
-
setInputValue(placeholder);
|
|
3063
|
-
return;
|
|
3064
|
-
}
|
|
3065
|
-
setInputValue(
|
|
3066
|
-
stringifyYearlessDate(value2.day ?? 1, value2.month ?? 1, mode)
|
|
3067
|
-
);
|
|
3068
|
-
}
|
|
3069
|
-
});
|
|
3070
|
-
}, [mode, placeholder]);
|
|
3071
|
-
return /* @__PURE__ */ jsx(
|
|
3072
|
-
TextField,
|
|
3073
|
-
{
|
|
3074
|
-
ref: combinedRef,
|
|
3075
|
-
"data-month-value": value?.month,
|
|
3076
|
-
"data-day-value": value?.day,
|
|
3077
|
-
"data-input-valid": currentParsedData.isInputValid,
|
|
3078
|
-
"data-input-empty": currentParsedData.isInputEmpty,
|
|
3079
|
-
...props,
|
|
3080
|
-
showCounter: false,
|
|
3081
|
-
value: inputValue,
|
|
3082
|
-
onChange: handleChange,
|
|
3083
|
-
hint: disableHint ? void 0 : `Format: ${mode}`
|
|
3084
|
-
}
|
|
3085
|
-
);
|
|
3086
|
-
});
|
|
3087
|
-
MaskedYearlessDateInput.displayName = "MaskedYearlessDateInput";
|
|
3088
|
-
function parseInputValue(value, mode, removePlaceholder) {
|
|
3089
|
-
const valueMinusPlaceholder = removePlaceholder(value);
|
|
3090
|
-
const [segment1, segment2] = valueMinusPlaceholder.split("/");
|
|
3091
|
-
const incompleteFirstSegment = segment1?.length !== 2;
|
|
3092
|
-
const incompleteSecondSegment = segment2?.length !== 2;
|
|
3093
|
-
if (mode === "dd/mm") {
|
|
3094
|
-
return {
|
|
3095
|
-
value: {
|
|
3096
|
-
day: incompleteFirstSegment ? null : parseInt(segment1),
|
|
3097
|
-
month: incompleteSecondSegment ? null : parseInt(segment2)
|
|
3098
|
-
},
|
|
3099
|
-
isInputValid: !incompleteFirstSegment && !incompleteSecondSegment,
|
|
3100
|
-
isInputEmpty: valueMinusPlaceholder === ""
|
|
3101
|
-
};
|
|
3102
|
-
}
|
|
3103
|
-
return {
|
|
3104
|
-
value: {
|
|
3105
|
-
day: incompleteSecondSegment ? null : parseInt(segment2),
|
|
3106
|
-
month: incompleteFirstSegment ? null : parseInt(segment1)
|
|
3107
|
-
},
|
|
3108
|
-
isInputValid: !incompleteFirstSegment && !incompleteSecondSegment,
|
|
3109
|
-
isInputEmpty: valueMinusPlaceholder === ""
|
|
3110
|
-
};
|
|
3111
|
-
}
|
|
3112
|
-
function swapMode(inputString, previousMode, mode) {
|
|
3113
|
-
const { day, month } = divideSegments(inputString, previousMode);
|
|
3114
|
-
return stringifyYearlessDate(day, month, mode);
|
|
3115
|
-
}
|
|
3116
|
-
function divideSegments(value, mode) {
|
|
3117
|
-
const [segment1, segment2] = value.split("/");
|
|
3118
|
-
if (mode === "dd/mm") {
|
|
3119
|
-
return { day: segment1, month: segment2 };
|
|
3120
|
-
}
|
|
3121
|
-
return { day: segment2, month: segment1 };
|
|
3122
|
-
}
|
|
3123
|
-
function stringifyYearlessDate(day, month, mode) {
|
|
3124
|
-
const dd = day.toString().padStart(2, "0");
|
|
3125
|
-
const mm = month.toString().padStart(2, "0");
|
|
3126
|
-
if (mode === "dd/mm") {
|
|
3127
|
-
return `${dd}/${mm}`;
|
|
3128
|
-
}
|
|
3129
|
-
return `${mm}/${dd}`;
|
|
3130
|
-
}
|
|
3131
|
-
|
|
3132
|
-
const useConditionalChange = ({
|
|
3133
|
-
changeHandler,
|
|
3134
|
-
compareFn
|
|
3135
|
-
}) => {
|
|
3136
|
-
const lastChangeRef = useRef(null);
|
|
3137
|
-
const onChange = useCallback(
|
|
3138
|
-
(change) => {
|
|
3139
|
-
if (!!lastChangeRef.current && !compareFn(lastChangeRef.current, change)) {
|
|
3140
|
-
return;
|
|
3141
|
-
}
|
|
3142
|
-
changeHandler?.(change);
|
|
3143
|
-
lastChangeRef.current = change;
|
|
3144
|
-
},
|
|
3145
|
-
[changeHandler, compareFn]
|
|
3146
|
-
);
|
|
3147
|
-
return onChange;
|
|
3148
|
-
};
|
|
3149
|
-
|
|
3150
|
-
const DateFieldYearless = ({
|
|
3151
|
-
value: valueProp,
|
|
3152
|
-
defaultValue: defaultValueProp,
|
|
3153
|
-
onChange,
|
|
3154
|
-
minDate,
|
|
3155
|
-
maxDate,
|
|
3156
|
-
unavailable,
|
|
3157
|
-
required,
|
|
3158
|
-
...restProps
|
|
3159
|
-
}) => {
|
|
3160
|
-
const [value, setValue] = useOptionallyControlledState({
|
|
3161
|
-
controlledValue: valueProp,
|
|
3162
|
-
defaultValue: defaultValueProp
|
|
3163
|
-
});
|
|
3164
|
-
const currentValidity = useMemo(
|
|
3165
|
-
() => validateYearlessDate({
|
|
3166
|
-
value: value ?? null,
|
|
3167
|
-
constraints: {
|
|
3168
|
-
required,
|
|
3169
|
-
unavailable,
|
|
3170
|
-
minDate,
|
|
3171
|
-
maxDate
|
|
3172
|
-
}
|
|
3173
|
-
}),
|
|
3174
|
-
[value, required, unavailable, minDate, maxDate]
|
|
3175
|
-
);
|
|
3176
|
-
const conditionalChange = useConditionalChange({
|
|
3177
|
-
changeHandler: onChange,
|
|
3178
|
-
compareFn: (a, b) => a.isInputValid !== b.isInputValid || a.isInputEmpty !== b.isInputEmpty || a.isValid !== b.isValid || a.value?.day !== b.value?.day || a.value?.month !== b.value?.month
|
|
3179
|
-
});
|
|
3180
|
-
const handleInputChange = (change) => {
|
|
3181
|
-
const { event, value: value2, ...restChange } = change;
|
|
3182
|
-
setValue(value2);
|
|
3183
|
-
return conditionalChange({
|
|
3184
|
-
...restChange,
|
|
3185
|
-
value: value2,
|
|
3186
|
-
isValid: validateYearlessDate({
|
|
3187
|
-
value: value2,
|
|
3188
|
-
constraints: {
|
|
3189
|
-
required,
|
|
3190
|
-
unavailable,
|
|
3191
|
-
minDate,
|
|
3192
|
-
maxDate
|
|
3193
|
-
}
|
|
3194
|
-
})
|
|
3195
|
-
});
|
|
3196
|
-
};
|
|
3197
|
-
return /* @__PURE__ */ jsx(
|
|
3198
|
-
MaskedYearlessDateInput,
|
|
3199
|
-
{
|
|
3200
|
-
required,
|
|
3201
|
-
...restProps,
|
|
3202
|
-
autoComplete: "off",
|
|
3203
|
-
onChange: handleInputChange,
|
|
3204
|
-
value,
|
|
3205
|
-
"data-valid": currentValidity
|
|
3206
|
-
}
|
|
3207
|
-
);
|
|
3208
|
-
};
|
|
3209
|
-
DateFieldYearless.displayName = "DateFieldYearless";
|
|
3210
|
-
|
|
3211
|
-
export { DateFieldRange as D, DateFieldSingle as a, DateFieldYearless as b };
|
|
3212
|
-
//# sourceMappingURL=DateFieldYearless-DU0z2fEA-ByR2ixI5.js.map
|
|
2234
|
+
export { useMaskito as a, maskitoWithPlaceholder as b, maskitoParseDate as c, maskitoDateOptionsGenerator as d, maskitoTimeOptionsGenerator as e, maskitoDateRangeOptionsGenerator as m, usePopoverSupport as u };
|
|
2235
|
+
//# sourceMappingURL=usePopoverSupport-B9Lsqryr-DhZHMoNb.js.map
|