@monolith-forensics/monolith-ui 2.1.2 → 2.1.3
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/dist/DateInput/DateInput.js +192 -37
- package/package.json +1 -1
|
@@ -191,6 +191,7 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
191
191
|
const [unControlledValue, setUncontrolledValue] = useState(defaultValue);
|
|
192
192
|
const _value = isControlled.current ? value : unControlledValue;
|
|
193
193
|
const [selectedSegment, setSelectedSegment] = useState();
|
|
194
|
+
const [draftSegment, setDraftSegment] = useState(null);
|
|
194
195
|
const [isOpen, setIsOpen] = useState(false);
|
|
195
196
|
// Check if format is date only and does not include time
|
|
196
197
|
const isDateOnly = format.match(/(HH|h|mm|ss|SSS|A|Z)/) === null;
|
|
@@ -281,6 +282,7 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
281
282
|
const handleClear = (e) => {
|
|
282
283
|
e.preventDefault();
|
|
283
284
|
e.stopPropagation();
|
|
285
|
+
clearTypedSegment();
|
|
284
286
|
commitValue(null);
|
|
285
287
|
setIsOpen(false);
|
|
286
288
|
};
|
|
@@ -289,6 +291,8 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
289
291
|
e.preventDefault();
|
|
290
292
|
if (segment.type === "separator")
|
|
291
293
|
return;
|
|
294
|
+
commitSelectedDraftSegment();
|
|
295
|
+
clearTypedSegment();
|
|
292
296
|
if (mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) {
|
|
293
297
|
const input = mainRef.current.querySelector("div[data-type='input']");
|
|
294
298
|
if (input) {
|
|
@@ -300,6 +304,7 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
300
304
|
const handleContainerClick = (e) => {
|
|
301
305
|
e.stopPropagation();
|
|
302
306
|
e.preventDefault();
|
|
307
|
+
commitSelectedDraftSegment();
|
|
303
308
|
if (mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) {
|
|
304
309
|
const input = mainRef.current.querySelector("div[data-type='input']");
|
|
305
310
|
if (input) {
|
|
@@ -307,11 +312,13 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
307
312
|
}
|
|
308
313
|
}
|
|
309
314
|
setIsOpen(true);
|
|
315
|
+
clearTypedSegment();
|
|
310
316
|
setSelectedSegment(segments[0]);
|
|
311
317
|
};
|
|
312
318
|
const handleCalendarTriggerClick = (e) => {
|
|
313
319
|
e.stopPropagation();
|
|
314
320
|
e.preventDefault();
|
|
321
|
+
commitSelectedDraftSegment();
|
|
315
322
|
if (mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) {
|
|
316
323
|
const input = mainRef.current.querySelector("div[data-type='input']");
|
|
317
324
|
if (input) {
|
|
@@ -319,6 +326,7 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
319
326
|
}
|
|
320
327
|
}
|
|
321
328
|
setIsOpen((prev) => !prev);
|
|
329
|
+
clearTypedSegment();
|
|
322
330
|
setSelectedSegment(segments[0]);
|
|
323
331
|
};
|
|
324
332
|
const nextSegment = () => {
|
|
@@ -329,6 +337,145 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
329
337
|
return next || prev;
|
|
330
338
|
});
|
|
331
339
|
};
|
|
340
|
+
const clearTypedSegment = useCallback(() => {
|
|
341
|
+
typedKeys.current = "";
|
|
342
|
+
setDraftSegment(null);
|
|
343
|
+
}, []);
|
|
344
|
+
const getSegmentInputLength = useCallback((segment) => {
|
|
345
|
+
switch (segment.pattern) {
|
|
346
|
+
case "YYYY":
|
|
347
|
+
return 4;
|
|
348
|
+
case "SSS":
|
|
349
|
+
return 3;
|
|
350
|
+
case "MM":
|
|
351
|
+
case "DD":
|
|
352
|
+
case "HH":
|
|
353
|
+
case "mm":
|
|
354
|
+
case "ss":
|
|
355
|
+
case "h":
|
|
356
|
+
return 2;
|
|
357
|
+
default:
|
|
358
|
+
return segment.text.length;
|
|
359
|
+
}
|
|
360
|
+
}, []);
|
|
361
|
+
const getMomentWithSegmentValue = useCallback((segment, rawValue, currentMoment) => {
|
|
362
|
+
if (!segment.momentType)
|
|
363
|
+
return null;
|
|
364
|
+
const parsedValue = parseInt(rawValue, 10);
|
|
365
|
+
if (!Number.isFinite(parsedValue))
|
|
366
|
+
return null;
|
|
367
|
+
const nextMoment = currentMoment.clone();
|
|
368
|
+
switch (segment.momentType) {
|
|
369
|
+
case "year": {
|
|
370
|
+
if (parsedValue < 0 || parsedValue > 9999)
|
|
371
|
+
return null;
|
|
372
|
+
const daysInTargetMonth = currentMoment
|
|
373
|
+
.clone()
|
|
374
|
+
.year(parsedValue)
|
|
375
|
+
.month(currentMoment.month())
|
|
376
|
+
.daysInMonth();
|
|
377
|
+
if (currentMoment.date() > daysInTargetMonth)
|
|
378
|
+
return null;
|
|
379
|
+
nextMoment.year(parsedValue);
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
case "month": {
|
|
383
|
+
if (parsedValue < 1 || parsedValue > 12)
|
|
384
|
+
return null;
|
|
385
|
+
const targetMonth = parsedValue - 1;
|
|
386
|
+
const daysInTargetMonth = currentMoment
|
|
387
|
+
.clone()
|
|
388
|
+
.month(targetMonth)
|
|
389
|
+
.daysInMonth();
|
|
390
|
+
if (currentMoment.date() > daysInTargetMonth)
|
|
391
|
+
return null;
|
|
392
|
+
nextMoment.month(targetMonth);
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
case "date": {
|
|
396
|
+
if (parsedValue < 1 || parsedValue > currentMoment.daysInMonth()) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
nextMoment.date(parsedValue);
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
case "hour": {
|
|
403
|
+
const isTwelveHourSegment = segment.pattern === "h";
|
|
404
|
+
if (isTwelveHourSegment) {
|
|
405
|
+
if (parsedValue < 1 || parsedValue > 12)
|
|
406
|
+
return null;
|
|
407
|
+
const isPm = currentMoment.hour() >= 12;
|
|
408
|
+
const nextHour = parsedValue === 12
|
|
409
|
+
? isPm
|
|
410
|
+
? 12
|
|
411
|
+
: 0
|
|
412
|
+
: parsedValue + (isPm ? 12 : 0);
|
|
413
|
+
nextMoment.hour(nextHour);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
if (parsedValue < 0 || parsedValue > 23)
|
|
417
|
+
return null;
|
|
418
|
+
nextMoment.hour(parsedValue);
|
|
419
|
+
}
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
case "minute": {
|
|
423
|
+
if (parsedValue < 0 || parsedValue > 59)
|
|
424
|
+
return null;
|
|
425
|
+
nextMoment.minute(parsedValue);
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
case "second": {
|
|
429
|
+
if (parsedValue < 0 || parsedValue > 59)
|
|
430
|
+
return null;
|
|
431
|
+
nextMoment.second(parsedValue);
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
case "millisecond": {
|
|
435
|
+
if (parsedValue < 0 || parsedValue > 999)
|
|
436
|
+
return null;
|
|
437
|
+
nextMoment.millisecond(parsedValue);
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
default:
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
if (!nextMoment.isValid())
|
|
444
|
+
return null;
|
|
445
|
+
return nextMoment;
|
|
446
|
+
}, []);
|
|
447
|
+
const commitTypedSegment = useCallback((segment, rawValue) => {
|
|
448
|
+
if (!segment.momentType || !rawValue)
|
|
449
|
+
return false;
|
|
450
|
+
const normalizedValue = ["month", "day"].includes(segment.type) &&
|
|
451
|
+
Array.from(rawValue).every((c) => c === "0")
|
|
452
|
+
? "01"
|
|
453
|
+
: rawValue;
|
|
454
|
+
const nextMoment = getMomentWithSegmentValue(segment, normalizedValue, getEditableMoment(_value));
|
|
455
|
+
if (!nextMoment)
|
|
456
|
+
return false;
|
|
457
|
+
const nextValue = serializeMoment(nextMoment);
|
|
458
|
+
if (!checkValidRange(nextValue))
|
|
459
|
+
return false;
|
|
460
|
+
commitValue(nextValue);
|
|
461
|
+
return true;
|
|
462
|
+
}, [
|
|
463
|
+
_value,
|
|
464
|
+
getEditableMoment,
|
|
465
|
+
getMomentWithSegmentValue,
|
|
466
|
+
serializeMoment,
|
|
467
|
+
checkValidRange,
|
|
468
|
+
commitValue,
|
|
469
|
+
]);
|
|
470
|
+
const commitSelectedDraftSegment = useCallback(() => {
|
|
471
|
+
if (!selectedSegment || !typedKeys.current)
|
|
472
|
+
return false;
|
|
473
|
+
if (selectedSegment.pattern === "YYYY" &&
|
|
474
|
+
typedKeys.current.length < getSegmentInputLength(selectedSegment)) {
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
return commitTypedSegment(selectedSegment, typedKeys.current);
|
|
478
|
+
}, [commitTypedSegment, getSegmentInputLength, selectedSegment]);
|
|
332
479
|
// prevent click and drag selection
|
|
333
480
|
const handleMouseDown = (e) => {
|
|
334
481
|
e.preventDefault();
|
|
@@ -338,6 +485,8 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
338
485
|
return;
|
|
339
486
|
// tab
|
|
340
487
|
if (e.key === "Tab") {
|
|
488
|
+
commitSelectedDraftSegment();
|
|
489
|
+
clearTypedSegment();
|
|
341
490
|
setSelectedSegment(null);
|
|
342
491
|
setIsOpen(false);
|
|
343
492
|
return;
|
|
@@ -345,28 +494,35 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
345
494
|
// Right Arrow
|
|
346
495
|
if (e.key === "ArrowRight") {
|
|
347
496
|
e.preventDefault();
|
|
497
|
+
commitSelectedDraftSegment();
|
|
498
|
+
clearTypedSegment();
|
|
348
499
|
setSelectedSegment((prev) => {
|
|
349
500
|
if (!prev)
|
|
350
501
|
return segments[0];
|
|
351
502
|
const next = segments[prev.index + 2];
|
|
352
503
|
return next || prev;
|
|
353
504
|
});
|
|
354
|
-
typedKeys.current = ""; // clear typed keys when moving to next segment
|
|
355
505
|
}
|
|
356
506
|
// Left Arrow
|
|
357
507
|
if (e.key === "ArrowLeft") {
|
|
358
508
|
e.preventDefault();
|
|
509
|
+
commitSelectedDraftSegment();
|
|
510
|
+
clearTypedSegment();
|
|
359
511
|
setSelectedSegment((prev) => {
|
|
360
512
|
if (!prev)
|
|
361
513
|
return segments[0];
|
|
362
514
|
const next = segments[prev.index - 2];
|
|
363
515
|
return next || prev;
|
|
364
516
|
});
|
|
365
|
-
typedKeys.current = ""; // clear typed keys when moving to next segment
|
|
366
517
|
}
|
|
367
518
|
// Up Arrow
|
|
368
519
|
if (e.key === "ArrowUp") {
|
|
369
520
|
e.preventDefault();
|
|
521
|
+
const hadDraftSegment = !!typedKeys.current;
|
|
522
|
+
commitSelectedDraftSegment();
|
|
523
|
+
clearTypedSegment();
|
|
524
|
+
if (hadDraftSegment)
|
|
525
|
+
return;
|
|
370
526
|
const segmentType = selectedSegment.type;
|
|
371
527
|
if (segmentType === "separator")
|
|
372
528
|
return;
|
|
@@ -375,6 +531,11 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
375
531
|
// Down Arrow
|
|
376
532
|
if (e.key === "ArrowDown") {
|
|
377
533
|
e.preventDefault();
|
|
534
|
+
const hadDraftSegment = !!typedKeys.current;
|
|
535
|
+
commitSelectedDraftSegment();
|
|
536
|
+
clearTypedSegment();
|
|
537
|
+
if (hadDraftSegment)
|
|
538
|
+
return;
|
|
378
539
|
const segmentType = selectedSegment.type;
|
|
379
540
|
if (segmentType === "separator")
|
|
380
541
|
return;
|
|
@@ -382,43 +543,29 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
382
543
|
}
|
|
383
544
|
// handle paste
|
|
384
545
|
if (e.key === "v" && (e.metaKey || e.ctrlKey)) {
|
|
546
|
+
clearTypedSegment();
|
|
385
547
|
handlePaste();
|
|
386
548
|
}
|
|
387
549
|
// only allow numbers
|
|
388
|
-
if (e.key.match(
|
|
550
|
+
if (e.key.match(/^[0-9]$/)) {
|
|
389
551
|
e.preventDefault();
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const momentValue = getEditableMoment(prev);
|
|
401
|
-
let newValue = moment(momentValue)
|
|
402
|
-
.set(selectedSegment.momentType, parseInt(tempValue, 10) -
|
|
403
|
-
(selectedSegment.type === "month" ? 1 : 0))
|
|
404
|
-
.toISOString();
|
|
405
|
-
if (!checkValidRange(newValue))
|
|
406
|
-
return prev;
|
|
407
|
-
if (isDateOnly) {
|
|
408
|
-
newValue = utc
|
|
409
|
-
? moment.utc(newValue).format("YYYY-MM-DD")
|
|
410
|
-
: moment(newValue).format("YYYY-MM-DD");
|
|
411
|
-
}
|
|
412
|
-
if (isControlled.current) {
|
|
413
|
-
onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
|
|
414
|
-
return prev;
|
|
415
|
-
}
|
|
416
|
-
onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
|
|
417
|
-
return newValue;
|
|
552
|
+
if (!selectedSegment.momentType)
|
|
553
|
+
return;
|
|
554
|
+
const segmentLength = getSegmentInputLength(selectedSegment);
|
|
555
|
+
const nextTypedKeys = typedKeys.current.length >= segmentLength
|
|
556
|
+
? e.key
|
|
557
|
+
: `${typedKeys.current}${e.key}`;
|
|
558
|
+
typedKeys.current = nextTypedKeys;
|
|
559
|
+
setDraftSegment({
|
|
560
|
+
index: selectedSegment.index,
|
|
561
|
+
text: nextTypedKeys,
|
|
418
562
|
});
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
563
|
+
if (nextTypedKeys.length === segmentLength) {
|
|
564
|
+
const didCommit = commitTypedSegment(selectedSegment, nextTypedKeys);
|
|
565
|
+
clearTypedSegment();
|
|
566
|
+
if (didCommit) {
|
|
567
|
+
nextSegment();
|
|
568
|
+
}
|
|
422
569
|
}
|
|
423
570
|
}
|
|
424
571
|
};
|
|
@@ -492,11 +639,12 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
492
639
|
) {
|
|
493
640
|
setIsOpen(false);
|
|
494
641
|
}
|
|
642
|
+
clearTypedSegment();
|
|
495
643
|
setSelectedSegment(null); // clear selected segment when clicked outside component
|
|
496
644
|
};
|
|
497
645
|
document.addEventListener("click", close);
|
|
498
646
|
return () => document.removeEventListener("click", close);
|
|
499
|
-
}, [refs.floating]);
|
|
647
|
+
}, [clearTypedSegment, refs.floating]);
|
|
500
648
|
return (_jsxs(StyledContainer, Object.assign({ ref: (ref) => {
|
|
501
649
|
if (typeof _ref === "function") {
|
|
502
650
|
_ref(ref);
|
|
@@ -507,15 +655,22 @@ const DateInput = forwardRef(({ className, style = {}, defaultValue, value, form
|
|
|
507
655
|
e.preventDefault();
|
|
508
656
|
setSelectedSegment(segments[0]);
|
|
509
657
|
}, onBlur: () => {
|
|
658
|
+
commitSelectedDraftSegment();
|
|
659
|
+
clearTypedSegment();
|
|
510
660
|
setSelectedSegment(null);
|
|
511
661
|
}, "data-empty": !_value, "data-disabled": disabled, role: "textbox", size: size, variant: variant, "data-button-left": enableCalendar, "data-button-right": arrow || clearable, style: style, children: [enableCalendar && (_jsx(CalendarTriggerButton, { size: size, type: "button", "aria-label": "Toggle calendar", onClick: handleCalendarTriggerClick, "data-default-btn": true, children: _jsx(CalendarDaysIcon, {}) })), segments.map((segment, i) => {
|
|
662
|
+
const segmentText = (draftSegment === null || draftSegment === void 0 ? void 0 : draftSegment.index) === segment.index
|
|
663
|
+
? draftSegment.text
|
|
664
|
+
: segment.text;
|
|
512
665
|
if (segment.type === "separator") {
|
|
513
666
|
return (_jsx("div", { className: "separator", tabIndex: -1, onClick: (e) => {
|
|
514
667
|
e.preventDefault();
|
|
515
668
|
e.stopPropagation();
|
|
516
|
-
}, onFocus: (e) => e.preventDefault(), onPointerDown: (e) => e.preventDefault(), "data-type": "separator", "data-identifier": segment.type, "data-has-space":
|
|
669
|
+
}, onFocus: (e) => e.preventDefault(), onPointerDown: (e) => e.preventDefault(), "data-type": "separator", "data-identifier": segment.type, "data-has-space": segmentText.includes(" "), "data-placeholder": segment.placeholder, "data-value": segment.value, children: segmentText }, i));
|
|
517
670
|
}
|
|
518
|
-
return (_jsx(InputSegment, { className: "input", tabIndex: i === 0 ? 0 : -1, onClick: (e) => handleSegmentClick(e, segment), "data-type": "input", size: size, "data-identifier": segment.type, "data-has-space":
|
|
671
|
+
return (_jsx(InputSegment, { className: "input", tabIndex: i === 0 ? 0 : -1, onClick: (e) => handleSegmentClick(e, segment), "data-type": "input", size: size, "data-identifier": segment.type, "data-has-space": segmentText.includes(" "), "data-placeholder": segment.placeholder, "data-value": segment.value, "data-selected": (selectedSegment === null || selectedSegment === void 0 ? void 0 : selectedSegment.index) === segment.index, children: _value || (draftSegment === null || draftSegment === void 0 ? void 0 : draftSegment.index) === segment.index
|
|
672
|
+
? segmentText
|
|
673
|
+
: segment.placeholder }, i));
|
|
519
674
|
}), utc && _jsx("div", { style: { marginLeft: 5 }, children: "UTC" }), clearable && _value ? (_jsx(ClearButton, { size: size, onClick: handleClear })) : arrow ? (_jsx(ArrowButton, { size: size })) : null] }), !disabled && enableCalendar && isOpen && (_jsx(FloatingPortal, { preserveTabOrder: true, children: _jsx(StyledFloatContainer, Object.assign({ ref: refs.setFloating, style: floatingStyles }, getFloatingProps(), { children: _jsx(StyledContent, { maxDropdownHeight: "fit-content", children: _jsx("div", { children: _jsx(Calendar, { value: getCalendarValue(), clearable: false, min: min, max: max, onChange: (date, meta) => {
|
|
520
675
|
if (!date) {
|
|
521
676
|
commitValue(null);
|