@xaui/native 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1647 @@
1
+ import {
2
+ CalendarIcon,
3
+ ChevronDownIcon,
4
+ ChevronLeftIcon,
5
+ ChevronRightIcon
6
+ } from "./chunk-CKGZOJVV.js";
7
+ import {
8
+ CloseIcon
9
+ } from "./chunk-SIXET7TJ.js";
10
+ import {
11
+ Portal,
12
+ useXUITheme
13
+ } from "./chunk-NBRASCX4.js";
14
+
15
+ // src/components/datepicker/datepicker.tsx
16
+ import React7, { useCallback as useCallback5, useMemo as useMemo6, useRef as useRef6 } from "react";
17
+ import { Text as Text7, View as View7 } from "react-native";
18
+
19
+ // src/components/datepicker/datepicker.hook.ts
20
+ import { useMemo } from "react";
21
+ import { getSafeThemeColor } from "@xaui/core";
22
+ import { colors } from "@xaui/core/palette";
23
+ var useDatePickerColorScheme = (themeColor) => {
24
+ const theme = useXUITheme();
25
+ const safeThemeColor = getSafeThemeColor(themeColor);
26
+ const colorScheme = theme.colors[safeThemeColor];
27
+ return { theme, colorScheme };
28
+ };
29
+ var useDatePickerSizeStyles = (size) => {
30
+ const theme = useXUITheme();
31
+ return useMemo(() => {
32
+ const sizes = {
33
+ xs: {
34
+ minHeight: 34,
35
+ paddingHorizontal: theme.spacing.sm,
36
+ paddingVertical: theme.spacing.xs,
37
+ fontSize: theme.fontSizes.xs,
38
+ labelSize: theme.fontSizes.xs
39
+ },
40
+ sm: {
41
+ minHeight: 38,
42
+ paddingHorizontal: theme.spacing.md,
43
+ paddingVertical: theme.spacing.xs,
44
+ fontSize: theme.fontSizes.sm,
45
+ labelSize: theme.fontSizes.xs
46
+ },
47
+ md: {
48
+ minHeight: 42,
49
+ paddingHorizontal: theme.spacing.md,
50
+ paddingVertical: theme.spacing.sm,
51
+ fontSize: theme.fontSizes.md,
52
+ labelSize: theme.fontSizes.sm
53
+ },
54
+ lg: {
55
+ minHeight: 50,
56
+ paddingHorizontal: theme.spacing.lg,
57
+ paddingVertical: theme.spacing.md,
58
+ fontSize: theme.fontSizes.lg,
59
+ labelSize: theme.fontSizes.md
60
+ }
61
+ };
62
+ return sizes[size];
63
+ }, [size, theme]);
64
+ };
65
+ var useDatePickerRadiusStyles = (radius) => {
66
+ const theme = useXUITheme();
67
+ return useMemo(() => {
68
+ const radii = {
69
+ none: theme.borderRadius.none,
70
+ sm: theme.borderRadius.sm,
71
+ md: theme.borderRadius.md,
72
+ lg: theme.borderRadius.lg,
73
+ full: theme.borderRadius.full
74
+ };
75
+ return { borderRadius: radii[radius] };
76
+ }, [radius, theme]);
77
+ };
78
+ var useDatePickerVariantStyles = (themeColor, variant, isInvalid) => {
79
+ const { theme, colorScheme } = useDatePickerColorScheme(themeColor);
80
+ return useMemo(() => {
81
+ let borderColor = isInvalid ? theme.colors.danger.main : colorScheme.main;
82
+ if ((variant === "outlined" || variant === "faded") && themeColor === "default") {
83
+ borderColor = colors.gray[300];
84
+ }
85
+ const variantStyles = {
86
+ outlined: {
87
+ backgroundColor: "transparent",
88
+ borderWidth: theme.borderWidth.md,
89
+ borderColor
90
+ },
91
+ flat: {
92
+ backgroundColor: colorScheme.background,
93
+ borderWidth: 0
94
+ },
95
+ light: {
96
+ backgroundColor: "transparent",
97
+ borderWidth: 0
98
+ },
99
+ faded: {
100
+ backgroundColor: `${colorScheme.background}90`,
101
+ borderWidth: theme.borderWidth.md,
102
+ borderColor
103
+ },
104
+ underlined: {
105
+ backgroundColor: "transparent",
106
+ borderBottomWidth: theme.borderWidth.md,
107
+ borderColor
108
+ }
109
+ };
110
+ return variantStyles[variant];
111
+ }, [variant, theme, colorScheme, isInvalid, themeColor]);
112
+ };
113
+ var useDatePickerLabelStyle = (themeColor, isInvalid, labelSize) => {
114
+ const { theme, colorScheme } = useDatePickerColorScheme(themeColor);
115
+ return useMemo(() => {
116
+ let baseColor = theme.colors.foreground;
117
+ if (isInvalid) {
118
+ baseColor = theme.colors.danger.main;
119
+ } else if (themeColor !== "default") {
120
+ baseColor = colorScheme.main;
121
+ }
122
+ return {
123
+ fontSize: labelSize,
124
+ color: baseColor
125
+ };
126
+ }, [isInvalid, labelSize, theme, themeColor, colorScheme]);
127
+ };
128
+ var useDatePickerHelperColor = (isInvalid) => {
129
+ const theme = useXUITheme();
130
+ return useMemo(() => {
131
+ if (isInvalid) {
132
+ return theme.colors.danger.main;
133
+ }
134
+ return colors.gray[600];
135
+ }, [isInvalid, theme]);
136
+ };
137
+
138
+ // src/components/datepicker/datepicker.state.hook.ts
139
+ import { useCallback, useState } from "react";
140
+ var useDatePickerState = ({
141
+ value,
142
+ defaultValue,
143
+ onChange
144
+ }) => {
145
+ const [internalValue, setInternalValue] = useState(
146
+ defaultValue ?? null
147
+ );
148
+ const selectedDate = value !== void 0 ? value : internalValue;
149
+ const updateDate = useCallback(
150
+ (date) => {
151
+ if (value === void 0) {
152
+ setInternalValue(date);
153
+ }
154
+ onChange?.(date);
155
+ },
156
+ [value, onChange]
157
+ );
158
+ return { selectedDate, updateDate };
159
+ };
160
+ var useDatePickerOpenState = ({
161
+ isDisabled,
162
+ onOpenChange,
163
+ onOpen,
164
+ onClose
165
+ }) => {
166
+ const [isOpen, setInternalOpen] = useState(false);
167
+ const setOpen = useCallback(
168
+ (open) => {
169
+ if (isDisabled && open) return;
170
+ setInternalOpen(open);
171
+ onOpenChange?.(open);
172
+ if (open) {
173
+ onOpen?.();
174
+ } else {
175
+ onClose?.();
176
+ }
177
+ },
178
+ [isDisabled, onOpenChange, onOpen, onClose]
179
+ );
180
+ return { isOpen, setOpen };
181
+ };
182
+ var useDatePickerViewState = (initialDate) => {
183
+ const now = /* @__PURE__ */ new Date();
184
+ const [viewDate, setViewDate] = useState(initialDate ?? now);
185
+ const [viewMode, setViewMode] = useState("calendar");
186
+ const goToPreviousMonth = useCallback(() => {
187
+ setViewDate((prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1));
188
+ }, []);
189
+ const goToNextMonth = useCallback(() => {
190
+ setViewDate((prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1));
191
+ }, []);
192
+ const goToYear = useCallback((year) => {
193
+ setViewDate((prev) => new Date(year, prev.getMonth(), 1));
194
+ setViewMode("month");
195
+ }, []);
196
+ const goToMonth = useCallback((month) => {
197
+ setViewDate((prev) => new Date(prev.getFullYear(), month, 1));
198
+ setViewMode("calendar");
199
+ }, []);
200
+ const goToToday = useCallback(() => {
201
+ setViewDate(/* @__PURE__ */ new Date());
202
+ setViewMode("calendar");
203
+ }, []);
204
+ const toggleYearPicker = useCallback(() => {
205
+ setViewMode((prev) => prev === "year" ? "calendar" : "year");
206
+ }, []);
207
+ const syncViewToDate = useCallback((date) => {
208
+ setViewDate(date);
209
+ }, []);
210
+ return {
211
+ viewDate,
212
+ viewMode,
213
+ goToPreviousMonth,
214
+ goToNextMonth,
215
+ goToYear,
216
+ goToMonth,
217
+ goToToday,
218
+ toggleYearPicker,
219
+ syncViewToDate
220
+ };
221
+ };
222
+
223
+ // src/components/datepicker/datepicker.utils.ts
224
+ var getWeekdayNames = (locale, firstDayOfWeek) => {
225
+ const baseDate = new Date(2024, 0, 1);
226
+ const dayIndex = baseDate.getDay();
227
+ const offset = firstDayOfWeek - dayIndex;
228
+ const days = [];
229
+ for (let i = 0; i < 7; i++) {
230
+ const date = new Date(2024, 0, 1 + offset + i);
231
+ days.push(
232
+ date.toLocaleDateString(locale, { weekday: "short" }).slice(0, 2)
233
+ );
234
+ }
235
+ return days;
236
+ };
237
+ var getMonthNames = (locale) => {
238
+ const months = [];
239
+ for (let i = 0; i < 12; i++) {
240
+ const date = new Date(2024, i, 1);
241
+ months.push(date.toLocaleDateString(locale, { month: "long" }));
242
+ }
243
+ return months;
244
+ };
245
+ var getMonthName = (month, locale) => {
246
+ const date = new Date(2024, month, 1);
247
+ return date.toLocaleDateString(locale, { month: "long" });
248
+ };
249
+ var formatDate = (date, locale) => {
250
+ return date.toLocaleDateString(locale, {
251
+ year: "numeric",
252
+ month: "short",
253
+ day: "numeric"
254
+ });
255
+ };
256
+ var isSameDay = (a, b) => {
257
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
258
+ };
259
+ var isToday = (date) => {
260
+ return isSameDay(date, /* @__PURE__ */ new Date());
261
+ };
262
+ var isDateInRange = (date, minDate, maxDate) => {
263
+ const normalized = new Date(date.getFullYear(), date.getMonth(), date.getDate());
264
+ if (minDate) {
265
+ const minNormalized = new Date(
266
+ minDate.getFullYear(),
267
+ minDate.getMonth(),
268
+ minDate.getDate()
269
+ );
270
+ if (normalized < minNormalized) return false;
271
+ }
272
+ if (maxDate) {
273
+ const maxNormalized = new Date(
274
+ maxDate.getFullYear(),
275
+ maxDate.getMonth(),
276
+ maxDate.getDate()
277
+ );
278
+ if (normalized > maxNormalized) return false;
279
+ }
280
+ return true;
281
+ };
282
+ var getCalendarDays = (year, month, firstDayOfWeek, minDate, maxDate) => {
283
+ const firstOfMonth = new Date(year, month, 1);
284
+ const lastOfMonth = new Date(year, month + 1, 0);
285
+ const daysInMonth = lastOfMonth.getDate();
286
+ let startDayOfWeek = firstOfMonth.getDay() - firstDayOfWeek;
287
+ if (startDayOfWeek < 0) startDayOfWeek += 7;
288
+ const days = [];
289
+ const prevMonth = new Date(year, month, 0);
290
+ const prevMonthDays = prevMonth.getDate();
291
+ for (let i = startDayOfWeek - 1; i >= 0; i--) {
292
+ const day = prevMonthDays - i;
293
+ const date = new Date(year, month - 1, day);
294
+ days.push({
295
+ date,
296
+ day,
297
+ isCurrentMonth: false,
298
+ isToday: isToday(date),
299
+ isDisabled: !isDateInRange(date, minDate, maxDate)
300
+ });
301
+ }
302
+ for (let day = 1; day <= daysInMonth; day++) {
303
+ const date = new Date(year, month, day);
304
+ days.push({
305
+ date,
306
+ day,
307
+ isCurrentMonth: true,
308
+ isToday: isToday(date),
309
+ isDisabled: !isDateInRange(date, minDate, maxDate)
310
+ });
311
+ }
312
+ const remainingSlots = 42 - days.length;
313
+ for (let day = 1; day <= remainingSlots; day++) {
314
+ const date = new Date(year, month + 1, day);
315
+ days.push({
316
+ date,
317
+ day,
318
+ isCurrentMonth: false,
319
+ isToday: isToday(date),
320
+ isDisabled: !isDateInRange(date, minDate, maxDate)
321
+ });
322
+ }
323
+ return days;
324
+ };
325
+ var getYearRange = (minDate, maxDate) => {
326
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
327
+ const startYear = minDate ? minDate.getFullYear() : currentYear - 50;
328
+ const endYear = maxDate ? maxDate.getFullYear() : currentYear + 50;
329
+ const years = [];
330
+ for (let year = startYear; year <= endYear; year++) {
331
+ years.push(year);
332
+ }
333
+ return years;
334
+ };
335
+ var LOCALE_LABELS = {
336
+ fr: { selectDate: "Choisir une date", today: "Aujourd'hui", confirm: "OK" },
337
+ de: { selectDate: "Datum ausw\xE4hlen", today: "Heute", confirm: "OK" },
338
+ es: { selectDate: "Seleccionar fecha", today: "Hoy", confirm: "OK" },
339
+ it: { selectDate: "Seleziona data", today: "Oggi", confirm: "OK" },
340
+ pt: { selectDate: "Selecionar data", today: "Hoje", confirm: "OK" },
341
+ nl: { selectDate: "Datum selecteren", today: "Vandaag", confirm: "OK" },
342
+ pl: { selectDate: "Wybierz dat\u0119", today: "Dzisiaj", confirm: "OK" },
343
+ ru: { selectDate: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0430\u0442\u0443", today: "\u0421\u0435\u0433\u043E\u0434\u043D\u044F", confirm: "OK" },
344
+ ja: { selectDate: "\u65E5\u4ED8\u3092\u9078\u629E", today: "\u4ECA\u65E5", confirm: "OK" },
345
+ ko: { selectDate: "\uB0A0\uC9DC \uC120\uD0DD", today: "\uC624\uB298", confirm: "\uD655\uC778" },
346
+ zh: { selectDate: "\u9009\u62E9\u65E5\u671F", today: "\u4ECA\u5929", confirm: "\u786E\u5B9A" },
347
+ ar: { selectDate: "\u0627\u062E\u062A\u0631 \u0627\u0644\u062A\u0627\u0631\u064A\u062E", today: "\u0627\u0644\u064A\u0648\u0645", confirm: "\u0645\u0648\u0627\u0641\u0642" },
348
+ tr: { selectDate: "Tarih se\xE7in", today: "Bug\xFCn", confirm: "Tamam" }
349
+ };
350
+ var DEFAULT_LABELS = {
351
+ selectDate: "Select date",
352
+ today: "Today",
353
+ confirm: "OK"
354
+ };
355
+ var getDatePickerLabels = (locale) => {
356
+ const baseLocale = locale.split("-")[0].toLowerCase();
357
+ return LOCALE_LABELS[baseLocale] ?? DEFAULT_LABELS;
358
+ };
359
+ var getFirstDayOfWeek = (locale) => {
360
+ const mondayLocales = [
361
+ "fr",
362
+ "de",
363
+ "es",
364
+ "it",
365
+ "pt",
366
+ "nl",
367
+ "pl",
368
+ "ru",
369
+ "sv",
370
+ "da",
371
+ "fi",
372
+ "nb",
373
+ "nn",
374
+ "cs",
375
+ "sk",
376
+ "hu",
377
+ "ro",
378
+ "bg",
379
+ "hr",
380
+ "sl",
381
+ "uk",
382
+ "tr",
383
+ "el",
384
+ "et",
385
+ "lt",
386
+ "lv"
387
+ ];
388
+ const baseLocale = locale.split("-")[0].toLowerCase();
389
+ return mondayLocales.includes(baseLocale) ? 1 : 0;
390
+ };
391
+
392
+ // src/components/datepicker/datepicker.style.ts
393
+ import { StyleSheet } from "react-native";
394
+ var styles = StyleSheet.create({
395
+ container: {
396
+ gap: 6,
397
+ position: "relative"
398
+ },
399
+ fullWidth: {
400
+ flexShrink: 1,
401
+ flexBasis: "auto",
402
+ width: "100%"
403
+ },
404
+ minWidth: {
405
+ minWidth: 170
406
+ },
407
+ label: {
408
+ fontWeight: "500"
409
+ },
410
+ trigger: {
411
+ flexDirection: "row",
412
+ alignItems: "center",
413
+ justifyContent: "space-between",
414
+ gap: 8
415
+ },
416
+ triggerContent: {
417
+ flexDirection: "row",
418
+ alignItems: "center",
419
+ flex: 1,
420
+ gap: 8
421
+ },
422
+ triggerText: {
423
+ flex: 1
424
+ },
425
+ clearButton: {
426
+ padding: 2,
427
+ paddingLeft: 4,
428
+ marginRight: -4
429
+ },
430
+ helperText: {
431
+ fontSize: 12
432
+ },
433
+ disabled: {
434
+ opacity: 0.5
435
+ },
436
+ outsideLeftRow: {
437
+ flexDirection: "row",
438
+ alignItems: "center",
439
+ gap: 12
440
+ }
441
+ });
442
+
443
+ // src/components/dialogs/datepicker-dialog/datepicker-dialog.tsx
444
+ import React5, { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef5 } from "react";
445
+ import {
446
+ Animated as Animated5,
447
+ BackHandler,
448
+ Pressable as Pressable5,
449
+ Text as Text5,
450
+ View as View5,
451
+ useWindowDimensions
452
+ } from "react-native";
453
+ import { GestureHandlerRootView } from "react-native-gesture-handler";
454
+
455
+ // src/components/dialogs/datepicker-dialog/datepicker-dialog.animation.ts
456
+ import { useCallback as useCallback2, useEffect, useRef, useState as useState2 } from "react";
457
+ import { Animated, Easing } from "react-native";
458
+ var useDatePickerDialogAnimation = ({
459
+ visible,
460
+ fadeAnim,
461
+ slideAnim,
462
+ scaleAnim,
463
+ onCloseComplete
464
+ }) => {
465
+ const [shouldRender, setShouldRender] = useState2(false);
466
+ const isClosingRef = useRef(false);
467
+ useEffect(() => {
468
+ if (visible) {
469
+ setShouldRender(true);
470
+ isClosingRef.current = false;
471
+ Animated.parallel([
472
+ Animated.timing(fadeAnim, {
473
+ toValue: 1,
474
+ duration: 220,
475
+ useNativeDriver: true
476
+ }),
477
+ Animated.spring(slideAnim, {
478
+ toValue: 1,
479
+ useNativeDriver: true,
480
+ damping: 20,
481
+ stiffness: 300,
482
+ mass: 0.8
483
+ }),
484
+ Animated.spring(scaleAnim, {
485
+ toValue: 1,
486
+ useNativeDriver: true,
487
+ damping: 18,
488
+ stiffness: 280,
489
+ mass: 0.8
490
+ })
491
+ ]).start();
492
+ return;
493
+ }
494
+ if (!shouldRender || isClosingRef.current) return;
495
+ isClosingRef.current = true;
496
+ Animated.parallel([
497
+ Animated.timing(fadeAnim, {
498
+ toValue: 0,
499
+ duration: 180,
500
+ easing: Easing.out(Easing.ease),
501
+ useNativeDriver: true
502
+ }),
503
+ Animated.timing(slideAnim, {
504
+ toValue: 0,
505
+ duration: 180,
506
+ easing: Easing.out(Easing.ease),
507
+ useNativeDriver: true
508
+ }),
509
+ Animated.timing(scaleAnim, {
510
+ toValue: 0,
511
+ duration: 180,
512
+ easing: Easing.out(Easing.ease),
513
+ useNativeDriver: true
514
+ })
515
+ ]).start(() => {
516
+ setShouldRender(false);
517
+ isClosingRef.current = false;
518
+ onCloseComplete();
519
+ });
520
+ }, [visible, fadeAnim, slideAnim, scaleAnim, shouldRender, onCloseComplete]);
521
+ return { shouldRender };
522
+ };
523
+ var useViewTransitionAnimation = ({
524
+ fadeAnim
525
+ }) => {
526
+ const animate = useCallback2(() => {
527
+ fadeAnim.setValue(0);
528
+ Animated.timing(fadeAnim, {
529
+ toValue: 1,
530
+ duration: 200,
531
+ useNativeDriver: true
532
+ }).start();
533
+ }, [fadeAnim]);
534
+ return { animate };
535
+ };
536
+ var useMonthSlideAnimation = ({
537
+ slideAnim,
538
+ fadeAnim
539
+ }) => {
540
+ const animate = useCallback2(
541
+ (direction) => {
542
+ const startX = direction === "right" ? 60 : -60;
543
+ slideAnim.setValue(startX);
544
+ fadeAnim.setValue(0);
545
+ Animated.parallel([
546
+ Animated.spring(slideAnim, {
547
+ toValue: 0,
548
+ useNativeDriver: true,
549
+ damping: 22,
550
+ stiffness: 280,
551
+ mass: 0.7
552
+ }),
553
+ Animated.timing(fadeAnim, {
554
+ toValue: 1,
555
+ duration: 180,
556
+ useNativeDriver: true
557
+ })
558
+ ]).start();
559
+ },
560
+ [slideAnim, fadeAnim]
561
+ );
562
+ return { animate };
563
+ };
564
+
565
+ // src/components/dialogs/datepicker-dialog/datepicker-dialog-calendar.tsx
566
+ import React, { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2 } from "react";
567
+ import { Animated as Animated2, Pressable, Text, View } from "react-native";
568
+ import {
569
+ PanGestureHandler,
570
+ State
571
+ } from "react-native-gesture-handler";
572
+
573
+ // src/components/dialogs/datepicker-dialog/datepicker-dialog.style.ts
574
+ import { StyleSheet as StyleSheet2 } from "react-native";
575
+ var styles2 = StyleSheet2.create({
576
+ backdrop: {
577
+ ...StyleSheet2.absoluteFillObject,
578
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
579
+ },
580
+ dialogContainer: {
581
+ flex: 1,
582
+ justifyContent: "center",
583
+ alignItems: "center",
584
+ zIndex: 1,
585
+ paddingHorizontal: 24
586
+ },
587
+ container: {
588
+ width: "100%",
589
+ maxWidth: 360,
590
+ borderRadius: 16,
591
+ overflow: "hidden",
592
+ elevation: 6,
593
+ shadowColor: "#000",
594
+ shadowOffset: { width: 0, height: 2 },
595
+ shadowOpacity: 0.25,
596
+ shadowRadius: 8
597
+ },
598
+ header: {
599
+ paddingHorizontal: 24,
600
+ paddingTop: 16,
601
+ paddingBottom: 12
602
+ },
603
+ headerLabel: {
604
+ fontSize: 12,
605
+ fontWeight: "500",
606
+ textTransform: "uppercase",
607
+ letterSpacing: 0.5,
608
+ marginBottom: 8
609
+ },
610
+ headerDateRow: {
611
+ flexDirection: "row",
612
+ alignItems: "center",
613
+ justifyContent: "space-between"
614
+ },
615
+ headerDate: {
616
+ fontSize: 24,
617
+ fontWeight: "400"
618
+ },
619
+ navigationRow: {
620
+ flexDirection: "row",
621
+ alignItems: "center",
622
+ justifyContent: "space-between",
623
+ paddingHorizontal: 12,
624
+ paddingVertical: 8
625
+ },
626
+ monthYearButton: {
627
+ flexDirection: "row",
628
+ alignItems: "center",
629
+ gap: 4,
630
+ paddingVertical: 8,
631
+ paddingHorizontal: 12,
632
+ borderRadius: 20
633
+ },
634
+ monthYearText: {
635
+ fontSize: 14,
636
+ fontWeight: "600"
637
+ },
638
+ navButtons: {
639
+ flexDirection: "row",
640
+ alignItems: "center",
641
+ gap: 4
642
+ },
643
+ navButton: {
644
+ padding: 8,
645
+ borderRadius: 20
646
+ },
647
+ calendarGrid: {
648
+ paddingHorizontal: 12
649
+ },
650
+ weekdayRow: {
651
+ flexDirection: "row",
652
+ justifyContent: "space-around",
653
+ marginBottom: 4
654
+ },
655
+ weekdayCell: {
656
+ width: 44,
657
+ height: 32,
658
+ justifyContent: "center",
659
+ alignItems: "center"
660
+ },
661
+ weekdayText: {
662
+ fontSize: 12,
663
+ fontWeight: "500"
664
+ },
665
+ dayRow: {
666
+ flexDirection: "row",
667
+ justifyContent: "space-around"
668
+ },
669
+ dayCell: {
670
+ width: 44,
671
+ height: 44,
672
+ justifyContent: "center",
673
+ alignItems: "center",
674
+ borderRadius: 22
675
+ },
676
+ dayText: {
677
+ fontSize: 14
678
+ },
679
+ todayCell: {
680
+ borderWidth: 1
681
+ },
682
+ selectedCell: {
683
+ borderWidth: 0
684
+ },
685
+ selectedCellInner: {
686
+ width: 44,
687
+ height: 44,
688
+ justifyContent: "center",
689
+ alignItems: "center",
690
+ borderRadius: 22
691
+ },
692
+ disabledText: {
693
+ opacity: 0.3
694
+ },
695
+ otherMonthText: {
696
+ opacity: 0.4
697
+ },
698
+ footer: {
699
+ flexDirection: "row",
700
+ justifyContent: "flex-end",
701
+ alignItems: "center",
702
+ gap: 8,
703
+ paddingHorizontal: 12,
704
+ paddingVertical: 12
705
+ },
706
+ footerButton: {
707
+ paddingVertical: 10,
708
+ paddingHorizontal: 16,
709
+ borderRadius: 20
710
+ },
711
+ footerButtonText: {
712
+ fontSize: 14,
713
+ fontWeight: "600"
714
+ },
715
+ yearPickerContainer: {
716
+ paddingHorizontal: 12,
717
+ height: 280
718
+ },
719
+ yearGrid: {
720
+ flexDirection: "row",
721
+ flexWrap: "wrap",
722
+ justifyContent: "center",
723
+ gap: 4
724
+ },
725
+ yearCell: {
726
+ width: 76,
727
+ height: 44,
728
+ justifyContent: "center",
729
+ alignItems: "center",
730
+ borderRadius: 22,
731
+ margin: 2
732
+ },
733
+ yearCellInner: {
734
+ width: 76,
735
+ height: 44,
736
+ justifyContent: "center",
737
+ alignItems: "center",
738
+ borderRadius: 22
739
+ },
740
+ yearText: {
741
+ fontSize: 14
742
+ },
743
+ monthPickerContainer: {
744
+ paddingHorizontal: 24,
745
+ paddingVertical: 8
746
+ },
747
+ monthGrid: {
748
+ flexDirection: "row",
749
+ flexWrap: "wrap",
750
+ justifyContent: "center",
751
+ gap: 8
752
+ },
753
+ monthCell: {
754
+ width: 90,
755
+ height: 44,
756
+ justifyContent: "center",
757
+ alignItems: "center",
758
+ borderRadius: 22
759
+ },
760
+ monthCellInner: {
761
+ width: 90,
762
+ height: 44,
763
+ justifyContent: "center",
764
+ alignItems: "center",
765
+ borderRadius: 22
766
+ },
767
+ monthText: {
768
+ fontSize: 14
769
+ }
770
+ });
771
+
772
+ // src/components/dialogs/datepicker-dialog/datepicker-dialog-calendar.tsx
773
+ var AnimatedDayCell = ({
774
+ isSelected,
775
+ isToday: isToday2,
776
+ isCurrentMonth,
777
+ isDisabled,
778
+ day,
779
+ date,
780
+ locale,
781
+ themeColor,
782
+ onSelectDay
783
+ }) => {
784
+ const theme = useXUITheme();
785
+ const colorScheme = theme.colors[themeColor] ?? theme.colors.primary;
786
+ const isDefault = themeColor === "default";
787
+ const accentColor = isDefault ? theme.colors.foreground : colorScheme.main;
788
+ const accentFg = isDefault ? theme.colors.background : colorScheme.foreground;
789
+ const scaleAnim = useRef2(new Animated2.Value(isSelected ? 1 : 0)).current;
790
+ useEffect2(() => {
791
+ if (isSelected) {
792
+ scaleAnim.setValue(0.92);
793
+ Animated2.spring(scaleAnim, {
794
+ toValue: 1,
795
+ useNativeDriver: true,
796
+ damping: 20,
797
+ stiffness: 300,
798
+ mass: 0.6
799
+ }).start();
800
+ } else {
801
+ scaleAnim.setValue(0);
802
+ }
803
+ }, [isSelected, scaleAnim]);
804
+ const animatedStyle = isSelected ? { transform: [{ scale: scaleAnim }] } : void 0;
805
+ return /* @__PURE__ */ React.createElement(
806
+ Pressable,
807
+ {
808
+ style: [
809
+ styles2.dayCell,
810
+ isToday2 && !isSelected && {
811
+ ...styles2.todayCell,
812
+ borderColor: accentColor
813
+ }
814
+ ],
815
+ onPress: () => {
816
+ if (!isDisabled) {
817
+ onSelectDay(date);
818
+ }
819
+ },
820
+ disabled: isDisabled,
821
+ accessibilityLabel: date.toLocaleDateString(locale),
822
+ accessibilityRole: "button",
823
+ accessibilityState: {
824
+ selected: isSelected,
825
+ disabled: isDisabled
826
+ }
827
+ },
828
+ isSelected ? /* @__PURE__ */ React.createElement(
829
+ Animated2.View,
830
+ {
831
+ style: [
832
+ styles2.selectedCellInner,
833
+ { backgroundColor: accentColor },
834
+ animatedStyle
835
+ ]
836
+ },
837
+ /* @__PURE__ */ React.createElement(Text, { style: [styles2.dayText, { color: accentFg }] }, day)
838
+ ) : /* @__PURE__ */ React.createElement(
839
+ Text,
840
+ {
841
+ style: [
842
+ styles2.dayText,
843
+ { color: theme.colors.foreground },
844
+ !isCurrentMonth && styles2.otherMonthText,
845
+ isDisabled && styles2.disabledText,
846
+ isToday2 && {
847
+ color: accentColor,
848
+ fontWeight: "600"
849
+ }
850
+ ]
851
+ },
852
+ day
853
+ )
854
+ );
855
+ };
856
+ var DatePickerDialogCalendar = ({
857
+ viewDate,
858
+ selectedDate,
859
+ locale,
860
+ firstDayOfWeek,
861
+ themeColor,
862
+ minDate,
863
+ maxDate,
864
+ onSelectDay,
865
+ onPreviousMonth,
866
+ onNextMonth
867
+ }) => {
868
+ const theme = useXUITheme();
869
+ const swipeHandledRef = useRef2(false);
870
+ const weekdays = useMemo2(
871
+ () => getWeekdayNames(locale, firstDayOfWeek),
872
+ [locale, firstDayOfWeek]
873
+ );
874
+ const days = useMemo2(
875
+ () => getCalendarDays(
876
+ viewDate.getFullYear(),
877
+ viewDate.getMonth(),
878
+ firstDayOfWeek,
879
+ minDate,
880
+ maxDate
881
+ ),
882
+ [viewDate, firstDayOfWeek, minDate, maxDate]
883
+ );
884
+ const weeks = useMemo2(() => {
885
+ const result = [];
886
+ for (let i = 0; i < days.length; i += 7) {
887
+ result.push(days.slice(i, i + 7));
888
+ }
889
+ return result;
890
+ }, [days]);
891
+ const onSwipeEnd = (event) => {
892
+ const { state, translationX, velocityX } = event.nativeEvent;
893
+ if (state !== State.END || swipeHandledRef.current) return;
894
+ const shouldSwipe = Math.abs(translationX) > 40 || Math.abs(velocityX) > 600;
895
+ if (!shouldSwipe) return;
896
+ swipeHandledRef.current = true;
897
+ if (translationX < 0) {
898
+ onNextMonth();
899
+ } else {
900
+ onPreviousMonth();
901
+ }
902
+ };
903
+ const onSwipeStateChange = (event) => {
904
+ if (event.nativeEvent.state === State.BEGAN) {
905
+ swipeHandledRef.current = false;
906
+ }
907
+ onSwipeEnd(event);
908
+ };
909
+ return /* @__PURE__ */ React.createElement(
910
+ PanGestureHandler,
911
+ {
912
+ activeOffsetX: [-12, 12],
913
+ failOffsetY: [-12, 12],
914
+ onHandlerStateChange: onSwipeStateChange
915
+ },
916
+ /* @__PURE__ */ React.createElement(View, { style: styles2.calendarGrid }, /* @__PURE__ */ React.createElement(View, { style: styles2.weekdayRow }, weekdays.map((day, index) => /* @__PURE__ */ React.createElement(View, { key: index, style: styles2.weekdayCell }, /* @__PURE__ */ React.createElement(
917
+ Text,
918
+ {
919
+ style: [
920
+ styles2.weekdayText,
921
+ { color: theme.colors.foreground, opacity: 0.6 }
922
+ ]
923
+ },
924
+ day
925
+ )))), weeks.map((week, weekIndex) => /* @__PURE__ */ React.createElement(View, { key: weekIndex, style: styles2.dayRow }, week.map((dayInfo, dayIndex) => /* @__PURE__ */ React.createElement(
926
+ AnimatedDayCell,
927
+ {
928
+ key: dayIndex,
929
+ isSelected: !!(selectedDate && isSameDay(dayInfo.date, selectedDate)),
930
+ isToday: dayInfo.isToday,
931
+ isCurrentMonth: dayInfo.isCurrentMonth,
932
+ isDisabled: dayInfo.isDisabled,
933
+ day: dayInfo.day,
934
+ date: dayInfo.date,
935
+ locale,
936
+ themeColor,
937
+ onSelectDay
938
+ }
939
+ )))))
940
+ );
941
+ };
942
+
943
+ // src/components/dialogs/datepicker-dialog/datepicker-dialog-header.tsx
944
+ import React2, { useMemo as useMemo3 } from "react";
945
+ import { Pressable as Pressable2, Text as Text2, View as View2 } from "react-native";
946
+ var DatePickerDialogHeader = ({
947
+ viewDate,
948
+ selectedDate,
949
+ locale,
950
+ themeColor,
951
+ selectDateLabel,
952
+ onClearValue,
953
+ onPreviousMonth,
954
+ onNextMonth,
955
+ onToggleYearPicker
956
+ }) => {
957
+ const theme = useXUITheme();
958
+ const colorScheme = theme.colors[themeColor] ?? theme.colors.primary;
959
+ const dateText = useMemo3(() => {
960
+ if (!selectedDate) return "---";
961
+ return formatDate(selectedDate, locale);
962
+ }, [selectedDate, locale]);
963
+ const monthYearLabel = useMemo3(() => {
964
+ const month = getMonthName(viewDate.getMonth(), locale);
965
+ return `${month} ${viewDate.getFullYear()}`;
966
+ }, [viewDate, locale]);
967
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(View2, { style: [styles2.header, { backgroundColor: colorScheme.main }] }, /* @__PURE__ */ React2.createElement(Text2, { style: [styles2.headerLabel, { color: colorScheme.foreground }] }, selectDateLabel), /* @__PURE__ */ React2.createElement(View2, { style: styles2.headerDateRow }, /* @__PURE__ */ React2.createElement(Text2, { style: [styles2.headerDate, { color: colorScheme.foreground }] }, dateText), /* @__PURE__ */ React2.createElement(
968
+ Pressable2,
969
+ {
970
+ onPress: onClearValue,
971
+ hitSlop: { top: 8, right: 8, bottom: 8, left: 8 },
972
+ accessibilityLabel: "Clear date",
973
+ accessibilityRole: "button"
974
+ },
975
+ /* @__PURE__ */ React2.createElement(CloseIcon, { size: 20, color: colorScheme.foreground })
976
+ ))), /* @__PURE__ */ React2.createElement(View2, { style: styles2.navigationRow }, /* @__PURE__ */ React2.createElement(
977
+ Pressable2,
978
+ {
979
+ style: styles2.monthYearButton,
980
+ onPress: onToggleYearPicker,
981
+ accessibilityLabel: `${monthYearLabel}, tap to change`,
982
+ accessibilityRole: "button"
983
+ },
984
+ /* @__PURE__ */ React2.createElement(
985
+ Text2,
986
+ {
987
+ style: [styles2.monthYearText, { color: theme.colors.foreground }]
988
+ },
989
+ monthYearLabel
990
+ ),
991
+ /* @__PURE__ */ React2.createElement(
992
+ ChevronDownIcon,
993
+ {
994
+ size: 18,
995
+ color: theme.colors.foreground
996
+ }
997
+ )
998
+ ), /* @__PURE__ */ React2.createElement(View2, { style: styles2.navButtons }, /* @__PURE__ */ React2.createElement(
999
+ Pressable2,
1000
+ {
1001
+ style: styles2.navButton,
1002
+ onPress: onPreviousMonth,
1003
+ accessibilityLabel: "Previous month",
1004
+ accessibilityRole: "button"
1005
+ },
1006
+ /* @__PURE__ */ React2.createElement(ChevronLeftIcon, { size: 20, color: theme.colors.foreground })
1007
+ ), /* @__PURE__ */ React2.createElement(
1008
+ Pressable2,
1009
+ {
1010
+ style: styles2.navButton,
1011
+ onPress: onNextMonth,
1012
+ accessibilityLabel: "Next month",
1013
+ accessibilityRole: "button"
1014
+ },
1015
+ /* @__PURE__ */ React2.createElement(ChevronRightIcon, { size: 20, color: theme.colors.foreground })
1016
+ ))));
1017
+ };
1018
+
1019
+ // src/components/dialogs/datepicker-dialog/datepicker-dialog-month-picker.tsx
1020
+ import React3, { useEffect as useEffect3, useMemo as useMemo4, useRef as useRef3 } from "react";
1021
+ import { Animated as Animated3, Easing as Easing2, Pressable as Pressable3, Text as Text3, View as View3 } from "react-native";
1022
+ var DatePickerDialogMonthPicker = ({ viewDate, locale, themeColor, onSelectMonth }) => {
1023
+ const theme = useXUITheme();
1024
+ const colorScheme = theme.colors[themeColor] ?? theme.colors.primary;
1025
+ const isDefault = themeColor === "default";
1026
+ const accentColor = isDefault ? theme.colors.foreground : colorScheme.main;
1027
+ const accentFg = isDefault ? theme.colors.background : colorScheme.foreground;
1028
+ const months = useMemo4(() => getMonthNames(locale), [locale]);
1029
+ const currentMonth = viewDate.getMonth();
1030
+ const groupAnim = useRef3(new Animated3.Value(0)).current;
1031
+ useEffect3(() => {
1032
+ groupAnim.setValue(0);
1033
+ Animated3.timing(groupAnim, {
1034
+ toValue: 1,
1035
+ duration: 220,
1036
+ easing: Easing2.out(Easing2.cubic),
1037
+ useNativeDriver: true
1038
+ }).start();
1039
+ }, [groupAnim]);
1040
+ return /* @__PURE__ */ React3.createElement(View3, { style: styles2.monthPickerContainer }, /* @__PURE__ */ React3.createElement(
1041
+ Animated3.View,
1042
+ {
1043
+ style: {
1044
+ opacity: groupAnim,
1045
+ transform: [
1046
+ {
1047
+ translateY: groupAnim.interpolate({
1048
+ inputRange: [0, 1],
1049
+ outputRange: [12, 0]
1050
+ })
1051
+ }
1052
+ ]
1053
+ }
1054
+ },
1055
+ /* @__PURE__ */ React3.createElement(View3, { style: styles2.monthGrid }, months.map((name, index) => {
1056
+ const isCurrentMonth = index === currentMonth;
1057
+ return /* @__PURE__ */ React3.createElement(
1058
+ Pressable3,
1059
+ {
1060
+ key: index,
1061
+ style: [
1062
+ styles2.monthCell,
1063
+ isCurrentMonth && { backgroundColor: accentColor }
1064
+ ],
1065
+ onPress: () => onSelectMonth(index),
1066
+ accessibilityLabel: name,
1067
+ accessibilityRole: "button",
1068
+ accessibilityState: { selected: isCurrentMonth }
1069
+ },
1070
+ /* @__PURE__ */ React3.createElement(
1071
+ Text3,
1072
+ {
1073
+ style: [
1074
+ styles2.monthText,
1075
+ { color: theme.colors.foreground },
1076
+ isCurrentMonth && {
1077
+ color: accentFg,
1078
+ fontWeight: "600"
1079
+ }
1080
+ ]
1081
+ },
1082
+ name
1083
+ )
1084
+ );
1085
+ }))
1086
+ ));
1087
+ };
1088
+
1089
+ // src/components/dialogs/datepicker-dialog/datepicker-dialog-year-picker.tsx
1090
+ import React4, { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo5, useRef as useRef4 } from "react";
1091
+ import { Animated as Animated4, Easing as Easing3, FlatList, Pressable as Pressable4, Text as Text4, View as View4 } from "react-native";
1092
+ var ITEM_HEIGHT = 48;
1093
+ var NUM_COLUMNS = 4;
1094
+ var DatePickerDialogYearPicker = ({ viewDate, themeColor, minDate, maxDate, onSelectYear }) => {
1095
+ const listRef = useRef4(null);
1096
+ const groupAnim = useRef4(new Animated4.Value(0)).current;
1097
+ const theme = useXUITheme();
1098
+ const colorScheme = theme.colors[themeColor] ?? theme.colors.primary;
1099
+ const isDefault = themeColor === "default";
1100
+ const accentColor = isDefault ? theme.colors.foreground : colorScheme.main;
1101
+ const accentFg = isDefault ? theme.colors.background : colorScheme.foreground;
1102
+ const years = useMemo5(
1103
+ () => getYearRange(minDate, maxDate),
1104
+ [minDate, maxDate]
1105
+ );
1106
+ const currentYear = viewDate.getFullYear();
1107
+ const initialIndex = useMemo5(() => {
1108
+ const index = years.indexOf(currentYear);
1109
+ const rowIndex = Math.floor(index / NUM_COLUMNS);
1110
+ return Math.max(0, rowIndex);
1111
+ }, [years, currentYear]);
1112
+ useEffect4(() => {
1113
+ groupAnim.setValue(0);
1114
+ Animated4.timing(groupAnim, {
1115
+ toValue: 1,
1116
+ duration: 220,
1117
+ easing: Easing3.out(Easing3.cubic),
1118
+ useNativeDriver: true
1119
+ }).start();
1120
+ }, [groupAnim]);
1121
+ const getItemLayout = useCallback3(
1122
+ (_, index) => ({
1123
+ length: ITEM_HEIGHT,
1124
+ offset: ITEM_HEIGHT * index,
1125
+ index
1126
+ }),
1127
+ []
1128
+ );
1129
+ const renderYear = useCallback3(
1130
+ ({ item }) => {
1131
+ const isCurrentYear = item === currentYear;
1132
+ return /* @__PURE__ */ React4.createElement(
1133
+ Pressable4,
1134
+ {
1135
+ style: [
1136
+ styles2.yearCell,
1137
+ isCurrentYear && { backgroundColor: accentColor }
1138
+ ],
1139
+ onPress: () => onSelectYear(item),
1140
+ accessibilityLabel: `${item}`,
1141
+ accessibilityRole: "button",
1142
+ accessibilityState: { selected: isCurrentYear }
1143
+ },
1144
+ /* @__PURE__ */ React4.createElement(
1145
+ Text4,
1146
+ {
1147
+ style: [
1148
+ styles2.yearText,
1149
+ { color: theme.colors.foreground },
1150
+ isCurrentYear && {
1151
+ color: accentFg,
1152
+ fontWeight: "600"
1153
+ }
1154
+ ]
1155
+ },
1156
+ item
1157
+ )
1158
+ );
1159
+ },
1160
+ [currentYear, onSelectYear, accentColor, accentFg, theme.colors.foreground]
1161
+ );
1162
+ return /* @__PURE__ */ React4.createElement(View4, { style: styles2.yearPickerContainer }, /* @__PURE__ */ React4.createElement(
1163
+ Animated4.View,
1164
+ {
1165
+ style: {
1166
+ opacity: groupAnim,
1167
+ transform: [
1168
+ {
1169
+ translateY: groupAnim.interpolate({
1170
+ inputRange: [0, 1],
1171
+ outputRange: [12, 0]
1172
+ })
1173
+ }
1174
+ ]
1175
+ }
1176
+ },
1177
+ /* @__PURE__ */ React4.createElement(
1178
+ FlatList,
1179
+ {
1180
+ ref: listRef,
1181
+ data: years,
1182
+ renderItem: renderYear,
1183
+ keyExtractor: (item) => String(item),
1184
+ numColumns: NUM_COLUMNS,
1185
+ getItemLayout,
1186
+ initialScrollIndex: initialIndex,
1187
+ showsVerticalScrollIndicator: false,
1188
+ columnWrapperStyle: { justifyContent: "center" }
1189
+ }
1190
+ )
1191
+ ));
1192
+ };
1193
+
1194
+ // src/components/dialogs/datepicker-dialog/datepicker-dialog.tsx
1195
+ var DatePickerDialog = ({
1196
+ visible,
1197
+ selectedDate,
1198
+ locale,
1199
+ firstDayOfWeek,
1200
+ themeColor = "primary",
1201
+ minDate,
1202
+ maxDate,
1203
+ style,
1204
+ todayLabel,
1205
+ confirmLabel,
1206
+ onDateSelect,
1207
+ onClearValue,
1208
+ onClose
1209
+ }) => {
1210
+ const theme = useXUITheme();
1211
+ const { width: screenWidth, height: screenHeight } = useWindowDimensions();
1212
+ const fadeAnim = useRef5(new Animated5.Value(0)).current;
1213
+ const slideAnim = useRef5(new Animated5.Value(0)).current;
1214
+ const scaleAnim = useRef5(new Animated5.Value(0)).current;
1215
+ const viewFadeAnim = useRef5(new Animated5.Value(1)).current;
1216
+ const monthSlideXAnim = useRef5(new Animated5.Value(0)).current;
1217
+ const monthFadeAnim = useRef5(new Animated5.Value(1)).current;
1218
+ const colorScheme = theme.colors[themeColor] ?? theme.colors.primary;
1219
+ const isDefault = themeColor === "default";
1220
+ const accentColor = isDefault ? theme.colors.foreground : colorScheme.main;
1221
+ const labels = getDatePickerLabels(locale);
1222
+ const resolvedTodayLabel = todayLabel ?? labels.today;
1223
+ const resolvedConfirmLabel = confirmLabel ?? labels.confirm;
1224
+ const {
1225
+ viewDate,
1226
+ viewMode,
1227
+ goToPreviousMonth,
1228
+ goToNextMonth,
1229
+ goToYear,
1230
+ goToMonth,
1231
+ goToToday,
1232
+ toggleYearPicker,
1233
+ syncViewToDate
1234
+ } = useDatePickerViewState(selectedDate);
1235
+ const onCloseComplete = useCallback4(() => {
1236
+ fadeAnim.setValue(0);
1237
+ slideAnim.setValue(0);
1238
+ scaleAnim.setValue(0);
1239
+ viewFadeAnim.setValue(1);
1240
+ monthSlideXAnim.setValue(0);
1241
+ monthFadeAnim.setValue(1);
1242
+ }, [fadeAnim, slideAnim, scaleAnim, viewFadeAnim, monthSlideXAnim, monthFadeAnim]);
1243
+ const { shouldRender } = useDatePickerDialogAnimation({
1244
+ visible,
1245
+ fadeAnim,
1246
+ slideAnim,
1247
+ scaleAnim,
1248
+ onCloseComplete
1249
+ });
1250
+ const { animate: animateViewTransition } = useViewTransitionAnimation({
1251
+ fadeAnim: viewFadeAnim
1252
+ });
1253
+ const { animate: animateMonthSlide } = useMonthSlideAnimation({
1254
+ slideAnim: monthSlideXAnim,
1255
+ fadeAnim: monthFadeAnim
1256
+ });
1257
+ const prevViewModeRef = useRef5(viewMode);
1258
+ useEffect5(() => {
1259
+ if (prevViewModeRef.current !== viewMode) {
1260
+ prevViewModeRef.current = viewMode;
1261
+ animateViewTransition();
1262
+ }
1263
+ }, [viewMode, animateViewTransition]);
1264
+ useEffect5(() => {
1265
+ if (visible && selectedDate) {
1266
+ syncViewToDate(selectedDate);
1267
+ }
1268
+ }, [visible, selectedDate, syncViewToDate]);
1269
+ useEffect5(() => {
1270
+ if (!visible) return;
1271
+ const sub = BackHandler.addEventListener("hardwareBackPress", () => {
1272
+ onClose();
1273
+ return true;
1274
+ });
1275
+ return () => sub.remove();
1276
+ }, [visible, onClose]);
1277
+ const handlePreviousMonth = useCallback4(() => {
1278
+ goToPreviousMonth();
1279
+ animateMonthSlide("left");
1280
+ }, [goToPreviousMonth, animateMonthSlide]);
1281
+ const handleNextMonth = useCallback4(() => {
1282
+ goToNextMonth();
1283
+ animateMonthSlide("right");
1284
+ }, [goToNextMonth, animateMonthSlide]);
1285
+ const handleDaySelect = useCallback4(
1286
+ (date) => {
1287
+ onDateSelect(date);
1288
+ },
1289
+ [onDateSelect]
1290
+ );
1291
+ const handleTodayPress = useCallback4(() => {
1292
+ const today = /* @__PURE__ */ new Date();
1293
+ if (!isDateInRange(today, minDate, maxDate)) return;
1294
+ goToToday();
1295
+ onDateSelect(today);
1296
+ }, [goToToday, onDateSelect, minDate, maxDate]);
1297
+ if (!shouldRender) return null;
1298
+ const overlayStyle = {
1299
+ position: "absolute",
1300
+ top: 0,
1301
+ left: 0,
1302
+ width: screenWidth,
1303
+ height: screenHeight
1304
+ };
1305
+ const containerAnimatedStyle = {
1306
+ opacity: fadeAnim,
1307
+ transform: [
1308
+ {
1309
+ scale: scaleAnim.interpolate({
1310
+ inputRange: [0, 1],
1311
+ outputRange: [0.92, 1]
1312
+ })
1313
+ },
1314
+ {
1315
+ translateY: slideAnim.interpolate({
1316
+ inputRange: [0, 1],
1317
+ outputRange: [40, 0]
1318
+ })
1319
+ }
1320
+ ]
1321
+ };
1322
+ const viewTransitionStyle = {
1323
+ opacity: viewFadeAnim,
1324
+ transform: [
1325
+ {
1326
+ translateY: viewFadeAnim.interpolate({
1327
+ inputRange: [0, 1],
1328
+ outputRange: [8, 0]
1329
+ })
1330
+ }
1331
+ ]
1332
+ };
1333
+ const monthSlideStyle = {
1334
+ opacity: monthFadeAnim,
1335
+ transform: [{ translateX: monthSlideXAnim }]
1336
+ };
1337
+ const renderContent = () => {
1338
+ switch (viewMode) {
1339
+ case "year":
1340
+ return /* @__PURE__ */ React5.createElement(
1341
+ DatePickerDialogYearPicker,
1342
+ {
1343
+ viewDate,
1344
+ themeColor,
1345
+ minDate,
1346
+ maxDate,
1347
+ onSelectYear: goToYear
1348
+ }
1349
+ );
1350
+ case "month":
1351
+ return /* @__PURE__ */ React5.createElement(
1352
+ DatePickerDialogMonthPicker,
1353
+ {
1354
+ viewDate,
1355
+ locale,
1356
+ themeColor,
1357
+ onSelectMonth: goToMonth
1358
+ }
1359
+ );
1360
+ case "calendar":
1361
+ default:
1362
+ return /* @__PURE__ */ React5.createElement(Animated5.View, { style: [viewTransitionStyle, monthSlideStyle] }, /* @__PURE__ */ React5.createElement(
1363
+ DatePickerDialogCalendar,
1364
+ {
1365
+ viewDate,
1366
+ selectedDate,
1367
+ locale,
1368
+ firstDayOfWeek,
1369
+ themeColor,
1370
+ minDate,
1371
+ maxDate,
1372
+ onSelectDay: handleDaySelect,
1373
+ onPreviousMonth: handlePreviousMonth,
1374
+ onNextMonth: handleNextMonth
1375
+ }
1376
+ ));
1377
+ }
1378
+ };
1379
+ return /* @__PURE__ */ React5.createElement(Portal, null, /* @__PURE__ */ React5.createElement(GestureHandlerRootView, { style: [overlayStyle, style] }, /* @__PURE__ */ React5.createElement(View5, { style: overlayStyle }, /* @__PURE__ */ React5.createElement(Animated5.View, { style: [styles2.backdrop, { opacity: fadeAnim }] }, /* @__PURE__ */ React5.createElement(
1380
+ Pressable5,
1381
+ {
1382
+ style: { flex: 1 },
1383
+ onPress: onClose,
1384
+ accessibilityLabel: "Close calendar",
1385
+ accessibilityRole: "button"
1386
+ }
1387
+ )), /* @__PURE__ */ React5.createElement(Animated5.View, { style: [styles2.dialogContainer, containerAnimatedStyle] }, /* @__PURE__ */ React5.createElement(
1388
+ View5,
1389
+ {
1390
+ style: [
1391
+ styles2.container,
1392
+ { backgroundColor: theme.colors.background }
1393
+ ]
1394
+ },
1395
+ /* @__PURE__ */ React5.createElement(
1396
+ DatePickerDialogHeader,
1397
+ {
1398
+ viewDate,
1399
+ selectedDate,
1400
+ locale,
1401
+ themeColor,
1402
+ selectDateLabel: labels.selectDate,
1403
+ onClearValue,
1404
+ onPreviousMonth: handlePreviousMonth,
1405
+ onNextMonth: handleNextMonth,
1406
+ onToggleYearPicker: toggleYearPicker
1407
+ }
1408
+ ),
1409
+ renderContent(),
1410
+ /* @__PURE__ */ React5.createElement(View5, { style: styles2.footer }, /* @__PURE__ */ React5.createElement(
1411
+ Pressable5,
1412
+ {
1413
+ style: styles2.footerButton,
1414
+ onPress: handleTodayPress,
1415
+ accessibilityLabel: resolvedTodayLabel,
1416
+ accessibilityRole: "button"
1417
+ },
1418
+ /* @__PURE__ */ React5.createElement(Text5, { style: [styles2.footerButtonText, { color: accentColor }] }, resolvedTodayLabel)
1419
+ ), /* @__PURE__ */ React5.createElement(
1420
+ Pressable5,
1421
+ {
1422
+ style: styles2.footerButton,
1423
+ onPress: onClose,
1424
+ accessibilityLabel: resolvedConfirmLabel,
1425
+ accessibilityRole: "button"
1426
+ },
1427
+ /* @__PURE__ */ React5.createElement(Text5, { style: [styles2.footerButtonText, { color: accentColor }] }, resolvedConfirmLabel)
1428
+ ))
1429
+ )))));
1430
+ };
1431
+
1432
+ // src/components/datepicker/datepicker-trigger.tsx
1433
+ import React6 from "react";
1434
+ import { Pressable as Pressable6, Text as Text6, TouchableOpacity, View as View6 } from "react-native";
1435
+ var DatePickerTrigger = ({
1436
+ triggerRef,
1437
+ isDisabled,
1438
+ hasValue,
1439
+ displayValue,
1440
+ sizeStyles,
1441
+ radiusStyles,
1442
+ variantStyles,
1443
+ theme,
1444
+ isClearable,
1445
+ label,
1446
+ labelText,
1447
+ isLabelInside,
1448
+ calendarIcon,
1449
+ style,
1450
+ textStyle,
1451
+ onPress: handleTriggerPress,
1452
+ onClear: handleClear,
1453
+ onLayout: handleTriggerLayout
1454
+ }) => {
1455
+ const renderLabel = isLabelInside && label;
1456
+ return /* @__PURE__ */ React6.createElement(
1457
+ Pressable6,
1458
+ {
1459
+ ref: triggerRef,
1460
+ onPress: handleTriggerPress,
1461
+ onLayout: handleTriggerLayout,
1462
+ disabled: isDisabled,
1463
+ style: [
1464
+ styles.trigger,
1465
+ {
1466
+ minHeight: sizeStyles.minHeight,
1467
+ paddingHorizontal: sizeStyles.paddingHorizontal,
1468
+ paddingVertical: sizeStyles.paddingVertical
1469
+ },
1470
+ radiusStyles,
1471
+ variantStyles,
1472
+ isDisabled && styles.disabled,
1473
+ style
1474
+ ],
1475
+ accessibilityLabel: labelText ?? (typeof label === "string" ? label : void 0),
1476
+ accessibilityRole: "button",
1477
+ accessibilityState: { disabled: isDisabled }
1478
+ },
1479
+ /* @__PURE__ */ React6.createElement(View6, { style: styles.triggerContent }, isLabelInside && renderLabel, /* @__PURE__ */ React6.createElement(
1480
+ Text6,
1481
+ {
1482
+ style: [
1483
+ styles.triggerText,
1484
+ { fontSize: sizeStyles.fontSize, color: theme.colors.foreground },
1485
+ !hasValue && { opacity: 0.5 },
1486
+ textStyle
1487
+ ],
1488
+ numberOfLines: 1,
1489
+ ellipsizeMode: "tail"
1490
+ },
1491
+ displayValue
1492
+ )),
1493
+ isClearable && hasValue ? /* @__PURE__ */ React6.createElement(
1494
+ TouchableOpacity,
1495
+ {
1496
+ onPress: handleClear,
1497
+ style: styles.clearButton,
1498
+ hitSlop: { top: 8, right: 8, bottom: 8, left: 8 }
1499
+ },
1500
+ /* @__PURE__ */ React6.createElement(CloseIcon, { color: theme.colors.foreground, size: 20 })
1501
+ ) : calendarIcon ?? /* @__PURE__ */ React6.createElement(CalendarIcon, { color: theme.colors.foreground, size: 20 })
1502
+ );
1503
+ };
1504
+
1505
+ // src/components/datepicker/datepicker.tsx
1506
+ var DatePicker = ({
1507
+ value,
1508
+ defaultValue,
1509
+ onChange,
1510
+ locale = "en",
1511
+ minDate,
1512
+ maxDate,
1513
+ firstDayOfWeek: firstDayOfWeekProp,
1514
+ variant = "flat",
1515
+ themeColor = "default",
1516
+ size = "md",
1517
+ radius = "md",
1518
+ label,
1519
+ placeholder = "Select a date",
1520
+ description,
1521
+ errorMessage,
1522
+ labelPlacement = "outside",
1523
+ fullWidth = false,
1524
+ isDisabled = false,
1525
+ isInvalid = false,
1526
+ isReadOnly = false,
1527
+ isClearable = true,
1528
+ customAppearance,
1529
+ calendarIcon,
1530
+ onOpen,
1531
+ onClose,
1532
+ onOpenChange
1533
+ }) => {
1534
+ const theme = useXUITheme();
1535
+ const { selectedDate, updateDate } = useDatePickerState({
1536
+ value,
1537
+ defaultValue,
1538
+ onChange
1539
+ });
1540
+ const { isOpen, setOpen } = useDatePickerOpenState({
1541
+ isDisabled: isDisabled || isReadOnly,
1542
+ onOpenChange,
1543
+ onOpen,
1544
+ onClose
1545
+ });
1546
+ const triggerRef = useRef6(null);
1547
+ const handleTriggerLayout = useCallback5(() => {
1548
+ }, []);
1549
+ const firstDayOfWeek = useMemo6(
1550
+ () => firstDayOfWeekProp ?? getFirstDayOfWeek(locale),
1551
+ [firstDayOfWeekProp, locale]
1552
+ );
1553
+ const sizeStyles = useDatePickerSizeStyles(size);
1554
+ const radiusStyles = useDatePickerRadiusStyles(radius);
1555
+ const variantStyles = useDatePickerVariantStyles(themeColor, variant, isInvalid);
1556
+ const labelStyle = useDatePickerLabelStyle(
1557
+ themeColor,
1558
+ isInvalid,
1559
+ sizeStyles.labelSize
1560
+ );
1561
+ const helperColor = useDatePickerHelperColor(isInvalid);
1562
+ const displayValue = useMemo6(() => {
1563
+ if (!selectedDate) return placeholder;
1564
+ return formatDate(selectedDate, locale);
1565
+ }, [selectedDate, locale, placeholder]);
1566
+ const handleTriggerPress = useCallback5(() => {
1567
+ if (!isDisabled && !isReadOnly) {
1568
+ setOpen(true);
1569
+ }
1570
+ }, [isDisabled, isReadOnly, setOpen]);
1571
+ const handleClear = useCallback5(() => {
1572
+ if (isDisabled || isReadOnly) return;
1573
+ updateDate(null);
1574
+ }, [isDisabled, isReadOnly, updateDate]);
1575
+ const handleDateSelect = useCallback5(
1576
+ (date) => {
1577
+ updateDate(date);
1578
+ },
1579
+ [updateDate]
1580
+ );
1581
+ const handleClose = useCallback5(() => {
1582
+ setOpen(false);
1583
+ }, [setOpen]);
1584
+ const isLabelInside = labelPlacement === "inside";
1585
+ const isLabelOutsideLeft = labelPlacement === "outside-left";
1586
+ const isLabelOutside = labelPlacement === "outside" || labelPlacement === "outside-top";
1587
+ const renderLabel = label ? typeof label === "string" || typeof label === "number" ? /* @__PURE__ */ React7.createElement(Text7, { style: [styles.label, labelStyle] }, label) : /* @__PURE__ */ React7.createElement(View7, null, label) : null;
1588
+ const shouldShowHelper = Boolean(description || errorMessage);
1589
+ const helperContent = isInvalid && errorMessage ? errorMessage : description;
1590
+ const triggerContent = /* @__PURE__ */ React7.createElement(
1591
+ DatePickerTrigger,
1592
+ {
1593
+ triggerRef,
1594
+ isDisabled: isDisabled || isReadOnly,
1595
+ hasValue: !!selectedDate,
1596
+ displayValue,
1597
+ sizeStyles,
1598
+ radiusStyles,
1599
+ variantStyles,
1600
+ theme,
1601
+ isClearable,
1602
+ label: renderLabel,
1603
+ labelText: typeof label === "string" ? label : void 0,
1604
+ isLabelInside,
1605
+ calendarIcon,
1606
+ style: customAppearance?.trigger,
1607
+ textStyle: customAppearance?.text,
1608
+ onPress: handleTriggerPress,
1609
+ onClear: handleClear,
1610
+ onLayout: handleTriggerLayout
1611
+ }
1612
+ );
1613
+ const labelBlock = isLabelOutside || isLabelInside ? renderLabel : null;
1614
+ return /* @__PURE__ */ React7.createElement(
1615
+ View7,
1616
+ {
1617
+ style: [
1618
+ styles.container,
1619
+ fullWidth ? styles.fullWidth : styles.minWidth,
1620
+ customAppearance?.container
1621
+ ]
1622
+ },
1623
+ isLabelOutside && labelBlock,
1624
+ isLabelOutsideLeft ? /* @__PURE__ */ React7.createElement(View7, { style: styles.outsideLeftRow }, renderLabel, triggerContent) : triggerContent,
1625
+ shouldShowHelper && helperContent ? typeof helperContent === "string" || typeof helperContent === "number" ? /* @__PURE__ */ React7.createElement(Text7, { style: [styles.helperText, { color: helperColor }] }, helperContent) : /* @__PURE__ */ React7.createElement(View7, null, helperContent) : null,
1626
+ /* @__PURE__ */ React7.createElement(
1627
+ DatePickerDialog,
1628
+ {
1629
+ visible: isOpen,
1630
+ selectedDate,
1631
+ locale,
1632
+ firstDayOfWeek,
1633
+ themeColor,
1634
+ minDate,
1635
+ maxDate,
1636
+ style: customAppearance?.calendar,
1637
+ onDateSelect: handleDateSelect,
1638
+ onClearValue: handleClear,
1639
+ onClose: handleClose
1640
+ }
1641
+ )
1642
+ );
1643
+ };
1644
+
1645
+ export {
1646
+ DatePicker
1647
+ };