@bigbinary/neeto-atoms 1.0.145 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/{DatePicker-DznpJnhm.js → DatePicker-BC9AOZJ2.js} +696 -131
  2. package/dist/DatePicker-BC9AOZJ2.js.map +1 -0
  3. package/dist/{TimePicker-BA7JhrYd.js → TimePicker-CJ8K2zea.js} +192 -17
  4. package/dist/TimePicker-CJ8K2zea.js.map +1 -0
  5. package/dist/{TranslationProvider-Dd0ITHC0.js → TranslationProvider-BJjfFEgX.js} +12 -1
  6. package/dist/{TranslationProvider-Dd0ITHC0.js.map → TranslationProvider-BJjfFEgX.js.map} +1 -1
  7. package/dist/cjs/{DatePicker-CMcp7TGb.js → DatePicker-aGLz-3Bz.js} +707 -141
  8. package/dist/cjs/DatePicker-aGLz-3Bz.js.map +1 -0
  9. package/dist/cjs/{TimePicker-CVGXEgk5.js → TimePicker-BhOW7vbV.js} +217 -41
  10. package/dist/cjs/TimePicker-BhOW7vbV.js.map +1 -0
  11. package/dist/cjs/{TranslationProvider-DOeyGtbV.js → TranslationProvider-C1JDwKEN.js} +12 -1
  12. package/dist/cjs/{TranslationProvider-DOeyGtbV.js.map → TranslationProvider-C1JDwKEN.js.map} +1 -1
  13. package/dist/cjs/components/DatePicker.js +7 -6
  14. package/dist/cjs/components/DatePicker.js.map +1 -1
  15. package/dist/cjs/components/TimePicker.js +7 -7
  16. package/dist/cjs/components/TranslationProvider.js +1 -1
  17. package/dist/cjs/components/index.js +7 -7
  18. package/dist/cjs/index.js +8 -8
  19. package/dist/cjs/{TimePickerPanel-DvVRLqeM.js → utils-S4yTubWZ.js} +25 -197
  20. package/dist/cjs/utils-S4yTubWZ.js.map +1 -0
  21. package/dist/components/DatePicker/DatePickerFooter.d.ts +1 -0
  22. package/dist/components/DatePicker/RangeCalendar.d.ts +2 -1
  23. package/dist/components/DatePicker/SegmentedTimeInput.d.ts +17 -0
  24. package/dist/components/DatePicker/TimeSection.d.ts +22 -0
  25. package/dist/components/DatePicker/constants.d.ts +1 -1
  26. package/dist/components/DatePicker/segmentedTime.d.ts +58 -0
  27. package/dist/components/DatePicker/types.d.ts +33 -4
  28. package/dist/components/DatePicker/utils.d.ts +3 -0
  29. package/dist/components/DatePicker.js +7 -6
  30. package/dist/components/DatePicker.js.map +1 -1
  31. package/dist/components/TimePicker.js +6 -6
  32. package/dist/components/TranslationProvider.js +1 -1
  33. package/dist/components/index.js +6 -6
  34. package/dist/index.js +8 -8
  35. package/dist/translations/index.d.ts +11 -0
  36. package/dist/utils/inputMask.d.ts +2 -1
  37. package/dist/{TimePickerPanel-n-Dz1p5N.js → utils-Ca3DmOrC.js} +22 -197
  38. package/dist/utils-Ca3DmOrC.js.map +1 -0
  39. package/package.json +1 -1
  40. package/dist/DatePicker-DznpJnhm.js.map +0 -1
  41. package/dist/TimePicker-BA7JhrYd.js.map +0 -1
  42. package/dist/TimePickerPanel-n-Dz1p5N.js.map +0 -1
  43. package/dist/cjs/DatePicker-CMcp7TGb.js.map +0 -1
  44. package/dist/cjs/TimePicker-CVGXEgk5.js.map +0 -1
  45. package/dist/cjs/TimePickerPanel-DvVRLqeM.js.map +0 -1
@@ -5,7 +5,7 @@ var React = require('react');
5
5
  var primitives_Calendar = require('./primitives/Calendar.js');
6
6
  var primitives_Popover = require('./primitives/Popover.js');
7
7
  var utils$1 = require('./utils-BhM0B89p.js');
8
- var TimePickerPanel = require('./TimePickerPanel-DvVRLqeM.js');
8
+ var utils$2 = require('./utils-S4yTubWZ.js');
9
9
  var pureDayjs = require('dayjs');
10
10
  var customParseFormat = require('dayjs/plugin/customParseFormat');
11
11
  var utils = require('@bigbinary/neeto-commons-frontend/utils');
@@ -14,11 +14,15 @@ var primitives_Button = require('./primitives/Button.js');
14
14
  var primitives_Field = require('./primitives/Field.js');
15
15
  var createLucideIcon = require('./createLucideIcon-D0tRgV6l.js');
16
16
  var x = require('./x-Brw3FJst.js');
17
+ var reactI18next = require('react-i18next');
18
+ var primitives_Label = require('./primitives/Label.js');
19
+ var primitives_Switch = require('./primitives/Switch.js');
17
20
  var chevronLeft = require('./chevron-left-BldoOh5p.js');
18
21
  var chevronRight = require('./chevron-right-0jNdwX2Q.js');
19
22
 
20
23
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
24
 
25
+ var React__default = /*#__PURE__*/_interopDefault(React);
22
26
  var pureDayjs__default = /*#__PURE__*/_interopDefault(pureDayjs);
23
27
  var customParseFormat__default = /*#__PURE__*/_interopDefault(customParseFormat);
24
28
 
@@ -30,16 +34,31 @@ var customParseFormat__default = /*#__PURE__*/_interopDefault(customParseFormat)
30
34
  */
31
35
 
32
36
 
33
- const __iconNode = [
37
+ const __iconNode$1 = [
34
38
  ["path", { d: "M8 2v4", key: "1cmpym" }],
35
39
  ["path", { d: "M16 2v4", key: "4m81vk" }],
36
40
  ["rect", { width: "18", height: "18", x: "3", y: "4", rx: "2", key: "1hopcy" }],
37
41
  ["path", { d: "M3 10h18", key: "8toen8" }]
38
42
  ];
39
- const Calendar = createLucideIcon.createLucideIcon("calendar", __iconNode);
43
+ const Calendar = createLucideIcon.createLucideIcon("calendar", __iconNode$1);
44
+
45
+ /**
46
+ * @license lucide-react v0.577.0 - ISC
47
+ *
48
+ * This source code is licensed under the ISC license.
49
+ * See the LICENSE file in the root directory of this source tree.
50
+ */
51
+
52
+
53
+ const __iconNode = [
54
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
55
+ ["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20", key: "13o1zl" }],
56
+ ["path", { d: "M2 12h20", key: "9i4pu4" }]
57
+ ];
58
+ const Globe = createLucideIcon.createLucideIcon("globe", __iconNode);
40
59
 
41
60
  const DEFAULT_DATE_FORMAT = "dd/MM/yyyy";
42
- const DEFAULT_TIME_FORMAT = "HH:mm:ss";
61
+ const DEFAULT_TIME_FORMAT = "hh:mm A";
43
62
  const CALENDAR_NAVIGATION_YEAR_RANGE = 100;
44
63
  const INITIAL_TIME_VALUE = {
45
64
  hours: 0,
@@ -110,6 +129,33 @@ const getInitialCalendarMonth = (value, defaultValue, type) => {
110
129
  };
111
130
  const normalizeToDayjsFormat = (fmt) => fmt.replace(/\byyyy\b/g, "YYYY").replace(/\byy\b/g, "YY").replace(/\bdd\b/g, "DD").replace(/\bd\b/g, "D");
112
131
  const normalizeToDateFnsFormat = (fmt) => fmt.replace(/YYYY/g, "yyyy").replace(/YY/g, "yy").replace(/DD/g, "dd").replace(/\bD\b/g, "d");
132
+ const roundToMinuteStep = (date, step) => {
133
+ const rounded = new Date(date);
134
+ const minutes = Math.min(
135
+ 60 - step,
136
+ Math.round(date.getMinutes() / step) * step
137
+ );
138
+ rounded.setMinutes(minutes, 0, 0);
139
+ return rounded;
140
+ };
141
+ const getTimezoneLabel = (timezone) => {
142
+ const zone = timezone === "utc" ? "UTC" : utils.dayjs().$x?.$timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
143
+ try {
144
+ const zoneName = (timeZoneName) => new Intl.DateTimeFormat("en-US", { timeZone: zone, timeZoneName }).formatToParts(/* @__PURE__ */ new Date()).find((part) => part.type === "timeZoneName")?.value;
145
+ const name = zoneName("long") ?? zone;
146
+ const offset = (zoneName("longOffset") ?? "GMT").replace("GMT", "UTC").replace(/([+-])0?(\d+):00$/, "$1$2").replace(/([+-])0(\d)/, "$1$2");
147
+ return `${name} (${offset === "UTC" ? "UTC+0" : offset})`;
148
+ } catch {
149
+ return zone;
150
+ }
151
+ };
152
+ const roundUpToMinuteStep = (date, step) => {
153
+ const rounded = new Date(date);
154
+ const hasSeconds = date.getSeconds() > 0 || date.getMilliseconds() > 0;
155
+ const minutes = date.getMinutes() + (hasSeconds ? 1 : 0);
156
+ rounded.setMinutes(Math.ceil(minutes / step) * step, 0, 0);
157
+ return rounded;
158
+ };
113
159
  const formatDate = (date, formatStr) => {
114
160
  if (!date) return "";
115
161
  try {
@@ -268,6 +314,7 @@ const DatePickerFooter = ({
268
314
  timezone,
269
315
  onTimezoneChange,
270
316
  nowDisabled,
317
+ showNow = true,
271
318
  onNow,
272
319
  onOk,
273
320
  okLabel = "OK",
@@ -282,7 +329,7 @@ const DatePickerFooter = ({
282
329
  className
283
330
  ),
284
331
  children: [
285
- /* @__PURE__ */ jsxRuntime.jsx(
332
+ showNow && /* @__PURE__ */ jsxRuntime.jsx(
286
333
  primitives_Button.Button,
287
334
  {
288
335
  variant: "ghost",
@@ -442,6 +489,443 @@ const DatePickerTrigger = React.forwardRef(
442
489
  );
443
490
  DatePickerTrigger.displayName = "DatePickerTrigger";
444
491
 
492
+ const SEGMENT_KEYS = ["hour", "minute", "period"];
493
+ const EMPTY_SEGMENT = { text: "", done: false, auto: false };
494
+ const segment = (text, done, auto = false) => ({
495
+ text,
496
+ done,
497
+ auto
498
+ });
499
+ const clampIndex = (index) => Math.max(0, Math.min(2, index));
500
+ const withSegment = (state, key, value) => ({ ...state, [key]: value });
501
+ const snapMinute = (minutes, step = 1) => utils$2.padNumber(Math.min(60 - step, Math.round(Number(minutes) / step) * step));
502
+ const emptySegmentedTime = () => ({
503
+ hour: EMPTY_SEGMENT,
504
+ minute: EMPTY_SEGMENT,
505
+ period: EMPTY_SEGMENT,
506
+ active: 0
507
+ });
508
+ const segmentedTimeFromValue = (value, step = 1) => {
509
+ const { hour12, period } = utils$2.to12Hour(value.hours);
510
+ return {
511
+ hour: segment(String(hour12), true),
512
+ minute: segment(snapMinute(value.minutes, step), true),
513
+ period: segment(period, true),
514
+ active: 0
515
+ };
516
+ };
517
+ const isSegmentedTimeEmpty = (state) => SEGMENT_KEYS.every((key) => state[key].text === "");
518
+ const getSegmentedTimeError = (state) => {
519
+ if (isSegmentedTimeEmpty(state)) return "timeRequired";
520
+ if (!state.hour.text) return "hourRequired";
521
+ if (!state.minute.text) return "minuteRequired";
522
+ if (!state.period.text) return "periodRequired";
523
+ return null;
524
+ };
525
+ const segmentedTimeToValue = (state) => {
526
+ if (getSegmentedTimeError(state)) return null;
527
+ return {
528
+ hours: utils$2.to24Hour(Number(state.hour.text), state.period.text),
529
+ minutes: Number(state.minute.text),
530
+ seconds: 0
531
+ };
532
+ };
533
+ const finishSegment = (state, key, step) => {
534
+ const { text } = state[key];
535
+ if (!text) return state;
536
+ if (key === "hour" && text === "0") {
537
+ return withSegment(state, "hour", EMPTY_SEGMENT);
538
+ }
539
+ if (key === "minute" && text.length === 1) {
540
+ return withSegment(
541
+ state,
542
+ "minute",
543
+ segment(snapMinute(text, step), true, true)
544
+ );
545
+ }
546
+ return withSegment(state, key, { ...state[key], done: true });
547
+ };
548
+ const finishActiveSegment = (state, step = 1) => finishSegment(state, SEGMENT_KEYS[state.active], step);
549
+ const focusSegment = (state, index, step = 1) => {
550
+ const next = clampIndex(index);
551
+ const finished = next > state.active ? finishActiveSegment(state, step) : state;
552
+ return { ...finished, active: next };
553
+ };
554
+ const focusFirstEmptySegment = (state) => {
555
+ const index = SEGMENT_KEYS.findIndex((key) => !state[key].text);
556
+ return { ...state, active: index === -1 ? 2 : index };
557
+ };
558
+ const nextSegment = (state, step = 1) => state[SEGMENT_KEYS[state.active]].text ? focusSegment(state, state.active + 1, step) : state;
559
+ const previousSegment = (state) => focusSegment(state, state.active - 1);
560
+ const typeDigit = (state, digit, step = 1) => {
561
+ const advance = (next, index) => focusSegment(next, index, step);
562
+ const key = SEGMENT_KEYS[state.active];
563
+ if (key === "period") return state;
564
+ const current = state[key];
565
+ const text = current.done ? "" : current.text;
566
+ if (key === "hour") {
567
+ if (text === "") {
568
+ if (digit === "0" || digit === "1") {
569
+ return withSegment(state, "hour", segment(digit, false));
570
+ }
571
+ return advance(withSegment(state, "hour", segment(digit, true)), 1);
572
+ }
573
+ if (text === "1") {
574
+ if ("012".includes(digit)) {
575
+ return advance(
576
+ withSegment(state, "hour", segment(`1${digit}`, true)),
577
+ 1
578
+ );
579
+ }
580
+ const moved = advance(withSegment(state, "hour", segment("1", true)), 1);
581
+ return typeDigit(moved, digit, step);
582
+ }
583
+ if (digit === "0") return state;
584
+ return advance(withSegment(state, "hour", segment(digit, true)), 1);
585
+ }
586
+ if (text === "") {
587
+ if (digit >= "6") {
588
+ return advance(
589
+ withSegment(
590
+ state,
591
+ "minute",
592
+ segment(snapMinute(digit, step), true, true)
593
+ ),
594
+ 2
595
+ );
596
+ }
597
+ return withSegment(state, "minute", segment(digit, false));
598
+ }
599
+ return advance(
600
+ withSegment(state, "minute", segment(snapMinute(text + digit, step), true)),
601
+ 2
602
+ );
603
+ };
604
+ const typePeriod = (state, letter, step = 1) => withSegment(
605
+ focusSegment(state, 2, step),
606
+ "period",
607
+ segment(letter === "a" ? "AM" : "PM", true)
608
+ );
609
+ const togglePeriod = (state) => withSegment(
610
+ state,
611
+ "period",
612
+ segment(state.period.text === "AM" ? "PM" : "AM", true)
613
+ );
614
+ const stepSegment = (state, direction, step = 1) => {
615
+ const key = SEGMENT_KEYS[state.active];
616
+ if (key === "hour") {
617
+ const hour = Number(state.hour.text) || (direction > 0 ? 0 : 1);
618
+ const next = (hour - 1 + direction + 12) % 12 + 1;
619
+ return withSegment(state, "hour", segment(String(next), true));
620
+ }
621
+ if (key === "minute") {
622
+ const minute = state.minute.text === "" ? direction > 0 ? -step : 0 : Number(state.minute.text);
623
+ const stepped = direction > 0 ? Math.floor(minute / step) * step + step : Math.ceil(minute / step) * step - step;
624
+ return withSegment(
625
+ state,
626
+ "minute",
627
+ segment(utils$2.padNumber((stepped + 60) % 60), true)
628
+ );
629
+ }
630
+ return togglePeriod(state);
631
+ };
632
+ const backspace = (state) => {
633
+ const moveBack = state[SEGMENT_KEYS[state.active]].text === "" && state.active > 0;
634
+ const active = moveBack ? clampIndex(state.active - 1) : state.active;
635
+ const key = SEGMENT_KEYS[active];
636
+ const current = state[key];
637
+ const text = key === "period" || current.auto ? "" : current.text.slice(0, -1);
638
+ return { ...withSegment(state, key, segment(text, false)), active };
639
+ };
640
+ const parsePastedTime = (text, step = 1) => {
641
+ const match = text.trim().toLowerCase().match(/^(\d{1,2}):?(\d{2})?\s*([ap])?\.?m?\.?$/);
642
+ if (!match) return null;
643
+ let hours = Number(match[1]);
644
+ const minutes = Number(match[2] ?? 0);
645
+ const period = match[3];
646
+ if (minutes > 59) return null;
647
+ if (period) {
648
+ if (hours < 1 || hours > 12) return null;
649
+ hours = hours % 12 + (period === "p" ? 12 : 0);
650
+ } else if (hours > 23) {
651
+ return null;
652
+ }
653
+ return { hours, minutes: Number(snapMinute(minutes, step)), seconds: 0 };
654
+ };
655
+
656
+ const PLACEHOLDERS = ["hh", "mm", "--"];
657
+ const SegmentedTimeInput = ({
658
+ value,
659
+ minuteStep,
660
+ onChange,
661
+ onErrorChange,
662
+ invalid = false,
663
+ disabled = false,
664
+ autoFocus = false,
665
+ "aria-describedby": ariaDescribedBy
666
+ }) => {
667
+ const { t } = reactI18next.useTranslation();
668
+ const fieldRef = React.useRef(null);
669
+ const step = minuteStep ?? 1;
670
+ const [time, setTime] = React.useState(
671
+ () => segmentedTimeFromValue(value, step)
672
+ );
673
+ const lastEmittedRef = React.useRef(value);
674
+ React.useEffect(() => {
675
+ if (utils$2.timeValueEquals(value, lastEmittedRef.current)) return;
676
+ lastEmittedRef.current = value;
677
+ setTime(segmentedTimeFromValue(value, step));
678
+ }, [value]);
679
+ const error = getSegmentedTimeError(time);
680
+ React.useEffect(() => {
681
+ onErrorChange(error);
682
+ }, [error, onErrorChange]);
683
+ React.useEffect(() => {
684
+ const snapped = segmentedTimeToValue(time);
685
+ if (snapped && !utils$2.timeValueEquals(snapped, value)) {
686
+ lastEmittedRef.current = snapped;
687
+ onChange(snapped);
688
+ }
689
+ }, []);
690
+ React.useEffect(() => {
691
+ if (autoFocus) fieldRef.current?.focus();
692
+ }, [autoFocus]);
693
+ const update = (next) => {
694
+ setTime(next);
695
+ const nextValue = segmentedTimeToValue(next);
696
+ if (!nextValue || utils$2.timeValueEquals(nextValue, lastEmittedRef.current)) {
697
+ return;
698
+ }
699
+ lastEmittedRef.current = nextValue;
700
+ onChange(nextValue);
701
+ };
702
+ const consume = (event) => {
703
+ event.preventDefault();
704
+ event.stopPropagation();
705
+ };
706
+ const handleKeyDown = (event) => {
707
+ const { key } = event;
708
+ if (key === "Tab") {
709
+ if (!event.shiftKey || time.active !== 2) return;
710
+ consume(event);
711
+ update(togglePeriod(time));
712
+ return;
713
+ }
714
+ if ((event.metaKey || event.ctrlKey) && key === "Backspace") {
715
+ consume(event);
716
+ update(emptySegmentedTime());
717
+ return;
718
+ }
719
+ if (event.metaKey || event.ctrlKey || event.altKey) return;
720
+ let next = null;
721
+ if (/^[0-9]$/.test(key)) next = typeDigit(time, key, step);
722
+ else if (/^[ap]$/i.test(key)) {
723
+ next = typePeriod(time, key.toLowerCase(), step);
724
+ } else if (/^m$/i.test(key))
725
+ next = time;
726
+ else if (key === ":" || key === " " || key === "ArrowRight") {
727
+ next = nextSegment(time, step);
728
+ } else if (key === "ArrowLeft") next = previousSegment(time);
729
+ else if (key === "ArrowUp") next = stepSegment(time, 1, step);
730
+ else if (key === "ArrowDown") next = stepSegment(time, -1, step);
731
+ else if (key === "Backspace" || key === "Delete") next = backspace(time);
732
+ if (!next) return;
733
+ consume(event);
734
+ update(next);
735
+ };
736
+ const handlePaste = (event) => {
737
+ event.preventDefault();
738
+ const pasted = parsePastedTime(event.clipboardData.getData("text"), step);
739
+ if (pasted) update({ ...segmentedTimeFromValue(pasted, step), active: 2 });
740
+ };
741
+ const handleMouseDown = (event) => {
742
+ if (disabled) return;
743
+ const segmentEl = event.target.closest(
744
+ "[data-segment]"
745
+ );
746
+ if (segmentEl) {
747
+ event.preventDefault();
748
+ fieldRef.current?.focus();
749
+ update(focusSegment(time, Number(segmentEl.dataset.segment), step));
750
+ return;
751
+ }
752
+ update(focusFirstEmptySegment(time));
753
+ };
754
+ const segmentText = (index) => {
755
+ const { text, done } = time[SEGMENT_KEYS[index]];
756
+ if (!text) return PLACEHOLDERS[index];
757
+ return index < 2 && !done ? `${text}_` : text;
758
+ };
759
+ return /* @__PURE__ */ jsxRuntime.jsx(
760
+ "div",
761
+ {
762
+ ref: fieldRef,
763
+ role: "group",
764
+ tabIndex: disabled ? -1 : 0,
765
+ "aria-label": t("neetoatoms.datePicker.timeField", "Time"),
766
+ "aria-describedby": ariaDescribedBy,
767
+ "aria-invalid": invalid || void 0,
768
+ "aria-disabled": disabled || void 0,
769
+ "data-testid": "datepicker-time-input",
770
+ className: utils$1.cn(
771
+ "group/time flex h-9 w-full cursor-text items-center rounded-md border border-input bg-transparent px-3 text-sm tabular-nums outline-none select-none",
772
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
773
+ invalid && "border-destructive",
774
+ disabled && "pointer-events-none cursor-not-allowed opacity-50"
775
+ ),
776
+ onBlur: () => update(finishActiveSegment(time, step)),
777
+ onKeyDown: handleKeyDown,
778
+ onMouseDown: handleMouseDown,
779
+ onPaste: handlePaste,
780
+ children: SEGMENT_KEYS.map((key, index) => /* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
781
+ index === 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "px-0.5 text-muted-foreground", children: ":" }),
782
+ index === 2 && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "w-1" }),
783
+ /* @__PURE__ */ jsxRuntime.jsx(
784
+ "span",
785
+ {
786
+ "data-segment": index,
787
+ "data-testid": `datepicker-time-${key}`,
788
+ className: utils$1.cn(
789
+ "min-w-[2ch] px-0.5 text-center whitespace-pre",
790
+ !time[key].text && "text-muted-foreground",
791
+ index === time.active && "group-focus/time:shadow-[inset_0_-1.5px_0_var(--color-muted-foreground)]"
792
+ ),
793
+ children: segmentText(index)
794
+ }
795
+ )
796
+ ] }, key))
797
+ }
798
+ );
799
+ };
800
+ SegmentedTimeInput.displayName = "SegmentedTimeInput";
801
+
802
+ const ERROR_DEFAULTS = {
803
+ timeRequired: "Enter a time, or turn off Add time.",
804
+ hourRequired: "Enter an hour.",
805
+ minuteRequired: "Enter minutes.",
806
+ periodRequired: "Type a or p for AM or PM."
807
+ };
808
+ const stepExamples = (step) => {
809
+ const pad = (minutes) => `1:${String(minutes).padStart(2, "0")}`;
810
+ if (step * 2 >= 60) return [pad(0), pad(step)];
811
+ return [pad(step), pad(30 % step === 0 && step < 30 ? 30 : step * 2)];
812
+ };
813
+ const TimeSection = ({
814
+ value,
815
+ minuteStep,
816
+ onChange,
817
+ onErrorChange,
818
+ showError,
819
+ optional,
820
+ hasTime,
821
+ onHasTimeChange,
822
+ timezoneLabel,
823
+ disabled = false,
824
+ className
825
+ }) => {
826
+ const { t } = reactI18next.useTranslation();
827
+ const id = React.useId();
828
+ const switchId = `datepicker-add-time-${id}`;
829
+ const hintId = `datepicker-time-hint-${id}`;
830
+ const errorId = `datepicker-time-error-${id}`;
831
+ const [error, setError] = React.useState(null);
832
+ const [focusOnMount, setFocusOnMount] = React.useState(false);
833
+ const handleErrorChange = React.useCallback(
834
+ (nextError) => {
835
+ setError(nextError);
836
+ onErrorChange(!!nextError);
837
+ },
838
+ [onErrorChange]
839
+ );
840
+ const handleHasTimeChange = (checked) => {
841
+ setFocusOnMount(checked);
842
+ if (!checked) {
843
+ setError(null);
844
+ onErrorChange(false);
845
+ }
846
+ onHasTimeChange(checked);
847
+ };
848
+ const isTimeOn = !optional || hasTime;
849
+ const showHint = (minuteStep ?? 1) > 1;
850
+ const errorMessage = isTimeOn && showError && error ? t(`neetoatoms.datePicker.errors.${error}`, ERROR_DEFAULTS[error]) : null;
851
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils$1.cn("flex flex-col gap-2", className), children: [
852
+ optional && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3", children: [
853
+ /* @__PURE__ */ jsxRuntime.jsxs(
854
+ primitives_Label.Label,
855
+ {
856
+ htmlFor: switchId,
857
+ className: "flex items-center gap-2 font-medium",
858
+ children: [
859
+ /* @__PURE__ */ jsxRuntime.jsx(utils$2.Clock, { "aria-hidden": true, className: "size-4 text-muted-foreground" }),
860
+ t("neetoatoms.datePicker.addTime", "Add time")
861
+ ]
862
+ }
863
+ ),
864
+ /* @__PURE__ */ jsxRuntime.jsx(
865
+ primitives_Switch.Switch,
866
+ {
867
+ id: switchId,
868
+ checked: hasTime,
869
+ "data-testid": "datepicker-add-time-switch",
870
+ disabled,
871
+ onCheckedChange: handleHasTimeChange
872
+ }
873
+ )
874
+ ] }),
875
+ isTimeOn && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
876
+ /* @__PURE__ */ jsxRuntime.jsx(
877
+ SegmentedTimeInput,
878
+ {
879
+ "aria-describedby": [showHint ? hintId : null, errorMessage ? errorId : null].filter(Boolean).join(" ") || void 0,
880
+ autoFocus: focusOnMount,
881
+ disabled,
882
+ invalid: !!errorMessage,
883
+ minuteStep,
884
+ value,
885
+ onChange,
886
+ onErrorChange: handleErrorChange
887
+ }
888
+ ),
889
+ showHint && /* @__PURE__ */ jsxRuntime.jsx(
890
+ "p",
891
+ {
892
+ id: hintId,
893
+ className: "text-xs text-muted-foreground",
894
+ "data-testid": "datepicker-time-hint",
895
+ children: t("neetoatoms.datePicker.timeHint", {
896
+ step: minuteStep,
897
+ first: stepExamples(minuteStep)[0],
898
+ second: stepExamples(minuteStep)[1],
899
+ defaultValue: "Only {{step}}-minute steps are allowed, like {{first}} or {{second}}."
900
+ })
901
+ }
902
+ ),
903
+ errorMessage && /* @__PURE__ */ jsxRuntime.jsx(
904
+ "p",
905
+ {
906
+ id: errorId,
907
+ className: "text-xs text-destructive",
908
+ "data-testid": "datepicker-time-error",
909
+ role: "alert",
910
+ children: errorMessage
911
+ }
912
+ ),
913
+ timezoneLabel && /* @__PURE__ */ jsxRuntime.jsxs(
914
+ "p",
915
+ {
916
+ className: "mt-1 flex items-center gap-2 text-xs text-muted-foreground",
917
+ "data-testid": "datepicker-timezone",
918
+ children: [
919
+ /* @__PURE__ */ jsxRuntime.jsx(Globe, { "aria-hidden": true, className: "size-3.5 shrink-0" }),
920
+ timezoneLabel
921
+ ]
922
+ }
923
+ )
924
+ ] })
925
+ ] });
926
+ };
927
+ TimeSection.displayName = "TimeSection";
928
+
445
929
  const MonthPicker = ({
446
930
  value,
447
931
  displayedYear,
@@ -514,7 +998,8 @@ const RangeCalendar = ({
514
998
  endMonth,
515
999
  onHoverIn,
516
1000
  onHoverOut,
517
- numberOfMonths = 2
1001
+ numberOfMonths = 2,
1002
+ className
518
1003
  }) => /* @__PURE__ */ jsxRuntime.jsx("div", { onMouseOver: onHoverIn, onMouseLeave: onHoverOut, children: /* @__PURE__ */ jsxRuntime.jsx(
519
1004
  primitives_Calendar.Calendar,
520
1005
  {
@@ -523,7 +1008,10 @@ const RangeCalendar = ({
523
1008
  captionLayout: "dropdown",
524
1009
  today: todayDate,
525
1010
  weekStartsOn,
526
- className: "[&_[data-outside][data-selected=true]]:!bg-transparent [&_[data-outside][data-selected=true]]:after:!bg-transparent [&_[data-outside]_button]:!bg-transparent [&_[data-outside]_button]:!text-muted-foreground",
1011
+ className: utils$1.cn(
1012
+ "[&_[data-outside][data-selected=true]]:!bg-transparent [&_[data-outside][data-selected=true]]:after:!bg-transparent [&_[data-outside]_button]:!bg-transparent [&_[data-outside]_button]:!text-muted-foreground",
1013
+ className
1014
+ ),
527
1015
  month,
528
1016
  onMonthChange,
529
1017
  selected,
@@ -697,6 +1185,11 @@ const YearPicker = ({
697
1185
  ] });
698
1186
  };
699
1187
 
1188
+ const TIME_SECTION_CLASS_NAME = "w-0 min-w-full border-t border-border px-3 py-3";
1189
+ const SHOW_TIME_CALENDAR_CLASS_NAME = "[--cell-size:--spacing(8.125)]";
1190
+ const DEFAULT_ADDED_TIME = { hours: 12, minutes: 0, seconds: 0 };
1191
+ const noop = () => {
1192
+ };
700
1193
  const DatePicker = React.forwardRef(
701
1194
  ({
702
1195
  value,
@@ -707,8 +1200,10 @@ const DatePicker = React.forwardRef(
707
1200
  dateFormat = DEFAULT_DATE_FORMAT,
708
1201
  timeFormat = DEFAULT_TIME_FORMAT,
709
1202
  showTime = false,
710
- timePickerFormat,
711
- showSeconds = false,
1203
+ showNow = true,
1204
+ minuteStep,
1205
+ timeOptional = true,
1206
+ hasTime: hasTimeProp,
712
1207
  minDate,
713
1208
  maxDate,
714
1209
  disabledDate,
@@ -721,6 +1216,7 @@ const DatePicker = React.forwardRef(
721
1216
  allowClear = true,
722
1217
  required = false,
723
1218
  timezone,
1219
+ timezoneLabel: timezoneLabelProp,
724
1220
  onTimezoneChange,
725
1221
  onOk,
726
1222
  needConfirm = false,
@@ -745,7 +1241,7 @@ const DatePicker = React.forwardRef(
745
1241
  const popoverContentId = React.useRef(
746
1242
  `datepicker-popover-${generatedId}`
747
1243
  ).current;
748
- const { open, setOpen } = TimePickerPanel.useControlledOpen(openProp, onOpenChange);
1244
+ const { open, setOpen } = utils$2.useControlledOpen(openProp, onOpenChange);
749
1245
  const [internalValue, setInternalValue] = React.useState(
750
1246
  coerceDateValue(defaultValue, type) ?? null
751
1247
  );
@@ -755,6 +1251,15 @@ const DatePicker = React.forwardRef(
755
1251
  const [pendingDate, setPendingDate] = React.useState(null);
756
1252
  const [pendingTime, setPendingTime] = React.useState(INITIAL_TIME_VALUE);
757
1253
  const [pendingTimeTo, setPendingTimeTo] = React.useState(INITIAL_TIME_VALUE);
1254
+ const [hasTimeError, setHasTimeError] = React.useState(false);
1255
+ const [isTimeErrorRequested, setIsTimeErrorRequested] = React.useState(false);
1256
+ const isTimeOptional = showTime && timeOptional && type === "date";
1257
+ const [internalHasTime, setInternalHasTime] = React.useState(
1258
+ () => hasTimeProp ?? (value ?? defaultValue) != null
1259
+ );
1260
+ const committedHasTime = showTime && (!isTimeOptional || (hasTimeProp ?? internalHasTime));
1261
+ const [pendingHasTime, setPendingHasTime] = React.useState(committedHasTime);
1262
+ const isPendingTimeOn = showTime && (!isTimeOptional || pendingHasTime);
758
1263
  const currentValue = React.useMemo(
759
1264
  () => value !== void 0 ? coerceDateValue(value, type) : internalValue,
760
1265
  [value, type, internalValue]
@@ -767,10 +1272,13 @@ const DatePicker = React.forwardRef(
767
1272
  () => toNativeDate(maxDate) ?? void 0,
768
1273
  [maxDate]
769
1274
  );
770
- const displayFormat = getDisplayFormat(dateFormat, timeFormat, showTime);
1275
+ const displayFormat = getDisplayFormat(
1276
+ dateFormat,
1277
+ timeFormat,
1278
+ committedHasTime
1279
+ );
771
1280
  const sizeConfig = SIZE_CONFIG[size];
772
- const maskEnabled = TimePickerPanel.isFixedWidthFormat(displayFormat);
773
- const effectiveTimePickerFormat = timePickerFormat ?? (/\ba\b/i.test(timeFormat) ? "12" : "24");
1281
+ const maskEnabled = utils$2.isFixedWidthFormat(displayFormat);
774
1282
  const defaultPlaceholder = getDatePlaceholder(
775
1283
  dateFormat,
776
1284
  timeFormat,
@@ -778,11 +1286,11 @@ const DatePicker = React.forwardRef(
778
1286
  type
779
1287
  );
780
1288
  const maskTemplate = React.useMemo(
781
- () => maskEnabled ? type === "range" ? TimePickerPanel.buildRangeMaskTemplate(displayFormat) : TimePickerPanel.buildMaskTemplate(displayFormat) : null,
1289
+ () => maskEnabled ? type === "range" ? utils$2.buildRangeMaskTemplate(displayFormat) : utils$2.buildMaskTemplate(displayFormat) : null,
782
1290
  [displayFormat, type, maskEnabled]
783
1291
  );
784
1292
  const singleDateLen = React.useMemo(
785
- () => TimePickerPanel.buildMaskTemplate(displayFormat).pattern.length,
1293
+ () => utils$2.buildMaskTemplate(displayFormat).pattern.length,
786
1294
  [displayFormat]
787
1295
  );
788
1296
  const calendarDisabled = React.useCallback(
@@ -807,38 +1315,46 @@ const DatePicker = React.forwardRef(
807
1315
  return `${from} - ${to}`;
808
1316
  }, [currentValue, displayFormat, type]);
809
1317
  const [inputText, setInputText] = React.useState(() => getDisplayText());
810
- const commitValue = (date) => {
1318
+ const emitChange = (changed, formatted, withTime) => {
1319
+ if (isTimeOptional) onChange?.(changed, formatted, { hasTime: withTime });
1320
+ else onChange?.(changed, formatted);
1321
+ };
1322
+ const commitValue = (date, withTime = committedHasTime) => {
811
1323
  if (date === null) {
812
1324
  setInternalValue(null);
1325
+ setInternalHasTime(false);
813
1326
  if (type === "range") {
814
- onChange?.([null, null], ["", ""]);
1327
+ emitChange([null, null], ["", ""], false);
815
1328
  } else {
816
- onChange?.(null, "");
1329
+ emitChange(null, "", false);
817
1330
  }
818
1331
  setInputText("");
819
1332
  return;
820
1333
  }
1334
+ const commitFormat = getDisplayFormat(dateFormat, timeFormat, withTime);
821
1335
  const toSerializableDayjs = (d) => {
822
1336
  const userTzDayjs = toUserTzDayjs(d);
823
- if (showTime) return userTzDayjs;
1337
+ if (withTime) return userTzDayjs;
824
1338
  return userTzDayjs.hour(12).minute(0).second(0).millisecond(0);
825
1339
  };
826
1340
  if (Array.isArray(date)) {
827
1341
  setInternalValue(date);
828
1342
  const formatted2 = [
829
- formatDate(date[0], displayFormat),
830
- formatDate(date[1], displayFormat)
1343
+ formatDate(date[0], commitFormat),
1344
+ formatDate(date[1], commitFormat)
831
1345
  ];
832
- onChange?.(
1346
+ emitChange(
833
1347
  [toSerializableDayjs(date[0]), toSerializableDayjs(date[1])],
834
- formatted2
1348
+ formatted2,
1349
+ withTime
835
1350
  );
836
1351
  setInputText(`${formatted2[0]} - ${formatted2[1]}`);
837
1352
  return;
838
1353
  }
839
1354
  setInternalValue(date);
840
- const formatted = formatDate(date, displayFormat);
841
- onChange?.(toSerializableDayjs(date), formatted);
1355
+ setInternalHasTime(withTime);
1356
+ const formatted = formatDate(date, commitFormat);
1357
+ emitChange(toSerializableDayjs(date), formatted, withTime);
842
1358
  setInputText(formatted);
843
1359
  };
844
1360
  const {
@@ -898,30 +1414,33 @@ const DatePicker = React.forwardRef(
898
1414
  };
899
1415
  const commitPendingOnClose = React.useCallback(() => {
900
1416
  const hasOkButton = showTime || needConfirm;
901
- if (!hasOkButton || !pendingDate) {
1417
+ if (!hasOkButton || !pendingDate || isPendingTimeOn && hasTimeError) {
902
1418
  setInputText(getDisplayText());
903
1419
  return;
904
1420
  }
905
- const finalDate = showTime ? applyTimeToDate(pendingDate, pendingTime) : pendingDate;
1421
+ const finalDate = isPendingTimeOn ? applyTimeToDate(pendingDate, pendingTime) : pendingDate;
906
1422
  const currentSingle = type === "date" ? currentValue : null;
907
- const isSame = currentSingle && finalDate.getTime() === currentSingle.getTime();
1423
+ const isSame = currentSingle && isPendingTimeOn === committedHasTime && finalDate.getTime() === currentSingle.getTime();
908
1424
  if (isSame) {
909
1425
  setInputText(getDisplayText());
910
1426
  return;
911
1427
  }
912
- commitValue(finalDate);
1428
+ commitValue(finalDate, isPendingTimeOn);
913
1429
  }, [
914
1430
  showTime,
915
1431
  needConfirm,
916
1432
  pendingDate,
917
1433
  pendingTime,
1434
+ isPendingTimeOn,
1435
+ committedHasTime,
918
1436
  type,
919
1437
  currentValue,
920
1438
  getDisplayText,
921
- onChange
1439
+ onChange,
1440
+ hasTimeError
922
1441
  ]);
923
1442
  const closePopover = React.useCallback(() => {
924
- if (type === "range" && pendingRange?.[0] && pendingRange?.[1]) {
1443
+ if (type === "range" && pendingRange?.[0] && pendingRange?.[1] && !(showTime && hasTimeError)) {
925
1444
  const [fromBase, toBase] = orderRange(
926
1445
  pendingRange[0],
927
1446
  pendingRange[1]
@@ -942,9 +1461,10 @@ const DatePicker = React.forwardRef(
942
1461
  pendingTime,
943
1462
  pendingTimeTo,
944
1463
  showTime,
945
- resetTransientState
1464
+ resetTransientState,
1465
+ hasTimeError
946
1466
  ]);
947
- TimePickerPanel.useOutsideClickClose({
1467
+ utils$2.useOutsideClickClose({
948
1468
  enabled: open,
949
1469
  containerRef,
950
1470
  popoverElementId: popoverContentId,
@@ -953,26 +1473,32 @@ const DatePicker = React.forwardRef(
953
1473
  const openPopover = () => {
954
1474
  setOpen(true);
955
1475
  resetTransientState();
1476
+ setIsTimeErrorRequested(false);
956
1477
  if (type === "range") {
957
1478
  const rangeVal = currentValue;
958
1479
  const fromDate = rangeVal?.[0] ?? null;
959
1480
  const toDate = rangeVal?.[1] ?? null;
960
1481
  setPendingDate(null);
961
1482
  setPendingTime(
962
- fromDate ? TimePickerPanel.dateToTimeValue(fromDate) : INITIAL_TIME_VALUE
1483
+ fromDate ? utils$2.dateToTimeValue(fromDate) : INITIAL_TIME_VALUE
963
1484
  );
964
- setPendingTimeTo(toDate ? TimePickerPanel.dateToTimeValue(toDate) : INITIAL_TIME_VALUE);
1485
+ setPendingTimeTo(toDate ? utils$2.dateToTimeValue(toDate) : INITIAL_TIME_VALUE);
965
1486
  return;
966
1487
  }
967
1488
  const dateVal = currentValue;
1489
+ setPendingHasTime(committedHasTime);
968
1490
  if (!dateVal) {
969
1491
  setPendingDate(null);
970
- setPendingTime(INITIAL_TIME_VALUE);
1492
+ setPendingTime(
1493
+ isTimeOptional ? DEFAULT_ADDED_TIME : INITIAL_TIME_VALUE
1494
+ );
971
1495
  return;
972
1496
  }
973
1497
  setCalendarMonth(dateVal);
974
1498
  setPendingDate(dateVal);
975
- setPendingTime(TimePickerPanel.dateToTimeValue(dateVal));
1499
+ setPendingTime(
1500
+ committedHasTime || !isTimeOptional ? utils$2.dateToTimeValue(dateVal) : DEFAULT_ADDED_TIME
1501
+ );
976
1502
  };
977
1503
  const handleDateSelect = (selected) => {
978
1504
  if (!selected) return;
@@ -985,13 +1511,15 @@ const DatePicker = React.forwardRef(
985
1511
  setOpen(false);
986
1512
  };
987
1513
  const handleNow = () => {
988
- const now = toBrowserLocalDate(/* @__PURE__ */ new Date());
1514
+ const now = toBrowserLocalDate(
1515
+ showTime && minuteStep ? roundUpToMinuteStep(/* @__PURE__ */ new Date(), minuteStep) : /* @__PURE__ */ new Date()
1516
+ );
989
1517
  if (calendarDisabled(now)) return;
990
1518
  setCalendarMonth(now);
991
1519
  if (type === "range") {
992
1520
  handleRangeSelect(void 0, now);
993
1521
  if (showTime) {
994
- const nowTime = TimePickerPanel.dateToTimeValue(now);
1522
+ const nowTime = utils$2.dateToTimeValue(now);
995
1523
  if (rangeSelectionStep === "from") setPendingTime(nowTime);
996
1524
  else setPendingTimeTo(nowTime);
997
1525
  }
@@ -999,13 +1527,18 @@ const DatePicker = React.forwardRef(
999
1527
  }
1000
1528
  if (showTime || needConfirm) {
1001
1529
  setPendingDate(now);
1002
- setPendingTime(TimePickerPanel.dateToTimeValue(now));
1530
+ setPendingTime(utils$2.dateToTimeValue(now));
1531
+ if (isTimeOptional) setPendingHasTime(true);
1003
1532
  return;
1004
1533
  }
1005
1534
  commitValue(now);
1006
1535
  setOpen(false);
1007
1536
  };
1008
1537
  const handleOk = () => {
1538
+ if (isPendingTimeOn && hasTimeError) {
1539
+ setIsTimeErrorRequested(true);
1540
+ return;
1541
+ }
1009
1542
  if (type === "range" && (showTime || needConfirm)) {
1010
1543
  const committedRange = currentValue;
1011
1544
  const fromValue = pendingRange?.[0] ?? committedRange?.[0] ?? null;
@@ -1024,7 +1557,7 @@ const DatePicker = React.forwardRef(
1024
1557
  const [fromBase, toBase] = orderRange(fromValue, toValue);
1025
1558
  const fromFinal = showTime ? applyTimeToDate(fromBase, pendingTime) : fromBase;
1026
1559
  const toFinal = showTime ? applyTimeToDate(toBase, pendingTimeTo) : toBase;
1027
- commitValue([fromFinal, toFinal]);
1560
+ commitValue([fromFinal, toFinal], showTime);
1028
1561
  onOk?.([fromFinal, toFinal]);
1029
1562
  resetTransientState();
1030
1563
  setOpen(false);
@@ -1033,8 +1566,8 @@ const DatePicker = React.forwardRef(
1033
1566
  return;
1034
1567
  }
1035
1568
  if (pendingDate) {
1036
- const finalDate = showTime ? applyTimeToDate(pendingDate, pendingTime) : pendingDate;
1037
- commitValue(finalDate);
1569
+ const finalDate = isPendingTimeOn ? applyTimeToDate(pendingDate, pendingTime) : pendingDate;
1570
+ commitValue(finalDate, isPendingTimeOn);
1038
1571
  onOk?.(finalDate);
1039
1572
  }
1040
1573
  setOpen(false);
@@ -1047,7 +1580,7 @@ const DatePicker = React.forwardRef(
1047
1580
  const handleInputChange = (e) => {
1048
1581
  let text = e.target.value;
1049
1582
  if (maskTemplate) {
1050
- const masked = TimePickerPanel.applyMask(
1583
+ const masked = utils$2.applyMask(
1051
1584
  text,
1052
1585
  maskTemplate,
1053
1586
  inputText,
@@ -1068,7 +1601,7 @@ const DatePicker = React.forwardRef(
1068
1601
  setCalendarMonth(parsed);
1069
1602
  if (showTime || needConfirm) {
1070
1603
  setPendingDate(parsed);
1071
- setPendingTime(TimePickerPanel.dateToTimeValue(parsed));
1604
+ setPendingTime(utils$2.dateToTimeValue(parsed));
1072
1605
  return;
1073
1606
  }
1074
1607
  commitValue(parsed);
@@ -1089,10 +1622,21 @@ const DatePicker = React.forwardRef(
1089
1622
  maskEnabled,
1090
1623
  singleDateLen
1091
1624
  );
1092
- if (range) commitValue(range);
1625
+ if (range) {
1626
+ commitValue(
1627
+ showTime && minuteStep ? [
1628
+ roundToMinuteStep(range[0], minuteStep),
1629
+ roundToMinuteStep(range[1], minuteStep)
1630
+ ] : range
1631
+ );
1632
+ }
1093
1633
  } else {
1094
1634
  const parsed = parseDate(inputText, displayFormat);
1095
- if (parsed) commitValue(parsed);
1635
+ if (parsed) {
1636
+ commitValue(
1637
+ committedHasTime && minuteStep ? roundToMinuteStep(parsed, minuteStep) : parsed
1638
+ );
1639
+ }
1096
1640
  }
1097
1641
  setOpen(false);
1098
1642
  };
@@ -1106,16 +1650,26 @@ const DatePicker = React.forwardRef(
1106
1650
  React.useEffect(() => {
1107
1651
  if (!open) setInputText(getDisplayText());
1108
1652
  }, [getDisplayText, open]);
1109
- TimePickerPanel.useCursorRestore([{ inputRef, cursorRef: cursorPosRef }]);
1653
+ utils$2.useCursorRestore([{ inputRef, cursorRef: cursorPosRef }]);
1110
1654
  React.useEffect(() => {
1111
1655
  if (!open || type !== "date") return;
1112
1656
  const dateVal = currentValue;
1113
1657
  if (dateVal instanceof Date) setCalendarMonth(dateVal);
1114
1658
  }, [open]);
1659
+ const timezoneLabel = showTime && !onTimezoneChange ? (timezoneLabelProp ?? getTimezoneLabel(timezone)) || null : null;
1115
1660
  const hasField = !!(label || error || helpText);
1116
1661
  const testIdPrefix = fieldIds.buildTestIdPrefix(dataTestId, label);
1117
1662
  const showFooter = showTime || needConfirm || !!onTimezoneChange;
1118
1663
  const todayDate = toBrowserLocalDate(/* @__PURE__ */ new Date());
1664
+ const handleHasTimeChange = (checked) => {
1665
+ setPendingHasTime(checked);
1666
+ setIsTimeErrorRequested(false);
1667
+ if (!checked) setHasTimeError(false);
1668
+ if (checked && !pendingDate && !calendarDisabled(todayDate)) {
1669
+ setPendingDate(todayDate);
1670
+ setCalendarMonth(todayDate);
1671
+ }
1672
+ };
1119
1673
  const defaultStartMonth = new Date(
1120
1674
  todayDate.getFullYear() - CALENDAR_NAVIGATION_YEAR_RANGE,
1121
1675
  0,
@@ -1214,99 +1768,110 @@ const DatePicker = React.forwardRef(
1214
1768
  onPointerDownOutside: (e) => e.preventDefault(),
1215
1769
  onInteractOutside: (e) => e.preventDefault(),
1216
1770
  children: [
1217
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils$1.cn("flex", showTime && "flex-row"), children: [
1218
- type === "date" && picker === "month" ? /* @__PURE__ */ jsxRuntime.jsx(
1219
- MonthPicker,
1220
- {
1221
- value: currentValue ?? null,
1222
- displayedYear: calendarMonth.getFullYear(),
1223
- onYearChange: (year) => setCalendarMonth(
1224
- new Date(year, calendarMonth.getMonth(), 1)
1225
- ),
1226
- onSelect: handleGridPickerSelect,
1227
- isDisabled: (date) => isMonthDisabled(date, normalizedMinDate, normalizedMaxDate)
1228
- }
1229
- ) : type === "date" && picker === "year" ? /* @__PURE__ */ jsxRuntime.jsx(
1230
- YearPicker,
1231
- {
1232
- value: currentValue ?? null,
1233
- displayedYear: calendarMonth.getFullYear(),
1234
- onYearChange: (year) => setCalendarMonth(new Date(year, 0, 1)),
1235
- onSelect: handleGridPickerSelect,
1236
- isDisabled: (date) => isYearDisabled(date, normalizedMinDate, normalizedMaxDate)
1237
- }
1238
- ) : type === "date" ? /* @__PURE__ */ jsxRuntime.jsx(
1239
- primitives_Calendar.Calendar,
1240
- {
1241
- mode: "single",
1242
- captionLayout: "dropdown",
1243
- today: todayDate,
1244
- month: calendarMonth,
1245
- onMonthChange: setCalendarMonth,
1246
- selected: calendarSelected(),
1247
- onSelect: handleSingleDateSelectWithPicker,
1248
- disabled: calendarDisabled,
1249
- weekStartsOn,
1250
- classNames: weekRowHoverClassName ? { week: utils$1.cn("mt-2 flex w-full", weekRowHoverClassName) } : void 0,
1251
- ...weekHighlightProps,
1252
- startMonth: calendarStartMonth,
1253
- endMonth: calendarEndMonth
1254
- }
1255
- ) : showTime ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1256
- /* @__PURE__ */ jsxRuntime.jsx(
1257
- RangeCalendar,
1258
- {
1259
- todayDate,
1260
- weekStartsOn,
1261
- month: calendarMonth,
1262
- onMonthChange: setCalendarMonth,
1263
- selected: memoizedRangeSelected,
1264
- onSelect: handleRangeSelect,
1265
- disabled: calendarDisabled,
1266
- startMonth: calendarStartMonth,
1267
- endMonth: calendarEndMonth,
1268
- onHoverIn: handleHoverIn,
1269
- onHoverOut: handleHoverOut,
1270
- numberOfMonths: 1
1271
- }
1771
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex", children: type === "date" && picker === "month" ? /* @__PURE__ */ jsxRuntime.jsx(
1772
+ MonthPicker,
1773
+ {
1774
+ value: currentValue ?? null,
1775
+ displayedYear: calendarMonth.getFullYear(),
1776
+ onYearChange: (year) => setCalendarMonth(
1777
+ new Date(year, calendarMonth.getMonth(), 1)
1272
1778
  ),
1273
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-inline-start border-border pt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
1274
- TimePickerPanel.TimePickerPanel,
1275
- {
1276
- value: rangeSelectionStep === "from" ? pendingTime : pendingTimeTo,
1277
- onChange: rangeSelectionStep === "from" ? setPendingTime : setPendingTimeTo,
1278
- format: effectiveTimePickerFormat,
1279
- showSeconds,
1280
- disabled
1281
- }
1282
- ) })
1283
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1284
- RangeCalendar,
1285
- {
1286
- todayDate,
1287
- weekStartsOn,
1288
- month: calendarMonth,
1289
- onMonthChange: setCalendarMonth,
1290
- selected: memoizedRangeSelected,
1291
- onSelect: handleRangeSelect,
1292
- disabled: calendarDisabled,
1293
- startMonth: calendarStartMonth,
1294
- endMonth: calendarEndMonth,
1295
- onHoverIn: handleHoverIn,
1296
- onHoverOut: handleHoverOut
1297
- }
1298
- ),
1299
- showTime && type !== "range" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-inline-start border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
1300
- TimePickerPanel.TimePickerPanel,
1301
- {
1302
- value: pendingTime,
1303
- onChange: setPendingTime,
1304
- format: effectiveTimePickerFormat,
1305
- showSeconds,
1306
- disabled
1307
- }
1308
- ) })
1309
- ] }),
1779
+ onSelect: handleGridPickerSelect,
1780
+ isDisabled: (date) => isMonthDisabled(date, normalizedMinDate, normalizedMaxDate)
1781
+ }
1782
+ ) : type === "date" && picker === "year" ? /* @__PURE__ */ jsxRuntime.jsx(
1783
+ YearPicker,
1784
+ {
1785
+ value: currentValue ?? null,
1786
+ displayedYear: calendarMonth.getFullYear(),
1787
+ onYearChange: (year) => setCalendarMonth(new Date(year, 0, 1)),
1788
+ onSelect: handleGridPickerSelect,
1789
+ isDisabled: (date) => isYearDisabled(date, normalizedMinDate, normalizedMaxDate)
1790
+ }
1791
+ ) : type === "date" ? /* @__PURE__ */ jsxRuntime.jsx(
1792
+ primitives_Calendar.Calendar,
1793
+ {
1794
+ className: utils$1.cn(showTime && SHOW_TIME_CALENDAR_CLASS_NAME),
1795
+ mode: "single",
1796
+ captionLayout: "dropdown",
1797
+ today: todayDate,
1798
+ month: calendarMonth,
1799
+ onMonthChange: setCalendarMonth,
1800
+ selected: calendarSelected(),
1801
+ onSelect: handleSingleDateSelectWithPicker,
1802
+ disabled: calendarDisabled,
1803
+ weekStartsOn,
1804
+ classNames: weekRowHoverClassName ? { week: utils$1.cn("mt-2 flex w-full", weekRowHoverClassName) } : void 0,
1805
+ ...weekHighlightProps,
1806
+ startMonth: calendarStartMonth,
1807
+ endMonth: calendarEndMonth
1808
+ }
1809
+ ) : showTime ? /* @__PURE__ */ jsxRuntime.jsx(
1810
+ RangeCalendar,
1811
+ {
1812
+ className: SHOW_TIME_CALENDAR_CLASS_NAME,
1813
+ todayDate,
1814
+ weekStartsOn,
1815
+ month: calendarMonth,
1816
+ onMonthChange: setCalendarMonth,
1817
+ selected: memoizedRangeSelected,
1818
+ onSelect: handleRangeSelect,
1819
+ disabled: calendarDisabled,
1820
+ startMonth: calendarStartMonth,
1821
+ endMonth: calendarEndMonth,
1822
+ onHoverIn: handleHoverIn,
1823
+ onHoverOut: handleHoverOut,
1824
+ numberOfMonths: 1
1825
+ }
1826
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1827
+ RangeCalendar,
1828
+ {
1829
+ todayDate,
1830
+ weekStartsOn,
1831
+ month: calendarMonth,
1832
+ onMonthChange: setCalendarMonth,
1833
+ selected: memoizedRangeSelected,
1834
+ onSelect: handleRangeSelect,
1835
+ disabled: calendarDisabled,
1836
+ startMonth: calendarStartMonth,
1837
+ endMonth: calendarEndMonth,
1838
+ onHoverIn: handleHoverIn,
1839
+ onHoverOut: handleHoverOut
1840
+ }
1841
+ ) }),
1842
+ showTime && type === "range" && /* @__PURE__ */ jsxRuntime.jsx(
1843
+ TimeSection,
1844
+ {
1845
+ className: TIME_SECTION_CLASS_NAME,
1846
+ disabled,
1847
+ hasTime: true,
1848
+ optional: false,
1849
+ showError: isTimeErrorRequested,
1850
+ timezoneLabel,
1851
+ value: rangeSelectionStep === "from" ? pendingTime : pendingTimeTo,
1852
+ onChange: rangeSelectionStep === "from" ? setPendingTime : setPendingTimeTo,
1853
+ minuteStep,
1854
+ onErrorChange: setHasTimeError,
1855
+ onHasTimeChange: noop
1856
+ },
1857
+ rangeSelectionStep
1858
+ ),
1859
+ showTime && type !== "range" && /* @__PURE__ */ jsxRuntime.jsx(
1860
+ TimeSection,
1861
+ {
1862
+ className: TIME_SECTION_CLASS_NAME,
1863
+ disabled,
1864
+ hasTime: pendingHasTime,
1865
+ optional: isTimeOptional,
1866
+ showError: isTimeErrorRequested,
1867
+ timezoneLabel,
1868
+ value: pendingTime,
1869
+ onChange: setPendingTime,
1870
+ minuteStep,
1871
+ onErrorChange: setHasTimeError,
1872
+ onHasTimeChange: handleHasTimeChange
1873
+ }
1874
+ ),
1310
1875
  showFooter && /* @__PURE__ */ jsxRuntime.jsx(
1311
1876
  DatePickerFooter,
1312
1877
  {
@@ -1315,6 +1880,7 @@ const DatePicker = React.forwardRef(
1315
1880
  timezone,
1316
1881
  onTimezoneChange,
1317
1882
  nowDisabled: calendarDisabled(todayDate),
1883
+ showNow,
1318
1884
  onNow: handleNow,
1319
1885
  onOk: handleOk,
1320
1886
  okLabel: type === "range" && (showTime || needConfirm) && rangeSelectionStep === "from" ? "Next" : "OK"
@@ -1351,4 +1917,4 @@ const DatePicker = React.forwardRef(
1351
1917
  DatePicker.displayName = "DatePicker";
1352
1918
 
1353
1919
  exports.DatePicker = DatePicker;
1354
- //# sourceMappingURL=DatePicker-CMcp7TGb.js.map
1920
+ //# sourceMappingURL=DatePicker-aGLz-3Bz.js.map