@kalyx/react 1.0.0-rc.1 → 1.0.0-rc.3

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/dist/index.d.cts CHANGED
@@ -198,6 +198,12 @@ declare function DatePickerPreset({ value: presetKey, date: directDate, children
198
198
  interface DatePickerInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type'> {
199
199
  /** Date display format (defaults to parent's displayFormat) */
200
200
  format?: string;
201
+ /**
202
+ * Form field name. When set, a hidden `<input type="hidden" name={name} value={ISO}>`
203
+ * is rendered alongside the visible input so the value participates in native form
204
+ * submission (and integrates with `react-hook-form` Controller-less flows).
205
+ */
206
+ name?: string;
201
207
  }
202
208
 
203
209
  interface DatePickerTriggerProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type'> {
package/dist/index.d.ts CHANGED
@@ -198,6 +198,12 @@ declare function DatePickerPreset({ value: presetKey, date: directDate, children
198
198
  interface DatePickerInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type'> {
199
199
  /** Date display format (defaults to parent's displayFormat) */
200
200
  format?: string;
201
+ /**
202
+ * Form field name. When set, a hidden `<input type="hidden" name={name} value={ISO}>`
203
+ * is rendered alongside the visible input so the value participates in native form
204
+ * submission (and integrates with `react-hook-form` Controller-less flows).
205
+ */
206
+ name?: string;
201
207
  }
202
208
 
203
209
  interface DatePickerTriggerProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type'> {
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
- import { createContext, forwardRef, useState, useCallback, useContext, useId, useRef, useMemo, useEffect } from 'react';
1
+ "use client";
2
+ import { createContext, forwardRef, useState, useRef, useCallback, useContext, useId, useMemo, useEffect } from 'react';
2
3
  import { parseInputValue, formatTimeString, parseTimeString, getTimeInTimezone, getTime, DateFnsAdapter, DEFAULT_DATEPICKER_LABELS, civilMidnightFromUtcDay, getMonthName, getWeekdayNames, getCalendarDays, formatMonthYear, isDateDisabled, DEFAULT_RANGEPICKER_LABELS, DEFAULT_TIMEPICKER_LABELS, setTimeInTimezone, setTime, to12Hour, to24Hour, generateMinutes, generateHours, formatFullDate } from '@kalyx/core';
3
4
  export { DateFnsAdapter } from '@kalyx/core';
4
- import { jsx, jsxs } from 'react/jsx-runtime';
5
- import { useFloating, autoUpdate, offset, flip, shift } from '@floating-ui/react';
5
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
6
+ import { offset, flip, shift, useFloating, autoUpdate } from '@floating-ui/react';
6
7
 
7
8
  // src/components/DatePicker/Root.tsx
8
9
  var DatePickerContext = createContext(null);
@@ -56,10 +57,10 @@ function DatePickerRoot({
56
57
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
57
58
  const [isOpen, setIsOpen] = useState(false);
58
59
  const [viewMonth, setViewMonth] = useState(
59
- currentValue ?? adapter.today(displayTimezone)
60
+ () => currentValue ?? adapter.today(displayTimezone)
60
61
  );
61
62
  const [focusedDate, setFocusedDate] = useState(
62
- currentValue ?? adapter.today(displayTimezone)
63
+ () => currentValue ?? adapter.today(displayTimezone)
63
64
  );
64
65
  useChangeEffect(isOpen, onOpenChange);
65
66
  const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
@@ -150,10 +151,11 @@ function DatePickerRoot({
150
151
  return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: contextValue, children });
151
152
  }
152
153
  var DatePickerInput = forwardRef(
153
- function DatePickerInput2({ format: formatProp, onClick, onBlur, onKeyDown, ...props }, ref) {
154
+ function DatePickerInput2({ format: formatProp, name, onClick, onBlur, onKeyDown, ...props }, ref) {
154
155
  const ctx = useDatePickerContext("DatePicker.Input");
155
156
  const displayFormat = formatProp ?? ctx.displayFormat;
156
157
  const [inputText, setInputText] = useState(null);
158
+ const isComposingRef = useRef(false);
157
159
  let formattedValue = "";
158
160
  if (ctx.value) {
159
161
  try {
@@ -170,47 +172,62 @@ var DatePickerInput = forwardRef(
170
172
  },
171
173
  [ctx, onClick]
172
174
  );
175
+ const commitText = useCallback(
176
+ (text) => {
177
+ if (!text) {
178
+ ctx.selectDate(null);
179
+ setInputText(null);
180
+ return true;
181
+ }
182
+ const parsed = parseInputValue(text, ctx.adapter);
183
+ if (parsed) {
184
+ ctx.selectDate(parsed);
185
+ setInputText(null);
186
+ return true;
187
+ }
188
+ return false;
189
+ },
190
+ [ctx]
191
+ );
173
192
  const handleBlur = useCallback(
174
193
  (e) => {
175
194
  if (inputText !== null) {
176
- const parsed = parseInputValue(inputText, ctx.adapter);
177
- if (parsed) {
178
- ctx.selectDate(parsed);
179
- }
195
+ commitText(inputText);
180
196
  setInputText(null);
181
197
  }
182
198
  onBlur?.(e);
183
199
  },
184
- [inputText, displayFormat, ctx, onBlur]
200
+ [inputText, commitText, onBlur]
185
201
  );
186
202
  const handleChange = useCallback(
187
203
  (e) => {
188
204
  const text = e.target.value;
189
205
  setInputText(text);
190
- if (!text) {
191
- ctx.selectDate(null);
192
- setInputText(null);
193
- return;
194
- }
195
- const parsed = parseInputValue(text, ctx.adapter);
196
- if (parsed) {
197
- ctx.selectDate(parsed);
198
- setInputText(null);
199
- }
206
+ if (isComposingRef.current) return;
207
+ commitText(text);
208
+ },
209
+ [commitText]
210
+ );
211
+ const handleCompositionStart = useCallback(() => {
212
+ isComposingRef.current = true;
213
+ }, []);
214
+ const handleCompositionEnd = useCallback(
215
+ (e) => {
216
+ isComposingRef.current = false;
217
+ commitText(e.target.value);
200
218
  },
201
- [displayFormat, ctx]
219
+ [commitText]
202
220
  );
203
221
  const handleKeyDown = useCallback(
204
222
  (e) => {
205
223
  if (e.key === "Escape") {
206
224
  ctx.close();
207
225
  } else if (e.key === "Enter") {
226
+ if (ctx.isOpen) e.preventDefault();
208
227
  if (inputText !== null) {
209
- const parsed = parseInputValue(inputText, ctx.adapter);
210
- if (parsed) {
211
- ctx.selectDate(parsed);
212
- setInputText(null);
213
- }
228
+ commitText(inputText);
229
+ } else if (ctx.isOpen) {
230
+ ctx.selectDate(ctx.focusedDate);
214
231
  }
215
232
  } else if (e.key === "ArrowDown" && !ctx.isOpen) {
216
233
  e.preventDefault();
@@ -218,36 +235,42 @@ var DatePickerInput = forwardRef(
218
235
  }
219
236
  onKeyDown?.(e);
220
237
  },
221
- [ctx, inputText, displayFormat, onKeyDown]
238
+ [ctx, inputText, commitText, onKeyDown]
222
239
  );
223
240
  const calendarId = `${ctx.pickerId}-calendar`;
224
- return /* @__PURE__ */ jsx(
225
- "input",
226
- {
227
- ref: (node) => {
228
- ctx.referenceRef.current = node;
229
- if (typeof ref === "function") ref(node);
230
- else if (ref) ref.current = node;
231
- },
232
- type: "text",
233
- role: "combobox",
234
- "aria-expanded": ctx.isOpen,
235
- "aria-haspopup": "dialog",
236
- "aria-controls": ctx.isOpen ? calendarId : void 0,
237
- "aria-autocomplete": "none",
238
- autoComplete: "off",
239
- value: displayValue,
240
- disabled: ctx.isDisabled || props.disabled,
241
- readOnly: ctx.isReadOnly,
242
- onChange: handleChange,
243
- onClick: handleClick,
244
- onBlur: handleBlur,
245
- onKeyDown: handleKeyDown,
246
- ...props
247
- }
248
- );
241
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
242
+ /* @__PURE__ */ jsx(
243
+ "input",
244
+ {
245
+ ref: (node) => {
246
+ ctx.referenceRef.current = node;
247
+ if (typeof ref === "function") ref(node);
248
+ else if (ref) ref.current = node;
249
+ },
250
+ type: "text",
251
+ role: "combobox",
252
+ "aria-expanded": ctx.isOpen,
253
+ "aria-haspopup": "dialog",
254
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
255
+ "aria-autocomplete": "none",
256
+ autoComplete: "off",
257
+ value: displayValue,
258
+ disabled: ctx.isDisabled || props.disabled,
259
+ readOnly: ctx.isReadOnly,
260
+ onChange: handleChange,
261
+ onClick: handleClick,
262
+ onBlur: handleBlur,
263
+ onKeyDown: handleKeyDown,
264
+ onCompositionStart: handleCompositionStart,
265
+ onCompositionEnd: handleCompositionEnd,
266
+ ...props
267
+ }
268
+ ),
269
+ name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: ctx.value ?? "" }) : null
270
+ ] });
249
271
  }
250
272
  );
273
+ DatePickerInput.displayName = "DatePicker.Input";
251
274
  var DatePickerTrigger = forwardRef(
252
275
  function DatePickerTrigger2({ onClick, children, ...props }, ref) {
253
276
  const ctx = useDatePickerContext("DatePicker.Trigger");
@@ -271,6 +294,7 @@ var DatePickerTrigger = forwardRef(
271
294
  tabIndex: 0,
272
295
  "aria-label": ctx.isOpen ? ctx.labels.triggerClose : ctx.labels.triggerOpen,
273
296
  "aria-expanded": ctx.isOpen,
297
+ "aria-haspopup": "dialog",
274
298
  "aria-controls": ctx.isOpen ? calendarId : void 0,
275
299
  disabled: ctx.isDisabled || props.disabled,
276
300
  onClick: handleClick,
@@ -301,6 +325,8 @@ var DatePickerTrigger = forwardRef(
301
325
  );
302
326
  }
303
327
  );
328
+ DatePickerTrigger.displayName = "DatePicker.Trigger";
329
+ var POPOVER_MIDDLEWARE = [offset(4), flip(), shift({ padding: 8 })];
304
330
  function usePopover({
305
331
  isOpen,
306
332
  close,
@@ -312,7 +338,7 @@ function usePopover({
312
338
  const { refs, floatingStyles, isPositioned } = useFloating({
313
339
  open: isOpen,
314
340
  placement,
315
- middleware: [offset(4), flip(), shift({ padding: 8 })],
341
+ middleware: POPOVER_MIDDLEWARE,
316
342
  whileElementsMounted: autoUpdate
317
343
  });
318
344
  useEffect(() => {
@@ -359,6 +385,24 @@ function usePopover({
359
385
  document.addEventListener("keydown", handleKeyDown);
360
386
  return () => document.removeEventListener("keydown", handleKeyDown);
361
387
  }, [isOpen, close]);
388
+ useEffect(() => {
389
+ if (!isOpen) return;
390
+ function handleFocusOut(e) {
391
+ const next = e.relatedTarget;
392
+ const floating = floatingRef.current;
393
+ const reference = referenceRef.current;
394
+ if (!next) return;
395
+ const insideFloating = floating?.contains(next) ?? false;
396
+ const insideReference = reference?.contains(next) ?? false;
397
+ if (!insideFloating && !insideReference) {
398
+ close();
399
+ }
400
+ }
401
+ const node = floatingRef.current;
402
+ if (!node) return;
403
+ node.addEventListener("focusout", handleFocusOut);
404
+ return () => node.removeEventListener("focusout", handleFocusOut);
405
+ }, [isOpen, close, referenceRef]);
362
406
  const setFloatingRef = useCallback(
363
407
  (node) => {
364
408
  floatingRef.current = node;
@@ -426,14 +470,17 @@ function DatePickerCalendar({
426
470
  const gridRef = useRef(null);
427
471
  const [announcement, setAnnouncement] = useState("");
428
472
  const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale, displayTimezone } = ctx;
429
- const weekdays = getWeekdayNames(locale, weekStartsOn);
430
- const weeks = getCalendarDays(viewMonth, adapter, {
431
- weekStartsOn,
432
- selected: ctx.value,
433
- focusedDate,
434
- disabled,
435
- timezone: displayTimezone
436
- });
473
+ const weekdays = useMemo(() => getWeekdayNames(locale, weekStartsOn), [locale, weekStartsOn]);
474
+ const weeks = useMemo(
475
+ () => getCalendarDays(viewMonth, adapter, {
476
+ weekStartsOn,
477
+ selected: ctx.value,
478
+ focusedDate,
479
+ disabled,
480
+ timezone: displayTimezone
481
+ }),
482
+ [viewMonth, adapter, weekStartsOn, ctx.value, focusedDate, disabled, displayTimezone]
483
+ );
437
484
  const year = adapter.getYear(viewMonth);
438
485
  const month = adapter.getMonth(viewMonth);
439
486
  const title = formatMonthYear(year, month, locale);
@@ -513,6 +560,15 @@ function DatePickerCalendar({
513
560
  }
514
561
  if (newFocused) {
515
562
  e.preventDefault();
563
+ const skipStep = e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "PageUp" || e.key === "Home" ? -1 : 1;
564
+ let attempts = 0;
565
+ while (isDateDisabled(newFocused, disabled, adapter) && attempts < 42) {
566
+ newFocused = adapter.addDays(newFocused, skipStep);
567
+ attempts++;
568
+ }
569
+ if (attempts >= 42) {
570
+ return;
571
+ }
516
572
  ctx.setFocusedDate(newFocused);
517
573
  if (!adapter.isSameMonth(newFocused, viewMonth)) {
518
574
  ctx.setViewMonth(newFocused);
@@ -560,56 +616,69 @@ function DatePickerCalendar({
560
616
  ref: gridRef,
561
617
  role: "grid",
562
618
  "aria-label": title,
619
+ "aria-rowcount": weeks.length + 1,
620
+ "aria-colcount": 7,
563
621
  className: classNames?.grid,
564
622
  onKeyDown: handleKeyDown,
565
623
  children: [
566
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { role: "row", children: weekdays.map((day) => /* @__PURE__ */ jsx(
624
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { role: "row", "aria-rowindex": 1, children: weekdays.map((day, colIndex) => /* @__PURE__ */ jsx(
567
625
  "th",
568
626
  {
569
627
  role: "columnheader",
570
628
  abbr: day.full,
571
629
  scope: "col",
630
+ "aria-colindex": colIndex + 1,
572
631
  className: classNames?.weekdayHeader,
573
632
  children: day.short
574
633
  },
575
634
  day.short
576
635
  )) }) }),
577
- /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsx("tr", { role: "row", className: classNames?.gridRow, children: week.map((day) => {
578
- const dayClasses = [
579
- classNames?.day,
580
- day.isSelected && classNames?.daySelected,
581
- day.isToday && classNames?.dayToday,
582
- day.isDisabled && classNames?.dayDisabled,
583
- !day.isCurrentMonth && classNames?.dayOutsideMonth
584
- ].filter(Boolean).join(" ") || void 0;
585
- return /* @__PURE__ */ jsx(
586
- "td",
587
- {
588
- role: "gridcell",
589
- "aria-selected": day.isSelected || void 0,
590
- "aria-disabled": day.isDisabled || void 0,
591
- "aria-current": day.isToday ? "date" : void 0,
592
- className: classNames?.gridCell,
593
- children: /* @__PURE__ */ jsx(
594
- "button",
636
+ /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsx(
637
+ "tr",
638
+ {
639
+ role: "row",
640
+ "aria-rowindex": weekIndex + 2,
641
+ className: classNames?.gridRow,
642
+ children: week.map((day, colIndex) => {
643
+ const dayClasses = [
644
+ classNames?.day,
645
+ day.isSelected && classNames?.daySelected,
646
+ day.isToday && classNames?.dayToday,
647
+ day.isDisabled && classNames?.dayDisabled,
648
+ !day.isCurrentMonth && classNames?.dayOutsideMonth
649
+ ].filter(Boolean).join(" ") || void 0;
650
+ return /* @__PURE__ */ jsx(
651
+ "td",
595
652
  {
596
- type: "button",
597
- tabIndex: day.isFocused ? 0 : -1,
598
- disabled: day.isDisabled,
599
- "data-focused": day.isFocused || void 0,
600
- "data-selected": day.isSelected || void 0,
601
- "data-today": day.isToday || void 0,
602
- "data-outside-month": !day.isCurrentMonth || void 0,
603
- className: dayClasses,
604
- onClick: () => handleDayClick(day),
605
- "aria-label": safeFormatFullDate(day.isoString, locale),
606
- children: day.dayNumber
607
- }
608
- )
609
- },
610
- day.isoString
611
- );
612
- }) }, weekIndex)) })
653
+ role: "gridcell",
654
+ "aria-colindex": colIndex + 1,
655
+ "aria-selected": day.isSelected || void 0,
656
+ "aria-disabled": day.isDisabled || void 0,
657
+ "aria-current": day.isToday ? "date" : void 0,
658
+ className: classNames?.gridCell,
659
+ children: /* @__PURE__ */ jsx(
660
+ "button",
661
+ {
662
+ type: "button",
663
+ tabIndex: day.isFocused ? 0 : -1,
664
+ disabled: day.isDisabled,
665
+ "data-focused": day.isFocused || void 0,
666
+ "data-selected": day.isSelected || void 0,
667
+ "data-today": day.isToday || void 0,
668
+ "data-outside-month": !day.isCurrentMonth || void 0,
669
+ className: dayClasses,
670
+ onClick: () => handleDayClick(day),
671
+ "aria-label": safeFormatFullDate(day.isoString, locale),
672
+ children: day.dayNumber
673
+ }
674
+ )
675
+ },
676
+ day.isoString
677
+ );
678
+ })
679
+ },
680
+ weekIndex
681
+ )) })
613
682
  ]
614
683
  }
615
684
  ),
@@ -930,10 +999,10 @@ function RangePickerRoot({
930
999
  const [selectingTarget, setSelectingTarget] = useState("start");
931
1000
  const [hoverDate, setHoverDate] = useState(null);
932
1001
  const [viewMonth, setViewMonth] = useState(
933
- currentValue.start ?? adapter.today(displayTimezone)
1002
+ () => currentValue.start ?? adapter.today(displayTimezone)
934
1003
  );
935
1004
  const [focusedDate, setFocusedDate] = useState(
936
- currentValue.start ?? adapter.today(displayTimezone)
1005
+ () => currentValue.start ?? adapter.today(displayTimezone)
937
1006
  );
938
1007
  useChangeEffect(isOpen, onOpenChange);
939
1008
  const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
@@ -1083,6 +1152,8 @@ var RangePickerInput = forwardRef(
1083
1152
  (e) => {
1084
1153
  if (e.key === "Escape") {
1085
1154
  ctx.close();
1155
+ } else if (e.key === "Enter" && ctx.isOpen) {
1156
+ e.preventDefault();
1086
1157
  } else if (e.key === "ArrowDown" && !ctx.isOpen) {
1087
1158
  e.preventDefault();
1088
1159
  ctx.open();
@@ -1119,6 +1190,7 @@ var RangePickerInput = forwardRef(
1119
1190
  );
1120
1191
  }
1121
1192
  );
1193
+ RangePickerInput.displayName = "RangePicker.Input";
1122
1194
  function RangePickerPopover({ children, ...props }) {
1123
1195
  const ctx = useRangePickerContext("RangePicker.Popover");
1124
1196
  const calendarId = `${ctx.pickerId}-calendar`;
@@ -1185,15 +1257,18 @@ function RangePickerCalendar({
1185
1257
  displayTimezone
1186
1258
  } = ctx;
1187
1259
  const { locale } = ctx;
1188
- const weekdays = getWeekdayNames(locale, weekStartsOn);
1189
- const weeks = getCalendarDays(viewMonth, adapter, {
1190
- weekStartsOn,
1191
- focusedDate,
1192
- disabled,
1193
- range: value,
1194
- rangeHover: hoverDate,
1195
- timezone: displayTimezone
1196
- });
1260
+ const weekdays = useMemo(() => getWeekdayNames(locale, weekStartsOn), [locale, weekStartsOn]);
1261
+ const weeks = useMemo(
1262
+ () => getCalendarDays(viewMonth, adapter, {
1263
+ weekStartsOn,
1264
+ focusedDate,
1265
+ disabled,
1266
+ range: value,
1267
+ rangeHover: hoverDate,
1268
+ timezone: displayTimezone
1269
+ }),
1270
+ [viewMonth, adapter, weekStartsOn, focusedDate, disabled, value, hoverDate, displayTimezone]
1271
+ );
1197
1272
  const year = adapter.getYear(viewMonth);
1198
1273
  const month = adapter.getMonth(viewMonth);
1199
1274
  const title = formatMonthYear(year, month, locale);
@@ -1293,6 +1368,13 @@ function RangePickerCalendar({
1293
1368
  }
1294
1369
  if (newFocused) {
1295
1370
  e.preventDefault();
1371
+ const skipStep = e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "PageUp" || e.key === "Home" ? -1 : 1;
1372
+ let attempts = 0;
1373
+ while (isDateDisabled(newFocused, disabled, adapter) && attempts < 42) {
1374
+ newFocused = adapter.addDays(newFocused, skipStep);
1375
+ attempts++;
1376
+ }
1377
+ if (attempts >= 42) return;
1296
1378
  ctx.setFocusedDate(newFocused);
1297
1379
  if (!adapter.isSameMonth(newFocused, viewMonth)) {
1298
1380
  ctx.setViewMonth(newFocused);
@@ -1346,62 +1428,75 @@ function RangePickerCalendar({
1346
1428
  role: "grid",
1347
1429
  "aria-label": title,
1348
1430
  "aria-multiselectable": "true",
1431
+ "aria-rowcount": weeks.length + 1,
1432
+ "aria-colcount": 7,
1349
1433
  className: classNames?.grid,
1350
1434
  onKeyDown: handleKeyDown,
1351
1435
  children: [
1352
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { role: "row", children: weekdays.map((day) => /* @__PURE__ */ jsx(
1436
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { role: "row", "aria-rowindex": 1, children: weekdays.map((day, colIndex) => /* @__PURE__ */ jsx(
1353
1437
  "th",
1354
1438
  {
1355
1439
  role: "columnheader",
1356
1440
  abbr: day.full,
1357
1441
  scope: "col",
1442
+ "aria-colindex": colIndex + 1,
1358
1443
  className: classNames?.weekdayHeader,
1359
1444
  children: day.short
1360
1445
  },
1361
1446
  day.short
1362
1447
  )) }) }),
1363
- /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsx("tr", { role: "row", className: classNames?.gridRow, children: week.map((day) => {
1364
- const dayClasses = [
1365
- classNames?.day,
1366
- day.isRangeStart && classNames?.dayRangeStart,
1367
- day.isRangeEnd && classNames?.dayRangeEnd,
1368
- day.isInRange && classNames?.dayInRange,
1369
- day.isToday && classNames?.dayToday,
1370
- day.isDisabled && classNames?.dayDisabled,
1371
- !day.isCurrentMonth && classNames?.dayOutsideMonth
1372
- ].filter(Boolean).join(" ") || void 0;
1373
- const isSelected = selectionMode === "week" ? day.isRangeStart || day.isRangeEnd || day.isInRange : day.isRangeStart || day.isRangeEnd;
1374
- return /* @__PURE__ */ jsx(
1375
- "td",
1376
- {
1377
- role: "gridcell",
1378
- "aria-selected": isSelected || void 0,
1379
- "aria-disabled": day.isDisabled || void 0,
1380
- "aria-current": day.isToday ? "date" : void 0,
1381
- className: classNames?.gridCell,
1382
- children: /* @__PURE__ */ jsx(
1383
- "button",
1448
+ /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsx(
1449
+ "tr",
1450
+ {
1451
+ role: "row",
1452
+ "aria-rowindex": weekIndex + 2,
1453
+ className: classNames?.gridRow,
1454
+ children: week.map((day, colIndex) => {
1455
+ const dayClasses = [
1456
+ classNames?.day,
1457
+ day.isRangeStart && classNames?.dayRangeStart,
1458
+ day.isRangeEnd && classNames?.dayRangeEnd,
1459
+ day.isInRange && classNames?.dayInRange,
1460
+ day.isToday && classNames?.dayToday,
1461
+ day.isDisabled && classNames?.dayDisabled,
1462
+ !day.isCurrentMonth && classNames?.dayOutsideMonth
1463
+ ].filter(Boolean).join(" ") || void 0;
1464
+ const isSelected = selectionMode === "week" ? day.isRangeStart || day.isRangeEnd || day.isInRange : day.isRangeStart || day.isRangeEnd;
1465
+ return /* @__PURE__ */ jsx(
1466
+ "td",
1384
1467
  {
1385
- type: "button",
1386
- tabIndex: day.isFocused ? 0 : -1,
1387
- disabled: day.isDisabled,
1388
- "data-focused": day.isFocused || void 0,
1389
- "data-range-start": day.isRangeStart || void 0,
1390
- "data-range-end": day.isRangeEnd || void 0,
1391
- "data-in-range": day.isInRange || void 0,
1392
- "data-today": day.isToday || void 0,
1393
- "data-outside-month": !day.isCurrentMonth || void 0,
1394
- className: dayClasses,
1395
- onClick: () => handleDayClick(day),
1396
- onMouseEnter: () => handleDayMouseEnter(day),
1397
- "aria-label": safeFormatFullDate2(day.isoString, locale),
1398
- children: day.dayNumber
1399
- }
1400
- )
1401
- },
1402
- day.isoString
1403
- );
1404
- }) }, weekIndex)) })
1468
+ role: "gridcell",
1469
+ "aria-colindex": colIndex + 1,
1470
+ "aria-selected": isSelected || void 0,
1471
+ "aria-disabled": day.isDisabled || void 0,
1472
+ "aria-current": day.isToday ? "date" : void 0,
1473
+ className: classNames?.gridCell,
1474
+ children: /* @__PURE__ */ jsx(
1475
+ "button",
1476
+ {
1477
+ type: "button",
1478
+ tabIndex: day.isFocused ? 0 : -1,
1479
+ disabled: day.isDisabled,
1480
+ "data-focused": day.isFocused || void 0,
1481
+ "data-range-start": day.isRangeStart || void 0,
1482
+ "data-range-end": day.isRangeEnd || void 0,
1483
+ "data-in-range": day.isInRange || void 0,
1484
+ "data-today": day.isToday || void 0,
1485
+ "data-outside-month": !day.isCurrentMonth || void 0,
1486
+ className: dayClasses,
1487
+ onClick: () => handleDayClick(day),
1488
+ onMouseEnter: () => handleDayMouseEnter(day),
1489
+ "aria-label": safeFormatFullDate2(day.isoString, locale),
1490
+ children: day.dayNumber
1491
+ }
1492
+ )
1493
+ },
1494
+ day.isoString
1495
+ );
1496
+ })
1497
+ },
1498
+ weekIndex
1499
+ )) })
1405
1500
  ]
1406
1501
  }
1407
1502
  ),
@@ -1654,6 +1749,7 @@ var TimePickerInput = forwardRef(
1654
1749
  );
1655
1750
  }
1656
1751
  );
1752
+ TimePickerInput.displayName = "TimePicker.Input";
1657
1753
  function useListboxNavigation({
1658
1754
  items,
1659
1755
  onSelect,
@@ -1702,7 +1798,7 @@ function useListboxNavigation({
1702
1798
  function TimePickerHourList({ classNames, ...props }) {
1703
1799
  const ctx = useTimePickerContext("TimePicker.HourList");
1704
1800
  const { format, currentTime, isDisabled, isReadOnly } = ctx;
1705
- const hours = generateHours(format);
1801
+ const hours = useMemo(() => generateHours(format), [format]);
1706
1802
  const selectedHourDisplay = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;
1707
1803
  const currentPeriod = format === "12h" ? to12Hour(currentTime.hours).period : null;
1708
1804
  const handleSelect = useCallback(
@@ -1753,7 +1849,7 @@ function TimePickerHourList({ classNames, ...props }) {
1753
1849
  function TimePickerMinuteList({ classNames, ...props }) {
1754
1850
  const ctx = useTimePickerContext("TimePicker.MinuteList");
1755
1851
  const { step, currentTime, isDisabled, isReadOnly } = ctx;
1756
- const minutes = generateMinutes(step);
1852
+ const minutes = useMemo(() => generateMinutes(step), [step]);
1757
1853
  const handleSelect = useCallback(
1758
1854
  (minute) => {
1759
1855
  if (isDisabled || isReadOnly) return;
@@ -1887,10 +1983,10 @@ function DateTimePickerRoot({
1887
1983
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1888
1984
  const [isOpen, setIsOpen] = useState(false);
1889
1985
  const [viewMonth, setViewMonth] = useState(
1890
- currentValue ?? adapter.today(displayTimezone)
1986
+ () => currentValue ?? adapter.today(displayTimezone)
1891
1987
  );
1892
1988
  const [focusedDate, setFocusedDate] = useState(
1893
- currentValue ?? adapter.today(displayTimezone)
1989
+ () => currentValue ?? adapter.today(displayTimezone)
1894
1990
  );
1895
1991
  useChangeEffect(isOpen, onOpenChange);
1896
1992
  const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
@@ -2048,6 +2144,8 @@ var DateTimePickerInput = forwardRef(
2048
2144
  (e) => {
2049
2145
  if (e.key === "Escape") {
2050
2146
  ctx.close();
2147
+ } else if (e.key === "Enter" && ctx.isOpen) {
2148
+ e.preventDefault();
2051
2149
  } else if (e.key === "ArrowDown" && !ctx.isOpen) {
2052
2150
  e.preventDefault();
2053
2151
  ctx.open();
@@ -2083,6 +2181,7 @@ var DateTimePickerInput = forwardRef(
2083
2181
  );
2084
2182
  }
2085
2183
  );
2184
+ DateTimePickerInput.displayName = "DateTimePicker.Input";
2086
2185
 
2087
2186
  // src/components/DateTimePicker/index.ts
2088
2187
  var DateTimePicker = Object.assign(DateTimePickerRoot, {