@kalyx/react 1.0.0-rc.6 → 1.0.0-rc.8

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 CHANGED
@@ -1,5 +1,84 @@
1
1
  # @kalyx/react
2
2
 
3
+ ## 1.0.0-rc.8
4
+
5
+ ### Minor Changes
6
+
7
+ - 0d3b845: `TimePicker.Root` gains a programmatic **`filterTime`** prop — `(hours: number, minutes: number) => boolean` returning `true` for any slot that should be unselectable. Equivalent to `react-datepicker`'s `filterTime` and MUI X's `shouldDisableTime`, covering use cases the static `step` prop can't (business-hours-only, lunch breaks, blackout slots, per-day variations).
8
+
9
+ ```tsx
10
+ <TimePicker
11
+ value={time}
12
+ onChange={setTime}
13
+ step={15}
14
+ // Business hours only: 09:00–11:45 and 13:00–17:45 (no lunch slot)
15
+ filterTime={(h, m) => h < 9 || h >= 18 || h === 12}
16
+ >
17
+ <TimePicker.Input />
18
+ <TimePicker.HourList />
19
+ <TimePicker.MinuteList />
20
+ </TimePicker>
21
+ ```
22
+
23
+ Behavior:
24
+ - **`MinuteList`** — minutes for which `filterTime(currentHour, minute)` returns `true` get `aria-disabled="true"` and reject click/Enter.
25
+ - **`HourList`** — an hour is marked `aria-disabled="true"` only when `filterTime` returns `true` for **every** step minute within it. Hours with at least one open minute remain selectable.
26
+ - 12-hour mode — the predicate always receives 24-hour values (`0`–`23`) regardless of the picker's display format.
27
+
28
+ **Note**: `DateTimePicker` does not yet wire this through — combine `DatePicker.Root` + `TimePicker.Root` manually if you need both date and time-slot filtering in the same picker.
29
+
30
+ Bundle ceiling raised 15 → 16 KB (PR #N follows the 12→13→14→15 cadence — each raise tied to a documented feature; CLAUDE.md §2 records the chain). Measured 15.01 KB ESM / 15.16 KB CJS at this commit, ~4× smaller than react-datepicker.
31
+
32
+ ## 1.0.0-rc.7
33
+
34
+ ### Minor Changes
35
+
36
+ - 0eca2e8: Two new `DatePicker.Calendar` / `RangePicker.Calendar` props plus an ISO-week utility:
37
+ - **`showWeekNumber`** — render an ISO 8601 week-number column (1–53) on the left of the grid. The column uses `<th scope="row" aria-hidden="true">` so it doesn't participate in the WAI-ARIA grid data region; keyboard navigation across date cells is unchanged. New className slots: `weekNumberHeader`, `weekNumber`.
38
+ - **`fixedWeeks`** — when true, always render 6 rows (42 cells) regardless of the month. Useful for popover layouts that need a stable height across month navigation.
39
+
40
+ Both also accepted on `CalendarOptions` (the `getCalendarDays` core util gains `fixedWeeks`).
41
+
42
+ New core export: **`getISOWeekNumber(iso)`** — pure UTC computation, no date-fns dep. Anchored to the Thursday of the week (so the same week always returns the same number regardless of `weekStartsOn`).
43
+
44
+ ```tsx
45
+ <DatePicker value={date} onChange={setDate}>
46
+ <DatePicker.Input />
47
+ <DatePicker.Popover>
48
+ <DatePicker.Calendar showWeekNumber fixedWeeks />
49
+ </DatePicker.Popover>
50
+ </DatePicker>
51
+ ```
52
+
53
+ Bundle impact: +0.46 KB ESM gzip (13.96 → 14.42 KB). Still well under the 15 KB ceiling.
54
+
55
+ - d62c84e: `DisabledRule` gains a programmatic `filter` variant — pass any predicate `(iso: ISODateString) => boolean` to disable arbitrary days that don't fit the declarative `before` / `after` / `dayOfWeek` / `date` rules.
56
+
57
+ ```tsx
58
+ const holidays = new Set(['2026-01-01T00:00:00.000Z', '2026-12-25T00:00:00.000Z']);
59
+
60
+ <DatePicker
61
+ disabled={[
62
+ { dayOfWeek: [0, 6] }, // weekends
63
+ { filter: (iso) => holidays.has(iso) }, // holidays
64
+ ]}
65
+ >
66
+
67
+ </DatePicker>;
68
+ ```
69
+
70
+ The new variant slots into the existing `isDateDisabled` evaluation (short-circuits on first match) and works with keyboard-navigation disabled-skip in `DatePicker.Calendar` / `RangePicker.Calendar` with no further changes. Equivalent to `react-datepicker`'s `filterDate` prop and MUI X DatePicker's `shouldDisableDate`. Bundle impact: 0 KB (still 13.96 KB ESM gzip).
71
+
72
+ ### Patch Changes
73
+
74
+ - b40080d: Internal: sync the `tsup` onSuccess bundle-size budget from `13 KB` to `15 KB` so the per-build warning matches the actual CI gate (`scripts/check-bundle-size.js`, `pr-check.yml`, `release.yml`). No runtime change; the published artifact is byte-identical.
75
+
76
+ This was a leftover from the 13 → 14 → 15 KB ceiling raises during RC (PR #46 / PR #48); only the tsup-side TARGET_KB was missed during those bumps, so local `pnpm build` printed a spurious `⚠️` even though CI passed.
77
+
78
+ - Updated dependencies [0eca2e8]
79
+ - Updated dependencies [d62c84e]
80
+ - @kalyx/core@1.0.0-rc.7
81
+
3
82
  ## 1.0.0-rc.6
4
83
 
5
84
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -465,6 +465,8 @@ var srOnly = {
465
465
  function DatePickerCalendar({
466
466
  classNames,
467
467
  onTitleClick,
468
+ showWeekNumber = false,
469
+ fixedWeeks = false,
468
470
  ...props
469
471
  }) {
470
472
  const ctx = useDatePickerContext("DatePicker.Calendar");
@@ -478,10 +480,21 @@ function DatePickerCalendar({
478
480
  selected: ctx.value,
479
481
  focusedDate,
480
482
  disabled,
481
- timezone: displayTimezone
483
+ timezone: displayTimezone,
484
+ fixedWeeks
482
485
  }),
483
- [viewMonth, adapter, weekStartsOn, ctx.value, focusedDate, disabled, displayTimezone]
486
+ [
487
+ viewMonth,
488
+ adapter,
489
+ weekStartsOn,
490
+ ctx.value,
491
+ focusedDate,
492
+ disabled,
493
+ displayTimezone,
494
+ fixedWeeks
495
+ ]
484
496
  );
497
+ const thursdayIndex = weekStartsOn === 0 ? 4 : 3;
485
498
  const year = adapter.getYear(viewMonth);
486
499
  const month = adapter.getMonth(viewMonth);
487
500
  const title = core.formatMonthYear(year, month, locale);
@@ -622,61 +635,76 @@ function DatePickerCalendar({
622
635
  className: classNames?.grid,
623
636
  onKeyDown: handleKeyDown,
624
637
  children: [
625
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { role: "row", "aria-rowindex": 1, children: weekdays.map((day, colIndex) => /* @__PURE__ */ jsxRuntime.jsx(
626
- "th",
627
- {
628
- role: "columnheader",
629
- abbr: day.full,
630
- scope: "col",
631
- "aria-colindex": colIndex + 1,
632
- className: classNames?.weekdayHeader,
633
- children: day.short
634
- },
635
- day.short
636
- )) }) }),
637
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxRuntime.jsx(
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",
642
+ {
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(
638
654
  "tr",
639
655
  {
640
656
  role: "row",
641
657
  "aria-rowindex": weekIndex + 2,
642
658
  className: classNames?.gridRow,
643
- children: week.map((day, colIndex) => {
644
- const dayClasses = [
645
- classNames?.day,
646
- day.isSelected && classNames?.daySelected,
647
- day.isToday && classNames?.dayToday,
648
- day.isDisabled && classNames?.dayDisabled,
649
- !day.isCurrentMonth && classNames?.dayOutsideMonth
650
- ].filter(Boolean).join(" ") || void 0;
651
- return /* @__PURE__ */ jsxRuntime.jsx(
652
- "td",
659
+ children: [
660
+ showWeekNumber ? /* @__PURE__ */ jsxRuntime.jsx(
661
+ "th",
653
662
  {
654
- role: "gridcell",
655
- "aria-colindex": colIndex + 1,
656
- "aria-selected": day.isSelected || void 0,
657
- "aria-disabled": day.isDisabled || void 0,
658
- "aria-current": day.isToday ? "date" : void 0,
659
- className: classNames?.gridCell,
660
- children: /* @__PURE__ */ jsxRuntime.jsx(
661
- "button",
662
- {
663
- type: "button",
664
- tabIndex: day.isFocused ? 0 : -1,
665
- disabled: day.isDisabled,
666
- "data-focused": day.isFocused || void 0,
667
- "data-selected": day.isSelected || void 0,
668
- "data-today": day.isToday || void 0,
669
- "data-outside-month": !day.isCurrentMonth || void 0,
670
- className: dayClasses,
671
- onClick: () => handleDayClick(day),
672
- "aria-label": safeFormatFullDate(day.isoString, locale),
673
- children: day.dayNumber
674
- }
675
- )
676
- },
677
- day.isoString
678
- );
679
- })
663
+ scope: "row",
664
+ "aria-hidden": "true",
665
+ className: classNames?.weekNumber,
666
+ "data-week-number": true,
667
+ children: core.getISOWeekNumber(week[thursdayIndex].isoString)
668
+ }
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
+ ]
680
708
  },
681
709
  weekIndex
682
710
  )) })
@@ -1339,6 +1367,8 @@ var srOnly2 = {
1339
1367
  function RangePickerCalendar({
1340
1368
  classNames,
1341
1369
  selectionMode = "range",
1370
+ showWeekNumber = false,
1371
+ fixedWeeks = false,
1342
1372
  ...props
1343
1373
  }) {
1344
1374
  const ctx = useRangePickerContext("RangePicker.Calendar");
@@ -1364,10 +1394,22 @@ function RangePickerCalendar({
1364
1394
  disabled,
1365
1395
  range: value,
1366
1396
  rangeHover: hoverDate,
1367
- timezone: displayTimezone
1397
+ timezone: displayTimezone,
1398
+ fixedWeeks
1368
1399
  }),
1369
- [viewMonth, adapter, weekStartsOn, focusedDate, disabled, value, hoverDate, displayTimezone]
1400
+ [
1401
+ viewMonth,
1402
+ adapter,
1403
+ weekStartsOn,
1404
+ focusedDate,
1405
+ disabled,
1406
+ value,
1407
+ hoverDate,
1408
+ displayTimezone,
1409
+ fixedWeeks
1410
+ ]
1370
1411
  );
1412
+ const thursdayIndex = weekStartsOn === 0 ? 4 : 3;
1371
1413
  const year = adapter.getYear(viewMonth);
1372
1414
  const month = adapter.getMonth(viewMonth);
1373
1415
  const title = core.formatMonthYear(year, month, locale);
@@ -1531,67 +1573,82 @@ function RangePickerCalendar({
1531
1573
  className: classNames?.grid,
1532
1574
  onKeyDown: handleKeyDown,
1533
1575
  children: [
1534
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { role: "row", "aria-rowindex": 1, children: weekdays.map((day, colIndex) => /* @__PURE__ */ jsxRuntime.jsx(
1535
- "th",
1536
- {
1537
- role: "columnheader",
1538
- abbr: day.full,
1539
- scope: "col",
1540
- "aria-colindex": colIndex + 1,
1541
- className: classNames?.weekdayHeader,
1542
- children: day.short
1543
- },
1544
- day.short
1545
- )) }) }),
1546
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxRuntime.jsx(
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",
1580
+ {
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(
1547
1592
  "tr",
1548
1593
  {
1549
1594
  role: "row",
1550
1595
  "aria-rowindex": weekIndex + 2,
1551
1596
  className: classNames?.gridRow,
1552
- children: week.map((day, colIndex) => {
1553
- const dayClasses = [
1554
- classNames?.day,
1555
- day.isRangeStart && classNames?.dayRangeStart,
1556
- day.isRangeEnd && classNames?.dayRangeEnd,
1557
- day.isInRange && classNames?.dayInRange,
1558
- day.isToday && classNames?.dayToday,
1559
- day.isDisabled && classNames?.dayDisabled,
1560
- !day.isCurrentMonth && classNames?.dayOutsideMonth
1561
- ].filter(Boolean).join(" ") || void 0;
1562
- const isSelected = selectionMode === "week" ? day.isRangeStart || day.isRangeEnd || day.isInRange : day.isRangeStart || day.isRangeEnd;
1563
- return /* @__PURE__ */ jsxRuntime.jsx(
1564
- "td",
1597
+ children: [
1598
+ showWeekNumber ? /* @__PURE__ */ jsxRuntime.jsx(
1599
+ "th",
1565
1600
  {
1566
- role: "gridcell",
1567
- "aria-colindex": colIndex + 1,
1568
- "aria-selected": isSelected || void 0,
1569
- "aria-disabled": day.isDisabled || void 0,
1570
- "aria-current": day.isToday ? "date" : void 0,
1571
- className: classNames?.gridCell,
1572
- children: /* @__PURE__ */ jsxRuntime.jsx(
1573
- "button",
1574
- {
1575
- type: "button",
1576
- tabIndex: day.isFocused ? 0 : -1,
1577
- disabled: day.isDisabled,
1578
- "data-focused": day.isFocused || void 0,
1579
- "data-range-start": day.isRangeStart || void 0,
1580
- "data-range-end": day.isRangeEnd || void 0,
1581
- "data-in-range": day.isInRange || void 0,
1582
- "data-today": day.isToday || void 0,
1583
- "data-outside-month": !day.isCurrentMonth || void 0,
1584
- className: dayClasses,
1585
- onClick: () => handleDayClick(day),
1586
- onMouseEnter: () => handleDayMouseEnter(day),
1587
- "aria-label": safeFormatFullDate2(day.isoString, locale),
1588
- children: day.dayNumber
1589
- }
1590
- )
1591
- },
1592
- day.isoString
1593
- );
1594
- })
1601
+ scope: "row",
1602
+ "aria-hidden": "true",
1603
+ className: classNames?.weekNumber,
1604
+ "data-week-number": true,
1605
+ children: core.getISOWeekNumber(week[thursdayIndex].isoString)
1606
+ }
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
+ ]
1595
1652
  },
1596
1653
  weekIndex
1597
1654
  )) })
@@ -1735,6 +1792,7 @@ function TimePickerRoot({
1735
1792
  displayTimezone,
1736
1793
  disabled = false,
1737
1794
  readOnly = false,
1795
+ filterTime,
1738
1796
  labels: labelsProp,
1739
1797
  children
1740
1798
  }) {
@@ -1776,7 +1834,8 @@ function TimePickerRoot({
1776
1834
  isReadOnly: readOnly,
1777
1835
  currentTime,
1778
1836
  pickerId,
1779
- labels: mergedLabels
1837
+ labels: mergedLabels,
1838
+ filterTime
1780
1839
  }),
1781
1840
  [
1782
1841
  currentValue,
@@ -1789,7 +1848,8 @@ function TimePickerRoot({
1789
1848
  readOnly,
1790
1849
  currentTime,
1791
1850
  pickerId,
1792
- mergedLabels
1851
+ mergedLabels,
1852
+ filterTime
1793
1853
  ]
1794
1854
  );
1795
1855
  return /* @__PURE__ */ jsxRuntime.jsx(TimePickerContext.Provider, { value: contextValue, children });
@@ -1894,17 +1954,41 @@ function useListboxNavigation({
1894
1954
  }
1895
1955
  function TimePickerHourList({ classNames, ...props }) {
1896
1956
  const ctx = useTimePickerContext("TimePicker.HourList");
1897
- const { format, currentTime, isDisabled, isReadOnly } = ctx;
1957
+ const { format, step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
1898
1958
  const hours = react.useMemo(() => core.generateHours(format), [format]);
1899
1959
  const selectedHourDisplay = format === "12h" ? core.to12Hour(currentTime.hours).hours12 : currentTime.hours;
1900
1960
  const currentPeriod = format === "12h" ? core.to12Hour(currentTime.hours).period : null;
1961
+ const fullyDisabledHours24 = react.useMemo(() => {
1962
+ if (!filterTime) return null;
1963
+ const disabled = /* @__PURE__ */ new Set();
1964
+ for (let h = 0; h < 24; h++) {
1965
+ let allRejected = true;
1966
+ for (let m = 0; m < 60; m += step) {
1967
+ if (!filterTime(h, m)) {
1968
+ allRejected = false;
1969
+ break;
1970
+ }
1971
+ }
1972
+ if (allRejected) disabled.add(h);
1973
+ }
1974
+ return disabled;
1975
+ }, [filterTime, step]);
1976
+ const isHourDisabled = react.useCallback(
1977
+ (hourDisplay) => {
1978
+ if (!fullyDisabledHours24) return false;
1979
+ const hours24 = format === "12h" && currentPeriod ? core.to24Hour(hourDisplay, currentPeriod) : hourDisplay;
1980
+ return fullyDisabledHours24.has(hours24);
1981
+ },
1982
+ [fullyDisabledHours24, format, currentPeriod]
1983
+ );
1901
1984
  const handleSelect = react.useCallback(
1902
1985
  (hourDisplay) => {
1903
1986
  if (isDisabled || isReadOnly) return;
1987
+ if (isHourDisabled(hourDisplay)) return;
1904
1988
  const hours24 = format === "12h" && currentPeriod ? core.to24Hour(hourDisplay, currentPeriod) : hourDisplay;
1905
1989
  ctx.setTime({ hours: hours24 });
1906
1990
  },
1907
- [format, currentPeriod, ctx, isDisabled, isReadOnly]
1991
+ [format, currentPeriod, ctx, isDisabled, isReadOnly, isHourDisabled]
1908
1992
  );
1909
1993
  const { listRef, handleKeyDown } = useListboxNavigation({
1910
1994
  items: hours,
@@ -1922,13 +2006,14 @@ function TimePickerHourList({ classNames, ...props }) {
1922
2006
  ...props,
1923
2007
  children: hours.map((hour) => {
1924
2008
  const isSelected = hour === selectedHourDisplay;
2009
+ const isHourFullyDisabled = isHourDisabled(hour);
1925
2010
  const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
1926
2011
  return /* @__PURE__ */ jsxRuntime.jsx(
1927
2012
  "li",
1928
2013
  {
1929
2014
  role: "option",
1930
2015
  "aria-selected": isSelected,
1931
- "aria-disabled": isDisabled || void 0,
2016
+ "aria-disabled": isDisabled || isHourFullyDisabled || void 0,
1932
2017
  "aria-label": ctx.labels.hourOption(hour),
1933
2018
  "data-selected": isSelected || void 0,
1934
2019
  tabIndex: isSelected ? 0 : -1,
@@ -1945,14 +2030,22 @@ function TimePickerHourList({ classNames, ...props }) {
1945
2030
  }
1946
2031
  function TimePickerMinuteList({ classNames, ...props }) {
1947
2032
  const ctx = useTimePickerContext("TimePicker.MinuteList");
1948
- const { step, currentTime, isDisabled, isReadOnly } = ctx;
2033
+ const { step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
1949
2034
  const minutes = react.useMemo(() => core.generateMinutes(step), [step]);
2035
+ const isMinuteDisabled = react.useCallback(
2036
+ (minute) => {
2037
+ if (!filterTime) return false;
2038
+ return filterTime(currentTime.hours, minute);
2039
+ },
2040
+ [filterTime, currentTime.hours]
2041
+ );
1950
2042
  const handleSelect = react.useCallback(
1951
2043
  (minute) => {
1952
2044
  if (isDisabled || isReadOnly) return;
2045
+ if (isMinuteDisabled(minute)) return;
1953
2046
  ctx.setTime({ minutes: minute });
1954
2047
  },
1955
- [ctx, isDisabled, isReadOnly]
2048
+ [ctx, isDisabled, isReadOnly, isMinuteDisabled]
1956
2049
  );
1957
2050
  const { listRef, handleKeyDown } = useListboxNavigation({
1958
2051
  items: minutes,
@@ -1970,13 +2063,14 @@ function TimePickerMinuteList({ classNames, ...props }) {
1970
2063
  ...props,
1971
2064
  children: minutes.map((minute) => {
1972
2065
  const isSelected = minute === currentTime.minutes;
2066
+ const isMinuteFullyDisabled = isMinuteDisabled(minute);
1973
2067
  const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
1974
2068
  return /* @__PURE__ */ jsxRuntime.jsx(
1975
2069
  "li",
1976
2070
  {
1977
2071
  role: "option",
1978
2072
  "aria-selected": isSelected,
1979
- "aria-disabled": isDisabled || void 0,
2073
+ "aria-disabled": isDisabled || isMinuteFullyDisabled || void 0,
1980
2074
  "aria-label": ctx.labels.minuteOption(minute),
1981
2075
  "data-selected": isSelected || void 0,
1982
2076
  tabIndex: isSelected ? 0 : -1,