@kalyx/react 1.0.0-rc.9 → 1.0.1

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