@brickslab./ui-mobile 0.1.1

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,1836 @@
1
+ // src/components/ui/button/Button.tsx
2
+ import { TouchableOpacity, Text, StyleSheet, View, ActivityIndicator } from "react-native";
3
+
4
+ // src/tokens.ts
5
+ var tokens = {
6
+ // Colors
7
+ colorBg: "#ffffff",
8
+ colorFg: "#0b1220",
9
+ colorMuted: "#52607a",
10
+ colorBrand: "#CC4A48",
11
+ colorSuccess: "#4ADE80",
12
+ colorWarning: "#F59E0B",
13
+ colorError: "#CC4A48",
14
+ colorInfo: "#3B82F6",
15
+ colorTransparent: "transparent",
16
+ // Badge colors
17
+ cSurfaceElevated: "#f8f9fa",
18
+ cBorder: "#e9ecef",
19
+ cBrandSubtle: "#fef2f2",
20
+ cBrandBorder: "#fecaca",
21
+ cSuccessSubtle: "#f0fdf4",
22
+ cSuccessBorder: "#bbf7d0",
23
+ cWarningSubtle: "#fffbeb",
24
+ cWarningBorder: "#fde68a",
25
+ // Font sizes
26
+ fontsize2xs: 10,
27
+ fontsizeXs: 12,
28
+ fontsizeSm: 14,
29
+ fontsizeMedium: 16,
30
+ fontsizeLg: 18,
31
+ // Simplified, no clamp
32
+ fontsizeXl: 20,
33
+ fontsize2xl: 24,
34
+ fontsize3xl: 30,
35
+ fontsize4xl: 36,
36
+ fontsize5xl: 32,
37
+ // Font weights
38
+ fontweightThin: "100",
39
+ fontweightNormal: "400",
40
+ fontweightLight: "300",
41
+ fontweightMedium: "500",
42
+ fontweightSemibold: "600",
43
+ fontweightBold: "700",
44
+ fontweightExtrabold: "800",
45
+ fontweightBlack: "900",
46
+ // Heights
47
+ heightXm: 1,
48
+ heightSm: 8,
49
+ heightMd: 32,
50
+ heightInput: 38,
51
+ heightLg: 42,
52
+ heightXl: 62,
53
+ height2xl: 150,
54
+ // Spaces
55
+ space1: 2,
56
+ space15: 4,
57
+ space2: 8,
58
+ space3: 12,
59
+ space4: 16,
60
+ space5: 20,
61
+ space6: 24,
62
+ space7: 28,
63
+ space8: 32,
64
+ space10: 40,
65
+ space12: 48,
66
+ space16: 60,
67
+ // Radii
68
+ radiusSm: 6,
69
+ radiusMd: 12,
70
+ radiusLg: 16,
71
+ radiusFull: 999,
72
+ // Other
73
+ borderXm: 1,
74
+ borderSm: 2,
75
+ borderMd: 4
76
+ };
77
+
78
+ // src/components/ui/button/Button.tsx
79
+ import { jsx, jsxs } from "react/jsx-runtime";
80
+ var variantStyles = (variant, customBg, customColor) => {
81
+ switch (variant) {
82
+ case "primary":
83
+ return { backgroundColor: tokens.colorBrand, borderWidth: 0 };
84
+ case "secondary":
85
+ return { backgroundColor: tokens.colorBg, borderWidth: 1, borderColor: tokens.colorMuted };
86
+ case "ghost":
87
+ return { backgroundColor: "transparent", borderWidth: 0 };
88
+ case "danger":
89
+ return { backgroundColor: tokens.colorError, borderWidth: 0 };
90
+ case "custom":
91
+ return { backgroundColor: customBg || tokens.colorBrand, borderWidth: 0 };
92
+ default:
93
+ return {};
94
+ }
95
+ };
96
+ var sizeStyles = (size) => {
97
+ switch (size) {
98
+ case "sm":
99
+ return { height: 28, paddingHorizontal: tokens.space3, paddingVertical: tokens.space2 };
100
+ case "md":
101
+ return { height: tokens.heightInput, paddingHorizontal: tokens.space5, paddingVertical: tokens.space3 };
102
+ case "lg":
103
+ return { height: 44, paddingHorizontal: tokens.space6, paddingVertical: tokens.space4 };
104
+ default:
105
+ return {};
106
+ }
107
+ };
108
+ var textStyles = (variant, size, customColor) => {
109
+ const base = { fontWeight: "600" };
110
+ switch (size) {
111
+ case "sm":
112
+ base.fontSize = tokens.fontsizeXs;
113
+ break;
114
+ case "md":
115
+ base.fontSize = tokens.fontsizeSm;
116
+ break;
117
+ case "lg":
118
+ base.fontSize = tokens.fontsizeMedium;
119
+ break;
120
+ }
121
+ switch (variant) {
122
+ case "primary":
123
+ case "danger":
124
+ base.color = "#FBFBFB";
125
+ break;
126
+ case "secondary":
127
+ base.color = tokens.colorFg;
128
+ break;
129
+ case "ghost":
130
+ base.color = tokens.colorFg;
131
+ break;
132
+ case "custom":
133
+ base.color = customColor || tokens.colorFg;
134
+ break;
135
+ }
136
+ return base;
137
+ };
138
+ function Button({
139
+ variant = "primary",
140
+ size = "md",
141
+ disabled = false,
142
+ isLoading = false,
143
+ leftIcon,
144
+ rightIcon,
145
+ fullWidth = false,
146
+ onPress,
147
+ children,
148
+ customBg,
149
+ customColor,
150
+ style: userStyle
151
+ }) {
152
+ const isDisabled = disabled || isLoading;
153
+ return /* @__PURE__ */ jsx(
154
+ TouchableOpacity,
155
+ {
156
+ style: [
157
+ styles.button,
158
+ variantStyles(variant, customBg, customColor),
159
+ sizeStyles(size),
160
+ fullWidth && { width: "100%" },
161
+ isDisabled && { opacity: 0.6 },
162
+ userStyle
163
+ ],
164
+ onPress: isDisabled ? void 0 : onPress,
165
+ disabled: isDisabled,
166
+ activeOpacity: 0.8,
167
+ children: /* @__PURE__ */ jsxs(View, { style: styles.content, children: [
168
+ isLoading ? /* @__PURE__ */ jsx(ActivityIndicator, { size: "small", color: textStyles(variant, size, customColor).color }) : leftIcon,
169
+ /* @__PURE__ */ jsx(Text, { style: [styles.text, textStyles(variant, size, customColor)], children }),
170
+ !isLoading && rightIcon
171
+ ] })
172
+ }
173
+ );
174
+ }
175
+ var styles = StyleSheet.create({
176
+ button: {
177
+ borderRadius: tokens.radiusSm,
178
+ alignItems: "center",
179
+ justifyContent: "center",
180
+ flexDirection: "row"
181
+ },
182
+ content: {
183
+ flexDirection: "row",
184
+ alignItems: "center",
185
+ gap: tokens.space2
186
+ },
187
+ text: {
188
+ textAlign: "center"
189
+ }
190
+ });
191
+
192
+ // src/components/ui/input/Input.tsx
193
+ import { View as View2, TextInput, Text as Text2, StyleSheet as StyleSheet2 } from "react-native";
194
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
195
+ var sizeStyles2 = (size) => {
196
+ switch (size) {
197
+ case "sm":
198
+ return { height: 28, paddingHorizontal: tokens.space3, fontSize: tokens.fontsizeXs };
199
+ case "md":
200
+ return { height: tokens.heightInput, paddingHorizontal: tokens.space4, fontSize: tokens.fontsizeSm };
201
+ case "lg":
202
+ return { height: 44, paddingHorizontal: tokens.space5, fontSize: tokens.fontsizeMedium };
203
+ default:
204
+ return {};
205
+ }
206
+ };
207
+ function Input({
208
+ value,
209
+ onChangeText,
210
+ keyboardType = "default",
211
+ label,
212
+ placeholder,
213
+ helperText,
214
+ errorText,
215
+ disabled = false,
216
+ editable = true,
217
+ leftIcon,
218
+ rightIcon,
219
+ size = "md",
220
+ fullWidth = false,
221
+ style: userStyle,
222
+ inputStyle: userInputStyle
223
+ }) {
224
+ const hasError = Boolean(errorText);
225
+ const isEditable = editable && !disabled;
226
+ const config = sizeStyles2(size);
227
+ const containerStyle = [
228
+ styles2.container,
229
+ fullWidth && { width: "100%" },
230
+ !isEditable && { opacity: 0.5 },
231
+ userStyle
232
+ ];
233
+ const inputContainerStyle = [
234
+ styles2.inputContainer,
235
+ { borderColor: hasError ? tokens.colorError : tokens.colorMuted },
236
+ !isEditable && { backgroundColor: tokens.colorMuted }
237
+ ];
238
+ const textInputStyle = [
239
+ styles2.input,
240
+ config,
241
+ { color: tokens.colorFg },
242
+ userInputStyle
243
+ ];
244
+ return /* @__PURE__ */ jsxs2(View2, { style: containerStyle, children: [
245
+ label && /* @__PURE__ */ jsx2(Text2, { style: styles2.label, children: label }),
246
+ /* @__PURE__ */ jsxs2(View2, { style: inputContainerStyle, children: [
247
+ leftIcon && /* @__PURE__ */ jsx2(View2, { style: styles2.leftIcon, children: leftIcon }),
248
+ /* @__PURE__ */ jsx2(
249
+ TextInput,
250
+ {
251
+ value,
252
+ onChangeText,
253
+ placeholder,
254
+ placeholderTextColor: tokens.colorMuted,
255
+ keyboardType,
256
+ editable: isEditable,
257
+ style: textInputStyle
258
+ }
259
+ ),
260
+ rightIcon && /* @__PURE__ */ jsx2(View2, { style: styles2.rightIcon, children: rightIcon })
261
+ ] }),
262
+ (errorText || helperText) && /* @__PURE__ */ jsx2(Text2, { style: [styles2.helper, hasError && { color: tokens.colorError }], children: errorText ?? helperText })
263
+ ] });
264
+ }
265
+ var styles2 = StyleSheet2.create({
266
+ container: {
267
+ marginBottom: tokens.space2
268
+ },
269
+ label: {
270
+ marginBottom: tokens.space1,
271
+ fontSize: tokens.fontsizeSm,
272
+ fontWeight: tokens.fontweightMedium,
273
+ color: tokens.colorFg
274
+ },
275
+ inputContainer: {
276
+ flexDirection: "row",
277
+ alignItems: "center",
278
+ borderWidth: 1,
279
+ borderRadius: tokens.radiusSm,
280
+ backgroundColor: tokens.colorBg
281
+ },
282
+ input: {
283
+ flex: 1,
284
+ color: tokens.colorFg
285
+ },
286
+ leftIcon: {
287
+ marginLeft: tokens.space3
288
+ },
289
+ rightIcon: {
290
+ marginRight: tokens.space3
291
+ },
292
+ helper: {
293
+ marginTop: tokens.space1,
294
+ fontSize: tokens.fontsizeXs,
295
+ color: tokens.colorMuted,
296
+ lineHeight: 14
297
+ }
298
+ });
299
+
300
+ // src/components/ui/checkbox/Checkbox.tsx
301
+ import { useState } from "react";
302
+ import { TouchableOpacity as TouchableOpacity2, View as View3, Text as Text3, StyleSheet as StyleSheet3 } from "react-native";
303
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
304
+ var sizeMap = {
305
+ sm: 14,
306
+ md: 16,
307
+ lg: 20
308
+ };
309
+ var CheckIcon = () => /* @__PURE__ */ jsx3(Text3, { style: styles3.checkmark, children: "\u2713" });
310
+ function Checkbox({
311
+ checked,
312
+ defaultChecked,
313
+ onValueChange,
314
+ label,
315
+ disabled = false,
316
+ size = "md",
317
+ style: userStyle
318
+ }) {
319
+ const [internalChecked, setInternalChecked] = useState(defaultChecked || false);
320
+ const isChecked = checked !== void 0 ? checked : internalChecked;
321
+ const handlePress = () => {
322
+ if (disabled) return;
323
+ const newChecked = !isChecked;
324
+ if (checked === void 0) {
325
+ setInternalChecked(newChecked);
326
+ }
327
+ onValueChange?.(newChecked);
328
+ };
329
+ const px = sizeMap[size];
330
+ return /* @__PURE__ */ jsxs3(
331
+ TouchableOpacity2,
332
+ {
333
+ style: [styles3.container, userStyle],
334
+ onPress: handlePress,
335
+ disabled,
336
+ activeOpacity: 0.8,
337
+ children: [
338
+ /* @__PURE__ */ jsx3(
339
+ View3,
340
+ {
341
+ style: [
342
+ styles3.checkbox,
343
+ { width: px, height: px },
344
+ isChecked && styles3.checked,
345
+ disabled && styles3.disabled
346
+ ],
347
+ children: isChecked && /* @__PURE__ */ jsx3(CheckIcon, {})
348
+ }
349
+ ),
350
+ label && /* @__PURE__ */ jsx3(Text3, { style: [styles3.label, disabled && styles3.disabledText], children: label })
351
+ ]
352
+ }
353
+ );
354
+ }
355
+ var styles3 = StyleSheet3.create({
356
+ container: {
357
+ flexDirection: "row",
358
+ alignItems: "center",
359
+ gap: tokens.space2
360
+ },
361
+ checkbox: {
362
+ borderWidth: 1.5,
363
+ borderColor: tokens.colorMuted,
364
+ borderRadius: 3,
365
+ backgroundColor: tokens.colorBg,
366
+ alignItems: "center",
367
+ justifyContent: "center"
368
+ },
369
+ checked: {
370
+ backgroundColor: tokens.colorBrand,
371
+ borderColor: tokens.colorBrand
372
+ },
373
+ disabled: {
374
+ opacity: 0.45
375
+ },
376
+ checkmark: {
377
+ color: "#FBFBFB",
378
+ fontSize: 10,
379
+ fontWeight: "bold"
380
+ },
381
+ label: {
382
+ fontSize: tokens.fontsizeSm,
383
+ color: tokens.colorFg,
384
+ lineHeight: 14
385
+ },
386
+ disabledText: {
387
+ opacity: 0.45
388
+ }
389
+ });
390
+
391
+ // src/components/ui/radio/Radio.tsx
392
+ import React2 from "react";
393
+ import { TouchableOpacity as TouchableOpacity3, View as View4, Text as Text4, StyleSheet as StyleSheet4 } from "react-native";
394
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
395
+ var sizeMap2 = {
396
+ sm: 14,
397
+ md: 16,
398
+ lg: 20
399
+ };
400
+ function Radio({
401
+ value,
402
+ checked,
403
+ onChange,
404
+ label,
405
+ name,
406
+ disabled = false,
407
+ size = "md",
408
+ style: userStyle
409
+ }) {
410
+ const handlePress = () => {
411
+ if (disabled) return;
412
+ onChange?.(value);
413
+ };
414
+ const px = sizeMap2[size];
415
+ return /* @__PURE__ */ jsxs4(
416
+ TouchableOpacity3,
417
+ {
418
+ style: [styles4.container, userStyle],
419
+ onPress: handlePress,
420
+ disabled,
421
+ activeOpacity: 0.8,
422
+ children: [
423
+ /* @__PURE__ */ jsx4(
424
+ View4,
425
+ {
426
+ style: [
427
+ styles4.radio,
428
+ { width: px, height: px },
429
+ checked && styles4.checked,
430
+ disabled && styles4.disabled
431
+ ],
432
+ children: checked && /* @__PURE__ */ jsx4(View4, { style: styles4.dot })
433
+ }
434
+ ),
435
+ label && /* @__PURE__ */ jsx4(Text4, { style: [styles4.label, disabled && styles4.disabledText], children: label })
436
+ ]
437
+ }
438
+ );
439
+ }
440
+ function RadioGroup({
441
+ name,
442
+ value,
443
+ onChange,
444
+ children,
445
+ direction = "vertical",
446
+ gap = 10,
447
+ style: userStyle
448
+ }) {
449
+ const cloned = React2.Children.map(children, (child) => {
450
+ if (!React2.isValidElement(child)) return child;
451
+ const childProps = child.props;
452
+ return React2.cloneElement(child, {
453
+ name,
454
+ ...value !== void 0 && { checked: childProps.value === value },
455
+ ...onChange && { onChange }
456
+ });
457
+ });
458
+ return /* @__PURE__ */ jsx4(
459
+ View4,
460
+ {
461
+ style: [
462
+ styles4.group,
463
+ { flexDirection: direction === "horizontal" ? "row" : "column", gap },
464
+ userStyle
465
+ ],
466
+ children: cloned
467
+ }
468
+ );
469
+ }
470
+ var styles4 = StyleSheet4.create({
471
+ container: {
472
+ flexDirection: "row",
473
+ alignItems: "center",
474
+ gap: tokens.space2
475
+ },
476
+ radio: {
477
+ borderWidth: 1.5,
478
+ borderColor: tokens.colorMuted,
479
+ borderRadius: 50,
480
+ backgroundColor: tokens.colorBg,
481
+ alignItems: "center",
482
+ justifyContent: "center"
483
+ },
484
+ checked: {
485
+ borderColor: tokens.colorBrand
486
+ },
487
+ disabled: {
488
+ opacity: 0.45
489
+ },
490
+ dot: {
491
+ width: 6,
492
+ height: 6,
493
+ borderRadius: 3,
494
+ backgroundColor: tokens.colorBrand
495
+ },
496
+ label: {
497
+ fontSize: tokens.fontsizeSm,
498
+ color: tokens.colorFg,
499
+ lineHeight: 14
500
+ },
501
+ disabledText: {
502
+ opacity: 0.45
503
+ },
504
+ group: {
505
+ flexWrap: "wrap"
506
+ }
507
+ });
508
+
509
+ // src/components/ui/toggle_switch/ToggleSwitch.tsx
510
+ import { TouchableOpacity as TouchableOpacity4, View as View5, Text as Text5, StyleSheet as StyleSheet5 } from "react-native";
511
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
512
+ function ToggleSwitch({
513
+ checked,
514
+ onValueChange,
515
+ label,
516
+ disabled = false,
517
+ style: userStyle
518
+ }) {
519
+ const handlePress = () => {
520
+ if (disabled) return;
521
+ onValueChange(!checked);
522
+ };
523
+ return /* @__PURE__ */ jsxs5(
524
+ TouchableOpacity4,
525
+ {
526
+ style: [styles5.container, userStyle],
527
+ onPress: handlePress,
528
+ disabled,
529
+ activeOpacity: 0.8,
530
+ children: [
531
+ /* @__PURE__ */ jsx5(
532
+ View5,
533
+ {
534
+ style: [
535
+ styles5.switch,
536
+ { backgroundColor: checked ? tokens.colorBrand : tokens.colorMuted },
537
+ disabled && styles5.disabled
538
+ ],
539
+ children: /* @__PURE__ */ jsx5(
540
+ View5,
541
+ {
542
+ style: [
543
+ styles5.thumb,
544
+ { transform: [{ translateX: checked ? 18 : 0 }] }
545
+ ]
546
+ }
547
+ )
548
+ }
549
+ ),
550
+ label && /* @__PURE__ */ jsx5(Text5, { style: [styles5.label, disabled && styles5.disabledText], children: label })
551
+ ]
552
+ }
553
+ );
554
+ }
555
+ var styles5 = StyleSheet5.create({
556
+ container: {
557
+ flexDirection: "row",
558
+ alignItems: "center",
559
+ gap: tokens.space3
560
+ },
561
+ switch: {
562
+ width: 40,
563
+ height: 22,
564
+ borderRadius: 11,
565
+ justifyContent: "center",
566
+ paddingHorizontal: 3
567
+ },
568
+ thumb: {
569
+ width: 16,
570
+ height: 16,
571
+ borderRadius: 8,
572
+ backgroundColor: tokens.colorBg,
573
+ shadowColor: "#000",
574
+ shadowOffset: { width: 0, height: 1 },
575
+ shadowOpacity: 0.2,
576
+ shadowRadius: 1,
577
+ elevation: 2
578
+ },
579
+ disabled: {
580
+ opacity: 0.5
581
+ },
582
+ label: {
583
+ fontSize: tokens.fontsizeSm,
584
+ color: tokens.colorFg
585
+ },
586
+ disabledText: {
587
+ opacity: 0.5
588
+ }
589
+ });
590
+
591
+ // src/components/ui/badge/Badge.tsx
592
+ import { View as View6, Text as Text6, StyleSheet as StyleSheet6 } from "react-native";
593
+ import { jsx as jsx6 } from "react/jsx-runtime";
594
+ var variantMap = {
595
+ default: {
596
+ bg: tokens.cSurfaceElevated,
597
+ border: tokens.cBorder,
598
+ color: tokens.colorFg
599
+ },
600
+ info: {
601
+ bg: tokens.cBrandSubtle,
602
+ border: tokens.cBrandBorder,
603
+ color: tokens.colorBrand
604
+ },
605
+ success: {
606
+ bg: tokens.cSuccessSubtle,
607
+ border: tokens.cSuccessBorder,
608
+ color: tokens.colorSuccess
609
+ },
610
+ warning: {
611
+ bg: tokens.cWarningSubtle,
612
+ border: tokens.cWarningBorder,
613
+ color: tokens.colorWarning
614
+ },
615
+ error: {
616
+ bg: tokens.cBrandSubtle,
617
+ border: tokens.cBrandBorder,
618
+ color: tokens.colorError
619
+ }
620
+ };
621
+ var sizeMap3 = {
622
+ sm: { fontSize: tokens.fontsize2xs, paddingHorizontal: 6, paddingVertical: 1, dotSize: 6 },
623
+ md: { fontSize: tokens.fontsizeXs, paddingHorizontal: 8, paddingVertical: 2, dotSize: 8 },
624
+ lg: { fontSize: tokens.fontsizeXs, paddingHorizontal: 10, paddingVertical: 3, dotSize: 10 }
625
+ };
626
+ function Badge({
627
+ variant = "default",
628
+ size = "md",
629
+ children,
630
+ dot = false,
631
+ outlined = false,
632
+ max,
633
+ style: userStyle,
634
+ textStyle: userTextStyle
635
+ }) {
636
+ const { bg, border, color } = variantMap[variant];
637
+ const { fontSize, paddingHorizontal, paddingVertical, dotSize } = sizeMap3[size];
638
+ let content = children;
639
+ if (!dot && max !== void 0 && typeof children === "number" && children > max) {
640
+ content = `${max}+`;
641
+ }
642
+ if (dot) {
643
+ return /* @__PURE__ */ jsx6(
644
+ View6,
645
+ {
646
+ style: [
647
+ styles6.dot,
648
+ { width: dotSize, height: dotSize, backgroundColor: outlined ? "transparent" : color, borderColor: color },
649
+ userStyle
650
+ ]
651
+ }
652
+ );
653
+ }
654
+ return /* @__PURE__ */ jsx6(
655
+ View6,
656
+ {
657
+ style: [
658
+ styles6.badge,
659
+ {
660
+ paddingHorizontal,
661
+ paddingVertical,
662
+ backgroundColor: outlined ? "transparent" : bg,
663
+ borderColor: border
664
+ },
665
+ userStyle
666
+ ],
667
+ children: /* @__PURE__ */ jsx6(
668
+ Text6,
669
+ {
670
+ style: [
671
+ styles6.text,
672
+ { fontSize, color },
673
+ userTextStyle
674
+ ],
675
+ children: content
676
+ }
677
+ )
678
+ }
679
+ );
680
+ }
681
+ var styles6 = StyleSheet6.create({
682
+ badge: {
683
+ borderWidth: 1,
684
+ borderRadius: tokens.radiusFull,
685
+ alignSelf: "flex-start"
686
+ },
687
+ text: {
688
+ fontWeight: "600",
689
+ textAlign: "center"
690
+ },
691
+ dot: {
692
+ borderWidth: 1.5,
693
+ borderRadius: 50
694
+ }
695
+ });
696
+
697
+ // src/components/ui/tabs/Tabs.tsx
698
+ import { ScrollView, TouchableOpacity as TouchableOpacity5, Text as Text7, View as View7, StyleSheet as StyleSheet7 } from "react-native";
699
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
700
+ var sizeMap4 = {
701
+ sm: { fontSize: tokens.fontsizeXs, paddingHorizontal: tokens.space3, paddingVertical: tokens.space2 },
702
+ md: { fontSize: tokens.fontsizeSm, paddingHorizontal: tokens.space4, paddingVertical: tokens.space2 },
703
+ lg: { fontSize: tokens.fontsizeMedium, paddingHorizontal: tokens.space5, paddingVertical: tokens.space3 }
704
+ };
705
+ var getVariantStyles = (variant) => {
706
+ switch (variant) {
707
+ case "underline":
708
+ return {
709
+ container: { borderBottomWidth: 1, borderBottomColor: tokens.cBorder },
710
+ tab: { borderBottomWidth: 2, borderBottomColor: "transparent" },
711
+ activeTab: { borderBottomColor: tokens.colorBrand }
712
+ };
713
+ case "pills":
714
+ return {
715
+ container: {},
716
+ tab: { borderRadius: tokens.radiusSm },
717
+ activeTab: { backgroundColor: tokens.cBrandSubtle }
718
+ };
719
+ case "boxed":
720
+ return {
721
+ container: { backgroundColor: tokens.cSurfaceElevated, padding: 4, borderRadius: tokens.radiusMd },
722
+ tab: { borderRadius: tokens.radiusSm },
723
+ activeTab: { backgroundColor: tokens.colorBg, shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.08, shadowRadius: 3, elevation: 1 }
724
+ };
725
+ default:
726
+ return {};
727
+ }
728
+ };
729
+ function Tabs({
730
+ tabs,
731
+ value,
732
+ onChange,
733
+ variant = "underline",
734
+ size = "md",
735
+ fullWidth = false,
736
+ style: userStyle
737
+ }) {
738
+ const config = sizeMap4[size];
739
+ const variantStyles3 = getVariantStyles(variant);
740
+ return /* @__PURE__ */ jsx7(
741
+ ScrollView,
742
+ {
743
+ horizontal: true,
744
+ showsHorizontalScrollIndicator: false,
745
+ style: [styles7.container, variantStyles3.container, userStyle],
746
+ contentContainerStyle: fullWidth ? { flex: 1 } : void 0,
747
+ children: tabs.map((tab) => {
748
+ const isActive = tab.value === value;
749
+ return /* @__PURE__ */ jsxs6(
750
+ TouchableOpacity5,
751
+ {
752
+ style: [
753
+ styles7.tab,
754
+ config,
755
+ variantStyles3.tab,
756
+ isActive && variantStyles3.activeTab,
757
+ fullWidth && { flex: 1 },
758
+ tab.disabled && styles7.disabled
759
+ ],
760
+ onPress: () => !tab.disabled && onChange(tab.value),
761
+ disabled: tab.disabled,
762
+ activeOpacity: 0.8,
763
+ children: [
764
+ tab.icon,
765
+ /* @__PURE__ */ jsx7(Text7, { style: [styles7.label, { fontSize: config.fontSize, color: isActive ? tokens.colorFg : tokens.colorMuted }], children: tab.label })
766
+ ]
767
+ },
768
+ tab.value
769
+ );
770
+ })
771
+ }
772
+ );
773
+ }
774
+ function TabPanel({ value, activeValue, children, style: userStyle }) {
775
+ if (value !== activeValue) return null;
776
+ return /* @__PURE__ */ jsx7(View7, { style: userStyle, children });
777
+ }
778
+ var styles7 = StyleSheet7.create({
779
+ container: {
780
+ flexDirection: "row"
781
+ },
782
+ tab: {
783
+ flexDirection: "row",
784
+ alignItems: "center",
785
+ gap: 6,
786
+ marginRight: 2
787
+ },
788
+ label: {
789
+ fontWeight: "500"
790
+ },
791
+ disabled: {
792
+ opacity: 0.4
793
+ }
794
+ });
795
+
796
+ // src/components/ui/alert/Alert.tsx
797
+ import { View as View8, Text as Text8, TouchableOpacity as TouchableOpacity6, StyleSheet as StyleSheet8 } from "react-native";
798
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
799
+ var variantMap2 = {
800
+ info: {
801
+ bg: tokens.cBrandSubtle,
802
+ border: tokens.cBrandBorder,
803
+ accent: tokens.colorBrand,
804
+ icon: "\u2139\uFE0F"
805
+ },
806
+ success: {
807
+ bg: tokens.cSuccessSubtle,
808
+ border: tokens.cSuccessBorder,
809
+ accent: tokens.colorSuccess,
810
+ icon: "\u2705"
811
+ },
812
+ warning: {
813
+ bg: tokens.cWarningSubtle,
814
+ border: tokens.cWarningBorder,
815
+ accent: tokens.colorWarning,
816
+ icon: "\u26A0\uFE0F"
817
+ },
818
+ error: {
819
+ bg: tokens.cBrandSubtle,
820
+ border: tokens.cBrandBorder,
821
+ accent: tokens.colorError,
822
+ icon: "\u274C"
823
+ }
824
+ };
825
+ function Alert({
826
+ variant = "info",
827
+ title,
828
+ children,
829
+ dismissible = false,
830
+ onDismiss,
831
+ icon = true,
832
+ fullWidth = false,
833
+ style: userStyle
834
+ }) {
835
+ const config = variantMap2[variant];
836
+ return /* @__PURE__ */ jsxs7(
837
+ View8,
838
+ {
839
+ style: [
840
+ styles8.alert,
841
+ { backgroundColor: config.bg, borderColor: config.border },
842
+ fullWidth && { width: "100%" },
843
+ userStyle
844
+ ],
845
+ children: [
846
+ icon && /* @__PURE__ */ jsx8(Text8, { style: styles8.icon, children: config.icon }),
847
+ /* @__PURE__ */ jsxs7(View8, { style: styles8.content, children: [
848
+ title && /* @__PURE__ */ jsx8(Text8, { style: styles8.title, children: title }),
849
+ /* @__PURE__ */ jsx8(Text8, { style: styles8.message, children })
850
+ ] }),
851
+ dismissible && onDismiss && /* @__PURE__ */ jsx8(TouchableOpacity6, { onPress: onDismiss, style: styles8.dismiss, children: /* @__PURE__ */ jsx8(Text8, { style: styles8.dismissText, children: "\u2715" }) })
852
+ ]
853
+ }
854
+ );
855
+ }
856
+ var styles8 = StyleSheet8.create({
857
+ alert: {
858
+ borderWidth: 1,
859
+ borderRadius: tokens.radiusMd,
860
+ padding: tokens.space3,
861
+ flexDirection: "row",
862
+ alignItems: "flex-start"
863
+ },
864
+ icon: {
865
+ fontSize: 16,
866
+ marginRight: tokens.space3,
867
+ marginTop: 2
868
+ },
869
+ content: {
870
+ flex: 1
871
+ },
872
+ title: {
873
+ fontWeight: "600",
874
+ fontSize: tokens.fontsizeSm,
875
+ color: tokens.colorFg,
876
+ marginBottom: 4
877
+ },
878
+ message: {
879
+ fontSize: tokens.fontsizeSm,
880
+ color: tokens.colorFg,
881
+ lineHeight: 20
882
+ },
883
+ dismiss: {
884
+ padding: tokens.space1,
885
+ borderRadius: tokens.radiusSm,
886
+ marginLeft: tokens.space3
887
+ },
888
+ dismissText: {
889
+ fontSize: 14,
890
+ color: tokens.colorFg,
891
+ opacity: 0.6
892
+ }
893
+ });
894
+
895
+ // src/components/ui/progress_bar/ProgressBar.tsx
896
+ import { useEffect, useRef } from "react";
897
+ import { View as View9, Text as Text9, Animated as Animated2, StyleSheet as StyleSheet9 } from "react-native";
898
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
899
+ var colorMap = {
900
+ brand: tokens.colorBrand,
901
+ green: tokens.colorSuccess,
902
+ amber: tokens.colorWarning,
903
+ red: tokens.colorError
904
+ };
905
+ var trackColorMap = {
906
+ brand: tokens.cBrandSubtle,
907
+ green: tokens.cSuccessSubtle,
908
+ amber: tokens.cWarningSubtle,
909
+ red: tokens.cBrandSubtle
910
+ };
911
+ var sizeMap5 = {
912
+ sm: 4,
913
+ md: 8
914
+ };
915
+ function ProgressBar({
916
+ value,
917
+ max = 100,
918
+ label,
919
+ colorScheme = "brand",
920
+ size = "md",
921
+ showValue = false,
922
+ animate = true,
923
+ duration = 800,
924
+ style: userStyle
925
+ }) {
926
+ const clamped = Math.min(Math.max(value, 0), max);
927
+ const percent = clamped / max * 100;
928
+ const barColor = colorMap[colorScheme];
929
+ const trackColor = trackColorMap[colorScheme];
930
+ const height = sizeMap5[size];
931
+ const animatedWidth = useRef(new Animated2.Value(0)).current;
932
+ useEffect(() => {
933
+ if (animate) {
934
+ Animated2.timing(animatedWidth, {
935
+ toValue: percent,
936
+ duration,
937
+ useNativeDriver: false
938
+ }).start();
939
+ } else {
940
+ animatedWidth.setValue(percent);
941
+ }
942
+ }, [percent, animate, duration, animatedWidth]);
943
+ const widthInterpolation = animatedWidth.interpolate({
944
+ inputRange: [0, 100],
945
+ outputRange: ["0%", "100%"]
946
+ });
947
+ return /* @__PURE__ */ jsxs8(View9, { style: [styles9.container, userStyle], children: [
948
+ (label || showValue) && /* @__PURE__ */ jsxs8(View9, { style: styles9.header, children: [
949
+ label && /* @__PURE__ */ jsx9(Text9, { style: styles9.label, children: label }),
950
+ showValue && /* @__PURE__ */ jsxs8(Text9, { style: styles9.value, children: [
951
+ Math.round(percent),
952
+ "%"
953
+ ] })
954
+ ] }),
955
+ /* @__PURE__ */ jsx9(
956
+ View9,
957
+ {
958
+ style: [styles9.track, { height, backgroundColor: trackColor }],
959
+ accessible: true,
960
+ accessibilityRole: "progressbar",
961
+ accessibilityValue: { now: clamped, min: 0, max },
962
+ accessibilityLabel: label,
963
+ children: /* @__PURE__ */ jsx9(
964
+ Animated2.View,
965
+ {
966
+ style: [
967
+ styles9.bar,
968
+ { height, backgroundColor: barColor, width: widthInterpolation }
969
+ ]
970
+ }
971
+ )
972
+ }
973
+ )
974
+ ] });
975
+ }
976
+ var styles9 = StyleSheet9.create({
977
+ container: {
978
+ gap: tokens.space1
979
+ },
980
+ header: {
981
+ flexDirection: "row",
982
+ justifyContent: "space-between",
983
+ alignItems: "center"
984
+ },
985
+ label: {
986
+ fontSize: tokens.fontsizeXs,
987
+ color: tokens.colorMuted,
988
+ fontWeight: "500"
989
+ },
990
+ value: {
991
+ fontSize: tokens.fontsizeXs,
992
+ color: tokens.colorMuted,
993
+ fontWeight: "600",
994
+ fontVariant: ["tabular-nums"]
995
+ },
996
+ track: {
997
+ width: "100%",
998
+ borderRadius: tokens.radiusFull,
999
+ overflow: "hidden"
1000
+ },
1001
+ bar: {
1002
+ borderRadius: tokens.radiusFull
1003
+ }
1004
+ });
1005
+
1006
+ // src/components/ui/spinner/Spinner.tsx
1007
+ import { ActivityIndicator as ActivityIndicator2, View as View10, StyleSheet as StyleSheet10 } from "react-native";
1008
+ import { jsx as jsx10 } from "react/jsx-runtime";
1009
+ var variantMap3 = {
1010
+ default: tokens.colorMuted,
1011
+ brand: tokens.colorBrand,
1012
+ success: tokens.colorSuccess,
1013
+ warning: tokens.colorWarning,
1014
+ error: tokens.colorError,
1015
+ white: "#FBFBFB"
1016
+ };
1017
+ function Spinner({
1018
+ size = "large",
1019
+ variant = "brand",
1020
+ label = "Loading...",
1021
+ style: userStyle
1022
+ }) {
1023
+ const color = variantMap3[variant];
1024
+ return /* @__PURE__ */ jsx10(View10, { style: [styles10.container, userStyle], accessible: true, accessibilityRole: "progressbar", accessibilityLabel: label, children: /* @__PURE__ */ jsx10(ActivityIndicator2, { size, color }) });
1025
+ }
1026
+ var styles10 = StyleSheet10.create({
1027
+ container: {
1028
+ justifyContent: "center",
1029
+ alignItems: "center"
1030
+ }
1031
+ });
1032
+
1033
+ // src/components/ui/select/Select.tsx
1034
+ import { useState as useState2 } from "react";
1035
+ import { View as View11, Text as Text10, TouchableOpacity as TouchableOpacity7, Modal, FlatList, StyleSheet as StyleSheet11 } from "react-native";
1036
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1037
+ var sizeStyles3 = (size) => {
1038
+ switch (size) {
1039
+ case "sm":
1040
+ return { height: 28, paddingHorizontal: tokens.space3, fontSize: tokens.fontsizeXs };
1041
+ case "md":
1042
+ return { height: tokens.heightInput, paddingHorizontal: tokens.space4, fontSize: tokens.fontsizeSm };
1043
+ case "lg":
1044
+ return { height: 44, paddingHorizontal: tokens.space5, fontSize: tokens.fontsizeMedium };
1045
+ default:
1046
+ return {};
1047
+ }
1048
+ };
1049
+ function Select({
1050
+ value,
1051
+ onChange,
1052
+ options,
1053
+ label,
1054
+ placeholder,
1055
+ helperText,
1056
+ errorText,
1057
+ disabled = false,
1058
+ size = "md",
1059
+ fullWidth = false,
1060
+ style: userStyle
1061
+ }) {
1062
+ const [modalVisible, setModalVisible] = useState2(false);
1063
+ const hasError = Boolean(errorText);
1064
+ const config = sizeStyles3(size);
1065
+ const selectedOption = options.find((opt) => opt.value === value);
1066
+ const displayText = selectedOption ? selectedOption.label : placeholder || "Select...";
1067
+ const handleSelect = (option) => {
1068
+ if (!option.disabled) {
1069
+ onChange(option.value);
1070
+ setModalVisible(false);
1071
+ }
1072
+ };
1073
+ return /* @__PURE__ */ jsxs9(View11, { style: [styles11.container, fullWidth && { width: "100%" }, userStyle], children: [
1074
+ label && /* @__PURE__ */ jsx11(Text10, { style: styles11.label, children: label }),
1075
+ /* @__PURE__ */ jsxs9(
1076
+ TouchableOpacity7,
1077
+ {
1078
+ style: [
1079
+ styles11.select,
1080
+ config,
1081
+ hasError && { borderColor: tokens.colorError },
1082
+ disabled && { opacity: 0.5 }
1083
+ ],
1084
+ onPress: () => !disabled && setModalVisible(true),
1085
+ disabled,
1086
+ activeOpacity: 0.8,
1087
+ children: [
1088
+ /* @__PURE__ */ jsx11(Text10, { style: [styles11.selectText, { fontSize: config.fontSize, color: selectedOption ? tokens.colorFg : tokens.colorMuted }], children: displayText }),
1089
+ /* @__PURE__ */ jsx11(Text10, { style: styles11.arrow, children: "\u25BC" })
1090
+ ]
1091
+ }
1092
+ ),
1093
+ (errorText || helperText) && /* @__PURE__ */ jsx11(Text10, { style: [styles11.helper, hasError && { color: tokens.colorError }], children: errorText ?? helperText }),
1094
+ /* @__PURE__ */ jsx11(
1095
+ Modal,
1096
+ {
1097
+ visible: modalVisible,
1098
+ transparent: true,
1099
+ animationType: "slide",
1100
+ onRequestClose: () => setModalVisible(false),
1101
+ children: /* @__PURE__ */ jsx11(View11, { style: styles11.modalOverlay, children: /* @__PURE__ */ jsxs9(View11, { style: styles11.modalContent, children: [
1102
+ /* @__PURE__ */ jsx11(
1103
+ FlatList,
1104
+ {
1105
+ data: options,
1106
+ keyExtractor: (item) => item.value,
1107
+ renderItem: ({ item }) => /* @__PURE__ */ jsx11(
1108
+ TouchableOpacity7,
1109
+ {
1110
+ style: [styles11.option, item.disabled && styles11.disabledOption],
1111
+ onPress: () => handleSelect(item),
1112
+ disabled: item.disabled,
1113
+ children: /* @__PURE__ */ jsx11(Text10, { style: [styles11.optionText, item.disabled && styles11.disabledText], children: item.label })
1114
+ }
1115
+ )
1116
+ }
1117
+ ),
1118
+ /* @__PURE__ */ jsx11(TouchableOpacity7, { style: styles11.closeButton, onPress: () => setModalVisible(false), children: /* @__PURE__ */ jsx11(Text10, { style: styles11.closeText, children: "Close" }) })
1119
+ ] }) })
1120
+ }
1121
+ )
1122
+ ] });
1123
+ }
1124
+ var styles11 = StyleSheet11.create({
1125
+ container: {
1126
+ marginBottom: tokens.space2
1127
+ },
1128
+ label: {
1129
+ marginBottom: tokens.space1,
1130
+ fontSize: tokens.fontsizeSm,
1131
+ fontWeight: "500",
1132
+ color: tokens.colorFg
1133
+ },
1134
+ select: {
1135
+ flexDirection: "row",
1136
+ alignItems: "center",
1137
+ justifyContent: "space-between",
1138
+ borderWidth: 1,
1139
+ borderColor: tokens.cBorder,
1140
+ borderRadius: tokens.radiusSm,
1141
+ backgroundColor: tokens.colorBg
1142
+ },
1143
+ selectText: {
1144
+ flex: 1,
1145
+ color: tokens.colorFg
1146
+ },
1147
+ arrow: {
1148
+ marginRight: tokens.space3,
1149
+ fontSize: 12,
1150
+ color: tokens.colorMuted
1151
+ },
1152
+ helper: {
1153
+ marginTop: tokens.space1,
1154
+ fontSize: tokens.fontsizeXs,
1155
+ color: tokens.colorMuted,
1156
+ lineHeight: 14
1157
+ },
1158
+ modalOverlay: {
1159
+ flex: 1,
1160
+ justifyContent: "flex-end",
1161
+ backgroundColor: "rgba(0,0,0,0.5)"
1162
+ },
1163
+ modalContent: {
1164
+ backgroundColor: tokens.colorBg,
1165
+ borderTopLeftRadius: tokens.radiusMd,
1166
+ borderTopRightRadius: tokens.radiusMd,
1167
+ maxHeight: "50%"
1168
+ },
1169
+ option: {
1170
+ padding: tokens.space4,
1171
+ borderBottomWidth: 1,
1172
+ borderBottomColor: tokens.cBorder
1173
+ },
1174
+ optionText: {
1175
+ fontSize: tokens.fontsizeSm,
1176
+ color: tokens.colorFg
1177
+ },
1178
+ disabledOption: {
1179
+ opacity: 0.5
1180
+ },
1181
+ disabledText: {
1182
+ color: tokens.colorMuted
1183
+ },
1184
+ closeButton: {
1185
+ padding: tokens.space4,
1186
+ alignItems: "center",
1187
+ backgroundColor: tokens.cSurfaceElevated
1188
+ },
1189
+ closeText: {
1190
+ fontSize: tokens.fontsizeSm,
1191
+ color: tokens.colorBrand,
1192
+ fontWeight: "600"
1193
+ }
1194
+ });
1195
+
1196
+ // src/components/ui/textarea/Textarea.tsx
1197
+ import { View as View12, Text as Text11, TextInput as TextInput2, StyleSheet as StyleSheet12 } from "react-native";
1198
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1199
+ var sizeStyles4 = (size) => {
1200
+ switch (size) {
1201
+ case "sm":
1202
+ return { paddingHorizontal: tokens.space3, paddingVertical: tokens.space2, fontSize: tokens.fontsizeXs };
1203
+ case "md":
1204
+ return { paddingHorizontal: tokens.space4, paddingVertical: tokens.space3, fontSize: tokens.fontsizeSm };
1205
+ case "lg":
1206
+ return { paddingHorizontal: tokens.space5, paddingVertical: tokens.space4, fontSize: tokens.fontsizeMedium };
1207
+ default:
1208
+ return {};
1209
+ }
1210
+ };
1211
+ function Textarea({
1212
+ value,
1213
+ onChangeText,
1214
+ label,
1215
+ placeholder,
1216
+ helperText,
1217
+ errorText,
1218
+ disabled = false,
1219
+ editable = true,
1220
+ size = "md",
1221
+ fullWidth = false,
1222
+ numberOfLines = 4,
1223
+ maxLength,
1224
+ style: userStyle,
1225
+ inputStyle: userInputStyle
1226
+ }) {
1227
+ const hasError = Boolean(errorText);
1228
+ const isEditable = editable && !disabled;
1229
+ const config = sizeStyles4(size);
1230
+ const isAtLimit = maxLength !== void 0 && value.length >= maxLength;
1231
+ return /* @__PURE__ */ jsxs10(View12, { style: [styles12.container, fullWidth && { width: "100%" }, userStyle], children: [
1232
+ label && /* @__PURE__ */ jsx12(Text11, { style: styles12.label, children: label }),
1233
+ /* @__PURE__ */ jsx12(
1234
+ TextInput2,
1235
+ {
1236
+ value,
1237
+ onChangeText,
1238
+ placeholder,
1239
+ placeholderTextColor: tokens.colorMuted,
1240
+ multiline: true,
1241
+ numberOfLines,
1242
+ editable: isEditable,
1243
+ maxLength,
1244
+ style: [
1245
+ styles12.input,
1246
+ config,
1247
+ { borderColor: hasError ? tokens.colorError : tokens.cBorder },
1248
+ !isEditable && { backgroundColor: tokens.colorMuted },
1249
+ userInputStyle
1250
+ ]
1251
+ }
1252
+ ),
1253
+ /* @__PURE__ */ jsxs10(View12, { style: styles12.footer, children: [
1254
+ (errorText || helperText) && /* @__PURE__ */ jsx12(Text11, { style: [styles12.helper, hasError && { color: tokens.colorError }], children: errorText ?? helperText }),
1255
+ maxLength !== void 0 && /* @__PURE__ */ jsxs10(Text11, { style: [styles12.counter, isAtLimit && { color: tokens.colorError }], children: [
1256
+ value.length,
1257
+ "/",
1258
+ maxLength
1259
+ ] })
1260
+ ] })
1261
+ ] });
1262
+ }
1263
+ var styles12 = StyleSheet12.create({
1264
+ container: {
1265
+ marginBottom: tokens.space2
1266
+ },
1267
+ label: {
1268
+ marginBottom: tokens.space1,
1269
+ fontSize: tokens.fontsizeSm,
1270
+ fontWeight: "500",
1271
+ color: tokens.colorFg
1272
+ },
1273
+ input: {
1274
+ borderWidth: 1,
1275
+ borderRadius: tokens.radiusSm,
1276
+ backgroundColor: tokens.colorBg,
1277
+ color: tokens.colorFg,
1278
+ textAlignVertical: "top",
1279
+ minHeight: 80
1280
+ },
1281
+ footer: {
1282
+ flexDirection: "row",
1283
+ justifyContent: "space-between",
1284
+ alignItems: "flex-start",
1285
+ marginTop: tokens.space1
1286
+ },
1287
+ helper: {
1288
+ flex: 1,
1289
+ fontSize: tokens.fontsizeXs,
1290
+ color: tokens.colorMuted,
1291
+ lineHeight: 14
1292
+ },
1293
+ counter: {
1294
+ fontSize: tokens.fontsizeXs,
1295
+ color: tokens.colorMuted,
1296
+ marginLeft: tokens.space2
1297
+ }
1298
+ });
1299
+
1300
+ // src/components/ui/accordion/Accordion.tsx
1301
+ import React5, { useState as useState3, useRef as useRef2 } from "react";
1302
+ import { View as View13, TouchableOpacity as TouchableOpacity8, Text as Text12, Animated as Animated3, StyleSheet as StyleSheet13 } from "react-native";
1303
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1304
+ var sizeMap6 = {
1305
+ sm: { paddingVertical: 8, paddingHorizontal: 12, fontSize: tokens.fontsizeXs },
1306
+ md: { paddingVertical: 12, paddingHorizontal: 16, fontSize: tokens.fontsizeSm },
1307
+ lg: { paddingVertical: 16, paddingHorizontal: 20, fontSize: tokens.fontsizeMedium }
1308
+ };
1309
+ var getVariantStyles2 = (variant) => {
1310
+ switch (variant) {
1311
+ case "bordered":
1312
+ return {
1313
+ container: { borderWidth: 1, borderColor: tokens.cBorder, borderRadius: tokens.radiusMd, overflow: "hidden" },
1314
+ item: {},
1315
+ separator: { borderTopWidth: 1, borderTopColor: tokens.cBorder }
1316
+ };
1317
+ case "separated":
1318
+ return {
1319
+ container: { gap: 8 },
1320
+ item: { borderWidth: 1, borderColor: tokens.cBorder, borderRadius: tokens.radiusMd, overflow: "hidden" },
1321
+ separator: {}
1322
+ };
1323
+ case "ghost":
1324
+ return {
1325
+ container: {},
1326
+ item: {},
1327
+ separator: { borderTopWidth: 1, borderTopColor: tokens.cBorder }
1328
+ };
1329
+ default:
1330
+ return {
1331
+ container: {},
1332
+ item: {},
1333
+ separator: {}
1334
+ };
1335
+ }
1336
+ };
1337
+ function Accordion({
1338
+ children,
1339
+ variant = "bordered",
1340
+ size = "md",
1341
+ style: userStyle
1342
+ }) {
1343
+ const variantStyles3 = getVariantStyles2(variant);
1344
+ return /* @__PURE__ */ jsx13(View13, { style: [variantStyles3.container, userStyle], children: React5.Children.map(children, (child, index) => /* @__PURE__ */ jsxs11(View13, { children: [
1345
+ index > 0 && /* @__PURE__ */ jsx13(View13, { style: variantStyles3.separator }),
1346
+ child
1347
+ ] }, index)) });
1348
+ }
1349
+ function AccordionItem({
1350
+ title,
1351
+ children,
1352
+ open: controlledOpen,
1353
+ onToggle,
1354
+ disabled = false,
1355
+ icon,
1356
+ style: userStyle
1357
+ }) {
1358
+ const [internalOpen, setInternalOpen] = useState3(false);
1359
+ const isControlled = controlledOpen !== void 0;
1360
+ const isOpen = isControlled ? controlledOpen : internalOpen;
1361
+ const animatedHeight = useRef2(new Animated3.Value(0)).current;
1362
+ const toggle = () => {
1363
+ if (disabled) return;
1364
+ const newOpen = !isOpen;
1365
+ if (isControlled) {
1366
+ onToggle?.(newOpen);
1367
+ } else {
1368
+ setInternalOpen(newOpen);
1369
+ }
1370
+ };
1371
+ React5.useEffect(() => {
1372
+ Animated3.timing(animatedHeight, {
1373
+ toValue: isOpen ? 1 : 0,
1374
+ duration: 200,
1375
+ useNativeDriver: false
1376
+ }).start();
1377
+ }, [isOpen, animatedHeight]);
1378
+ const contentHeight = animatedHeight.interpolate({
1379
+ inputRange: [0, 1],
1380
+ outputRange: [0, 200]
1381
+ // Adjust based on content
1382
+ });
1383
+ return /* @__PURE__ */ jsxs11(View13, { style: [styles13.item, userStyle], children: [
1384
+ /* @__PURE__ */ jsxs11(
1385
+ TouchableOpacity8,
1386
+ {
1387
+ style: [styles13.header, disabled && styles13.disabled],
1388
+ onPress: toggle,
1389
+ disabled,
1390
+ activeOpacity: 0.8,
1391
+ children: [
1392
+ /* @__PURE__ */ jsxs11(View13, { style: styles13.titleContainer, children: [
1393
+ icon && /* @__PURE__ */ jsx13(View13, { style: styles13.icon, children: icon }),
1394
+ /* @__PURE__ */ jsx13(Text12, { style: [styles13.title, disabled && styles13.disabledText], children: title })
1395
+ ] }),
1396
+ /* @__PURE__ */ jsx13(Text12, { style: [styles13.arrow, { transform: [{ rotate: isOpen ? "180deg" : "0deg" }] }], children: "\u25BC" })
1397
+ ]
1398
+ }
1399
+ ),
1400
+ /* @__PURE__ */ jsx13(Animated3.View, { style: [styles13.content, { height: contentHeight }], children: /* @__PURE__ */ jsx13(View13, { style: styles13.contentInner, children }) })
1401
+ ] });
1402
+ }
1403
+ var styles13 = StyleSheet13.create({
1404
+ item: {
1405
+ overflow: "hidden"
1406
+ },
1407
+ header: {
1408
+ flexDirection: "row",
1409
+ alignItems: "center",
1410
+ justifyContent: "space-between",
1411
+ paddingVertical: 12,
1412
+ paddingHorizontal: 16,
1413
+ backgroundColor: "transparent"
1414
+ },
1415
+ titleContainer: {
1416
+ flexDirection: "row",
1417
+ alignItems: "center",
1418
+ flex: 1
1419
+ },
1420
+ icon: {
1421
+ marginRight: 8
1422
+ },
1423
+ title: {
1424
+ fontSize: tokens.fontsizeSm,
1425
+ fontWeight: "500",
1426
+ color: tokens.colorFg,
1427
+ flex: 1
1428
+ },
1429
+ arrow: {
1430
+ fontSize: 12,
1431
+ color: tokens.colorMuted
1432
+ },
1433
+ disabled: {
1434
+ opacity: 0.45
1435
+ },
1436
+ disabledText: {
1437
+ opacity: 0.45
1438
+ },
1439
+ content: {
1440
+ overflow: "hidden"
1441
+ },
1442
+ contentInner: {
1443
+ paddingHorizontal: 16,
1444
+ paddingBottom: 12
1445
+ }
1446
+ });
1447
+
1448
+ // src/components/ui/callout/Callout.tsx
1449
+ import { View as View14, Text as Text13, StyleSheet as StyleSheet14 } from "react-native";
1450
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1451
+ var variantMap4 = {
1452
+ info: {
1453
+ bg: tokens.cBrandSubtle,
1454
+ border: tokens.cBrandBorder,
1455
+ accent: tokens.colorBrand,
1456
+ icon: "\u2139\uFE0F",
1457
+ defaultTitle: "Info"
1458
+ },
1459
+ warning: {
1460
+ bg: tokens.cWarningSubtle,
1461
+ border: tokens.cWarningBorder,
1462
+ accent: tokens.colorWarning,
1463
+ icon: "\u26A0\uFE0F",
1464
+ defaultTitle: "Attention"
1465
+ },
1466
+ tip: {
1467
+ bg: tokens.cSuccessSubtle,
1468
+ border: tokens.cSuccessBorder,
1469
+ accent: tokens.colorSuccess,
1470
+ icon: "\u{1F4A1}",
1471
+ defaultTitle: "Astuce"
1472
+ },
1473
+ danger: {
1474
+ bg: tokens.cBrandSubtle,
1475
+ border: tokens.cBrandBorder,
1476
+ accent: tokens.colorError,
1477
+ icon: "\u{1F6A8}",
1478
+ defaultTitle: "Danger"
1479
+ }
1480
+ };
1481
+ function Callout({
1482
+ variant = "info",
1483
+ title,
1484
+ children,
1485
+ style: userStyle
1486
+ }) {
1487
+ const config = variantMap4[variant];
1488
+ const displayTitle = title ?? config.defaultTitle;
1489
+ return /* @__PURE__ */ jsxs12(View14, { style: [styles14.callout, { backgroundColor: config.bg, borderColor: config.border }, userStyle], children: [
1490
+ /* @__PURE__ */ jsx14(Text13, { style: styles14.icon, children: config.icon }),
1491
+ /* @__PURE__ */ jsxs12(View14, { style: styles14.content, children: [
1492
+ /* @__PURE__ */ jsx14(Text13, { style: [styles14.title, { color: config.accent, marginBottom: children ? tokens.space1 : 0 }], children: displayTitle }),
1493
+ children && /* @__PURE__ */ jsx14(Text13, { style: styles14.body, children })
1494
+ ] })
1495
+ ] });
1496
+ }
1497
+ var styles14 = StyleSheet14.create({
1498
+ callout: {
1499
+ borderWidth: 1,
1500
+ borderLeftWidth: 3,
1501
+ borderRadius: tokens.radiusMd,
1502
+ padding: tokens.space3,
1503
+ flexDirection: "row",
1504
+ alignItems: "flex-start"
1505
+ },
1506
+ icon: {
1507
+ fontSize: 16,
1508
+ marginRight: tokens.space3,
1509
+ marginTop: 2
1510
+ },
1511
+ content: {
1512
+ flex: 1
1513
+ },
1514
+ title: {
1515
+ fontSize: tokens.fontsizeSm,
1516
+ fontWeight: "600"
1517
+ },
1518
+ body: {
1519
+ fontSize: tokens.fontsizeSm,
1520
+ color: tokens.colorFg,
1521
+ lineHeight: 20
1522
+ }
1523
+ });
1524
+
1525
+ // src/components/ui/status_label/StatusLabel.tsx
1526
+ import { View as View15, Text as Text14, StyleSheet as StyleSheet15 } from "react-native";
1527
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1528
+ var statusConfig = {
1529
+ active: { icon: "\u2705", color: tokens.colorSuccess, defaultLabel: "Active" },
1530
+ inactive: { icon: "\u274C", color: tokens.colorMuted, defaultLabel: "Inactive" },
1531
+ pending: { icon: "\u23F3", color: tokens.colorWarning, defaultLabel: "Pending" },
1532
+ error: { icon: "\u26A0\uFE0F", color: tokens.colorError, defaultLabel: "Error" }
1533
+ };
1534
+ function StatusLabel({
1535
+ status,
1536
+ label,
1537
+ style: userStyle
1538
+ }) {
1539
+ const config = statusConfig[status];
1540
+ const displayLabel = label ?? config.defaultLabel;
1541
+ return /* @__PURE__ */ jsxs13(View15, { style: [styles15.container, userStyle], children: [
1542
+ /* @__PURE__ */ jsx15(Text14, { style: styles15.icon, children: config.icon }),
1543
+ /* @__PURE__ */ jsx15(Text14, { style: [styles15.label, { color: config.color }], children: displayLabel })
1544
+ ] });
1545
+ }
1546
+ var styles15 = StyleSheet15.create({
1547
+ container: {
1548
+ flexDirection: "row",
1549
+ alignItems: "center",
1550
+ gap: 6
1551
+ },
1552
+ icon: {
1553
+ fontSize: 14
1554
+ },
1555
+ label: {
1556
+ fontSize: 13,
1557
+ fontWeight: "500"
1558
+ }
1559
+ });
1560
+
1561
+ // src/components/ui/tag_chip/TagChip.tsx
1562
+ import { View as View16, Text as Text15, StyleSheet as StyleSheet16 } from "react-native";
1563
+ import { jsx as jsx16 } from "react/jsx-runtime";
1564
+ var variantStyles2 = {
1565
+ default: { color: tokens.colorFg, backgroundColor: tokens.cSurfaceElevated },
1566
+ brand: { color: tokens.colorBrand, backgroundColor: tokens.cBrandSubtle },
1567
+ muted: { color: tokens.colorMuted, backgroundColor: "transparent" }
1568
+ };
1569
+ var sizeStyles5 = {
1570
+ sm: { fontSize: tokens.fontsizeXs, paddingHorizontal: 8, paddingVertical: 2 },
1571
+ md: { fontSize: tokens.fontsizeXs, paddingHorizontal: 10, paddingVertical: 3 }
1572
+ };
1573
+ function TagChip({
1574
+ label,
1575
+ variant = "default",
1576
+ size = "md",
1577
+ style: userStyle,
1578
+ textStyle: userTextStyle
1579
+ }) {
1580
+ const variantStyle = variantStyles2[variant];
1581
+ const sizeStyle = sizeStyles5[size];
1582
+ return /* @__PURE__ */ jsx16(View16, { style: [styles16.chip, variantStyle, sizeStyle, userStyle], children: /* @__PURE__ */ jsx16(Text15, { style: [styles16.label, { color: variantStyle.color }, userTextStyle], children: label }) });
1583
+ }
1584
+ var styles16 = StyleSheet16.create({
1585
+ chip: {
1586
+ borderWidth: 1,
1587
+ borderColor: tokens.cBorder,
1588
+ borderRadius: tokens.radiusSm,
1589
+ alignSelf: "flex-start"
1590
+ },
1591
+ label: {
1592
+ fontWeight: "500"
1593
+ }
1594
+ });
1595
+
1596
+ // src/components/typographie/text/Text.tsx
1597
+ import { Text as RNText } from "react-native";
1598
+ import { jsx as jsx17 } from "react/jsx-runtime";
1599
+ var textConfig = {
1600
+ "body-sm": {
1601
+ fontSize: tokens.fontsizeMedium,
1602
+ fontWeight: tokens.fontweightMedium
1603
+ },
1604
+ "body-md": {
1605
+ fontSize: tokens.fontsizeXl,
1606
+ fontWeight: tokens.fontweightMedium
1607
+ },
1608
+ "body-lg": {
1609
+ fontSize: tokens.fontsize2xl,
1610
+ fontWeight: tokens.fontweightMedium
1611
+ },
1612
+ caption: {
1613
+ fontSize: tokens.fontsizeXs,
1614
+ fontWeight: tokens.fontweightLight
1615
+ }
1616
+ };
1617
+ var getToneColor = (tone) => {
1618
+ switch (tone) {
1619
+ case "muted":
1620
+ return tokens.colorMuted;
1621
+ case "brand":
1622
+ return tokens.colorBrand;
1623
+ default:
1624
+ return tokens.colorFg;
1625
+ }
1626
+ };
1627
+ function Text16({
1628
+ texte,
1629
+ variant = "body-sm",
1630
+ align = "left",
1631
+ tone = "default",
1632
+ opacity,
1633
+ blurPx,
1634
+ style: userStyle
1635
+ }) {
1636
+ const config = textConfig[variant] || textConfig["body-sm"];
1637
+ const color = getToneColor(tone);
1638
+ const textStyle = {
1639
+ ...config,
1640
+ color,
1641
+ textAlign: align,
1642
+ opacity
1643
+ // Note: blur is not directly supported in RN Text, might need a wrapper View with blur
1644
+ };
1645
+ return /* @__PURE__ */ jsx17(RNText, { style: [textStyle, userStyle], children: texte });
1646
+ }
1647
+
1648
+ // src/components/typographie/heading/Heading.tsx
1649
+ import { Text as RNText2 } from "react-native";
1650
+ import { jsx as jsx18 } from "react/jsx-runtime";
1651
+ var headingConfig = {
1652
+ 1: {
1653
+ fontSize: tokens.fontsize5xl,
1654
+ fontWeight: tokens.fontweightBlack
1655
+ },
1656
+ 2: {
1657
+ fontSize: tokens.fontsize4xl,
1658
+ fontWeight: tokens.fontweightExtrabold
1659
+ },
1660
+ 3: {
1661
+ fontSize: tokens.fontsize3xl,
1662
+ fontWeight: tokens.fontweightBold
1663
+ },
1664
+ 4: {
1665
+ fontSize: tokens.fontsize2xl,
1666
+ fontWeight: tokens.fontweightSemibold
1667
+ },
1668
+ 5: {
1669
+ fontSize: tokens.fontsizeXl,
1670
+ fontWeight: tokens.fontweightBold
1671
+ },
1672
+ 6: {
1673
+ fontSize: tokens.fontsizeLg,
1674
+ fontWeight: tokens.fontweightMedium
1675
+ }
1676
+ };
1677
+ var getToneColor2 = (tone) => {
1678
+ return tone === "muted" ? tokens.colorMuted : tokens.colorBrand;
1679
+ };
1680
+ function Heading({
1681
+ title,
1682
+ level = 1,
1683
+ align = "left",
1684
+ opacity = 1,
1685
+ blurPx = 0,
1686
+ tone = "brand",
1687
+ style: userStyle
1688
+ }) {
1689
+ const config = headingConfig[level];
1690
+ const color = getToneColor2(tone);
1691
+ const safeOpacity = Math.min(Math.max(opacity, 0), 1);
1692
+ const textStyle = {
1693
+ ...config,
1694
+ color,
1695
+ textAlign: align,
1696
+ opacity: safeOpacity,
1697
+ margin: tokens.space2
1698
+ };
1699
+ return /* @__PURE__ */ jsx18(RNText2, { style: [textStyle, userStyle], children: title });
1700
+ }
1701
+
1702
+ // src/components/layout/app_shell/AppShell.tsx
1703
+ import { SafeAreaView, View as View17, StyleSheet as StyleSheet17 } from "react-native";
1704
+ import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
1705
+ function AppShell({
1706
+ header,
1707
+ sidebar,
1708
+ footer,
1709
+ children,
1710
+ sidebarWidth = 232,
1711
+ headerHeight = 60,
1712
+ footerHeight = 60,
1713
+ style: userStyle
1714
+ }) {
1715
+ return /* @__PURE__ */ jsxs14(SafeAreaView, { style: [styles17.container, userStyle], children: [
1716
+ header && /* @__PURE__ */ jsx19(View17, { style: [styles17.header, { height: headerHeight }], children: header }),
1717
+ /* @__PURE__ */ jsxs14(View17, { style: styles17.content, children: [
1718
+ sidebar && /* @__PURE__ */ jsx19(View17, { style: [styles17.sidebar, { width: sidebarWidth }], children: sidebar }),
1719
+ /* @__PURE__ */ jsx19(View17, { style: styles17.main, children })
1720
+ ] }),
1721
+ footer && /* @__PURE__ */ jsx19(View17, { style: [styles17.footer, { height: footerHeight }], children: footer })
1722
+ ] });
1723
+ }
1724
+ var styles17 = StyleSheet17.create({
1725
+ container: {
1726
+ flex: 1,
1727
+ backgroundColor: tokens.colorBg
1728
+ },
1729
+ header: {
1730
+ backgroundColor: tokens.colorBg
1731
+ },
1732
+ content: {
1733
+ flex: 1,
1734
+ flexDirection: "row"
1735
+ },
1736
+ sidebar: {
1737
+ backgroundColor: tokens.colorBg,
1738
+ borderRightWidth: 1,
1739
+ borderRightColor: tokens.colorMuted
1740
+ },
1741
+ main: {
1742
+ flex: 1,
1743
+ padding: tokens.space6
1744
+ },
1745
+ footer: {
1746
+ backgroundColor: tokens.colorBg,
1747
+ borderTopWidth: 1,
1748
+ borderTopColor: tokens.colorMuted
1749
+ }
1750
+ });
1751
+
1752
+ // src/components/layout/screen/Screen.tsx
1753
+ import { SafeAreaView as SafeAreaView2, ScrollView as ScrollView2, View as View18, StyleSheet as StyleSheet18 } from "react-native";
1754
+ import { jsx as jsx20 } from "react/jsx-runtime";
1755
+ function Screen({ children, scrollable = false, style: userStyle }) {
1756
+ const content = scrollable ? /* @__PURE__ */ jsx20(ScrollView2, { contentInsetAdjustmentBehavior: "automatic", style: styles18.scroll, children }) : /* @__PURE__ */ jsx20(View18, { style: styles18.content, children });
1757
+ return /* @__PURE__ */ jsx20(SafeAreaView2, { style: [styles18.container, userStyle], children: content });
1758
+ }
1759
+ var styles18 = StyleSheet18.create({
1760
+ container: {
1761
+ flex: 1,
1762
+ backgroundColor: tokens.colorBg
1763
+ },
1764
+ content: {
1765
+ flex: 1
1766
+ },
1767
+ scroll: {
1768
+ flex: 1
1769
+ }
1770
+ });
1771
+
1772
+ // src/components/ui/card/Card.tsx
1773
+ import { View as View19 } from "react-native";
1774
+ import { jsx as jsx21 } from "react/jsx-runtime";
1775
+ function Card({
1776
+ children,
1777
+ style,
1778
+ variant = "default",
1779
+ padding = "md"
1780
+ }) {
1781
+ const paddingValue = padding === "sm" ? tokens.space2 : padding === "lg" ? tokens.space6 : tokens.space4;
1782
+ const cardStyle = {
1783
+ backgroundColor: tokens.colorBg,
1784
+ borderRadius: tokens.radiusMd,
1785
+ padding: paddingValue,
1786
+ ...getVariantStyle(variant),
1787
+ ...style
1788
+ };
1789
+ return /* @__PURE__ */ jsx21(View19, { style: cardStyle, children });
1790
+ }
1791
+ function getVariantStyle(variant) {
1792
+ switch (variant) {
1793
+ case "elevated":
1794
+ return {
1795
+ shadowColor: "#000",
1796
+ shadowOffset: { width: 0, height: 2 },
1797
+ shadowOpacity: 0.1,
1798
+ shadowRadius: 4,
1799
+ elevation: 3
1800
+ };
1801
+ case "outlined":
1802
+ return {
1803
+ borderWidth: 1,
1804
+ borderColor: tokens.cBorder
1805
+ };
1806
+ default:
1807
+ return {};
1808
+ }
1809
+ }
1810
+ export {
1811
+ Accordion,
1812
+ AccordionItem,
1813
+ Alert,
1814
+ AppShell,
1815
+ Badge,
1816
+ Button,
1817
+ Callout,
1818
+ Card,
1819
+ Checkbox,
1820
+ Heading,
1821
+ Input,
1822
+ ProgressBar,
1823
+ Radio,
1824
+ RadioGroup,
1825
+ Screen,
1826
+ Select,
1827
+ Spinner,
1828
+ StatusLabel,
1829
+ TabPanel,
1830
+ Tabs,
1831
+ TagChip,
1832
+ Text16 as Text,
1833
+ Textarea,
1834
+ ToggleSwitch,
1835
+ tokens
1836
+ };