@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 +79 -0
- package/dist/index.cjs +210 -116
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -3
- package/dist/index.d.ts +39 -3
- package/dist/index.js +211 -117
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
-
[
|
|
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.
|
|
626
|
-
"th",
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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:
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
|
|
655
|
-
"aria-
|
|
656
|
-
|
|
657
|
-
"
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
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
|
-
[
|
|
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.
|
|
1535
|
-
"th",
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
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:
|
|
1553
|
-
|
|
1554
|
-
|
|
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
|
-
|
|
1567
|
-
"aria-
|
|
1568
|
-
|
|
1569
|
-
"
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
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,
|