@underverse-ui/underverse 2.0.35 → 2.0.37

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 (38) hide show
  1. package/api-reference.json +1 -1
  2. package/dist/DatePicker-FDOTQMNL.js +15 -0
  3. package/dist/{EmojiPicker-6H52Q4YE.js → EmojiPicker-Z7QPW4DA.js} +5 -4
  4. package/dist/EmojiPicker-Z7QPW4DA.js.map +1 -0
  5. package/dist/chunk-5GMQ23IU.js +719 -0
  6. package/dist/chunk-5GMQ23IU.js.map +1 -0
  7. package/dist/{chunk-L73HBI6E.js → chunk-5VCM2Z7M.js} +6 -4
  8. package/dist/{chunk-L73HBI6E.js.map → chunk-5VCM2Z7M.js.map} +1 -1
  9. package/dist/{chunk-H4AVZA4K.js → chunk-6K6CKZUL.js} +469 -360
  10. package/dist/chunk-6K6CKZUL.js.map +1 -0
  11. package/dist/{chunk-6FAHXNDQ.js → chunk-6XQTRIP6.js} +10 -349
  12. package/dist/chunk-6XQTRIP6.js.map +1 -0
  13. package/dist/chunk-EN33T3LJ.js +358 -0
  14. package/dist/chunk-EN33T3LJ.js.map +1 -0
  15. package/dist/{chunk-F2P3DJSC.js → chunk-GZ7EDNGF.js} +333 -1022
  16. package/dist/chunk-GZ7EDNGF.js.map +1 -0
  17. package/dist/{chunk-WFQV7XFC.js → chunk-JABD2ONI.js} +3 -3
  18. package/dist/chunk-JG3U26V6.js +1692 -0
  19. package/dist/chunk-JG3U26V6.js.map +1 -0
  20. package/dist/{chunk-W3QTP2DM.js → chunk-TSBKJELM.js} +2 -2
  21. package/dist/index.cjs +13513 -13323
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +20 -0
  24. package/dist/index.d.ts +20 -0
  25. package/dist/index.js +1942 -3569
  26. package/dist/index.js.map +1 -1
  27. package/dist/{menu-bar-MUISXLMR.js → menu-bar-VXAMTP3E.js} +6 -4
  28. package/dist/{menu-bar-MUISXLMR.js.map → menu-bar-VXAMTP3E.js.map} +1 -1
  29. package/dist/ueditor.cjs +2803 -1098
  30. package/dist/ueditor.cjs.map +1 -1
  31. package/dist/ueditor.js +6 -4
  32. package/package.json +1 -1
  33. package/dist/chunk-6FAHXNDQ.js.map +0 -1
  34. package/dist/chunk-F2P3DJSC.js.map +0 -1
  35. package/dist/chunk-H4AVZA4K.js.map +0 -1
  36. /package/dist/{EmojiPicker-6H52Q4YE.js.map → DatePicker-FDOTQMNL.js.map} +0 -0
  37. /package/dist/{chunk-WFQV7XFC.js.map → chunk-JABD2ONI.js.map} +0 -0
  38. /package/dist/{chunk-W3QTP2DM.js.map → chunk-TSBKJELM.js.map} +0 -0
@@ -0,0 +1,1692 @@
1
+ import {
2
+ Popover,
3
+ getBorderRadiusClass,
4
+ useShadCNAnimations,
5
+ useUnderverseUIConfig
6
+ } from "./chunk-5GMQ23IU.js";
7
+ import {
8
+ cn,
9
+ formControlFixedClass,
10
+ formControlGroupOutlineClass,
11
+ formControlLabelClass,
12
+ formControlSizeStyles,
13
+ useSmartLocale,
14
+ useSmartTranslations
15
+ } from "./chunk-6XQTRIP6.js";
16
+ import {
17
+ __export
18
+ } from "./chunk-NSBPE2FW.js";
19
+
20
+ // src/utils/date.ts
21
+ var date_exports = {};
22
+ __export(date_exports, {
23
+ formatDate: () => formatDate,
24
+ formatDateForInput: () => formatDateForInput,
25
+ formatDateShort: () => formatDateShort,
26
+ formatDateSmart: () => formatDateSmart,
27
+ formatDateTime: () => formatDateTime,
28
+ formatDateTimeForInput: () => formatDateTimeForInput,
29
+ formatDateWithDay: () => formatDateWithDay,
30
+ formatTime: () => formatTime,
31
+ formatTimeAgo: () => formatTimeAgo,
32
+ getDayOfWeek: () => getDayOfWeek,
33
+ isToday: () => isToday,
34
+ isYesterday: () => isYesterday,
35
+ parseDateString: () => parseDateString
36
+ });
37
+ var localeMap = {
38
+ en: "en-US",
39
+ vi: "vi-VN",
40
+ ko: "ko-KR",
41
+ ja: "ja-JP"
42
+ };
43
+ function getLocaleString(locale = "en") {
44
+ return localeMap[locale] || localeMap.en;
45
+ }
46
+ function formatDate(date, locale = "en") {
47
+ if (!date) return "";
48
+ const d = new Date(date);
49
+ if (isNaN(d.getTime())) return "";
50
+ return d.toLocaleDateString(getLocaleString(locale), {
51
+ day: "numeric",
52
+ month: "long",
53
+ year: "numeric"
54
+ });
55
+ }
56
+ function formatDateShort(date, locale = "en") {
57
+ if (!date) return "";
58
+ const d = new Date(date);
59
+ if (isNaN(d.getTime())) return "";
60
+ return d.toLocaleDateString(getLocaleString(locale), {
61
+ day: "2-digit",
62
+ month: "2-digit",
63
+ year: "numeric"
64
+ });
65
+ }
66
+ function formatTime(date, locale = "en") {
67
+ if (!date) return "";
68
+ const d = new Date(date);
69
+ if (isNaN(d.getTime())) return "";
70
+ return d.toLocaleTimeString(getLocaleString(locale), {
71
+ hour: "2-digit",
72
+ minute: "2-digit"
73
+ });
74
+ }
75
+ function formatDateTime(date, locale = "en") {
76
+ if (!date) return "";
77
+ const d = new Date(date);
78
+ if (isNaN(d.getTime())) return "";
79
+ return d.toLocaleString(getLocaleString(locale), {
80
+ day: "2-digit",
81
+ month: "2-digit",
82
+ year: "numeric",
83
+ hour: "2-digit",
84
+ minute: "2-digit"
85
+ });
86
+ }
87
+ function formatTimeAgo(date, locale = "en") {
88
+ if (!date) return "";
89
+ const d = new Date(date);
90
+ if (isNaN(d.getTime())) return "";
91
+ const now = /* @__PURE__ */ new Date();
92
+ const diffMs = now.getTime() - d.getTime();
93
+ const diffSec = Math.floor(diffMs / 1e3);
94
+ const diffMin = Math.floor(diffSec / 60);
95
+ const diffHour = Math.floor(diffMin / 60);
96
+ const diffDay = Math.floor(diffHour / 24);
97
+ const diffWeek = Math.floor(diffDay / 7);
98
+ const diffMonth = Math.floor(diffDay / 30);
99
+ const diffYear = Math.floor(diffDay / 365);
100
+ const labels = {
101
+ en: {
102
+ justNow: "Just now",
103
+ minute: (n) => `${n} minute${n > 1 ? "s" : ""} ago`,
104
+ hour: (n) => `${n} hour${n > 1 ? "s" : ""} ago`,
105
+ day: (n) => `${n} day${n > 1 ? "s" : ""} ago`,
106
+ week: (n) => `${n} week${n > 1 ? "s" : ""} ago`,
107
+ month: (n) => `${n} month${n > 1 ? "s" : ""} ago`,
108
+ year: (n) => `${n} year${n > 1 ? "s" : ""} ago`
109
+ },
110
+ vi: {
111
+ justNow: "V\u1EEBa xong",
112
+ minute: (n) => `${n} ph\xFAt tr\u01B0\u1EDBc`,
113
+ hour: (n) => `${n} gi\u1EDD tr\u01B0\u1EDBc`,
114
+ day: (n) => `${n} ng\xE0y tr\u01B0\u1EDBc`,
115
+ week: (n) => `${n} tu\u1EA7n tr\u01B0\u1EDBc`,
116
+ month: (n) => `${n} th\xE1ng tr\u01B0\u1EDBc`,
117
+ year: (n) => `${n} n\u0103m tr\u01B0\u1EDBc`
118
+ },
119
+ ko: {
120
+ justNow: "\uBC29\uAE08 \uC804",
121
+ minute: (n) => `${n}\uBD84 \uC804`,
122
+ hour: (n) => `${n}\uC2DC\uAC04 \uC804`,
123
+ day: (n) => `${n}\uC77C \uC804`,
124
+ week: (n) => `${n}\uC8FC \uC804`,
125
+ month: (n) => `${n}\uAC1C\uC6D4 \uC804`,
126
+ year: (n) => `${n}\uB144 \uC804`
127
+ },
128
+ ja: {
129
+ justNow: "\u305F\u3063\u305F\u4ECA",
130
+ minute: (n) => `${n}\u5206\u524D`,
131
+ hour: (n) => `${n}\u6642\u9593\u524D`,
132
+ day: (n) => `${n}\u65E5\u524D`,
133
+ week: (n) => `${n}\u9031\u9593\u524D`,
134
+ month: (n) => `${n}\u30F6\u6708\u524D`,
135
+ year: (n) => `${n}\u5E74\u524D`
136
+ }
137
+ };
138
+ const l = labels[locale] || labels.en;
139
+ if (diffSec < 60) return l.justNow;
140
+ if (diffMin < 60) return l.minute(diffMin);
141
+ if (diffHour < 24) return l.hour(diffHour);
142
+ if (diffDay < 7) return l.day(diffDay);
143
+ if (diffWeek < 4) return l.week(diffWeek);
144
+ if (diffMonth < 12) return l.month(diffMonth);
145
+ return l.year(diffYear);
146
+ }
147
+ function isToday(date) {
148
+ if (!date) return false;
149
+ const d = new Date(date);
150
+ if (isNaN(d.getTime())) return false;
151
+ const today = /* @__PURE__ */ new Date();
152
+ return d.getDate() === today.getDate() && d.getMonth() === today.getMonth() && d.getFullYear() === today.getFullYear();
153
+ }
154
+ function isYesterday(date) {
155
+ if (!date) return false;
156
+ const d = new Date(date);
157
+ if (isNaN(d.getTime())) return false;
158
+ const yesterday = /* @__PURE__ */ new Date();
159
+ yesterday.setDate(yesterday.getDate() - 1);
160
+ return d.getDate() === yesterday.getDate() && d.getMonth() === yesterday.getMonth() && d.getFullYear() === yesterday.getFullYear();
161
+ }
162
+ function formatDateForInput(date) {
163
+ if (!date) return "";
164
+ const d = new Date(date);
165
+ if (isNaN(d.getTime())) return "";
166
+ const year = d.getFullYear();
167
+ const month = String(d.getMonth() + 1).padStart(2, "0");
168
+ const day = String(d.getDate()).padStart(2, "0");
169
+ return `${year}-${month}-${day}`;
170
+ }
171
+ function formatDateTimeForInput(date) {
172
+ if (!date) return "";
173
+ const d = new Date(date);
174
+ if (isNaN(d.getTime())) return "";
175
+ const dateStr = formatDateForInput(date);
176
+ const hours = String(d.getHours()).padStart(2, "0");
177
+ const minutes = String(d.getMinutes()).padStart(2, "0");
178
+ return `${dateStr}T${hours}:${minutes}`;
179
+ }
180
+ function getDayOfWeek(date, locale = "en") {
181
+ if (!date) return "";
182
+ const d = new Date(date);
183
+ if (isNaN(d.getTime())) return "";
184
+ return d.toLocaleDateString(getLocaleString(locale), {
185
+ weekday: "long"
186
+ });
187
+ }
188
+ function formatDateWithDay(date, locale = "en") {
189
+ if (!date) return "";
190
+ const dayOfWeek = getDayOfWeek(date, locale);
191
+ const dateStr = formatDateShort(date, locale);
192
+ return `${dayOfWeek}, ${dateStr}`;
193
+ }
194
+ function formatDateSmart(date, locale = "en") {
195
+ if (!date) return "";
196
+ const d = new Date(date);
197
+ if (isNaN(d.getTime())) return "";
198
+ const now = /* @__PURE__ */ new Date();
199
+ const diffMs = now.getTime() - d.getTime();
200
+ const diffHour = Math.floor(diffMs / (1e3 * 60 * 60));
201
+ const labels = {
202
+ en: {
203
+ today: "Today at",
204
+ yesterday: "Yesterday at"
205
+ },
206
+ vi: {
207
+ today: "H\xF4m nay l\xFAc",
208
+ yesterday: "H\xF4m qua l\xFAc"
209
+ },
210
+ ko: {
211
+ today: "\uC624\uB298",
212
+ yesterday: "\uC5B4\uC81C"
213
+ },
214
+ ja: {
215
+ today: "\u4ECA\u65E5",
216
+ yesterday: "\u6628\u65E5"
217
+ }
218
+ };
219
+ const l = labels[locale] || labels.en;
220
+ if (diffHour < 1) {
221
+ return formatTimeAgo(date, locale);
222
+ }
223
+ if (isToday(date)) {
224
+ return `${l.today} ${formatTime(date, locale)}`;
225
+ }
226
+ if (isYesterday(date)) {
227
+ return `${l.yesterday} ${formatTime(date, locale)}`;
228
+ }
229
+ if (diffHour < 24 * 7) {
230
+ return formatTimeAgo(date, locale);
231
+ }
232
+ return formatDateTime(date, locale);
233
+ }
234
+ function parseDateString(str, locale = "en") {
235
+ if (!str) return null;
236
+ const cleaned = str.trim();
237
+ if (!cleaned) return null;
238
+ const parts = cleaned.split(/[\/\-\.\s]+/).filter(Boolean);
239
+ if (parts.length !== 3) return null;
240
+ let day = 0;
241
+ let month = 0;
242
+ let year = 0;
243
+ if (locale === "vi") {
244
+ day = parseInt(parts[0], 10);
245
+ month = parseInt(parts[1], 10);
246
+ year = parseInt(parts[2], 10);
247
+ } else if (locale === "ja" || locale === "ko") {
248
+ year = parseInt(parts[0], 10);
249
+ month = parseInt(parts[1], 10);
250
+ day = parseInt(parts[2], 10);
251
+ } else {
252
+ if (parts[0].length === 4) {
253
+ year = parseInt(parts[0], 10);
254
+ month = parseInt(parts[1], 10);
255
+ day = parseInt(parts[2], 10);
256
+ } else {
257
+ month = parseInt(parts[0], 10);
258
+ day = parseInt(parts[1], 10);
259
+ year = parseInt(parts[2], 10);
260
+ }
261
+ }
262
+ if (isNaN(day) || isNaN(month) || isNaN(year)) return null;
263
+ if (year < 100) {
264
+ year += year >= 50 ? 1900 : 2e3;
265
+ }
266
+ const parsedDate = new Date(year, month - 1, day);
267
+ if (parsedDate.getFullYear() === year && parsedDate.getMonth() === month - 1 && parsedDate.getDate() === day) {
268
+ return parsedDate;
269
+ }
270
+ return null;
271
+ }
272
+
273
+ // src/components/DatePicker.tsx
274
+ import { Calendar, ChevronLeft, ChevronRight, Sparkles, X as XIcon } from "lucide-react";
275
+ import * as React from "react";
276
+ import { useId } from "react";
277
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
278
+ function normalizeToLocalDate(date) {
279
+ if (!date) return null;
280
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
281
+ }
282
+ var DatePicker = ({
283
+ id,
284
+ value,
285
+ onChange,
286
+ placeholder,
287
+ className,
288
+ disabled = false,
289
+ size: requestedSize = "md",
290
+ label,
291
+ labelClassName,
292
+ required,
293
+ todayLabel,
294
+ clearLabel,
295
+ weekdayLabels,
296
+ disablePastDates = false,
297
+ minDate,
298
+ maxDate,
299
+ borderMode,
300
+ iconOnly = false,
301
+ iconOnlyDateFormat = "locale",
302
+ getPortalContainer
303
+ }) => {
304
+ const size = requestedSize === "xs" ? "sm" : requestedSize === "xl" ? "lg" : requestedSize;
305
+ const t = useSmartTranslations("DatePicker");
306
+ const tv = useSmartTranslations("ValidationInput");
307
+ const locale = useSmartLocale();
308
+ const [isOpen, setIsOpen] = React.useState(false);
309
+ const [viewDate, setViewDate] = React.useState(value || /* @__PURE__ */ new Date());
310
+ const [viewMode, setViewMode] = React.useState("calendar");
311
+ const [localRequiredError, setLocalRequiredError] = React.useState();
312
+ const triggerRef = React.useRef(null);
313
+ const inputRef = React.useRef(null);
314
+ const wheelContainerRef = React.useRef(null);
315
+ const wheelDeltaRef = React.useRef(0);
316
+ const [isFocused, setIsFocused] = React.useState(false);
317
+ const [inputValue, setInputValue] = React.useState("");
318
+ React.useEffect(() => {
319
+ setInputValue((currentInput) => {
320
+ if (value) {
321
+ const parsed = parseDateString(currentInput, locale);
322
+ const isSame = parsed && parsed.getTime() === value.getTime();
323
+ return isSame ? currentInput : formatDateShort(value, locale);
324
+ }
325
+ return isFocused ? currentInput : "";
326
+ });
327
+ }, [isFocused, locale, value]);
328
+ const handleInputChange = (e) => {
329
+ const text = e.target.value;
330
+ setInputValue(text);
331
+ const parsedDate = parseDateString(text, locale);
332
+ if (parsedDate) {
333
+ if (!isDateDisabled(parsedDate)) {
334
+ onChange(parsedDate);
335
+ setViewDate(parsedDate);
336
+ setLocalRequiredError(void 0);
337
+ }
338
+ }
339
+ };
340
+ const handleInputBlur = () => {
341
+ setIsFocused(false);
342
+ if (!inputValue.trim()) {
343
+ if (required) {
344
+ setLocalRequiredError(tv("required"));
345
+ } else {
346
+ if (value) onChange(void 0);
347
+ setLocalRequiredError(void 0);
348
+ }
349
+ } else {
350
+ const parsedDate = parseDateString(inputValue, locale);
351
+ if (parsedDate && !isDateDisabled(parsedDate)) {
352
+ onChange(parsedDate);
353
+ setInputValue(formatDateShort(parsedDate, locale));
354
+ setLocalRequiredError(void 0);
355
+ } else {
356
+ if (value) {
357
+ setInputValue(formatDateShort(value, locale));
358
+ setLocalRequiredError(void 0);
359
+ } else {
360
+ setInputValue("");
361
+ if (required) {
362
+ setLocalRequiredError(tv("required"));
363
+ }
364
+ }
365
+ }
366
+ }
367
+ };
368
+ const handleInputKeyDown = (e) => {
369
+ if (e.key === "Enter") {
370
+ e.preventDefault();
371
+ const parsedDate = parseDateString(inputValue, locale);
372
+ if (parsedDate && !isDateDisabled(parsedDate)) {
373
+ onChange(parsedDate);
374
+ setInputValue(formatDateShort(parsedDate, locale));
375
+ setViewDate(parsedDate);
376
+ setIsOpen(false);
377
+ setLocalRequiredError(void 0);
378
+ } else if (!inputValue.trim() && !required) {
379
+ onChange(void 0);
380
+ setIsOpen(false);
381
+ setLocalRequiredError(void 0);
382
+ } else {
383
+ setIsOpen(false);
384
+ }
385
+ } else if (e.key === "Escape") {
386
+ setIsOpen(false);
387
+ }
388
+ };
389
+ const normalizeToLocalDay = React.useCallback((date) => {
390
+ if (!date) return null;
391
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
392
+ }, []);
393
+ const minDay = React.useMemo(() => normalizeToLocalDay(minDate), [minDate, normalizeToLocalDay]);
394
+ const maxDay = React.useMemo(() => normalizeToLocalDay(maxDate), [maxDate, normalizeToLocalDay]);
395
+ const isDateDisabled = React.useCallback(
396
+ (date) => {
397
+ const day = normalizeToLocalDay(date);
398
+ if (!day) return false;
399
+ if (disablePastDates) {
400
+ const today = /* @__PURE__ */ new Date();
401
+ today.setHours(0, 0, 0, 0);
402
+ if (day < today) return true;
403
+ }
404
+ if (minDay && day < minDay) return true;
405
+ if (maxDay && day > maxDay) return true;
406
+ return false;
407
+ },
408
+ [disablePastDates, maxDay, minDay, normalizeToLocalDay]
409
+ );
410
+ useShadCNAnimations();
411
+ const sizeStyles = {
412
+ sm: {
413
+ trigger: formControlSizeStyles.sm.compactControl,
414
+ dayCell: "w-7 h-7 text-xs",
415
+ monthCell: "w-6 h-6 text-xs",
416
+ navButton: "p-1.5",
417
+ navIcon: "h-3 w-3",
418
+ calendarIcon: "h-3.5 w-3.5",
419
+ clearIcon: "w-2.5 h-2.5",
420
+ actionButton: "px-2 py-1 text-[10px] gap-1",
421
+ actionIcon: "w-2.5 h-2.5",
422
+ contentWidth: 240,
423
+ contentPadding: "p-4",
424
+ label: formControlSizeStyles.sm.label,
425
+ weekdayLabel: "text-[9px] py-1",
426
+ headerMargin: "mb-2",
427
+ footerMargin: "mt-3 pt-2 gap-1.5"
428
+ },
429
+ md: {
430
+ trigger: formControlSizeStyles.md.control,
431
+ dayCell: "w-8 h-8 text-sm",
432
+ monthCell: "w-8 h-8 text-sm",
433
+ navButton: "p-2",
434
+ navIcon: "h-4 w-4",
435
+ calendarIcon: "h-4 w-4",
436
+ clearIcon: "w-3.5 h-3.5",
437
+ actionButton: "px-3 py-2 text-xs gap-2",
438
+ actionIcon: "w-3.5 h-3.5",
439
+ contentWidth: 280,
440
+ contentPadding: "p-5",
441
+ label: formControlSizeStyles.md.label,
442
+ weekdayLabel: "text-[10px] py-1.5",
443
+ headerMargin: "mb-4",
444
+ footerMargin: "mt-4 pt-3 gap-2"
445
+ },
446
+ lg: {
447
+ trigger: formControlSizeStyles.lg.control,
448
+ dayCell: "w-10 h-10 text-base",
449
+ monthCell: "w-10 h-10 text-base",
450
+ navButton: "p-2.5",
451
+ navIcon: "h-5 w-5",
452
+ calendarIcon: "h-5 w-5",
453
+ clearIcon: "w-4 h-4",
454
+ actionButton: "px-4 py-2.5 text-sm gap-2",
455
+ actionIcon: "w-4 h-4",
456
+ contentWidth: 340,
457
+ contentPadding: "p-6",
458
+ label: formControlSizeStyles.lg.label,
459
+ weekdayLabel: "text-xs py-2",
460
+ headerMargin: "mb-5",
461
+ footerMargin: "mt-5 pt-4 gap-2.5"
462
+ }
463
+ };
464
+ React.useEffect(() => {
465
+ if (value) {
466
+ setViewDate(value);
467
+ } else {
468
+ setViewDate(/* @__PURE__ */ new Date());
469
+ }
470
+ }, [value]);
471
+ React.useEffect(() => {
472
+ if (disabled || !required || value) {
473
+ setLocalRequiredError(void 0);
474
+ }
475
+ }, [disabled, required, value]);
476
+ React.useEffect(() => {
477
+ if (!isOpen) {
478
+ setViewMode("calendar");
479
+ }
480
+ }, [isOpen]);
481
+ const handleDateSelect = (date) => {
482
+ let selectedDate;
483
+ if (value && (value.getHours() !== 0 || value.getMinutes() !== 0 || value.getSeconds() !== 0)) {
484
+ selectedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), value.getHours(), value.getMinutes(), value.getSeconds());
485
+ } else {
486
+ const now = /* @__PURE__ */ new Date();
487
+ selectedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), now.getHours(), now.getMinutes(), now.getSeconds());
488
+ }
489
+ onChange(selectedDate);
490
+ setLocalRequiredError(void 0);
491
+ setIsOpen(false);
492
+ };
493
+ const formatDateDisplay = (date) => {
494
+ return date.toLocaleDateString(locale === "vi" ? "vi-VN" : "en-US", {
495
+ day: "numeric",
496
+ month: "long",
497
+ year: "numeric"
498
+ });
499
+ };
500
+ const formatIconOnlyDate = (date) => {
501
+ if (iconOnlyDateFormat === "year-month-day") {
502
+ const month = String(date.getMonth() + 1).padStart(2, "0");
503
+ const day = String(date.getDate()).padStart(2, "0");
504
+ return `${date.getFullYear()}/${month}/${day}`;
505
+ }
506
+ return formatDateDisplay(date);
507
+ };
508
+ const getDaysInMonth = (date) => {
509
+ return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
510
+ };
511
+ const getFirstDayOfMonth = (date) => {
512
+ return new Date(date.getFullYear(), date.getMonth(), 1).getDay();
513
+ };
514
+ const navigateMonth = React.useCallback((direction) => {
515
+ setViewDate((prev) => {
516
+ const newDate = new Date(prev);
517
+ newDate.setMonth(prev.getMonth() + (direction === "next" ? 1 : -1));
518
+ return newDate;
519
+ });
520
+ }, []);
521
+ const isElementVerticallyScrollable = (el) => {
522
+ const style = window.getComputedStyle(el);
523
+ const overflowY = style.overflowY;
524
+ if (overflowY !== "auto" && overflowY !== "scroll") return false;
525
+ const node = el;
526
+ return node.scrollHeight > node.clientHeight + 1;
527
+ };
528
+ const renderCalendar = () => {
529
+ const daysInMonth = getDaysInMonth(viewDate);
530
+ const firstDayOfMonth = getFirstDayOfMonth(viewDate);
531
+ const days = [];
532
+ for (let i = 0; i < firstDayOfMonth; i++) {
533
+ days.push(/* @__PURE__ */ jsx("div", { className: sizeStyles[size].dayCell.split(" ").slice(0, 2).join(" ") }, `empty-${i}`));
534
+ }
535
+ for (let day = 1; day <= daysInMonth; day++) {
536
+ const date = new Date(viewDate.getFullYear(), viewDate.getMonth(), day);
537
+ const isSelected = value && date.getDate() === value.getDate() && date.getMonth() === value.getMonth() && date.getFullYear() === value.getFullYear();
538
+ const isToday2 = date.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
539
+ const isDisabled = isDateDisabled(date);
540
+ const totalDaysFromStart = firstDayOfMonth + day - 1;
541
+ const rowIndex = Math.floor(totalDaysFromStart / 7);
542
+ days.push(
543
+ /* @__PURE__ */ jsxs(
544
+ "button",
545
+ {
546
+ type: "button",
547
+ onClick: () => !isDisabled && handleDateSelect(date),
548
+ disabled: isDisabled,
549
+ style: {
550
+ animationDelay: isOpen ? `${rowIndex * 40}ms` : "0ms"
551
+ },
552
+ className: cn(
553
+ sizeStyles[size].dayCell,
554
+ "datepicker-day rounded-lg relative cursor-pointer",
555
+ "transition-all duration-200 font-medium active:scale-95",
556
+ isDisabled && "opacity-30 cursor-not-allowed text-muted-foreground active:scale-100",
557
+ isSelected ? "bg-linear-to-br from-primary to-primary/80 text-primary-foreground font-bold shadow-lg shadow-primary/30 scale-110 z-10 hover:from-primary hover:to-primary/70 focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus:outline-none" : !isDisabled && "hover:bg-accent/80 hover:text-accent-foreground hover:scale-105 focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus:outline-none focus:bg-accent/90",
558
+ isToday2 && !isSelected && "bg-primary/15 text-primary font-bold ring-2 ring-primary/30"
559
+ ),
560
+ children: [
561
+ day,
562
+ isToday2 && !isSelected && /* @__PURE__ */ jsx("span", { className: "absolute -bottom-0.5 left-1/2 -translate-x-1/2 w-1 h-1 rounded-full bg-primary" })
563
+ ]
564
+ },
565
+ day
566
+ )
567
+ );
568
+ }
569
+ return days;
570
+ };
571
+ const renderMonthSelector = () => {
572
+ const months = locale === "vi" ? ["Th1", "Th2", "Th3", "Th4", "Th5", "Th6", "Th7", "Th8", "Th9", "Th10", "Th11", "Th12"] : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
573
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 p-2", children: months.map((month, idx) => {
574
+ const isSelected = viewDate.getMonth() === idx;
575
+ return /* @__PURE__ */ jsx(
576
+ "button",
577
+ {
578
+ type: "button",
579
+ onClick: () => {
580
+ setViewDate(new Date(viewDate.getFullYear(), idx, 1));
581
+ setViewMode("calendar");
582
+ },
583
+ className: cn(
584
+ "py-2 px-3 rounded-lg text-sm font-medium transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 active:scale-95",
585
+ isSelected ? "bg-primary text-primary-foreground shadow-md" : "hover:bg-accent/80 text-foreground hover:scale-105"
586
+ ),
587
+ children: month
588
+ },
589
+ month
590
+ );
591
+ }) });
592
+ };
593
+ const renderYearSelector = () => {
594
+ const currentYear = viewDate.getFullYear();
595
+ const startYear = Math.floor(currentYear / 12) * 12;
596
+ const years = Array.from({ length: 12 }, (_, i) => startYear + i);
597
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 p-2", children: years.map((year) => {
598
+ const isSelected = viewDate.getFullYear() === year;
599
+ return /* @__PURE__ */ jsx(
600
+ "button",
601
+ {
602
+ type: "button",
603
+ onClick: () => {
604
+ setViewDate(new Date(year, viewDate.getMonth(), 1));
605
+ setViewMode("month");
606
+ },
607
+ className: cn(
608
+ "py-2 px-3 rounded-lg text-sm font-medium transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 active:scale-95",
609
+ isSelected ? "bg-primary text-primary-foreground shadow-md" : "hover:bg-accent/80 text-foreground hover:scale-105"
610
+ ),
611
+ children: year
612
+ },
613
+ year
614
+ );
615
+ }) });
616
+ };
617
+ const navigateYearRange = (direction) => {
618
+ const currentYear = viewDate.getFullYear();
619
+ const offset = direction === "next" ? 12 : -12;
620
+ setViewDate(new Date(currentYear + offset, viewDate.getMonth(), 1));
621
+ };
622
+ const datePickerContent = /* @__PURE__ */ jsxs("div", { ref: wheelContainerRef, "data-datepicker": true, className: "w-full", children: [
623
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4 px-1", children: [
624
+ /* @__PURE__ */ jsx(
625
+ "button",
626
+ {
627
+ type: "button",
628
+ onClick: () => viewMode === "year" ? navigateYearRange("prev") : navigateMonth("prev"),
629
+ className: cn(
630
+ "p-2 rounded-xl transition-all duration-200",
631
+ "bg-muted/50 hover:bg-muted text-muted-foreground hover:text-foreground",
632
+ "hover:scale-110 active:scale-95 hover:shadow-md",
633
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
634
+ ),
635
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
636
+ }
637
+ ),
638
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
639
+ /* @__PURE__ */ jsx(
640
+ "button",
641
+ {
642
+ type: "button",
643
+ onClick: () => setViewMode(viewMode === "month" ? "calendar" : "month"),
644
+ className: cn(
645
+ "text-sm font-bold transition-all duration-200 px-2 py-1 rounded-lg",
646
+ viewMode === "month" ? "bg-primary/15 text-primary" : "text-foreground hover:bg-accent/50"
647
+ ),
648
+ children: viewDate.toLocaleDateString(locale === "vi" ? "vi-VN" : "en-US", { month: "long" })
649
+ }
650
+ ),
651
+ /* @__PURE__ */ jsx(
652
+ "button",
653
+ {
654
+ type: "button",
655
+ onClick: () => setViewMode(viewMode === "year" ? "calendar" : "year"),
656
+ className: cn(
657
+ "text-sm font-bold transition-all duration-200 px-2 py-1 rounded-lg",
658
+ viewMode === "year" ? "bg-primary/15 text-primary" : "text-foreground hover:bg-accent/50"
659
+ ),
660
+ children: viewDate.getFullYear()
661
+ }
662
+ )
663
+ ] }),
664
+ /* @__PURE__ */ jsx(
665
+ "button",
666
+ {
667
+ type: "button",
668
+ onClick: () => viewMode === "year" ? navigateYearRange("next") : navigateMonth("next"),
669
+ className: cn(
670
+ "p-2 rounded-xl transition-all duration-200",
671
+ "bg-muted/50 hover:bg-muted text-muted-foreground hover:text-foreground",
672
+ "hover:scale-110 active:scale-95 hover:shadow-md",
673
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
674
+ ),
675
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
676
+ }
677
+ )
678
+ ] }),
679
+ viewMode === "calendar" && /* @__PURE__ */ jsxs(Fragment, { children: [
680
+ /* @__PURE__ */ jsx("div", { className: cn("grid grid-cols-7 gap-1 mb-2 px-0.5"), children: (weekdayLabels || (locale === "vi" ? ["CN", "T2", "T3", "T4", "T5", "T6", "T7"] : ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"])).map(
681
+ (day, idx) => /* @__PURE__ */ jsx(
682
+ "div",
683
+ {
684
+ className: cn(
685
+ "text-center font-bold uppercase tracking-wide",
686
+ sizeStyles[size].weekdayLabel,
687
+ idx === 0 || idx === 6 ? "text-primary/70" : "text-muted-foreground/60"
688
+ ),
689
+ children: day
690
+ },
691
+ day
692
+ )
693
+ ) }),
694
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1 p-1 rounded-xl bg-muted/20", children: renderCalendar() })
695
+ ] }),
696
+ viewMode === "month" && renderMonthSelector(),
697
+ viewMode === "year" && renderYearSelector(),
698
+ /* @__PURE__ */ jsxs("div", { className: cn("flex items-center border-t border-border/50", sizeStyles[size].footerMargin), children: [
699
+ /* @__PURE__ */ jsxs(
700
+ "button",
701
+ {
702
+ type: "button",
703
+ onClick: () => {
704
+ const today = /* @__PURE__ */ new Date();
705
+ if (isDateDisabled(today)) return;
706
+ handleDateSelect(today);
707
+ },
708
+ disabled: isDateDisabled(/* @__PURE__ */ new Date()),
709
+ className: cn(
710
+ "flex-1 font-semibold rounded-xl",
711
+ "bg-linear-to-r from-primary/10 to-primary/5 border border-primary/30",
712
+ "text-primary hover:from-primary/20 hover:to-primary/10 hover:border-primary/50",
713
+ "transition-all duration-300 flex items-center justify-center",
714
+ "hover:scale-[1.02] active:scale-[0.98] hover:shadow-md hover:shadow-primary/10",
715
+ sizeStyles[size].actionButton,
716
+ isDateDisabled(/* @__PURE__ */ new Date()) && "opacity-50 cursor-not-allowed hover:scale-100 active:scale-100"
717
+ ),
718
+ children: [
719
+ /* @__PURE__ */ jsx(Sparkles, { className: sizeStyles[size].actionIcon }),
720
+ todayLabel || t("today")
721
+ ]
722
+ }
723
+ ),
724
+ /* @__PURE__ */ jsxs(
725
+ "button",
726
+ {
727
+ type: "button",
728
+ onClick: () => {
729
+ onChange(void 0);
730
+ setIsOpen(false);
731
+ setViewDate(/* @__PURE__ */ new Date());
732
+ },
733
+ className: cn(
734
+ "flex-1 font-semibold rounded-xl",
735
+ "bg-linear-to-r from-destructive/10 to-destructive/5 border border-destructive/30",
736
+ "text-destructive hover:from-destructive/20 hover:to-destructive/10 hover:border-destructive/50",
737
+ "transition-all duration-300 flex items-center justify-center",
738
+ "hover:scale-[1.02] active:scale-[0.98] hover:shadow-md hover:shadow-destructive/10",
739
+ sizeStyles[size].actionButton
740
+ ),
741
+ children: [
742
+ /* @__PURE__ */ jsx(XIcon, { className: sizeStyles[size].actionIcon }),
743
+ clearLabel || t("clear")
744
+ ]
745
+ }
746
+ )
747
+ ] })
748
+ ] });
749
+ const autoId = useId();
750
+ const resolvedId = id ? String(id) : `datepicker-${autoId}`;
751
+ const labelId = label ? `${resolvedId}-label` : void 0;
752
+ const panelId = `${resolvedId}-panel`;
753
+ const labelSize = sizeStyles[size].label;
754
+ const radiusClass = size === "sm" ? "rounded-md" : "rounded-lg";
755
+ const verticalGap = size === "sm" ? "space-y-1.5" : size === "lg" ? "space-y-2.5" : "space-y-2";
756
+ const effectiveError = localRequiredError;
757
+ const globalConfig = useUnderverseUIConfig();
758
+ const resolvedBorderMode = borderMode ?? globalConfig.borderMode ?? "full";
759
+ return /* @__PURE__ */ jsxs("div", { className: cn("w-full group", verticalGap), children: [
760
+ label && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsxs(
761
+ "label",
762
+ {
763
+ id: labelId,
764
+ htmlFor: resolvedId,
765
+ onClick: () => inputRef.current?.focus(),
766
+ className: cn(
767
+ labelSize,
768
+ formControlLabelClass,
769
+ "transition-colors duration-300 cursor-pointer",
770
+ disabled ? "text-muted-foreground" : "text-foreground group-focus-within:text-primary hover:text-primary",
771
+ isOpen && "text-primary",
772
+ effectiveError && "text-destructive",
773
+ labelClassName
774
+ ),
775
+ children: [
776
+ label,
777
+ required && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1 animate-pulse", children: "*" })
778
+ ]
779
+ }
780
+ ) }),
781
+ /* @__PURE__ */ jsx(
782
+ "input",
783
+ {
784
+ tabIndex: -1,
785
+ "aria-hidden": "true",
786
+ value: value ? "selected" : "",
787
+ onChange: () => {
788
+ },
789
+ required,
790
+ disabled,
791
+ onInvalid: (e) => {
792
+ e.preventDefault();
793
+ setLocalRequiredError(tv("required"));
794
+ },
795
+ className: "pointer-events-none absolute h-0 w-0 opacity-0"
796
+ }
797
+ ),
798
+ /* @__PURE__ */ jsx(
799
+ Popover,
800
+ {
801
+ getPortalContainer,
802
+ open: isOpen,
803
+ onOpenChange: setIsOpen,
804
+ contentProps: { id: panelId },
805
+ placement: "bottom-start",
806
+ disabled,
807
+ contentWidth: sizeStyles[size].contentWidth,
808
+ borderMode: resolvedBorderMode,
809
+ contentClassName: cn(
810
+ "p-0",
811
+ "backdrop-blur-xl bg-popover/95 border-border/40 shadow-2xl",
812
+ // Keep usable on small viewports (wheel scroll should stay within the popover if it overflows)
813
+ "max-w-[calc(100vw-1rem)] max-h-[calc(100vh-6rem)] overflow-auto overscroll-contain",
814
+ sizeStyles[size].contentPadding,
815
+ "animate-in fade-in-0 zoom-in-95 slide-in-from-top-2 duration-300"
816
+ ),
817
+ trigger: iconOnly ? /* @__PURE__ */ jsxs(
818
+ "button",
819
+ {
820
+ type: "button",
821
+ "aria-label": value ? formatDateDisplay(value) : placeholder || t("placeholder"),
822
+ "aria-haspopup": "dialog",
823
+ "aria-expanded": isOpen,
824
+ "aria-controls": panelId,
825
+ onMouseDown: (event) => event.preventDefault(),
826
+ className: cn(
827
+ "inline-flex flex-col items-center gap-0.5 rounded px-1 py-0.5 text-sm text-foreground transition-colors",
828
+ disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/45",
829
+ className
830
+ ),
831
+ children: [
832
+ value ? /* @__PURE__ */ jsx("span", { className: "text-[10px] leading-none whitespace-nowrap", children: formatIconOnlyDate(value) }) : null,
833
+ /* @__PURE__ */ jsx(Calendar, { className: "h-4 w-4", "aria-hidden": "true" })
834
+ ]
835
+ }
836
+ ) : /* @__PURE__ */ jsxs(
837
+ "div",
838
+ {
839
+ ref: triggerRef,
840
+ className: cn(
841
+ "group flex w-full items-center justify-between bg-background/80 backdrop-blur-sm",
842
+ formControlFixedClass,
843
+ formControlGroupOutlineClass,
844
+ getBorderRadiusClass(resolvedBorderMode),
845
+ sizeStyles[size].trigger,
846
+ formControlSizeStyles[requestedSize].control,
847
+ disabled ? "border-border/40 opacity-50 cursor-not-allowed" : [
848
+ "focus-within:border-ring",
849
+ "hover:bg-accent/10"
850
+ ],
851
+ "transition-all duration-300 ease-out",
852
+ isOpen && "border-ring shadow-lg shadow-primary/10",
853
+ effectiveError && "border-destructive/60 bg-destructive/5",
854
+ className
855
+ ),
856
+ children: [
857
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 items-center gap-2.5 overflow-hidden", children: [
858
+ /* @__PURE__ */ jsx(
859
+ "div",
860
+ {
861
+ className: cn(
862
+ "flex items-center justify-center transition-colors duration-300",
863
+ isOpen ? "text-primary" : "text-muted-foreground group-hover:text-primary"
864
+ ),
865
+ children: /* @__PURE__ */ jsx(Calendar, { className: cn(sizeStyles[size].calendarIcon, "transition-transform duration-300", isOpen && "scale-110") })
866
+ }
867
+ ),
868
+ /* @__PURE__ */ jsx(
869
+ "input",
870
+ {
871
+ ref: inputRef,
872
+ id: resolvedId,
873
+ type: "text",
874
+ role: "combobox",
875
+ "aria-label": value ? formatDateDisplay(value) : placeholder || t("placeholder"),
876
+ "aria-labelledby": labelId,
877
+ "aria-haspopup": "dialog",
878
+ "aria-expanded": isOpen,
879
+ "aria-controls": panelId,
880
+ "aria-required": required,
881
+ "aria-invalid": !!effectiveError,
882
+ disabled,
883
+ placeholder: placeholder || t("placeholder"),
884
+ value: isFocused ? inputValue : value ? formatDateDisplay(value) : "",
885
+ onChange: handleInputChange,
886
+ onFocus: () => {
887
+ setIsFocused(true);
888
+ if (!disabled) setIsOpen(true);
889
+ },
890
+ onBlur: handleInputBlur,
891
+ onKeyDown: handleInputKeyDown,
892
+ onClick: (e) => {
893
+ if (isOpen) {
894
+ e.stopPropagation();
895
+ }
896
+ },
897
+ className: "h-full min-w-0 flex-1 truncate bg-transparent border-none outline-none focus:outline-none p-0 text-foreground font-medium placeholder-muted-foreground/60"
898
+ }
899
+ )
900
+ ] }),
901
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
902
+ value && /* @__PURE__ */ jsx(
903
+ "span",
904
+ {
905
+ role: "button",
906
+ "aria-label": clearLabel || t("clear"),
907
+ tabIndex: 0,
908
+ onClick: (e) => {
909
+ e.preventDefault();
910
+ e.stopPropagation();
911
+ onChange(void 0);
912
+ setViewDate(/* @__PURE__ */ new Date());
913
+ },
914
+ onKeyDown: (e) => {
915
+ if (e.key === "Enter" || e.key === " ") {
916
+ e.preventDefault();
917
+ e.stopPropagation();
918
+ onChange(void 0);
919
+ setLocalRequiredError(void 0);
920
+ setViewDate(/* @__PURE__ */ new Date());
921
+ }
922
+ },
923
+ className: cn(
924
+ "inline-flex items-center justify-center rounded-lg p-1",
925
+ "text-muted-foreground hover:text-destructive hover:bg-destructive/10",
926
+ "transition-all duration-200 cursor-pointer hover:scale-110"
927
+ ),
928
+ children: /* @__PURE__ */ jsx(XIcon, { className: "h-3.5 w-3.5" })
929
+ }
930
+ ),
931
+ /* @__PURE__ */ jsx(
932
+ "span",
933
+ {
934
+ className: cn("transition-all duration-300 text-muted-foreground group-hover:text-foreground", isOpen && "rotate-180 text-primary"),
935
+ children: /* @__PURE__ */ jsx("svg", { className: sizeStyles[size].calendarIcon, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
936
+ }
937
+ )
938
+ ] })
939
+ ]
940
+ }
941
+ ),
942
+ children: datePickerContent
943
+ }
944
+ ),
945
+ effectiveError && /* @__PURE__ */ jsx("div", { className: "text-xs text-destructive", children: effectiveError })
946
+ ] });
947
+ };
948
+ var DateRangePicker = ({
949
+ id,
950
+ startDate,
951
+ endDate,
952
+ onChange,
953
+ placeholder = "Select date range...",
954
+ className,
955
+ label,
956
+ labelClassName,
957
+ required = false,
958
+ disablePastDates = false,
959
+ minDate,
960
+ maxDate,
961
+ size: requestedSize = "md",
962
+ disabled = false,
963
+ borderMode,
964
+ getPortalContainer
965
+ }) => {
966
+ const size = requestedSize === "xs" ? "sm" : requestedSize === "xl" ? "lg" : requestedSize;
967
+ const locale = useSmartLocale();
968
+ const t = useSmartTranslations("DatePicker");
969
+ const tv = useSmartTranslations("ValidationInput");
970
+ const [isOpen, setIsOpen] = React.useState(false);
971
+ const [viewMode, setViewMode] = React.useState("calendar");
972
+ const [localRequiredError, setLocalRequiredError] = React.useState();
973
+ const wheelContainerRef = React.useRef(null);
974
+ const wheelDeltaRef = React.useRef(0);
975
+ const triggerRef = React.useRef(null);
976
+ const inputRef = React.useRef(null);
977
+ const [isFocused, setIsFocused] = React.useState(false);
978
+ const [inputValue, setInputValue] = React.useState("");
979
+ const todayDate = React.useMemo(() => {
980
+ const today = /* @__PURE__ */ new Date();
981
+ return new Date(today.getFullYear(), today.getMonth(), today.getDate());
982
+ }, []);
983
+ const getRangeString = React.useCallback((s, e) => {
984
+ if (!s) return "";
985
+ const startStr = formatDateShort(s, locale);
986
+ if (!e) return `${startStr} - `;
987
+ return `${startStr} - ${formatDateShort(e, locale)}`;
988
+ }, [locale]);
989
+ React.useEffect(() => {
990
+ setInputValue((currentInput) => {
991
+ if (startDate) {
992
+ const parts = currentInput.split(/\s*-\s*/);
993
+ const inputStart = parts[0] ? parseDateString(parts[0], locale) : null;
994
+ const inputEnd = parts[1] ? parseDateString(parts[1], locale) : null;
995
+ const startSame = inputStart && inputStart.getTime() === startDate.getTime();
996
+ const endSame = !endDate && !inputEnd || endDate && inputEnd && endDate.getTime() === inputEnd.getTime();
997
+ return startSame && endSame ? currentInput : getRangeString(startDate, endDate);
998
+ }
999
+ return isFocused ? currentInput : "";
1000
+ });
1001
+ }, [endDate, getRangeString, isFocused, locale, startDate]);
1002
+ const handleInputChange = (e) => {
1003
+ const text = e.target.value;
1004
+ setInputValue(text);
1005
+ const parts = text.split(/\s*-\s*/);
1006
+ if (parts.length === 2) {
1007
+ const s = parseDateString(parts[0], locale);
1008
+ const eVal = parseDateString(parts[1], locale);
1009
+ if (s) {
1010
+ const isStartDisabled = disablePastDates && s < todayDate || !!minDay && s < minDay || !!maxDay && s > maxDay;
1011
+ if (!isStartDisabled) {
1012
+ setTempStart(s);
1013
+ if (eVal) {
1014
+ const isEndDisabled = disablePastDates && eVal < todayDate || !!minDay && eVal < minDay || !!maxDay && eVal > maxDay;
1015
+ if (!isEndDisabled && s <= eVal) {
1016
+ setTempEnd(eVal);
1017
+ onChange(s, eVal);
1018
+ setViewDate(s);
1019
+ setLocalRequiredError(void 0);
1020
+ } else {
1021
+ setTempEnd(null);
1022
+ onChange(s, null);
1023
+ }
1024
+ } else {
1025
+ setTempEnd(null);
1026
+ onChange(s, null);
1027
+ }
1028
+ }
1029
+ }
1030
+ } else if (parts.length === 1 && parts[0].trim()) {
1031
+ const s = parseDateString(parts[0], locale);
1032
+ if (s) {
1033
+ const isStartDisabled = disablePastDates && s < todayDate || !!minDay && s < minDay || !!maxDay && s > maxDay;
1034
+ if (!isStartDisabled) {
1035
+ setTempStart(s);
1036
+ setTempEnd(null);
1037
+ onChange(s, null);
1038
+ }
1039
+ }
1040
+ }
1041
+ };
1042
+ const handleInputBlur = () => {
1043
+ setIsFocused(false);
1044
+ if (!inputValue.trim()) {
1045
+ if (required) {
1046
+ setLocalRequiredError(tv("required"));
1047
+ } else {
1048
+ setTempStart(null);
1049
+ setTempEnd(null);
1050
+ if (startDate || endDate) onChange(void 0, void 0);
1051
+ setLocalRequiredError(void 0);
1052
+ }
1053
+ } else {
1054
+ const parts = inputValue.split(/\s*-\s*/);
1055
+ const s = parts[0] ? parseDateString(parts[0], locale) : null;
1056
+ const eVal = parts[1] ? parseDateString(parts[1], locale) : null;
1057
+ if (s) {
1058
+ const isStartDisabled = disablePastDates && s < todayDate || !!minDay && s < minDay || !!maxDay && s > maxDay;
1059
+ if (!isStartDisabled) {
1060
+ if (eVal) {
1061
+ const isEndDisabled = disablePastDates && eVal < todayDate || !!minDay && eVal < minDay || !!maxDay && eVal > maxDay;
1062
+ if (!isEndDisabled && s <= eVal) {
1063
+ setTempStart(s);
1064
+ setTempEnd(eVal);
1065
+ onChange(s, eVal);
1066
+ setInputValue(getRangeString(s, eVal));
1067
+ setLocalRequiredError(void 0);
1068
+ return;
1069
+ }
1070
+ } else {
1071
+ setTempStart(s);
1072
+ setTempEnd(null);
1073
+ onChange(s, null);
1074
+ setInputValue(getRangeString(s, null));
1075
+ setLocalRequiredError(void 0);
1076
+ return;
1077
+ }
1078
+ }
1079
+ }
1080
+ if (startDate) {
1081
+ setTempStart(normalizeToLocalDate(startDate));
1082
+ setTempEnd(normalizeToLocalDate(endDate));
1083
+ setInputValue(getRangeString(startDate, endDate));
1084
+ setLocalRequiredError(void 0);
1085
+ } else {
1086
+ setTempStart(null);
1087
+ setTempEnd(null);
1088
+ setInputValue("");
1089
+ if (required) {
1090
+ setLocalRequiredError(tv("required"));
1091
+ }
1092
+ }
1093
+ }
1094
+ };
1095
+ const handleInputKeyDown = (e) => {
1096
+ if (e.key === "Enter") {
1097
+ e.preventDefault();
1098
+ const parts = inputValue.split(/\s*-\s*/);
1099
+ const s = parts[0] ? parseDateString(parts[0], locale) : null;
1100
+ const eVal = parts[1] ? parseDateString(parts[1], locale) : null;
1101
+ if (s) {
1102
+ const isStartDisabled = disablePastDates && s < todayDate || !!minDay && s < minDay || !!maxDay && s > maxDay;
1103
+ if (!isStartDisabled) {
1104
+ if (eVal) {
1105
+ const isEndDisabled = disablePastDates && eVal < todayDate || !!minDay && eVal < minDay || !!maxDay && eVal > maxDay;
1106
+ if (!isEndDisabled && s <= eVal) {
1107
+ setTempStart(s);
1108
+ setTempEnd(eVal);
1109
+ onChange(s, eVal);
1110
+ setInputValue(getRangeString(s, eVal));
1111
+ setViewDate(s);
1112
+ setIsOpen(false);
1113
+ setLocalRequiredError(void 0);
1114
+ return;
1115
+ }
1116
+ } else {
1117
+ setTempStart(s);
1118
+ setTempEnd(null);
1119
+ onChange(s, null);
1120
+ setInputValue(getRangeString(s, null));
1121
+ setViewDate(s);
1122
+ setIsOpen(false);
1123
+ setLocalRequiredError(void 0);
1124
+ return;
1125
+ }
1126
+ }
1127
+ }
1128
+ setIsOpen(false);
1129
+ } else if (e.key === "Escape") {
1130
+ setIsOpen(false);
1131
+ }
1132
+ };
1133
+ const sizeStyles = {
1134
+ sm: {
1135
+ trigger: formControlSizeStyles.sm.compactControl,
1136
+ dayCell: "w-7 h-7 text-xs",
1137
+ monthCell: "w-6 h-6 text-xs",
1138
+ navButton: "p-1.5",
1139
+ navIcon: "h-3 w-3",
1140
+ calendarIcon: "h-3.5 w-3.5",
1141
+ clearIcon: "w-2.5 h-2.5",
1142
+ actionButton: "px-2 py-1 text-[10px] gap-1",
1143
+ actionIcon: "w-2.5 h-2.5",
1144
+ contentWidth: 240,
1145
+ contentPadding: "p-4",
1146
+ label: formControlSizeStyles.sm.label,
1147
+ weekdayLabel: "text-[9px] py-1",
1148
+ headerMargin: "mb-2",
1149
+ footerMargin: "mt-3 pt-2 gap-1.5"
1150
+ },
1151
+ md: {
1152
+ trigger: formControlSizeStyles.md.control,
1153
+ dayCell: "w-8 h-8 text-sm",
1154
+ monthCell: "w-8 h-8 text-sm",
1155
+ navButton: "p-2",
1156
+ navIcon: "h-4 w-4",
1157
+ calendarIcon: "h-4 w-4",
1158
+ clearIcon: "w-3.5 h-3.5",
1159
+ actionButton: "px-3 py-2 text-xs gap-2",
1160
+ actionIcon: "w-3.5 h-3.5",
1161
+ contentWidth: 280,
1162
+ contentPadding: "p-5",
1163
+ label: formControlSizeStyles.md.label,
1164
+ weekdayLabel: "text-[10px] py-1.5",
1165
+ headerMargin: "mb-4",
1166
+ footerMargin: "mt-4 pt-3 gap-2"
1167
+ },
1168
+ lg: {
1169
+ trigger: formControlSizeStyles.lg.control,
1170
+ dayCell: "w-10 h-10 text-base",
1171
+ monthCell: "w-10 h-10 text-base",
1172
+ navButton: "p-2.5",
1173
+ navIcon: "h-5 w-5",
1174
+ calendarIcon: "h-5 w-5",
1175
+ clearIcon: "w-4 h-4",
1176
+ actionButton: "px-4 py-2.5 text-sm gap-2",
1177
+ actionIcon: "w-4 h-4",
1178
+ contentWidth: 340,
1179
+ contentPadding: "p-6",
1180
+ label: formControlSizeStyles.lg.label,
1181
+ weekdayLabel: "text-xs py-2",
1182
+ headerMargin: "mb-5",
1183
+ footerMargin: "mt-5 pt-4 gap-2.5"
1184
+ }
1185
+ };
1186
+ const minDay = normalizeToLocalDate(minDate);
1187
+ const maxDay = normalizeToLocalDate(maxDate);
1188
+ const [viewDate, setViewDate] = React.useState(startDate || /* @__PURE__ */ new Date());
1189
+ const [tempStart, setTempStart] = React.useState(normalizeToLocalDate(startDate));
1190
+ const [tempEnd, setTempEnd] = React.useState(normalizeToLocalDate(endDate));
1191
+ const [hoveredDate, setHoveredDate] = React.useState(null);
1192
+ React.useEffect(() => {
1193
+ setTempStart(normalizeToLocalDate(startDate));
1194
+ }, [startDate]);
1195
+ React.useEffect(() => {
1196
+ setTempEnd(normalizeToLocalDate(endDate));
1197
+ }, [endDate]);
1198
+ React.useEffect(() => {
1199
+ if (!isOpen) {
1200
+ setViewMode("calendar");
1201
+ }
1202
+ }, [isOpen]);
1203
+ React.useEffect(() => {
1204
+ if (!required || startDate && endDate) {
1205
+ setLocalRequiredError(void 0);
1206
+ }
1207
+ }, [endDate, required, startDate]);
1208
+ const isSameDay = (a, b) => {
1209
+ if (!a || !b) return false;
1210
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
1211
+ };
1212
+ const inRange = (d, s, e) => d > s && d < e;
1213
+ const getDaysInMonth = (d) => new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
1214
+ const getFirstDayOfMonth = (d) => new Date(d.getFullYear(), d.getMonth(), 1).getDay();
1215
+ const navigateMonth = (direction) => {
1216
+ setViewDate((prev) => new Date(prev.getFullYear(), prev.getMonth() + (direction === "next" ? 1 : -1), 1));
1217
+ };
1218
+ const navigateYearRange = (direction) => {
1219
+ setViewDate((prev) => new Date(prev.getFullYear() + (direction === "next" ? 12 : -12), prev.getMonth(), 1));
1220
+ };
1221
+ const isElementVerticallyScrollable = (el) => {
1222
+ const style = window.getComputedStyle(el);
1223
+ const overflowY = style.overflowY;
1224
+ if (overflowY !== "auto" && overflowY !== "scroll") return false;
1225
+ const node = el;
1226
+ return node.scrollHeight > node.clientHeight + 1;
1227
+ };
1228
+ React.useEffect(() => {
1229
+ if (!isOpen) return;
1230
+ const container = wheelContainerRef.current;
1231
+ if (!container) return;
1232
+ const onWheel = (event) => {
1233
+ if (!container.contains(event.target)) return;
1234
+ if (event.ctrlKey) return;
1235
+ let node = event.target;
1236
+ while (node && node !== container) {
1237
+ if (isElementVerticallyScrollable(node)) return;
1238
+ node = node.parentElement;
1239
+ }
1240
+ if (isElementVerticallyScrollable(container)) return;
1241
+ event.preventDefault();
1242
+ event.stopPropagation();
1243
+ wheelDeltaRef.current += event.deltaY;
1244
+ const threshold = 70;
1245
+ if (Math.abs(wheelDeltaRef.current) < threshold) return;
1246
+ const steps = Math.trunc(wheelDeltaRef.current / threshold);
1247
+ wheelDeltaRef.current -= steps * threshold;
1248
+ const direction = steps > 0 ? "next" : "prev";
1249
+ for (let i = 0; i < Math.abs(steps); i++) {
1250
+ setViewDate((prev) => new Date(prev.getFullYear(), prev.getMonth() + (direction === "next" ? 1 : -1), 1));
1251
+ }
1252
+ };
1253
+ container.addEventListener("wheel", onWheel, { passive: false });
1254
+ return () => container.removeEventListener("wheel", onWheel);
1255
+ }, [isOpen]);
1256
+ const handleSelect = (date) => {
1257
+ const localDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
1258
+ if (!tempStart || tempStart && tempEnd) {
1259
+ setTempStart(localDate);
1260
+ setTempEnd(null);
1261
+ setHoveredDate(null);
1262
+ onChange(localDate, null);
1263
+ } else if (tempStart && !tempEnd) {
1264
+ if (localDate < tempStart) {
1265
+ setTempStart(localDate);
1266
+ onChange(localDate, null);
1267
+ } else {
1268
+ setTempEnd(localDate);
1269
+ setLocalRequiredError(void 0);
1270
+ onChange(tempStart, localDate);
1271
+ setIsOpen(false);
1272
+ }
1273
+ }
1274
+ };
1275
+ const handleSelectToday = () => {
1276
+ const today = /* @__PURE__ */ new Date();
1277
+ today.setHours(0, 0, 0, 0);
1278
+ const localToday = new Date(today.getFullYear(), today.getMonth(), today.getDate());
1279
+ const isPastDate = disablePastDates && localToday < today;
1280
+ const isOutOfRange = !!minDay && localToday < minDay || !!maxDay && localToday > maxDay;
1281
+ if (isPastDate || isOutOfRange) return;
1282
+ setTempStart(localToday);
1283
+ setTempEnd(localToday);
1284
+ setHoveredDate(null);
1285
+ setLocalRequiredError(void 0);
1286
+ onChange(localToday, localToday);
1287
+ setIsOpen(false);
1288
+ };
1289
+ const handleClear = () => {
1290
+ setTempStart(null);
1291
+ setTempEnd(null);
1292
+ setHoveredDate(null);
1293
+ onChange(void 0, void 0);
1294
+ setIsOpen(false);
1295
+ };
1296
+ const renderGrid = () => {
1297
+ const nodes = [];
1298
+ const daysInMonth = getDaysInMonth(viewDate);
1299
+ const firstDay = getFirstDayOfMonth(viewDate);
1300
+ const today = /* @__PURE__ */ new Date();
1301
+ today.setHours(0, 0, 0, 0);
1302
+ for (let i = 0; i < firstDay; i++) nodes.push(/* @__PURE__ */ jsx("div", { className: "w-8 h-8" }, `e-${i}`));
1303
+ for (let d = 1; d <= daysInMonth; d++) {
1304
+ const date = new Date(viewDate.getFullYear(), viewDate.getMonth(), d);
1305
+ const isPastDate = disablePastDates && date < today;
1306
+ const isOutOfRange = !!minDay && date < minDay || !!maxDay && date > maxDay;
1307
+ const isDisabled = isPastDate || isOutOfRange;
1308
+ const isSelectedStart = isSameDay(date, tempStart);
1309
+ const isSelectedEnd = isSameDay(date, tempEnd);
1310
+ const isHovering = hoveredDate && tempStart && !tempEnd;
1311
+ let isInRange = false;
1312
+ let isRangeStart = false;
1313
+ let isRangeEnd = false;
1314
+ if (tempStart && tempEnd) {
1315
+ if (isSameDay(date, tempStart)) isRangeStart = true;
1316
+ if (isSameDay(date, tempEnd)) isRangeEnd = true;
1317
+ if (inRange(date, tempStart, tempEnd)) isInRange = true;
1318
+ } else if (isHovering) {
1319
+ if (hoveredDate > tempStart) {
1320
+ if (isSameDay(date, tempStart)) isRangeStart = true;
1321
+ if (isSameDay(date, hoveredDate)) isRangeEnd = true;
1322
+ if (inRange(date, tempStart, hoveredDate)) isInRange = true;
1323
+ } else {
1324
+ if (isSameDay(date, hoveredDate)) isRangeStart = true;
1325
+ if (isSameDay(date, tempStart)) isRangeEnd = true;
1326
+ if (inRange(date, hoveredDate, tempStart)) isInRange = true;
1327
+ }
1328
+ }
1329
+ nodes.push(
1330
+ /* @__PURE__ */ jsx(
1331
+ "button",
1332
+ {
1333
+ type: "button",
1334
+ onClick: () => !isDisabled && handleSelect(date),
1335
+ disabled: isDisabled,
1336
+ onMouseEnter: () => !isDisabled && tempStart && !tempEnd && setHoveredDate(date),
1337
+ onMouseLeave: () => tempStart && !tempEnd && setHoveredDate(null),
1338
+ className: cn(
1339
+ "transition-all duration-200 focus:outline-none relative font-medium cursor-pointer",
1340
+ sizeStyles[size].dayCell,
1341
+ // Disabled/past date state
1342
+ isDisabled && "opacity-30 cursor-not-allowed text-muted-foreground",
1343
+ // Default state
1344
+ !isDisabled && !isInRange && !isRangeStart && !isRangeEnd && "hover:bg-accent/80 hover:text-accent-foreground hover:scale-105 rounded-lg",
1345
+ // Range selection styling - smooth continuous background with gradient
1346
+ isInRange && "bg-primary/15 text-foreground",
1347
+ (isRangeStart || isRangeEnd) && "bg-linear-to-br from-primary to-primary/80 text-primary-foreground hover:from-primary hover:to-primary/70 shadow-lg shadow-primary/25",
1348
+ // Only round the actual start and end of the range
1349
+ isRangeStart && !isRangeEnd && "rounded-l-lg rounded-r-none",
1350
+ isRangeEnd && !isRangeStart && "rounded-r-lg rounded-l-none",
1351
+ isRangeStart && isRangeEnd && "rounded-lg",
1352
+ // Single day selection
1353
+ // Hover effects for range
1354
+ isInRange && !isDisabled && "hover:bg-primary/25",
1355
+ !isDisabled && "focus:bg-accent focus:text-accent-foreground focus:z-10 focus:shadow-md"
1356
+ ),
1357
+ children: d
1358
+ },
1359
+ d
1360
+ )
1361
+ );
1362
+ }
1363
+ return nodes;
1364
+ };
1365
+ const renderMonthSelector = () => {
1366
+ const months = locale === "vi" ? ["Th1", "Th2", "Th3", "Th4", "Th5", "Th6", "Th7", "Th8", "Th9", "Th10", "Th11", "Th12"] : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1367
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 p-2", children: months.map((month, idx) => {
1368
+ const isSelected = viewDate.getMonth() === idx;
1369
+ return /* @__PURE__ */ jsx(
1370
+ "button",
1371
+ {
1372
+ type: "button",
1373
+ onClick: () => {
1374
+ setViewDate(new Date(viewDate.getFullYear(), idx, 1));
1375
+ setViewMode("calendar");
1376
+ },
1377
+ className: cn(
1378
+ "py-2 px-3 rounded-lg text-sm font-medium transition-all duration-200",
1379
+ isSelected ? "bg-primary text-primary-foreground shadow-md" : "hover:bg-accent/80 text-foreground hover:scale-105"
1380
+ ),
1381
+ children: month
1382
+ },
1383
+ month
1384
+ );
1385
+ }) });
1386
+ };
1387
+ const renderYearSelector = () => {
1388
+ const startYear = Math.floor(viewDate.getFullYear() / 12) * 12;
1389
+ const years = Array.from({ length: 12 }, (_, i) => startYear + i);
1390
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 p-2", children: years.map((year) => {
1391
+ const isSelected = viewDate.getFullYear() === year;
1392
+ return /* @__PURE__ */ jsx(
1393
+ "button",
1394
+ {
1395
+ type: "button",
1396
+ onClick: () => {
1397
+ setViewDate(new Date(year, viewDate.getMonth(), 1));
1398
+ setViewMode("month");
1399
+ },
1400
+ className: cn(
1401
+ "py-2 px-3 rounded-lg text-sm font-medium transition-all duration-200",
1402
+ isSelected ? "bg-primary text-primary-foreground shadow-md" : "hover:bg-accent/80 text-foreground hover:scale-105"
1403
+ ),
1404
+ children: year
1405
+ },
1406
+ year
1407
+ );
1408
+ }) });
1409
+ };
1410
+ const isTodayUnavailable = !!minDay && todayDate < minDay || !!maxDay && todayDate > maxDay;
1411
+ const panel = /* @__PURE__ */ jsxs("div", { ref: wheelContainerRef, className: "w-full", children: [
1412
+ /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between px-1", sizeStyles[size].headerMargin), children: [
1413
+ /* @__PURE__ */ jsx(
1414
+ "button",
1415
+ {
1416
+ type: "button",
1417
+ onClick: () => viewMode === "year" ? navigateYearRange("prev") : navigateMonth("prev"),
1418
+ className: cn(
1419
+ "rounded-xl transition-all duration-200",
1420
+ sizeStyles[size].navButton,
1421
+ "bg-muted/50 hover:bg-muted text-muted-foreground hover:text-foreground",
1422
+ "hover:scale-110 active:scale-95 hover:shadow-md",
1423
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
1424
+ ),
1425
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: sizeStyles[size].navIcon })
1426
+ }
1427
+ ),
1428
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1429
+ /* @__PURE__ */ jsx(
1430
+ "button",
1431
+ {
1432
+ type: "button",
1433
+ onClick: () => setViewMode(viewMode === "month" ? "calendar" : "month"),
1434
+ className: cn(
1435
+ "rounded-lg px-2 py-0.5 font-bold transition-colors duration-200",
1436
+ size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm",
1437
+ viewMode === "month" ? "bg-primary/15 text-primary" : "text-foreground hover:bg-accent/50"
1438
+ ),
1439
+ children: viewDate.toLocaleDateString(locale === "vi" ? "vi-VN" : "en-US", { month: "long" })
1440
+ }
1441
+ ),
1442
+ /* @__PURE__ */ jsx(
1443
+ "button",
1444
+ {
1445
+ type: "button",
1446
+ onClick: () => setViewMode(viewMode === "year" ? "calendar" : "year"),
1447
+ className: cn(
1448
+ "rounded-lg px-2 py-0.5 font-bold transition-colors duration-200",
1449
+ size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm",
1450
+ viewMode === "year" ? "bg-primary/15 text-primary" : "text-foreground hover:bg-accent/50"
1451
+ ),
1452
+ children: viewDate.getFullYear()
1453
+ }
1454
+ )
1455
+ ] }),
1456
+ /* @__PURE__ */ jsx(
1457
+ "button",
1458
+ {
1459
+ type: "button",
1460
+ onClick: () => viewMode === "year" ? navigateYearRange("next") : navigateMonth("next"),
1461
+ className: cn(
1462
+ "rounded-xl transition-all duration-200",
1463
+ sizeStyles[size].navButton,
1464
+ "bg-muted/50 hover:bg-muted text-muted-foreground hover:text-foreground",
1465
+ "hover:scale-110 active:scale-95 hover:shadow-md",
1466
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
1467
+ ),
1468
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: sizeStyles[size].navIcon })
1469
+ }
1470
+ )
1471
+ ] }),
1472
+ viewMode === "calendar" && /* @__PURE__ */ jsxs(Fragment, { children: [
1473
+ /* @__PURE__ */ jsx("div", { className: cn("grid grid-cols-7 gap-1 px-0.5", size === "sm" ? "mb-1" : size === "lg" ? "mb-3" : "mb-2"), children: (locale === "vi" ? ["CN", "T2", "T3", "T4", "T5", "T6", "T7"] : ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]).map((d, idx) => /* @__PURE__ */ jsx(
1474
+ "div",
1475
+ {
1476
+ className: cn(
1477
+ "text-center font-bold uppercase tracking-wide",
1478
+ sizeStyles[size].weekdayLabel,
1479
+ idx === 0 || idx === 6 ? "text-primary/70" : "text-muted-foreground/60"
1480
+ ),
1481
+ children: d
1482
+ },
1483
+ d
1484
+ )) }),
1485
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-0.5 p-1 rounded-xl bg-muted/20", children: renderGrid() })
1486
+ ] }),
1487
+ viewMode === "month" && renderMonthSelector(),
1488
+ viewMode === "year" && renderYearSelector(),
1489
+ /* @__PURE__ */ jsxs("div", { className: cn("flex items-center border-t border-border/50", sizeStyles[size].footerMargin), children: [
1490
+ /* @__PURE__ */ jsxs(
1491
+ "button",
1492
+ {
1493
+ type: "button",
1494
+ onClick: handleSelectToday,
1495
+ disabled: isTodayUnavailable,
1496
+ className: cn(
1497
+ "flex-1 font-semibold rounded-xl",
1498
+ "bg-linear-to-r from-primary/10 to-primary/5 border border-primary/30",
1499
+ "text-primary hover:from-primary/20 hover:to-primary/10 hover:border-primary/50",
1500
+ "transition-all duration-300 flex items-center justify-center",
1501
+ "hover:scale-[1.02] active:scale-[0.98] hover:shadow-md hover:shadow-primary/10",
1502
+ sizeStyles[size].actionButton,
1503
+ isTodayUnavailable && "opacity-50 cursor-not-allowed hover:scale-100 active:scale-100"
1504
+ ),
1505
+ children: [
1506
+ /* @__PURE__ */ jsx(Sparkles, { className: sizeStyles[size].actionIcon }),
1507
+ t("today")
1508
+ ]
1509
+ }
1510
+ ),
1511
+ /* @__PURE__ */ jsxs(
1512
+ "button",
1513
+ {
1514
+ type: "button",
1515
+ onClick: handleClear,
1516
+ className: cn(
1517
+ "flex-1 font-semibold rounded-xl",
1518
+ "bg-linear-to-r from-destructive/10 to-destructive/5 border border-destructive/30",
1519
+ "text-destructive hover:from-destructive/20 hover:to-destructive/10 hover:border-destructive/50",
1520
+ "transition-all duration-300 flex items-center justify-center",
1521
+ "hover:scale-[1.02] active:scale-[0.98] hover:shadow-md hover:shadow-destructive/10",
1522
+ sizeStyles[size].actionButton
1523
+ ),
1524
+ children: [
1525
+ /* @__PURE__ */ jsx(XIcon, { className: sizeStyles[size].actionIcon }),
1526
+ t("clear")
1527
+ ]
1528
+ }
1529
+ )
1530
+ ] })
1531
+ ] });
1532
+ const displayFormat = (date) => formatDateShort(date);
1533
+ const displayLabel = tempStart && tempEnd ? `${displayFormat(tempStart)} - ${displayFormat(tempEnd)}` : tempStart ? `${displayFormat(tempStart)} - ...` : placeholder;
1534
+ const effectiveError = localRequiredError;
1535
+ const autoId = useId();
1536
+ const resolvedId = id ? String(id) : `daterangepicker-${autoId}`;
1537
+ const labelId = label ? `${resolvedId}-label` : void 0;
1538
+ const panelId = `${resolvedId}-panel`;
1539
+ const globalConfig = useUnderverseUIConfig();
1540
+ const resolvedBorderMode = borderMode ?? globalConfig.borderMode ?? "full";
1541
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-1.5", className), children: [
1542
+ label && /* @__PURE__ */ jsxs(
1543
+ "label",
1544
+ {
1545
+ id: labelId,
1546
+ htmlFor: resolvedId,
1547
+ onClick: () => inputRef.current?.focus(),
1548
+ className: cn(
1549
+ size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm",
1550
+ formControlLabelClass,
1551
+ "transition-colors duration-300",
1552
+ disabled ? "text-muted-foreground cursor-not-allowed" : "text-foreground group-focus-within:text-primary hover:text-primary cursor-pointer",
1553
+ isOpen && "text-primary",
1554
+ effectiveError && "text-destructive",
1555
+ labelClassName
1556
+ ),
1557
+ children: [
1558
+ label,
1559
+ required && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
1560
+ ]
1561
+ }
1562
+ ),
1563
+ /* @__PURE__ */ jsx(
1564
+ "input",
1565
+ {
1566
+ tabIndex: -1,
1567
+ "aria-hidden": "true",
1568
+ value: startDate && endDate ? "selected" : "",
1569
+ onChange: () => {
1570
+ },
1571
+ required,
1572
+ disabled,
1573
+ onInvalid: (e) => {
1574
+ e.preventDefault();
1575
+ setLocalRequiredError(tv("required"));
1576
+ },
1577
+ className: "pointer-events-none absolute h-0 w-0 opacity-0"
1578
+ }
1579
+ ),
1580
+ /* @__PURE__ */ jsx(
1581
+ Popover,
1582
+ {
1583
+ getPortalContainer,
1584
+ open: isOpen,
1585
+ onOpenChange: setIsOpen,
1586
+ contentProps: { id: panelId },
1587
+ placement: "bottom-start",
1588
+ disabled,
1589
+ contentWidth: sizeStyles[size].contentWidth,
1590
+ borderMode: resolvedBorderMode,
1591
+ contentClassName: cn(
1592
+ "p-0",
1593
+ "backdrop-blur-xl bg-popover/95 border-border/40 shadow-2xl",
1594
+ "max-w-[calc(100vw-1rem)] max-h-[calc(100vh-6rem)] overflow-auto overscroll-contain",
1595
+ sizeStyles[size].contentPadding,
1596
+ "animate-in fade-in-0 zoom-in-95 slide-in-from-top-2 duration-300"
1597
+ ),
1598
+ trigger: /* @__PURE__ */ jsxs(
1599
+ "div",
1600
+ {
1601
+ ref: triggerRef,
1602
+ className: cn(
1603
+ "group flex w-full items-center justify-between bg-background/80 backdrop-blur-sm",
1604
+ formControlFixedClass,
1605
+ formControlGroupOutlineClass,
1606
+ getBorderRadiusClass(resolvedBorderMode),
1607
+ sizeStyles[size].trigger,
1608
+ formControlSizeStyles[requestedSize].control,
1609
+ disabled ? "border-border/40 opacity-50 cursor-not-allowed" : [
1610
+ "focus-within:border-ring",
1611
+ "hover:bg-accent/10"
1612
+ ],
1613
+ "transition-all duration-300 ease-out",
1614
+ isOpen && "border-ring shadow-lg shadow-primary/10",
1615
+ effectiveError && "border-destructive/60 bg-destructive/5"
1616
+ ),
1617
+ children: [
1618
+ /* @__PURE__ */ jsxs("div", { className: cn("flex min-h-0 min-w-0 flex-1 items-center overflow-hidden", size === "sm" ? "gap-1.5" : "gap-2.5"), children: [
1619
+ /* @__PURE__ */ jsx(
1620
+ "div",
1621
+ {
1622
+ className: cn(
1623
+ "flex items-center justify-center transition-colors duration-300",
1624
+ effectiveError ? "text-destructive" : isOpen ? "text-primary" : "text-muted-foreground group-hover:text-primary"
1625
+ ),
1626
+ children: /* @__PURE__ */ jsx(Calendar, { className: cn("transition-transform duration-300", sizeStyles[size].calendarIcon, isOpen && "scale-110") })
1627
+ }
1628
+ ),
1629
+ /* @__PURE__ */ jsx(
1630
+ "input",
1631
+ {
1632
+ ref: inputRef,
1633
+ id: resolvedId,
1634
+ type: "text",
1635
+ role: "combobox",
1636
+ "aria-label": tempStart ? displayLabel : placeholder,
1637
+ "aria-labelledby": labelId,
1638
+ "aria-haspopup": "dialog",
1639
+ "aria-expanded": isOpen,
1640
+ "aria-controls": panelId,
1641
+ "aria-required": required,
1642
+ "aria-invalid": !!effectiveError,
1643
+ disabled,
1644
+ placeholder,
1645
+ value: isFocused ? inputValue : tempStart ? displayLabel : "",
1646
+ onChange: handleInputChange,
1647
+ onFocus: () => {
1648
+ setIsFocused(true);
1649
+ if (!disabled) setIsOpen(true);
1650
+ },
1651
+ onBlur: handleInputBlur,
1652
+ onKeyDown: handleInputKeyDown,
1653
+ onClick: (e) => {
1654
+ if (isOpen) {
1655
+ e.stopPropagation();
1656
+ }
1657
+ },
1658
+ className: "h-full min-w-0 flex-1 truncate bg-transparent border-none outline-none focus:outline-none p-0 text-foreground font-medium placeholder-muted-foreground/60"
1659
+ }
1660
+ )
1661
+ ] }),
1662
+ /* @__PURE__ */ jsx("span", { className: cn("shrink-0 transition-all duration-300 text-muted-foreground group-hover:text-foreground", isOpen && "rotate-180 text-primary"), children: /* @__PURE__ */ jsx("svg", { className: cn(sizeStyles[size].navIcon), fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) }) })
1663
+ ]
1664
+ }
1665
+ ),
1666
+ children: panel
1667
+ }
1668
+ ),
1669
+ effectiveError && /* @__PURE__ */ jsx("div", { className: "text-xs text-destructive", children: effectiveError })
1670
+ ] });
1671
+ };
1672
+ var CompactDatePicker = ({ value, onChange, className, getPortalContainer }) => {
1673
+ return /* @__PURE__ */ jsx(
1674
+ DatePicker,
1675
+ {
1676
+ value,
1677
+ onChange,
1678
+ size: "sm",
1679
+ className: cn("max-w-56", className),
1680
+ placeholder: "Date",
1681
+ getPortalContainer
1682
+ }
1683
+ );
1684
+ };
1685
+
1686
+ export {
1687
+ date_exports,
1688
+ DatePicker,
1689
+ DateRangePicker,
1690
+ CompactDatePicker
1691
+ };
1692
+ //# sourceMappingURL=chunk-JG3U26V6.js.map