@kalyx/react 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
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';
2
+ import { parseInputValue, formatTimeString, parseTimeString, getTimeInTimezone, getTime, DateFnsAdapter, DEFAULT_DATEPICKER_LABELS, civilMidnightFromUtcDay, getMonthName, getWeekdayNames, getCalendarDays, formatMonthYear, isDateDisabled, DEFAULT_RANGEPICKER_LABELS, DEFAULT_TIMEPICKER_LABELS, setTimeInTimezone, setTime, to12Hour, to24Hour, generateMinutes, generateHours, formatFullDate } from '@kalyx/core';
3
3
  export { DateFnsAdapter } from '@kalyx/core';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
5
  import { useFloating, autoUpdate, offset, flip, shift } from '@floating-ui/react';
@@ -29,7 +29,9 @@ function DatePickerRoot({
29
29
  weekStartsOn = 0,
30
30
  displayFormat = "yyyy-MM-dd",
31
31
  locale = "en-US",
32
+ displayTimezone,
32
33
  adapter = DateFnsAdapter,
34
+ labels: labelsProp,
33
35
  children
34
36
  }) {
35
37
  const pickerId = useId();
@@ -41,31 +43,39 @@ function DatePickerRoot({
41
43
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
42
44
  const [isOpen, setIsOpen] = useState(false);
43
45
  const [viewMonth, setViewMonth] = useState(
44
- currentValue ?? adapter.today()
46
+ currentValue ?? adapter.today(displayTimezone)
45
47
  );
46
48
  const [focusedDate, setFocusedDate] = useState(
47
- currentValue ?? adapter.today()
49
+ currentValue ?? adapter.today(displayTimezone)
50
+ );
51
+ const mergedLabels = useMemo(
52
+ () => ({ ...DEFAULT_DATEPICKER_LABELS, ...labelsProp }),
53
+ [labelsProp]
48
54
  );
49
55
  const isDisabled = typeof disabled === "boolean" ? disabled : false;
50
- const disabledRules = Array.isArray(disabled) ? disabled : [];
56
+ const disabledRules = useMemo(
57
+ () => Array.isArray(disabled) ? disabled : [],
58
+ [disabled]
59
+ );
51
60
  const selectDate = useCallback(
52
61
  (iso) => {
53
62
  if (isDisabled || readOnly) return;
63
+ const normalized = iso && displayTimezone ? civilMidnightFromUtcDay(iso, displayTimezone) : iso;
54
64
  if (!isControlled) {
55
- setUncontrolledValue(iso);
65
+ setUncontrolledValue(normalized);
56
66
  }
57
- onChange?.(iso);
67
+ onChange?.(normalized);
58
68
  setIsOpen(false);
59
69
  },
60
- [isControlled, isDisabled, readOnly, onChange]
70
+ [isControlled, isDisabled, readOnly, onChange, displayTimezone]
61
71
  );
62
72
  const open = useCallback(() => {
63
73
  if (isDisabled || readOnly) return;
64
74
  setIsOpen(true);
65
- const target = currentValue ?? adapter.today();
75
+ const target = currentValue ?? adapter.today(displayTimezone);
66
76
  setViewMonth(target);
67
77
  setFocusedDate(target);
68
- }, [isDisabled, readOnly, currentValue, adapter]);
78
+ }, [isDisabled, readOnly, currentValue, adapter, displayTimezone]);
69
79
  const close = useCallback(() => {
70
80
  setIsOpen(false);
71
81
  }, []);
@@ -94,9 +104,11 @@ function DatePickerRoot({
94
104
  weekStartsOn,
95
105
  displayFormat,
96
106
  locale,
107
+ displayTimezone,
97
108
  isDisabled,
98
109
  isReadOnly: readOnly,
99
- pickerId
110
+ pickerId,
111
+ labels: mergedLabels
100
112
  }),
101
113
  [
102
114
  currentValue,
@@ -112,30 +124,40 @@ function DatePickerRoot({
112
124
  weekStartsOn,
113
125
  displayFormat,
114
126
  locale,
127
+ displayTimezone,
115
128
  isDisabled,
116
129
  readOnly,
117
- pickerId
130
+ pickerId,
131
+ mergedLabels
118
132
  ]
119
133
  );
120
134
  return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: contextValue, children });
121
135
  }
122
136
  var DatePickerInput = forwardRef(
123
- function DatePickerInput2({ format: formatProp, onFocus, onBlur, onKeyDown, ...props }, ref) {
137
+ function DatePickerInput2({ format: formatProp, onClick, onBlur, onKeyDown, ...props }, ref) {
124
138
  const ctx = useDatePickerContext("DatePicker.Input");
125
139
  const displayFormat = formatProp ?? ctx.displayFormat;
126
140
  const [inputText, setInputText] = useState(null);
127
- const displayValue = inputText !== null ? inputText : ctx.value ? ctx.adapter.format(ctx.value, displayFormat) : "";
128
- const handleFocus = useCallback(
141
+ let formattedValue = "";
142
+ if (ctx.value) {
143
+ try {
144
+ formattedValue = ctx.adapter.format(ctx.value, displayFormat, ctx.displayTimezone);
145
+ } catch {
146
+ formattedValue = ctx.value;
147
+ }
148
+ }
149
+ const displayValue = inputText !== null ? inputText : formattedValue;
150
+ const handleClick = useCallback(
129
151
  (e) => {
130
- ctx.open();
131
- onFocus?.(e);
152
+ if (!ctx.isOpen) ctx.open();
153
+ onClick?.(e);
132
154
  },
133
- [ctx, onFocus]
155
+ [ctx, onClick]
134
156
  );
135
157
  const handleBlur = useCallback(
136
158
  (e) => {
137
159
  if (inputText !== null) {
138
- const parsed = parseInputValue(inputText, displayFormat, ctx.adapter);
160
+ const parsed = parseInputValue(inputText, ctx.adapter);
139
161
  if (parsed) {
140
162
  ctx.selectDate(parsed);
141
163
  }
@@ -154,7 +176,7 @@ var DatePickerInput = forwardRef(
154
176
  setInputText(null);
155
177
  return;
156
178
  }
157
- const parsed = parseInputValue(text, displayFormat, ctx.adapter);
179
+ const parsed = parseInputValue(text, ctx.adapter);
158
180
  if (parsed) {
159
181
  ctx.selectDate(parsed);
160
182
  setInputText(null);
@@ -168,7 +190,7 @@ var DatePickerInput = forwardRef(
168
190
  ctx.close();
169
191
  } else if (e.key === "Enter") {
170
192
  if (inputText !== null) {
171
- const parsed = parseInputValue(inputText, displayFormat, ctx.adapter);
193
+ const parsed = parseInputValue(inputText, ctx.adapter);
172
194
  if (parsed) {
173
195
  ctx.selectDate(parsed);
174
196
  setInputText(null);
@@ -202,7 +224,7 @@ var DatePickerInput = forwardRef(
202
224
  disabled: ctx.isDisabled || props.disabled,
203
225
  readOnly: ctx.isReadOnly,
204
226
  onChange: handleChange,
205
- onFocus: handleFocus,
227
+ onClick: handleClick,
206
228
  onBlur: handleBlur,
207
229
  onKeyDown: handleKeyDown,
208
230
  ...props
@@ -231,7 +253,7 @@ var DatePickerTrigger = forwardRef(
231
253
  },
232
254
  type: "button",
233
255
  tabIndex: 0,
234
- "aria-label": ctx.isOpen ? "\uCE98\uB9B0\uB354 \uB2EB\uAE30" : "\uCE98\uB9B0\uB354 \uC5F4\uAE30",
256
+ "aria-label": ctx.isOpen ? ctx.labels.triggerClose : ctx.labels.triggerOpen,
235
257
  "aria-expanded": ctx.isOpen,
236
258
  "aria-controls": ctx.isOpen ? calendarId : void 0,
237
259
  disabled: ctx.isDisabled || props.disabled,
@@ -263,38 +285,39 @@ var DatePickerTrigger = forwardRef(
263
285
  );
264
286
  }
265
287
  );
266
- function DatePickerPopover({ children, ...props }) {
267
- const ctx = useDatePickerContext("DatePicker.Popover");
268
- const calendarId = `${ctx.pickerId}-calendar`;
288
+ function usePopover({ isOpen, close, referenceRef, placement = "bottom-start" }) {
269
289
  const floatingRef = useRef(null);
290
+ const previousFocusRef = useRef(null);
270
291
  const { refs, floatingStyles } = useFloating({
271
- open: ctx.isOpen,
272
- placement: "bottom-start",
292
+ open: isOpen,
293
+ placement,
273
294
  middleware: [offset(4), flip(), shift({ padding: 8 })],
274
295
  whileElementsMounted: autoUpdate
275
296
  });
276
297
  useEffect(() => {
277
- if (ctx.referenceRef.current) {
278
- refs.setReference(ctx.referenceRef.current);
298
+ if (referenceRef.current) {
299
+ refs.setReference(referenceRef.current);
279
300
  }
280
- }, [ctx.referenceRef, refs, ctx.isOpen]);
281
- const previousFocusRef = useRef(null);
301
+ }, [referenceRef, refs, isOpen]);
282
302
  useEffect(() => {
283
- if (ctx.isOpen) {
303
+ if (isOpen) {
284
304
  previousFocusRef.current = document.activeElement;
285
305
  } else if (previousFocusRef.current) {
286
- previousFocusRef.current.focus();
306
+ const el = previousFocusRef.current;
287
307
  previousFocusRef.current = null;
308
+ if (el !== referenceRef.current && typeof el.focus === "function") {
309
+ el.focus({ preventScroll: true });
310
+ }
288
311
  }
289
- }, [ctx.isOpen]);
312
+ }, [isOpen, referenceRef]);
290
313
  useEffect(() => {
291
- if (!ctx.isOpen) return;
314
+ if (!isOpen) return;
292
315
  function handleClickOutside(e) {
293
316
  const floating = floatingRef.current;
294
- const reference = ctx.referenceRef.current;
317
+ const reference = referenceRef.current;
295
318
  const target = e.target;
296
319
  if (floating && !floating.contains(target) && (!reference || !reference.contains(target))) {
297
- ctx.close();
320
+ close();
298
321
  }
299
322
  }
300
323
  const timer = setTimeout(() => {
@@ -304,28 +327,39 @@ function DatePickerPopover({ children, ...props }) {
304
327
  clearTimeout(timer);
305
328
  document.removeEventListener("mousedown", handleClickOutside);
306
329
  };
307
- }, [ctx.isOpen, ctx]);
330
+ }, [isOpen, close, referenceRef]);
308
331
  useEffect(() => {
309
- if (!ctx.isOpen) return;
332
+ if (!isOpen) return;
310
333
  function handleKeyDown(e) {
311
334
  if (e.key === "Escape") {
312
- ctx.close();
335
+ close();
313
336
  }
314
337
  }
315
338
  document.addEventListener("keydown", handleKeyDown);
316
339
  return () => document.removeEventListener("keydown", handleKeyDown);
317
- }, [ctx.isOpen, ctx]);
340
+ }, [isOpen, close]);
341
+ const setFloatingRef = (node) => {
342
+ floatingRef.current = node;
343
+ refs.setFloating(node);
344
+ };
345
+ return { floatingStyles, setFloatingRef };
346
+ }
347
+ function DatePickerPopover({ children, ...props }) {
348
+ const ctx = useDatePickerContext("DatePicker.Popover");
349
+ const calendarId = `${ctx.pickerId}-calendar`;
350
+ const { floatingStyles, setFloatingRef } = usePopover({
351
+ isOpen: ctx.isOpen,
352
+ close: ctx.close,
353
+ referenceRef: ctx.referenceRef
354
+ });
318
355
  if (!ctx.isOpen) return null;
319
356
  return /* @__PURE__ */ jsx(
320
357
  "div",
321
358
  {
322
- ref: (node) => {
323
- floatingRef.current = node;
324
- refs.setFloating(node);
325
- },
359
+ ref: setFloatingRef,
326
360
  id: calendarId,
327
361
  role: "dialog",
328
- "aria-label": "\uB0A0\uC9DC \uC120\uD0DD",
362
+ "aria-label": ctx.labels.popoverLabel,
329
363
  "aria-modal": "false",
330
364
  style: floatingStyles,
331
365
  ...props,
@@ -333,6 +367,13 @@ function DatePickerPopover({ children, ...props }) {
333
367
  }
334
368
  );
335
369
  }
370
+ function safeFormatFullDate(iso, locale) {
371
+ try {
372
+ return formatFullDate(iso, locale);
373
+ } catch {
374
+ return iso;
375
+ }
376
+ }
336
377
  var srOnly = {
337
378
  position: "absolute",
338
379
  width: "1px",
@@ -348,13 +389,14 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
348
389
  const ctx = useDatePickerContext("DatePicker.Calendar");
349
390
  const gridRef = useRef(null);
350
391
  const [announcement, setAnnouncement] = useState("");
351
- const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale } = ctx;
392
+ const { adapter, viewMonth, focusedDate, weekStartsOn, disabled, locale, displayTimezone } = ctx;
352
393
  const weekdays = getWeekdayNames(locale, weekStartsOn);
353
394
  const weeks = getCalendarDays(viewMonth, adapter, {
354
395
  weekStartsOn,
355
396
  selected: ctx.value,
356
397
  focusedDate,
357
- disabled
398
+ disabled,
399
+ timezone: displayTimezone
358
400
  });
359
401
  const year = adapter.getYear(viewMonth);
360
402
  const month = adapter.getMonth(viewMonth);
@@ -364,7 +406,7 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
364
406
  const focusedButton = gridRef.current.querySelector(
365
407
  '[data-focused="true"]'
366
408
  );
367
- focusedButton?.focus();
409
+ focusedButton?.focus({ preventScroll: true });
368
410
  }, [focusedDate, ctx.isOpen]);
369
411
  const navigateMonth = useCallback(
370
412
  (direction) => {
@@ -381,7 +423,7 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
381
423
  (day) => {
382
424
  if (day.isDisabled) return;
383
425
  ctx.selectDate(day.isoString);
384
- setAnnouncement(formatFullDate(day.isoString, locale));
426
+ setAnnouncement(safeFormatFullDate(day.isoString, locale));
385
427
  },
386
428
  [ctx, locale]
387
429
  );
@@ -453,7 +495,7 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
453
495
  type: "button",
454
496
  className: classNames?.navButton,
455
497
  onClick: () => navigateMonth(-1),
456
- "aria-label": "\uC774\uC804 \uB2EC",
498
+ "aria-label": ctx.labels.prevMonth,
457
499
  children: "<"
458
500
  }
459
501
  ),
@@ -473,7 +515,7 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
473
515
  type: "button",
474
516
  className: classNames?.navButton,
475
517
  onClick: () => navigateMonth(1),
476
- "aria-label": "\uB2E4\uC74C \uB2EC",
518
+ "aria-label": ctx.labels.nextMonth,
477
519
  children: ">"
478
520
  }
479
521
  )
@@ -526,7 +568,7 @@ function DatePickerCalendar({ classNames, onTitleClick, ...props }) {
526
568
  "data-outside-month": !day.isCurrentMonth || void 0,
527
569
  className: dayClasses,
528
570
  onClick: () => handleDayClick(day),
529
- "aria-label": formatFullDate(day.isoString, locale),
571
+ "aria-label": safeFormatFullDate(day.isoString, locale),
530
572
  children: day.dayNumber
531
573
  }
532
574
  )
@@ -582,7 +624,7 @@ function DatePickerMonthGrid({
582
624
  type: "button",
583
625
  className: classNames?.navButton,
584
626
  onClick: () => navigateYear(-1),
585
- "aria-label": "\uC774\uC804 \uB144",
627
+ "aria-label": ctx.labels.prevYear,
586
628
  children: "<"
587
629
  }
588
630
  ),
@@ -601,7 +643,7 @@ function DatePickerMonthGrid({
601
643
  type: "button",
602
644
  className: classNames?.navButton,
603
645
  onClick: () => navigateYear(1),
604
- "aria-label": "\uB2E4\uC74C \uB144",
646
+ "aria-label": ctx.labels.nextYear,
605
647
  children: ">"
606
648
  }
607
649
  )
@@ -686,7 +728,7 @@ function DatePickerYearGrid({
686
728
  type: "button",
687
729
  className: classNames?.navButton,
688
730
  onClick: () => navigateDecade(-1),
689
- "aria-label": "\uC774\uC804 12\uB144",
731
+ "aria-label": ctx.labels.prevDecade,
690
732
  children: "<"
691
733
  }
692
734
  ),
@@ -697,7 +739,7 @@ function DatePickerYearGrid({
697
739
  type: "button",
698
740
  className: classNames?.navButton,
699
741
  onClick: () => navigateDecade(1),
700
- "aria-label": "\uB2E4\uC74C 12\uB144",
742
+ "aria-label": ctx.labels.nextDecade,
701
743
  children: ">"
702
744
  }
703
745
  )
@@ -770,7 +812,9 @@ function RangePickerRoot({
770
812
  weekStartsOn = 0,
771
813
  displayFormat = "yyyy-MM-dd",
772
814
  locale = "en-US",
815
+ displayTimezone,
773
816
  adapter = DateFnsAdapter,
817
+ labels: labelsProp,
774
818
  children
775
819
  }) {
776
820
  const pickerId = useId();
@@ -784,13 +828,20 @@ function RangePickerRoot({
784
828
  const [selectingTarget, setSelectingTarget] = useState("start");
785
829
  const [hoverDate, setHoverDate] = useState(null);
786
830
  const [viewMonth, setViewMonth] = useState(
787
- currentValue.start ?? adapter.today()
831
+ currentValue.start ?? adapter.today(displayTimezone)
788
832
  );
789
833
  const [focusedDate, setFocusedDate] = useState(
790
- currentValue.start ?? adapter.today()
834
+ currentValue.start ?? adapter.today(displayTimezone)
835
+ );
836
+ const mergedLabels = useMemo(
837
+ () => ({ ...DEFAULT_RANGEPICKER_LABELS, ...labelsProp }),
838
+ [labelsProp]
791
839
  );
792
840
  const isDisabled = typeof disabled === "boolean" ? disabled : false;
793
- const disabledRules = Array.isArray(disabled) ? disabled : [];
841
+ const disabledRules = useMemo(
842
+ () => Array.isArray(disabled) ? disabled : [],
843
+ [disabled]
844
+ );
794
845
  const setRange = useCallback(
795
846
  (range) => {
796
847
  if (isDisabled || readOnly) return;
@@ -804,23 +855,24 @@ function RangePickerRoot({
804
855
  const selectDate = useCallback(
805
856
  (iso) => {
806
857
  if (isDisabled || readOnly) return;
858
+ const normalized = displayTimezone ? civilMidnightFromUtcDay(iso, displayTimezone) : iso;
807
859
  if (selectingTarget === "start") {
808
- const newRange = { start: iso, end: null };
860
+ const newRange = { start: normalized, end: null };
809
861
  setRange(newRange);
810
862
  setSelectingTarget("end");
811
863
  setHoverDate(null);
812
864
  } else {
813
865
  const start = currentValue.start;
814
866
  if (!start) {
815
- setRange({ start: iso, end: null });
867
+ setRange({ start: normalized, end: null });
816
868
  setSelectingTarget("end");
817
869
  return;
818
870
  }
819
871
  let newRange;
820
- if (adapter.isBefore(iso, start)) {
821
- newRange = { start: iso, end: start };
872
+ if (adapter.isBefore(normalized, start)) {
873
+ newRange = { start: normalized, end: start };
822
874
  } else {
823
- newRange = { start, end: iso };
875
+ newRange = { start, end: normalized };
824
876
  }
825
877
  setRange(newRange);
826
878
  setSelectingTarget("start");
@@ -828,18 +880,18 @@ function RangePickerRoot({
828
880
  setIsOpen(false);
829
881
  }
830
882
  },
831
- [isDisabled, readOnly, selectingTarget, currentValue.start, adapter, setRange]
883
+ [isDisabled, readOnly, selectingTarget, currentValue.start, adapter, setRange, displayTimezone]
832
884
  );
833
885
  const open = useCallback(() => {
834
886
  if (isDisabled || readOnly) return;
835
887
  setIsOpen(true);
836
- const target = currentValue.start ?? adapter.today();
888
+ const target = currentValue.start ?? adapter.today(displayTimezone);
837
889
  setViewMonth(target);
838
890
  setFocusedDate(target);
839
891
  if (currentValue.start && currentValue.end) {
840
892
  setSelectingTarget("start");
841
893
  }
842
- }, [isDisabled, readOnly, currentValue, adapter]);
894
+ }, [isDisabled, readOnly, currentValue, adapter, displayTimezone]);
843
895
  const close = useCallback(() => {
844
896
  setIsOpen(false);
845
897
  setHoverDate(null);
@@ -870,9 +922,11 @@ function RangePickerRoot({
870
922
  weekStartsOn,
871
923
  displayFormat,
872
924
  locale,
925
+ displayTimezone,
873
926
  isDisabled,
874
927
  isReadOnly: readOnly,
875
- pickerId
928
+ pickerId,
929
+ labels: mergedLabels
876
930
  }),
877
931
  [
878
932
  currentValue,
@@ -891,25 +945,34 @@ function RangePickerRoot({
891
945
  weekStartsOn,
892
946
  displayFormat,
893
947
  locale,
948
+ displayTimezone,
894
949
  isDisabled,
895
950
  readOnly,
896
- pickerId
951
+ pickerId,
952
+ mergedLabels
897
953
  ]
898
954
  );
899
955
  return /* @__PURE__ */ jsx(RangePickerContext.Provider, { value: contextValue, children });
900
956
  }
901
957
  var RangePickerInput = forwardRef(
902
- function RangePickerInput2({ part, format: formatProp, onFocus, onKeyDown, ...props }, ref) {
958
+ function RangePickerInput2({ part, format: formatProp, onClick, onKeyDown, ...props }, ref) {
903
959
  const ctx = useRangePickerContext("RangePicker.Input");
904
960
  const displayFormat = formatProp ?? ctx.displayFormat;
905
961
  const value = ctx.value[part];
906
- const displayValue = value ? ctx.adapter.format(value, displayFormat) : "";
907
- const handleFocus = useCallback(
962
+ let displayValue = "";
963
+ if (value) {
964
+ try {
965
+ displayValue = ctx.adapter.format(value, displayFormat, ctx.displayTimezone);
966
+ } catch {
967
+ displayValue = value;
968
+ }
969
+ }
970
+ const handleClick = useCallback(
908
971
  (e) => {
909
- ctx.open();
910
- onFocus?.(e);
972
+ if (!ctx.isOpen) ctx.open();
973
+ onClick?.(e);
911
974
  },
912
- [ctx, onFocus]
975
+ [ctx, onClick]
913
976
  );
914
977
  const handleKeyDown = useCallback(
915
978
  (e) => {
@@ -939,11 +1002,11 @@ var RangePickerInput = forwardRef(
939
1002
  "aria-haspopup": "dialog",
940
1003
  "aria-controls": ctx.isOpen ? calendarId : void 0,
941
1004
  "aria-autocomplete": "none",
942
- "aria-label": part === "start" ? "\uC2DC\uC791\uC77C" : "\uC885\uB8CC\uC77C",
1005
+ "aria-label": part === "start" ? ctx.labels.startInput : ctx.labels.endInput,
943
1006
  autoComplete: "off",
944
1007
  value: displayValue,
945
1008
  disabled: ctx.isDisabled || props.disabled,
946
- onFocus: handleFocus,
1009
+ onClick: handleClick,
947
1010
  onKeyDown: handleKeyDown,
948
1011
  "data-part": part,
949
1012
  ...props
@@ -954,66 +1017,19 @@ var RangePickerInput = forwardRef(
954
1017
  function RangePickerPopover({ children, ...props }) {
955
1018
  const ctx = useRangePickerContext("RangePicker.Popover");
956
1019
  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
1020
+ const { floatingStyles, setFloatingRef } = usePopover({
1021
+ isOpen: ctx.isOpen,
1022
+ close: ctx.close,
1023
+ referenceRef: ctx.referenceRef
963
1024
  });
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
1025
  if (!ctx.isOpen) return null;
1007
1026
  return /* @__PURE__ */ jsx(
1008
1027
  "div",
1009
1028
  {
1010
- ref: (node) => {
1011
- floatingRef.current = node;
1012
- refs.setFloating(node);
1013
- },
1029
+ ref: setFloatingRef,
1014
1030
  id: calendarId,
1015
1031
  role: "dialog",
1016
- "aria-label": "\uB0A0\uC9DC \uBC94\uC704 \uC120\uD0DD",
1032
+ "aria-label": ctx.labels.popoverLabel,
1017
1033
  "aria-modal": "false",
1018
1034
  style: floatingStyles,
1019
1035
  ...props,
@@ -1021,6 +1037,13 @@ function RangePickerPopover({ children, ...props }) {
1021
1037
  }
1022
1038
  );
1023
1039
  }
1040
+ function safeFormatFullDate2(iso, locale) {
1041
+ try {
1042
+ return formatFullDate(iso, locale);
1043
+ } catch {
1044
+ return iso;
1045
+ }
1046
+ }
1024
1047
  var srOnly2 = {
1025
1048
  position: "absolute",
1026
1049
  width: "1px",
@@ -1044,7 +1067,8 @@ function RangePickerCalendar({ classNames, ...props }) {
1044
1067
  disabled,
1045
1068
  value,
1046
1069
  hoverDate,
1047
- selectingTarget
1070
+ selectingTarget,
1071
+ displayTimezone
1048
1072
  } = ctx;
1049
1073
  const { locale } = ctx;
1050
1074
  const weekdays = getWeekdayNames(locale, weekStartsOn);
@@ -1053,7 +1077,8 @@ function RangePickerCalendar({ classNames, ...props }) {
1053
1077
  focusedDate,
1054
1078
  disabled,
1055
1079
  range: value,
1056
- rangeHover: hoverDate
1080
+ rangeHover: hoverDate,
1081
+ timezone: displayTimezone
1057
1082
  });
1058
1083
  const year = adapter.getYear(viewMonth);
1059
1084
  const month = adapter.getMonth(viewMonth);
@@ -1063,7 +1088,7 @@ function RangePickerCalendar({ classNames, ...props }) {
1063
1088
  const focusedButton = gridRef.current.querySelector(
1064
1089
  '[data-focused="true"]'
1065
1090
  );
1066
- focusedButton?.focus();
1091
+ focusedButton?.focus({ preventScroll: true });
1067
1092
  }, [focusedDate, ctx.isOpen]);
1068
1093
  const navigateMonth = useCallback(
1069
1094
  (direction) => {
@@ -1080,7 +1105,7 @@ function RangePickerCalendar({ classNames, ...props }) {
1080
1105
  (day) => {
1081
1106
  if (day.isDisabled) return;
1082
1107
  ctx.selectDate(day.isoString);
1083
- setAnnouncement(formatFullDate(day.isoString, locale));
1108
+ setAnnouncement(safeFormatFullDate2(day.isoString, locale));
1084
1109
  },
1085
1110
  [ctx, locale]
1086
1111
  );
@@ -1157,7 +1182,7 @@ function RangePickerCalendar({ classNames, ...props }) {
1157
1182
  type: "button",
1158
1183
  className: classNames?.navButton,
1159
1184
  onClick: () => navigateMonth(-1),
1160
- "aria-label": "\uC774\uC804 \uB2EC",
1185
+ "aria-label": ctx.labels.prevMonth,
1161
1186
  children: "<"
1162
1187
  }
1163
1188
  ),
@@ -1168,7 +1193,7 @@ function RangePickerCalendar({ classNames, ...props }) {
1168
1193
  type: "button",
1169
1194
  className: classNames?.navButton,
1170
1195
  onClick: () => navigateMonth(1),
1171
- "aria-label": "\uB2E4\uC74C \uB2EC",
1196
+ "aria-label": ctx.labels.nextMonth,
1172
1197
  children: ">"
1173
1198
  }
1174
1199
  )
@@ -1228,7 +1253,7 @@ function RangePickerCalendar({ classNames, ...props }) {
1228
1253
  className: dayClasses,
1229
1254
  onClick: () => handleDayClick(day),
1230
1255
  onMouseEnter: () => handleDayMouseEnter(day),
1231
- "aria-label": formatFullDate(day.isoString, locale),
1256
+ "aria-label": safeFormatFullDate2(day.isoString, locale),
1232
1257
  children: day.dayNumber
1233
1258
  }
1234
1259
  )
@@ -1243,7 +1268,8 @@ function RangePickerCalendar({ classNames, ...props }) {
1243
1268
  ] });
1244
1269
  }
1245
1270
  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 });
1271
+ const ctx = useRangePickerContext("RangePicker.Presets");
1272
+ return /* @__PURE__ */ jsx("div", { role: "group", "aria-label": ctx.labels.presetsGroup, className: classNames?.root, ...props, children });
1247
1273
  }
1248
1274
  function resolvePreset(key, today, adapter) {
1249
1275
  switch (key) {
@@ -1282,8 +1308,9 @@ function resolvePreset(key, today, adapter) {
1282
1308
  };
1283
1309
  }
1284
1310
  case "thisYear": {
1311
+ const currentMonth = new Date(today).getUTCMonth();
1285
1312
  const yearStart = adapter.startOfMonth(
1286
- adapter.addMonths(today, -new Date(today).getUTCMonth())
1313
+ adapter.addMonths(today, -currentMonth)
1287
1314
  );
1288
1315
  return { start: yearStart, end: today };
1289
1316
  }
@@ -1365,10 +1392,7 @@ function useTimePickerContext(componentName) {
1365
1392
  return context;
1366
1393
  }
1367
1394
  function getDefaultIso() {
1368
- const now = /* @__PURE__ */ new Date();
1369
- return new Date(
1370
- Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
1371
- ).toISOString();
1395
+ return DateFnsAdapter.today();
1372
1396
  }
1373
1397
  function TimePickerRoot({
1374
1398
  value: controlledValue,
@@ -1377,28 +1401,37 @@ function TimePickerRoot({
1377
1401
  format = "24h",
1378
1402
  step = 1,
1379
1403
  withSeconds = false,
1404
+ displayTimezone,
1380
1405
  disabled = false,
1381
1406
  readOnly = false,
1407
+ labels: labelsProp,
1382
1408
  children
1383
1409
  }) {
1384
1410
  const pickerId = useId();
1411
+ const mergedLabels = useMemo(
1412
+ () => ({ ...DEFAULT_TIMEPICKER_LABELS, ...labelsProp }),
1413
+ [labelsProp]
1414
+ );
1385
1415
  const isControlled = useRef(controlledValue !== void 0).current;
1386
1416
  const [uncontrolledValue, setUncontrolledValue] = useState(
1387
1417
  defaultValue ?? null
1388
1418
  );
1389
1419
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1390
1420
  const baseIso = currentValue ?? getDefaultIso();
1391
- const currentTime = useMemo(() => getTime(baseIso), [baseIso]);
1421
+ const currentTime = useMemo(
1422
+ () => displayTimezone ? getTimeInTimezone(baseIso, displayTimezone) : getTime(baseIso),
1423
+ [baseIso, displayTimezone]
1424
+ );
1392
1425
  const setTime$1 = useCallback(
1393
1426
  (partial) => {
1394
1427
  if (disabled || readOnly) return;
1395
- const newIso = setTime(baseIso, partial);
1428
+ const newIso = displayTimezone ? setTimeInTimezone(baseIso, partial, displayTimezone) : setTime(baseIso, partial);
1396
1429
  if (!isControlled) {
1397
1430
  setUncontrolledValue(newIso);
1398
1431
  }
1399
1432
  onChange?.(newIso);
1400
1433
  },
1401
- [disabled, readOnly, baseIso, isControlled, onChange]
1434
+ [disabled, readOnly, baseIso, isControlled, onChange, displayTimezone]
1402
1435
  );
1403
1436
  const contextValue = useMemo(
1404
1437
  () => ({
@@ -1407,12 +1440,14 @@ function TimePickerRoot({
1407
1440
  format,
1408
1441
  step,
1409
1442
  withSeconds,
1443
+ displayTimezone,
1410
1444
  isDisabled: disabled,
1411
1445
  isReadOnly: readOnly,
1412
1446
  currentTime,
1413
- pickerId
1447
+ pickerId,
1448
+ labels: mergedLabels
1414
1449
  }),
1415
- [currentValue, setTime$1, format, step, withSeconds, disabled, readOnly, currentTime, pickerId]
1450
+ [currentValue, setTime$1, format, step, withSeconds, displayTimezone, disabled, readOnly, currentTime, pickerId, mergedLabels]
1416
1451
  );
1417
1452
  return /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: contextValue, children });
1418
1453
  }
@@ -1458,7 +1493,7 @@ var TimePickerInput = forwardRef(
1458
1493
  type: "text",
1459
1494
  inputMode: "numeric",
1460
1495
  autoComplete: "off",
1461
- "aria-label": "\uC2DC\uAC04 \uC785\uB825",
1496
+ "aria-label": ctx.labels.timeInput,
1462
1497
  placeholder: ctx.withSeconds ? "HH:MM:SS" : "HH:MM",
1463
1498
  value: displayValue,
1464
1499
  disabled: ctx.isDisabled || props.disabled,
@@ -1471,46 +1506,42 @@ var TimePickerInput = forwardRef(
1471
1506
  );
1472
1507
  }
1473
1508
  );
1474
- function TimePickerHourList({ classNames, ...props }) {
1475
- const ctx = useTimePickerContext("TimePicker.HourList");
1476
- const { format, currentTime, isDisabled, isReadOnly } = ctx;
1509
+ function useListboxNavigation({
1510
+ items,
1511
+ onSelect,
1512
+ disabled = false
1513
+ }) {
1477
1514
  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
- );
1515
+ const rafIdRef = useRef(0);
1516
+ useEffect(() => {
1517
+ return () => cancelAnimationFrame(rafIdRef.current);
1518
+ }, []);
1489
1519
  const handleKeyDown = useCallback(
1490
- (e, hour) => {
1491
- if (isDisabled || isReadOnly) return;
1492
- const currentIndex = hours.indexOf(hour);
1520
+ (e, item) => {
1521
+ if (disabled) return;
1522
+ const currentIndex = items.indexOf(item);
1493
1523
  let newIndex = -1;
1494
1524
  if (e.key === "ArrowDown") {
1495
- newIndex = Math.min(currentIndex + 1, hours.length - 1);
1525
+ newIndex = Math.min(currentIndex + 1, items.length - 1);
1496
1526
  } else if (e.key === "ArrowUp") {
1497
1527
  newIndex = Math.max(currentIndex - 1, 0);
1498
1528
  } else if (e.key === "Home") {
1499
1529
  newIndex = 0;
1500
1530
  } else if (e.key === "End") {
1501
- newIndex = hours.length - 1;
1531
+ newIndex = items.length - 1;
1502
1532
  } else if (e.key === "Enter" || e.key === " ") {
1503
1533
  e.preventDefault();
1504
- handleSelect(hour);
1534
+ onSelect(item);
1505
1535
  return;
1506
1536
  } else {
1507
1537
  return;
1508
1538
  }
1509
1539
  e.preventDefault();
1510
- const targetHour = hours[newIndex];
1511
- if (targetHour !== void 0) {
1512
- handleSelect(targetHour);
1513
- requestAnimationFrame(() => {
1540
+ const target = items[newIndex];
1541
+ if (target !== void 0) {
1542
+ onSelect(target);
1543
+ cancelAnimationFrame(rafIdRef.current);
1544
+ rafIdRef.current = requestAnimationFrame(() => {
1514
1545
  const next = listRef.current?.querySelector(
1515
1546
  '[data-selected="true"]'
1516
1547
  );
@@ -1518,14 +1549,35 @@ function TimePickerHourList({ classNames, ...props }) {
1518
1549
  });
1519
1550
  }
1520
1551
  },
1521
- [hours, handleSelect, isDisabled, isReadOnly]
1552
+ [items, onSelect, disabled]
1553
+ );
1554
+ return { listRef, handleKeyDown };
1555
+ }
1556
+ function TimePickerHourList({ classNames, ...props }) {
1557
+ const ctx = useTimePickerContext("TimePicker.HourList");
1558
+ const { format, currentTime, isDisabled, isReadOnly } = ctx;
1559
+ const hours = generateHours(format);
1560
+ const selectedHourDisplay = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;
1561
+ const currentPeriod = format === "12h" ? to12Hour(currentTime.hours).period : null;
1562
+ const handleSelect = useCallback(
1563
+ (hourDisplay) => {
1564
+ if (isDisabled || isReadOnly) return;
1565
+ const hours24 = format === "12h" && currentPeriod ? to24Hour(hourDisplay, currentPeriod) : hourDisplay;
1566
+ ctx.setTime({ hours: hours24 });
1567
+ },
1568
+ [format, currentPeriod, ctx, isDisabled, isReadOnly]
1522
1569
  );
1570
+ const { listRef, handleKeyDown } = useListboxNavigation({
1571
+ items: hours,
1572
+ onSelect: handleSelect,
1573
+ disabled: isDisabled || isReadOnly
1574
+ });
1523
1575
  return /* @__PURE__ */ jsx(
1524
1576
  "ul",
1525
1577
  {
1526
1578
  ref: listRef,
1527
1579
  role: "listbox",
1528
- "aria-label": "\uC2DC",
1580
+ "aria-label": ctx.labels.hourList,
1529
1581
  "aria-disabled": isDisabled || void 0,
1530
1582
  className: classNames?.root,
1531
1583
  ...props,
@@ -1538,7 +1590,7 @@ function TimePickerHourList({ classNames, ...props }) {
1538
1590
  role: "option",
1539
1591
  "aria-selected": isSelected,
1540
1592
  "aria-disabled": isDisabled || void 0,
1541
- "aria-label": `${hour}\uC2DC`,
1593
+ "aria-label": ctx.labels.hourOption(hour),
1542
1594
  "data-selected": isSelected || void 0,
1543
1595
  tabIndex: isSelected ? 0 : -1,
1544
1596
  className: optionClass,
@@ -1555,7 +1607,6 @@ function TimePickerHourList({ classNames, ...props }) {
1555
1607
  function TimePickerMinuteList({ classNames, ...props }) {
1556
1608
  const ctx = useTimePickerContext("TimePicker.MinuteList");
1557
1609
  const { step, currentTime, isDisabled, isReadOnly } = ctx;
1558
- const listRef = useRef(null);
1559
1610
  const minutes = generateMinutes(step);
1560
1611
  const handleSelect = useCallback(
1561
1612
  (minute) => {
@@ -1564,46 +1615,17 @@ function TimePickerMinuteList({ classNames, ...props }) {
1564
1615
  },
1565
1616
  [ctx, isDisabled, isReadOnly]
1566
1617
  );
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
- );
1618
+ const { listRef, handleKeyDown } = useListboxNavigation({
1619
+ items: minutes,
1620
+ onSelect: handleSelect,
1621
+ disabled: isDisabled || isReadOnly
1622
+ });
1601
1623
  return /* @__PURE__ */ jsx(
1602
1624
  "ul",
1603
1625
  {
1604
1626
  ref: listRef,
1605
1627
  role: "listbox",
1606
- "aria-label": "\uBD84",
1628
+ "aria-label": ctx.labels.minuteList,
1607
1629
  "aria-disabled": isDisabled || void 0,
1608
1630
  className: classNames?.root,
1609
1631
  ...props,
@@ -1616,7 +1638,7 @@ function TimePickerMinuteList({ classNames, ...props }) {
1616
1638
  role: "option",
1617
1639
  "aria-selected": isSelected,
1618
1640
  "aria-disabled": isDisabled || void 0,
1619
- "aria-label": `${minute}\uBD84`,
1641
+ "aria-label": ctx.labels.minuteOption(minute),
1620
1642
  "data-selected": isSelected || void 0,
1621
1643
  tabIndex: isSelected ? 0 : -1,
1622
1644
  className: optionClass,
@@ -1659,7 +1681,7 @@ function TimePickerAmPmToggle({ classNames, ...props }) {
1659
1681
  }
1660
1682
  );
1661
1683
  };
1662
- return /* @__PURE__ */ jsxs("div", { role: "radiogroup", "aria-label": "\uC624\uC804/\uC624\uD6C4", className: classNames?.root, ...props, children: [
1684
+ return /* @__PURE__ */ jsxs("div", { role: "radiogroup", "aria-label": ctx.labels.amPmToggle, className: classNames?.root, ...props, children: [
1663
1685
  renderButton("AM"),
1664
1686
  renderButton("PM")
1665
1687
  ] });
@@ -1673,10 +1695,7 @@ var TimePicker = Object.assign(TimePickerRoot, {
1673
1695
  AmPmToggle: TimePickerAmPmToggle
1674
1696
  });
1675
1697
  function getDefaultIso2() {
1676
- const now = /* @__PURE__ */ new Date();
1677
- return new Date(
1678
- Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
1679
- ).toISOString();
1698
+ return DateFnsAdapter.today();
1680
1699
  }
1681
1700
  function DateTimePickerRoot({
1682
1701
  value: controlledValue,
@@ -1689,10 +1708,20 @@ function DateTimePickerRoot({
1689
1708
  weekStartsOn = 0,
1690
1709
  displayFormat = "yyyy-MM-dd HH:mm",
1691
1710
  locale = "en-US",
1711
+ displayTimezone,
1692
1712
  adapter = DateFnsAdapter,
1713
+ labels: labelsProp,
1693
1714
  children
1694
1715
  }) {
1695
1716
  const pickerId = useId();
1717
+ const mergedDateLabels = useMemo(
1718
+ () => ({ ...DEFAULT_DATEPICKER_LABELS, ...labelsProp }),
1719
+ [labelsProp]
1720
+ );
1721
+ const mergedTimeLabels = useMemo(
1722
+ () => ({ ...DEFAULT_TIMEPICKER_LABELS, ...labelsProp }),
1723
+ [labelsProp]
1724
+ );
1696
1725
  const isControlled = useRef(controlledValue !== void 0).current;
1697
1726
  const referenceRef = useRef(null);
1698
1727
  const [uncontrolledValue, setUncontrolledValue] = useState(
@@ -1701,15 +1730,21 @@ function DateTimePickerRoot({
1701
1730
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1702
1731
  const [isOpen, setIsOpen] = useState(false);
1703
1732
  const [viewMonth, setViewMonth] = useState(
1704
- currentValue ?? adapter.today()
1733
+ currentValue ?? adapter.today(displayTimezone)
1705
1734
  );
1706
1735
  const [focusedDate, setFocusedDate] = useState(
1707
- currentValue ?? adapter.today()
1736
+ currentValue ?? adapter.today(displayTimezone)
1708
1737
  );
1709
1738
  const isDisabled = typeof disabled === "boolean" ? disabled : false;
1710
- const disabledRules = Array.isArray(disabled) ? disabled : [];
1739
+ const disabledRules = useMemo(
1740
+ () => Array.isArray(disabled) ? disabled : [],
1741
+ [disabled]
1742
+ );
1711
1743
  const baseIso = currentValue ?? getDefaultIso2();
1712
- const currentTime = useMemo(() => getTime(baseIso), [baseIso]);
1744
+ const currentTime = useMemo(
1745
+ () => displayTimezone ? getTimeInTimezone(baseIso, displayTimezone) : getTime(baseIso),
1746
+ [baseIso, displayTimezone]
1747
+ );
1713
1748
  const updateValue = useCallback(
1714
1749
  (next) => {
1715
1750
  if (isDisabled || readOnly) return;
@@ -1726,27 +1761,28 @@ function DateTimePickerRoot({
1726
1761
  updateValue(null);
1727
1762
  return;
1728
1763
  }
1729
- const time = currentValue ? getTime(currentValue) : currentTime;
1730
- const merged = setTime(newDateIso, time);
1764
+ const normalizedDate = displayTimezone ? civilMidnightFromUtcDay(newDateIso, displayTimezone) : newDateIso;
1765
+ const time = currentValue ? displayTimezone ? getTimeInTimezone(currentValue, displayTimezone) : getTime(currentValue) : currentTime;
1766
+ const merged = displayTimezone ? setTimeInTimezone(normalizedDate, time, displayTimezone) : setTime(normalizedDate, time);
1731
1767
  updateValue(merged);
1732
1768
  },
1733
- [currentValue, currentTime, updateValue]
1769
+ [currentValue, currentTime, updateValue, displayTimezone]
1734
1770
  );
1735
1771
  const setTime$1 = useCallback(
1736
1772
  (partial) => {
1737
1773
  const base = currentValue ?? getDefaultIso2();
1738
- const merged = setTime(base, partial);
1774
+ const merged = displayTimezone ? setTimeInTimezone(base, partial, displayTimezone) : setTime(base, partial);
1739
1775
  updateValue(merged);
1740
1776
  },
1741
- [currentValue, updateValue]
1777
+ [currentValue, updateValue, displayTimezone]
1742
1778
  );
1743
1779
  const open = useCallback(() => {
1744
1780
  if (isDisabled || readOnly) return;
1745
1781
  setIsOpen(true);
1746
- const target = currentValue ?? adapter.today();
1782
+ const target = currentValue ?? adapter.today(displayTimezone);
1747
1783
  setViewMonth(target);
1748
1784
  setFocusedDate(target);
1749
- }, [isDisabled, readOnly, currentValue, adapter]);
1785
+ }, [isDisabled, readOnly, currentValue, adapter, displayTimezone]);
1750
1786
  const close = useCallback(() => {
1751
1787
  setIsOpen(false);
1752
1788
  }, []);
@@ -1772,9 +1808,11 @@ function DateTimePickerRoot({
1772
1808
  weekStartsOn,
1773
1809
  displayFormat,
1774
1810
  locale,
1811
+ displayTimezone,
1775
1812
  isDisabled,
1776
1813
  isReadOnly: readOnly,
1777
- pickerId
1814
+ pickerId,
1815
+ labels: mergedDateLabels
1778
1816
  }),
1779
1817
  [
1780
1818
  currentValue,
@@ -1790,9 +1828,11 @@ function DateTimePickerRoot({
1790
1828
  weekStartsOn,
1791
1829
  displayFormat,
1792
1830
  locale,
1831
+ displayTimezone,
1793
1832
  isDisabled,
1794
1833
  readOnly,
1795
- pickerId
1834
+ pickerId,
1835
+ mergedDateLabels
1796
1836
  ]
1797
1837
  );
1798
1838
  const timeContext = useMemo(
@@ -1802,25 +1842,36 @@ function DateTimePickerRoot({
1802
1842
  format,
1803
1843
  step,
1804
1844
  withSeconds: false,
1845
+ displayTimezone,
1805
1846
  isDisabled,
1806
1847
  isReadOnly: readOnly,
1807
1848
  currentTime,
1808
- pickerId
1849
+ pickerId,
1850
+ labels: mergedTimeLabels
1809
1851
  }),
1810
- [currentValue, setTime$1, format, step, isDisabled, readOnly, currentTime, pickerId]
1852
+ [currentValue, setTime$1, format, step, displayTimezone, isDisabled, readOnly, currentTime, pickerId, mergedTimeLabels]
1811
1853
  );
1812
1854
  return /* @__PURE__ */ jsx(DatePickerContext.Provider, { value: dateContext, children: /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: timeContext, children }) });
1813
1855
  }
1814
1856
  var DateTimePickerInput = forwardRef(
1815
- function DateTimePickerInput2({ onFocus, onKeyDown, ...props }, ref) {
1857
+ function DateTimePickerInput2({ onClick, onKeyDown, ...props }, ref) {
1816
1858
  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(
1859
+ let displayValue = "";
1860
+ if (ctx.value) {
1861
+ try {
1862
+ const datePart = ctx.adapter.format(ctx.value, "yyyy-MM-dd", ctx.displayTimezone);
1863
+ const time = ctx.displayTimezone ? getTimeInTimezone(ctx.value, ctx.displayTimezone) : getTime(ctx.value);
1864
+ displayValue = `${datePart} ${formatTimeString(time)}`;
1865
+ } catch {
1866
+ displayValue = ctx.value;
1867
+ }
1868
+ }
1869
+ const handleClick = useCallback(
1819
1870
  (e) => {
1820
- ctx.open();
1821
- onFocus?.(e);
1871
+ if (!ctx.isOpen) ctx.open();
1872
+ onClick?.(e);
1822
1873
  },
1823
- [ctx, onFocus]
1874
+ [ctx, onClick]
1824
1875
  );
1825
1876
  const handleKeyDown = useCallback(
1826
1877
  (e) => {
@@ -1846,7 +1897,7 @@ var DateTimePickerInput = forwardRef(
1846
1897
  type: "text",
1847
1898
  role: "combobox",
1848
1899
  readOnly: true,
1849
- "aria-label": "\uB0A0\uC9DC \uBC0F \uC2DC\uAC04",
1900
+ "aria-label": ctx.labels.dateTimeInput ?? "Date and time",
1850
1901
  "aria-expanded": ctx.isOpen,
1851
1902
  "aria-haspopup": "dialog",
1852
1903
  "aria-controls": ctx.isOpen ? calendarId : void 0,
@@ -1854,7 +1905,7 @@ var DateTimePickerInput = forwardRef(
1854
1905
  autoComplete: "off",
1855
1906
  value: displayValue,
1856
1907
  disabled: ctx.isDisabled || props.disabled,
1857
- onFocus: handleFocus,
1908
+ onClick: handleClick,
1858
1909
  onKeyDown: handleKeyDown,
1859
1910
  ...props
1860
1911
  }
@@ -1880,7 +1931,8 @@ function useDatePicker(options = {}) {
1880
1931
  onChange,
1881
1932
  disabled = [],
1882
1933
  weekStartsOn = 0,
1883
- adapter = DateFnsAdapter
1934
+ adapter = DateFnsAdapter,
1935
+ displayTimezone
1884
1936
  } = options;
1885
1937
  const pickerId = useId();
1886
1938
  const isControlled = useRef(controlledValue !== void 0).current;
@@ -1889,24 +1941,29 @@ function useDatePicker(options = {}) {
1889
1941
  );
1890
1942
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
1891
1943
  const [isOpen, setIsOpen] = useState(false);
1892
- const [viewMonth, setViewMonth] = useState(currentValue ?? adapter.today());
1893
- const [focusedDate, setFocusedDate] = useState(currentValue ?? adapter.today());
1944
+ const [viewMonth, setViewMonth] = useState(
1945
+ currentValue ?? adapter.today(displayTimezone)
1946
+ );
1947
+ const [focusedDate, setFocusedDate] = useState(
1948
+ currentValue ?? adapter.today(displayTimezone)
1949
+ );
1894
1950
  const selectDate = useCallback(
1895
1951
  (iso) => {
1952
+ const normalized = iso && displayTimezone ? civilMidnightFromUtcDay(iso, displayTimezone) : iso;
1896
1953
  if (!isControlled) {
1897
- setUncontrolledValue(iso);
1954
+ setUncontrolledValue(normalized);
1898
1955
  }
1899
- onChange?.(iso);
1956
+ onChange?.(normalized);
1900
1957
  setIsOpen(false);
1901
1958
  },
1902
- [isControlled, onChange]
1959
+ [isControlled, onChange, displayTimezone]
1903
1960
  );
1904
1961
  const open = useCallback(() => {
1905
1962
  setIsOpen(true);
1906
- const target = currentValue ?? adapter.today();
1963
+ const target = currentValue ?? adapter.today(displayTimezone);
1907
1964
  setViewMonth(target);
1908
1965
  setFocusedDate(target);
1909
- }, [currentValue, adapter]);
1966
+ }, [currentValue, adapter, displayTimezone]);
1910
1967
  const close = useCallback(() => {
1911
1968
  setIsOpen(false);
1912
1969
  }, []);
@@ -1928,7 +1985,8 @@ function useDatePicker(options = {}) {
1928
1985
  weekStartsOn,
1929
1986
  selected: currentValue,
1930
1987
  focusedDate,
1931
- disabled
1988
+ disabled,
1989
+ timezone: displayTimezone
1932
1990
  });
1933
1991
  return {
1934
1992
  value: currentValue,
@@ -1956,7 +2014,8 @@ function useRangePicker(options = {}) {
1956
2014
  onChange,
1957
2015
  disabled = [],
1958
2016
  weekStartsOn = 0,
1959
- adapter = DateFnsAdapter
2017
+ adapter = DateFnsAdapter,
2018
+ displayTimezone
1960
2019
  } = options;
1961
2020
  const pickerId = useId();
1962
2021
  const isControlled = useRef(controlledValue !== void 0).current;
@@ -1968,10 +2027,10 @@ function useRangePicker(options = {}) {
1968
2027
  const [selectingTarget, setSelectingTarget] = useState("start");
1969
2028
  const [hoverDate, setHoverDate] = useState(null);
1970
2029
  const [viewMonth, setViewMonth] = useState(
1971
- currentValue.start ?? adapter.today()
2030
+ currentValue.start ?? adapter.today(displayTimezone)
1972
2031
  );
1973
2032
  const [focusedDate, setFocusedDate] = useState(
1974
- currentValue.start ?? adapter.today()
2033
+ currentValue.start ?? adapter.today(displayTimezone)
1975
2034
  );
1976
2035
  const setRange = useCallback(
1977
2036
  (range) => {
@@ -1984,35 +2043,36 @@ function useRangePicker(options = {}) {
1984
2043
  );
1985
2044
  const selectDate = useCallback(
1986
2045
  (iso) => {
2046
+ const normalized = displayTimezone ? civilMidnightFromUtcDay(iso, displayTimezone) : iso;
1987
2047
  if (selectingTarget === "start") {
1988
- setRange({ start: iso, end: null });
2048
+ setRange({ start: normalized, end: null });
1989
2049
  setSelectingTarget("end");
1990
2050
  setHoverDate(null);
1991
2051
  } else {
1992
2052
  const start = currentValue.start;
1993
2053
  if (!start) {
1994
- setRange({ start: iso, end: null });
2054
+ setRange({ start: normalized, end: null });
1995
2055
  setSelectingTarget("end");
1996
2056
  return;
1997
2057
  }
1998
- const newRange = adapter.isBefore(iso, start) ? { start: iso, end: start } : { start, end: iso };
2058
+ const newRange = adapter.isBefore(normalized, start) ? { start: normalized, end: start } : { start, end: normalized };
1999
2059
  setRange(newRange);
2000
2060
  setSelectingTarget("start");
2001
2061
  setHoverDate(null);
2002
2062
  setIsOpen(false);
2003
2063
  }
2004
2064
  },
2005
- [selectingTarget, currentValue.start, adapter, setRange]
2065
+ [selectingTarget, currentValue.start, adapter, setRange, displayTimezone]
2006
2066
  );
2007
2067
  const open = useCallback(() => {
2008
2068
  setIsOpen(true);
2009
- const target = currentValue.start ?? adapter.today();
2069
+ const target = currentValue.start ?? adapter.today(displayTimezone);
2010
2070
  setViewMonth(target);
2011
2071
  setFocusedDate(target);
2012
2072
  if (currentValue.start && currentValue.end) {
2013
2073
  setSelectingTarget("start");
2014
2074
  }
2015
- }, [currentValue, adapter]);
2075
+ }, [currentValue, adapter, displayTimezone]);
2016
2076
  const close = useCallback(() => {
2017
2077
  setIsOpen(false);
2018
2078
  setHoverDate(null);
@@ -2036,7 +2096,8 @@ function useRangePicker(options = {}) {
2036
2096
  focusedDate,
2037
2097
  disabled,
2038
2098
  range: currentValue,
2039
- rangeHover: hoverDate
2099
+ rangeHover: hoverDate,
2100
+ timezone: displayTimezone
2040
2101
  });
2041
2102
  return {
2042
2103
  value: currentValue,
@@ -2061,10 +2122,7 @@ function useRangePicker(options = {}) {
2061
2122
  };
2062
2123
  }
2063
2124
  function getDefaultIso3() {
2064
- const now = /* @__PURE__ */ new Date();
2065
- return new Date(
2066
- Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
2067
- ).toISOString();
2125
+ return DateFnsAdapter.today();
2068
2126
  }
2069
2127
  function useTimePicker(options = {}) {
2070
2128
  const {
@@ -2072,7 +2130,8 @@ function useTimePicker(options = {}) {
2072
2130
  defaultValue,
2073
2131
  onChange,
2074
2132
  format = "24h",
2075
- step = 1
2133
+ step = 1,
2134
+ displayTimezone
2076
2135
  } = options;
2077
2136
  const pickerId = useId();
2078
2137
  const isControlled = useRef(controlledValue !== void 0).current;
@@ -2081,16 +2140,19 @@ function useTimePicker(options = {}) {
2081
2140
  );
2082
2141
  const currentValue = isControlled ? controlledValue ?? null : uncontrolledValue;
2083
2142
  const baseIso = currentValue ?? getDefaultIso3();
2084
- const currentTime = useMemo(() => getTime(baseIso), [baseIso]);
2143
+ const currentTime = useMemo(
2144
+ () => displayTimezone ? getTimeInTimezone(baseIso, displayTimezone) : getTime(baseIso),
2145
+ [baseIso, displayTimezone]
2146
+ );
2085
2147
  const setTime$1 = useCallback(
2086
2148
  (partial) => {
2087
- const newIso = setTime(baseIso, partial);
2149
+ const newIso = displayTimezone ? setTimeInTimezone(baseIso, partial, displayTimezone) : setTime(baseIso, partial);
2088
2150
  if (!isControlled) {
2089
2151
  setUncontrolledValue(newIso);
2090
2152
  }
2091
2153
  onChange?.(newIso);
2092
2154
  },
2093
- [baseIso, isControlled, onChange]
2155
+ [baseIso, isControlled, onChange, displayTimezone]
2094
2156
  );
2095
2157
  const period = format === "12h" ? to12Hour(currentTime.hours).period : null;
2096
2158
  const displayHour = format === "12h" ? to12Hour(currentTime.hours).hours12 : currentTime.hours;