@kalyx/react 1.0.0-rc.8 → 1.0.0

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.
@@ -0,0 +1,3026 @@
1
+ "use client";
2
+ import { createContext, forwardRef, useState, useRef, useEffect, useCallback, useContext, useId, useMemo } from 'react';
3
+ import { parseInputValue, formatTimeString, parseTimeString, getTimeInTimezone, getTime, 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 } from '@kalyx/core';
5
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
6
+ import { offset, flip, shift, useFloating, autoUpdate } from '@floating-ui/react';
7
+
8
+ // src/components/DatePicker/Root.tsx
9
+ var DatePickerContext = createContext(null);
10
+ function useDatePickerContext(componentName) {
11
+ const context = useContext(DatePickerContext);
12
+ if (!context) {
13
+ throw new Error(
14
+ `[${componentName}] DatePicker.Root \uB0B4\uBD80\uC5D0\uC11C \uC0AC\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4.
15
+
16
+ \uC62C\uBC14\uB978 \uC0AC\uC6A9\uBC95:
17
+ <DatePicker>
18
+ <DatePicker.${componentName.replace("DatePicker.", "")} />
19
+ </DatePicker>`
20
+ );
21
+ }
22
+ return context;
23
+ }
24
+ function useChangeEffect(value, callback) {
25
+ const callbackRef = useRef(callback);
26
+ callbackRef.current = callback;
27
+ const prevRef = useRef(value);
28
+ useEffect(() => {
29
+ if (prevRef.current !== value) {
30
+ prevRef.current = value;
31
+ callbackRef.current?.(value);
32
+ }
33
+ }, [value]);
34
+ }
35
+
36
+ // src/internal/defaultAdapter.ts
37
+ var __defaultAdapter = null;
38
+ function getDefaultAdapter() {
39
+ return __defaultAdapter;
40
+ }
41
+ function resolveAdapter(passed, fallback, componentName) {
42
+ if (passed) return passed;
43
+ throw new Error(
44
+ `[@kalyx/react/headless] ${componentName} requires an adapter. Pass one via <${componentName} adapter={...}>. If you don't need a custom adapter, import from '@kalyx/react' instead.`
45
+ );
46
+ }
47
+ function DatePickerRoot({
48
+ value: controlledValue,
49
+ defaultValue,
50
+ onChange,
51
+ onOpenChange,
52
+ onCalendarNavigate,
53
+ disabled = false,
54
+ readOnly = false,
55
+ weekStartsOn = 0,
56
+ displayFormat = "yyyy-MM-dd",
57
+ locale = "en-US",
58
+ displayTimezone,
59
+ adapter: adapterProp,
60
+ labels: labelsProp,
61
+ children
62
+ }) {
63
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "DatePicker");
64
+ const pickerId = useId();
65
+ const isControlled = useRef(controlledValue !== void 0).current;
66
+ const referenceRef = useRef(null);
67
+ const [uncontrolledValue, setUncontrolledValue] = useState(
68
+ defaultValue ?? null
69
+ );
70
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
71
+ const [isOpen, setIsOpen] = useState(false);
72
+ const [viewMonth, setViewMonth] = useState(
73
+ () => currentValue ?? adapter.today(displayTimezone)
74
+ );
75
+ const [focusedDate, setFocusedDate] = useState(
76
+ () => currentValue ?? adapter.today(displayTimezone)
77
+ );
78
+ useChangeEffect(isOpen, onOpenChange);
79
+ const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
80
+ useChangeEffect(viewMonthStart, onCalendarNavigate);
81
+ const mergedLabels = useMemo(
82
+ () => ({ ...DEFAULT_DATEPICKER_LABELS, ...labelsProp }),
83
+ [labelsProp]
84
+ );
85
+ const isDisabled = typeof disabled === "boolean" ? disabled : false;
86
+ const disabledRules = useMemo(
87
+ () => Array.isArray(disabled) ? disabled : [],
88
+ [disabled]
89
+ );
90
+ const selectDate = useCallback(
91
+ (iso) => {
92
+ if (isDisabled || readOnly) return;
93
+ const normalized = iso && displayTimezone ? civilMidnightFromUtcDay(iso, displayTimezone) : iso;
94
+ if (!isControlled) {
95
+ setUncontrolledValue(normalized);
96
+ }
97
+ onChange?.(normalized);
98
+ setIsOpen(false);
99
+ },
100
+ [isControlled, isDisabled, readOnly, onChange, displayTimezone]
101
+ );
102
+ const open = useCallback(() => {
103
+ if (isDisabled || readOnly) return;
104
+ setIsOpen(true);
105
+ const target = currentValue ?? adapter.today(displayTimezone);
106
+ setViewMonth(target);
107
+ setFocusedDate(target);
108
+ }, [isDisabled, readOnly, currentValue, adapter, displayTimezone]);
109
+ const close = useCallback(() => {
110
+ setIsOpen(false);
111
+ }, []);
112
+ const toggle = useCallback(() => {
113
+ if (isOpen) {
114
+ close();
115
+ } else {
116
+ open();
117
+ }
118
+ }, [isOpen, open, close]);
119
+ const contextValue = useMemo(
120
+ () => ({
121
+ referenceRef,
122
+ value: currentValue,
123
+ selectDate,
124
+ isOpen,
125
+ open,
126
+ close,
127
+ toggle,
128
+ viewMonth,
129
+ setViewMonth,
130
+ focusedDate,
131
+ setFocusedDate,
132
+ adapter,
133
+ disabled: disabledRules,
134
+ weekStartsOn,
135
+ displayFormat,
136
+ locale,
137
+ displayTimezone,
138
+ isDisabled,
139
+ isReadOnly: readOnly,
140
+ pickerId,
141
+ labels: mergedLabels
142
+ }),
143
+ [
144
+ currentValue,
145
+ selectDate,
146
+ isOpen,
147
+ open,
148
+ close,
149
+ toggle,
150
+ viewMonth,
151
+ focusedDate,
152
+ adapter,
153
+ disabledRules,
154
+ weekStartsOn,
155
+ displayFormat,
156
+ locale,
157
+ displayTimezone,
158
+ isDisabled,
159
+ readOnly,
160
+ pickerId,
161
+ mergedLabels
162
+ ]
163
+ );
164
+ return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: contextValue, children });
165
+ }
166
+ var DatePickerInput = forwardRef(
167
+ function DatePickerInput2({ format: formatProp, name, onClick, onBlur, onKeyDown, ...props }, ref) {
168
+ const ctx = useDatePickerContext("DatePicker.Input");
169
+ const displayFormat = formatProp ?? ctx.displayFormat;
170
+ const [inputText, setInputText] = useState(null);
171
+ const isComposingRef = useRef(false);
172
+ useEffect(() => {
173
+ if (isComposingRef.current) return;
174
+ setInputText(null);
175
+ }, [ctx.value]);
176
+ let formattedValue = "";
177
+ if (ctx.value) {
178
+ try {
179
+ formattedValue = ctx.adapter.format(ctx.value, displayFormat, ctx.displayTimezone);
180
+ } catch {
181
+ formattedValue = ctx.value;
182
+ }
183
+ }
184
+ const displayValue = inputText !== null ? inputText : formattedValue;
185
+ const handleClick = useCallback(
186
+ (e) => {
187
+ if (!ctx.isOpen) ctx.open();
188
+ onClick?.(e);
189
+ },
190
+ [ctx, onClick]
191
+ );
192
+ const commitText = useCallback(
193
+ (text) => {
194
+ if (!text) {
195
+ ctx.selectDate(null);
196
+ setInputText(null);
197
+ return true;
198
+ }
199
+ const parsed = parseInputValue(text, ctx.adapter);
200
+ if (parsed) {
201
+ ctx.selectDate(parsed);
202
+ setInputText(null);
203
+ return true;
204
+ }
205
+ return false;
206
+ },
207
+ [ctx]
208
+ );
209
+ const handleBlur = useCallback(
210
+ (e) => {
211
+ if (inputText !== null) {
212
+ commitText(inputText);
213
+ setInputText(null);
214
+ }
215
+ onBlur?.(e);
216
+ },
217
+ [inputText, commitText, onBlur]
218
+ );
219
+ const handleChange = useCallback(
220
+ (e) => {
221
+ const text = e.target.value;
222
+ setInputText(text);
223
+ if (isComposingRef.current) return;
224
+ commitText(text);
225
+ },
226
+ [commitText]
227
+ );
228
+ const handleCompositionStart = useCallback(() => {
229
+ isComposingRef.current = true;
230
+ }, []);
231
+ const handleCompositionEnd = useCallback(
232
+ (e) => {
233
+ isComposingRef.current = false;
234
+ commitText(e.target.value);
235
+ },
236
+ [commitText]
237
+ );
238
+ const handleKeyDown = useCallback(
239
+ (e) => {
240
+ if (e.key === "Escape") {
241
+ ctx.close();
242
+ } else if (e.key === "Enter") {
243
+ if (ctx.isOpen) e.preventDefault();
244
+ if (inputText !== null) {
245
+ commitText(inputText);
246
+ } else if (ctx.isOpen) {
247
+ ctx.selectDate(ctx.focusedDate);
248
+ }
249
+ } else if (e.key === "ArrowDown" && !ctx.isOpen) {
250
+ e.preventDefault();
251
+ ctx.open();
252
+ }
253
+ onKeyDown?.(e);
254
+ },
255
+ [ctx, inputText, commitText, onKeyDown]
256
+ );
257
+ const calendarId = `${ctx.pickerId}-calendar`;
258
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
259
+ /* @__PURE__ */ jsx(
260
+ "input",
261
+ {
262
+ ref: (node) => {
263
+ ctx.referenceRef.current = node;
264
+ if (typeof ref === "function") ref(node);
265
+ else if (ref) ref.current = node;
266
+ },
267
+ type: "text",
268
+ role: "combobox",
269
+ "aria-expanded": ctx.isOpen,
270
+ "aria-haspopup": "dialog",
271
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
272
+ "aria-autocomplete": "none",
273
+ autoComplete: "off",
274
+ value: displayValue,
275
+ disabled: ctx.isDisabled || props.disabled,
276
+ readOnly: ctx.isReadOnly,
277
+ onChange: handleChange,
278
+ onClick: handleClick,
279
+ onBlur: handleBlur,
280
+ onKeyDown: handleKeyDown,
281
+ onCompositionStart: handleCompositionStart,
282
+ onCompositionEnd: handleCompositionEnd,
283
+ ...props
284
+ }
285
+ ),
286
+ name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: ctx.value ?? "" }) : null
287
+ ] });
288
+ }
289
+ );
290
+ DatePickerInput.displayName = "DatePicker.Input";
291
+ var DatePickerTrigger = forwardRef(
292
+ function DatePickerTrigger2({ onClick, children, ...props }, ref) {
293
+ const ctx = useDatePickerContext("DatePicker.Trigger");
294
+ const handleClick = useCallback(
295
+ (e) => {
296
+ ctx.toggle();
297
+ onClick?.(e);
298
+ },
299
+ [ctx, onClick]
300
+ );
301
+ const calendarId = `${ctx.pickerId}-calendar`;
302
+ return /* @__PURE__ */ jsx(
303
+ "button",
304
+ {
305
+ ref: (node) => {
306
+ if (!ctx.referenceRef.current) ctx.referenceRef.current = node;
307
+ if (typeof ref === "function") ref(node);
308
+ else if (ref) ref.current = node;
309
+ },
310
+ type: "button",
311
+ tabIndex: 0,
312
+ "aria-label": ctx.isOpen ? ctx.labels.triggerClose : ctx.labels.triggerOpen,
313
+ "aria-expanded": ctx.isOpen,
314
+ "aria-haspopup": "dialog",
315
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
316
+ disabled: ctx.isDisabled || props.disabled,
317
+ onClick: handleClick,
318
+ ...props,
319
+ children: children ?? /* @__PURE__ */ jsx(
320
+ "svg",
321
+ {
322
+ "aria-hidden": "true",
323
+ focusable: "false",
324
+ width: "16",
325
+ height: "16",
326
+ viewBox: "0 0 16 16",
327
+ fill: "none",
328
+ xmlns: "http://www.w3.org/2000/svg",
329
+ children: /* @__PURE__ */ jsx(
330
+ "path",
331
+ {
332
+ d: "M5 1v2M11 1v2M1 6h14M3 3h10a2 2 0 012 2v8a2 2 0 01-2 2H3a2 2 0 01-2-2V5a2 2 0 012-2z",
333
+ stroke: "currentColor",
334
+ strokeWidth: "1.5",
335
+ strokeLinecap: "round",
336
+ strokeLinejoin: "round"
337
+ }
338
+ )
339
+ }
340
+ )
341
+ }
342
+ );
343
+ }
344
+ );
345
+ DatePickerTrigger.displayName = "DatePicker.Trigger";
346
+ var POPOVER_MIDDLEWARE = [offset(4), flip(), shift({ padding: 8 })];
347
+ function usePopover({
348
+ isOpen,
349
+ close,
350
+ referenceRef,
351
+ placement = "bottom-start"
352
+ }) {
353
+ const floatingRef = useRef(null);
354
+ const previousFocusRef = useRef(null);
355
+ const { refs, floatingStyles, isPositioned } = useFloating({
356
+ open: isOpen,
357
+ placement,
358
+ middleware: POPOVER_MIDDLEWARE,
359
+ whileElementsMounted: autoUpdate
360
+ });
361
+ useEffect(() => {
362
+ if (referenceRef.current) {
363
+ refs.setReference(referenceRef.current);
364
+ }
365
+ }, [referenceRef, refs, isOpen]);
366
+ useEffect(() => {
367
+ if (isOpen) {
368
+ previousFocusRef.current = document.activeElement;
369
+ } else if (previousFocusRef.current) {
370
+ const el = previousFocusRef.current;
371
+ previousFocusRef.current = null;
372
+ if (el !== referenceRef.current && typeof el.focus === "function") {
373
+ el.focus({ preventScroll: true });
374
+ }
375
+ }
376
+ }, [isOpen, referenceRef]);
377
+ useEffect(() => {
378
+ if (!isOpen) return;
379
+ function handleClickOutside(e) {
380
+ const floating = floatingRef.current;
381
+ const reference = referenceRef.current;
382
+ const target = e.target;
383
+ if (floating && !floating.contains(target) && (!reference || !reference.contains(target))) {
384
+ close();
385
+ }
386
+ }
387
+ const timer = setTimeout(() => {
388
+ document.addEventListener("mousedown", handleClickOutside);
389
+ }, 0);
390
+ return () => {
391
+ clearTimeout(timer);
392
+ document.removeEventListener("mousedown", handleClickOutside);
393
+ };
394
+ }, [isOpen, close, referenceRef]);
395
+ useEffect(() => {
396
+ if (!isOpen) return;
397
+ function handleKeyDown(e) {
398
+ if (e.key === "Escape") {
399
+ close();
400
+ }
401
+ }
402
+ document.addEventListener("keydown", handleKeyDown);
403
+ return () => document.removeEventListener("keydown", handleKeyDown);
404
+ }, [isOpen, close]);
405
+ useEffect(() => {
406
+ if (!isOpen) return;
407
+ function handleFocusOut(e) {
408
+ const next = e.relatedTarget;
409
+ const floating = floatingRef.current;
410
+ const reference = referenceRef.current;
411
+ if (!next) return;
412
+ const insideFloating = floating?.contains(next) ?? false;
413
+ const insideReference = reference?.contains(next) ?? false;
414
+ if (!insideFloating && !insideReference) {
415
+ close();
416
+ }
417
+ }
418
+ const node = floatingRef.current;
419
+ if (!node) return;
420
+ node.addEventListener("focusout", handleFocusOut);
421
+ return () => node.removeEventListener("focusout", handleFocusOut);
422
+ }, [isOpen, close, referenceRef]);
423
+ const setFloatingRef = useCallback(
424
+ (node) => {
425
+ floatingRef.current = node;
426
+ refs.setFloating(node);
427
+ if (node && referenceRef.current) {
428
+ refs.setReference(referenceRef.current);
429
+ }
430
+ },
431
+ [refs, referenceRef]
432
+ );
433
+ return { floatingStyles, setFloatingRef, isPositioned };
434
+ }
435
+ function DatePickerPopover({ children, ...props }) {
436
+ const ctx = useDatePickerContext("DatePicker.Popover");
437
+ const calendarId = `${ctx.pickerId}-calendar`;
438
+ const { floatingStyles, setFloatingRef, isPositioned } = usePopover({
439
+ isOpen: ctx.isOpen,
440
+ close: ctx.close,
441
+ referenceRef: ctx.referenceRef
442
+ });
443
+ if (!ctx.isOpen) return null;
444
+ const { style: userStyle, ...rest } = props;
445
+ return /* @__PURE__ */ jsx(
446
+ "div",
447
+ {
448
+ ref: setFloatingRef,
449
+ id: calendarId,
450
+ role: "dialog",
451
+ "aria-label": ctx.labels.popoverLabel,
452
+ "aria-modal": "false",
453
+ ...rest,
454
+ style: {
455
+ ...userStyle,
456
+ ...floatingStyles,
457
+ visibility: isPositioned ? void 0 : "hidden"
458
+ },
459
+ children
460
+ }
461
+ );
462
+ }
463
+ function safeFormatFullDate(iso, locale) {
464
+ try {
465
+ return formatFullDate(iso, locale);
466
+ } catch {
467
+ return iso;
468
+ }
469
+ }
470
+ var srOnly = {
471
+ position: "absolute",
472
+ width: "1px",
473
+ height: "1px",
474
+ padding: 0,
475
+ margin: "-1px",
476
+ overflow: "hidden",
477
+ clip: "rect(0, 0, 0, 0)",
478
+ whiteSpace: "nowrap",
479
+ border: 0
480
+ };
481
+ function DatePickerCalendar({
482
+ classNames,
483
+ onTitleClick,
484
+ showWeekNumber = false,
485
+ fixedWeeks = false,
486
+ ...props
487
+ }) {
488
+ const ctx = useDatePickerContext("DatePicker.Calendar");
489
+ const gridRef = useRef(null);
490
+ const [announcement, setAnnouncement] = useState("");
491
+ const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale, displayTimezone } = ctx;
492
+ const weekdays = useMemo(() => getWeekdayNames(locale, weekStartsOn), [locale, weekStartsOn]);
493
+ const weeks = useMemo(
494
+ () => getCalendarDays(viewMonth, adapter, {
495
+ weekStartsOn,
496
+ selected: ctx.value,
497
+ focusedDate,
498
+ disabled,
499
+ timezone: displayTimezone,
500
+ fixedWeeks
501
+ }),
502
+ [
503
+ viewMonth,
504
+ adapter,
505
+ weekStartsOn,
506
+ ctx.value,
507
+ focusedDate,
508
+ disabled,
509
+ displayTimezone,
510
+ fixedWeeks
511
+ ]
512
+ );
513
+ const thursdayIndex = weekStartsOn === 0 ? 4 : 3;
514
+ const year = adapter.getYear(viewMonth);
515
+ const month = adapter.getMonth(viewMonth);
516
+ const title = formatMonthYear(year, month, locale);
517
+ useEffect(() => {
518
+ if (!ctx.isOpen || !gridRef.current) return;
519
+ const focusedButton = gridRef.current.querySelector('[data-focused="true"]');
520
+ focusedButton?.focus({ preventScroll: true });
521
+ }, [focusedDate, ctx.isOpen]);
522
+ const navigateMonth = useCallback(
523
+ (direction) => {
524
+ const newMonth = adapter.addMonths(viewMonth, direction);
525
+ ctx.setViewMonth(newMonth);
526
+ ctx.setFocusedDate(adapter.startOfMonth(newMonth));
527
+ const y = adapter.getYear(newMonth);
528
+ const m = adapter.getMonth(newMonth);
529
+ setAnnouncement(formatMonthYear(y, m, locale));
530
+ },
531
+ [adapter, viewMonth, ctx, locale]
532
+ );
533
+ const handleDayClick = useCallback(
534
+ (day) => {
535
+ if (day.isDisabled) return;
536
+ ctx.selectDate(day.isoString);
537
+ setAnnouncement(safeFormatFullDate(day.isoString, locale));
538
+ },
539
+ [ctx, locale]
540
+ );
541
+ const handleKeyDown = useCallback(
542
+ (e) => {
543
+ let newFocused = null;
544
+ switch (e.key) {
545
+ case "ArrowLeft":
546
+ newFocused = adapter.addDays(focusedDate, -1);
547
+ break;
548
+ case "ArrowRight":
549
+ newFocused = adapter.addDays(focusedDate, 1);
550
+ break;
551
+ case "ArrowUp":
552
+ newFocused = adapter.addDays(focusedDate, -7);
553
+ break;
554
+ case "ArrowDown":
555
+ newFocused = adapter.addDays(focusedDate, 7);
556
+ break;
557
+ case "PageUp":
558
+ if (e.shiftKey) {
559
+ newFocused = adapter.addYears(focusedDate, -1);
560
+ } else {
561
+ newFocused = adapter.addMonths(focusedDate, -1);
562
+ }
563
+ break;
564
+ case "PageDown":
565
+ if (e.shiftKey) {
566
+ newFocused = adapter.addYears(focusedDate, 1);
567
+ } else {
568
+ newFocused = adapter.addMonths(focusedDate, 1);
569
+ }
570
+ break;
571
+ case "Home":
572
+ newFocused = adapter.startOfWeek(focusedDate, weekStartsOn);
573
+ break;
574
+ case "End":
575
+ newFocused = adapter.endOfWeek(focusedDate, weekStartsOn);
576
+ newFocused = adapter.startOfDay(newFocused);
577
+ break;
578
+ case "Enter":
579
+ case " ":
580
+ e.preventDefault();
581
+ if (!isDateDisabled(focusedDate, disabled, adapter)) {
582
+ ctx.selectDate(focusedDate);
583
+ }
584
+ return;
585
+ case "Escape":
586
+ ctx.close();
587
+ return;
588
+ default:
589
+ return;
590
+ }
591
+ if (newFocused) {
592
+ e.preventDefault();
593
+ const skipStep = e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "PageUp" || e.key === "Home" ? -1 : 1;
594
+ let attempts = 0;
595
+ while (isDateDisabled(newFocused, disabled, adapter) && attempts < 42) {
596
+ newFocused = adapter.addDays(newFocused, skipStep);
597
+ attempts++;
598
+ }
599
+ if (attempts >= 42) {
600
+ return;
601
+ }
602
+ ctx.setFocusedDate(newFocused);
603
+ if (!adapter.isSameMonth(newFocused, viewMonth)) {
604
+ ctx.setViewMonth(newFocused);
605
+ }
606
+ }
607
+ },
608
+ [adapter, focusedDate, viewMonth, weekStartsOn, disabled, ctx]
609
+ );
610
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
611
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
612
+ /* @__PURE__ */ jsx(
613
+ "button",
614
+ {
615
+ type: "button",
616
+ className: classNames?.navButton,
617
+ onClick: () => navigateMonth(-1),
618
+ "aria-label": ctx.labels.prevMonth,
619
+ children: "<"
620
+ }
621
+ ),
622
+ onTitleClick ? /* @__PURE__ */ jsx(
623
+ "button",
624
+ {
625
+ type: "button",
626
+ className: classNames?.title,
627
+ onClick: onTitleClick,
628
+ "aria-live": "polite",
629
+ children: title
630
+ }
631
+ ) : /* @__PURE__ */ jsx("span", { className: classNames?.title, "aria-live": "polite", children: title }),
632
+ /* @__PURE__ */ jsx(
633
+ "button",
634
+ {
635
+ type: "button",
636
+ className: classNames?.navButton,
637
+ onClick: () => navigateMonth(1),
638
+ "aria-label": ctx.labels.nextMonth,
639
+ children: ">"
640
+ }
641
+ )
642
+ ] }),
643
+ /* @__PURE__ */ jsxs(
644
+ "table",
645
+ {
646
+ ref: gridRef,
647
+ role: "grid",
648
+ "aria-label": title,
649
+ "aria-rowcount": weeks.length + 1,
650
+ "aria-colcount": 7,
651
+ className: classNames?.grid,
652
+ onKeyDown: handleKeyDown,
653
+ children: [
654
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
655
+ showWeekNumber ? /* @__PURE__ */ jsx("th", { scope: "col", "aria-hidden": "true", className: classNames?.weekNumberHeader, children: "#" }) : null,
656
+ weekdays.map((day, colIndex) => /* @__PURE__ */ jsx(
657
+ "th",
658
+ {
659
+ role: "columnheader",
660
+ abbr: day.full,
661
+ scope: "col",
662
+ "aria-colindex": colIndex + 1,
663
+ className: classNames?.weekdayHeader,
664
+ children: day.short
665
+ },
666
+ day.short
667
+ ))
668
+ ] }) }),
669
+ /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxs(
670
+ "tr",
671
+ {
672
+ role: "row",
673
+ "aria-rowindex": weekIndex + 2,
674
+ className: classNames?.gridRow,
675
+ children: [
676
+ showWeekNumber ? /* @__PURE__ */ jsx(
677
+ "th",
678
+ {
679
+ scope: "row",
680
+ "aria-hidden": "true",
681
+ className: classNames?.weekNumber,
682
+ "data-week-number": true,
683
+ children: getISOWeekNumber(week[thursdayIndex].isoString)
684
+ }
685
+ ) : null,
686
+ week.map((day, colIndex) => {
687
+ const dayClasses = [
688
+ classNames?.day,
689
+ day.isSelected && classNames?.daySelected,
690
+ day.isToday && classNames?.dayToday,
691
+ day.isDisabled && classNames?.dayDisabled,
692
+ !day.isCurrentMonth && classNames?.dayOutsideMonth
693
+ ].filter(Boolean).join(" ") || void 0;
694
+ return /* @__PURE__ */ jsx(
695
+ "td",
696
+ {
697
+ role: "gridcell",
698
+ "aria-colindex": colIndex + 1,
699
+ "aria-selected": day.isSelected || void 0,
700
+ "aria-disabled": day.isDisabled || void 0,
701
+ "aria-current": day.isToday ? "date" : void 0,
702
+ className: classNames?.gridCell,
703
+ children: /* @__PURE__ */ jsx(
704
+ "button",
705
+ {
706
+ type: "button",
707
+ tabIndex: day.isFocused ? 0 : -1,
708
+ disabled: day.isDisabled,
709
+ "data-focused": day.isFocused || void 0,
710
+ "data-selected": day.isSelected || void 0,
711
+ "data-today": day.isToday || void 0,
712
+ "data-outside-month": !day.isCurrentMonth || void 0,
713
+ className: dayClasses,
714
+ onClick: () => handleDayClick(day),
715
+ "aria-label": safeFormatFullDate(day.isoString, locale),
716
+ children: day.dayNumber
717
+ }
718
+ )
719
+ },
720
+ day.isoString
721
+ );
722
+ })
723
+ ]
724
+ },
725
+ weekIndex
726
+ )) })
727
+ ]
728
+ }
729
+ ),
730
+ /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: srOnly, children: announcement })
731
+ ] });
732
+ }
733
+ function isRangeFullyDisabled(start, end, rules, adapter) {
734
+ for (const rule of rules) {
735
+ if ("before" in rule && adapter.isBefore(end, rule.before)) return true;
736
+ if ("after" in rule && adapter.isAfter(start, rule.after)) return true;
737
+ }
738
+ return false;
739
+ }
740
+ function useGridState(opts) {
741
+ const { initialIndex, disabledFlags, onSelect, onPageUp, onPageDown, onEscape } = opts;
742
+ const gridRef = useRef(null);
743
+ const [focusedIndex, setFocusedIndex] = useState(initialIndex);
744
+ const handleKeyDown = (e) => {
745
+ let next = null;
746
+ let step = 1;
747
+ switch (e.key) {
748
+ case "ArrowLeft":
749
+ next = Math.max(0, focusedIndex - 1);
750
+ step = -1;
751
+ break;
752
+ case "ArrowRight":
753
+ next = Math.min(11, focusedIndex + 1);
754
+ break;
755
+ case "ArrowUp":
756
+ next = Math.max(0, focusedIndex - 3);
757
+ step = -1;
758
+ break;
759
+ case "ArrowDown":
760
+ next = Math.min(11, focusedIndex + 3);
761
+ break;
762
+ case "Home":
763
+ next = focusedIndex - focusedIndex % 3;
764
+ step = -1;
765
+ break;
766
+ case "End":
767
+ next = focusedIndex - focusedIndex % 3 + 2;
768
+ break;
769
+ case "PageUp":
770
+ e.preventDefault();
771
+ onPageUp();
772
+ return;
773
+ case "PageDown":
774
+ e.preventDefault();
775
+ onPageDown();
776
+ return;
777
+ case "Enter":
778
+ case " ":
779
+ e.preventDefault();
780
+ onSelect(focusedIndex);
781
+ return;
782
+ case "Escape":
783
+ onEscape();
784
+ return;
785
+ default:
786
+ return;
787
+ }
788
+ if (next === null) return;
789
+ e.preventDefault();
790
+ if (disabledFlags) {
791
+ let attempts = 0;
792
+ while (next >= 0 && next < 12 && disabledFlags[next] && attempts < 12) {
793
+ next += step;
794
+ attempts++;
795
+ }
796
+ if (next < 0 || next >= 12 || disabledFlags[next]) return;
797
+ }
798
+ if (next !== focusedIndex) setFocusedIndex(next);
799
+ };
800
+ useEffect(() => {
801
+ if (!disabledFlags || !disabledFlags[focusedIndex]) return;
802
+ const firstEnabled = disabledFlags.findIndex((d) => !d);
803
+ if (firstEnabled !== -1 && firstEnabled !== focusedIndex) {
804
+ setFocusedIndex(firstEnabled);
805
+ }
806
+ }, [disabledFlags, focusedIndex]);
807
+ useEffect(() => {
808
+ const btn = gridRef.current?.querySelector('[data-focused="true"]');
809
+ btn?.focus({ preventScroll: true });
810
+ }, [focusedIndex]);
811
+ return { gridRef, focusedIndex, handleKeyDown };
812
+ }
813
+ function DatePickerMonthGrid({
814
+ classNames,
815
+ onSelect,
816
+ onTitleClick,
817
+ ...props
818
+ }) {
819
+ const ctx = useDatePickerContext("DatePicker.MonthGrid");
820
+ const { adapter, viewMonth, locale, displayTimezone } = ctx;
821
+ const currentYear = adapter.getYear(viewMonth);
822
+ const currentMonth = adapter.getMonth(viewMonth);
823
+ const [today, setToday] = useState(null);
824
+ useEffect(() => {
825
+ setToday(adapter.today(displayTimezone));
826
+ }, [adapter, displayTimezone]);
827
+ const todayMonth = today !== null ? adapter.getMonth(today) : -1;
828
+ const todayYear = today !== null ? adapter.getYear(today) : -1;
829
+ const navigateYear = useCallback(
830
+ (direction) => {
831
+ ctx.setViewMonth(adapter.addYears(viewMonth, direction));
832
+ },
833
+ [adapter, viewMonth, ctx]
834
+ );
835
+ const handleMonthSelect = useCallback(
836
+ (monthIndex) => {
837
+ const target = new Date(Date.UTC(currentYear, monthIndex, 1)).toISOString();
838
+ ctx.setViewMonth(target);
839
+ ctx.setFocusedDate(target);
840
+ onSelect?.();
841
+ },
842
+ [currentYear, ctx, onSelect]
843
+ );
844
+ const { gridRef, focusedIndex, handleKeyDown } = useGridState({
845
+ initialIndex: currentMonth,
846
+ onSelect: handleMonthSelect,
847
+ onPageUp: () => navigateYear(-1),
848
+ onPageDown: () => navigateYear(1),
849
+ onEscape: ctx.close
850
+ });
851
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
852
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
853
+ /* @__PURE__ */ jsx(
854
+ "button",
855
+ {
856
+ type: "button",
857
+ className: classNames?.navButton,
858
+ onClick: () => navigateYear(-1),
859
+ "aria-label": ctx.labels.prevYear,
860
+ children: "<"
861
+ }
862
+ ),
863
+ onTitleClick ? /* @__PURE__ */ jsx("button", { type: "button", className: classNames?.title, onClick: onTitleClick, children: currentYear }) : /* @__PURE__ */ jsx("span", { className: classNames?.title, children: currentYear }),
864
+ /* @__PURE__ */ jsx(
865
+ "button",
866
+ {
867
+ type: "button",
868
+ className: classNames?.navButton,
869
+ onClick: () => navigateYear(1),
870
+ "aria-label": ctx.labels.nextYear,
871
+ children: ">"
872
+ }
873
+ )
874
+ ] }),
875
+ /* @__PURE__ */ jsx(
876
+ "div",
877
+ {
878
+ ref: gridRef,
879
+ role: "grid",
880
+ "aria-label": `${currentYear} months`,
881
+ className: classNames?.grid,
882
+ style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
883
+ onKeyDown: handleKeyDown,
884
+ children: Array.from({ length: 12 }, (_, i) => {
885
+ const isSelected = i === currentMonth;
886
+ const isCurrent = i === todayMonth && currentYear === todayYear;
887
+ const isFocused = i === focusedIndex;
888
+ const cls = [
889
+ classNames?.month,
890
+ isSelected && classNames?.monthSelected,
891
+ isCurrent && classNames?.monthCurrent
892
+ ].filter(Boolean).join(" ") || void 0;
893
+ return /* @__PURE__ */ jsx(
894
+ "button",
895
+ {
896
+ type: "button",
897
+ role: "gridcell",
898
+ tabIndex: isFocused ? 0 : -1,
899
+ "aria-selected": isSelected || void 0,
900
+ "aria-current": isCurrent ? "date" : void 0,
901
+ "data-selected": isSelected || void 0,
902
+ "data-current": isCurrent || void 0,
903
+ "data-focused": isFocused || void 0,
904
+ className: cls,
905
+ onClick: () => handleMonthSelect(i),
906
+ children: getMonthName(i, locale)
907
+ },
908
+ i
909
+ );
910
+ })
911
+ }
912
+ )
913
+ ] });
914
+ }
915
+ function DatePickerYearGrid({ classNames, onSelect, ...props }) {
916
+ const ctx = useDatePickerContext("DatePicker.YearGrid");
917
+ const { adapter, viewMonth, displayTimezone } = ctx;
918
+ const currentYear = adapter.getYear(viewMonth);
919
+ const [today, setToday] = useState(null);
920
+ useEffect(() => {
921
+ setToday(adapter.today(displayTimezone));
922
+ }, [adapter, displayTimezone]);
923
+ const todayYear = today !== null ? adapter.getYear(today) : -1;
924
+ const decadeStart = currentYear - currentYear % 12;
925
+ const navigateDecade = useCallback(
926
+ (direction) => {
927
+ ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
928
+ },
929
+ [adapter, viewMonth, ctx]
930
+ );
931
+ const handleYearSelect = useCallback(
932
+ (indexInDecade) => {
933
+ const year = decadeStart + indexInDecade;
934
+ const currentMonth = adapter.getMonth(viewMonth);
935
+ const target = new Date(Date.UTC(year, currentMonth, 1)).toISOString();
936
+ ctx.setViewMonth(target);
937
+ ctx.setFocusedDate(target);
938
+ onSelect?.();
939
+ },
940
+ [adapter, viewMonth, ctx, onSelect, decadeStart]
941
+ );
942
+ const { gridRef, focusedIndex, handleKeyDown } = useGridState({
943
+ initialIndex: currentYear - decadeStart,
944
+ onSelect: handleYearSelect,
945
+ onPageUp: () => navigateDecade(-1),
946
+ onPageDown: () => navigateDecade(1),
947
+ onEscape: ctx.close
948
+ });
949
+ const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
950
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
951
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
952
+ /* @__PURE__ */ jsx(
953
+ "button",
954
+ {
955
+ type: "button",
956
+ className: classNames?.navButton,
957
+ onClick: () => navigateDecade(-1),
958
+ "aria-label": ctx.labels.prevDecade,
959
+ children: "<"
960
+ }
961
+ ),
962
+ /* @__PURE__ */ jsx("span", { className: classNames?.title, children: rangeLabel }),
963
+ /* @__PURE__ */ jsx(
964
+ "button",
965
+ {
966
+ type: "button",
967
+ className: classNames?.navButton,
968
+ onClick: () => navigateDecade(1),
969
+ "aria-label": ctx.labels.nextDecade,
970
+ children: ">"
971
+ }
972
+ )
973
+ ] }),
974
+ /* @__PURE__ */ jsx(
975
+ "div",
976
+ {
977
+ ref: gridRef,
978
+ role: "grid",
979
+ "aria-label": rangeLabel,
980
+ className: classNames?.grid,
981
+ style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
982
+ onKeyDown: handleKeyDown,
983
+ children: Array.from({ length: 12 }, (_, i) => {
984
+ const year = decadeStart + i;
985
+ const isSelected = year === currentYear;
986
+ const isCurrent = year === todayYear;
987
+ const isFocused = i === focusedIndex;
988
+ const cls = [
989
+ classNames?.year,
990
+ isSelected && classNames?.yearSelected,
991
+ isCurrent && classNames?.yearCurrent
992
+ ].filter(Boolean).join(" ") || void 0;
993
+ return /* @__PURE__ */ jsx(
994
+ "button",
995
+ {
996
+ type: "button",
997
+ role: "gridcell",
998
+ tabIndex: isFocused ? 0 : -1,
999
+ "aria-selected": isSelected || void 0,
1000
+ "aria-current": isCurrent ? "date" : void 0,
1001
+ "data-selected": isSelected || void 0,
1002
+ "data-current": isCurrent || void 0,
1003
+ "data-focused": isFocused || void 0,
1004
+ className: cls,
1005
+ onClick: () => handleYearSelect(i),
1006
+ children: year
1007
+ },
1008
+ i
1009
+ );
1010
+ })
1011
+ }
1012
+ )
1013
+ ] });
1014
+ }
1015
+ function DatePickerPresets({ classNames, children, ...props }) {
1016
+ const ctx = useDatePickerContext("DatePicker.Presets");
1017
+ return /* @__PURE__ */ jsx("div", { role: "group", "aria-label": ctx.labels.popoverLabel, className: classNames?.root, ...props, children });
1018
+ }
1019
+ function resolveDatePreset(key, today, adapter) {
1020
+ switch (key) {
1021
+ case "today":
1022
+ return today;
1023
+ case "tomorrow":
1024
+ return adapter.addDays(today, 1);
1025
+ case "yesterday":
1026
+ return adapter.addDays(today, -1);
1027
+ case "startOfMonth":
1028
+ return adapter.startOfMonth(today);
1029
+ case "endOfMonth":
1030
+ return adapter.startOfDay(adapter.endOfMonth(today));
1031
+ case "startOfYear": {
1032
+ const currentMonth = adapter.getMonth(today);
1033
+ return adapter.startOfMonth(adapter.addMonths(today, -currentMonth));
1034
+ }
1035
+ }
1036
+ }
1037
+ function DatePickerPreset({
1038
+ value: presetKey,
1039
+ date: directDate,
1040
+ children,
1041
+ onClick,
1042
+ ...props
1043
+ }) {
1044
+ const ctx = useDatePickerContext("DatePicker.Preset");
1045
+ const handleClick = useCallback(
1046
+ (e) => {
1047
+ if (ctx.isDisabled || ctx.isReadOnly) return;
1048
+ let resolved;
1049
+ if (directDate) {
1050
+ resolved = directDate;
1051
+ } else if (presetKey) {
1052
+ resolved = resolveDatePreset(
1053
+ presetKey,
1054
+ ctx.adapter.today(ctx.displayTimezone),
1055
+ ctx.adapter
1056
+ );
1057
+ } else {
1058
+ return;
1059
+ }
1060
+ ctx.selectDate(resolved);
1061
+ onClick?.(e);
1062
+ },
1063
+ [ctx, presetKey, directDate, onClick]
1064
+ );
1065
+ const isActive = (() => {
1066
+ if (!ctx.value) return false;
1067
+ let target;
1068
+ if (directDate) {
1069
+ target = directDate;
1070
+ } else if (presetKey) {
1071
+ target = resolveDatePreset(presetKey, ctx.adapter.today(ctx.displayTimezone), ctx.adapter);
1072
+ } else {
1073
+ return false;
1074
+ }
1075
+ return ctx.adapter.isSameDay(ctx.value, target, ctx.displayTimezone);
1076
+ })();
1077
+ return /* @__PURE__ */ jsx(
1078
+ "button",
1079
+ {
1080
+ type: "button",
1081
+ "aria-pressed": isActive,
1082
+ "data-active": isActive || void 0,
1083
+ disabled: ctx.isDisabled,
1084
+ onClick: handleClick,
1085
+ ...props,
1086
+ children
1087
+ }
1088
+ );
1089
+ }
1090
+
1091
+ // src/components/DatePicker/index.ts
1092
+ var DatePicker = Object.assign(DatePickerRoot, {
1093
+ Input: DatePickerInput,
1094
+ Trigger: DatePickerTrigger,
1095
+ Popover: DatePickerPopover,
1096
+ Calendar: DatePickerCalendar,
1097
+ MonthGrid: DatePickerMonthGrid,
1098
+ YearGrid: DatePickerYearGrid,
1099
+ Presets: DatePickerPresets,
1100
+ Preset: DatePickerPreset
1101
+ });
1102
+ var RangePickerContext = createContext(null);
1103
+ function useRangePickerContext(componentName) {
1104
+ const context = useContext(RangePickerContext);
1105
+ if (!context) {
1106
+ throw new Error(
1107
+ `[${componentName}] RangePicker.Root \uB0B4\uBD80\uC5D0\uC11C \uC0AC\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4.
1108
+
1109
+ \uC62C\uBC14\uB978 \uC0AC\uC6A9\uBC95:
1110
+ <RangePicker>
1111
+ <RangePicker.${componentName.replace("RangePicker.", "")} />
1112
+ </RangePicker>`
1113
+ );
1114
+ }
1115
+ return context;
1116
+ }
1117
+ var EMPTY_RANGE = { start: null, end: null };
1118
+ var SR_ONLY = {
1119
+ position: "absolute",
1120
+ width: 1,
1121
+ height: 1,
1122
+ padding: 0,
1123
+ margin: -1,
1124
+ overflow: "hidden",
1125
+ clip: "rect(0, 0, 0, 0)",
1126
+ whiteSpace: "nowrap",
1127
+ border: 0
1128
+ };
1129
+ function RangePickerRoot({
1130
+ value: controlledValue,
1131
+ defaultValue,
1132
+ onChange,
1133
+ onOpenChange,
1134
+ onCalendarNavigate,
1135
+ disabled = false,
1136
+ readOnly = false,
1137
+ weekStartsOn = 0,
1138
+ displayFormat = "yyyy-MM-dd",
1139
+ locale = "en-US",
1140
+ displayTimezone,
1141
+ adapter: adapterProp,
1142
+ labels: labelsProp,
1143
+ children
1144
+ }) {
1145
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "RangePicker");
1146
+ const pickerId = useId();
1147
+ const isControlled = useRef(controlledValue !== void 0).current;
1148
+ const referenceRef = useRef(null);
1149
+ const [uncontrolledValue, setUncontrolledValue] = useState(
1150
+ defaultValue ?? EMPTY_RANGE
1151
+ );
1152
+ const currentValue = isControlled ? controlledValue ?? EMPTY_RANGE : uncontrolledValue;
1153
+ const [isOpen, setIsOpen] = useState(false);
1154
+ const [selectingTarget, setSelectingTarget] = useState("start");
1155
+ const [hoverDate, setHoverDate] = useState(null);
1156
+ const [announcement, setAnnouncement] = useState("");
1157
+ const announce = useCallback((message) => setAnnouncement(message), []);
1158
+ const [viewMonth, setViewMonth] = useState(
1159
+ () => currentValue.start ?? adapter.today(displayTimezone)
1160
+ );
1161
+ const [focusedDate, setFocusedDate] = useState(
1162
+ () => currentValue.start ?? adapter.today(displayTimezone)
1163
+ );
1164
+ useChangeEffect(isOpen, onOpenChange);
1165
+ const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
1166
+ useChangeEffect(viewMonthStart, onCalendarNavigate);
1167
+ const mergedLabels = useMemo(
1168
+ () => ({ ...DEFAULT_RANGEPICKER_LABELS, ...labelsProp }),
1169
+ [labelsProp]
1170
+ );
1171
+ const isDisabled = typeof disabled === "boolean" ? disabled : false;
1172
+ const disabledRules = useMemo(
1173
+ () => Array.isArray(disabled) ? disabled : [],
1174
+ [disabled]
1175
+ );
1176
+ const setRange = useCallback(
1177
+ (range) => {
1178
+ if (isDisabled || readOnly) return;
1179
+ if (!isControlled) {
1180
+ setUncontrolledValue(range);
1181
+ }
1182
+ onChange?.(range);
1183
+ },
1184
+ [isControlled, isDisabled, readOnly, onChange]
1185
+ );
1186
+ const selectDate = useCallback(
1187
+ (iso) => {
1188
+ if (isDisabled || readOnly) return;
1189
+ const normalized = displayTimezone ? civilMidnightFromUtcDay(iso, displayTimezone) : iso;
1190
+ if (selectingTarget === "start") {
1191
+ const newRange = { start: normalized, end: null };
1192
+ setRange(newRange);
1193
+ setSelectingTarget("end");
1194
+ setHoverDate(null);
1195
+ } else {
1196
+ const start = currentValue.start;
1197
+ if (!start) {
1198
+ setRange({ start: normalized, end: null });
1199
+ setSelectingTarget("end");
1200
+ return;
1201
+ }
1202
+ let newRange;
1203
+ if (adapter.isBefore(normalized, start)) {
1204
+ newRange = { start: normalized, end: start };
1205
+ } else {
1206
+ newRange = { start, end: normalized };
1207
+ }
1208
+ setRange(newRange);
1209
+ setSelectingTarget("start");
1210
+ setHoverDate(null);
1211
+ setIsOpen(false);
1212
+ }
1213
+ },
1214
+ [isDisabled, readOnly, selectingTarget, currentValue.start, adapter, setRange, displayTimezone]
1215
+ );
1216
+ const open = useCallback(() => {
1217
+ if (isDisabled || readOnly) return;
1218
+ setIsOpen(true);
1219
+ const target = currentValue.start ?? adapter.today(displayTimezone);
1220
+ setViewMonth(target);
1221
+ setFocusedDate(target);
1222
+ if (currentValue.start && currentValue.end) {
1223
+ setSelectingTarget("start");
1224
+ }
1225
+ }, [isDisabled, readOnly, currentValue, adapter, displayTimezone]);
1226
+ const close = useCallback(() => {
1227
+ setIsOpen(false);
1228
+ setHoverDate(null);
1229
+ }, []);
1230
+ const toggle = useCallback(() => {
1231
+ if (isOpen) close();
1232
+ else open();
1233
+ }, [isOpen, open, close]);
1234
+ const contextValue = useMemo(
1235
+ () => ({
1236
+ referenceRef,
1237
+ value: currentValue,
1238
+ setRange,
1239
+ selectDate,
1240
+ selectingTarget,
1241
+ hoverDate,
1242
+ setHoverDate,
1243
+ isOpen,
1244
+ open,
1245
+ close,
1246
+ toggle,
1247
+ viewMonth,
1248
+ setViewMonth,
1249
+ focusedDate,
1250
+ setFocusedDate,
1251
+ adapter,
1252
+ disabled: disabledRules,
1253
+ weekStartsOn,
1254
+ displayFormat,
1255
+ locale,
1256
+ displayTimezone,
1257
+ isDisabled,
1258
+ isReadOnly: readOnly,
1259
+ pickerId,
1260
+ labels: mergedLabels,
1261
+ announce
1262
+ }),
1263
+ [
1264
+ currentValue,
1265
+ setRange,
1266
+ selectDate,
1267
+ selectingTarget,
1268
+ hoverDate,
1269
+ isOpen,
1270
+ open,
1271
+ close,
1272
+ toggle,
1273
+ viewMonth,
1274
+ focusedDate,
1275
+ adapter,
1276
+ disabledRules,
1277
+ weekStartsOn,
1278
+ displayFormat,
1279
+ locale,
1280
+ displayTimezone,
1281
+ isDisabled,
1282
+ readOnly,
1283
+ pickerId,
1284
+ mergedLabels,
1285
+ announce
1286
+ ]
1287
+ );
1288
+ return /* @__PURE__ */ jsxs(RangePickerContext.Provider, { value: contextValue, children: [
1289
+ children,
1290
+ /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: SR_ONLY, children: announcement })
1291
+ ] });
1292
+ }
1293
+ var RangePickerInput = forwardRef(
1294
+ function RangePickerInput2({ part, format: formatProp, onClick, onKeyDown, ...props }, ref) {
1295
+ const ctx = useRangePickerContext("RangePicker.Input");
1296
+ const displayFormat = formatProp ?? ctx.displayFormat;
1297
+ const value = ctx.value[part];
1298
+ let displayValue = "";
1299
+ if (value) {
1300
+ try {
1301
+ displayValue = ctx.adapter.format(value, displayFormat, ctx.displayTimezone);
1302
+ } catch {
1303
+ displayValue = value;
1304
+ }
1305
+ }
1306
+ const handleClick = useCallback(
1307
+ (e) => {
1308
+ if (!ctx.isOpen) ctx.open();
1309
+ onClick?.(e);
1310
+ },
1311
+ [ctx, onClick]
1312
+ );
1313
+ const handleKeyDown = useCallback(
1314
+ (e) => {
1315
+ if (e.key === "Escape") {
1316
+ ctx.close();
1317
+ } else if (e.key === "Enter" && ctx.isOpen) {
1318
+ e.preventDefault();
1319
+ } else if (e.key === "ArrowDown" && !ctx.isOpen) {
1320
+ e.preventDefault();
1321
+ ctx.open();
1322
+ }
1323
+ onKeyDown?.(e);
1324
+ },
1325
+ [ctx, onKeyDown]
1326
+ );
1327
+ const calendarId = `${ctx.pickerId}-calendar`;
1328
+ return /* @__PURE__ */ jsx(
1329
+ "input",
1330
+ {
1331
+ ref: (node) => {
1332
+ if (part === "start" && node) ctx.referenceRef.current = node;
1333
+ if (typeof ref === "function") ref(node);
1334
+ else if (ref) ref.current = node;
1335
+ },
1336
+ type: "text",
1337
+ role: "combobox",
1338
+ readOnly: true,
1339
+ "aria-expanded": ctx.isOpen,
1340
+ "aria-haspopup": "dialog",
1341
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
1342
+ "aria-autocomplete": "none",
1343
+ "aria-label": part === "start" ? ctx.labels.startInput : ctx.labels.endInput,
1344
+ autoComplete: "off",
1345
+ value: displayValue,
1346
+ disabled: ctx.isDisabled || props.disabled,
1347
+ onClick: handleClick,
1348
+ onKeyDown: handleKeyDown,
1349
+ "data-part": part,
1350
+ ...props
1351
+ }
1352
+ );
1353
+ }
1354
+ );
1355
+ RangePickerInput.displayName = "RangePicker.Input";
1356
+ function RangePickerPopover({ children, ...props }) {
1357
+ const ctx = useRangePickerContext("RangePicker.Popover");
1358
+ const calendarId = `${ctx.pickerId}-calendar`;
1359
+ const { floatingStyles, setFloatingRef, isPositioned } = usePopover({
1360
+ isOpen: ctx.isOpen,
1361
+ close: ctx.close,
1362
+ referenceRef: ctx.referenceRef
1363
+ });
1364
+ if (!ctx.isOpen) return null;
1365
+ const { style: userStyle, ...rest } = props;
1366
+ return /* @__PURE__ */ jsx(
1367
+ "div",
1368
+ {
1369
+ ref: setFloatingRef,
1370
+ id: calendarId,
1371
+ role: "dialog",
1372
+ "aria-label": ctx.labels.popoverLabel,
1373
+ "aria-modal": "false",
1374
+ ...rest,
1375
+ style: {
1376
+ ...userStyle,
1377
+ ...floatingStyles,
1378
+ visibility: isPositioned ? void 0 : "hidden"
1379
+ },
1380
+ children
1381
+ }
1382
+ );
1383
+ }
1384
+ function safeFormatFullDate2(iso, locale) {
1385
+ try {
1386
+ return formatFullDate(iso, locale);
1387
+ } catch {
1388
+ return iso;
1389
+ }
1390
+ }
1391
+ function RangePickerCalendar({
1392
+ classNames,
1393
+ selectionMode = "range",
1394
+ showWeekNumber = false,
1395
+ fixedWeeks = false,
1396
+ ...props
1397
+ }) {
1398
+ const ctx = useRangePickerContext("RangePicker.Calendar");
1399
+ const gridRef = useRef(null);
1400
+ const {
1401
+ adapter,
1402
+ viewMonth,
1403
+ focusedDate,
1404
+ weekStartsOn,
1405
+ disabled,
1406
+ value,
1407
+ hoverDate,
1408
+ selectingTarget,
1409
+ displayTimezone
1410
+ } = ctx;
1411
+ const { locale } = ctx;
1412
+ const weekdays = useMemo(() => getWeekdayNames(locale, weekStartsOn), [locale, weekStartsOn]);
1413
+ const weeks = useMemo(
1414
+ () => getCalendarDays(viewMonth, adapter, {
1415
+ weekStartsOn,
1416
+ focusedDate,
1417
+ disabled,
1418
+ range: value,
1419
+ rangeHover: hoverDate,
1420
+ timezone: displayTimezone,
1421
+ fixedWeeks
1422
+ }),
1423
+ [
1424
+ viewMonth,
1425
+ adapter,
1426
+ weekStartsOn,
1427
+ focusedDate,
1428
+ disabled,
1429
+ value,
1430
+ hoverDate,
1431
+ displayTimezone,
1432
+ fixedWeeks
1433
+ ]
1434
+ );
1435
+ const thursdayIndex = weekStartsOn === 0 ? 4 : 3;
1436
+ const year = adapter.getYear(viewMonth);
1437
+ const month = adapter.getMonth(viewMonth);
1438
+ const title = formatMonthYear(year, month, locale);
1439
+ useEffect(() => {
1440
+ if (!ctx.isOpen || !gridRef.current) return;
1441
+ const focusedButton = gridRef.current.querySelector('[data-focused="true"]');
1442
+ focusedButton?.focus({ preventScroll: true });
1443
+ }, [focusedDate, ctx.isOpen]);
1444
+ const navigateMonth = useCallback(
1445
+ (direction) => {
1446
+ const newMonth = adapter.addMonths(viewMonth, direction);
1447
+ ctx.setViewMonth(newMonth);
1448
+ ctx.setFocusedDate(adapter.startOfMonth(newMonth));
1449
+ const y = adapter.getYear(newMonth);
1450
+ const m = adapter.getMonth(newMonth);
1451
+ ctx.announce(formatMonthYear(y, m, locale));
1452
+ },
1453
+ [adapter, viewMonth, ctx, locale]
1454
+ );
1455
+ const commitDay = useCallback(
1456
+ (iso) => {
1457
+ if (selectionMode === "week") {
1458
+ const weekStart = adapter.startOfWeek(iso, weekStartsOn);
1459
+ const weekEnd = adapter.startOfDay(adapter.endOfWeek(iso, weekStartsOn));
1460
+ const range = { start: weekStart, end: weekEnd };
1461
+ ctx.setRange(range);
1462
+ ctx.close();
1463
+ ctx.announce(
1464
+ `${ctx.labels.rangeSelected}: ${safeFormatFullDate2(weekStart, locale)} \u2013 ${safeFormatFullDate2(weekEnd, locale)}`
1465
+ );
1466
+ } else {
1467
+ const wasPickingStart = selectingTarget === "start";
1468
+ const previousStart = value.start;
1469
+ ctx.selectDate(iso);
1470
+ const formatted = safeFormatFullDate2(iso, locale);
1471
+ if (wasPickingStart) {
1472
+ ctx.announce(`${formatted}. ${ctx.labels.selectingEnd}`);
1473
+ } else if (previousStart) {
1474
+ const [start, end] = adapter.isBefore(iso, previousStart) ? [iso, previousStart] : [previousStart, iso];
1475
+ ctx.announce(
1476
+ `${ctx.labels.rangeSelected}: ${safeFormatFullDate2(start, locale)} \u2013 ${safeFormatFullDate2(end, locale)}`
1477
+ );
1478
+ } else {
1479
+ ctx.announce(formatted);
1480
+ }
1481
+ }
1482
+ },
1483
+ [selectionMode, adapter, weekStartsOn, ctx, locale, selectingTarget, value.start]
1484
+ );
1485
+ const handleDayClick = useCallback(
1486
+ (day) => {
1487
+ if (day.isDisabled) return;
1488
+ commitDay(day.isoString);
1489
+ },
1490
+ [commitDay]
1491
+ );
1492
+ const handleDayMouseEnter = useCallback(
1493
+ (day) => {
1494
+ if (selectionMode === "week") return;
1495
+ if (selectingTarget === "end" && value.start && !day.isDisabled) {
1496
+ ctx.setHoverDate(day.isoString);
1497
+ }
1498
+ },
1499
+ [selectionMode, selectingTarget, value.start, ctx]
1500
+ );
1501
+ const handleMouseLeave = useCallback(() => {
1502
+ ctx.setHoverDate(null);
1503
+ }, [ctx]);
1504
+ const handleKeyDown = useCallback(
1505
+ (e) => {
1506
+ let newFocused = null;
1507
+ switch (e.key) {
1508
+ case "ArrowLeft":
1509
+ newFocused = adapter.addDays(focusedDate, -1);
1510
+ break;
1511
+ case "ArrowRight":
1512
+ newFocused = adapter.addDays(focusedDate, 1);
1513
+ break;
1514
+ case "ArrowUp":
1515
+ newFocused = adapter.addDays(focusedDate, -7);
1516
+ break;
1517
+ case "ArrowDown":
1518
+ newFocused = adapter.addDays(focusedDate, 7);
1519
+ break;
1520
+ case "PageUp":
1521
+ newFocused = e.shiftKey ? adapter.addYears(focusedDate, -1) : adapter.addMonths(focusedDate, -1);
1522
+ break;
1523
+ case "PageDown":
1524
+ newFocused = e.shiftKey ? adapter.addYears(focusedDate, 1) : adapter.addMonths(focusedDate, 1);
1525
+ break;
1526
+ case "Home":
1527
+ newFocused = adapter.startOfWeek(focusedDate, weekStartsOn);
1528
+ break;
1529
+ case "End":
1530
+ newFocused = adapter.startOfDay(adapter.endOfWeek(focusedDate, weekStartsOn));
1531
+ break;
1532
+ case "Enter":
1533
+ case " ":
1534
+ e.preventDefault();
1535
+ if (!isDateDisabled(focusedDate, disabled, adapter)) {
1536
+ commitDay(focusedDate);
1537
+ }
1538
+ return;
1539
+ case "Escape":
1540
+ ctx.close();
1541
+ return;
1542
+ default:
1543
+ return;
1544
+ }
1545
+ if (newFocused) {
1546
+ e.preventDefault();
1547
+ const skipStep = e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "PageUp" || e.key === "Home" ? -1 : 1;
1548
+ let attempts = 0;
1549
+ while (isDateDisabled(newFocused, disabled, adapter) && attempts < 42) {
1550
+ newFocused = adapter.addDays(newFocused, skipStep);
1551
+ attempts++;
1552
+ }
1553
+ if (attempts >= 42) return;
1554
+ ctx.setFocusedDate(newFocused);
1555
+ if (!adapter.isSameMonth(newFocused, viewMonth)) {
1556
+ ctx.setViewMonth(newFocused);
1557
+ }
1558
+ if (selectionMode === "range" && selectingTarget === "end" && value.start) {
1559
+ ctx.setHoverDate(newFocused);
1560
+ }
1561
+ }
1562
+ },
1563
+ [
1564
+ adapter,
1565
+ focusedDate,
1566
+ viewMonth,
1567
+ weekStartsOn,
1568
+ disabled,
1569
+ ctx,
1570
+ selectionMode,
1571
+ selectingTarget,
1572
+ value.start,
1573
+ commitDay
1574
+ ]
1575
+ );
1576
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, onMouseLeave: handleMouseLeave, children: [
1577
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
1578
+ /* @__PURE__ */ jsx(
1579
+ "button",
1580
+ {
1581
+ type: "button",
1582
+ className: classNames?.navButton,
1583
+ onClick: () => navigateMonth(-1),
1584
+ "aria-label": ctx.labels.prevMonth,
1585
+ children: "<"
1586
+ }
1587
+ ),
1588
+ /* @__PURE__ */ jsx("span", { className: classNames?.title, "aria-live": "polite", children: title }),
1589
+ /* @__PURE__ */ jsx(
1590
+ "button",
1591
+ {
1592
+ type: "button",
1593
+ className: classNames?.navButton,
1594
+ onClick: () => navigateMonth(1),
1595
+ "aria-label": ctx.labels.nextMonth,
1596
+ children: ">"
1597
+ }
1598
+ )
1599
+ ] }),
1600
+ /* @__PURE__ */ jsxs(
1601
+ "table",
1602
+ {
1603
+ ref: gridRef,
1604
+ role: "grid",
1605
+ "aria-label": title,
1606
+ "aria-rowcount": weeks.length + 1,
1607
+ "aria-colcount": 7,
1608
+ className: classNames?.grid,
1609
+ onKeyDown: handleKeyDown,
1610
+ children: [
1611
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { role: "row", "aria-rowindex": 1, children: [
1612
+ showWeekNumber ? /* @__PURE__ */ jsx("th", { scope: "col", "aria-hidden": "true", className: classNames?.weekNumberHeader, children: "#" }) : null,
1613
+ weekdays.map((day, colIndex) => /* @__PURE__ */ jsx(
1614
+ "th",
1615
+ {
1616
+ role: "columnheader",
1617
+ abbr: day.full,
1618
+ scope: "col",
1619
+ "aria-colindex": colIndex + 1,
1620
+ className: classNames?.weekdayHeader,
1621
+ children: day.short
1622
+ },
1623
+ day.short
1624
+ ))
1625
+ ] }) }),
1626
+ /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsxs(
1627
+ "tr",
1628
+ {
1629
+ role: "row",
1630
+ "aria-rowindex": weekIndex + 2,
1631
+ className: classNames?.gridRow,
1632
+ children: [
1633
+ showWeekNumber ? /* @__PURE__ */ jsx(
1634
+ "th",
1635
+ {
1636
+ scope: "row",
1637
+ "aria-hidden": "true",
1638
+ className: classNames?.weekNumber,
1639
+ "data-week-number": true,
1640
+ children: getISOWeekNumber(week[thursdayIndex].isoString)
1641
+ }
1642
+ ) : null,
1643
+ week.map((day, colIndex) => {
1644
+ const dayClasses = [
1645
+ classNames?.day,
1646
+ day.isRangeStart && classNames?.dayRangeStart,
1647
+ day.isRangeEnd && classNames?.dayRangeEnd,
1648
+ day.isInRange && classNames?.dayInRange,
1649
+ day.isToday && classNames?.dayToday,
1650
+ day.isDisabled && classNames?.dayDisabled,
1651
+ !day.isCurrentMonth && classNames?.dayOutsideMonth
1652
+ ].filter(Boolean).join(" ") || void 0;
1653
+ const isSelected = selectionMode === "week" ? day.isRangeStart || day.isRangeEnd || day.isInRange : day.isRangeStart || day.isRangeEnd;
1654
+ return /* @__PURE__ */ jsx(
1655
+ "td",
1656
+ {
1657
+ role: "gridcell",
1658
+ "aria-colindex": colIndex + 1,
1659
+ "aria-selected": isSelected || void 0,
1660
+ "aria-disabled": day.isDisabled || void 0,
1661
+ "aria-current": day.isToday ? "date" : void 0,
1662
+ className: classNames?.gridCell,
1663
+ children: /* @__PURE__ */ jsx(
1664
+ "button",
1665
+ {
1666
+ type: "button",
1667
+ tabIndex: day.isFocused ? 0 : -1,
1668
+ disabled: day.isDisabled,
1669
+ "data-focused": day.isFocused || void 0,
1670
+ "data-range-start": day.isRangeStart || void 0,
1671
+ "data-range-end": day.isRangeEnd || void 0,
1672
+ "data-in-range": day.isInRange || void 0,
1673
+ "data-today": day.isToday || void 0,
1674
+ "data-outside-month": !day.isCurrentMonth || void 0,
1675
+ className: dayClasses,
1676
+ onClick: () => handleDayClick(day),
1677
+ onMouseEnter: () => handleDayMouseEnter(day),
1678
+ "aria-label": safeFormatFullDate2(day.isoString, locale),
1679
+ children: day.dayNumber
1680
+ }
1681
+ )
1682
+ },
1683
+ day.isoString
1684
+ );
1685
+ })
1686
+ ]
1687
+ },
1688
+ weekIndex
1689
+ )) })
1690
+ ]
1691
+ }
1692
+ )
1693
+ ] });
1694
+ }
1695
+ function RangePickerPresets({ classNames, children, ...props }) {
1696
+ const ctx = useRangePickerContext("RangePicker.Presets");
1697
+ return /* @__PURE__ */ jsx("div", { role: "group", "aria-label": ctx.labels.presetsGroup, className: classNames?.root, ...props, children });
1698
+ }
1699
+ function resolvePreset(key, today, adapter) {
1700
+ switch (key) {
1701
+ case "today":
1702
+ return { start: today, end: today };
1703
+ case "yesterday": {
1704
+ const yesterday = adapter.addDays(today, -1);
1705
+ return { start: yesterday, end: yesterday };
1706
+ }
1707
+ case "last7days":
1708
+ return { start: adapter.addDays(today, -6), end: today };
1709
+ case "last30days":
1710
+ return { start: adapter.addDays(today, -29), end: today };
1711
+ case "thisWeek":
1712
+ return {
1713
+ start: adapter.startOfDay(adapter.startOfWeek(today)),
1714
+ end: adapter.startOfDay(adapter.endOfWeek(today))
1715
+ };
1716
+ case "lastWeek": {
1717
+ const prevWeek = adapter.addDays(today, -7);
1718
+ return {
1719
+ start: adapter.startOfDay(adapter.startOfWeek(prevWeek)),
1720
+ end: adapter.startOfDay(adapter.endOfWeek(prevWeek))
1721
+ };
1722
+ }
1723
+ case "thisMonth":
1724
+ return {
1725
+ start: adapter.startOfMonth(today),
1726
+ end: adapter.startOfDay(adapter.endOfMonth(today))
1727
+ };
1728
+ case "lastMonth": {
1729
+ const prevMonth = adapter.addMonths(today, -1);
1730
+ return {
1731
+ start: adapter.startOfMonth(prevMonth),
1732
+ end: adapter.startOfDay(adapter.endOfMonth(prevMonth))
1733
+ };
1734
+ }
1735
+ case "thisYear": {
1736
+ const currentMonth = new Date(today).getUTCMonth();
1737
+ const yearStart = adapter.startOfMonth(adapter.addMonths(today, -currentMonth));
1738
+ return { start: yearStart, end: today };
1739
+ }
1740
+ }
1741
+ }
1742
+ function RangePickerPreset({
1743
+ value: presetKey,
1744
+ range: directRange,
1745
+ children,
1746
+ onClick,
1747
+ ...props
1748
+ }) {
1749
+ const ctx = useRangePickerContext("RangePicker.Preset");
1750
+ const resolved = useMemo(() => {
1751
+ if (directRange) return directRange;
1752
+ if (presetKey)
1753
+ return resolvePreset(presetKey, ctx.adapter.today(ctx.displayTimezone), ctx.adapter);
1754
+ return null;
1755
+ }, [directRange, presetKey, ctx.adapter, ctx.displayTimezone]);
1756
+ const handleClick = useCallback(
1757
+ (e) => {
1758
+ if (ctx.isDisabled || ctx.isReadOnly) return;
1759
+ if (!resolved) return;
1760
+ ctx.setRange(resolved);
1761
+ ctx.close();
1762
+ onClick?.(e);
1763
+ },
1764
+ [ctx, resolved, onClick]
1765
+ );
1766
+ const isActive = useMemo(() => {
1767
+ if (!ctx.value.start || !ctx.value.end || !resolved || !resolved.start || !resolved.end) {
1768
+ return false;
1769
+ }
1770
+ return ctx.adapter.isSameDay(ctx.value.start, resolved.start) && ctx.adapter.isSameDay(ctx.value.end, resolved.end);
1771
+ }, [ctx.value.start, ctx.value.end, ctx.adapter, resolved]);
1772
+ return /* @__PURE__ */ jsx(
1773
+ "button",
1774
+ {
1775
+ type: "button",
1776
+ "aria-pressed": isActive,
1777
+ "data-active": isActive || void 0,
1778
+ disabled: ctx.isDisabled,
1779
+ onClick: handleClick,
1780
+ ...props,
1781
+ children
1782
+ }
1783
+ );
1784
+ }
1785
+
1786
+ // src/components/RangePicker/index.ts
1787
+ var RangePicker = Object.assign(RangePickerRoot, {
1788
+ Input: RangePickerInput,
1789
+ Popover: RangePickerPopover,
1790
+ Calendar: RangePickerCalendar,
1791
+ Presets: RangePickerPresets,
1792
+ Preset: RangePickerPreset
1793
+ });
1794
+ var TimePickerContext = createContext(null);
1795
+ function useTimePickerContext(componentName) {
1796
+ const context = useContext(TimePickerContext);
1797
+ if (!context) {
1798
+ throw new Error(
1799
+ `[${componentName}] TimePicker.Root \uB0B4\uBD80\uC5D0\uC11C \uC0AC\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4.
1800
+
1801
+ \uC62C\uBC14\uB978 \uC0AC\uC6A9\uBC95:
1802
+ <TimePicker>
1803
+ <TimePicker.${componentName.replace("TimePicker.", "")} />
1804
+ </TimePicker>`
1805
+ );
1806
+ }
1807
+ return context;
1808
+ }
1809
+ function TimePickerRoot({
1810
+ value: controlledValue,
1811
+ defaultValue,
1812
+ onChange,
1813
+ format = "24h",
1814
+ step = 1,
1815
+ withSeconds = false,
1816
+ displayTimezone,
1817
+ disabled = false,
1818
+ readOnly = false,
1819
+ filterTime,
1820
+ adapter: adapterProp,
1821
+ labels: labelsProp,
1822
+ children
1823
+ }) {
1824
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "TimePicker");
1825
+ const pickerId = useId();
1826
+ const mergedLabels = useMemo(
1827
+ () => ({ ...DEFAULT_TIMEPICKER_LABELS, ...labelsProp }),
1828
+ [labelsProp]
1829
+ );
1830
+ const isControlled = useRef(controlledValue !== void 0).current;
1831
+ const [uncontrolledValue, setUncontrolledValue] = useState(
1832
+ defaultValue ?? null
1833
+ );
1834
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1835
+ const currentTime = useMemo(() => {
1836
+ if (!currentValue) return { hours: 0, minutes: 0, seconds: 0 };
1837
+ return displayTimezone ? getTimeInTimezone(currentValue, displayTimezone) : getTime(currentValue);
1838
+ }, [currentValue, displayTimezone]);
1839
+ const setTime$1 = useCallback(
1840
+ (partial) => {
1841
+ if (disabled || readOnly) return;
1842
+ const base = currentValue ?? adapter.today(displayTimezone);
1843
+ const newIso = displayTimezone ? setTimeInTimezone(base, partial, displayTimezone) : setTime(base, partial);
1844
+ if (!isControlled) {
1845
+ setUncontrolledValue(newIso);
1846
+ }
1847
+ onChange?.(newIso);
1848
+ },
1849
+ [disabled, readOnly, currentValue, displayTimezone, isControlled, onChange, adapter]
1850
+ );
1851
+ const contextValue = useMemo(
1852
+ () => ({
1853
+ value: currentValue,
1854
+ setTime: setTime$1,
1855
+ format,
1856
+ step,
1857
+ withSeconds,
1858
+ displayTimezone,
1859
+ isDisabled: disabled,
1860
+ isReadOnly: readOnly,
1861
+ currentTime,
1862
+ pickerId,
1863
+ labels: mergedLabels,
1864
+ filterTime
1865
+ }),
1866
+ [
1867
+ currentValue,
1868
+ setTime$1,
1869
+ format,
1870
+ step,
1871
+ withSeconds,
1872
+ displayTimezone,
1873
+ disabled,
1874
+ readOnly,
1875
+ currentTime,
1876
+ pickerId,
1877
+ mergedLabels,
1878
+ filterTime
1879
+ ]
1880
+ );
1881
+ return /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: contextValue, children });
1882
+ }
1883
+ var TimePickerInput = forwardRef(
1884
+ function TimePickerInput2({ onBlur, onKeyDown, ...props }, ref) {
1885
+ const ctx = useTimePickerContext("TimePicker.Input");
1886
+ const [inputText, setInputText] = useState(null);
1887
+ useEffect(() => {
1888
+ setInputText(null);
1889
+ }, [ctx.value]);
1890
+ const displayValue = inputText !== null ? inputText : formatTimeString(ctx.currentTime, ctx.withSeconds);
1891
+ const commitInput = useCallback(() => {
1892
+ if (inputText === null) return;
1893
+ const parsed = parseTimeString(inputText);
1894
+ if (parsed) {
1895
+ ctx.setTime(parsed);
1896
+ }
1897
+ setInputText(null);
1898
+ }, [inputText, ctx]);
1899
+ const handleChange = useCallback((e) => {
1900
+ setInputText(e.target.value);
1901
+ }, []);
1902
+ const handleBlur = useCallback(
1903
+ (e) => {
1904
+ commitInput();
1905
+ onBlur?.(e);
1906
+ },
1907
+ [commitInput, onBlur]
1908
+ );
1909
+ const handleKeyDown = useCallback(
1910
+ (e) => {
1911
+ if (e.key === "Enter") {
1912
+ commitInput();
1913
+ }
1914
+ onKeyDown?.(e);
1915
+ },
1916
+ [commitInput, onKeyDown]
1917
+ );
1918
+ return /* @__PURE__ */ jsx(
1919
+ "input",
1920
+ {
1921
+ ref,
1922
+ type: "text",
1923
+ inputMode: "numeric",
1924
+ autoComplete: "off",
1925
+ "aria-label": ctx.labels.timeInput,
1926
+ placeholder: ctx.withSeconds ? "HH:MM:SS" : "HH:MM",
1927
+ value: displayValue,
1928
+ disabled: ctx.isDisabled || props.disabled,
1929
+ readOnly: ctx.isReadOnly,
1930
+ onChange: handleChange,
1931
+ onBlur: handleBlur,
1932
+ onKeyDown: handleKeyDown,
1933
+ ...props
1934
+ }
1935
+ );
1936
+ }
1937
+ );
1938
+ TimePickerInput.displayName = "TimePicker.Input";
1939
+ function useListboxNavigation({
1940
+ items,
1941
+ onSelect,
1942
+ disabled = false
1943
+ }) {
1944
+ const listRef = useRef(null);
1945
+ const rafIdRef = useRef(0);
1946
+ useEffect(() => {
1947
+ return () => cancelAnimationFrame(rafIdRef.current);
1948
+ }, []);
1949
+ const handleKeyDown = useCallback(
1950
+ (e, item) => {
1951
+ if (disabled) return;
1952
+ const currentIndex = items.indexOf(item);
1953
+ let newIndex = -1;
1954
+ if (e.key === "ArrowDown") {
1955
+ newIndex = Math.min(currentIndex + 1, items.length - 1);
1956
+ } else if (e.key === "ArrowUp") {
1957
+ newIndex = Math.max(currentIndex - 1, 0);
1958
+ } else if (e.key === "Home") {
1959
+ newIndex = 0;
1960
+ } else if (e.key === "End") {
1961
+ newIndex = items.length - 1;
1962
+ } else if (e.key === "Enter" || e.key === " ") {
1963
+ e.preventDefault();
1964
+ onSelect(item);
1965
+ return;
1966
+ } else {
1967
+ return;
1968
+ }
1969
+ e.preventDefault();
1970
+ const target = items[newIndex];
1971
+ if (target !== void 0) {
1972
+ onSelect(target);
1973
+ cancelAnimationFrame(rafIdRef.current);
1974
+ rafIdRef.current = requestAnimationFrame(() => {
1975
+ const next = listRef.current?.querySelector('[data-selected="true"]');
1976
+ next?.focus();
1977
+ });
1978
+ }
1979
+ },
1980
+ [items, onSelect, disabled]
1981
+ );
1982
+ return { listRef, handleKeyDown };
1983
+ }
1984
+ function TimePickerHourList({ classNames, ...props }) {
1985
+ const ctx = useTimePickerContext("TimePicker.HourList");
1986
+ const { format, step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
1987
+ const hours = useMemo(() => generateHours(format), [format]);
1988
+ const selectedHourDisplay = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;
1989
+ const currentPeriod = format === "12h" ? to12Hour(currentTime.hours).period : null;
1990
+ const fullyDisabledHours24 = useMemo(() => {
1991
+ if (!filterTime) return null;
1992
+ const disabled = /* @__PURE__ */ new Set();
1993
+ for (let h = 0; h < 24; h++) {
1994
+ let allRejected = true;
1995
+ for (let m = 0; m < 60; m += step) {
1996
+ if (!filterTime(h, m)) {
1997
+ allRejected = false;
1998
+ break;
1999
+ }
2000
+ }
2001
+ if (allRejected) disabled.add(h);
2002
+ }
2003
+ return disabled;
2004
+ }, [filterTime, step]);
2005
+ const isHourDisabled = useCallback(
2006
+ (hourDisplay) => {
2007
+ if (!fullyDisabledHours24) return false;
2008
+ const hours24 = format === "12h" && currentPeriod ? to24Hour(hourDisplay, currentPeriod) : hourDisplay;
2009
+ return fullyDisabledHours24.has(hours24);
2010
+ },
2011
+ [fullyDisabledHours24, format, currentPeriod]
2012
+ );
2013
+ const handleSelect = useCallback(
2014
+ (hourDisplay) => {
2015
+ if (isDisabled || isReadOnly) return;
2016
+ if (isHourDisabled(hourDisplay)) return;
2017
+ const hours24 = format === "12h" && currentPeriod ? to24Hour(hourDisplay, currentPeriod) : hourDisplay;
2018
+ ctx.setTime({ hours: hours24 });
2019
+ },
2020
+ [format, currentPeriod, ctx, isDisabled, isReadOnly, isHourDisabled]
2021
+ );
2022
+ const { listRef, handleKeyDown } = useListboxNavigation({
2023
+ items: hours,
2024
+ onSelect: handleSelect,
2025
+ disabled: isDisabled || isReadOnly
2026
+ });
2027
+ return /* @__PURE__ */ jsx(
2028
+ "ul",
2029
+ {
2030
+ ref: listRef,
2031
+ role: "listbox",
2032
+ "aria-label": ctx.labels.hourList,
2033
+ "aria-disabled": isDisabled || void 0,
2034
+ className: classNames?.root,
2035
+ ...props,
2036
+ children: hours.map((hour) => {
2037
+ const isSelected = hour === selectedHourDisplay;
2038
+ const isHourFullyDisabled = isHourDisabled(hour);
2039
+ const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
2040
+ return /* @__PURE__ */ jsx(
2041
+ "li",
2042
+ {
2043
+ role: "option",
2044
+ "aria-selected": isSelected,
2045
+ "aria-disabled": isDisabled || isHourFullyDisabled || void 0,
2046
+ "aria-label": ctx.labels.hourOption(hour),
2047
+ "data-selected": isSelected || void 0,
2048
+ tabIndex: isSelected ? 0 : -1,
2049
+ className: optionClass,
2050
+ onClick: () => handleSelect(hour),
2051
+ onKeyDown: (e) => handleKeyDown(e, hour),
2052
+ children: String(hour).padStart(2, "0")
2053
+ },
2054
+ hour
2055
+ );
2056
+ })
2057
+ }
2058
+ );
2059
+ }
2060
+ function TimePickerMinuteList({ classNames, ...props }) {
2061
+ const ctx = useTimePickerContext("TimePicker.MinuteList");
2062
+ const { step, currentTime, isDisabled, isReadOnly, filterTime } = ctx;
2063
+ const minutes = useMemo(() => generateMinutes(step), [step]);
2064
+ const isMinuteDisabled = useCallback(
2065
+ (minute) => {
2066
+ if (!filterTime) return false;
2067
+ return filterTime(currentTime.hours, minute);
2068
+ },
2069
+ [filterTime, currentTime.hours]
2070
+ );
2071
+ const handleSelect = useCallback(
2072
+ (minute) => {
2073
+ if (isDisabled || isReadOnly) return;
2074
+ if (isMinuteDisabled(minute)) return;
2075
+ ctx.setTime({ minutes: minute });
2076
+ },
2077
+ [ctx, isDisabled, isReadOnly, isMinuteDisabled]
2078
+ );
2079
+ const { listRef, handleKeyDown } = useListboxNavigation({
2080
+ items: minutes,
2081
+ onSelect: handleSelect,
2082
+ disabled: isDisabled || isReadOnly
2083
+ });
2084
+ return /* @__PURE__ */ jsx(
2085
+ "ul",
2086
+ {
2087
+ ref: listRef,
2088
+ role: "listbox",
2089
+ "aria-label": ctx.labels.minuteList,
2090
+ "aria-disabled": isDisabled || void 0,
2091
+ className: classNames?.root,
2092
+ ...props,
2093
+ children: minutes.map((minute) => {
2094
+ const isSelected = minute === currentTime.minutes;
2095
+ const isMinuteFullyDisabled = isMinuteDisabled(minute);
2096
+ const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
2097
+ return /* @__PURE__ */ jsx(
2098
+ "li",
2099
+ {
2100
+ role: "option",
2101
+ "aria-selected": isSelected,
2102
+ "aria-disabled": isDisabled || isMinuteFullyDisabled || void 0,
2103
+ "aria-label": ctx.labels.minuteOption(minute),
2104
+ "data-selected": isSelected || void 0,
2105
+ tabIndex: isSelected ? 0 : -1,
2106
+ className: optionClass,
2107
+ onClick: () => handleSelect(minute),
2108
+ onKeyDown: (e) => handleKeyDown(e, minute),
2109
+ children: String(minute).padStart(2, "0")
2110
+ },
2111
+ minute
2112
+ );
2113
+ })
2114
+ }
2115
+ );
2116
+ }
2117
+ function TimePickerAmPmToggle({ classNames, ...props }) {
2118
+ const ctx = useTimePickerContext("TimePicker.AmPmToggle");
2119
+ const amRef = useRef(null);
2120
+ const pmRef = useRef(null);
2121
+ const setPeriod = useCallback(
2122
+ (newPeriod) => {
2123
+ if (ctx.isDisabled || ctx.isReadOnly) return;
2124
+ const { hours12 } = to12Hour(ctx.currentTime.hours);
2125
+ const newHours24 = to24Hour(hours12, newPeriod);
2126
+ ctx.setTime({ hours: newHours24 });
2127
+ },
2128
+ [ctx]
2129
+ );
2130
+ if (ctx.format !== "12h") return null;
2131
+ const { period } = to12Hour(ctx.currentTime.hours);
2132
+ const focusOther = (target) => {
2133
+ (target === "AM" ? amRef : pmRef).current?.focus();
2134
+ };
2135
+ const handleKeyDown = (e, target) => {
2136
+ switch (e.key) {
2137
+ case "ArrowRight":
2138
+ case "ArrowDown":
2139
+ case "ArrowLeft":
2140
+ case "ArrowUp": {
2141
+ e.preventDefault();
2142
+ const next = target === "AM" ? "PM" : "AM";
2143
+ setPeriod(next);
2144
+ focusOther(next);
2145
+ break;
2146
+ }
2147
+ case "Home": {
2148
+ e.preventDefault();
2149
+ setPeriod("AM");
2150
+ focusOther("AM");
2151
+ break;
2152
+ }
2153
+ case "End": {
2154
+ e.preventDefault();
2155
+ setPeriod("PM");
2156
+ focusOther("PM");
2157
+ break;
2158
+ }
2159
+ case " ":
2160
+ case "Enter": {
2161
+ e.preventDefault();
2162
+ setPeriod(target);
2163
+ break;
2164
+ }
2165
+ }
2166
+ };
2167
+ const renderButton = (target) => {
2168
+ const isSelected = period === target;
2169
+ const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
2170
+ return /* @__PURE__ */ jsx(
2171
+ "button",
2172
+ {
2173
+ ref: target === "AM" ? amRef : pmRef,
2174
+ type: "button",
2175
+ role: "radio",
2176
+ "aria-checked": isSelected,
2177
+ tabIndex: isSelected ? 0 : -1,
2178
+ "data-selected": isSelected || void 0,
2179
+ disabled: ctx.isDisabled,
2180
+ className: optionClass,
2181
+ onClick: () => setPeriod(target),
2182
+ onKeyDown: (e) => handleKeyDown(e, target),
2183
+ children: target
2184
+ }
2185
+ );
2186
+ };
2187
+ return /* @__PURE__ */ jsxs(
2188
+ "div",
2189
+ {
2190
+ role: "radiogroup",
2191
+ "aria-label": ctx.labels.amPmToggle,
2192
+ className: classNames?.root,
2193
+ ...props,
2194
+ children: [
2195
+ renderButton("AM"),
2196
+ renderButton("PM")
2197
+ ]
2198
+ }
2199
+ );
2200
+ }
2201
+
2202
+ // src/components/TimePicker/index.ts
2203
+ var TimePicker = Object.assign(TimePickerRoot, {
2204
+ Input: TimePickerInput,
2205
+ HourList: TimePickerHourList,
2206
+ MinuteList: TimePickerMinuteList,
2207
+ AmPmToggle: TimePickerAmPmToggle
2208
+ });
2209
+ function DateTimePickerRoot({
2210
+ value: controlledValue,
2211
+ defaultValue,
2212
+ onChange,
2213
+ onOpenChange,
2214
+ onCalendarNavigate,
2215
+ format = "24h",
2216
+ step = 1,
2217
+ withSeconds = false,
2218
+ filterTime,
2219
+ disabled = false,
2220
+ readOnly = false,
2221
+ weekStartsOn = 0,
2222
+ displayFormat = "yyyy-MM-dd HH:mm",
2223
+ locale = "en-US",
2224
+ displayTimezone,
2225
+ adapter: adapterProp,
2226
+ labels: labelsProp,
2227
+ children
2228
+ }) {
2229
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "DateTimePicker");
2230
+ const pickerId = useId();
2231
+ const mergedDateLabels = useMemo(
2232
+ () => ({ ...DEFAULT_DATEPICKER_LABELS, ...labelsProp }),
2233
+ [labelsProp]
2234
+ );
2235
+ const mergedTimeLabels = useMemo(
2236
+ () => ({ ...DEFAULT_TIMEPICKER_LABELS, ...labelsProp }),
2237
+ [labelsProp]
2238
+ );
2239
+ const isControlled = useRef(controlledValue !== void 0).current;
2240
+ const referenceRef = useRef(null);
2241
+ const [uncontrolledValue, setUncontrolledValue] = useState(
2242
+ defaultValue ?? null
2243
+ );
2244
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
2245
+ const [isOpen, setIsOpen] = useState(false);
2246
+ const [viewMonth, setViewMonth] = useState(
2247
+ () => currentValue ?? adapter.today(displayTimezone)
2248
+ );
2249
+ const [focusedDate, setFocusedDate] = useState(
2250
+ () => currentValue ?? adapter.today(displayTimezone)
2251
+ );
2252
+ useChangeEffect(isOpen, onOpenChange);
2253
+ const viewMonthStart = useMemo(() => adapter.startOfMonth(viewMonth), [viewMonth, adapter]);
2254
+ useChangeEffect(viewMonthStart, onCalendarNavigate);
2255
+ const isDisabled = typeof disabled === "boolean" ? disabled : false;
2256
+ const disabledRules = useMemo(
2257
+ () => Array.isArray(disabled) ? disabled : [],
2258
+ [disabled]
2259
+ );
2260
+ const currentTime = useMemo(() => {
2261
+ if (!currentValue) return { hours: 0, minutes: 0, seconds: 0 };
2262
+ return displayTimezone ? getTimeInTimezone(currentValue, displayTimezone) : getTime(currentValue);
2263
+ }, [currentValue, displayTimezone]);
2264
+ const updateValue = useCallback(
2265
+ (next) => {
2266
+ if (isDisabled || readOnly) return;
2267
+ if (!isControlled) {
2268
+ setUncontrolledValue(next);
2269
+ }
2270
+ onChange?.(next);
2271
+ },
2272
+ [isControlled, isDisabled, readOnly, onChange]
2273
+ );
2274
+ const selectDate = useCallback(
2275
+ (newDateIso) => {
2276
+ if (newDateIso === null) {
2277
+ updateValue(null);
2278
+ return;
2279
+ }
2280
+ const normalizedDate = displayTimezone ? civilMidnightFromUtcDay(newDateIso, displayTimezone) : newDateIso;
2281
+ const time = currentValue ? displayTimezone ? getTimeInTimezone(currentValue, displayTimezone) : getTime(currentValue) : currentTime;
2282
+ const merged = displayTimezone ? setTimeInTimezone(normalizedDate, time, displayTimezone) : setTime(normalizedDate, time);
2283
+ updateValue(merged);
2284
+ },
2285
+ [currentValue, currentTime, updateValue, displayTimezone]
2286
+ );
2287
+ const setTime$1 = useCallback(
2288
+ (partial) => {
2289
+ const base = currentValue ?? adapter.today(displayTimezone);
2290
+ const merged = displayTimezone ? setTimeInTimezone(base, partial, displayTimezone) : setTime(base, partial);
2291
+ updateValue(merged);
2292
+ },
2293
+ [currentValue, updateValue, displayTimezone, adapter]
2294
+ );
2295
+ const open = useCallback(() => {
2296
+ if (isDisabled || readOnly) return;
2297
+ setIsOpen(true);
2298
+ const target = currentValue ?? adapter.today(displayTimezone);
2299
+ setViewMonth(target);
2300
+ setFocusedDate(target);
2301
+ }, [isDisabled, readOnly, currentValue, adapter, displayTimezone]);
2302
+ const close = useCallback(() => {
2303
+ setIsOpen(false);
2304
+ }, []);
2305
+ const toggle = useCallback(() => {
2306
+ if (isOpen) close();
2307
+ else open();
2308
+ }, [isOpen, open, close]);
2309
+ const dateContext = useMemo(
2310
+ () => ({
2311
+ referenceRef,
2312
+ value: currentValue,
2313
+ selectDate,
2314
+ isOpen,
2315
+ open,
2316
+ close,
2317
+ toggle,
2318
+ viewMonth,
2319
+ setViewMonth,
2320
+ focusedDate,
2321
+ setFocusedDate,
2322
+ adapter,
2323
+ disabled: disabledRules,
2324
+ weekStartsOn,
2325
+ displayFormat,
2326
+ locale,
2327
+ displayTimezone,
2328
+ isDisabled,
2329
+ isReadOnly: readOnly,
2330
+ pickerId,
2331
+ labels: mergedDateLabels
2332
+ }),
2333
+ [
2334
+ currentValue,
2335
+ selectDate,
2336
+ isOpen,
2337
+ open,
2338
+ close,
2339
+ toggle,
2340
+ viewMonth,
2341
+ focusedDate,
2342
+ adapter,
2343
+ disabledRules,
2344
+ weekStartsOn,
2345
+ displayFormat,
2346
+ locale,
2347
+ displayTimezone,
2348
+ isDisabled,
2349
+ readOnly,
2350
+ pickerId,
2351
+ mergedDateLabels
2352
+ ]
2353
+ );
2354
+ const timeContext = useMemo(
2355
+ () => ({
2356
+ value: currentValue,
2357
+ setTime: setTime$1,
2358
+ format,
2359
+ step,
2360
+ withSeconds,
2361
+ displayTimezone,
2362
+ isDisabled,
2363
+ isReadOnly: readOnly,
2364
+ currentTime,
2365
+ pickerId,
2366
+ labels: mergedTimeLabels,
2367
+ filterTime
2368
+ }),
2369
+ [
2370
+ currentValue,
2371
+ setTime$1,
2372
+ format,
2373
+ step,
2374
+ withSeconds,
2375
+ displayTimezone,
2376
+ isDisabled,
2377
+ readOnly,
2378
+ currentTime,
2379
+ pickerId,
2380
+ mergedTimeLabels,
2381
+ filterTime
2382
+ ]
2383
+ );
2384
+ return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: dateContext, children: /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: timeContext, children }) });
2385
+ }
2386
+ var DateTimePickerInput = forwardRef(
2387
+ function DateTimePickerInput2({ onClick, onKeyDown, ...props }, ref) {
2388
+ const ctx = useDatePickerContext("DateTimePicker.Input");
2389
+ let displayValue = "";
2390
+ if (ctx.value) {
2391
+ try {
2392
+ const datePart = ctx.adapter.format(ctx.value, "yyyy-MM-dd", ctx.displayTimezone);
2393
+ const time = ctx.displayTimezone ? getTimeInTimezone(ctx.value, ctx.displayTimezone) : getTime(ctx.value);
2394
+ displayValue = `${datePart} ${formatTimeString(time)}`;
2395
+ } catch {
2396
+ displayValue = ctx.value;
2397
+ }
2398
+ }
2399
+ const handleClick = useCallback(
2400
+ (e) => {
2401
+ if (!ctx.isOpen) ctx.open();
2402
+ onClick?.(e);
2403
+ },
2404
+ [ctx, onClick]
2405
+ );
2406
+ const handleKeyDown = useCallback(
2407
+ (e) => {
2408
+ if (e.key === "Escape") {
2409
+ ctx.close();
2410
+ } else if (e.key === "Enter" && ctx.isOpen) {
2411
+ e.preventDefault();
2412
+ } else if (e.key === "ArrowDown" && !ctx.isOpen) {
2413
+ e.preventDefault();
2414
+ ctx.open();
2415
+ }
2416
+ onKeyDown?.(e);
2417
+ },
2418
+ [ctx, onKeyDown]
2419
+ );
2420
+ const calendarId = `${ctx.pickerId}-calendar`;
2421
+ return /* @__PURE__ */ jsx(
2422
+ "input",
2423
+ {
2424
+ ref: (node) => {
2425
+ ctx.referenceRef.current = node;
2426
+ if (typeof ref === "function") ref(node);
2427
+ else if (ref) ref.current = node;
2428
+ },
2429
+ type: "text",
2430
+ role: "combobox",
2431
+ readOnly: true,
2432
+ "aria-label": ctx.labels.dateTimeInput ?? "Date and time",
2433
+ "aria-expanded": ctx.isOpen,
2434
+ "aria-haspopup": "dialog",
2435
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
2436
+ "aria-autocomplete": "none",
2437
+ autoComplete: "off",
2438
+ value: displayValue,
2439
+ disabled: ctx.isDisabled || props.disabled,
2440
+ onClick: handleClick,
2441
+ onKeyDown: handleKeyDown,
2442
+ ...props
2443
+ }
2444
+ );
2445
+ }
2446
+ );
2447
+ DateTimePickerInput.displayName = "DateTimePicker.Input";
2448
+
2449
+ // src/components/DateTimePicker/index.ts
2450
+ var DateTimePicker = Object.assign(DateTimePickerRoot, {
2451
+ Input: DateTimePickerInput,
2452
+ Popover: DatePickerPopover,
2453
+ Calendar: DatePickerCalendar,
2454
+ MonthGrid: DatePickerMonthGrid,
2455
+ YearGrid: DatePickerYearGrid,
2456
+ HourList: TimePickerHourList,
2457
+ MinuteList: TimePickerMinuteList,
2458
+ AmPmToggle: TimePickerAmPmToggle
2459
+ });
2460
+ function MonthPickerRoot(props) {
2461
+ const displayFormat = props.displayFormat ?? "yyyy-MM";
2462
+ return /* @__PURE__ */ jsx(DatePickerRoot, { ...props, displayFormat });
2463
+ }
2464
+ function MonthPickerGrid({ classNames, ...props }) {
2465
+ const ctx = useDatePickerContext("MonthPicker.Grid");
2466
+ const { adapter, viewMonth, locale, value, displayTimezone, labels, disabled } = ctx;
2467
+ const currentYear = adapter.getYear(viewMonth);
2468
+ const [valueYear, valueMonthZeroBased] = useMemo(() => {
2469
+ if (!value) return [null, null];
2470
+ try {
2471
+ const [y, m] = adapter.format(value, "yyyy-MM", displayTimezone).split("-").map(Number);
2472
+ return [y, m - 1];
2473
+ } catch {
2474
+ return [null, null];
2475
+ }
2476
+ }, [value, adapter, displayTimezone]);
2477
+ const [today, setToday] = useState(null);
2478
+ useEffect(() => {
2479
+ setToday(adapter.today(displayTimezone));
2480
+ }, [adapter, displayTimezone]);
2481
+ const todayYear = today !== null ? adapter.getYear(today) : -1;
2482
+ const todayMonth = today !== null ? adapter.getMonth(today) : -1;
2483
+ const monthDisabledFlags = useMemo(
2484
+ () => Array.from({ length: 12 }, (_, i) => {
2485
+ const monthStart = new Date(Date.UTC(currentYear, i, 1)).toISOString();
2486
+ return isRangeFullyDisabled(monthStart, adapter.endOfMonth(monthStart), disabled, adapter);
2487
+ }),
2488
+ [currentYear, disabled, adapter]
2489
+ );
2490
+ const navigateYear = useCallback(
2491
+ (direction) => {
2492
+ ctx.setViewMonth(adapter.addYears(viewMonth, direction));
2493
+ },
2494
+ [adapter, viewMonth, ctx]
2495
+ );
2496
+ const handleMonthSelect = useCallback(
2497
+ (monthIndex) => {
2498
+ if (monthDisabledFlags[monthIndex]) return;
2499
+ const target = new Date(Date.UTC(currentYear, monthIndex, 1)).toISOString();
2500
+ ctx.selectDate(target);
2501
+ },
2502
+ [currentYear, ctx, monthDisabledFlags]
2503
+ );
2504
+ const naturalIndex = valueYear === currentYear && valueMonthZeroBased !== null ? valueMonthZeroBased : adapter.getMonth(viewMonth);
2505
+ const firstEnabled = monthDisabledFlags.findIndex((d) => !d);
2506
+ const initialIndex = monthDisabledFlags[naturalIndex] ? firstEnabled === -1 ? naturalIndex : firstEnabled : naturalIndex;
2507
+ const { gridRef, focusedIndex, handleKeyDown } = useGridState({
2508
+ initialIndex,
2509
+ disabledFlags: monthDisabledFlags,
2510
+ onSelect: handleMonthSelect,
2511
+ onPageUp: () => navigateYear(-1),
2512
+ onPageDown: () => navigateYear(1),
2513
+ onEscape: ctx.close
2514
+ });
2515
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
2516
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
2517
+ /* @__PURE__ */ jsx(
2518
+ "button",
2519
+ {
2520
+ type: "button",
2521
+ className: classNames?.navButton,
2522
+ onClick: () => navigateYear(-1),
2523
+ "aria-label": labels.prevYear,
2524
+ children: "<"
2525
+ }
2526
+ ),
2527
+ /* @__PURE__ */ jsx("span", { className: classNames?.title, children: currentYear }),
2528
+ /* @__PURE__ */ jsx(
2529
+ "button",
2530
+ {
2531
+ type: "button",
2532
+ className: classNames?.navButton,
2533
+ onClick: () => navigateYear(1),
2534
+ "aria-label": labels.nextYear,
2535
+ children: ">"
2536
+ }
2537
+ )
2538
+ ] }),
2539
+ /* @__PURE__ */ jsx(
2540
+ "div",
2541
+ {
2542
+ ref: gridRef,
2543
+ role: "grid",
2544
+ "aria-label": `${currentYear} months`,
2545
+ className: classNames?.grid,
2546
+ onKeyDown: handleKeyDown,
2547
+ children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
2548
+ "div",
2549
+ {
2550
+ role: "row",
2551
+ className: classNames?.gridRow,
2552
+ style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
2553
+ children: Array.from({ length: 3 }, (_2, col) => {
2554
+ const i = rowIndex * 3 + col;
2555
+ const isSelected = valueYear === currentYear && valueMonthZeroBased === i;
2556
+ const isCurrent = todayYear === currentYear && todayMonth === i;
2557
+ const isFocused = i === focusedIndex;
2558
+ const isDisabled = monthDisabledFlags[i] ?? false;
2559
+ const cls = [
2560
+ classNames?.month,
2561
+ isSelected && classNames?.monthSelected,
2562
+ isCurrent && classNames?.monthCurrent,
2563
+ isDisabled && classNames?.monthDisabled
2564
+ ].filter(Boolean).join(" ") || void 0;
2565
+ return /* @__PURE__ */ jsx(
2566
+ "button",
2567
+ {
2568
+ type: "button",
2569
+ role: "gridcell",
2570
+ tabIndex: isFocused ? 0 : -1,
2571
+ disabled: isDisabled,
2572
+ "aria-selected": isSelected || void 0,
2573
+ "aria-disabled": isDisabled || void 0,
2574
+ "aria-current": isCurrent ? "date" : void 0,
2575
+ "data-selected": isSelected || void 0,
2576
+ "data-current": isCurrent || void 0,
2577
+ "data-focused": isFocused || void 0,
2578
+ className: cls,
2579
+ onClick: () => handleMonthSelect(i),
2580
+ children: getMonthName(i, locale)
2581
+ },
2582
+ i
2583
+ );
2584
+ })
2585
+ },
2586
+ rowIndex
2587
+ ))
2588
+ }
2589
+ )
2590
+ ] });
2591
+ }
2592
+
2593
+ // src/components/MonthPicker/index.ts
2594
+ var MonthPicker = Object.assign(MonthPickerRoot, {
2595
+ Input: DatePickerInput,
2596
+ Trigger: DatePickerTrigger,
2597
+ Popover: DatePickerPopover,
2598
+ Grid: MonthPickerGrid
2599
+ });
2600
+ function YearPickerRoot(props) {
2601
+ const displayFormat = props.displayFormat ?? "yyyy";
2602
+ return /* @__PURE__ */ jsx(DatePickerRoot, { ...props, displayFormat });
2603
+ }
2604
+ function YearPickerGrid({ classNames, ...props }) {
2605
+ const ctx = useDatePickerContext("YearPicker.Grid");
2606
+ const { adapter, viewMonth, value, displayTimezone, labels, disabled } = ctx;
2607
+ const currentYear = adapter.getYear(viewMonth);
2608
+ const decadeStart = currentYear - currentYear % 12;
2609
+ const valueYear = useMemo(() => {
2610
+ if (!value) return null;
2611
+ try {
2612
+ return Number(adapter.format(value, "yyyy", displayTimezone));
2613
+ } catch {
2614
+ return null;
2615
+ }
2616
+ }, [value, adapter, displayTimezone]);
2617
+ const [today, setToday] = useState(null);
2618
+ useEffect(() => {
2619
+ setToday(adapter.today(displayTimezone));
2620
+ }, [adapter, displayTimezone]);
2621
+ const todayYear = today !== null ? adapter.getYear(today) : -1;
2622
+ const yearDisabledFlags = useMemo(
2623
+ () => Array.from({ length: 12 }, (_, i) => {
2624
+ const year = decadeStart + i;
2625
+ const yearStart = new Date(Date.UTC(year, 0, 1)).toISOString();
2626
+ const yearEnd = new Date(Date.UTC(year, 11, 31, 23, 59, 59, 999)).toISOString();
2627
+ return isRangeFullyDisabled(yearStart, yearEnd, disabled, adapter);
2628
+ }),
2629
+ [decadeStart, disabled, adapter]
2630
+ );
2631
+ const navigateDecade = useCallback(
2632
+ (direction) => {
2633
+ ctx.setViewMonth(adapter.addYears(viewMonth, direction * 12));
2634
+ },
2635
+ [adapter, viewMonth, ctx]
2636
+ );
2637
+ const handleYearSelect = useCallback(
2638
+ (indexInDecade) => {
2639
+ if (yearDisabledFlags[indexInDecade]) return;
2640
+ const year = decadeStart + indexInDecade;
2641
+ const target = new Date(Date.UTC(year, 0, 1)).toISOString();
2642
+ ctx.selectDate(target);
2643
+ },
2644
+ [ctx, decadeStart, yearDisabledFlags]
2645
+ );
2646
+ const naturalIndex = valueYear !== null && valueYear >= decadeStart && valueYear <= decadeStart + 11 ? valueYear - decadeStart : currentYear - decadeStart;
2647
+ const firstEnabled = yearDisabledFlags.findIndex((d) => !d);
2648
+ const initialIndex = yearDisabledFlags[naturalIndex] ? firstEnabled === -1 ? naturalIndex : firstEnabled : naturalIndex;
2649
+ const { gridRef, focusedIndex, handleKeyDown } = useGridState({
2650
+ initialIndex,
2651
+ disabledFlags: yearDisabledFlags,
2652
+ onSelect: handleYearSelect,
2653
+ onPageUp: () => navigateDecade(-1),
2654
+ onPageDown: () => navigateDecade(1),
2655
+ onEscape: ctx.close
2656
+ });
2657
+ const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
2658
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
2659
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
2660
+ /* @__PURE__ */ jsx(
2661
+ "button",
2662
+ {
2663
+ type: "button",
2664
+ className: classNames?.navButton,
2665
+ onClick: () => navigateDecade(-1),
2666
+ "aria-label": labels.prevDecade,
2667
+ children: "<"
2668
+ }
2669
+ ),
2670
+ /* @__PURE__ */ jsx("span", { className: classNames?.title, children: rangeLabel }),
2671
+ /* @__PURE__ */ jsx(
2672
+ "button",
2673
+ {
2674
+ type: "button",
2675
+ className: classNames?.navButton,
2676
+ onClick: () => navigateDecade(1),
2677
+ "aria-label": labels.nextDecade,
2678
+ children: ">"
2679
+ }
2680
+ )
2681
+ ] }),
2682
+ /* @__PURE__ */ jsx(
2683
+ "div",
2684
+ {
2685
+ ref: gridRef,
2686
+ role: "grid",
2687
+ "aria-label": rangeLabel,
2688
+ className: classNames?.grid,
2689
+ onKeyDown: handleKeyDown,
2690
+ children: Array.from({ length: 4 }, (_, rowIndex) => /* @__PURE__ */ jsx(
2691
+ "div",
2692
+ {
2693
+ role: "row",
2694
+ className: classNames?.gridRow,
2695
+ style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
2696
+ children: Array.from({ length: 3 }, (_2, col) => {
2697
+ const i = rowIndex * 3 + col;
2698
+ const year = decadeStart + i;
2699
+ const isSelected = year === valueYear;
2700
+ const isCurrent = year === todayYear;
2701
+ const isFocused = i === focusedIndex;
2702
+ const isDisabled = yearDisabledFlags[i] ?? false;
2703
+ const cls = [
2704
+ classNames?.year,
2705
+ isSelected && classNames?.yearSelected,
2706
+ isCurrent && classNames?.yearCurrent,
2707
+ isDisabled && classNames?.yearDisabled
2708
+ ].filter(Boolean).join(" ") || void 0;
2709
+ return /* @__PURE__ */ jsx(
2710
+ "button",
2711
+ {
2712
+ type: "button",
2713
+ role: "gridcell",
2714
+ tabIndex: isFocused ? 0 : -1,
2715
+ disabled: isDisabled,
2716
+ "aria-selected": isSelected || void 0,
2717
+ "aria-disabled": isDisabled || void 0,
2718
+ "aria-current": isCurrent ? "date" : void 0,
2719
+ "data-selected": isSelected || void 0,
2720
+ "data-current": isCurrent || void 0,
2721
+ "data-focused": isFocused || void 0,
2722
+ className: cls,
2723
+ onClick: () => handleYearSelect(i),
2724
+ children: year
2725
+ },
2726
+ i
2727
+ );
2728
+ })
2729
+ },
2730
+ rowIndex
2731
+ ))
2732
+ }
2733
+ )
2734
+ ] });
2735
+ }
2736
+
2737
+ // src/components/YearPicker/index.ts
2738
+ var YearPicker = Object.assign(YearPickerRoot, {
2739
+ Input: DatePickerInput,
2740
+ Trigger: DatePickerTrigger,
2741
+ Popover: DatePickerPopover,
2742
+ Grid: YearPickerGrid
2743
+ });
2744
+ function WeekPickerRoot(props) {
2745
+ return /* @__PURE__ */ jsx(RangePickerRoot, { ...props });
2746
+ }
2747
+ function WeekPickerCalendar(props) {
2748
+ return /* @__PURE__ */ jsx(RangePickerCalendar, { ...props, selectionMode: "week" });
2749
+ }
2750
+
2751
+ // src/components/WeekPicker/index.ts
2752
+ var WeekPicker = Object.assign(WeekPickerRoot, {
2753
+ Input: RangePickerInput,
2754
+ Popover: RangePickerPopover,
2755
+ Calendar: WeekPickerCalendar
2756
+ });
2757
+ function useDatePicker(options = {}) {
2758
+ const {
2759
+ value: controlledValue,
2760
+ defaultValue,
2761
+ onChange,
2762
+ disabled = [],
2763
+ weekStartsOn = 0,
2764
+ adapter: adapterProp,
2765
+ displayTimezone
2766
+ } = options;
2767
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "useDatePicker");
2768
+ const pickerId = useId();
2769
+ const isControlled = useRef(controlledValue !== void 0).current;
2770
+ const [uncontrolledValue, setUncontrolledValue] = useState(
2771
+ defaultValue ?? null
2772
+ );
2773
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
2774
+ const [isOpen, setIsOpen] = useState(false);
2775
+ const [viewMonth, setViewMonth] = useState(
2776
+ currentValue ?? adapter.today(displayTimezone)
2777
+ );
2778
+ const [focusedDate, setFocusedDate] = useState(
2779
+ currentValue ?? adapter.today(displayTimezone)
2780
+ );
2781
+ const selectDate = useCallback(
2782
+ (iso) => {
2783
+ const normalized = iso && displayTimezone ? civilMidnightFromUtcDay(iso, displayTimezone) : iso;
2784
+ if (!isControlled) {
2785
+ setUncontrolledValue(normalized);
2786
+ }
2787
+ onChange?.(normalized);
2788
+ setIsOpen(false);
2789
+ },
2790
+ [isControlled, onChange, displayTimezone]
2791
+ );
2792
+ const open = useCallback(() => {
2793
+ setIsOpen(true);
2794
+ const target = currentValue ?? adapter.today(displayTimezone);
2795
+ setViewMonth(target);
2796
+ setFocusedDate(target);
2797
+ }, [currentValue, adapter, displayTimezone]);
2798
+ const close = useCallback(() => {
2799
+ setIsOpen(false);
2800
+ }, []);
2801
+ const toggle = useCallback(() => {
2802
+ if (isOpen) close();
2803
+ else open();
2804
+ }, [isOpen, open, close]);
2805
+ const previousMonth = useCallback(() => {
2806
+ const newMonth = adapter.addMonths(viewMonth, -1);
2807
+ setViewMonth(newMonth);
2808
+ setFocusedDate(adapter.startOfMonth(newMonth));
2809
+ }, [adapter, viewMonth]);
2810
+ const nextMonth = useCallback(() => {
2811
+ const newMonth = adapter.addMonths(viewMonth, 1);
2812
+ setViewMonth(newMonth);
2813
+ setFocusedDate(adapter.startOfMonth(newMonth));
2814
+ }, [adapter, viewMonth]);
2815
+ const calendar = getCalendarDays(viewMonth, adapter, {
2816
+ weekStartsOn,
2817
+ selected: currentValue,
2818
+ focusedDate,
2819
+ disabled,
2820
+ timezone: displayTimezone
2821
+ });
2822
+ return {
2823
+ value: currentValue,
2824
+ isOpen,
2825
+ open,
2826
+ close,
2827
+ toggle,
2828
+ selectDate,
2829
+ viewMonth,
2830
+ setViewMonth,
2831
+ calendar,
2832
+ focusedDate,
2833
+ setFocusedDate,
2834
+ previousMonth,
2835
+ nextMonth,
2836
+ pickerId,
2837
+ adapter
2838
+ };
2839
+ }
2840
+ var EMPTY_RANGE2 = { start: null, end: null };
2841
+ function useRangePicker(options = {}) {
2842
+ const {
2843
+ value: controlledValue,
2844
+ defaultValue,
2845
+ onChange,
2846
+ disabled = [],
2847
+ weekStartsOn = 0,
2848
+ adapter: adapterProp,
2849
+ displayTimezone
2850
+ } = options;
2851
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "useRangePicker");
2852
+ const pickerId = useId();
2853
+ const isControlled = useRef(controlledValue !== void 0).current;
2854
+ const [uncontrolledValue, setUncontrolledValue] = useState(
2855
+ defaultValue ?? EMPTY_RANGE2
2856
+ );
2857
+ const currentValue = isControlled ? controlledValue ?? EMPTY_RANGE2 : uncontrolledValue;
2858
+ const [isOpen, setIsOpen] = useState(false);
2859
+ const [selectingTarget, setSelectingTarget] = useState("start");
2860
+ const [hoverDate, setHoverDate] = useState(null);
2861
+ const [viewMonth, setViewMonth] = useState(
2862
+ currentValue.start ?? adapter.today(displayTimezone)
2863
+ );
2864
+ const [focusedDate, setFocusedDate] = useState(
2865
+ currentValue.start ?? adapter.today(displayTimezone)
2866
+ );
2867
+ const setRange = useCallback(
2868
+ (range) => {
2869
+ if (!isControlled) {
2870
+ setUncontrolledValue(range);
2871
+ }
2872
+ onChange?.(range);
2873
+ },
2874
+ [isControlled, onChange]
2875
+ );
2876
+ const selectDate = useCallback(
2877
+ (iso) => {
2878
+ const normalized = displayTimezone ? civilMidnightFromUtcDay(iso, displayTimezone) : iso;
2879
+ if (selectingTarget === "start") {
2880
+ setRange({ start: normalized, end: null });
2881
+ setSelectingTarget("end");
2882
+ setHoverDate(null);
2883
+ } else {
2884
+ const start = currentValue.start;
2885
+ if (!start) {
2886
+ setRange({ start: normalized, end: null });
2887
+ setSelectingTarget("end");
2888
+ return;
2889
+ }
2890
+ const newRange = adapter.isBefore(normalized, start) ? { start: normalized, end: start } : { start, end: normalized };
2891
+ setRange(newRange);
2892
+ setSelectingTarget("start");
2893
+ setHoverDate(null);
2894
+ setIsOpen(false);
2895
+ }
2896
+ },
2897
+ [selectingTarget, currentValue.start, adapter, setRange, displayTimezone]
2898
+ );
2899
+ const open = useCallback(() => {
2900
+ setIsOpen(true);
2901
+ const target = currentValue.start ?? adapter.today(displayTimezone);
2902
+ setViewMonth(target);
2903
+ setFocusedDate(target);
2904
+ if (currentValue.start && currentValue.end) {
2905
+ setSelectingTarget("start");
2906
+ }
2907
+ }, [currentValue, adapter, displayTimezone]);
2908
+ const close = useCallback(() => {
2909
+ setIsOpen(false);
2910
+ setHoverDate(null);
2911
+ }, []);
2912
+ const toggle = useCallback(() => {
2913
+ if (isOpen) close();
2914
+ else open();
2915
+ }, [isOpen, open, close]);
2916
+ const previousMonth = useCallback(() => {
2917
+ const newMonth = adapter.addMonths(viewMonth, -1);
2918
+ setViewMonth(newMonth);
2919
+ setFocusedDate(adapter.startOfMonth(newMonth));
2920
+ }, [adapter, viewMonth]);
2921
+ const nextMonth = useCallback(() => {
2922
+ const newMonth = adapter.addMonths(viewMonth, 1);
2923
+ setViewMonth(newMonth);
2924
+ setFocusedDate(adapter.startOfMonth(newMonth));
2925
+ }, [adapter, viewMonth]);
2926
+ const calendar = getCalendarDays(viewMonth, adapter, {
2927
+ weekStartsOn,
2928
+ focusedDate,
2929
+ disabled,
2930
+ range: currentValue,
2931
+ rangeHover: hoverDate,
2932
+ timezone: displayTimezone
2933
+ });
2934
+ return {
2935
+ value: currentValue,
2936
+ selectingTarget,
2937
+ selectDate,
2938
+ setRange,
2939
+ isOpen,
2940
+ open,
2941
+ close,
2942
+ toggle,
2943
+ hoverDate,
2944
+ setHoverDate,
2945
+ viewMonth,
2946
+ setViewMonth,
2947
+ calendar,
2948
+ focusedDate,
2949
+ setFocusedDate,
2950
+ previousMonth,
2951
+ nextMonth,
2952
+ pickerId,
2953
+ adapter
2954
+ };
2955
+ }
2956
+ function useTimePicker(options = {}) {
2957
+ const {
2958
+ value: controlledValue,
2959
+ defaultValue,
2960
+ onChange,
2961
+ format = "24h",
2962
+ step = 1,
2963
+ displayTimezone,
2964
+ adapter: adapterProp
2965
+ } = options;
2966
+ const adapter = resolveAdapter(adapterProp, getDefaultAdapter(), "useTimePicker");
2967
+ const pickerId = useId();
2968
+ const isControlled = useRef(controlledValue !== void 0).current;
2969
+ const [uncontrolledValue, setUncontrolledValue] = useState(
2970
+ defaultValue ?? null
2971
+ );
2972
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
2973
+ const baseIso = currentValue ?? adapter.today();
2974
+ const currentTime = useMemo(
2975
+ () => displayTimezone ? getTimeInTimezone(baseIso, displayTimezone) : getTime(baseIso),
2976
+ [baseIso, displayTimezone]
2977
+ );
2978
+ const setTime$1 = useCallback(
2979
+ (partial) => {
2980
+ const newIso = displayTimezone ? setTimeInTimezone(baseIso, partial, displayTimezone) : setTime(baseIso, partial);
2981
+ if (!isControlled) {
2982
+ setUncontrolledValue(newIso);
2983
+ }
2984
+ onChange?.(newIso);
2985
+ },
2986
+ [baseIso, isControlled, onChange, displayTimezone]
2987
+ );
2988
+ const period = format === "12h" ? to12Hour(currentTime.hours).period : null;
2989
+ const displayHour = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;
2990
+ const setHour = useCallback(
2991
+ (hour) => {
2992
+ const hours24 = format === "12h" && period ? to24Hour(hour, period) : hour;
2993
+ setTime$1({ hours: hours24 });
2994
+ },
2995
+ [format, period, setTime$1]
2996
+ );
2997
+ const setMinute = useCallback((minute) => setTime$1({ minutes: minute }), [setTime$1]);
2998
+ const setSecond = useCallback((second) => setTime$1({ seconds: second }), [setTime$1]);
2999
+ const setPeriod = useCallback(
3000
+ (newPeriod) => {
3001
+ if (format !== "12h") return;
3002
+ const newHours24 = to24Hour(displayHour, newPeriod);
3003
+ setTime$1({ hours: newHours24 });
3004
+ },
3005
+ [format, displayHour, setTime$1]
3006
+ );
3007
+ return {
3008
+ value: currentValue,
3009
+ currentTime,
3010
+ setTime: setTime$1,
3011
+ setHour,
3012
+ setMinute,
3013
+ setSecond,
3014
+ setPeriod,
3015
+ availableHours: generateHours(format),
3016
+ availableMinutes: generateMinutes(step),
3017
+ format,
3018
+ displayHour,
3019
+ period,
3020
+ pickerId
3021
+ };
3022
+ }
3023
+
3024
+ export { DatePicker, DateTimePicker, MonthPicker, RangePicker, TimePicker, WeekPicker, YearPicker, useDatePicker, useRangePicker, useTimePicker };
3025
+ //# sourceMappingURL=headless.js.map
3026
+ //# sourceMappingURL=headless.js.map