@kalyx/react 1.0.0-rc.0 → 1.0.0-rc.10

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.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  'use strict';
2
3
 
3
4
  var react = require('react');
@@ -57,10 +58,10 @@ function DatePickerRoot({
57
58
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
58
59
  const [isOpen, setIsOpen] = react.useState(false);
59
60
  const [viewMonth, setViewMonth] = react.useState(
60
- currentValue ?? adapter.today(displayTimezone)
61
+ () => currentValue ?? adapter.today(displayTimezone)
61
62
  );
62
63
  const [focusedDate, setFocusedDate] = react.useState(
63
- currentValue ?? adapter.today(displayTimezone)
64
+ () => currentValue ?? adapter.today(displayTimezone)
64
65
  );
65
66
  useChangeEffect(isOpen, onOpenChange);
66
67
  const viewMonthStart = react.useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
@@ -151,10 +152,11 @@ function DatePickerRoot({
151
152
  return /* @__PURE__ */ jsxRuntime.jsx(DatePickerContext.Provider, { value: contextValue, children });
152
153
  }
153
154
  var DatePickerInput = react.forwardRef(
154
- function DatePickerInput2({ format: formatProp, onClick, onBlur, onKeyDown, ...props }, ref) {
155
+ function DatePickerInput2({ format: formatProp, name, onClick, onBlur, onKeyDown, ...props }, ref) {
155
156
  const ctx = useDatePickerContext("DatePicker.Input");
156
157
  const displayFormat = formatProp ?? ctx.displayFormat;
157
158
  const [inputText, setInputText] = react.useState(null);
159
+ const isComposingRef = react.useRef(false);
158
160
  let formattedValue = "";
159
161
  if (ctx.value) {
160
162
  try {
@@ -171,47 +173,62 @@ var DatePickerInput = react.forwardRef(
171
173
  },
172
174
  [ctx, onClick]
173
175
  );
176
+ const commitText = react.useCallback(
177
+ (text) => {
178
+ if (!text) {
179
+ ctx.selectDate(null);
180
+ setInputText(null);
181
+ return true;
182
+ }
183
+ const parsed = core.parseInputValue(text, ctx.adapter);
184
+ if (parsed) {
185
+ ctx.selectDate(parsed);
186
+ setInputText(null);
187
+ return true;
188
+ }
189
+ return false;
190
+ },
191
+ [ctx]
192
+ );
174
193
  const handleBlur = react.useCallback(
175
194
  (e) => {
176
195
  if (inputText !== null) {
177
- const parsed = core.parseInputValue(inputText, ctx.adapter);
178
- if (parsed) {
179
- ctx.selectDate(parsed);
180
- }
196
+ commitText(inputText);
181
197
  setInputText(null);
182
198
  }
183
199
  onBlur?.(e);
184
200
  },
185
- [inputText, displayFormat, ctx, onBlur]
201
+ [inputText, commitText, onBlur]
186
202
  );
187
203
  const handleChange = react.useCallback(
188
204
  (e) => {
189
205
  const text = e.target.value;
190
206
  setInputText(text);
191
- if (!text) {
192
- ctx.selectDate(null);
193
- setInputText(null);
194
- return;
195
- }
196
- const parsed = core.parseInputValue(text, ctx.adapter);
197
- if (parsed) {
198
- ctx.selectDate(parsed);
199
- setInputText(null);
200
- }
207
+ if (isComposingRef.current) return;
208
+ commitText(text);
209
+ },
210
+ [commitText]
211
+ );
212
+ const handleCompositionStart = react.useCallback(() => {
213
+ isComposingRef.current = true;
214
+ }, []);
215
+ const handleCompositionEnd = react.useCallback(
216
+ (e) => {
217
+ isComposingRef.current = false;
218
+ commitText(e.target.value);
201
219
  },
202
- [displayFormat, ctx]
220
+ [commitText]
203
221
  );
204
222
  const handleKeyDown = react.useCallback(
205
223
  (e) => {
206
224
  if (e.key === "Escape") {
207
225
  ctx.close();
208
226
  } else if (e.key === "Enter") {
227
+ if (ctx.isOpen) e.preventDefault();
209
228
  if (inputText !== null) {
210
- const parsed = core.parseInputValue(inputText, ctx.adapter);
211
- if (parsed) {
212
- ctx.selectDate(parsed);
213
- setInputText(null);
214
- }
229
+ commitText(inputText);
230
+ } else if (ctx.isOpen) {
231
+ ctx.selectDate(ctx.focusedDate);
215
232
  }
216
233
  } else if (e.key === "ArrowDown" && !ctx.isOpen) {
217
234
  e.preventDefault();
@@ -219,36 +236,42 @@ var DatePickerInput = react.forwardRef(
219
236
  }
220
237
  onKeyDown?.(e);
221
238
  },
222
- [ctx, inputText, displayFormat, onKeyDown]
239
+ [ctx, inputText, commitText, onKeyDown]
223
240
  );
224
241
  const calendarId = `${ctx.pickerId}-calendar`;
225
- return /* @__PURE__ */ jsxRuntime.jsx(
226
- "input",
227
- {
228
- ref: (node) => {
229
- ctx.referenceRef.current = node;
230
- if (typeof ref === "function") ref(node);
231
- else if (ref) ref.current = node;
232
- },
233
- type: "text",
234
- role: "combobox",
235
- "aria-expanded": ctx.isOpen,
236
- "aria-haspopup": "dialog",
237
- "aria-controls": ctx.isOpen ? calendarId : void 0,
238
- "aria-autocomplete": "none",
239
- autoComplete: "off",
240
- value: displayValue,
241
- disabled: ctx.isDisabled || props.disabled,
242
- readOnly: ctx.isReadOnly,
243
- onChange: handleChange,
244
- onClick: handleClick,
245
- onBlur: handleBlur,
246
- onKeyDown: handleKeyDown,
247
- ...props
248
- }
249
- );
242
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
243
+ /* @__PURE__ */ jsxRuntime.jsx(
244
+ "input",
245
+ {
246
+ ref: (node) => {
247
+ ctx.referenceRef.current = node;
248
+ if (typeof ref === "function") ref(node);
249
+ else if (ref) ref.current = node;
250
+ },
251
+ type: "text",
252
+ role: "combobox",
253
+ "aria-expanded": ctx.isOpen,
254
+ "aria-haspopup": "dialog",
255
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
256
+ "aria-autocomplete": "none",
257
+ autoComplete: "off",
258
+ value: displayValue,
259
+ disabled: ctx.isDisabled || props.disabled,
260
+ readOnly: ctx.isReadOnly,
261
+ onChange: handleChange,
262
+ onClick: handleClick,
263
+ onBlur: handleBlur,
264
+ onKeyDown: handleKeyDown,
265
+ onCompositionStart: handleCompositionStart,
266
+ onCompositionEnd: handleCompositionEnd,
267
+ ...props
268
+ }
269
+ ),
270
+ name ? /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: ctx.value ?? "" }) : null
271
+ ] });
250
272
  }
251
273
  );
274
+ DatePickerInput.displayName = "DatePicker.Input";
252
275
  var DatePickerTrigger = react.forwardRef(
253
276
  function DatePickerTrigger2({ onClick, children, ...props }, ref) {
254
277
  const ctx = useDatePickerContext("DatePicker.Trigger");
@@ -272,6 +295,7 @@ var DatePickerTrigger = react.forwardRef(
272
295
  tabIndex: 0,
273
296
  "aria-label": ctx.isOpen ? ctx.labels.triggerClose : ctx.labels.triggerOpen,
274
297
  "aria-expanded": ctx.isOpen,
298
+ "aria-haspopup": "dialog",
275
299
  "aria-controls": ctx.isOpen ? calendarId : void 0,
276
300
  disabled: ctx.isDisabled || props.disabled,
277
301
  onClick: handleClick,
@@ -302,13 +326,20 @@ var DatePickerTrigger = react.forwardRef(
302
326
  );
303
327
  }
304
328
  );
305
- function usePopover({ isOpen, close, referenceRef, placement = "bottom-start" }) {
329
+ DatePickerTrigger.displayName = "DatePicker.Trigger";
330
+ var POPOVER_MIDDLEWARE = [react$1.offset(4), react$1.flip(), react$1.shift({ padding: 8 })];
331
+ function usePopover({
332
+ isOpen,
333
+ close,
334
+ referenceRef,
335
+ placement = "bottom-start"
336
+ }) {
306
337
  const floatingRef = react.useRef(null);
307
338
  const previousFocusRef = react.useRef(null);
308
- const { refs, floatingStyles } = react$1.useFloating({
339
+ const { refs, floatingStyles, isPositioned } = react$1.useFloating({
309
340
  open: isOpen,
310
341
  placement,
311
- middleware: [react$1.offset(4), react$1.flip(), react$1.shift({ padding: 8 })],
342
+ middleware: POPOVER_MIDDLEWARE,
312
343
  whileElementsMounted: react$1.autoUpdate
313
344
  });
314
345
  react.useEffect(() => {
@@ -355,21 +386,46 @@ function usePopover({ isOpen, close, referenceRef, placement = "bottom-start" })
355
386
  document.addEventListener("keydown", handleKeyDown);
356
387
  return () => document.removeEventListener("keydown", handleKeyDown);
357
388
  }, [isOpen, close]);
358
- const setFloatingRef = (node) => {
359
- floatingRef.current = node;
360
- refs.setFloating(node);
361
- };
362
- return { floatingStyles, setFloatingRef };
389
+ react.useEffect(() => {
390
+ if (!isOpen) return;
391
+ function handleFocusOut(e) {
392
+ const next = e.relatedTarget;
393
+ const floating = floatingRef.current;
394
+ const reference = referenceRef.current;
395
+ if (!next) return;
396
+ const insideFloating = floating?.contains(next) ?? false;
397
+ const insideReference = reference?.contains(next) ?? false;
398
+ if (!insideFloating && !insideReference) {
399
+ close();
400
+ }
401
+ }
402
+ const node = floatingRef.current;
403
+ if (!node) return;
404
+ node.addEventListener("focusout", handleFocusOut);
405
+ return () => node.removeEventListener("focusout", handleFocusOut);
406
+ }, [isOpen, close, referenceRef]);
407
+ const setFloatingRef = react.useCallback(
408
+ (node) => {
409
+ floatingRef.current = node;
410
+ refs.setFloating(node);
411
+ if (node && referenceRef.current) {
412
+ refs.setReference(referenceRef.current);
413
+ }
414
+ },
415
+ [refs, referenceRef]
416
+ );
417
+ return { floatingStyles, setFloatingRef, isPositioned };
363
418
  }
364
419
  function DatePickerPopover({ children, ...props }) {
365
420
  const ctx = useDatePickerContext("DatePicker.Popover");
366
421
  const calendarId = `${ctx.pickerId}-calendar`;
367
- const { floatingStyles, setFloatingRef } = usePopover({
422
+ const { floatingStyles, setFloatingRef, isPositioned } = usePopover({
368
423
  isOpen: ctx.isOpen,
369
424
  close: ctx.close,
370
425
  referenceRef: ctx.referenceRef
371
426
  });
372
427
  if (!ctx.isOpen) return null;
428
+ const { style: userStyle, ...rest } = props;
373
429
  return /* @__PURE__ */ jsxRuntime.jsx(
374
430
  "div",
375
431
  {
@@ -378,8 +434,12 @@ function DatePickerPopover({ children, ...props }) {
378
434
  role: "dialog",
379
435
  "aria-label": ctx.labels.popoverLabel,
380
436
  "aria-modal": "false",
381
- style: floatingStyles,
382
- ...props,
437
+ ...rest,
438
+ style: {
439
+ ...userStyle,
440
+ ...floatingStyles,
441
+ visibility: isPositioned ? void 0 : "hidden"
442
+ },
383
443
  children
384
444
  }
385
445
  );
@@ -402,27 +462,45 @@ var srOnly = {
402
462
  whiteSpace: "nowrap",
403
463
  border: 0
404
464
  };
405
- function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
465
+ function DatePickerCalendar({
466
+ classNames,
467
+ onTitleClick,
468
+ showWeekNumber = false,
469
+ fixedWeeks = false,
470
+ ...props
471
+ }) {
406
472
  const ctx = useDatePickerContext("DatePicker.Calendar");
407
473
  const gridRef = react.useRef(null);
408
474
  const [announcement, setAnnouncement] = react.useState("");
409
475
  const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale, displayTimezone } = ctx;
410
- const weekdays = core.getWeekdayNames(locale, weekStartsOn);
411
- const weeks = core.getCalendarDays(viewMonth, adapter, {
412
- weekStartsOn,
413
- selected: ctx.value,
414
- focusedDate,
415
- disabled,
416
- timezone: displayTimezone
417
- });
476
+ const weekdays = react.useMemo(() => core.getWeekdayNames(locale, weekStartsOn), [locale, weekStartsOn]);
477
+ const weeks = react.useMemo(
478
+ () => core.getCalendarDays(viewMonth, adapter, {
479
+ weekStartsOn,
480
+ selected: ctx.value,
481
+ focusedDate,
482
+ disabled,
483
+ timezone: displayTimezone,
484
+ fixedWeeks
485
+ }),
486
+ [
487
+ viewMonth,
488
+ adapter,
489
+ weekStartsOn,
490
+ ctx.value,
491
+ focusedDate,
492
+ disabled,
493
+ displayTimezone,
494
+ fixedWeeks
495
+ ]
496
+ );
497
+ const thursdayIndex = weekStartsOn === 0 ? 4 : 3;
418
498
  const year = adapter.getYear(viewMonth);
419
499
  const month = adapter.getMonth(viewMonth);
420
500
  const title = core.formatMonthYear(year, month, locale);
421
501
  react.useEffect(() => {
422
502
  if (!ctx.isOpen || !gridRef.current) return;
423
- const focusedButton = gridRef.current.querySelector(
424
- '[data-focused="true"]'
425
- );
503
+ const focusedButton = gridRef.current.querySelector('[data-focused="true"]');
426
504
  focusedButton?.focus({ preventScroll: true });
427
505
  }, [focusedDate, ctx.isOpen]);
428
506
  const navigateMonth = react.useCallback(
@@ -496,6 +574,15 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
496
574
  }
497
575
  if (newFocused) {
498
576
  e.preventDefault();
577
+ const skipStep = e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "PageUp" || e.key === "Home" ? -1 : 1;
578
+ let attempts = 0;
579
+ while (core.isDateDisabled(newFocused, disabled, adapter) && attempts < 42) {
580
+ newFocused = adapter.addDays(newFocused, skipStep);
581
+ attempts++;
582
+ }
583
+ if (attempts >= 42) {
584
+ return;
585
+ }
499
586
  ctx.setFocusedDate(newFocused);
500
587
  if (!adapter.isSameMonth(newFocused, viewMonth)) {
501
588
  ctx.setViewMonth(newFocused);
@@ -543,62 +630,170 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
543
630
  ref: gridRef,
544
631
  role: "grid",
545
632
  "aria-label": title,
633
+ "aria-rowcount": weeks.length + 1,
634
+ "aria-colcount": 7,
546
635
  className: classNames?.grid,
547
636
  onKeyDown: handleKeyDown,
548
637
  children: [
549
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { role: "row", children: weekdays.map((day) => /* @__PURE__ */ jsxRuntime.jsx(
550
- "th",
551
- {
552
- role: "columnheader",
553
- abbr: day.full,
554
- scope: "col",
555
- className: classNames?.weekdayHeader,
556
- children: day.short
557
- },
558
- day.short
559
- )) }) }),
560
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxRuntime.jsx("tr", { role: "row", className: classNames?.gridRow, children: week.map((day) => {
561
- const dayClasses = [
562
- classNames?.day,
563
- day.isSelected && classNames?.daySelected,
564
- day.isToday && classNames?.dayToday,
565
- day.isDisabled && classNames?.dayDisabled,
566
- !day.isCurrentMonth && classNames?.dayOutsideMonth
567
- ].filter(Boolean).join(" ") || void 0;
568
- return /* @__PURE__ */ jsxRuntime.jsx(
569
- "td",
638
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
639
+ showWeekNumber ? /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", "aria-hidden": "true", className: classNames?.weekNumberHeader, children: "#" }) : null,
640
+ weekdays.map((day, colIndex) => /* @__PURE__ */ jsxRuntime.jsx(
641
+ "th",
570
642
  {
571
- role: "gridcell",
572
- "aria-selected": day.isSelected || void 0,
573
- "aria-disabled": day.isDisabled || void 0,
574
- "aria-current": day.isToday ? "date" : void 0,
575
- className: classNames?.gridCell,
576
- children: /* @__PURE__ */ jsxRuntime.jsx(
577
- "button",
643
+ role: "columnheader",
644
+ abbr: day.full,
645
+ scope: "col",
646
+ "aria-colindex": colIndex + 1,
647
+ className: classNames?.weekdayHeader,
648
+ children: day.short
649
+ },
650
+ day.short
651
+ ))
652
+ ] }) }),
653
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxRuntime.jsxs(
654
+ "tr",
655
+ {
656
+ role: "row",
657
+ "aria-rowindex": weekIndex + 2,
658
+ className: classNames?.gridRow,
659
+ children: [
660
+ showWeekNumber ? /* @__PURE__ */ jsxRuntime.jsx(
661
+ "th",
578
662
  {
579
- type: "button",
580
- tabIndex: day.isFocused ? 0 : -1,
581
- disabled: day.isDisabled,
582
- "data-focused": day.isFocused || void 0,
583
- "data-selected": day.isSelected || void 0,
584
- "data-today": day.isToday || void 0,
585
- "data-outside-month": !day.isCurrentMonth || void 0,
586
- className: dayClasses,
587
- onClick: () => handleDayClick(day),
588
- "aria-label": safeFormatFullDate(day.isoString, locale),
589
- children: day.dayNumber
663
+ scope: "row",
664
+ "aria-hidden": "true",
665
+ className: classNames?.weekNumber,
666
+ "data-week-number": true,
667
+ children: core.getISOWeekNumber(week[thursdayIndex].isoString)
590
668
  }
591
- )
592
- },
593
- day.isoString
594
- );
595
- }) }, weekIndex)) })
669
+ ) : null,
670
+ week.map((day, colIndex) => {
671
+ const dayClasses = [
672
+ classNames?.day,
673
+ day.isSelected && classNames?.daySelected,
674
+ day.isToday && classNames?.dayToday,
675
+ day.isDisabled && classNames?.dayDisabled,
676
+ !day.isCurrentMonth && classNames?.dayOutsideMonth
677
+ ].filter(Boolean).join(" ") || void 0;
678
+ return /* @__PURE__ */ jsxRuntime.jsx(
679
+ "td",
680
+ {
681
+ role: "gridcell",
682
+ "aria-colindex": colIndex + 1,
683
+ "aria-selected": day.isSelected || void 0,
684
+ "aria-disabled": day.isDisabled || void 0,
685
+ "aria-current": day.isToday ? "date" : void 0,
686
+ className: classNames?.gridCell,
687
+ children: /* @__PURE__ */ jsxRuntime.jsx(
688
+ "button",
689
+ {
690
+ type: "button",
691
+ tabIndex: day.isFocused ? 0 : -1,
692
+ disabled: day.isDisabled,
693
+ "data-focused": day.isFocused || void 0,
694
+ "data-selected": day.isSelected || void 0,
695
+ "data-today": day.isToday || void 0,
696
+ "data-outside-month": !day.isCurrentMonth || void 0,
697
+ className: dayClasses,
698
+ onClick: () => handleDayClick(day),
699
+ "aria-label": safeFormatFullDate(day.isoString, locale),
700
+ children: day.dayNumber
701
+ }
702
+ )
703
+ },
704
+ day.isoString
705
+ );
706
+ })
707
+ ]
708
+ },
709
+ weekIndex
710
+ )) })
596
711
  ]
597
712
  }
598
713
  ),
599
714
  /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: srOnly, children: announcement })
600
715
  ] });
601
716
  }
717
+ function isRangeFullyDisabled(start, end, rules, adapter) {
718
+ for (const rule of rules) {
719
+ if ("before" in rule && adapter.isBefore(end, rule.before)) return true;
720
+ if ("after" in rule && adapter.isAfter(start, rule.after)) return true;
721
+ }
722
+ return false;
723
+ }
724
+ function useGridState(opts) {
725
+ const { initialIndex, disabledFlags, onSelect, onPageUp, onPageDown, onEscape } = opts;
726
+ const gridRef = react.useRef(null);
727
+ const [focusedIndex, setFocusedIndex] = react.useState(initialIndex);
728
+ const handleKeyDown = (e) => {
729
+ let next = null;
730
+ let step = 1;
731
+ switch (e.key) {
732
+ case "ArrowLeft":
733
+ next = Math.max(0, focusedIndex - 1);
734
+ step = -1;
735
+ break;
736
+ case "ArrowRight":
737
+ next = Math.min(11, focusedIndex + 1);
738
+ break;
739
+ case "ArrowUp":
740
+ next = Math.max(0, focusedIndex - 3);
741
+ step = -1;
742
+ break;
743
+ case "ArrowDown":
744
+ next = Math.min(11, focusedIndex + 3);
745
+ break;
746
+ case "Home":
747
+ next = focusedIndex - focusedIndex % 3;
748
+ step = -1;
749
+ break;
750
+ case "End":
751
+ next = focusedIndex - focusedIndex % 3 + 2;
752
+ break;
753
+ case "PageUp":
754
+ e.preventDefault();
755
+ onPageUp();
756
+ return;
757
+ case "PageDown":
758
+ e.preventDefault();
759
+ onPageDown();
760
+ return;
761
+ case "Enter":
762
+ case " ":
763
+ e.preventDefault();
764
+ onSelect(focusedIndex);
765
+ return;
766
+ case "Escape":
767
+ onEscape();
768
+ return;
769
+ default:
770
+ return;
771
+ }
772
+ if (next === null) return;
773
+ e.preventDefault();
774
+ if (disabledFlags) {
775
+ let attempts = 0;
776
+ while (next >= 0 && next < 12 && disabledFlags[next] && attempts < 12) {
777
+ next += step;
778
+ attempts++;
779
+ }
780
+ if (next < 0 || next >= 12 || disabledFlags[next]) return;
781
+ }
782
+ if (next !== focusedIndex) setFocusedIndex(next);
783
+ };
784
+ react.useEffect(() => {
785
+ if (!disabledFlags || !disabledFlags[focusedIndex]) return;
786
+ const firstEnabled = disabledFlags.findIndex((d) => !d);
787
+ if (firstEnabled !== -1 && firstEnabled !== focusedIndex) {
788
+ setFocusedIndex(firstEnabled);
789
+ }
790
+ }, [disabledFlags, focusedIndex]);
791
+ react.useEffect(() => {
792
+ const btn = gridRef.current?.querySelector('[data-focused="true"]');
793
+ btn?.focus({ preventScroll: true });
794
+ }, [focusedIndex]);
795
+ return { gridRef, focusedIndex, handleKeyDown };
796
+ }
602
797
  function DatePickerMonthGrid({
603
798
  classNames,
604
799
  onSelect,
@@ -606,15 +801,18 @@ function DatePickerMonthGrid({
606
801
  ...props
607
802
  }) {
608
803
  const ctx = useDatePickerContext("DatePicker.MonthGrid");
609
- const { adapter, viewMonth, locale } = ctx;
804
+ const { adapter, viewMonth, locale, displayTimezone } = ctx;
610
805
  const currentYear = adapter.getYear(viewMonth);
611
806
  const currentMonth = adapter.getMonth(viewMonth);
612
- const todayMonth = adapter.getMonth(adapter.today());
613
- const todayYear = adapter.getYear(adapter.today());
807
+ const [today, setToday] = react.useState(null);
808
+ react.useEffect(() => {
809
+ setToday(adapter.today(displayTimezone));
810
+ }, [adapter, displayTimezone]);
811
+ const todayMonth = today !== null ? adapter.getMonth(today) : -1;
812
+ const todayYear = today !== null ? adapter.getYear(today) : -1;
614
813
  const navigateYear = react.useCallback(
615
814
  (direction) => {
616
- const newDate = adapter.addYears(viewMonth, direction);
617
- ctx.setViewMonth(newDate);
815
+ ctx.setViewMonth(adapter.addYears(viewMonth, direction));
618
816
  },
619
817
  [adapter, viewMonth, ctx]
620
818
  );
@@ -627,12 +825,13 @@ function DatePickerMonthGrid({
627
825
  },
628
826
  [currentYear, ctx, onSelect]
629
827
  );
630
- const months = Array.from({ length: 12 }, (_, i) => ({
631
- index: i,
632
- name: core.getMonthName(i, locale),
633
- isSelected: i === currentMonth,
634
- isCurrent: i === todayMonth && currentYear === todayYear
635
- }));
828
+ const { gridRef, focusedIndex, handleKeyDown } = useGridState({
829
+ initialIndex: currentMonth,
830
+ onSelect: handleMonthSelect,
831
+ onPageUp: () => navigateYear(-1),
832
+ onPageDown: () => navigateYear(1),
833
+ onEscape: ctx.close
834
+ });
636
835
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.root, ...props, children: [
637
836
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.header, children: [
638
837
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -645,15 +844,7 @@ function DatePickerMonthGrid({
645
844
  children: "<"
646
845
  }
647
846
  ),
648
- onTitleClick ? /* @__PURE__ */ jsxRuntime.jsx(
649
- "button",
650
- {
651
- type: "button",
652
- className: classNames?.title,
653
- onClick: onTitleClick,
654
- children: currentYear
655
- }
656
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: classNames?.title, children: currentYear }),
847
+ onTitleClick ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: classNames?.title, onClick: onTitleClick, children: currentYear }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: classNames?.title, children: currentYear }),
657
848
  /* @__PURE__ */ jsxRuntime.jsx(
658
849
  "button",
659
850
  {
@@ -668,74 +859,77 @@ function DatePickerMonthGrid({
668
859
  /* @__PURE__ */ jsxRuntime.jsx(
669
860
  "div",
670
861
  {
862
+ ref: gridRef,
671
863
  role: "grid",
672
864
  "aria-label": `${currentYear} months`,
673
865
  className: classNames?.grid,
674
866
  style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
675
- children: months.map((m) => {
676
- const monthClass = [
867
+ onKeyDown: handleKeyDown,
868
+ children: Array.from({ length: 12 }, (_, i) => {
869
+ const isSelected = i === currentMonth;
870
+ const isCurrent = i === todayMonth && currentYear === todayYear;
871
+ const isFocused = i === focusedIndex;
872
+ const cls = [
677
873
  classNames?.month,
678
- m.isSelected && classNames?.monthSelected,
679
- m.isCurrent && classNames?.monthCurrent
874
+ isSelected && classNames?.monthSelected,
875
+ isCurrent && classNames?.monthCurrent
680
876
  ].filter(Boolean).join(" ") || void 0;
681
877
  return /* @__PURE__ */ jsxRuntime.jsx(
682
878
  "button",
683
879
  {
684
880
  type: "button",
685
881
  role: "gridcell",
686
- "aria-selected": m.isSelected || void 0,
687
- "aria-current": m.isCurrent ? "date" : void 0,
688
- "data-selected": m.isSelected || void 0,
689
- "data-current": m.isCurrent || void 0,
690
- className: monthClass,
691
- onClick: () => handleMonthSelect(m.index),
692
- children: m.name
882
+ tabIndex: isFocused ? 0 : -1,
883
+ "aria-selected": isSelected || void 0,
884
+ "aria-current": isCurrent ? "date" : void 0,
885
+ "data-selected": isSelected || void 0,
886
+ "data-current": isCurrent || void 0,
887
+ "data-focused": isFocused || void 0,
888
+ className: cls,
889
+ onClick: () => handleMonthSelect(i),
890
+ children: core.getMonthName(i, locale)
693
891
  },
694
- m.index
892
+ i
695
893
  );
696
894
  })
697
895
  }
698
896
  )
699
897
  ] });
700
898
  }
701
- function DatePickerYearGrid({
702
- classNames,
703
- onSelect,
704
- ...props
705
- }) {
899
+ function DatePickerYearGrid({ classNames, onSelect, ...props }) {
706
900
  const ctx = useDatePickerContext("DatePicker.YearGrid");
707
- const { adapter, viewMonth } = ctx;
901
+ const { adapter, viewMonth, displayTimezone } = ctx;
708
902
  const currentYear = adapter.getYear(viewMonth);
709
- const todayYear = adapter.getYear(adapter.today());
903
+ const [today, setToday] = react.useState(null);
904
+ react.useEffect(() => {
905
+ setToday(adapter.today(displayTimezone));
906
+ }, [adapter, displayTimezone]);
907
+ const todayYear = today !== null ? adapter.getYear(today) : -1;
710
908
  const decadeStart = currentYear - currentYear % 12;
711
909
  const navigateDecade = react.useCallback(
712
910
  (direction) => {
713
- const newDate = adapter.addYears(viewMonth, direction * 12);
714
- ctx.setViewMonth(newDate);
911
+ ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
715
912
  },
716
913
  [adapter, viewMonth, ctx]
717
914
  );
718
915
  const handleYearSelect = react.useCallback(
719
- (year) => {
916
+ (indexInDecade) => {
917
+ const year = decadeStart + indexInDecade;
720
918
  const currentMonth = adapter.getMonth(viewMonth);
721
919
  const target = new Date(Date.UTC(year, currentMonth, 1)).toISOString();
722
920
  ctx.setViewMonth(target);
723
921
  ctx.setFocusedDate(target);
724
922
  onSelect?.();
725
923
  },
726
- [adapter, viewMonth, ctx, onSelect]
727
- );
728
- const years = react.useMemo(
729
- () => Array.from({ length: 12 }, (_, i) => {
730
- const year = decadeStart + i;
731
- return {
732
- value: year,
733
- isSelected: year === currentYear,
734
- isCurrent: year === todayYear
735
- };
736
- }),
737
- [decadeStart, currentYear, todayYear]
738
- );
924
+ [adapter, viewMonth, ctx, onSelect, decadeStart]
925
+ );
926
+ const { gridRef, focusedIndex, handleKeyDown } = useGridState({
927
+ initialIndex: currentYear - decadeStart,
928
+ onSelect: handleYearSelect,
929
+ onPageUp: () => navigateDecade(-1),
930
+ onPageDown: () => navigateDecade(1),
931
+ onEscape: ctx.close
932
+ });
739
933
  const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
740
934
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.root, ...props, children: [
741
935
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.header, children: [
@@ -764,30 +958,38 @@ function DatePickerYearGrid({
764
958
  /* @__PURE__ */ jsxRuntime.jsx(
765
959
  "div",
766
960
  {
961
+ ref: gridRef,
767
962
  role: "grid",
768
963
  "aria-label": rangeLabel,
769
964
  className: classNames?.grid,
770
965
  style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
771
- children: years.map((y) => {
772
- const yearClass = [
966
+ onKeyDown: handleKeyDown,
967
+ children: Array.from({ length: 12 }, (_, i) => {
968
+ const year = decadeStart + i;
969
+ const isSelected = year === currentYear;
970
+ const isCurrent = year === todayYear;
971
+ const isFocused = i === focusedIndex;
972
+ const cls = [
773
973
  classNames?.year,
774
- y.isSelected && classNames?.yearSelected,
775
- y.isCurrent && classNames?.yearCurrent
974
+ isSelected && classNames?.yearSelected,
975
+ isCurrent && classNames?.yearCurrent
776
976
  ].filter(Boolean).join(" ") || void 0;
777
977
  return /* @__PURE__ */ jsxRuntime.jsx(
778
978
  "button",
779
979
  {
780
980
  type: "button",
781
981
  role: "gridcell",
782
- "aria-selected": y.isSelected || void 0,
783
- "aria-current": y.isCurrent ? "date" : void 0,
784
- "data-selected": y.isSelected || void 0,
785
- "data-current": y.isCurrent || void 0,
786
- className: yearClass,
787
- onClick: () => handleYearSelect(y.value),
788
- children: y.value
982
+ tabIndex: isFocused ? 0 : -1,
983
+ "aria-selected": isSelected || void 0,
984
+ "aria-current": isCurrent ? "date" : void 0,
985
+ "data-selected": isSelected || void 0,
986
+ "data-current": isCurrent || void 0,
987
+ "data-focused": isFocused || void 0,
988
+ className: cls,
989
+ onClick: () => handleYearSelect(i),
990
+ children: year
789
991
  },
790
- y.value
992
+ i
791
993
  );
792
994
  })
793
995
  }
@@ -796,16 +998,7 @@ function DatePickerYearGrid({
796
998
  }
797
999
  function DatePickerPresets({ classNames, children, ...props }) {
798
1000
  const ctx = useDatePickerContext("DatePicker.Presets");
799
- return /* @__PURE__ */ jsxRuntime.jsx(
800
- "div",
801
- {
802
- role: "group",
803
- "aria-label": ctx.labels.popoverLabel,
804
- className: classNames?.root,
805
- ...props,
806
- children
807
- }
808
- );
1001
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "group", "aria-label": ctx.labels.popoverLabel, className: classNames?.root, ...props, children });
809
1002
  }
810
1003
  function resolveDatePreset(key, today, adapter) {
811
1004
  switch (key) {
@@ -859,11 +1052,7 @@ function DatePickerPreset({
859
1052
  if (directDate) {
860
1053
  target = directDate;
861
1054
  } else if (presetKey) {
862
- target = resolveDatePreset(
863
- presetKey,
864
- ctx.adapter.today(ctx.displayTimezone),
865
- ctx.adapter
866
- );
1055
+ target = resolveDatePreset(presetKey, ctx.adapter.today(ctx.displayTimezone), ctx.adapter);
867
1056
  } else {
868
1057
  return false;
869
1058
  }
@@ -873,8 +1062,7 @@ function DatePickerPreset({
873
1062
  "button",
874
1063
  {
875
1064
  type: "button",
876
- role: "option",
877
- "aria-selected": isActive,
1065
+ "aria-pressed": isActive,
878
1066
  "data-active": isActive || void 0,
879
1067
  disabled: ctx.isDisabled,
880
1068
  onClick: handleClick,
@@ -938,10 +1126,10 @@ function RangePickerRoot({
938
1126
  const [selectingTarget, setSelectingTarget] = react.useState("start");
939
1127
  const [hoverDate, setHoverDate] = react.useState(null);
940
1128
  const [viewMonth, setViewMonth] = react.useState(
941
- currentValue.start ?? adapter.today(displayTimezone)
1129
+ () => currentValue.start ?? adapter.today(displayTimezone)
942
1130
  );
943
1131
  const [focusedDate, setFocusedDate] = react.useState(
944
- currentValue.start ?? adapter.today(displayTimezone)
1132
+ () => currentValue.start ?? adapter.today(displayTimezone)
945
1133
  );
946
1134
  useChangeEffect(isOpen, onOpenChange);
947
1135
  const viewMonthStart = react.useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
@@ -1091,6 +1279,8 @@ var RangePickerInput = react.forwardRef(
1091
1279
  (e) => {
1092
1280
  if (e.key === "Escape") {
1093
1281
  ctx.close();
1282
+ } else if (e.key === "Enter" && ctx.isOpen) {
1283
+ e.preventDefault();
1094
1284
  } else if (e.key === "ArrowDown" && !ctx.isOpen) {
1095
1285
  e.preventDefault();
1096
1286
  ctx.open();
@@ -1127,15 +1317,17 @@ var RangePickerInput = react.forwardRef(
1127
1317
  );
1128
1318
  }
1129
1319
  );
1320
+ RangePickerInput.displayName = "RangePicker.Input";
1130
1321
  function RangePickerPopover({ children, ...props }) {
1131
1322
  const ctx = useRangePickerContext("RangePicker.Popover");
1132
1323
  const calendarId = `${ctx.pickerId}-calendar`;
1133
- const { floatingStyles, setFloatingRef } = usePopover({
1324
+ const { floatingStyles, setFloatingRef, isPositioned } = usePopover({
1134
1325
  isOpen: ctx.isOpen,
1135
1326
  close: ctx.close,
1136
1327
  referenceRef: ctx.referenceRef
1137
1328
  });
1138
1329
  if (!ctx.isOpen) return null;
1330
+ const { style: userStyle, ...rest } = props;
1139
1331
  return /* @__PURE__ */ jsxRuntime.jsx(
1140
1332
  "div",
1141
1333
  {
@@ -1144,8 +1336,12 @@ function RangePickerPopover({ children, ...props }) {
1144
1336
  role: "dialog",
1145
1337
  "aria-label": ctx.labels.popoverLabel,
1146
1338
  "aria-modal": "false",
1147
- style: floatingStyles,
1148
- ...props,
1339
+ ...rest,
1340
+ style: {
1341
+ ...userStyle,
1342
+ ...floatingStyles,
1343
+ visibility: isPositioned ? void 0 : "hidden"
1344
+ },
1149
1345
  children
1150
1346
  }
1151
1347
  );
@@ -1171,6 +1367,8 @@ var srOnly2 = {
1171
1367
  function RangePickerCalendar({
1172
1368
  classNames,
1173
1369
  selectionMode = "range",
1370
+ showWeekNumber = false,
1371
+ fixedWeeks = false,
1174
1372
  ...props
1175
1373
  }) {
1176
1374
  const ctx = useRangePickerContext("RangePicker.Calendar");
@@ -1188,23 +1386,36 @@ function RangePickerCalendar({
1188
1386
  displayTimezone
1189
1387
  } = ctx;
1190
1388
  const { locale } = ctx;
1191
- const weekdays = core.getWeekdayNames(locale, weekStartsOn);
1192
- const weeks = core.getCalendarDays(viewMonth, adapter, {
1193
- weekStartsOn,
1194
- focusedDate,
1195
- disabled,
1196
- range: value,
1197
- rangeHover: hoverDate,
1198
- timezone: displayTimezone
1199
- });
1389
+ const weekdays = react.useMemo(() => core.getWeekdayNames(locale, weekStartsOn), [locale, weekStartsOn]);
1390
+ const weeks = react.useMemo(
1391
+ () => core.getCalendarDays(viewMonth, adapter, {
1392
+ weekStartsOn,
1393
+ focusedDate,
1394
+ disabled,
1395
+ range: value,
1396
+ rangeHover: hoverDate,
1397
+ timezone: displayTimezone,
1398
+ fixedWeeks
1399
+ }),
1400
+ [
1401
+ viewMonth,
1402
+ adapter,
1403
+ weekStartsOn,
1404
+ focusedDate,
1405
+ disabled,
1406
+ value,
1407
+ hoverDate,
1408
+ displayTimezone,
1409
+ fixedWeeks
1410
+ ]
1411
+ );
1412
+ const thursdayIndex = weekStartsOn === 0 ? 4 : 3;
1200
1413
  const year = adapter.getYear(viewMonth);
1201
1414
  const month = adapter.getMonth(viewMonth);
1202
1415
  const title = core.formatMonthYear(year, month, locale);
1203
1416
  react.useEffect(() => {
1204
1417
  if (!ctx.isOpen || !gridRef.current) return;
1205
- const focusedButton = gridRef.current.querySelector(
1206
- '[data-focused="true"]'
1207
- );
1418
+ const focusedButton = gridRef.current.querySelector('[data-focused="true"]');
1208
1419
  focusedButton?.focus({ preventScroll: true });
1209
1420
  }, [focusedDate, ctx.isOpen]);
1210
1421
  const navigateMonth = react.useCallback(
@@ -1298,6 +1509,13 @@ function RangePickerCalendar({
1298
1509
  }
1299
1510
  if (newFocused) {
1300
1511
  e.preventDefault();
1512
+ const skipStep = e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "PageUp" || e.key === "Home" ? -1 : 1;
1513
+ let attempts = 0;
1514
+ while (core.isDateDisabled(newFocused, disabled, adapter) && attempts < 42) {
1515
+ newFocused = adapter.addDays(newFocused, skipStep);
1516
+ attempts++;
1517
+ }
1518
+ if (attempts >= 42) return;
1301
1519
  ctx.setFocusedDate(newFocused);
1302
1520
  if (!adapter.isSameMonth(newFocused, viewMonth)) {
1303
1521
  ctx.setViewMonth(newFocused);
@@ -1307,7 +1525,18 @@ function RangePickerCalendar({
1307
1525
  }
1308
1526
  }
1309
1527
  },
1310
- [adapter, focusedDate, viewMonth, weekStartsOn, disabled, ctx, selectionMode, selectingTarget, value.start, commitDay]
1528
+ [
1529
+ adapter,
1530
+ focusedDate,
1531
+ viewMonth,
1532
+ weekStartsOn,
1533
+ disabled,
1534
+ ctx,
1535
+ selectionMode,
1536
+ selectingTarget,
1537
+ value.start,
1538
+ commitDay
1539
+ ]
1311
1540
  );
1312
1541
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.root, ...props, onMouseLeave: handleMouseLeave, children: [
1313
1542
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.header, children: [
@@ -1339,63 +1568,90 @@ function RangePickerCalendar({
1339
1568
  ref: gridRef,
1340
1569
  role: "grid",
1341
1570
  "aria-label": title,
1342
- "aria-multiselectable": "true",
1571
+ "aria-rowcount": weeks.length + 1,
1572
+ "aria-colcount": 7,
1343
1573
  className: classNames?.grid,
1344
1574
  onKeyDown: handleKeyDown,
1345
1575
  children: [
1346
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { role: "row", children: weekdays.map((day) => /* @__PURE__ */ jsxRuntime.jsx(
1347
- "th",
1348
- {
1349
- role: "columnheader",
1350
- abbr: day.full,
1351
- scope: "col",
1352
- className: classNames?.weekdayHeader,
1353
- children: day.short
1354
- },
1355
- day.short
1356
- )) }) }),
1357
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxRuntime.jsx("tr", { role: "row", className: classNames?.gridRow, children: week.map((day) => {
1358
- const dayClasses = [
1359
- classNames?.day,
1360
- day.isRangeStart && classNames?.dayRangeStart,
1361
- day.isRangeEnd && classNames?.dayRangeEnd,
1362
- day.isInRange && classNames?.dayInRange,
1363
- day.isToday && classNames?.dayToday,
1364
- day.isDisabled && classNames?.dayDisabled,
1365
- !day.isCurrentMonth && classNames?.dayOutsideMonth
1366
- ].filter(Boolean).join(" ") || void 0;
1367
- const isSelected = selectionMode === "week" ? day.isRangeStart || day.isRangeEnd || day.isInRange : day.isRangeStart || day.isRangeEnd;
1368
- return /* @__PURE__ */ jsxRuntime.jsx(
1369
- "td",
1576
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
1577
+ showWeekNumber ? /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", "aria-hidden": "true", className: classNames?.weekNumberHeader, children: "#" }) : null,
1578
+ weekdays.map((day, colIndex) => /* @__PURE__ */ jsxRuntime.jsx(
1579
+ "th",
1370
1580
  {
1371
- role: "gridcell",
1372
- "aria-selected": isSelected || void 0,
1373
- "aria-disabled": day.isDisabled || void 0,
1374
- "aria-current": day.isToday ? "date" : void 0,
1375
- className: classNames?.gridCell,
1376
- children: /* @__PURE__ */ jsxRuntime.jsx(
1377
- "button",
1581
+ role: "columnheader",
1582
+ abbr: day.full,
1583
+ scope: "col",
1584
+ "aria-colindex": colIndex + 1,
1585
+ className: classNames?.weekdayHeader,
1586
+ children: day.short
1587
+ },
1588
+ day.short
1589
+ ))
1590
+ ] }) }),
1591
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxRuntime.jsxs(
1592
+ "tr",
1593
+ {
1594
+ role: "row",
1595
+ "aria-rowindex": weekIndex + 2,
1596
+ className: classNames?.gridRow,
1597
+ children: [
1598
+ showWeekNumber ? /* @__PURE__ */ jsxRuntime.jsx(
1599
+ "th",
1378
1600
  {
1379
- type: "button",
1380
- tabIndex: day.isFocused ? 0 : -1,
1381
- disabled: day.isDisabled,
1382
- "data-focused": day.isFocused || void 0,
1383
- "data-range-start": day.isRangeStart || void 0,
1384
- "data-range-end": day.isRangeEnd || void 0,
1385
- "data-in-range": day.isInRange || void 0,
1386
- "data-today": day.isToday || void 0,
1387
- "data-outside-month": !day.isCurrentMonth || void 0,
1388
- className: dayClasses,
1389
- onClick: () => handleDayClick(day),
1390
- onMouseEnter: () => handleDayMouseEnter(day),
1391
- "aria-label": safeFormatFullDate2(day.isoString, locale),
1392
- children: day.dayNumber
1601
+ scope: "row",
1602
+ "aria-hidden": "true",
1603
+ className: classNames?.weekNumber,
1604
+ "data-week-number": true,
1605
+ children: core.getISOWeekNumber(week[thursdayIndex].isoString)
1393
1606
  }
1394
- )
1395
- },
1396
- day.isoString
1397
- );
1398
- }) }, weekIndex)) })
1607
+ ) : null,
1608
+ week.map((day, colIndex) => {
1609
+ const dayClasses = [
1610
+ classNames?.day,
1611
+ day.isRangeStart && classNames?.dayRangeStart,
1612
+ day.isRangeEnd && classNames?.dayRangeEnd,
1613
+ day.isInRange && classNames?.dayInRange,
1614
+ day.isToday && classNames?.dayToday,
1615
+ day.isDisabled && classNames?.dayDisabled,
1616
+ !day.isCurrentMonth && classNames?.dayOutsideMonth
1617
+ ].filter(Boolean).join(" ") || void 0;
1618
+ const isSelected = selectionMode === "week" ? day.isRangeStart || day.isRangeEnd || day.isInRange : day.isRangeStart || day.isRangeEnd;
1619
+ return /* @__PURE__ */ jsxRuntime.jsx(
1620
+ "td",
1621
+ {
1622
+ role: "gridcell",
1623
+ "aria-colindex": colIndex + 1,
1624
+ "aria-selected": isSelected || void 0,
1625
+ "aria-disabled": day.isDisabled || void 0,
1626
+ "aria-current": day.isToday ? "date" : void 0,
1627
+ className: classNames?.gridCell,
1628
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1629
+ "button",
1630
+ {
1631
+ type: "button",
1632
+ tabIndex: day.isFocused ? 0 : -1,
1633
+ disabled: day.isDisabled,
1634
+ "data-focused": day.isFocused || void 0,
1635
+ "data-range-start": day.isRangeStart || void 0,
1636
+ "data-range-end": day.isRangeEnd || void 0,
1637
+ "data-in-range": day.isInRange || void 0,
1638
+ "data-today": day.isToday || void 0,
1639
+ "data-outside-month": !day.isCurrentMonth || void 0,
1640
+ className: dayClasses,
1641
+ onClick: () => handleDayClick(day),
1642
+ onMouseEnter: () => handleDayMouseEnter(day),
1643
+ "aria-label": safeFormatFullDate2(day.isoString, locale),
1644
+ children: day.dayNumber
1645
+ }
1646
+ )
1647
+ },
1648
+ day.isoString
1649
+ );
1650
+ })
1651
+ ]
1652
+ },
1653
+ weekIndex
1654
+ )) })
1399
1655
  ]
1400
1656
  }
1401
1657
  ),
@@ -1444,9 +1700,7 @@ function resolvePreset(key, today, adapter) {
1444
1700
  }
1445
1701
  case "thisYear": {
1446
1702
  const currentMonth = new Date(today).getUTCMonth();
1447
- const yearStart = adapter.startOfMonth(
1448
- adapter.addMonths(today, -currentMonth)
1449
- );
1703
+ const yearStart = adapter.startOfMonth(adapter.addMonths(today, -currentMonth));
1450
1704
  return { start: yearStart, end: today };
1451
1705
  }
1452
1706
  }
@@ -1459,41 +1713,33 @@ function RangePickerPreset({
1459
1713
  ...props
1460
1714
  }) {
1461
1715
  const ctx = useRangePickerContext("RangePicker.Preset");
1716
+ const resolved = react.useMemo(() => {
1717
+ if (directRange) return directRange;
1718
+ if (presetKey)
1719
+ return resolvePreset(presetKey, ctx.adapter.today(ctx.displayTimezone), ctx.adapter);
1720
+ return null;
1721
+ }, [directRange, presetKey, ctx.adapter, ctx.displayTimezone]);
1462
1722
  const handleClick = react.useCallback(
1463
1723
  (e) => {
1464
1724
  if (ctx.isDisabled || ctx.isReadOnly) return;
1465
- let resolved;
1466
- if (directRange) {
1467
- resolved = directRange;
1468
- } else if (presetKey) {
1469
- resolved = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
1470
- } else {
1471
- return;
1472
- }
1725
+ if (!resolved) return;
1473
1726
  ctx.setRange(resolved);
1474
1727
  ctx.close();
1475
1728
  onClick?.(e);
1476
1729
  },
1477
- [ctx, presetKey, directRange, onClick]
1730
+ [ctx, resolved, onClick]
1478
1731
  );
1479
- const isActive = (() => {
1480
- if (!ctx.value.start || !ctx.value.end) return false;
1481
- let target;
1482
- if (directRange) {
1483
- target = directRange;
1484
- } else if (presetKey) {
1485
- target = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
1486
- } else {
1732
+ const isActive = react.useMemo(() => {
1733
+ if (!ctx.value.start || !ctx.value.end || !resolved || !resolved.start || !resolved.end) {
1487
1734
  return false;
1488
1735
  }
1489
- return target.start !== null && target.end !== null && ctx.adapter.isSameDay(ctx.value.start, target.start) && ctx.adapter.isSameDay(ctx.value.end, target.end);
1490
- })();
1736
+ return ctx.adapter.isSameDay(ctx.value.start, resolved.start) && ctx.adapter.isSameDay(ctx.value.end, resolved.end);
1737
+ }, [ctx.value.start, ctx.value.end, ctx.adapter, resolved]);
1491
1738
  return /* @__PURE__ */ jsxRuntime.jsx(
1492
1739
  "button",
1493
1740
  {
1494
1741
  type: "button",
1495
- role: "option",
1496
- "aria-selected": isActive,
1742
+ "aria-pressed": isActive,
1497
1743
  "data-active": isActive || void 0,
1498
1744
  disabled: ctx.isDisabled,
1499
1745
  onClick: handleClick,
@@ -1526,9 +1772,6 @@ function useTimePickerContext(componentName) {
1526
1772
  }
1527
1773
  return context;
1528
1774
  }
1529
- function getDefaultIso() {
1530
- return core.DateFnsAdapter.today();
1531
- }
1532
1775
  function TimePickerRoot({
1533
1776
  value: controlledValue,
1534
1777
  defaultValue,
@@ -1539,6 +1782,7 @@ function TimePickerRoot({
1539
1782
  displayTimezone,
1540
1783
  disabled = false,
1541
1784
  readOnly = false,
1785
+ filterTime,
1542
1786
  labels: labelsProp,
1543
1787
  children
1544
1788
  }) {
@@ -1552,21 +1796,21 @@ function TimePickerRoot({
1552
1796
  defaultValue ?? null
1553
1797
  );
1554
1798
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1555
- const baseIso = currentValue ?? getDefaultIso();
1556
- const currentTime = react.useMemo(
1557
- () => displayTimezone ? core.getTimeInTimezone(baseIso, displayTimezone) : core.getTime(baseIso),
1558
- [baseIso, displayTimezone]
1559
- );
1799
+ const currentTime = react.useMemo(() => {
1800
+ if (!currentValue) return { hours: 0, minutes: 0, seconds: 0 };
1801
+ return displayTimezone ? core.getTimeInTimezone(currentValue, displayTimezone) : core.getTime(currentValue);
1802
+ }, [currentValue, displayTimezone]);
1560
1803
  const setTime = react.useCallback(
1561
1804
  (partial) => {
1562
1805
  if (disabled || readOnly) return;
1563
- const newIso = displayTimezone ? core.setTimeInTimezone(baseIso, partial, displayTimezone) : core.setTime(baseIso, partial);
1806
+ const base = currentValue ?? core.DateFnsAdapter.today(displayTimezone);
1807
+ const newIso = displayTimezone ? core.setTimeInTimezone(base, partial, displayTimezone) : core.setTime(base, partial);
1564
1808
  if (!isControlled) {
1565
1809
  setUncontrolledValue(newIso);
1566
1810
  }
1567
1811
  onChange?.(newIso);
1568
1812
  },
1569
- [disabled, readOnly, baseIso, isControlled, onChange, displayTimezone]
1813
+ [disabled, readOnly, currentValue, displayTimezone, isControlled, onChange]
1570
1814
  );
1571
1815
  const contextValue = react.useMemo(
1572
1816
  () => ({
@@ -1580,9 +1824,23 @@ function TimePickerRoot({
1580
1824
  isReadOnly: readOnly,
1581
1825
  currentTime,
1582
1826
  pickerId,
1583
- labels: mergedLabels
1827
+ labels: mergedLabels,
1828
+ filterTime
1584
1829
  }),
1585
- [currentValue, setTime, format, step, withSeconds, displayTimezone, disabled, readOnly, currentTime, pickerId, mergedLabels]
1830
+ [
1831
+ currentValue,
1832
+ setTime,
1833
+ format,
1834
+ step,
1835
+ withSeconds,
1836
+ displayTimezone,
1837
+ disabled,
1838
+ readOnly,
1839
+ currentTime,
1840
+ pickerId,
1841
+ mergedLabels,
1842
+ filterTime
1843
+ ]
1586
1844
  );
1587
1845
  return /* @__PURE__ */ jsxRuntime.jsx(TimePickerContext.Provider, { value: contextValue, children });
1588
1846
  }
@@ -1599,12 +1857,9 @@ var TimePickerInput = react.forwardRef(
1599
1857
  }
1600
1858
  setInputText(null);
1601
1859
  }, [inputText, ctx]);
1602
- const handleChange = react.useCallback(
1603
- (e) => {
1604
- setInputText(e.target.value);
1605
- },
1606
- []
1607
- );
1860
+ const handleChange = react.useCallback((e) => {
1861
+ setInputText(e.target.value);
1862
+ }, []);
1608
1863
  const handleBlur = react.useCallback(
1609
1864
  (e) => {
1610
1865
  commitInput();
@@ -1641,6 +1896,7 @@ var TimePickerInput = react.forwardRef(
1641
1896
  );
1642
1897
  }
1643
1898
  );
1899
+ TimePickerInput.displayName = "TimePicker.Input";
1644
1900
  function useListboxNavigation({
1645
1901
  items,
1646
1902
  onSelect,
@@ -1677,9 +1933,7 @@ function useListboxNavigation({
1677
1933
  onSelect(target);
1678
1934
  cancelAnimationFrame(rafIdRef.current);
1679
1935
  rafIdRef.current = requestAnimationFrame(() => {
1680
- const next = listRef.current?.querySelector(
1681
- '[data-selected="true"]'
1682
- );
1936
+ const next = listRef.current?.querySelector('[data-selected="true"]');
1683
1937
  next?.focus();
1684
1938
  });
1685
1939
  }
@@ -1690,17 +1944,41 @@ function useListboxNavigation({
1690
1944
  }
1691
1945
  function TimePickerHourList({ classNames, ...props }) {
1692
1946
  const ctx = useTimePickerContext("TimePicker.HourList");
1693
- const { format, currentTime, isDisabled, isReadOnly } = ctx;
1694
- const hours = core.generateHours(format);
1947
+ const { format, step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
1948
+ const hours = react.useMemo(() => core.generateHours(format), [format]);
1695
1949
  const selectedHourDisplay = format === "12h" ? core.to12Hour(currentTime.hours).hours12 : currentTime.hours;
1696
1950
  const currentPeriod = format === "12h" ? core.to12Hour(currentTime.hours).period : null;
1951
+ const fullyDisabledHours24 = react.useMemo(() => {
1952
+ if (!filterTime) return null;
1953
+ const disabled = /* @__PURE__ */ new Set();
1954
+ for (let h = 0; h < 24; h++) {
1955
+ let allRejected = true;
1956
+ for (let m = 0; m < 60; m += step) {
1957
+ if (!filterTime(h, m)) {
1958
+ allRejected = false;
1959
+ break;
1960
+ }
1961
+ }
1962
+ if (allRejected) disabled.add(h);
1963
+ }
1964
+ return disabled;
1965
+ }, [filterTime, step]);
1966
+ const isHourDisabled = react.useCallback(
1967
+ (hourDisplay) => {
1968
+ if (!fullyDisabledHours24) return false;
1969
+ const hours24 = format === "12h" && currentPeriod ? core.to24Hour(hourDisplay, currentPeriod) : hourDisplay;
1970
+ return fullyDisabledHours24.has(hours24);
1971
+ },
1972
+ [fullyDisabledHours24, format, currentPeriod]
1973
+ );
1697
1974
  const handleSelect = react.useCallback(
1698
1975
  (hourDisplay) => {
1699
1976
  if (isDisabled || isReadOnly) return;
1977
+ if (isHourDisabled(hourDisplay)) return;
1700
1978
  const hours24 = format === "12h" && currentPeriod ? core.to24Hour(hourDisplay, currentPeriod) : hourDisplay;
1701
1979
  ctx.setTime({ hours: hours24 });
1702
1980
  },
1703
- [format, currentPeriod, ctx, isDisabled, isReadOnly]
1981
+ [format, currentPeriod, ctx, isDisabled, isReadOnly, isHourDisabled]
1704
1982
  );
1705
1983
  const { listRef, handleKeyDown } = useListboxNavigation({
1706
1984
  items: hours,
@@ -1718,13 +1996,14 @@ function TimePickerHourList({ classNames, ...props }) {
1718
1996
  ...props,
1719
1997
  children: hours.map((hour) => {
1720
1998
  const isSelected = hour === selectedHourDisplay;
1999
+ const isHourFullyDisabled = isHourDisabled(hour);
1721
2000
  const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
1722
2001
  return /* @__PURE__ */ jsxRuntime.jsx(
1723
2002
  "li",
1724
2003
  {
1725
2004
  role: "option",
1726
2005
  "aria-selected": isSelected,
1727
- "aria-disabled": isDisabled || void 0,
2006
+ "aria-disabled": isDisabled || isHourFullyDisabled || void 0,
1728
2007
  "aria-label": ctx.labels.hourOption(hour),
1729
2008
  "data-selected": isSelected || void 0,
1730
2009
  tabIndex: isSelected ? 0 : -1,
@@ -1741,14 +2020,22 @@ function TimePickerHourList({ classNames, ...props }) {
1741
2020
  }
1742
2021
  function TimePickerMinuteList({ classNames, ...props }) {
1743
2022
  const ctx = useTimePickerContext("TimePicker.MinuteList");
1744
- const { step, currentTime, isDisabled, isReadOnly } = ctx;
1745
- const minutes = core.generateMinutes(step);
2023
+ const { step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
2024
+ const minutes = react.useMemo(() => core.generateMinutes(step), [step]);
2025
+ const isMinuteDisabled = react.useCallback(
2026
+ (minute) => {
2027
+ if (!filterTime) return false;
2028
+ return filterTime(currentTime.hours, minute);
2029
+ },
2030
+ [filterTime, currentTime.hours]
2031
+ );
1746
2032
  const handleSelect = react.useCallback(
1747
2033
  (minute) => {
1748
2034
  if (isDisabled || isReadOnly) return;
2035
+ if (isMinuteDisabled(minute)) return;
1749
2036
  ctx.setTime({ minutes: minute });
1750
2037
  },
1751
- [ctx, isDisabled, isReadOnly]
2038
+ [ctx, isDisabled, isReadOnly, isMinuteDisabled]
1752
2039
  );
1753
2040
  const { listRef, handleKeyDown } = useListboxNavigation({
1754
2041
  items: minutes,
@@ -1766,13 +2053,14 @@ function TimePickerMinuteList({ classNames, ...props }) {
1766
2053
  ...props,
1767
2054
  children: minutes.map((minute) => {
1768
2055
  const isSelected = minute === currentTime.minutes;
2056
+ const isMinuteFullyDisabled = isMinuteDisabled(minute);
1769
2057
  const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
1770
2058
  return /* @__PURE__ */ jsxRuntime.jsx(
1771
2059
  "li",
1772
2060
  {
1773
2061
  role: "option",
1774
2062
  "aria-selected": isSelected,
1775
- "aria-disabled": isDisabled || void 0,
2063
+ "aria-disabled": isDisabled || isMinuteFullyDisabled || void 0,
1776
2064
  "aria-label": ctx.labels.minuteOption(minute),
1777
2065
  "data-selected": isSelected || void 0,
1778
2066
  tabIndex: isSelected ? 0 : -1,
@@ -1789,37 +2077,87 @@ function TimePickerMinuteList({ classNames, ...props }) {
1789
2077
  }
1790
2078
  function TimePickerAmPmToggle({ classNames, ...props }) {
1791
2079
  const ctx = useTimePickerContext("TimePicker.AmPmToggle");
1792
- if (ctx.format !== "12h") return null;
1793
- const { period, hours12 } = core.to12Hour(ctx.currentTime.hours);
2080
+ const amRef = react.useRef(null);
2081
+ const pmRef = react.useRef(null);
1794
2082
  const setPeriod = react.useCallback(
1795
2083
  (newPeriod) => {
1796
2084
  if (ctx.isDisabled || ctx.isReadOnly) return;
2085
+ const { hours12 } = core.to12Hour(ctx.currentTime.hours);
1797
2086
  const newHours24 = core.to24Hour(hours12, newPeriod);
1798
2087
  ctx.setTime({ hours: newHours24 });
1799
2088
  },
1800
- [hours12, ctx]
2089
+ [ctx]
1801
2090
  );
2091
+ if (ctx.format !== "12h") return null;
2092
+ const { period } = core.to12Hour(ctx.currentTime.hours);
2093
+ const focusOther = (target) => {
2094
+ (target === "AM" ? amRef : pmRef).current?.focus();
2095
+ };
2096
+ const handleKeyDown = (e, target) => {
2097
+ switch (e.key) {
2098
+ case "ArrowRight":
2099
+ case "ArrowDown":
2100
+ case "ArrowLeft":
2101
+ case "ArrowUp": {
2102
+ e.preventDefault();
2103
+ const next = target === "AM" ? "PM" : "AM";
2104
+ setPeriod(next);
2105
+ focusOther(next);
2106
+ break;
2107
+ }
2108
+ case "Home": {
2109
+ e.preventDefault();
2110
+ setPeriod("AM");
2111
+ focusOther("AM");
2112
+ break;
2113
+ }
2114
+ case "End": {
2115
+ e.preventDefault();
2116
+ setPeriod("PM");
2117
+ focusOther("PM");
2118
+ break;
2119
+ }
2120
+ case " ":
2121
+ case "Enter": {
2122
+ e.preventDefault();
2123
+ setPeriod(target);
2124
+ break;
2125
+ }
2126
+ }
2127
+ };
1802
2128
  const renderButton = (target) => {
1803
2129
  const isSelected = period === target;
1804
2130
  const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
1805
2131
  return /* @__PURE__ */ jsxRuntime.jsx(
1806
2132
  "button",
1807
2133
  {
2134
+ ref: target === "AM" ? amRef : pmRef,
1808
2135
  type: "button",
1809
2136
  role: "radio",
1810
2137
  "aria-checked": isSelected,
2138
+ tabIndex: isSelected ? 0 : -1,
1811
2139
  "data-selected": isSelected || void 0,
1812
2140
  disabled: ctx.isDisabled,
1813
2141
  className: optionClass,
1814
2142
  onClick: () => setPeriod(target),
2143
+ onKeyDown: (e) => handleKeyDown(e, target),
1815
2144
  children: target
1816
2145
  }
1817
2146
  );
1818
2147
  };
1819
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "radiogroup", "aria-label": ctx.labels.amPmToggle, className: classNames?.root, ...props, children: [
1820
- renderButton("AM"),
1821
- renderButton("PM")
1822
- ] });
2148
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2149
+ "div",
2150
+ {
2151
+ role: "radiogroup",
2152
+ "aria-label": ctx.labels.amPmToggle,
2153
+ className: classNames?.root,
2154
+ ...props,
2155
+ children: [
2156
+ renderButton("AM"),
2157
+ renderButton("PM")
2158
+ ]
2159
+ }
2160
+ );
1823
2161
  }
1824
2162
 
1825
2163
  // src/components/TimePicker/index.ts
@@ -1829,9 +2167,6 @@ var TimePicker = Object.assign(TimePickerRoot, {
1829
2167
  MinuteList: TimePickerMinuteList,
1830
2168
  AmPmToggle: TimePickerAmPmToggle
1831
2169
  });
1832
- function getDefaultIso2() {
1833
- return core.DateFnsAdapter.today();
1834
- }
1835
2170
  function DateTimePickerRoot({
1836
2171
  value: controlledValue,
1837
2172
  defaultValue,
@@ -1840,6 +2175,8 @@ function DateTimePickerRoot({
1840
2175
  onCalendarNavigate,
1841
2176
  format = "24h",
1842
2177
  step = 1,
2178
+ withSeconds = false,
2179
+ filterTime,
1843
2180
  disabled = false,
1844
2181
  readOnly = false,
1845
2182
  weekStartsOn = 0,
@@ -1867,10 +2204,10 @@ function DateTimePickerRoot({
1867
2204
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1868
2205
  const [isOpen, setIsOpen] = react.useState(false);
1869
2206
  const [viewMonth, setViewMonth] = react.useState(
1870
- currentValue ?? adapter.today(displayTimezone)
2207
+ () => currentValue ?? adapter.today(displayTimezone)
1871
2208
  );
1872
2209
  const [focusedDate, setFocusedDate] = react.useState(
1873
- currentValue ?? adapter.today(displayTimezone)
2210
+ () => currentValue ?? adapter.today(displayTimezone)
1874
2211
  );
1875
2212
  useChangeEffect(isOpen, onOpenChange);
1876
2213
  const viewMonthStart = react.useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
@@ -1880,11 +2217,10 @@ function DateTimePickerRoot({
1880
2217
  () => Array.isArray(disabled) ? disabled : [],
1881
2218
  [disabled]
1882
2219
  );
1883
- const baseIso = currentValue ?? getDefaultIso2();
1884
- const currentTime = react.useMemo(
1885
- () => displayTimezone ? core.getTimeInTimezone(baseIso, displayTimezone) : core.getTime(baseIso),
1886
- [baseIso, displayTimezone]
1887
- );
2220
+ const currentTime = react.useMemo(() => {
2221
+ if (!currentValue) return { hours: 0, minutes: 0, seconds: 0 };
2222
+ return displayTimezone ? core.getTimeInTimezone(currentValue, displayTimezone) : core.getTime(currentValue);
2223
+ }, [currentValue, displayTimezone]);
1888
2224
  const updateValue = react.useCallback(
1889
2225
  (next) => {
1890
2226
  if (isDisabled || readOnly) return;
@@ -1910,11 +2246,11 @@ function DateTimePickerRoot({
1910
2246
  );
1911
2247
  const setTime = react.useCallback(
1912
2248
  (partial) => {
1913
- const base = currentValue ?? getDefaultIso2();
2249
+ const base = currentValue ?? adapter.today(displayTimezone);
1914
2250
  const merged = displayTimezone ? core.setTimeInTimezone(base, partial, displayTimezone) : core.setTime(base, partial);
1915
2251
  updateValue(merged);
1916
2252
  },
1917
- [currentValue, updateValue, displayTimezone]
2253
+ [currentValue, updateValue, displayTimezone, adapter]
1918
2254
  );
1919
2255
  const open = react.useCallback(() => {
1920
2256
  if (isDisabled || readOnly) return;
@@ -1981,15 +2317,29 @@ function DateTimePickerRoot({
1981
2317
  setTime,
1982
2318
  format,
1983
2319
  step,
1984
- withSeconds: false,
2320
+ withSeconds,
1985
2321
  displayTimezone,
1986
2322
  isDisabled,
1987
2323
  isReadOnly: readOnly,
1988
2324
  currentTime,
1989
2325
  pickerId,
1990
- labels: mergedTimeLabels
2326
+ labels: mergedTimeLabels,
2327
+ filterTime
1991
2328
  }),
1992
- [currentValue, setTime, format, step, displayTimezone, isDisabled, readOnly, currentTime, pickerId, mergedTimeLabels]
2329
+ [
2330
+ currentValue,
2331
+ setTime,
2332
+ format,
2333
+ step,
2334
+ withSeconds,
2335
+ displayTimezone,
2336
+ isDisabled,
2337
+ readOnly,
2338
+ currentTime,
2339
+ pickerId,
2340
+ mergedTimeLabels,
2341
+ filterTime
2342
+ ]
1993
2343
  );
1994
2344
  return /* @__PURE__ */ jsxRuntime.jsx(DatePickerContext.Provider, { value: dateContext, children: /* @__PURE__ */ jsxRuntime.jsx(TimePickerContext.Provider, { value: timeContext, children }) });
1995
2345
  }
@@ -2017,6 +2367,8 @@ var DateTimePickerInput = react.forwardRef(
2017
2367
  (e) => {
2018
2368
  if (e.key === "Escape") {
2019
2369
  ctx.close();
2370
+ } else if (e.key === "Enter" && ctx.isOpen) {
2371
+ e.preventDefault();
2020
2372
  } else if (e.key === "ArrowDown" && !ctx.isOpen) {
2021
2373
  e.preventDefault();
2022
2374
  ctx.open();
@@ -2052,6 +2404,7 @@ var DateTimePickerInput = react.forwardRef(
2052
2404
  );
2053
2405
  }
2054
2406
  );
2407
+ DateTimePickerInput.displayName = "DateTimePicker.Input";
2055
2408
 
2056
2409
  // src/components/DateTimePicker/index.ts
2057
2410
  var DateTimePicker = Object.assign(DateTimePickerRoot, {
@@ -2068,12 +2421,9 @@ function MonthPickerRoot(props) {
2068
2421
  const displayFormat = props.displayFormat ?? "yyyy-MM";
2069
2422
  return /* @__PURE__ */ jsxRuntime.jsx(DatePickerRoot, { ...props, displayFormat });
2070
2423
  }
2071
- function MonthPickerGrid({
2072
- classNames,
2073
- ...props
2074
- }) {
2424
+ function MonthPickerGrid({ classNames, ...props }) {
2075
2425
  const ctx = useDatePickerContext("MonthPicker.Grid");
2076
- const { adapter, viewMonth, locale, value, displayTimezone, labels } = ctx;
2426
+ const { adapter, viewMonth, locale, value, displayTimezone, labels, disabled } = ctx;
2077
2427
  const currentYear = adapter.getYear(viewMonth);
2078
2428
  const [valueYear, valueMonthZeroBased] = react.useMemo(() => {
2079
2429
  if (!value) return [null, null];
@@ -2084,9 +2434,19 @@ function MonthPickerGrid({
2084
2434
  return [null, null];
2085
2435
  }
2086
2436
  }, [value, adapter, displayTimezone]);
2087
- const today = adapter.today(displayTimezone);
2088
- const todayYear = adapter.getYear(today);
2089
- const todayMonth = adapter.getMonth(today);
2437
+ const [today, setToday] = react.useState(null);
2438
+ react.useEffect(() => {
2439
+ setToday(adapter.today(displayTimezone));
2440
+ }, [adapter, displayTimezone]);
2441
+ const todayYear = today !== null ? adapter.getYear(today) : -1;
2442
+ const todayMonth = today !== null ? adapter.getMonth(today) : -1;
2443
+ const monthDisabledFlags = react.useMemo(
2444
+ () => Array.from({ length: 12 }, (_, i) => {
2445
+ const monthStart = new Date(Date.UTC(currentYear, i, 1)).toISOString();
2446
+ return isRangeFullyDisabled(monthStart, adapter.endOfMonth(monthStart), disabled, adapter);
2447
+ }),
2448
+ [currentYear, disabled, adapter]
2449
+ );
2090
2450
  const navigateYear = react.useCallback(
2091
2451
  (direction) => {
2092
2452
  ctx.setViewMonth(adapter.addYears(viewMonth, direction));
@@ -2095,19 +2455,23 @@ function MonthPickerGrid({
2095
2455
  );
2096
2456
  const handleMonthSelect = react.useCallback(
2097
2457
  (monthIndex) => {
2098
- const target = new Date(
2099
- Date.UTC(currentYear, monthIndex, 1)
2100
- ).toISOString();
2458
+ if (monthDisabledFlags[monthIndex]) return;
2459
+ const target = new Date(Date.UTC(currentYear, monthIndex, 1)).toISOString();
2101
2460
  ctx.selectDate(target);
2102
2461
  },
2103
- [currentYear, ctx]
2104
- );
2105
- const months = Array.from({ length: 12 }, (_, i) => ({
2106
- index: i,
2107
- name: core.getMonthName(i, locale),
2108
- isSelected: valueYear === currentYear && valueMonthZeroBased === i,
2109
- isCurrent: todayYear === currentYear && todayMonth === i
2110
- }));
2462
+ [currentYear, ctx, monthDisabledFlags]
2463
+ );
2464
+ const naturalIndex = valueYear === currentYear && valueMonthZeroBased !== null ? valueMonthZeroBased : adapter.getMonth(viewMonth);
2465
+ const firstEnabled = monthDisabledFlags.findIndex((d) => !d);
2466
+ const initialIndex = monthDisabledFlags[naturalIndex] ? firstEnabled === -1 ? naturalIndex : firstEnabled : naturalIndex;
2467
+ const { gridRef, focusedIndex, handleKeyDown } = useGridState({
2468
+ initialIndex,
2469
+ disabledFlags: monthDisabledFlags,
2470
+ onSelect: handleMonthSelect,
2471
+ onPageUp: () => navigateYear(-1),
2472
+ onPageDown: () => navigateYear(1),
2473
+ onEscape: ctx.close
2474
+ });
2111
2475
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.root, ...props, children: [
2112
2476
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.header, children: [
2113
2477
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2135,35 +2499,47 @@ function MonthPickerGrid({
2135
2499
  /* @__PURE__ */ jsxRuntime.jsx(
2136
2500
  "div",
2137
2501
  {
2502
+ ref: gridRef,
2138
2503
  role: "grid",
2139
2504
  "aria-label": `${currentYear} months`,
2140
2505
  className: classNames?.grid,
2506
+ onKeyDown: handleKeyDown,
2141
2507
  children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx(
2142
2508
  "div",
2143
2509
  {
2144
2510
  role: "row",
2145
2511
  className: classNames?.gridRow,
2146
2512
  style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
2147
- children: months.slice(rowIndex * 3, rowIndex * 3 + 3).map((m) => {
2148
- const monthClass = [
2513
+ children: Array.from({ length: 3 }, (_2, col) => {
2514
+ const i = rowIndex * 3 + col;
2515
+ const isSelected = valueYear === currentYear && valueMonthZeroBased === i;
2516
+ const isCurrent = todayYear === currentYear && todayMonth === i;
2517
+ const isFocused = i === focusedIndex;
2518
+ const isDisabled = monthDisabledFlags[i] ?? false;
2519
+ const cls = [
2149
2520
  classNames?.month,
2150
- m.isSelected && classNames?.monthSelected,
2151
- m.isCurrent && classNames?.monthCurrent
2521
+ isSelected && classNames?.monthSelected,
2522
+ isCurrent && classNames?.monthCurrent,
2523
+ isDisabled && classNames?.monthDisabled
2152
2524
  ].filter(Boolean).join(" ") || void 0;
2153
2525
  return /* @__PURE__ */ jsxRuntime.jsx(
2154
2526
  "button",
2155
2527
  {
2156
2528
  type: "button",
2157
2529
  role: "gridcell",
2158
- "aria-selected": m.isSelected || void 0,
2159
- "aria-current": m.isCurrent ? "date" : void 0,
2160
- "data-selected": m.isSelected || void 0,
2161
- "data-current": m.isCurrent || void 0,
2162
- className: monthClass,
2163
- onClick: () => handleMonthSelect(m.index),
2164
- children: m.name
2530
+ tabIndex: isFocused ? 0 : -1,
2531
+ disabled: isDisabled,
2532
+ "aria-selected": isSelected || void 0,
2533
+ "aria-disabled": isDisabled || void 0,
2534
+ "aria-current": isCurrent ? "date" : void 0,
2535
+ "data-selected": isSelected || void 0,
2536
+ "data-current": isCurrent || void 0,
2537
+ "data-focused": isFocused || void 0,
2538
+ className: cls,
2539
+ onClick: () => handleMonthSelect(i),
2540
+ children: core.getMonthName(i, locale)
2165
2541
  },
2166
- m.index
2542
+ i
2167
2543
  );
2168
2544
  })
2169
2545
  },
@@ -2187,7 +2563,7 @@ function YearPickerRoot(props) {
2187
2563
  }
2188
2564
  function YearPickerGrid({ classNames, ...props }) {
2189
2565
  const ctx = useDatePickerContext("YearPicker.Grid");
2190
- const { adapter, viewMonth, value, displayTimezone, labels } = ctx;
2566
+ const { adapter, viewMonth, value, displayTimezone, labels, disabled } = ctx;
2191
2567
  const currentYear = adapter.getYear(viewMonth);
2192
2568
  const decadeStart = currentYear - currentYear % 12;
2193
2569
  const valueYear = react.useMemo(() => {
@@ -2198,7 +2574,20 @@ function YearPickerGrid({ classNames, ...props }) {
2198
2574
  return null;
2199
2575
  }
2200
2576
  }, [value, adapter, displayTimezone]);
2201
- const todayYear = adapter.getYear(adapter.today(displayTimezone));
2577
+ const [today, setToday] = react.useState(null);
2578
+ react.useEffect(() => {
2579
+ setToday(adapter.today(displayTimezone));
2580
+ }, [adapter, displayTimezone]);
2581
+ const todayYear = today !== null ? adapter.getYear(today) : -1;
2582
+ const yearDisabledFlags = react.useMemo(
2583
+ () => Array.from({ length: 12 }, (_, i) => {
2584
+ const year = decadeStart + i;
2585
+ const yearStart = new Date(Date.UTC(year, 0, 1)).toISOString();
2586
+ const yearEnd = new Date(Date.UTC(year, 11, 31, 23, 59, 59, 999)).toISOString();
2587
+ return isRangeFullyDisabled(yearStart, yearEnd, disabled, adapter);
2588
+ }),
2589
+ [decadeStart, disabled, adapter]
2590
+ );
2202
2591
  const navigateDecade = react.useCallback(
2203
2592
  (direction) => {
2204
2593
  ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
@@ -2206,19 +2595,24 @@ function YearPickerGrid({ classNames, ...props }) {
2206
2595
  [adapter, viewMonth, ctx]
2207
2596
  );
2208
2597
  const handleYearSelect = react.useCallback(
2209
- (year) => {
2598
+ (indexInDecade) => {
2599
+ if (yearDisabledFlags[indexInDecade]) return;
2600
+ const year = decadeStart + indexInDecade;
2210
2601
  const target = new Date(Date.UTC(year, 0, 1)).toISOString();
2211
2602
  ctx.selectDate(target);
2212
2603
  },
2213
- [ctx]
2214
- );
2215
- const years = Array.from({ length: 12 }, (_, i) => {
2216
- const year = decadeStart + i;
2217
- return {
2218
- value: year,
2219
- isSelected: year === valueYear,
2220
- isCurrent: year === todayYear
2221
- };
2604
+ [ctx, decadeStart, yearDisabledFlags]
2605
+ );
2606
+ const naturalIndex = valueYear !== null && valueYear >= decadeStart && valueYear <= decadeStart + 11 ? valueYear - decadeStart : currentYear - decadeStart;
2607
+ const firstEnabled = yearDisabledFlags.findIndex((d) => !d);
2608
+ const initialIndex = yearDisabledFlags[naturalIndex] ? firstEnabled === -1 ? naturalIndex : firstEnabled : naturalIndex;
2609
+ const { gridRef, focusedIndex, handleKeyDown } = useGridState({
2610
+ initialIndex,
2611
+ disabledFlags: yearDisabledFlags,
2612
+ onSelect: handleYearSelect,
2613
+ onPageUp: () => navigateDecade(-1),
2614
+ onPageDown: () => navigateDecade(1),
2615
+ onEscape: ctx.close
2222
2616
  });
2223
2617
  const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
2224
2618
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.root, ...props, children: [
@@ -2248,35 +2642,48 @@ function YearPickerGrid({ classNames, ...props }) {
2248
2642
  /* @__PURE__ */ jsxRuntime.jsx(
2249
2643
  "div",
2250
2644
  {
2645
+ ref: gridRef,
2251
2646
  role: "grid",
2252
2647
  "aria-label": rangeLabel,
2253
2648
  className: classNames?.grid,
2649
+ onKeyDown: handleKeyDown,
2254
2650
  children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx(
2255
2651
  "div",
2256
2652
  {
2257
2653
  role: "row",
2258
2654
  className: classNames?.gridRow,
2259
2655
  style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
2260
- children: years.slice(rowIndex * 3, rowIndex * 3 + 3).map((y) => {
2261
- const yearClass = [
2656
+ children: Array.from({ length: 3 }, (_2, col) => {
2657
+ const i = rowIndex * 3 + col;
2658
+ const year = decadeStart + i;
2659
+ const isSelected = year === valueYear;
2660
+ const isCurrent = year === todayYear;
2661
+ const isFocused = i === focusedIndex;
2662
+ const isDisabled = yearDisabledFlags[i] ?? false;
2663
+ const cls = [
2262
2664
  classNames?.year,
2263
- y.isSelected && classNames?.yearSelected,
2264
- y.isCurrent && classNames?.yearCurrent
2665
+ isSelected && classNames?.yearSelected,
2666
+ isCurrent && classNames?.yearCurrent,
2667
+ isDisabled && classNames?.yearDisabled
2265
2668
  ].filter(Boolean).join(" ") || void 0;
2266
2669
  return /* @__PURE__ */ jsxRuntime.jsx(
2267
2670
  "button",
2268
2671
  {
2269
2672
  type: "button",
2270
2673
  role: "gridcell",
2271
- "aria-selected": y.isSelected || void 0,
2272
- "aria-current": y.isCurrent ? "date" : void 0,
2273
- "data-selected": y.isSelected || void 0,
2274
- "data-current": y.isCurrent || void 0,
2275
- className: yearClass,
2276
- onClick: () => handleYearSelect(y.value),
2277
- children: y.value
2674
+ tabIndex: isFocused ? 0 : -1,
2675
+ disabled: isDisabled,
2676
+ "aria-selected": isSelected || void 0,
2677
+ "aria-disabled": isDisabled || void 0,
2678
+ "aria-current": isCurrent ? "date" : void 0,
2679
+ "data-selected": isSelected || void 0,
2680
+ "data-current": isCurrent || void 0,
2681
+ "data-focused": isFocused || void 0,
2682
+ className: cls,
2683
+ onClick: () => handleYearSelect(i),
2684
+ children: year
2278
2685
  },
2279
- y.value
2686
+ i
2280
2687
  );
2281
2688
  })
2282
2689
  },
@@ -2504,7 +2911,7 @@ function useRangePicker(options = {}) {
2504
2911
  adapter
2505
2912
  };
2506
2913
  }
2507
- function getDefaultIso3() {
2914
+ function getDefaultIso() {
2508
2915
  return core.DateFnsAdapter.today();
2509
2916
  }
2510
2917
  function useTimePicker(options = {}) {
@@ -2522,7 +2929,7 @@ function useTimePicker(options = {}) {
2522
2929
  defaultValue ?? null
2523
2930
  );
2524
2931
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
2525
- const baseIso = currentValue ?? getDefaultIso3();
2932
+ const baseIso = currentValue ?? getDefaultIso();
2526
2933
  const currentTime = react.useMemo(
2527
2934
  () => displayTimezone ? core.getTimeInTimezone(baseIso, displayTimezone) : core.getTime(baseIso),
2528
2935
  [baseIso, displayTimezone]
@@ -2546,14 +2953,8 @@ function useTimePicker(options = {}) {
2546
2953
  },
2547
2954
  [format, period, setTime]
2548
2955
  );
2549
- const setMinute = react.useCallback(
2550
- (minute) => setTime({ minutes: minute }),
2551
- [setTime]
2552
- );
2553
- const setSecond = react.useCallback(
2554
- (second) => setTime({ seconds: second }),
2555
- [setTime]
2556
- );
2956
+ const setMinute = react.useCallback((minute) => setTime({ minutes: minute }), [setTime]);
2957
+ const setSecond = react.useCallback((second) => setTime({ seconds: second }), [setTime]);
2557
2958
  const setPeriod = react.useCallback(
2558
2959
  (newPeriod) => {
2559
2960
  if (format !== "12h") return;
@@ -2579,6 +2980,22 @@ function useTimePicker(options = {}) {
2579
2980
  };
2580
2981
  }
2581
2982
 
2983
+ Object.defineProperty(exports, "DEFAULT_DATEPICKER_LABELS", {
2984
+ enumerable: true,
2985
+ get: function () { return core.DEFAULT_DATEPICKER_LABELS; }
2986
+ });
2987
+ Object.defineProperty(exports, "DEFAULT_DATETIMEPICKER_LABELS", {
2988
+ enumerable: true,
2989
+ get: function () { return core.DEFAULT_DATETIMEPICKER_LABELS; }
2990
+ });
2991
+ Object.defineProperty(exports, "DEFAULT_RANGEPICKER_LABELS", {
2992
+ enumerable: true,
2993
+ get: function () { return core.DEFAULT_RANGEPICKER_LABELS; }
2994
+ });
2995
+ Object.defineProperty(exports, "DEFAULT_TIMEPICKER_LABELS", {
2996
+ enumerable: true,
2997
+ get: function () { return core.DEFAULT_TIMEPICKER_LABELS; }
2998
+ });
2582
2999
  Object.defineProperty(exports, "DateFnsAdapter", {
2583
3000
  enumerable: true,
2584
3001
  get: function () { return core.DateFnsAdapter; }