@kalyx/react 1.0.0-rc.4 → 1.0.0-rc.6
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/CHANGELOG.md +83 -0
- package/README.md +2 -2
- package/dist/index.cjs +285 -126
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -2
- package/dist/index.d.ts +20 -2
- package/dist/index.js +285 -126
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -692,9 +692,18 @@ interface MonthPickerGridProps extends Omit<HTMLAttributes<HTMLDivElement>, 'rol
|
|
|
692
692
|
* Unlike `DatePicker.MonthGrid` (drilldown), this component commits the month selection
|
|
693
693
|
* via `ctx.selectDate`, emitting the month-start ISO string.
|
|
694
694
|
*
|
|
695
|
+
* Disabled state: a month is marked unselectable when every day in it is
|
|
696
|
+
* excluded by a `before`/`after` rule on the `disabled` prop. The cell is
|
|
697
|
+
* rendered with `disabled` + `aria-disabled` + the `monthDisabled` className,
|
|
698
|
+
* and keyboard navigation skips it.
|
|
699
|
+
*
|
|
695
700
|
* @example
|
|
696
701
|
* ```tsx
|
|
697
|
-
* <MonthPicker
|
|
702
|
+
* <MonthPicker
|
|
703
|
+
* value={month}
|
|
704
|
+
* onChange={setMonth}
|
|
705
|
+
* disabled={[{ before: '2026-04-01T00:00:00.000Z' }]}
|
|
706
|
+
* >
|
|
698
707
|
* <MonthPicker.Input />
|
|
699
708
|
* <MonthPicker.Popover>
|
|
700
709
|
* <MonthPicker.Grid />
|
|
@@ -756,9 +765,18 @@ interface YearPickerGridProps extends Omit<HTMLAttributes<HTMLDivElement>, 'role
|
|
|
756
765
|
* Unlike `DatePicker.YearGrid` (drilldown), this component commits the year selection via
|
|
757
766
|
* `ctx.selectDate`, emitting the year-start ISO string (Jan 1 at UTC midnight).
|
|
758
767
|
*
|
|
768
|
+
* Disabled state: a year is marked unselectable when every day in it is
|
|
769
|
+
* excluded by a `before`/`after` rule on the `disabled` prop. The cell is
|
|
770
|
+
* rendered with `disabled` + `aria-disabled` + the `yearDisabled` className,
|
|
771
|
+
* and keyboard navigation skips it.
|
|
772
|
+
*
|
|
759
773
|
* @example
|
|
760
774
|
* ```tsx
|
|
761
|
-
* <YearPicker
|
|
775
|
+
* <YearPicker
|
|
776
|
+
* value={year}
|
|
777
|
+
* onChange={setYear}
|
|
778
|
+
* disabled={[{ before: '2024-01-01T00:00:00.000Z' }]}
|
|
779
|
+
* >
|
|
762
780
|
* <YearPicker.Input />
|
|
763
781
|
* <YearPicker.Popover>
|
|
764
782
|
* <YearPicker.Grid />
|
package/dist/index.d.ts
CHANGED
|
@@ -692,9 +692,18 @@ interface MonthPickerGridProps extends Omit<HTMLAttributes<HTMLDivElement>, 'rol
|
|
|
692
692
|
* Unlike `DatePicker.MonthGrid` (drilldown), this component commits the month selection
|
|
693
693
|
* via `ctx.selectDate`, emitting the month-start ISO string.
|
|
694
694
|
*
|
|
695
|
+
* Disabled state: a month is marked unselectable when every day in it is
|
|
696
|
+
* excluded by a `before`/`after` rule on the `disabled` prop. The cell is
|
|
697
|
+
* rendered with `disabled` + `aria-disabled` + the `monthDisabled` className,
|
|
698
|
+
* and keyboard navigation skips it.
|
|
699
|
+
*
|
|
695
700
|
* @example
|
|
696
701
|
* ```tsx
|
|
697
|
-
* <MonthPicker
|
|
702
|
+
* <MonthPicker
|
|
703
|
+
* value={month}
|
|
704
|
+
* onChange={setMonth}
|
|
705
|
+
* disabled={[{ before: '2026-04-01T00:00:00.000Z' }]}
|
|
706
|
+
* >
|
|
698
707
|
* <MonthPicker.Input />
|
|
699
708
|
* <MonthPicker.Popover>
|
|
700
709
|
* <MonthPicker.Grid />
|
|
@@ -756,9 +765,18 @@ interface YearPickerGridProps extends Omit<HTMLAttributes<HTMLDivElement>, 'role
|
|
|
756
765
|
* Unlike `DatePicker.YearGrid` (drilldown), this component commits the year selection via
|
|
757
766
|
* `ctx.selectDate`, emitting the year-start ISO string (Jan 1 at UTC midnight).
|
|
758
767
|
*
|
|
768
|
+
* Disabled state: a year is marked unselectable when every day in it is
|
|
769
|
+
* excluded by a `before`/`after` rule on the `disabled` prop. The cell is
|
|
770
|
+
* rendered with `disabled` + `aria-disabled` + the `yearDisabled` className,
|
|
771
|
+
* and keyboard navigation skips it.
|
|
772
|
+
*
|
|
759
773
|
* @example
|
|
760
774
|
* ```tsx
|
|
761
|
-
* <YearPicker
|
|
775
|
+
* <YearPicker
|
|
776
|
+
* value={year}
|
|
777
|
+
* onChange={setYear}
|
|
778
|
+
* disabled={[{ before: '2024-01-01T00:00:00.000Z' }]}
|
|
779
|
+
* >
|
|
762
780
|
* <YearPicker.Input />
|
|
763
781
|
* <YearPicker.Popover>
|
|
764
782
|
* <YearPicker.Grid />
|
package/dist/index.js
CHANGED
|
@@ -685,6 +685,86 @@ function DatePickerCalendar({
|
|
|
685
685
|
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: srOnly, children: announcement })
|
|
686
686
|
] });
|
|
687
687
|
}
|
|
688
|
+
function isRangeFullyDisabled(start, end, rules, adapter) {
|
|
689
|
+
for (const rule of rules) {
|
|
690
|
+
if ("before" in rule && adapter.isBefore(end, rule.before)) return true;
|
|
691
|
+
if ("after" in rule && adapter.isAfter(start, rule.after)) return true;
|
|
692
|
+
}
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
function useGridState(opts) {
|
|
696
|
+
const { initialIndex, disabledFlags, onSelect, onPageUp, onPageDown, onEscape } = opts;
|
|
697
|
+
const gridRef = useRef(null);
|
|
698
|
+
const [focusedIndex, setFocusedIndex] = useState(initialIndex);
|
|
699
|
+
const handleKeyDown = (e) => {
|
|
700
|
+
let next = null;
|
|
701
|
+
let step = 1;
|
|
702
|
+
switch (e.key) {
|
|
703
|
+
case "ArrowLeft":
|
|
704
|
+
next = Math.max(0, focusedIndex - 1);
|
|
705
|
+
step = -1;
|
|
706
|
+
break;
|
|
707
|
+
case "ArrowRight":
|
|
708
|
+
next = Math.min(11, focusedIndex + 1);
|
|
709
|
+
break;
|
|
710
|
+
case "ArrowUp":
|
|
711
|
+
next = Math.max(0, focusedIndex - 3);
|
|
712
|
+
step = -1;
|
|
713
|
+
break;
|
|
714
|
+
case "ArrowDown":
|
|
715
|
+
next = Math.min(11, focusedIndex + 3);
|
|
716
|
+
break;
|
|
717
|
+
case "Home":
|
|
718
|
+
next = focusedIndex - focusedIndex % 3;
|
|
719
|
+
step = -1;
|
|
720
|
+
break;
|
|
721
|
+
case "End":
|
|
722
|
+
next = focusedIndex - focusedIndex % 3 + 2;
|
|
723
|
+
break;
|
|
724
|
+
case "PageUp":
|
|
725
|
+
e.preventDefault();
|
|
726
|
+
onPageUp();
|
|
727
|
+
return;
|
|
728
|
+
case "PageDown":
|
|
729
|
+
e.preventDefault();
|
|
730
|
+
onPageDown();
|
|
731
|
+
return;
|
|
732
|
+
case "Enter":
|
|
733
|
+
case " ":
|
|
734
|
+
e.preventDefault();
|
|
735
|
+
onSelect(focusedIndex);
|
|
736
|
+
return;
|
|
737
|
+
case "Escape":
|
|
738
|
+
onEscape();
|
|
739
|
+
return;
|
|
740
|
+
default:
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (next === null) return;
|
|
744
|
+
e.preventDefault();
|
|
745
|
+
if (disabledFlags) {
|
|
746
|
+
let attempts = 0;
|
|
747
|
+
while (next >= 0 && next < 12 && disabledFlags[next] && attempts < 12) {
|
|
748
|
+
next += step;
|
|
749
|
+
attempts++;
|
|
750
|
+
}
|
|
751
|
+
if (next < 0 || next >= 12 || disabledFlags[next]) return;
|
|
752
|
+
}
|
|
753
|
+
if (next !== focusedIndex) setFocusedIndex(next);
|
|
754
|
+
};
|
|
755
|
+
useEffect(() => {
|
|
756
|
+
if (!disabledFlags || !disabledFlags[focusedIndex]) return;
|
|
757
|
+
const firstEnabled = disabledFlags.findIndex((d) => !d);
|
|
758
|
+
if (firstEnabled !== -1 && firstEnabled !== focusedIndex) {
|
|
759
|
+
setFocusedIndex(firstEnabled);
|
|
760
|
+
}
|
|
761
|
+
}, [disabledFlags, focusedIndex]);
|
|
762
|
+
useEffect(() => {
|
|
763
|
+
const btn = gridRef.current?.querySelector('[data-focused="true"]');
|
|
764
|
+
btn?.focus({ preventScroll: true });
|
|
765
|
+
}, [focusedIndex]);
|
|
766
|
+
return { gridRef, focusedIndex, handleKeyDown };
|
|
767
|
+
}
|
|
688
768
|
function DatePickerMonthGrid({
|
|
689
769
|
classNames,
|
|
690
770
|
onSelect,
|
|
@@ -703,8 +783,7 @@ function DatePickerMonthGrid({
|
|
|
703
783
|
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
704
784
|
const navigateYear = useCallback(
|
|
705
785
|
(direction) => {
|
|
706
|
-
|
|
707
|
-
ctx.setViewMonth(newDate);
|
|
786
|
+
ctx.setViewMonth(adapter.addYears(viewMonth, direction));
|
|
708
787
|
},
|
|
709
788
|
[adapter, viewMonth, ctx]
|
|
710
789
|
);
|
|
@@ -717,12 +796,13 @@ function DatePickerMonthGrid({
|
|
|
717
796
|
},
|
|
718
797
|
[currentYear, ctx, onSelect]
|
|
719
798
|
);
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
799
|
+
const { gridRef, focusedIndex, handleKeyDown } = useGridState({
|
|
800
|
+
initialIndex: currentMonth,
|
|
801
|
+
onSelect: handleMonthSelect,
|
|
802
|
+
onPageUp: () => navigateYear(-1),
|
|
803
|
+
onPageDown: () => navigateYear(1),
|
|
804
|
+
onEscape: ctx.close
|
|
805
|
+
});
|
|
726
806
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
727
807
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
728
808
|
/* @__PURE__ */ jsx(
|
|
@@ -750,30 +830,37 @@ function DatePickerMonthGrid({
|
|
|
750
830
|
/* @__PURE__ */ jsx(
|
|
751
831
|
"div",
|
|
752
832
|
{
|
|
833
|
+
ref: gridRef,
|
|
753
834
|
role: "grid",
|
|
754
835
|
"aria-label": `${currentYear} months`,
|
|
755
836
|
className: classNames?.grid,
|
|
756
837
|
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
757
|
-
|
|
758
|
-
|
|
838
|
+
onKeyDown: handleKeyDown,
|
|
839
|
+
children: Array.from({ length: 12 }, (_, i) => {
|
|
840
|
+
const isSelected = i === currentMonth;
|
|
841
|
+
const isCurrent = i === todayMonth && currentYear === todayYear;
|
|
842
|
+
const isFocused = i === focusedIndex;
|
|
843
|
+
const cls = [
|
|
759
844
|
classNames?.month,
|
|
760
|
-
|
|
761
|
-
|
|
845
|
+
isSelected && classNames?.monthSelected,
|
|
846
|
+
isCurrent && classNames?.monthCurrent
|
|
762
847
|
].filter(Boolean).join(" ") || void 0;
|
|
763
848
|
return /* @__PURE__ */ jsx(
|
|
764
849
|
"button",
|
|
765
850
|
{
|
|
766
851
|
type: "button",
|
|
767
852
|
role: "gridcell",
|
|
768
|
-
|
|
769
|
-
"aria-
|
|
770
|
-
"
|
|
771
|
-
"data-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
853
|
+
tabIndex: isFocused ? 0 : -1,
|
|
854
|
+
"aria-selected": isSelected || void 0,
|
|
855
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
856
|
+
"data-selected": isSelected || void 0,
|
|
857
|
+
"data-current": isCurrent || void 0,
|
|
858
|
+
"data-focused": isFocused || void 0,
|
|
859
|
+
className: cls,
|
|
860
|
+
onClick: () => handleMonthSelect(i),
|
|
861
|
+
children: getMonthName(i, locale)
|
|
775
862
|
},
|
|
776
|
-
|
|
863
|
+
i
|
|
777
864
|
);
|
|
778
865
|
})
|
|
779
866
|
}
|
|
@@ -792,32 +879,28 @@ function DatePickerYearGrid({ classNames, onSelect, ...props }) {
|
|
|
792
879
|
const decadeStart = currentYear - currentYear % 12;
|
|
793
880
|
const navigateDecade = useCallback(
|
|
794
881
|
(direction) => {
|
|
795
|
-
|
|
796
|
-
ctx.setViewMonth(newDate);
|
|
882
|
+
ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
|
|
797
883
|
},
|
|
798
884
|
[adapter, viewMonth, ctx]
|
|
799
885
|
);
|
|
800
886
|
const handleYearSelect = useCallback(
|
|
801
|
-
(
|
|
887
|
+
(indexInDecade) => {
|
|
888
|
+
const year = decadeStart + indexInDecade;
|
|
802
889
|
const currentMonth = adapter.getMonth(viewMonth);
|
|
803
890
|
const target = new Date(Date.UTC(year, currentMonth, 1)).toISOString();
|
|
804
891
|
ctx.setViewMonth(target);
|
|
805
892
|
ctx.setFocusedDate(target);
|
|
806
893
|
onSelect?.();
|
|
807
894
|
},
|
|
808
|
-
[adapter, viewMonth, ctx, onSelect]
|
|
809
|
-
);
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
};
|
|
818
|
-
}),
|
|
819
|
-
[decadeStart, currentYear, todayYear]
|
|
820
|
-
);
|
|
895
|
+
[adapter, viewMonth, ctx, onSelect, decadeStart]
|
|
896
|
+
);
|
|
897
|
+
const { gridRef, focusedIndex, handleKeyDown } = useGridState({
|
|
898
|
+
initialIndex: currentYear - decadeStart,
|
|
899
|
+
onSelect: handleYearSelect,
|
|
900
|
+
onPageUp: () => navigateDecade(-1),
|
|
901
|
+
onPageDown: () => navigateDecade(1),
|
|
902
|
+
onEscape: ctx.close
|
|
903
|
+
});
|
|
821
904
|
const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
|
|
822
905
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
823
906
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
@@ -846,30 +929,38 @@ function DatePickerYearGrid({ classNames, onSelect, ...props }) {
|
|
|
846
929
|
/* @__PURE__ */ jsx(
|
|
847
930
|
"div",
|
|
848
931
|
{
|
|
932
|
+
ref: gridRef,
|
|
849
933
|
role: "grid",
|
|
850
934
|
"aria-label": rangeLabel,
|
|
851
935
|
className: classNames?.grid,
|
|
852
936
|
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
853
|
-
|
|
854
|
-
|
|
937
|
+
onKeyDown: handleKeyDown,
|
|
938
|
+
children: Array.from({ length: 12 }, (_, i) => {
|
|
939
|
+
const year = decadeStart + i;
|
|
940
|
+
const isSelected = year === currentYear;
|
|
941
|
+
const isCurrent = year === todayYear;
|
|
942
|
+
const isFocused = i === focusedIndex;
|
|
943
|
+
const cls = [
|
|
855
944
|
classNames?.year,
|
|
856
|
-
|
|
857
|
-
|
|
945
|
+
isSelected && classNames?.yearSelected,
|
|
946
|
+
isCurrent && classNames?.yearCurrent
|
|
858
947
|
].filter(Boolean).join(" ") || void 0;
|
|
859
948
|
return /* @__PURE__ */ jsx(
|
|
860
949
|
"button",
|
|
861
950
|
{
|
|
862
951
|
type: "button",
|
|
863
952
|
role: "gridcell",
|
|
864
|
-
|
|
865
|
-
"aria-
|
|
866
|
-
"
|
|
867
|
-
"data-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
953
|
+
tabIndex: isFocused ? 0 : -1,
|
|
954
|
+
"aria-selected": isSelected || void 0,
|
|
955
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
956
|
+
"data-selected": isSelected || void 0,
|
|
957
|
+
"data-current": isCurrent || void 0,
|
|
958
|
+
"data-focused": isFocused || void 0,
|
|
959
|
+
className: cls,
|
|
960
|
+
onClick: () => handleYearSelect(i),
|
|
961
|
+
children: year
|
|
871
962
|
},
|
|
872
|
-
|
|
963
|
+
i
|
|
873
964
|
);
|
|
874
965
|
})
|
|
875
966
|
}
|
|
@@ -2246,7 +2337,7 @@ function MonthPickerRoot(props) {
|
|
|
2246
2337
|
}
|
|
2247
2338
|
function MonthPickerGrid({ classNames, ...props }) {
|
|
2248
2339
|
const ctx = useDatePickerContext("MonthPicker.Grid");
|
|
2249
|
-
const { adapter, viewMonth, locale, value, displayTimezone, labels } = ctx;
|
|
2340
|
+
const { adapter, viewMonth, locale, value, displayTimezone, labels, disabled } = ctx;
|
|
2250
2341
|
const currentYear = adapter.getYear(viewMonth);
|
|
2251
2342
|
const [valueYear, valueMonthZeroBased] = useMemo(() => {
|
|
2252
2343
|
if (!value) return [null, null];
|
|
@@ -2263,6 +2354,13 @@ function MonthPickerGrid({ classNames, ...props }) {
|
|
|
2263
2354
|
}, [adapter, displayTimezone]);
|
|
2264
2355
|
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
2265
2356
|
const todayMonth = today !== null ? adapter.getMonth(today) : -1;
|
|
2357
|
+
const monthDisabledFlags = useMemo(
|
|
2358
|
+
() => Array.from({ length: 12 }, (_, i) => {
|
|
2359
|
+
const monthStart = new Date(Date.UTC(currentYear, i, 1)).toISOString();
|
|
2360
|
+
return isRangeFullyDisabled(monthStart, adapter.endOfMonth(monthStart), disabled, adapter);
|
|
2361
|
+
}),
|
|
2362
|
+
[currentYear, disabled, adapter]
|
|
2363
|
+
);
|
|
2266
2364
|
const navigateYear = useCallback(
|
|
2267
2365
|
(direction) => {
|
|
2268
2366
|
ctx.setViewMonth(adapter.addYears(viewMonth, direction));
|
|
@@ -2271,17 +2369,23 @@ function MonthPickerGrid({ classNames, ...props }) {
|
|
|
2271
2369
|
);
|
|
2272
2370
|
const handleMonthSelect = useCallback(
|
|
2273
2371
|
(monthIndex) => {
|
|
2372
|
+
if (monthDisabledFlags[monthIndex]) return;
|
|
2274
2373
|
const target = new Date(Date.UTC(currentYear, monthIndex, 1)).toISOString();
|
|
2275
2374
|
ctx.selectDate(target);
|
|
2276
2375
|
},
|
|
2277
|
-
[currentYear, ctx]
|
|
2278
|
-
);
|
|
2279
|
-
const
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2376
|
+
[currentYear, ctx, monthDisabledFlags]
|
|
2377
|
+
);
|
|
2378
|
+
const naturalIndex = valueYear === currentYear && valueMonthZeroBased !== null ? valueMonthZeroBased : adapter.getMonth(viewMonth);
|
|
2379
|
+
const firstEnabled = monthDisabledFlags.findIndex((d) => !d);
|
|
2380
|
+
const initialIndex = monthDisabledFlags[naturalIndex] ? firstEnabled === -1 ? naturalIndex : firstEnabled : naturalIndex;
|
|
2381
|
+
const { gridRef, focusedIndex, handleKeyDown } = useGridState({
|
|
2382
|
+
initialIndex,
|
|
2383
|
+
disabledFlags: monthDisabledFlags,
|
|
2384
|
+
onSelect: handleMonthSelect,
|
|
2385
|
+
onPageUp: () => navigateYear(-1),
|
|
2386
|
+
onPageDown: () => navigateYear(1),
|
|
2387
|
+
onEscape: ctx.close
|
|
2388
|
+
});
|
|
2285
2389
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
2286
2390
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
2287
2391
|
/* @__PURE__ */ jsx(
|
|
@@ -2306,37 +2410,57 @@ function MonthPickerGrid({ classNames, ...props }) {
|
|
|
2306
2410
|
}
|
|
2307
2411
|
)
|
|
2308
2412
|
] }),
|
|
2309
|
-
/* @__PURE__ */ jsx(
|
|
2413
|
+
/* @__PURE__ */ jsx(
|
|
2310
2414
|
"div",
|
|
2311
2415
|
{
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
"
|
|
2323
|
-
{
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2416
|
+
ref: gridRef,
|
|
2417
|
+
role: "grid",
|
|
2418
|
+
"aria-label": `${currentYear} months`,
|
|
2419
|
+
className: classNames?.grid,
|
|
2420
|
+
onKeyDown: handleKeyDown,
|
|
2421
|
+
children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
|
|
2422
|
+
"div",
|
|
2423
|
+
{
|
|
2424
|
+
role: "row",
|
|
2425
|
+
className: classNames?.gridRow,
|
|
2426
|
+
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
2427
|
+
children: Array.from({ length: 3 }, (_2, col) => {
|
|
2428
|
+
const i = rowIndex * 3 + col;
|
|
2429
|
+
const isSelected = valueYear === currentYear && valueMonthZeroBased === i;
|
|
2430
|
+
const isCurrent = todayYear === currentYear && todayMonth === i;
|
|
2431
|
+
const isFocused = i === focusedIndex;
|
|
2432
|
+
const isDisabled = monthDisabledFlags[i] ?? false;
|
|
2433
|
+
const cls = [
|
|
2434
|
+
classNames?.month,
|
|
2435
|
+
isSelected && classNames?.monthSelected,
|
|
2436
|
+
isCurrent && classNames?.monthCurrent,
|
|
2437
|
+
isDisabled && classNames?.monthDisabled
|
|
2438
|
+
].filter(Boolean).join(" ") || void 0;
|
|
2439
|
+
return /* @__PURE__ */ jsx(
|
|
2440
|
+
"button",
|
|
2441
|
+
{
|
|
2442
|
+
type: "button",
|
|
2443
|
+
role: "gridcell",
|
|
2444
|
+
tabIndex: isFocused ? 0 : -1,
|
|
2445
|
+
disabled: isDisabled,
|
|
2446
|
+
"aria-selected": isSelected || void 0,
|
|
2447
|
+
"aria-disabled": isDisabled || void 0,
|
|
2448
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
2449
|
+
"data-selected": isSelected || void 0,
|
|
2450
|
+
"data-current": isCurrent || void 0,
|
|
2451
|
+
"data-focused": isFocused || void 0,
|
|
2452
|
+
className: cls,
|
|
2453
|
+
onClick: () => handleMonthSelect(i),
|
|
2454
|
+
children: getMonthName(i, locale)
|
|
2455
|
+
},
|
|
2456
|
+
i
|
|
2457
|
+
);
|
|
2458
|
+
})
|
|
2459
|
+
},
|
|
2460
|
+
rowIndex
|
|
2461
|
+
))
|
|
2462
|
+
}
|
|
2463
|
+
)
|
|
2340
2464
|
] });
|
|
2341
2465
|
}
|
|
2342
2466
|
|
|
@@ -2353,7 +2477,7 @@ function YearPickerRoot(props) {
|
|
|
2353
2477
|
}
|
|
2354
2478
|
function YearPickerGrid({ classNames, ...props }) {
|
|
2355
2479
|
const ctx = useDatePickerContext("YearPicker.Grid");
|
|
2356
|
-
const { adapter, viewMonth, value, displayTimezone, labels } = ctx;
|
|
2480
|
+
const { adapter, viewMonth, value, displayTimezone, labels, disabled } = ctx;
|
|
2357
2481
|
const currentYear = adapter.getYear(viewMonth);
|
|
2358
2482
|
const decadeStart = currentYear - currentYear % 12;
|
|
2359
2483
|
const valueYear = useMemo(() => {
|
|
@@ -2369,6 +2493,15 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2369
2493
|
setToday(adapter.today(displayTimezone));
|
|
2370
2494
|
}, [adapter, displayTimezone]);
|
|
2371
2495
|
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
2496
|
+
const yearDisabledFlags = useMemo(
|
|
2497
|
+
() => Array.from({ length: 12 }, (_, i) => {
|
|
2498
|
+
const year = decadeStart + i;
|
|
2499
|
+
const yearStart = new Date(Date.UTC(year, 0, 1)).toISOString();
|
|
2500
|
+
const yearEnd = new Date(Date.UTC(year, 11, 31, 23, 59, 59, 999)).toISOString();
|
|
2501
|
+
return isRangeFullyDisabled(yearStart, yearEnd, disabled, adapter);
|
|
2502
|
+
}),
|
|
2503
|
+
[decadeStart, disabled, adapter]
|
|
2504
|
+
);
|
|
2372
2505
|
const navigateDecade = useCallback(
|
|
2373
2506
|
(direction) => {
|
|
2374
2507
|
ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
|
|
@@ -2376,19 +2509,24 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2376
2509
|
[adapter, viewMonth, ctx]
|
|
2377
2510
|
);
|
|
2378
2511
|
const handleYearSelect = useCallback(
|
|
2379
|
-
(
|
|
2512
|
+
(indexInDecade) => {
|
|
2513
|
+
if (yearDisabledFlags[indexInDecade]) return;
|
|
2514
|
+
const year = decadeStart + indexInDecade;
|
|
2380
2515
|
const target = new Date(Date.UTC(year, 0, 1)).toISOString();
|
|
2381
2516
|
ctx.selectDate(target);
|
|
2382
2517
|
},
|
|
2383
|
-
[ctx]
|
|
2384
|
-
);
|
|
2385
|
-
const
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2518
|
+
[ctx, decadeStart, yearDisabledFlags]
|
|
2519
|
+
);
|
|
2520
|
+
const naturalIndex = valueYear !== null && valueYear >= decadeStart && valueYear <= decadeStart + 11 ? valueYear - decadeStart : currentYear - decadeStart;
|
|
2521
|
+
const firstEnabled = yearDisabledFlags.findIndex((d) => !d);
|
|
2522
|
+
const initialIndex = yearDisabledFlags[naturalIndex] ? firstEnabled === -1 ? naturalIndex : firstEnabled : naturalIndex;
|
|
2523
|
+
const { gridRef, focusedIndex, handleKeyDown } = useGridState({
|
|
2524
|
+
initialIndex,
|
|
2525
|
+
disabledFlags: yearDisabledFlags,
|
|
2526
|
+
onSelect: handleYearSelect,
|
|
2527
|
+
onPageUp: () => navigateDecade(-1),
|
|
2528
|
+
onPageDown: () => navigateDecade(1),
|
|
2529
|
+
onEscape: ctx.close
|
|
2392
2530
|
});
|
|
2393
2531
|
const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
|
|
2394
2532
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
@@ -2415,37 +2553,58 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2415
2553
|
}
|
|
2416
2554
|
)
|
|
2417
2555
|
] }),
|
|
2418
|
-
/* @__PURE__ */ jsx(
|
|
2556
|
+
/* @__PURE__ */ jsx(
|
|
2419
2557
|
"div",
|
|
2420
2558
|
{
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
"
|
|
2432
|
-
{
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2559
|
+
ref: gridRef,
|
|
2560
|
+
role: "grid",
|
|
2561
|
+
"aria-label": rangeLabel,
|
|
2562
|
+
className: classNames?.grid,
|
|
2563
|
+
onKeyDown: handleKeyDown,
|
|
2564
|
+
children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
|
|
2565
|
+
"div",
|
|
2566
|
+
{
|
|
2567
|
+
role: "row",
|
|
2568
|
+
className: classNames?.gridRow,
|
|
2569
|
+
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
2570
|
+
children: Array.from({ length: 3 }, (_2, col) => {
|
|
2571
|
+
const i = rowIndex * 3 + col;
|
|
2572
|
+
const year = decadeStart + i;
|
|
2573
|
+
const isSelected = year === valueYear;
|
|
2574
|
+
const isCurrent = year === todayYear;
|
|
2575
|
+
const isFocused = i === focusedIndex;
|
|
2576
|
+
const isDisabled = yearDisabledFlags[i] ?? false;
|
|
2577
|
+
const cls = [
|
|
2578
|
+
classNames?.year,
|
|
2579
|
+
isSelected && classNames?.yearSelected,
|
|
2580
|
+
isCurrent && classNames?.yearCurrent,
|
|
2581
|
+
isDisabled && classNames?.yearDisabled
|
|
2582
|
+
].filter(Boolean).join(" ") || void 0;
|
|
2583
|
+
return /* @__PURE__ */ jsx(
|
|
2584
|
+
"button",
|
|
2585
|
+
{
|
|
2586
|
+
type: "button",
|
|
2587
|
+
role: "gridcell",
|
|
2588
|
+
tabIndex: isFocused ? 0 : -1,
|
|
2589
|
+
disabled: isDisabled,
|
|
2590
|
+
"aria-selected": isSelected || void 0,
|
|
2591
|
+
"aria-disabled": isDisabled || void 0,
|
|
2592
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
2593
|
+
"data-selected": isSelected || void 0,
|
|
2594
|
+
"data-current": isCurrent || void 0,
|
|
2595
|
+
"data-focused": isFocused || void 0,
|
|
2596
|
+
className: cls,
|
|
2597
|
+
onClick: () => handleYearSelect(i),
|
|
2598
|
+
children: year
|
|
2599
|
+
},
|
|
2600
|
+
i
|
|
2601
|
+
);
|
|
2602
|
+
})
|
|
2603
|
+
},
|
|
2604
|
+
rowIndex
|
|
2605
|
+
))
|
|
2606
|
+
}
|
|
2607
|
+
)
|
|
2449
2608
|
] });
|
|
2450
2609
|
}
|
|
2451
2610
|
|