@retray-dev/ui-kit 6.2.0 → 7.0.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.
Files changed (317) hide show
  1. package/COMPONENTS.md +444 -10
  2. package/EXAMPLES.md +248 -0
  3. package/README.md +11 -10
  4. package/dist/Accordion.d.mts +28 -0
  5. package/dist/Accordion.d.ts +28 -0
  6. package/dist/Accordion.js +340 -0
  7. package/dist/Accordion.mjs +6 -0
  8. package/dist/AlertBanner.d.mts +16 -0
  9. package/dist/AlertBanner.d.ts +16 -0
  10. package/dist/AlertBanner.js +247 -0
  11. package/dist/AlertBanner.mjs +5 -0
  12. package/dist/Avatar.d.mts +20 -0
  13. package/dist/Avatar.d.ts +20 -0
  14. package/dist/Avatar.js +234 -0
  15. package/dist/Avatar.mjs +3 -0
  16. package/dist/Badge.d.mts +26 -0
  17. package/dist/Badge.d.ts +26 -0
  18. package/dist/Badge.js +247 -0
  19. package/dist/Badge.mjs +4 -0
  20. package/dist/Button.d.mts +25 -0
  21. package/dist/Button.d.ts +25 -0
  22. package/dist/Button.js +414 -0
  23. package/dist/Button.mjs +8 -0
  24. package/dist/ButtonGroup.d.mts +26 -0
  25. package/dist/ButtonGroup.d.ts +26 -0
  26. package/dist/ButtonGroup.js +52 -0
  27. package/dist/ButtonGroup.mjs +2 -0
  28. package/dist/Card.d.mts +39 -0
  29. package/dist/Card.d.ts +39 -0
  30. package/dist/Card.js +329 -0
  31. package/dist/Card.mjs +7 -0
  32. package/dist/CategoryStrip.d.mts +26 -0
  33. package/dist/CategoryStrip.d.ts +26 -0
  34. package/dist/CategoryStrip.js +396 -0
  35. package/dist/CategoryStrip.mjs +9 -0
  36. package/dist/Checkbox.d.mts +14 -0
  37. package/dist/Checkbox.d.ts +14 -0
  38. package/dist/Checkbox.js +304 -0
  39. package/dist/Checkbox.mjs +7 -0
  40. package/dist/Chip.d.mts +31 -0
  41. package/dist/Chip.d.ts +31 -0
  42. package/dist/Chip.js +370 -0
  43. package/dist/Chip.mjs +8 -0
  44. package/dist/ConfirmDialog.d.mts +15 -0
  45. package/dist/ConfirmDialog.d.ts +15 -0
  46. package/dist/ConfirmDialog.js +530 -0
  47. package/dist/ConfirmDialog.mjs +9 -0
  48. package/dist/CurrencyDisplay.d.mts +24 -0
  49. package/dist/CurrencyDisplay.d.ts +24 -0
  50. package/dist/CurrencyDisplay.js +189 -0
  51. package/dist/CurrencyDisplay.mjs +3 -0
  52. package/dist/CurrencyInput.d.mts +26 -0
  53. package/dist/CurrencyInput.d.ts +26 -0
  54. package/dist/CurrencyInput.js +404 -0
  55. package/dist/CurrencyInput.mjs +7 -0
  56. package/dist/DetailRow.d.mts +32 -0
  57. package/dist/DetailRow.d.ts +32 -0
  58. package/dist/DetailRow.js +275 -0
  59. package/dist/DetailRow.mjs +4 -0
  60. package/dist/EmptyState.d.mts +27 -0
  61. package/dist/EmptyState.d.ts +27 -0
  62. package/dist/EmptyState.js +503 -0
  63. package/dist/EmptyState.mjs +9 -0
  64. package/dist/Form.d.mts +52 -0
  65. package/dist/Form.d.ts +52 -0
  66. package/dist/Form.js +204 -0
  67. package/dist/Form.mjs +3 -0
  68. package/dist/IconButton.d.mts +22 -0
  69. package/dist/IconButton.d.ts +22 -0
  70. package/dist/IconButton.js +383 -0
  71. package/dist/IconButton.mjs +7 -0
  72. package/dist/Input.d.mts +23 -0
  73. package/dist/Input.d.ts +23 -0
  74. package/dist/Input.js +351 -0
  75. package/dist/Input.mjs +6 -0
  76. package/dist/LabelValue.d.mts +16 -0
  77. package/dist/LabelValue.d.ts +16 -0
  78. package/dist/LabelValue.js +225 -0
  79. package/dist/LabelValue.mjs +4 -0
  80. package/dist/ListGroup.d.mts +34 -0
  81. package/dist/ListGroup.d.ts +34 -0
  82. package/dist/ListGroup.js +217 -0
  83. package/dist/ListGroup.mjs +4 -0
  84. package/dist/ListItem.d.mts +64 -0
  85. package/dist/ListItem.d.ts +64 -0
  86. package/dist/ListItem.js +430 -0
  87. package/dist/ListItem.mjs +8 -0
  88. package/dist/MediaCard.d.mts +39 -0
  89. package/dist/MediaCard.d.ts +39 -0
  90. package/dist/MediaCard.js +427 -0
  91. package/dist/MediaCard.mjs +8 -0
  92. package/dist/MenuGroup.d.mts +34 -0
  93. package/dist/MenuGroup.d.ts +34 -0
  94. package/dist/MenuGroup.js +217 -0
  95. package/dist/MenuGroup.mjs +4 -0
  96. package/dist/MenuItem.d.mts +48 -0
  97. package/dist/MenuItem.d.ts +48 -0
  98. package/dist/MenuItem.js +403 -0
  99. package/dist/MenuItem.mjs +8 -0
  100. package/dist/MonthPicker.d.mts +20 -0
  101. package/dist/MonthPicker.d.ts +20 -0
  102. package/dist/MonthPicker.js +234 -0
  103. package/dist/MonthPicker.mjs +4 -0
  104. package/dist/Pressable.d.mts +34 -0
  105. package/dist/Pressable.d.ts +34 -0
  106. package/dist/Pressable.js +132 -0
  107. package/dist/Pressable.mjs +4 -0
  108. package/dist/Progress.d.mts +14 -0
  109. package/dist/Progress.d.ts +14 -0
  110. package/dist/Progress.js +191 -0
  111. package/dist/Progress.mjs +4 -0
  112. package/dist/RadioGroup.d.mts +19 -0
  113. package/dist/RadioGroup.d.ts +19 -0
  114. package/dist/RadioGroup.js +341 -0
  115. package/dist/RadioGroup.mjs +7 -0
  116. package/dist/Select.d.mts +22 -0
  117. package/dist/Select.d.ts +22 -0
  118. package/dist/Select.js +441 -0
  119. package/dist/Select.mjs +6 -0
  120. package/dist/Separator.d.mts +10 -0
  121. package/dist/Separator.d.ts +10 -0
  122. package/dist/Separator.js +156 -0
  123. package/dist/Separator.mjs +2 -0
  124. package/dist/Sheet.d.mts +81 -0
  125. package/dist/Sheet.d.ts +81 -0
  126. package/dist/Sheet.js +340 -0
  127. package/dist/Sheet.mjs +4 -0
  128. package/dist/Skeleton.d.mts +17 -0
  129. package/dist/Skeleton.d.ts +17 -0
  130. package/dist/Skeleton.js +205 -0
  131. package/dist/Skeleton.mjs +4 -0
  132. package/dist/Slider.d.mts +20 -0
  133. package/dist/Slider.d.ts +20 -0
  134. package/dist/Slider.js +232 -0
  135. package/dist/Slider.mjs +4 -0
  136. package/dist/Spinner.d.mts +12 -0
  137. package/dist/Spinner.d.ts +12 -0
  138. package/dist/Spinner.js +172 -0
  139. package/dist/Spinner.mjs +3 -0
  140. package/dist/Switch.d.mts +13 -0
  141. package/dist/Switch.d.ts +13 -0
  142. package/dist/Switch.js +261 -0
  143. package/dist/Switch.mjs +5 -0
  144. package/dist/Tabs.d.mts +27 -0
  145. package/dist/Tabs.d.ts +27 -0
  146. package/dist/Tabs.js +389 -0
  147. package/dist/Tabs.mjs +6 -0
  148. package/dist/Text.d.mts +12 -0
  149. package/dist/Text.d.ts +12 -0
  150. package/dist/Text.js +311 -0
  151. package/dist/Text.mjs +4 -0
  152. package/dist/Textarea.d.mts +16 -0
  153. package/dist/Textarea.d.ts +16 -0
  154. package/dist/Textarea.js +333 -0
  155. package/dist/Textarea.mjs +6 -0
  156. package/dist/Toast.d.mts +47 -0
  157. package/dist/Toast.d.ts +47 -0
  158. package/dist/Toast.js +185 -0
  159. package/dist/Toast.mjs +3 -0
  160. package/dist/Toggle.d.mts +33 -0
  161. package/dist/Toggle.d.ts +33 -0
  162. package/dist/Toggle.js +397 -0
  163. package/dist/Toggle.mjs +8 -0
  164. package/dist/VirtualList.d.mts +19 -0
  165. package/dist/VirtualList.d.ts +19 -0
  166. package/dist/VirtualList.js +38 -0
  167. package/dist/VirtualList.mjs +1 -0
  168. package/dist/chunk-2CE3TQVY.mjs +11 -0
  169. package/dist/chunk-2UYENBLV.mjs +49 -0
  170. package/dist/chunk-3BBOZ3OQ.mjs +41 -0
  171. package/dist/chunk-5IKW3VNC.mjs +43 -0
  172. package/dist/chunk-63357L2X.mjs +51 -0
  173. package/dist/chunk-6LQYY7HC.mjs +127 -0
  174. package/dist/chunk-6Q64UFIA.mjs +71 -0
  175. package/dist/chunk-76PFOSM2.mjs +41 -0
  176. package/dist/chunk-7H2OR44A.mjs +14 -0
  177. package/dist/chunk-A4MDAP7G.mjs +42 -0
  178. package/dist/chunk-AU2VDY4P.mjs +190 -0
  179. package/dist/chunk-BRKYVJVV.mjs +60 -0
  180. package/dist/chunk-CRYBX2CM.mjs +146 -0
  181. package/dist/chunk-DITNP6PL.mjs +106 -0
  182. package/dist/chunk-FTLJOUOQ.mjs +97 -0
  183. package/dist/chunk-GCWOGZYL.mjs +104 -0
  184. package/dist/chunk-GNGLDL6Z.mjs +60 -0
  185. package/dist/chunk-GPOUINK5.mjs +148 -0
  186. package/dist/chunk-HSPSMN6U.mjs +115 -0
  187. package/dist/chunk-IRRY3CRZ.mjs +82 -0
  188. package/dist/chunk-JB67UOB5.mjs +92 -0
  189. package/dist/chunk-JBLL7U3U.mjs +64 -0
  190. package/dist/chunk-KWCPOM6W.mjs +136 -0
  191. package/dist/chunk-KZJRQOIU.mjs +64 -0
  192. package/dist/chunk-L7E7TVEZ.mjs +145 -0
  193. package/dist/chunk-LG4DO3DK.mjs +174 -0
  194. package/dist/chunk-LWG526VX.mjs +139 -0
  195. package/dist/chunk-MN7OG7IY.mjs +96 -0
  196. package/dist/chunk-MX6HRKMI.mjs +29 -0
  197. package/dist/chunk-NC5ZTR2Y.mjs +32 -0
  198. package/dist/chunk-NQGVLMWG.mjs +90 -0
  199. package/dist/chunk-QCNARS3X.mjs +46 -0
  200. package/dist/chunk-QXGYKWI7.mjs +134 -0
  201. package/dist/chunk-QY3X2UYR.mjs +191 -0
  202. package/dist/chunk-RKLHUDZS.mjs +92 -0
  203. package/dist/chunk-RMMK64W5.mjs +54 -0
  204. package/dist/chunk-RR2VQLKE.mjs +190 -0
  205. package/dist/chunk-RTC3CFXF.mjs +29 -0
  206. package/dist/chunk-SBZYEV4S.mjs +61 -0
  207. package/dist/chunk-SOA2Z4RB.mjs +82 -0
  208. package/dist/chunk-SOYNZDVY.mjs +151 -0
  209. package/dist/chunk-T7XZ7H7Y.mjs +57 -0
  210. package/dist/chunk-TAJ2PQ2O.mjs +163 -0
  211. package/dist/chunk-U4N7WF4Z.mjs +108 -0
  212. package/dist/chunk-URDE3EUU.mjs +132 -0
  213. package/dist/chunk-URLL5JBR.mjs +245 -0
  214. package/dist/chunk-XDMN67KV.mjs +59 -0
  215. package/dist/chunk-Y6MXOREN.mjs +120 -0
  216. package/dist/chunk-YZJAFS4P.mjs +131 -0
  217. package/dist/index.d.mts +94 -873
  218. package/dist/index.d.ts +94 -873
  219. package/dist/index.js +751 -357
  220. package/dist/index.mjs +50 -3895
  221. package/package.json +23 -14
  222. package/src/assets/fonts/Sohne-Bold.otf +0 -0
  223. package/src/assets/fonts/Sohne-BoldItalic.otf +0 -0
  224. package/src/assets/fonts/Sohne-ExtraBold.otf +0 -0
  225. package/src/assets/fonts/Sohne-ExtraBoldItalic.otf +0 -0
  226. package/src/assets/fonts/Sohne-ExtraLight.otf +0 -0
  227. package/src/assets/fonts/Sohne-ExtraLightItalic.otf +0 -0
  228. package/src/assets/fonts/Sohne-Italic.otf +0 -0
  229. package/src/assets/fonts/Sohne-Light.otf +0 -0
  230. package/src/assets/fonts/Sohne-LightItalic.otf +0 -0
  231. package/src/assets/fonts/Sohne-Medium.otf +0 -0
  232. package/src/assets/fonts/Sohne-MediumItalic.otf +0 -0
  233. package/src/assets/fonts/Sohne-Regular.otf +0 -0
  234. package/src/assets/fonts/Sohne-SemiBold.otf +0 -0
  235. package/src/assets/fonts/Sohne-SemiBoldItalic.otf +0 -0
  236. package/src/assets/fonts/SohneMono-Bold.otf +0 -0
  237. package/src/assets/fonts/SohneMono-BoldItalic.otf +0 -0
  238. package/src/assets/fonts/SohneMono-ExtraBold.otf +0 -0
  239. package/src/assets/fonts/SohneMono-ExtraBoldItalic.otf +0 -0
  240. package/src/assets/fonts/SohneMono-ExtraLight.otf +0 -0
  241. package/src/assets/fonts/SohneMono-ExtraLightItalic.otf +0 -0
  242. package/src/assets/fonts/SohneMono-Italic.otf +0 -0
  243. package/src/assets/fonts/SohneMono-Light.otf +0 -0
  244. package/src/assets/fonts/SohneMono-LightItalic.otf +0 -0
  245. package/src/assets/fonts/SohneMono-Medium.otf +0 -0
  246. package/src/assets/fonts/SohneMono-MediumItalic.otf +0 -0
  247. package/src/assets/fonts/SohneMono-Regular.otf +0 -0
  248. package/src/assets/fonts/SohneMono-SemiBold.otf +0 -0
  249. package/src/assets/fonts/SohneMono-SemiBoldItalic.otf +0 -0
  250. package/src/components/Accordion/Accordion.tsx +3 -3
  251. package/src/components/AlertBanner/AlertBanner.tsx +33 -12
  252. package/src/components/Avatar/Avatar.tsx +4 -2
  253. package/src/components/Badge/Badge.tsx +4 -2
  254. package/src/components/Button/Button.tsx +10 -11
  255. package/src/components/ButtonGroup/ButtonGroup.tsx +13 -10
  256. package/src/components/Card/Card.tsx +17 -34
  257. package/src/components/CategoryStrip/CategoryStrip.tsx +24 -21
  258. package/src/components/Checkbox/Checkbox.tsx +11 -6
  259. package/src/components/Chip/Chip.tsx +17 -15
  260. package/src/components/ConfirmDialog/ConfirmDialog.tsx +2 -2
  261. package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +4 -2
  262. package/src/components/CurrencyInput/CurrencyInput.tsx +2 -2
  263. package/src/components/DetailRow/DetailRow.tsx +9 -7
  264. package/src/components/EmptyState/EmptyState.tsx +2 -2
  265. package/src/components/Form/Form.tsx +149 -0
  266. package/src/components/Form/index.ts +1 -0
  267. package/src/components/IconButton/IconButton.tsx +4 -2
  268. package/src/components/Input/Input.tsx +27 -31
  269. package/src/components/LabelValue/LabelValue.tsx +6 -4
  270. package/src/components/ListGroup/ListGroup.tsx +145 -0
  271. package/src/components/ListGroup/index.ts +1 -0
  272. package/src/components/ListItem/ListItem.tsx +9 -10
  273. package/src/components/MediaCard/MediaCard.tsx +7 -5
  274. package/src/components/MenuGroup/MenuGroup.tsx +145 -0
  275. package/src/components/MenuGroup/index.ts +1 -0
  276. package/src/components/MenuItem/MenuItem.tsx +7 -9
  277. package/src/components/MonthPicker/MonthPicker.tsx +2 -2
  278. package/src/components/RadioGroup/RadioGroup.tsx +11 -14
  279. package/src/components/Select/Select.tsx +6 -6
  280. package/src/components/Separator/Separator.tsx +1 -3
  281. package/src/components/Sheet/Sheet.tsx +81 -17
  282. package/src/components/Skeleton/Skeleton.tsx +1 -1
  283. package/src/components/Slider/Slider.tsx +2 -2
  284. package/src/components/Spinner/Spinner.tsx +1 -1
  285. package/src/components/Switch/Switch.tsx +28 -5
  286. package/src/components/Tabs/Tabs.tsx +22 -18
  287. package/src/components/Text/Text.tsx +3 -1
  288. package/src/components/Textarea/Textarea.tsx +18 -14
  289. package/src/components/Toast/Toast.tsx +6 -6
  290. package/src/components/Toggle/Toggle.tsx +47 -23
  291. package/src/components/VirtualList/VirtualList.tsx +60 -0
  292. package/src/components/VirtualList/index.ts +1 -0
  293. package/src/fonts.ts +38 -20
  294. package/src/index.ts +5 -1
  295. package/src/theme/colors.ts +53 -39
  296. package/src/theme/types.ts +3 -0
  297. package/src/tokens.ts +49 -39
  298. package/src/utils/icons.ts +47 -20
  299. package/src/utils/usePressScale.ts +2 -0
  300. package/src/assets/fonts/Poppins-Black.ttf +0 -0
  301. package/src/assets/fonts/Poppins-BlackItalic.ttf +0 -0
  302. package/src/assets/fonts/Poppins-Bold.ttf +0 -0
  303. package/src/assets/fonts/Poppins-BoldItalic.ttf +0 -0
  304. package/src/assets/fonts/Poppins-ExtraBold.ttf +0 -0
  305. package/src/assets/fonts/Poppins-ExtraBoldItalic.ttf +0 -0
  306. package/src/assets/fonts/Poppins-ExtraLight.ttf +0 -0
  307. package/src/assets/fonts/Poppins-ExtraLightItalic.ttf +0 -0
  308. package/src/assets/fonts/Poppins-Italic.ttf +0 -0
  309. package/src/assets/fonts/Poppins-Light.ttf +0 -0
  310. package/src/assets/fonts/Poppins-LightItalic.ttf +0 -0
  311. package/src/assets/fonts/Poppins-Medium.ttf +0 -0
  312. package/src/assets/fonts/Poppins-MediumItalic.ttf +0 -0
  313. package/src/assets/fonts/Poppins-Regular.ttf +0 -0
  314. package/src/assets/fonts/Poppins-SemiBold.ttf +0 -0
  315. package/src/assets/fonts/Poppins-SemiBoldItalic.ttf +0 -0
  316. package/src/assets/fonts/Poppins-Thin.ttf +0 -0
  317. package/src/assets/fonts/Poppins-ThinItalic.ttf +0 -0
package/dist/index.mjs CHANGED
@@ -1,3897 +1,52 @@
1
- import React26, { createContext, useMemo, useContext, useState, useEffect, useRef, useCallback } from 'react';
2
- import { Platform, StyleSheet, Dimensions, useColorScheme, TouchableOpacity, ActivityIndicator, Text, View, TextInput, Image, Modal, ScrollView, Pressable } from 'react-native';
3
- import Animated9, { Easing, useAnimatedStyle, interpolateColor, useSharedValue, withRepeat, withTiming, withSpring, useDerivedValue } from 'react-native-reanimated';
4
- import { verticalScale, scale, moderateVerticalScale, moderateScale } from 'react-native-size-matters';
5
- import AntDesign from '@expo/vector-icons/AntDesign';
6
- import Entypo from '@expo/vector-icons/Entypo';
7
- import Feather from '@expo/vector-icons/Feather';
8
- import FontAwesome5 from '@expo/vector-icons/FontAwesome5';
9
- import MaterialIcons from '@expo/vector-icons/MaterialIcons';
10
- import Ionicons from '@expo/vector-icons/Ionicons';
11
- import { AntDesign as AntDesign$1, FontAwesome5 as FontAwesome5$1, MaterialIcons as MaterialIcons$1, Entypo as Entypo$1, Feather as Feather$1 } from '@expo/vector-icons';
12
- import { LinearGradient } from 'expo-linear-gradient';
13
- import RNSlider from '@react-native-community/slider';
14
- import { BottomSheetBackdrop, BottomSheetFooter, BottomSheetModal, BottomSheetScrollView, BottomSheetView } from '@gorhom/bottom-sheet';
15
- export { BottomSheetModalProvider, BottomSheetTextInput as SheetTextInput } from '@gorhom/bottom-sheet';
16
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
17
- import { Picker } from '@react-native-picker/picker';
18
- import { toast, Toaster } from 'sonner-native';
19
- export { toast } from 'sonner-native';
20
-
21
- // src/theme/ThemeProvider.tsx
22
-
23
- // src/theme/colorUtils.ts
24
- function hexToRgb(hex) {
25
- const clean = hex.replace("#", "");
26
- const full = clean.length === 3 ? clean.split("").map((c) => c + c).join("") : clean;
27
- if (full.length !== 6) return null;
28
- return {
29
- r: parseInt(full.slice(0, 2), 16),
30
- g: parseInt(full.slice(2, 4), 16),
31
- b: parseInt(full.slice(4, 6), 16)
32
- };
33
- }
34
- function componentToHex(c) {
35
- return Math.round(Math.max(0, Math.min(255, c))).toString(16).padStart(2, "0");
36
- }
37
- function rgbToHex(r, g, b) {
38
- return `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
39
- }
40
- function withAlphaOnWhite(hex, alpha) {
41
- const rgb = hexToRgb(hex);
42
- if (!rgb) return hex;
43
- const r = rgb.r * alpha + 255 * (1 - alpha);
44
- const g = rgb.g * alpha + 255 * (1 - alpha);
45
- const b = rgb.b * alpha + 255 * (1 - alpha);
46
- return rgbToHex(r, g, b);
47
- }
48
- function withAlphaOnDark(hex, alpha, bgHex = "#0f0f0f") {
49
- const rgb = hexToRgb(hex);
50
- const bg = hexToRgb(bgHex);
51
- if (!rgb || !bg) return hex;
52
- const r = rgb.r * alpha + bg.r * (1 - alpha);
53
- const g = rgb.g * alpha + bg.g * (1 - alpha);
54
- const b = rgb.b * alpha + bg.b * (1 - alpha);
55
- return rgbToHex(r, g, b);
56
- }
57
- function mixWithBackground(fgHex, bgHex, opacity) {
58
- const fg = hexToRgb(fgHex);
59
- const bg = hexToRgb(bgHex);
60
- if (!fg || !bg) return fgHex;
61
- const r = fg.r * opacity + bg.r * (1 - opacity);
62
- const g = fg.g * opacity + bg.g * (1 - opacity);
63
- const b = fg.b * opacity + bg.b * (1 - opacity);
64
- return rgbToHex(r, g, b);
65
- }
66
- function lighten(hex, amount) {
67
- return withAlphaOnWhite(hex, 1 - amount);
68
- }
69
- function darken(hex, amount) {
70
- const rgb = hexToRgb(hex);
71
- if (!rgb) return hex;
72
- return rgbToHex(rgb.r * (1 - amount), rgb.g * (1 - amount), rgb.b * (1 - amount));
73
- }
74
-
75
- // src/theme/colors.ts
76
- var defaultLight = {
77
- background: "#ffffff",
78
- foreground: "#222222",
79
- // Airbnb ink — deep near-black, never pure black
80
- card: "#ffffff",
81
- primary: "#1a1a1a",
82
- // Near-black primary — clean, premium default
83
- primaryForeground: "#ffffff",
84
- border: "#dddddd",
85
- // Airbnb hairline — light, airy
86
- destructive: "#e53935",
87
- destructiveForeground: "#ffffff",
88
- success: "#1a7a45",
89
- successForeground: "#ffffff",
90
- warning: "#e67e00",
91
- warningForeground: "#ffffff"
92
- };
93
- var defaultDark = {
94
- background: "#0f0f0f",
95
- foreground: "#fafafa",
96
- card: "#1c1c1c",
97
- primary: "#fafafa",
98
- primaryForeground: "#0f0f0f",
99
- border: "#303030",
100
- destructive: "#ef5350",
101
- destructiveForeground: "#ffffff",
102
- success: "#2e7d52",
103
- successForeground: "#ffffff",
104
- warning: "#f57c00",
105
- warningForeground: "#ffffff"
106
- };
107
- function deriveColors(t, scheme) {
108
- const dark = scheme === "dark";
109
- const bg = t.background;
110
- const foregroundSubtle = mixWithBackground(t.foreground, bg, 0.55);
111
- const foregroundMuted = mixWithBackground(t.foreground, bg, 0.38);
112
- const surface = dark ? lighten(bg, -0.06) : darken(bg, 0.04);
113
- const surfaceStrong = dark ? lighten(bg, -0.12) : darken(bg, 0.08);
114
- const destructiveTint = dark ? withAlphaOnDark(t.destructive, 0.15, bg) : withAlphaOnWhite(t.destructive, 0.08);
115
- const destructiveBorder = dark ? withAlphaOnDark(t.destructive, 0.45, bg) : withAlphaOnWhite(t.destructive, 0.3);
116
- const successTint = dark ? withAlphaOnDark(t.success, 0.15, bg) : withAlphaOnWhite(t.success, 0.08);
117
- const successBorder = dark ? withAlphaOnDark(t.success, 0.45, bg) : withAlphaOnWhite(t.success, 0.3);
118
- const warningTint = dark ? withAlphaOnDark(t.warning, 0.15, bg) : withAlphaOnWhite(t.warning, 0.08);
119
- const warningBorder = dark ? withAlphaOnDark(t.warning, 0.45, bg) : withAlphaOnWhite(t.warning, 0.3);
120
- return {
121
- ...t,
122
- foregroundSubtle,
123
- foregroundMuted,
124
- surface,
125
- surfaceStrong,
126
- destructiveTint,
127
- destructiveBorder,
128
- successTint,
129
- successBorder,
130
- warningTint,
131
- warningBorder,
132
- overlay: t.overlay ?? "rgba(0,0,0,0.45)",
133
- accentResolved: t.accent ?? t.primary,
134
- accentForegroundResolved: t.accentForeground ?? t.primaryForeground,
135
- ring: t.primary,
136
- // focus ring always = primary
137
- input: t.border
138
- // input border always = border
139
- };
140
- }
141
-
142
- // src/theme/ThemeProvider.tsx
143
- var ThemeContext = createContext({
144
- colors: deriveColors(defaultLight, "light"),
145
- colorScheme: "light"
146
- });
147
- function ThemeProvider({ children, theme, colorScheme = "system" }) {
148
- const systemScheme = useColorScheme() ?? "light";
149
- const resolvedScheme = colorScheme === "system" ? systemScheme : colorScheme;
150
- const colors = useMemo(() => {
151
- const base = resolvedScheme === "dark" ? defaultDark : defaultLight;
152
- const override = resolvedScheme === "dark" ? theme?.dark : theme?.light;
153
- const merged = override ? { ...base, ...override } : base;
154
- return deriveColors(merged, resolvedScheme);
155
- }, [resolvedScheme, theme]);
156
- return /* @__PURE__ */ React26.createElement(ThemeContext.Provider, { value: { colors, colorScheme: resolvedScheme } }, children);
157
- }
158
- function useTheme() {
159
- const context = useContext(ThemeContext);
160
- if (!context) {
161
- throw new Error("useTheme must be used within a ThemeProvider");
162
- }
163
- return context;
164
- }
165
- var _haptics = null;
166
- async function getHaptics() {
167
- if (Platform.OS === "web") return null;
168
- if (!_haptics) {
169
- _haptics = await import('expo-haptics');
170
- }
171
- return _haptics;
172
- }
173
- function selectionAsync() {
174
- if (Platform.OS === "web") return;
175
- getHaptics().then((h) => h?.selectionAsync());
176
- }
177
- function impactLight() {
178
- if (Platform.OS === "web") return;
179
- getHaptics().then((h) => h?.impactAsync(h.ImpactFeedbackStyle.Light));
180
- }
181
- function impactMedium() {
182
- if (Platform.OS === "web") return;
183
- getHaptics().then((h) => h?.impactAsync(h.ImpactFeedbackStyle.Medium));
184
- }
185
- function notificationSuccess() {
186
- if (Platform.OS === "web") return;
187
- getHaptics().then((h) => h?.notificationAsync(h.NotificationFeedbackType.Success));
188
- }
189
- var isWeb = Platform.OS === "web";
190
- var s = isWeb ? (n) => n : scale;
191
- var vs = isWeb ? (n) => n : verticalScale;
192
- var ms = isWeb ? (n, _factor) => n : moderateScale;
193
- var mvs = isWeb ? (n, _factor) => n : moderateVerticalScale;
194
- var ICON_FAMILIES = [
195
- { name: "Ionicons", component: Ionicons, glyphMap: Ionicons.glyphMap },
196
- { name: "MaterialIcons", component: MaterialIcons, glyphMap: MaterialIcons.glyphMap },
197
- { name: "FontAwesome5", component: FontAwesome5, glyphMap: FontAwesome5.glyphMap },
198
- { name: "Entypo", component: Entypo, glyphMap: Entypo.glyphMap },
199
- { name: "AntDesign", component: AntDesign, glyphMap: AntDesign.glyphMap },
200
- { name: "Feather", component: Feather, glyphMap: Feather.glyphMap }
201
- ];
202
- var resolvedCache = null;
203
- function buildCache() {
204
- const cache = /* @__PURE__ */ new Map();
205
- for (const family of ICON_FAMILIES) {
206
- if (!family.glyphMap) continue;
207
- for (const iconName of Object.keys(family.glyphMap)) {
208
- cache.set(iconName, family);
209
- }
210
- }
211
- return cache;
212
- }
213
- function resolveFamily(name) {
214
- if (!resolvedCache) {
215
- resolvedCache = buildCache();
216
- }
217
- return resolvedCache.get(name) ?? null;
218
- }
219
- function Icon({ name, size, color, family }) {
220
- let resolved = null;
221
- if (family) {
222
- resolved = ICON_FAMILIES.find((f) => f.name === family) ?? null;
223
- } else {
224
- resolved = resolveFamily(name);
225
- }
226
- if (!resolved) return null;
227
- const Component = resolved.component;
228
- return React26.createElement(Component, { name, size, color });
229
- }
230
- function renderIcon(name, size, color) {
231
- return React26.createElement(Icon, { name, size, color });
232
- }
233
-
234
- // src/tokens.ts
235
- var SPACING = {
236
- xxs: 2,
237
- xs: 4,
238
- sm: 8,
239
- md: 12,
240
- base: 16,
241
- lg: 24,
242
- xl: 32,
243
- xxl: 48,
244
- section: 64
245
- };
246
- var ICON_SIZES = {
247
- sm: 14,
248
- md: 18,
249
- lg: 22,
250
- xl: 28,
251
- "2xl": 32
252
- };
253
- var RADIUS = {
254
- none: 0,
255
- xs: 4,
256
- sm: 8,
257
- md: 14,
258
- lg: 20,
259
- xl: 32,
260
- full: 9999
261
- };
262
- var SHADOWS = {
263
- sm: {
264
- shadowColor: "#000",
265
- shadowOffset: { width: 0, height: 1 },
266
- shadowOpacity: 0.06,
267
- shadowRadius: 4,
268
- elevation: 2
269
- },
270
- md: {
271
- shadowColor: "#000",
272
- shadowOffset: { width: 0, height: 2 },
273
- shadowOpacity: 0.1,
274
- shadowRadius: 8,
275
- elevation: 5
276
- },
277
- lg: {
278
- shadowColor: "#000",
279
- shadowOffset: { width: 0, height: 6 },
280
- shadowOpacity: 0.16,
281
- shadowRadius: 16,
282
- elevation: 10
283
- },
284
- xl: {
285
- shadowColor: "#000",
286
- shadowOffset: { width: 0, height: 12 },
287
- shadowOpacity: 0.24,
288
- shadowRadius: 24,
289
- elevation: 18
290
- }
291
- };
292
- var BREAKPOINTS = {
293
- wide: 700
294
- };
295
- var TYPOGRAPHY = {
296
- "display-hero": {
297
- fontFamily: "Poppins-Bold",
298
- fontSize: 64,
299
- fontWeight: "700",
300
- lineHeight: 70,
301
- letterSpacing: -1
302
- },
303
- "display-xl": {
304
- fontFamily: "Poppins-Bold",
305
- fontSize: 28,
306
- fontWeight: "700",
307
- lineHeight: 40,
308
- letterSpacing: 0
309
- },
310
- "display-lg": {
311
- fontFamily: "Poppins-Medium",
312
- fontSize: 22,
313
- fontWeight: "500",
314
- lineHeight: 26,
315
- letterSpacing: -0.44
316
- },
317
- "display-md": {
318
- fontFamily: "Poppins-Bold",
319
- fontSize: 21,
320
- fontWeight: "700",
321
- lineHeight: 30,
322
- letterSpacing: 0
323
- },
324
- "display-sm": {
325
- fontFamily: "Poppins-SemiBold",
326
- fontSize: 20,
327
- fontWeight: "600",
328
- lineHeight: 24,
329
- letterSpacing: -0.18
330
- },
331
- "title-md": {
332
- fontFamily: "Poppins-SemiBold",
333
- fontSize: 16,
334
- fontWeight: "600",
335
- lineHeight: 20,
336
- letterSpacing: 0
337
- },
338
- "title-sm": {
339
- fontFamily: "Poppins-Medium",
340
- fontSize: 16,
341
- fontWeight: "500",
342
- lineHeight: 20,
343
- letterSpacing: 0
344
- },
345
- "body-md": {
346
- fontFamily: "Poppins-Regular",
347
- fontSize: 16,
348
- fontWeight: "400",
349
- lineHeight: 24,
350
- letterSpacing: 0
351
- },
352
- "body-sm": {
353
- fontFamily: "Poppins-Regular",
354
- fontSize: 14,
355
- fontWeight: "400",
356
- lineHeight: 20,
357
- letterSpacing: 0
358
- },
359
- caption: {
360
- fontFamily: "Poppins-Medium",
361
- fontSize: 14,
362
- fontWeight: "500",
363
- lineHeight: 18,
364
- letterSpacing: 0
365
- },
366
- "caption-sm": {
367
- fontFamily: "Poppins-Regular",
368
- fontSize: 13,
369
- fontWeight: "400",
370
- lineHeight: 16,
371
- letterSpacing: 0
372
- },
373
- "badge-text": {
374
- fontFamily: "Poppins-SemiBold",
375
- fontSize: 11,
376
- fontWeight: "600",
377
- lineHeight: 13,
378
- letterSpacing: 0
379
- },
380
- "micro-label": {
381
- fontFamily: "Poppins-Bold",
382
- fontSize: 12,
383
- fontWeight: "700",
384
- lineHeight: 16,
385
- letterSpacing: 0
386
- },
387
- "uppercase-tag": {
388
- fontFamily: "Poppins-Bold",
389
- fontSize: 10,
390
- fontWeight: "700",
391
- lineHeight: 13,
392
- letterSpacing: 0.8,
393
- textTransform: "uppercase"
394
- },
395
- "button-lg": {
396
- fontFamily: "Poppins-Medium",
397
- fontSize: 16,
398
- fontWeight: "500",
399
- lineHeight: 22,
400
- letterSpacing: 0
401
- },
402
- "button-sm": {
403
- fontFamily: "Poppins-Medium",
404
- fontSize: 14,
405
- fontWeight: "500",
406
- lineHeight: 18,
407
- letterSpacing: 0
408
- }
409
- };
410
- var SPRINGS = {
411
- /** Tight, premium press feel — Buttons, Toggle, Tabs triggers. */
412
- pressIn: { stiffness: 600, damping: 35, mass: 0.8 },
413
- pressOut: { stiffness: 280, damping: 22, mass: 0.8 },
414
- /** Slightly softer for larger surfaces — Card, ListItem, MenuItem. */
415
- surfacePressIn: { stiffness: 380, damping: 30, mass: 0.95 },
416
- surfacePressOut: { stiffness: 220, damping: 20, mass: 0.95 },
417
- /** Settled transitions for moving indicators — Tabs pill, Switch thumb. */
418
- glide: { stiffness: 380, damping: 38, mass: 1 },
419
- /** Elastic indicator — Switch thumb, RadioGroup dot. */
420
- elastic: { stiffness: 320, damping: 22, mass: 0.7 }
421
- };
422
- var TIMINGS = {
423
- /** Color/opacity transitions on toggles, checkboxes, switches. */
424
- state: { duration: 160 },
425
- /** Focus ring on inputs. */
426
- focusIn: { duration: 140 },
427
- focusOut: { duration: 100 },
428
- /** Accordion / collapsible content. */
429
- expand: { duration: 240 },
430
- collapse: { duration: 200 },
431
- /** Skeleton shimmer cycle (full pass). */
432
- shimmer: { duration: 1400 }
433
- };
434
- var EASINGS = {
435
- /** Material-style ease-out — natural deceleration for state changes. */
436
- standard: Easing.bezier(0.2, 0, 0, 1),
437
- /** Strong ease-out for expanding surfaces (Accordion open). */
438
- expand: Easing.bezier(0.23, 1, 0.32, 1),
439
- /** Quick ease-in for collapsing. */
440
- collapse: Easing.in(Easing.ease)
441
- };
442
- var PRESS_SCALE = {
443
- button: 0.95,
444
- card: 0.98,
445
- row: 0.97,
446
- chip: 0.94
447
- };
448
- function useHover() {
449
- const [hovered, setHovered] = useState(false);
450
- const onMouseEnter = useCallback(() => setHovered(true), []);
451
- const onMouseLeave = useCallback(() => setHovered(false), []);
452
- if (Platform.OS !== "web") {
453
- return { hovered: false, hoverHandlers: {} };
454
- }
455
- return { hovered, hoverHandlers: { onMouseEnter, onMouseLeave } };
456
- }
457
-
458
- // src/utils/usePressScale.ts
459
- function usePressScale({
460
- pressScale = PRESS_SCALE.button,
461
- hoverScale = 1.02,
462
- pressInSpring = SPRINGS.pressIn,
463
- pressOutSpring = SPRINGS.pressOut,
464
- disabled = false
465
- } = {}) {
466
- const scale2 = useSharedValue(1);
467
- const { hovered, hoverHandlers } = useHover();
468
- const onPressIn = useCallback(() => {
469
- if (disabled) return;
470
- scale2.value = withSpring(pressScale, pressInSpring);
471
- }, [disabled, pressScale, pressInSpring, scale2]);
472
- const onPressOut = useCallback(() => {
473
- if (disabled) return;
474
- scale2.value = withSpring(1, pressOutSpring);
475
- }, [disabled, pressOutSpring, scale2]);
476
- const hoverActive = Platform.OS === "web" && hovered && hoverScale !== 1 && !disabled;
477
- const animatedStyle = useAnimatedStyle(() => ({
478
- transform: [
479
- { scale: scale2.value * (hoverActive ? hoverScale : 1) }
480
- ]
481
- }));
482
- return {
483
- animatedStyle,
484
- onPressIn,
485
- onPressOut,
486
- hoverHandlers
487
- };
488
- }
489
-
490
- // src/components/Button/Button.tsx
491
- var containerSizeStyles = {
492
- sm: { paddingHorizontal: s(16), paddingVertical: vs(10), minHeight: 40 },
493
- md: { paddingHorizontal: s(24), paddingVertical: vs(14), minHeight: 48 },
494
- lg: { paddingHorizontal: s(28), paddingVertical: vs(16), minHeight: 56 }
495
- };
496
- var labelSizeStyles = {
497
- sm: { ...TYPOGRAPHY["button-sm"], fontSize: ms(TYPOGRAPHY["button-sm"].fontSize) },
498
- md: { ...TYPOGRAPHY["button-lg"], fontSize: ms(TYPOGRAPHY["button-lg"].fontSize) },
499
- lg: { ...TYPOGRAPHY["button-lg"], fontSize: ms(TYPOGRAPHY["button-lg"].fontSize + 1), lineHeight: mvs(24) }
500
- };
501
- var iconSizeMap = { sm: 16, md: 18, lg: 20 };
502
- function Button({
503
- label,
504
- variant = "primary",
505
- size = "md",
506
- loading = false,
507
- fullWidth = false,
508
- icon,
509
- iconName,
510
- iconColor,
511
- iconPosition = "left",
512
- disabled,
513
- style,
514
- onPress,
515
- accessibilityLabel,
516
- accessibilityHint,
517
- ...props
518
- }) {
519
- const { colors } = useTheme();
520
- const isDisabled = disabled || loading;
521
- const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
522
- pressScale: PRESS_SCALE.button,
523
- disabled: isDisabled
524
- });
525
- const handlePress = (e) => {
526
- impactMedium();
527
- onPress?.(e);
528
- };
529
- const containerVariantStyle = {
530
- primary: { backgroundColor: colors.primary },
531
- secondary: { backgroundColor: "transparent", borderWidth: 1.5, borderColor: colors.primary },
532
- text: { backgroundColor: "transparent" },
533
- destructive: { backgroundColor: colors.destructive }
534
- }[variant];
535
- const labelVariantStyle = {
536
- primary: { color: colors.primaryForeground },
537
- secondary: { color: colors.primary },
538
- text: { color: colors.foreground },
539
- destructive: { color: colors.destructiveForeground }
540
- }[variant];
541
- const textColor = iconColor ?? labelVariantStyle.color;
542
- const effectiveIcon = iconName ? renderIcon(iconName, iconSizeMap[size], textColor) : typeof icon === "function" ? icon({ label, size, variant, color: textColor }) : icon;
543
- const spinnerColor = variant === "destructive" ? colors.destructiveForeground : variant === "primary" ? colors.primaryForeground : colors.foreground;
544
- const styleArray = Array.isArray(style) ? style : style ? [style] : [];
545
- const flatStyle = StyleSheet.flatten(styleArray);
546
- const { flex, ...restStyle } = flatStyle || {};
547
- return /* @__PURE__ */ React26.createElement(
548
- Animated9.View,
549
- {
550
- style: [fullWidth && styles.fullWidth, flex !== void 0 && { flex }, animatedStyle],
551
- ...hoverHandlers
552
- },
553
- /* @__PURE__ */ React26.createElement(
554
- TouchableOpacity,
555
- {
556
- style: [
557
- styles.base,
558
- containerVariantStyle,
559
- containerSizeStyles[size],
560
- fullWidth && styles.fullWidth,
561
- isDisabled && styles.disabled,
562
- restStyle
563
- ],
564
- disabled: isDisabled,
565
- activeOpacity: 1,
566
- touchSoundDisabled: true,
567
- onPress: handlePress,
568
- onPressIn,
569
- onPressOut,
570
- accessibilityRole: "button",
571
- accessibilityLabel: accessibilityLabel ?? label,
572
- accessibilityHint,
573
- accessibilityState: { disabled: isDisabled, busy: loading },
574
- ...props
575
- },
576
- loading ? /* @__PURE__ */ React26.createElement(React26.Fragment, null, /* @__PURE__ */ React26.createElement(ActivityIndicator, { size: "small", color: spinnerColor, style: { marginRight: s(6) } }), /* @__PURE__ */ React26.createElement(
577
- Text,
578
- {
579
- style: [styles.label, labelVariantStyle, labelSizeStyles[size], styles.labelLoading],
580
- allowFontScaling: true,
581
- numberOfLines: 1
582
- },
583
- label
584
- )) : /* @__PURE__ */ React26.createElement(React26.Fragment, null, effectiveIcon && iconPosition === "left" && /* @__PURE__ */ React26.createElement(React26.Fragment, null, effectiveIcon), /* @__PURE__ */ React26.createElement(
585
- Text,
586
- {
587
- style: [styles.label, labelVariantStyle, labelSizeStyles[size], effectiveIcon ? styles.labelWithIcon : void 0],
588
- allowFontScaling: true,
589
- numberOfLines: 1
590
- },
591
- label
592
- ), effectiveIcon && iconPosition === "right" && /* @__PURE__ */ React26.createElement(React26.Fragment, null, effectiveIcon))
593
- )
594
- );
595
- }
596
- var styles = StyleSheet.create({
597
- base: {
598
- borderRadius: RADIUS.md,
599
- // 14px — Airbnb-aligned rounded rect (not pill)
600
- alignItems: "center",
601
- justifyContent: "center",
602
- flexDirection: "row"
603
- },
604
- fullWidth: {
605
- width: "100%"
606
- },
607
- disabled: {
608
- opacity: 0.45
609
- },
610
- label: {
611
- fontFamily: "Poppins-Medium",
612
- flexShrink: 1
613
- },
614
- labelWithIcon: {
615
- marginHorizontal: s(6)
616
- },
617
- labelLoading: {
618
- opacity: 0.6
619
- }
620
- });
621
- function ButtonGroup({ children, gap = 12, vertical = false, style }) {
622
- return /* @__PURE__ */ React26.createElement(
623
- View,
624
- {
625
- style: [
626
- styles2.container,
627
- vertical ? styles2.vertical : styles2.horizontal,
628
- { gap: s(gap) },
629
- style
630
- ]
631
- },
632
- React26.Children.map(
633
- children,
634
- (child) => React26.isValidElement(child) ? React26.cloneElement(child, {
635
- style: [
636
- child.props.style,
637
- { flex: 1 }
638
- ]
639
- }) : child
640
- )
641
- );
642
- }
643
- var styles2 = StyleSheet.create({
644
- container: {
645
- width: "100%"
646
- },
647
- horizontal: {
648
- flexDirection: "row"
649
- },
650
- vertical: {
651
- flexDirection: "column"
652
- }
653
- });
654
- var sizeMap = {
655
- sm: { container: s(32), icon: 16 },
656
- md: { container: s(44), icon: 20 },
657
- lg: { container: s(52), icon: 24 }
658
- };
659
- function IconButton({
660
- iconName,
661
- icon,
662
- iconColor,
663
- variant = "primary",
664
- size = "md",
665
- loading = false,
666
- badge,
667
- disabled,
668
- style,
669
- onPress,
670
- accessibilityLabel,
671
- accessibilityHint,
672
- ...props
673
- }) {
674
- const { colors } = useTheme();
675
- const isDisabled = disabled || loading;
676
- const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
677
- pressScale: PRESS_SCALE.button,
678
- disabled: isDisabled
679
- });
680
- const handlePress = (e) => {
681
- impactLight();
682
- onPress?.(e);
683
- };
684
- const containerVariantStyle = {
685
- primary: { backgroundColor: colors.primary },
686
- secondary: { backgroundColor: colors.surface },
687
- outline: { backgroundColor: "transparent", borderWidth: 1.5, borderColor: colors.border },
688
- text: { backgroundColor: "transparent" },
689
- destructive: { backgroundColor: colors.destructive }
690
- }[variant];
691
- const defaultIconColor = {
692
- primary: colors.primaryForeground,
693
- secondary: colors.foreground,
694
- outline: colors.foreground,
695
- text: colors.foreground,
696
- destructive: colors.destructiveForeground
697
- }[variant];
698
- const spinnerColor = variant === "destructive" ? colors.destructiveForeground : variant === "primary" ? colors.primaryForeground : colors.foreground;
699
- const { container: containerSize, icon: iconSize } = sizeMap[size];
700
- const resolvedIcon = iconName ? renderIcon(iconName, iconSize, iconColor ?? defaultIconColor) : icon;
701
- const showBadge = badge !== void 0 && badge !== false && badge !== 0;
702
- const badgeCount = typeof badge === "number" ? Math.min(badge, 99) : null;
703
- const showCount = typeof badge === "number" && badge > 0;
704
- return /* @__PURE__ */ React26.createElement(
705
- Animated9.View,
706
- {
707
- style: [styles3.wrapper, animatedStyle],
708
- ...hoverHandlers
709
- },
710
- /* @__PURE__ */ React26.createElement(
711
- TouchableOpacity,
712
- {
713
- style: [
714
- styles3.base,
715
- containerVariantStyle,
716
- { width: containerSize, height: containerSize },
717
- isDisabled && styles3.disabled,
718
- style
719
- ],
720
- disabled: isDisabled,
721
- activeOpacity: 1,
722
- touchSoundDisabled: true,
723
- onPress: handlePress,
724
- onPressIn,
725
- onPressOut,
726
- accessibilityRole: "button",
727
- accessibilityLabel: accessibilityLabel ?? iconName ?? "icon button",
728
- accessibilityHint,
729
- accessibilityState: { disabled: isDisabled, busy: loading },
730
- ...props
731
- },
732
- loading ? /* @__PURE__ */ React26.createElement(ActivityIndicator, { size: "small", color: spinnerColor }) : resolvedIcon
733
- ),
734
- showBadge && /* @__PURE__ */ React26.createElement(View, { style: [
735
- styles3.badge,
736
- { backgroundColor: colors.primary },
737
- showCount ? styles3.badgeCount : styles3.badgeDot
738
- ] }, showCount && /* @__PURE__ */ React26.createElement(Text, { style: [styles3.badgeText, { color: colors.primaryForeground }] }, badgeCount))
739
- );
740
- }
741
- var styles3 = StyleSheet.create({
742
- wrapper: {
743
- alignSelf: "flex-start"
744
- },
745
- base: {
746
- borderRadius: 9999,
747
- alignItems: "center",
748
- justifyContent: "center"
749
- },
750
- disabled: {
751
- opacity: 0.45
752
- },
753
- badge: {
754
- position: "absolute",
755
- top: -2,
756
- right: -2,
757
- alignItems: "center",
758
- justifyContent: "center"
759
- },
760
- badgeDot: {
761
- width: 8,
762
- height: 8,
763
- borderRadius: 9999
764
- },
765
- badgeCount: {
766
- minWidth: 16,
767
- height: 16,
768
- borderRadius: 9999,
769
- paddingHorizontal: 3
770
- },
771
- badgeText: {
772
- fontFamily: "Poppins-Bold",
773
- fontSize: ms(9),
774
- lineHeight: 14
775
- }
776
- });
777
- var variantStyles = {
778
- "display-hero": { ...TYPOGRAPHY["display-hero"], fontSize: ms(TYPOGRAPHY["display-hero"].fontSize), lineHeight: mvs(TYPOGRAPHY["display-hero"].lineHeight) },
779
- "display-xl": { ...TYPOGRAPHY["display-xl"], fontSize: ms(TYPOGRAPHY["display-xl"].fontSize), lineHeight: mvs(TYPOGRAPHY["display-xl"].lineHeight) },
780
- "display-lg": { ...TYPOGRAPHY["display-lg"], fontSize: ms(TYPOGRAPHY["display-lg"].fontSize), lineHeight: mvs(TYPOGRAPHY["display-lg"].lineHeight) },
781
- "display-md": { ...TYPOGRAPHY["display-md"], fontSize: ms(TYPOGRAPHY["display-md"].fontSize), lineHeight: mvs(TYPOGRAPHY["display-md"].lineHeight) },
782
- "display-sm": { ...TYPOGRAPHY["display-sm"], fontSize: ms(TYPOGRAPHY["display-sm"].fontSize), lineHeight: mvs(TYPOGRAPHY["display-sm"].lineHeight) },
783
- "title-md": { ...TYPOGRAPHY["title-md"], fontSize: ms(TYPOGRAPHY["title-md"].fontSize), lineHeight: mvs(TYPOGRAPHY["title-md"].lineHeight) },
784
- "title-sm": { ...TYPOGRAPHY["title-sm"], fontSize: ms(TYPOGRAPHY["title-sm"].fontSize), lineHeight: mvs(TYPOGRAPHY["title-sm"].lineHeight) },
785
- "body-md": { ...TYPOGRAPHY["body-md"], fontSize: ms(TYPOGRAPHY["body-md"].fontSize), lineHeight: mvs(TYPOGRAPHY["body-md"].lineHeight) },
786
- "body-sm": { ...TYPOGRAPHY["body-sm"], fontSize: ms(TYPOGRAPHY["body-sm"].fontSize), lineHeight: mvs(TYPOGRAPHY["body-sm"].lineHeight) },
787
- caption: { ...TYPOGRAPHY["caption"], fontSize: ms(TYPOGRAPHY["caption"].fontSize), lineHeight: mvs(TYPOGRAPHY["caption"].lineHeight) },
788
- "caption-sm": { ...TYPOGRAPHY["caption-sm"], fontSize: ms(TYPOGRAPHY["caption-sm"].fontSize), lineHeight: mvs(TYPOGRAPHY["caption-sm"].lineHeight) },
789
- "badge-text": { ...TYPOGRAPHY["badge-text"], fontSize: ms(TYPOGRAPHY["badge-text"].fontSize), lineHeight: mvs(TYPOGRAPHY["badge-text"].lineHeight) },
790
- "micro-label": { ...TYPOGRAPHY["micro-label"], fontSize: ms(TYPOGRAPHY["micro-label"].fontSize), lineHeight: mvs(TYPOGRAPHY["micro-label"].lineHeight) },
791
- "uppercase-tag": { ...TYPOGRAPHY["uppercase-tag"], fontSize: ms(TYPOGRAPHY["uppercase-tag"].fontSize), lineHeight: mvs(TYPOGRAPHY["uppercase-tag"].lineHeight) },
792
- "button-lg": { ...TYPOGRAPHY["button-lg"], fontSize: ms(TYPOGRAPHY["button-lg"].fontSize), lineHeight: mvs(TYPOGRAPHY["button-lg"].lineHeight) },
793
- "button-sm": { ...TYPOGRAPHY["button-sm"], fontSize: ms(TYPOGRAPHY["button-sm"].fontSize), lineHeight: mvs(TYPOGRAPHY["button-sm"].lineHeight) }
794
- };
795
- var defaultColorVariant = {
796
- "display-hero": "foreground",
797
- "display-xl": "foreground",
798
- "display-lg": "foreground",
799
- "display-md": "foreground",
800
- "display-sm": "foreground",
801
- "title-md": "foreground",
802
- "title-sm": "foreground",
803
- "body-md": "foregroundSubtle",
804
- // running text — slightly softer
805
- "body-sm": "foregroundSubtle",
806
- caption: "foregroundMuted",
807
- "caption-sm": "foregroundMuted",
808
- "badge-text": "foreground",
809
- "micro-label": "foreground",
810
- "uppercase-tag": "foregroundMuted",
811
- "button-lg": "foreground",
812
- "button-sm": "foreground"
813
- };
814
- function Text3({ variant = "body-md", color, style, children, ...props }) {
815
- const { colors } = useTheme();
816
- const colorKey = defaultColorVariant[variant] ?? "foreground";
817
- const resolvedColor = color ?? colors[colorKey];
818
- return /* @__PURE__ */ React26.createElement(
819
- Text,
820
- {
821
- style: [variantStyles[variant], { color: resolvedColor }, style],
822
- allowFontScaling: true,
823
- ...props
824
- },
825
- children
826
- );
827
- }
828
- function useColorTransition(active, options = {}) {
829
- const { duration = TIMINGS.state.duration } = options;
830
- const progress = useSharedValue(active ? 1 : 0);
831
- useEffect(() => {
832
- progress.value = withTiming(active ? 1 : 0, { duration, easing: EASINGS.standard });
833
- }, [active, duration, progress]);
834
- return progress;
835
- }
836
-
837
- // src/components/Input/Input.tsx
838
- var webInputResetStyle = Platform.OS === "web" ? { outlineStyle: "none", outlineWidth: 0, outlineColor: "transparent", boxShadow: "none" } : {};
839
- function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type = "text", containerStyle, inputWrapperStyle, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }) {
840
- const { colors } = useTheme();
841
- const [focused, setFocused] = useState(false);
842
- const [showPassword, setShowPassword] = useState(false);
843
- const focusProgress = useColorTransition(focused, {
844
- duration: focused ? TIMINGS.focusIn.duration : TIMINGS.focusOut.duration
845
- });
846
- const isDisabled = disabled || editable === false;
847
- const isPassword = type === "password";
848
- const effectiveSecure = isPassword ? !showPassword : secureTextEntry;
849
- const effectivePrefix = prefixIcon ? renderIcon(prefixIcon, 20, prefixIconColor ?? colors.foregroundMuted) : prefix;
850
- const effectiveSuffix = isPassword && !suffix && !suffixIcon ? /* @__PURE__ */ React26.createElement(
851
- TouchableOpacity,
852
- {
853
- onPress: () => setShowPassword(!showPassword),
854
- style: styles4.passwordToggle,
855
- activeOpacity: 0.6,
856
- accessibilityRole: "button",
857
- accessibilityLabel: showPassword ? "Hide password" : "Show password"
858
- },
859
- /* @__PURE__ */ React26.createElement(AntDesign$1, { name: showPassword ? "eye" : "eye-invisible", size: 20, color: colors.foregroundMuted })
860
- ) : suffixIcon ? renderIcon(suffixIcon, 20, suffixIconColor ?? colors.foregroundMuted) : suffix;
861
- const borderColorStyle = useAnimatedStyle(() => ({
862
- borderColor: error ? colors.destructive : interpolateColor(focusProgress.value, [0, 1], [colors.border, colors.primary])
863
- }));
864
- return /* @__PURE__ */ React26.createElement(View, { style: [styles4.container, isDisabled && styles4.containerDisabled, containerStyle] }, label ? /* @__PURE__ */ React26.createElement(Text, { style: [styles4.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, /* @__PURE__ */ React26.createElement(
865
- Animated9.View,
866
- {
867
- style: [
868
- styles4.inputWrapper,
869
- { backgroundColor: isDisabled ? colors.surface : colors.background },
870
- borderColorStyle,
871
- inputWrapperStyle
872
- ]
873
- },
874
- effectivePrefix ? typeof effectivePrefix === "string" ? /* @__PURE__ */ React26.createElement(Text, { style: [styles4.prefixText, { color: colors.foregroundMuted }, prefixStyle], allowFontScaling: true }, effectivePrefix) : /* @__PURE__ */ React26.createElement(View, { style: styles4.prefixContainer }, effectivePrefix) : null,
875
- /* @__PURE__ */ React26.createElement(
876
- TextInput,
877
- {
878
- style: [
879
- styles4.input,
880
- { color: colors.foreground },
881
- webInputResetStyle,
882
- style
883
- ],
884
- onFocus: (e) => {
885
- setFocused(true);
886
- onFocus?.(e);
887
- },
888
- onBlur: (e) => {
889
- setFocused(false);
890
- onBlur?.(e);
891
- },
892
- placeholderTextColor: colors.foregroundMuted,
893
- allowFontScaling: true,
894
- secureTextEntry: effectiveSecure,
895
- editable: isDisabled ? false : editable,
896
- accessibilityLabel: accessibilityLabel ?? label,
897
- ...props
898
- }
899
- ),
900
- effectiveSuffix ? typeof effectiveSuffix === "string" ? /* @__PURE__ */ React26.createElement(Text, { style: [styles4.suffixText, { color: colors.foregroundMuted }, suffixStyle], allowFontScaling: true }, effectiveSuffix) : /* @__PURE__ */ React26.createElement(View, { style: styles4.suffixContainer }, effectiveSuffix) : null
901
- ), error ? /* @__PURE__ */ React26.createElement(
902
- Text,
903
- {
904
- style: [styles4.helperText, { color: colors.destructive }],
905
- allowFontScaling: true,
906
- accessibilityLiveRegion: "polite"
907
- },
908
- error
909
- ) : null, !error && hint ? /* @__PURE__ */ React26.createElement(Text, { style: [styles4.helperText, { color: colors.foregroundMuted }], allowFontScaling: true }, hint) : null);
910
- }
911
- var styles4 = StyleSheet.create({
912
- container: {
913
- gap: vs(8)
914
- },
915
- containerDisabled: {
916
- opacity: 0.6
917
- },
918
- label: {
919
- fontFamily: "Poppins-Medium",
920
- fontSize: ms(14)
921
- },
922
- inputWrapper: {
923
- flexDirection: "row",
924
- alignItems: "center",
925
- borderWidth: 2,
926
- borderRadius: 8,
927
- paddingHorizontal: s(14),
928
- paddingVertical: vs(11),
929
- minHeight: 48
930
- },
931
- input: {
932
- fontFamily: "Poppins-Regular",
933
- flex: 1,
934
- fontSize: ms(16),
935
- paddingVertical: vs(2),
936
- includeFontPadding: false
937
- },
938
- prefixContainer: {
939
- marginRight: s(8)
940
- },
941
- prefixText: {
942
- fontFamily: "Poppins-Regular",
943
- fontSize: ms(15),
944
- marginRight: s(8)
945
- },
946
- suffixContainer: {
947
- marginLeft: s(8)
948
- },
949
- suffixText: {
950
- fontFamily: "Poppins-Regular",
951
- fontSize: ms(15),
952
- marginLeft: s(8)
953
- },
954
- passwordToggle: {
955
- padding: s(4)
956
- },
957
- helperText: {
958
- fontFamily: "Poppins-Regular",
959
- fontSize: ms(13)
960
- }
961
- });
962
- var sizePadding = {
963
- sm: { paddingHorizontal: s(8), paddingVertical: vs(2) },
964
- md: { paddingHorizontal: s(10), paddingVertical: vs(4) },
965
- lg: { paddingHorizontal: s(12), paddingVertical: vs(6) }
966
- };
967
- var sizeFontSize = {
968
- sm: { fontSize: ms(11) },
969
- md: { fontSize: ms(13) },
970
- lg: { fontSize: ms(15) }
971
- };
972
- var sizeIconGap = {
973
- sm: s(4),
974
- md: s(6),
975
- lg: s(6)
976
- };
977
- var sizeIconSize = { sm: 10, md: 12, lg: 14 };
978
- function Badge({ label, children, variant = "default", size = "md", icon, iconName, iconColor, style }) {
979
- const { colors } = useTheme();
980
- const containerStyle = {
981
- default: { backgroundColor: colors.primary },
982
- secondary: { backgroundColor: colors.surface },
983
- destructive: { backgroundColor: colors.destructive },
984
- outline: { backgroundColor: "transparent", borderWidth: 1, borderColor: colors.border },
985
- success: { backgroundColor: colors.success },
986
- warning: { backgroundColor: colors.warning },
987
- successOutline: { backgroundColor: colors.successTint, borderWidth: 1, borderColor: colors.successBorder },
988
- destructiveOutline: { backgroundColor: colors.destructiveTint, borderWidth: 1, borderColor: colors.destructiveBorder },
989
- warningOutline: { backgroundColor: colors.warningTint, borderWidth: 1, borderColor: colors.warningBorder }
990
- }[variant];
991
- const textColor = {
992
- default: colors.primaryForeground,
993
- secondary: colors.foreground,
994
- destructive: colors.destructiveForeground,
995
- outline: colors.foreground,
996
- success: colors.successForeground,
997
- warning: colors.warningForeground,
998
- successOutline: colors.success,
999
- destructiveOutline: colors.destructive,
1000
- warningOutline: colors.warning
1001
- }[variant];
1002
- const effectiveIcon = iconName ? renderIcon(iconName, sizeIconSize[size], iconColor ?? textColor) : icon;
1003
- const content = children ?? label;
1004
- return /* @__PURE__ */ React26.createElement(View, { style: [styles5.container, containerStyle, sizePadding[size], { gap: sizeIconGap[size] }, style] }, effectiveIcon, typeof content === "string" ? /* @__PURE__ */ React26.createElement(Text, { style: [styles5.label, { color: textColor }, sizeFontSize[size]], allowFontScaling: true }, content) : content);
1005
- }
1006
- var styles5 = StyleSheet.create({
1007
- container: {
1008
- borderRadius: 9999,
1009
- alignSelf: "flex-start",
1010
- flexDirection: "row",
1011
- alignItems: "center"
1012
- },
1013
- label: {
1014
- fontFamily: "Poppins-Medium"
1015
- }
1016
- });
1017
- function Card({ children, variant = "elevated", onPress, style, accessibilityLabel }) {
1018
- const { colors } = useTheme();
1019
- const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
1020
- pressScale: PRESS_SCALE.card,
1021
- pressInSpring: SPRINGS.surfacePressIn,
1022
- pressOutSpring: SPRINGS.surfacePressOut,
1023
- disabled: !onPress
1024
- });
1025
- const handlePress = () => {
1026
- if (!onPress) return;
1027
- impactLight();
1028
- onPress();
1029
- };
1030
- const variantStyle = {
1031
- elevated: {
1032
- backgroundColor: colors.card,
1033
- borderColor: colors.border,
1034
- shadowColor: "#000",
1035
- shadowOffset: { width: 0, height: 6 },
1036
- shadowOpacity: 0.1,
1037
- shadowRadius: 16,
1038
- elevation: 4
1039
- },
1040
- outlined: {
1041
- backgroundColor: colors.card,
1042
- borderColor: colors.border,
1043
- shadowOpacity: 0,
1044
- elevation: 0
1045
- },
1046
- filled: {
1047
- backgroundColor: colors.surfaceStrong,
1048
- borderColor: colors.border,
1049
- shadowOpacity: 0,
1050
- elevation: 0
1051
- }
1052
- }[variant];
1053
- const cardContent = /* @__PURE__ */ React26.createElement(View, { style: [styles6.card, variantStyle, style] }, children);
1054
- if (onPress) {
1055
- return /* @__PURE__ */ React26.createElement(Animated9.View, { style: animatedStyle, ...hoverHandlers }, /* @__PURE__ */ React26.createElement(
1056
- TouchableOpacity,
1057
- {
1058
- onPress: handlePress,
1059
- onPressIn,
1060
- onPressOut,
1061
- activeOpacity: 1,
1062
- touchSoundDisabled: true,
1063
- accessibilityRole: "button",
1064
- accessibilityLabel
1065
- },
1066
- cardContent
1067
- ));
1068
- }
1069
- return cardContent;
1070
- }
1071
- function CardHeader({ children, style }) {
1072
- return /* @__PURE__ */ React26.createElement(View, { style: [styles6.header, style] }, children);
1073
- }
1074
- function CardTitle({ children, style }) {
1075
- const { colors } = useTheme();
1076
- return /* @__PURE__ */ React26.createElement(Text, { style: [styles6.title, { color: colors.foreground }, style], allowFontScaling: true }, children);
1077
- }
1078
- function CardDescription({ children, style }) {
1079
- const { colors } = useTheme();
1080
- return /* @__PURE__ */ React26.createElement(Text, { style: [styles6.description, { color: colors.foregroundMuted }, style], allowFontScaling: true }, children);
1081
- }
1082
- function CardContent({ children, style }) {
1083
- return /* @__PURE__ */ React26.createElement(View, { style: [styles6.content, style] }, children);
1084
- }
1085
- function CardFooter({ children, style }) {
1086
- return /* @__PURE__ */ React26.createElement(View, { style: [styles6.footer, style] }, children);
1087
- }
1088
- var styles6 = StyleSheet.create({
1089
- card: {
1090
- borderRadius: RADIUS.md,
1091
- borderWidth: 1
1092
- },
1093
- header: {
1094
- padding: s(16),
1095
- paddingBottom: 0,
1096
- gap: vs(4)
1097
- },
1098
- title: {
1099
- fontFamily: "Poppins-SemiBold",
1100
- fontSize: ms(16),
1101
- lineHeight: mvs(22)
1102
- },
1103
- description: {
1104
- fontFamily: "Poppins-Regular",
1105
- fontSize: ms(13),
1106
- lineHeight: mvs(18)
1107
- },
1108
- content: {
1109
- padding: s(16),
1110
- paddingTop: s(12)
1111
- },
1112
- footer: {
1113
- paddingHorizontal: s(16),
1114
- paddingBottom: vs(14),
1115
- paddingTop: 0,
1116
- flexDirection: "row",
1117
- alignItems: "center"
1118
- }
1119
- });
1120
- function Separator({ orientation = "horizontal", style }) {
1121
- const { colors } = useTheme();
1122
- return /* @__PURE__ */ React26.createElement(
1123
- View,
1124
- {
1125
- style: [
1126
- orientation === "horizontal" ? styles7.horizontal : styles7.vertical,
1127
- { backgroundColor: colors.border },
1128
- style
1129
- ]
1130
- }
1131
- );
1132
- }
1133
- var styles7 = StyleSheet.create({
1134
- horizontal: {
1135
- height: 1,
1136
- width: "100%",
1137
- opacity: 0.7
1138
- },
1139
- vertical: {
1140
- width: 1,
1141
- height: "100%",
1142
- opacity: 0.7
1143
- }
1144
- });
1145
- var sizeMap2 = {
1146
- sm: "small",
1147
- md: "small",
1148
- lg: "large"
1149
- };
1150
- var labelFontSize = {
1151
- sm: ms(11),
1152
- md: ms(13),
1153
- lg: ms(14)
1154
- };
1155
- function Spinner({ size = "md", color, label, ...props }) {
1156
- const { colors } = useTheme();
1157
- if (label) {
1158
- return /* @__PURE__ */ React26.createElement(View, { style: styles8.wrapper }, /* @__PURE__ */ React26.createElement(ActivityIndicator, { size: sizeMap2[size], color: color ?? colors.primary, ...props }), /* @__PURE__ */ React26.createElement(
1159
- Text,
1160
- {
1161
- style: [styles8.label, { color: colors.foregroundMuted, fontSize: labelFontSize[size] }],
1162
- allowFontScaling: true
1163
- },
1164
- label
1165
- ));
1166
- }
1167
- return /* @__PURE__ */ React26.createElement(ActivityIndicator, { size: sizeMap2[size], color: color ?? colors.primary, ...props });
1168
- }
1169
- var styles8 = StyleSheet.create({
1170
- wrapper: {
1171
- alignItems: "center",
1172
- gap: vs(6)
1173
- },
1174
- label: {
1175
- fontFamily: "Poppins-Regular",
1176
- lineHeight: mvs(18)
1177
- }
1178
- });
1179
- function Skeleton({
1180
- width = "100%",
1181
- height = 16,
1182
- borderRadius = 6,
1183
- preset = "base",
1184
- diameter = 40,
1185
- style
1186
- }) {
1187
- const { colors, colorScheme } = useTheme();
1188
- const shimmer = useSharedValue(0);
1189
- const [containerWidth, setContainerWidth] = useState(300);
1190
- const shimmerHighlight = colorScheme === "dark" ? "rgba(255,255,255,0.08)" : "rgba(255,255,255,0.7)";
1191
- useEffect(() => {
1192
- shimmer.value = withRepeat(
1193
- withTiming(1, { duration: TIMINGS.shimmer.duration, easing: Easing.linear }),
1194
- -1,
1195
- false
1196
- );
1197
- }, [shimmer]);
1198
- const shimmerStyle = useAnimatedStyle(() => ({
1199
- transform: [{ translateX: -containerWidth + shimmer.value * (containerWidth * 2) }]
1200
- }));
1201
- const resolvedWidth = preset === "circle" ? s(diameter) : preset === "text" ? "60%" : width;
1202
- const resolvedHeight = preset === "circle" ? s(diameter) : preset === "text" ? 14 : height;
1203
- const resolvedRadius = preset === "circle" ? 9999 : preset === "text" ? 4 : borderRadius;
1204
- return /* @__PURE__ */ React26.createElement(
1205
- View,
1206
- {
1207
- style: [
1208
- styles9.base,
1209
- { width: resolvedWidth, height: resolvedHeight, borderRadius: resolvedRadius, backgroundColor: colors.surface },
1210
- style
1211
- ],
1212
- onLayout: (e) => setContainerWidth(e.nativeEvent.layout.width),
1213
- accessibilityRole: "progressbar",
1214
- accessibilityLabel: "Loading",
1215
- accessibilityState: { busy: true }
1216
- },
1217
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: [StyleSheet.absoluteFill, shimmerStyle] }, /* @__PURE__ */ React26.createElement(
1218
- LinearGradient,
1219
- {
1220
- colors: ["transparent", shimmerHighlight, "transparent"],
1221
- start: { x: 0, y: 0 },
1222
- end: { x: 1, y: 0 },
1223
- style: StyleSheet.absoluteFill
1224
- }
1225
- ))
1226
- );
1227
- }
1228
- var styles9 = StyleSheet.create({
1229
- base: {
1230
- overflow: "hidden"
1231
- }
1232
- });
1233
- var sizeMap3 = {
1234
- sm: s(28),
1235
- md: s(40),
1236
- lg: s(56),
1237
- xl: s(72)
1238
- };
1239
- var fontSizeMap = {
1240
- sm: ms(12),
1241
- md: ms(16),
1242
- lg: ms(22),
1243
- xl: ms(28)
1244
- };
1245
- var statusSizeMap = {
1246
- sm: 8,
1247
- md: 10,
1248
- lg: 13,
1249
- xl: 16
1250
- };
1251
- function getInitials(fallback, fallbackText) {
1252
- if (fallback) return fallback.slice(0, 2).toUpperCase();
1253
- if (fallbackText) {
1254
- const words = fallbackText.trim().split(/\s+/);
1255
- if (words.length === 1) return words[0].slice(0, 2).toUpperCase();
1256
- return (words[0][0] + words[words.length - 1][0]).toUpperCase();
1257
- }
1258
- return "?";
1259
- }
1260
- function Avatar({ src, fallback, fallbackText, size = "md", status, style }) {
1261
- const { colors } = useTheme();
1262
- const [imageError, setImageError] = useState(false);
1263
- const dimension = typeof size === "number" ? size : sizeMap3[size];
1264
- const fontSize = typeof size === "number" ? size * 0.38 : fontSizeMap[size];
1265
- const showFallback = !src || imageError;
1266
- const statusSize = typeof size === "number" ? size * 0.25 : statusSizeMap[size];
1267
- const statusColor = {
1268
- online: "#22c55e",
1269
- offline: "transparent",
1270
- busy: colors.destructive,
1271
- away: colors.warning
1272
- };
1273
- const containerStyle = {
1274
- width: dimension,
1275
- height: dimension,
1276
- borderRadius: dimension / 2,
1277
- backgroundColor: colors.surface,
1278
- overflow: "hidden"
1279
- };
1280
- return /* @__PURE__ */ React26.createElement(View, { style: [styles10.wrapper, style] }, /* @__PURE__ */ React26.createElement(View, { style: [styles10.base, containerStyle] }, !showFallback ? /* @__PURE__ */ React26.createElement(
1281
- Image,
1282
- {
1283
- source: { uri: src },
1284
- style: { width: dimension, height: dimension },
1285
- onError: () => setImageError(true)
1286
- }
1287
- ) : /* @__PURE__ */ React26.createElement(
1288
- Text,
1289
- {
1290
- style: [styles10.fallback, { color: colors.foregroundMuted, fontSize }],
1291
- allowFontScaling: true
1292
- },
1293
- getInitials(fallback, fallbackText)
1294
- )), status && /* @__PURE__ */ React26.createElement(
1295
- View,
1296
- {
1297
- style: [
1298
- styles10.statusDot,
1299
- {
1300
- width: statusSize,
1301
- height: statusSize,
1302
- borderRadius: statusSize / 2,
1303
- backgroundColor: statusColor[status],
1304
- borderWidth: status === "offline" ? 2 : 1.5,
1305
- borderColor: status === "offline" ? colors.border : colors.background
1306
- }
1307
- ]
1308
- }
1309
- ));
1310
- }
1311
- var styles10 = StyleSheet.create({
1312
- wrapper: {
1313
- alignSelf: "flex-start",
1314
- position: "relative"
1315
- },
1316
- base: {
1317
- alignItems: "center",
1318
- justifyContent: "center"
1319
- },
1320
- fallback: {
1321
- fontFamily: "Poppins-Medium"
1322
- },
1323
- statusDot: {
1324
- position: "absolute",
1325
- bottom: 0,
1326
- right: 0
1327
- }
1328
- });
1329
- function AlertBanner({ title, description, variant = "default", icon, iconName, iconColor, style }) {
1330
- const { colors } = useTheme();
1331
- const accentColor = variant === "destructive" ? colors.destructive : variant === "success" ? colors.success : variant === "warning" ? colors.warning : colors.primary;
1332
- const defaultIcon = variant === "success" ? /* @__PURE__ */ React26.createElement(FontAwesome5$1, { name: "check-circle", size: ms(16), color: accentColor }) : variant === "destructive" ? /* @__PURE__ */ React26.createElement(MaterialIcons$1, { name: "error-outline", size: ms(17), color: accentColor }) : variant === "warning" ? /* @__PURE__ */ React26.createElement(MaterialIcons$1, { name: "warning-amber", size: ms(17), color: accentColor }) : /* @__PURE__ */ React26.createElement(Entypo$1, { name: "info-with-circle", size: ms(16), color: accentColor });
1333
- const effectiveIcon = iconName ? renderIcon(iconName, ms(16), iconColor ?? accentColor) : icon ?? defaultIcon;
1334
- return /* @__PURE__ */ React26.createElement(View, { style: [styles11.container, { backgroundColor: colors.card }, style] }, /* @__PURE__ */ React26.createElement(View, { style: styles11.iconSlot }, effectiveIcon), /* @__PURE__ */ React26.createElement(View, { style: styles11.content }, /* @__PURE__ */ React26.createElement(Text, { style: [styles11.title, { color: colors.foreground }], allowFontScaling: true }, title), description ? /* @__PURE__ */ React26.createElement(Text, { style: [styles11.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null));
1335
- }
1336
- var styles11 = StyleSheet.create({
1337
- container: {
1338
- flexDirection: "row",
1339
- alignItems: "flex-start",
1340
- borderRadius: RADIUS.lg,
1341
- gap: s(8),
1342
- paddingVertical: vs(8),
1343
- paddingHorizontal: s(10)
1344
- },
1345
- iconSlot: {
1346
- marginTop: vs(1)
1347
- },
1348
- content: {
1349
- flex: 1,
1350
- gap: vs(2)
1351
- },
1352
- title: {
1353
- fontFamily: "Poppins-Medium",
1354
- fontSize: ms(13),
1355
- lineHeight: ms(19)
1356
- },
1357
- description: {
1358
- fontFamily: "Poppins-Regular",
1359
- fontSize: ms(12),
1360
- lineHeight: ms(17)
1361
- }
1362
- });
1363
- function Progress({ value = 0, max = 100, variant = "default", style, accessibilityLabel }) {
1364
- const { colors } = useTheme();
1365
- const percent = Math.min(Math.max(value / max * 100, 0), 100);
1366
- const [trackWidth, setTrackWidth] = useState(0);
1367
- const animatedWidth = useSharedValue(0);
1368
- useEffect(() => {
1369
- if (trackWidth === 0) return;
1370
- animatedWidth.value = withSpring(percent / 100 * trackWidth, SPRINGS.glide);
1371
- }, [percent, trackWidth, animatedWidth]);
1372
- const indicatorAnimatedStyle = useAnimatedStyle(() => ({
1373
- width: animatedWidth.value
1374
- }));
1375
- const indicatorColor = variant === "success" ? colors.success : variant === "warning" ? colors.warning : variant === "destructive" ? colors.destructive : colors.primary;
1376
- return /* @__PURE__ */ React26.createElement(
1377
- View,
1378
- {
1379
- style: [styles12.track, { backgroundColor: colors.surface }, style],
1380
- onLayout: (e) => setTrackWidth(e.nativeEvent.layout.width),
1381
- accessibilityRole: "progressbar",
1382
- accessibilityLabel,
1383
- accessibilityValue: { min: 0, max: 100, now: Math.round(percent) }
1384
- },
1385
- /* @__PURE__ */ React26.createElement(
1386
- Animated9.View,
1387
- {
1388
- style: [styles12.indicator, { backgroundColor: indicatorColor }, indicatorAnimatedStyle]
1389
- }
1390
- )
1391
- );
1392
- }
1393
- var styles12 = StyleSheet.create({
1394
- track: {
1395
- height: vs(8),
1396
- borderRadius: 9999,
1397
- overflow: "hidden",
1398
- width: "100%"
1399
- },
1400
- indicator: {
1401
- height: "100%",
1402
- borderRadius: 9999
1403
- }
1404
- });
1405
- function EmptyState({ icon, iconName, iconColor, title, description, action, actionLabel, onAction, size = "default", style }) {
1406
- const { colors } = useTheme();
1407
- const isCompact = size === "compact";
1408
- const effectiveIcon = iconName ? renderIcon(iconName, isCompact ? 32 : 48, iconColor ?? colors.foregroundMuted) : icon;
1409
- return /* @__PURE__ */ React26.createElement(
1410
- View,
1411
- {
1412
- style: [
1413
- styles13.container,
1414
- isCompact && styles13.containerCompact,
1415
- { borderColor: colors.border },
1416
- style
1417
- ]
1418
- },
1419
- effectiveIcon ? /* @__PURE__ */ React26.createElement(
1420
- View,
1421
- {
1422
- style: [
1423
- styles13.iconWrapper,
1424
- isCompact && styles13.iconWrapperCompact,
1425
- { backgroundColor: colors.surface }
1426
- ]
1427
- },
1428
- effectiveIcon
1429
- ) : null,
1430
- /* @__PURE__ */ React26.createElement(View, { style: styles13.textWrapper }, /* @__PURE__ */ React26.createElement(
1431
- Text,
1432
- {
1433
- style: [styles13.title, isCompact && styles13.titleCompact, { color: colors.foreground }],
1434
- allowFontScaling: true
1435
- },
1436
- title
1437
- ), description && !isCompact ? /* @__PURE__ */ React26.createElement(Text, { style: [styles13.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null),
1438
- !isCompact && (action ? /* @__PURE__ */ React26.createElement(View, { style: styles13.action }, action) : actionLabel && onAction ? /* @__PURE__ */ React26.createElement(View, { style: styles13.action }, /* @__PURE__ */ React26.createElement(Button, { label: actionLabel, variant: "primary", onPress: onAction })) : null)
1439
- );
1440
- }
1441
- var styles13 = StyleSheet.create({
1442
- container: {
1443
- alignItems: "center",
1444
- justifyContent: "center",
1445
- borderWidth: 1,
1446
- borderStyle: "dashed",
1447
- borderRadius: ms(12)
1448
- },
1449
- containerCompact: {},
1450
- iconWrapper: {
1451
- width: s(80),
1452
- height: s(80),
1453
- borderRadius: ms(20),
1454
- alignItems: "center",
1455
- justifyContent: "center",
1456
- marginTop: s(32)
1457
- },
1458
- iconWrapperCompact: {
1459
- width: s(56),
1460
- height: s(56),
1461
- borderRadius: ms(14),
1462
- marginTop: s(20)
1463
- },
1464
- textWrapper: {
1465
- alignItems: "center",
1466
- gap: vs(8),
1467
- maxWidth: s(320),
1468
- paddingHorizontal: s(32),
1469
- marginTop: vs(16)
1470
- },
1471
- title: {
1472
- fontFamily: "Poppins-Medium",
1473
- fontSize: ms(18),
1474
- textAlign: "center"
1475
- },
1476
- titleCompact: {
1477
- fontSize: ms(15),
1478
- marginTop: vs(10)
1479
- },
1480
- description: {
1481
- fontFamily: "Poppins-Regular",
1482
- fontSize: ms(14),
1483
- lineHeight: mvs(20),
1484
- textAlign: "center"
1485
- },
1486
- action: {
1487
- marginTop: vs(8),
1488
- marginBottom: s(32),
1489
- paddingHorizontal: s(32)
1490
- }
1491
- });
1492
- var webInputResetStyle2 = Platform.OS === "web" ? { outlineStyle: "none", outlineWidth: 0, outlineColor: "transparent", boxShadow: "none" } : {};
1493
- function Textarea({
1494
- label,
1495
- error,
1496
- hint,
1497
- rows = 4,
1498
- prefixIcon,
1499
- prefixIconNode,
1500
- prefixIconColor,
1501
- containerStyle,
1502
- style,
1503
- onFocus,
1504
- onBlur,
1505
- accessibilityLabel,
1506
- ...props
1507
- }) {
1508
- const { colors } = useTheme();
1509
- const [focused, setFocused] = useState(false);
1510
- const focusProgress = useColorTransition(focused, {
1511
- duration: focused ? TIMINGS.focusIn.duration : TIMINGS.focusOut.duration
1512
- });
1513
- const resolvedPrefixIcon = prefixIcon ? renderIcon(prefixIcon, ms(16), prefixIconColor ?? colors.foregroundMuted) : prefixIconNode;
1514
- const borderColorStyle = useAnimatedStyle(() => ({
1515
- borderColor: error ? colors.destructive : interpolateColor(focusProgress.value, [0, 1], [colors.border, colors.primary])
1516
- }));
1517
- return /* @__PURE__ */ React26.createElement(View, { style: [styles14.container, containerStyle] }, label ? /* @__PURE__ */ React26.createElement(Text, { style: [styles14.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, /* @__PURE__ */ React26.createElement(
1518
- Animated9.View,
1519
- {
1520
- style: [
1521
- styles14.inputWrapper,
1522
- { backgroundColor: colors.background },
1523
- borderColorStyle
1524
- ]
1525
- },
1526
- resolvedPrefixIcon ? /* @__PURE__ */ React26.createElement(View, { style: styles14.prefixIcon }, resolvedPrefixIcon) : null,
1527
- /* @__PURE__ */ React26.createElement(
1528
- TextInput,
1529
- {
1530
- multiline: true,
1531
- numberOfLines: rows,
1532
- textAlignVertical: "top",
1533
- style: [
1534
- styles14.input,
1535
- {
1536
- color: colors.foreground,
1537
- minHeight: rows * vs(30)
1538
- },
1539
- webInputResetStyle2,
1540
- style
1541
- ],
1542
- onFocus: (e) => {
1543
- setFocused(true);
1544
- onFocus?.(e);
1545
- },
1546
- onBlur: (e) => {
1547
- setFocused(false);
1548
- onBlur?.(e);
1549
- },
1550
- placeholderTextColor: colors.foregroundMuted,
1551
- allowFontScaling: true,
1552
- accessibilityLabel: accessibilityLabel ?? label,
1553
- ...props
1554
- }
1555
- )
1556
- ), error ? /* @__PURE__ */ React26.createElement(
1557
- Text,
1558
- {
1559
- style: [styles14.helperText, { color: colors.destructive }],
1560
- allowFontScaling: true,
1561
- accessibilityLiveRegion: "polite"
1562
- },
1563
- error
1564
- ) : null, !error && hint ? /* @__PURE__ */ React26.createElement(Text, { style: [styles14.helperText, { color: colors.foregroundMuted }], allowFontScaling: true }, hint) : null);
1565
- }
1566
- var styles14 = StyleSheet.create({
1567
- container: {
1568
- gap: vs(4)
1569
- },
1570
- label: {
1571
- fontFamily: "Poppins-Medium",
1572
- fontSize: ms(13),
1573
- lineHeight: vs(18),
1574
- marginBottom: vs(2)
1575
- },
1576
- inputWrapper: {
1577
- borderWidth: 2,
1578
- borderRadius: 8,
1579
- paddingHorizontal: s(14),
1580
- paddingVertical: vs(11),
1581
- gap: s(8)
1582
- },
1583
- prefixIcon: {
1584
- alignItems: "flex-start",
1585
- justifyContent: "flex-start",
1586
- paddingTop: vs(2)
1587
- },
1588
- input: {
1589
- fontFamily: "Poppins-Regular",
1590
- fontSize: ms(14),
1591
- lineHeight: vs(22),
1592
- padding: 0,
1593
- margin: 0
1594
- },
1595
- helperText: {
1596
- fontFamily: "Poppins-Regular",
1597
- fontSize: ms(12),
1598
- lineHeight: vs(16),
1599
- marginTop: vs(4)
1600
- }
1601
- });
1602
- function Checkbox({
1603
- checked = false,
1604
- onCheckedChange,
1605
- label,
1606
- disabled,
1607
- style,
1608
- accessibilityLabel
1609
- }) {
1610
- const { colors } = useTheme();
1611
- const { animatedStyle: scaleStyle, onPressIn, onPressOut } = usePressScale({
1612
- pressScale: PRESS_SCALE.button,
1613
- disabled
1614
- });
1615
- const progress = useColorTransition(checked);
1616
- const boxStyle = useAnimatedStyle(() => ({
1617
- borderColor: interpolateColor(progress.value, [0, 1], [colors.border, colors.primary]),
1618
- backgroundColor: interpolateColor(progress.value, [0, 1], ["transparent", colors.primary])
1619
- }));
1620
- const checkStyle = useAnimatedStyle(() => ({
1621
- opacity: withTiming(checked ? 1 : 0, { duration: TIMINGS.state.duration, easing: EASINGS.standard })
1622
- }));
1623
- return /* @__PURE__ */ React26.createElement(
1624
- TouchableOpacity,
1625
- {
1626
- style: [styles15.row, style],
1627
- onPress: () => {
1628
- selectionAsync();
1629
- onCheckedChange?.(!checked);
1630
- },
1631
- onPressIn,
1632
- onPressOut,
1633
- disabled,
1634
- activeOpacity: 1,
1635
- touchSoundDisabled: true,
1636
- accessibilityRole: "checkbox",
1637
- accessibilityLabel: accessibilityLabel ?? label,
1638
- accessibilityState: { checked, disabled: !!disabled }
1639
- },
1640
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: scaleStyle }, /* @__PURE__ */ React26.createElement(
1641
- Animated9.View,
1642
- {
1643
- style: [styles15.box, { opacity: disabled ? 0.45 : 1 }, boxStyle]
1644
- },
1645
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: checkStyle }, /* @__PURE__ */ React26.createElement(View, { style: [styles15.checkmark, { borderColor: colors.primaryForeground }] }))
1646
- )),
1647
- label ? /* @__PURE__ */ React26.createElement(
1648
- Text,
1649
- {
1650
- style: [styles15.label, { color: disabled ? colors.foregroundMuted : colors.foreground }],
1651
- allowFontScaling: true
1652
- },
1653
- label
1654
- ) : null
1655
- );
1656
- }
1657
- var styles15 = StyleSheet.create({
1658
- row: {
1659
- flexDirection: "row",
1660
- alignItems: "center",
1661
- gap: s(12)
1662
- },
1663
- box: {
1664
- width: s(24),
1665
- height: s(24),
1666
- borderRadius: ms(4),
1667
- borderWidth: 1.5,
1668
- alignItems: "center",
1669
- justifyContent: "center"
1670
- },
1671
- checkmark: {
1672
- width: s(12),
1673
- height: vs(7),
1674
- borderLeftWidth: 2,
1675
- borderBottomWidth: 2,
1676
- transform: [{ rotate: "-45deg" }, { translateY: -1 }]
1677
- },
1678
- label: {
1679
- fontFamily: "Poppins-Regular",
1680
- fontSize: ms(14),
1681
- lineHeight: mvs(20)
1682
- }
1683
- });
1684
- var TRACK_WIDTH = s(52);
1685
- var TRACK_HEIGHT = s(30);
1686
- var THUMB_SIZE = s(24);
1687
- var THUMB_OFFSET = s(3);
1688
- var THUMB_TRAVEL = TRACK_WIDTH - THUMB_SIZE - THUMB_OFFSET * 2;
1689
- var ICON_SIZE = s(13);
1690
- function Switch({ checked = false, onCheckedChange, disabled, style, accessibilityLabel }) {
1691
- const { colors } = useTheme();
1692
- const progress = useSharedValue(checked ? 1 : 0);
1693
- useEffect(() => {
1694
- progress.value = withSpring(checked ? 1 : 0, SPRINGS.elastic);
1695
- }, [checked, progress]);
1696
- const thumbStyle = useAnimatedStyle(() => ({
1697
- transform: [{ translateX: progress.value * THUMB_TRAVEL }]
1698
- }));
1699
- const trackStyle = useAnimatedStyle(() => ({
1700
- backgroundColor: interpolateColor(
1701
- progress.value,
1702
- [0, 1],
1703
- [colors.surfaceStrong, colors.primary]
1704
- )
1705
- }));
1706
- const checkIconStyle = useAnimatedStyle(() => ({
1707
- opacity: withTiming(checked ? 1 : 0, { duration: TIMINGS.state.duration, easing: EASINGS.standard })
1708
- }));
1709
- const crossIconStyle = useAnimatedStyle(() => ({
1710
- opacity: withTiming(checked ? 0 : 1, { duration: TIMINGS.state.duration, easing: EASINGS.standard })
1711
- }));
1712
- return /* @__PURE__ */ React26.createElement(View, { style: [{ opacity: disabled ? 0.45 : 1 }, style] }, /* @__PURE__ */ React26.createElement(
1713
- TouchableOpacity,
1714
- {
1715
- onPress: () => {
1716
- selectionAsync();
1717
- onCheckedChange?.(!checked);
1718
- },
1719
- disabled,
1720
- activeOpacity: 0.8,
1721
- touchSoundDisabled: true,
1722
- accessibilityRole: "switch",
1723
- accessibilityLabel,
1724
- accessibilityState: { checked, disabled: !!disabled }
1725
- },
1726
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: [styles16.track, trackStyle] }, /* @__PURE__ */ React26.createElement(
1727
- Animated9.View,
1728
- {
1729
- style: [styles16.thumb, { backgroundColor: colors.primaryForeground }, thumbStyle]
1730
- },
1731
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: [styles16.iconWrapper, checkIconStyle] }, /* @__PURE__ */ React26.createElement(Feather$1, { name: "check", size: ICON_SIZE, color: colors.primary })),
1732
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: [styles16.iconWrapper, crossIconStyle] }, /* @__PURE__ */ React26.createElement(Feather$1, { name: "x", size: ICON_SIZE, color: colors.foregroundMuted }))
1733
- ))
1734
- ));
1735
- }
1736
- var styles16 = StyleSheet.create({
1737
- track: {
1738
- width: TRACK_WIDTH,
1739
- height: TRACK_HEIGHT,
1740
- borderRadius: TRACK_HEIGHT / 2
1741
- },
1742
- thumb: {
1743
- position: "absolute",
1744
- top: THUMB_OFFSET,
1745
- left: THUMB_OFFSET,
1746
- width: THUMB_SIZE,
1747
- height: THUMB_SIZE,
1748
- borderRadius: THUMB_SIZE / 2,
1749
- shadowColor: "#000",
1750
- shadowOffset: { width: 0, height: 1 },
1751
- shadowOpacity: 0.15,
1752
- shadowRadius: 2,
1753
- elevation: 2,
1754
- alignItems: "center",
1755
- justifyContent: "center"
1756
- },
1757
- iconWrapper: {
1758
- position: "absolute"
1759
- }
1760
- });
1761
- var sizeStyles = {
1762
- sm: { paddingHorizontal: s(12), paddingVertical: vs(8), minWidth: s(40), minHeight: vs(40) },
1763
- md: { paddingHorizontal: s(16), paddingVertical: vs(12), minWidth: s(44), minHeight: vs(44) },
1764
- lg: { paddingHorizontal: s(20), paddingVertical: vs(14), minWidth: s(48), minHeight: vs(48) }
1765
- };
1766
- var iconSizeMap2 = { sm: 16, md: 18, lg: 20 };
1767
- function Toggle({
1768
- pressed = false,
1769
- onPressedChange,
1770
- variant = "default",
1771
- size = "md",
1772
- label,
1773
- icon,
1774
- activeIcon,
1775
- iconName,
1776
- activeIconName,
1777
- iconColor,
1778
- activeIconColor,
1779
- disabled,
1780
- style,
1781
- accessibilityLabel,
1782
- ...props
1783
- }) {
1784
- const { colors } = useTheme();
1785
- const { animatedStyle: scaleStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
1786
- pressScale: PRESS_SCALE.button,
1787
- disabled
1788
- });
1789
- const progress = useColorTransition(pressed);
1790
- const inactiveBorder = variant === "outline" ? colors.border : "transparent";
1791
- const surfaceStyle = useAnimatedStyle(() => ({
1792
- borderColor: interpolateColor(progress.value, [0, 1], [inactiveBorder, colors.primary]),
1793
- backgroundColor: interpolateColor(progress.value, [0, 1], ["transparent", colors.surfaceStrong])
1794
- }));
1795
- const textStyle = useAnimatedStyle(() => ({
1796
- color: interpolateColor(progress.value, [0, 1], [colors.foreground, colors.primary])
1797
- }));
1798
- const iconSize = iconSizeMap2[size];
1799
- const LeftIcon = () => {
1800
- const renderProp = (prop) => {
1801
- if (!prop) return null;
1802
- if (typeof prop === "function") return prop(pressed);
1803
- return prop;
1804
- };
1805
- if (pressed) {
1806
- if (activeIconName) return /* @__PURE__ */ React26.createElement(React26.Fragment, null, renderIcon(activeIconName, iconSize, activeIconColor ?? colors.primary));
1807
- const active = renderProp(activeIcon);
1808
- if (active) return /* @__PURE__ */ React26.createElement(React26.Fragment, null, active);
1809
- return /* @__PURE__ */ React26.createElement(FontAwesome5$1, { name: "check-circle", size: iconSize, color: colors.primary });
1810
- }
1811
- if (iconName) return /* @__PURE__ */ React26.createElement(React26.Fragment, null, renderIcon(iconName, iconSize, iconColor ?? colors.foregroundMuted));
1812
- const custom = renderProp(icon);
1813
- if (custom) return /* @__PURE__ */ React26.createElement(React26.Fragment, null, custom);
1814
- return /* @__PURE__ */ React26.createElement(FontAwesome5$1, { name: "circle", size: iconSize, color: colors.foregroundMuted });
1815
- };
1816
- return /* @__PURE__ */ React26.createElement(
1817
- Animated9.View,
1818
- {
1819
- style: [scaleStyle, disabled && styles17.disabled, style],
1820
- ...hoverHandlers
1821
- },
1822
- /* @__PURE__ */ React26.createElement(
1823
- TouchableOpacity,
1824
- {
1825
- onPress: () => {
1826
- selectionAsync();
1827
- onPressedChange?.(!pressed);
1828
- },
1829
- onPressIn,
1830
- onPressOut,
1831
- disabled,
1832
- activeOpacity: 1,
1833
- touchSoundDisabled: true,
1834
- accessibilityRole: "button",
1835
- accessibilityLabel: accessibilityLabel ?? label,
1836
- accessibilityState: { selected: pressed, disabled: !!disabled },
1837
- ...props
1838
- },
1839
- /* @__PURE__ */ React26.createElement(
1840
- Animated9.View,
1841
- {
1842
- style: [
1843
- styles17.base,
1844
- sizeStyles[size],
1845
- { borderWidth: 2 },
1846
- surfaceStyle
1847
- ]
1848
- },
1849
- /* @__PURE__ */ React26.createElement(View, { style: styles17.inner }, /* @__PURE__ */ React26.createElement(LeftIcon, null), label ? /* @__PURE__ */ React26.createElement(Animated9.Text, { style: [styles17.label, textStyle], allowFontScaling: true }, label) : null)
1850
- )
1851
- )
1852
- );
1853
- }
1854
- var styles17 = StyleSheet.create({
1855
- base: {
1856
- borderRadius: ms(8)
1857
- },
1858
- inner: {
1859
- flexDirection: "row",
1860
- alignItems: "center",
1861
- justifyContent: "center",
1862
- gap: s(8)
1863
- },
1864
- disabled: {
1865
- opacity: 0.45
1866
- },
1867
- label: {
1868
- fontFamily: "Poppins-Medium",
1869
- fontSize: ms(14)
1870
- }
1871
- });
1872
- function RadioItem({
1873
- option,
1874
- selected,
1875
- onSelect
1876
- }) {
1877
- const { colors } = useTheme();
1878
- const { animatedStyle: scaleStyle, onPressIn, onPressOut } = usePressScale({
1879
- pressScale: PRESS_SCALE.button,
1880
- disabled: option.disabled
1881
- });
1882
- const colorProgress = useColorTransition(selected);
1883
- const dotScale = useSharedValue(selected ? 1 : 0);
1884
- useEffect(() => {
1885
- dotScale.value = withSpring(selected ? 1 : 0, SPRINGS.elastic);
1886
- }, [selected, dotScale]);
1887
- const radioStyle = useAnimatedStyle(() => ({
1888
- borderColor: interpolateColor(colorProgress.value, [0, 1], [colors.border, colors.primary])
1889
- }));
1890
- const dotStyle = useAnimatedStyle(() => ({
1891
- transform: [{ scale: dotScale.value }],
1892
- opacity: dotScale.value
1893
- }));
1894
- return /* @__PURE__ */ React26.createElement(
1895
- TouchableOpacity,
1896
- {
1897
- style: styles18.row,
1898
- onPress: () => {
1899
- if (!option.disabled) {
1900
- selectionAsync();
1901
- onSelect();
1902
- }
1903
- },
1904
- onPressIn,
1905
- onPressOut,
1906
- activeOpacity: 1,
1907
- touchSoundDisabled: true,
1908
- disabled: option.disabled,
1909
- accessibilityRole: "radio",
1910
- accessibilityLabel: option.label,
1911
- accessibilityState: { checked: selected, disabled: !!option.disabled }
1912
- },
1913
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: scaleStyle }, /* @__PURE__ */ React26.createElement(
1914
- Animated9.View,
1915
- {
1916
- style: [
1917
- styles18.radio,
1918
- { opacity: option.disabled ? 0.45 : 1 },
1919
- radioStyle
1920
- ]
1921
- },
1922
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: [styles18.dot, { backgroundColor: colors.primary }, dotStyle] })
1923
- )),
1924
- /* @__PURE__ */ React26.createElement(
1925
- Text,
1926
- {
1927
- style: [
1928
- styles18.label,
1929
- { color: option.disabled ? colors.foregroundMuted : colors.foreground }
1930
- ],
1931
- allowFontScaling: true
1932
- },
1933
- option.label
1934
- )
1935
- );
1936
- }
1937
- function RadioGroup({
1938
- options,
1939
- value,
1940
- onValueChange,
1941
- orientation = "vertical",
1942
- style,
1943
- accessibilityLabel
1944
- }) {
1945
- return /* @__PURE__ */ React26.createElement(
1946
- View,
1947
- {
1948
- style: [styles18.container, orientation === "horizontal" && styles18.horizontal, style],
1949
- accessibilityRole: "radiogroup",
1950
- accessibilityLabel
1951
- },
1952
- options.map((option) => /* @__PURE__ */ React26.createElement(
1953
- RadioItem,
1954
- {
1955
- key: option.value,
1956
- option,
1957
- selected: option.value === value,
1958
- onSelect: () => onValueChange?.(option.value)
1959
- }
1960
- ))
1961
- );
1962
- }
1963
- var styles18 = StyleSheet.create({
1964
- container: {
1965
- gap: vs(12)
1966
- },
1967
- horizontal: {
1968
- flexDirection: "row",
1969
- flexWrap: "wrap"
1970
- },
1971
- row: {
1972
- flexDirection: "row",
1973
- alignItems: "center",
1974
- gap: s(12)
1975
- },
1976
- radio: {
1977
- width: s(24),
1978
- height: s(24),
1979
- borderRadius: s(12),
1980
- borderWidth: 1.5,
1981
- alignItems: "center",
1982
- justifyContent: "center"
1983
- },
1984
- dot: {
1985
- width: s(10),
1986
- height: s(10),
1987
- borderRadius: s(5)
1988
- },
1989
- label: {
1990
- fontFamily: "Poppins-Regular",
1991
- fontSize: ms(14),
1992
- lineHeight: mvs(20)
1993
- }
1994
- });
1995
- function TabTrigger({
1996
- tab,
1997
- isActive,
1998
- onPress,
1999
- onLayout,
2000
- variant
2001
- }) {
2002
- const { colors } = useTheme();
2003
- const { animatedStyle, onPressIn, onPressOut } = usePressScale({
2004
- pressScale: PRESS_SCALE.button
2005
- });
2006
- const isUnderline = variant === "underline";
2007
- return /* @__PURE__ */ React26.createElement(
2008
- TouchableOpacity,
2009
- {
2010
- style: [
2011
- styles19.trigger,
2012
- isUnderline && styles19.triggerUnderline,
2013
- isUnderline && isActive && { borderBottomColor: colors.primary }
2014
- ],
2015
- onPress,
2016
- onPressIn,
2017
- onPressOut,
2018
- onLayout,
2019
- activeOpacity: 1,
2020
- touchSoundDisabled: true,
2021
- accessibilityRole: "tab",
2022
- accessibilityState: { selected: isActive },
2023
- accessibilityLabel: tab.label
2024
- },
2025
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: animatedStyle }, /* @__PURE__ */ React26.createElement(View, { style: styles19.triggerInner }, tab.icon ? typeof tab.icon === "function" ? tab.icon(isActive) : tab.icon : null, /* @__PURE__ */ React26.createElement(
2026
- Text,
2027
- {
2028
- style: [
2029
- styles19.triggerLabel,
2030
- { color: isActive ? colors.foreground : colors.foregroundMuted },
2031
- isActive && (isUnderline ? styles19.activeTriggerLabelUnderline : styles19.activeTriggerLabel)
2032
- ],
2033
- allowFontScaling: true
2034
- },
2035
- tab.label
2036
- )))
2037
- );
2038
- }
2039
- function Tabs({ tabs, variant = "pill", value, onValueChange, children, style }) {
2040
- const [internal, setInternal] = useState(tabs[0]?.value ?? "");
2041
- const { colors } = useTheme();
2042
- const active = value ?? internal;
2043
- const tabLayouts = useRef({});
2044
- const pillX = useSharedValue(0);
2045
- const pillWidth = useSharedValue(0);
2046
- const initialised = useRef(false);
2047
- const animatePill = (tabValue, animate) => {
2048
- const layout = tabLayouts.current[tabValue];
2049
- if (!layout) return;
2050
- if (animate) {
2051
- pillX.value = withSpring(layout.x, SPRINGS.glide);
2052
- pillWidth.value = withSpring(layout.width, SPRINGS.glide);
2053
- } else {
2054
- pillX.value = layout.x;
2055
- pillWidth.value = layout.width;
2056
- }
2057
- };
2058
- useEffect(() => {
2059
- if (initialised.current) animatePill(active, true);
2060
- }, [active]);
2061
- const handlePress = (v) => {
2062
- selectionAsync();
2063
- if (!value) setInternal(v);
2064
- onValueChange?.(v);
2065
- };
2066
- const pillAnimatedStyle = useAnimatedStyle(() => ({
2067
- transform: [{ translateX: pillX.value }],
2068
- width: pillWidth.value
2069
- }));
2070
- return /* @__PURE__ */ React26.createElement(View, { style }, /* @__PURE__ */ React26.createElement(
2071
- View,
2072
- {
2073
- style: [
2074
- variant === "pill" ? [styles19.list, { backgroundColor: colors.surface }] : styles19.listUnderline
2075
- ],
2076
- accessibilityRole: "tablist"
2077
- },
2078
- variant === "pill" && /* @__PURE__ */ React26.createElement(
2079
- Animated9.View,
2080
- {
2081
- style: [
2082
- styles19.pill,
2083
- {
2084
- backgroundColor: colors.background,
2085
- position: "absolute",
2086
- top: 4,
2087
- bottom: 4,
2088
- left: 0,
2089
- borderRadius: 8,
2090
- shadowColor: "#000",
2091
- shadowOffset: { width: 0, height: 1 },
2092
- shadowOpacity: 0.08,
2093
- shadowRadius: 2,
2094
- elevation: 2
2095
- },
2096
- pillAnimatedStyle
2097
- ]
2098
- }
2099
- ),
2100
- tabs.map((tab) => /* @__PURE__ */ React26.createElement(
2101
- TabTrigger,
2102
- {
2103
- key: tab.value,
2104
- tab,
2105
- isActive: tab.value === active,
2106
- onPress: () => handlePress(tab.value),
2107
- variant,
2108
- onLayout: (e) => {
2109
- const { x, width } = e.nativeEvent.layout;
2110
- tabLayouts.current[tab.value] = { x, width };
2111
- if (tab.value === active) {
2112
- animatePill(tab.value, false);
2113
- initialised.current = true;
2114
- }
2115
- }
2116
- }
2117
- ))
2118
- ), children);
2119
- }
2120
- function TabsContent({ value, activeValue, children, style }) {
2121
- if (value !== activeValue) return null;
2122
- return /* @__PURE__ */ React26.createElement(View, { style, accessibilityRole: "none" }, children);
2123
- }
2124
- var styles19 = StyleSheet.create({
2125
- list: {
2126
- flexDirection: "row",
2127
- borderRadius: 12,
2128
- padding: s(4),
2129
- gap: s(4)
2130
- },
2131
- listUnderline: {
2132
- flexDirection: "row",
2133
- borderBottomWidth: 1
2134
- },
2135
- pill: {},
2136
- trigger: {
2137
- flex: 1,
2138
- paddingVertical: vs(7),
2139
- paddingHorizontal: s(10),
2140
- borderRadius: 8,
2141
- alignItems: "center",
2142
- justifyContent: "center",
2143
- zIndex: 1
2144
- },
2145
- triggerUnderline: {
2146
- flex: 0,
2147
- paddingVertical: vs(12),
2148
- paddingHorizontal: s(16),
2149
- borderRadius: 0,
2150
- borderBottomWidth: 2,
2151
- borderBottomColor: "transparent"
2152
- },
2153
- triggerInner: {
2154
- flexDirection: "row",
2155
- alignItems: "center",
2156
- justifyContent: "center",
2157
- gap: s(4)
2158
- },
2159
- triggerLabel: {
2160
- fontFamily: "Poppins-Regular",
2161
- fontSize: ms(13)
2162
- },
2163
- activeTriggerLabel: {
2164
- fontFamily: "Poppins-Medium"
2165
- },
2166
- activeTriggerLabelUnderline: {
2167
- fontFamily: "Poppins-SemiBold",
2168
- fontSize: ms(14)
2169
- }
2170
- });
2171
- function AccordionItemComponent({
2172
- item,
2173
- isOpen,
2174
- onToggle
2175
- }) {
2176
- const { colors } = useTheme();
2177
- const resolvedIcon = item.iconName ? renderIcon(item.iconName, ms(16), item.iconColor ?? colors.foregroundMuted) : item.icon;
2178
- const isExpanded = useSharedValue(isOpen);
2179
- const height = useSharedValue(0);
2180
- React26.useEffect(() => {
2181
- isExpanded.value = isOpen;
2182
- }, [isOpen]);
2183
- const derivedHeight = useDerivedValue(
2184
- () => withTiming(height.value * Number(isExpanded.value), {
2185
- duration: isExpanded.value ? TIMINGS.expand.duration : TIMINGS.collapse.duration,
2186
- easing: isExpanded.value ? EASINGS.expand : EASINGS.collapse
2187
- })
2188
- );
2189
- const derivedRotation = useDerivedValue(
2190
- () => withTiming(isExpanded.value ? 1 : 0, {
2191
- duration: isExpanded.value ? TIMINGS.expand.duration : TIMINGS.collapse.duration,
2192
- easing: isExpanded.value ? EASINGS.expand : EASINGS.collapse
2193
- })
2194
- );
2195
- const bodyStyle = useAnimatedStyle(() => ({
2196
- height: derivedHeight.value,
2197
- overflow: "hidden"
2198
- }));
2199
- const rotationStyle = useAnimatedStyle(() => ({
2200
- transform: [{ rotate: `${derivedRotation.value * 180}deg` }]
2201
- }));
2202
- return /* @__PURE__ */ React26.createElement(View, { style: [styles20.item, { backgroundColor: colors.card, borderColor: colors.border }] }, /* @__PURE__ */ React26.createElement(
2203
- Pressable,
2204
- {
2205
- style: ({ pressed }) => [styles20.trigger, { opacity: pressed ? 0.6 : 1 }],
2206
- onPress: () => {
2207
- selectionAsync();
2208
- onToggle();
2209
- },
2210
- accessibilityRole: "button",
2211
- accessibilityState: { expanded: isOpen },
2212
- accessibilityLabel: item.trigger
2213
- },
2214
- /* @__PURE__ */ React26.createElement(View, { style: styles20.triggerContent }, resolvedIcon ? /* @__PURE__ */ React26.createElement(View, { style: styles20.icon }, resolvedIcon) : null, /* @__PURE__ */ React26.createElement(Text, { style: [styles20.triggerText, { color: colors.foreground }], allowFontScaling: true }, item.trigger)),
2215
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: [styles20.chevron, rotationStyle] }, /* @__PURE__ */ React26.createElement(Entypo$1, { name: "chevron-down", size: 18, color: colors.foregroundMuted }))
2216
- ), /* @__PURE__ */ React26.createElement(Animated9.View, { style: bodyStyle }, /* @__PURE__ */ React26.createElement(
2217
- View,
2218
- {
2219
- style: styles20.content,
2220
- onLayout: (e) => {
2221
- height.value = e.nativeEvent.layout.height;
2222
- }
2223
- },
2224
- item.content
2225
- )));
2226
- }
2227
- function Accordion({ items, type = "single", defaultValue, style }) {
2228
- const [openValues, setOpenValues] = useState(() => {
2229
- if (!defaultValue) return [];
2230
- return Array.isArray(defaultValue) ? defaultValue : [defaultValue];
2231
- });
2232
- const toggle = (value) => {
2233
- if (type === "single") {
2234
- setOpenValues((prev) => prev.includes(value) ? [] : [value]);
2235
- } else {
2236
- setOpenValues(
2237
- (prev) => prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
2238
- );
2239
- }
2240
- };
2241
- return /* @__PURE__ */ React26.createElement(View, { style: [styles20.list, style] }, items.map((item) => /* @__PURE__ */ React26.createElement(
2242
- AccordionItemComponent,
2243
- {
2244
- key: item.value,
2245
- item,
2246
- isOpen: openValues.includes(item.value),
2247
- onToggle: () => toggle(item.value)
2248
- }
2249
- )));
2250
- }
2251
- var styles20 = StyleSheet.create({
2252
- list: {
2253
- gap: s(6)
2254
- },
2255
- item: {
2256
- borderWidth: 1,
2257
- borderRadius: ms(10),
2258
- overflow: "hidden"
2259
- },
2260
- trigger: {
2261
- flexDirection: "row",
2262
- justifyContent: "space-between",
2263
- alignItems: "center",
2264
- paddingHorizontal: s(14),
2265
- paddingVertical: vs(12)
2266
- },
2267
- triggerContent: {
2268
- flexDirection: "row",
2269
- alignItems: "center",
2270
- gap: s(8),
2271
- flex: 1
2272
- },
2273
- icon: {
2274
- alignItems: "center",
2275
- justifyContent: "center"
2276
- },
2277
- triggerText: {
2278
- fontFamily: "Poppins-Medium",
2279
- fontSize: ms(14)
2280
- },
2281
- chevron: {
2282
- marginLeft: s(8)
2283
- },
2284
- // position:'absolute' is the key — the inner View escapes the animated wrapper's
2285
- // clipped height so onLayout always reports the true content height.
2286
- content: {
2287
- paddingHorizontal: s(14),
2288
- paddingBottom: vs(12),
2289
- position: "absolute",
2290
- width: "100%"
2291
- }
2292
- });
2293
- function Slider({
2294
- value = 0,
2295
- minimumValue = 0,
2296
- maximumValue = 1,
2297
- step = 0,
2298
- onValueChange,
2299
- onSlidingComplete,
2300
- label,
2301
- showValue = false,
2302
- formatValue: formatValue2 = (v) => v.toFixed(2),
2303
- accessibilityLabel,
2304
- disabled,
2305
- style
2306
- }) {
2307
- const { colors } = useTheme();
2308
- const lastSteppedValue = useRef(value);
2309
- const handleValueChange = (v) => {
2310
- if (step && v !== lastSteppedValue.current) {
2311
- lastSteppedValue.current = v;
2312
- selectionAsync();
2313
- }
2314
- onValueChange?.(v);
2315
- };
2316
- return /* @__PURE__ */ React26.createElement(
2317
- View,
2318
- {
2319
- style: [styles21.wrapper, style],
2320
- accessibilityRole: "adjustable",
2321
- accessibilityLabel: accessibilityLabel ?? label,
2322
- accessibilityValue: {
2323
- min: minimumValue,
2324
- max: maximumValue,
2325
- now: value,
2326
- text: formatValue2(value)
2327
- }
2328
- },
2329
- label || showValue ? /* @__PURE__ */ React26.createElement(View, { style: styles21.header }, label ? /* @__PURE__ */ React26.createElement(Text, { style: [styles21.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, showValue ? /* @__PURE__ */ React26.createElement(Text, { style: [styles21.valueText, { color: colors.foregroundMuted }], allowFontScaling: true }, formatValue2(value)) : null) : null,
2330
- /* @__PURE__ */ React26.createElement(View, { style: disabled ? styles21.disabled : void 0 }, /* @__PURE__ */ React26.createElement(
2331
- RNSlider,
2332
- {
2333
- value,
2334
- minimumValue,
2335
- maximumValue,
2336
- step: step || 0,
2337
- disabled,
2338
- onValueChange: handleValueChange,
2339
- onSlidingComplete,
2340
- minimumTrackTintColor: colors.primary,
2341
- maximumTrackTintColor: colors.surface,
2342
- thumbTintColor: colors.primary,
2343
- style: styles21.slider,
2344
- accessibilityLabel
2345
- }
2346
- ))
2347
- );
2348
- }
2349
- var styles21 = StyleSheet.create({
2350
- wrapper: {
2351
- gap: vs(8)
2352
- },
2353
- header: {
2354
- flexDirection: "row",
2355
- justifyContent: "space-between",
2356
- alignItems: "center"
2357
- },
2358
- label: {
2359
- fontFamily: "Poppins-Medium",
2360
- fontSize: ms(15)
2361
- },
2362
- valueText: {
2363
- fontFamily: "Poppins-Medium",
2364
- fontSize: ms(14)
2365
- },
2366
- slider: {
2367
- width: "100%",
2368
- height: vs(60)
2369
- },
2370
- disabled: {
2371
- opacity: 0.45
2372
- }
2373
- });
2374
- var SCREEN_HEIGHT = Dimensions.get("window").height;
2375
- var DEFAULT_MAX_HEIGHT = SCREEN_HEIGHT * 0.85;
2376
- var isAndroid = Platform.OS === "android";
2377
- function Sheet({
2378
- open,
2379
- onClose,
2380
- title,
2381
- subtitle,
2382
- description,
2383
- showCloseButton = false,
2384
- children,
2385
- style,
2386
- contentStyle,
2387
- scrollable,
2388
- maxHeight,
2389
- keyboardBehavior,
2390
- keyboardBlurBehavior = "restore",
2391
- enableBlurKeyboardOnGesture = true,
2392
- android_keyboardInputMode = "adjustPan",
2393
- footer,
2394
- snapPoints
2395
- }) {
2396
- const { colors } = useTheme();
2397
- const insets = useSafeAreaInsets();
2398
- const ref = useRef(null);
2399
- const effectiveKeyboardBehavior = keyboardBehavior ?? "interactive";
2400
- useEffect(() => {
2401
- if (open) {
2402
- impactMedium();
2403
- ref.current?.present();
2404
- } else {
2405
- ref.current?.dismiss();
2406
- }
2407
- }, [open]);
2408
- const renderBackdrop = useCallback((props) => /* @__PURE__ */ React26.createElement(
2409
- BottomSheetBackdrop,
2410
- {
2411
- ...props,
2412
- disappearsOnIndex: -1,
2413
- appearsOnIndex: 0,
2414
- pressBehavior: "close"
2415
- }
2416
- ), []);
2417
- const renderFooter = useCallback((props) => {
2418
- if (!footer) return null;
2419
- return /* @__PURE__ */ React26.createElement(BottomSheetFooter, { ...props }, footer);
2420
- }, [footer]);
2421
- const effectiveSubtitle = subtitle ?? description;
2422
- const showHeader = !!(title || effectiveSubtitle || showCloseButton);
2423
- const headerNode = showHeader ? /* @__PURE__ */ React26.createElement(View, { style: styles22.header, accessibilityRole: "header" }, /* @__PURE__ */ React26.createElement(View, { style: styles22.headerRow }, title ? /* @__PURE__ */ React26.createElement(Text, { style: [styles22.title, { color: colors.foreground }], allowFontScaling: true }, title) : /* @__PURE__ */ React26.createElement(View, { style: { flex: 1 } }), showCloseButton ? /* @__PURE__ */ React26.createElement(
2424
- TouchableOpacity,
2425
- {
2426
- onPress: onClose,
2427
- style: styles22.closeButton,
2428
- activeOpacity: 0.6,
2429
- touchSoundDisabled: true,
2430
- accessibilityRole: "button",
2431
- accessibilityLabel: "Close",
2432
- hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }
2433
- },
2434
- /* @__PURE__ */ React26.createElement(AntDesign$1, { name: "close", size: ms(18), color: colors.foregroundMuted })
2435
- ) : null), effectiveSubtitle ? /* @__PURE__ */ React26.createElement(Text, { style: [styles22.subtitle, { color: colors.foregroundMuted }], allowFontScaling: true }, effectiveSubtitle) : null) : null;
2436
- const useScroll = scrollable || !!maxHeight;
2437
- const effectiveMaxHeight = maxHeight ?? DEFAULT_MAX_HEIGHT;
2438
- const useDynamicSizing = !snapPoints;
2439
- return /* @__PURE__ */ React26.createElement(
2440
- BottomSheetModal,
2441
- {
2442
- ref,
2443
- enableDynamicSizing: useDynamicSizing,
2444
- snapPoints,
2445
- maxDynamicContentSize: useDynamicSizing ? effectiveMaxHeight : void 0,
2446
- onDismiss: onClose,
2447
- backdropComponent: renderBackdrop,
2448
- footerComponent: footer ? renderFooter : void 0,
2449
- backgroundStyle: [styles22.background, { backgroundColor: colors.card }],
2450
- handleIndicatorStyle: [styles22.handle, { backgroundColor: colors.border }],
2451
- enablePanDownToClose: true,
2452
- topInset: insets.top,
2453
- keyboardBehavior: effectiveKeyboardBehavior,
2454
- keyboardBlurBehavior,
2455
- android_keyboardInputMode,
2456
- enableBlurKeyboardOnGesture
2457
- },
2458
- useScroll ? /* @__PURE__ */ React26.createElement(
2459
- BottomSheetScrollView,
2460
- {
2461
- contentContainerStyle: [
2462
- styles22.scrollContent,
2463
- style
2464
- ],
2465
- style: contentStyle,
2466
- showsVerticalScrollIndicator: true,
2467
- indicatorStyle: "black",
2468
- persistentScrollbar: isAndroid
2469
- },
2470
- headerNode,
2471
- children
2472
- ) : /* @__PURE__ */ React26.createElement(BottomSheetView, { style: [styles22.content, contentStyle, style] }, headerNode, children)
2473
- );
2474
- }
2475
- var styles22 = StyleSheet.create({
2476
- background: {
2477
- borderTopLeftRadius: ms(16),
2478
- borderTopRightRadius: ms(16)
2479
- },
2480
- handle: {
2481
- width: s(36),
2482
- height: vs(4),
2483
- borderRadius: ms(2)
2484
- },
2485
- header: {
2486
- paddingHorizontal: s(16),
2487
- paddingTop: vs(4),
2488
- paddingBottom: vs(12),
2489
- gap: vs(4)
2490
- },
2491
- headerRow: {
2492
- flexDirection: "row",
2493
- alignItems: "center",
2494
- justifyContent: "space-between"
2495
- },
2496
- title: {
2497
- fontFamily: "Poppins-SemiBold",
2498
- fontSize: ms(18),
2499
- flex: 1
2500
- },
2501
- subtitle: {
2502
- fontFamily: "Poppins-Regular",
2503
- fontSize: ms(14),
2504
- lineHeight: mvs(20)
2505
- },
2506
- closeButton: {
2507
- padding: s(4),
2508
- marginLeft: s(8)
2509
- },
2510
- content: {
2511
- paddingHorizontal: s(16),
2512
- paddingBottom: vs(32)
2513
- },
2514
- scrollContent: {
2515
- paddingHorizontal: s(16),
2516
- paddingBottom: vs(32),
2517
- paddingRight: s(16)
2518
- }
2519
- });
2520
- var isIOS = Platform.OS === "ios";
2521
- var isAndroid2 = Platform.OS === "android";
2522
- var isWeb2 = Platform.OS === "web";
2523
- function Select({
2524
- options,
2525
- value,
2526
- onValueChange,
2527
- placeholder = "Select an option",
2528
- label,
2529
- error,
2530
- disabled,
2531
- style,
2532
- accessibilityLabel
2533
- }) {
2534
- const { colors } = useTheme();
2535
- const { animatedStyle, onPressIn, onPressOut } = usePressScale({
2536
- pressScale: PRESS_SCALE.button,
2537
- disabled
2538
- });
2539
- const [pickerVisible, setPickerVisible] = useState(false);
2540
- const [pendingValue, setPendingValue] = useState(value);
2541
- const pickerRef = useRef(null);
2542
- const selected = options.find((o) => o.value === value);
2543
- const handleOpen = () => {
2544
- if (disabled) return;
2545
- selectionAsync();
2546
- if (isIOS) {
2547
- setPendingValue(value);
2548
- setPickerVisible(true);
2549
- } else if (isAndroid2) {
2550
- pickerRef.current?.focus();
2551
- }
2552
- };
2553
- const handleDismiss = () => {
2554
- setPickerVisible(false);
2555
- };
2556
- const handleConfirm = () => {
2557
- if (pendingValue !== void 0 && pendingValue !== "") {
2558
- selectionAsync();
2559
- onValueChange?.(pendingValue);
2560
- }
2561
- setPickerVisible(false);
2562
- };
2563
- return /* @__PURE__ */ React26.createElement(View, { style: [styles23.container, style] }, label ? /* @__PURE__ */ React26.createElement(Text, { style: [styles23.label, { color: colors.foreground }], allowFontScaling: true }, label) : null, !isWeb2 ? /* @__PURE__ */ React26.createElement(Animated9.View, { style: [animatedStyle, { opacity: disabled ? 0.45 : 1 }] }, /* @__PURE__ */ React26.createElement(
2564
- TouchableOpacity,
2565
- {
2566
- style: [
2567
- styles23.trigger,
2568
- {
2569
- borderColor: error ? colors.destructive : colors.border,
2570
- backgroundColor: colors.background
2571
- }
2572
- ],
2573
- onPress: handleOpen,
2574
- onPressIn,
2575
- onPressOut,
2576
- activeOpacity: 1,
2577
- touchSoundDisabled: true,
2578
- accessibilityRole: "combobox",
2579
- accessibilityLabel: accessibilityLabel ?? label,
2580
- accessibilityValue: { text: selected?.label ?? placeholder },
2581
- accessibilityState: { disabled: !!disabled, expanded: pickerVisible }
2582
- },
2583
- /* @__PURE__ */ React26.createElement(
2584
- Text,
2585
- {
2586
- style: [
2587
- styles23.triggerText,
2588
- { color: selected ? colors.foreground : colors.foregroundMuted }
2589
- ],
2590
- numberOfLines: 1,
2591
- allowFontScaling: true
2592
- },
2593
- selected?.label ?? placeholder
2594
- ),
2595
- /* @__PURE__ */ React26.createElement(Entypo$1, { name: "chevron-down", size: 20, color: colors.foregroundMuted })
2596
- )) : null, isIOS ? /* @__PURE__ */ React26.createElement(
2597
- Modal,
2598
- {
2599
- visible: pickerVisible,
2600
- transparent: true,
2601
- animationType: "slide",
2602
- onRequestClose: handleDismiss
2603
- },
2604
- /* @__PURE__ */ React26.createElement(TouchableOpacity, { style: styles23.iosBackdrop, activeOpacity: 1, onPress: handleDismiss }),
2605
- /* @__PURE__ */ React26.createElement(View, { style: [styles23.iosSheet, { backgroundColor: colors.card }] }, /* @__PURE__ */ React26.createElement(View, { style: [styles23.iosToolbar, { borderBottomColor: colors.border }] }, label ? /* @__PURE__ */ React26.createElement(Text, { style: [styles23.iosToolbarTitle, { color: colors.foreground }], allowFontScaling: true }, label) : /* @__PURE__ */ React26.createElement(View, null), /* @__PURE__ */ React26.createElement(TouchableOpacity, { onPress: handleConfirm, style: styles23.iosDoneBtn, hitSlop: { top: 8, bottom: 8, left: 8, right: 8 } }, /* @__PURE__ */ React26.createElement(Text, { style: [styles23.iosDoneBtnText, { color: colors.primary }], allowFontScaling: true }, "Done"))), /* @__PURE__ */ React26.createElement(
2606
- Picker,
2607
- {
2608
- selectedValue: pendingValue ?? "",
2609
- onValueChange: (val) => setPendingValue(val),
2610
- itemStyle: { color: colors.foreground }
2611
- },
2612
- !value ? /* @__PURE__ */ React26.createElement(Picker.Item, { label: placeholder, value: "", color: colors.foregroundMuted, enabled: false }) : null,
2613
- options.map((o) => /* @__PURE__ */ React26.createElement(
2614
- Picker.Item,
2615
- {
2616
- key: o.value,
2617
- label: o.label,
2618
- value: o.value,
2619
- enabled: !o.disabled,
2620
- color: o.disabled ? colors.foregroundMuted : colors.foreground
2621
- }
2622
- ))
2623
- ))
2624
- ) : null, isAndroid2 ? /* @__PURE__ */ React26.createElement(
2625
- Picker,
2626
- {
2627
- ref: pickerRef,
2628
- selectedValue: value ?? "",
2629
- onValueChange: (val) => {
2630
- if (val !== "") {
2631
- selectionAsync();
2632
- onValueChange?.(val);
2633
- }
2634
- },
2635
- mode: "dialog",
2636
- enabled: !disabled,
2637
- prompt: label,
2638
- style: styles23.androidHiddenPicker
2639
- },
2640
- !value ? /* @__PURE__ */ React26.createElement(Picker.Item, { label: placeholder, value: "", enabled: false }) : null,
2641
- options.map((o) => /* @__PURE__ */ React26.createElement(
2642
- Picker.Item,
2643
- {
2644
- key: o.value,
2645
- label: o.label,
2646
- value: o.value,
2647
- enabled: !o.disabled
2648
- }
2649
- ))
2650
- ) : null, isWeb2 ? /* @__PURE__ */ React26.createElement(
2651
- Picker,
2652
- {
2653
- selectedValue: value ?? "",
2654
- onValueChange: (val) => {
2655
- if (val !== "") {
2656
- onValueChange?.(val);
2657
- }
2658
- },
2659
- enabled: !disabled,
2660
- style: [
2661
- styles23.webPicker,
2662
- {
2663
- borderColor: error ? colors.destructive : colors.border,
2664
- color: selected ? colors.foreground : colors.foregroundMuted,
2665
- backgroundColor: colors.background,
2666
- opacity: disabled ? 0.45 : 1
2667
- }
2668
- ]
2669
- },
2670
- /* @__PURE__ */ React26.createElement(Picker.Item, { label: placeholder, value: "", enabled: false }),
2671
- options.map((o) => /* @__PURE__ */ React26.createElement(
2672
- Picker.Item,
2673
- {
2674
- key: o.value,
2675
- label: o.label,
2676
- value: o.value,
2677
- enabled: !o.disabled
2678
- }
2679
- ))
2680
- ) : null, error ? /* @__PURE__ */ React26.createElement(Text, { style: [styles23.helperText, { color: colors.destructive }], allowFontScaling: true }, error) : null);
2681
- }
2682
- var styles23 = StyleSheet.create({
2683
- container: {
2684
- gap: vs(8)
2685
- },
2686
- label: {
2687
- fontFamily: "Poppins-Medium",
2688
- fontSize: ms(13)
2689
- },
2690
- trigger: {
2691
- flexDirection: "row",
2692
- alignItems: "center",
2693
- justifyContent: "space-between",
2694
- borderWidth: 1,
2695
- borderRadius: ms(8),
2696
- paddingHorizontal: s(14),
2697
- paddingVertical: vs(11)
2698
- },
2699
- triggerText: {
2700
- fontFamily: "Poppins-Regular",
2701
- fontSize: ms(15),
2702
- flex: 1
2703
- },
2704
- chevron: {
2705
- marginLeft: s(8)
2706
- },
2707
- helperText: {
2708
- fontFamily: "Poppins-Regular",
2709
- fontSize: ms(13)
2710
- },
2711
- iosBackdrop: {
2712
- flex: 1,
2713
- backgroundColor: "rgba(0,0,0,0.4)"
2714
- },
2715
- iosSheet: {
2716
- borderTopLeftRadius: ms(16),
2717
- borderTopRightRadius: ms(16),
2718
- paddingBottom: vs(32)
2719
- },
2720
- iosToolbar: {
2721
- flexDirection: "row",
2722
- alignItems: "center",
2723
- justifyContent: "space-between",
2724
- paddingHorizontal: s(16),
2725
- paddingVertical: vs(12),
2726
- borderBottomWidth: 1
2727
- },
2728
- iosToolbarTitle: {
2729
- fontFamily: "Poppins-SemiBold",
2730
- fontSize: ms(17)
2731
- },
2732
- iosDoneBtn: {
2733
- padding: s(4)
2734
- },
2735
- iosDoneBtnText: {
2736
- fontFamily: "Poppins-SemiBold",
2737
- fontSize: ms(17)
2738
- },
2739
- androidHiddenPicker: {
2740
- height: 0,
2741
- opacity: 0,
2742
- position: "absolute"
2743
- },
2744
- webPicker: {
2745
- borderWidth: 1,
2746
- borderRadius: ms(8),
2747
- paddingHorizontal: s(14),
2748
- paddingVertical: vs(11),
2749
- fontSize: ms(15)
2750
- }
2751
- });
2752
- function useToast() {
2753
- return {
2754
- toast: toast,
2755
- dismiss: toast.dismiss
2756
- };
2757
- }
2758
- function ToastProvider({ children }) {
2759
- const { colorScheme } = useTheme();
2760
- const insets = useSafeAreaInsets();
2761
- return /* @__PURE__ */ React26.createElement(React26.Fragment, null, children, /* @__PURE__ */ React26.createElement(
2762
- Toaster,
2763
- {
2764
- theme: colorScheme,
2765
- position: "top-center",
2766
- richColors: false,
2767
- gap: vs(8),
2768
- offset: insets.top + vs(8),
2769
- visibleToasts: 3,
2770
- closeButton: false,
2771
- swipeToDismissDirection: "up",
2772
- duration: 4e3,
2773
- toastOptions: {
2774
- style: {
2775
- borderRadius: ms(12),
2776
- paddingHorizontal: s(12),
2777
- paddingVertical: vs(10)
2778
- },
2779
- titleStyle: {
2780
- fontFamily: "Poppins-Medium",
2781
- fontSize: ms(13)
2782
- },
2783
- descriptionStyle: {
2784
- fontFamily: "Poppins-Regular",
2785
- fontSize: ms(12),
2786
- opacity: 0.85
2787
- }
2788
- }
2789
- }
2790
- ));
2791
- }
2792
- function formatCurrency(raw, separator) {
2793
- const digits = raw.replace(/\D/g, "");
2794
- if (!digits) return "";
2795
- return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
2796
- }
2797
- function CurrencyInput({
2798
- value,
2799
- onChangeText,
2800
- onChangeValue,
2801
- prefix = "$",
2802
- thousandsSeparator = ".",
2803
- size = "default",
2804
- label,
2805
- error,
2806
- hint,
2807
- placeholder,
2808
- editable,
2809
- containerStyle,
2810
- style
2811
- }) {
2812
- const { colors } = useTheme();
2813
- const handleChange = (text) => {
2814
- const withoutPrefix = prefix && text.startsWith(prefix) ? text.slice(prefix.length) : text;
2815
- const formatted = formatCurrency(withoutPrefix, thousandsSeparator);
2816
- const display = formatted;
2817
- onChangeText?.(display);
2818
- const separatorRegex = new RegExp(`\\${thousandsSeparator}`, "g");
2819
- const raw = parseFloat(formatted.replace(separatorRegex, "") || "0");
2820
- onChangeValue?.(isNaN(raw) ? 0 : raw);
2821
- };
2822
- const inputStyle = size === "large" ? { fontFamily: "Poppins-Regular", fontSize: ms(36) } : { fontFamily: "Poppins-Regular" };
2823
- const dollarIcon = renderIcon("dollar-sign", size === "large" ? 24 : 16, colors.foregroundMuted);
2824
- const displayValue = value && prefix && value.startsWith(prefix) ? value.slice(prefix.length) : value;
2825
- return /* @__PURE__ */ React26.createElement(
2826
- Input,
2827
- {
2828
- value: displayValue,
2829
- onChangeText: handleChange,
2830
- keyboardType: "numeric",
2831
- label,
2832
- error,
2833
- hint,
2834
- placeholder: placeholder ?? "0",
2835
- editable,
2836
- prefix: dollarIcon,
2837
- containerStyle,
2838
- inputWrapperStyle: size === "large" ? { paddingVertical: vs(16), minHeight: 72 } : void 0,
2839
- style: [inputStyle, style]
2840
- }
2841
- );
2842
- }
2843
- var variantFontSize = {
2844
- hero: ms(48),
2845
- large: ms(32),
2846
- medium: ms(18),
2847
- small: ms(14)
2848
- };
2849
- var variantLetterSpacing = {
2850
- hero: -2,
2851
- large: -1,
2852
- medium: -0.5,
2853
- small: 0
2854
- };
2855
- function formatValue(value, prefix, showDecimals) {
2856
- const num = typeof value === "string" ? parseFloat(value.replace(/[^0-9.-]/g, "")) : value;
2857
- if (isNaN(num)) return `${prefix}0`;
2858
- const abs = Math.abs(num);
2859
- const sign = num < 0 ? "-" : "";
2860
- const intPart = Math.floor(abs).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
2861
- if (showDecimals) {
2862
- const decStr = (abs % 1).toFixed(2).slice(2);
2863
- return `${sign}${prefix}${intPart},${decStr}`;
2864
- }
2865
- return `${sign}${prefix}${intPart}`;
2866
- }
2867
- function CurrencyDisplay({ value, prefix = "$", showDecimals = false, textColor, variant, autoScale, maxFontSize, style }) {
2868
- const { colors } = useTheme();
2869
- const formatted = formatValue(value, prefix, showDecimals);
2870
- const baseFontSize = variant ? variantFontSize[variant] : ms(56);
2871
- const fontSize = maxFontSize ?? baseFontSize;
2872
- const letterSpacing = variant ? variantLetterSpacing[variant] : -2;
2873
- return /* @__PURE__ */ React26.createElement(View, { style: [styles24.container, style] }, /* @__PURE__ */ React26.createElement(
2874
- Text,
2875
- {
2876
- style: [styles24.amount, { color: textColor ?? colors.foreground, fontSize, letterSpacing }],
2877
- allowFontScaling: true,
2878
- numberOfLines: autoScale ? 1 : void 0,
2879
- adjustsFontSizeToFit: autoScale,
2880
- minimumFontScale: autoScale ? 0.5 : void 0
2881
- },
2882
- formatted
2883
- ));
2884
- }
2885
- var styles24 = StyleSheet.create({
2886
- container: {
2887
- alignSelf: "flex-start"
2888
- },
2889
- amount: {
2890
- fontFamily: "Poppins-Bold",
2891
- includeFontPadding: false,
2892
- textAlignVertical: "top"
2893
- }
2894
- });
2895
- function ListItem({
2896
- leftRender,
2897
- rightRender,
2898
- trailing,
2899
- icon,
2900
- leftIcon,
2901
- rightIcon,
2902
- leftIconColor,
2903
- rightIconColor,
2904
- title,
2905
- subtitle,
2906
- caption,
2907
- variant = "plain",
2908
- showChevron = false,
2909
- showSeparator = false,
2910
- onPress,
2911
- disabled,
2912
- style,
2913
- titleStyle,
2914
- subtitleStyle,
2915
- captionStyle,
2916
- accessibilityLabel
2917
- }) {
2918
- const { colors } = useTheme();
2919
- const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
2920
- pressScale: PRESS_SCALE.row,
2921
- pressInSpring: SPRINGS.surfacePressIn,
2922
- pressOutSpring: SPRINGS.surfacePressOut,
2923
- disabled: !onPress || disabled
2924
- });
2925
- const handlePress = () => {
2926
- selectionAsync();
2927
- onPress?.();
2928
- };
2929
- const effectiveLeft = leftIcon ? renderIcon(leftIcon, 24, leftIconColor ?? colors.foreground) : leftRender ?? icon;
2930
- const effectiveRight = rightIcon ? renderIcon(rightIcon, 24, rightIconColor ?? colors.foregroundMuted) : rightRender ?? trailing;
2931
- const cardStyle = variant === "card" ? {
2932
- backgroundColor: colors.card,
2933
- borderRadius: RADIUS.md,
2934
- borderWidth: 1,
2935
- borderColor: colors.border,
2936
- shadowColor: "#000",
2937
- shadowOffset: { width: 0, height: 2 },
2938
- shadowOpacity: 0.06,
2939
- shadowRadius: 6,
2940
- elevation: 2
2941
- } : {};
2942
- const a11yLabel = accessibilityLabel ?? [title, subtitle, caption].filter(Boolean).join(". ");
2943
- return /* @__PURE__ */ React26.createElement(Animated9.View, { style: [animatedStyle, disabled && styles25.disabled], ...hoverHandlers }, /* @__PURE__ */ React26.createElement(
2944
- TouchableOpacity,
2945
- {
2946
- style: [styles25.container, cardStyle, style],
2947
- onPress: onPress ? handlePress : void 0,
2948
- onPressIn,
2949
- onPressOut,
2950
- disabled,
2951
- activeOpacity: 1,
2952
- touchSoundDisabled: true,
2953
- accessibilityRole: onPress ? "button" : void 0,
2954
- accessibilityLabel: onPress ? a11yLabel : void 0,
2955
- accessibilityState: onPress ? { disabled: !!disabled } : void 0
2956
- },
2957
- effectiveLeft ? /* @__PURE__ */ React26.createElement(View, { style: styles25.leftContainer }, effectiveLeft) : null,
2958
- /* @__PURE__ */ React26.createElement(View, { style: styles25.content }, /* @__PURE__ */ React26.createElement(
2959
- Text,
2960
- {
2961
- style: [styles25.title, { color: colors.foreground }, titleStyle],
2962
- numberOfLines: 2,
2963
- allowFontScaling: true
2964
- },
2965
- title
2966
- ), subtitle ? /* @__PURE__ */ React26.createElement(
2967
- Text,
2968
- {
2969
- style: [styles25.subtitle, { color: colors.foregroundMuted }, subtitleStyle],
2970
- numberOfLines: 2,
2971
- allowFontScaling: true
2972
- },
2973
- subtitle
2974
- ) : null, caption ? /* @__PURE__ */ React26.createElement(
2975
- Text,
2976
- {
2977
- style: [styles25.caption, { color: colors.foregroundMuted }, captionStyle],
2978
- numberOfLines: 1,
2979
- allowFontScaling: true
2980
- },
2981
- caption
2982
- ) : null),
2983
- effectiveRight !== void 0 ? /* @__PURE__ */ React26.createElement(View, { style: styles25.rightContainer }, typeof effectiveRight === "string" ? /* @__PURE__ */ React26.createElement(
2984
- Text,
2985
- {
2986
- style: [styles25.rightText, { color: colors.foregroundMuted }],
2987
- allowFontScaling: true
2988
- },
2989
- effectiveRight
2990
- ) : effectiveRight) : showChevron ? /* @__PURE__ */ React26.createElement(Entypo$1, { name: "chevron-with-circle-right", size: 20, color: colors.foregroundMuted }) : null
2991
- ), showSeparator ? /* @__PURE__ */ React26.createElement(
2992
- View,
2993
- {
2994
- style: [
2995
- styles25.separator,
2996
- {
2997
- backgroundColor: colors.border,
2998
- marginLeft: effectiveLeft ? s(44) + s(12) : 0
2999
- }
3000
- ]
3001
- }
3002
- ) : null);
3003
- }
3004
- var styles25 = StyleSheet.create({
3005
- container: {
3006
- flexDirection: "row",
3007
- alignItems: "center",
3008
- paddingHorizontal: 0,
3009
- paddingVertical: vs(10),
3010
- gap: s(12)
3011
- },
3012
- leftContainer: {
3013
- width: s(44),
3014
- height: s(44),
3015
- alignItems: "center",
3016
- justifyContent: "center",
3017
- flexShrink: 0
3018
- },
3019
- content: {
3020
- flex: 1,
3021
- gap: vs(4)
3022
- },
3023
- title: {
3024
- fontFamily: "Poppins-Medium",
3025
- fontSize: ms(15),
3026
- lineHeight: mvs(22)
3027
- },
3028
- subtitle: {
3029
- fontFamily: "Poppins-Regular",
3030
- fontSize: ms(13),
3031
- lineHeight: mvs(18)
3032
- },
3033
- caption: {
3034
- fontFamily: "Poppins-Regular",
3035
- fontSize: ms(12),
3036
- lineHeight: mvs(16),
3037
- opacity: 0.7
3038
- },
3039
- rightContainer: {
3040
- alignItems: "flex-end",
3041
- justifyContent: "center",
3042
- flexShrink: 0,
3043
- maxWidth: s(160)
3044
- },
3045
- rightText: {
3046
- fontFamily: "Poppins-Regular",
3047
- fontSize: ms(14)
3048
- },
3049
- separator: {
3050
- height: StyleSheet.hairlineWidth,
3051
- marginRight: 0
3052
- },
3053
- disabled: {
3054
- opacity: 0.45
3055
- }
3056
- });
3057
- function MenuItem({
3058
- label,
3059
- subtitle,
3060
- iconName,
3061
- icon,
3062
- iconColor,
3063
- rightRender,
3064
- showChevron = true,
3065
- onPress,
3066
- disabled = false,
3067
- variant = "plain",
3068
- showSeparator = false,
3069
- style,
3070
- labelStyle,
3071
- accessibilityLabel
3072
- }) {
3073
- const { colors } = useTheme();
3074
- const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
3075
- pressScale: PRESS_SCALE.row,
3076
- pressInSpring: SPRINGS.surfacePressIn,
3077
- pressOutSpring: SPRINGS.surfacePressOut,
3078
- disabled
3079
- });
3080
- const handlePress = () => {
3081
- selectionAsync();
3082
- onPress();
3083
- };
3084
- const resolvedIcon = iconName ? renderIcon(iconName, 22, iconColor ?? colors.foreground) : icon;
3085
- const cardStyle = variant === "card" ? {
3086
- backgroundColor: colors.card,
3087
- borderRadius: RADIUS.md,
3088
- borderWidth: 1,
3089
- borderColor: colors.border,
3090
- shadowColor: "#000",
3091
- shadowOffset: { width: 0, height: 2 },
3092
- shadowOpacity: 0.06,
3093
- shadowRadius: 6,
3094
- elevation: 2
3095
- } : {};
3096
- const a11yLabel = accessibilityLabel ?? (subtitle ? `${label}. ${subtitle}` : label);
3097
- return /* @__PURE__ */ React26.createElement(Animated9.View, { style: [animatedStyle, disabled && styles26.disabled], ...hoverHandlers }, /* @__PURE__ */ React26.createElement(
3098
- TouchableOpacity,
3099
- {
3100
- style: [styles26.container, cardStyle, style],
3101
- onPress: handlePress,
3102
- onPressIn,
3103
- onPressOut,
3104
- disabled,
3105
- activeOpacity: 1,
3106
- touchSoundDisabled: true,
3107
- accessibilityRole: "button",
3108
- accessibilityLabel: a11yLabel,
3109
- accessibilityState: { disabled }
3110
- },
3111
- resolvedIcon ? /* @__PURE__ */ React26.createElement(View, { style: styles26.iconContainer }, resolvedIcon) : null,
3112
- /* @__PURE__ */ React26.createElement(View, { style: styles26.labelContainer }, /* @__PURE__ */ React26.createElement(
3113
- Text,
3114
- {
3115
- style: [styles26.label, { color: colors.foreground }, labelStyle],
3116
- numberOfLines: 1,
3117
- allowFontScaling: true
3118
- },
3119
- label
3120
- ), subtitle ? /* @__PURE__ */ React26.createElement(
3121
- Text,
3122
- {
3123
- style: [styles26.subtitle, { color: colors.foregroundMuted }],
3124
- numberOfLines: 1,
3125
- allowFontScaling: true
3126
- },
3127
- subtitle
3128
- ) : null),
3129
- rightRender !== void 0 ? /* @__PURE__ */ React26.createElement(
3130
- View,
3131
- {
3132
- style: styles26.rightContainer,
3133
- onStartShouldSetResponder: () => true,
3134
- onResponderRelease: () => {
3135
- }
3136
- },
3137
- rightRender
3138
- ) : showChevron ? /* @__PURE__ */ React26.createElement(Entypo$1, { name: "chevron-right", size: 18, color: colors.foregroundMuted }) : null
3139
- ), showSeparator ? /* @__PURE__ */ React26.createElement(
3140
- View,
3141
- {
3142
- style: [
3143
- styles26.separator,
3144
- {
3145
- backgroundColor: colors.border,
3146
- marginLeft: resolvedIcon ? s(22) + s(12) : 0,
3147
- opacity: 0.6
3148
- }
3149
- ]
3150
- }
3151
- ) : null);
3152
- }
3153
- var styles26 = StyleSheet.create({
3154
- container: {
3155
- flexDirection: "row",
3156
- alignItems: "center",
3157
- paddingHorizontal: 0,
3158
- paddingVertical: vs(16),
3159
- minHeight: vs(54),
3160
- gap: s(12)
3161
- },
3162
- iconContainer: {
3163
- width: s(22),
3164
- alignItems: "center",
3165
- justifyContent: "center",
3166
- flexShrink: 0
3167
- },
3168
- labelContainer: {
3169
- flex: 1,
3170
- justifyContent: "center"
3171
- },
3172
- label: {
3173
- fontFamily: "Poppins-Medium",
3174
- fontSize: ms(15)
3175
- },
3176
- subtitle: {
3177
- fontFamily: "Poppins-Regular",
3178
- fontSize: ms(12),
3179
- marginTop: vs(1)
3180
- },
3181
- rightContainer: {
3182
- alignItems: "flex-end",
3183
- justifyContent: "center",
3184
- flexShrink: 0
3185
- },
3186
- separator: {
3187
- height: StyleSheet.hairlineWidth,
3188
- marginRight: 0
3189
- },
3190
- disabled: {
3191
- opacity: 0.45
3192
- }
3193
- });
3194
- function Chip({ label, selected = false, onPress, icon, iconName, style, accessibilityLabel }) {
3195
- const { colors } = useTheme();
3196
- const { animatedStyle: scaleStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
3197
- pressScale: PRESS_SCALE.chip
3198
- });
3199
- const colorProgress = useColorTransition(selected);
3200
- const surfaceStyle = useAnimatedStyle(() => ({
3201
- backgroundColor: interpolateColor(colorProgress.value, [0, 1], [colors.surface, colors.primary]),
3202
- borderColor: interpolateColor(colorProgress.value, [0, 1], [colors.border, colors.primary])
3203
- }));
3204
- const textStyle = useAnimatedStyle(() => ({
3205
- color: interpolateColor(colorProgress.value, [0, 1], [colors.foreground, colors.primaryForeground])
3206
- }));
3207
- const handlePress = () => {
3208
- selectionAsync();
3209
- onPress?.();
3210
- };
3211
- const resolvedIcon = iconName ? renderIcon(iconName, ms(13), selected ? colors.primaryForeground : colors.foreground) : icon;
3212
- return /* @__PURE__ */ React26.createElement(Animated9.View, { style: [styles27.wrapper, scaleStyle, style], ...hoverHandlers }, /* @__PURE__ */ React26.createElement(
3213
- TouchableOpacity,
3214
- {
3215
- onPress: handlePress,
3216
- onPressIn,
3217
- onPressOut,
3218
- activeOpacity: 1,
3219
- touchSoundDisabled: true,
3220
- accessibilityRole: "button",
3221
- accessibilityLabel: accessibilityLabel ?? label,
3222
- accessibilityState: { selected }
3223
- },
3224
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: [styles27.chip, surfaceStyle] }, resolvedIcon ? /* @__PURE__ */ React26.createElement(View, { style: styles27.chipIcon }, resolvedIcon) : null, /* @__PURE__ */ React26.createElement(Animated9.Text, { style: [styles27.label, textStyle], allowFontScaling: true }, label))
3225
- ));
3226
- }
3227
- function ChipGroup({ options, value, onValueChange, multiSelect = false, style }) {
3228
- const handlePress = (optionValue) => {
3229
- if (!multiSelect) {
3230
- onValueChange?.(optionValue);
3231
- return;
3232
- }
3233
- const currentArray = Array.isArray(value) ? value : value ? [value] : [];
3234
- const isSelected2 = currentArray.includes(optionValue);
3235
- const newArray = isSelected2 ? currentArray.filter((v) => v !== optionValue) : [...currentArray, optionValue];
3236
- onValueChange?.(newArray);
3237
- };
3238
- const isSelected = (optionValue) => {
3239
- if (Array.isArray(value)) {
3240
- return value.includes(optionValue);
3241
- }
3242
- return optionValue === value;
3243
- };
3244
- return /* @__PURE__ */ React26.createElement(View, { style: [styles27.group, style] }, options.map((opt) => /* @__PURE__ */ React26.createElement(
3245
- Chip,
3246
- {
3247
- key: opt.value,
3248
- label: opt.label,
3249
- selected: isSelected(opt.value),
3250
- onPress: opt.disabled ? void 0 : () => handlePress(opt.value),
3251
- iconName: opt.iconName,
3252
- style: opt.disabled ? { opacity: 0.4 } : void 0
3253
- }
3254
- )));
3255
- }
3256
- var styles27 = StyleSheet.create({
3257
- wrapper: {},
3258
- chip: {
3259
- borderRadius: 999,
3260
- paddingHorizontal: s(14),
3261
- paddingVertical: vs(5),
3262
- borderWidth: 1,
3263
- alignItems: "center",
3264
- justifyContent: "center",
3265
- flexDirection: "row",
3266
- gap: s(5)
3267
- },
3268
- chipIcon: {
3269
- alignItems: "center",
3270
- justifyContent: "center"
3271
- },
3272
- label: {
3273
- fontFamily: "Poppins-Medium",
3274
- fontSize: ms(13),
3275
- lineHeight: mvs(18)
3276
- },
3277
- group: {
3278
- flexDirection: "row",
3279
- flexWrap: "wrap",
3280
- gap: s(8)
3281
- }
3282
- });
3283
- function ConfirmDialog({
3284
- visible,
3285
- title,
3286
- description,
3287
- confirmLabel = "Confirm",
3288
- cancelLabel = "Cancel",
3289
- confirmVariant = "primary",
3290
- onConfirm,
3291
- onCancel
3292
- }) {
3293
- const { colors } = useTheme();
3294
- const ref = useRef(null);
3295
- useEffect(() => {
3296
- if (visible) {
3297
- impactMedium();
3298
- ref.current?.present();
3299
- } else {
3300
- ref.current?.dismiss();
3301
- }
3302
- }, [visible]);
3303
- const renderBackdrop = (props) => /* @__PURE__ */ React26.createElement(
3304
- BottomSheetBackdrop,
3305
- {
3306
- ...props,
3307
- disappearsOnIndex: -1,
3308
- appearsOnIndex: 0,
3309
- pressBehavior: "close"
3310
- }
3311
- );
3312
- return /* @__PURE__ */ React26.createElement(
3313
- BottomSheetModal,
3314
- {
3315
- ref,
3316
- enableDynamicSizing: true,
3317
- onDismiss: onCancel,
3318
- backdropComponent: renderBackdrop,
3319
- backgroundStyle: [styles28.background, { backgroundColor: colors.card }],
3320
- handleIndicatorStyle: [styles28.handle, { backgroundColor: colors.border }],
3321
- enablePanDownToClose: true
3322
- },
3323
- /* @__PURE__ */ React26.createElement(BottomSheetView, { style: styles28.content }, /* @__PURE__ */ React26.createElement(Text, { style: [styles28.title, { color: colors.foreground }], allowFontScaling: true }, title), description ? /* @__PURE__ */ React26.createElement(Text, { style: [styles28.description, { color: colors.foregroundMuted }], allowFontScaling: true }, description) : null, /* @__PURE__ */ React26.createElement(View, { style: styles28.actions }, /* @__PURE__ */ React26.createElement(
3324
- Button,
3325
- {
3326
- label: confirmLabel,
3327
- variant: confirmVariant,
3328
- fullWidth: true,
3329
- onPress: () => {
3330
- notificationSuccess();
3331
- onConfirm();
3332
- },
3333
- icon: /* @__PURE__ */ React26.createElement(
3334
- Feather$1,
3335
- {
3336
- name: confirmVariant === "destructive" ? "trash-2" : "check",
3337
- size: 15,
3338
- color: confirmVariant === "destructive" ? colors.destructiveForeground : colors.primaryForeground
3339
- }
3340
- )
3341
- }
3342
- ), /* @__PURE__ */ React26.createElement(
3343
- Button,
3344
- {
3345
- label: cancelLabel,
3346
- variant: "secondary",
3347
- fullWidth: true,
3348
- onPress: () => {
3349
- selectionAsync();
3350
- onCancel();
3351
- },
3352
- icon: /* @__PURE__ */ React26.createElement(Feather$1, { name: "x", size: 15, color: colors.foreground })
3353
- }
3354
- )))
3355
- );
3356
- }
3357
- var styles28 = StyleSheet.create({
3358
- background: {
3359
- borderTopLeftRadius: ms(16),
3360
- borderTopRightRadius: ms(16)
3361
- },
3362
- handle: {
3363
- width: s(36),
3364
- height: vs(4),
3365
- borderRadius: ms(2)
3366
- },
3367
- content: {
3368
- paddingHorizontal: s(24),
3369
- paddingBottom: vs(32),
3370
- gap: vs(12)
3371
- },
3372
- title: {
3373
- fontFamily: "Poppins-SemiBold",
3374
- fontSize: ms(18),
3375
- lineHeight: mvs(26)
3376
- },
3377
- description: {
3378
- fontFamily: "Poppins-Regular",
3379
- fontSize: ms(15),
3380
- lineHeight: mvs(22)
3381
- },
3382
- actions: {
3383
- gap: vs(10),
3384
- marginTop: vs(8)
3385
- }
3386
- });
3387
- function LabelValue({ label, value, iconName, iconColor, style }) {
3388
- const { colors } = useTheme();
3389
- const resolvedIcon = iconName ? renderIcon(iconName, ms(14), iconColor ?? colors.foregroundMuted) : null;
3390
- return /* @__PURE__ */ React26.createElement(View, { style: [styles29.container, style] }, /* @__PURE__ */ React26.createElement(View, { style: styles29.labelSide }, resolvedIcon ? /* @__PURE__ */ React26.createElement(View, { style: styles29.icon }, resolvedIcon) : null, /* @__PURE__ */ React26.createElement(Text, { style: [styles29.label, { color: colors.foregroundMuted }], allowFontScaling: true }, label)), typeof value === "string" ? /* @__PURE__ */ React26.createElement(Text, { style: [styles29.value, { color: colors.foreground }], allowFontScaling: true }, value) : value);
3391
- }
3392
- var styles29 = StyleSheet.create({
3393
- container: {
3394
- flexDirection: "row",
3395
- justifyContent: "space-between",
3396
- alignItems: "center",
3397
- gap: s(12)
3398
- },
3399
- labelSide: {
3400
- flexDirection: "row",
3401
- alignItems: "center",
3402
- gap: s(4)
3403
- },
3404
- icon: {
3405
- alignItems: "center",
3406
- justifyContent: "center"
3407
- },
3408
- label: {
3409
- fontFamily: "Poppins-Regular",
3410
- fontSize: ms(13),
3411
- lineHeight: mvs(18)
3412
- },
3413
- value: {
3414
- fontFamily: "Poppins-Medium",
3415
- fontSize: ms(14),
3416
- lineHeight: mvs(20),
3417
- textAlign: "right"
3418
- }
3419
- });
3420
- var MONTH_NAMES = {
3421
- en: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
3422
- es: ["enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"],
3423
- pt: ["janeiro", "fevereiro", "mar\xE7o", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"],
3424
- fr: ["janvier", "f\xE9vrier", "mars", "avril", "mai", "juin", "juillet", "ao\xFBt", "septembre", "octobre", "novembre", "d\xE9cembre"]
3425
- };
3426
- function MonthPicker({ value, onChange, locale = "en", formatLabel, style }) {
3427
- const { colors } = useTheme();
3428
- const getLabel = () => {
3429
- if (formatLabel) return formatLabel(value);
3430
- const names = MONTH_NAMES[locale] ?? MONTH_NAMES.en;
3431
- return `${names[value.month - 1]} ${value.year}`;
3432
- };
3433
- const handlePrev = () => {
3434
- selectionAsync();
3435
- if (value.month === 1) {
3436
- onChange({ month: 12, year: value.year - 1 });
3437
- } else {
3438
- onChange({ month: value.month - 1, year: value.year });
3439
- }
3440
- };
3441
- const handleNext = () => {
3442
- selectionAsync();
3443
- if (value.month === 12) {
3444
- onChange({ month: 1, year: value.year + 1 });
3445
- } else {
3446
- onChange({ month: value.month + 1, year: value.year });
3447
- }
3448
- };
3449
- return /* @__PURE__ */ React26.createElement(View, { style: [styles30.container, style], accessibilityRole: "adjustable", accessibilityLabel: getLabel() }, /* @__PURE__ */ React26.createElement(
3450
- TouchableOpacity,
3451
- {
3452
- style: styles30.arrow,
3453
- onPress: handlePrev,
3454
- activeOpacity: 0.6,
3455
- touchSoundDisabled: true,
3456
- accessibilityRole: "button",
3457
- accessibilityLabel: "Previous month",
3458
- hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }
3459
- },
3460
- /* @__PURE__ */ React26.createElement(Entypo$1, { name: "chevron-left", size: 22, color: colors.foreground })
3461
- ), /* @__PURE__ */ React26.createElement(
3462
- Text,
3463
- {
3464
- style: [styles30.label, { color: colors.foreground }],
3465
- allowFontScaling: true,
3466
- accessibilityLiveRegion: "polite"
3467
- },
3468
- getLabel()
3469
- ), /* @__PURE__ */ React26.createElement(
3470
- TouchableOpacity,
3471
- {
3472
- style: styles30.arrow,
3473
- onPress: handleNext,
3474
- activeOpacity: 0.6,
3475
- touchSoundDisabled: true,
3476
- accessibilityRole: "button",
3477
- accessibilityLabel: "Next month",
3478
- hitSlop: { top: 8, bottom: 8, left: 8, right: 8 }
3479
- },
3480
- /* @__PURE__ */ React26.createElement(Entypo$1, { name: "chevron-right", size: 22, color: colors.foreground })
3481
- ));
3482
- }
3483
- var styles30 = StyleSheet.create({
3484
- container: {
3485
- flexDirection: "row",
3486
- alignItems: "center",
3487
- justifyContent: "space-between"
3488
- },
3489
- arrow: {
3490
- width: s(44),
3491
- height: s(44),
3492
- alignItems: "center",
3493
- justifyContent: "center"
3494
- },
3495
- label: {
3496
- fontFamily: "Poppins-Medium",
3497
- fontSize: ms(17),
3498
- lineHeight: mvs(24),
3499
- textAlign: "center",
3500
- minWidth: s(160)
3501
- }
3502
- });
3503
- var aspectRatioMap = {
3504
- "1:1": 1,
3505
- "4:3": 3 / 4,
3506
- "16:9": 9 / 16,
3507
- "4:5": 5 / 4,
3508
- "3:2": 2 / 3
3509
- };
3510
- function MediaCard({
3511
- imageSource,
3512
- aspectRatio = "4:3",
3513
- badge,
3514
- actionIcon,
3515
- actionIconName,
3516
- actionActive = false,
3517
- onActionPress,
3518
- title,
3519
- subtitle,
3520
- caption,
3521
- onPress,
3522
- style,
3523
- imageStyle,
3524
- footer,
3525
- accessibilityLabel
3526
- }) {
3527
- const { colors } = useTheme();
3528
- const { hovered, hoverHandlers } = useHover();
3529
- const { animatedStyle, onPressIn, onPressOut } = usePressScale({
3530
- pressScale: PRESS_SCALE.card,
3531
- pressInSpring: SPRINGS.surfacePressIn,
3532
- pressOutSpring: SPRINGS.surfacePressOut,
3533
- disabled: !onPress
3534
- });
3535
- const handlePress = () => {
3536
- if (!onPress) return;
3537
- impactLight();
3538
- onPress();
3539
- };
3540
- const ratio = aspectRatioMap[aspectRatio];
3541
- const resolvedActionIcon = actionIconName ? renderIcon(actionIconName, 18, actionActive ? colors.primary : colors.background) : actionIcon ?? renderIcon("heart", 18, actionActive ? colors.primary : colors.background);
3542
- const a11yLabel = accessibilityLabel ?? [title, subtitle].filter(Boolean).join(". ");
3543
- const cardContent = /* @__PURE__ */ React26.createElement(
3544
- View,
3545
- {
3546
- style: [
3547
- styles31.card,
3548
- hovered && styles31.cardHovered,
3549
- style
3550
- ],
3551
- ...Platform.OS === "web" ? hoverHandlers : {}
3552
- },
3553
- /* @__PURE__ */ React26.createElement(View, { style: [styles31.imageContainer, imageStyle] }, /* @__PURE__ */ React26.createElement(View, { style: { paddingTop: `${ratio * 100}%` } }, /* @__PURE__ */ React26.createElement(View, { style: StyleSheet.absoluteFill }, imageSource ? /* @__PURE__ */ React26.createElement(
3554
- Image,
3555
- {
3556
- source: imageSource,
3557
- style: styles31.image,
3558
- resizeMode: "cover"
3559
- }
3560
- ) : /* @__PURE__ */ React26.createElement(View, { style: [styles31.imagePlaceholder, { backgroundColor: colors.surface }] }))), badge && /* @__PURE__ */ React26.createElement(View, { style: styles31.badgeContainer }, badge), (onActionPress || actionIcon || actionIconName) && /* @__PURE__ */ React26.createElement(
3561
- TouchableOpacity,
3562
- {
3563
- style: [styles31.actionButton, { backgroundColor: "rgba(0,0,0,0.24)" }],
3564
- onPress: () => {
3565
- impactLight();
3566
- onActionPress?.();
3567
- },
3568
- activeOpacity: 0.8,
3569
- touchSoundDisabled: true,
3570
- accessibilityRole: "button",
3571
- accessibilityLabel: actionIconName ?? "action",
3572
- accessibilityState: { selected: actionActive }
3573
- },
3574
- resolvedActionIcon
3575
- )),
3576
- (title || subtitle || caption || footer) && /* @__PURE__ */ React26.createElement(View, { style: styles31.meta }, title ? /* @__PURE__ */ React26.createElement(Text, { style: [styles31.title, { color: colors.foreground }], numberOfLines: 2, allowFontScaling: true }, title) : null, subtitle ? /* @__PURE__ */ React26.createElement(Text, { style: [styles31.subtitle, { color: colors.foregroundSubtle }], numberOfLines: 1, allowFontScaling: true }, subtitle) : null, caption ? /* @__PURE__ */ React26.createElement(Text, { style: [styles31.caption, { color: colors.foregroundMuted }], numberOfLines: 1, allowFontScaling: true }, caption) : null, footer)
3577
- );
3578
- if (onPress) {
3579
- return /* @__PURE__ */ React26.createElement(Animated9.View, { style: animatedStyle }, /* @__PURE__ */ React26.createElement(
3580
- TouchableOpacity,
3581
- {
3582
- onPress: handlePress,
3583
- onPressIn,
3584
- onPressOut,
3585
- activeOpacity: 1,
3586
- touchSoundDisabled: true,
3587
- accessibilityRole: "button",
3588
- accessibilityLabel: a11yLabel
3589
- },
3590
- cardContent
3591
- ));
3592
- }
3593
- return cardContent;
3594
- }
3595
- var styles31 = StyleSheet.create({
3596
- card: {
3597
- borderRadius: RADIUS.md,
3598
- overflow: "hidden",
3599
- backgroundColor: "transparent"
3600
- },
3601
- cardHovered: {
3602
- ...SHADOWS.md
3603
- },
3604
- imageContainer: {
3605
- borderRadius: RADIUS.md,
3606
- overflow: "hidden"
3607
- },
3608
- image: {
3609
- width: "100%",
3610
- height: "100%"
3611
- },
3612
- imagePlaceholder: {
3613
- width: "100%",
3614
- height: "100%"
3615
- },
3616
- badgeContainer: {
3617
- position: "absolute",
3618
- top: s(8),
3619
- left: s(8)
3620
- },
3621
- actionButton: {
3622
- position: "absolute",
3623
- top: s(8),
3624
- right: s(8),
3625
- width: s(32),
3626
- height: s(32),
3627
- borderRadius: 9999,
3628
- alignItems: "center",
3629
- justifyContent: "center"
3630
- },
3631
- meta: {
3632
- paddingTop: vs(8),
3633
- paddingBottom: vs(4),
3634
- gap: vs(2)
3635
- },
3636
- title: {
3637
- fontFamily: "Poppins-SemiBold",
3638
- fontSize: ms(14),
3639
- lineHeight: mvs(20)
3640
- },
3641
- subtitle: {
3642
- fontFamily: "Poppins-Regular",
3643
- fontSize: ms(13),
3644
- lineHeight: mvs(18)
3645
- },
3646
- caption: {
3647
- fontFamily: "Poppins-Regular",
3648
- fontSize: ms(12),
3649
- lineHeight: mvs(16)
3650
- }
3651
- });
3652
- function CategoryChip({
3653
- item,
3654
- selected,
3655
- onPress
3656
- }) {
3657
- const { colors } = useTheme();
3658
- const { animatedStyle: scaleStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
3659
- pressScale: PRESS_SCALE.chip
3660
- });
3661
- const progress = useColorTransition(selected);
3662
- const surfaceStyle = useAnimatedStyle(() => ({
3663
- backgroundColor: interpolateColor(progress.value, [0, 1], [colors.surface, colors.primary]),
3664
- borderColor: interpolateColor(progress.value, [0, 1], [colors.border, colors.primary])
3665
- }));
3666
- const textColorStyle = useAnimatedStyle(() => ({
3667
- color: interpolateColor(progress.value, [0, 1], [colors.foregroundSubtle, colors.primaryForeground])
3668
- }));
3669
- const iconColor = selected ? colors.primaryForeground : colors.foregroundSubtle;
3670
- const resolvedIcon = typeof item.icon === "string" ? renderIcon(item.icon, 16, iconColor) : item.icon ?? null;
3671
- return /* @__PURE__ */ React26.createElement(Animated9.View, { style: scaleStyle, ...hoverHandlers }, /* @__PURE__ */ React26.createElement(
3672
- TouchableOpacity,
3673
- {
3674
- onPress,
3675
- onPressIn,
3676
- onPressOut,
3677
- activeOpacity: 1,
3678
- touchSoundDisabled: true,
3679
- accessibilityRole: "button",
3680
- accessibilityLabel: item.label,
3681
- accessibilityState: { selected }
3682
- },
3683
- /* @__PURE__ */ React26.createElement(Animated9.View, { style: [styles32.chip, surfaceStyle] }, resolvedIcon && /* @__PURE__ */ React26.createElement(View, { style: styles32.chipIcon }, resolvedIcon), /* @__PURE__ */ React26.createElement(Animated9.Text, { style: [styles32.chipLabel, textColorStyle], allowFontScaling: true }, item.label), item.badge !== void 0 && item.badge > 0 && /* @__PURE__ */ React26.createElement(View, { style: [styles32.chipBadge, { backgroundColor: colors.primary }] }, /* @__PURE__ */ React26.createElement(Text, { style: [styles32.chipBadgeText, { color: colors.primaryForeground }] }, Math.min(item.badge, 99))))
3684
- ));
3685
- }
3686
- function CategoryStrip({
3687
- categories,
3688
- value,
3689
- onValueChange,
3690
- multiSelect = false,
3691
- style,
3692
- itemStyle,
3693
- accessibilityLabel
3694
- }) {
3695
- const selected = Array.isArray(value) ? value : value ? [value] : [];
3696
- const handlePress = (v) => {
3697
- selectionAsync();
3698
- if (multiSelect) {
3699
- const current = Array.isArray(value) ? value : value ? [value] : [];
3700
- const next = current.includes(v) ? current.filter((x) => x !== v) : [...current, v];
3701
- onValueChange?.(next);
3702
- } else {
3703
- onValueChange?.(v === value ? "" : v);
3704
- }
3705
- };
3706
- return /* @__PURE__ */ React26.createElement(
3707
- ScrollView,
3708
- {
3709
- horizontal: true,
3710
- showsHorizontalScrollIndicator: false,
3711
- contentContainerStyle: [styles32.container, style],
3712
- style: styles32.scroll,
3713
- accessibilityRole: multiSelect ? void 0 : "radiogroup",
3714
- accessibilityLabel
3715
- },
3716
- categories.map((cat) => /* @__PURE__ */ React26.createElement(View, { key: cat.value, style: itemStyle }, /* @__PURE__ */ React26.createElement(
3717
- CategoryChip,
3718
- {
3719
- item: cat,
3720
- selected: selected.includes(cat.value),
3721
- onPress: () => handlePress(cat.value)
3722
- }
3723
- )))
3724
- );
3725
- }
3726
- var styles32 = StyleSheet.create({
3727
- scroll: {
3728
- flexGrow: 0
3729
- },
3730
- container: {
3731
- flexDirection: "row",
3732
- gap: s(8),
3733
- paddingHorizontal: s(4),
3734
- paddingVertical: vs(4)
3735
- },
3736
- chip: {
3737
- flexDirection: "row",
3738
- alignItems: "center",
3739
- borderRadius: RADIUS.full,
3740
- borderWidth: 1,
3741
- paddingHorizontal: s(14),
3742
- paddingVertical: vs(8),
3743
- gap: s(6)
3744
- },
3745
- chipIcon: {
3746
- alignItems: "center",
3747
- justifyContent: "center"
3748
- },
3749
- chipLabel: {
3750
- fontFamily: "Poppins-Medium",
3751
- fontSize: ms(13)
3752
- },
3753
- chipBadge: {
3754
- minWidth: 16,
3755
- height: 16,
3756
- borderRadius: 9999,
3757
- paddingHorizontal: 3,
3758
- alignItems: "center",
3759
- justifyContent: "center"
3760
- },
3761
- chipBadgeText: {
3762
- fontFamily: "Poppins-Bold",
3763
- fontSize: ms(9),
3764
- lineHeight: 14
3765
- }
3766
- });
3767
- function Pressable2({
3768
- children,
3769
- onPress,
3770
- pressScale = PRESS_SCALE.card,
3771
- haptics = true,
3772
- style,
3773
- disabled,
3774
- hoverScale = 1.02,
3775
- ...touchableProps
3776
- }) {
3777
- const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
3778
- pressScale,
3779
- hoverScale,
3780
- pressInSpring: SPRINGS.surfacePressIn,
3781
- pressOutSpring: SPRINGS.surfacePressOut,
3782
- disabled
3783
- });
3784
- const handlePress = () => {
3785
- if (disabled || !onPress) return;
3786
- if (haptics) impactLight();
3787
- onPress();
3788
- };
3789
- return /* @__PURE__ */ React26.createElement(
3790
- Animated9.View,
3791
- {
3792
- style: [animatedStyle, style],
3793
- ...Platform.OS === "web" ? hoverHandlers : {}
3794
- },
3795
- /* @__PURE__ */ React26.createElement(
3796
- TouchableOpacity,
3797
- {
3798
- onPress: handlePress,
3799
- onPressIn,
3800
- onPressOut,
3801
- activeOpacity: 1,
3802
- disabled,
3803
- touchSoundDisabled: true,
3804
- accessibilityRole: "button",
3805
- accessibilityState: { disabled: !!disabled },
3806
- ...touchableProps
3807
- },
3808
- children
3809
- )
3810
- );
3811
- }
3812
- var weightMap = {
3813
- normal: "Poppins-Regular",
3814
- medium: "Poppins-Medium",
3815
- semibold: "Poppins-SemiBold",
3816
- bold: "Poppins-Bold"
3817
- };
3818
- function DetailRow({
3819
- label,
3820
- value,
3821
- separator = "dotted",
3822
- labelWeight = "normal",
3823
- valueColor,
3824
- leftIcon,
3825
- leftIconName,
3826
- leftIconColor,
3827
- rightIconName,
3828
- rightIconColor,
3829
- style,
3830
- labelStyle,
3831
- valueStyle
3832
- }) {
3833
- const { colors } = useTheme();
3834
- const resolvedLeftIcon = leftIconName ? renderIcon(leftIconName, ms(14), leftIconColor ?? colors.foregroundMuted) : leftIcon;
3835
- const resolvedRightIcon = rightIconName ? renderIcon(rightIconName, ms(14), rightIconColor ?? colors.foregroundMuted) : null;
3836
- const separatorStyle = separator === "none" ? null : {
3837
- flex: 1,
3838
- height: 1,
3839
- borderBottomWidth: 1,
3840
- borderStyle: separator,
3841
- borderColor: "rgba(128,128,128,0.3)",
3842
- marginHorizontal: s(4)
3843
- };
3844
- return /* @__PURE__ */ React26.createElement(View, { style: [styles33.row, style] }, /* @__PURE__ */ React26.createElement(View, { style: styles33.labelSide }, resolvedLeftIcon ? /* @__PURE__ */ React26.createElement(View, { style: styles33.icon }, resolvedLeftIcon) : null, typeof label === "string" ? /* @__PURE__ */ React26.createElement(
3845
- Text,
3846
- {
3847
- style: [styles33.labelText, { color: colors.foregroundMuted, fontFamily: weightMap[labelWeight] }, labelStyle],
3848
- allowFontScaling: true
3849
- },
3850
- label
3851
- ) : label), separatorStyle ? /* @__PURE__ */ React26.createElement(View, { style: separatorStyle }) : /* @__PURE__ */ React26.createElement(View, { style: styles33.spacer }), /* @__PURE__ */ React26.createElement(View, { style: styles33.valueSide }, typeof value === "string" ? /* @__PURE__ */ React26.createElement(
3852
- Text,
3853
- {
3854
- style: [styles33.valueText, { color: valueColor ?? colors.foreground }, valueStyle],
3855
- allowFontScaling: true
3856
- },
3857
- value
3858
- ) : value, resolvedRightIcon ? /* @__PURE__ */ React26.createElement(View, { style: styles33.icon }, resolvedRightIcon) : null));
3859
- }
3860
- var styles33 = StyleSheet.create({
3861
- row: {
3862
- flexDirection: "row",
3863
- alignItems: "center",
3864
- gap: s(4)
3865
- },
3866
- labelSide: {
3867
- flexDirection: "row",
3868
- alignItems: "center",
3869
- gap: s(4),
3870
- flexShrink: 0
3871
- },
3872
- icon: {
3873
- alignItems: "center",
3874
- justifyContent: "center"
3875
- },
3876
- spacer: {
3877
- flex: 1
3878
- },
3879
- labelText: {
3880
- fontSize: ms(13),
3881
- lineHeight: mvs(18)
3882
- },
3883
- valueSide: {
3884
- flexDirection: "row",
3885
- alignItems: "center",
3886
- gap: s(4),
3887
- flexShrink: 0
3888
- },
3889
- valueText: {
3890
- fontFamily: "Poppins-SemiBold",
3891
- fontSize: ms(13),
3892
- lineHeight: mvs(18)
3893
- }
3894
- });
1
+ export { Toggle } from './chunk-LWG526VX.mjs';
2
+ export { VirtualList } from './chunk-NC5ZTR2Y.mjs';
3
+ export { Skeleton } from './chunk-JBLL7U3U.mjs';
4
+ export { Slider } from './chunk-NQGVLMWG.mjs';
5
+ export { Spinner } from './chunk-76PFOSM2.mjs';
6
+ export { Switch } from './chunk-DITNP6PL.mjs';
7
+ export { Tabs, TabsContent } from './chunk-RR2VQLKE.mjs';
8
+ export { Text } from './chunk-GNGLDL6Z.mjs';
9
+ export { Textarea } from './chunk-6LQYY7HC.mjs';
10
+ export { ToastProvider, sonnerToast as toast, useToast } from './chunk-2UYENBLV.mjs';
11
+ export { MenuItem } from './chunk-GPOUINK5.mjs';
12
+ export { MonthPicker } from './chunk-RKLHUDZS.mjs';
13
+ export { Pressable } from './chunk-RMMK64W5.mjs';
14
+ export { Progress } from './chunk-63357L2X.mjs';
15
+ export { RadioGroup } from './chunk-YZJAFS4P.mjs';
16
+ export { Select } from './chunk-URLL5JBR.mjs';
17
+ export { Separator } from './chunk-MX6HRKMI.mjs';
18
+ export { BottomSheetModalProvider, Sheet, BottomSheetTextInput as SheetTextInput } from './chunk-AU2VDY4P.mjs';
19
+ export { Form, FormField, FormFooter, FormSection } from './chunk-6Q64UFIA.mjs';
20
+ export { IconButton } from './chunk-KWCPOM6W.mjs';
21
+ export { LabelValue } from './chunk-A4MDAP7G.mjs';
22
+ export { ListGroup, ListGroupFooter, ListGroupHeader } from './chunk-SOA2Z4RB.mjs';
23
+ export { ListItem } from './chunk-LG4DO3DK.mjs';
24
+ export { MediaCard } from './chunk-TAJ2PQ2O.mjs';
25
+ export { MenuGroup, MenuGroupFooter, MenuGroupHeader } from './chunk-IRRY3CRZ.mjs';
26
+ export { CategoryStrip } from './chunk-URDE3EUU.mjs';
27
+ export { Checkbox } from './chunk-FTLJOUOQ.mjs';
28
+ export { Chip, ChipGroup } from './chunk-U4N7WF4Z.mjs';
29
+ export { ConfirmDialog } from './chunk-HSPSMN6U.mjs';
30
+ export { CurrencyDisplay } from './chunk-BRKYVJVV.mjs';
31
+ export { CurrencyInput, CurrencyInput as CurrencyInputLarge } from './chunk-XDMN67KV.mjs';
32
+ export { Input } from './chunk-L7E7TVEZ.mjs';
33
+ import './chunk-7H2OR44A.mjs';
34
+ export { DetailRow } from './chunk-JB67UOB5.mjs';
35
+ export { EmptyState } from './chunk-MN7OG7IY.mjs';
36
+ export { Accordion } from './chunk-QXGYKWI7.mjs';
37
+ export { AlertBanner } from './chunk-SBZYEV4S.mjs';
38
+ export { Avatar } from './chunk-GCWOGZYL.mjs';
39
+ export { Badge } from './chunk-KZJRQOIU.mjs';
40
+ export { Button } from './chunk-CRYBX2CM.mjs';
41
+ export { Icon, configureIconFamilies, renderIcon } from './chunk-T7XZ7H7Y.mjs';
42
+ export { ButtonGroup } from './chunk-3BBOZ3OQ.mjs';
43
+ export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './chunk-Y6MXOREN.mjs';
44
+ import './chunk-QCNARS3X.mjs';
45
+ import './chunk-RTC3CFXF.mjs';
46
+ import './chunk-5IKW3VNC.mjs';
47
+ export { BREAKPOINTS, ICON_SIZES, RADIUS, SHADOWS, SPACING, TYPOGRAPHY } from './chunk-QY3X2UYR.mjs';
48
+ export { ThemeProvider, defaultDark, defaultLight, deriveColors, useTheme } from './chunk-SOYNZDVY.mjs';
49
+ import './chunk-2CE3TQVY.mjs';
3895
50
 
3896
51
  // src/utils/typography.ts
3897
52
  function getResponsiveFontSize(text, maxSize, steps = [
@@ -3907,4 +62,4 @@ function getResponsiveFontSize(text, maxSize, steps = [
3907
62
  return maxSize - 8;
3908
63
  }
3909
64
 
3910
- export { Accordion, AlertBanner, Avatar, BREAKPOINTS, Badge, Button, ButtonGroup, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryStrip, Checkbox, Chip, ChipGroup, ConfirmDialog, CurrencyDisplay, CurrencyInput, CurrencyInput as CurrencyInputLarge, DetailRow, EmptyState, ICON_SIZES, Icon, IconButton, Input, LabelValue, ListItem, MediaCard, MenuItem, MonthPicker, Pressable2 as Pressable, Progress, RADIUS, RadioGroup, SHADOWS, SPACING, Select, Separator, Sheet, Skeleton, Slider, Spinner, Switch, TYPOGRAPHY, Tabs, TabsContent, Text3 as Text, Textarea, ThemeProvider, ToastProvider, Toggle, defaultDark, defaultLight, deriveColors, getResponsiveFontSize, renderIcon, useTheme, useToast };
65
+ export { getResponsiveFontSize };