@kalyx/react 0.4.0 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20,10 +20,23 @@ function useDatePickerContext(componentName) {
20
20
  }
21
21
  return context;
22
22
  }
23
+ function useChangeEffect(value, callback) {
24
+ const callbackRef = useRef(callback);
25
+ callbackRef.current = callback;
26
+ const prevRef = useRef(value);
27
+ useEffect(() => {
28
+ if (prevRef.current !== value) {
29
+ prevRef.current = value;
30
+ callbackRef.current?.(value);
31
+ }
32
+ }, [value]);
33
+ }
23
34
  function DatePickerRoot({
24
35
  value: controlledValue,
25
36
  defaultValue,
26
37
  onChange,
38
+ onOpenChange,
39
+ onCalendarNavigate,
27
40
  disabled = false,
28
41
  readOnly = false,
29
42
  weekStartsOn = 0,
@@ -48,6 +61,9 @@ function DatePickerRoot({
48
61
  const [focusedDate, setFocusedDate] = useState(
49
62
  currentValue ?? adapter.today(displayTimezone)
50
63
  );
64
+ useChangeEffect(isOpen, onOpenChange);
65
+ const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
66
+ useChangeEffect(viewMonthStart, onCalendarNavigate);
51
67
  const mergedLabels = useMemo(
52
68
  () => ({ ...DEFAULT_DATEPICKER_LABELS, ...labelsProp }),
53
69
  [labelsProp]
@@ -285,10 +301,15 @@ var DatePickerTrigger = forwardRef(
285
301
  );
286
302
  }
287
303
  );
288
- function usePopover({ isOpen, close, referenceRef, placement = "bottom-start" }) {
304
+ function usePopover({
305
+ isOpen,
306
+ close,
307
+ referenceRef,
308
+ placement = "bottom-start"
309
+ }) {
289
310
  const floatingRef = useRef(null);
290
311
  const previousFocusRef = useRef(null);
291
- const { refs, floatingStyles } = useFloating({
312
+ const { refs, floatingStyles, isPositioned } = useFloating({
292
313
  open: isOpen,
293
314
  placement,
294
315
  middleware: [offset(4), flip(), shift({ padding: 8 })],
@@ -338,21 +359,28 @@ function usePopover({ isOpen, close, referenceRef, placement = "bottom-start" })
338
359
  document.addEventListener("keydown", handleKeyDown);
339
360
  return () => document.removeEventListener("keydown", handleKeyDown);
340
361
  }, [isOpen, close]);
341
- const setFloatingRef = (node) => {
342
- floatingRef.current = node;
343
- refs.setFloating(node);
344
- };
345
- return { floatingStyles, setFloatingRef };
362
+ const setFloatingRef = useCallback(
363
+ (node) => {
364
+ floatingRef.current = node;
365
+ refs.setFloating(node);
366
+ if (node && referenceRef.current) {
367
+ refs.setReference(referenceRef.current);
368
+ }
369
+ },
370
+ [refs, referenceRef]
371
+ );
372
+ return { floatingStyles, setFloatingRef, isPositioned };
346
373
  }
347
374
  function DatePickerPopover({ children, ...props }) {
348
375
  const ctx = useDatePickerContext("DatePicker.Popover");
349
376
  const calendarId = `${ctx.pickerId}-calendar`;
350
- const { floatingStyles, setFloatingRef } = usePopover({
377
+ const { floatingStyles, setFloatingRef, isPositioned } = usePopover({
351
378
  isOpen: ctx.isOpen,
352
379
  close: ctx.close,
353
380
  referenceRef: ctx.referenceRef
354
381
  });
355
382
  if (!ctx.isOpen) return null;
383
+ const { style: userStyle, ...rest } = props;
356
384
  return /* @__PURE__ */ jsx(
357
385
  "div",
358
386
  {
@@ -361,8 +389,12 @@ function DatePickerPopover({ children, ...props }) {
361
389
  role: "dialog",
362
390
  "aria-label": ctx.labels.popoverLabel,
363
391
  "aria-modal": "false",
364
- style: floatingStyles,
365
- ...props,
392
+ ...rest,
393
+ style: {
394
+ ...userStyle,
395
+ ...floatingStyles,
396
+ visibility: isPositioned ? void 0 : "hidden"
397
+ },
366
398
  children
367
399
  }
368
400
  );
@@ -385,7 +417,11 @@ var srOnly = {
385
417
  whiteSpace: "nowrap",
386
418
  border: 0
387
419
  };
388
- function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
420
+ function DatePickerCalendar({
421
+ classNames,
422
+ onTitleClick,
423
+ ...props
424
+ }) {
389
425
  const ctx = useDatePickerContext("DatePicker.Calendar");
390
426
  const gridRef = useRef(null);
391
427
  const [announcement, setAnnouncement] = useState("");
@@ -403,9 +439,7 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
403
439
  const title = formatMonthYear(year, month, locale);
404
440
  useEffect(() => {
405
441
  if (!ctx.isOpen || !gridRef.current) return;
406
- const focusedButton = gridRef.current.querySelector(
407
- '[data-focused="true"]'
408
- );
442
+ const focusedButton = gridRef.current.querySelector('[data-focused="true"]');
409
443
  focusedButton?.focus({ preventScroll: true });
410
444
  }, [focusedDate, ctx.isOpen]);
411
445
  const navigateMonth = useCallback(
@@ -628,15 +662,7 @@ function DatePickerMonthGrid({
628
662
  children: "<"
629
663
  }
630
664
  ),
631
- onTitleClick ? /* @__PURE__ */ jsx(
632
- "button",
633
- {
634
- type: "button",
635
- className: classNames?.title,
636
- onClick: onTitleClick,
637
- children: currentYear
638
- }
639
- ) : /* @__PURE__ */ jsx("span", { className: classNames?.title, children: currentYear }),
665
+ onTitleClick ? /* @__PURE__ */ jsx("button", { type: "button", className: classNames?.title, onClick: onTitleClick, children: currentYear }) : /* @__PURE__ */ jsx("span", { className: classNames?.title, children: currentYear }),
640
666
  /* @__PURE__ */ jsx(
641
667
  "button",
642
668
  {
@@ -681,11 +707,7 @@ function DatePickerMonthGrid({
681
707
  )
682
708
  ] });
683
709
  }
684
- function DatePickerYearGrid({
685
- classNames,
686
- onSelect,
687
- ...props
688
- }) {
710
+ function DatePickerYearGrid({ classNames, onSelect, ...props }) {
689
711
  const ctx = useDatePickerContext("DatePicker.YearGrid");
690
712
  const { adapter, viewMonth } = ctx;
691
713
  const currentYear = adapter.getYear(viewMonth);
@@ -777,6 +799,82 @@ function DatePickerYearGrid({
777
799
  )
778
800
  ] });
779
801
  }
802
+ function DatePickerPresets({ classNames, children, ...props }) {
803
+ const ctx = useDatePickerContext("DatePicker.Presets");
804
+ return /* @__PURE__ */ jsx("div", { role: "group", "aria-label": ctx.labels.popoverLabel, className: classNames?.root, ...props, children });
805
+ }
806
+ function resolveDatePreset(key, today, adapter) {
807
+ switch (key) {
808
+ case "today":
809
+ return today;
810
+ case "tomorrow":
811
+ return adapter.addDays(today, 1);
812
+ case "yesterday":
813
+ return adapter.addDays(today, -1);
814
+ case "startOfMonth":
815
+ return adapter.startOfMonth(today);
816
+ case "endOfMonth":
817
+ return adapter.startOfDay(adapter.endOfMonth(today));
818
+ case "startOfYear": {
819
+ const currentMonth = adapter.getMonth(today);
820
+ return adapter.startOfMonth(adapter.addMonths(today, -currentMonth));
821
+ }
822
+ }
823
+ }
824
+ function DatePickerPreset({
825
+ value: presetKey,
826
+ date: directDate,
827
+ children,
828
+ onClick,
829
+ ...props
830
+ }) {
831
+ const ctx = useDatePickerContext("DatePicker.Preset");
832
+ const handleClick = useCallback(
833
+ (e) => {
834
+ if (ctx.isDisabled || ctx.isReadOnly) return;
835
+ let resolved;
836
+ if (directDate) {
837
+ resolved = directDate;
838
+ } else if (presetKey) {
839
+ resolved = resolveDatePreset(
840
+ presetKey,
841
+ ctx.adapter.today(ctx.displayTimezone),
842
+ ctx.adapter
843
+ );
844
+ } else {
845
+ return;
846
+ }
847
+ ctx.selectDate(resolved);
848
+ onClick?.(e);
849
+ },
850
+ [ctx, presetKey, directDate, onClick]
851
+ );
852
+ const isActive = (() => {
853
+ if (!ctx.value) return false;
854
+ let target;
855
+ if (directDate) {
856
+ target = directDate;
857
+ } else if (presetKey) {
858
+ target = resolveDatePreset(presetKey, ctx.adapter.today(ctx.displayTimezone), ctx.adapter);
859
+ } else {
860
+ return false;
861
+ }
862
+ return ctx.adapter.isSameDay(ctx.value, target, ctx.displayTimezone);
863
+ })();
864
+ return /* @__PURE__ */ jsx(
865
+ "button",
866
+ {
867
+ type: "button",
868
+ role: "option",
869
+ "aria-selected": isActive,
870
+ "data-active": isActive || void 0,
871
+ disabled: ctx.isDisabled,
872
+ onClick: handleClick,
873
+ ...props,
874
+ children
875
+ }
876
+ );
877
+ }
780
878
 
781
879
  // src/components/DatePicker/index.ts
782
880
  var DatePicker = Object.assign(DatePickerRoot, {
@@ -785,7 +883,9 @@ var DatePicker = Object.assign(DatePickerRoot, {
785
883
  Popover: DatePickerPopover,
786
884
  Calendar: DatePickerCalendar,
787
885
  MonthGrid: DatePickerMonthGrid,
788
- YearGrid: DatePickerYearGrid
886
+ YearGrid: DatePickerYearGrid,
887
+ Presets: DatePickerPresets,
888
+ Preset: DatePickerPreset
789
889
  });
790
890
  var RangePickerContext = createContext(null);
791
891
  function useRangePickerContext(componentName) {
@@ -807,6 +907,8 @@ function RangePickerRoot({
807
907
  value: controlledValue,
808
908
  defaultValue,
809
909
  onChange,
910
+ onOpenChange,
911
+ onCalendarNavigate,
810
912
  disabled = false,
811
913
  readOnly = false,
812
914
  weekStartsOn = 0,
@@ -833,6 +935,9 @@ function RangePickerRoot({
833
935
  const [focusedDate, setFocusedDate] = useState(
834
936
  currentValue.start ?? adapter.today(displayTimezone)
835
937
  );
938
+ useChangeEffect(isOpen, onOpenChange);
939
+ const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
940
+ useChangeEffect(viewMonthStart, onCalendarNavigate);
836
941
  const mergedLabels = useMemo(
837
942
  () => ({ ...DEFAULT_RANGEPICKER_LABELS, ...labelsProp }),
838
943
  [labelsProp]
@@ -1017,12 +1122,13 @@ var RangePickerInput = forwardRef(
1017
1122
  function RangePickerPopover({ children, ...props }) {
1018
1123
  const ctx = useRangePickerContext("RangePicker.Popover");
1019
1124
  const calendarId = `${ctx.pickerId}-calendar`;
1020
- const { floatingStyles, setFloatingRef } = usePopover({
1125
+ const { floatingStyles, setFloatingRef, isPositioned } = usePopover({
1021
1126
  isOpen: ctx.isOpen,
1022
1127
  close: ctx.close,
1023
1128
  referenceRef: ctx.referenceRef
1024
1129
  });
1025
1130
  if (!ctx.isOpen) return null;
1131
+ const { style: userStyle, ...rest } = props;
1026
1132
  return /* @__PURE__ */ jsx(
1027
1133
  "div",
1028
1134
  {
@@ -1031,8 +1137,12 @@ function RangePickerPopover({ children, ...props }) {
1031
1137
  role: "dialog",
1032
1138
  "aria-label": ctx.labels.popoverLabel,
1033
1139
  "aria-modal": "false",
1034
- style: floatingStyles,
1035
- ...props,
1140
+ ...rest,
1141
+ style: {
1142
+ ...userStyle,
1143
+ ...floatingStyles,
1144
+ visibility: isPositioned ? void 0 : "hidden"
1145
+ },
1036
1146
  children
1037
1147
  }
1038
1148
  );
@@ -1055,7 +1165,11 @@ var srOnly2 = {
1055
1165
  whiteSpace: "nowrap",
1056
1166
  border: 0
1057
1167
  };
1058
- function RangePickerCalendar({ classNames, ...props }) {
1168
+ function RangePickerCalendar({
1169
+ classNames,
1170
+ selectionMode = "range",
1171
+ ...props
1172
+ }) {
1059
1173
  const ctx = useRangePickerContext("RangePicker.Calendar");
1060
1174
  const gridRef = useRef(null);
1061
1175
  const [announcement, setAnnouncement] = useState("");
@@ -1085,9 +1199,7 @@ function RangePickerCalendar({ classNames, ...props }) {
1085
1199
  const title = formatMonthYear(year, month, locale);
1086
1200
  useEffect(() => {
1087
1201
  if (!ctx.isOpen || !gridRef.current) return;
1088
- const focusedButton = gridRef.current.querySelector(
1089
- '[data-focused="true"]'
1090
- );
1202
+ const focusedButton = gridRef.current.querySelector('[data-focused="true"]');
1091
1203
  focusedButton?.focus({ preventScroll: true });
1092
1204
  }, [focusedDate, ctx.isOpen]);
1093
1205
  const navigateMonth = useCallback(
@@ -1101,21 +1213,39 @@ function RangePickerCalendar({ classNames, ...props }) {
1101
1213
  },
1102
1214
  [adapter, viewMonth, ctx, locale]
1103
1215
  );
1216
+ const commitDay = useCallback(
1217
+ (iso) => {
1218
+ if (selectionMode === "week") {
1219
+ const weekStart = adapter.startOfWeek(iso, weekStartsOn);
1220
+ const weekEnd = adapter.startOfDay(adapter.endOfWeek(iso, weekStartsOn));
1221
+ const range = { start: weekStart, end: weekEnd };
1222
+ ctx.setRange(range);
1223
+ ctx.close();
1224
+ setAnnouncement(
1225
+ `${safeFormatFullDate2(weekStart, locale)} \u2013 ${safeFormatFullDate2(weekEnd, locale)}`
1226
+ );
1227
+ } else {
1228
+ ctx.selectDate(iso);
1229
+ setAnnouncement(safeFormatFullDate2(iso, locale));
1230
+ }
1231
+ },
1232
+ [selectionMode, adapter, weekStartsOn, ctx, locale]
1233
+ );
1104
1234
  const handleDayClick = useCallback(
1105
1235
  (day) => {
1106
1236
  if (day.isDisabled) return;
1107
- ctx.selectDate(day.isoString);
1108
- setAnnouncement(safeFormatFullDate2(day.isoString, locale));
1237
+ commitDay(day.isoString);
1109
1238
  },
1110
- [ctx, locale]
1239
+ [commitDay]
1111
1240
  );
1112
1241
  const handleDayMouseEnter = useCallback(
1113
1242
  (day) => {
1243
+ if (selectionMode === "week") return;
1114
1244
  if (selectingTarget === "end" && value.start && !day.isDisabled) {
1115
1245
  ctx.setHoverDate(day.isoString);
1116
1246
  }
1117
1247
  },
1118
- [selectingTarget, value.start, ctx]
1248
+ [selectionMode, selectingTarget, value.start, ctx]
1119
1249
  );
1120
1250
  const handleMouseLeave = useCallback(() => {
1121
1251
  ctx.setHoverDate(null);
@@ -1152,7 +1282,7 @@ function RangePickerCalendar({ classNames, ...props }) {
1152
1282
  case " ":
1153
1283
  e.preventDefault();
1154
1284
  if (!isDateDisabled(focusedDate, disabled, adapter)) {
1155
- ctx.selectDate(focusedDate);
1285
+ commitDay(focusedDate);
1156
1286
  }
1157
1287
  return;
1158
1288
  case "Escape":
@@ -1167,12 +1297,23 @@ function RangePickerCalendar({ classNames, ...props }) {
1167
1297
  if (!adapter.isSameMonth(newFocused, viewMonth)) {
1168
1298
  ctx.setViewMonth(newFocused);
1169
1299
  }
1170
- if (selectingTarget === "end" && value.start) {
1300
+ if (selectionMode === "range" && selectingTarget === "end" && value.start) {
1171
1301
  ctx.setHoverDate(newFocused);
1172
1302
  }
1173
1303
  }
1174
1304
  },
1175
- [adapter, focusedDate, viewMonth, weekStartsOn, disabled, ctx, selectingTarget, value.start]
1305
+ [
1306
+ adapter,
1307
+ focusedDate,
1308
+ viewMonth,
1309
+ weekStartsOn,
1310
+ disabled,
1311
+ ctx,
1312
+ selectionMode,
1313
+ selectingTarget,
1314
+ value.start,
1315
+ commitDay
1316
+ ]
1176
1317
  );
1177
1318
  return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, onMouseLeave: handleMouseLeave, children: [
1178
1319
  /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
@@ -1229,7 +1370,7 @@ function RangePickerCalendar({ classNames, ...props }) {
1229
1370
  day.isDisabled && classNames?.dayDisabled,
1230
1371
  !day.isCurrentMonth && classNames?.dayOutsideMonth
1231
1372
  ].filter(Boolean).join(" ") || void 0;
1232
- const isSelected = day.isRangeStart || day.isRangeEnd;
1373
+ const isSelected = selectionMode === "week" ? day.isRangeStart || day.isRangeEnd || day.isInRange : day.isRangeStart || day.isRangeEnd;
1233
1374
  return /* @__PURE__ */ jsx(
1234
1375
  "td",
1235
1376
  {
@@ -1309,9 +1450,7 @@ function resolvePreset(key, today, adapter) {
1309
1450
  }
1310
1451
  case "thisYear": {
1311
1452
  const currentMonth = new Date(today).getUTCMonth();
1312
- const yearStart = adapter.startOfMonth(
1313
- adapter.addMonths(today, -currentMonth)
1314
- );
1453
+ const yearStart = adapter.startOfMonth(adapter.addMonths(today, -currentMonth));
1315
1454
  return { start: yearStart, end: today };
1316
1455
  }
1317
1456
  }
@@ -1447,7 +1586,19 @@ function TimePickerRoot({
1447
1586
  pickerId,
1448
1587
  labels: mergedLabels
1449
1588
  }),
1450
- [currentValue, setTime$1, format, step, withSeconds, displayTimezone, disabled, readOnly, currentTime, pickerId, mergedLabels]
1589
+ [
1590
+ currentValue,
1591
+ setTime$1,
1592
+ format,
1593
+ step,
1594
+ withSeconds,
1595
+ displayTimezone,
1596
+ disabled,
1597
+ readOnly,
1598
+ currentTime,
1599
+ pickerId,
1600
+ mergedLabels
1601
+ ]
1451
1602
  );
1452
1603
  return /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: contextValue, children });
1453
1604
  }
@@ -1464,12 +1615,9 @@ var TimePickerInput = forwardRef(
1464
1615
  }
1465
1616
  setInputText(null);
1466
1617
  }, [inputText, ctx]);
1467
- const handleChange = useCallback(
1468
- (e) => {
1469
- setInputText(e.target.value);
1470
- },
1471
- []
1472
- );
1618
+ const handleChange = useCallback((e) => {
1619
+ setInputText(e.target.value);
1620
+ }, []);
1473
1621
  const handleBlur = useCallback(
1474
1622
  (e) => {
1475
1623
  commitInput();
@@ -1542,9 +1690,7 @@ function useListboxNavigation({
1542
1690
  onSelect(target);
1543
1691
  cancelAnimationFrame(rafIdRef.current);
1544
1692
  rafIdRef.current = requestAnimationFrame(() => {
1545
- const next = listRef.current?.querySelector(
1546
- '[data-selected="true"]'
1547
- );
1693
+ const next = listRef.current?.querySelector('[data-selected="true"]');
1548
1694
  next?.focus();
1549
1695
  });
1550
1696
  }
@@ -1681,10 +1827,19 @@ function TimePickerAmPmToggle({ classNames, ...props }) {
1681
1827
  }
1682
1828
  );
1683
1829
  };
1684
- return /* @__PURE__ */ jsxs("div", { role: "radiogroup", "aria-label": ctx.labels.amPmToggle, className: classNames?.root, ...props, children: [
1685
- renderButton("AM"),
1686
- renderButton("PM")
1687
- ] });
1830
+ return /* @__PURE__ */ jsxs(
1831
+ "div",
1832
+ {
1833
+ role: "radiogroup",
1834
+ "aria-label": ctx.labels.amPmToggle,
1835
+ className: classNames?.root,
1836
+ ...props,
1837
+ children: [
1838
+ renderButton("AM"),
1839
+ renderButton("PM")
1840
+ ]
1841
+ }
1842
+ );
1688
1843
  }
1689
1844
 
1690
1845
  // src/components/TimePicker/index.ts
@@ -1701,6 +1856,8 @@ function DateTimePickerRoot({
1701
1856
  value: controlledValue,
1702
1857
  defaultValue,
1703
1858
  onChange,
1859
+ onOpenChange,
1860
+ onCalendarNavigate,
1704
1861
  format = "24h",
1705
1862
  step = 1,
1706
1863
  disabled = false,
@@ -1735,6 +1892,9 @@ function DateTimePickerRoot({
1735
1892
  const [focusedDate, setFocusedDate] = useState(
1736
1893
  currentValue ?? adapter.today(displayTimezone)
1737
1894
  );
1895
+ useChangeEffect(isOpen, onOpenChange);
1896
+ const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
1897
+ useChangeEffect(viewMonthStart, onCalendarNavigate);
1738
1898
  const isDisabled = typeof disabled === "boolean" ? disabled : false;
1739
1899
  const disabledRules = useMemo(
1740
1900
  () => Array.isArray(disabled) ? disabled : [],
@@ -1849,7 +2009,18 @@ function DateTimePickerRoot({
1849
2009
  pickerId,
1850
2010
  labels: mergedTimeLabels
1851
2011
  }),
1852
- [currentValue, setTime$1, format, step, displayTimezone, isDisabled, readOnly, currentTime, pickerId, mergedTimeLabels]
2012
+ [
2013
+ currentValue,
2014
+ setTime$1,
2015
+ format,
2016
+ step,
2017
+ displayTimezone,
2018
+ isDisabled,
2019
+ readOnly,
2020
+ currentTime,
2021
+ pickerId,
2022
+ mergedTimeLabels
2023
+ ]
1853
2024
  );
1854
2025
  return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: dateContext, children: /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: timeContext, children }) });
1855
2026
  }
@@ -1924,6 +2095,228 @@ var DateTimePicker = Object.assign(DateTimePickerRoot, {
1924
2095
  MinuteList: TimePickerMinuteList,
1925
2096
  AmPmToggle: TimePickerAmPmToggle
1926
2097
  });
2098
+ function MonthPickerRoot(props) {
2099
+ const displayFormat = props.displayFormat ?? "yyyy-MM";
2100
+ return /* @__PURE__ */ jsx(DatePickerRoot, { ...props, displayFormat });
2101
+ }
2102
+ function MonthPickerGrid({ classNames, ...props }) {
2103
+ const ctx = useDatePickerContext("MonthPicker.Grid");
2104
+ const { adapter, viewMonth, locale, value, displayTimezone, labels } = ctx;
2105
+ const currentYear = adapter.getYear(viewMonth);
2106
+ const [valueYear, valueMonthZeroBased] = useMemo(() => {
2107
+ if (!value) return [null, null];
2108
+ try {
2109
+ const [y, m] = adapter.format(value, "yyyy-MM", displayTimezone).split("-").map(Number);
2110
+ return [y, m - 1];
2111
+ } catch {
2112
+ return [null, null];
2113
+ }
2114
+ }, [value, adapter, displayTimezone]);
2115
+ const today = adapter.today(displayTimezone);
2116
+ const todayYear = adapter.getYear(today);
2117
+ const todayMonth = adapter.getMonth(today);
2118
+ const navigateYear = useCallback(
2119
+ (direction) => {
2120
+ ctx.setViewMonth(adapter.addYears(viewMonth, direction));
2121
+ },
2122
+ [adapter, viewMonth, ctx]
2123
+ );
2124
+ const handleMonthSelect = useCallback(
2125
+ (monthIndex) => {
2126
+ const target = new Date(Date.UTC(currentYear, monthIndex, 1)).toISOString();
2127
+ ctx.selectDate(target);
2128
+ },
2129
+ [currentYear, ctx]
2130
+ );
2131
+ const months = Array.from({ length: 12 }, (_, i) => ({
2132
+ index: i,
2133
+ name: getMonthName(i, locale),
2134
+ isSelected: valueYear === currentYear && valueMonthZeroBased === i,
2135
+ isCurrent: todayYear === currentYear && todayMonth === i
2136
+ }));
2137
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
2138
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
2139
+ /* @__PURE__ */ jsx(
2140
+ "button",
2141
+ {
2142
+ type: "button",
2143
+ className: classNames?.navButton,
2144
+ onClick: () => navigateYear(-1),
2145
+ "aria-label": labels.prevYear,
2146
+ children: "<"
2147
+ }
2148
+ ),
2149
+ /* @__PURE__ */ jsx("span", { className: classNames?.title, children: currentYear }),
2150
+ /* @__PURE__ */ jsx(
2151
+ "button",
2152
+ {
2153
+ type: "button",
2154
+ className: classNames?.navButton,
2155
+ onClick: () => navigateYear(1),
2156
+ "aria-label": labels.nextYear,
2157
+ children: ">"
2158
+ }
2159
+ )
2160
+ ] }),
2161
+ /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": `${currentYear} months`, className: classNames?.grid, children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
2162
+ "div",
2163
+ {
2164
+ role: "row",
2165
+ className: classNames?.gridRow,
2166
+ style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
2167
+ children: months.slice(rowIndex * 3, rowIndex * 3 + 3).map((m) => {
2168
+ const monthClass = [
2169
+ classNames?.month,
2170
+ m.isSelected && classNames?.monthSelected,
2171
+ m.isCurrent && classNames?.monthCurrent
2172
+ ].filter(Boolean).join(" ") || void 0;
2173
+ return /* @__PURE__ */ jsx(
2174
+ "button",
2175
+ {
2176
+ type: "button",
2177
+ role: "gridcell",
2178
+ "aria-selected": m.isSelected || void 0,
2179
+ "aria-current": m.isCurrent ? "date" : void 0,
2180
+ "data-selected": m.isSelected || void 0,
2181
+ "data-current": m.isCurrent || void 0,
2182
+ className: monthClass,
2183
+ onClick: () => handleMonthSelect(m.index),
2184
+ children: m.name
2185
+ },
2186
+ m.index
2187
+ );
2188
+ })
2189
+ },
2190
+ rowIndex
2191
+ )) })
2192
+ ] });
2193
+ }
2194
+
2195
+ // src/components/MonthPicker/index.ts
2196
+ var MonthPicker = Object.assign(MonthPickerRoot, {
2197
+ Input: DatePickerInput,
2198
+ Trigger: DatePickerTrigger,
2199
+ Popover: DatePickerPopover,
2200
+ Grid: MonthPickerGrid
2201
+ });
2202
+ function YearPickerRoot(props) {
2203
+ const displayFormat = props.displayFormat ?? "yyyy";
2204
+ return /* @__PURE__ */ jsx(DatePickerRoot, { ...props, displayFormat });
2205
+ }
2206
+ function YearPickerGrid({ classNames, ...props }) {
2207
+ const ctx = useDatePickerContext("YearPicker.Grid");
2208
+ const { adapter, viewMonth, value, displayTimezone, labels } = ctx;
2209
+ const currentYear = adapter.getYear(viewMonth);
2210
+ const decadeStart = currentYear - currentYear % 12;
2211
+ const valueYear = useMemo(() => {
2212
+ if (!value) return null;
2213
+ try {
2214
+ return Number(adapter.format(value, "yyyy", displayTimezone));
2215
+ } catch {
2216
+ return null;
2217
+ }
2218
+ }, [value, adapter, displayTimezone]);
2219
+ const todayYear = adapter.getYear(adapter.today(displayTimezone));
2220
+ const navigateDecade = useCallback(
2221
+ (direction) => {
2222
+ ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
2223
+ },
2224
+ [adapter, viewMonth, ctx]
2225
+ );
2226
+ const handleYearSelect = useCallback(
2227
+ (year) => {
2228
+ const target = new Date(Date.UTC(year, 0, 1)).toISOString();
2229
+ ctx.selectDate(target);
2230
+ },
2231
+ [ctx]
2232
+ );
2233
+ const years = Array.from({ length: 12 }, (_, i) => {
2234
+ const year = decadeStart + i;
2235
+ return {
2236
+ value: year,
2237
+ isSelected: year === valueYear,
2238
+ isCurrent: year === todayYear
2239
+ };
2240
+ });
2241
+ const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
2242
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
2243
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
2244
+ /* @__PURE__ */ jsx(
2245
+ "button",
2246
+ {
2247
+ type: "button",
2248
+ className: classNames?.navButton,
2249
+ onClick: () => navigateDecade(-1),
2250
+ "aria-label": labels.prevDecade,
2251
+ children: "<"
2252
+ }
2253
+ ),
2254
+ /* @__PURE__ */ jsx("span", { className: classNames?.title, children: rangeLabel }),
2255
+ /* @__PURE__ */ jsx(
2256
+ "button",
2257
+ {
2258
+ type: "button",
2259
+ className: classNames?.navButton,
2260
+ onClick: () => navigateDecade(1),
2261
+ "aria-label": labels.nextDecade,
2262
+ children: ">"
2263
+ }
2264
+ )
2265
+ ] }),
2266
+ /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": rangeLabel, className: classNames?.grid, children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
2267
+ "div",
2268
+ {
2269
+ role: "row",
2270
+ className: classNames?.gridRow,
2271
+ style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
2272
+ children: years.slice(rowIndex * 3, rowIndex * 3 + 3).map((y) => {
2273
+ const yearClass = [
2274
+ classNames?.year,
2275
+ y.isSelected && classNames?.yearSelected,
2276
+ y.isCurrent && classNames?.yearCurrent
2277
+ ].filter(Boolean).join(" ") || void 0;
2278
+ return /* @__PURE__ */ jsx(
2279
+ "button",
2280
+ {
2281
+ type: "button",
2282
+ role: "gridcell",
2283
+ "aria-selected": y.isSelected || void 0,
2284
+ "aria-current": y.isCurrent ? "date" : void 0,
2285
+ "data-selected": y.isSelected || void 0,
2286
+ "data-current": y.isCurrent || void 0,
2287
+ className: yearClass,
2288
+ onClick: () => handleYearSelect(y.value),
2289
+ children: y.value
2290
+ },
2291
+ y.value
2292
+ );
2293
+ })
2294
+ },
2295
+ rowIndex
2296
+ )) })
2297
+ ] });
2298
+ }
2299
+
2300
+ // src/components/YearPicker/index.ts
2301
+ var YearPicker = Object.assign(YearPickerRoot, {
2302
+ Input: DatePickerInput,
2303
+ Trigger: DatePickerTrigger,
2304
+ Popover: DatePickerPopover,
2305
+ Grid: YearPickerGrid
2306
+ });
2307
+ function WeekPickerRoot(props) {
2308
+ return /* @__PURE__ */ jsx(RangePickerRoot, { ...props });
2309
+ }
2310
+ function WeekPickerCalendar(props) {
2311
+ return /* @__PURE__ */ jsx(RangePickerCalendar, { ...props, selectionMode: "week" });
2312
+ }
2313
+
2314
+ // src/components/WeekPicker/index.ts
2315
+ var WeekPicker = Object.assign(WeekPickerRoot, {
2316
+ Input: RangePickerInput,
2317
+ Popover: RangePickerPopover,
2318
+ Calendar: WeekPickerCalendar
2319
+ });
1927
2320
  function useDatePicker(options = {}) {
1928
2321
  const {
1929
2322
  value: controlledValue,
@@ -2163,14 +2556,8 @@ function useTimePicker(options = {}) {
2163
2556
  },
2164
2557
  [format, period, setTime$1]
2165
2558
  );
2166
- const setMinute = useCallback(
2167
- (minute) => setTime$1({ minutes: minute }),
2168
- [setTime$1]
2169
- );
2170
- const setSecond = useCallback(
2171
- (second) => setTime$1({ seconds: second }),
2172
- [setTime$1]
2173
- );
2559
+ const setMinute = useCallback((minute) => setTime$1({ minutes: minute }), [setTime$1]);
2560
+ const setSecond = useCallback((second) => setTime$1({ seconds: second }), [setTime$1]);
2174
2561
  const setPeriod = useCallback(
2175
2562
  (newPeriod) => {
2176
2563
  if (format !== "12h") return;
@@ -2196,6 +2583,6 @@ function useTimePicker(options = {}) {
2196
2583
  };
2197
2584
  }
2198
2585
 
2199
- export { DatePicker, DateTimePicker, RangePicker, TimePicker, useDatePicker, useRangePicker, useTimePicker };
2586
+ export { DatePicker, DateTimePicker, MonthPicker, RangePicker, TimePicker, WeekPicker, YearPicker, useDatePicker, useRangePicker, useTimePicker };
2200
2587
  //# sourceMappingURL=index.js.map
2201
2588
  //# sourceMappingURL=index.js.map