@retray-dev/ui-kit 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/COMPONENTS.md +654 -0
  2. package/LICENSE +21 -0
  3. package/README.md +151 -0
  4. package/dist/index.d.mts +309 -3
  5. package/dist/index.d.ts +309 -3
  6. package/dist/index.js +1477 -57
  7. package/dist/index.mjs +1424 -57
  8. package/package.json +27 -5
  9. package/src/components/Accordion/Accordion.tsx +161 -0
  10. package/src/components/Accordion/index.ts +2 -0
  11. package/src/components/Alert/Alert.tsx +57 -0
  12. package/src/components/Alert/index.ts +2 -0
  13. package/src/components/Avatar/Avatar.tsx +67 -0
  14. package/src/components/Avatar/index.ts +2 -0
  15. package/src/components/Badge/Badge.tsx +48 -0
  16. package/src/components/Badge/index.ts +2 -0
  17. package/src/components/Button/Button.tsx +78 -45
  18. package/src/components/Card/Card.tsx +109 -0
  19. package/src/components/Card/index.ts +9 -0
  20. package/src/components/Checkbox/Checkbox.tsx +70 -0
  21. package/src/components/Checkbox/index.ts +2 -0
  22. package/src/components/EmptyState/EmptyState.tsx +69 -0
  23. package/src/components/EmptyState/index.ts +2 -0
  24. package/src/components/Input/Input.tsx +26 -41
  25. package/src/components/Progress/Progress.tsx +53 -0
  26. package/src/components/Progress/index.ts +2 -0
  27. package/src/components/RadioGroup/RadioGroup.tsx +105 -0
  28. package/src/components/RadioGroup/index.ts +2 -0
  29. package/src/components/Select/Select.tsx +185 -0
  30. package/src/components/Select/index.ts +2 -0
  31. package/src/components/Separator/Separator.tsx +33 -0
  32. package/src/components/Separator/index.ts +2 -0
  33. package/src/components/Sheet/Sheet.tsx +108 -0
  34. package/src/components/Sheet/index.ts +2 -0
  35. package/src/components/Skeleton/Skeleton.tsx +40 -0
  36. package/src/components/Skeleton/index.ts +2 -0
  37. package/src/components/Slider/Slider.tsx +142 -0
  38. package/src/components/Slider/index.ts +2 -0
  39. package/src/components/Spinner/Spinner.tsx +27 -0
  40. package/src/components/Spinner/index.ts +2 -0
  41. package/src/components/Switch/Switch.tsx +82 -0
  42. package/src/components/Switch/index.ts +2 -0
  43. package/src/components/Tabs/Tabs.tsx +145 -0
  44. package/src/components/Tabs/index.ts +2 -0
  45. package/src/components/Text/Text.tsx +10 -4
  46. package/src/components/Textarea/Textarea.tsx +70 -0
  47. package/src/components/Textarea/index.ts +2 -0
  48. package/src/components/Toast/Toast.tsx +164 -0
  49. package/src/components/Toast/index.ts +2 -0
  50. package/src/components/Toggle/Toggle.tsx +80 -0
  51. package/src/components/Toggle/index.ts +2 -0
  52. package/src/index.ts +26 -0
  53. package/src/theme/ThemeProvider.tsx +47 -0
  54. package/src/theme/colors.ts +41 -0
  55. package/src/theme/index.ts +4 -0
  56. package/src/theme/types.ts +31 -0
package/dist/index.mjs CHANGED
@@ -1,28 +1,79 @@
1
- import React3, { useState } from 'react';
2
- import { StyleSheet, TouchableOpacity, ActivityIndicator, Text, View, TextInput } from 'react-native';
1
+ import React23, { createContext, useMemo, useContext, useRef, useState, useEffect, useCallback } from 'react';
2
+ import { StyleSheet, useColorScheme, Animated, TouchableOpacity, ActivityIndicator, Text, View, TextInput, Image, PanResponder, Modal, FlatList, Easing } from 'react-native';
3
+ import * as Haptics10 from 'expo-haptics';
4
+ import { BottomSheetModal, BottomSheetView, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
5
+ export { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
6
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
3
7
 
4
- // src/components/Button/Button.tsx
5
- var containerVariantStyles = {
6
- primary: { backgroundColor: "#000" },
7
- secondary: { backgroundColor: "#6B7280" },
8
- outline: { backgroundColor: "transparent", borderWidth: 1.5, borderColor: "#000" },
9
- ghost: { backgroundColor: "transparent" }
8
+ // src/theme/ThemeProvider.tsx
9
+
10
+ // src/theme/colors.ts
11
+ var defaultLight = {
12
+ background: "#ffffff",
13
+ foreground: "#171717",
14
+ card: "#ffffff",
15
+ cardForeground: "#171717",
16
+ primary: "#1a1a1a",
17
+ primaryForeground: "#fafafa",
18
+ secondary: "#f5f5f5",
19
+ secondaryForeground: "#1a1a1a",
20
+ muted: "#f5f5f5",
21
+ mutedForeground: "#646464",
22
+ accent: "#f5f5f5",
23
+ accentForeground: "#1a1a1a",
24
+ destructive: "#ef4444",
25
+ destructiveForeground: "#fafafa",
26
+ border: "#e5e5e5",
27
+ input: "#e5e5e5",
28
+ ring: "#a3a3a3"
10
29
  };
11
- var containerSizeStyles = {
12
- sm: { paddingHorizontal: 12, paddingVertical: 6 },
13
- md: { paddingHorizontal: 16, paddingVertical: 10 },
14
- lg: { paddingHorizontal: 24, paddingVertical: 14 }
30
+ var defaultDark = {
31
+ background: "#171717",
32
+ foreground: "#fafafa",
33
+ card: "#1f1f1f",
34
+ cardForeground: "#fafafa",
35
+ primary: "#fafafa",
36
+ primaryForeground: "#1a1a1a",
37
+ secondary: "#2a2a2a",
38
+ secondaryForeground: "#fafafa",
39
+ muted: "#2a2a2a",
40
+ mutedForeground: "#a3a3a3",
41
+ accent: "#2a2a2a",
42
+ accentForeground: "#fafafa",
43
+ destructive: "#dc2626",
44
+ destructiveForeground: "#fafafa",
45
+ border: "#2a2a2a",
46
+ input: "#2a2a2a",
47
+ ring: "#d4d4d4"
15
48
  };
16
- var labelVariantStyles = {
17
- primary: { color: "#fff" },
18
- secondary: { color: "#fff" },
19
- outline: { color: "#000" },
20
- ghost: { color: "#000" }
49
+
50
+ // src/theme/ThemeProvider.tsx
51
+ var ThemeContext = createContext({
52
+ colors: defaultLight,
53
+ colorScheme: "light"
54
+ });
55
+ function ThemeProvider({ children, theme, colorScheme = "system" }) {
56
+ const systemScheme = useColorScheme() ?? "light";
57
+ const resolvedScheme = colorScheme === "system" ? systemScheme : colorScheme;
58
+ const colors = useMemo(() => {
59
+ const base = resolvedScheme === "dark" ? defaultDark : defaultLight;
60
+ const overrides = resolvedScheme === "dark" ? theme?.dark : theme?.light;
61
+ return { ...base, ...overrides };
62
+ }, [resolvedScheme, theme]);
63
+ return /* @__PURE__ */ React23.createElement(ThemeContext.Provider, { value: { colors, colorScheme: resolvedScheme } }, children);
64
+ }
65
+ function useTheme() {
66
+ return useContext(ThemeContext);
67
+ }
68
+ var containerSizeStyles = {
69
+ sm: { paddingHorizontal: 16, paddingVertical: 10 },
70
+ md: { paddingHorizontal: 20, paddingVertical: 14 },
71
+ lg: { paddingHorizontal: 28, paddingVertical: 18 }
21
72
  };
22
73
  var labelSizeStyles = {
23
- sm: { fontSize: 13 },
24
- md: { fontSize: 15 },
25
- lg: { fontSize: 17 }
74
+ sm: { fontSize: 14 },
75
+ md: { fontSize: 16 },
76
+ lg: { fontSize: 18 }
26
77
  };
27
78
  function Button({
28
79
  label,
@@ -32,32 +83,56 @@ function Button({
32
83
  fullWidth = false,
33
84
  disabled,
34
85
  style,
86
+ onPress,
35
87
  ...props
36
88
  }) {
89
+ const { colors } = useTheme();
37
90
  const isDisabled = disabled || loading;
38
- return /* @__PURE__ */ React3.createElement(
91
+ const scale = useRef(new Animated.Value(1)).current;
92
+ const handlePressIn = () => {
93
+ if (isDisabled) return;
94
+ Animated.spring(scale, { toValue: 0.97, useNativeDriver: true, speed: 40, bounciness: 0 }).start();
95
+ };
96
+ const handlePressOut = () => {
97
+ Animated.spring(scale, { toValue: 1, useNativeDriver: true, speed: 40, bounciness: 2 }).start();
98
+ };
99
+ const handlePress = (e) => {
100
+ Haptics10.impactAsync(Haptics10.ImpactFeedbackStyle.Light);
101
+ onPress?.(e);
102
+ };
103
+ const containerVariantStyle = {
104
+ primary: { backgroundColor: colors.primary },
105
+ secondary: { backgroundColor: colors.secondary },
106
+ outline: { backgroundColor: "transparent", borderWidth: 1.5, borderColor: colors.border },
107
+ ghost: { backgroundColor: "transparent" }
108
+ }[variant];
109
+ const labelVariantStyle = {
110
+ primary: { color: colors.primaryForeground },
111
+ secondary: { color: colors.secondaryForeground },
112
+ outline: { color: colors.foreground },
113
+ ghost: { color: colors.foreground }
114
+ }[variant];
115
+ const spinnerColor = variant === "primary" || variant === "secondary" ? colors.primaryForeground : colors.foreground;
116
+ return /* @__PURE__ */ React23.createElement(Animated.View, { style: [fullWidth && styles.fullWidth, { transform: [{ scale }] }] }, /* @__PURE__ */ React23.createElement(
39
117
  TouchableOpacity,
40
118
  {
41
119
  style: [
42
120
  styles.base,
43
- containerVariantStyles[variant],
121
+ containerVariantStyle,
44
122
  containerSizeStyles[size],
45
123
  fullWidth && styles.fullWidth,
46
124
  isDisabled && styles.disabled,
47
125
  style
48
126
  ],
49
127
  disabled: isDisabled,
50
- activeOpacity: 0.75,
128
+ activeOpacity: 1,
129
+ onPress: handlePress,
130
+ onPressIn: handlePressIn,
131
+ onPressOut: handlePressOut,
51
132
  ...props
52
133
  },
53
- loading ? /* @__PURE__ */ React3.createElement(
54
- ActivityIndicator,
55
- {
56
- size: "small",
57
- color: variant === "outline" || variant === "ghost" ? "#000" : "#fff"
58
- }
59
- ) : /* @__PURE__ */ React3.createElement(Text, { style: [styles.label, labelVariantStyles[variant], labelSizeStyles[size]] }, label)
60
- );
134
+ loading ? /* @__PURE__ */ React23.createElement(ActivityIndicator, { size: "small", color: spinnerColor }) : /* @__PURE__ */ React23.createElement(Text, { style: [styles.label, labelVariantStyle, labelSizeStyles[size]] }, label)
135
+ ));
61
136
  }
62
137
  var styles = StyleSheet.create({
63
138
  base: {
@@ -77,32 +152,39 @@ var styles = StyleSheet.create({
77
152
  }
78
153
  });
79
154
  var variantStyles = {
80
- h1: { fontSize: 32, fontWeight: "700", lineHeight: 40 },
155
+ h1: { fontSize: 32, fontWeight: "700", lineHeight: 44 },
81
156
  h2: { fontSize: 24, fontWeight: "700", lineHeight: 32 },
82
157
  h3: { fontSize: 20, fontWeight: "600", lineHeight: 28 },
83
158
  body: { fontSize: 16, fontWeight: "400", lineHeight: 24 },
84
- caption: { fontSize: 12, fontWeight: "400", lineHeight: 18, color: "#6B7280" },
159
+ caption: { fontSize: 12, fontWeight: "400", lineHeight: 18 },
85
160
  label: { fontSize: 14, fontWeight: "500", lineHeight: 20 }
86
161
  };
87
162
  function Text2({ variant = "body", color, style, children, ...props }) {
88
- return /* @__PURE__ */ React3.createElement(
163
+ const { colors } = useTheme();
164
+ const defaultColor = variant === "caption" ? colors.mutedForeground : colors.foreground;
165
+ return /* @__PURE__ */ React23.createElement(
89
166
  Text,
90
167
  {
91
- style: [variantStyles[variant], color ? { color } : void 0, style],
168
+ style: [variantStyles[variant], { color: color ?? defaultColor }, style],
169
+ allowFontScaling: true,
92
170
  ...props
93
171
  },
94
172
  children
95
173
  );
96
174
  }
97
175
  function Input({ label, error, hint, style, onFocus, onBlur, ...props }) {
176
+ const { colors } = useTheme();
98
177
  const [focused, setFocused] = useState(false);
99
- return /* @__PURE__ */ React3.createElement(View, { style: styles2.container }, label ? /* @__PURE__ */ React3.createElement(Text, { style: styles2.label }, label) : null, /* @__PURE__ */ React3.createElement(
178
+ return /* @__PURE__ */ React23.createElement(View, { style: styles2.container }, label ? /* @__PURE__ */ React23.createElement(Text, { style: [styles2.label, { color: colors.foreground }] }, label) : null, /* @__PURE__ */ React23.createElement(
100
179
  TextInput,
101
180
  {
102
181
  style: [
103
182
  styles2.input,
104
- focused && styles2.inputFocused,
105
- error ? styles2.inputError : void 0,
183
+ {
184
+ borderColor: error ? colors.destructive : focused ? colors.ring : colors.border,
185
+ color: colors.foreground,
186
+ backgroundColor: colors.background
187
+ },
106
188
  style
107
189
  ],
108
190
  onFocus: (e) => {
@@ -113,10 +195,11 @@ function Input({ label, error, hint, style, onFocus, onBlur, ...props }) {
113
195
  setFocused(false);
114
196
  onBlur?.(e);
115
197
  },
116
- placeholderTextColor: "#9CA3AF",
198
+ placeholderTextColor: colors.mutedForeground,
199
+ allowFontScaling: true,
117
200
  ...props
118
201
  }
119
- ), error ? /* @__PURE__ */ React3.createElement(Text, { style: styles2.errorText }, error) : null, !error && hint ? /* @__PURE__ */ React3.createElement(Text, { style: styles2.hintText }, hint) : null);
202
+ ), error ? /* @__PURE__ */ React23.createElement(Text, { style: [styles2.helperText, { color: colors.destructive }] }, error) : null, !error && hint ? /* @__PURE__ */ React23.createElement(Text, { style: [styles2.helperText, { color: colors.mutedForeground }] }, hint) : null);
120
203
  }
121
204
  var styles2 = StyleSheet.create({
122
205
  container: {
@@ -125,33 +208,1317 @@ var styles2 = StyleSheet.create({
125
208
  label: {
126
209
  fontSize: 14,
127
210
  fontWeight: "500",
128
- color: "#111827",
129
- marginBottom: 2
211
+ marginBottom: 4
130
212
  },
131
213
  input: {
132
214
  borderWidth: 1.5,
133
- borderColor: "#D1D5DB",
134
215
  borderRadius: 8,
216
+ paddingHorizontal: 16,
217
+ paddingVertical: 14,
218
+ fontSize: 16
219
+ },
220
+ helperText: {
221
+ fontSize: 12
222
+ }
223
+ });
224
+ function Badge({ label, variant = "default", style }) {
225
+ const { colors } = useTheme();
226
+ const containerStyle = {
227
+ default: { backgroundColor: colors.primary },
228
+ secondary: { backgroundColor: colors.secondary },
229
+ destructive: { backgroundColor: colors.destructive },
230
+ outline: { backgroundColor: "transparent", borderWidth: 1, borderColor: colors.border }
231
+ }[variant];
232
+ const textColor = {
233
+ default: colors.primaryForeground,
234
+ secondary: colors.secondaryForeground,
235
+ destructive: colors.destructiveForeground,
236
+ outline: colors.foreground
237
+ }[variant];
238
+ return /* @__PURE__ */ React23.createElement(View, { style: [styles3.container, containerStyle, style] }, /* @__PURE__ */ React23.createElement(Text, { style: [styles3.label, { color: textColor }] }, label));
239
+ }
240
+ var styles3 = StyleSheet.create({
241
+ container: {
242
+ borderRadius: 6,
243
+ paddingHorizontal: 8,
244
+ paddingVertical: 2,
245
+ alignSelf: "flex-start"
246
+ },
247
+ label: {
248
+ fontSize: 12,
249
+ fontWeight: "500"
250
+ }
251
+ });
252
+ function Card({ children, style }) {
253
+ const { colors } = useTheme();
254
+ return /* @__PURE__ */ React23.createElement(
255
+ View,
256
+ {
257
+ style: [
258
+ styles4.card,
259
+ { backgroundColor: colors.card, borderColor: colors.border },
260
+ style
261
+ ]
262
+ },
263
+ children
264
+ );
265
+ }
266
+ function CardHeader({ children, style }) {
267
+ return /* @__PURE__ */ React23.createElement(View, { style: [styles4.header, style] }, children);
268
+ }
269
+ function CardTitle({ children, style }) {
270
+ const { colors } = useTheme();
271
+ return /* @__PURE__ */ React23.createElement(Text, { style: [styles4.title, { color: colors.cardForeground }, style] }, children);
272
+ }
273
+ function CardDescription({ children, style }) {
274
+ const { colors } = useTheme();
275
+ return /* @__PURE__ */ React23.createElement(Text, { style: [styles4.description, { color: colors.mutedForeground }, style] }, children);
276
+ }
277
+ function CardContent({ children, style }) {
278
+ return /* @__PURE__ */ React23.createElement(View, { style: [styles4.content, style] }, children);
279
+ }
280
+ function CardFooter({ children, style }) {
281
+ return /* @__PURE__ */ React23.createElement(View, { style: [styles4.footer, style] }, children);
282
+ }
283
+ var styles4 = StyleSheet.create({
284
+ card: {
285
+ borderRadius: 12,
286
+ borderWidth: 1,
287
+ shadowColor: "#000",
288
+ shadowOffset: { width: 0, height: 1 },
289
+ shadowOpacity: 0.05,
290
+ shadowRadius: 2,
291
+ elevation: 1
292
+ },
293
+ header: {
294
+ padding: 24,
295
+ paddingBottom: 0,
296
+ gap: 6
297
+ },
298
+ title: {
299
+ fontSize: 18,
300
+ fontWeight: "600",
301
+ lineHeight: 24
302
+ },
303
+ description: {
304
+ fontSize: 14,
305
+ lineHeight: 20
306
+ },
307
+ content: {
308
+ padding: 24
309
+ },
310
+ footer: {
311
+ padding: 24,
312
+ paddingTop: 0,
313
+ flexDirection: "row",
314
+ alignItems: "center"
315
+ }
316
+ });
317
+ function Separator({ orientation = "horizontal", style }) {
318
+ const { colors } = useTheme();
319
+ return /* @__PURE__ */ React23.createElement(
320
+ View,
321
+ {
322
+ style: [
323
+ orientation === "horizontal" ? styles5.horizontal : styles5.vertical,
324
+ { backgroundColor: colors.border },
325
+ style
326
+ ]
327
+ }
328
+ );
329
+ }
330
+ var styles5 = StyleSheet.create({
331
+ horizontal: {
332
+ height: 1,
333
+ width: "100%"
334
+ },
335
+ vertical: {
336
+ width: 1,
337
+ height: "100%"
338
+ }
339
+ });
340
+ var sizeMap = {
341
+ sm: "small",
342
+ md: "small",
343
+ lg: "large"
344
+ };
345
+ function Spinner({ size = "md", color, ...props }) {
346
+ const { colors } = useTheme();
347
+ return /* @__PURE__ */ React23.createElement(
348
+ ActivityIndicator,
349
+ {
350
+ size: sizeMap[size],
351
+ color: color ?? colors.primary,
352
+ ...props
353
+ }
354
+ );
355
+ }
356
+ function Skeleton({ width = "100%", height = 16, borderRadius = 6, style }) {
357
+ const { colors } = useTheme();
358
+ const opacity = useRef(new Animated.Value(1)).current;
359
+ useEffect(() => {
360
+ const animation = Animated.loop(
361
+ Animated.sequence([
362
+ Animated.timing(opacity, { toValue: 0.4, duration: 800, useNativeDriver: true }),
363
+ Animated.timing(opacity, { toValue: 1, duration: 800, useNativeDriver: true })
364
+ ])
365
+ );
366
+ animation.start();
367
+ return () => animation.stop();
368
+ }, [opacity]);
369
+ return /* @__PURE__ */ React23.createElement(
370
+ Animated.View,
371
+ {
372
+ style: [
373
+ styles6.base,
374
+ { width, height, borderRadius, backgroundColor: colors.muted, opacity },
375
+ style
376
+ ]
377
+ }
378
+ );
379
+ }
380
+ var styles6 = StyleSheet.create({
381
+ base: {}
382
+ });
383
+ var sizeMap2 = {
384
+ sm: 24,
385
+ md: 32,
386
+ lg: 48,
387
+ xl: 64
388
+ };
389
+ var fontSizeMap = {
390
+ sm: 10,
391
+ md: 13,
392
+ lg: 18,
393
+ xl: 24
394
+ };
395
+ function Avatar({ src, fallback, size = "md", style }) {
396
+ const { colors } = useTheme();
397
+ const [imageError, setImageError] = useState(false);
398
+ const dimension = sizeMap2[size];
399
+ const showFallback = !src || imageError;
400
+ const containerStyle = {
401
+ width: dimension,
402
+ height: dimension,
403
+ borderRadius: dimension / 2,
404
+ backgroundColor: colors.muted,
405
+ overflow: "hidden"
406
+ };
407
+ return /* @__PURE__ */ React23.createElement(View, { style: [styles7.base, containerStyle, style] }, !showFallback ? /* @__PURE__ */ React23.createElement(
408
+ Image,
409
+ {
410
+ source: { uri: src },
411
+ style: { width: dimension, height: dimension },
412
+ onError: () => setImageError(true)
413
+ }
414
+ ) : /* @__PURE__ */ React23.createElement(Text, { style: [styles7.fallback, { color: colors.mutedForeground, fontSize: fontSizeMap[size] }] }, fallback?.slice(0, 2).toUpperCase() ?? "?"));
415
+ }
416
+ var styles7 = StyleSheet.create({
417
+ base: {
418
+ alignItems: "center",
419
+ justifyContent: "center"
420
+ },
421
+ fallback: {
422
+ fontWeight: "500"
423
+ }
424
+ });
425
+ function Alert({ title, description, variant = "default", icon, style }) {
426
+ const { colors } = useTheme();
427
+ const borderColor = variant === "destructive" ? colors.destructive : colors.border;
428
+ const titleColor = variant === "destructive" ? colors.destructive : colors.foreground;
429
+ const descColor = variant === "destructive" ? colors.destructive : colors.mutedForeground;
430
+ return /* @__PURE__ */ React23.createElement(View, { style: [styles8.container, { backgroundColor: colors.card, borderColor }, style] }, icon ? /* @__PURE__ */ React23.createElement(View, { style: styles8.icon }, icon) : null, /* @__PURE__ */ React23.createElement(View, { style: styles8.content }, title ? /* @__PURE__ */ React23.createElement(Text, { style: [styles8.title, { color: titleColor }] }, title) : null, description ? /* @__PURE__ */ React23.createElement(Text, { style: [styles8.description, { color: descColor }] }, description) : null));
431
+ }
432
+ var styles8 = StyleSheet.create({
433
+ container: {
434
+ flexDirection: "row",
435
+ borderWidth: 1,
436
+ borderRadius: 8,
437
+ padding: 16,
438
+ gap: 12
439
+ },
440
+ icon: {
441
+ marginTop: 2
442
+ },
443
+ content: {
444
+ flex: 1,
445
+ gap: 4
446
+ },
447
+ title: {
448
+ fontSize: 14,
449
+ fontWeight: "500",
450
+ lineHeight: 20
451
+ },
452
+ description: {
453
+ fontSize: 14,
454
+ lineHeight: 20
455
+ }
456
+ });
457
+ function Progress({ value = 0, max = 100, style }) {
458
+ const { colors } = useTheme();
459
+ const percent = Math.min(Math.max(value / max * 100, 0), 100);
460
+ const [trackWidth, setTrackWidth] = useState(0);
461
+ const animatedWidth = useRef(new Animated.Value(0)).current;
462
+ useEffect(() => {
463
+ if (trackWidth === 0) return;
464
+ Animated.spring(animatedWidth, {
465
+ toValue: percent / 100 * trackWidth,
466
+ useNativeDriver: false,
467
+ speed: 20,
468
+ bounciness: 0
469
+ }).start();
470
+ }, [percent, trackWidth]);
471
+ return /* @__PURE__ */ React23.createElement(
472
+ View,
473
+ {
474
+ style: [styles9.track, { backgroundColor: colors.muted }, style],
475
+ onLayout: (e) => setTrackWidth(e.nativeEvent.layout.width)
476
+ },
477
+ /* @__PURE__ */ React23.createElement(
478
+ Animated.View,
479
+ {
480
+ style: [
481
+ styles9.indicator,
482
+ { width: animatedWidth, backgroundColor: colors.primary }
483
+ ]
484
+ }
485
+ )
486
+ );
487
+ }
488
+ var styles9 = StyleSheet.create({
489
+ track: {
490
+ height: 8,
491
+ borderRadius: 999,
492
+ overflow: "hidden",
493
+ width: "100%"
494
+ },
495
+ indicator: {
496
+ height: "100%",
497
+ borderRadius: 999
498
+ }
499
+ });
500
+ function EmptyState({ icon, title, description, action, style }) {
501
+ const { colors } = useTheme();
502
+ return /* @__PURE__ */ React23.createElement(View, { style: [styles10.container, { borderColor: colors.border }, style] }, icon ? /* @__PURE__ */ React23.createElement(View, { style: [styles10.iconWrapper, { backgroundColor: colors.muted }] }, icon) : null, /* @__PURE__ */ React23.createElement(View, { style: styles10.textWrapper }, /* @__PURE__ */ React23.createElement(Text, { style: [styles10.title, { color: colors.foreground }] }, title), description ? /* @__PURE__ */ React23.createElement(Text, { style: [styles10.description, { color: colors.mutedForeground }] }, description) : null), action ? /* @__PURE__ */ React23.createElement(View, { style: styles10.action }, action) : null);
503
+ }
504
+ var styles10 = StyleSheet.create({
505
+ container: {
506
+ alignItems: "center",
507
+ justifyContent: "center",
508
+ borderWidth: 1,
509
+ borderStyle: "dashed",
510
+ borderRadius: 12,
511
+ padding: 32,
512
+ gap: 16
513
+ },
514
+ iconWrapper: {
515
+ width: 48,
516
+ height: 48,
517
+ borderRadius: 12,
518
+ alignItems: "center",
519
+ justifyContent: "center"
520
+ },
521
+ textWrapper: {
522
+ alignItems: "center",
523
+ gap: 8,
524
+ maxWidth: 320
525
+ },
526
+ title: {
527
+ fontSize: 18,
528
+ fontWeight: "500",
529
+ textAlign: "center"
530
+ },
531
+ description: {
532
+ fontSize: 14,
533
+ lineHeight: 20,
534
+ textAlign: "center"
535
+ },
536
+ action: {
537
+ marginTop: 8
538
+ }
539
+ });
540
+ function Textarea({ label, error, hint, rows = 4, style, onFocus, onBlur, ...props }) {
541
+ const { colors } = useTheme();
542
+ const [focused, setFocused] = useState(false);
543
+ return /* @__PURE__ */ React23.createElement(View, { style: styles11.container }, label ? /* @__PURE__ */ React23.createElement(Text, { style: [styles11.label, { color: colors.foreground }] }, label) : null, /* @__PURE__ */ React23.createElement(
544
+ TextInput,
545
+ {
546
+ multiline: true,
547
+ numberOfLines: rows,
548
+ textAlignVertical: "top",
549
+ style: [
550
+ styles11.input,
551
+ {
552
+ borderColor: error ? colors.destructive : focused ? colors.ring : colors.border,
553
+ color: colors.foreground,
554
+ backgroundColor: colors.background,
555
+ minHeight: rows * 28
556
+ },
557
+ style
558
+ ],
559
+ onFocus: (e) => {
560
+ setFocused(true);
561
+ onFocus?.(e);
562
+ },
563
+ onBlur: (e) => {
564
+ setFocused(false);
565
+ onBlur?.(e);
566
+ },
567
+ placeholderTextColor: colors.mutedForeground,
568
+ allowFontScaling: true,
569
+ ...props
570
+ }
571
+ ), error ? /* @__PURE__ */ React23.createElement(Text, { style: [styles11.helperText, { color: colors.destructive }] }, error) : null, !error && hint ? /* @__PURE__ */ React23.createElement(Text, { style: [styles11.helperText, { color: colors.mutedForeground }] }, hint) : null);
572
+ }
573
+ var styles11 = StyleSheet.create({
574
+ container: {
575
+ gap: 4
576
+ },
577
+ label: {
578
+ fontSize: 14,
579
+ fontWeight: "500",
580
+ marginBottom: 4
581
+ },
582
+ input: {
583
+ borderWidth: 1.5,
584
+ borderRadius: 8,
585
+ paddingHorizontal: 16,
586
+ paddingVertical: 14,
587
+ fontSize: 16
588
+ },
589
+ helperText: {
590
+ fontSize: 12
591
+ }
592
+ });
593
+ function Checkbox({ checked = false, onCheckedChange, label, disabled, style }) {
594
+ const { colors } = useTheme();
595
+ return /* @__PURE__ */ React23.createElement(
596
+ TouchableOpacity,
597
+ {
598
+ style: [styles12.row, style],
599
+ onPress: () => {
600
+ Haptics10.selectionAsync();
601
+ onCheckedChange?.(!checked);
602
+ },
603
+ disabled,
604
+ activeOpacity: 0.7
605
+ },
606
+ /* @__PURE__ */ React23.createElement(
607
+ View,
608
+ {
609
+ style: [
610
+ styles12.box,
611
+ {
612
+ borderColor: checked ? colors.primary : colors.border,
613
+ backgroundColor: checked ? colors.primary : "transparent",
614
+ opacity: disabled ? 0.45 : 1
615
+ }
616
+ ]
617
+ },
618
+ checked ? /* @__PURE__ */ React23.createElement(View, { style: [styles12.checkmark, { borderColor: colors.primaryForeground }] }) : null
619
+ ),
620
+ label ? /* @__PURE__ */ React23.createElement(Text, { style: [styles12.label, { color: disabled ? colors.mutedForeground : colors.foreground }] }, label) : null
621
+ );
622
+ }
623
+ var styles12 = StyleSheet.create({
624
+ row: {
625
+ flexDirection: "row",
626
+ alignItems: "center",
627
+ gap: 10
628
+ },
629
+ box: {
630
+ width: 24,
631
+ height: 24,
632
+ borderRadius: 6,
633
+ borderWidth: 1.5,
634
+ alignItems: "center",
635
+ justifyContent: "center"
636
+ },
637
+ checkmark: {
638
+ width: 13,
639
+ height: 8,
640
+ borderLeftWidth: 2,
641
+ borderBottomWidth: 2,
642
+ transform: [{ rotate: "-45deg" }, { translateY: -1 }]
643
+ },
644
+ label: {
645
+ fontSize: 14,
646
+ lineHeight: 20
647
+ }
648
+ });
649
+ var TRACK_WIDTH = 56;
650
+ var TRACK_HEIGHT = 32;
651
+ var THUMB_SIZE = 24;
652
+ var THUMB_OFFSET = 4;
653
+ var THUMB_TRAVEL = TRACK_WIDTH - THUMB_SIZE - THUMB_OFFSET * 2;
654
+ function Switch({ checked = false, onCheckedChange, disabled, style }) {
655
+ const { colors } = useTheme();
656
+ const translateX = useRef(new Animated.Value(checked ? THUMB_TRAVEL : 0)).current;
657
+ const trackOpacity = useRef(new Animated.Value(checked ? 1 : 0)).current;
658
+ useEffect(() => {
659
+ Animated.parallel([
660
+ Animated.spring(translateX, {
661
+ toValue: checked ? THUMB_TRAVEL : 0,
662
+ useNativeDriver: true,
663
+ bounciness: 4
664
+ }),
665
+ Animated.timing(trackOpacity, {
666
+ toValue: checked ? 1 : 0,
667
+ duration: 150,
668
+ useNativeDriver: false
669
+ })
670
+ ]).start();
671
+ }, [checked, translateX, trackOpacity]);
672
+ const trackColor = trackOpacity.interpolate({
673
+ inputRange: [0, 1],
674
+ outputRange: [colors.muted, colors.primary]
675
+ });
676
+ return /* @__PURE__ */ React23.createElement(
677
+ TouchableOpacity,
678
+ {
679
+ onPress: () => {
680
+ Haptics10.selectionAsync();
681
+ onCheckedChange?.(!checked);
682
+ },
683
+ disabled,
684
+ activeOpacity: 0.8,
685
+ style: [styles13.wrapper, { opacity: disabled ? 0.45 : 1 }, style]
686
+ },
687
+ /* @__PURE__ */ React23.createElement(Animated.View, { style: [styles13.track, { backgroundColor: trackColor }] }, /* @__PURE__ */ React23.createElement(
688
+ Animated.View,
689
+ {
690
+ style: [
691
+ styles13.thumb,
692
+ { backgroundColor: colors.primaryForeground, transform: [{ translateX }] }
693
+ ]
694
+ }
695
+ ))
696
+ );
697
+ }
698
+ var styles13 = StyleSheet.create({
699
+ wrapper: {},
700
+ track: {
701
+ width: TRACK_WIDTH,
702
+ height: TRACK_HEIGHT,
703
+ borderRadius: TRACK_HEIGHT / 2,
704
+ justifyContent: "center",
705
+ paddingHorizontal: THUMB_OFFSET
706
+ },
707
+ thumb: {
708
+ width: THUMB_SIZE,
709
+ height: THUMB_SIZE,
710
+ borderRadius: THUMB_SIZE / 2,
711
+ shadowColor: "#000",
712
+ shadowOffset: { width: 0, height: 1 },
713
+ shadowOpacity: 0.15,
714
+ shadowRadius: 2,
715
+ elevation: 2
716
+ }
717
+ });
718
+ var sizeStyles = {
719
+ sm: { paddingHorizontal: 12, paddingVertical: 8, minWidth: 40, minHeight: 40 },
720
+ md: { paddingHorizontal: 16, paddingVertical: 12, minWidth: 44, minHeight: 44 },
721
+ lg: { paddingHorizontal: 20, paddingVertical: 14, minWidth: 48, minHeight: 48 }
722
+ };
723
+ function Toggle({
724
+ pressed = false,
725
+ onPressedChange,
726
+ variant = "default",
727
+ size = "md",
728
+ label,
729
+ icon,
730
+ disabled,
731
+ style,
732
+ ...props
733
+ }) {
734
+ const { colors } = useTheme();
735
+ const containerStyle = pressed ? { backgroundColor: colors.accent } : variant === "outline" ? { backgroundColor: "transparent", borderWidth: 1, borderColor: colors.border } : { backgroundColor: "transparent" };
736
+ const textColor = pressed ? colors.accentForeground : colors.foreground;
737
+ return /* @__PURE__ */ React23.createElement(
738
+ TouchableOpacity,
739
+ {
740
+ style: [
741
+ styles14.base,
742
+ containerStyle,
743
+ sizeStyles[size],
744
+ disabled && styles14.disabled,
745
+ style
746
+ ],
747
+ onPress: () => {
748
+ Haptics10.selectionAsync();
749
+ onPressedChange?.(!pressed);
750
+ },
751
+ disabled,
752
+ activeOpacity: 0.7,
753
+ ...props
754
+ },
755
+ icon,
756
+ label ? /* @__PURE__ */ React23.createElement(Text, { style: [styles14.label, { color: textColor }] }, label) : null
757
+ );
758
+ }
759
+ var styles14 = StyleSheet.create({
760
+ base: {
761
+ borderRadius: 8,
762
+ flexDirection: "row",
763
+ alignItems: "center",
764
+ justifyContent: "center",
765
+ gap: 8
766
+ },
767
+ disabled: {
768
+ opacity: 0.45
769
+ },
770
+ label: {
771
+ fontSize: 14,
772
+ fontWeight: "500"
773
+ }
774
+ });
775
+ function RadioGroup({
776
+ options,
777
+ value,
778
+ onValueChange,
779
+ orientation = "vertical",
780
+ style
781
+ }) {
782
+ const { colors } = useTheme();
783
+ return /* @__PURE__ */ React23.createElement(
784
+ View,
785
+ {
786
+ style: [
787
+ styles15.container,
788
+ orientation === "horizontal" && styles15.horizontal,
789
+ style
790
+ ]
791
+ },
792
+ options.map((option) => {
793
+ const selected = option.value === value;
794
+ return /* @__PURE__ */ React23.createElement(
795
+ TouchableOpacity,
796
+ {
797
+ key: option.value,
798
+ style: styles15.row,
799
+ onPress: () => {
800
+ if (!option.disabled) {
801
+ Haptics10.selectionAsync();
802
+ onValueChange?.(option.value);
803
+ }
804
+ },
805
+ activeOpacity: 0.7,
806
+ disabled: option.disabled
807
+ },
808
+ /* @__PURE__ */ React23.createElement(
809
+ View,
810
+ {
811
+ style: [
812
+ styles15.radio,
813
+ {
814
+ borderColor: selected ? colors.primary : colors.border,
815
+ opacity: option.disabled ? 0.45 : 1
816
+ }
817
+ ]
818
+ },
819
+ selected ? /* @__PURE__ */ React23.createElement(View, { style: [styles15.dot, { backgroundColor: colors.primary }] }) : null
820
+ ),
821
+ /* @__PURE__ */ React23.createElement(
822
+ Text,
823
+ {
824
+ style: [
825
+ styles15.label,
826
+ { color: option.disabled ? colors.mutedForeground : colors.foreground }
827
+ ]
828
+ },
829
+ option.label
830
+ )
831
+ );
832
+ })
833
+ );
834
+ }
835
+ var styles15 = StyleSheet.create({
836
+ container: {
837
+ gap: 10
838
+ },
839
+ horizontal: {
840
+ flexDirection: "row",
841
+ flexWrap: "wrap"
842
+ },
843
+ row: {
844
+ flexDirection: "row",
845
+ alignItems: "center",
846
+ gap: 10
847
+ },
848
+ radio: {
849
+ width: 24,
850
+ height: 24,
851
+ borderRadius: 12,
852
+ borderWidth: 1.5,
853
+ alignItems: "center",
854
+ justifyContent: "center"
855
+ },
856
+ dot: {
857
+ width: 10,
858
+ height: 10,
859
+ borderRadius: 5
860
+ },
861
+ label: {
862
+ fontSize: 14,
863
+ lineHeight: 20
864
+ }
865
+ });
866
+ function Tabs({ tabs, value, onValueChange, children, style }) {
867
+ const [internal, setInternal] = useState(tabs[0]?.value ?? "");
868
+ const { colors } = useTheme();
869
+ const active = value ?? internal;
870
+ const tabLayouts = useRef({});
871
+ const pillX = useRef(new Animated.Value(0)).current;
872
+ const pillWidth = useRef(new Animated.Value(0)).current;
873
+ const initialised = useRef(false);
874
+ const animatePill = (tabValue, animate) => {
875
+ const layout = tabLayouts.current[tabValue];
876
+ if (!layout) return;
877
+ if (animate) {
878
+ Animated.parallel([
879
+ Animated.spring(pillX, { toValue: layout.x, useNativeDriver: false, speed: 20, bounciness: 0 }),
880
+ Animated.spring(pillWidth, { toValue: layout.width, useNativeDriver: false, speed: 20, bounciness: 0 })
881
+ ]).start();
882
+ } else {
883
+ pillX.setValue(layout.x);
884
+ pillWidth.setValue(layout.width);
885
+ }
886
+ };
887
+ useEffect(() => {
888
+ if (initialised.current) {
889
+ animatePill(active, true);
890
+ }
891
+ }, [active]);
892
+ const handlePress = (v) => {
893
+ if (!value) setInternal(v);
894
+ onValueChange?.(v);
895
+ };
896
+ return /* @__PURE__ */ React23.createElement(View, { style }, /* @__PURE__ */ React23.createElement(View, { style: [styles16.list, { backgroundColor: colors.muted }] }, /* @__PURE__ */ React23.createElement(
897
+ Animated.View,
898
+ {
899
+ style: [
900
+ styles16.pill,
901
+ {
902
+ backgroundColor: colors.background,
903
+ position: "absolute",
904
+ top: 4,
905
+ bottom: 4,
906
+ left: pillX,
907
+ width: pillWidth,
908
+ borderRadius: 6,
909
+ shadowColor: "#000",
910
+ shadowOffset: { width: 0, height: 1 },
911
+ shadowOpacity: 0.1,
912
+ shadowRadius: 2,
913
+ elevation: 2
914
+ }
915
+ ]
916
+ }
917
+ ), tabs.map((tab) => {
918
+ const isActive = tab.value === active;
919
+ return /* @__PURE__ */ React23.createElement(
920
+ TouchableOpacity,
921
+ {
922
+ key: tab.value,
923
+ style: styles16.trigger,
924
+ onPress: () => handlePress(tab.value),
925
+ activeOpacity: 0.7,
926
+ onLayout: (e) => {
927
+ const { x, width } = e.nativeEvent.layout;
928
+ tabLayouts.current[tab.value] = { x, width };
929
+ if (tab.value === active) {
930
+ animatePill(tab.value, false);
931
+ initialised.current = true;
932
+ }
933
+ }
934
+ },
935
+ /* @__PURE__ */ React23.createElement(
936
+ Text,
937
+ {
938
+ style: [
939
+ styles16.triggerLabel,
940
+ { color: isActive ? colors.foreground : colors.mutedForeground },
941
+ isActive && styles16.activeTriggerLabel
942
+ ]
943
+ },
944
+ tab.label
945
+ )
946
+ );
947
+ })), children);
948
+ }
949
+ function TabsContent({ value, activeValue, children, style }) {
950
+ if (value !== activeValue) return null;
951
+ return /* @__PURE__ */ React23.createElement(View, { style }, children);
952
+ }
953
+ var styles16 = StyleSheet.create({
954
+ list: {
955
+ flexDirection: "row",
956
+ borderRadius: 8,
957
+ padding: 4,
958
+ gap: 4
959
+ },
960
+ pill: {},
961
+ trigger: {
962
+ flex: 1,
963
+ paddingVertical: 8,
135
964
  paddingHorizontal: 12,
136
- paddingVertical: 10,
965
+ borderRadius: 6,
966
+ alignItems: "center",
967
+ zIndex: 1
968
+ },
969
+ triggerLabel: {
970
+ fontSize: 14,
971
+ fontWeight: "400"
972
+ },
973
+ activeTriggerLabel: {
974
+ fontWeight: "500"
975
+ }
976
+ });
977
+ function AccordionItemComponent({
978
+ item,
979
+ isOpen,
980
+ onToggle
981
+ }) {
982
+ const { colors } = useTheme();
983
+ const animatedHeight = useRef(new Animated.Value(0)).current;
984
+ const animatedRotation = useRef(new Animated.Value(0)).current;
985
+ const contentHeight = useRef(0);
986
+ const toggle = (open) => {
987
+ Animated.parallel([
988
+ Animated.timing(animatedHeight, {
989
+ toValue: open ? contentHeight.current : 0,
990
+ duration: 220,
991
+ easing: open ? Easing.out(Easing.ease) : Easing.in(Easing.ease),
992
+ useNativeDriver: false
993
+ }),
994
+ Animated.timing(animatedRotation, {
995
+ toValue: open ? 1 : 0,
996
+ duration: 220,
997
+ easing: open ? Easing.out(Easing.ease) : Easing.in(Easing.ease),
998
+ useNativeDriver: true
999
+ })
1000
+ ]).start();
1001
+ };
1002
+ React23.useEffect(() => {
1003
+ toggle(isOpen);
1004
+ }, [isOpen]);
1005
+ const onLayout = (e) => {
1006
+ if (contentHeight.current === 0) {
1007
+ contentHeight.current = e.nativeEvent.layout.height;
1008
+ if (isOpen) animatedHeight.setValue(contentHeight.current);
1009
+ }
1010
+ };
1011
+ const rotate = animatedRotation.interpolate({
1012
+ inputRange: [0, 1],
1013
+ outputRange: ["0deg", "180deg"]
1014
+ });
1015
+ return /* @__PURE__ */ React23.createElement(View, { style: [styles17.item, { borderBottomColor: colors.border }] }, /* @__PURE__ */ React23.createElement(
1016
+ TouchableOpacity,
1017
+ {
1018
+ style: styles17.trigger,
1019
+ onPress: () => {
1020
+ Haptics10.selectionAsync();
1021
+ onToggle();
1022
+ },
1023
+ activeOpacity: 0.7
1024
+ },
1025
+ /* @__PURE__ */ React23.createElement(Text, { style: [styles17.triggerText, { color: colors.foreground }] }, item.trigger),
1026
+ /* @__PURE__ */ React23.createElement(
1027
+ Animated.Text,
1028
+ {
1029
+ style: [styles17.chevron, { color: colors.foreground, transform: [{ rotate }] }]
1030
+ },
1031
+ "\u25BE"
1032
+ )
1033
+ ), /* @__PURE__ */ React23.createElement(Animated.View, { style: [styles17.contentWrapper, { height: animatedHeight, overflow: "hidden" }] }, /* @__PURE__ */ React23.createElement(View, { style: styles17.content, onLayout }, item.content)));
1034
+ }
1035
+ function Accordion({ items, type = "single", defaultValue, style }) {
1036
+ const [openValues, setOpenValues] = useState(() => {
1037
+ if (!defaultValue) return [];
1038
+ return Array.isArray(defaultValue) ? defaultValue : [defaultValue];
1039
+ });
1040
+ const toggle = (value) => {
1041
+ if (type === "single") {
1042
+ setOpenValues((prev) => prev.includes(value) ? [] : [value]);
1043
+ } else {
1044
+ setOpenValues(
1045
+ (prev) => prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
1046
+ );
1047
+ }
1048
+ };
1049
+ return /* @__PURE__ */ React23.createElement(View, { style }, items.map((item) => /* @__PURE__ */ React23.createElement(
1050
+ AccordionItemComponent,
1051
+ {
1052
+ key: item.value,
1053
+ item,
1054
+ isOpen: openValues.includes(item.value),
1055
+ onToggle: () => toggle(item.value)
1056
+ }
1057
+ )));
1058
+ }
1059
+ var styles17 = StyleSheet.create({
1060
+ item: {
1061
+ borderBottomWidth: 1
1062
+ },
1063
+ trigger: {
1064
+ flexDirection: "row",
1065
+ justifyContent: "space-between",
1066
+ alignItems: "center",
1067
+ paddingVertical: 16
1068
+ },
1069
+ triggerText: {
137
1070
  fontSize: 15,
138
- color: "#111827",
139
- backgroundColor: "#fff"
1071
+ fontWeight: "500",
1072
+ flex: 1
140
1073
  },
141
- inputFocused: {
142
- borderColor: "#000"
1074
+ chevron: {
1075
+ fontSize: 16,
1076
+ marginLeft: 8
143
1077
  },
144
- inputError: {
145
- borderColor: "#EF4444"
1078
+ contentWrapper: {},
1079
+ content: {
1080
+ paddingBottom: 16,
1081
+ position: "absolute",
1082
+ width: "100%"
1083
+ }
1084
+ });
1085
+ function Slider({
1086
+ value = 0,
1087
+ minimumValue = 0,
1088
+ maximumValue = 1,
1089
+ step = 0,
1090
+ onValueChange,
1091
+ onSlidingComplete,
1092
+ disabled,
1093
+ style
1094
+ }) {
1095
+ const { colors } = useTheme();
1096
+ const trackWidth = useRef(0);
1097
+ const lastSteppedValue = useRef(value);
1098
+ const [internalValue, setInternalValue] = useState(value);
1099
+ const currentValue = value ?? internalValue;
1100
+ const clamp = (v) => Math.min(Math.max(v, minimumValue), maximumValue);
1101
+ const snapToStep = (v) => {
1102
+ if (!step) return v;
1103
+ return Math.round((v - minimumValue) / step) * step + minimumValue;
1104
+ };
1105
+ const xToValue = (x) => {
1106
+ const ratio = Math.min(Math.max(x / trackWidth.current, 0), 1);
1107
+ const raw = ratio * (maximumValue - minimumValue) + minimumValue;
1108
+ return clamp(snapToStep(raw));
1109
+ };
1110
+ const panResponder = useRef(
1111
+ PanResponder.create({
1112
+ onStartShouldSetPanResponder: () => !disabled,
1113
+ onMoveShouldSetPanResponder: () => !disabled,
1114
+ onPanResponderGrant: (e) => {
1115
+ const x = e.nativeEvent.locationX;
1116
+ const newValue = xToValue(x);
1117
+ setInternalValue(newValue);
1118
+ onValueChange?.(newValue);
1119
+ },
1120
+ onPanResponderMove: (e) => {
1121
+ const x = e.nativeEvent.locationX;
1122
+ const newValue = xToValue(x);
1123
+ if (newValue !== lastSteppedValue.current) {
1124
+ lastSteppedValue.current = newValue;
1125
+ Haptics10.selectionAsync();
1126
+ }
1127
+ setInternalValue(newValue);
1128
+ onValueChange?.(newValue);
1129
+ },
1130
+ onPanResponderRelease: (e) => {
1131
+ const x = e.nativeEvent.locationX;
1132
+ const newValue = xToValue(x);
1133
+ setInternalValue(newValue);
1134
+ onSlidingComplete?.(newValue);
1135
+ }
1136
+ })
1137
+ ).current;
1138
+ const onLayout = (e) => {
1139
+ trackWidth.current = e.nativeEvent.layout.width;
1140
+ };
1141
+ const percent = (currentValue - minimumValue) / (maximumValue - minimumValue) * 100;
1142
+ return /* @__PURE__ */ React23.createElement(
1143
+ View,
1144
+ {
1145
+ style: [styles18.container, disabled && styles18.disabled, style],
1146
+ ...panResponder.panHandlers,
1147
+ onLayout
1148
+ },
1149
+ /* @__PURE__ */ React23.createElement(View, { style: [styles18.track, { backgroundColor: colors.muted }] }, /* @__PURE__ */ React23.createElement(
1150
+ View,
1151
+ {
1152
+ style: [
1153
+ styles18.range,
1154
+ { width: `${percent}%`, backgroundColor: colors.primary }
1155
+ ]
1156
+ }
1157
+ )),
1158
+ /* @__PURE__ */ React23.createElement(
1159
+ View,
1160
+ {
1161
+ style: [
1162
+ styles18.thumb,
1163
+ {
1164
+ left: `${percent}%`,
1165
+ backgroundColor: colors.primary,
1166
+ borderColor: colors.background,
1167
+ transform: [{ translateX: -14 }]
1168
+ }
1169
+ ],
1170
+ pointerEvents: "none"
1171
+ }
1172
+ )
1173
+ );
1174
+ }
1175
+ var styles18 = StyleSheet.create({
1176
+ container: {
1177
+ height: 32,
1178
+ justifyContent: "center",
1179
+ position: "relative"
146
1180
  },
147
- errorText: {
148
- fontSize: 12,
149
- color: "#EF4444"
1181
+ disabled: {
1182
+ opacity: 0.45
150
1183
  },
151
- hintText: {
152
- fontSize: 12,
153
- color: "#6B7280"
1184
+ track: {
1185
+ height: 6,
1186
+ borderRadius: 3,
1187
+ overflow: "hidden",
1188
+ width: "100%"
1189
+ },
1190
+ range: {
1191
+ height: "100%",
1192
+ borderRadius: 3
1193
+ },
1194
+ thumb: {
1195
+ position: "absolute",
1196
+ width: 28,
1197
+ height: 28,
1198
+ borderRadius: 14,
1199
+ borderWidth: 2,
1200
+ shadowColor: "#000",
1201
+ shadowOffset: { width: 0, height: 1 },
1202
+ shadowOpacity: 0.2,
1203
+ shadowRadius: 2,
1204
+ elevation: 2
1205
+ }
1206
+ });
1207
+ function Sheet({
1208
+ open,
1209
+ onClose,
1210
+ title,
1211
+ description,
1212
+ children,
1213
+ snapPoints = ["50%"],
1214
+ style
1215
+ }) {
1216
+ const { colors } = useTheme();
1217
+ const ref = useRef(null);
1218
+ useEffect(() => {
1219
+ if (open) {
1220
+ Haptics10.impactAsync(Haptics10.ImpactFeedbackStyle.Light);
1221
+ ref.current?.present();
1222
+ } else {
1223
+ ref.current?.dismiss();
1224
+ }
1225
+ }, [open]);
1226
+ const renderBackdrop = (props) => /* @__PURE__ */ React23.createElement(
1227
+ BottomSheetBackdrop,
1228
+ {
1229
+ ...props,
1230
+ disappearsOnIndex: -1,
1231
+ appearsOnIndex: 0,
1232
+ pressBehavior: "close"
1233
+ }
1234
+ );
1235
+ return /* @__PURE__ */ React23.createElement(
1236
+ BottomSheetModal,
1237
+ {
1238
+ ref,
1239
+ snapPoints,
1240
+ onDismiss: onClose,
1241
+ backdropComponent: renderBackdrop,
1242
+ backgroundStyle: [styles19.background, { backgroundColor: colors.card }],
1243
+ handleIndicatorStyle: [styles19.handle, { backgroundColor: colors.border }],
1244
+ enablePanDownToClose: true
1245
+ },
1246
+ /* @__PURE__ */ React23.createElement(BottomSheetView, { style: [styles19.content, style] }, title || description ? /* @__PURE__ */ React23.createElement(View, { style: styles19.header }, title ? /* @__PURE__ */ React23.createElement(Text, { style: [styles19.title, { color: colors.cardForeground }] }, title) : null, description ? /* @__PURE__ */ React23.createElement(Text, { style: [styles19.description, { color: colors.mutedForeground }] }, description) : null) : null, children)
1247
+ );
1248
+ }
1249
+ var styles19 = StyleSheet.create({
1250
+ background: {
1251
+ borderTopLeftRadius: 16,
1252
+ borderTopRightRadius: 16
1253
+ },
1254
+ handle: {
1255
+ width: 36,
1256
+ height: 4,
1257
+ borderRadius: 2
1258
+ },
1259
+ content: {
1260
+ paddingHorizontal: 24,
1261
+ paddingBottom: 32
1262
+ },
1263
+ header: {
1264
+ gap: 8,
1265
+ marginBottom: 16
1266
+ },
1267
+ title: {
1268
+ fontSize: 18,
1269
+ fontWeight: "600"
1270
+ },
1271
+ description: {
1272
+ fontSize: 14,
1273
+ lineHeight: 20
1274
+ }
1275
+ });
1276
+ function Select({
1277
+ options,
1278
+ value,
1279
+ onValueChange,
1280
+ placeholder = "Select an option",
1281
+ label,
1282
+ error,
1283
+ disabled,
1284
+ style
1285
+ }) {
1286
+ const { colors } = useTheme();
1287
+ const [open, setOpen] = useState(false);
1288
+ const selected = options.find((o) => o.value === value);
1289
+ return /* @__PURE__ */ React23.createElement(View, { style: [styles20.container, style] }, label ? /* @__PURE__ */ React23.createElement(Text, { style: [styles20.label, { color: colors.foreground }] }, label) : null, /* @__PURE__ */ React23.createElement(
1290
+ TouchableOpacity,
1291
+ {
1292
+ style: [
1293
+ styles20.trigger,
1294
+ {
1295
+ borderColor: error ? colors.destructive : colors.border,
1296
+ backgroundColor: colors.background,
1297
+ opacity: disabled ? 0.45 : 1
1298
+ }
1299
+ ],
1300
+ onPress: () => {
1301
+ if (!disabled) {
1302
+ Haptics10.selectionAsync();
1303
+ setOpen(true);
1304
+ }
1305
+ },
1306
+ activeOpacity: 0.7
1307
+ },
1308
+ /* @__PURE__ */ React23.createElement(
1309
+ Text,
1310
+ {
1311
+ style: [
1312
+ styles20.triggerText,
1313
+ { color: selected ? colors.foreground : colors.mutedForeground }
1314
+ ],
1315
+ numberOfLines: 1
1316
+ },
1317
+ selected?.label ?? placeholder
1318
+ ),
1319
+ /* @__PURE__ */ React23.createElement(Text, { style: [styles20.chevron, { color: colors.mutedForeground }] }, "\u25BE")
1320
+ ), error ? /* @__PURE__ */ React23.createElement(Text, { style: [styles20.helperText, { color: colors.destructive }] }, error) : null, /* @__PURE__ */ React23.createElement(Modal, { transparent: true, visible: open, onRequestClose: () => setOpen(false), animationType: "fade" }, /* @__PURE__ */ React23.createElement(TouchableOpacity, { style: styles20.overlay, onPress: () => setOpen(false), activeOpacity: 1 }, /* @__PURE__ */ React23.createElement(View, { style: [styles20.list, { backgroundColor: colors.card, borderColor: colors.border }] }, /* @__PURE__ */ React23.createElement(
1321
+ FlatList,
1322
+ {
1323
+ data: options,
1324
+ keyExtractor: (item) => item.value,
1325
+ renderItem: ({ item }) => {
1326
+ const isSelected = item.value === value;
1327
+ return /* @__PURE__ */ React23.createElement(
1328
+ TouchableOpacity,
1329
+ {
1330
+ style: [
1331
+ styles20.option,
1332
+ isSelected && { backgroundColor: colors.accent },
1333
+ item.disabled && styles20.disabledOption
1334
+ ],
1335
+ onPress: () => {
1336
+ if (!item.disabled) {
1337
+ Haptics10.selectionAsync();
1338
+ onValueChange?.(item.value);
1339
+ setOpen(false);
1340
+ }
1341
+ },
1342
+ activeOpacity: 0.7
1343
+ },
1344
+ /* @__PURE__ */ React23.createElement(
1345
+ Text,
1346
+ {
1347
+ style: [
1348
+ styles20.optionText,
1349
+ { color: item.disabled ? colors.mutedForeground : colors.foreground },
1350
+ isSelected && { fontWeight: "500" }
1351
+ ]
1352
+ },
1353
+ item.label
1354
+ ),
1355
+ isSelected ? /* @__PURE__ */ React23.createElement(Text, { style: [styles20.checkmark, { color: colors.primary }] }, "\u2713") : null
1356
+ );
1357
+ }
1358
+ }
1359
+ )))));
1360
+ }
1361
+ var styles20 = StyleSheet.create({
1362
+ container: {
1363
+ gap: 4
1364
+ },
1365
+ label: {
1366
+ fontSize: 14,
1367
+ fontWeight: "500",
1368
+ marginBottom: 2
1369
+ },
1370
+ trigger: {
1371
+ flexDirection: "row",
1372
+ alignItems: "center",
1373
+ justifyContent: "space-between",
1374
+ borderWidth: 1.5,
1375
+ borderRadius: 8,
1376
+ paddingHorizontal: 16,
1377
+ paddingVertical: 14
1378
+ },
1379
+ triggerText: {
1380
+ fontSize: 16,
1381
+ flex: 1
1382
+ },
1383
+ chevron: {
1384
+ fontSize: 14,
1385
+ marginLeft: 8
1386
+ },
1387
+ helperText: {
1388
+ fontSize: 12
1389
+ },
1390
+ overlay: {
1391
+ flex: 1,
1392
+ backgroundColor: "rgba(0,0,0,0.3)",
1393
+ justifyContent: "center",
1394
+ padding: 24
1395
+ },
1396
+ list: {
1397
+ borderRadius: 12,
1398
+ borderWidth: 1,
1399
+ maxHeight: 300,
1400
+ overflow: "hidden"
1401
+ },
1402
+ option: {
1403
+ flexDirection: "row",
1404
+ alignItems: "center",
1405
+ justifyContent: "space-between",
1406
+ paddingHorizontal: 12,
1407
+ paddingVertical: 10
1408
+ },
1409
+ optionText: {
1410
+ fontSize: 15
1411
+ },
1412
+ disabledOption: {
1413
+ opacity: 0.45
1414
+ },
1415
+ checkmark: {
1416
+ fontSize: 14,
1417
+ fontWeight: "600"
1418
+ }
1419
+ });
1420
+ var ToastContext = createContext({
1421
+ toast: () => {
1422
+ },
1423
+ dismiss: () => {
1424
+ }
1425
+ });
1426
+ function useToast() {
1427
+ return useContext(ToastContext);
1428
+ }
1429
+ function ToastNotification({ item, onDismiss }) {
1430
+ const { colors } = useTheme();
1431
+ const translateY = useRef(new Animated.Value(-80)).current;
1432
+ const opacity = useRef(new Animated.Value(0)).current;
1433
+ useEffect(() => {
1434
+ Animated.parallel([
1435
+ Animated.spring(translateY, { toValue: 0, useNativeDriver: true, bounciness: 2 }),
1436
+ Animated.timing(opacity, { toValue: 1, duration: 200, useNativeDriver: true })
1437
+ ]).start();
1438
+ const timer = setTimeout(() => {
1439
+ Animated.parallel([
1440
+ Animated.timing(translateY, { toValue: -80, duration: 200, useNativeDriver: true }),
1441
+ Animated.timing(opacity, { toValue: 0, duration: 200, useNativeDriver: true })
1442
+ ]).start(onDismiss);
1443
+ }, item.duration ?? 3e3);
1444
+ return () => clearTimeout(timer);
1445
+ }, []);
1446
+ const bgColor = {
1447
+ default: colors.foreground,
1448
+ destructive: colors.destructive,
1449
+ success: "#16a34a"
1450
+ }[item.variant ?? "default"];
1451
+ const textColor = {
1452
+ default: colors.background,
1453
+ destructive: colors.destructiveForeground,
1454
+ success: "#ffffff"
1455
+ }[item.variant ?? "default"];
1456
+ return /* @__PURE__ */ React23.createElement(
1457
+ Animated.View,
1458
+ {
1459
+ style: [styles21.toast, { backgroundColor: bgColor, opacity, transform: [{ translateY }] }]
1460
+ },
1461
+ /* @__PURE__ */ React23.createElement(View, { style: styles21.toastContent }, item.title ? /* @__PURE__ */ React23.createElement(Text, { style: [styles21.toastTitle, { color: textColor }] }, item.title) : null, item.description ? /* @__PURE__ */ React23.createElement(Text, { style: [styles21.toastDescription, { color: textColor, opacity: 0.85 }] }, item.description) : null),
1462
+ /* @__PURE__ */ React23.createElement(TouchableOpacity, { onPress: onDismiss, style: styles21.dismissButton }, /* @__PURE__ */ React23.createElement(Text, { style: [styles21.dismissIcon, { color: textColor }] }, "\u2715"))
1463
+ );
1464
+ }
1465
+ function ToastProvider({ children }) {
1466
+ const [toasts, setToasts] = useState([]);
1467
+ const insets = useSafeAreaInsets();
1468
+ const toast = useCallback((item) => {
1469
+ const id = Math.random().toString(36).slice(2);
1470
+ if (item.variant === "success") {
1471
+ Haptics10.notificationAsync(Haptics10.NotificationFeedbackType.Success);
1472
+ } else if (item.variant === "destructive") {
1473
+ Haptics10.notificationAsync(Haptics10.NotificationFeedbackType.Error);
1474
+ } else {
1475
+ Haptics10.impactAsync(Haptics10.ImpactFeedbackStyle.Light);
1476
+ }
1477
+ setToasts((prev) => [{ ...item, id }, ...prev].slice(0, 3));
1478
+ }, []);
1479
+ const dismiss = useCallback((id) => {
1480
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1481
+ }, []);
1482
+ return /* @__PURE__ */ React23.createElement(ToastContext.Provider, { value: { toast, dismiss } }, children, /* @__PURE__ */ React23.createElement(View, { style: [styles21.container, { top: insets.top + 8 }], pointerEvents: "box-none" }, toasts.map((item) => /* @__PURE__ */ React23.createElement(ToastNotification, { key: item.id, item, onDismiss: () => dismiss(item.id) }))));
1483
+ }
1484
+ var styles21 = StyleSheet.create({
1485
+ container: {
1486
+ position: "absolute",
1487
+ left: 16,
1488
+ right: 16,
1489
+ gap: 8,
1490
+ zIndex: 9999
1491
+ },
1492
+ toast: {
1493
+ flexDirection: "row",
1494
+ alignItems: "center",
1495
+ borderRadius: 12,
1496
+ paddingHorizontal: 16,
1497
+ paddingVertical: 12,
1498
+ shadowColor: "#000",
1499
+ shadowOffset: { width: 0, height: 4 },
1500
+ shadowOpacity: 0.15,
1501
+ shadowRadius: 8,
1502
+ elevation: 6
1503
+ },
1504
+ toastContent: {
1505
+ flex: 1,
1506
+ gap: 4
1507
+ },
1508
+ toastTitle: {
1509
+ fontSize: 14,
1510
+ fontWeight: "600"
1511
+ },
1512
+ toastDescription: {
1513
+ fontSize: 13
1514
+ },
1515
+ dismissButton: {
1516
+ padding: 4,
1517
+ marginLeft: 8
1518
+ },
1519
+ dismissIcon: {
1520
+ fontSize: 12
154
1521
  }
155
1522
  });
156
1523
 
157
- export { Button, Input, Text2 as Text };
1524
+ export { Accordion, Alert, Avatar, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, EmptyState, Input, Progress, RadioGroup, Select, Separator, Sheet, Skeleton, Slider, Spinner, Switch, Tabs, TabsContent, Text2 as Text, Textarea, ThemeProvider, ToastProvider, Toggle, defaultDark, defaultLight, useTheme, useToast };