@kalyx/react 1.0.0-rc.0 → 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 +283 -9
- package/README.md +7 -2
- package/dist/index.cjs +827 -410
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -8
- package/dist/index.d.ts +81 -8
- package/dist/index.js +816 -415
- 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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
[
|
|
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,13 +325,20 @@ var DatePickerTrigger = forwardRef(
|
|
|
301
325
|
);
|
|
302
326
|
}
|
|
303
327
|
);
|
|
304
|
-
|
|
328
|
+
DatePickerTrigger.displayName = "DatePicker.Trigger";
|
|
329
|
+
var POPOVER_MIDDLEWARE = [offset(4), flip(), shift({ padding: 8 })];
|
|
330
|
+
function usePopover({
|
|
331
|
+
isOpen,
|
|
332
|
+
close,
|
|
333
|
+
referenceRef,
|
|
334
|
+
placement = "bottom-start"
|
|
335
|
+
}) {
|
|
305
336
|
const floatingRef = useRef(null);
|
|
306
337
|
const previousFocusRef = useRef(null);
|
|
307
|
-
const { refs, floatingStyles } = useFloating({
|
|
338
|
+
const { refs, floatingStyles, isPositioned } = useFloating({
|
|
308
339
|
open: isOpen,
|
|
309
340
|
placement,
|
|
310
|
-
middleware:
|
|
341
|
+
middleware: POPOVER_MIDDLEWARE,
|
|
311
342
|
whileElementsMounted: autoUpdate
|
|
312
343
|
});
|
|
313
344
|
useEffect(() => {
|
|
@@ -354,21 +385,46 @@ function usePopover({ isOpen, close, referenceRef, placement = "bottom-start" })
|
|
|
354
385
|
document.addEventListener("keydown", handleKeyDown);
|
|
355
386
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
356
387
|
}, [isOpen, close]);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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]);
|
|
406
|
+
const setFloatingRef = useCallback(
|
|
407
|
+
(node) => {
|
|
408
|
+
floatingRef.current = node;
|
|
409
|
+
refs.setFloating(node);
|
|
410
|
+
if (node && referenceRef.current) {
|
|
411
|
+
refs.setReference(referenceRef.current);
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
[refs, referenceRef]
|
|
415
|
+
);
|
|
416
|
+
return { floatingStyles, setFloatingRef, isPositioned };
|
|
362
417
|
}
|
|
363
418
|
function DatePickerPopover({ children, ...props }) {
|
|
364
419
|
const ctx = useDatePickerContext("DatePicker.Popover");
|
|
365
420
|
const calendarId = `${ctx.pickerId}-calendar`;
|
|
366
|
-
const { floatingStyles, setFloatingRef } = usePopover({
|
|
421
|
+
const { floatingStyles, setFloatingRef, isPositioned } = usePopover({
|
|
367
422
|
isOpen: ctx.isOpen,
|
|
368
423
|
close: ctx.close,
|
|
369
424
|
referenceRef: ctx.referenceRef
|
|
370
425
|
});
|
|
371
426
|
if (!ctx.isOpen) return null;
|
|
427
|
+
const { style: userStyle, ...rest } = props;
|
|
372
428
|
return /* @__PURE__ */ jsx(
|
|
373
429
|
"div",
|
|
374
430
|
{
|
|
@@ -377,8 +433,12 @@ function DatePickerPopover({ children, ...props }) {
|
|
|
377
433
|
role: "dialog",
|
|
378
434
|
"aria-label": ctx.labels.popoverLabel,
|
|
379
435
|
"aria-modal": "false",
|
|
380
|
-
|
|
381
|
-
|
|
436
|
+
...rest,
|
|
437
|
+
style: {
|
|
438
|
+
...userStyle,
|
|
439
|
+
...floatingStyles,
|
|
440
|
+
visibility: isPositioned ? void 0 : "hidden"
|
|
441
|
+
},
|
|
382
442
|
children
|
|
383
443
|
}
|
|
384
444
|
);
|
|
@@ -401,27 +461,45 @@ var srOnly = {
|
|
|
401
461
|
whiteSpace: "nowrap",
|
|
402
462
|
border: 0
|
|
403
463
|
};
|
|
404
|
-
function DatePickerCalendar({
|
|
464
|
+
function DatePickerCalendar({
|
|
465
|
+
classNames,
|
|
466
|
+
onTitleClick,
|
|
467
|
+
showWeekNumber = false,
|
|
468
|
+
fixedWeeks = false,
|
|
469
|
+
...props
|
|
470
|
+
}) {
|
|
405
471
|
const ctx = useDatePickerContext("DatePicker.Calendar");
|
|
406
472
|
const gridRef = useRef(null);
|
|
407
473
|
const [announcement, setAnnouncement] = useState("");
|
|
408
474
|
const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale, displayTimezone } = ctx;
|
|
409
|
-
const weekdays = getWeekdayNames(locale, weekStartsOn);
|
|
410
|
-
const weeks =
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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;
|
|
417
497
|
const year = adapter.getYear(viewMonth);
|
|
418
498
|
const month = adapter.getMonth(viewMonth);
|
|
419
499
|
const title = formatMonthYear(year, month, locale);
|
|
420
500
|
useEffect(() => {
|
|
421
501
|
if (!ctx.isOpen || !gridRef.current) return;
|
|
422
|
-
const focusedButton = gridRef.current.querySelector(
|
|
423
|
-
'[data-focused="true"]'
|
|
424
|
-
);
|
|
502
|
+
const focusedButton = gridRef.current.querySelector('[data-focused="true"]');
|
|
425
503
|
focusedButton?.focus({ preventScroll: true });
|
|
426
504
|
}, [focusedDate, ctx.isOpen]);
|
|
427
505
|
const navigateMonth = useCallback(
|
|
@@ -495,6 +573,15 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
|
|
|
495
573
|
}
|
|
496
574
|
if (newFocused) {
|
|
497
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
|
+
}
|
|
498
585
|
ctx.setFocusedDate(newFocused);
|
|
499
586
|
if (!adapter.isSameMonth(newFocused, viewMonth)) {
|
|
500
587
|
ctx.setViewMonth(newFocused);
|
|
@@ -542,62 +629,170 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
|
|
|
542
629
|
ref: gridRef,
|
|
543
630
|
role: "grid",
|
|
544
631
|
"aria-label": title,
|
|
632
|
+
"aria-rowcount": weeks.length + 1,
|
|
633
|
+
"aria-colcount": 7,
|
|
545
634
|
className: classNames?.grid,
|
|
546
635
|
onKeyDown: handleKeyDown,
|
|
547
636
|
children: [
|
|
548
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */
|
|
549
|
-
"th",
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
abbr: day.full,
|
|
553
|
-
scope: "col",
|
|
554
|
-
className: classNames?.weekdayHeader,
|
|
555
|
-
children: day.short
|
|
556
|
-
},
|
|
557
|
-
day.short
|
|
558
|
-
)) }) }),
|
|
559
|
-
/* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsx("tr", { role: "row", className: classNames?.gridRow, children: week.map((day) => {
|
|
560
|
-
const dayClasses = [
|
|
561
|
-
classNames?.day,
|
|
562
|
-
day.isSelected && classNames?.daySelected,
|
|
563
|
-
day.isToday && classNames?.dayToday,
|
|
564
|
-
day.isDisabled && classNames?.dayDisabled,
|
|
565
|
-
!day.isCurrentMonth && classNames?.dayOutsideMonth
|
|
566
|
-
].filter(Boolean).join(" ") || void 0;
|
|
567
|
-
return /* @__PURE__ */ jsx(
|
|
568
|
-
"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",
|
|
569
641
|
{
|
|
570
|
-
role: "
|
|
571
|
-
|
|
572
|
-
"
|
|
573
|
-
"aria-
|
|
574
|
-
className: classNames?.
|
|
575
|
-
children:
|
|
576
|
-
|
|
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",
|
|
577
661
|
{
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
"data-
|
|
582
|
-
|
|
583
|
-
"data-today": day.isToday || void 0,
|
|
584
|
-
"data-outside-month": !day.isCurrentMonth || void 0,
|
|
585
|
-
className: dayClasses,
|
|
586
|
-
onClick: () => handleDayClick(day),
|
|
587
|
-
"aria-label": safeFormatFullDate(day.isoString, locale),
|
|
588
|
-
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)
|
|
589
667
|
}
|
|
590
|
-
)
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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
|
+
)) })
|
|
595
710
|
]
|
|
596
711
|
}
|
|
597
712
|
),
|
|
598
713
|
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: srOnly, children: announcement })
|
|
599
714
|
] });
|
|
600
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
|
+
}
|
|
601
796
|
function DatePickerMonthGrid({
|
|
602
797
|
classNames,
|
|
603
798
|
onSelect,
|
|
@@ -605,15 +800,18 @@ function DatePickerMonthGrid({
|
|
|
605
800
|
...props
|
|
606
801
|
}) {
|
|
607
802
|
const ctx = useDatePickerContext("DatePicker.MonthGrid");
|
|
608
|
-
const { adapter, viewMonth, locale } = ctx;
|
|
803
|
+
const { adapter, viewMonth, locale, displayTimezone } = ctx;
|
|
609
804
|
const currentYear = adapter.getYear(viewMonth);
|
|
610
805
|
const currentMonth = adapter.getMonth(viewMonth);
|
|
611
|
-
const
|
|
612
|
-
|
|
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;
|
|
613
812
|
const navigateYear = useCallback(
|
|
614
813
|
(direction) => {
|
|
615
|
-
|
|
616
|
-
ctx.setViewMonth(newDate);
|
|
814
|
+
ctx.setViewMonth(adapter.addYears(viewMonth, direction));
|
|
617
815
|
},
|
|
618
816
|
[adapter, viewMonth, ctx]
|
|
619
817
|
);
|
|
@@ -626,12 +824,13 @@ function DatePickerMonthGrid({
|
|
|
626
824
|
},
|
|
627
825
|
[currentYear, ctx, onSelect]
|
|
628
826
|
);
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
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
|
+
});
|
|
635
834
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
636
835
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
637
836
|
/* @__PURE__ */ jsx(
|
|
@@ -644,15 +843,7 @@ function DatePickerMonthGrid({
|
|
|
644
843
|
children: "<"
|
|
645
844
|
}
|
|
646
845
|
),
|
|
647
|
-
onTitleClick ? /* @__PURE__ */ jsx(
|
|
648
|
-
"button",
|
|
649
|
-
{
|
|
650
|
-
type: "button",
|
|
651
|
-
className: classNames?.title,
|
|
652
|
-
onClick: onTitleClick,
|
|
653
|
-
children: currentYear
|
|
654
|
-
}
|
|
655
|
-
) : /* @__PURE__ */ jsx("span", { className: classNames?.title, children: currentYear }),
|
|
846
|
+
onTitleClick ? /* @__PURE__ */ jsx("button", { type: "button", className: classNames?.title, onClick: onTitleClick, children: currentYear }) : /* @__PURE__ */ jsx("span", { className: classNames?.title, children: currentYear }),
|
|
656
847
|
/* @__PURE__ */ jsx(
|
|
657
848
|
"button",
|
|
658
849
|
{
|
|
@@ -667,74 +858,77 @@ function DatePickerMonthGrid({
|
|
|
667
858
|
/* @__PURE__ */ jsx(
|
|
668
859
|
"div",
|
|
669
860
|
{
|
|
861
|
+
ref: gridRef,
|
|
670
862
|
role: "grid",
|
|
671
863
|
"aria-label": `${currentYear} months`,
|
|
672
864
|
className: classNames?.grid,
|
|
673
865
|
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
674
|
-
|
|
675
|
-
|
|
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 = [
|
|
676
872
|
classNames?.month,
|
|
677
|
-
|
|
678
|
-
|
|
873
|
+
isSelected && classNames?.monthSelected,
|
|
874
|
+
isCurrent && classNames?.monthCurrent
|
|
679
875
|
].filter(Boolean).join(" ") || void 0;
|
|
680
876
|
return /* @__PURE__ */ jsx(
|
|
681
877
|
"button",
|
|
682
878
|
{
|
|
683
879
|
type: "button",
|
|
684
880
|
role: "gridcell",
|
|
685
|
-
|
|
686
|
-
"aria-
|
|
687
|
-
"
|
|
688
|
-
"data-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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)
|
|
692
890
|
},
|
|
693
|
-
|
|
891
|
+
i
|
|
694
892
|
);
|
|
695
893
|
})
|
|
696
894
|
}
|
|
697
895
|
)
|
|
698
896
|
] });
|
|
699
897
|
}
|
|
700
|
-
function DatePickerYearGrid({
|
|
701
|
-
classNames,
|
|
702
|
-
onSelect,
|
|
703
|
-
...props
|
|
704
|
-
}) {
|
|
898
|
+
function DatePickerYearGrid({ classNames, onSelect, ...props }) {
|
|
705
899
|
const ctx = useDatePickerContext("DatePicker.YearGrid");
|
|
706
|
-
const { adapter, viewMonth } = ctx;
|
|
900
|
+
const { adapter, viewMonth, displayTimezone } = ctx;
|
|
707
901
|
const currentYear = adapter.getYear(viewMonth);
|
|
708
|
-
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;
|
|
709
907
|
const decadeStart = currentYear - currentYear % 12;
|
|
710
908
|
const navigateDecade = useCallback(
|
|
711
909
|
(direction) => {
|
|
712
|
-
|
|
713
|
-
ctx.setViewMonth(newDate);
|
|
910
|
+
ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
|
|
714
911
|
},
|
|
715
912
|
[adapter, viewMonth, ctx]
|
|
716
913
|
);
|
|
717
914
|
const handleYearSelect = useCallback(
|
|
718
|
-
(
|
|
915
|
+
(indexInDecade) => {
|
|
916
|
+
const year = decadeStart + indexInDecade;
|
|
719
917
|
const currentMonth = adapter.getMonth(viewMonth);
|
|
720
918
|
const target = new Date(Date.UTC(year, currentMonth, 1)).toISOString();
|
|
721
919
|
ctx.setViewMonth(target);
|
|
722
920
|
ctx.setFocusedDate(target);
|
|
723
921
|
onSelect?.();
|
|
724
922
|
},
|
|
725
|
-
[adapter, viewMonth, ctx, onSelect]
|
|
726
|
-
);
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
};
|
|
735
|
-
}),
|
|
736
|
-
[decadeStart, currentYear, todayYear]
|
|
737
|
-
);
|
|
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
|
+
});
|
|
738
932
|
const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
|
|
739
933
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
740
934
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
@@ -763,30 +957,38 @@ function DatePickerYearGrid({
|
|
|
763
957
|
/* @__PURE__ */ jsx(
|
|
764
958
|
"div",
|
|
765
959
|
{
|
|
960
|
+
ref: gridRef,
|
|
766
961
|
role: "grid",
|
|
767
962
|
"aria-label": rangeLabel,
|
|
768
963
|
className: classNames?.grid,
|
|
769
964
|
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
770
|
-
|
|
771
|
-
|
|
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 = [
|
|
772
972
|
classNames?.year,
|
|
773
|
-
|
|
774
|
-
|
|
973
|
+
isSelected && classNames?.yearSelected,
|
|
974
|
+
isCurrent && classNames?.yearCurrent
|
|
775
975
|
].filter(Boolean).join(" ") || void 0;
|
|
776
976
|
return /* @__PURE__ */ jsx(
|
|
777
977
|
"button",
|
|
778
978
|
{
|
|
779
979
|
type: "button",
|
|
780
980
|
role: "gridcell",
|
|
781
|
-
|
|
782
|
-
"aria-
|
|
783
|
-
"
|
|
784
|
-
"data-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
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
|
|
788
990
|
},
|
|
789
|
-
|
|
991
|
+
i
|
|
790
992
|
);
|
|
791
993
|
})
|
|
792
994
|
}
|
|
@@ -795,16 +997,7 @@ function DatePickerYearGrid({
|
|
|
795
997
|
}
|
|
796
998
|
function DatePickerPresets({ classNames, children, ...props }) {
|
|
797
999
|
const ctx = useDatePickerContext("DatePicker.Presets");
|
|
798
|
-
return /* @__PURE__ */ jsx(
|
|
799
|
-
"div",
|
|
800
|
-
{
|
|
801
|
-
role: "group",
|
|
802
|
-
"aria-label": ctx.labels.popoverLabel,
|
|
803
|
-
className: classNames?.root,
|
|
804
|
-
...props,
|
|
805
|
-
children
|
|
806
|
-
}
|
|
807
|
-
);
|
|
1000
|
+
return /* @__PURE__ */ jsx("div", { role: "group", "aria-label": ctx.labels.popoverLabel, className: classNames?.root, ...props, children });
|
|
808
1001
|
}
|
|
809
1002
|
function resolveDatePreset(key, today, adapter) {
|
|
810
1003
|
switch (key) {
|
|
@@ -858,11 +1051,7 @@ function DatePickerPreset({
|
|
|
858
1051
|
if (directDate) {
|
|
859
1052
|
target = directDate;
|
|
860
1053
|
} else if (presetKey) {
|
|
861
|
-
target = resolveDatePreset(
|
|
862
|
-
presetKey,
|
|
863
|
-
ctx.adapter.today(ctx.displayTimezone),
|
|
864
|
-
ctx.adapter
|
|
865
|
-
);
|
|
1054
|
+
target = resolveDatePreset(presetKey, ctx.adapter.today(ctx.displayTimezone), ctx.adapter);
|
|
866
1055
|
} else {
|
|
867
1056
|
return false;
|
|
868
1057
|
}
|
|
@@ -872,8 +1061,7 @@ function DatePickerPreset({
|
|
|
872
1061
|
"button",
|
|
873
1062
|
{
|
|
874
1063
|
type: "button",
|
|
875
|
-
|
|
876
|
-
"aria-selected": isActive,
|
|
1064
|
+
"aria-pressed": isActive,
|
|
877
1065
|
"data-active": isActive || void 0,
|
|
878
1066
|
disabled: ctx.isDisabled,
|
|
879
1067
|
onClick: handleClick,
|
|
@@ -937,10 +1125,10 @@ function RangePickerRoot({
|
|
|
937
1125
|
const [selectingTarget, setSelectingTarget] = useState("start");
|
|
938
1126
|
const [hoverDate, setHoverDate] = useState(null);
|
|
939
1127
|
const [viewMonth, setViewMonth] = useState(
|
|
940
|
-
currentValue.start ?? adapter.today(displayTimezone)
|
|
1128
|
+
() => currentValue.start ?? adapter.today(displayTimezone)
|
|
941
1129
|
);
|
|
942
1130
|
const [focusedDate, setFocusedDate] = useState(
|
|
943
|
-
currentValue.start ?? adapter.today(displayTimezone)
|
|
1131
|
+
() => currentValue.start ?? adapter.today(displayTimezone)
|
|
944
1132
|
);
|
|
945
1133
|
useChangeEffect(isOpen, onOpenChange);
|
|
946
1134
|
const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
|
|
@@ -1090,6 +1278,8 @@ var RangePickerInput = forwardRef(
|
|
|
1090
1278
|
(e) => {
|
|
1091
1279
|
if (e.key === "Escape") {
|
|
1092
1280
|
ctx.close();
|
|
1281
|
+
} else if (e.key === "Enter" && ctx.isOpen) {
|
|
1282
|
+
e.preventDefault();
|
|
1093
1283
|
} else if (e.key === "ArrowDown" && !ctx.isOpen) {
|
|
1094
1284
|
e.preventDefault();
|
|
1095
1285
|
ctx.open();
|
|
@@ -1126,15 +1316,17 @@ var RangePickerInput = forwardRef(
|
|
|
1126
1316
|
);
|
|
1127
1317
|
}
|
|
1128
1318
|
);
|
|
1319
|
+
RangePickerInput.displayName = "RangePicker.Input";
|
|
1129
1320
|
function RangePickerPopover({ children, ...props }) {
|
|
1130
1321
|
const ctx = useRangePickerContext("RangePicker.Popover");
|
|
1131
1322
|
const calendarId = `${ctx.pickerId}-calendar`;
|
|
1132
|
-
const { floatingStyles, setFloatingRef } = usePopover({
|
|
1323
|
+
const { floatingStyles, setFloatingRef, isPositioned } = usePopover({
|
|
1133
1324
|
isOpen: ctx.isOpen,
|
|
1134
1325
|
close: ctx.close,
|
|
1135
1326
|
referenceRef: ctx.referenceRef
|
|
1136
1327
|
});
|
|
1137
1328
|
if (!ctx.isOpen) return null;
|
|
1329
|
+
const { style: userStyle, ...rest } = props;
|
|
1138
1330
|
return /* @__PURE__ */ jsx(
|
|
1139
1331
|
"div",
|
|
1140
1332
|
{
|
|
@@ -1143,8 +1335,12 @@ function RangePickerPopover({ children, ...props }) {
|
|
|
1143
1335
|
role: "dialog",
|
|
1144
1336
|
"aria-label": ctx.labels.popoverLabel,
|
|
1145
1337
|
"aria-modal": "false",
|
|
1146
|
-
|
|
1147
|
-
|
|
1338
|
+
...rest,
|
|
1339
|
+
style: {
|
|
1340
|
+
...userStyle,
|
|
1341
|
+
...floatingStyles,
|
|
1342
|
+
visibility: isPositioned ? void 0 : "hidden"
|
|
1343
|
+
},
|
|
1148
1344
|
children
|
|
1149
1345
|
}
|
|
1150
1346
|
);
|
|
@@ -1170,6 +1366,8 @@ var srOnly2 = {
|
|
|
1170
1366
|
function RangePickerCalendar({
|
|
1171
1367
|
classNames,
|
|
1172
1368
|
selectionMode = "range",
|
|
1369
|
+
showWeekNumber = false,
|
|
1370
|
+
fixedWeeks = false,
|
|
1173
1371
|
...props
|
|
1174
1372
|
}) {
|
|
1175
1373
|
const ctx = useRangePickerContext("RangePicker.Calendar");
|
|
@@ -1187,23 +1385,36 @@ function RangePickerCalendar({
|
|
|
1187
1385
|
displayTimezone
|
|
1188
1386
|
} = ctx;
|
|
1189
1387
|
const { locale } = ctx;
|
|
1190
|
-
const weekdays = getWeekdayNames(locale, weekStartsOn);
|
|
1191
|
-
const weeks =
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
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;
|
|
1199
1412
|
const year = adapter.getYear(viewMonth);
|
|
1200
1413
|
const month = adapter.getMonth(viewMonth);
|
|
1201
1414
|
const title = formatMonthYear(year, month, locale);
|
|
1202
1415
|
useEffect(() => {
|
|
1203
1416
|
if (!ctx.isOpen || !gridRef.current) return;
|
|
1204
|
-
const focusedButton = gridRef.current.querySelector(
|
|
1205
|
-
'[data-focused="true"]'
|
|
1206
|
-
);
|
|
1417
|
+
const focusedButton = gridRef.current.querySelector('[data-focused="true"]');
|
|
1207
1418
|
focusedButton?.focus({ preventScroll: true });
|
|
1208
1419
|
}, [focusedDate, ctx.isOpen]);
|
|
1209
1420
|
const navigateMonth = useCallback(
|
|
@@ -1297,6 +1508,13 @@ function RangePickerCalendar({
|
|
|
1297
1508
|
}
|
|
1298
1509
|
if (newFocused) {
|
|
1299
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;
|
|
1300
1518
|
ctx.setFocusedDate(newFocused);
|
|
1301
1519
|
if (!adapter.isSameMonth(newFocused, viewMonth)) {
|
|
1302
1520
|
ctx.setViewMonth(newFocused);
|
|
@@ -1306,7 +1524,18 @@ function RangePickerCalendar({
|
|
|
1306
1524
|
}
|
|
1307
1525
|
}
|
|
1308
1526
|
},
|
|
1309
|
-
[
|
|
1527
|
+
[
|
|
1528
|
+
adapter,
|
|
1529
|
+
focusedDate,
|
|
1530
|
+
viewMonth,
|
|
1531
|
+
weekStartsOn,
|
|
1532
|
+
disabled,
|
|
1533
|
+
ctx,
|
|
1534
|
+
selectionMode,
|
|
1535
|
+
selectingTarget,
|
|
1536
|
+
value.start,
|
|
1537
|
+
commitDay
|
|
1538
|
+
]
|
|
1310
1539
|
);
|
|
1311
1540
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, onMouseLeave: handleMouseLeave, children: [
|
|
1312
1541
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
@@ -1338,63 +1567,90 @@ function RangePickerCalendar({
|
|
|
1338
1567
|
ref: gridRef,
|
|
1339
1568
|
role: "grid",
|
|
1340
1569
|
"aria-label": title,
|
|
1341
|
-
"aria-
|
|
1570
|
+
"aria-rowcount": weeks.length + 1,
|
|
1571
|
+
"aria-colcount": 7,
|
|
1342
1572
|
className: classNames?.grid,
|
|
1343
1573
|
onKeyDown: handleKeyDown,
|
|
1344
1574
|
children: [
|
|
1345
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */
|
|
1346
|
-
"th",
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
abbr: day.full,
|
|
1350
|
-
scope: "col",
|
|
1351
|
-
className: classNames?.weekdayHeader,
|
|
1352
|
-
children: day.short
|
|
1353
|
-
},
|
|
1354
|
-
day.short
|
|
1355
|
-
)) }) }),
|
|
1356
|
-
/* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsx("tr", { role: "row", className: classNames?.gridRow, children: week.map((day) => {
|
|
1357
|
-
const dayClasses = [
|
|
1358
|
-
classNames?.day,
|
|
1359
|
-
day.isRangeStart && classNames?.dayRangeStart,
|
|
1360
|
-
day.isRangeEnd && classNames?.dayRangeEnd,
|
|
1361
|
-
day.isInRange && classNames?.dayInRange,
|
|
1362
|
-
day.isToday && classNames?.dayToday,
|
|
1363
|
-
day.isDisabled && classNames?.dayDisabled,
|
|
1364
|
-
!day.isCurrentMonth && classNames?.dayOutsideMonth
|
|
1365
|
-
].filter(Boolean).join(" ") || void 0;
|
|
1366
|
-
const isSelected = selectionMode === "week" ? day.isRangeStart || day.isRangeEnd || day.isInRange : day.isRangeStart || day.isRangeEnd;
|
|
1367
|
-
return /* @__PURE__ */ jsx(
|
|
1368
|
-
"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",
|
|
1369
1579
|
{
|
|
1370
|
-
role: "
|
|
1371
|
-
|
|
1372
|
-
"
|
|
1373
|
-
"aria-
|
|
1374
|
-
className: classNames?.
|
|
1375
|
-
children:
|
|
1376
|
-
|
|
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",
|
|
1377
1599
|
{
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
"data-
|
|
1382
|
-
|
|
1383
|
-
"data-range-end": day.isRangeEnd || void 0,
|
|
1384
|
-
"data-in-range": day.isInRange || void 0,
|
|
1385
|
-
"data-today": day.isToday || void 0,
|
|
1386
|
-
"data-outside-month": !day.isCurrentMonth || void 0,
|
|
1387
|
-
className: dayClasses,
|
|
1388
|
-
onClick: () => handleDayClick(day),
|
|
1389
|
-
onMouseEnter: () => handleDayMouseEnter(day),
|
|
1390
|
-
"aria-label": safeFormatFullDate2(day.isoString, locale),
|
|
1391
|
-
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)
|
|
1392
1605
|
}
|
|
1393
|
-
)
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
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
|
+
)) })
|
|
1398
1654
|
]
|
|
1399
1655
|
}
|
|
1400
1656
|
),
|
|
@@ -1443,9 +1699,7 @@ function resolvePreset(key, today, adapter) {
|
|
|
1443
1699
|
}
|
|
1444
1700
|
case "thisYear": {
|
|
1445
1701
|
const currentMonth = new Date(today).getUTCMonth();
|
|
1446
|
-
const yearStart = adapter.startOfMonth(
|
|
1447
|
-
adapter.addMonths(today, -currentMonth)
|
|
1448
|
-
);
|
|
1702
|
+
const yearStart = adapter.startOfMonth(adapter.addMonths(today, -currentMonth));
|
|
1449
1703
|
return { start: yearStart, end: today };
|
|
1450
1704
|
}
|
|
1451
1705
|
}
|
|
@@ -1458,41 +1712,33 @@ function RangePickerPreset({
|
|
|
1458
1712
|
...props
|
|
1459
1713
|
}) {
|
|
1460
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]);
|
|
1461
1721
|
const handleClick = useCallback(
|
|
1462
1722
|
(e) => {
|
|
1463
1723
|
if (ctx.isDisabled || ctx.isReadOnly) return;
|
|
1464
|
-
|
|
1465
|
-
if (directRange) {
|
|
1466
|
-
resolved = directRange;
|
|
1467
|
-
} else if (presetKey) {
|
|
1468
|
-
resolved = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
|
|
1469
|
-
} else {
|
|
1470
|
-
return;
|
|
1471
|
-
}
|
|
1724
|
+
if (!resolved) return;
|
|
1472
1725
|
ctx.setRange(resolved);
|
|
1473
1726
|
ctx.close();
|
|
1474
1727
|
onClick?.(e);
|
|
1475
1728
|
},
|
|
1476
|
-
[ctx,
|
|
1729
|
+
[ctx, resolved, onClick]
|
|
1477
1730
|
);
|
|
1478
|
-
const isActive = (() => {
|
|
1479
|
-
if (!ctx.value.start || !ctx.value.end)
|
|
1480
|
-
let target;
|
|
1481
|
-
if (directRange) {
|
|
1482
|
-
target = directRange;
|
|
1483
|
-
} else if (presetKey) {
|
|
1484
|
-
target = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
|
|
1485
|
-
} else {
|
|
1731
|
+
const isActive = useMemo(() => {
|
|
1732
|
+
if (!ctx.value.start || !ctx.value.end || !resolved || !resolved.start || !resolved.end) {
|
|
1486
1733
|
return false;
|
|
1487
1734
|
}
|
|
1488
|
-
return
|
|
1489
|
-
})
|
|
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]);
|
|
1490
1737
|
return /* @__PURE__ */ jsx(
|
|
1491
1738
|
"button",
|
|
1492
1739
|
{
|
|
1493
1740
|
type: "button",
|
|
1494
|
-
|
|
1495
|
-
"aria-selected": isActive,
|
|
1741
|
+
"aria-pressed": isActive,
|
|
1496
1742
|
"data-active": isActive || void 0,
|
|
1497
1743
|
disabled: ctx.isDisabled,
|
|
1498
1744
|
onClick: handleClick,
|
|
@@ -1525,9 +1771,6 @@ function useTimePickerContext(componentName) {
|
|
|
1525
1771
|
}
|
|
1526
1772
|
return context;
|
|
1527
1773
|
}
|
|
1528
|
-
function getDefaultIso() {
|
|
1529
|
-
return DateFnsAdapter.today();
|
|
1530
|
-
}
|
|
1531
1774
|
function TimePickerRoot({
|
|
1532
1775
|
value: controlledValue,
|
|
1533
1776
|
defaultValue,
|
|
@@ -1538,6 +1781,7 @@ function TimePickerRoot({
|
|
|
1538
1781
|
displayTimezone,
|
|
1539
1782
|
disabled = false,
|
|
1540
1783
|
readOnly = false,
|
|
1784
|
+
filterTime,
|
|
1541
1785
|
labels: labelsProp,
|
|
1542
1786
|
children
|
|
1543
1787
|
}) {
|
|
@@ -1551,21 +1795,21 @@ function TimePickerRoot({
|
|
|
1551
1795
|
defaultValue ?? null
|
|
1552
1796
|
);
|
|
1553
1797
|
const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
);
|
|
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]);
|
|
1559
1802
|
const setTime$1 = useCallback(
|
|
1560
1803
|
(partial) => {
|
|
1561
1804
|
if (disabled || readOnly) return;
|
|
1562
|
-
const
|
|
1805
|
+
const base = currentValue ?? DateFnsAdapter.today(displayTimezone);
|
|
1806
|
+
const newIso = displayTimezone ? setTimeInTimezone(base, partial, displayTimezone) : setTime(base, partial);
|
|
1563
1807
|
if (!isControlled) {
|
|
1564
1808
|
setUncontrolledValue(newIso);
|
|
1565
1809
|
}
|
|
1566
1810
|
onChange?.(newIso);
|
|
1567
1811
|
},
|
|
1568
|
-
[disabled, readOnly,
|
|
1812
|
+
[disabled, readOnly, currentValue, displayTimezone, isControlled, onChange]
|
|
1569
1813
|
);
|
|
1570
1814
|
const contextValue = useMemo(
|
|
1571
1815
|
() => ({
|
|
@@ -1579,9 +1823,23 @@ function TimePickerRoot({
|
|
|
1579
1823
|
isReadOnly: readOnly,
|
|
1580
1824
|
currentTime,
|
|
1581
1825
|
pickerId,
|
|
1582
|
-
labels: mergedLabels
|
|
1826
|
+
labels: mergedLabels,
|
|
1827
|
+
filterTime
|
|
1583
1828
|
}),
|
|
1584
|
-
[
|
|
1829
|
+
[
|
|
1830
|
+
currentValue,
|
|
1831
|
+
setTime$1,
|
|
1832
|
+
format,
|
|
1833
|
+
step,
|
|
1834
|
+
withSeconds,
|
|
1835
|
+
displayTimezone,
|
|
1836
|
+
disabled,
|
|
1837
|
+
readOnly,
|
|
1838
|
+
currentTime,
|
|
1839
|
+
pickerId,
|
|
1840
|
+
mergedLabels,
|
|
1841
|
+
filterTime
|
|
1842
|
+
]
|
|
1585
1843
|
);
|
|
1586
1844
|
return /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: contextValue, children });
|
|
1587
1845
|
}
|
|
@@ -1598,12 +1856,9 @@ var TimePickerInput = forwardRef(
|
|
|
1598
1856
|
}
|
|
1599
1857
|
setInputText(null);
|
|
1600
1858
|
}, [inputText, ctx]);
|
|
1601
|
-
const handleChange = useCallback(
|
|
1602
|
-
(e)
|
|
1603
|
-
|
|
1604
|
-
},
|
|
1605
|
-
[]
|
|
1606
|
-
);
|
|
1859
|
+
const handleChange = useCallback((e) => {
|
|
1860
|
+
setInputText(e.target.value);
|
|
1861
|
+
}, []);
|
|
1607
1862
|
const handleBlur = useCallback(
|
|
1608
1863
|
(e) => {
|
|
1609
1864
|
commitInput();
|
|
@@ -1640,6 +1895,7 @@ var TimePickerInput = forwardRef(
|
|
|
1640
1895
|
);
|
|
1641
1896
|
}
|
|
1642
1897
|
);
|
|
1898
|
+
TimePickerInput.displayName = "TimePicker.Input";
|
|
1643
1899
|
function useListboxNavigation({
|
|
1644
1900
|
items,
|
|
1645
1901
|
onSelect,
|
|
@@ -1676,9 +1932,7 @@ function useListboxNavigation({
|
|
|
1676
1932
|
onSelect(target);
|
|
1677
1933
|
cancelAnimationFrame(rafIdRef.current);
|
|
1678
1934
|
rafIdRef.current = requestAnimationFrame(() => {
|
|
1679
|
-
const next = listRef.current?.querySelector(
|
|
1680
|
-
'[data-selected="true"]'
|
|
1681
|
-
);
|
|
1935
|
+
const next = listRef.current?.querySelector('[data-selected="true"]');
|
|
1682
1936
|
next?.focus();
|
|
1683
1937
|
});
|
|
1684
1938
|
}
|
|
@@ -1689,17 +1943,41 @@ function useListboxNavigation({
|
|
|
1689
1943
|
}
|
|
1690
1944
|
function TimePickerHourList({ classNames, ...props }) {
|
|
1691
1945
|
const ctx = useTimePickerContext("TimePicker.HourList");
|
|
1692
|
-
const { format, currentTime, isDisabled, isReadOnly } = ctx;
|
|
1693
|
-
const hours = generateHours(format);
|
|
1946
|
+
const { format, step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
|
|
1947
|
+
const hours = useMemo(() => generateHours(format), [format]);
|
|
1694
1948
|
const selectedHourDisplay = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;
|
|
1695
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
|
+
);
|
|
1696
1973
|
const handleSelect = useCallback(
|
|
1697
1974
|
(hourDisplay) => {
|
|
1698
1975
|
if (isDisabled || isReadOnly) return;
|
|
1976
|
+
if (isHourDisabled(hourDisplay)) return;
|
|
1699
1977
|
const hours24 = format === "12h" && currentPeriod ? to24Hour(hourDisplay, currentPeriod) : hourDisplay;
|
|
1700
1978
|
ctx.setTime({ hours: hours24 });
|
|
1701
1979
|
},
|
|
1702
|
-
[format, currentPeriod, ctx, isDisabled, isReadOnly]
|
|
1980
|
+
[format, currentPeriod, ctx, isDisabled, isReadOnly, isHourDisabled]
|
|
1703
1981
|
);
|
|
1704
1982
|
const { listRef, handleKeyDown } = useListboxNavigation({
|
|
1705
1983
|
items: hours,
|
|
@@ -1717,13 +1995,14 @@ function TimePickerHourList({ classNames, ...props }) {
|
|
|
1717
1995
|
...props,
|
|
1718
1996
|
children: hours.map((hour) => {
|
|
1719
1997
|
const isSelected = hour === selectedHourDisplay;
|
|
1998
|
+
const isHourFullyDisabled = isHourDisabled(hour);
|
|
1720
1999
|
const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
|
|
1721
2000
|
return /* @__PURE__ */ jsx(
|
|
1722
2001
|
"li",
|
|
1723
2002
|
{
|
|
1724
2003
|
role: "option",
|
|
1725
2004
|
"aria-selected": isSelected,
|
|
1726
|
-
"aria-disabled": isDisabled || void 0,
|
|
2005
|
+
"aria-disabled": isDisabled || isHourFullyDisabled || void 0,
|
|
1727
2006
|
"aria-label": ctx.labels.hourOption(hour),
|
|
1728
2007
|
"data-selected": isSelected || void 0,
|
|
1729
2008
|
tabIndex: isSelected ? 0 : -1,
|
|
@@ -1740,14 +2019,22 @@ function TimePickerHourList({ classNames, ...props }) {
|
|
|
1740
2019
|
}
|
|
1741
2020
|
function TimePickerMinuteList({ classNames, ...props }) {
|
|
1742
2021
|
const ctx = useTimePickerContext("TimePicker.MinuteList");
|
|
1743
|
-
const { step, currentTime, isDisabled, isReadOnly } = ctx;
|
|
1744
|
-
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
|
+
);
|
|
1745
2031
|
const handleSelect = useCallback(
|
|
1746
2032
|
(minute) => {
|
|
1747
2033
|
if (isDisabled || isReadOnly) return;
|
|
2034
|
+
if (isMinuteDisabled(minute)) return;
|
|
1748
2035
|
ctx.setTime({ minutes: minute });
|
|
1749
2036
|
},
|
|
1750
|
-
[ctx, isDisabled, isReadOnly]
|
|
2037
|
+
[ctx, isDisabled, isReadOnly, isMinuteDisabled]
|
|
1751
2038
|
);
|
|
1752
2039
|
const { listRef, handleKeyDown } = useListboxNavigation({
|
|
1753
2040
|
items: minutes,
|
|
@@ -1765,13 +2052,14 @@ function TimePickerMinuteList({ classNames, ...props }) {
|
|
|
1765
2052
|
...props,
|
|
1766
2053
|
children: minutes.map((minute) => {
|
|
1767
2054
|
const isSelected = minute === currentTime.minutes;
|
|
2055
|
+
const isMinuteFullyDisabled = isMinuteDisabled(minute);
|
|
1768
2056
|
const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
|
|
1769
2057
|
return /* @__PURE__ */ jsx(
|
|
1770
2058
|
"li",
|
|
1771
2059
|
{
|
|
1772
2060
|
role: "option",
|
|
1773
2061
|
"aria-selected": isSelected,
|
|
1774
|
-
"aria-disabled": isDisabled || void 0,
|
|
2062
|
+
"aria-disabled": isDisabled || isMinuteFullyDisabled || void 0,
|
|
1775
2063
|
"aria-label": ctx.labels.minuteOption(minute),
|
|
1776
2064
|
"data-selected": isSelected || void 0,
|
|
1777
2065
|
tabIndex: isSelected ? 0 : -1,
|
|
@@ -1788,37 +2076,87 @@ function TimePickerMinuteList({ classNames, ...props }) {
|
|
|
1788
2076
|
}
|
|
1789
2077
|
function TimePickerAmPmToggle({ classNames, ...props }) {
|
|
1790
2078
|
const ctx = useTimePickerContext("TimePicker.AmPmToggle");
|
|
1791
|
-
|
|
1792
|
-
const
|
|
2079
|
+
const amRef = useRef(null);
|
|
2080
|
+
const pmRef = useRef(null);
|
|
1793
2081
|
const setPeriod = useCallback(
|
|
1794
2082
|
(newPeriod) => {
|
|
1795
2083
|
if (ctx.isDisabled || ctx.isReadOnly) return;
|
|
2084
|
+
const { hours12 } = to12Hour(ctx.currentTime.hours);
|
|
1796
2085
|
const newHours24 = to24Hour(hours12, newPeriod);
|
|
1797
2086
|
ctx.setTime({ hours: newHours24 });
|
|
1798
2087
|
},
|
|
1799
|
-
[
|
|
2088
|
+
[ctx]
|
|
1800
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
|
+
};
|
|
1801
2127
|
const renderButton = (target) => {
|
|
1802
2128
|
const isSelected = period === target;
|
|
1803
2129
|
const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
|
|
1804
2130
|
return /* @__PURE__ */ jsx(
|
|
1805
2131
|
"button",
|
|
1806
2132
|
{
|
|
2133
|
+
ref: target === "AM" ? amRef : pmRef,
|
|
1807
2134
|
type: "button",
|
|
1808
2135
|
role: "radio",
|
|
1809
2136
|
"aria-checked": isSelected,
|
|
2137
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1810
2138
|
"data-selected": isSelected || void 0,
|
|
1811
2139
|
disabled: ctx.isDisabled,
|
|
1812
2140
|
className: optionClass,
|
|
1813
2141
|
onClick: () => setPeriod(target),
|
|
2142
|
+
onKeyDown: (e) => handleKeyDown(e, target),
|
|
1814
2143
|
children: target
|
|
1815
2144
|
}
|
|
1816
2145
|
);
|
|
1817
2146
|
};
|
|
1818
|
-
return /* @__PURE__ */ jsxs(
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
2147
|
+
return /* @__PURE__ */ jsxs(
|
|
2148
|
+
"div",
|
|
2149
|
+
{
|
|
2150
|
+
role: "radiogroup",
|
|
2151
|
+
"aria-label": ctx.labels.amPmToggle,
|
|
2152
|
+
className: classNames?.root,
|
|
2153
|
+
...props,
|
|
2154
|
+
children: [
|
|
2155
|
+
renderButton("AM"),
|
|
2156
|
+
renderButton("PM")
|
|
2157
|
+
]
|
|
2158
|
+
}
|
|
2159
|
+
);
|
|
1822
2160
|
}
|
|
1823
2161
|
|
|
1824
2162
|
// src/components/TimePicker/index.ts
|
|
@@ -1828,9 +2166,6 @@ var TimePicker = Object.assign(TimePickerRoot, {
|
|
|
1828
2166
|
MinuteList: TimePickerMinuteList,
|
|
1829
2167
|
AmPmToggle: TimePickerAmPmToggle
|
|
1830
2168
|
});
|
|
1831
|
-
function getDefaultIso2() {
|
|
1832
|
-
return DateFnsAdapter.today();
|
|
1833
|
-
}
|
|
1834
2169
|
function DateTimePickerRoot({
|
|
1835
2170
|
value: controlledValue,
|
|
1836
2171
|
defaultValue,
|
|
@@ -1839,6 +2174,8 @@ function DateTimePickerRoot({
|
|
|
1839
2174
|
onCalendarNavigate,
|
|
1840
2175
|
format = "24h",
|
|
1841
2176
|
step = 1,
|
|
2177
|
+
withSeconds = false,
|
|
2178
|
+
filterTime,
|
|
1842
2179
|
disabled = false,
|
|
1843
2180
|
readOnly = false,
|
|
1844
2181
|
weekStartsOn = 0,
|
|
@@ -1866,10 +2203,10 @@ function DateTimePickerRoot({
|
|
|
1866
2203
|
const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
|
|
1867
2204
|
const [isOpen, setIsOpen] = useState(false);
|
|
1868
2205
|
const [viewMonth, setViewMonth] = useState(
|
|
1869
|
-
currentValue ?? adapter.today(displayTimezone)
|
|
2206
|
+
() => currentValue ?? adapter.today(displayTimezone)
|
|
1870
2207
|
);
|
|
1871
2208
|
const [focusedDate, setFocusedDate] = useState(
|
|
1872
|
-
currentValue ?? adapter.today(displayTimezone)
|
|
2209
|
+
() => currentValue ?? adapter.today(displayTimezone)
|
|
1873
2210
|
);
|
|
1874
2211
|
useChangeEffect(isOpen, onOpenChange);
|
|
1875
2212
|
const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
|
|
@@ -1879,11 +2216,10 @@ function DateTimePickerRoot({
|
|
|
1879
2216
|
() => Array.isArray(disabled) ? disabled : [],
|
|
1880
2217
|
[disabled]
|
|
1881
2218
|
);
|
|
1882
|
-
const
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
);
|
|
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]);
|
|
1887
2223
|
const updateValue = useCallback(
|
|
1888
2224
|
(next) => {
|
|
1889
2225
|
if (isDisabled || readOnly) return;
|
|
@@ -1909,11 +2245,11 @@ function DateTimePickerRoot({
|
|
|
1909
2245
|
);
|
|
1910
2246
|
const setTime$1 = useCallback(
|
|
1911
2247
|
(partial) => {
|
|
1912
|
-
const base = currentValue ??
|
|
2248
|
+
const base = currentValue ?? adapter.today(displayTimezone);
|
|
1913
2249
|
const merged = displayTimezone ? setTimeInTimezone(base, partial, displayTimezone) : setTime(base, partial);
|
|
1914
2250
|
updateValue(merged);
|
|
1915
2251
|
},
|
|
1916
|
-
[currentValue, updateValue, displayTimezone]
|
|
2252
|
+
[currentValue, updateValue, displayTimezone, adapter]
|
|
1917
2253
|
);
|
|
1918
2254
|
const open = useCallback(() => {
|
|
1919
2255
|
if (isDisabled || readOnly) return;
|
|
@@ -1980,15 +2316,29 @@ function DateTimePickerRoot({
|
|
|
1980
2316
|
setTime: setTime$1,
|
|
1981
2317
|
format,
|
|
1982
2318
|
step,
|
|
1983
|
-
withSeconds
|
|
2319
|
+
withSeconds,
|
|
1984
2320
|
displayTimezone,
|
|
1985
2321
|
isDisabled,
|
|
1986
2322
|
isReadOnly: readOnly,
|
|
1987
2323
|
currentTime,
|
|
1988
2324
|
pickerId,
|
|
1989
|
-
labels: mergedTimeLabels
|
|
2325
|
+
labels: mergedTimeLabels,
|
|
2326
|
+
filterTime
|
|
1990
2327
|
}),
|
|
1991
|
-
[
|
|
2328
|
+
[
|
|
2329
|
+
currentValue,
|
|
2330
|
+
setTime$1,
|
|
2331
|
+
format,
|
|
2332
|
+
step,
|
|
2333
|
+
withSeconds,
|
|
2334
|
+
displayTimezone,
|
|
2335
|
+
isDisabled,
|
|
2336
|
+
readOnly,
|
|
2337
|
+
currentTime,
|
|
2338
|
+
pickerId,
|
|
2339
|
+
mergedTimeLabels,
|
|
2340
|
+
filterTime
|
|
2341
|
+
]
|
|
1992
2342
|
);
|
|
1993
2343
|
return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: dateContext, children: /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: timeContext, children }) });
|
|
1994
2344
|
}
|
|
@@ -2016,6 +2366,8 @@ var DateTimePickerInput = forwardRef(
|
|
|
2016
2366
|
(e) => {
|
|
2017
2367
|
if (e.key === "Escape") {
|
|
2018
2368
|
ctx.close();
|
|
2369
|
+
} else if (e.key === "Enter" && ctx.isOpen) {
|
|
2370
|
+
e.preventDefault();
|
|
2019
2371
|
} else if (e.key === "ArrowDown" && !ctx.isOpen) {
|
|
2020
2372
|
e.preventDefault();
|
|
2021
2373
|
ctx.open();
|
|
@@ -2051,6 +2403,7 @@ var DateTimePickerInput = forwardRef(
|
|
|
2051
2403
|
);
|
|
2052
2404
|
}
|
|
2053
2405
|
);
|
|
2406
|
+
DateTimePickerInput.displayName = "DateTimePicker.Input";
|
|
2054
2407
|
|
|
2055
2408
|
// src/components/DateTimePicker/index.ts
|
|
2056
2409
|
var DateTimePicker = Object.assign(DateTimePickerRoot, {
|
|
@@ -2067,12 +2420,9 @@ function MonthPickerRoot(props) {
|
|
|
2067
2420
|
const displayFormat = props.displayFormat ?? "yyyy-MM";
|
|
2068
2421
|
return /* @__PURE__ */ jsx(DatePickerRoot, { ...props, displayFormat });
|
|
2069
2422
|
}
|
|
2070
|
-
function MonthPickerGrid({
|
|
2071
|
-
classNames,
|
|
2072
|
-
...props
|
|
2073
|
-
}) {
|
|
2423
|
+
function MonthPickerGrid({ classNames, ...props }) {
|
|
2074
2424
|
const ctx = useDatePickerContext("MonthPicker.Grid");
|
|
2075
|
-
const { adapter, viewMonth, locale, value, displayTimezone, labels } = ctx;
|
|
2425
|
+
const { adapter, viewMonth, locale, value, displayTimezone, labels, disabled } = ctx;
|
|
2076
2426
|
const currentYear = adapter.getYear(viewMonth);
|
|
2077
2427
|
const [valueYear, valueMonthZeroBased] = useMemo(() => {
|
|
2078
2428
|
if (!value) return [null, null];
|
|
@@ -2083,9 +2433,19 @@ function MonthPickerGrid({
|
|
|
2083
2433
|
return [null, null];
|
|
2084
2434
|
}
|
|
2085
2435
|
}, [value, adapter, displayTimezone]);
|
|
2086
|
-
const today =
|
|
2087
|
-
|
|
2088
|
-
|
|
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
|
+
);
|
|
2089
2449
|
const navigateYear = useCallback(
|
|
2090
2450
|
(direction) => {
|
|
2091
2451
|
ctx.setViewMonth(adapter.addYears(viewMonth, direction));
|
|
@@ -2094,19 +2454,23 @@ function MonthPickerGrid({
|
|
|
2094
2454
|
);
|
|
2095
2455
|
const handleMonthSelect = useCallback(
|
|
2096
2456
|
(monthIndex) => {
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
).toISOString();
|
|
2457
|
+
if (monthDisabledFlags[monthIndex]) return;
|
|
2458
|
+
const target = new Date(Date.UTC(currentYear, monthIndex, 1)).toISOString();
|
|
2100
2459
|
ctx.selectDate(target);
|
|
2101
2460
|
},
|
|
2102
|
-
[currentYear, ctx]
|
|
2103
|
-
);
|
|
2104
|
-
const
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
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
|
+
});
|
|
2110
2474
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
2111
2475
|
/* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
|
|
2112
2476
|
/* @__PURE__ */ jsx(
|
|
@@ -2134,35 +2498,47 @@ function MonthPickerGrid({
|
|
|
2134
2498
|
/* @__PURE__ */ jsx(
|
|
2135
2499
|
"div",
|
|
2136
2500
|
{
|
|
2501
|
+
ref: gridRef,
|
|
2137
2502
|
role: "grid",
|
|
2138
2503
|
"aria-label": `${currentYear} months`,
|
|
2139
2504
|
className: classNames?.grid,
|
|
2505
|
+
onKeyDown: handleKeyDown,
|
|
2140
2506
|
children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
|
|
2141
2507
|
"div",
|
|
2142
2508
|
{
|
|
2143
2509
|
role: "row",
|
|
2144
2510
|
className: classNames?.gridRow,
|
|
2145
2511
|
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
2146
|
-
children:
|
|
2147
|
-
const
|
|
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 = [
|
|
2148
2519
|
classNames?.month,
|
|
2149
|
-
|
|
2150
|
-
|
|
2520
|
+
isSelected && classNames?.monthSelected,
|
|
2521
|
+
isCurrent && classNames?.monthCurrent,
|
|
2522
|
+
isDisabled && classNames?.monthDisabled
|
|
2151
2523
|
].filter(Boolean).join(" ") || void 0;
|
|
2152
2524
|
return /* @__PURE__ */ jsx(
|
|
2153
2525
|
"button",
|
|
2154
2526
|
{
|
|
2155
2527
|
type: "button",
|
|
2156
2528
|
role: "gridcell",
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
"
|
|
2160
|
-
"
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
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)
|
|
2164
2540
|
},
|
|
2165
|
-
|
|
2541
|
+
i
|
|
2166
2542
|
);
|
|
2167
2543
|
})
|
|
2168
2544
|
},
|
|
@@ -2186,7 +2562,7 @@ function YearPickerRoot(props) {
|
|
|
2186
2562
|
}
|
|
2187
2563
|
function YearPickerGrid({ classNames, ...props }) {
|
|
2188
2564
|
const ctx = useDatePickerContext("YearPicker.Grid");
|
|
2189
|
-
const { adapter, viewMonth, value, displayTimezone, labels } = ctx;
|
|
2565
|
+
const { adapter, viewMonth, value, displayTimezone, labels, disabled } = ctx;
|
|
2190
2566
|
const currentYear = adapter.getYear(viewMonth);
|
|
2191
2567
|
const decadeStart = currentYear - currentYear % 12;
|
|
2192
2568
|
const valueYear = useMemo(() => {
|
|
@@ -2197,7 +2573,20 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2197
2573
|
return null;
|
|
2198
2574
|
}
|
|
2199
2575
|
}, [value, adapter, displayTimezone]);
|
|
2200
|
-
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
|
+
);
|
|
2201
2590
|
const navigateDecade = useCallback(
|
|
2202
2591
|
(direction) => {
|
|
2203
2592
|
ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
|
|
@@ -2205,19 +2594,24 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2205
2594
|
[adapter, viewMonth, ctx]
|
|
2206
2595
|
);
|
|
2207
2596
|
const handleYearSelect = useCallback(
|
|
2208
|
-
(
|
|
2597
|
+
(indexInDecade) => {
|
|
2598
|
+
if (yearDisabledFlags[indexInDecade]) return;
|
|
2599
|
+
const year = decadeStart + indexInDecade;
|
|
2209
2600
|
const target = new Date(Date.UTC(year, 0, 1)).toISOString();
|
|
2210
2601
|
ctx.selectDate(target);
|
|
2211
2602
|
},
|
|
2212
|
-
[ctx]
|
|
2213
|
-
);
|
|
2214
|
-
const
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
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
|
|
2221
2615
|
});
|
|
2222
2616
|
const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
|
|
2223
2617
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
|
|
@@ -2247,35 +2641,48 @@ function YearPickerGrid({ classNames, ...props }) {
|
|
|
2247
2641
|
/* @__PURE__ */ jsx(
|
|
2248
2642
|
"div",
|
|
2249
2643
|
{
|
|
2644
|
+
ref: gridRef,
|
|
2250
2645
|
role: "grid",
|
|
2251
2646
|
"aria-label": rangeLabel,
|
|
2252
2647
|
className: classNames?.grid,
|
|
2648
|
+
onKeyDown: handleKeyDown,
|
|
2253
2649
|
children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
|
|
2254
2650
|
"div",
|
|
2255
2651
|
{
|
|
2256
2652
|
role: "row",
|
|
2257
2653
|
className: classNames?.gridRow,
|
|
2258
2654
|
style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
|
|
2259
|
-
children:
|
|
2260
|
-
const
|
|
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 = [
|
|
2261
2663
|
classNames?.year,
|
|
2262
|
-
|
|
2263
|
-
|
|
2664
|
+
isSelected && classNames?.yearSelected,
|
|
2665
|
+
isCurrent && classNames?.yearCurrent,
|
|
2666
|
+
isDisabled && classNames?.yearDisabled
|
|
2264
2667
|
].filter(Boolean).join(" ") || void 0;
|
|
2265
2668
|
return /* @__PURE__ */ jsx(
|
|
2266
2669
|
"button",
|
|
2267
2670
|
{
|
|
2268
2671
|
type: "button",
|
|
2269
2672
|
role: "gridcell",
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
"
|
|
2273
|
-
"
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
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
|
|
2277
2684
|
},
|
|
2278
|
-
|
|
2685
|
+
i
|
|
2279
2686
|
);
|
|
2280
2687
|
})
|
|
2281
2688
|
},
|
|
@@ -2503,7 +2910,7 @@ function useRangePicker(options = {}) {
|
|
|
2503
2910
|
adapter
|
|
2504
2911
|
};
|
|
2505
2912
|
}
|
|
2506
|
-
function
|
|
2913
|
+
function getDefaultIso() {
|
|
2507
2914
|
return DateFnsAdapter.today();
|
|
2508
2915
|
}
|
|
2509
2916
|
function useTimePicker(options = {}) {
|
|
@@ -2521,7 +2928,7 @@ function useTimePicker(options = {}) {
|
|
|
2521
2928
|
defaultValue ?? null
|
|
2522
2929
|
);
|
|
2523
2930
|
const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
|
|
2524
|
-
const baseIso = currentValue ??
|
|
2931
|
+
const baseIso = currentValue ?? getDefaultIso();
|
|
2525
2932
|
const currentTime = useMemo(
|
|
2526
2933
|
() => displayTimezone ? getTimeInTimezone(baseIso, displayTimezone) : getTime(baseIso),
|
|
2527
2934
|
[baseIso, displayTimezone]
|
|
@@ -2545,14 +2952,8 @@ function useTimePicker(options = {}) {
|
|
|
2545
2952
|
},
|
|
2546
2953
|
[format, period, setTime$1]
|
|
2547
2954
|
);
|
|
2548
|
-
const setMinute = useCallback(
|
|
2549
|
-
|
|
2550
|
-
[setTime$1]
|
|
2551
|
-
);
|
|
2552
|
-
const setSecond = useCallback(
|
|
2553
|
-
(second) => setTime$1({ seconds: second }),
|
|
2554
|
-
[setTime$1]
|
|
2555
|
-
);
|
|
2955
|
+
const setMinute = useCallback((minute) => setTime$1({ minutes: minute }), [setTime$1]);
|
|
2956
|
+
const setSecond = useCallback((second) => setTime$1({ seconds: second }), [setTime$1]);
|
|
2556
2957
|
const setPeriod = useCallback(
|
|
2557
2958
|
(newPeriod) => {
|
|
2558
2959
|
if (format !== "12h") return;
|