@vellira-ui/react-native 2.19.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2304 @@
1
+ // src/components/Dropdown/Content/DropdownContent.tsx
2
+ import { Modal, Pressable, View } from "react-native";
3
+
4
+ // src/theme/ThemeProvider.tsx
5
+ import { useMemo } from "react";
6
+ import { useControllableState } from "@vellira-ui/core";
7
+
8
+ // src/theme/ThemeContext.ts
9
+ import { createContext } from "react";
10
+
11
+ // src/theme/themes.ts
12
+ import {
13
+ darkTheme,
14
+ highContrastTheme,
15
+ lightTheme
16
+ } from "@vellira-ui/tokens";
17
+ var nativeThemes = {
18
+ light: lightTheme,
19
+ dark: darkTheme,
20
+ highContrast: highContrastTheme
21
+ };
22
+
23
+ // src/theme/ThemeContext.ts
24
+ var ThemeContext = createContext({
25
+ themeName: "light",
26
+ theme: nativeThemes.light,
27
+ setTheme: () => void 0
28
+ });
29
+
30
+ // src/theme/ThemeProvider.tsx
31
+ import { jsx } from "react/jsx-runtime";
32
+ var ThemeProvider = ({
33
+ theme,
34
+ defaultTheme = "light",
35
+ onThemeChange,
36
+ children
37
+ }) => {
38
+ const [currentTheme, setCurrentTheme] = useControllableState({
39
+ value: theme,
40
+ defaultValue: defaultTheme,
41
+ onChange: onThemeChange
42
+ });
43
+ const value = useMemo(
44
+ () => ({
45
+ themeName: currentTheme,
46
+ theme: nativeThemes[currentTheme],
47
+ setTheme: setCurrentTheme
48
+ }),
49
+ [currentTheme, setCurrentTheme]
50
+ );
51
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
52
+ };
53
+ ThemeProvider.displayName = "ThemeProvider";
54
+
55
+ // src/theme/useTheme.ts
56
+ import { useContext } from "react";
57
+ function useTheme() {
58
+ return useContext(ThemeContext);
59
+ }
60
+
61
+ // src/theme/useThemeStyles.ts
62
+ import { useMemo as useMemo2 } from "react";
63
+ function useThemeStyles(createStyles24) {
64
+ const { theme } = useTheme();
65
+ return useMemo2(() => createStyles24(theme), [createStyles24, theme]);
66
+ }
67
+
68
+ // src/components/Dropdown/Content/DropdownContent.styles.ts
69
+ import { overlay } from "@vellira-ui/tokens";
70
+ import { StyleSheet } from "react-native";
71
+ var createStyles = (theme) => StyleSheet.create({
72
+ modalRoot: {
73
+ flex: 1,
74
+ justifyContent: "flex-end"
75
+ },
76
+ backdrop: {
77
+ ...StyleSheet.absoluteFill,
78
+ backgroundColor: overlay.backdrop
79
+ },
80
+ menu: {
81
+ maxHeight: "50%",
82
+ overflow: "hidden",
83
+ padding: theme.tokens.spacing[1],
84
+ backgroundColor: theme.components.dropdown.content.bg,
85
+ borderColor: theme.components.dropdown.content.border,
86
+ borderTopLeftRadius: theme.tokens.radius.lg,
87
+ borderTopRightRadius: theme.tokens.radius.lg,
88
+ borderWidth: 1
89
+ }
90
+ });
91
+
92
+ // src/components/Dropdown/Content/DropdownContent.tsx
93
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
94
+ function DropdownContent({
95
+ isOpen,
96
+ children,
97
+ onClose
98
+ }) {
99
+ const styles = useThemeStyles(createStyles);
100
+ return /* @__PURE__ */ jsx2(
101
+ Modal,
102
+ {
103
+ transparent: true,
104
+ visible: isOpen,
105
+ animationType: "slide",
106
+ onRequestClose: onClose,
107
+ children: /* @__PURE__ */ jsxs(View, { style: styles.modalRoot, children: [
108
+ /* @__PURE__ */ jsx2(
109
+ Pressable,
110
+ {
111
+ accessibilityRole: "button",
112
+ accessibilityLabel: "Close menu",
113
+ style: styles.backdrop,
114
+ onPress: onClose
115
+ }
116
+ ),
117
+ /* @__PURE__ */ jsx2(View, { accessibilityRole: "menu", style: styles.menu, children })
118
+ ] })
119
+ }
120
+ );
121
+ }
122
+ DropdownContent.displayName = "DropdownContent";
123
+
124
+ // src/components/Dropdown/Dropdown.tsx
125
+ import { useState as useState2 } from "react";
126
+ import { View as View4 } from "react-native";
127
+
128
+ // src/components/Dropdown/Group/DropdownGroup.tsx
129
+ import { Text } from "react-native";
130
+
131
+ // src/components/Dropdown/Group/DropdownGroup.styles.ts
132
+ import { StyleSheet as StyleSheet2 } from "react-native";
133
+ var createStyles2 = (theme) => StyleSheet2.create({
134
+ groupLabel: {
135
+ paddingHorizontal: theme.tokens.spacing[4],
136
+ paddingVertical: theme.tokens.spacing[2],
137
+ color: theme.components.dropdown.groupLabel.fg,
138
+ fontFamily: theme.tokens.typography.family.medium,
139
+ fontSize: theme.tokens.typography.size.xs,
140
+ lineHeight: theme.tokens.typography.lineHeight.sm,
141
+ textTransform: "uppercase"
142
+ }
143
+ });
144
+
145
+ // src/components/Dropdown/Group/DropdownGroup.tsx
146
+ import { jsx as jsx3 } from "react/jsx-runtime";
147
+ function DropdownGroup({ label }) {
148
+ const styles = useThemeStyles(createStyles2);
149
+ return /* @__PURE__ */ jsx3(Text, { style: styles.groupLabel, children: label });
150
+ }
151
+ DropdownGroup.displayName = "DropdownGroup";
152
+
153
+ // src/components/Dropdown/Item/DropdownItem.tsx
154
+ import { Pressable as Pressable2, Text as Text2 } from "react-native";
155
+
156
+ // src/components/Dropdown/Item/DropdownItem.styles.ts
157
+ import { StyleSheet as StyleSheet3 } from "react-native";
158
+ var createStyles3 = (theme) => StyleSheet3.create({
159
+ item: {
160
+ minWidth: 0,
161
+ alignItems: "center",
162
+ flexDirection: "row",
163
+ gap: theme.tokens.spacing[4],
164
+ paddingHorizontal: theme.tokens.spacing[3],
165
+ paddingVertical: theme.tokens.spacing[2],
166
+ backgroundColor: theme.components.dropdown.item.default.bg,
167
+ borderRadius: theme.tokens.radius.sm
168
+ },
169
+ itemPressed: {
170
+ backgroundColor: theme.components.dropdown.item.hover.bg
171
+ },
172
+ itemDisabled: {
173
+ backgroundColor: theme.components.dropdown.item.disabled.bg
174
+ },
175
+ itemDangerPressed: {
176
+ backgroundColor: theme.components.dropdown.item.danger.hover.bg
177
+ },
178
+ itemText: {
179
+ flex: 1,
180
+ minWidth: 0,
181
+ color: theme.components.dropdown.item.default.fg,
182
+ fontFamily: theme.tokens.typography.family.regular,
183
+ fontSize: theme.tokens.typography.size.md,
184
+ lineHeight: 24
185
+ },
186
+ itemTextDisabled: {
187
+ color: theme.components.dropdown.item.disabled.fg
188
+ },
189
+ dangerText: {
190
+ color: theme.components.dropdown.item.danger.default.fg
191
+ }
192
+ });
193
+
194
+ // src/components/Dropdown/Item/DropdownItem.tsx
195
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
196
+ function DropdownItem({
197
+ label,
198
+ value,
199
+ icon,
200
+ danger = false,
201
+ disabled = false,
202
+ textWrap = "truncate",
203
+ itemStyle,
204
+ textStyle,
205
+ onSelect
206
+ }) {
207
+ const styles = useThemeStyles(createStyles3);
208
+ return /* @__PURE__ */ jsxs2(
209
+ Pressable2,
210
+ {
211
+ disabled,
212
+ accessibilityRole: "menuitem",
213
+ accessibilityLabel: label,
214
+ accessibilityState: { disabled },
215
+ onPress: () => {
216
+ if (disabled) return;
217
+ onSelect(value);
218
+ },
219
+ style: ({ pressed }) => [
220
+ styles.item,
221
+ pressed && styles.itemPressed,
222
+ disabled && styles.itemDisabled,
223
+ pressed && danger && !disabled && styles.itemDangerPressed,
224
+ itemStyle
225
+ ],
226
+ children: [
227
+ icon,
228
+ /* @__PURE__ */ jsx4(
229
+ Text2,
230
+ {
231
+ numberOfLines: textWrap === "wrap" ? void 0 : 1,
232
+ style: [
233
+ styles.itemText,
234
+ danger && styles.dangerText,
235
+ disabled && styles.itemTextDisabled,
236
+ textStyle
237
+ ],
238
+ children: label
239
+ }
240
+ )
241
+ ]
242
+ }
243
+ );
244
+ }
245
+ DropdownItem.displayName = "DropdownItem";
246
+
247
+ // src/components/Dropdown/Separator/DropdownSeparator.tsx
248
+ import { View as View2 } from "react-native";
249
+
250
+ // src/components/Dropdown/Separator/DropdownSeparator.styles.ts
251
+ import { StyleSheet as StyleSheet4 } from "react-native";
252
+ var createStyles4 = (theme) => StyleSheet4.create({
253
+ separator: {
254
+ height: 1,
255
+ backgroundColor: theme.components.dropdown.separator.bg
256
+ }
257
+ });
258
+
259
+ // src/components/Dropdown/Separator/DropdownSeparator.tsx
260
+ import { jsx as jsx5 } from "react/jsx-runtime";
261
+ function DropdownSeparator() {
262
+ const styles = useThemeStyles(createStyles4);
263
+ return /* @__PURE__ */ jsx5(View2, { style: styles.separator });
264
+ }
265
+ DropdownSeparator.displayName = "DropdownSeparator";
266
+
267
+ // src/components/Dropdown/Trigger/DropdownTrigger.tsx
268
+ import {
269
+ cloneElement,
270
+ isValidElement,
271
+ useEffect,
272
+ useRef,
273
+ useState
274
+ } from "react";
275
+ import { ChevronDown } from "@vellira-ui/icons";
276
+ import { Animated, Pressable as Pressable3, Text as Text3, View as View3 } from "react-native";
277
+
278
+ // src/components/Dropdown/Trigger/DropdownTrigger.styles.ts
279
+ import { StyleSheet as StyleSheet5 } from "react-native";
280
+ var createStyles5 = (theme) => StyleSheet5.create({
281
+ trigger: {
282
+ minWidth: 32,
283
+ minHeight: 32,
284
+ alignItems: "center",
285
+ justifyContent: "space-between",
286
+ flexDirection: "row",
287
+ gap: theme.tokens.spacing[2],
288
+ padding: theme.tokens.spacing[2],
289
+ backgroundColor: theme.components.dropdown.trigger.default.bg,
290
+ borderColor: theme.components.dropdown.trigger.default.border,
291
+ borderRadius: theme.tokens.radius.lg,
292
+ borderWidth: 2
293
+ },
294
+ iconOnly: {
295
+ width: 42,
296
+ height: 42,
297
+ padding: theme.tokens.spacing[2],
298
+ borderRadius: theme.tokens.radius.full
299
+ },
300
+ triggerDisabled: {
301
+ backgroundColor: theme.components.dropdown.trigger.disabled.bg,
302
+ borderColor: theme.components.dropdown.trigger.disabled.border,
303
+ borderStyle: "dashed"
304
+ },
305
+ triggerPressed: {
306
+ backgroundColor: theme.components.dropdown.trigger.hover.bg
307
+ },
308
+ triggerText: {
309
+ color: theme.components.dropdown.trigger.default.fg,
310
+ fontFamily: theme.tokens.typography.family.regular,
311
+ fontSize: theme.tokens.typography.size.md,
312
+ lineHeight: theme.tokens.typography.lineHeight.md
313
+ },
314
+ triggerTextDisabled: {
315
+ color: theme.components.dropdown.trigger.disabled.fg
316
+ },
317
+ icon: {
318
+ alignItems: "center",
319
+ justifyContent: "center"
320
+ },
321
+ arrow: {
322
+ width: 16,
323
+ height: 16,
324
+ alignItems: "center",
325
+ justifyContent: "center"
326
+ }
327
+ });
328
+
329
+ // src/components/Dropdown/Trigger/DropdownTrigger.tsx
330
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
331
+ function DropdownTrigger({
332
+ label,
333
+ trigger,
334
+ icon,
335
+ arrowIcon,
336
+ showArrow = true,
337
+ disabled = false,
338
+ isOpen,
339
+ triggerStyle,
340
+ onPress
341
+ }) {
342
+ const { theme } = useTheme();
343
+ const styles = useThemeStyles(createStyles5);
344
+ const hasIcon = Boolean(icon);
345
+ const isIconOnly = !trigger && hasIcon && !showArrow;
346
+ const [isPressed, setIsPressed] = useState(false);
347
+ const rotateAnim = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
348
+ useEffect(() => {
349
+ Animated.timing(rotateAnim, {
350
+ toValue: isOpen ? 1 : 0,
351
+ duration: 180,
352
+ useNativeDriver: true
353
+ }).start();
354
+ }, [isOpen, rotateAnim]);
355
+ const arrowRotate = rotateAnim.interpolate({
356
+ inputRange: [0, 1],
357
+ outputRange: ["0deg", "180deg"]
358
+ });
359
+ const renderColoredNode = (node, color) => {
360
+ if (typeof node === "string" || typeof node === "number") {
361
+ return /* @__PURE__ */ jsx6(
362
+ Text3,
363
+ {
364
+ numberOfLines: 1,
365
+ style: [
366
+ styles.triggerText,
367
+ { color },
368
+ disabled && styles.triggerTextDisabled
369
+ ],
370
+ children: node
371
+ }
372
+ );
373
+ }
374
+ if (!isValidElement(node)) return node;
375
+ return cloneElement(node, { color });
376
+ };
377
+ const contentColor = disabled ? theme.components.dropdown.trigger.disabled.fg : isPressed ? theme.components.dropdown.trigger.hover.fg : theme.components.dropdown.trigger.default.fg;
378
+ const arrow = arrowIcon ? renderColoredNode(arrowIcon, contentColor) : /* @__PURE__ */ jsx6(ChevronDown, { width: 16, height: 16, color: contentColor });
379
+ const renderedIcon = icon ? renderColoredNode(icon, contentColor) : null;
380
+ return /* @__PURE__ */ jsxs3(
381
+ Pressable3,
382
+ {
383
+ disabled,
384
+ accessibilityRole: "button",
385
+ accessibilityLabel: label,
386
+ accessibilityState: { expanded: isOpen, disabled },
387
+ onPress,
388
+ onPressIn: () => setIsPressed(true),
389
+ onPressOut: () => setIsPressed(false),
390
+ style: [
391
+ styles.trigger,
392
+ isPressed && !disabled && styles.triggerPressed,
393
+ disabled && styles.triggerDisabled,
394
+ isIconOnly && styles.iconOnly,
395
+ triggerStyle
396
+ ],
397
+ children: [
398
+ hasIcon && /* @__PURE__ */ jsx6(View3, { style: styles.icon, children: renderedIcon }),
399
+ !isIconOnly && (trigger ? renderColoredNode(trigger, contentColor) : /* @__PURE__ */ jsx6(
400
+ Text3,
401
+ {
402
+ numberOfLines: 1,
403
+ style: [
404
+ styles.triggerText,
405
+ { color: contentColor },
406
+ disabled && styles.triggerTextDisabled
407
+ ],
408
+ children: label
409
+ }
410
+ )),
411
+ showArrow && /* @__PURE__ */ jsx6(
412
+ Animated.View,
413
+ {
414
+ style: [styles.arrow, { transform: [{ rotate: arrowRotate }] }],
415
+ children: arrow
416
+ }
417
+ )
418
+ ]
419
+ }
420
+ );
421
+ }
422
+ DropdownTrigger.displayName = "DropdownTrigger";
423
+
424
+ // src/components/Dropdown/Dropdown.styles.ts
425
+ import { StyleSheet as StyleSheet6 } from "react-native";
426
+ var createStyles6 = () => StyleSheet6.create({
427
+ root: {
428
+ alignSelf: "flex-start"
429
+ }
430
+ });
431
+
432
+ // src/components/Dropdown/types.ts
433
+ var isGroup = (item) => item.type === "group";
434
+ var isSeparator = (item) => item.type === "separator";
435
+
436
+ // src/components/Dropdown/Dropdown.tsx
437
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
438
+ function Dropdown({
439
+ label = "Menu",
440
+ trigger,
441
+ icon,
442
+ arrowIcon,
443
+ showArrow = true,
444
+ items,
445
+ onSelect,
446
+ disabled = false,
447
+ style,
448
+ triggerStyle,
449
+ itemStyle,
450
+ textStyle
451
+ }) {
452
+ const styles = useThemeStyles(createStyles6);
453
+ const [isOpen, setIsOpen] = useState2(false);
454
+ const close = () => setIsOpen(false);
455
+ const handleSelect = (value) => {
456
+ onSelect?.(value);
457
+ close();
458
+ };
459
+ return /* @__PURE__ */ jsxs4(View4, { style: [styles.root, style], children: [
460
+ /* @__PURE__ */ jsx7(
461
+ DropdownTrigger,
462
+ {
463
+ label,
464
+ trigger,
465
+ icon,
466
+ arrowIcon,
467
+ showArrow,
468
+ disabled,
469
+ isOpen,
470
+ triggerStyle,
471
+ onPress: () => {
472
+ if (disabled) return;
473
+ setIsOpen(true);
474
+ }
475
+ }
476
+ ),
477
+ /* @__PURE__ */ jsx7(DropdownContent, { isOpen, onClose: close, children: items.map((item, index) => {
478
+ if (isGroup(item)) {
479
+ return /* @__PURE__ */ jsx7(
480
+ DropdownGroup,
481
+ {
482
+ label: item.label
483
+ },
484
+ `group-${item.label}-${index}`
485
+ );
486
+ }
487
+ if (isSeparator(item)) {
488
+ return /* @__PURE__ */ jsx7(DropdownSeparator, {}, `separator-${index}`);
489
+ }
490
+ return /* @__PURE__ */ jsx7(
491
+ DropdownItem,
492
+ {
493
+ label: item.label,
494
+ value: item.value,
495
+ icon: item.icon,
496
+ danger: item.danger,
497
+ disabled: item.disabled,
498
+ textWrap: item.textWrap,
499
+ itemStyle,
500
+ textStyle,
501
+ onSelect: handleSelect
502
+ },
503
+ item.value
504
+ );
505
+ }) })
506
+ ] });
507
+ }
508
+ Dropdown.displayName = "Dropdown";
509
+
510
+ // src/components/Modal/Body/ModalBody.tsx
511
+ import { Children, cloneElement as cloneElement2, isValidElement as isValidElement2 } from "react";
512
+ import { Text as Text4, View as View5 } from "react-native";
513
+
514
+ // src/components/Modal/Body/ModalBody.styles.ts
515
+ import { StyleSheet as StyleSheet7 } from "react-native";
516
+ var createStyles7 = (theme) => StyleSheet7.create({
517
+ body: {
518
+ paddingBottom: theme.tokens.spacing[4]
519
+ },
520
+ text: {
521
+ color: theme.components.modal.content.fg,
522
+ fontFamily: theme.tokens.typography.family.regular,
523
+ fontSize: theme.tokens.typography.size.md,
524
+ lineHeight: theme.tokens.typography.lineHeight.md
525
+ }
526
+ });
527
+
528
+ // src/components/Modal/Body/ModalBody.tsx
529
+ import { jsx as jsx8 } from "react/jsx-runtime";
530
+ var isTextElement = (child) => isValidElement2(child) && child.type === Text4;
531
+ var renderBodyChildren = (children, textStyle) => Children.map(children, (child) => {
532
+ if (typeof child === "string" || typeof child === "number") {
533
+ return /* @__PURE__ */ jsx8(Text4, { style: textStyle, children: child });
534
+ }
535
+ if (!isTextElement(child)) {
536
+ return child;
537
+ }
538
+ return cloneElement2(child, {
539
+ style: [textStyle, child.props.style]
540
+ });
541
+ });
542
+ var ModalBody = ({ children, style }) => {
543
+ const styles = useThemeStyles(createStyles7);
544
+ return /* @__PURE__ */ jsx8(View5, { style: [styles.body, style], children: renderBodyChildren(children, styles.text) });
545
+ };
546
+ ModalBody.displayName = "ModalBody";
547
+
548
+ // src/components/Modal/Content/ModalContent.tsx
549
+ import { View as View6 } from "react-native";
550
+
551
+ // src/components/Modal/Content/ModalContent.styles.ts
552
+ import { StyleSheet as StyleSheet8 } from "react-native";
553
+ var createStyles8 = (theme) => StyleSheet8.create({
554
+ content: {
555
+ minWidth: 320,
556
+ maxWidth: 600,
557
+ maxHeight: "90%",
558
+ padding: theme.tokens.spacing[4],
559
+ backgroundColor: theme.components.modal.content.bg,
560
+ borderColor: theme.components.modal.content.border,
561
+ borderRadius: theme.tokens.radius.lg,
562
+ borderWidth: 1,
563
+ gap: theme.tokens.spacing[4],
564
+ shadowColor: theme.tokens.shadows.lg.color,
565
+ shadowOffset: {
566
+ width: theme.tokens.shadows.lg.x,
567
+ height: theme.tokens.shadows.lg.y
568
+ },
569
+ shadowOpacity: theme.tokens.shadows.lg.opacity,
570
+ shadowRadius: theme.tokens.shadows.lg.blur,
571
+ elevation: theme.tokens.shadows.lg.elevation
572
+ }
573
+ });
574
+
575
+ // src/components/Modal/Content/ModalContent.tsx
576
+ import { jsx as jsx9 } from "react/jsx-runtime";
577
+ var ModalContent = ({ children, style }) => {
578
+ const styles = useThemeStyles(createStyles8);
579
+ return /* @__PURE__ */ jsx9(View6, { style: [styles.content, style], children });
580
+ };
581
+ ModalContent.displayName = "ModalContent";
582
+
583
+ // src/components/Modal/Footer/ModalFooter.tsx
584
+ import { View as View7 } from "react-native";
585
+
586
+ // src/components/Modal/Footer/ModalFooter.styles.ts
587
+ import { StyleSheet as StyleSheet9 } from "react-native";
588
+ var createStyles9 = (theme) => StyleSheet9.create({
589
+ footer: {
590
+ flexDirection: "row",
591
+ gap: theme.tokens.spacing[3],
592
+ justifyContent: "flex-end",
593
+ paddingTop: theme.tokens.spacing[4]
594
+ }
595
+ });
596
+
597
+ // src/components/Modal/Footer/ModalFooter.tsx
598
+ import { jsx as jsx10 } from "react/jsx-runtime";
599
+ var ModalFooter = ({ children, style }) => {
600
+ const styles = useThemeStyles(createStyles9);
601
+ return /* @__PURE__ */ jsx10(View7, { style: [styles.footer, style], children });
602
+ };
603
+ ModalFooter.displayName = "ModalFooter";
604
+
605
+ // src/components/Modal/Header/ModalHeader.tsx
606
+ import { Close } from "@vellira-ui/icons";
607
+ import { Pressable as Pressable4, Text as Text5, View as View8 } from "react-native";
608
+
609
+ // src/components/Modal/ModalContext.tsx
610
+ import { createContext as createContext2, useContext as useContext2 } from "react";
611
+ var ModalContext = createContext2(void 0);
612
+ ModalContext.displayName = "ModalContext";
613
+ var useModalContext = () => {
614
+ const context = useContext2(ModalContext);
615
+ if (!context) {
616
+ throw new Error("Modal compound components must be used inside Modal");
617
+ }
618
+ return context;
619
+ };
620
+ var ModalContext_default = ModalContext;
621
+
622
+ // src/components/Modal/Header/ModalHeader.styles.ts
623
+ import { StyleSheet as StyleSheet10 } from "react-native";
624
+ var createStyles10 = (theme) => StyleSheet10.create({
625
+ header: {
626
+ alignItems: "center",
627
+ flexDirection: "row",
628
+ gap: theme.tokens.spacing[3],
629
+ justifyContent: "space-between",
630
+ paddingBottom: theme.tokens.spacing[4],
631
+ backgroundColor: theme.components.modal.content.bg
632
+ },
633
+ title: {
634
+ color: theme.components.modal.title.fg,
635
+ flex: 1,
636
+ fontFamily: theme.tokens.typography.family.semibold,
637
+ fontSize: theme.tokens.typography.size.lg,
638
+ lineHeight: theme.tokens.typography.lineHeight.md
639
+ },
640
+ closeButton: {
641
+ alignItems: "center",
642
+ backgroundColor: theme.components.modal.closeButton.default.bg,
643
+ borderRadius: theme.tokens.radius.full,
644
+ height: 32,
645
+ justifyContent: "center",
646
+ width: 32
647
+ }
648
+ });
649
+
650
+ // src/components/Modal/Header/ModalHeader.tsx
651
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
652
+ var ModalHeader = ({
653
+ children,
654
+ style,
655
+ textStyle
656
+ }) => {
657
+ const { theme } = useTheme();
658
+ const styles = useThemeStyles(createStyles10);
659
+ const { onClose } = useModalContext();
660
+ return /* @__PURE__ */ jsxs5(View8, { style: [styles.header, style], children: [
661
+ /* @__PURE__ */ jsx11(Text5, { style: [styles.title, textStyle], children }),
662
+ onClose && /* @__PURE__ */ jsx11(
663
+ Pressable4,
664
+ {
665
+ accessibilityRole: "button",
666
+ accessibilityLabel: "Close modal",
667
+ onPress: onClose,
668
+ style: styles.closeButton,
669
+ children: /* @__PURE__ */ jsx11(
670
+ Close,
671
+ {
672
+ size: 16,
673
+ color: theme.components.modal.closeButton.default.fg
674
+ }
675
+ )
676
+ }
677
+ )
678
+ ] });
679
+ };
680
+ ModalHeader.displayName = "ModalHeader";
681
+
682
+ // src/components/Modal/ModalOverlay.tsx
683
+ import { Modal as RNModal, Pressable as Pressable5, View as View9 } from "react-native";
684
+
685
+ // src/components/Modal/Modal.styles.ts
686
+ import { StyleSheet as StyleSheet11 } from "react-native";
687
+ var createStyles11 = (theme) => StyleSheet11.create({
688
+ overlay: {
689
+ alignItems: "center",
690
+ flex: 1,
691
+ justifyContent: "center",
692
+ padding: theme.tokens.spacing[5]
693
+ },
694
+ backdrop: {
695
+ ...StyleSheet11.absoluteFill,
696
+ backgroundColor: theme.components.modal.overlay.bg
697
+ }
698
+ });
699
+
700
+ // src/components/Modal/ModalOverlay.tsx
701
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
702
+ var ModalOverlay = ({
703
+ isOpen,
704
+ onClose,
705
+ closeOnBackdrop = true,
706
+ children,
707
+ overlayStyle
708
+ }) => {
709
+ const styles = useThemeStyles(createStyles11);
710
+ return /* @__PURE__ */ jsx12(
711
+ RNModal,
712
+ {
713
+ visible: isOpen,
714
+ transparent: true,
715
+ animationType: "fade",
716
+ onRequestClose: onClose,
717
+ children: /* @__PURE__ */ jsxs6(View9, { style: [styles.overlay, overlayStyle], children: [
718
+ /* @__PURE__ */ jsx12(
719
+ Pressable5,
720
+ {
721
+ testID: "modal-backdrop",
722
+ accessibilityRole: closeOnBackdrop ? "button" : void 0,
723
+ accessibilityLabel: closeOnBackdrop ? "Close modal" : void 0,
724
+ style: styles.backdrop,
725
+ onPress: closeOnBackdrop ? onClose : void 0
726
+ }
727
+ ),
728
+ children
729
+ ] })
730
+ }
731
+ );
732
+ };
733
+
734
+ // src/components/Modal/Modal.tsx
735
+ import { jsx as jsx13 } from "react/jsx-runtime";
736
+ var ModalRoot = ({
737
+ isOpen,
738
+ onClose,
739
+ closeOnBackdrop = true,
740
+ children,
741
+ overlayStyle,
742
+ contentStyle
743
+ }) => {
744
+ return /* @__PURE__ */ jsx13(ModalContext_default.Provider, { value: { onClose }, children: /* @__PURE__ */ jsx13(
745
+ ModalOverlay,
746
+ {
747
+ isOpen,
748
+ onClose,
749
+ closeOnBackdrop,
750
+ overlayStyle,
751
+ children: /* @__PURE__ */ jsx13(ModalContent, { style: contentStyle, children })
752
+ }
753
+ ) });
754
+ };
755
+ ModalRoot.displayName = "Modal";
756
+
757
+ // src/components/Modal/index.ts
758
+ var Modal2 = Object.assign(ModalRoot, {
759
+ Header: ModalHeader,
760
+ Body: ModalBody,
761
+ Footer: ModalFooter,
762
+ Content: ModalContent
763
+ });
764
+
765
+ // src/components/RadioGroup/RadioGroup.tsx
766
+ import { useControllableState as useControllableState2 } from "@vellira-ui/core";
767
+ import { Pressable as Pressable6, Text as Text7, View as View11 } from "react-native";
768
+
769
+ // src/patterns/FormField/FormField.tsx
770
+ import { Text as Text6, View as View10 } from "react-native";
771
+
772
+ // src/patterns/FormField/FormField.styles.ts
773
+ import { StyleSheet as StyleSheet12 } from "react-native";
774
+ var fontWeight = (value) => value;
775
+ var createStyles12 = (theme) => StyleSheet12.create({
776
+ root: {
777
+ width: "100%",
778
+ minWidth: 0,
779
+ alignSelf: "stretch",
780
+ gap: theme.tokens.spacing[2]
781
+ },
782
+ label: {
783
+ color: theme.components.formField.label.fg,
784
+ fontFamily: theme.tokens.typography.family.medium,
785
+ fontSize: theme.tokens.typography.size.md,
786
+ fontWeight: fontWeight(theme.tokens.typography.weight.medium),
787
+ lineHeight: theme.tokens.typography.lineHeight.md
788
+ },
789
+ labelDisabled: {
790
+ color: theme.components.formField.disabled.labelFg
791
+ },
792
+ required: {
793
+ marginLeft: theme.tokens.spacing[1],
794
+ color: theme.components.formField.requiredMark.fg
795
+ },
796
+ description: {
797
+ color: theme.components.formField.description.fg,
798
+ fontFamily: theme.tokens.typography.family.regular,
799
+ fontSize: theme.tokens.typography.size.sm,
800
+ lineHeight: theme.tokens.typography.lineHeight.sm
801
+ },
802
+ descriptionDisabled: {
803
+ color: theme.components.formField.disabled.descriptionFg
804
+ },
805
+ control: {
806
+ width: "100%",
807
+ minWidth: 0,
808
+ alignSelf: "stretch"
809
+ },
810
+ error: {
811
+ color: theme.components.formField.helperText.error.fg,
812
+ fontFamily: theme.tokens.typography.family.regular,
813
+ fontSize: theme.tokens.typography.size.sm,
814
+ lineHeight: theme.tokens.typography.lineHeight.sm
815
+ }
816
+ });
817
+
818
+ // src/patterns/FormField/FormField.tsx
819
+ import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
820
+ function FormField({
821
+ label,
822
+ description,
823
+ error,
824
+ required = false,
825
+ disabled = false,
826
+ children,
827
+ style,
828
+ controlStyle,
829
+ labelStyle,
830
+ descriptionStyle,
831
+ errorStyle
832
+ }) {
833
+ const styles = useThemeStyles(createStyles12);
834
+ return /* @__PURE__ */ jsxs7(
835
+ View10,
836
+ {
837
+ style: [styles.root, style],
838
+ accessibilityState: {
839
+ disabled
840
+ },
841
+ children: [
842
+ label && /* @__PURE__ */ jsxs7(
843
+ Text6,
844
+ {
845
+ style: [styles.label, disabled && styles.labelDisabled, labelStyle],
846
+ children: [
847
+ label,
848
+ required && /* @__PURE__ */ jsx14(Text6, { style: styles.required, children: " *" })
849
+ ]
850
+ }
851
+ ),
852
+ description && /* @__PURE__ */ jsx14(
853
+ Text6,
854
+ {
855
+ style: [
856
+ styles.description,
857
+ disabled && styles.descriptionDisabled,
858
+ descriptionStyle
859
+ ],
860
+ children: description
861
+ }
862
+ ),
863
+ /* @__PURE__ */ jsx14(View10, { style: [styles.control, controlStyle], children }),
864
+ error && /* @__PURE__ */ jsx14(
865
+ Text6,
866
+ {
867
+ style: [styles.error, errorStyle],
868
+ accessibilityRole: "alert",
869
+ accessibilityLiveRegion: "polite",
870
+ children: error
871
+ }
872
+ )
873
+ ]
874
+ }
875
+ );
876
+ }
877
+
878
+ // src/components/RadioGroup/RadioGroup.styles.ts
879
+ import { StyleSheet as StyleSheet13 } from "react-native";
880
+ var createStyles13 = (theme) => StyleSheet13.create({
881
+ group: {
882
+ width: "100%",
883
+ minWidth: 0,
884
+ alignSelf: "stretch",
885
+ gap: theme.tokens.spacing[2]
886
+ },
887
+ horizontal: {
888
+ flexDirection: "row",
889
+ flexWrap: "wrap",
890
+ gap: theme.tokens.spacing[4]
891
+ },
892
+ option: {
893
+ minWidth: 0,
894
+ alignItems: "center",
895
+ flexDirection: "row",
896
+ gap: theme.tokens.spacing[2]
897
+ },
898
+ optionDisabled: {
899
+ opacity: 1
900
+ },
901
+ radio: {
902
+ width: 16,
903
+ height: 16,
904
+ alignItems: "center",
905
+ justifyContent: "center",
906
+ borderColor: theme.components.radio.default.border,
907
+ borderRadius: 999,
908
+ borderWidth: 1,
909
+ backgroundColor: theme.components.radio.default.bg
910
+ },
911
+ radioSelected: {
912
+ borderColor: theme.components.radio.checked.default.border
913
+ },
914
+ radioDisabled: {
915
+ borderColor: theme.components.radio.disabled.border,
916
+ backgroundColor: theme.components.radio.disabled.bg
917
+ },
918
+ dot: {
919
+ width: 12,
920
+ height: 12,
921
+ backgroundColor: theme.components.radio.checked.default.bg,
922
+ borderRadius: 999
923
+ },
924
+ dotDisabled: {
925
+ backgroundColor: theme.components.radio.disabled.fg
926
+ },
927
+ label: {
928
+ minWidth: 0,
929
+ color: theme.components.radio.default.fg,
930
+ fontFamily: theme.tokens.typography.family.regular,
931
+ fontSize: theme.tokens.typography.size.md,
932
+ lineHeight: theme.tokens.typography.lineHeight.md
933
+ },
934
+ labelDisabled: {
935
+ color: theme.components.radio.disabled.fg
936
+ },
937
+ labelSelected: {
938
+ color: theme.components.radio.checked.default.fg
939
+ }
940
+ });
941
+
942
+ // src/components/RadioGroup/RadioGroup.tsx
943
+ import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
944
+ function RadioGroup({
945
+ label,
946
+ description,
947
+ value,
948
+ defaultValue = "",
949
+ onChange,
950
+ options,
951
+ required = false,
952
+ disabled = false,
953
+ error,
954
+ orientation = "vertical",
955
+ style,
956
+ optionStyle,
957
+ labelStyle
958
+ }) {
959
+ const styles = useThemeStyles(createStyles13);
960
+ const [selectedValue, setSelectedValue] = useControllableState2({
961
+ value,
962
+ defaultValue,
963
+ onChange
964
+ });
965
+ return /* @__PURE__ */ jsx15(
966
+ FormField,
967
+ {
968
+ label,
969
+ description,
970
+ error,
971
+ required,
972
+ disabled,
973
+ children: /* @__PURE__ */ jsx15(
974
+ View11,
975
+ {
976
+ accessibilityRole: "radiogroup",
977
+ accessibilityState: { disabled },
978
+ style: [
979
+ styles.group,
980
+ orientation === "horizontal" && styles.horizontal,
981
+ style
982
+ ],
983
+ children: options.map((option) => {
984
+ const isSelected = selectedValue === option.value;
985
+ const isDisabled = disabled || !!option.disabled;
986
+ return /* @__PURE__ */ jsxs8(
987
+ Pressable6,
988
+ {
989
+ disabled: isDisabled,
990
+ accessibilityRole: "radio",
991
+ accessibilityLabel: option.label,
992
+ accessibilityState: {
993
+ checked: isSelected,
994
+ disabled: isDisabled
995
+ },
996
+ onPress: () => {
997
+ if (isDisabled) return;
998
+ setSelectedValue(option.value);
999
+ },
1000
+ style: [
1001
+ styles.option,
1002
+ isDisabled && styles.optionDisabled,
1003
+ optionStyle
1004
+ ],
1005
+ children: [
1006
+ /* @__PURE__ */ jsx15(
1007
+ View11,
1008
+ {
1009
+ style: [
1010
+ styles.radio,
1011
+ isSelected && styles.radioSelected,
1012
+ isDisabled && styles.radioDisabled
1013
+ ],
1014
+ children: isSelected && /* @__PURE__ */ jsx15(
1015
+ View11,
1016
+ {
1017
+ style: [styles.dot, isDisabled && styles.dotDisabled]
1018
+ }
1019
+ )
1020
+ }
1021
+ ),
1022
+ /* @__PURE__ */ jsx15(
1023
+ Text7,
1024
+ {
1025
+ style: [
1026
+ styles.label,
1027
+ isSelected && styles.labelSelected,
1028
+ isDisabled && styles.labelDisabled,
1029
+ labelStyle
1030
+ ],
1031
+ children: option.label
1032
+ }
1033
+ )
1034
+ ]
1035
+ },
1036
+ option.value
1037
+ );
1038
+ })
1039
+ }
1040
+ )
1041
+ }
1042
+ );
1043
+ }
1044
+ RadioGroup.displayName = "RadioGroup";
1045
+
1046
+ // src/components/Select/Select.tsx
1047
+ import { useState as useState3 } from "react";
1048
+ import { Picker } from "@react-native-picker/picker";
1049
+ import { useControllableState as useControllableState3 } from "@vellira-ui/core";
1050
+ import { Modal as Modal3, Pressable as Pressable8, Text as Text9, View as View13 } from "react-native";
1051
+
1052
+ // src/components/Select/SelectTrigger/SelectTrigger.tsx
1053
+ import { ChevronDown as ChevronDown2 } from "@vellira-ui/icons";
1054
+ import { Pressable as Pressable7, Text as Text8, View as View12 } from "react-native";
1055
+
1056
+ // src/components/Select/SelectTrigger/SelectTrigger.styles.ts
1057
+ import { StyleSheet as StyleSheet14 } from "react-native";
1058
+ var createStyles14 = (theme) => StyleSheet14.create({
1059
+ trigger: {
1060
+ width: "100%",
1061
+ minWidth: 0,
1062
+ alignItems: "center",
1063
+ justifyContent: "space-between",
1064
+ flexDirection: "row",
1065
+ paddingHorizontal: theme.tokens.spacing[4],
1066
+ paddingVertical: theme.tokens.spacing[3],
1067
+ backgroundColor: theme.components.select.trigger.default.bg,
1068
+ borderColor: theme.components.select.trigger.default.border,
1069
+ borderRadius: theme.tokens.radius.md,
1070
+ borderWidth: 1
1071
+ },
1072
+ triggerOpen: {
1073
+ borderColor: theme.components.select.trigger.focus.border
1074
+ },
1075
+ triggerError: {
1076
+ borderColor: theme.components.select.trigger.error.border
1077
+ },
1078
+ triggerDisabled: {
1079
+ backgroundColor: theme.components.select.trigger.disabled.bg,
1080
+ borderColor: theme.components.select.trigger.disabled.border
1081
+ },
1082
+ text: {
1083
+ flex: 1,
1084
+ minWidth: 0,
1085
+ color: theme.components.select.trigger.default.fg,
1086
+ fontFamily: theme.tokens.typography.family.regular,
1087
+ fontSize: theme.tokens.typography.size.md,
1088
+ lineHeight: theme.tokens.typography.lineHeight.md
1089
+ },
1090
+ textDisabled: {
1091
+ color: theme.components.select.trigger.disabled.fg
1092
+ },
1093
+ placeholder: {
1094
+ color: theme.components.select.trigger.placeholder.fg
1095
+ },
1096
+ icon: {
1097
+ width: 16,
1098
+ height: 16,
1099
+ marginLeft: theme.tokens.spacing[2],
1100
+ alignItems: "center",
1101
+ justifyContent: "center"
1102
+ },
1103
+ iconOpen: {
1104
+ transform: [{ rotate: "180deg" }]
1105
+ }
1106
+ });
1107
+
1108
+ // src/components/Select/SelectTrigger/SelectTrigger.tsx
1109
+ import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
1110
+ function SelectTrigger({
1111
+ displayText,
1112
+ isPlaceholder,
1113
+ isOpen,
1114
+ disabled = false,
1115
+ hasError = false,
1116
+ accessibilityLabel,
1117
+ triggerStyle,
1118
+ textStyle,
1119
+ onPress
1120
+ }) {
1121
+ const { theme } = useTheme();
1122
+ const styles = useThemeStyles(createStyles14);
1123
+ return /* @__PURE__ */ jsxs9(
1124
+ Pressable7,
1125
+ {
1126
+ disabled,
1127
+ accessibilityRole: "button",
1128
+ accessibilityLabel,
1129
+ accessibilityHint: "Opens a picker",
1130
+ accessibilityState: {
1131
+ expanded: isOpen,
1132
+ disabled
1133
+ },
1134
+ onPress,
1135
+ style: [
1136
+ styles.trigger,
1137
+ isOpen && styles.triggerOpen,
1138
+ hasError && styles.triggerError,
1139
+ disabled && styles.triggerDisabled,
1140
+ triggerStyle
1141
+ ],
1142
+ children: [
1143
+ /* @__PURE__ */ jsx16(
1144
+ Text8,
1145
+ {
1146
+ numberOfLines: 1,
1147
+ style: [
1148
+ styles.text,
1149
+ isPlaceholder && styles.placeholder,
1150
+ disabled && styles.textDisabled,
1151
+ textStyle
1152
+ ],
1153
+ children: displayText
1154
+ }
1155
+ ),
1156
+ /* @__PURE__ */ jsx16(
1157
+ View12,
1158
+ {
1159
+ style: [styles.icon, isOpen && styles.iconOpen],
1160
+ accessibilityElementsHidden: true,
1161
+ importantForAccessibility: "no",
1162
+ children: /* @__PURE__ */ jsx16(
1163
+ ChevronDown2,
1164
+ {
1165
+ width: 16,
1166
+ height: 16,
1167
+ color: disabled ? theme.components.select.trigger.disabled.fg : theme.components.select.trigger.placeholder.fg
1168
+ }
1169
+ )
1170
+ }
1171
+ )
1172
+ ]
1173
+ }
1174
+ );
1175
+ }
1176
+ SelectTrigger.displayName = "SelectTrigger";
1177
+
1178
+ // src/components/Select/Select.styles.ts
1179
+ import { overlay as overlay2 } from "@vellira-ui/tokens";
1180
+ import { StyleSheet as StyleSheet15 } from "react-native";
1181
+ var createStyles15 = (theme) => StyleSheet15.create({
1182
+ modalRoot: {
1183
+ flex: 1,
1184
+ justifyContent: "flex-end"
1185
+ },
1186
+ backdrop: {
1187
+ ...StyleSheet15.absoluteFill,
1188
+ backgroundColor: overlay2.backdrop
1189
+ },
1190
+ sheet: {
1191
+ maxHeight: "50%",
1192
+ backgroundColor: theme.components.select.dropdown.bg,
1193
+ borderColor: theme.components.select.dropdown.border,
1194
+ borderTopLeftRadius: theme.tokens.radius.lg,
1195
+ borderTopRightRadius: theme.tokens.radius.lg,
1196
+ borderWidth: 1,
1197
+ overflow: "hidden"
1198
+ },
1199
+ toolbar: {
1200
+ minHeight: 48,
1201
+ alignItems: "center",
1202
+ justifyContent: "space-between",
1203
+ flexDirection: "row",
1204
+ paddingHorizontal: theme.tokens.spacing[4],
1205
+ borderBottomColor: theme.components.select.dropdown.border,
1206
+ borderBottomWidth: 1
1207
+ },
1208
+ title: {
1209
+ color: theme.components.select.dropdown.fg,
1210
+ fontFamily: theme.tokens.typography.family.medium,
1211
+ fontSize: theme.tokens.typography.size.md
1212
+ },
1213
+ cancelText: {
1214
+ color: theme.components.select.trigger.placeholder.fg,
1215
+ fontFamily: theme.tokens.typography.family.medium,
1216
+ fontSize: theme.tokens.typography.size.md
1217
+ },
1218
+ doneText: {
1219
+ color: theme.components.select.option.selected.bg,
1220
+ fontFamily: theme.tokens.typography.family.medium,
1221
+ fontSize: theme.tokens.typography.size.md
1222
+ },
1223
+ picker: {
1224
+ width: "100%"
1225
+ }
1226
+ });
1227
+
1228
+ // src/components/Select/Select.tsx
1229
+ import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
1230
+ function Select({
1231
+ label,
1232
+ description,
1233
+ value,
1234
+ defaultValue,
1235
+ onChange,
1236
+ options,
1237
+ placeholder = "Select...",
1238
+ required = false,
1239
+ disabled = false,
1240
+ error,
1241
+ style,
1242
+ triggerStyle,
1243
+ textStyle,
1244
+ pickerStyle,
1245
+ accessibilityLabel
1246
+ }) {
1247
+ const { theme } = useTheme();
1248
+ const styles = useThemeStyles(createStyles15);
1249
+ const [isOpen, setIsOpen] = useState3(false);
1250
+ const [selectedValue, setSelectedValue] = useControllableState3({
1251
+ value,
1252
+ defaultValue: defaultValue ?? "",
1253
+ onChange
1254
+ });
1255
+ const [draftValue, setDraftValue] = useState3(selectedValue);
1256
+ const selectedOption = options.find((o) => o.value === selectedValue);
1257
+ const resolvedLabel = accessibilityLabel ?? label ?? selectedOption?.label ?? placeholder ?? "Select";
1258
+ const openPicker = () => {
1259
+ if (disabled) return;
1260
+ setDraftValue(selectedValue);
1261
+ setIsOpen(true);
1262
+ };
1263
+ const closePicker = () => {
1264
+ setIsOpen(false);
1265
+ };
1266
+ const handleValueChange = (nextValue) => {
1267
+ const nextOption = options.find((option) => option.value === nextValue);
1268
+ if (!nextOption || nextOption.disabled) {
1269
+ return;
1270
+ }
1271
+ setDraftValue(nextValue);
1272
+ };
1273
+ const confirmPicker = () => {
1274
+ setSelectedValue(draftValue);
1275
+ setIsOpen(false);
1276
+ };
1277
+ return /* @__PURE__ */ jsxs10(
1278
+ FormField,
1279
+ {
1280
+ label,
1281
+ description,
1282
+ error,
1283
+ required,
1284
+ disabled,
1285
+ style,
1286
+ children: [
1287
+ /* @__PURE__ */ jsx17(
1288
+ SelectTrigger,
1289
+ {
1290
+ displayText: selectedOption?.label ?? placeholder,
1291
+ isPlaceholder: !selectedOption,
1292
+ isOpen,
1293
+ disabled,
1294
+ hasError: !!error,
1295
+ accessibilityLabel: resolvedLabel,
1296
+ triggerStyle,
1297
+ textStyle,
1298
+ onPress: openPicker
1299
+ }
1300
+ ),
1301
+ /* @__PURE__ */ jsx17(
1302
+ Modal3,
1303
+ {
1304
+ transparent: true,
1305
+ visible: isOpen,
1306
+ animationType: "slide",
1307
+ onRequestClose: closePicker,
1308
+ children: /* @__PURE__ */ jsxs10(View13, { style: styles.modalRoot, children: [
1309
+ /* @__PURE__ */ jsx17(Pressable8, { style: styles.backdrop, onPress: closePicker }),
1310
+ /* @__PURE__ */ jsxs10(View13, { style: styles.sheet, children: [
1311
+ /* @__PURE__ */ jsxs10(View13, { style: styles.toolbar, children: [
1312
+ /* @__PURE__ */ jsx17(Pressable8, { onPress: closePicker, hitSlop: 8, children: /* @__PURE__ */ jsx17(Text9, { style: styles.cancelText, children: "Cancel" }) }),
1313
+ /* @__PURE__ */ jsx17(Text9, { style: styles.title, children: resolvedLabel }),
1314
+ /* @__PURE__ */ jsx17(Pressable8, { onPress: confirmPicker, hitSlop: 8, children: /* @__PURE__ */ jsx17(Text9, { style: styles.doneText, children: "Done" }) })
1315
+ ] }),
1316
+ /* @__PURE__ */ jsxs10(
1317
+ Picker,
1318
+ {
1319
+ selectedValue: draftValue,
1320
+ onValueChange: handleValueChange,
1321
+ enabled: !disabled,
1322
+ style: [styles.picker, pickerStyle],
1323
+ itemStyle: {
1324
+ color: theme.components.select.option.default.fg
1325
+ },
1326
+ children: [
1327
+ /* @__PURE__ */ jsx17(
1328
+ Picker.Item,
1329
+ {
1330
+ label: placeholder,
1331
+ value: "",
1332
+ enabled: false,
1333
+ color: theme.components.select.option.disabled.fg
1334
+ }
1335
+ ),
1336
+ options.map((option) => /* @__PURE__ */ jsx17(
1337
+ Picker.Item,
1338
+ {
1339
+ label: option.disabled ? `${option.label} - unavailable` : option.label,
1340
+ value: option.value,
1341
+ enabled: !option.disabled,
1342
+ color: option.disabled ? theme.components.select.option.disabled.fg : theme.components.select.option.default.fg
1343
+ },
1344
+ option.value
1345
+ ))
1346
+ ]
1347
+ }
1348
+ )
1349
+ ] })
1350
+ ] })
1351
+ }
1352
+ )
1353
+ ]
1354
+ }
1355
+ );
1356
+ }
1357
+ Select.displayName = "Select";
1358
+
1359
+ // src/components/Tabs/List/TabsList.tsx
1360
+ import { View as View14 } from "react-native";
1361
+
1362
+ // src/components/Tabs/TabsContext.tsx
1363
+ import { createContext as createContext3, useContext as useContext3 } from "react";
1364
+ var TabsContext = createContext3(null);
1365
+ var TabsProvider = TabsContext.Provider;
1366
+ var useTabs = () => {
1367
+ const context = useContext3(TabsContext);
1368
+ if (!context) {
1369
+ throw new Error("Tabs components must be used inside <Tabs>.");
1370
+ }
1371
+ return context;
1372
+ };
1373
+ TabsContext.displayName = "TabsContext";
1374
+
1375
+ // src/components/Tabs/List/TabsList.styles.ts
1376
+ import { StyleSheet as StyleSheet16 } from "react-native";
1377
+ var createStyles16 = (theme) => StyleSheet16.create({
1378
+ list: {
1379
+ flexDirection: "row",
1380
+ alignSelf: "stretch",
1381
+ width: "100%",
1382
+ gap: theme.tokens.spacing[5],
1383
+ marginBottom: theme.tokens.spacing[4]
1384
+ },
1385
+ listPills: {
1386
+ backgroundColor: theme.components.tabs.pills.default.bg,
1387
+ padding: 0
1388
+ },
1389
+ listVertical: {
1390
+ alignSelf: "flex-start",
1391
+ flexDirection: "column",
1392
+ gap: theme.tokens.spacing[1],
1393
+ marginRight: theme.tokens.spacing[4],
1394
+ marginBottom: 0
1395
+ }
1396
+ });
1397
+
1398
+ // src/components/Tabs/List/TabsList.tsx
1399
+ import { jsx as jsx18 } from "react/jsx-runtime";
1400
+ var TabsList = ({ children, style }) => {
1401
+ const styles = useThemeStyles(createStyles16);
1402
+ const { orientation, appearance } = useTabs();
1403
+ return /* @__PURE__ */ jsx18(
1404
+ View14,
1405
+ {
1406
+ accessibilityRole: "tablist",
1407
+ style: [
1408
+ styles.list,
1409
+ appearance === "pills" && styles.listPills,
1410
+ orientation === "vertical" && styles.listVertical,
1411
+ style
1412
+ ],
1413
+ children
1414
+ }
1415
+ );
1416
+ };
1417
+ TabsList.displayName = "TabsList";
1418
+
1419
+ // src/components/Tabs/Panel/TabsPanel.tsx
1420
+ import { View as View15 } from "react-native";
1421
+
1422
+ // src/components/Tabs/Panel/TabsPanel.styles.ts
1423
+ import { StyleSheet as StyleSheet17 } from "react-native";
1424
+ var createStyles17 = (theme) => StyleSheet17.create({
1425
+ panel: {
1426
+ flex: 1,
1427
+ minWidth: 0,
1428
+ padding: theme.tokens.spacing[4]
1429
+ }
1430
+ });
1431
+
1432
+ // src/components/Tabs/Panel/TabsPanel.tsx
1433
+ import { jsx as jsx19 } from "react/jsx-runtime";
1434
+ var TabsPanel = ({ index, children, style }) => {
1435
+ const styles = useThemeStyles(createStyles17);
1436
+ const { activeIndex } = useTabs();
1437
+ if (activeIndex !== index) return null;
1438
+ return /* @__PURE__ */ jsx19(View15, { style: [styles.panel, style], children });
1439
+ };
1440
+ TabsPanel.displayName = "TabsPanel";
1441
+
1442
+ // src/components/Tabs/Tab/Tab.tsx
1443
+ import { cloneElement as cloneElement3, isValidElement as isValidElement3 } from "react";
1444
+ import { Pressable as Pressable9, Text as Text10, View as View16 } from "react-native";
1445
+
1446
+ // src/components/Tabs/Tab/Tab.styles.ts
1447
+ import { StyleSheet as StyleSheet18 } from "react-native";
1448
+ var fontWeight2 = (value) => value;
1449
+ var createStyles18 = (theme) => StyleSheet18.create({
1450
+ tab: {
1451
+ alignItems: "center",
1452
+ flexDirection: "row",
1453
+ gap: theme.tokens.spacing[2],
1454
+ justifyContent: "center",
1455
+ paddingHorizontal: theme.tokens.spacing[4],
1456
+ paddingVertical: theme.tokens.spacing[2],
1457
+ backgroundColor: theme.components.tabs.trigger.default.bg,
1458
+ borderWidth: 0
1459
+ },
1460
+ tabVertical: {
1461
+ flex: 0,
1462
+ width: "100%",
1463
+ justifyContent: "flex-start"
1464
+ },
1465
+ tabActive: {
1466
+ backgroundColor: theme.components.tabs.trigger.active.bg
1467
+ },
1468
+ tabUnderline: {
1469
+ borderBottomColor: "transparent",
1470
+ borderBottomWidth: 3,
1471
+ borderRadius: 0
1472
+ },
1473
+ tabUnderlineActive: {
1474
+ borderBottomColor: theme.components.tabs.indicator.active.bg
1475
+ },
1476
+ tabDisabled: {
1477
+ backgroundColor: theme.components.tabs.trigger.disabled.bg
1478
+ },
1479
+ tabText: {
1480
+ color: theme.components.tabs.trigger.default.fg,
1481
+ fontFamily: theme.tokens.typography.family.regular,
1482
+ fontSize: theme.tokens.typography.size.md,
1483
+ fontWeight: fontWeight2(theme.tokens.typography.weight.regular)
1484
+ },
1485
+ tabTextDisabled: {
1486
+ color: theme.components.tabs.trigger.disabled.fg
1487
+ },
1488
+ tabTextActive: {
1489
+ color: theme.components.tabs.trigger.active.fg
1490
+ },
1491
+ tabTextPillsActive: {
1492
+ color: theme.components.tabs.pills.active.fg
1493
+ },
1494
+ tabPillsActive: {
1495
+ backgroundColor: theme.components.tabs.pills.active.bg,
1496
+ borderRadius: theme.tokens.radius.md
1497
+ },
1498
+ tabDefaultActive: {
1499
+ backgroundColor: theme.components.tabs.trigger.active.bg
1500
+ },
1501
+ tabIcon: {
1502
+ alignItems: "center",
1503
+ justifyContent: "center"
1504
+ },
1505
+ tabIconActive: {
1506
+ color: theme.components.tabs.trigger.active.fg
1507
+ },
1508
+ tabIconPillsActive: {
1509
+ color: theme.components.tabs.pills.active.fg
1510
+ }
1511
+ });
1512
+
1513
+ // src/components/Tabs/Tab/Tab.tsx
1514
+ import { jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
1515
+ var Tab = ({
1516
+ index,
1517
+ children,
1518
+ icon,
1519
+ disabled,
1520
+ style,
1521
+ textStyle
1522
+ }) => {
1523
+ const { theme } = useTheme();
1524
+ const styles = useThemeStyles(createStyles18);
1525
+ const { activeIndex, appearance, orientation, setActiveIndex } = useTabs();
1526
+ const isActive = activeIndex === index;
1527
+ const isPills = appearance === "pills";
1528
+ const isUnderline = appearance === "underline";
1529
+ const isDefault = appearance === "default";
1530
+ const iconColor = isPills && isActive ? theme.components.tabs.pills.active.fg : isActive ? theme.components.tabs.trigger.active.fg : theme.components.tabs.trigger.default.fg;
1531
+ const renderedIcon = isValidElement3(icon) ? cloneElement3(icon, {
1532
+ color: iconColor
1533
+ }) : icon;
1534
+ return /* @__PURE__ */ jsxs11(
1535
+ Pressable9,
1536
+ {
1537
+ disabled,
1538
+ accessibilityRole: "tab",
1539
+ accessibilityState: { selected: isActive, disabled },
1540
+ onPress: () => setActiveIndex(index),
1541
+ style: [
1542
+ styles.tab,
1543
+ orientation === "vertical" && styles.tabVertical,
1544
+ isPills && isActive && styles.tabPillsActive,
1545
+ isUnderline && styles.tabUnderline,
1546
+ isUnderline && isActive && styles.tabUnderlineActive,
1547
+ isDefault && isActive && styles.tabDefaultActive,
1548
+ disabled && styles.tabDisabled,
1549
+ style
1550
+ ],
1551
+ children: [
1552
+ icon != null && /* @__PURE__ */ jsx20(View16, { style: styles.tabIcon, children: renderedIcon }),
1553
+ children != null && /* @__PURE__ */ jsx20(
1554
+ Text10,
1555
+ {
1556
+ style: [
1557
+ styles.tabText,
1558
+ isPills && isActive && styles.tabTextPillsActive,
1559
+ !isPills && isActive && styles.tabTextActive,
1560
+ disabled && styles.tabTextDisabled,
1561
+ textStyle
1562
+ ],
1563
+ children
1564
+ }
1565
+ )
1566
+ ]
1567
+ }
1568
+ );
1569
+ };
1570
+ Tab.displayName = "Tab";
1571
+
1572
+ // src/components/Tabs/Tabs.tsx
1573
+ import { useCallback, useMemo as useMemo3, useState as useState4 } from "react";
1574
+ import { View as View17 } from "react-native";
1575
+
1576
+ // src/components/Tabs/Tabs.styles.ts
1577
+ import { StyleSheet as StyleSheet19 } from "react-native";
1578
+ var createStyles19 = (theme) => StyleSheet19.create({
1579
+ root: {
1580
+ width: "100%"
1581
+ },
1582
+ rootVertical: {
1583
+ flexDirection: "row",
1584
+ alignItems: "flex-start",
1585
+ gap: theme.tokens.spacing[6]
1586
+ },
1587
+ listVertical: {
1588
+ flexDirection: "column",
1589
+ width: 140,
1590
+ flexShrink: 0
1591
+ },
1592
+ tabVertical: {
1593
+ flex: 0,
1594
+ width: "100%",
1595
+ justifyContent: "flex-start"
1596
+ },
1597
+ panel: {
1598
+ flex: 1,
1599
+ flexShrink: 1,
1600
+ minWidth: 0
1601
+ }
1602
+ });
1603
+
1604
+ // src/components/Tabs/Tabs.tsx
1605
+ import { jsx as jsx21 } from "react/jsx-runtime";
1606
+ var TabsRoot = ({
1607
+ children,
1608
+ activeIndex: controlledActiveIndex,
1609
+ defaultActiveIndex = 0,
1610
+ onChange,
1611
+ orientation = "horizontal",
1612
+ appearance = "pills",
1613
+ style
1614
+ }) => {
1615
+ const styles = useThemeStyles(createStyles19);
1616
+ const [uncontrolledActiveIndex, setUncontrolledActiveIndex] = useState4(defaultActiveIndex);
1617
+ const isControlled = controlledActiveIndex !== void 0;
1618
+ const activeIndex = isControlled ? controlledActiveIndex : uncontrolledActiveIndex;
1619
+ const setActiveIndex = useCallback(
1620
+ (nextIndex) => {
1621
+ if (!isControlled) {
1622
+ setUncontrolledActiveIndex(nextIndex);
1623
+ }
1624
+ onChange?.(nextIndex);
1625
+ },
1626
+ [isControlled, onChange]
1627
+ );
1628
+ const value = useMemo3(
1629
+ () => ({ activeIndex, appearance, orientation, setActiveIndex }),
1630
+ [activeIndex, appearance, orientation, setActiveIndex]
1631
+ );
1632
+ return /* @__PURE__ */ jsx21(TabsProvider, { value, children: /* @__PURE__ */ jsx21(
1633
+ View17,
1634
+ {
1635
+ style: [
1636
+ styles.root,
1637
+ orientation === "vertical" && styles.rootVertical,
1638
+ style
1639
+ ],
1640
+ children
1641
+ }
1642
+ ) });
1643
+ };
1644
+ TabsRoot.displayName = "TabsRoot";
1645
+
1646
+ // src/components/Tabs/index.ts
1647
+ var Tabs = Object.assign(TabsRoot, {
1648
+ List: TabsList,
1649
+ Tab,
1650
+ Panel: TabsPanel
1651
+ });
1652
+
1653
+ // src/components/Tooltip/Tooltip.tsx
1654
+ import { useEffect as useEffect2, useRef as useRef3, useState as useState6 } from "react";
1655
+ import { Modal as Modal4, Pressable as Pressable10, Text as Text11, View as View18 } from "react-native";
1656
+
1657
+ // src/hooks/useNativeFloatingPosition.ts
1658
+ import { useCallback as useCallback2, useRef as useRef2, useState as useState5 } from "react";
1659
+ import { Dimensions } from "react-native";
1660
+ var safePadding = 12;
1661
+ function useNativeFloatingPosition(placement = "top", offset = 8) {
1662
+ const [position, setPosition] = useState5({ top: 0, left: 0 });
1663
+ const floatingSizeRef = useRef2({
1664
+ width: 0,
1665
+ height: 0
1666
+ });
1667
+ const lastTriggerRef = useRef2(null);
1668
+ const clamp = useCallback2((value, min, max) => {
1669
+ return Math.min(Math.max(value, min), Math.max(min, max));
1670
+ }, []);
1671
+ const calculatePosition = useCallback2(
1672
+ (triggerRect, size) => {
1673
+ const { width: screenWidth, height: screenHeight } = Dimensions.get("window");
1674
+ const [side, align = "center"] = placement.split("-");
1675
+ const horizontalTop = side === "bottom" ? triggerRect.y + triggerRect.height + offset : triggerRect.y - size.height - offset;
1676
+ const verticalTop = align === "start" ? triggerRect.y : align === "end" ? triggerRect.y + triggerRect.height - size.height : triggerRect.y + triggerRect.height / 2 - size.height / 2;
1677
+ const horizontalLeft = align === "start" ? triggerRect.x : align === "end" ? triggerRect.x + triggerRect.width - size.width : triggerRect.x + triggerRect.width / 2 - size.width / 2;
1678
+ const verticalLeft = side === "right" ? triggerRect.x + triggerRect.width + offset : triggerRect.x - size.width - offset;
1679
+ const rawPosition = side === "left" || side === "right" ? { top: verticalTop, left: verticalLeft } : { top: horizontalTop, left: horizontalLeft };
1680
+ return {
1681
+ top: clamp(
1682
+ rawPosition.top,
1683
+ safePadding,
1684
+ screenHeight - size.height - safePadding
1685
+ ),
1686
+ left: clamp(
1687
+ rawPosition.left,
1688
+ safePadding,
1689
+ screenWidth - size.width - safePadding
1690
+ )
1691
+ };
1692
+ },
1693
+ [placement, offset, clamp]
1694
+ );
1695
+ const updatePosition = useCallback2(
1696
+ (triggerRef, measuredSize = floatingSizeRef.current) => {
1697
+ lastTriggerRef.current = triggerRef;
1698
+ const node = triggerRef.current;
1699
+ if (!node || typeof node.measureInWindow !== "function") {
1700
+ setPosition({ top: 0, left: 0 });
1701
+ return;
1702
+ }
1703
+ node.measureInWindow((x, y, width, height) => {
1704
+ setPosition(calculatePosition({ x, y, width, height }, measuredSize));
1705
+ });
1706
+ },
1707
+ [calculatePosition]
1708
+ );
1709
+ const onFloatingLayout = useCallback2(
1710
+ (event) => {
1711
+ const { width, height } = event.nativeEvent.layout;
1712
+ const nextSize = { width, height };
1713
+ floatingSizeRef.current = nextSize;
1714
+ if (lastTriggerRef.current) {
1715
+ updatePosition(lastTriggerRef.current, nextSize);
1716
+ }
1717
+ },
1718
+ [updatePosition]
1719
+ );
1720
+ return { position, updatePosition, onFloatingLayout };
1721
+ }
1722
+
1723
+ // src/components/Tooltip/Tooltip.styles.ts
1724
+ import { StyleSheet as StyleSheet20 } from "react-native";
1725
+ var createStyles20 = (theme) => StyleSheet20.create({
1726
+ root: {
1727
+ alignSelf: "flex-start"
1728
+ },
1729
+ overlay: {
1730
+ ...StyleSheet20.absoluteFill
1731
+ },
1732
+ bubble: {
1733
+ position: "absolute",
1734
+ zIndex: 1e3,
1735
+ backgroundColor: theme.components.tooltip.content.bg,
1736
+ borderColor: theme.components.tooltip.content.border,
1737
+ borderRadius: theme.tokens.radius.sm,
1738
+ borderWidth: 1,
1739
+ paddingHorizontal: theme.tokens.spacing[3],
1740
+ paddingVertical: theme.tokens.spacing[2],
1741
+ shadowColor: theme.tokens.shadows.sm.color,
1742
+ shadowOffset: {
1743
+ width: theme.tokens.shadows.sm.x,
1744
+ height: theme.tokens.shadows.sm.y
1745
+ },
1746
+ shadowOpacity: theme.tokens.shadows.sm.opacity,
1747
+ shadowRadius: theme.tokens.shadows.sm.blur,
1748
+ elevation: theme.tokens.shadows.sm.elevation
1749
+ },
1750
+ text: {
1751
+ color: theme.components.tooltip.content.fg,
1752
+ fontFamily: theme.tokens.typography.family.regular,
1753
+ fontSize: theme.tokens.typography.size.sm,
1754
+ lineHeight: 20,
1755
+ textAlign: "center"
1756
+ }
1757
+ });
1758
+
1759
+ // src/components/Tooltip/Tooltip.tsx
1760
+ import { jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
1761
+ function Tooltip({
1762
+ children,
1763
+ content,
1764
+ placement = "top",
1765
+ disabled = false,
1766
+ maxWidth = 240,
1767
+ delay,
1768
+ style,
1769
+ contentStyle,
1770
+ textStyle
1771
+ }) {
1772
+ const styles = useThemeStyles(createStyles20);
1773
+ const [visible, setVisible] = useState6(false);
1774
+ const triggerRef = useRef3(null);
1775
+ const closeTimerRef = useRef3(null);
1776
+ const { position, updatePosition, onFloatingLayout } = useNativeFloatingPosition(placement, 8);
1777
+ const hideDelay = delay?.close ?? 2500;
1778
+ const clearCloseTimer = () => {
1779
+ if (closeTimerRef.current) {
1780
+ clearTimeout(closeTimerRef.current);
1781
+ closeTimerRef.current = null;
1782
+ }
1783
+ };
1784
+ const showTooltip = () => {
1785
+ if (disabled) return;
1786
+ clearCloseTimer();
1787
+ updatePosition(triggerRef);
1788
+ setVisible(true);
1789
+ closeTimerRef.current = setTimeout(() => {
1790
+ setVisible(false);
1791
+ closeTimerRef.current = null;
1792
+ }, hideDelay);
1793
+ };
1794
+ useEffect2(() => {
1795
+ return clearCloseTimer;
1796
+ }, []);
1797
+ return /* @__PURE__ */ jsxs12(View18, { style: [styles.root, style], children: [
1798
+ /* @__PURE__ */ jsx22(Pressable10, { ref: triggerRef, onLongPress: showTooltip, children }),
1799
+ /* @__PURE__ */ jsx22(Modal4, { visible: visible && !disabled, transparent: true, animationType: "fade", children: /* @__PURE__ */ jsx22(
1800
+ Pressable10,
1801
+ {
1802
+ style: styles.overlay,
1803
+ onPress: () => {
1804
+ clearCloseTimer();
1805
+ setVisible(false);
1806
+ },
1807
+ children: /* @__PURE__ */ jsx22(
1808
+ View18,
1809
+ {
1810
+ pointerEvents: "none",
1811
+ style: [
1812
+ styles.bubble,
1813
+ {
1814
+ maxWidth,
1815
+ top: position.top,
1816
+ left: position.left
1817
+ },
1818
+ contentStyle
1819
+ ],
1820
+ onLayout: onFloatingLayout,
1821
+ children: typeof content === "string" ? /* @__PURE__ */ jsx22(Text11, { style: [styles.text, textStyle], children: content }) : content
1822
+ }
1823
+ )
1824
+ }
1825
+ ) })
1826
+ ] });
1827
+ }
1828
+ Tooltip.displayName = "Tooltip";
1829
+
1830
+ // src/primitives/Button/Button.tsx
1831
+ import { cloneElement as cloneElement4, useState as useState7 } from "react";
1832
+ import { Pressable as Pressable11, Text as Text12 } from "react-native";
1833
+
1834
+ // src/primitives/Button/Button.styles.ts
1835
+ import { StyleSheet as StyleSheet21 } from "react-native";
1836
+ var fontWeight3 = (value) => value;
1837
+ var createStyles21 = (theme) => StyleSheet21.create({
1838
+ button: {
1839
+ flexDirection: "row",
1840
+ alignItems: "center",
1841
+ justifyContent: "center",
1842
+ gap: 8,
1843
+ borderRadius: theme.tokens.radius.md,
1844
+ borderWidth: 0
1845
+ },
1846
+ fullWidth: {
1847
+ alignSelf: "stretch",
1848
+ width: "100%"
1849
+ },
1850
+ text: {
1851
+ fontFamily: theme.tokens.typography.family.regular,
1852
+ fontWeight: fontWeight3(theme.tokens.typography.weight.regular),
1853
+ lineHeight: theme.tokens.typography.lineHeight.md,
1854
+ // Кнопка сама задает цвет текста через props,
1855
+ // поэтому это значение используется как fallback.
1856
+ color: theme.components.button.primary.default.fg
1857
+ },
1858
+ disabled: {
1859
+ borderColor: theme.components.button.disabled.border,
1860
+ borderWidth: 1
1861
+ },
1862
+ pressed: {
1863
+ transform: [{ scale: 0.98 }]
1864
+ }
1865
+ });
1866
+
1867
+ // src/primitives/Button/Button.tsx
1868
+ import { jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
1869
+ var sizeMap = {
1870
+ sm: {
1871
+ px: 12,
1872
+ py: 8,
1873
+ fontSize: 12,
1874
+ iconSize: 16
1875
+ },
1876
+ md: {
1877
+ px: 16,
1878
+ py: 12,
1879
+ fontSize: 14,
1880
+ iconSize: 20
1881
+ },
1882
+ lg: {
1883
+ px: 20,
1884
+ py: 16,
1885
+ fontSize: 16,
1886
+ iconSize: 24
1887
+ }
1888
+ };
1889
+ function Button({
1890
+ children,
1891
+ disabled = false,
1892
+ onPress,
1893
+ variant = "primary",
1894
+ size = "md",
1895
+ leftIcon,
1896
+ rightIcon,
1897
+ fullWidth = false,
1898
+ style,
1899
+ accessibilityLabel,
1900
+ iconSize
1901
+ }) {
1902
+ const { theme } = useTheme();
1903
+ const styles = useThemeStyles(createStyles21);
1904
+ const config = sizeMap[size];
1905
+ const variantTheme = theme.components.button[variant];
1906
+ const [isPressed, setIsPressed] = useState7(false);
1907
+ const iconOnly = !children && Boolean(leftIcon || rightIcon);
1908
+ if (iconOnly && !accessibilityLabel) {
1909
+ console.warn(
1910
+ "Vellira Button: icon-only buttons must provide an accessibilityLabel."
1911
+ );
1912
+ }
1913
+ const contentColor = disabled ? theme.components.button.disabled.fg : isPressed ? variantTheme.pressed.fg : variantTheme.default.fg;
1914
+ const resolvedIconSize = iconSize ?? config.iconSize;
1915
+ const renderIcon = (icon, color) => {
1916
+ return cloneElement4(icon, {
1917
+ color,
1918
+ size: resolvedIconSize
1919
+ });
1920
+ };
1921
+ return /* @__PURE__ */ jsxs13(
1922
+ Pressable11,
1923
+ {
1924
+ disabled,
1925
+ onPress,
1926
+ accessibilityRole: "button",
1927
+ accessibilityState: { disabled },
1928
+ accessibilityLabel: accessibilityLabel ?? (typeof children === "string" ? children : void 0),
1929
+ onPressIn: () => setIsPressed(true),
1930
+ onPressOut: () => setIsPressed(false),
1931
+ style: ({ pressed }) => [
1932
+ styles.button,
1933
+ {
1934
+ backgroundColor: disabled ? theme.components.button.disabled.bg : pressed ? variantTheme.pressed.bg : variantTheme.default.bg,
1935
+ paddingHorizontal: iconOnly ? config.py : config.px,
1936
+ paddingVertical: config.py
1937
+ },
1938
+ fullWidth && styles.fullWidth,
1939
+ disabled && styles.disabled,
1940
+ pressed && !disabled && styles.pressed,
1941
+ style
1942
+ ],
1943
+ children: [
1944
+ leftIcon && renderIcon(leftIcon, contentColor),
1945
+ children && /* @__PURE__ */ jsx23(
1946
+ Text12,
1947
+ {
1948
+ style: [
1949
+ styles.text,
1950
+ {
1951
+ fontSize: config.fontSize,
1952
+ color: contentColor
1953
+ }
1954
+ ],
1955
+ children
1956
+ }
1957
+ ),
1958
+ rightIcon && renderIcon(rightIcon, contentColor)
1959
+ ]
1960
+ }
1961
+ );
1962
+ }
1963
+
1964
+ // src/primitives/Checkbox/Checkbox.tsx
1965
+ import { forwardRef } from "react";
1966
+ import { useControllableState as useControllableState4 } from "@vellira-ui/core";
1967
+ import { Check } from "@vellira-ui/icons";
1968
+ import { Pressable as Pressable12, Text as Text13, View as View19 } from "react-native";
1969
+
1970
+ // src/primitives/Checkbox/Checkbox.styles.ts
1971
+ import { StyleSheet as StyleSheet22 } from "react-native";
1972
+ var createStyles22 = (theme) => StyleSheet22.create({
1973
+ wrapper: {
1974
+ flexDirection: "row",
1975
+ alignItems: "center",
1976
+ gap: theme.tokens.spacing[3]
1977
+ },
1978
+ disabled: {
1979
+ opacity: 1
1980
+ },
1981
+ box: {
1982
+ width: 22,
1983
+ height: 22,
1984
+ borderWidth: 2,
1985
+ borderColor: theme.components.checkbox.default.border,
1986
+ borderRadius: theme.tokens.radius.md,
1987
+ alignItems: "center",
1988
+ justifyContent: "center",
1989
+ backgroundColor: theme.components.checkbox.default.bg
1990
+ },
1991
+ boxChecked: {
1992
+ backgroundColor: theme.components.checkbox.checked.default.bg,
1993
+ borderColor: theme.components.checkbox.checked.default.border
1994
+ },
1995
+ boxDisabled: {
1996
+ backgroundColor: theme.components.checkbox.disabled.bg,
1997
+ borderColor: theme.components.checkbox.disabled.border
1998
+ },
1999
+ boxError: {
2000
+ borderColor: theme.components.checkbox.error.border
2001
+ },
2002
+ label: {
2003
+ fontFamily: theme.tokens.typography.family.regular,
2004
+ fontSize: theme.tokens.typography.size.md,
2005
+ color: theme.components.checkbox.default.fg
2006
+ },
2007
+ labelDisabled: {
2008
+ color: theme.components.checkbox.disabled.fg
2009
+ },
2010
+ container: {
2011
+ gap: theme.tokens.spacing[2]
2012
+ },
2013
+ errorText: {
2014
+ color: theme.components.checkbox.error.fg,
2015
+ fontSize: theme.tokens.typography.size.sm,
2016
+ lineHeight: theme.tokens.typography.lineHeight.sm,
2017
+ marginLeft: 22 + theme.tokens.spacing[3]
2018
+ }
2019
+ });
2020
+
2021
+ // src/primitives/Checkbox/Checkbox.tsx
2022
+ import { jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
2023
+ var Checkbox = forwardRef(
2024
+ ({
2025
+ label,
2026
+ checked,
2027
+ defaultChecked = false,
2028
+ disabled = false,
2029
+ onCheckedChange,
2030
+ error,
2031
+ style,
2032
+ ...props
2033
+ }, ref) => {
2034
+ const { theme } = useTheme();
2035
+ const styles = useThemeStyles(createStyles22);
2036
+ const hasError = Boolean(error);
2037
+ const [isChecked, setIsChecked] = useControllableState4({
2038
+ value: checked,
2039
+ defaultValue: defaultChecked,
2040
+ onChange: onCheckedChange
2041
+ });
2042
+ const handlePress = () => {
2043
+ if (disabled) return;
2044
+ setIsChecked(!isChecked);
2045
+ };
2046
+ const checkColor = disabled ? theme.components.checkbox.disabled.fg : theme.components.checkbox.checked.default.fg;
2047
+ return /* @__PURE__ */ jsxs14(View19, { style: styles.container, children: [
2048
+ /* @__PURE__ */ jsxs14(
2049
+ Pressable12,
2050
+ {
2051
+ ref,
2052
+ onPress: handlePress,
2053
+ disabled,
2054
+ accessibilityRole: "checkbox",
2055
+ accessibilityState: {
2056
+ checked: isChecked,
2057
+ disabled
2058
+ },
2059
+ accessibilityHint: hasError ? error : void 0,
2060
+ accessibilityLabel: label ?? "Checkbox",
2061
+ style: [styles.wrapper, disabled && styles.disabled, style],
2062
+ ...props,
2063
+ children: [
2064
+ /* @__PURE__ */ jsx24(
2065
+ View19,
2066
+ {
2067
+ style: [
2068
+ styles.box,
2069
+ isChecked && styles.boxChecked,
2070
+ hasError && styles.boxError,
2071
+ disabled && styles.boxDisabled
2072
+ ],
2073
+ children: isChecked && /* @__PURE__ */ jsx24(Check, { size: 12, color: checkColor })
2074
+ }
2075
+ ),
2076
+ label && /* @__PURE__ */ jsx24(Text13, { style: [styles.label, disabled && styles.labelDisabled], children: label })
2077
+ ]
2078
+ }
2079
+ ),
2080
+ hasError && /* @__PURE__ */ jsx24(Text13, { style: styles.errorText, children: error })
2081
+ ] });
2082
+ }
2083
+ );
2084
+ Checkbox.displayName = "Checkbox";
2085
+
2086
+ // src/primitives/Input/Input.tsx
2087
+ import { cloneElement as cloneElement5, forwardRef as forwardRef2, useState as useState8 } from "react";
2088
+ import { TextInput, View as View20 } from "react-native";
2089
+
2090
+ // src/primitives/Input/Input.styles.ts
2091
+ import { StyleSheet as StyleSheet23 } from "react-native";
2092
+ var getPlaceholderTextColor = (theme) => theme.components.input.default.placeholder;
2093
+ var getDisabledPlaceholderTextColor = (theme) => theme.components.input.disabled.placeholder;
2094
+ var createStyles23 = (theme) => StyleSheet23.create({
2095
+ inputWrapper: {
2096
+ position: "relative",
2097
+ width: "100%",
2098
+ minWidth: 0,
2099
+ alignSelf: "stretch"
2100
+ },
2101
+ input: {
2102
+ width: "100%",
2103
+ minWidth: 0,
2104
+ alignSelf: "stretch",
2105
+ color: theme.components.input.default.fg,
2106
+ fontFamily: theme.tokens.typography.family.regular,
2107
+ fontSize: theme.tokens.typography.size.md,
2108
+ backgroundColor: theme.components.input.default.bg,
2109
+ borderColor: theme.components.input.default.border,
2110
+ borderRadius: theme.tokens.radius.md,
2111
+ borderWidth: 1
2112
+ },
2113
+ inputWithLeftIcon: {
2114
+ paddingLeft: theme.tokens.spacing[5] + 20
2115
+ },
2116
+ leftIcon: {
2117
+ position: "absolute",
2118
+ left: theme.tokens.spacing[4],
2119
+ top: 0,
2120
+ bottom: 0,
2121
+ zIndex: 1,
2122
+ alignItems: "center",
2123
+ justifyContent: "center"
2124
+ },
2125
+ sm: {
2126
+ minHeight: 36,
2127
+ paddingHorizontal: theme.tokens.spacing[3],
2128
+ paddingVertical: theme.tokens.spacing[2],
2129
+ fontSize: theme.tokens.typography.size.sm
2130
+ },
2131
+ md: {
2132
+ minHeight: 44,
2133
+ paddingHorizontal: theme.tokens.spacing[4],
2134
+ paddingVertical: theme.tokens.spacing[3],
2135
+ fontSize: theme.tokens.typography.size.md
2136
+ },
2137
+ lg: {
2138
+ minHeight: 52,
2139
+ paddingHorizontal: theme.tokens.spacing[5],
2140
+ paddingVertical: theme.tokens.spacing[4],
2141
+ fontSize: theme.tokens.typography.size.lg
2142
+ },
2143
+ error: {
2144
+ borderColor: theme.components.input.error.border
2145
+ },
2146
+ focused: {
2147
+ color: theme.components.input.focus.fg,
2148
+ backgroundColor: theme.components.input.focus.bg,
2149
+ borderColor: theme.components.input.focus.border
2150
+ },
2151
+ errorFocused: {
2152
+ borderColor: theme.components.input.error.border
2153
+ },
2154
+ disabled: {
2155
+ opacity: 1,
2156
+ color: theme.components.input.disabled.fg,
2157
+ backgroundColor: theme.components.input.disabled.bg,
2158
+ borderColor: theme.components.input.disabled.border
2159
+ }
2160
+ });
2161
+
2162
+ // src/primitives/Input/Input.tsx
2163
+ import { jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
2164
+ var keyboardTypeByInputType = {
2165
+ text: "default",
2166
+ email: "email-address",
2167
+ password: "default",
2168
+ number: "numeric",
2169
+ tel: "phone-pad",
2170
+ url: "url",
2171
+ search: "web-search"
2172
+ };
2173
+ var autoCapitalizeByInputType = {
2174
+ text: "sentences",
2175
+ email: "none",
2176
+ password: "none",
2177
+ number: "none",
2178
+ tel: "none",
2179
+ url: "none",
2180
+ search: "none"
2181
+ };
2182
+ var autoCorrectByInputType = {
2183
+ text: true,
2184
+ email: false,
2185
+ password: false,
2186
+ number: false,
2187
+ tel: false,
2188
+ url: false,
2189
+ search: false
2190
+ };
2191
+ var Input = forwardRef2(
2192
+ ({
2193
+ label,
2194
+ value,
2195
+ onChange,
2196
+ placeholder,
2197
+ size = "md",
2198
+ error,
2199
+ disabled = false,
2200
+ required = false,
2201
+ type = "text",
2202
+ leftIcon,
2203
+ iconSize,
2204
+ containerStyle,
2205
+ inputStyle,
2206
+ keyboardType,
2207
+ secureTextEntry,
2208
+ autoCapitalize,
2209
+ autoCorrect,
2210
+ onBlur,
2211
+ onFocus,
2212
+ accessibilityLabel,
2213
+ accessibilityHint,
2214
+ ...props
2215
+ }, ref) => {
2216
+ const { theme } = useTheme();
2217
+ const styles = useThemeStyles(createStyles23);
2218
+ const [isFocused, setIsFocused] = useState8(false);
2219
+ const placeholderTextColor = disabled ? getDisabledPlaceholderTextColor(theme) : getPlaceholderTextColor(theme);
2220
+ const isPassword = type === "password";
2221
+ const resolvedIconSize = iconSize ?? 16;
2222
+ const iconColor = disabled ? theme.components.input.disabled.fg : isFocused ? theme.components.input.focus.fg : theme.components.input.default.placeholder;
2223
+ const handleFocus = (event) => {
2224
+ setIsFocused(true);
2225
+ onFocus?.(event);
2226
+ };
2227
+ const handleBlur = (event) => {
2228
+ setIsFocused(false);
2229
+ onBlur?.(event);
2230
+ };
2231
+ return /* @__PURE__ */ jsx25(
2232
+ FormField,
2233
+ {
2234
+ label,
2235
+ error,
2236
+ required,
2237
+ disabled,
2238
+ style: containerStyle,
2239
+ children: /* @__PURE__ */ jsxs15(View20, { style: styles.inputWrapper, children: [
2240
+ leftIcon && /* @__PURE__ */ jsx25(
2241
+ View20,
2242
+ {
2243
+ pointerEvents: "none",
2244
+ style: styles.leftIcon,
2245
+ accessibilityElementsHidden: true,
2246
+ importantForAccessibility: "no",
2247
+ children: cloneElement5(leftIcon, {
2248
+ color: iconColor,
2249
+ size: resolvedIconSize
2250
+ })
2251
+ }
2252
+ ),
2253
+ /* @__PURE__ */ jsx25(
2254
+ TextInput,
2255
+ {
2256
+ ...props,
2257
+ ref,
2258
+ value,
2259
+ editable: !disabled,
2260
+ placeholder,
2261
+ keyboardType: keyboardType ?? keyboardTypeByInputType[type],
2262
+ secureTextEntry: secureTextEntry ?? isPassword,
2263
+ autoCapitalize: autoCapitalize ?? autoCapitalizeByInputType[type],
2264
+ autoCorrect: autoCorrect ?? autoCorrectByInputType[type],
2265
+ onChangeText: onChange,
2266
+ onBlur: handleBlur,
2267
+ onFocus: handleFocus,
2268
+ placeholderTextColor,
2269
+ accessibilityLabel: accessibilityLabel ?? label,
2270
+ accessibilityHint,
2271
+ accessibilityState: { disabled },
2272
+ style: [
2273
+ styles.input,
2274
+ styles[size],
2275
+ inputStyle,
2276
+ leftIcon && styles.inputWithLeftIcon,
2277
+ isFocused && !disabled && styles.focused,
2278
+ error && styles.error,
2279
+ isFocused && error && !disabled && styles.errorFocused,
2280
+ disabled && styles.disabled
2281
+ ]
2282
+ }
2283
+ )
2284
+ ] })
2285
+ }
2286
+ );
2287
+ }
2288
+ );
2289
+ Input.displayName = "Input";
2290
+ export {
2291
+ Button,
2292
+ Checkbox,
2293
+ Dropdown,
2294
+ FormField,
2295
+ Input,
2296
+ Modal2 as Modal,
2297
+ RadioGroup,
2298
+ Select,
2299
+ Tabs,
2300
+ ThemeProvider,
2301
+ Tooltip,
2302
+ nativeThemes,
2303
+ useTheme
2304
+ };