@kalyx/react 0.2.0 → 0.2.2

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.
Files changed (125) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +67 -58
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +160 -162
  5. package/dist/index.d.ts +742 -17
  6. package/dist/index.js +2137 -11
  7. package/dist/index.js.map +1 -1
  8. package/package.json +2 -2
  9. package/dist/components/DatePicker/Calendar.d.ts +0 -23
  10. package/dist/components/DatePicker/Calendar.d.ts.map +0 -1
  11. package/dist/components/DatePicker/Calendar.js +0 -127
  12. package/dist/components/DatePicker/Calendar.js.map +0 -1
  13. package/dist/components/DatePicker/Input.d.ts +0 -7
  14. package/dist/components/DatePicker/Input.d.ts.map +0 -1
  15. package/dist/components/DatePicker/Input.js +0 -73
  16. package/dist/components/DatePicker/Input.js.map +0 -1
  17. package/dist/components/DatePicker/MonthGrid.d.ts +0 -34
  18. package/dist/components/DatePicker/MonthGrid.d.ts.map +0 -1
  19. package/dist/components/DatePicker/MonthGrid.js +0 -54
  20. package/dist/components/DatePicker/MonthGrid.js.map +0 -1
  21. package/dist/components/DatePicker/Popover.d.ts +0 -6
  22. package/dist/components/DatePicker/Popover.d.ts.map +0 -1
  23. package/dist/components/DatePicker/Popover.js +0 -72
  24. package/dist/components/DatePicker/Popover.js.map +0 -1
  25. package/dist/components/DatePicker/Root.d.ts +0 -46
  26. package/dist/components/DatePicker/Root.d.ts.map +0 -1
  27. package/dist/components/DatePicker/Root.js +0 -90
  28. package/dist/components/DatePicker/Root.js.map +0 -1
  29. package/dist/components/DatePicker/Trigger.d.ts +0 -6
  30. package/dist/components/DatePicker/Trigger.d.ts.map +0 -1
  31. package/dist/components/DatePicker/Trigger.js +0 -21
  32. package/dist/components/DatePicker/Trigger.js.map +0 -1
  33. package/dist/components/DatePicker/YearGrid.d.ts +0 -29
  34. package/dist/components/DatePicker/YearGrid.d.ts.map +0 -1
  35. package/dist/components/DatePicker/YearGrid.js +0 -53
  36. package/dist/components/DatePicker/YearGrid.js.map +0 -1
  37. package/dist/components/DatePicker/index.d.ts +0 -48
  38. package/dist/components/DatePicker/index.d.ts.map +0 -1
  39. package/dist/components/DatePicker/index.js +0 -42
  40. package/dist/components/DatePicker/index.js.map +0 -1
  41. package/dist/components/DateTimePicker/Input.d.ts +0 -12
  42. package/dist/components/DateTimePicker/Input.d.ts.map +0 -1
  43. package/dist/components/DateTimePicker/Input.js +0 -41
  44. package/dist/components/DateTimePicker/Input.js.map +0 -1
  45. package/dist/components/DateTimePicker/Root.d.ts +0 -60
  46. package/dist/components/DateTimePicker/Root.d.ts.map +0 -1
  47. package/dist/components/DateTimePicker/Root.js +0 -140
  48. package/dist/components/DateTimePicker/Root.js.map +0 -1
  49. package/dist/components/DateTimePicker/index.d.ts +0 -55
  50. package/dist/components/DateTimePicker/index.d.ts.map +0 -1
  51. package/dist/components/DateTimePicker/index.js +0 -55
  52. package/dist/components/DateTimePicker/index.js.map +0 -1
  53. package/dist/components/RangePicker/Calendar.d.ts +0 -24
  54. package/dist/components/RangePicker/Calendar.d.ts.map +0 -1
  55. package/dist/components/RangePicker/Calendar.js +0 -134
  56. package/dist/components/RangePicker/Calendar.js.map +0 -1
  57. package/dist/components/RangePicker/Input.d.ts +0 -14
  58. package/dist/components/RangePicker/Input.d.ts.map +0 -1
  59. package/dist/components/RangePicker/Input.js +0 -38
  60. package/dist/components/RangePicker/Input.js.map +0 -1
  61. package/dist/components/RangePicker/Popover.d.ts +0 -6
  62. package/dist/components/RangePicker/Popover.d.ts.map +0 -1
  63. package/dist/components/RangePicker/Popover.js +0 -71
  64. package/dist/components/RangePicker/Popover.js.map +0 -1
  65. package/dist/components/RangePicker/Presets.d.ts +0 -49
  66. package/dist/components/RangePicker/Presets.d.ts.map +0 -1
  67. package/dist/components/RangePicker/Presets.js +0 -117
  68. package/dist/components/RangePicker/Presets.js.map +0 -1
  69. package/dist/components/RangePicker/Root.d.ts +0 -40
  70. package/dist/components/RangePicker/Root.d.ts.map +0 -1
  71. package/dist/components/RangePicker/Root.js +0 -138
  72. package/dist/components/RangePicker/Root.js.map +0 -1
  73. package/dist/components/RangePicker/index.d.ts +0 -48
  74. package/dist/components/RangePicker/index.d.ts.map +0 -1
  75. package/dist/components/RangePicker/index.js +0 -43
  76. package/dist/components/RangePicker/index.js.map +0 -1
  77. package/dist/components/TimePicker/AmPmToggle.d.ts +0 -15
  78. package/dist/components/TimePicker/AmPmToggle.d.ts.map +0 -1
  79. package/dist/components/TimePicker/AmPmToggle.js +0 -29
  80. package/dist/components/TimePicker/AmPmToggle.js.map +0 -1
  81. package/dist/components/TimePicker/HourList.d.ts +0 -18
  82. package/dist/components/TimePicker/HourList.d.ts.map +0 -1
  83. package/dist/components/TimePicker/HourList.js +0 -71
  84. package/dist/components/TimePicker/HourList.js.map +0 -1
  85. package/dist/components/TimePicker/Input.d.ts +0 -9
  86. package/dist/components/TimePicker/Input.d.ts.map +0 -1
  87. package/dist/components/TimePicker/Input.js +0 -37
  88. package/dist/components/TimePicker/Input.js.map +0 -1
  89. package/dist/components/TimePicker/MinuteList.d.ts +0 -15
  90. package/dist/components/TimePicker/MinuteList.d.ts.map +0 -1
  91. package/dist/components/TimePicker/MinuteList.js +0 -62
  92. package/dist/components/TimePicker/MinuteList.js.map +0 -1
  93. package/dist/components/TimePicker/Root.d.ts +0 -38
  94. package/dist/components/TimePicker/Root.d.ts.map +0 -1
  95. package/dist/components/TimePicker/Root.js +0 -40
  96. package/dist/components/TimePicker/Root.js.map +0 -1
  97. package/dist/components/TimePicker/index.d.ts +0 -32
  98. package/dist/components/TimePicker/index.d.ts.map +0 -1
  99. package/dist/components/TimePicker/index.js +0 -27
  100. package/dist/components/TimePicker/index.js.map +0 -1
  101. package/dist/context/DatePickerContext.d.ts +0 -49
  102. package/dist/context/DatePickerContext.d.ts.map +0 -1
  103. package/dist/context/DatePickerContext.js +0 -18
  104. package/dist/context/DatePickerContext.js.map +0 -1
  105. package/dist/context/RangePickerContext.d.ts +0 -53
  106. package/dist/context/RangePickerContext.d.ts.map +0 -1
  107. package/dist/context/RangePickerContext.js +0 -18
  108. package/dist/context/RangePickerContext.js.map +0 -1
  109. package/dist/context/TimePickerContext.d.ts +0 -29
  110. package/dist/context/TimePickerContext.d.ts.map +0 -1
  111. package/dist/context/TimePickerContext.js +0 -18
  112. package/dist/context/TimePickerContext.js.map +0 -1
  113. package/dist/hooks/useDatePicker.d.ts +0 -63
  114. package/dist/hooks/useDatePicker.d.ts.map +0 -1
  115. package/dist/hooks/useDatePicker.js +0 -82
  116. package/dist/hooks/useDatePicker.js.map +0 -1
  117. package/dist/hooks/useRangePicker.d.ts +0 -67
  118. package/dist/hooks/useRangePicker.d.ts.map +0 -1
  119. package/dist/hooks/useRangePicker.js +0 -116
  120. package/dist/hooks/useRangePicker.js.map +0 -1
  121. package/dist/hooks/useTimePicker.d.ts +0 -63
  122. package/dist/hooks/useTimePicker.d.ts.map +0 -1
  123. package/dist/hooks/useTimePicker.js +0 -69
  124. package/dist/hooks/useTimePicker.js.map +0 -1
  125. package/dist/index.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,13 +1,2139 @@
1
- // @kalyx/react 공개 API 진입점
2
- // 컴포넌트
3
- export { DatePicker } from './components/DatePicker/index.js';
4
- export { RangePicker } from './components/RangePicker/index.js';
5
- export { TimePicker } from './components/TimePicker/index.js';
6
- export { DateTimePicker } from './components/DateTimePicker/index.js';
7
- // 훅
8
- export { useDatePicker } from './hooks/useDatePicker.js';
9
- export { useRangePicker } from './hooks/useRangePicker.js';
10
- export { useTimePicker } from './hooks/useTimePicker.js';
11
- // @kalyx/core 재export (사용자 편의)
1
+ import { createContext, forwardRef, useState, useCallback, useContext, useId, useRef, useMemo, useEffect } from 'react';
2
+ import { parseInputValue, formatTimeString, parseTimeString, getTime, DateFnsAdapter, getMonthName, getWeekdayNames, getCalendarDays, formatMonthYear, formatFullDate, isDateDisabled, setTime, to12Hour, to24Hour, generateMinutes, generateHours } from '@kalyx/core';
12
3
  export { DateFnsAdapter } from '@kalyx/core';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+ import { useFloating, autoUpdate, offset, flip, shift } from '@floating-ui/react';
6
+
7
+ // src/components/DatePicker/Root.tsx
8
+ var DatePickerContext = createContext(null);
9
+ function useDatePickerContext(componentName) {
10
+ const context = useContext(DatePickerContext);
11
+ if (!context) {
12
+ throw new Error(
13
+ `[${componentName}] DatePicker.Root \uB0B4\uBD80\uC5D0\uC11C \uC0AC\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4.
14
+
15
+ \uC62C\uBC14\uB978 \uC0AC\uC6A9\uBC95:
16
+ <DatePicker>
17
+ <DatePicker.${componentName.replace("DatePicker.", "")} />
18
+ </DatePicker>`
19
+ );
20
+ }
21
+ return context;
22
+ }
23
+ function DatePickerRoot({
24
+ value: controlledValue,
25
+ defaultValue,
26
+ onChange,
27
+ disabled = false,
28
+ readOnly = false,
29
+ weekStartsOn = 0,
30
+ displayFormat = "yyyy-MM-dd",
31
+ locale = "en-US",
32
+ adapter = DateFnsAdapter,
33
+ children
34
+ }) {
35
+ const pickerId = useId();
36
+ const isControlled = useRef(controlledValue !== void 0).current;
37
+ const referenceRef = useRef(null);
38
+ const [uncontrolledValue, setUncontrolledValue] = useState(
39
+ defaultValue ?? null
40
+ );
41
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
42
+ const [isOpen, setIsOpen] = useState(false);
43
+ const [viewMonth, setViewMonth] = useState(
44
+ currentValue ?? adapter.today()
45
+ );
46
+ const [focusedDate, setFocusedDate] = useState(
47
+ currentValue ?? adapter.today()
48
+ );
49
+ const isDisabled = typeof disabled === "boolean" ? disabled : false;
50
+ const disabledRules = Array.isArray(disabled) ? disabled : [];
51
+ const selectDate = useCallback(
52
+ (iso) => {
53
+ if (isDisabled || readOnly) return;
54
+ if (!isControlled) {
55
+ setUncontrolledValue(iso);
56
+ }
57
+ onChange?.(iso);
58
+ setIsOpen(false);
59
+ },
60
+ [isControlled, isDisabled, readOnly, onChange]
61
+ );
62
+ const open = useCallback(() => {
63
+ if (isDisabled || readOnly) return;
64
+ setIsOpen(true);
65
+ const target = currentValue ?? adapter.today();
66
+ setViewMonth(target);
67
+ setFocusedDate(target);
68
+ }, [isDisabled, readOnly, currentValue, adapter]);
69
+ const close = useCallback(() => {
70
+ setIsOpen(false);
71
+ }, []);
72
+ const toggle = useCallback(() => {
73
+ if (isOpen) {
74
+ close();
75
+ } else {
76
+ open();
77
+ }
78
+ }, [isOpen, open, close]);
79
+ const contextValue = useMemo(
80
+ () => ({
81
+ referenceRef,
82
+ value: currentValue,
83
+ selectDate,
84
+ isOpen,
85
+ open,
86
+ close,
87
+ toggle,
88
+ viewMonth,
89
+ setViewMonth,
90
+ focusedDate,
91
+ setFocusedDate,
92
+ adapter,
93
+ disabled: disabledRules,
94
+ weekStartsOn,
95
+ displayFormat,
96
+ locale,
97
+ isDisabled,
98
+ isReadOnly: readOnly,
99
+ pickerId
100
+ }),
101
+ [
102
+ currentValue,
103
+ selectDate,
104
+ isOpen,
105
+ open,
106
+ close,
107
+ toggle,
108
+ viewMonth,
109
+ focusedDate,
110
+ adapter,
111
+ disabledRules,
112
+ weekStartsOn,
113
+ displayFormat,
114
+ locale,
115
+ isDisabled,
116
+ readOnly,
117
+ pickerId
118
+ ]
119
+ );
120
+ return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: contextValue, children });
121
+ }
122
+ var DatePickerInput = forwardRef(
123
+ function DatePickerInput2({ format: formatProp, onFocus, onBlur, onKeyDown, ...props }, ref) {
124
+ const ctx = useDatePickerContext("DatePicker.Input");
125
+ const displayFormat = formatProp ?? ctx.displayFormat;
126
+ const [inputText, setInputText] = useState(null);
127
+ const displayValue = inputText !== null ? inputText : ctx.value ? ctx.adapter.format(ctx.value, displayFormat) : "";
128
+ const handleFocus = useCallback(
129
+ (e) => {
130
+ ctx.open();
131
+ onFocus?.(e);
132
+ },
133
+ [ctx, onFocus]
134
+ );
135
+ const handleBlur = useCallback(
136
+ (e) => {
137
+ if (inputText !== null) {
138
+ const parsed = parseInputValue(inputText, displayFormat, ctx.adapter);
139
+ if (parsed) {
140
+ ctx.selectDate(parsed);
141
+ }
142
+ setInputText(null);
143
+ }
144
+ onBlur?.(e);
145
+ },
146
+ [inputText, displayFormat, ctx, onBlur]
147
+ );
148
+ const handleChange = useCallback(
149
+ (e) => {
150
+ const text = e.target.value;
151
+ setInputText(text);
152
+ if (!text) {
153
+ ctx.selectDate(null);
154
+ setInputText(null);
155
+ return;
156
+ }
157
+ const parsed = parseInputValue(text, displayFormat, ctx.adapter);
158
+ if (parsed) {
159
+ ctx.selectDate(parsed);
160
+ setInputText(null);
161
+ }
162
+ },
163
+ [displayFormat, ctx]
164
+ );
165
+ const handleKeyDown = useCallback(
166
+ (e) => {
167
+ if (e.key === "Escape") {
168
+ ctx.close();
169
+ } else if (e.key === "Enter") {
170
+ if (inputText !== null) {
171
+ const parsed = parseInputValue(inputText, displayFormat, ctx.adapter);
172
+ if (parsed) {
173
+ ctx.selectDate(parsed);
174
+ setInputText(null);
175
+ }
176
+ }
177
+ } else if (e.key === "ArrowDown" && !ctx.isOpen) {
178
+ e.preventDefault();
179
+ ctx.open();
180
+ }
181
+ onKeyDown?.(e);
182
+ },
183
+ [ctx, inputText, displayFormat, onKeyDown]
184
+ );
185
+ const calendarId = `${ctx.pickerId}-calendar`;
186
+ return /* @__PURE__ */ jsx(
187
+ "input",
188
+ {
189
+ ref: (node) => {
190
+ ctx.referenceRef.current = node;
191
+ if (typeof ref === "function") ref(node);
192
+ else if (ref) ref.current = node;
193
+ },
194
+ type: "text",
195
+ role: "combobox",
196
+ "aria-expanded": ctx.isOpen,
197
+ "aria-haspopup": "dialog",
198
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
199
+ "aria-autocomplete": "none",
200
+ autoComplete: "off",
201
+ value: displayValue,
202
+ disabled: ctx.isDisabled || props.disabled,
203
+ readOnly: ctx.isReadOnly,
204
+ onChange: handleChange,
205
+ onFocus: handleFocus,
206
+ onBlur: handleBlur,
207
+ onKeyDown: handleKeyDown,
208
+ ...props
209
+ }
210
+ );
211
+ }
212
+ );
213
+ var DatePickerTrigger = forwardRef(
214
+ function DatePickerTrigger2({ onClick, children, ...props }, ref) {
215
+ const ctx = useDatePickerContext("DatePicker.Trigger");
216
+ const handleClick = useCallback(
217
+ (e) => {
218
+ ctx.toggle();
219
+ onClick?.(e);
220
+ },
221
+ [ctx, onClick]
222
+ );
223
+ const calendarId = `${ctx.pickerId}-calendar`;
224
+ return /* @__PURE__ */ jsx(
225
+ "button",
226
+ {
227
+ ref: (node) => {
228
+ if (!ctx.referenceRef.current) ctx.referenceRef.current = node;
229
+ if (typeof ref === "function") ref(node);
230
+ else if (ref) ref.current = node;
231
+ },
232
+ type: "button",
233
+ tabIndex: 0,
234
+ "aria-label": ctx.isOpen ? "\uCE98\uB9B0\uB354 \uB2EB\uAE30" : "\uCE98\uB9B0\uB354 \uC5F4\uAE30",
235
+ "aria-expanded": ctx.isOpen,
236
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
237
+ disabled: ctx.isDisabled || props.disabled,
238
+ onClick: handleClick,
239
+ ...props,
240
+ children: children ?? /* @__PURE__ */ jsx(
241
+ "svg",
242
+ {
243
+ "aria-hidden": "true",
244
+ focusable: "false",
245
+ width: "16",
246
+ height: "16",
247
+ viewBox: "0 0 16 16",
248
+ fill: "none",
249
+ xmlns: "http://www.w3.org/2000/svg",
250
+ children: /* @__PURE__ */ jsx(
251
+ "path",
252
+ {
253
+ d: "M5 1v2M11 1v2M1 6h14M3 3h10a2 2 0 012 2v8a2 2 0 01-2 2H3a2 2 0 01-2-2V5a2 2 0 012-2z",
254
+ stroke: "currentColor",
255
+ strokeWidth: "1.5",
256
+ strokeLinecap: "round",
257
+ strokeLinejoin: "round"
258
+ }
259
+ )
260
+ }
261
+ )
262
+ }
263
+ );
264
+ }
265
+ );
266
+ function DatePickerPopover({ children, ...props }) {
267
+ const ctx = useDatePickerContext("DatePicker.Popover");
268
+ const calendarId = `${ctx.pickerId}-calendar`;
269
+ const floatingRef = useRef(null);
270
+ const { refs, floatingStyles } = useFloating({
271
+ open: ctx.isOpen,
272
+ placement: "bottom-start",
273
+ middleware: [offset(4), flip(), shift({ padding: 8 })],
274
+ whileElementsMounted: autoUpdate
275
+ });
276
+ useEffect(() => {
277
+ if (ctx.referenceRef.current) {
278
+ refs.setReference(ctx.referenceRef.current);
279
+ }
280
+ }, [ctx.referenceRef, refs, ctx.isOpen]);
281
+ const previousFocusRef = useRef(null);
282
+ useEffect(() => {
283
+ if (ctx.isOpen) {
284
+ previousFocusRef.current = document.activeElement;
285
+ } else if (previousFocusRef.current) {
286
+ previousFocusRef.current.focus();
287
+ previousFocusRef.current = null;
288
+ }
289
+ }, [ctx.isOpen]);
290
+ useEffect(() => {
291
+ if (!ctx.isOpen) return;
292
+ function handleClickOutside(e) {
293
+ const floating = floatingRef.current;
294
+ const reference = ctx.referenceRef.current;
295
+ const target = e.target;
296
+ if (floating && !floating.contains(target) && (!reference || !reference.contains(target))) {
297
+ ctx.close();
298
+ }
299
+ }
300
+ const timer = setTimeout(() => {
301
+ document.addEventListener("mousedown", handleClickOutside);
302
+ }, 0);
303
+ return () => {
304
+ clearTimeout(timer);
305
+ document.removeEventListener("mousedown", handleClickOutside);
306
+ };
307
+ }, [ctx.isOpen, ctx]);
308
+ useEffect(() => {
309
+ if (!ctx.isOpen) return;
310
+ function handleKeyDown(e) {
311
+ if (e.key === "Escape") {
312
+ ctx.close();
313
+ }
314
+ }
315
+ document.addEventListener("keydown", handleKeyDown);
316
+ return () => document.removeEventListener("keydown", handleKeyDown);
317
+ }, [ctx.isOpen, ctx]);
318
+ if (!ctx.isOpen) return null;
319
+ return /* @__PURE__ */ jsx(
320
+ "div",
321
+ {
322
+ ref: (node) => {
323
+ floatingRef.current = node;
324
+ refs.setFloating(node);
325
+ },
326
+ id: calendarId,
327
+ role: "dialog",
328
+ "aria-label": "\uB0A0\uC9DC \uC120\uD0DD",
329
+ "aria-modal": "false",
330
+ style: floatingStyles,
331
+ ...props,
332
+ children
333
+ }
334
+ );
335
+ }
336
+ var srOnly = {
337
+ position: "absolute",
338
+ width: "1px",
339
+ height: "1px",
340
+ padding: 0,
341
+ margin: "-1px",
342
+ overflow: "hidden",
343
+ clip: "rect(0, 0, 0, 0)",
344
+ whiteSpace: "nowrap",
345
+ border: 0
346
+ };
347
+ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
348
+ const ctx = useDatePickerContext("DatePicker.Calendar");
349
+ const gridRef = useRef(null);
350
+ const [announcement, setAnnouncement] = useState("");
351
+ const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale } = ctx;
352
+ const weekdays = getWeekdayNames(locale, weekStartsOn);
353
+ const weeks = getCalendarDays(viewMonth, adapter, {
354
+ weekStartsOn,
355
+ selected: ctx.value,
356
+ focusedDate,
357
+ disabled
358
+ });
359
+ const year = adapter.getYear(viewMonth);
360
+ const month = adapter.getMonth(viewMonth);
361
+ const title = formatMonthYear(year, month, locale);
362
+ useEffect(() => {
363
+ if (!ctx.isOpen || !gridRef.current) return;
364
+ const focusedButton = gridRef.current.querySelector(
365
+ '[data-focused="true"]'
366
+ );
367
+ focusedButton?.focus();
368
+ }, [focusedDate, ctx.isOpen]);
369
+ const navigateMonth = useCallback(
370
+ (direction) => {
371
+ const newMonth = adapter.addMonths(viewMonth, direction);
372
+ ctx.setViewMonth(newMonth);
373
+ ctx.setFocusedDate(adapter.startOfMonth(newMonth));
374
+ const y = adapter.getYear(newMonth);
375
+ const m = adapter.getMonth(newMonth);
376
+ setAnnouncement(formatMonthYear(y, m, locale));
377
+ },
378
+ [adapter, viewMonth, ctx, locale]
379
+ );
380
+ const handleDayClick = useCallback(
381
+ (day) => {
382
+ if (day.isDisabled) return;
383
+ ctx.selectDate(day.isoString);
384
+ setAnnouncement(formatFullDate(day.isoString, locale));
385
+ },
386
+ [ctx, locale]
387
+ );
388
+ const handleKeyDown = useCallback(
389
+ (e) => {
390
+ let newFocused = null;
391
+ switch (e.key) {
392
+ case "ArrowLeft":
393
+ newFocused = adapter.addDays(focusedDate, -1);
394
+ break;
395
+ case "ArrowRight":
396
+ newFocused = adapter.addDays(focusedDate, 1);
397
+ break;
398
+ case "ArrowUp":
399
+ newFocused = adapter.addDays(focusedDate, -7);
400
+ break;
401
+ case "ArrowDown":
402
+ newFocused = adapter.addDays(focusedDate, 7);
403
+ break;
404
+ case "PageUp":
405
+ if (e.shiftKey) {
406
+ newFocused = adapter.addYears(focusedDate, -1);
407
+ } else {
408
+ newFocused = adapter.addMonths(focusedDate, -1);
409
+ }
410
+ break;
411
+ case "PageDown":
412
+ if (e.shiftKey) {
413
+ newFocused = adapter.addYears(focusedDate, 1);
414
+ } else {
415
+ newFocused = adapter.addMonths(focusedDate, 1);
416
+ }
417
+ break;
418
+ case "Home":
419
+ newFocused = adapter.startOfWeek(focusedDate, weekStartsOn);
420
+ break;
421
+ case "End":
422
+ newFocused = adapter.endOfWeek(focusedDate, weekStartsOn);
423
+ newFocused = adapter.startOfDay(newFocused);
424
+ break;
425
+ case "Enter":
426
+ case " ":
427
+ e.preventDefault();
428
+ if (!isDateDisabled(focusedDate, disabled, adapter)) {
429
+ ctx.selectDate(focusedDate);
430
+ }
431
+ return;
432
+ case "Escape":
433
+ ctx.close();
434
+ return;
435
+ default:
436
+ return;
437
+ }
438
+ if (newFocused) {
439
+ e.preventDefault();
440
+ ctx.setFocusedDate(newFocused);
441
+ if (!adapter.isSameMonth(newFocused, viewMonth)) {
442
+ ctx.setViewMonth(newFocused);
443
+ }
444
+ }
445
+ },
446
+ [adapter, focusedDate, viewMonth, weekStartsOn, disabled, ctx]
447
+ );
448
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
449
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
450
+ /* @__PURE__ */ jsx(
451
+ "button",
452
+ {
453
+ type: "button",
454
+ className: classNames?.navButton,
455
+ onClick: () => navigateMonth(-1),
456
+ "aria-label": "\uC774\uC804 \uB2EC",
457
+ children: "<"
458
+ }
459
+ ),
460
+ onTitleClick ? /* @__PURE__ */ jsx(
461
+ "button",
462
+ {
463
+ type: "button",
464
+ className: classNames?.title,
465
+ onClick: onTitleClick,
466
+ "aria-live": "polite",
467
+ children: title
468
+ }
469
+ ) : /* @__PURE__ */ jsx("span", { className: classNames?.title, "aria-live": "polite", children: title }),
470
+ /* @__PURE__ */ jsx(
471
+ "button",
472
+ {
473
+ type: "button",
474
+ className: classNames?.navButton,
475
+ onClick: () => navigateMonth(1),
476
+ "aria-label": "\uB2E4\uC74C \uB2EC",
477
+ children: ">"
478
+ }
479
+ )
480
+ ] }),
481
+ /* @__PURE__ */ jsxs(
482
+ "table",
483
+ {
484
+ ref: gridRef,
485
+ role: "grid",
486
+ "aria-label": title,
487
+ className: classNames?.grid,
488
+ onKeyDown: handleKeyDown,
489
+ children: [
490
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { role: "row", children: weekdays.map((day) => /* @__PURE__ */ jsx(
491
+ "th",
492
+ {
493
+ role: "columnheader",
494
+ abbr: day.full,
495
+ scope: "col",
496
+ className: classNames?.weekdayHeader,
497
+ children: day.short
498
+ },
499
+ day.short
500
+ )) }) }),
501
+ /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsx("tr", { role: "row", className: classNames?.gridRow, children: week.map((day) => {
502
+ const dayClasses = [
503
+ classNames?.day,
504
+ day.isSelected && classNames?.daySelected,
505
+ day.isToday && classNames?.dayToday,
506
+ day.isDisabled && classNames?.dayDisabled,
507
+ !day.isCurrentMonth && classNames?.dayOutsideMonth
508
+ ].filter(Boolean).join(" ") || void 0;
509
+ return /* @__PURE__ */ jsx(
510
+ "td",
511
+ {
512
+ role: "gridcell",
513
+ "aria-selected": day.isSelected || void 0,
514
+ "aria-disabled": day.isDisabled || void 0,
515
+ "aria-current": day.isToday ? "date" : void 0,
516
+ className: classNames?.gridCell,
517
+ children: /* @__PURE__ */ jsx(
518
+ "button",
519
+ {
520
+ type: "button",
521
+ tabIndex: day.isFocused ? 0 : -1,
522
+ disabled: day.isDisabled,
523
+ "data-focused": day.isFocused || void 0,
524
+ "data-selected": day.isSelected || void 0,
525
+ "data-today": day.isToday || void 0,
526
+ "data-outside-month": !day.isCurrentMonth || void 0,
527
+ className: dayClasses,
528
+ onClick: () => handleDayClick(day),
529
+ "aria-label": formatFullDate(day.isoString, locale),
530
+ children: day.dayNumber
531
+ }
532
+ )
533
+ },
534
+ day.isoString
535
+ );
536
+ }) }, weekIndex)) })
537
+ ]
538
+ }
539
+ ),
540
+ /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: srOnly, children: announcement })
541
+ ] });
542
+ }
543
+ function DatePickerMonthGrid({
544
+ classNames,
545
+ onSelect,
546
+ onTitleClick,
547
+ ...props
548
+ }) {
549
+ const ctx = useDatePickerContext("DatePicker.MonthGrid");
550
+ const { adapter, viewMonth, locale } = ctx;
551
+ const currentYear = adapter.getYear(viewMonth);
552
+ const currentMonth = adapter.getMonth(viewMonth);
553
+ const todayMonth = adapter.getMonth(adapter.today());
554
+ const todayYear = adapter.getYear(adapter.today());
555
+ const navigateYear = useCallback(
556
+ (direction) => {
557
+ const newDate = adapter.addYears(viewMonth, direction);
558
+ ctx.setViewMonth(newDate);
559
+ },
560
+ [adapter, viewMonth, ctx]
561
+ );
562
+ const handleMonthSelect = useCallback(
563
+ (monthIndex) => {
564
+ const target = new Date(Date.UTC(currentYear, monthIndex, 1)).toISOString();
565
+ ctx.setViewMonth(target);
566
+ ctx.setFocusedDate(target);
567
+ onSelect?.();
568
+ },
569
+ [currentYear, ctx, onSelect]
570
+ );
571
+ const months = Array.from({ length: 12 }, (_, i) => ({
572
+ index: i,
573
+ name: getMonthName(i, locale),
574
+ isSelected: i === currentMonth,
575
+ isCurrent: i === todayMonth && currentYear === todayYear
576
+ }));
577
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
578
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
579
+ /* @__PURE__ */ jsx(
580
+ "button",
581
+ {
582
+ type: "button",
583
+ className: classNames?.navButton,
584
+ onClick: () => navigateYear(-1),
585
+ "aria-label": "\uC774\uC804 \uB144",
586
+ children: "<"
587
+ }
588
+ ),
589
+ onTitleClick ? /* @__PURE__ */ jsx(
590
+ "button",
591
+ {
592
+ type: "button",
593
+ className: classNames?.title,
594
+ onClick: onTitleClick,
595
+ children: currentYear
596
+ }
597
+ ) : /* @__PURE__ */ jsx("span", { className: classNames?.title, children: currentYear }),
598
+ /* @__PURE__ */ jsx(
599
+ "button",
600
+ {
601
+ type: "button",
602
+ className: classNames?.navButton,
603
+ onClick: () => navigateYear(1),
604
+ "aria-label": "\uB2E4\uC74C \uB144",
605
+ children: ">"
606
+ }
607
+ )
608
+ ] }),
609
+ /* @__PURE__ */ jsx(
610
+ "div",
611
+ {
612
+ role: "grid",
613
+ "aria-label": `${currentYear} months`,
614
+ className: classNames?.grid,
615
+ style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
616
+ children: months.map((m) => {
617
+ const monthClass = [
618
+ classNames?.month,
619
+ m.isSelected && classNames?.monthSelected,
620
+ m.isCurrent && classNames?.monthCurrent
621
+ ].filter(Boolean).join(" ") || void 0;
622
+ return /* @__PURE__ */ jsx(
623
+ "button",
624
+ {
625
+ type: "button",
626
+ role: "gridcell",
627
+ "aria-selected": m.isSelected || void 0,
628
+ "aria-current": m.isCurrent ? "date" : void 0,
629
+ "data-selected": m.isSelected || void 0,
630
+ "data-current": m.isCurrent || void 0,
631
+ className: monthClass,
632
+ onClick: () => handleMonthSelect(m.index),
633
+ children: m.name
634
+ },
635
+ m.index
636
+ );
637
+ })
638
+ }
639
+ )
640
+ ] });
641
+ }
642
+ function DatePickerYearGrid({
643
+ classNames,
644
+ onSelect,
645
+ ...props
646
+ }) {
647
+ const ctx = useDatePickerContext("DatePicker.YearGrid");
648
+ const { adapter, viewMonth } = ctx;
649
+ const currentYear = adapter.getYear(viewMonth);
650
+ const todayYear = adapter.getYear(adapter.today());
651
+ const decadeStart = currentYear - currentYear % 12;
652
+ const navigateDecade = useCallback(
653
+ (direction) => {
654
+ const newDate = adapter.addYears(viewMonth, direction * 12);
655
+ ctx.setViewMonth(newDate);
656
+ },
657
+ [adapter, viewMonth, ctx]
658
+ );
659
+ const handleYearSelect = useCallback(
660
+ (year) => {
661
+ const currentMonth = adapter.getMonth(viewMonth);
662
+ const target = new Date(Date.UTC(year, currentMonth, 1)).toISOString();
663
+ ctx.setViewMonth(target);
664
+ ctx.setFocusedDate(target);
665
+ onSelect?.();
666
+ },
667
+ [adapter, viewMonth, ctx, onSelect]
668
+ );
669
+ const years = useMemo(
670
+ () => Array.from({ length: 12 }, (_, i) => {
671
+ const year = decadeStart + i;
672
+ return {
673
+ value: year,
674
+ isSelected: year === currentYear,
675
+ isCurrent: year === todayYear
676
+ };
677
+ }),
678
+ [decadeStart, currentYear, todayYear]
679
+ );
680
+ const rangeLabel = `${decadeStart}\u2013${decadeStart + 11}`;
681
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, children: [
682
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
683
+ /* @__PURE__ */ jsx(
684
+ "button",
685
+ {
686
+ type: "button",
687
+ className: classNames?.navButton,
688
+ onClick: () => navigateDecade(-1),
689
+ "aria-label": "\uC774\uC804 12\uB144",
690
+ children: "<"
691
+ }
692
+ ),
693
+ /* @__PURE__ */ jsx("span", { className: classNames?.title, children: rangeLabel }),
694
+ /* @__PURE__ */ jsx(
695
+ "button",
696
+ {
697
+ type: "button",
698
+ className: classNames?.navButton,
699
+ onClick: () => navigateDecade(1),
700
+ "aria-label": "\uB2E4\uC74C 12\uB144",
701
+ children: ">"
702
+ }
703
+ )
704
+ ] }),
705
+ /* @__PURE__ */ jsx(
706
+ "div",
707
+ {
708
+ role: "grid",
709
+ "aria-label": rangeLabel,
710
+ className: classNames?.grid,
711
+ style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)" },
712
+ children: years.map((y) => {
713
+ const yearClass = [
714
+ classNames?.year,
715
+ y.isSelected && classNames?.yearSelected,
716
+ y.isCurrent && classNames?.yearCurrent
717
+ ].filter(Boolean).join(" ") || void 0;
718
+ return /* @__PURE__ */ jsx(
719
+ "button",
720
+ {
721
+ type: "button",
722
+ role: "gridcell",
723
+ "aria-selected": y.isSelected || void 0,
724
+ "aria-current": y.isCurrent ? "date" : void 0,
725
+ "data-selected": y.isSelected || void 0,
726
+ "data-current": y.isCurrent || void 0,
727
+ className: yearClass,
728
+ onClick: () => handleYearSelect(y.value),
729
+ children: y.value
730
+ },
731
+ y.value
732
+ );
733
+ })
734
+ }
735
+ )
736
+ ] });
737
+ }
738
+
739
+ // src/components/DatePicker/index.ts
740
+ var DatePicker = Object.assign(DatePickerRoot, {
741
+ Input: DatePickerInput,
742
+ Trigger: DatePickerTrigger,
743
+ Popover: DatePickerPopover,
744
+ Calendar: DatePickerCalendar,
745
+ MonthGrid: DatePickerMonthGrid,
746
+ YearGrid: DatePickerYearGrid
747
+ });
748
+ var RangePickerContext = createContext(null);
749
+ function useRangePickerContext(componentName) {
750
+ const context = useContext(RangePickerContext);
751
+ if (!context) {
752
+ throw new Error(
753
+ `[${componentName}] RangePicker.Root \uB0B4\uBD80\uC5D0\uC11C \uC0AC\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4.
754
+
755
+ \uC62C\uBC14\uB978 \uC0AC\uC6A9\uBC95:
756
+ <RangePicker>
757
+ <RangePicker.${componentName.replace("RangePicker.", "")} />
758
+ </RangePicker>`
759
+ );
760
+ }
761
+ return context;
762
+ }
763
+ var EMPTY_RANGE = { start: null, end: null };
764
+ function RangePickerRoot({
765
+ value: controlledValue,
766
+ defaultValue,
767
+ onChange,
768
+ disabled = false,
769
+ readOnly = false,
770
+ weekStartsOn = 0,
771
+ displayFormat = "yyyy-MM-dd",
772
+ locale = "en-US",
773
+ adapter = DateFnsAdapter,
774
+ children
775
+ }) {
776
+ const pickerId = useId();
777
+ const isControlled = useRef(controlledValue !== void 0).current;
778
+ const referenceRef = useRef(null);
779
+ const [uncontrolledValue, setUncontrolledValue] = useState(
780
+ defaultValue ?? EMPTY_RANGE
781
+ );
782
+ const currentValue = isControlled ? controlledValue ?? EMPTY_RANGE : uncontrolledValue;
783
+ const [isOpen, setIsOpen] = useState(false);
784
+ const [selectingTarget, setSelectingTarget] = useState("start");
785
+ const [hoverDate, setHoverDate] = useState(null);
786
+ const [viewMonth, setViewMonth] = useState(
787
+ currentValue.start ?? adapter.today()
788
+ );
789
+ const [focusedDate, setFocusedDate] = useState(
790
+ currentValue.start ?? adapter.today()
791
+ );
792
+ const isDisabled = typeof disabled === "boolean" ? disabled : false;
793
+ const disabledRules = Array.isArray(disabled) ? disabled : [];
794
+ const setRange = useCallback(
795
+ (range) => {
796
+ if (isDisabled || readOnly) return;
797
+ if (!isControlled) {
798
+ setUncontrolledValue(range);
799
+ }
800
+ onChange?.(range);
801
+ },
802
+ [isControlled, isDisabled, readOnly, onChange]
803
+ );
804
+ const selectDate = useCallback(
805
+ (iso) => {
806
+ if (isDisabled || readOnly) return;
807
+ if (selectingTarget === "start") {
808
+ const newRange = { start: iso, end: null };
809
+ setRange(newRange);
810
+ setSelectingTarget("end");
811
+ setHoverDate(null);
812
+ } else {
813
+ const start = currentValue.start;
814
+ if (!start) {
815
+ setRange({ start: iso, end: null });
816
+ setSelectingTarget("end");
817
+ return;
818
+ }
819
+ let newRange;
820
+ if (adapter.isBefore(iso, start)) {
821
+ newRange = { start: iso, end: start };
822
+ } else {
823
+ newRange = { start, end: iso };
824
+ }
825
+ setRange(newRange);
826
+ setSelectingTarget("start");
827
+ setHoverDate(null);
828
+ setIsOpen(false);
829
+ }
830
+ },
831
+ [isDisabled, readOnly, selectingTarget, currentValue.start, adapter, setRange]
832
+ );
833
+ const open = useCallback(() => {
834
+ if (isDisabled || readOnly) return;
835
+ setIsOpen(true);
836
+ const target = currentValue.start ?? adapter.today();
837
+ setViewMonth(target);
838
+ setFocusedDate(target);
839
+ if (currentValue.start && currentValue.end) {
840
+ setSelectingTarget("start");
841
+ }
842
+ }, [isDisabled, readOnly, currentValue, adapter]);
843
+ const close = useCallback(() => {
844
+ setIsOpen(false);
845
+ setHoverDate(null);
846
+ }, []);
847
+ const toggle = useCallback(() => {
848
+ if (isOpen) close();
849
+ else open();
850
+ }, [isOpen, open, close]);
851
+ const contextValue = useMemo(
852
+ () => ({
853
+ referenceRef,
854
+ value: currentValue,
855
+ setRange,
856
+ selectDate,
857
+ selectingTarget,
858
+ hoverDate,
859
+ setHoverDate,
860
+ isOpen,
861
+ open,
862
+ close,
863
+ toggle,
864
+ viewMonth,
865
+ setViewMonth,
866
+ focusedDate,
867
+ setFocusedDate,
868
+ adapter,
869
+ disabled: disabledRules,
870
+ weekStartsOn,
871
+ displayFormat,
872
+ locale,
873
+ isDisabled,
874
+ isReadOnly: readOnly,
875
+ pickerId
876
+ }),
877
+ [
878
+ currentValue,
879
+ setRange,
880
+ selectDate,
881
+ selectingTarget,
882
+ hoverDate,
883
+ isOpen,
884
+ open,
885
+ close,
886
+ toggle,
887
+ viewMonth,
888
+ focusedDate,
889
+ adapter,
890
+ disabledRules,
891
+ weekStartsOn,
892
+ displayFormat,
893
+ locale,
894
+ isDisabled,
895
+ readOnly,
896
+ pickerId
897
+ ]
898
+ );
899
+ return /* @__PURE__ */ jsx(RangePickerContext.Provider, { value: contextValue, children });
900
+ }
901
+ var RangePickerInput = forwardRef(
902
+ function RangePickerInput2({ part, format: formatProp, onFocus, onKeyDown, ...props }, ref) {
903
+ const ctx = useRangePickerContext("RangePicker.Input");
904
+ const displayFormat = formatProp ?? ctx.displayFormat;
905
+ const value = ctx.value[part];
906
+ const displayValue = value ? ctx.adapter.format(value, displayFormat) : "";
907
+ const handleFocus = useCallback(
908
+ (e) => {
909
+ ctx.open();
910
+ onFocus?.(e);
911
+ },
912
+ [ctx, onFocus]
913
+ );
914
+ const handleKeyDown = useCallback(
915
+ (e) => {
916
+ if (e.key === "Escape") {
917
+ ctx.close();
918
+ } else if (e.key === "ArrowDown" && !ctx.isOpen) {
919
+ e.preventDefault();
920
+ ctx.open();
921
+ }
922
+ onKeyDown?.(e);
923
+ },
924
+ [ctx, onKeyDown]
925
+ );
926
+ const calendarId = `${ctx.pickerId}-calendar`;
927
+ return /* @__PURE__ */ jsx(
928
+ "input",
929
+ {
930
+ ref: (node) => {
931
+ if (part === "start" && node) ctx.referenceRef.current = node;
932
+ if (typeof ref === "function") ref(node);
933
+ else if (ref) ref.current = node;
934
+ },
935
+ type: "text",
936
+ role: "combobox",
937
+ readOnly: true,
938
+ "aria-expanded": ctx.isOpen,
939
+ "aria-haspopup": "dialog",
940
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
941
+ "aria-autocomplete": "none",
942
+ "aria-label": part === "start" ? "\uC2DC\uC791\uC77C" : "\uC885\uB8CC\uC77C",
943
+ autoComplete: "off",
944
+ value: displayValue,
945
+ disabled: ctx.isDisabled || props.disabled,
946
+ onFocus: handleFocus,
947
+ onKeyDown: handleKeyDown,
948
+ "data-part": part,
949
+ ...props
950
+ }
951
+ );
952
+ }
953
+ );
954
+ function RangePickerPopover({ children, ...props }) {
955
+ const ctx = useRangePickerContext("RangePicker.Popover");
956
+ const calendarId = `${ctx.pickerId}-calendar`;
957
+ const floatingRef = useRef(null);
958
+ const { refs, floatingStyles } = useFloating({
959
+ open: ctx.isOpen,
960
+ placement: "bottom-start",
961
+ middleware: [offset(4), flip(), shift({ padding: 8 })],
962
+ whileElementsMounted: autoUpdate
963
+ });
964
+ useEffect(() => {
965
+ if (ctx.referenceRef.current) {
966
+ refs.setReference(ctx.referenceRef.current);
967
+ }
968
+ }, [ctx.referenceRef, refs, ctx.isOpen]);
969
+ const previousFocusRef = useRef(null);
970
+ useEffect(() => {
971
+ if (ctx.isOpen) {
972
+ previousFocusRef.current = document.activeElement;
973
+ } else if (previousFocusRef.current) {
974
+ previousFocusRef.current.focus();
975
+ previousFocusRef.current = null;
976
+ }
977
+ }, [ctx.isOpen]);
978
+ useEffect(() => {
979
+ if (!ctx.isOpen) return;
980
+ function handleClickOutside(e) {
981
+ const floating = floatingRef.current;
982
+ const reference = ctx.referenceRef.current;
983
+ const target = e.target;
984
+ if (floating && !floating.contains(target) && (!reference || !reference.contains(target))) {
985
+ ctx.close();
986
+ }
987
+ }
988
+ const timer = setTimeout(() => {
989
+ document.addEventListener("mousedown", handleClickOutside);
990
+ }, 0);
991
+ return () => {
992
+ clearTimeout(timer);
993
+ document.removeEventListener("mousedown", handleClickOutside);
994
+ };
995
+ }, [ctx.isOpen, ctx]);
996
+ useEffect(() => {
997
+ if (!ctx.isOpen) return;
998
+ function handleKeyDown(e) {
999
+ if (e.key === "Escape") {
1000
+ ctx.close();
1001
+ }
1002
+ }
1003
+ document.addEventListener("keydown", handleKeyDown);
1004
+ return () => document.removeEventListener("keydown", handleKeyDown);
1005
+ }, [ctx.isOpen, ctx]);
1006
+ if (!ctx.isOpen) return null;
1007
+ return /* @__PURE__ */ jsx(
1008
+ "div",
1009
+ {
1010
+ ref: (node) => {
1011
+ floatingRef.current = node;
1012
+ refs.setFloating(node);
1013
+ },
1014
+ id: calendarId,
1015
+ role: "dialog",
1016
+ "aria-label": "\uB0A0\uC9DC \uBC94\uC704 \uC120\uD0DD",
1017
+ "aria-modal": "false",
1018
+ style: floatingStyles,
1019
+ ...props,
1020
+ children
1021
+ }
1022
+ );
1023
+ }
1024
+ var srOnly2 = {
1025
+ position: "absolute",
1026
+ width: "1px",
1027
+ height: "1px",
1028
+ padding: 0,
1029
+ margin: "-1px",
1030
+ overflow: "hidden",
1031
+ clip: "rect(0, 0, 0, 0)",
1032
+ whiteSpace: "nowrap",
1033
+ border: 0
1034
+ };
1035
+ function RangePickerCalendar({ classNames, ...props }) {
1036
+ const ctx = useRangePickerContext("RangePicker.Calendar");
1037
+ const gridRef = useRef(null);
1038
+ const [announcement, setAnnouncement] = useState("");
1039
+ const {
1040
+ adapter,
1041
+ viewMonth,
1042
+ focusedDate,
1043
+ weekStartsOn,
1044
+ disabled,
1045
+ value,
1046
+ hoverDate,
1047
+ selectingTarget
1048
+ } = ctx;
1049
+ const { locale } = ctx;
1050
+ const weekdays = getWeekdayNames(locale, weekStartsOn);
1051
+ const weeks = getCalendarDays(viewMonth, adapter, {
1052
+ weekStartsOn,
1053
+ focusedDate,
1054
+ disabled,
1055
+ range: value,
1056
+ rangeHover: hoverDate
1057
+ });
1058
+ const year = adapter.getYear(viewMonth);
1059
+ const month = adapter.getMonth(viewMonth);
1060
+ const title = formatMonthYear(year, month, locale);
1061
+ useEffect(() => {
1062
+ if (!ctx.isOpen || !gridRef.current) return;
1063
+ const focusedButton = gridRef.current.querySelector(
1064
+ '[data-focused="true"]'
1065
+ );
1066
+ focusedButton?.focus();
1067
+ }, [focusedDate, ctx.isOpen]);
1068
+ const navigateMonth = useCallback(
1069
+ (direction) => {
1070
+ const newMonth = adapter.addMonths(viewMonth, direction);
1071
+ ctx.setViewMonth(newMonth);
1072
+ ctx.setFocusedDate(adapter.startOfMonth(newMonth));
1073
+ const y = adapter.getYear(newMonth);
1074
+ const m = adapter.getMonth(newMonth);
1075
+ setAnnouncement(formatMonthYear(y, m, locale));
1076
+ },
1077
+ [adapter, viewMonth, ctx, locale]
1078
+ );
1079
+ const handleDayClick = useCallback(
1080
+ (day) => {
1081
+ if (day.isDisabled) return;
1082
+ ctx.selectDate(day.isoString);
1083
+ setAnnouncement(formatFullDate(day.isoString, locale));
1084
+ },
1085
+ [ctx, locale]
1086
+ );
1087
+ const handleDayMouseEnter = useCallback(
1088
+ (day) => {
1089
+ if (selectingTarget === "end" && value.start && !day.isDisabled) {
1090
+ ctx.setHoverDate(day.isoString);
1091
+ }
1092
+ },
1093
+ [selectingTarget, value.start, ctx]
1094
+ );
1095
+ const handleMouseLeave = useCallback(() => {
1096
+ ctx.setHoverDate(null);
1097
+ }, [ctx]);
1098
+ const handleKeyDown = useCallback(
1099
+ (e) => {
1100
+ let newFocused = null;
1101
+ switch (e.key) {
1102
+ case "ArrowLeft":
1103
+ newFocused = adapter.addDays(focusedDate, -1);
1104
+ break;
1105
+ case "ArrowRight":
1106
+ newFocused = adapter.addDays(focusedDate, 1);
1107
+ break;
1108
+ case "ArrowUp":
1109
+ newFocused = adapter.addDays(focusedDate, -7);
1110
+ break;
1111
+ case "ArrowDown":
1112
+ newFocused = adapter.addDays(focusedDate, 7);
1113
+ break;
1114
+ case "PageUp":
1115
+ newFocused = e.shiftKey ? adapter.addYears(focusedDate, -1) : adapter.addMonths(focusedDate, -1);
1116
+ break;
1117
+ case "PageDown":
1118
+ newFocused = e.shiftKey ? adapter.addYears(focusedDate, 1) : adapter.addMonths(focusedDate, 1);
1119
+ break;
1120
+ case "Home":
1121
+ newFocused = adapter.startOfWeek(focusedDate, weekStartsOn);
1122
+ break;
1123
+ case "End":
1124
+ newFocused = adapter.startOfDay(adapter.endOfWeek(focusedDate, weekStartsOn));
1125
+ break;
1126
+ case "Enter":
1127
+ case " ":
1128
+ e.preventDefault();
1129
+ if (!isDateDisabled(focusedDate, disabled, adapter)) {
1130
+ ctx.selectDate(focusedDate);
1131
+ }
1132
+ return;
1133
+ case "Escape":
1134
+ ctx.close();
1135
+ return;
1136
+ default:
1137
+ return;
1138
+ }
1139
+ if (newFocused) {
1140
+ e.preventDefault();
1141
+ ctx.setFocusedDate(newFocused);
1142
+ if (!adapter.isSameMonth(newFocused, viewMonth)) {
1143
+ ctx.setViewMonth(newFocused);
1144
+ }
1145
+ if (selectingTarget === "end" && value.start) {
1146
+ ctx.setHoverDate(newFocused);
1147
+ }
1148
+ }
1149
+ },
1150
+ [adapter, focusedDate, viewMonth, weekStartsOn, disabled, ctx, selectingTarget, value.start]
1151
+ );
1152
+ return /* @__PURE__ */ jsxs("div", { className: classNames?.root, ...props, onMouseLeave: handleMouseLeave, children: [
1153
+ /* @__PURE__ */ jsxs("div", { className: classNames?.header, children: [
1154
+ /* @__PURE__ */ jsx(
1155
+ "button",
1156
+ {
1157
+ type: "button",
1158
+ className: classNames?.navButton,
1159
+ onClick: () => navigateMonth(-1),
1160
+ "aria-label": "\uC774\uC804 \uB2EC",
1161
+ children: "<"
1162
+ }
1163
+ ),
1164
+ /* @__PURE__ */ jsx("span", { className: classNames?.title, "aria-live": "polite", children: title }),
1165
+ /* @__PURE__ */ jsx(
1166
+ "button",
1167
+ {
1168
+ type: "button",
1169
+ className: classNames?.navButton,
1170
+ onClick: () => navigateMonth(1),
1171
+ "aria-label": "\uB2E4\uC74C \uB2EC",
1172
+ children: ">"
1173
+ }
1174
+ )
1175
+ ] }),
1176
+ /* @__PURE__ */ jsxs(
1177
+ "table",
1178
+ {
1179
+ ref: gridRef,
1180
+ role: "grid",
1181
+ "aria-label": title,
1182
+ "aria-multiselectable": "true",
1183
+ className: classNames?.grid,
1184
+ onKeyDown: handleKeyDown,
1185
+ children: [
1186
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { role: "row", children: weekdays.map((day) => /* @__PURE__ */ jsx(
1187
+ "th",
1188
+ {
1189
+ role: "columnheader",
1190
+ abbr: day.full,
1191
+ scope: "col",
1192
+ className: classNames?.weekdayHeader,
1193
+ children: day.short
1194
+ },
1195
+ day.short
1196
+ )) }) }),
1197
+ /* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => /* @__PURE__ */ jsx("tr", { role: "row", className: classNames?.gridRow, children: week.map((day) => {
1198
+ const dayClasses = [
1199
+ classNames?.day,
1200
+ day.isRangeStart && classNames?.dayRangeStart,
1201
+ day.isRangeEnd && classNames?.dayRangeEnd,
1202
+ day.isInRange && classNames?.dayInRange,
1203
+ day.isToday && classNames?.dayToday,
1204
+ day.isDisabled && classNames?.dayDisabled,
1205
+ !day.isCurrentMonth && classNames?.dayOutsideMonth
1206
+ ].filter(Boolean).join(" ") || void 0;
1207
+ const isSelected = day.isRangeStart || day.isRangeEnd;
1208
+ return /* @__PURE__ */ jsx(
1209
+ "td",
1210
+ {
1211
+ role: "gridcell",
1212
+ "aria-selected": isSelected || void 0,
1213
+ "aria-disabled": day.isDisabled || void 0,
1214
+ "aria-current": day.isToday ? "date" : void 0,
1215
+ className: classNames?.gridCell,
1216
+ children: /* @__PURE__ */ jsx(
1217
+ "button",
1218
+ {
1219
+ type: "button",
1220
+ tabIndex: day.isFocused ? 0 : -1,
1221
+ disabled: day.isDisabled,
1222
+ "data-focused": day.isFocused || void 0,
1223
+ "data-range-start": day.isRangeStart || void 0,
1224
+ "data-range-end": day.isRangeEnd || void 0,
1225
+ "data-in-range": day.isInRange || void 0,
1226
+ "data-today": day.isToday || void 0,
1227
+ "data-outside-month": !day.isCurrentMonth || void 0,
1228
+ className: dayClasses,
1229
+ onClick: () => handleDayClick(day),
1230
+ onMouseEnter: () => handleDayMouseEnter(day),
1231
+ "aria-label": formatFullDate(day.isoString, locale),
1232
+ children: day.dayNumber
1233
+ }
1234
+ )
1235
+ },
1236
+ day.isoString
1237
+ );
1238
+ }) }, weekIndex)) })
1239
+ ]
1240
+ }
1241
+ ),
1242
+ /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", style: srOnly2, children: announcement })
1243
+ ] });
1244
+ }
1245
+ function RangePickerPresets({ classNames, children, ...props }) {
1246
+ return /* @__PURE__ */ jsx("div", { role: "group", "aria-label": "\uB0A0\uC9DC \uBC94\uC704 \uD504\uB9AC\uC14B", className: classNames?.root, ...props, children });
1247
+ }
1248
+ function resolvePreset(key, today, adapter) {
1249
+ switch (key) {
1250
+ case "today":
1251
+ return { start: today, end: today };
1252
+ case "yesterday": {
1253
+ const yesterday = adapter.addDays(today, -1);
1254
+ return { start: yesterday, end: yesterday };
1255
+ }
1256
+ case "last7days":
1257
+ return { start: adapter.addDays(today, -6), end: today };
1258
+ case "last30days":
1259
+ return { start: adapter.addDays(today, -29), end: today };
1260
+ case "thisWeek":
1261
+ return {
1262
+ start: adapter.startOfDay(adapter.startOfWeek(today)),
1263
+ end: adapter.startOfDay(adapter.endOfWeek(today))
1264
+ };
1265
+ case "lastWeek": {
1266
+ const prevWeek = adapter.addDays(today, -7);
1267
+ return {
1268
+ start: adapter.startOfDay(adapter.startOfWeek(prevWeek)),
1269
+ end: adapter.startOfDay(adapter.endOfWeek(prevWeek))
1270
+ };
1271
+ }
1272
+ case "thisMonth":
1273
+ return {
1274
+ start: adapter.startOfMonth(today),
1275
+ end: adapter.startOfDay(adapter.endOfMonth(today))
1276
+ };
1277
+ case "lastMonth": {
1278
+ const prevMonth = adapter.addMonths(today, -1);
1279
+ return {
1280
+ start: adapter.startOfMonth(prevMonth),
1281
+ end: adapter.startOfDay(adapter.endOfMonth(prevMonth))
1282
+ };
1283
+ }
1284
+ case "thisYear": {
1285
+ const yearStart = adapter.startOfMonth(
1286
+ adapter.addMonths(today, -new Date(today).getUTCMonth())
1287
+ );
1288
+ return { start: yearStart, end: today };
1289
+ }
1290
+ }
1291
+ }
1292
+ function RangePickerPreset({
1293
+ value: presetKey,
1294
+ range: directRange,
1295
+ children,
1296
+ onClick,
1297
+ ...props
1298
+ }) {
1299
+ const ctx = useRangePickerContext("RangePicker.Preset");
1300
+ const handleClick = useCallback(
1301
+ (e) => {
1302
+ if (ctx.isDisabled || ctx.isReadOnly) return;
1303
+ let resolved;
1304
+ if (directRange) {
1305
+ resolved = directRange;
1306
+ } else if (presetKey) {
1307
+ resolved = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
1308
+ } else {
1309
+ return;
1310
+ }
1311
+ ctx.setRange(resolved);
1312
+ ctx.close();
1313
+ onClick?.(e);
1314
+ },
1315
+ [ctx, presetKey, directRange, onClick]
1316
+ );
1317
+ const isActive = (() => {
1318
+ if (!ctx.value.start || !ctx.value.end) return false;
1319
+ let target;
1320
+ if (directRange) {
1321
+ target = directRange;
1322
+ } else if (presetKey) {
1323
+ target = resolvePreset(presetKey, ctx.adapter.today(), ctx.adapter);
1324
+ } else {
1325
+ return false;
1326
+ }
1327
+ return target.start !== null && target.end !== null && ctx.adapter.isSameDay(ctx.value.start, target.start) && ctx.adapter.isSameDay(ctx.value.end, target.end);
1328
+ })();
1329
+ return /* @__PURE__ */ jsx(
1330
+ "button",
1331
+ {
1332
+ type: "button",
1333
+ role: "option",
1334
+ "aria-selected": isActive,
1335
+ "data-active": isActive || void 0,
1336
+ disabled: ctx.isDisabled,
1337
+ onClick: handleClick,
1338
+ ...props,
1339
+ children
1340
+ }
1341
+ );
1342
+ }
1343
+
1344
+ // src/components/RangePicker/index.ts
1345
+ var RangePicker = Object.assign(RangePickerRoot, {
1346
+ Input: RangePickerInput,
1347
+ Popover: RangePickerPopover,
1348
+ Calendar: RangePickerCalendar,
1349
+ Presets: RangePickerPresets,
1350
+ Preset: RangePickerPreset
1351
+ });
1352
+ var TimePickerContext = createContext(null);
1353
+ function useTimePickerContext(componentName) {
1354
+ const context = useContext(TimePickerContext);
1355
+ if (!context) {
1356
+ throw new Error(
1357
+ `[${componentName}] TimePicker.Root \uB0B4\uBD80\uC5D0\uC11C \uC0AC\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4.
1358
+
1359
+ \uC62C\uBC14\uB978 \uC0AC\uC6A9\uBC95:
1360
+ <TimePicker>
1361
+ <TimePicker.${componentName.replace("TimePicker.", "")} />
1362
+ </TimePicker>`
1363
+ );
1364
+ }
1365
+ return context;
1366
+ }
1367
+ function getDefaultIso() {
1368
+ const now = /* @__PURE__ */ new Date();
1369
+ return new Date(
1370
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
1371
+ ).toISOString();
1372
+ }
1373
+ function TimePickerRoot({
1374
+ value: controlledValue,
1375
+ defaultValue,
1376
+ onChange,
1377
+ format = "24h",
1378
+ step = 1,
1379
+ withSeconds = false,
1380
+ disabled = false,
1381
+ readOnly = false,
1382
+ children
1383
+ }) {
1384
+ const pickerId = useId();
1385
+ const isControlled = useRef(controlledValue !== void 0).current;
1386
+ const [uncontrolledValue, setUncontrolledValue] = useState(
1387
+ defaultValue ?? null
1388
+ );
1389
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1390
+ const baseIso = currentValue ?? getDefaultIso();
1391
+ const currentTime = useMemo(() => getTime(baseIso), [baseIso]);
1392
+ const setTime$1 = useCallback(
1393
+ (partial) => {
1394
+ if (disabled || readOnly) return;
1395
+ const newIso = setTime(baseIso, partial);
1396
+ if (!isControlled) {
1397
+ setUncontrolledValue(newIso);
1398
+ }
1399
+ onChange?.(newIso);
1400
+ },
1401
+ [disabled, readOnly, baseIso, isControlled, onChange]
1402
+ );
1403
+ const contextValue = useMemo(
1404
+ () => ({
1405
+ value: currentValue,
1406
+ setTime: setTime$1,
1407
+ format,
1408
+ step,
1409
+ withSeconds,
1410
+ isDisabled: disabled,
1411
+ isReadOnly: readOnly,
1412
+ currentTime,
1413
+ pickerId
1414
+ }),
1415
+ [currentValue, setTime$1, format, step, withSeconds, disabled, readOnly, currentTime, pickerId]
1416
+ );
1417
+ return /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: contextValue, children });
1418
+ }
1419
+ var TimePickerInput = forwardRef(
1420
+ function TimePickerInput2({ onBlur, onKeyDown, ...props }, ref) {
1421
+ const ctx = useTimePickerContext("TimePicker.Input");
1422
+ const [inputText, setInputText] = useState(null);
1423
+ const displayValue = inputText !== null ? inputText : formatTimeString(ctx.currentTime, ctx.withSeconds);
1424
+ const commitInput = useCallback(() => {
1425
+ if (inputText === null) return;
1426
+ const parsed = parseTimeString(inputText);
1427
+ if (parsed) {
1428
+ ctx.setTime(parsed);
1429
+ }
1430
+ setInputText(null);
1431
+ }, [inputText, ctx]);
1432
+ const handleChange = useCallback(
1433
+ (e) => {
1434
+ setInputText(e.target.value);
1435
+ },
1436
+ []
1437
+ );
1438
+ const handleBlur = useCallback(
1439
+ (e) => {
1440
+ commitInput();
1441
+ onBlur?.(e);
1442
+ },
1443
+ [commitInput, onBlur]
1444
+ );
1445
+ const handleKeyDown = useCallback(
1446
+ (e) => {
1447
+ if (e.key === "Enter") {
1448
+ commitInput();
1449
+ }
1450
+ onKeyDown?.(e);
1451
+ },
1452
+ [commitInput, onKeyDown]
1453
+ );
1454
+ return /* @__PURE__ */ jsx(
1455
+ "input",
1456
+ {
1457
+ ref,
1458
+ type: "text",
1459
+ inputMode: "numeric",
1460
+ autoComplete: "off",
1461
+ "aria-label": "\uC2DC\uAC04 \uC785\uB825",
1462
+ placeholder: ctx.withSeconds ? "HH:MM:SS" : "HH:MM",
1463
+ value: displayValue,
1464
+ disabled: ctx.isDisabled || props.disabled,
1465
+ readOnly: ctx.isReadOnly,
1466
+ onChange: handleChange,
1467
+ onBlur: handleBlur,
1468
+ onKeyDown: handleKeyDown,
1469
+ ...props
1470
+ }
1471
+ );
1472
+ }
1473
+ );
1474
+ function TimePickerHourList({ classNames, ...props }) {
1475
+ const ctx = useTimePickerContext("TimePicker.HourList");
1476
+ const { format, currentTime, isDisabled, isReadOnly } = ctx;
1477
+ const listRef = useRef(null);
1478
+ const hours = generateHours(format);
1479
+ const selectedHourDisplay = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;
1480
+ const currentPeriod = format === "12h" ? to12Hour(currentTime.hours).period : null;
1481
+ const handleSelect = useCallback(
1482
+ (hourDisplay) => {
1483
+ if (isDisabled || isReadOnly) return;
1484
+ const hours24 = format === "12h" && currentPeriod ? to24Hour(hourDisplay, currentPeriod) : hourDisplay;
1485
+ ctx.setTime({ hours: hours24 });
1486
+ },
1487
+ [format, currentPeriod, ctx, isDisabled, isReadOnly]
1488
+ );
1489
+ const handleKeyDown = useCallback(
1490
+ (e, hour) => {
1491
+ if (isDisabled || isReadOnly) return;
1492
+ const currentIndex = hours.indexOf(hour);
1493
+ let newIndex = -1;
1494
+ if (e.key === "ArrowDown") {
1495
+ newIndex = Math.min(currentIndex + 1, hours.length - 1);
1496
+ } else if (e.key === "ArrowUp") {
1497
+ newIndex = Math.max(currentIndex - 1, 0);
1498
+ } else if (e.key === "Home") {
1499
+ newIndex = 0;
1500
+ } else if (e.key === "End") {
1501
+ newIndex = hours.length - 1;
1502
+ } else if (e.key === "Enter" || e.key === " ") {
1503
+ e.preventDefault();
1504
+ handleSelect(hour);
1505
+ return;
1506
+ } else {
1507
+ return;
1508
+ }
1509
+ e.preventDefault();
1510
+ const targetHour = hours[newIndex];
1511
+ if (targetHour !== void 0) {
1512
+ handleSelect(targetHour);
1513
+ requestAnimationFrame(() => {
1514
+ const next = listRef.current?.querySelector(
1515
+ '[data-selected="true"]'
1516
+ );
1517
+ next?.focus();
1518
+ });
1519
+ }
1520
+ },
1521
+ [hours, handleSelect, isDisabled, isReadOnly]
1522
+ );
1523
+ return /* @__PURE__ */ jsx(
1524
+ "ul",
1525
+ {
1526
+ ref: listRef,
1527
+ role: "listbox",
1528
+ "aria-label": "\uC2DC",
1529
+ "aria-disabled": isDisabled || void 0,
1530
+ className: classNames?.root,
1531
+ ...props,
1532
+ children: hours.map((hour) => {
1533
+ const isSelected = hour === selectedHourDisplay;
1534
+ const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
1535
+ return /* @__PURE__ */ jsx(
1536
+ "li",
1537
+ {
1538
+ role: "option",
1539
+ "aria-selected": isSelected,
1540
+ "aria-disabled": isDisabled || void 0,
1541
+ "aria-label": `${hour}\uC2DC`,
1542
+ "data-selected": isSelected || void 0,
1543
+ tabIndex: isSelected ? 0 : -1,
1544
+ className: optionClass,
1545
+ onClick: () => handleSelect(hour),
1546
+ onKeyDown: (e) => handleKeyDown(e, hour),
1547
+ children: String(hour).padStart(2, "0")
1548
+ },
1549
+ hour
1550
+ );
1551
+ })
1552
+ }
1553
+ );
1554
+ }
1555
+ function TimePickerMinuteList({ classNames, ...props }) {
1556
+ const ctx = useTimePickerContext("TimePicker.MinuteList");
1557
+ const { step, currentTime, isDisabled, isReadOnly } = ctx;
1558
+ const listRef = useRef(null);
1559
+ const minutes = generateMinutes(step);
1560
+ const handleSelect = useCallback(
1561
+ (minute) => {
1562
+ if (isDisabled || isReadOnly) return;
1563
+ ctx.setTime({ minutes: minute });
1564
+ },
1565
+ [ctx, isDisabled, isReadOnly]
1566
+ );
1567
+ const handleKeyDown = useCallback(
1568
+ (e, minute) => {
1569
+ if (isDisabled || isReadOnly) return;
1570
+ const currentIndex = minutes.indexOf(minute);
1571
+ let newIndex = -1;
1572
+ if (e.key === "ArrowDown") {
1573
+ newIndex = Math.min(currentIndex + 1, minutes.length - 1);
1574
+ } else if (e.key === "ArrowUp") {
1575
+ newIndex = Math.max(currentIndex - 1, 0);
1576
+ } else if (e.key === "Home") {
1577
+ newIndex = 0;
1578
+ } else if (e.key === "End") {
1579
+ newIndex = minutes.length - 1;
1580
+ } else if (e.key === "Enter" || e.key === " ") {
1581
+ e.preventDefault();
1582
+ handleSelect(minute);
1583
+ return;
1584
+ } else {
1585
+ return;
1586
+ }
1587
+ e.preventDefault();
1588
+ const target = minutes[newIndex];
1589
+ if (target !== void 0) {
1590
+ handleSelect(target);
1591
+ requestAnimationFrame(() => {
1592
+ const next = listRef.current?.querySelector(
1593
+ '[data-selected="true"]'
1594
+ );
1595
+ next?.focus();
1596
+ });
1597
+ }
1598
+ },
1599
+ [minutes, handleSelect, isDisabled, isReadOnly]
1600
+ );
1601
+ return /* @__PURE__ */ jsx(
1602
+ "ul",
1603
+ {
1604
+ ref: listRef,
1605
+ role: "listbox",
1606
+ "aria-label": "\uBD84",
1607
+ "aria-disabled": isDisabled || void 0,
1608
+ className: classNames?.root,
1609
+ ...props,
1610
+ children: minutes.map((minute) => {
1611
+ const isSelected = minute === currentTime.minutes;
1612
+ const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
1613
+ return /* @__PURE__ */ jsx(
1614
+ "li",
1615
+ {
1616
+ role: "option",
1617
+ "aria-selected": isSelected,
1618
+ "aria-disabled": isDisabled || void 0,
1619
+ "aria-label": `${minute}\uBD84`,
1620
+ "data-selected": isSelected || void 0,
1621
+ tabIndex: isSelected ? 0 : -1,
1622
+ className: optionClass,
1623
+ onClick: () => handleSelect(minute),
1624
+ onKeyDown: (e) => handleKeyDown(e, minute),
1625
+ children: String(minute).padStart(2, "0")
1626
+ },
1627
+ minute
1628
+ );
1629
+ })
1630
+ }
1631
+ );
1632
+ }
1633
+ function TimePickerAmPmToggle({ classNames, ...props }) {
1634
+ const ctx = useTimePickerContext("TimePicker.AmPmToggle");
1635
+ if (ctx.format !== "12h") return null;
1636
+ const { period, hours12 } = to12Hour(ctx.currentTime.hours);
1637
+ const setPeriod = useCallback(
1638
+ (newPeriod) => {
1639
+ if (ctx.isDisabled || ctx.isReadOnly) return;
1640
+ const newHours24 = to24Hour(hours12, newPeriod);
1641
+ ctx.setTime({ hours: newHours24 });
1642
+ },
1643
+ [hours12, ctx]
1644
+ );
1645
+ const renderButton = (target) => {
1646
+ const isSelected = period === target;
1647
+ const optionClass = [classNames?.option, isSelected && classNames?.optionSelected].filter(Boolean).join(" ") || void 0;
1648
+ return /* @__PURE__ */ jsx(
1649
+ "button",
1650
+ {
1651
+ type: "button",
1652
+ role: "radio",
1653
+ "aria-checked": isSelected,
1654
+ "data-selected": isSelected || void 0,
1655
+ disabled: ctx.isDisabled,
1656
+ className: optionClass,
1657
+ onClick: () => setPeriod(target),
1658
+ children: target
1659
+ }
1660
+ );
1661
+ };
1662
+ return /* @__PURE__ */ jsxs("div", { role: "radiogroup", "aria-label": "\uC624\uC804/\uC624\uD6C4", className: classNames?.root, ...props, children: [
1663
+ renderButton("AM"),
1664
+ renderButton("PM")
1665
+ ] });
1666
+ }
1667
+
1668
+ // src/components/TimePicker/index.ts
1669
+ var TimePicker = Object.assign(TimePickerRoot, {
1670
+ Input: TimePickerInput,
1671
+ HourList: TimePickerHourList,
1672
+ MinuteList: TimePickerMinuteList,
1673
+ AmPmToggle: TimePickerAmPmToggle
1674
+ });
1675
+ function getDefaultIso2() {
1676
+ const now = /* @__PURE__ */ new Date();
1677
+ return new Date(
1678
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
1679
+ ).toISOString();
1680
+ }
1681
+ function DateTimePickerRoot({
1682
+ value: controlledValue,
1683
+ defaultValue,
1684
+ onChange,
1685
+ format = "24h",
1686
+ step = 1,
1687
+ disabled = false,
1688
+ readOnly = false,
1689
+ weekStartsOn = 0,
1690
+ displayFormat = "yyyy-MM-dd HH:mm",
1691
+ locale = "en-US",
1692
+ adapter = DateFnsAdapter,
1693
+ children
1694
+ }) {
1695
+ const pickerId = useId();
1696
+ const isControlled = useRef(controlledValue !== void 0).current;
1697
+ const referenceRef = useRef(null);
1698
+ const [uncontrolledValue, setUncontrolledValue] = useState(
1699
+ defaultValue ?? null
1700
+ );
1701
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1702
+ const [isOpen, setIsOpen] = useState(false);
1703
+ const [viewMonth, setViewMonth] = useState(
1704
+ currentValue ?? adapter.today()
1705
+ );
1706
+ const [focusedDate, setFocusedDate] = useState(
1707
+ currentValue ?? adapter.today()
1708
+ );
1709
+ const isDisabled = typeof disabled === "boolean" ? disabled : false;
1710
+ const disabledRules = Array.isArray(disabled) ? disabled : [];
1711
+ const baseIso = currentValue ?? getDefaultIso2();
1712
+ const currentTime = useMemo(() => getTime(baseIso), [baseIso]);
1713
+ const updateValue = useCallback(
1714
+ (next) => {
1715
+ if (isDisabled || readOnly) return;
1716
+ if (!isControlled) {
1717
+ setUncontrolledValue(next);
1718
+ }
1719
+ onChange?.(next);
1720
+ },
1721
+ [isControlled, isDisabled, readOnly, onChange]
1722
+ );
1723
+ const selectDate = useCallback(
1724
+ (newDateIso) => {
1725
+ if (newDateIso === null) {
1726
+ updateValue(null);
1727
+ return;
1728
+ }
1729
+ const time = currentValue ? getTime(currentValue) : currentTime;
1730
+ const merged = setTime(newDateIso, time);
1731
+ updateValue(merged);
1732
+ },
1733
+ [currentValue, currentTime, updateValue]
1734
+ );
1735
+ const setTime$1 = useCallback(
1736
+ (partial) => {
1737
+ const base = currentValue ?? getDefaultIso2();
1738
+ const merged = setTime(base, partial);
1739
+ updateValue(merged);
1740
+ },
1741
+ [currentValue, updateValue]
1742
+ );
1743
+ const open = useCallback(() => {
1744
+ if (isDisabled || readOnly) return;
1745
+ setIsOpen(true);
1746
+ const target = currentValue ?? adapter.today();
1747
+ setViewMonth(target);
1748
+ setFocusedDate(target);
1749
+ }, [isDisabled, readOnly, currentValue, adapter]);
1750
+ const close = useCallback(() => {
1751
+ setIsOpen(false);
1752
+ }, []);
1753
+ const toggle = useCallback(() => {
1754
+ if (isOpen) close();
1755
+ else open();
1756
+ }, [isOpen, open, close]);
1757
+ const dateContext = useMemo(
1758
+ () => ({
1759
+ referenceRef,
1760
+ value: currentValue,
1761
+ selectDate,
1762
+ isOpen,
1763
+ open,
1764
+ close,
1765
+ toggle,
1766
+ viewMonth,
1767
+ setViewMonth,
1768
+ focusedDate,
1769
+ setFocusedDate,
1770
+ adapter,
1771
+ disabled: disabledRules,
1772
+ weekStartsOn,
1773
+ displayFormat,
1774
+ locale,
1775
+ isDisabled,
1776
+ isReadOnly: readOnly,
1777
+ pickerId
1778
+ }),
1779
+ [
1780
+ currentValue,
1781
+ selectDate,
1782
+ isOpen,
1783
+ open,
1784
+ close,
1785
+ toggle,
1786
+ viewMonth,
1787
+ focusedDate,
1788
+ adapter,
1789
+ disabledRules,
1790
+ weekStartsOn,
1791
+ displayFormat,
1792
+ locale,
1793
+ isDisabled,
1794
+ readOnly,
1795
+ pickerId
1796
+ ]
1797
+ );
1798
+ const timeContext = useMemo(
1799
+ () => ({
1800
+ value: currentValue,
1801
+ setTime: setTime$1,
1802
+ format,
1803
+ step,
1804
+ withSeconds: false,
1805
+ isDisabled,
1806
+ isReadOnly: readOnly,
1807
+ currentTime,
1808
+ pickerId
1809
+ }),
1810
+ [currentValue, setTime$1, format, step, isDisabled, readOnly, currentTime, pickerId]
1811
+ );
1812
+ return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: dateContext, children: /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: timeContext, children }) });
1813
+ }
1814
+ var DateTimePickerInput = forwardRef(
1815
+ function DateTimePickerInput2({ onFocus, onKeyDown, ...props }, ref) {
1816
+ const ctx = useDatePickerContext("DateTimePicker.Input");
1817
+ const displayValue = ctx.value ? `${ctx.adapter.format(ctx.value, "yyyy-MM-dd")} ${formatTimeString(getTime(ctx.value))}` : "";
1818
+ const handleFocus = useCallback(
1819
+ (e) => {
1820
+ ctx.open();
1821
+ onFocus?.(e);
1822
+ },
1823
+ [ctx, onFocus]
1824
+ );
1825
+ const handleKeyDown = useCallback(
1826
+ (e) => {
1827
+ if (e.key === "Escape") {
1828
+ ctx.close();
1829
+ } else if (e.key === "ArrowDown" && !ctx.isOpen) {
1830
+ e.preventDefault();
1831
+ ctx.open();
1832
+ }
1833
+ onKeyDown?.(e);
1834
+ },
1835
+ [ctx, onKeyDown]
1836
+ );
1837
+ const calendarId = `${ctx.pickerId}-calendar`;
1838
+ return /* @__PURE__ */ jsx(
1839
+ "input",
1840
+ {
1841
+ ref: (node) => {
1842
+ ctx.referenceRef.current = node;
1843
+ if (typeof ref === "function") ref(node);
1844
+ else if (ref) ref.current = node;
1845
+ },
1846
+ type: "text",
1847
+ role: "combobox",
1848
+ readOnly: true,
1849
+ "aria-label": "\uB0A0\uC9DC \uBC0F \uC2DC\uAC04",
1850
+ "aria-expanded": ctx.isOpen,
1851
+ "aria-haspopup": "dialog",
1852
+ "aria-controls": ctx.isOpen ? calendarId : void 0,
1853
+ "aria-autocomplete": "none",
1854
+ autoComplete: "off",
1855
+ value: displayValue,
1856
+ disabled: ctx.isDisabled || props.disabled,
1857
+ onFocus: handleFocus,
1858
+ onKeyDown: handleKeyDown,
1859
+ ...props
1860
+ }
1861
+ );
1862
+ }
1863
+ );
1864
+
1865
+ // src/components/DateTimePicker/index.ts
1866
+ var DateTimePicker = Object.assign(DateTimePickerRoot, {
1867
+ Input: DateTimePickerInput,
1868
+ Popover: DatePickerPopover,
1869
+ Calendar: DatePickerCalendar,
1870
+ MonthGrid: DatePickerMonthGrid,
1871
+ YearGrid: DatePickerYearGrid,
1872
+ HourList: TimePickerHourList,
1873
+ MinuteList: TimePickerMinuteList,
1874
+ AmPmToggle: TimePickerAmPmToggle
1875
+ });
1876
+ function useDatePicker(options = {}) {
1877
+ const {
1878
+ value: controlledValue,
1879
+ defaultValue,
1880
+ onChange,
1881
+ disabled = [],
1882
+ weekStartsOn = 0,
1883
+ adapter = DateFnsAdapter
1884
+ } = options;
1885
+ const pickerId = useId();
1886
+ const isControlled = useRef(controlledValue !== void 0).current;
1887
+ const [uncontrolledValue, setUncontrolledValue] = useState(
1888
+ defaultValue ?? null
1889
+ );
1890
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1891
+ const [isOpen, setIsOpen] = useState(false);
1892
+ const [viewMonth, setViewMonth] = useState(currentValue ?? adapter.today());
1893
+ const [focusedDate, setFocusedDate] = useState(currentValue ?? adapter.today());
1894
+ const selectDate = useCallback(
1895
+ (iso) => {
1896
+ if (!isControlled) {
1897
+ setUncontrolledValue(iso);
1898
+ }
1899
+ onChange?.(iso);
1900
+ setIsOpen(false);
1901
+ },
1902
+ [isControlled, onChange]
1903
+ );
1904
+ const open = useCallback(() => {
1905
+ setIsOpen(true);
1906
+ const target = currentValue ?? adapter.today();
1907
+ setViewMonth(target);
1908
+ setFocusedDate(target);
1909
+ }, [currentValue, adapter]);
1910
+ const close = useCallback(() => {
1911
+ setIsOpen(false);
1912
+ }, []);
1913
+ const toggle = useCallback(() => {
1914
+ if (isOpen) close();
1915
+ else open();
1916
+ }, [isOpen, open, close]);
1917
+ const previousMonth = useCallback(() => {
1918
+ const newMonth = adapter.addMonths(viewMonth, -1);
1919
+ setViewMonth(newMonth);
1920
+ setFocusedDate(adapter.startOfMonth(newMonth));
1921
+ }, [adapter, viewMonth]);
1922
+ const nextMonth = useCallback(() => {
1923
+ const newMonth = adapter.addMonths(viewMonth, 1);
1924
+ setViewMonth(newMonth);
1925
+ setFocusedDate(adapter.startOfMonth(newMonth));
1926
+ }, [adapter, viewMonth]);
1927
+ const calendar = getCalendarDays(viewMonth, adapter, {
1928
+ weekStartsOn,
1929
+ selected: currentValue,
1930
+ focusedDate,
1931
+ disabled
1932
+ });
1933
+ return {
1934
+ value: currentValue,
1935
+ isOpen,
1936
+ open,
1937
+ close,
1938
+ toggle,
1939
+ selectDate,
1940
+ viewMonth,
1941
+ setViewMonth,
1942
+ calendar,
1943
+ focusedDate,
1944
+ setFocusedDate,
1945
+ previousMonth,
1946
+ nextMonth,
1947
+ pickerId,
1948
+ adapter
1949
+ };
1950
+ }
1951
+ var EMPTY_RANGE2 = { start: null, end: null };
1952
+ function useRangePicker(options = {}) {
1953
+ const {
1954
+ value: controlledValue,
1955
+ defaultValue,
1956
+ onChange,
1957
+ disabled = [],
1958
+ weekStartsOn = 0,
1959
+ adapter = DateFnsAdapter
1960
+ } = options;
1961
+ const pickerId = useId();
1962
+ const isControlled = useRef(controlledValue !== void 0).current;
1963
+ const [uncontrolledValue, setUncontrolledValue] = useState(
1964
+ defaultValue ?? EMPTY_RANGE2
1965
+ );
1966
+ const currentValue = isControlled ? controlledValue ?? EMPTY_RANGE2 : uncontrolledValue;
1967
+ const [isOpen, setIsOpen] = useState(false);
1968
+ const [selectingTarget, setSelectingTarget] = useState("start");
1969
+ const [hoverDate, setHoverDate] = useState(null);
1970
+ const [viewMonth, setViewMonth] = useState(
1971
+ currentValue.start ?? adapter.today()
1972
+ );
1973
+ const [focusedDate, setFocusedDate] = useState(
1974
+ currentValue.start ?? adapter.today()
1975
+ );
1976
+ const setRange = useCallback(
1977
+ (range) => {
1978
+ if (!isControlled) {
1979
+ setUncontrolledValue(range);
1980
+ }
1981
+ onChange?.(range);
1982
+ },
1983
+ [isControlled, onChange]
1984
+ );
1985
+ const selectDate = useCallback(
1986
+ (iso) => {
1987
+ if (selectingTarget === "start") {
1988
+ setRange({ start: iso, end: null });
1989
+ setSelectingTarget("end");
1990
+ setHoverDate(null);
1991
+ } else {
1992
+ const start = currentValue.start;
1993
+ if (!start) {
1994
+ setRange({ start: iso, end: null });
1995
+ setSelectingTarget("end");
1996
+ return;
1997
+ }
1998
+ const newRange = adapter.isBefore(iso, start) ? { start: iso, end: start } : { start, end: iso };
1999
+ setRange(newRange);
2000
+ setSelectingTarget("start");
2001
+ setHoverDate(null);
2002
+ setIsOpen(false);
2003
+ }
2004
+ },
2005
+ [selectingTarget, currentValue.start, adapter, setRange]
2006
+ );
2007
+ const open = useCallback(() => {
2008
+ setIsOpen(true);
2009
+ const target = currentValue.start ?? adapter.today();
2010
+ setViewMonth(target);
2011
+ setFocusedDate(target);
2012
+ if (currentValue.start && currentValue.end) {
2013
+ setSelectingTarget("start");
2014
+ }
2015
+ }, [currentValue, adapter]);
2016
+ const close = useCallback(() => {
2017
+ setIsOpen(false);
2018
+ setHoverDate(null);
2019
+ }, []);
2020
+ const toggle = useCallback(() => {
2021
+ if (isOpen) close();
2022
+ else open();
2023
+ }, [isOpen, open, close]);
2024
+ const previousMonth = useCallback(() => {
2025
+ const newMonth = adapter.addMonths(viewMonth, -1);
2026
+ setViewMonth(newMonth);
2027
+ setFocusedDate(adapter.startOfMonth(newMonth));
2028
+ }, [adapter, viewMonth]);
2029
+ const nextMonth = useCallback(() => {
2030
+ const newMonth = adapter.addMonths(viewMonth, 1);
2031
+ setViewMonth(newMonth);
2032
+ setFocusedDate(adapter.startOfMonth(newMonth));
2033
+ }, [adapter, viewMonth]);
2034
+ const calendar = getCalendarDays(viewMonth, adapter, {
2035
+ weekStartsOn,
2036
+ focusedDate,
2037
+ disabled,
2038
+ range: currentValue,
2039
+ rangeHover: hoverDate
2040
+ });
2041
+ return {
2042
+ value: currentValue,
2043
+ selectingTarget,
2044
+ selectDate,
2045
+ setRange,
2046
+ isOpen,
2047
+ open,
2048
+ close,
2049
+ toggle,
2050
+ hoverDate,
2051
+ setHoverDate,
2052
+ viewMonth,
2053
+ setViewMonth,
2054
+ calendar,
2055
+ focusedDate,
2056
+ setFocusedDate,
2057
+ previousMonth,
2058
+ nextMonth,
2059
+ pickerId,
2060
+ adapter
2061
+ };
2062
+ }
2063
+ function getDefaultIso3() {
2064
+ const now = /* @__PURE__ */ new Date();
2065
+ return new Date(
2066
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
2067
+ ).toISOString();
2068
+ }
2069
+ function useTimePicker(options = {}) {
2070
+ const {
2071
+ value: controlledValue,
2072
+ defaultValue,
2073
+ onChange,
2074
+ format = "24h",
2075
+ step = 1
2076
+ } = options;
2077
+ const pickerId = useId();
2078
+ const isControlled = useRef(controlledValue !== void 0).current;
2079
+ const [uncontrolledValue, setUncontrolledValue] = useState(
2080
+ defaultValue ?? null
2081
+ );
2082
+ const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
2083
+ const baseIso = currentValue ?? getDefaultIso3();
2084
+ const currentTime = useMemo(() => getTime(baseIso), [baseIso]);
2085
+ const setTime$1 = useCallback(
2086
+ (partial) => {
2087
+ const newIso = setTime(baseIso, partial);
2088
+ if (!isControlled) {
2089
+ setUncontrolledValue(newIso);
2090
+ }
2091
+ onChange?.(newIso);
2092
+ },
2093
+ [baseIso, isControlled, onChange]
2094
+ );
2095
+ const period = format === "12h" ? to12Hour(currentTime.hours).period : null;
2096
+ const displayHour = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;
2097
+ const setHour = useCallback(
2098
+ (hour) => {
2099
+ const hours24 = format === "12h" && period ? to24Hour(hour, period) : hour;
2100
+ setTime$1({ hours: hours24 });
2101
+ },
2102
+ [format, period, setTime$1]
2103
+ );
2104
+ const setMinute = useCallback(
2105
+ (minute) => setTime$1({ minutes: minute }),
2106
+ [setTime$1]
2107
+ );
2108
+ const setSecond = useCallback(
2109
+ (second) => setTime$1({ seconds: second }),
2110
+ [setTime$1]
2111
+ );
2112
+ const setPeriod = useCallback(
2113
+ (newPeriod) => {
2114
+ if (format !== "12h") return;
2115
+ const newHours24 = to24Hour(displayHour, newPeriod);
2116
+ setTime$1({ hours: newHours24 });
2117
+ },
2118
+ [format, displayHour, setTime$1]
2119
+ );
2120
+ return {
2121
+ value: currentValue,
2122
+ currentTime,
2123
+ setTime: setTime$1,
2124
+ setHour,
2125
+ setMinute,
2126
+ setSecond,
2127
+ setPeriod,
2128
+ availableHours: generateHours(format),
2129
+ availableMinutes: generateMinutes(step),
2130
+ format,
2131
+ displayHour,
2132
+ period,
2133
+ pickerId
2134
+ };
2135
+ }
2136
+
2137
+ export { DatePicker, DateTimePicker, RangePicker, TimePicker, useDatePicker, useRangePicker, useTimePicker };
2138
+ //# sourceMappingURL=index.js.map
13
2139
  //# sourceMappingURL=index.js.map