@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
@@ -1,14 +1,16 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { forwardRef, useId, useRef, useState, useMemo, useCallback, useEffect, useLayoutEffect } from 'react';
2
+ import { forwardRef, useId, useRef, useState, useMemo, useCallback, useEffect } from 'react';
3
3
  import { Calendar as Calendar$1 } from './primitives/Calendar.js';
4
- import { Popover, PopoverAnchor, PopoverContent } from './primitives/Popover.js';
4
+ import { Popover, PopoverTrigger, PopoverAnchor, PopoverContent } from './primitives/Popover.js';
5
5
  import { Field, FieldLabel, FieldContent, FieldError, FieldDescription } from './primitives/Field.js';
6
6
  import { c as cn } from './utils-BJnb9o5c.js';
7
+ import { u as useControlledOpen, l as isFixedWidthFormat, m as buildRangeMaskTemplate, d as buildMaskTemplate, b as useOutsideClickClose, e as useCursorRestore, T as TimePickerPanel, j as applyMask, n as dateToTimeValue } from './TimePickerPanel--KDX5QwS.js';
7
8
  import pureDayjs from 'dayjs';
8
9
  import customParseFormat from 'dayjs/plugin/customParseFormat';
9
10
  import { dayjs } from '@bigbinary/neeto-commons-frontend/utils';
10
- import { i as isFixedWidthFormat, c as buildRangeMaskTemplate, b as buildMaskTemplate, d as dateToTimeValue, T as TimePickerPanel, a as applyMask } from './TimePickerPanel-zWmOy3Eo.js';
11
11
  import { Button } from './primitives/Button.js';
12
+ import { C as ChevronLeft } from './chevron-left-BDoT8E2-.js';
13
+ import { C as ChevronRight } from './chevron-right-DQnrO-ek.js';
12
14
  import { c as createLucideIcon } from './createLucideIcon-C8ycilSN.js';
13
15
  import { X } from './x-_o2T3n6D.js';
14
16
 
@@ -30,6 +32,11 @@ const Calendar = createLucideIcon("calendar", __iconNode);
30
32
 
31
33
  const DEFAULT_DATE_FORMAT = "dd/MM/yyyy";
32
34
  const DEFAULT_TIME_FORMAT = "HH:mm:ss";
35
+ const INITIAL_TIME_VALUE = {
36
+ hours: 0,
37
+ minutes: 0,
38
+ seconds: 0
39
+ };
33
40
  const SIZE_CONFIG = {
34
41
  small: {
35
42
  trigger: "h-8 md:h-7",
@@ -123,6 +130,10 @@ const getDisplayFormat = (dateFormat, timeFormat, showTime) => {
123
130
  const fmt = showTime ? `${dateFormat} ${timeFormat}` : dateFormat;
124
131
  return normalizeToDateFnsFormat(fmt);
125
132
  };
133
+ const getDatePlaceholder = (dateFormat, timeFormat, showTime, type) => {
134
+ const single = showTime ? `${dateFormat.toUpperCase()} ${timeFormat}` : dateFormat.toUpperCase();
135
+ return type === "range" ? `${single} - ${single}` : single;
136
+ };
126
137
  const isDatePartComplete = (part, maskEnabled, singleDateLen) => maskEnabled ? part.length >= singleDateLen : part.length > 0;
127
138
  const parseRangeText = (text, displayFormat, maskEnabled, singleDateLen) => {
128
139
  const parts = text.split(" - ");
@@ -133,6 +144,43 @@ const parseRangeText = (text, displayFormat, maskEnabled, singleDateLen) => {
133
144
  const to = parseDate(parts[1], displayFormat);
134
145
  return from && to ? [from, to] : null;
135
146
  };
147
+ const startOfWeek = (date, weekStartsOn = 0) => {
148
+ const d = new Date(date);
149
+ const diff = (d.getDay() - weekStartsOn + 7) % 7;
150
+ d.setDate(d.getDate() - diff);
151
+ d.setHours(0, 0, 0, 0);
152
+ return d;
153
+ };
154
+ const weekDaysFrom = (start) => Array.from({ length: 7 }, (_, i) => {
155
+ const d = new Date(start);
156
+ d.setDate(start.getDate() + i);
157
+ return d;
158
+ });
159
+ const isMonthDisabled = (monthDate, minDate, maxDate) => {
160
+ const monthStart = new Date(monthDate.getFullYear(), monthDate.getMonth(), 1);
161
+ const monthEnd = new Date(
162
+ monthDate.getFullYear(),
163
+ monthDate.getMonth() + 1,
164
+ 0,
165
+ 23,
166
+ 59,
167
+ 59,
168
+ 999
169
+ );
170
+ if (maxDate && monthStart > maxDate) return true;
171
+ if (minDate && monthEnd < minDate) return true;
172
+ return false;
173
+ };
174
+ const isYearDisabled = (yearDate, minDate, maxDate) => {
175
+ const yearStart = new Date(yearDate.getFullYear(), 0, 1);
176
+ const yearEnd = new Date(yearDate.getFullYear(), 11, 31, 23, 59, 59, 999);
177
+ if (maxDate && yearStart > maxDate) return true;
178
+ if (minDate && yearEnd < minDate) return true;
179
+ return false;
180
+ };
181
+ const buildMonth = (year, month) => new Date(year, month, 1);
182
+ const isSameMonth = (a, b) => !!a && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
183
+ const decadeStartFor = (year) => Math.floor(year / 10) * 10;
136
184
 
137
185
  const TIMEZONE_OPTIONS = [
138
186
  { label: "Local", value: "" },
@@ -195,11 +243,136 @@ const DatePickerFooter = ({
195
243
  };
196
244
  DatePickerFooter.displayName = "DatePickerFooter";
197
245
 
246
+ const MonthPicker = ({
247
+ value,
248
+ displayedYear,
249
+ onYearChange,
250
+ onSelect,
251
+ isDisabled,
252
+ locale
253
+ }) => {
254
+ const months = Array.from(
255
+ { length: 12 },
256
+ (_, m) => buildMonth(displayedYear, m)
257
+ );
258
+ const monthName = (date) => date.toLocaleString(locale, { month: "short" });
259
+ return /* @__PURE__ */ jsxs("div", { className: "bg-background flex w-[16rem] flex-col gap-3 p-2", children: [
260
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
261
+ /* @__PURE__ */ jsx(
262
+ Button,
263
+ {
264
+ "aria-label": "Previous year",
265
+ size: "icon",
266
+ variant: "ghost",
267
+ onClick: () => onYearChange(displayedYear - 1),
268
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4 rtl:rotate-180" })
269
+ }
270
+ ),
271
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: displayedYear }),
272
+ /* @__PURE__ */ jsx(
273
+ Button,
274
+ {
275
+ "aria-label": "Next year",
276
+ size: "icon",
277
+ variant: "ghost",
278
+ onClick: () => onYearChange(displayedYear + 1),
279
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4 rtl:rotate-180" })
280
+ }
281
+ )
282
+ ] }),
283
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2", children: months.map((monthDate) => {
284
+ const disabled = isDisabled?.(monthDate) ?? false;
285
+ const selected = isSameMonth(value, monthDate);
286
+ return /* @__PURE__ */ jsx(
287
+ "button",
288
+ {
289
+ "aria-pressed": selected,
290
+ className: cn(
291
+ "rounded-md py-2 text-sm transition-colors",
292
+ "hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
293
+ selected && "bg-primary text-primary-foreground hover:bg-primary"
294
+ ),
295
+ disabled,
296
+ type: "button",
297
+ onClick: () => onSelect(monthDate),
298
+ children: monthName(monthDate)
299
+ },
300
+ monthDate.getMonth()
301
+ );
302
+ }) })
303
+ ] });
304
+ };
305
+
306
+ const YearPicker = ({
307
+ value,
308
+ displayedYear,
309
+ onYearChange,
310
+ onSelect,
311
+ isDisabled
312
+ }) => {
313
+ const decadeStart = decadeStartFor(displayedYear);
314
+ const years = Array.from({ length: 12 }, (_, i) => decadeStart - 1 + i);
315
+ const selectedYear = value?.getFullYear();
316
+ return /* @__PURE__ */ jsxs("div", { className: "bg-background flex w-[16rem] flex-col gap-3 p-2", children: [
317
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
318
+ /* @__PURE__ */ jsx(
319
+ Button,
320
+ {
321
+ "aria-label": "Previous decade",
322
+ size: "icon",
323
+ variant: "ghost",
324
+ onClick: () => onYearChange(decadeStart - 10),
325
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4 rtl:rotate-180" })
326
+ }
327
+ ),
328
+ /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium", children: [
329
+ decadeStart,
330
+ "-",
331
+ decadeStart + 9
332
+ ] }),
333
+ /* @__PURE__ */ jsx(
334
+ Button,
335
+ {
336
+ "aria-label": "Next decade",
337
+ size: "icon",
338
+ variant: "ghost",
339
+ onClick: () => onYearChange(decadeStart + 10),
340
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4 rtl:rotate-180" })
341
+ }
342
+ )
343
+ ] }),
344
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2", children: years.map((year) => {
345
+ const yearDate = new Date(year, 0, 1);
346
+ const disabled = isDisabled?.(yearDate) ?? false;
347
+ const selected = selectedYear === year;
348
+ const outsideDecade = year < decadeStart || year > decadeStart + 9;
349
+ return /* @__PURE__ */ jsx(
350
+ "button",
351
+ {
352
+ "aria-pressed": selected,
353
+ className: cn(
354
+ "rounded-md py-2 text-sm transition-colors",
355
+ "hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50",
356
+ outsideDecade && "text-muted-foreground",
357
+ selected && "bg-primary text-primary-foreground hover:bg-primary"
358
+ ),
359
+ disabled,
360
+ type: "button",
361
+ onClick: () => onSelect(yearDate),
362
+ children: year
363
+ },
364
+ year
365
+ );
366
+ }) })
367
+ ] });
368
+ };
369
+
198
370
  const DatePicker = forwardRef(
199
371
  ({
200
372
  value,
201
373
  defaultValue,
202
374
  onChange,
375
+ onBlur,
203
376
  type = "date",
204
377
  dateFormat = DEFAULT_DATE_FORMAT,
205
378
  timeFormat = DEFAULT_TIME_FORMAT,
@@ -208,6 +381,7 @@ const DatePicker = forwardRef(
208
381
  showSeconds = false,
209
382
  minDate,
210
383
  maxDate,
384
+ disabledDate,
211
385
  placeholder,
212
386
  label,
213
387
  error,
@@ -220,9 +394,15 @@ const DatePicker = forwardRef(
220
394
  onTimezoneChange,
221
395
  onOk,
222
396
  needConfirm = false,
397
+ open: openProp,
223
398
  onOpenChange,
224
399
  className,
225
- labelProps
400
+ labelProps,
401
+ picker = "date",
402
+ side = "bottom",
403
+ align = "start",
404
+ trigger,
405
+ weekStartsOn = 0
226
406
  }, ref) => {
227
407
  const generatedId = useId();
228
408
  const errorId = `error_${generatedId}`;
@@ -233,9 +413,10 @@ const DatePicker = forwardRef(
233
413
  const popoverContentId = useRef(
234
414
  `datepicker-popover-${generatedId}`
235
415
  ).current;
236
- const [open, setOpen] = useState(false);
237
- const [internalValue, setInternalValue] = useState(coerceDateValue(defaultValue, type) ?? null);
238
- const currentValue = value !== void 0 ? coerceDateValue(value, type) : internalValue;
416
+ const { open, setOpen } = useControlledOpen(openProp, onOpenChange);
417
+ const [internalValue, setInternalValue] = useState(
418
+ coerceDateValue(defaultValue, type) ?? null
419
+ );
239
420
  const [calendarMonth, setCalendarMonth] = useState(
240
421
  (type === "date" ? value ?? defaultValue : null) ?? /* @__PURE__ */ new Date()
241
422
  );
@@ -243,65 +424,47 @@ const DatePicker = forwardRef(
243
424
  "from"
244
425
  );
245
426
  const [pendingDate, setPendingDate] = useState(null);
246
- const [pendingTime, setPendingTime] = useState({
247
- hours: 0,
248
- minutes: 0,
249
- seconds: 0
250
- });
427
+ const [pendingTime, setPendingTime] = useState(INITIAL_TIME_VALUE);
428
+ const currentValue = value !== void 0 ? coerceDateValue(value, type) : internalValue;
251
429
  const displayFormat = getDisplayFormat(dateFormat, timeFormat, showTime);
252
430
  const sizeConfig = SIZE_CONFIG[size];
253
431
  const maskEnabled = isFixedWidthFormat(displayFormat);
432
+ const defaultPlaceholder = getDatePlaceholder(
433
+ dateFormat,
434
+ timeFormat,
435
+ showTime,
436
+ type
437
+ );
254
438
  const maskTemplate = useMemo(
255
439
  () => maskEnabled ? type === "range" ? buildRangeMaskTemplate(displayFormat) : buildMaskTemplate(displayFormat) : null,
256
440
  [displayFormat, type, maskEnabled]
257
441
  );
258
- const singlePlaceholder = showTime ? `${dateFormat.toUpperCase()} ${timeFormat}` : dateFormat.toUpperCase();
259
- const defaultPlaceholder = type === "range" ? `${singlePlaceholder} - ${singlePlaceholder}` : singlePlaceholder;
442
+ const singleDateLen = useMemo(
443
+ () => buildMaskTemplate(displayFormat).pattern.length,
444
+ [displayFormat]
445
+ );
446
+ const calendarDisabled = useCallback(
447
+ (date) => {
448
+ if (disabledDate?.(date)) return true;
449
+ if (minDate && date < new Date(new Date(minDate).setHours(0, 0, 0, 0)))
450
+ return true;
451
+ if (maxDate && date > new Date(new Date(maxDate).setHours(23, 59, 59, 999)))
452
+ return true;
453
+ return false;
454
+ },
455
+ [minDate, maxDate, disabledDate]
456
+ );
260
457
  const getDisplayText = useCallback(() => {
261
- if (type === "range") {
262
- const rangeValue = currentValue;
263
- if (!rangeValue || !rangeValue[0] && !rangeValue[1]) return "";
264
- const from = rangeValue[0] ? formatDate(rangeValue[0], displayFormat) : "";
265
- const to = rangeValue[1] ? formatDate(rangeValue[1], displayFormat) : "";
266
- return `${from} - ${to}`;
458
+ if (type !== "range") {
459
+ return formatDate(currentValue, displayFormat);
267
460
  }
268
- return formatDate(currentValue, displayFormat);
461
+ const rangeValue = currentValue;
462
+ if (!rangeValue || !rangeValue[0] && !rangeValue[1]) return "";
463
+ const from = rangeValue[0] ? formatDate(rangeValue[0], displayFormat) : "";
464
+ const to = rangeValue[1] ? formatDate(rangeValue[1], displayFormat) : "";
465
+ return `${from} - ${to}`;
269
466
  }, [currentValue, displayFormat, type]);
270
467
  const [inputText, setInputText] = useState(() => getDisplayText());
271
- useEffect(() => {
272
- if (!open) setInputText(getDisplayText());
273
- }, [getDisplayText, open]);
274
- const closePopover = useCallback(() => {
275
- setOpen(false);
276
- onOpenChange?.(false);
277
- setInputText(getDisplayText());
278
- }, [onOpenChange, getDisplayText]);
279
- const openPopover = useCallback(() => {
280
- setOpen(true);
281
- onOpenChange?.(true);
282
- setRangeSelectionStep("from");
283
- const dateVal = type === "date" ? currentValue : null;
284
- if (dateVal) {
285
- setCalendarMonth(dateVal);
286
- setPendingDate(dateVal);
287
- setPendingTime(dateToTimeValue(dateVal));
288
- } else {
289
- setPendingDate(null);
290
- setPendingTime({ hours: 0, minutes: 0, seconds: 0 });
291
- }
292
- }, [type, currentValue, onOpenChange]);
293
- useEffect(() => {
294
- if (!open) return;
295
- const handlePointerDown = (e) => {
296
- const target = e.target;
297
- if (containerRef.current?.contains(target)) return;
298
- const popoverEl = document.getElementById(popoverContentId);
299
- if (popoverEl?.contains(target)) return;
300
- closePopover();
301
- };
302
- document.addEventListener("pointerdown", handlePointerDown);
303
- return () => document.removeEventListener("pointerdown", handlePointerDown);
304
- }, [open, closePopover]);
305
468
  const commitValue = (date) => {
306
469
  if (date === null) {
307
470
  setInternalValue(null);
@@ -329,16 +492,76 @@ const DatePicker = forwardRef(
329
492
  onChange?.(toDayjs(converted), formatted);
330
493
  setInputText(formatted);
331
494
  };
495
+ const parseAndApplyRange = (text) => {
496
+ const parts = text.split(" - ");
497
+ if (!isDatePartComplete(parts[0] ?? "", maskEnabled, singleDateLen))
498
+ return;
499
+ const from = parseDate(parts[0], displayFormat);
500
+ if (from) setCalendarMonth(from);
501
+ const range = parseRangeText(
502
+ text,
503
+ displayFormat,
504
+ maskEnabled,
505
+ singleDateLen
506
+ );
507
+ if (range) commitValue(range);
508
+ };
509
+ const commitPendingOnClose = useCallback(() => {
510
+ const hasOkButton = showTime || needConfirm;
511
+ if (!hasOkButton || !pendingDate) {
512
+ setInputText(getDisplayText());
513
+ return;
514
+ }
515
+ const finalDate = showTime ? applyTimeToDate(pendingDate, pendingTime) : pendingDate;
516
+ const currentSingle = type === "date" ? currentValue : null;
517
+ const isSame = currentSingle && finalDate.getTime() === currentSingle.getTime();
518
+ if (isSame) {
519
+ setInputText(getDisplayText());
520
+ return;
521
+ }
522
+ commitValue(finalDate);
523
+ }, [
524
+ showTime,
525
+ needConfirm,
526
+ pendingDate,
527
+ pendingTime,
528
+ type,
529
+ currentValue,
530
+ getDisplayText,
531
+ onChange
532
+ ]);
533
+ const closePopover = useCallback(() => {
534
+ commitPendingOnClose();
535
+ setOpen(false);
536
+ }, [commitPendingOnClose, setOpen]);
537
+ useOutsideClickClose({
538
+ enabled: open,
539
+ containerRef,
540
+ popoverElementId: popoverContentId,
541
+ onClose: closePopover
542
+ });
543
+ const openPopover = () => {
544
+ setOpen(true);
545
+ setRangeSelectionStep("from");
546
+ const dateVal = type === "date" ? currentValue : null;
547
+ if (!dateVal) {
548
+ setPendingDate(null);
549
+ setPendingTime(INITIAL_TIME_VALUE);
550
+ return;
551
+ }
552
+ setCalendarMonth(dateVal);
553
+ setPendingDate(dateVal);
554
+ setPendingTime(dateToTimeValue(dateVal));
555
+ };
332
556
  const handleDateSelect = (selected) => {
333
557
  if (!selected) return;
334
558
  setCalendarMonth(selected);
335
559
  if (showTime || needConfirm) {
336
560
  setPendingDate(selected);
337
- } else {
338
- commitValue(selected);
339
- setOpen(false);
340
- onOpenChange?.(false);
561
+ return;
341
562
  }
563
+ commitValue(selected);
564
+ setOpen(false);
342
565
  };
343
566
  const handleRangeSelect = (range) => {
344
567
  if (!range) return;
@@ -347,34 +570,29 @@ const DatePicker = forwardRef(
347
570
  if (rangeSelectionStep === "from") {
348
571
  setInternalValue([from, null]);
349
572
  setRangeSelectionStep("to");
350
- } else {
351
- if (from && to) {
352
- if (showTime || needConfirm) {
353
- setPendingDate(from);
354
- } else {
355
- commitValue([from, to]);
356
- setOpen(false);
357
- onOpenChange?.(false);
358
- }
359
- } else if (from) {
360
- setInternalValue([from, null]);
573
+ return;
574
+ }
575
+ setRangeSelectionStep("from");
576
+ if (from && to) {
577
+ if (showTime || needConfirm) {
578
+ setPendingDate(from);
579
+ return;
361
580
  }
362
- setRangeSelectionStep("from");
581
+ commitValue([from, to]);
582
+ setOpen(false);
583
+ return;
363
584
  }
364
- };
365
- const handleTimeChange = (time) => {
366
- setPendingTime(time);
585
+ if (from) setInternalValue([from, null]);
367
586
  };
368
587
  const handleNow = () => {
369
588
  const now = toBrowserLocalDate(/* @__PURE__ */ new Date());
370
589
  if (showTime || needConfirm) {
371
590
  setPendingDate(now);
372
591
  setPendingTime(dateToTimeValue(now));
373
- } else {
374
- commitValue(now);
375
- setOpen(false);
376
- onOpenChange?.(false);
592
+ return;
377
593
  }
594
+ commitValue(now);
595
+ setOpen(false);
378
596
  };
379
597
  const handleOk = () => {
380
598
  if (pendingDate) {
@@ -383,40 +601,12 @@ const DatePicker = forwardRef(
383
601
  onOk?.(finalDate);
384
602
  }
385
603
  setOpen(false);
386
- onOpenChange?.(false);
387
604
  };
388
605
  const handleClear = (e) => {
389
606
  e.stopPropagation();
390
607
  e.preventDefault();
391
608
  commitValue(null);
392
609
  };
393
- useLayoutEffect(() => {
394
- if (cursorPosRef.current !== null && inputRef.current && document.activeElement === inputRef.current) {
395
- inputRef.current.setSelectionRange(
396
- cursorPosRef.current,
397
- cursorPosRef.current
398
- );
399
- cursorPosRef.current = null;
400
- }
401
- });
402
- const singleDateLen = useMemo(
403
- () => buildMaskTemplate(displayFormat).pattern.length,
404
- [displayFormat]
405
- );
406
- const parseAndApplyRange = (text) => {
407
- const parts = text.split(" - ");
408
- if (!isDatePartComplete(parts[0] ?? "", maskEnabled, singleDateLen))
409
- return;
410
- const from = parseDate(parts[0], displayFormat);
411
- if (from) setCalendarMonth(from);
412
- const range = parseRangeText(
413
- text,
414
- displayFormat,
415
- maskEnabled,
416
- singleDateLen
417
- );
418
- if (range) commitValue(range);
419
- };
420
610
  const handleInputChange = (e) => {
421
611
  let text = e.target.value;
422
612
  if (maskTemplate) {
@@ -442,12 +632,18 @@ const DatePicker = forwardRef(
442
632
  if (showTime || needConfirm) {
443
633
  setPendingDate(parsed);
444
634
  setPendingTime(dateToTimeValue(parsed));
445
- } else {
446
- commitValue(parsed);
635
+ return;
447
636
  }
637
+ commitValue(parsed);
448
638
  };
449
639
  const handleInputKeyDown = (e) => {
450
- if (e.key === "Escape") return closePopover();
640
+ if (e.key === "Escape") {
641
+ setPendingDate(null);
642
+ setPendingTime(INITIAL_TIME_VALUE);
643
+ setInputText(getDisplayText());
644
+ setOpen(false);
645
+ return;
646
+ }
451
647
  if (e.key !== "Enter") return;
452
648
  if (type === "range") {
453
649
  const range = parseRangeText(
@@ -462,31 +658,61 @@ const DatePicker = forwardRef(
462
658
  if (parsed) commitValue(parsed);
463
659
  }
464
660
  setOpen(false);
465
- onOpenChange?.(false);
466
661
  };
467
- const handleInputFocus = () => {
468
- if (!open) openPopover();
662
+ const handleInputBlur = (e) => {
663
+ const next = e.relatedTarget;
664
+ if (next && containerRef.current?.contains(next)) return;
665
+ const popoverEl = document.getElementById(popoverContentId);
666
+ if (next && popoverEl?.contains(next)) return;
667
+ onBlur?.(e);
469
668
  };
669
+ useEffect(() => {
670
+ if (!open) setInputText(getDisplayText());
671
+ }, [getDisplayText, open]);
672
+ useCursorRestore([{ inputRef, cursorRef: cursorPosRef }]);
673
+ useEffect(() => {
674
+ if (!open || type !== "date") return;
675
+ const dateVal = currentValue;
676
+ if (dateVal instanceof Date) setCalendarMonth(dateVal);
677
+ }, [open]);
470
678
  const hasField = !!(label || error || helpText);
471
679
  const showFooter = showTime || needConfirm || !!onTimezoneChange;
472
680
  const todayDate = toBrowserLocalDate(/* @__PURE__ */ new Date());
681
+ const handleGridPickerSelect = (date) => {
682
+ commitValue(date);
683
+ setOpen(false);
684
+ };
685
+ const handleSingleDateSelectWithPicker = (selected) => {
686
+ if (!selected) return handleDateSelect(void 0);
687
+ handleDateSelect(
688
+ picker === "week" ? startOfWeek(selected, weekStartsOn) : selected
689
+ );
690
+ };
691
+ const weekHighlightProps = (() => {
692
+ if (picker !== "week" || type !== "date" || !currentValue) return {};
693
+ const weekStart = startOfWeek(currentValue, weekStartsOn);
694
+ const weekDays = weekDaysFrom(weekStart);
695
+ return {
696
+ modifiers: {
697
+ weekStart: [weekDays[0]],
698
+ weekMiddle: weekDays.slice(1, 6),
699
+ weekEnd: [weekDays[6]]
700
+ },
701
+ modifiersClassNames: {
702
+ weekStart: "!bg-primary !text-primary-foreground !rounded-e-none hover:!bg-primary",
703
+ weekMiddle: "!bg-primary !text-primary-foreground !rounded-none hover:!bg-primary",
704
+ weekEnd: "!bg-primary !text-primary-foreground !rounded-s-none hover:!bg-primary"
705
+ }
706
+ };
707
+ })();
708
+ 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;
473
709
  const ariaDescribedBy = [error ? errorId : null, helpText ? helpTextId : null].filter(Boolean).join(" ") || void 0;
474
- const calendarDisabled = useCallback(
475
- (date) => {
476
- if (minDate && date < new Date(minDate.setHours(0, 0, 0, 0)))
477
- return true;
478
- if (maxDate && date > new Date(maxDate.setHours(23, 59, 59, 999)))
479
- return true;
480
- return false;
481
- },
482
- [minDate, maxDate]
483
- );
484
- const calendarSelected = useCallback(() => {
710
+ const calendarSelected = () => {
485
711
  if (showTime || needConfirm) return pendingDate ?? void 0;
486
712
  return currentValue ?? void 0;
487
- }, [showTime, needConfirm, pendingDate, currentValue]);
488
- const triggerContent = /* @__PURE__ */ jsxs(Popover, { open, children: [
489
- /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(
713
+ };
714
+ const triggerContent = /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: trigger ? setOpen : void 0, children: [
715
+ trigger ? /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: trigger }) : /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(
490
716
  "div",
491
717
  {
492
718
  ref: containerRef,
@@ -502,7 +728,10 @@ const DatePicker = forwardRef(
502
728
  /* @__PURE__ */ jsx(
503
729
  Calendar,
504
730
  {
505
- className: cn("shrink-0 text-muted-foreground", sizeConfig.icon)
731
+ className: cn(
732
+ "shrink-0 text-muted-foreground",
733
+ sizeConfig.icon
734
+ )
506
735
  }
507
736
  ),
508
737
  /* @__PURE__ */ jsx(
@@ -518,7 +747,10 @@ const DatePicker = forwardRef(
518
747
  value: inputText,
519
748
  onChange: handleInputChange,
520
749
  onKeyDown: handleInputKeyDown,
521
- onFocus: handleInputFocus,
750
+ onFocus: () => {
751
+ if (!open) openPopover();
752
+ },
753
+ onBlur: handleInputBlur,
522
754
  className: cn(
523
755
  "min-w-0 flex-1 bg-transparent outline-none placeholder:text-muted-foreground",
524
756
  "disabled:cursor-not-allowed",
@@ -550,14 +782,35 @@ const DatePicker = forwardRef(
550
782
  {
551
783
  id: popoverContentId,
552
784
  className: "w-auto p-0",
553
- align: "start",
785
+ align,
786
+ side,
554
787
  onOpenAutoFocus: (e) => e.preventDefault(),
555
788
  onCloseAutoFocus: (e) => e.preventDefault(),
556
789
  onPointerDownOutside: (e) => e.preventDefault(),
557
790
  onInteractOutside: (e) => e.preventDefault(),
558
791
  children: [
559
792
  /* @__PURE__ */ jsxs("div", { className: cn("flex", showTime && "flex-row"), children: [
560
- type === "date" ? /* @__PURE__ */ jsx(
793
+ type === "date" && picker === "month" ? /* @__PURE__ */ jsx(
794
+ MonthPicker,
795
+ {
796
+ value: currentValue ?? null,
797
+ displayedYear: calendarMonth.getFullYear(),
798
+ onYearChange: (year) => setCalendarMonth(
799
+ new Date(year, calendarMonth.getMonth(), 1)
800
+ ),
801
+ onSelect: handleGridPickerSelect,
802
+ isDisabled: (date) => isMonthDisabled(date, minDate, maxDate)
803
+ }
804
+ ) : type === "date" && picker === "year" ? /* @__PURE__ */ jsx(
805
+ YearPicker,
806
+ {
807
+ value: currentValue ?? null,
808
+ displayedYear: calendarMonth.getFullYear(),
809
+ onYearChange: (year) => setCalendarMonth(new Date(year, 0, 1)),
810
+ onSelect: handleGridPickerSelect,
811
+ isDisabled: (date) => isYearDisabled(date, minDate, maxDate)
812
+ }
813
+ ) : type === "date" ? /* @__PURE__ */ jsx(
561
814
  Calendar$1,
562
815
  {
563
816
  mode: "single",
@@ -566,8 +819,11 @@ const DatePicker = forwardRef(
566
819
  month: calendarMonth,
567
820
  onMonthChange: setCalendarMonth,
568
821
  selected: calendarSelected(),
569
- onSelect: handleDateSelect,
822
+ onSelect: handleSingleDateSelectWithPicker,
570
823
  disabled: calendarDisabled,
824
+ weekStartsOn,
825
+ classNames: weekRowHoverClassName ? { week: cn("mt-2 flex w-full", weekRowHoverClassName) } : void 0,
826
+ ...weekHighlightProps,
571
827
  ...minDate && { fromDate: minDate },
572
828
  ...maxDate && { toDate: maxDate }
573
829
  }
@@ -578,6 +834,7 @@ const DatePicker = forwardRef(
578
834
  numberOfMonths: 2,
579
835
  captionLayout: "dropdown",
580
836
  today: todayDate,
837
+ weekStartsOn,
581
838
  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",
582
839
  month: calendarMonth,
583
840
  onMonthChange: setCalendarMonth,
@@ -595,7 +852,7 @@ const DatePicker = forwardRef(
595
852
  TimePickerPanel,
596
853
  {
597
854
  value: pendingTime,
598
- onChange: handleTimeChange,
855
+ onChange: setPendingTime,
599
856
  format: timePickerFormat,
600
857
  showSeconds,
601
858
  disabled
@@ -651,4 +908,4 @@ const DatePicker = forwardRef(
651
908
  DatePicker.displayName = "DatePicker";
652
909
 
653
910
  export { DatePicker as D };
654
- //# sourceMappingURL=DatePicker-D-0HMiNG.js.map
911
+ //# sourceMappingURL=DatePicker-kulToqfM.js.map