@kalyx/react 1.0.0-rc.3 → 1.0.0-rc.4
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 +19 -0
- package/README.md +2 -2
- package/dist/index.cjs +70 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +70 -17
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @kalyx/react
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- df97687: P1 audit follow-ups for v1.0-rc:
|
|
8
|
+
- **SSR hydration safety in 4 commit/drilldown grids** — `DatePicker.MonthGrid`, `DatePicker.YearGrid`, `MonthPicker.Grid`, and `YearPicker.Grid` previously called `adapter.today()` directly inside their render bodies, producing a server/client clock-mismatch hydration warning across day boundaries (and intermittently wrong "today" highlights in tz-different SSR setups). Today is now snapshotted via `useState(null)` + post-mount `useEffect`, so the server output and the first client render agree, and the highlight settles on the first effect tick.
|
|
9
|
+
- **`AmPmToggle` now follows the WAI-ARIA radiogroup pattern** — Arrow / Home / End / Space / Enter move and commit selection between AM and PM, and `tabIndex` is roving (only the checked radio is in the tab order). Previously both buttons were tabbable and arrow keys were ignored.
|
|
10
|
+
- **`DatePicker.Preset` / `RangePicker.Preset` now use `aria-pressed`** instead of `role="option"` + `aria-selected`. `role="option"` is invalid outside `role="listbox"` / `role="combobox"`, so axe was flagging the previous markup. Active state still appears on `data-active` for CSS targeting.
|
|
11
|
+
- **`RangePicker.Calendar` no longer advertises `aria-multiselectable="true"`** — a date range is one selection (two endpoints), not a multi-select grid.
|
|
12
|
+
- **Test stability** — `useRangePicker` `respects disabled rules` test pinned to April 2026 via `defaultValue` so the calendar grid contains the expected weekend day regardless of the system clock (was failing once the clock crossed into May).
|
|
13
|
+
- **`labels.ts` test coverage** — first unit tests for the default-label exports.
|
|
14
|
+
|
|
15
|
+
Behavioral notes for users (none of these are breaking for code that follows the documented `data-*` styling contract):
|
|
16
|
+
- If you targeted Preset buttons via `[aria-selected="true"]` in CSS, switch to `[aria-pressed="true"]` or `[data-active]`.
|
|
17
|
+
- If you targeted the range grid via `[aria-multiselectable]`, that attribute is gone; use `[role="grid"]` on the calendar root instead.
|
|
18
|
+
|
|
19
|
+
- Updated dependencies [df97687]
|
|
20
|
+
- @kalyx/core@1.0.0-rc.4
|
|
21
|
+
|
|
3
22
|
## 1.0.0-rc.3
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @kalyx/react
|
|
2
2
|
|
|
3
|
-
> The headless React DatePicker, finally complete. Zero CSS · SSR-safe ·
|
|
3
|
+
> The headless React DatePicker, finally complete. Zero CSS · SSR-safe · ~12 KB gzip (≤ 13 KB ceiling).
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@kalyx/react)
|
|
6
|
-
[](https://kalyx-docs.vercel.app/docs/api/react#bundle-size)
|
|
7
7
|
[](https://www.typescriptlang.org/)
|
|
8
8
|
[](https://github.com/jiji-hoon96/kalyx/blob/main/LICENSE)
|
|
9
9
|
|
package/dist/index.cjs
CHANGED
|
@@ -693,11 +693,15 @@ function DatePickerMonthGrid({
|
|
|
693
693
|
...props
|
|
694
694
|
}) {
|
|
695
695
|
const ctx = useDatePickerContext("DatePicker.MonthGrid");
|
|
696
|
-
const { adapter, viewMonth, locale } = ctx;
|
|
696
|
+
const { adapter, viewMonth, locale, displayTimezone } = ctx;
|
|
697
697
|
const currentYear = adapter.getYear(viewMonth);
|
|
698
698
|
const currentMonth = adapter.getMonth(viewMonth);
|
|
699
|
-
const
|
|
700
|
-
|
|
699
|
+
const [today, setToday] = react.useState(null);
|
|
700
|
+
react.useEffect(() => {
|
|
701
|
+
setToday(adapter.today(displayTimezone));
|
|
702
|
+
}, [adapter, displayTimezone]);
|
|
703
|
+
const todayMonth = today !== null ? adapter.getMonth(today) : -1;
|
|
704
|
+
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
701
705
|
const navigateYear = react.useCallback(
|
|
702
706
|
(direction) => {
|
|
703
707
|
const newDate = adapter.addYears(viewMonth, direction);
|
|
@@ -779,9 +783,13 @@ function DatePickerMonthGrid({
|
|
|
779
783
|
}
|
|
780
784
|
function DatePickerYearGrid({ classNames, onSelect, ...props }) {
|
|
781
785
|
const ctx = useDatePickerContext("DatePicker.YearGrid");
|
|
782
|
-
const { adapter, viewMonth } = ctx;
|
|
786
|
+
const { adapter, viewMonth, displayTimezone } = ctx;
|
|
783
787
|
const currentYear = adapter.getYear(viewMonth);
|
|
784
|
-
const
|
|
788
|
+
const [today, setToday] = react.useState(null);
|
|
789
|
+
react.useEffect(() => {
|
|
790
|
+
setToday(adapter.today(displayTimezone));
|
|
791
|
+
}, [adapter, displayTimezone]);
|
|
792
|
+
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
785
793
|
const decadeStart = currentYear - currentYear % 12;
|
|
786
794
|
const navigateDecade = react.useCallback(
|
|
787
795
|
(direction) => {
|
|
@@ -935,8 +943,7 @@ function DatePickerPreset({
|
|
|
935
943
|
"button",
|
|
936
944
|
{
|
|
937
945
|
type: "button",
|
|
938
|
-
|
|
939
|
-
"aria-selected": isActive,
|
|
946
|
+
"aria-pressed": isActive,
|
|
940
947
|
"data-active": isActive || void 0,
|
|
941
948
|
disabled: ctx.isDisabled,
|
|
942
949
|
onClick: handleClick,
|
|
@@ -1428,7 +1435,6 @@ function RangePickerCalendar({
|
|
|
1428
1435
|
ref: gridRef,
|
|
1429
1436
|
role: "grid",
|
|
1430
1437
|
"aria-label": title,
|
|
1431
|
-
"aria-multiselectable": "true",
|
|
1432
1438
|
"aria-rowcount": weeks.length + 1,
|
|
1433
1439
|
"aria-colcount": 7,
|
|
1434
1440
|
className: classNames?.grid,
|
|
@@ -1592,8 +1598,7 @@ function RangePickerPreset({
|
|
|
1592
1598
|
"button",
|
|
1593
1599
|
{
|
|
1594
1600
|
type: "button",
|
|
1595
|
-
|
|
1596
|
-
"aria-selected": isActive,
|
|
1601
|
+
"aria-pressed": isActive,
|
|
1597
1602
|
"data-active": isActive || void 0,
|
|
1598
1603
|
disabled: ctx.isDisabled,
|
|
1599
1604
|
onClick: handleClick,
|
|
@@ -1897,29 +1902,70 @@ function TimePickerMinuteList({ classNames, ...props }) {
|
|
|
1897
1902
|
}
|
|
1898
1903
|
function TimePickerAmPmToggle({ classNames, ...props }) {
|
|
1899
1904
|
const ctx = useTimePickerContext("TimePicker.AmPmToggle");
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1905
|
+
const amRef = react.useRef(null);
|
|
1906
|
+
const pmRef = react.useRef(null);
|
|
1902
1907
|
const setPeriod = react.useCallback(
|
|
1903
1908
|
(newPeriod) => {
|
|
1904
1909
|
if (ctx.isDisabled || ctx.isReadOnly) return;
|
|
1910
|
+
const { hours12 } = core.to12Hour(ctx.currentTime.hours);
|
|
1905
1911
|
const newHours24 = core.to24Hour(hours12, newPeriod);
|
|
1906
1912
|
ctx.setTime({ hours: newHours24 });
|
|
1907
1913
|
},
|
|
1908
|
-
[
|
|
1914
|
+
[ctx]
|
|
1909
1915
|
);
|
|
1916
|
+
if (ctx.format !== "12h") return null;
|
|
1917
|
+
const { period } = core.to12Hour(ctx.currentTime.hours);
|
|
1918
|
+
const focusOther = (target) => {
|
|
1919
|
+
(target === "AM" ? amRef : pmRef).current?.focus();
|
|
1920
|
+
};
|
|
1921
|
+
const handleKeyDown = (e, target) => {
|
|
1922
|
+
switch (e.key) {
|
|
1923
|
+
case "ArrowRight":
|
|
1924
|
+
case "ArrowDown":
|
|
1925
|
+
case "ArrowLeft":
|
|
1926
|
+
case "ArrowUp": {
|
|
1927
|
+
e.preventDefault();
|
|
1928
|
+
const next = target === "AM" ? "PM" : "AM";
|
|
1929
|
+
setPeriod(next);
|
|
1930
|
+
focusOther(next);
|
|
1931
|
+
break;
|
|
1932
|
+
}
|
|
1933
|
+
case "Home": {
|
|
1934
|
+
e.preventDefault();
|
|
1935
|
+
setPeriod("AM");
|
|
1936
|
+
focusOther("AM");
|
|
1937
|
+
break;
|
|
1938
|
+
}
|
|
1939
|
+
case "End": {
|
|
1940
|
+
e.preventDefault();
|
|
1941
|
+
setPeriod("PM");
|
|
1942
|
+
focusOther("PM");
|
|
1943
|
+
break;
|
|
1944
|
+
}
|
|
1945
|
+
case " ":
|
|
1946
|
+
case "Enter": {
|
|
1947
|
+
e.preventDefault();
|
|
1948
|
+
setPeriod(target);
|
|
1949
|
+
break;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
};
|
|
1910
1953
|
const renderButton = (target) => {
|
|
1911
1954
|
const isSelected = period === target;
|
|
1912
1955
|
const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
|
|
1913
1956
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1914
1957
|
"button",
|
|
1915
1958
|
{
|
|
1959
|
+
ref: target === "AM" ? amRef : pmRef,
|
|
1916
1960
|
type: "button",
|
|
1917
1961
|
role: "radio",
|
|
1918
1962
|
"aria-checked": isSelected,
|
|
1963
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1919
1964
|
"data-selected": isSelected || void 0,
|
|
1920
1965
|
disabled: ctx.isDisabled,
|
|
1921
1966
|
className: optionClass,
|
|
1922
1967
|
onClick: () => setPeriod(target),
|
|
1968
|
+
onKeyDown: (e) => handleKeyDown(e, target),
|
|
1923
1969
|
children: target
|
|
1924
1970
|
}
|
|
1925
1971
|
);
|
|
@@ -2212,9 +2258,12 @@ function MonthPickerGrid({ classNames, ...props }) {
|
|
|
2212
2258
|
return [null, null];
|
|
2213
2259
|
}
|
|
2214
2260
|
}, [value, adapter, displayTimezone]);
|
|
2215
|
-
const today =
|
|
2216
|
-
|
|
2217
|
-
|
|
2261
|
+
const [today, setToday] = react.useState(null);
|
|
2262
|
+
react.useEffect(() => {
|
|
2263
|
+
setToday(adapter.today(displayTimezone));
|
|
2264
|
+
}, [adapter, displayTimezone]);
|
|
2265
|
+
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
2266
|
+
const todayMonth = today !== null ? adapter.getMonth(today) : -1;
|
|
2218
2267
|
const navigateYear = react.useCallback(
|
|
2219
2268
|
(direction) => {
|
|
2220
2269
|
ctx.setViewMonth(adapter.addYears(viewMonth, direction));
|
|
@@ -2316,7 +2365,11 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2316
2365
|
return null;
|
|
2317
2366
|
}
|
|
2318
2367
|
}, [value, adapter, displayTimezone]);
|
|
2319
|
-
const
|
|
2368
|
+
const [today, setToday] = react.useState(null);
|
|
2369
|
+
react.useEffect(() => {
|
|
2370
|
+
setToday(adapter.today(displayTimezone));
|
|
2371
|
+
}, [adapter, displayTimezone]);
|
|
2372
|
+
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
2320
2373
|
const navigateDecade = react.useCallback(
|
|
2321
2374
|
(direction) => {
|
|
2322
2375
|
ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
|