@kalyx/react 1.0.0-rc.10 → 1.0.0-rc.11
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 +35 -0
- package/dist/index.cjs +46 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +47 -23
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# @kalyx/react
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 19ac1c0: fix(core): allow `generateMinutes` step values up to 60
|
|
8
|
+
|
|
9
|
+
`generateMinutes(step)` rejected any step above 30, which prevented legitimate cases like `step=45` (quarter-and-three-quarters past the hour) and `step=60` (on-the-hour only). The slot-generation loop already works for any 1–60 integer, so the upper bound is now 60 with the same error message format. Steps `0`, `61+`, and negative values still throw. No callers in `@kalyx/react` relied on the previous narrower bound.
|
|
10
|
+
|
|
11
|
+
- eafc3c1: fix(input): drop stale typed text when the parent re-sets value externally
|
|
12
|
+
|
|
13
|
+
`<DatePicker.Input>` and `<TimePicker.Input>` keep half-typed text in a local `inputText` state while the user is editing — without it, parse-failed input would vanish on every keystroke. The state was reset only when the user committed via blur/Enter, which left a real gap:
|
|
14
|
+
|
|
15
|
+
If the value changed from anywhere else (parent re-rendered with a new `value`, a calendar click, a `Preset`, a custom-Hook `setRange`, an HourList option), the Input kept rendering the user's stale text. Source-of-truth and visible value diverged silently.
|
|
16
|
+
|
|
17
|
+
A `useEffect` keyed on `ctx.value` now resets `inputText` whenever the source-of-truth changes. The Input goes back to formatting the new value normally. For DatePicker the reset is skipped while an IME composition is in flight (Korean/Japanese/Chinese), so an in-flight character is never wiped mid-stroke.
|
|
18
|
+
|
|
19
|
+
Impact: `DatePicker`, `MonthPicker`, `YearPicker` (the last two reuse `DatePickerInput`), and `TimePicker` all get the fix. `RangePicker`/`WeekPicker`/`DateTimePicker` Inputs are read-only or non-editable and already track context directly, so they were never affected.
|
|
20
|
+
|
|
21
|
+
- 23bc187: docs(i18n): translate Korean intro and stop bumping demo apps on every patch
|
|
22
|
+
- `apps/docs-site/i18n/ko/.../intro.md` is now fully translated to Korean. Previously the file lived in the `i18n/ko/` tree but the body was the verbatim English copy, so Korean docs visitors saw English content on the landing page.
|
|
23
|
+
- `.changeset/config.json` `ignore` now includes the two demo workspaces (`@kalyx/docs`, `docs-site`). Changesets used to bump them on every release because they depend on `@kalyx/react`, polluting their CHANGELOG with `Updated dependencies` entries and adding noise to release PRs. Demo apps aren't published — they don't need versioning.
|
|
24
|
+
|
|
25
|
+
- c8a6609: fix(rangepicker): announce next selection target and final range to screen readers
|
|
26
|
+
|
|
27
|
+
`<RangePicker.Calendar>` now announces context-aware messages through its existing `role="status"` live region:
|
|
28
|
+
- After the first click (start), it announces `<formatted-date>. Now select end date.` so screen-reader users know the next click commits the other endpoint.
|
|
29
|
+
- After the second click (end), it announces `Range selected: <start> – <end>` instead of just the bare date — matching the swap-if-before behaviour so the announcement always reflects what was committed.
|
|
30
|
+
- Week-mode commits now share the same `Range selected: ...` prefix for consistency.
|
|
31
|
+
|
|
32
|
+
The two new strings are wired through `RangePickerLabels.selectingEnd` and `RangePickerLabels.rangeSelected` with English defaults, and they are fully overridable via the existing `labels` prop for i18n. `@kalyx/core` gets a `minor` bump because `RangePickerLabels` gained required fields (with defaults supplied by `DEFAULT_RANGEPICKER_LABELS`); any consumer constructing a literal `RangePickerLabels` from scratch will need to add the two keys.
|
|
33
|
+
|
|
34
|
+
- Updated dependencies [19ac1c0]
|
|
35
|
+
- Updated dependencies [c8a6609]
|
|
36
|
+
- @kalyx/core@1.0.0-rc.11
|
|
37
|
+
|
|
3
38
|
## 1.0.0-rc.10
|
|
4
39
|
|
|
5
40
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -157,6 +157,10 @@ var DatePickerInput = react.forwardRef(
|
|
|
157
157
|
const displayFormat = formatProp ?? ctx.displayFormat;
|
|
158
158
|
const [inputText, setInputText] = react.useState(null);
|
|
159
159
|
const isComposingRef = react.useRef(false);
|
|
160
|
+
react.useEffect(() => {
|
|
161
|
+
if (isComposingRef.current) return;
|
|
162
|
+
setInputText(null);
|
|
163
|
+
}, [ctx.value]);
|
|
160
164
|
let formattedValue = "";
|
|
161
165
|
if (ctx.value) {
|
|
162
166
|
try {
|
|
@@ -1099,6 +1103,17 @@ function useRangePickerContext(componentName) {
|
|
|
1099
1103
|
return context;
|
|
1100
1104
|
}
|
|
1101
1105
|
var EMPTY_RANGE = { start: null, end: null };
|
|
1106
|
+
var SR_ONLY = {
|
|
1107
|
+
position: "absolute",
|
|
1108
|
+
width: 1,
|
|
1109
|
+
height: 1,
|
|
1110
|
+
padding: 0,
|
|
1111
|
+
margin: -1,
|
|
1112
|
+
overflow: "hidden",
|
|
1113
|
+
clip: "rect(0, 0, 0, 0)",
|
|
1114
|
+
whiteSpace: "nowrap",
|
|
1115
|
+
border: 0
|
|
1116
|
+
};
|
|
1102
1117
|
function RangePickerRoot({
|
|
1103
1118
|
value: controlledValue,
|
|
1104
1119
|
defaultValue,
|
|
@@ -1125,6 +1140,8 @@ function RangePickerRoot({
|
|
|
1125
1140
|
const [isOpen, setIsOpen] = react.useState(false);
|
|
1126
1141
|
const [selectingTarget, setSelectingTarget] = react.useState("start");
|
|
1127
1142
|
const [hoverDate, setHoverDate] = react.useState(null);
|
|
1143
|
+
const [announcement, setAnnouncement] = react.useState("");
|
|
1144
|
+
const announce = react.useCallback((message) => setAnnouncement(message), []);
|
|
1128
1145
|
const [viewMonth, setViewMonth] = react.useState(
|
|
1129
1146
|
() => currentValue.start ?? adapter.today(displayTimezone)
|
|
1130
1147
|
);
|
|
@@ -1227,7 +1244,8 @@ function RangePickerRoot({
|
|
|
1227
1244
|
isDisabled,
|
|
1228
1245
|
isReadOnly: readOnly,
|
|
1229
1246
|
pickerId,
|
|
1230
|
-
labels: mergedLabels
|
|
1247
|
+
labels: mergedLabels,
|
|
1248
|
+
announce
|
|
1231
1249
|
}),
|
|
1232
1250
|
[
|
|
1233
1251
|
currentValue,
|
|
@@ -1250,10 +1268,14 @@ function RangePickerRoot({
|
|
|
1250
1268
|
isDisabled,
|
|
1251
1269
|
readOnly,
|
|
1252
1270
|
pickerId,
|
|
1253
|
-
mergedLabels
|
|
1271
|
+
mergedLabels,
|
|
1272
|
+
announce
|
|
1254
1273
|
]
|
|
1255
1274
|
);
|
|
1256
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
1275
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(RangePickerContext.Provider, { value: contextValue, children: [
|
|
1276
|
+
children,
|
|
1277
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: SR_ONLY, children: announcement })
|
|
1278
|
+
] });
|
|
1257
1279
|
}
|
|
1258
1280
|
var RangePickerInput = react.forwardRef(
|
|
1259
1281
|
function RangePickerInput2({ part, format: formatProp, onClick, onKeyDown, ...props }, ref) {
|
|
@@ -1353,17 +1375,6 @@ function safeFormatFullDate2(iso, locale) {
|
|
|
1353
1375
|
return iso;
|
|
1354
1376
|
}
|
|
1355
1377
|
}
|
|
1356
|
-
var srOnly2 = {
|
|
1357
|
-
position: "absolute",
|
|
1358
|
-
width: "1px",
|
|
1359
|
-
height: "1px",
|
|
1360
|
-
padding: 0,
|
|
1361
|
-
margin: "-1px",
|
|
1362
|
-
overflow: "hidden",
|
|
1363
|
-
clip: "rect(0, 0, 0, 0)",
|
|
1364
|
-
whiteSpace: "nowrap",
|
|
1365
|
-
border: 0
|
|
1366
|
-
};
|
|
1367
1378
|
function RangePickerCalendar({
|
|
1368
1379
|
classNames,
|
|
1369
1380
|
selectionMode = "range",
|
|
@@ -1373,7 +1384,6 @@ function RangePickerCalendar({
|
|
|
1373
1384
|
}) {
|
|
1374
1385
|
const ctx = useRangePickerContext("RangePicker.Calendar");
|
|
1375
1386
|
const gridRef = react.useRef(null);
|
|
1376
|
-
const [announcement, setAnnouncement] = react.useState("");
|
|
1377
1387
|
const {
|
|
1378
1388
|
adapter,
|
|
1379
1389
|
viewMonth,
|
|
@@ -1425,7 +1435,7 @@ function RangePickerCalendar({
|
|
|
1425
1435
|
ctx.setFocusedDate(adapter.startOfMonth(newMonth));
|
|
1426
1436
|
const y = adapter.getYear(newMonth);
|
|
1427
1437
|
const m = adapter.getMonth(newMonth);
|
|
1428
|
-
|
|
1438
|
+
ctx.announce(core.formatMonthYear(y, m, locale));
|
|
1429
1439
|
},
|
|
1430
1440
|
[adapter, viewMonth, ctx, locale]
|
|
1431
1441
|
);
|
|
@@ -1437,15 +1447,27 @@ function RangePickerCalendar({
|
|
|
1437
1447
|
const range = { start: weekStart, end: weekEnd };
|
|
1438
1448
|
ctx.setRange(range);
|
|
1439
1449
|
ctx.close();
|
|
1440
|
-
|
|
1441
|
-
`${safeFormatFullDate2(weekStart, locale)} \u2013 ${safeFormatFullDate2(weekEnd, locale)}`
|
|
1450
|
+
ctx.announce(
|
|
1451
|
+
`${ctx.labels.rangeSelected}: ${safeFormatFullDate2(weekStart, locale)} \u2013 ${safeFormatFullDate2(weekEnd, locale)}`
|
|
1442
1452
|
);
|
|
1443
1453
|
} else {
|
|
1454
|
+
const wasPickingStart = selectingTarget === "start";
|
|
1455
|
+
const previousStart = value.start;
|
|
1444
1456
|
ctx.selectDate(iso);
|
|
1445
|
-
|
|
1457
|
+
const formatted = safeFormatFullDate2(iso, locale);
|
|
1458
|
+
if (wasPickingStart) {
|
|
1459
|
+
ctx.announce(`${formatted}. ${ctx.labels.selectingEnd}`);
|
|
1460
|
+
} else if (previousStart) {
|
|
1461
|
+
const [start, end] = adapter.isBefore(iso, previousStart) ? [iso, previousStart] : [previousStart, iso];
|
|
1462
|
+
ctx.announce(
|
|
1463
|
+
`${ctx.labels.rangeSelected}: ${safeFormatFullDate2(start, locale)} \u2013 ${safeFormatFullDate2(end, locale)}`
|
|
1464
|
+
);
|
|
1465
|
+
} else {
|
|
1466
|
+
ctx.announce(formatted);
|
|
1467
|
+
}
|
|
1446
1468
|
}
|
|
1447
1469
|
},
|
|
1448
|
-
[selectionMode, adapter, weekStartsOn, ctx, locale]
|
|
1470
|
+
[selectionMode, adapter, weekStartsOn, ctx, locale, selectingTarget, value.start]
|
|
1449
1471
|
);
|
|
1450
1472
|
const handleDayClick = react.useCallback(
|
|
1451
1473
|
(day) => {
|
|
@@ -1654,8 +1676,7 @@ function RangePickerCalendar({
|
|
|
1654
1676
|
)) })
|
|
1655
1677
|
]
|
|
1656
1678
|
}
|
|
1657
|
-
)
|
|
1658
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: srOnly2, children: announcement })
|
|
1679
|
+
)
|
|
1659
1680
|
] });
|
|
1660
1681
|
}
|
|
1661
1682
|
function RangePickerPresets({ classNames, children, ...props }) {
|
|
@@ -1848,6 +1869,9 @@ var TimePickerInput = react.forwardRef(
|
|
|
1848
1869
|
function TimePickerInput2({ onBlur, onKeyDown, ...props }, ref) {
|
|
1849
1870
|
const ctx = useTimePickerContext("TimePicker.Input");
|
|
1850
1871
|
const [inputText, setInputText] = react.useState(null);
|
|
1872
|
+
react.useEffect(() => {
|
|
1873
|
+
setInputText(null);
|
|
1874
|
+
}, [ctx.value]);
|
|
1851
1875
|
const displayValue = inputText !== null ? inputText : core.formatTimeString(ctx.currentTime, ctx.withSeconds);
|
|
1852
1876
|
const commitInput = react.useCallback(() => {
|
|
1853
1877
|
if (inputText === null) return;
|