@xaui/native 0.0.7 → 0.0.9

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 (49) hide show
  1. package/dist/accordion/index.cjs +86 -68
  2. package/dist/accordion/index.js +2 -2
  3. package/dist/autocomplete/index.cjs +1503 -0
  4. package/dist/autocomplete/index.d.cts +70 -0
  5. package/dist/autocomplete/index.d.ts +70 -0
  6. package/dist/autocomplete/index.js +11 -0
  7. package/dist/button/index.cjs +87 -69
  8. package/dist/button/index.js +2 -2
  9. package/dist/checkbox/index.cjs +48 -30
  10. package/dist/checkbox/index.js +1 -1
  11. package/dist/{chunk-B2VGVZ3J.js → chunk-63LRW4QD.js} +1 -1
  12. package/dist/chunk-6HUSEZDJ.js +1138 -0
  13. package/dist/chunk-7LXW4BXD.js +606 -0
  14. package/dist/chunk-GBHQCAKW.js +19 -0
  15. package/dist/{chunk-R34CVLCX.js → chunk-GNJIET26.js} +1 -1
  16. package/dist/chunk-NBRASCX4.js +145 -0
  17. package/dist/chunk-ZYTBRHLJ.js +150 -0
  18. package/dist/core/index.cjs +68 -10
  19. package/dist/core/index.d.cts +9 -1
  20. package/dist/core/index.d.ts +9 -1
  21. package/dist/core/index.js +5 -1
  22. package/dist/divider/index.cjs +31 -13
  23. package/dist/divider/index.js +2 -2
  24. package/dist/icon/index.cjs +680 -0
  25. package/dist/icon/index.d.cts +36 -0
  26. package/dist/icon/index.d.ts +36 -0
  27. package/dist/icon/index.js +13 -0
  28. package/dist/index.cjs +1641 -53
  29. package/dist/index.d.cts +3 -0
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.js +16 -4
  32. package/dist/indicator/index.cjs +60 -42
  33. package/dist/indicator/index.js +2 -2
  34. package/dist/progress/index.cjs +45 -27
  35. package/dist/progress/index.js +1 -1
  36. package/dist/select/index.cjs +107 -89
  37. package/dist/select/index.js +10 -23
  38. package/dist/switch/index.cjs +58 -40
  39. package/dist/switch/index.js +1 -1
  40. package/dist/typography/index.cjs +223 -0
  41. package/dist/typography/index.d.cts +43 -0
  42. package/dist/typography/index.d.ts +43 -0
  43. package/dist/typography/index.js +7 -0
  44. package/dist/view/index.cjs +966 -0
  45. package/dist/view/index.d.cts +527 -0
  46. package/dist/view/index.d.ts +527 -0
  47. package/dist/view/index.js +923 -0
  48. package/package.json +21 -1
  49. package/dist/chunk-ORMNMNOK.js +0 -89
@@ -0,0 +1,1138 @@
1
+ import {
2
+ CheckmarkIcon
3
+ } from "./chunk-GBHQCAKW.js";
4
+ import {
5
+ ArrowBackIcon,
6
+ CloseIcon
7
+ } from "./chunk-7LXW4BXD.js";
8
+ import {
9
+ Portal,
10
+ useXUITheme
11
+ } from "./chunk-NBRASCX4.js";
12
+
13
+ // src/components/autocomplete/autocomplete.tsx
14
+ import React4, { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
15
+ import { Keyboard as Keyboard2, Text as Text3, View as View4 } from "react-native";
16
+
17
+ // src/components/autocomplete/autocomplete-context.ts
18
+ import { createContext } from "react";
19
+ var AutocompleteContext = createContext({
20
+ size: "md",
21
+ themeColor: "default",
22
+ isDisabled: false
23
+ });
24
+
25
+ // src/components/autocomplete/autocomplete.hook.ts
26
+ import { useMemo } from "react";
27
+ import { getSafeThemeColor } from "@xaui/core";
28
+ import { colors } from "@xaui/core/palette";
29
+ var useAutocompleteColorScheme = (themeColor) => {
30
+ const theme = useXUITheme();
31
+ const safeThemeColor = getSafeThemeColor(themeColor);
32
+ const colorScheme = theme.colors[safeThemeColor];
33
+ return { theme, colorScheme };
34
+ };
35
+ var useAutocompleteSizeStyles = (size) => {
36
+ const theme = useXUITheme();
37
+ return useMemo(() => {
38
+ const sizes = {
39
+ xs: {
40
+ minHeight: 34,
41
+ paddingHorizontal: theme.spacing.sm,
42
+ paddingVertical: theme.spacing.xs,
43
+ fontSize: theme.fontSizes.xs,
44
+ labelSize: theme.fontSizes.xs
45
+ },
46
+ sm: {
47
+ minHeight: 38,
48
+ paddingHorizontal: theme.spacing.md,
49
+ paddingVertical: theme.spacing.xs,
50
+ fontSize: theme.fontSizes.sm,
51
+ labelSize: theme.fontSizes.xs
52
+ },
53
+ md: {
54
+ minHeight: 42,
55
+ paddingHorizontal: theme.spacing.md,
56
+ paddingVertical: theme.spacing.sm,
57
+ fontSize: theme.fontSizes.md,
58
+ labelSize: theme.fontSizes.sm
59
+ },
60
+ lg: {
61
+ minHeight: 50,
62
+ paddingHorizontal: theme.spacing.lg,
63
+ paddingVertical: theme.spacing.md,
64
+ fontSize: theme.fontSizes.lg,
65
+ labelSize: theme.fontSizes.md
66
+ }
67
+ };
68
+ return sizes[size];
69
+ }, [size, theme]);
70
+ };
71
+ var useAutocompleteRadiusStyles = (radius) => {
72
+ const theme = useXUITheme();
73
+ const radiusStyles = useMemo(() => {
74
+ const radii = {
75
+ none: theme.borderRadius.none,
76
+ sm: theme.borderRadius.sm,
77
+ md: theme.borderRadius.md,
78
+ lg: theme.borderRadius.lg,
79
+ full: theme.borderRadius.full
80
+ };
81
+ return { borderRadius: radii[radius] };
82
+ }, [radius, theme]);
83
+ const listboxRadius = useMemo(() => {
84
+ const radii = {
85
+ none: theme.borderRadius.none,
86
+ sm: theme.borderRadius.sm,
87
+ md: theme.borderRadius.md,
88
+ lg: theme.borderRadius.lg,
89
+ full: theme.borderRadius.full
90
+ };
91
+ return Math.min(radii[radius], theme.borderRadius.lg);
92
+ }, [radius, theme]);
93
+ return { radiusStyles, listboxRadius };
94
+ };
95
+ var useAutocompleteVariantStyles = (themeColor, variant, isInvalid) => {
96
+ const { theme, colorScheme } = useAutocompleteColorScheme(themeColor);
97
+ return useMemo(() => {
98
+ let borderColor = isInvalid ? theme.colors.danger.main : colorScheme.main;
99
+ if ((variant === "outlined" || variant === "faded") && themeColor === "default") {
100
+ borderColor = colors.gray[300];
101
+ }
102
+ const styles4 = {
103
+ outlined: {
104
+ backgroundColor: "transparent",
105
+ borderWidth: theme.borderWidth.md,
106
+ borderColor
107
+ },
108
+ flat: {
109
+ backgroundColor: colorScheme.background,
110
+ borderWidth: 0
111
+ },
112
+ light: {
113
+ backgroundColor: "transparent",
114
+ borderWidth: 0
115
+ },
116
+ faded: {
117
+ backgroundColor: `${colorScheme.background}90`,
118
+ borderWidth: theme.borderWidth.md,
119
+ borderColor
120
+ },
121
+ underlined: {
122
+ backgroundColor: "transparent",
123
+ borderBottomWidth: theme.borderWidth.md,
124
+ borderColor
125
+ }
126
+ };
127
+ return styles4[variant];
128
+ }, [variant, theme, colorScheme, isInvalid, themeColor]);
129
+ };
130
+ var useAutocompleteLabelStyle = (themeColor, isInvalid, labelSize) => {
131
+ const { theme, colorScheme } = useAutocompleteColorScheme(themeColor);
132
+ return useMemo(() => {
133
+ let baseColor = theme.colors.foreground;
134
+ if (isInvalid) {
135
+ baseColor = theme.colors.danger.main;
136
+ } else if (themeColor !== "default") {
137
+ baseColor = colorScheme.main;
138
+ }
139
+ return {
140
+ fontSize: labelSize,
141
+ color: baseColor
142
+ };
143
+ }, [isInvalid, labelSize, theme, themeColor, colorScheme]);
144
+ };
145
+ var useAutocompleteHelperColor = (isInvalid) => {
146
+ const theme = useXUITheme();
147
+ return useMemo(() => {
148
+ if (isInvalid) {
149
+ return theme.colors.danger.main;
150
+ }
151
+ return colors.gray[600];
152
+ }, [isInvalid, theme]);
153
+ };
154
+
155
+ // src/components/autocomplete/autocomplete.style.ts
156
+ import { StyleSheet } from "react-native";
157
+ var styles = StyleSheet.create({
158
+ container: {
159
+ gap: 6,
160
+ position: "relative"
161
+ },
162
+ fullWidth: {
163
+ flexShrink: 1,
164
+ flexBasis: "auto",
165
+ width: "100%"
166
+ },
167
+ minWidth: {
168
+ minWidth: 170
169
+ },
170
+ label: {
171
+ fontWeight: "500"
172
+ },
173
+ trigger: {
174
+ flexDirection: "row",
175
+ alignItems: "center",
176
+ justifyContent: "space-between",
177
+ gap: 8
178
+ },
179
+ triggerText: {
180
+ flex: 1
181
+ },
182
+ triggerWrapper: {
183
+ position: "relative"
184
+ },
185
+ triggerContent: {
186
+ flexDirection: "row",
187
+ alignItems: "center",
188
+ flex: 1,
189
+ gap: 8
190
+ },
191
+ triggerContentColumn: {
192
+ flexDirection: "column",
193
+ alignItems: "flex-start"
194
+ },
195
+ inputWrapper: {
196
+ flex: 1,
197
+ gap: 2
198
+ },
199
+ input: {
200
+ flexShrink: 1,
201
+ padding: 0
202
+ },
203
+ endSlot: {
204
+ flexDirection: "row",
205
+ alignItems: "center",
206
+ gap: 8
207
+ },
208
+ clearButton: {
209
+ padding: 2,
210
+ paddingLeft: 4,
211
+ marginRight: -4
212
+ },
213
+ helperText: {
214
+ fontSize: 12
215
+ },
216
+ disabled: {
217
+ opacity: 0.5
218
+ },
219
+ listbox: {
220
+ overflow: "hidden",
221
+ marginTop: 0,
222
+ position: "absolute",
223
+ left: 0,
224
+ right: 0,
225
+ zIndex: 50,
226
+ elevation: 4
227
+ },
228
+ listboxContent: {
229
+ flexShrink: 1
230
+ },
231
+ emptyMessage: {
232
+ paddingHorizontal: 16,
233
+ paddingVertical: 12,
234
+ textAlign: "center"
235
+ },
236
+ outsideLeftRow: {
237
+ flexDirection: "row",
238
+ alignItems: "center",
239
+ gap: 12
240
+ }
241
+ });
242
+
243
+ // src/components/autocomplete/autocomplete.state.hook.ts
244
+ import { useCallback, useEffect, useRef, useState } from "react";
245
+ var useAutocompleteOpenState = ({
246
+ isOpened,
247
+ isDisabled,
248
+ onOpenChange,
249
+ onClose
250
+ }) => {
251
+ const [internalIsOpen, setInternalIsOpen] = useState(false);
252
+ const isOpen = isOpened !== void 0 ? isOpened : internalIsOpen;
253
+ const setOpen = useCallback(
254
+ (open) => {
255
+ if (isDisabled) {
256
+ return;
257
+ }
258
+ if (isOpened === void 0) {
259
+ setInternalIsOpen(open);
260
+ }
261
+ onOpenChange?.(open);
262
+ if (!open) {
263
+ onClose?.();
264
+ }
265
+ },
266
+ [isDisabled, isOpened, onOpenChange, onClose]
267
+ );
268
+ return { isOpen, setOpen };
269
+ };
270
+ var useAutocompleteInputState = ({
271
+ inputValue,
272
+ defaultInputValue,
273
+ selectedKey,
274
+ onInputChange
275
+ }) => {
276
+ const [internalInputValue, setInternalInputValue] = useState(defaultInputValue ?? "");
277
+ const currentInputValue = inputValue !== void 0 ? inputValue : internalInputValue;
278
+ const updateInputValue = useCallback(
279
+ (value) => {
280
+ if (inputValue === void 0) {
281
+ setInternalInputValue(value);
282
+ }
283
+ onInputChange?.(value);
284
+ },
285
+ [inputValue, onInputChange]
286
+ );
287
+ useEffect(() => {
288
+ if (selectedKey === null && inputValue === void 0) {
289
+ setInternalInputValue("");
290
+ }
291
+ }, [selectedKey, inputValue]);
292
+ return { currentInputValue, updateInputValue };
293
+ };
294
+ var useAutocompleteSelection = ({
295
+ onSelectionChange
296
+ }) => {
297
+ const [currentSelectedKey, setCurrentSelectedKey] = useState(null);
298
+ const updateSelection = useCallback(
299
+ (key) => {
300
+ setCurrentSelectedKey(key);
301
+ onSelectionChange?.(key);
302
+ },
303
+ [onSelectionChange]
304
+ );
305
+ return { currentSelectedKey, updateSelection };
306
+ };
307
+
308
+ // src/components/autocomplete/autocomplete.utils.ts
309
+ var getTextValue = (node) => {
310
+ if (typeof node === "string" || typeof node === "number") {
311
+ return String(node);
312
+ }
313
+ return null;
314
+ };
315
+ var defaultFilterFunction = (textValue, inputValue) => {
316
+ const normalizedText = textValue.toLowerCase().trim();
317
+ const normalizedInput = inputValue.toLowerCase().trim();
318
+ return normalizedText.includes(normalizedInput);
319
+ };
320
+
321
+ // src/components/dialogs/autocomplete-dialog/autocomplete-dialog.tsx
322
+ import React2, { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
323
+ import {
324
+ Animated as Animated3,
325
+ BackHandler,
326
+ FlatList,
327
+ InteractionManager,
328
+ Keyboard,
329
+ Platform,
330
+ Pressable as Pressable2,
331
+ View as View2,
332
+ useWindowDimensions
333
+ } from "react-native";
334
+
335
+ // src/components/dialogs/autocomplete-dialog/autocomplete-dialog-header.tsx
336
+ import React from "react";
337
+ import {
338
+ Animated,
339
+ Pressable,
340
+ Text,
341
+ TextInput,
342
+ View
343
+ } from "react-native";
344
+
345
+ // src/components/dialogs/autocomplete-dialog/autocomplete-dialog.style.ts
346
+ import { StyleSheet as StyleSheet2 } from "react-native";
347
+ var styles2 = StyleSheet2.create({
348
+ backdrop: {
349
+ ...StyleSheet2.absoluteFillObject,
350
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
351
+ },
352
+ dialogContainer: {
353
+ flex: 1,
354
+ zIndex: 1
355
+ },
356
+ container: {
357
+ flex: 1,
358
+ paddingTop: 55,
359
+ paddingHorizontal: 16
360
+ },
361
+ header: {
362
+ marginBottom: 12
363
+ },
364
+ titleRow: {
365
+ flexDirection: "row",
366
+ alignItems: "center",
367
+ gap: 8,
368
+ marginBottom: 16
369
+ },
370
+ backButton: {
371
+ padding: 4
372
+ },
373
+ title: {
374
+ fontSize: 20,
375
+ fontWeight: "600"
376
+ },
377
+ inputContainer: {
378
+ flexDirection: "row",
379
+ alignItems: "center",
380
+ gap: 8
381
+ },
382
+ inputWrapper: {
383
+ flex: 1,
384
+ flexDirection: "row",
385
+ alignItems: "center",
386
+ position: "relative"
387
+ },
388
+ input: {
389
+ flex: 1,
390
+ paddingVertical: 12,
391
+ paddingHorizontal: 16,
392
+ paddingRight: 40,
393
+ borderRadius: 8,
394
+ fontSize: 16
395
+ },
396
+ clearInputButton: {
397
+ position: "absolute",
398
+ right: 8,
399
+ padding: 4
400
+ },
401
+ listContainer: {
402
+ flex: 1
403
+ },
404
+ listContentContainer: {
405
+ paddingBottom: 80
406
+ },
407
+ checkmarkButtonContainer: {
408
+ position: "absolute",
409
+ bottom: 24,
410
+ right: 24
411
+ },
412
+ checkmarkButton: {
413
+ width: 54,
414
+ height: 54,
415
+ borderRadius: 27,
416
+ justifyContent: "center",
417
+ alignItems: "center",
418
+ elevation: 8
419
+ }
420
+ });
421
+
422
+ // src/components/dialogs/autocomplete-dialog/autocomplete-dialog-header.tsx
423
+ var addOpacityToColor = (color, opacity) => {
424
+ const hex = color.replace("#", "");
425
+ const r = parseInt(hex.substring(0, 2), 16);
426
+ const g = parseInt(hex.substring(2, 4), 16);
427
+ const b = parseInt(hex.substring(4, 6), 16);
428
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
429
+ };
430
+ var AutocompleteDialogHeader = ({
431
+ title,
432
+ inputValue,
433
+ placeholder = "Search...",
434
+ inputRef,
435
+ inputAnimatedStyle,
436
+ inputTextStyle,
437
+ onInputChange,
438
+ onClose,
439
+ onCheckmarkPress,
440
+ onFocus,
441
+ onBlur
442
+ }) => {
443
+ const theme = useXUITheme();
444
+ return /* @__PURE__ */ React.createElement(View, { style: styles2.header }, title ? /* @__PURE__ */ React.createElement(View, { style: styles2.titleRow }, /* @__PURE__ */ React.createElement(
445
+ Pressable,
446
+ {
447
+ style: styles2.backButton,
448
+ onPress: onClose,
449
+ accessibilityLabel: "Back",
450
+ accessibilityRole: "button"
451
+ },
452
+ /* @__PURE__ */ React.createElement(ArrowBackIcon, { size: 20, color: theme.colors.foreground })
453
+ ), /* @__PURE__ */ React.createElement(Text, { style: [styles2.title, { color: theme.colors.foreground }] }, title)) : null, /* @__PURE__ */ React.createElement(View, { style: styles2.inputContainer }, /* @__PURE__ */ React.createElement(Animated.View, { style: [styles2.inputWrapper, inputAnimatedStyle] }, /* @__PURE__ */ React.createElement(
454
+ TextInput,
455
+ {
456
+ ref: inputRef,
457
+ value: inputValue,
458
+ onChangeText: onInputChange,
459
+ placeholder,
460
+ placeholderTextColor: addOpacityToColor(
461
+ theme.colors.foreground,
462
+ 0.5
463
+ ),
464
+ style: [
465
+ styles2.input,
466
+ {
467
+ backgroundColor: theme.colors.default.background,
468
+ color: theme.colors.foreground
469
+ },
470
+ inputTextStyle
471
+ ],
472
+ autoFocus: true,
473
+ returnKeyType: "done",
474
+ onSubmitEditing: onCheckmarkPress,
475
+ onFocus,
476
+ onBlur
477
+ }
478
+ ), inputValue ? /* @__PURE__ */ React.createElement(
479
+ Pressable,
480
+ {
481
+ style: styles2.clearInputButton,
482
+ onPress: () => onInputChange?.(""),
483
+ accessibilityLabel: "Clear input",
484
+ accessibilityRole: "button"
485
+ },
486
+ /* @__PURE__ */ React.createElement(CloseIcon, { color: theme.colors.foreground })
487
+ ) : null)));
488
+ };
489
+
490
+ // src/components/dialogs/autocomplete-dialog/autocomplete-dialog.animation.ts
491
+ import { useEffect as useEffect2 } from "react";
492
+ import { Animated as Animated2 } from "react-native";
493
+ var useAutocompleteDialogAnimation = ({
494
+ visible,
495
+ fadeAnim,
496
+ slideAnim,
497
+ scaleAnim
498
+ }) => {
499
+ useEffect2(() => {
500
+ if (visible) {
501
+ Animated2.parallel([
502
+ Animated2.timing(fadeAnim, {
503
+ toValue: 1,
504
+ duration: 250,
505
+ useNativeDriver: true
506
+ }),
507
+ Animated2.spring(slideAnim, {
508
+ toValue: 1,
509
+ useNativeDriver: true,
510
+ tension: 65,
511
+ friction: 10
512
+ }),
513
+ Animated2.spring(scaleAnim, {
514
+ toValue: 1,
515
+ useNativeDriver: true,
516
+ tension: 50,
517
+ friction: 8
518
+ })
519
+ ]).start();
520
+ } else {
521
+ fadeAnim.setValue(0);
522
+ slideAnim.setValue(0);
523
+ scaleAnim.setValue(0);
524
+ }
525
+ }, [visible, fadeAnim, slideAnim, scaleAnim]);
526
+ };
527
+
528
+ // src/components/dialogs/autocomplete-dialog/autocomplete-dialog.tsx
529
+ var AutocompleteDialog = ({
530
+ visible,
531
+ inputValue,
532
+ placeholder = "Search...",
533
+ title,
534
+ themeColor = "primary",
535
+ children,
536
+ showCheckmark = true,
537
+ checkmarkIcon,
538
+ inputTextStyle,
539
+ style,
540
+ onInputChange,
541
+ onClose,
542
+ onCheckmark,
543
+ onFocus,
544
+ onBlur
545
+ }) => {
546
+ const theme = useXUITheme();
547
+ const { width: screenWidth, height: screenHeight } = useWindowDimensions();
548
+ const fadeAnim = useRef2(new Animated3.Value(0)).current;
549
+ const slideAnim = useRef2(new Animated3.Value(0)).current;
550
+ const scaleAnim = useRef2(new Animated3.Value(0)).current;
551
+ const inputRef = useRef2(null);
552
+ const [keyboardHeight, setKeyboardHeight] = useState2(0);
553
+ useEffect3(() => {
554
+ const showEvent = Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow";
555
+ const hideEvent = Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide";
556
+ const showSub = Keyboard.addListener(showEvent, (e) => {
557
+ setKeyboardHeight(e.endCoordinates.height);
558
+ });
559
+ const hideSub = Keyboard.addListener(hideEvent, () => {
560
+ setKeyboardHeight(0);
561
+ });
562
+ return () => {
563
+ showSub.remove();
564
+ hideSub.remove();
565
+ };
566
+ }, []);
567
+ const items = useMemo2(
568
+ () => React2.Children.toArray(children).filter(
569
+ React2.isValidElement
570
+ ),
571
+ [children]
572
+ );
573
+ const checkmarkColor = theme.colors[themeColor].main;
574
+ const checkmarkBackgroundColor = theme.colors[themeColor].background;
575
+ useAutocompleteDialogAnimation({ visible, fadeAnim, slideAnim, scaleAnim });
576
+ useEffect3(() => {
577
+ if (!visible) return;
578
+ const sub = BackHandler.addEventListener("hardwareBackPress", () => {
579
+ onClose?.();
580
+ return true;
581
+ });
582
+ return () => sub.remove();
583
+ }, [visible, onClose]);
584
+ const focusInput = useCallback2(() => {
585
+ const delay = Platform.OS === "android" ? 300 : 100;
586
+ InteractionManager.runAfterInteractions(() => {
587
+ setTimeout(() => {
588
+ inputRef.current?.focus();
589
+ }, delay);
590
+ });
591
+ }, []);
592
+ const handleCheckmarkPress = () => {
593
+ onCheckmark?.();
594
+ Keyboard.dismiss();
595
+ };
596
+ useEffect3(() => {
597
+ if (!visible) {
598
+ Keyboard.dismiss();
599
+ return;
600
+ }
601
+ focusInput();
602
+ }, [focusInput, visible]);
603
+ const listBottomPadding = useMemo2(() => {
604
+ const basePadding = showCheckmark ? 96 : 64;
605
+ return (keyboardHeight > 0 ? keyboardHeight : 0) + basePadding;
606
+ }, [keyboardHeight, showCheckmark]);
607
+ if (!visible) return null;
608
+ const overlayStyle = {
609
+ position: "absolute",
610
+ top: 0,
611
+ left: 0,
612
+ width: screenWidth,
613
+ height: screenHeight
614
+ };
615
+ const containerAnimatedStyle = {
616
+ transform: [
617
+ {
618
+ translateY: slideAnim.interpolate({
619
+ inputRange: [0, 1],
620
+ outputRange: [screenHeight, 0]
621
+ })
622
+ }
623
+ ]
624
+ };
625
+ const inputAnimatedStyle = {
626
+ transform: [{ scaleX: scaleAnim }]
627
+ };
628
+ const listHeader = /* @__PURE__ */ React2.createElement(
629
+ AutocompleteDialogHeader,
630
+ {
631
+ title,
632
+ inputValue,
633
+ placeholder,
634
+ inputRef,
635
+ inputAnimatedStyle,
636
+ inputTextStyle,
637
+ onInputChange,
638
+ onClose,
639
+ onCheckmarkPress: handleCheckmarkPress,
640
+ onFocus,
641
+ onBlur
642
+ }
643
+ );
644
+ return /* @__PURE__ */ React2.createElement(Portal, null, /* @__PURE__ */ React2.createElement(View2, { style: [overlayStyle, style] }, /* @__PURE__ */ React2.createElement(Animated3.View, { style: [styles2.backdrop, { opacity: fadeAnim }] }), /* @__PURE__ */ React2.createElement(Animated3.View, { style: [styles2.dialogContainer, containerAnimatedStyle] }, /* @__PURE__ */ React2.createElement(
645
+ View2,
646
+ {
647
+ style: [styles2.container, { backgroundColor: theme.colors.background }]
648
+ },
649
+ listHeader,
650
+ /* @__PURE__ */ React2.createElement(
651
+ FlatList,
652
+ {
653
+ data: items,
654
+ renderItem: ({ item }) => item,
655
+ keyExtractor: (_, index) => String(index),
656
+ style: styles2.listContainer,
657
+ contentContainerStyle: {
658
+ paddingBottom: listBottomPadding
659
+ },
660
+ keyboardShouldPersistTaps: "always",
661
+ keyboardDismissMode: "none",
662
+ showsVerticalScrollIndicator: false
663
+ }
664
+ ),
665
+ showCheckmark ? /* @__PURE__ */ React2.createElement(View2, { style: styles2.checkmarkButtonContainer }, /* @__PURE__ */ React2.createElement(
666
+ Pressable2,
667
+ {
668
+ style: [
669
+ styles2.checkmarkButton,
670
+ { backgroundColor: checkmarkBackgroundColor }
671
+ ],
672
+ onPress: handleCheckmarkPress,
673
+ accessibilityLabel: "Confirm",
674
+ accessibilityRole: "button"
675
+ },
676
+ checkmarkIcon ?? /* @__PURE__ */ React2.createElement(CheckmarkIcon, { color: checkmarkColor, size: 20 })
677
+ )) : null
678
+ ))));
679
+ };
680
+
681
+ // src/components/autocomplete/autocomplete-trigger.tsx
682
+ import React3 from "react";
683
+ import { Pressable as Pressable3, Text as Text2, TouchableOpacity, View as View3 } from "react-native";
684
+ var AutocompleteTrigger = ({
685
+ triggerRef,
686
+ isDisabled,
687
+ currentSelectedKey,
688
+ currentInputValue,
689
+ displayValue,
690
+ sizeStyles,
691
+ radiusStyles,
692
+ variantStyles,
693
+ theme,
694
+ isClearable,
695
+ label,
696
+ labelText,
697
+ isLabelInside,
698
+ clearIcon,
699
+ style,
700
+ textStyle,
701
+ onPress: handleTriggerPress,
702
+ onClear: handleClear,
703
+ onLayout: handleTriggerLayout
704
+ }) => {
705
+ const renderLabel = isLabelInside && label;
706
+ return /* @__PURE__ */ React3.createElement(
707
+ Pressable3,
708
+ {
709
+ ref: triggerRef,
710
+ onPress: handleTriggerPress,
711
+ onLayout: handleTriggerLayout,
712
+ disabled: isDisabled,
713
+ style: [
714
+ styles.trigger,
715
+ {
716
+ minHeight: sizeStyles.minHeight,
717
+ paddingHorizontal: sizeStyles.paddingHorizontal,
718
+ paddingVertical: sizeStyles.paddingVertical
719
+ },
720
+ radiusStyles,
721
+ variantStyles,
722
+ isDisabled && styles.disabled,
723
+ style
724
+ ],
725
+ accessibilityLabel: labelText ?? (typeof label === "string" ? label : void 0),
726
+ accessibilityRole: "button",
727
+ accessibilityState: { disabled: isDisabled }
728
+ },
729
+ /* @__PURE__ */ React3.createElement(View3, { style: styles.triggerContent }, isLabelInside && renderLabel, /* @__PURE__ */ React3.createElement(
730
+ Text2,
731
+ {
732
+ style: [
733
+ styles.triggerText,
734
+ { fontSize: sizeStyles.fontSize, color: theme.colors.foreground },
735
+ !currentSelectedKey && !currentInputValue && { opacity: 0.5 },
736
+ textStyle
737
+ ],
738
+ numberOfLines: 1,
739
+ ellipsizeMode: "tail"
740
+ },
741
+ displayValue
742
+ )),
743
+ isClearable && (currentSelectedKey || currentInputValue) ? /* @__PURE__ */ React3.createElement(
744
+ TouchableOpacity,
745
+ {
746
+ onPress: handleClear,
747
+ style: styles.clearButton,
748
+ hitSlop: { top: 8, right: 8, bottom: 8, left: 8 }
749
+ },
750
+ clearIcon ?? /* @__PURE__ */ React3.createElement(CloseIcon, { color: theme.colors.foreground, size: 20 })
751
+ ) : null
752
+ );
753
+ };
754
+
755
+ // src/components/autocomplete/autocomplete.tsx
756
+ var defaultPlaceholder = "Search...";
757
+ var Autocomplete = ({
758
+ children,
759
+ variant = "flat",
760
+ themeColor = "default",
761
+ size = "md",
762
+ radius = "md",
763
+ placeholder = defaultPlaceholder,
764
+ labelPlacement = "outside",
765
+ label,
766
+ description,
767
+ errorMessage,
768
+ clearIcon,
769
+ fullWidth = false,
770
+ isDisabled = false,
771
+ isInvalid = false,
772
+ isClearable = true,
773
+ allowsCustomValue = false,
774
+ forceSelection = false,
775
+ allowsEmptyCollection = true,
776
+ disableLocalFilter = false,
777
+ inputValue,
778
+ defaultInputValue,
779
+ style,
780
+ textStyle,
781
+ onClose,
782
+ onOpenChange,
783
+ onSelectionChange,
784
+ onInputChange,
785
+ onClear
786
+ }) => {
787
+ const { currentSelectedKey, updateSelection } = useAutocompleteSelection({
788
+ onSelectionChange
789
+ });
790
+ const { currentInputValue, updateInputValue } = useAutocompleteInputState({
791
+ inputValue,
792
+ defaultInputValue,
793
+ selectedKey: currentSelectedKey,
794
+ onInputChange
795
+ });
796
+ const { isOpen, setOpen } = useAutocompleteOpenState({
797
+ isOpened: void 0,
798
+ isDisabled,
799
+ onOpenChange,
800
+ onClose
801
+ });
802
+ const triggerRef = useRef3(null);
803
+ const [triggerLayout, setTriggerLayout] = useState3();
804
+ const handleTriggerLayout = useCallback3(() => {
805
+ triggerRef.current?.measureInWindow((x, y, width, height) => {
806
+ setTriggerLayout({ x, y, width, height });
807
+ });
808
+ }, []);
809
+ const items = useMemo3(() => {
810
+ const elements = React4.Children.toArray(children).filter(Boolean);
811
+ return elements.map((child, index) => {
812
+ if (!React4.isValidElement(child)) {
813
+ return null;
814
+ }
815
+ const key = child.props.value ?? String(index);
816
+ const labelText = getTextValue(child.props.label) ?? key;
817
+ return {
818
+ key,
819
+ element: child,
820
+ labelText
821
+ };
822
+ }).filter((item) => item !== null);
823
+ }, [children]);
824
+ const filteredItems = useMemo3(() => {
825
+ if (disableLocalFilter || !currentInputValue.trim()) {
826
+ return items;
827
+ }
828
+ return items.filter(
829
+ (item) => defaultFilterFunction(item.labelText, currentInputValue)
830
+ );
831
+ }, [disableLocalFilter, items, currentInputValue]);
832
+ const theme = useXUITheme();
833
+ const sizeStyles = useAutocompleteSizeStyles(size);
834
+ const { radiusStyles } = useAutocompleteRadiusStyles(radius);
835
+ const variantStyles = useAutocompleteVariantStyles(themeColor, variant, isInvalid);
836
+ const labelStyle = useAutocompleteLabelStyle(
837
+ themeColor,
838
+ isInvalid,
839
+ sizeStyles.labelSize
840
+ );
841
+ const helperColor = useAutocompleteHelperColor(isInvalid);
842
+ const selectedItem = items.find((item) => item.key === currentSelectedKey);
843
+ const displayValue = forceSelection ? selectedItem?.labelText || placeholder : currentInputValue || placeholder;
844
+ const handleInputChange = useCallback3(
845
+ (text) => {
846
+ updateInputValue(text);
847
+ const selectedLabel = selectedItem?.labelText ?? "";
848
+ const shouldClearSelection = !text.trim() && !allowsCustomValue || currentSelectedKey !== null && text !== selectedLabel;
849
+ if (shouldClearSelection) {
850
+ updateSelection(null);
851
+ }
852
+ },
853
+ [
854
+ updateInputValue,
855
+ allowsCustomValue,
856
+ updateSelection,
857
+ selectedItem,
858
+ currentSelectedKey
859
+ ]
860
+ );
861
+ const handleItemSelection = useCallback3(
862
+ (key, itemLabel) => {
863
+ if (isDisabled) {
864
+ return;
865
+ }
866
+ updateSelection(key);
867
+ updateInputValue(itemLabel);
868
+ setTimeout(() => {
869
+ Keyboard2.dismiss();
870
+ setOpen(false);
871
+ }, 50);
872
+ },
873
+ [isDisabled, updateSelection, updateInputValue, setOpen]
874
+ );
875
+ const handleCheckmark = useCallback3(() => {
876
+ setOpen(false);
877
+ }, [setOpen]);
878
+ const handleClear = useCallback3(() => {
879
+ if (isDisabled) {
880
+ return;
881
+ }
882
+ updateSelection(null);
883
+ updateInputValue("");
884
+ onClear?.();
885
+ }, [isDisabled, updateSelection, updateInputValue, onClear]);
886
+ const handleTriggerPress = useCallback3(() => {
887
+ if (!isDisabled) {
888
+ if (selectedItem && !currentInputValue) {
889
+ updateInputValue(selectedItem.labelText);
890
+ }
891
+ setOpen(true);
892
+ }
893
+ }, [isDisabled, setOpen, selectedItem, currentInputValue, updateInputValue]);
894
+ const listItems = filteredItems.map((item) => {
895
+ const itemProps = item.element.props;
896
+ const itemDisabled = isDisabled || itemProps.isDisabled;
897
+ const itemSelected = itemProps.isSelected ?? currentSelectedKey === item.key;
898
+ const handleItemSelected = () => {
899
+ if (itemDisabled || itemProps.isReadOnly) {
900
+ return;
901
+ }
902
+ handleItemSelection(item.key, item.labelText);
903
+ itemProps.onSelected?.();
904
+ };
905
+ return React4.cloneElement(item.element, {
906
+ key: item.key,
907
+ isDisabled: itemDisabled,
908
+ isSelected: itemSelected,
909
+ onSelected: handleItemSelected
910
+ });
911
+ });
912
+ const showEmptyMessage = !allowsEmptyCollection && listItems.length === 0;
913
+ const isLabelInside = labelPlacement === "inside";
914
+ const isLabelOutsideLeft = labelPlacement === "outside-left";
915
+ const isLabelOutside = labelPlacement === "outside" || labelPlacement === "outside-top";
916
+ const renderLabel = label ? typeof label === "string" || typeof label === "number" ? /* @__PURE__ */ React4.createElement(Text3, { style: [styles.label, labelStyle] }, label) : /* @__PURE__ */ React4.createElement(View4, null, label) : null;
917
+ const shouldShowHelper = Boolean(description || errorMessage);
918
+ const helperContent = isInvalid && errorMessage ? errorMessage : description;
919
+ const triggerContent = /* @__PURE__ */ React4.createElement(
920
+ AutocompleteTrigger,
921
+ {
922
+ triggerRef,
923
+ isDisabled,
924
+ currentSelectedKey,
925
+ currentInputValue,
926
+ displayValue,
927
+ sizeStyles,
928
+ radiusStyles,
929
+ variantStyles,
930
+ theme,
931
+ isClearable,
932
+ label: renderLabel,
933
+ labelText: typeof label === "string" ? label : void 0,
934
+ isLabelInside,
935
+ clearIcon,
936
+ style,
937
+ textStyle,
938
+ onPress: handleTriggerPress,
939
+ onClear: handleClear,
940
+ onLayout: handleTriggerLayout
941
+ }
942
+ );
943
+ const labelBlock = isLabelOutside || isLabelInside ? renderLabel : null;
944
+ return /* @__PURE__ */ React4.createElement(View4, { style: [styles.container, fullWidth ? styles.fullWidth : styles.minWidth] }, isLabelOutside && labelBlock, isLabelOutsideLeft ? /* @__PURE__ */ React4.createElement(View4, { style: styles.outsideLeftRow }, renderLabel, triggerContent) : triggerContent, shouldShowHelper && helperContent ? typeof helperContent === "string" || typeof helperContent === "number" ? /* @__PURE__ */ React4.createElement(Text3, { style: [styles.helperText, { color: helperColor }] }, helperContent) : /* @__PURE__ */ React4.createElement(View4, null, helperContent) : null, /* @__PURE__ */ React4.createElement(
945
+ AutocompleteDialog,
946
+ {
947
+ visible: isOpen,
948
+ inputValue: currentInputValue,
949
+ placeholder,
950
+ title: typeof label === "string" ? label : void 0,
951
+ themeColor,
952
+ _triggerLayout: triggerLayout,
953
+ showCheckmark: false,
954
+ onInputChange: handleInputChange,
955
+ onClose: () => setOpen(false),
956
+ onCheckmark: handleCheckmark
957
+ },
958
+ /* @__PURE__ */ React4.createElement(AutocompleteContext.Provider, { value: { size, themeColor, isDisabled } }, showEmptyMessage ? /* @__PURE__ */ React4.createElement(Text3, { style: [styles.emptyMessage, { color: theme.colors.foreground }] }, "No results found") : listItems)
959
+ ));
960
+ };
961
+
962
+ // src/components/autocomplete/autocomplete-item.tsx
963
+ import React5, { useContext as useContext2 } from "react";
964
+ import { Pressable as Pressable4, Text as Text4, View as View5 } from "react-native";
965
+
966
+ // src/components/autocomplete/autocomplete-item.style.ts
967
+ import { StyleSheet as StyleSheet3 } from "react-native";
968
+ var styles3 = StyleSheet3.create({
969
+ item: {
970
+ flexDirection: "row",
971
+ alignItems: "center",
972
+ gap: 8
973
+ },
974
+ content: {
975
+ flex: 1,
976
+ gap: 2
977
+ },
978
+ title: {
979
+ fontWeight: "500"
980
+ },
981
+ description: {
982
+ opacity: 0.7
983
+ },
984
+ disabled: {
985
+ opacity: 0.5
986
+ }
987
+ });
988
+
989
+ // src/components/autocomplete/autocomplete-item.hook.ts
990
+ import { useContext, useMemo as useMemo4 } from "react";
991
+ import { getSafeThemeColor as getSafeThemeColor2 } from "@xaui/core";
992
+ var useAutocompleteItemSizeStyles = (size) => {
993
+ const theme = useXUITheme();
994
+ return useMemo4(() => {
995
+ const sizes = {
996
+ xs: {
997
+ paddingVertical: theme.spacing.sm,
998
+ paddingHorizontal: theme.spacing.sm,
999
+ titleSize: theme.fontSizes.xs,
1000
+ descriptionSize: theme.fontSizes.xs
1001
+ },
1002
+ sm: {
1003
+ paddingVertical: theme.spacing.sm,
1004
+ paddingHorizontal: theme.spacing.sm,
1005
+ titleSize: theme.fontSizes.sm,
1006
+ descriptionSize: theme.fontSizes.xs
1007
+ },
1008
+ md: {
1009
+ paddingVertical: theme.spacing.sm,
1010
+ paddingHorizontal: theme.spacing.sm,
1011
+ titleSize: theme.fontSizes.md,
1012
+ descriptionSize: theme.fontSizes.xs
1013
+ },
1014
+ lg: {
1015
+ paddingVertical: theme.spacing.sm,
1016
+ paddingHorizontal: theme.spacing.sm,
1017
+ titleSize: theme.fontSizes.lg,
1018
+ descriptionSize: theme.fontSizes.md
1019
+ }
1020
+ };
1021
+ return sizes[size];
1022
+ }, [size, theme]);
1023
+ };
1024
+ var useAutocompleteItemBackgroundColor = (themeColor, isSelected) => {
1025
+ const theme = useXUITheme();
1026
+ const safeThemeColor = getSafeThemeColor2(themeColor);
1027
+ const colorScheme = theme.colors[safeThemeColor];
1028
+ return useMemo4(() => {
1029
+ return "transparent";
1030
+ }, [isSelected, colorScheme]);
1031
+ };
1032
+ var useAutocompleteItemTextColors = () => {
1033
+ const theme = useXUITheme();
1034
+ return useMemo4(() => {
1035
+ return {
1036
+ textColor: theme.colors.foreground,
1037
+ descriptionColor: theme.colors.foreground
1038
+ };
1039
+ }, [theme]);
1040
+ };
1041
+ var useAutocompleteItemCheckmarkColor = (themeColor) => {
1042
+ const theme = useXUITheme();
1043
+ const safeThemeColor = getSafeThemeColor2(themeColor);
1044
+ const colorScheme = theme.colors[safeThemeColor];
1045
+ return useMemo4(() => {
1046
+ if (themeColor === "default") {
1047
+ return theme.colors.primary.main;
1048
+ }
1049
+ return colorScheme.main;
1050
+ }, [themeColor, colorScheme, theme]);
1051
+ };
1052
+ var useAutocompleteItemStyles = (isSelected, _isDisabled) => {
1053
+ const context = useContext(AutocompleteContext);
1054
+ const backgroundColor = useAutocompleteItemBackgroundColor(
1055
+ context.themeColor,
1056
+ isSelected
1057
+ );
1058
+ const { textColor, descriptionColor } = useAutocompleteItemTextColors();
1059
+ const checkmarkColor = useAutocompleteItemCheckmarkColor(context.themeColor);
1060
+ return {
1061
+ backgroundColor,
1062
+ labelColor: textColor,
1063
+ descriptionColor,
1064
+ checkmarkColor
1065
+ };
1066
+ };
1067
+
1068
+ // src/components/autocomplete/autocomplete-item.tsx
1069
+ var defaultSize = "md";
1070
+ var AutocompleteItem = ({
1071
+ label,
1072
+ description,
1073
+ startContent,
1074
+ endContent,
1075
+ selectedIcon: _selectedIcon,
1076
+ isDisabled = false,
1077
+ isSelected = false,
1078
+ isReadOnly = false,
1079
+ style,
1080
+ textStyle,
1081
+ onSelected
1082
+ }) => {
1083
+ const context = useContext2(AutocompleteContext);
1084
+ const size = context?.size ?? defaultSize;
1085
+ const isItemDisabled = context?.isDisabled ? true : isDisabled;
1086
+ const sizeStyles = useAutocompleteItemSizeStyles(size);
1087
+ const { backgroundColor, labelColor, descriptionColor } = useAutocompleteItemStyles(isSelected, isItemDisabled);
1088
+ const handlePress = () => {
1089
+ if (isItemDisabled || isReadOnly) {
1090
+ return;
1091
+ }
1092
+ onSelected?.();
1093
+ };
1094
+ return /* @__PURE__ */ React5.createElement(
1095
+ Pressable4,
1096
+ {
1097
+ onPress: handlePress,
1098
+ disabled: isItemDisabled,
1099
+ style: [
1100
+ styles3.item,
1101
+ {
1102
+ paddingVertical: sizeStyles.paddingVertical,
1103
+ paddingHorizontal: sizeStyles.paddingHorizontal,
1104
+ backgroundColor
1105
+ },
1106
+ isItemDisabled && styles3.disabled,
1107
+ style
1108
+ ]
1109
+ },
1110
+ startContent,
1111
+ /* @__PURE__ */ React5.createElement(View5, { style: styles3.content }, /* @__PURE__ */ React5.createElement(
1112
+ Text4,
1113
+ {
1114
+ style: [
1115
+ styles3.title,
1116
+ { fontSize: sizeStyles.titleSize, color: labelColor },
1117
+ textStyle
1118
+ ]
1119
+ },
1120
+ label
1121
+ ), description && /* @__PURE__ */ React5.createElement(
1122
+ Text4,
1123
+ {
1124
+ style: [
1125
+ styles3.description,
1126
+ { fontSize: sizeStyles.descriptionSize, color: descriptionColor }
1127
+ ]
1128
+ },
1129
+ description
1130
+ )),
1131
+ endContent
1132
+ );
1133
+ };
1134
+
1135
+ export {
1136
+ Autocomplete,
1137
+ AutocompleteItem
1138
+ };