@bigbinary/neeto-atoms 1.0.56 → 1.0.58

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 (54) hide show
  1. package/dist/{DatePicker-D-0HMiNG.js → DatePicker-kulToqfM.js} +390 -133
  2. package/dist/DatePicker-kulToqfM.js.map +1 -0
  3. package/dist/Select-BiyQTuiQ.js.map +1 -1
  4. package/dist/TimePicker-DoL126Ql.js +444 -0
  5. package/dist/TimePicker-DoL126Ql.js.map +1 -0
  6. package/dist/{TimePickerPanel-zWmOy3Eo.js → TimePickerPanel--KDX5QwS.js} +116 -3
  7. package/dist/TimePickerPanel--KDX5QwS.js.map +1 -0
  8. package/dist/cjs/{DatePicker-JhQ7D2bu.js → DatePicker-BZd4rM2R.js} +388 -131
  9. package/dist/cjs/DatePicker-BZd4rM2R.js.map +1 -0
  10. package/dist/cjs/Select-DC23xcMU.js.map +1 -1
  11. package/dist/cjs/TimePicker-H3OpzvOm.js +446 -0
  12. package/dist/cjs/TimePicker-H3OpzvOm.js.map +1 -0
  13. package/dist/cjs/{TimePickerPanel-B5h5khbs.js → TimePickerPanel-DX6cjrSN.js} +130 -2
  14. package/dist/cjs/TimePickerPanel-DX6cjrSN.js.map +1 -0
  15. package/dist/cjs/components/DatePicker.js +5 -5
  16. package/dist/cjs/components/TimePicker.js +4 -2
  17. package/dist/cjs/components/TimePicker.js.map +1 -1
  18. package/dist/cjs/components/index.js +3 -3
  19. package/dist/cjs/formik/BlockNavigation.js.map +1 -1
  20. package/dist/cjs/index.js +5 -5
  21. package/dist/components/DatePicker/MonthPicker.d.ts +16 -0
  22. package/dist/components/DatePicker/YearPicker.d.ts +14 -0
  23. package/dist/components/DatePicker/constants.d.ts +2 -0
  24. package/dist/components/DatePicker/types.d.ts +21 -1
  25. package/dist/components/DatePicker/utils.d.ts +8 -0
  26. package/dist/components/DatePicker.js +5 -5
  27. package/dist/components/TimePicker/constants.d.ts +17 -0
  28. package/dist/components/TimePicker/index.d.ts +1 -1
  29. package/dist/components/TimePicker/types.d.ts +19 -4
  30. package/dist/components/TimePicker/utils.d.ts +13 -0
  31. package/dist/components/TimePicker.js +4 -2
  32. package/dist/components/TimePicker.js.map +1 -1
  33. package/dist/components/index.js +3 -3
  34. package/dist/formik/BlockNavigation.js.map +1 -1
  35. package/dist/hooks/useControlledOpen.d.ts +5 -0
  36. package/dist/hooks/useCursorRestore.d.ts +8 -0
  37. package/dist/hooks/useOutsideClickClose.d.ts +8 -0
  38. package/dist/index.js +5 -5
  39. package/package.json +1 -1
  40. package/dist/DatePicker-D-0HMiNG.js.map +0 -1
  41. package/dist/TimePicker-CSjiggpr.js +0 -301
  42. package/dist/TimePicker-CSjiggpr.js.map +0 -1
  43. package/dist/TimePickerPanel-zWmOy3Eo.js.map +0 -1
  44. package/dist/cjs/DatePicker-JhQ7D2bu.js.map +0 -1
  45. package/dist/cjs/TimePicker-CU7qJpoT.js +0 -303
  46. package/dist/cjs/TimePicker-CU7qJpoT.js.map +0 -1
  47. package/dist/cjs/TimePickerPanel-B5h5khbs.js.map +0 -1
  48. /package/dist/{hooks → components/Select/hooks}/useAsyncOptions.d.ts +0 -0
  49. /package/dist/{hooks → components/Select/hooks}/useCreatableItems.d.ts +0 -0
  50. /package/dist/{hooks → components/Select/hooks}/useLazyLoadSentinel.d.ts +0 -0
  51. /package/dist/{hooks → components/Select/hooks}/useMultiSelectOptions.d.ts +0 -0
  52. /package/dist/{hooks → components/Select/hooks}/useMultiSelectState.d.ts +0 -0
  53. /package/dist/{hooks → components/Select/hooks}/useSelectState.d.ts +0 -0
  54. /package/dist/{hooks → formik/BlockNavigation/hooks}/useNavPrompt.d.ts +0 -0
@@ -6,11 +6,13 @@ var primitives_Calendar = require('./primitives/Calendar.js');
6
6
  var primitives_Popover = require('./primitives/Popover.js');
7
7
  var primitives_Field = require('./primitives/Field.js');
8
8
  var utils$1 = require('./utils-BhM0B89p.js');
9
+ var TimePickerPanel = require('./TimePickerPanel-DX6cjrSN.js');
9
10
  var pureDayjs = require('dayjs');
10
11
  var customParseFormat = require('dayjs/plugin/customParseFormat');
11
12
  var utils = require('@bigbinary/neeto-commons-frontend/utils');
12
- var TimePickerPanel = require('./TimePickerPanel-B5h5khbs.js');
13
13
  var primitives_Button = require('./primitives/Button.js');
14
+ var chevronLeft = require('./chevron-left-BldoOh5p.js');
15
+ var chevronRight = require('./chevron-right-0jNdwX2Q.js');
14
16
  var createLucideIcon = require('./createLucideIcon-D0tRgV6l.js');
15
17
  var x = require('./x-Brw3FJst.js');
16
18
 
@@ -37,6 +39,11 @@ const Calendar = createLucideIcon.createLucideIcon("calendar", __iconNode);
37
39
 
38
40
  const DEFAULT_DATE_FORMAT = "dd/MM/yyyy";
39
41
  const DEFAULT_TIME_FORMAT = "HH:mm:ss";
42
+ const INITIAL_TIME_VALUE = {
43
+ hours: 0,
44
+ minutes: 0,
45
+ seconds: 0
46
+ };
40
47
  const SIZE_CONFIG = {
41
48
  small: {
42
49
  trigger: "h-8 md:h-7",
@@ -130,6 +137,10 @@ const getDisplayFormat = (dateFormat, timeFormat, showTime) => {
130
137
  const fmt = showTime ? `${dateFormat} ${timeFormat}` : dateFormat;
131
138
  return normalizeToDateFnsFormat(fmt);
132
139
  };
140
+ const getDatePlaceholder = (dateFormat, timeFormat, showTime, type) => {
141
+ const single = showTime ? `${dateFormat.toUpperCase()} ${timeFormat}` : dateFormat.toUpperCase();
142
+ return type === "range" ? `${single} - ${single}` : single;
143
+ };
133
144
  const isDatePartComplete = (part, maskEnabled, singleDateLen) => maskEnabled ? part.length >= singleDateLen : part.length > 0;
134
145
  const parseRangeText = (text, displayFormat, maskEnabled, singleDateLen) => {
135
146
  const parts = text.split(" - ");
@@ -140,6 +151,43 @@ const parseRangeText = (text, displayFormat, maskEnabled, singleDateLen) => {
140
151
  const to = parseDate(parts[1], displayFormat);
141
152
  return from && to ? [from, to] : null;
142
153
  };
154
+ const startOfWeek = (date, weekStartsOn = 0) => {
155
+ const d = new Date(date);
156
+ const diff = (d.getDay() - weekStartsOn + 7) % 7;
157
+ d.setDate(d.getDate() - diff);
158
+ d.setHours(0, 0, 0, 0);
159
+ return d;
160
+ };
161
+ const weekDaysFrom = (start) => Array.from({ length: 7 }, (_, i) => {
162
+ const d = new Date(start);
163
+ d.setDate(start.getDate() + i);
164
+ return d;
165
+ });
166
+ const isMonthDisabled = (monthDate, minDate, maxDate) => {
167
+ const monthStart = new Date(monthDate.getFullYear(), monthDate.getMonth(), 1);
168
+ const monthEnd = new Date(
169
+ monthDate.getFullYear(),
170
+ monthDate.getMonth() + 1,
171
+ 0,
172
+ 23,
173
+ 59,
174
+ 59,
175
+ 999
176
+ );
177
+ if (maxDate && monthStart > maxDate) return true;
178
+ if (minDate && monthEnd < minDate) return true;
179
+ return false;
180
+ };
181
+ const isYearDisabled = (yearDate, minDate, maxDate) => {
182
+ const yearStart = new Date(yearDate.getFullYear(), 0, 1);
183
+ const yearEnd = new Date(yearDate.getFullYear(), 11, 31, 23, 59, 59, 999);
184
+ if (maxDate && yearStart > maxDate) return true;
185
+ if (minDate && yearEnd < minDate) return true;
186
+ return false;
187
+ };
188
+ const buildMonth = (year, month) => new Date(year, month, 1);
189
+ const isSameMonth = (a, b) => !!a && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
190
+ const decadeStartFor = (year) => Math.floor(year / 10) * 10;
143
191
 
144
192
  const TIMEZONE_OPTIONS = [
145
193
  { label: "Local", value: "" },
@@ -202,11 +250,136 @@ const DatePickerFooter = ({
202
250
  };
203
251
  DatePickerFooter.displayName = "DatePickerFooter";
204
252
 
253
+ const MonthPicker = ({
254
+ value,
255
+ displayedYear,
256
+ onYearChange,
257
+ onSelect,
258
+ isDisabled,
259
+ locale
260
+ }) => {
261
+ const months = Array.from(
262
+ { length: 12 },
263
+ (_, m) => buildMonth(displayedYear, m)
264
+ );
265
+ const monthName = (date) => date.toLocaleString(locale, { month: "short" });
266
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-background flex w-[16rem] flex-col gap-3 p-2", children: [
267
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
268
+ /* @__PURE__ */ jsxRuntime.jsx(
269
+ primitives_Button.Button,
270
+ {
271
+ "aria-label": "Previous year",
272
+ size: "icon",
273
+ variant: "ghost",
274
+ onClick: () => onYearChange(displayedYear - 1),
275
+ children: /* @__PURE__ */ jsxRuntime.jsx(chevronLeft.ChevronLeft, { className: "size-4 rtl:rotate-180" })
276
+ }
277
+ ),
278
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: displayedYear }),
279
+ /* @__PURE__ */ jsxRuntime.jsx(
280
+ primitives_Button.Button,
281
+ {
282
+ "aria-label": "Next year",
283
+ size: "icon",
284
+ variant: "ghost",
285
+ onClick: () => onYearChange(displayedYear + 1),
286
+ children: /* @__PURE__ */ jsxRuntime.jsx(chevronRight.ChevronRight, { className: "size-4 rtl:rotate-180" })
287
+ }
288
+ )
289
+ ] }),
290
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-3 gap-2", children: months.map((monthDate) => {
291
+ const disabled = isDisabled?.(monthDate) ?? false;
292
+ const selected = isSameMonth(value, monthDate);
293
+ return /* @__PURE__ */ jsxRuntime.jsx(
294
+ "button",
295
+ {
296
+ "aria-pressed": selected,
297
+ className: utils$1.cn(
298
+ "rounded-md py-2 text-sm transition-colors",
299
+ "hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
300
+ selected && "bg-primary text-primary-foreground hover:bg-primary"
301
+ ),
302
+ disabled,
303
+ type: "button",
304
+ onClick: () => onSelect(monthDate),
305
+ children: monthName(monthDate)
306
+ },
307
+ monthDate.getMonth()
308
+ );
309
+ }) })
310
+ ] });
311
+ };
312
+
313
+ const YearPicker = ({
314
+ value,
315
+ displayedYear,
316
+ onYearChange,
317
+ onSelect,
318
+ isDisabled
319
+ }) => {
320
+ const decadeStart = decadeStartFor(displayedYear);
321
+ const years = Array.from({ length: 12 }, (_, i) => decadeStart - 1 + i);
322
+ const selectedYear = value?.getFullYear();
323
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-background flex w-[16rem] flex-col gap-3 p-2", children: [
324
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
325
+ /* @__PURE__ */ jsxRuntime.jsx(
326
+ primitives_Button.Button,
327
+ {
328
+ "aria-label": "Previous decade",
329
+ size: "icon",
330
+ variant: "ghost",
331
+ onClick: () => onYearChange(decadeStart - 10),
332
+ children: /* @__PURE__ */ jsxRuntime.jsx(chevronLeft.ChevronLeft, { className: "size-4 rtl:rotate-180" })
333
+ }
334
+ ),
335
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium", children: [
336
+ decadeStart,
337
+ "-",
338
+ decadeStart + 9
339
+ ] }),
340
+ /* @__PURE__ */ jsxRuntime.jsx(
341
+ primitives_Button.Button,
342
+ {
343
+ "aria-label": "Next decade",
344
+ size: "icon",
345
+ variant: "ghost",
346
+ onClick: () => onYearChange(decadeStart + 10),
347
+ children: /* @__PURE__ */ jsxRuntime.jsx(chevronRight.ChevronRight, { className: "size-4 rtl:rotate-180" })
348
+ }
349
+ )
350
+ ] }),
351
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-3 gap-2", children: years.map((year) => {
352
+ const yearDate = new Date(year, 0, 1);
353
+ const disabled = isDisabled?.(yearDate) ?? false;
354
+ const selected = selectedYear === year;
355
+ const outsideDecade = year < decadeStart || year > decadeStart + 9;
356
+ return /* @__PURE__ */ jsxRuntime.jsx(
357
+ "button",
358
+ {
359
+ "aria-pressed": selected,
360
+ className: utils$1.cn(
361
+ "rounded-md py-2 text-sm transition-colors",
362
+ "hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
363
+ outsideDecade && "text-muted-foreground",
364
+ selected && "bg-primary text-primary-foreground hover:bg-primary"
365
+ ),
366
+ disabled,
367
+ type: "button",
368
+ onClick: () => onSelect(yearDate),
369
+ children: year
370
+ },
371
+ year
372
+ );
373
+ }) })
374
+ ] });
375
+ };
376
+
205
377
  const DatePicker = React.forwardRef(
206
378
  ({
207
379
  value,
208
380
  defaultValue,
209
381
  onChange,
382
+ onBlur,
210
383
  type = "date",
211
384
  dateFormat = DEFAULT_DATE_FORMAT,
212
385
  timeFormat = DEFAULT_TIME_FORMAT,
@@ -215,6 +388,7 @@ const DatePicker = React.forwardRef(
215
388
  showSeconds = false,
216
389
  minDate,
217
390
  maxDate,
391
+ disabledDate,
218
392
  placeholder,
219
393
  label,
220
394
  error,
@@ -227,9 +401,15 @@ const DatePicker = React.forwardRef(
227
401
  onTimezoneChange,
228
402
  onOk,
229
403
  needConfirm = false,
404
+ open: openProp,
230
405
  onOpenChange,
231
406
  className,
232
- labelProps
407
+ labelProps,
408
+ picker = "date",
409
+ side = "bottom",
410
+ align = "start",
411
+ trigger,
412
+ weekStartsOn = 0
233
413
  }, ref) => {
234
414
  const generatedId = React.useId();
235
415
  const errorId = `error_${generatedId}`;
@@ -240,9 +420,10 @@ const DatePicker = React.forwardRef(
240
420
  const popoverContentId = React.useRef(
241
421
  `datepicker-popover-${generatedId}`
242
422
  ).current;
243
- const [open, setOpen] = React.useState(false);
244
- const [internalValue, setInternalValue] = React.useState(coerceDateValue(defaultValue, type) ?? null);
245
- const currentValue = value !== void 0 ? coerceDateValue(value, type) : internalValue;
423
+ const { open, setOpen } = TimePickerPanel.useControlledOpen(openProp, onOpenChange);
424
+ const [internalValue, setInternalValue] = React.useState(
425
+ coerceDateValue(defaultValue, type) ?? null
426
+ );
246
427
  const [calendarMonth, setCalendarMonth] = React.useState(
247
428
  (type === "date" ? value ?? defaultValue : null) ?? /* @__PURE__ */ new Date()
248
429
  );
@@ -250,65 +431,47 @@ const DatePicker = React.forwardRef(
250
431
  "from"
251
432
  );
252
433
  const [pendingDate, setPendingDate] = React.useState(null);
253
- const [pendingTime, setPendingTime] = React.useState({
254
- hours: 0,
255
- minutes: 0,
256
- seconds: 0
257
- });
434
+ const [pendingTime, setPendingTime] = React.useState(INITIAL_TIME_VALUE);
435
+ const currentValue = value !== void 0 ? coerceDateValue(value, type) : internalValue;
258
436
  const displayFormat = getDisplayFormat(dateFormat, timeFormat, showTime);
259
437
  const sizeConfig = SIZE_CONFIG[size];
260
438
  const maskEnabled = TimePickerPanel.isFixedWidthFormat(displayFormat);
439
+ const defaultPlaceholder = getDatePlaceholder(
440
+ dateFormat,
441
+ timeFormat,
442
+ showTime,
443
+ type
444
+ );
261
445
  const maskTemplate = React.useMemo(
262
446
  () => maskEnabled ? type === "range" ? TimePickerPanel.buildRangeMaskTemplate(displayFormat) : TimePickerPanel.buildMaskTemplate(displayFormat) : null,
263
447
  [displayFormat, type, maskEnabled]
264
448
  );
265
- const singlePlaceholder = showTime ? `${dateFormat.toUpperCase()} ${timeFormat}` : dateFormat.toUpperCase();
266
- const defaultPlaceholder = type === "range" ? `${singlePlaceholder} - ${singlePlaceholder}` : singlePlaceholder;
449
+ const singleDateLen = React.useMemo(
450
+ () => TimePickerPanel.buildMaskTemplate(displayFormat).pattern.length,
451
+ [displayFormat]
452
+ );
453
+ const calendarDisabled = React.useCallback(
454
+ (date) => {
455
+ if (disabledDate?.(date)) return true;
456
+ if (minDate && date < new Date(new Date(minDate).setHours(0, 0, 0, 0)))
457
+ return true;
458
+ if (maxDate && date > new Date(new Date(maxDate).setHours(23, 59, 59, 999)))
459
+ return true;
460
+ return false;
461
+ },
462
+ [minDate, maxDate, disabledDate]
463
+ );
267
464
  const getDisplayText = React.useCallback(() => {
268
- if (type === "range") {
269
- const rangeValue = currentValue;
270
- if (!rangeValue || !rangeValue[0] && !rangeValue[1]) return "";
271
- const from = rangeValue[0] ? formatDate(rangeValue[0], displayFormat) : "";
272
- const to = rangeValue[1] ? formatDate(rangeValue[1], displayFormat) : "";
273
- return `${from} - ${to}`;
465
+ if (type !== "range") {
466
+ return formatDate(currentValue, displayFormat);
274
467
  }
275
- return formatDate(currentValue, displayFormat);
468
+ const rangeValue = currentValue;
469
+ if (!rangeValue || !rangeValue[0] && !rangeValue[1]) return "";
470
+ const from = rangeValue[0] ? formatDate(rangeValue[0], displayFormat) : "";
471
+ const to = rangeValue[1] ? formatDate(rangeValue[1], displayFormat) : "";
472
+ return `${from} - ${to}`;
276
473
  }, [currentValue, displayFormat, type]);
277
474
  const [inputText, setInputText] = React.useState(() => getDisplayText());
278
- React.useEffect(() => {
279
- if (!open) setInputText(getDisplayText());
280
- }, [getDisplayText, open]);
281
- const closePopover = React.useCallback(() => {
282
- setOpen(false);
283
- onOpenChange?.(false);
284
- setInputText(getDisplayText());
285
- }, [onOpenChange, getDisplayText]);
286
- const openPopover = React.useCallback(() => {
287
- setOpen(true);
288
- onOpenChange?.(true);
289
- setRangeSelectionStep("from");
290
- const dateVal = type === "date" ? currentValue : null;
291
- if (dateVal) {
292
- setCalendarMonth(dateVal);
293
- setPendingDate(dateVal);
294
- setPendingTime(TimePickerPanel.dateToTimeValue(dateVal));
295
- } else {
296
- setPendingDate(null);
297
- setPendingTime({ hours: 0, minutes: 0, seconds: 0 });
298
- }
299
- }, [type, currentValue, onOpenChange]);
300
- React.useEffect(() => {
301
- if (!open) return;
302
- const handlePointerDown = (e) => {
303
- const target = e.target;
304
- if (containerRef.current?.contains(target)) return;
305
- const popoverEl = document.getElementById(popoverContentId);
306
- if (popoverEl?.contains(target)) return;
307
- closePopover();
308
- };
309
- document.addEventListener("pointerdown", handlePointerDown);
310
- return () => document.removeEventListener("pointerdown", handlePointerDown);
311
- }, [open, closePopover]);
312
475
  const commitValue = (date) => {
313
476
  if (date === null) {
314
477
  setInternalValue(null);
@@ -336,16 +499,76 @@ const DatePicker = React.forwardRef(
336
499
  onChange?.(toDayjs(converted), formatted);
337
500
  setInputText(formatted);
338
501
  };
502
+ const parseAndApplyRange = (text) => {
503
+ const parts = text.split(" - ");
504
+ if (!isDatePartComplete(parts[0] ?? "", maskEnabled, singleDateLen))
505
+ return;
506
+ const from = parseDate(parts[0], displayFormat);
507
+ if (from) setCalendarMonth(from);
508
+ const range = parseRangeText(
509
+ text,
510
+ displayFormat,
511
+ maskEnabled,
512
+ singleDateLen
513
+ );
514
+ if (range) commitValue(range);
515
+ };
516
+ const commitPendingOnClose = React.useCallback(() => {
517
+ const hasOkButton = showTime || needConfirm;
518
+ if (!hasOkButton || !pendingDate) {
519
+ setInputText(getDisplayText());
520
+ return;
521
+ }
522
+ const finalDate = showTime ? applyTimeToDate(pendingDate, pendingTime) : pendingDate;
523
+ const currentSingle = type === "date" ? currentValue : null;
524
+ const isSame = currentSingle && finalDate.getTime() === currentSingle.getTime();
525
+ if (isSame) {
526
+ setInputText(getDisplayText());
527
+ return;
528
+ }
529
+ commitValue(finalDate);
530
+ }, [
531
+ showTime,
532
+ needConfirm,
533
+ pendingDate,
534
+ pendingTime,
535
+ type,
536
+ currentValue,
537
+ getDisplayText,
538
+ onChange
539
+ ]);
540
+ const closePopover = React.useCallback(() => {
541
+ commitPendingOnClose();
542
+ setOpen(false);
543
+ }, [commitPendingOnClose, setOpen]);
544
+ TimePickerPanel.useOutsideClickClose({
545
+ enabled: open,
546
+ containerRef,
547
+ popoverElementId: popoverContentId,
548
+ onClose: closePopover
549
+ });
550
+ const openPopover = () => {
551
+ setOpen(true);
552
+ setRangeSelectionStep("from");
553
+ const dateVal = type === "date" ? currentValue : null;
554
+ if (!dateVal) {
555
+ setPendingDate(null);
556
+ setPendingTime(INITIAL_TIME_VALUE);
557
+ return;
558
+ }
559
+ setCalendarMonth(dateVal);
560
+ setPendingDate(dateVal);
561
+ setPendingTime(TimePickerPanel.dateToTimeValue(dateVal));
562
+ };
339
563
  const handleDateSelect = (selected) => {
340
564
  if (!selected) return;
341
565
  setCalendarMonth(selected);
342
566
  if (showTime || needConfirm) {
343
567
  setPendingDate(selected);
344
- } else {
345
- commitValue(selected);
346
- setOpen(false);
347
- onOpenChange?.(false);
568
+ return;
348
569
  }
570
+ commitValue(selected);
571
+ setOpen(false);
349
572
  };
350
573
  const handleRangeSelect = (range) => {
351
574
  if (!range) return;
@@ -354,34 +577,29 @@ const DatePicker = React.forwardRef(
354
577
  if (rangeSelectionStep === "from") {
355
578
  setInternalValue([from, null]);
356
579
  setRangeSelectionStep("to");
357
- } else {
358
- if (from && to) {
359
- if (showTime || needConfirm) {
360
- setPendingDate(from);
361
- } else {
362
- commitValue([from, to]);
363
- setOpen(false);
364
- onOpenChange?.(false);
365
- }
366
- } else if (from) {
367
- setInternalValue([from, null]);
580
+ return;
581
+ }
582
+ setRangeSelectionStep("from");
583
+ if (from && to) {
584
+ if (showTime || needConfirm) {
585
+ setPendingDate(from);
586
+ return;
368
587
  }
369
- setRangeSelectionStep("from");
588
+ commitValue([from, to]);
589
+ setOpen(false);
590
+ return;
370
591
  }
371
- };
372
- const handleTimeChange = (time) => {
373
- setPendingTime(time);
592
+ if (from) setInternalValue([from, null]);
374
593
  };
375
594
  const handleNow = () => {
376
595
  const now = toBrowserLocalDate(/* @__PURE__ */ new Date());
377
596
  if (showTime || needConfirm) {
378
597
  setPendingDate(now);
379
598
  setPendingTime(TimePickerPanel.dateToTimeValue(now));
380
- } else {
381
- commitValue(now);
382
- setOpen(false);
383
- onOpenChange?.(false);
599
+ return;
384
600
  }
601
+ commitValue(now);
602
+ setOpen(false);
385
603
  };
386
604
  const handleOk = () => {
387
605
  if (pendingDate) {
@@ -390,40 +608,12 @@ const DatePicker = React.forwardRef(
390
608
  onOk?.(finalDate);
391
609
  }
392
610
  setOpen(false);
393
- onOpenChange?.(false);
394
611
  };
395
612
  const handleClear = (e) => {
396
613
  e.stopPropagation();
397
614
  e.preventDefault();
398
615
  commitValue(null);
399
616
  };
400
- React.useLayoutEffect(() => {
401
- if (cursorPosRef.current !== null && inputRef.current && document.activeElement === inputRef.current) {
402
- inputRef.current.setSelectionRange(
403
- cursorPosRef.current,
404
- cursorPosRef.current
405
- );
406
- cursorPosRef.current = null;
407
- }
408
- });
409
- const singleDateLen = React.useMemo(
410
- () => TimePickerPanel.buildMaskTemplate(displayFormat).pattern.length,
411
- [displayFormat]
412
- );
413
- const parseAndApplyRange = (text) => {
414
- const parts = text.split(" - ");
415
- if (!isDatePartComplete(parts[0] ?? "", maskEnabled, singleDateLen))
416
- return;
417
- const from = parseDate(parts[0], displayFormat);
418
- if (from) setCalendarMonth(from);
419
- const range = parseRangeText(
420
- text,
421
- displayFormat,
422
- maskEnabled,
423
- singleDateLen
424
- );
425
- if (range) commitValue(range);
426
- };
427
617
  const handleInputChange = (e) => {
428
618
  let text = e.target.value;
429
619
  if (maskTemplate) {
@@ -449,12 +639,18 @@ const DatePicker = React.forwardRef(
449
639
  if (showTime || needConfirm) {
450
640
  setPendingDate(parsed);
451
641
  setPendingTime(TimePickerPanel.dateToTimeValue(parsed));
452
- } else {
453
- commitValue(parsed);
642
+ return;
454
643
  }
644
+ commitValue(parsed);
455
645
  };
456
646
  const handleInputKeyDown = (e) => {
457
- if (e.key === "Escape") return closePopover();
647
+ if (e.key === "Escape") {
648
+ setPendingDate(null);
649
+ setPendingTime(INITIAL_TIME_VALUE);
650
+ setInputText(getDisplayText());
651
+ setOpen(false);
652
+ return;
653
+ }
458
654
  if (e.key !== "Enter") return;
459
655
  if (type === "range") {
460
656
  const range = parseRangeText(
@@ -469,31 +665,61 @@ const DatePicker = React.forwardRef(
469
665
  if (parsed) commitValue(parsed);
470
666
  }
471
667
  setOpen(false);
472
- onOpenChange?.(false);
473
668
  };
474
- const handleInputFocus = () => {
475
- if (!open) openPopover();
669
+ const handleInputBlur = (e) => {
670
+ const next = e.relatedTarget;
671
+ if (next && containerRef.current?.contains(next)) return;
672
+ const popoverEl = document.getElementById(popoverContentId);
673
+ if (next && popoverEl?.contains(next)) return;
674
+ onBlur?.(e);
476
675
  };
676
+ React.useEffect(() => {
677
+ if (!open) setInputText(getDisplayText());
678
+ }, [getDisplayText, open]);
679
+ TimePickerPanel.useCursorRestore([{ inputRef, cursorRef: cursorPosRef }]);
680
+ React.useEffect(() => {
681
+ if (!open || type !== "date") return;
682
+ const dateVal = currentValue;
683
+ if (dateVal instanceof Date) setCalendarMonth(dateVal);
684
+ }, [open]);
477
685
  const hasField = !!(label || error || helpText);
478
686
  const showFooter = showTime || needConfirm || !!onTimezoneChange;
479
687
  const todayDate = toBrowserLocalDate(/* @__PURE__ */ new Date());
688
+ const handleGridPickerSelect = (date) => {
689
+ commitValue(date);
690
+ setOpen(false);
691
+ };
692
+ const handleSingleDateSelectWithPicker = (selected) => {
693
+ if (!selected) return handleDateSelect(void 0);
694
+ handleDateSelect(
695
+ picker === "week" ? startOfWeek(selected, weekStartsOn) : selected
696
+ );
697
+ };
698
+ const weekHighlightProps = (() => {
699
+ if (picker !== "week" || type !== "date" || !currentValue) return {};
700
+ const weekStart = startOfWeek(currentValue, weekStartsOn);
701
+ const weekDays = weekDaysFrom(weekStart);
702
+ return {
703
+ modifiers: {
704
+ weekStart: [weekDays[0]],
705
+ weekMiddle: weekDays.slice(1, 6),
706
+ weekEnd: [weekDays[6]]
707
+ },
708
+ modifiersClassNames: {
709
+ weekStart: "!bg-primary !text-primary-foreground !rounded-e-none hover:!bg-primary",
710
+ weekMiddle: "!bg-primary !text-primary-foreground !rounded-none hover:!bg-primary",
711
+ weekEnd: "!bg-primary !text-primary-foreground !rounded-s-none hover:!bg-primary"
712
+ }
713
+ };
714
+ })();
715
+ const weekRowHoverClassName = picker === "week" ? "[&_button]:!transition-none [&:hover_button]:!bg-primary [&:hover_button]:!text-primary-foreground [&:hover_button]:!rounded-none [&:hover_button]:cursor-pointer" : void 0;
480
716
  const ariaDescribedBy = [error ? errorId : null, helpText ? helpTextId : null].filter(Boolean).join(" ") || void 0;
481
- const calendarDisabled = React.useCallback(
482
- (date) => {
483
- if (minDate && date < new Date(minDate.setHours(0, 0, 0, 0)))
484
- return true;
485
- if (maxDate && date > new Date(maxDate.setHours(23, 59, 59, 999)))
486
- return true;
487
- return false;
488
- },
489
- [minDate, maxDate]
490
- );
491
- const calendarSelected = React.useCallback(() => {
717
+ const calendarSelected = () => {
492
718
  if (showTime || needConfirm) return pendingDate ?? void 0;
493
719
  return currentValue ?? void 0;
494
- }, [showTime, needConfirm, pendingDate, currentValue]);
495
- const triggerContent = /* @__PURE__ */ jsxRuntime.jsxs(primitives_Popover.Popover, { open, children: [
496
- /* @__PURE__ */ jsxRuntime.jsx(primitives_Popover.PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
720
+ };
721
+ const triggerContent = /* @__PURE__ */ jsxRuntime.jsxs(primitives_Popover.Popover, { open, onOpenChange: trigger ? setOpen : void 0, children: [
722
+ trigger ? /* @__PURE__ */ jsxRuntime.jsx(primitives_Popover.PopoverTrigger, { asChild: true, children: trigger }) : /* @__PURE__ */ jsxRuntime.jsx(primitives_Popover.PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
497
723
  "div",
498
724
  {
499
725
  ref: containerRef,
@@ -509,7 +735,10 @@ const DatePicker = React.forwardRef(
509
735
  /* @__PURE__ */ jsxRuntime.jsx(
510
736
  Calendar,
511
737
  {
512
- className: utils$1.cn("shrink-0 text-muted-foreground", sizeConfig.icon)
738
+ className: utils$1.cn(
739
+ "shrink-0 text-muted-foreground",
740
+ sizeConfig.icon
741
+ )
513
742
  }
514
743
  ),
515
744
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -525,7 +754,10 @@ const DatePicker = React.forwardRef(
525
754
  value: inputText,
526
755
  onChange: handleInputChange,
527
756
  onKeyDown: handleInputKeyDown,
528
- onFocus: handleInputFocus,
757
+ onFocus: () => {
758
+ if (!open) openPopover();
759
+ },
760
+ onBlur: handleInputBlur,
529
761
  className: utils$1.cn(
530
762
  "min-w-0 flex-1 bg-transparent outline-none placeholder:text-muted-foreground",
531
763
  "disabled:cursor-not-allowed",
@@ -557,14 +789,35 @@ const DatePicker = React.forwardRef(
557
789
  {
558
790
  id: popoverContentId,
559
791
  className: "w-auto p-0",
560
- align: "start",
792
+ align,
793
+ side,
561
794
  onOpenAutoFocus: (e) => e.preventDefault(),
562
795
  onCloseAutoFocus: (e) => e.preventDefault(),
563
796
  onPointerDownOutside: (e) => e.preventDefault(),
564
797
  onInteractOutside: (e) => e.preventDefault(),
565
798
  children: [
566
799
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils$1.cn("flex", showTime && "flex-row"), children: [
567
- type === "date" ? /* @__PURE__ */ jsxRuntime.jsx(
800
+ type === "date" && picker === "month" ? /* @__PURE__ */ jsxRuntime.jsx(
801
+ MonthPicker,
802
+ {
803
+ value: currentValue ?? null,
804
+ displayedYear: calendarMonth.getFullYear(),
805
+ onYearChange: (year) => setCalendarMonth(
806
+ new Date(year, calendarMonth.getMonth(), 1)
807
+ ),
808
+ onSelect: handleGridPickerSelect,
809
+ isDisabled: (date) => isMonthDisabled(date, minDate, maxDate)
810
+ }
811
+ ) : type === "date" && picker === "year" ? /* @__PURE__ */ jsxRuntime.jsx(
812
+ YearPicker,
813
+ {
814
+ value: currentValue ?? null,
815
+ displayedYear: calendarMonth.getFullYear(),
816
+ onYearChange: (year) => setCalendarMonth(new Date(year, 0, 1)),
817
+ onSelect: handleGridPickerSelect,
818
+ isDisabled: (date) => isYearDisabled(date, minDate, maxDate)
819
+ }
820
+ ) : type === "date" ? /* @__PURE__ */ jsxRuntime.jsx(
568
821
  primitives_Calendar.Calendar,
569
822
  {
570
823
  mode: "single",
@@ -573,8 +826,11 @@ const DatePicker = React.forwardRef(
573
826
  month: calendarMonth,
574
827
  onMonthChange: setCalendarMonth,
575
828
  selected: calendarSelected(),
576
- onSelect: handleDateSelect,
829
+ onSelect: handleSingleDateSelectWithPicker,
577
830
  disabled: calendarDisabled,
831
+ weekStartsOn,
832
+ classNames: weekRowHoverClassName ? { week: utils$1.cn("mt-2 flex w-full", weekRowHoverClassName) } : void 0,
833
+ ...weekHighlightProps,
578
834
  ...minDate && { fromDate: minDate },
579
835
  ...maxDate && { toDate: maxDate }
580
836
  }
@@ -585,6 +841,7 @@ const DatePicker = React.forwardRef(
585
841
  numberOfMonths: 2,
586
842
  captionLayout: "dropdown",
587
843
  today: todayDate,
844
+ weekStartsOn,
588
845
  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",
589
846
  month: calendarMonth,
590
847
  onMonthChange: setCalendarMonth,
@@ -602,7 +859,7 @@ const DatePicker = React.forwardRef(
602
859
  TimePickerPanel.TimePickerPanel,
603
860
  {
604
861
  value: pendingTime,
605
- onChange: handleTimeChange,
862
+ onChange: setPendingTime,
606
863
  format: timePickerFormat,
607
864
  showSeconds,
608
865
  disabled
@@ -658,4 +915,4 @@ const DatePicker = React.forwardRef(
658
915
  DatePicker.displayName = "DatePicker";
659
916
 
660
917
  exports.DatePicker = DatePicker;
661
- //# sourceMappingURL=DatePicker-JhQ7D2bu.js.map
918
+ //# sourceMappingURL=DatePicker-BZd4rM2R.js.map