@kalyx/react 1.0.0-rc.1 → 1.0.0-rc.10
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 +273 -0
- package/README.md +2 -2
- package/dist/index.cjs +775 -363
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -7
- package/dist/index.d.ts +80 -7
- package/dist/index.js +764 -368
- package/dist/index.js.map +1 -1
- package/package.json +18 -3
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import {
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, forwardRef, useState, useRef, useCallback, useContext, useId, useMemo, useEffect } from 'react';
|
|
3
|
+
import { parseInputValue, formatTimeString, parseTimeString, getTimeInTimezone, getTime, DateFnsAdapter, DEFAULT_DATEPICKER_LABELS, civilMidnightFromUtcDay, getMonthName, getWeekdayNames, getCalendarDays, formatMonthYear, isDateDisabled, getISOWeekNumber, DEFAULT_RANGEPICKER_LABELS, DEFAULT_TIMEPICKER_LABELS, setTimeInTimezone, setTime, to12Hour, to24Hour, generateMinutes, generateHours, formatFullDate } from '@kalyx/core';
|
|
4
|
+
export { DEFAULT_DATEPICKER_LABELS, DEFAULT_DATETIMEPICKER_LABELS, DEFAULT_RANGEPICKER_LABELS, DEFAULT_TIMEPICKER_LABELS, DateFnsAdapter } from '@kalyx/core';
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
191
|
-
|
|
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);
|
|
200
208
|
},
|
|
201
|
-
[
|
|
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);
|
|
218
|
+
},
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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,
|
|
238
|
+
[ctx, inputText, commitText, onKeyDown]
|
|
222
239
|
);
|
|
223
240
|
const calendarId = `${ctx.pickerId}-calendar`;
|
|
224
|
-
return /* @__PURE__ */
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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:
|
|
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;
|
|
@@ -420,20 +464,36 @@ var srOnly = {
|
|
|
420
464
|
function DatePickerCalendar({
|
|
421
465
|
classNames,
|
|
422
466
|
onTitleClick,
|
|
467
|
+
showWeekNumber = false,
|
|
468
|
+
fixedWeeks = false,
|
|
423
469
|
...props
|
|
424
470
|
}) {
|
|
425
471
|
const ctx = useDatePickerContext("DatePicker.Calendar");
|
|
426
472
|
const gridRef = useRef(null);
|
|
427
473
|
const [announcement, setAnnouncement] = useState("");
|
|
428
474
|
const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale, displayTimezone } = ctx;
|
|
429
|
-
const weekdays = getWeekdayNames(locale, weekStartsOn);
|
|
430
|
-
const weeks =
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
475
|
+
const weekdays = useMemo(() => getWeekdayNames(locale, weekStartsOn), [locale, weekStartsOn]);
|
|
476
|
+
const weeks = useMemo(
|
|
477
|
+
() => getCalendarDays(viewMonth, adapter, {
|
|
478
|
+
weekStartsOn,
|
|
479
|
+
selected: ctx.value,
|
|
480
|
+
focusedDate,
|
|
481
|
+
disabled,
|
|
482
|
+
timezone: displayTimezone,
|
|
483
|
+
fixedWeeks
|
|
484
|
+
}),
|
|
485
|
+
[
|
|
486
|
+
viewMonth,
|
|
487
|
+
adapter,
|
|
488
|
+
weekStartsOn,
|
|
489
|
+
ctx.value,
|
|
490
|
+
focusedDate,
|
|
491
|
+
disabled,
|
|
492
|
+
displayTimezone,
|
|
493
|
+
fixedWeeks
|
|
494
|
+
]
|
|
495
|
+
);
|
|
496
|
+
const thursdayIndex = weekStartsOn === 0 ? 4 : 3;
|
|
437
497
|
const year = adapter.getYear(viewMonth);
|
|
438
498
|
const month = adapter.getMonth(viewMonth);
|
|
439
499
|
const title = formatMonthYear(year, month, locale);
|
|
@@ -513,6 +573,15 @@ function DatePickerCalendar({
|
|
|
513
573
|
}
|
|
514
574
|
if (newFocused) {
|
|
515
575
|
e.preventDefault();
|
|
576
|
+
const skipStep = e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "PageUp" || e.key === "Home" ? -1 : 1;
|
|
577
|
+
let attempts = 0;
|
|
578
|
+
while (isDateDisabled(newFocused, disabled, adapter) && attempts < 42) {
|
|
579
|
+
newFocused = adapter.addDays(newFocused, skipStep);
|
|
580
|
+
attempts++;
|
|
581
|
+
}
|
|
582
|
+
if (attempts >= 42) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
516
585
|
ctx.setFocusedDate(newFocused);
|
|
517
586
|
if (!adapter.isSameMonth(newFocused, viewMonth)) {
|
|
518
587
|
ctx.setViewMonth(newFocused);
|
|
@@ -560,62 +629,170 @@ function DatePickerCalendar({
|
|
|
560
629
|
ref: gridRef,
|
|
561
630
|
role: "grid",
|
|
562
631
|
"aria-label": title,
|
|
632
|
+
"aria-rowcount": weeks.length + 1,
|
|
633
|
+
"aria-colcount": 7,
|
|
563
634
|
className: classNames?.grid,
|
|
564
635
|
onKeyDown: handleKeyDown,
|
|
565
636
|
children: [
|
|
566
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */
|
|
567
|
-
"th",
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
abbr: day.full,
|
|
571
|
-
scope: "col",
|
|
572
|
-
className: classNames?.weekdayHeader,
|
|
573
|
-
children: day.short
|
|
574
|
-
},
|
|
575
|
-
day.short
|
|
576
|
-
)) }) }),
|
|
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",
|
|
637
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
|
|
638
|
+
showWeekNumber ? /* @__PURE__ */ jsx("th", { scope: "col", "aria-hidden": "true", className: classNames?.weekNumberHeader, children: "#" }) : null,
|
|
639
|
+
weekdays.map((day, colIndex) => /* @__PURE__ */ jsx(
|
|
640
|
+
"th",
|
|
587
641
|
{
|
|
588
|
-
role: "
|
|
589
|
-
|
|
590
|
-
"
|
|
591
|
-
"aria-
|
|
592
|
-
className: classNames?.
|
|
593
|
-
children:
|
|
594
|
-
|
|
642
|
+
role: "columnheader",
|
|
643
|
+
abbr: day.full,
|
|
644
|
+
scope: "col",
|
|
645
|
+
"aria-colindex": colIndex + 1,
|
|
646
|
+
className: classNames?.weekdayHeader,
|
|
647
|
+
children: day.short
|
|
648
|
+
},
|
|
649
|
+
day.short
|
|
650
|
+
))
|
|
651
|
+
] }) }),
|
|
652
|
+
/* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxs(
|
|
653
|
+
"tr",
|
|
654
|
+
{
|
|
655
|
+
role: "row",
|
|
656
|
+
"aria-rowindex": weekIndex + 2,
|
|
657
|
+
className: classNames?.gridRow,
|
|
658
|
+
children: [
|
|
659
|
+
showWeekNumber ? /* @__PURE__ */ jsx(
|
|
660
|
+
"th",
|
|
595
661
|
{
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
"data-
|
|
600
|
-
|
|
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
|
|
662
|
+
scope: "row",
|
|
663
|
+
"aria-hidden": "true",
|
|
664
|
+
className: classNames?.weekNumber,
|
|
665
|
+
"data-week-number": true,
|
|
666
|
+
children: getISOWeekNumber(week[thursdayIndex].isoString)
|
|
607
667
|
}
|
|
608
|
-
)
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
668
|
+
) : null,
|
|
669
|
+
week.map((day, colIndex) => {
|
|
670
|
+
const dayClasses = [
|
|
671
|
+
classNames?.day,
|
|
672
|
+
day.isSelected && classNames?.daySelected,
|
|
673
|
+
day.isToday && classNames?.dayToday,
|
|
674
|
+
day.isDisabled && classNames?.dayDisabled,
|
|
675
|
+
!day.isCurrentMonth && classNames?.dayOutsideMonth
|
|
676
|
+
].filter(Boolean).join(" ") || void 0;
|
|
677
|
+
return /* @__PURE__ */ jsx(
|
|
678
|
+
"td",
|
|
679
|
+
{
|
|
680
|
+
role: "gridcell",
|
|
681
|
+
"aria-colindex": colIndex + 1,
|
|
682
|
+
"aria-selected": day.isSelected || void 0,
|
|
683
|
+
"aria-disabled": day.isDisabled || void 0,
|
|
684
|
+
"aria-current": day.isToday ? "date" : void 0,
|
|
685
|
+
className: classNames?.gridCell,
|
|
686
|
+
children: /* @__PURE__ */ jsx(
|
|
687
|
+
"button",
|
|
688
|
+
{
|
|
689
|
+
type: "button",
|
|
690
|
+
tabIndex: day.isFocused ? 0 : -1,
|
|
691
|
+
disabled: day.isDisabled,
|
|
692
|
+
"data-focused": day.isFocused || void 0,
|
|
693
|
+
"data-selected": day.isSelected || void 0,
|
|
694
|
+
"data-today": day.isToday || void 0,
|
|
695
|
+
"data-outside-month": !day.isCurrentMonth || void 0,
|
|
696
|
+
className: dayClasses,
|
|
697
|
+
onClick: () => handleDayClick(day),
|
|
698
|
+
"aria-label": safeFormatFullDate(day.isoString, locale),
|
|
699
|
+
children: day.dayNumber
|
|
700
|
+
}
|
|
701
|
+
)
|
|
702
|
+
},
|
|
703
|
+
day.isoString
|
|
704
|
+
);
|
|
705
|
+
})
|
|
706
|
+
]
|
|
707
|
+
},
|
|
708
|
+
weekIndex
|
|
709
|
+
)) })
|
|
613
710
|
]
|
|
614
711
|
}
|
|
615
712
|
),
|
|
616
713
|
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: srOnly, children: announcement })
|
|
617
714
|
] });
|
|
618
715
|
}
|
|
716
|
+
function isRangeFullyDisabled(start, end, rules, adapter) {
|
|
717
|
+
for (const rule of rules) {
|
|
718
|
+
if ("before" in rule && adapter.isBefore(end, rule.before)) return true;
|
|
719
|
+
if ("after" in rule && adapter.isAfter(start, rule.after)) return true;
|
|
720
|
+
}
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
function useGridState(opts) {
|
|
724
|
+
const { initialIndex, disabledFlags, onSelect, onPageUp, onPageDown, onEscape } = opts;
|
|
725
|
+
const gridRef = useRef(null);
|
|
726
|
+
const [focusedIndex, setFocusedIndex] = useState(initialIndex);
|
|
727
|
+
const handleKeyDown = (e) => {
|
|
728
|
+
let next = null;
|
|
729
|
+
let step = 1;
|
|
730
|
+
switch (e.key) {
|
|
731
|
+
case "ArrowLeft":
|
|
732
|
+
next = Math.max(0, focusedIndex - 1);
|
|
733
|
+
step = -1;
|
|
734
|
+
break;
|
|
735
|
+
case "ArrowRight":
|
|
736
|
+
next = Math.min(11, focusedIndex + 1);
|
|
737
|
+
break;
|
|
738
|
+
case "ArrowUp":
|
|
739
|
+
next = Math.max(0, focusedIndex - 3);
|
|
740
|
+
step = -1;
|
|
741
|
+
break;
|
|
742
|
+
case "ArrowDown":
|
|
743
|
+
next = Math.min(11, focusedIndex + 3);
|
|
744
|
+
break;
|
|
745
|
+
case "Home":
|
|
746
|
+
next = focusedIndex - focusedIndex % 3;
|
|
747
|
+
step = -1;
|
|
748
|
+
break;
|
|
749
|
+
case "End":
|
|
750
|
+
next = focusedIndex - focusedIndex % 3 + 2;
|
|
751
|
+
break;
|
|
752
|
+
case "PageUp":
|
|
753
|
+
e.preventDefault();
|
|
754
|
+
onPageUp();
|
|
755
|
+
return;
|
|
756
|
+
case "PageDown":
|
|
757
|
+
e.preventDefault();
|
|
758
|
+
onPageDown();
|
|
759
|
+
return;
|
|
760
|
+
case "Enter":
|
|
761
|
+
case " ":
|
|
762
|
+
e.preventDefault();
|
|
763
|
+
onSelect(focusedIndex);
|
|
764
|
+
return;
|
|
765
|
+
case "Escape":
|
|
766
|
+
onEscape();
|
|
767
|
+
return;
|
|
768
|
+
default:
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
if (next === null) return;
|
|
772
|
+
e.preventDefault();
|
|
773
|
+
if (disabledFlags) {
|
|
774
|
+
let attempts = 0;
|
|
775
|
+
while (next >= 0 && next < 12 && disabledFlags[next] && attempts < 12) {
|
|
776
|
+
next += step;
|
|
777
|
+
attempts++;
|
|
778
|
+
}
|
|
779
|
+
if (next < 0 || next >= 12 || disabledFlags[next]) return;
|
|
780
|
+
}
|
|
781
|
+
if (next !== focusedIndex) setFocusedIndex(next);
|
|
782
|
+
};
|
|
783
|
+
useEffect(() => {
|
|
784
|
+
if (!disabledFlags || !disabledFlags[focusedIndex]) return;
|
|
785
|
+
const firstEnabled = disabledFlags.findIndex((d) => !d);
|
|
786
|
+
if (firstEnabled !== -1 && firstEnabled !== focusedIndex) {
|
|
787
|
+
setFocusedIndex(firstEnabled);
|
|
788
|
+
}
|
|
789
|
+
}, [disabledFlags, focusedIndex]);
|
|
790
|
+
useEffect(() => {
|
|
791
|
+
const btn = gridRef.current?.querySelector('[data-focused="true"]');
|
|
792
|
+
btn?.focus({ preventScroll: true });
|
|
793
|
+
}, [focusedIndex]);
|
|
794
|
+
return { gridRef, focusedIndex, handleKeyDown };
|
|
795
|
+
}
|
|
619
796
|
function DatePickerMonthGrid({
|
|
620
797
|
classNames,
|
|
621
798
|
onSelect,
|
|
@@ -623,15 +800,18 @@ function DatePickerMonthGrid({
|
|
|
623
800
|
...props
|
|
624
801
|
}) {
|
|
625
802
|
const ctx = useDatePickerContext("DatePicker.MonthGrid");
|
|
626
|
-
const { adapter, viewMonth, locale } = ctx;
|
|
803
|
+
const { adapter, viewMonth, locale, displayTimezone } = ctx;
|
|
627
804
|
const currentYear = adapter.getYear(viewMonth);
|
|
628
805
|
const currentMonth = adapter.getMonth(viewMonth);
|
|
629
|
-
const
|
|
630
|
-
|
|
806
|
+
const [today, setToday] = useState(null);
|
|
807
|
+
useEffect(() => {
|
|
808
|
+
setToday(adapter.today(displayTimezone));
|
|
809
|
+
}, [adapter, displayTimezone]);
|
|
810
|
+
const todayMonth = today !== null ? adapter.getMonth(today) : -1;
|
|
811
|
+
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
631
812
|
const navigateYear = useCallback(
|
|
632
813
|
(direction) => {
|
|
633
|
-
|
|
634
|
-
ctx.setViewMonth(newDate);
|
|
814
|
+
ctx.setViewMonth(adapter.addYears(viewMonth, direction));
|
|
635
815
|
},
|
|
636
816
|
[adapter, viewMonth, ctx]
|
|
637
817
|
);
|
|
@@ -644,12 +824,13 @@ function DatePickerMonthGrid({
|
|
|
644
824
|
},
|
|
645
825
|
[currentYear, ctx, onSelect]
|
|
646
826
|
);
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
827
|
+
const { gridRef, focusedIndex, handleKeyDown } = useGridState({
|
|
828
|
+
initialIndex: currentMonth,
|
|
829
|
+
onSelect: handleMonthSelect,
|
|
830
|
+
onPageUp: () => navigateYear(-1),
|
|
831
|
+
onPageDown: () => navigateYear(1),
|
|
832
|
+
onEscape: ctx.close
|
|
833
|
+
});
|
|
653
834
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
654
835
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
655
836
|
/* @__PURE__ */ jsx(
|
|
@@ -677,30 +858,37 @@ function DatePickerMonthGrid({
|
|
|
677
858
|
/* @__PURE__ */ jsx(
|
|
678
859
|
"div",
|
|
679
860
|
{
|
|
861
|
+
ref: gridRef,
|
|
680
862
|
role: "grid",
|
|
681
863
|
"aria-label": `${currentYear} months`,
|
|
682
864
|
className: classNames?.grid,
|
|
683
865
|
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
684
|
-
|
|
685
|
-
|
|
866
|
+
onKeyDown: handleKeyDown,
|
|
867
|
+
children: Array.from({ length: 12 }, (_, i) => {
|
|
868
|
+
const isSelected = i === currentMonth;
|
|
869
|
+
const isCurrent = i === todayMonth && currentYear === todayYear;
|
|
870
|
+
const isFocused = i === focusedIndex;
|
|
871
|
+
const cls = [
|
|
686
872
|
classNames?.month,
|
|
687
|
-
|
|
688
|
-
|
|
873
|
+
isSelected && classNames?.monthSelected,
|
|
874
|
+
isCurrent && classNames?.monthCurrent
|
|
689
875
|
].filter(Boolean).join(" ") || void 0;
|
|
690
876
|
return /* @__PURE__ */ jsx(
|
|
691
877
|
"button",
|
|
692
878
|
{
|
|
693
879
|
type: "button",
|
|
694
880
|
role: "gridcell",
|
|
695
|
-
|
|
696
|
-
"aria-
|
|
697
|
-
"
|
|
698
|
-
"data-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
881
|
+
tabIndex: isFocused ? 0 : -1,
|
|
882
|
+
"aria-selected": isSelected || void 0,
|
|
883
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
884
|
+
"data-selected": isSelected || void 0,
|
|
885
|
+
"data-current": isCurrent || void 0,
|
|
886
|
+
"data-focused": isFocused || void 0,
|
|
887
|
+
className: cls,
|
|
888
|
+
onClick: () => handleMonthSelect(i),
|
|
889
|
+
children: getMonthName(i, locale)
|
|
702
890
|
},
|
|
703
|
-
|
|
891
|
+
i
|
|
704
892
|
);
|
|
705
893
|
})
|
|
706
894
|
}
|
|
@@ -709,38 +897,38 @@ function DatePickerMonthGrid({
|
|
|
709
897
|
}
|
|
710
898
|
function DatePickerYearGrid({ classNames, onSelect, ...props }) {
|
|
711
899
|
const ctx = useDatePickerContext("DatePicker.YearGrid");
|
|
712
|
-
const { adapter, viewMonth } = ctx;
|
|
900
|
+
const { adapter, viewMonth, displayTimezone } = ctx;
|
|
713
901
|
const currentYear = adapter.getYear(viewMonth);
|
|
714
|
-
const
|
|
902
|
+
const [today, setToday] = useState(null);
|
|
903
|
+
useEffect(() => {
|
|
904
|
+
setToday(adapter.today(displayTimezone));
|
|
905
|
+
}, [adapter, displayTimezone]);
|
|
906
|
+
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
715
907
|
const decadeStart = currentYear - currentYear % 12;
|
|
716
908
|
const navigateDecade = useCallback(
|
|
717
909
|
(direction) => {
|
|
718
|
-
|
|
719
|
-
ctx.setViewMonth(newDate);
|
|
910
|
+
ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
|
|
720
911
|
},
|
|
721
912
|
[adapter, viewMonth, ctx]
|
|
722
913
|
);
|
|
723
914
|
const handleYearSelect = useCallback(
|
|
724
|
-
(
|
|
915
|
+
(indexInDecade) => {
|
|
916
|
+
const year = decadeStart + indexInDecade;
|
|
725
917
|
const currentMonth = adapter.getMonth(viewMonth);
|
|
726
918
|
const target = new Date(Date.UTC(year, currentMonth, 1)).toISOString();
|
|
727
919
|
ctx.setViewMonth(target);
|
|
728
920
|
ctx.setFocusedDate(target);
|
|
729
921
|
onSelect?.();
|
|
730
922
|
},
|
|
731
|
-
[adapter, viewMonth, ctx, onSelect]
|
|
732
|
-
);
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
};
|
|
741
|
-
}),
|
|
742
|
-
[decadeStart, currentYear, todayYear]
|
|
743
|
-
);
|
|
923
|
+
[adapter, viewMonth, ctx, onSelect, decadeStart]
|
|
924
|
+
);
|
|
925
|
+
const { gridRef, focusedIndex, handleKeyDown } = useGridState({
|
|
926
|
+
initialIndex: currentYear - decadeStart,
|
|
927
|
+
onSelect: handleYearSelect,
|
|
928
|
+
onPageUp: () => navigateDecade(-1),
|
|
929
|
+
onPageDown: () => navigateDecade(1),
|
|
930
|
+
onEscape: ctx.close
|
|
931
|
+
});
|
|
744
932
|
const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
|
|
745
933
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
746
934
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
@@ -769,30 +957,38 @@ function DatePickerYearGrid({ classNames, onSelect, ...props }) {
|
|
|
769
957
|
/* @__PURE__ */ jsx(
|
|
770
958
|
"div",
|
|
771
959
|
{
|
|
960
|
+
ref: gridRef,
|
|
772
961
|
role: "grid",
|
|
773
962
|
"aria-label": rangeLabel,
|
|
774
963
|
className: classNames?.grid,
|
|
775
964
|
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
776
|
-
|
|
777
|
-
|
|
965
|
+
onKeyDown: handleKeyDown,
|
|
966
|
+
children: Array.from({ length: 12 }, (_, i) => {
|
|
967
|
+
const year = decadeStart + i;
|
|
968
|
+
const isSelected = year === currentYear;
|
|
969
|
+
const isCurrent = year === todayYear;
|
|
970
|
+
const isFocused = i === focusedIndex;
|
|
971
|
+
const cls = [
|
|
778
972
|
classNames?.year,
|
|
779
|
-
|
|
780
|
-
|
|
973
|
+
isSelected && classNames?.yearSelected,
|
|
974
|
+
isCurrent && classNames?.yearCurrent
|
|
781
975
|
].filter(Boolean).join(" ") || void 0;
|
|
782
976
|
return /* @__PURE__ */ jsx(
|
|
783
977
|
"button",
|
|
784
978
|
{
|
|
785
979
|
type: "button",
|
|
786
980
|
role: "gridcell",
|
|
787
|
-
|
|
788
|
-
"aria-
|
|
789
|
-
"
|
|
790
|
-
"data-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
981
|
+
tabIndex: isFocused ? 0 : -1,
|
|
982
|
+
"aria-selected": isSelected || void 0,
|
|
983
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
984
|
+
"data-selected": isSelected || void 0,
|
|
985
|
+
"data-current": isCurrent || void 0,
|
|
986
|
+
"data-focused": isFocused || void 0,
|
|
987
|
+
className: cls,
|
|
988
|
+
onClick: () => handleYearSelect(i),
|
|
989
|
+
children: year
|
|
794
990
|
},
|
|
795
|
-
|
|
991
|
+
i
|
|
796
992
|
);
|
|
797
993
|
})
|
|
798
994
|
}
|
|
@@ -865,8 +1061,7 @@ function DatePickerPreset({
|
|
|
865
1061
|
"button",
|
|
866
1062
|
{
|
|
867
1063
|
type: "button",
|
|
868
|
-
|
|
869
|
-
"aria-selected": isActive,
|
|
1064
|
+
"aria-pressed": isActive,
|
|
870
1065
|
"data-active": isActive || void 0,
|
|
871
1066
|
disabled: ctx.isDisabled,
|
|
872
1067
|
onClick: handleClick,
|
|
@@ -930,10 +1125,10 @@ function RangePickerRoot({
|
|
|
930
1125
|
const [selectingTarget, setSelectingTarget] = useState("start");
|
|
931
1126
|
const [hoverDate, setHoverDate] = useState(null);
|
|
932
1127
|
const [viewMonth, setViewMonth] = useState(
|
|
933
|
-
currentValue.start ?? adapter.today(displayTimezone)
|
|
1128
|
+
() => currentValue.start ?? adapter.today(displayTimezone)
|
|
934
1129
|
);
|
|
935
1130
|
const [focusedDate, setFocusedDate] = useState(
|
|
936
|
-
currentValue.start ?? adapter.today(displayTimezone)
|
|
1131
|
+
() => currentValue.start ?? adapter.today(displayTimezone)
|
|
937
1132
|
);
|
|
938
1133
|
useChangeEffect(isOpen, onOpenChange);
|
|
939
1134
|
const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
|
|
@@ -1083,6 +1278,8 @@ var RangePickerInput = forwardRef(
|
|
|
1083
1278
|
(e) => {
|
|
1084
1279
|
if (e.key === "Escape") {
|
|
1085
1280
|
ctx.close();
|
|
1281
|
+
} else if (e.key === "Enter" && ctx.isOpen) {
|
|
1282
|
+
e.preventDefault();
|
|
1086
1283
|
} else if (e.key === "ArrowDown" && !ctx.isOpen) {
|
|
1087
1284
|
e.preventDefault();
|
|
1088
1285
|
ctx.open();
|
|
@@ -1119,6 +1316,7 @@ var RangePickerInput = forwardRef(
|
|
|
1119
1316
|
);
|
|
1120
1317
|
}
|
|
1121
1318
|
);
|
|
1319
|
+
RangePickerInput.displayName = "RangePicker.Input";
|
|
1122
1320
|
function RangePickerPopover({ children, ...props }) {
|
|
1123
1321
|
const ctx = useRangePickerContext("RangePicker.Popover");
|
|
1124
1322
|
const calendarId = `${ctx.pickerId}-calendar`;
|
|
@@ -1168,6 +1366,8 @@ var srOnly2 = {
|
|
|
1168
1366
|
function RangePickerCalendar({
|
|
1169
1367
|
classNames,
|
|
1170
1368
|
selectionMode = "range",
|
|
1369
|
+
showWeekNumber = false,
|
|
1370
|
+
fixedWeeks = false,
|
|
1171
1371
|
...props
|
|
1172
1372
|
}) {
|
|
1173
1373
|
const ctx = useRangePickerContext("RangePicker.Calendar");
|
|
@@ -1185,15 +1385,30 @@ function RangePickerCalendar({
|
|
|
1185
1385
|
displayTimezone
|
|
1186
1386
|
} = ctx;
|
|
1187
1387
|
const { locale } = ctx;
|
|
1188
|
-
const weekdays = getWeekdayNames(locale, weekStartsOn);
|
|
1189
|
-
const weeks =
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1388
|
+
const weekdays = useMemo(() => getWeekdayNames(locale, weekStartsOn), [locale, weekStartsOn]);
|
|
1389
|
+
const weeks = useMemo(
|
|
1390
|
+
() => getCalendarDays(viewMonth, adapter, {
|
|
1391
|
+
weekStartsOn,
|
|
1392
|
+
focusedDate,
|
|
1393
|
+
disabled,
|
|
1394
|
+
range: value,
|
|
1395
|
+
rangeHover: hoverDate,
|
|
1396
|
+
timezone: displayTimezone,
|
|
1397
|
+
fixedWeeks
|
|
1398
|
+
}),
|
|
1399
|
+
[
|
|
1400
|
+
viewMonth,
|
|
1401
|
+
adapter,
|
|
1402
|
+
weekStartsOn,
|
|
1403
|
+
focusedDate,
|
|
1404
|
+
disabled,
|
|
1405
|
+
value,
|
|
1406
|
+
hoverDate,
|
|
1407
|
+
displayTimezone,
|
|
1408
|
+
fixedWeeks
|
|
1409
|
+
]
|
|
1410
|
+
);
|
|
1411
|
+
const thursdayIndex = weekStartsOn === 0 ? 4 : 3;
|
|
1197
1412
|
const year = adapter.getYear(viewMonth);
|
|
1198
1413
|
const month = adapter.getMonth(viewMonth);
|
|
1199
1414
|
const title = formatMonthYear(year, month, locale);
|
|
@@ -1293,6 +1508,13 @@ function RangePickerCalendar({
|
|
|
1293
1508
|
}
|
|
1294
1509
|
if (newFocused) {
|
|
1295
1510
|
e.preventDefault();
|
|
1511
|
+
const skipStep = e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "PageUp" || e.key === "Home" ? -1 : 1;
|
|
1512
|
+
let attempts = 0;
|
|
1513
|
+
while (isDateDisabled(newFocused, disabled, adapter) && attempts < 42) {
|
|
1514
|
+
newFocused = adapter.addDays(newFocused, skipStep);
|
|
1515
|
+
attempts++;
|
|
1516
|
+
}
|
|
1517
|
+
if (attempts >= 42) return;
|
|
1296
1518
|
ctx.setFocusedDate(newFocused);
|
|
1297
1519
|
if (!adapter.isSameMonth(newFocused, viewMonth)) {
|
|
1298
1520
|
ctx.setViewMonth(newFocused);
|
|
@@ -1345,63 +1567,90 @@ function RangePickerCalendar({
|
|
|
1345
1567
|
ref: gridRef,
|
|
1346
1568
|
role: "grid",
|
|
1347
1569
|
"aria-label": title,
|
|
1348
|
-
"aria-
|
|
1570
|
+
"aria-rowcount": weeks.length + 1,
|
|
1571
|
+
"aria-colcount": 7,
|
|
1349
1572
|
className: classNames?.grid,
|
|
1350
1573
|
onKeyDown: handleKeyDown,
|
|
1351
1574
|
children: [
|
|
1352
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */
|
|
1353
|
-
"th",
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
abbr: day.full,
|
|
1357
|
-
scope: "col",
|
|
1358
|
-
className: classNames?.weekdayHeader,
|
|
1359
|
-
children: day.short
|
|
1360
|
-
},
|
|
1361
|
-
day.short
|
|
1362
|
-
)) }) }),
|
|
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",
|
|
1575
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
|
|
1576
|
+
showWeekNumber ? /* @__PURE__ */ jsx("th", { scope: "col", "aria-hidden": "true", className: classNames?.weekNumberHeader, children: "#" }) : null,
|
|
1577
|
+
weekdays.map((day, colIndex) => /* @__PURE__ */ jsx(
|
|
1578
|
+
"th",
|
|
1376
1579
|
{
|
|
1377
|
-
role: "
|
|
1378
|
-
|
|
1379
|
-
"
|
|
1380
|
-
"aria-
|
|
1381
|
-
className: classNames?.
|
|
1382
|
-
children:
|
|
1383
|
-
|
|
1580
|
+
role: "columnheader",
|
|
1581
|
+
abbr: day.full,
|
|
1582
|
+
scope: "col",
|
|
1583
|
+
"aria-colindex": colIndex + 1,
|
|
1584
|
+
className: classNames?.weekdayHeader,
|
|
1585
|
+
children: day.short
|
|
1586
|
+
},
|
|
1587
|
+
day.short
|
|
1588
|
+
))
|
|
1589
|
+
] }) }),
|
|
1590
|
+
/* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxs(
|
|
1591
|
+
"tr",
|
|
1592
|
+
{
|
|
1593
|
+
role: "row",
|
|
1594
|
+
"aria-rowindex": weekIndex + 2,
|
|
1595
|
+
className: classNames?.gridRow,
|
|
1596
|
+
children: [
|
|
1597
|
+
showWeekNumber ? /* @__PURE__ */ jsx(
|
|
1598
|
+
"th",
|
|
1384
1599
|
{
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
"data-
|
|
1389
|
-
|
|
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
|
|
1600
|
+
scope: "row",
|
|
1601
|
+
"aria-hidden": "true",
|
|
1602
|
+
className: classNames?.weekNumber,
|
|
1603
|
+
"data-week-number": true,
|
|
1604
|
+
children: getISOWeekNumber(week[thursdayIndex].isoString)
|
|
1399
1605
|
}
|
|
1400
|
-
)
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1606
|
+
) : null,
|
|
1607
|
+
week.map((day, colIndex) => {
|
|
1608
|
+
const dayClasses = [
|
|
1609
|
+
classNames?.day,
|
|
1610
|
+
day.isRangeStart && classNames?.dayRangeStart,
|
|
1611
|
+
day.isRangeEnd && classNames?.dayRangeEnd,
|
|
1612
|
+
day.isInRange && classNames?.dayInRange,
|
|
1613
|
+
day.isToday && classNames?.dayToday,
|
|
1614
|
+
day.isDisabled && classNames?.dayDisabled,
|
|
1615
|
+
!day.isCurrentMonth && classNames?.dayOutsideMonth
|
|
1616
|
+
].filter(Boolean).join(" ") || void 0;
|
|
1617
|
+
const isSelected = selectionMode === "week" ? day.isRangeStart || day.isRangeEnd || day.isInRange : day.isRangeStart || day.isRangeEnd;
|
|
1618
|
+
return /* @__PURE__ */ jsx(
|
|
1619
|
+
"td",
|
|
1620
|
+
{
|
|
1621
|
+
role: "gridcell",
|
|
1622
|
+
"aria-colindex": colIndex + 1,
|
|
1623
|
+
"aria-selected": isSelected || void 0,
|
|
1624
|
+
"aria-disabled": day.isDisabled || void 0,
|
|
1625
|
+
"aria-current": day.isToday ? "date" : void 0,
|
|
1626
|
+
className: classNames?.gridCell,
|
|
1627
|
+
children: /* @__PURE__ */ jsx(
|
|
1628
|
+
"button",
|
|
1629
|
+
{
|
|
1630
|
+
type: "button",
|
|
1631
|
+
tabIndex: day.isFocused ? 0 : -1,
|
|
1632
|
+
disabled: day.isDisabled,
|
|
1633
|
+
"data-focused": day.isFocused || void 0,
|
|
1634
|
+
"data-range-start": day.isRangeStart || void 0,
|
|
1635
|
+
"data-range-end": day.isRangeEnd || void 0,
|
|
1636
|
+
"data-in-range": day.isInRange || void 0,
|
|
1637
|
+
"data-today": day.isToday || void 0,
|
|
1638
|
+
"data-outside-month": !day.isCurrentMonth || void 0,
|
|
1639
|
+
className: dayClasses,
|
|
1640
|
+
onClick: () => handleDayClick(day),
|
|
1641
|
+
onMouseEnter: () => handleDayMouseEnter(day),
|
|
1642
|
+
"aria-label": safeFormatFullDate2(day.isoString, locale),
|
|
1643
|
+
children: day.dayNumber
|
|
1644
|
+
}
|
|
1645
|
+
)
|
|
1646
|
+
},
|
|
1647
|
+
day.isoString
|
|
1648
|
+
);
|
|
1649
|
+
})
|
|
1650
|
+
]
|
|
1651
|
+
},
|
|
1652
|
+
weekIndex
|
|
1653
|
+
)) })
|
|
1405
1654
|
]
|
|
1406
1655
|
}
|
|
1407
1656
|
),
|
|
@@ -1463,41 +1712,33 @@ function RangePickerPreset({
|
|
|
1463
1712
|
...props
|
|
1464
1713
|
}) {
|
|
1465
1714
|
const ctx = useRangePickerContext("RangePicker.Preset");
|
|
1715
|
+
const resolved = useMemo(() => {
|
|
1716
|
+
if (directRange) return directRange;
|
|
1717
|
+
if (presetKey)
|
|
1718
|
+
return resolvePreset(presetKey, ctx.adapter.today(ctx.displayTimezone), ctx.adapter);
|
|
1719
|
+
return null;
|
|
1720
|
+
}, [directRange, presetKey, ctx.adapter, ctx.displayTimezone]);
|
|
1466
1721
|
const handleClick = useCallback(
|
|
1467
1722
|
(e) => {
|
|
1468
1723
|
if (ctx.isDisabled || ctx.isReadOnly) return;
|
|
1469
|
-
|
|
1470
|
-
if (directRange) {
|
|
1471
|
-
resolved = directRange;
|
|
1472
|
-
} else if (presetKey) {
|
|
1473
|
-
resolved = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
|
|
1474
|
-
} else {
|
|
1475
|
-
return;
|
|
1476
|
-
}
|
|
1724
|
+
if (!resolved) return;
|
|
1477
1725
|
ctx.setRange(resolved);
|
|
1478
1726
|
ctx.close();
|
|
1479
1727
|
onClick?.(e);
|
|
1480
1728
|
},
|
|
1481
|
-
[ctx,
|
|
1729
|
+
[ctx, resolved, onClick]
|
|
1482
1730
|
);
|
|
1483
|
-
const isActive = (() => {
|
|
1484
|
-
if (!ctx.value.start || !ctx.value.end)
|
|
1485
|
-
let target;
|
|
1486
|
-
if (directRange) {
|
|
1487
|
-
target = directRange;
|
|
1488
|
-
} else if (presetKey) {
|
|
1489
|
-
target = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
|
|
1490
|
-
} else {
|
|
1731
|
+
const isActive = useMemo(() => {
|
|
1732
|
+
if (!ctx.value.start || !ctx.value.end || !resolved || !resolved.start || !resolved.end) {
|
|
1491
1733
|
return false;
|
|
1492
1734
|
}
|
|
1493
|
-
return
|
|
1494
|
-
})
|
|
1735
|
+
return ctx.adapter.isSameDay(ctx.value.start, resolved.start) && ctx.adapter.isSameDay(ctx.value.end, resolved.end);
|
|
1736
|
+
}, [ctx.value.start, ctx.value.end, ctx.adapter, resolved]);
|
|
1495
1737
|
return /* @__PURE__ */ jsx(
|
|
1496
1738
|
"button",
|
|
1497
1739
|
{
|
|
1498
1740
|
type: "button",
|
|
1499
|
-
|
|
1500
|
-
"aria-selected": isActive,
|
|
1741
|
+
"aria-pressed": isActive,
|
|
1501
1742
|
"data-active": isActive || void 0,
|
|
1502
1743
|
disabled: ctx.isDisabled,
|
|
1503
1744
|
onClick: handleClick,
|
|
@@ -1530,9 +1771,6 @@ function useTimePickerContext(componentName) {
|
|
|
1530
1771
|
}
|
|
1531
1772
|
return context;
|
|
1532
1773
|
}
|
|
1533
|
-
function getDefaultIso() {
|
|
1534
|
-
return DateFnsAdapter.today();
|
|
1535
|
-
}
|
|
1536
1774
|
function TimePickerRoot({
|
|
1537
1775
|
value: controlledValue,
|
|
1538
1776
|
defaultValue,
|
|
@@ -1543,6 +1781,7 @@ function TimePickerRoot({
|
|
|
1543
1781
|
displayTimezone,
|
|
1544
1782
|
disabled = false,
|
|
1545
1783
|
readOnly = false,
|
|
1784
|
+
filterTime,
|
|
1546
1785
|
labels: labelsProp,
|
|
1547
1786
|
children
|
|
1548
1787
|
}) {
|
|
@@ -1556,21 +1795,21 @@ function TimePickerRoot({
|
|
|
1556
1795
|
defaultValue ?? null
|
|
1557
1796
|
);
|
|
1558
1797
|
const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
|
|
1559
|
-
const
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
);
|
|
1798
|
+
const currentTime = useMemo(() => {
|
|
1799
|
+
if (!currentValue) return { hours: 0, minutes: 0, seconds: 0 };
|
|
1800
|
+
return displayTimezone ? getTimeInTimezone(currentValue, displayTimezone) : getTime(currentValue);
|
|
1801
|
+
}, [currentValue, displayTimezone]);
|
|
1564
1802
|
const setTime$1 = useCallback(
|
|
1565
1803
|
(partial) => {
|
|
1566
1804
|
if (disabled || readOnly) return;
|
|
1567
|
-
const
|
|
1805
|
+
const base = currentValue ?? DateFnsAdapter.today(displayTimezone);
|
|
1806
|
+
const newIso = displayTimezone ? setTimeInTimezone(base, partial, displayTimezone) : setTime(base, partial);
|
|
1568
1807
|
if (!isControlled) {
|
|
1569
1808
|
setUncontrolledValue(newIso);
|
|
1570
1809
|
}
|
|
1571
1810
|
onChange?.(newIso);
|
|
1572
1811
|
},
|
|
1573
|
-
[disabled, readOnly,
|
|
1812
|
+
[disabled, readOnly, currentValue, displayTimezone, isControlled, onChange]
|
|
1574
1813
|
);
|
|
1575
1814
|
const contextValue = useMemo(
|
|
1576
1815
|
() => ({
|
|
@@ -1584,7 +1823,8 @@ function TimePickerRoot({
|
|
|
1584
1823
|
isReadOnly: readOnly,
|
|
1585
1824
|
currentTime,
|
|
1586
1825
|
pickerId,
|
|
1587
|
-
labels: mergedLabels
|
|
1826
|
+
labels: mergedLabels,
|
|
1827
|
+
filterTime
|
|
1588
1828
|
}),
|
|
1589
1829
|
[
|
|
1590
1830
|
currentValue,
|
|
@@ -1597,7 +1837,8 @@ function TimePickerRoot({
|
|
|
1597
1837
|
readOnly,
|
|
1598
1838
|
currentTime,
|
|
1599
1839
|
pickerId,
|
|
1600
|
-
mergedLabels
|
|
1840
|
+
mergedLabels,
|
|
1841
|
+
filterTime
|
|
1601
1842
|
]
|
|
1602
1843
|
);
|
|
1603
1844
|
return /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: contextValue, children });
|
|
@@ -1654,6 +1895,7 @@ var TimePickerInput = forwardRef(
|
|
|
1654
1895
|
);
|
|
1655
1896
|
}
|
|
1656
1897
|
);
|
|
1898
|
+
TimePickerInput.displayName = "TimePicker.Input";
|
|
1657
1899
|
function useListboxNavigation({
|
|
1658
1900
|
items,
|
|
1659
1901
|
onSelect,
|
|
@@ -1701,17 +1943,41 @@ function useListboxNavigation({
|
|
|
1701
1943
|
}
|
|
1702
1944
|
function TimePickerHourList({ classNames, ...props }) {
|
|
1703
1945
|
const ctx = useTimePickerContext("TimePicker.HourList");
|
|
1704
|
-
const { format, currentTime, isDisabled, isReadOnly } = ctx;
|
|
1705
|
-
const hours = generateHours(format);
|
|
1946
|
+
const { format, step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
|
|
1947
|
+
const hours = useMemo(() => generateHours(format), [format]);
|
|
1706
1948
|
const selectedHourDisplay = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;
|
|
1707
1949
|
const currentPeriod = format === "12h" ? to12Hour(currentTime.hours).period : null;
|
|
1950
|
+
const fullyDisabledHours24 = useMemo(() => {
|
|
1951
|
+
if (!filterTime) return null;
|
|
1952
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
1953
|
+
for (let h = 0; h < 24; h++) {
|
|
1954
|
+
let allRejected = true;
|
|
1955
|
+
for (let m = 0; m < 60; m += step) {
|
|
1956
|
+
if (!filterTime(h, m)) {
|
|
1957
|
+
allRejected = false;
|
|
1958
|
+
break;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
if (allRejected) disabled.add(h);
|
|
1962
|
+
}
|
|
1963
|
+
return disabled;
|
|
1964
|
+
}, [filterTime, step]);
|
|
1965
|
+
const isHourDisabled = useCallback(
|
|
1966
|
+
(hourDisplay) => {
|
|
1967
|
+
if (!fullyDisabledHours24) return false;
|
|
1968
|
+
const hours24 = format === "12h" && currentPeriod ? to24Hour(hourDisplay, currentPeriod) : hourDisplay;
|
|
1969
|
+
return fullyDisabledHours24.has(hours24);
|
|
1970
|
+
},
|
|
1971
|
+
[fullyDisabledHours24, format, currentPeriod]
|
|
1972
|
+
);
|
|
1708
1973
|
const handleSelect = useCallback(
|
|
1709
1974
|
(hourDisplay) => {
|
|
1710
1975
|
if (isDisabled || isReadOnly) return;
|
|
1976
|
+
if (isHourDisabled(hourDisplay)) return;
|
|
1711
1977
|
const hours24 = format === "12h" && currentPeriod ? to24Hour(hourDisplay, currentPeriod) : hourDisplay;
|
|
1712
1978
|
ctx.setTime({ hours: hours24 });
|
|
1713
1979
|
},
|
|
1714
|
-
[format, currentPeriod, ctx, isDisabled, isReadOnly]
|
|
1980
|
+
[format, currentPeriod, ctx, isDisabled, isReadOnly, isHourDisabled]
|
|
1715
1981
|
);
|
|
1716
1982
|
const { listRef, handleKeyDown } = useListboxNavigation({
|
|
1717
1983
|
items: hours,
|
|
@@ -1729,13 +1995,14 @@ function TimePickerHourList({ classNames, ...props }) {
|
|
|
1729
1995
|
...props,
|
|
1730
1996
|
children: hours.map((hour) => {
|
|
1731
1997
|
const isSelected = hour === selectedHourDisplay;
|
|
1998
|
+
const isHourFullyDisabled = isHourDisabled(hour);
|
|
1732
1999
|
const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
|
|
1733
2000
|
return /* @__PURE__ */ jsx(
|
|
1734
2001
|
"li",
|
|
1735
2002
|
{
|
|
1736
2003
|
role: "option",
|
|
1737
2004
|
"aria-selected": isSelected,
|
|
1738
|
-
"aria-disabled": isDisabled || void 0,
|
|
2005
|
+
"aria-disabled": isDisabled || isHourFullyDisabled || void 0,
|
|
1739
2006
|
"aria-label": ctx.labels.hourOption(hour),
|
|
1740
2007
|
"data-selected": isSelected || void 0,
|
|
1741
2008
|
tabIndex: isSelected ? 0 : -1,
|
|
@@ -1752,14 +2019,22 @@ function TimePickerHourList({ classNames, ...props }) {
|
|
|
1752
2019
|
}
|
|
1753
2020
|
function TimePickerMinuteList({ classNames, ...props }) {
|
|
1754
2021
|
const ctx = useTimePickerContext("TimePicker.MinuteList");
|
|
1755
|
-
const { step, currentTime, isDisabled, isReadOnly } = ctx;
|
|
1756
|
-
const minutes = generateMinutes(step);
|
|
2022
|
+
const { step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
|
|
2023
|
+
const minutes = useMemo(() => generateMinutes(step), [step]);
|
|
2024
|
+
const isMinuteDisabled = useCallback(
|
|
2025
|
+
(minute) => {
|
|
2026
|
+
if (!filterTime) return false;
|
|
2027
|
+
return filterTime(currentTime.hours, minute);
|
|
2028
|
+
},
|
|
2029
|
+
[filterTime, currentTime.hours]
|
|
2030
|
+
);
|
|
1757
2031
|
const handleSelect = useCallback(
|
|
1758
2032
|
(minute) => {
|
|
1759
2033
|
if (isDisabled || isReadOnly) return;
|
|
2034
|
+
if (isMinuteDisabled(minute)) return;
|
|
1760
2035
|
ctx.setTime({ minutes: minute });
|
|
1761
2036
|
},
|
|
1762
|
-
[ctx, isDisabled, isReadOnly]
|
|
2037
|
+
[ctx, isDisabled, isReadOnly, isMinuteDisabled]
|
|
1763
2038
|
);
|
|
1764
2039
|
const { listRef, handleKeyDown } = useListboxNavigation({
|
|
1765
2040
|
items: minutes,
|
|
@@ -1777,13 +2052,14 @@ function TimePickerMinuteList({ classNames, ...props }) {
|
|
|
1777
2052
|
...props,
|
|
1778
2053
|
children: minutes.map((minute) => {
|
|
1779
2054
|
const isSelected = minute === currentTime.minutes;
|
|
2055
|
+
const isMinuteFullyDisabled = isMinuteDisabled(minute);
|
|
1780
2056
|
const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
|
|
1781
2057
|
return /* @__PURE__ */ jsx(
|
|
1782
2058
|
"li",
|
|
1783
2059
|
{
|
|
1784
2060
|
role: "option",
|
|
1785
2061
|
"aria-selected": isSelected,
|
|
1786
|
-
"aria-disabled": isDisabled || void 0,
|
|
2062
|
+
"aria-disabled": isDisabled || isMinuteFullyDisabled || void 0,
|
|
1787
2063
|
"aria-label": ctx.labels.minuteOption(minute),
|
|
1788
2064
|
"data-selected": isSelected || void 0,
|
|
1789
2065
|
tabIndex: isSelected ? 0 : -1,
|
|
@@ -1800,29 +2076,70 @@ function TimePickerMinuteList({ classNames, ...props }) {
|
|
|
1800
2076
|
}
|
|
1801
2077
|
function TimePickerAmPmToggle({ classNames, ...props }) {
|
|
1802
2078
|
const ctx = useTimePickerContext("TimePicker.AmPmToggle");
|
|
1803
|
-
|
|
1804
|
-
const
|
|
2079
|
+
const amRef = useRef(null);
|
|
2080
|
+
const pmRef = useRef(null);
|
|
1805
2081
|
const setPeriod = useCallback(
|
|
1806
2082
|
(newPeriod) => {
|
|
1807
2083
|
if (ctx.isDisabled || ctx.isReadOnly) return;
|
|
2084
|
+
const { hours12 } = to12Hour(ctx.currentTime.hours);
|
|
1808
2085
|
const newHours24 = to24Hour(hours12, newPeriod);
|
|
1809
2086
|
ctx.setTime({ hours: newHours24 });
|
|
1810
2087
|
},
|
|
1811
|
-
[
|
|
2088
|
+
[ctx]
|
|
1812
2089
|
);
|
|
2090
|
+
if (ctx.format !== "12h") return null;
|
|
2091
|
+
const { period } = to12Hour(ctx.currentTime.hours);
|
|
2092
|
+
const focusOther = (target) => {
|
|
2093
|
+
(target === "AM" ? amRef : pmRef).current?.focus();
|
|
2094
|
+
};
|
|
2095
|
+
const handleKeyDown = (e, target) => {
|
|
2096
|
+
switch (e.key) {
|
|
2097
|
+
case "ArrowRight":
|
|
2098
|
+
case "ArrowDown":
|
|
2099
|
+
case "ArrowLeft":
|
|
2100
|
+
case "ArrowUp": {
|
|
2101
|
+
e.preventDefault();
|
|
2102
|
+
const next = target === "AM" ? "PM" : "AM";
|
|
2103
|
+
setPeriod(next);
|
|
2104
|
+
focusOther(next);
|
|
2105
|
+
break;
|
|
2106
|
+
}
|
|
2107
|
+
case "Home": {
|
|
2108
|
+
e.preventDefault();
|
|
2109
|
+
setPeriod("AM");
|
|
2110
|
+
focusOther("AM");
|
|
2111
|
+
break;
|
|
2112
|
+
}
|
|
2113
|
+
case "End": {
|
|
2114
|
+
e.preventDefault();
|
|
2115
|
+
setPeriod("PM");
|
|
2116
|
+
focusOther("PM");
|
|
2117
|
+
break;
|
|
2118
|
+
}
|
|
2119
|
+
case " ":
|
|
2120
|
+
case "Enter": {
|
|
2121
|
+
e.preventDefault();
|
|
2122
|
+
setPeriod(target);
|
|
2123
|
+
break;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
1813
2127
|
const renderButton = (target) => {
|
|
1814
2128
|
const isSelected = period === target;
|
|
1815
2129
|
const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
|
|
1816
2130
|
return /* @__PURE__ */ jsx(
|
|
1817
2131
|
"button",
|
|
1818
2132
|
{
|
|
2133
|
+
ref: target === "AM" ? amRef : pmRef,
|
|
1819
2134
|
type: "button",
|
|
1820
2135
|
role: "radio",
|
|
1821
2136
|
"aria-checked": isSelected,
|
|
2137
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1822
2138
|
"data-selected": isSelected || void 0,
|
|
1823
2139
|
disabled: ctx.isDisabled,
|
|
1824
2140
|
className: optionClass,
|
|
1825
2141
|
onClick: () => setPeriod(target),
|
|
2142
|
+
onKeyDown: (e) => handleKeyDown(e, target),
|
|
1826
2143
|
children: target
|
|
1827
2144
|
}
|
|
1828
2145
|
);
|
|
@@ -1849,9 +2166,6 @@ var TimePicker = Object.assign(TimePickerRoot, {
|
|
|
1849
2166
|
MinuteList: TimePickerMinuteList,
|
|
1850
2167
|
AmPmToggle: TimePickerAmPmToggle
|
|
1851
2168
|
});
|
|
1852
|
-
function getDefaultIso2() {
|
|
1853
|
-
return DateFnsAdapter.today();
|
|
1854
|
-
}
|
|
1855
2169
|
function DateTimePickerRoot({
|
|
1856
2170
|
value: controlledValue,
|
|
1857
2171
|
defaultValue,
|
|
@@ -1860,6 +2174,8 @@ function DateTimePickerRoot({
|
|
|
1860
2174
|
onCalendarNavigate,
|
|
1861
2175
|
format = "24h",
|
|
1862
2176
|
step = 1,
|
|
2177
|
+
withSeconds = false,
|
|
2178
|
+
filterTime,
|
|
1863
2179
|
disabled = false,
|
|
1864
2180
|
readOnly = false,
|
|
1865
2181
|
weekStartsOn = 0,
|
|
@@ -1887,10 +2203,10 @@ function DateTimePickerRoot({
|
|
|
1887
2203
|
const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
|
|
1888
2204
|
const [isOpen, setIsOpen] = useState(false);
|
|
1889
2205
|
const [viewMonth, setViewMonth] = useState(
|
|
1890
|
-
currentValue ?? adapter.today(displayTimezone)
|
|
2206
|
+
() => currentValue ?? adapter.today(displayTimezone)
|
|
1891
2207
|
);
|
|
1892
2208
|
const [focusedDate, setFocusedDate] = useState(
|
|
1893
|
-
currentValue ?? adapter.today(displayTimezone)
|
|
2209
|
+
() => currentValue ?? adapter.today(displayTimezone)
|
|
1894
2210
|
);
|
|
1895
2211
|
useChangeEffect(isOpen, onOpenChange);
|
|
1896
2212
|
const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
|
|
@@ -1900,11 +2216,10 @@ function DateTimePickerRoot({
|
|
|
1900
2216
|
() => Array.isArray(disabled) ? disabled : [],
|
|
1901
2217
|
[disabled]
|
|
1902
2218
|
);
|
|
1903
|
-
const
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
);
|
|
2219
|
+
const currentTime = useMemo(() => {
|
|
2220
|
+
if (!currentValue) return { hours: 0, minutes: 0, seconds: 0 };
|
|
2221
|
+
return displayTimezone ? getTimeInTimezone(currentValue, displayTimezone) : getTime(currentValue);
|
|
2222
|
+
}, [currentValue, displayTimezone]);
|
|
1908
2223
|
const updateValue = useCallback(
|
|
1909
2224
|
(next) => {
|
|
1910
2225
|
if (isDisabled || readOnly) return;
|
|
@@ -1930,11 +2245,11 @@ function DateTimePickerRoot({
|
|
|
1930
2245
|
);
|
|
1931
2246
|
const setTime$1 = useCallback(
|
|
1932
2247
|
(partial) => {
|
|
1933
|
-
const base = currentValue ??
|
|
2248
|
+
const base = currentValue ?? adapter.today(displayTimezone);
|
|
1934
2249
|
const merged = displayTimezone ? setTimeInTimezone(base, partial, displayTimezone) : setTime(base, partial);
|
|
1935
2250
|
updateValue(merged);
|
|
1936
2251
|
},
|
|
1937
|
-
[currentValue, updateValue, displayTimezone]
|
|
2252
|
+
[currentValue, updateValue, displayTimezone, adapter]
|
|
1938
2253
|
);
|
|
1939
2254
|
const open = useCallback(() => {
|
|
1940
2255
|
if (isDisabled || readOnly) return;
|
|
@@ -2001,25 +2316,28 @@ function DateTimePickerRoot({
|
|
|
2001
2316
|
setTime: setTime$1,
|
|
2002
2317
|
format,
|
|
2003
2318
|
step,
|
|
2004
|
-
withSeconds
|
|
2319
|
+
withSeconds,
|
|
2005
2320
|
displayTimezone,
|
|
2006
2321
|
isDisabled,
|
|
2007
2322
|
isReadOnly: readOnly,
|
|
2008
2323
|
currentTime,
|
|
2009
2324
|
pickerId,
|
|
2010
|
-
labels: mergedTimeLabels
|
|
2325
|
+
labels: mergedTimeLabels,
|
|
2326
|
+
filterTime
|
|
2011
2327
|
}),
|
|
2012
2328
|
[
|
|
2013
2329
|
currentValue,
|
|
2014
2330
|
setTime$1,
|
|
2015
2331
|
format,
|
|
2016
2332
|
step,
|
|
2333
|
+
withSeconds,
|
|
2017
2334
|
displayTimezone,
|
|
2018
2335
|
isDisabled,
|
|
2019
2336
|
readOnly,
|
|
2020
2337
|
currentTime,
|
|
2021
2338
|
pickerId,
|
|
2022
|
-
mergedTimeLabels
|
|
2339
|
+
mergedTimeLabels,
|
|
2340
|
+
filterTime
|
|
2023
2341
|
]
|
|
2024
2342
|
);
|
|
2025
2343
|
return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: dateContext, children: /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: timeContext, children }) });
|
|
@@ -2048,6 +2366,8 @@ var DateTimePickerInput = forwardRef(
|
|
|
2048
2366
|
(e) => {
|
|
2049
2367
|
if (e.key === "Escape") {
|
|
2050
2368
|
ctx.close();
|
|
2369
|
+
} else if (e.key === "Enter" && ctx.isOpen) {
|
|
2370
|
+
e.preventDefault();
|
|
2051
2371
|
} else if (e.key === "ArrowDown" && !ctx.isOpen) {
|
|
2052
2372
|
e.preventDefault();
|
|
2053
2373
|
ctx.open();
|
|
@@ -2083,6 +2403,7 @@ var DateTimePickerInput = forwardRef(
|
|
|
2083
2403
|
);
|
|
2084
2404
|
}
|
|
2085
2405
|
);
|
|
2406
|
+
DateTimePickerInput.displayName = "DateTimePicker.Input";
|
|
2086
2407
|
|
|
2087
2408
|
// src/components/DateTimePicker/index.ts
|
|
2088
2409
|
var DateTimePicker = Object.assign(DateTimePickerRoot, {
|
|
@@ -2101,7 +2422,7 @@ function MonthPickerRoot(props) {
|
|
|
2101
2422
|
}
|
|
2102
2423
|
function MonthPickerGrid({ classNames, ...props }) {
|
|
2103
2424
|
const ctx = useDatePickerContext("MonthPicker.Grid");
|
|
2104
|
-
const { adapter, viewMonth, locale, value, displayTimezone, labels } = ctx;
|
|
2425
|
+
const { adapter, viewMonth, locale, value, displayTimezone, labels, disabled } = ctx;
|
|
2105
2426
|
const currentYear = adapter.getYear(viewMonth);
|
|
2106
2427
|
const [valueYear, valueMonthZeroBased] = useMemo(() => {
|
|
2107
2428
|
if (!value) return [null, null];
|
|
@@ -2112,9 +2433,19 @@ function MonthPickerGrid({ classNames, ...props }) {
|
|
|
2112
2433
|
return [null, null];
|
|
2113
2434
|
}
|
|
2114
2435
|
}, [value, adapter, displayTimezone]);
|
|
2115
|
-
const today =
|
|
2116
|
-
|
|
2117
|
-
|
|
2436
|
+
const [today, setToday] = useState(null);
|
|
2437
|
+
useEffect(() => {
|
|
2438
|
+
setToday(adapter.today(displayTimezone));
|
|
2439
|
+
}, [adapter, displayTimezone]);
|
|
2440
|
+
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
2441
|
+
const todayMonth = today !== null ? adapter.getMonth(today) : -1;
|
|
2442
|
+
const monthDisabledFlags = useMemo(
|
|
2443
|
+
() => Array.from({ length: 12 }, (_, i) => {
|
|
2444
|
+
const monthStart = new Date(Date.UTC(currentYear, i, 1)).toISOString();
|
|
2445
|
+
return isRangeFullyDisabled(monthStart, adapter.endOfMonth(monthStart), disabled, adapter);
|
|
2446
|
+
}),
|
|
2447
|
+
[currentYear, disabled, adapter]
|
|
2448
|
+
);
|
|
2118
2449
|
const navigateYear = useCallback(
|
|
2119
2450
|
(direction) => {
|
|
2120
2451
|
ctx.setViewMonth(adapter.addYears(viewMonth, direction));
|
|
@@ -2123,17 +2454,23 @@ function MonthPickerGrid({ classNames, ...props }) {
|
|
|
2123
2454
|
);
|
|
2124
2455
|
const handleMonthSelect = useCallback(
|
|
2125
2456
|
(monthIndex) => {
|
|
2457
|
+
if (monthDisabledFlags[monthIndex]) return;
|
|
2126
2458
|
const target = new Date(Date.UTC(currentYear, monthIndex, 1)).toISOString();
|
|
2127
2459
|
ctx.selectDate(target);
|
|
2128
2460
|
},
|
|
2129
|
-
[currentYear, ctx]
|
|
2130
|
-
);
|
|
2131
|
-
const
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2461
|
+
[currentYear, ctx, monthDisabledFlags]
|
|
2462
|
+
);
|
|
2463
|
+
const naturalIndex = valueYear === currentYear && valueMonthZeroBased !== null ? valueMonthZeroBased : adapter.getMonth(viewMonth);
|
|
2464
|
+
const firstEnabled = monthDisabledFlags.findIndex((d) => !d);
|
|
2465
|
+
const initialIndex = monthDisabledFlags[naturalIndex] ? firstEnabled === -1 ? naturalIndex : firstEnabled : naturalIndex;
|
|
2466
|
+
const { gridRef, focusedIndex, handleKeyDown } = useGridState({
|
|
2467
|
+
initialIndex,
|
|
2468
|
+
disabledFlags: monthDisabledFlags,
|
|
2469
|
+
onSelect: handleMonthSelect,
|
|
2470
|
+
onPageUp: () => navigateYear(-1),
|
|
2471
|
+
onPageDown: () => navigateYear(1),
|
|
2472
|
+
onEscape: ctx.close
|
|
2473
|
+
});
|
|
2137
2474
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
2138
2475
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
2139
2476
|
/* @__PURE__ */ jsx(
|
|
@@ -2158,37 +2495,57 @@ function MonthPickerGrid({ classNames, ...props }) {
|
|
|
2158
2495
|
}
|
|
2159
2496
|
)
|
|
2160
2497
|
] }),
|
|
2161
|
-
/* @__PURE__ */ jsx(
|
|
2498
|
+
/* @__PURE__ */ jsx(
|
|
2162
2499
|
"div",
|
|
2163
2500
|
{
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
"
|
|
2175
|
-
{
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2501
|
+
ref: gridRef,
|
|
2502
|
+
role: "grid",
|
|
2503
|
+
"aria-label": `${currentYear} months`,
|
|
2504
|
+
className: classNames?.grid,
|
|
2505
|
+
onKeyDown: handleKeyDown,
|
|
2506
|
+
children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
|
|
2507
|
+
"div",
|
|
2508
|
+
{
|
|
2509
|
+
role: "row",
|
|
2510
|
+
className: classNames?.gridRow,
|
|
2511
|
+
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
2512
|
+
children: Array.from({ length: 3 }, (_2, col) => {
|
|
2513
|
+
const i = rowIndex * 3 + col;
|
|
2514
|
+
const isSelected = valueYear === currentYear && valueMonthZeroBased === i;
|
|
2515
|
+
const isCurrent = todayYear === currentYear && todayMonth === i;
|
|
2516
|
+
const isFocused = i === focusedIndex;
|
|
2517
|
+
const isDisabled = monthDisabledFlags[i] ?? false;
|
|
2518
|
+
const cls = [
|
|
2519
|
+
classNames?.month,
|
|
2520
|
+
isSelected && classNames?.monthSelected,
|
|
2521
|
+
isCurrent && classNames?.monthCurrent,
|
|
2522
|
+
isDisabled && classNames?.monthDisabled
|
|
2523
|
+
].filter(Boolean).join(" ") || void 0;
|
|
2524
|
+
return /* @__PURE__ */ jsx(
|
|
2525
|
+
"button",
|
|
2526
|
+
{
|
|
2527
|
+
type: "button",
|
|
2528
|
+
role: "gridcell",
|
|
2529
|
+
tabIndex: isFocused ? 0 : -1,
|
|
2530
|
+
disabled: isDisabled,
|
|
2531
|
+
"aria-selected": isSelected || void 0,
|
|
2532
|
+
"aria-disabled": isDisabled || void 0,
|
|
2533
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
2534
|
+
"data-selected": isSelected || void 0,
|
|
2535
|
+
"data-current": isCurrent || void 0,
|
|
2536
|
+
"data-focused": isFocused || void 0,
|
|
2537
|
+
className: cls,
|
|
2538
|
+
onClick: () => handleMonthSelect(i),
|
|
2539
|
+
children: getMonthName(i, locale)
|
|
2540
|
+
},
|
|
2541
|
+
i
|
|
2542
|
+
);
|
|
2543
|
+
})
|
|
2544
|
+
},
|
|
2545
|
+
rowIndex
|
|
2546
|
+
))
|
|
2547
|
+
}
|
|
2548
|
+
)
|
|
2192
2549
|
] });
|
|
2193
2550
|
}
|
|
2194
2551
|
|
|
@@ -2205,7 +2562,7 @@ function YearPickerRoot(props) {
|
|
|
2205
2562
|
}
|
|
2206
2563
|
function YearPickerGrid({ classNames, ...props }) {
|
|
2207
2564
|
const ctx = useDatePickerContext("YearPicker.Grid");
|
|
2208
|
-
const { adapter, viewMonth, value, displayTimezone, labels } = ctx;
|
|
2565
|
+
const { adapter, viewMonth, value, displayTimezone, labels, disabled } = ctx;
|
|
2209
2566
|
const currentYear = adapter.getYear(viewMonth);
|
|
2210
2567
|
const decadeStart = currentYear - currentYear % 12;
|
|
2211
2568
|
const valueYear = useMemo(() => {
|
|
@@ -2216,7 +2573,20 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2216
2573
|
return null;
|
|
2217
2574
|
}
|
|
2218
2575
|
}, [value, adapter, displayTimezone]);
|
|
2219
|
-
const
|
|
2576
|
+
const [today, setToday] = useState(null);
|
|
2577
|
+
useEffect(() => {
|
|
2578
|
+
setToday(adapter.today(displayTimezone));
|
|
2579
|
+
}, [adapter, displayTimezone]);
|
|
2580
|
+
const todayYear = today !== null ? adapter.getYear(today) : -1;
|
|
2581
|
+
const yearDisabledFlags = useMemo(
|
|
2582
|
+
() => Array.from({ length: 12 }, (_, i) => {
|
|
2583
|
+
const year = decadeStart + i;
|
|
2584
|
+
const yearStart = new Date(Date.UTC(year, 0, 1)).toISOString();
|
|
2585
|
+
const yearEnd = new Date(Date.UTC(year, 11, 31, 23, 59, 59, 999)).toISOString();
|
|
2586
|
+
return isRangeFullyDisabled(yearStart, yearEnd, disabled, adapter);
|
|
2587
|
+
}),
|
|
2588
|
+
[decadeStart, disabled, adapter]
|
|
2589
|
+
);
|
|
2220
2590
|
const navigateDecade = useCallback(
|
|
2221
2591
|
(direction) => {
|
|
2222
2592
|
ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
|
|
@@ -2224,19 +2594,24 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2224
2594
|
[adapter, viewMonth, ctx]
|
|
2225
2595
|
);
|
|
2226
2596
|
const handleYearSelect = useCallback(
|
|
2227
|
-
(
|
|
2597
|
+
(indexInDecade) => {
|
|
2598
|
+
if (yearDisabledFlags[indexInDecade]) return;
|
|
2599
|
+
const year = decadeStart + indexInDecade;
|
|
2228
2600
|
const target = new Date(Date.UTC(year, 0, 1)).toISOString();
|
|
2229
2601
|
ctx.selectDate(target);
|
|
2230
2602
|
},
|
|
2231
|
-
[ctx]
|
|
2232
|
-
);
|
|
2233
|
-
const
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2603
|
+
[ctx, decadeStart, yearDisabledFlags]
|
|
2604
|
+
);
|
|
2605
|
+
const naturalIndex = valueYear !== null && valueYear >= decadeStart && valueYear <= decadeStart + 11 ? valueYear - decadeStart : currentYear - decadeStart;
|
|
2606
|
+
const firstEnabled = yearDisabledFlags.findIndex((d) => !d);
|
|
2607
|
+
const initialIndex = yearDisabledFlags[naturalIndex] ? firstEnabled === -1 ? naturalIndex : firstEnabled : naturalIndex;
|
|
2608
|
+
const { gridRef, focusedIndex, handleKeyDown } = useGridState({
|
|
2609
|
+
initialIndex,
|
|
2610
|
+
disabledFlags: yearDisabledFlags,
|
|
2611
|
+
onSelect: handleYearSelect,
|
|
2612
|
+
onPageUp: () => navigateDecade(-1),
|
|
2613
|
+
onPageDown: () => navigateDecade(1),
|
|
2614
|
+
onEscape: ctx.close
|
|
2240
2615
|
});
|
|
2241
2616
|
const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
|
|
2242
2617
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
@@ -2263,37 +2638,58 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2263
2638
|
}
|
|
2264
2639
|
)
|
|
2265
2640
|
] }),
|
|
2266
|
-
/* @__PURE__ */ jsx(
|
|
2641
|
+
/* @__PURE__ */ jsx(
|
|
2267
2642
|
"div",
|
|
2268
2643
|
{
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
"
|
|
2280
|
-
{
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2644
|
+
ref: gridRef,
|
|
2645
|
+
role: "grid",
|
|
2646
|
+
"aria-label": rangeLabel,
|
|
2647
|
+
className: classNames?.grid,
|
|
2648
|
+
onKeyDown: handleKeyDown,
|
|
2649
|
+
children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
|
|
2650
|
+
"div",
|
|
2651
|
+
{
|
|
2652
|
+
role: "row",
|
|
2653
|
+
className: classNames?.gridRow,
|
|
2654
|
+
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
2655
|
+
children: Array.from({ length: 3 }, (_2, col) => {
|
|
2656
|
+
const i = rowIndex * 3 + col;
|
|
2657
|
+
const year = decadeStart + i;
|
|
2658
|
+
const isSelected = year === valueYear;
|
|
2659
|
+
const isCurrent = year === todayYear;
|
|
2660
|
+
const isFocused = i === focusedIndex;
|
|
2661
|
+
const isDisabled = yearDisabledFlags[i] ?? false;
|
|
2662
|
+
const cls = [
|
|
2663
|
+
classNames?.year,
|
|
2664
|
+
isSelected && classNames?.yearSelected,
|
|
2665
|
+
isCurrent && classNames?.yearCurrent,
|
|
2666
|
+
isDisabled && classNames?.yearDisabled
|
|
2667
|
+
].filter(Boolean).join(" ") || void 0;
|
|
2668
|
+
return /* @__PURE__ */ jsx(
|
|
2669
|
+
"button",
|
|
2670
|
+
{
|
|
2671
|
+
type: "button",
|
|
2672
|
+
role: "gridcell",
|
|
2673
|
+
tabIndex: isFocused ? 0 : -1,
|
|
2674
|
+
disabled: isDisabled,
|
|
2675
|
+
"aria-selected": isSelected || void 0,
|
|
2676
|
+
"aria-disabled": isDisabled || void 0,
|
|
2677
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
2678
|
+
"data-selected": isSelected || void 0,
|
|
2679
|
+
"data-current": isCurrent || void 0,
|
|
2680
|
+
"data-focused": isFocused || void 0,
|
|
2681
|
+
className: cls,
|
|
2682
|
+
onClick: () => handleYearSelect(i),
|
|
2683
|
+
children: year
|
|
2684
|
+
},
|
|
2685
|
+
i
|
|
2686
|
+
);
|
|
2687
|
+
})
|
|
2688
|
+
},
|
|
2689
|
+
rowIndex
|
|
2690
|
+
))
|
|
2691
|
+
}
|
|
2692
|
+
)
|
|
2297
2693
|
] });
|
|
2298
2694
|
}
|
|
2299
2695
|
|
|
@@ -2514,7 +2910,7 @@ function useRangePicker(options = {}) {
|
|
|
2514
2910
|
adapter
|
|
2515
2911
|
};
|
|
2516
2912
|
}
|
|
2517
|
-
function
|
|
2913
|
+
function getDefaultIso() {
|
|
2518
2914
|
return DateFnsAdapter.today();
|
|
2519
2915
|
}
|
|
2520
2916
|
function useTimePicker(options = {}) {
|
|
@@ -2532,7 +2928,7 @@ function useTimePicker(options = {}) {
|
|
|
2532
2928
|
defaultValue ?? null
|
|
2533
2929
|
);
|
|
2534
2930
|
const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
|
|
2535
|
-
const baseIso = currentValue ??
|
|
2931
|
+
const baseIso = currentValue ?? getDefaultIso();
|
|
2536
2932
|
const currentTime = useMemo(
|
|
2537
2933
|
() => displayTimezone ? getTimeInTimezone(baseIso, displayTimezone) : getTime(baseIso),
|
|
2538
2934
|
[baseIso, displayTimezone]
|