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