@retray-dev/ui-kit 13.2.0 → 13.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/CHANGELOG.md +680 -0
  2. package/CONSUMER.md +2 -0
  3. package/README.md +4 -4
  4. package/SKILL.md +276 -12
  5. package/dist/Accordion.d.mts +6 -6
  6. package/dist/Accordion.d.ts +6 -6
  7. package/dist/Accordion.js +39 -40
  8. package/dist/Accordion.mjs +4 -4
  9. package/dist/AlertBanner.d.mts +3 -3
  10. package/dist/AlertBanner.d.ts +3 -3
  11. package/dist/AlertBanner.js +7 -13
  12. package/dist/AlertBanner.mjs +4 -4
  13. package/dist/AppHeader.d.mts +4 -4
  14. package/dist/AppHeader.d.ts +4 -4
  15. package/dist/AppHeader.js +37 -28
  16. package/dist/AppHeader.mjs +6 -6
  17. package/dist/Avatar.d.mts +4 -4
  18. package/dist/Avatar.d.ts +4 -4
  19. package/dist/Avatar.mjs +2 -2
  20. package/dist/Badge.d.mts +5 -5
  21. package/dist/Badge.d.ts +5 -5
  22. package/dist/Badge.js +7 -13
  23. package/dist/Badge.mjs +3 -3
  24. package/dist/Button.d.mts +5 -5
  25. package/dist/Button.d.ts +5 -5
  26. package/dist/Button.js +31 -29
  27. package/dist/Button.mjs +5 -5
  28. package/dist/ButtonGroup.d.mts +3 -3
  29. package/dist/ButtonGroup.d.ts +3 -3
  30. package/dist/Card.d.mts +13 -13
  31. package/dist/Card.d.ts +13 -13
  32. package/dist/Card.js +23 -14
  33. package/dist/Card.mjs +4 -4
  34. package/dist/CategoryStrip.d.mts +3 -3
  35. package/dist/CategoryStrip.d.ts +3 -3
  36. package/dist/CategoryStrip.js +30 -27
  37. package/dist/CategoryStrip.mjs +5 -5
  38. package/dist/Checkbox.d.mts +2 -2
  39. package/dist/Checkbox.d.ts +2 -2
  40. package/dist/Checkbox.js +23 -14
  41. package/dist/Checkbox.mjs +3 -3
  42. package/dist/Chip.d.mts +5 -5
  43. package/dist/Chip.d.ts +5 -5
  44. package/dist/Chip.js +30 -27
  45. package/dist/Chip.mjs +5 -5
  46. package/dist/ConfirmDialog.d.mts +2 -2
  47. package/dist/ConfirmDialog.d.ts +2 -2
  48. package/dist/ConfirmDialog.js +64 -58
  49. package/dist/ConfirmDialog.mjs +7 -6
  50. package/dist/CurrencyDisplay.d.mts +3 -3
  51. package/dist/CurrencyDisplay.d.ts +3 -3
  52. package/dist/CurrencyDisplay.mjs +2 -2
  53. package/dist/CurrencyInput.d.mts +2 -2
  54. package/dist/CurrencyInput.d.ts +2 -2
  55. package/dist/CurrencyInput.js +7 -13
  56. package/dist/CurrencyInput.mjs +4 -4
  57. package/dist/DetailRow.d.mts +6 -6
  58. package/dist/DetailRow.d.ts +6 -6
  59. package/dist/DetailRow.js +7 -13
  60. package/dist/DetailRow.mjs +3 -3
  61. package/dist/EmptyState.d.mts +4 -4
  62. package/dist/EmptyState.d.ts +4 -4
  63. package/dist/EmptyState.js +31 -29
  64. package/dist/EmptyState.mjs +6 -6
  65. package/dist/ErrorBoundary.d.mts +9 -7
  66. package/dist/ErrorBoundary.d.ts +9 -7
  67. package/dist/ErrorBoundary.js +33 -29
  68. package/dist/ErrorBoundary.mjs +5 -5
  69. package/dist/Form.d.mts +9 -9
  70. package/dist/Form.d.ts +9 -9
  71. package/dist/Form.mjs +2 -2
  72. package/dist/HolographicCard.d.mts +2 -2
  73. package/dist/HolographicCard.d.ts +2 -2
  74. package/dist/HolographicCard.js +23 -14
  75. package/dist/HolographicCard.mjs +2 -2
  76. package/dist/IconButton.d.mts +4 -4
  77. package/dist/IconButton.d.ts +4 -4
  78. package/dist/IconButton.js +30 -27
  79. package/dist/IconButton.mjs +4 -4
  80. package/dist/IconPicker.d.mts +2 -2
  81. package/dist/IconPicker.d.ts +2 -2
  82. package/dist/IconPicker.js +40 -45
  83. package/dist/IconPicker.mjs +6 -6
  84. package/dist/Image.d.mts +18 -0
  85. package/dist/Image.d.ts +18 -0
  86. package/dist/Image.js +53 -0
  87. package/dist/Image.mjs +2 -0
  88. package/dist/ImageUpload.d.mts +2 -2
  89. package/dist/ImageUpload.d.ts +2 -2
  90. package/dist/ImageUpload.js +24 -15
  91. package/dist/ImageUpload.mjs +5 -5
  92. package/dist/ImageViewer.d.mts +2 -2
  93. package/dist/ImageViewer.d.ts +2 -2
  94. package/dist/ImageViewer.js +31 -28
  95. package/dist/ImageViewer.mjs +6 -6
  96. package/dist/Input.d.mts +4 -4
  97. package/dist/Input.d.ts +4 -4
  98. package/dist/Input.js +7 -13
  99. package/dist/Input.mjs +3 -3
  100. package/dist/ItemGroup.d.mts +23 -0
  101. package/dist/ItemGroup.d.ts +23 -0
  102. package/dist/{ListGroup.js → ItemGroup.js} +11 -13
  103. package/dist/ItemGroup.mjs +4 -0
  104. package/dist/LabelValue.d.mts +4 -4
  105. package/dist/LabelValue.d.ts +4 -4
  106. package/dist/LabelValue.js +7 -13
  107. package/dist/LabelValue.mjs +3 -3
  108. package/dist/ListItem.d.mts +6 -6
  109. package/dist/ListItem.d.ts +6 -6
  110. package/dist/ListItem.js +30 -27
  111. package/dist/ListItem.mjs +5 -5
  112. package/dist/MediaCard.d.mts +6 -6
  113. package/dist/MediaCard.d.ts +6 -6
  114. package/dist/MediaCard.js +30 -27
  115. package/dist/MediaCard.mjs +5 -5
  116. package/dist/MenuItem.d.mts +5 -5
  117. package/dist/MenuItem.d.ts +5 -5
  118. package/dist/MenuItem.js +30 -27
  119. package/dist/MenuItem.mjs +5 -5
  120. package/dist/MonthPicker.d.mts +2 -2
  121. package/dist/MonthPicker.d.ts +2 -2
  122. package/dist/MonthPicker.js +23 -14
  123. package/dist/MonthPicker.mjs +3 -3
  124. package/dist/NumberStepper.d.mts +3 -3
  125. package/dist/NumberStepper.d.ts +3 -3
  126. package/dist/NumberStepper.js +30 -27
  127. package/dist/NumberStepper.mjs +5 -5
  128. package/dist/PagerDots.d.mts +2 -2
  129. package/dist/PagerDots.d.ts +2 -2
  130. package/dist/PagerDots.js +30 -27
  131. package/dist/PagerDots.mjs +4 -4
  132. package/dist/Pressable.d.mts +3 -27
  133. package/dist/Pressable.d.ts +3 -27
  134. package/dist/Pressable.js +23 -14
  135. package/dist/Pressable.mjs +2 -2
  136. package/dist/PricingCard.d.mts +2 -2
  137. package/dist/PricingCard.d.ts +2 -2
  138. package/dist/PricingCard.js +31 -29
  139. package/dist/PricingCard.mjs +7 -7
  140. package/dist/Progress.d.mts +2 -2
  141. package/dist/Progress.d.ts +2 -2
  142. package/dist/Progress.mjs +2 -2
  143. package/dist/RadioGroup.d.mts +2 -2
  144. package/dist/RadioGroup.d.ts +2 -2
  145. package/dist/RadioGroup.js +23 -14
  146. package/dist/RadioGroup.mjs +3 -3
  147. package/dist/RetrayProvider.d.mts +1 -1
  148. package/dist/RetrayProvider.d.ts +1 -1
  149. package/dist/RetrayProvider.js +14 -34
  150. package/dist/RetrayProvider.mjs +3 -3
  151. package/dist/ScreenContainer.d.mts +24 -0
  152. package/dist/ScreenContainer.d.ts +24 -0
  153. package/dist/ScreenContainer.js +85 -0
  154. package/dist/ScreenContainer.mjs +3 -0
  155. package/dist/Select.d.mts +2 -2
  156. package/dist/Select.d.ts +2 -2
  157. package/dist/Select.js +38 -45
  158. package/dist/Select.mjs +3 -3
  159. package/dist/SelectableCard.d.mts +5 -5
  160. package/dist/SelectableCard.d.ts +5 -5
  161. package/dist/SelectableCard.js +30 -27
  162. package/dist/SelectableCard.mjs +5 -5
  163. package/dist/SelectableGrid.d.mts +5 -4
  164. package/dist/SelectableGrid.d.ts +5 -4
  165. package/dist/SelectableGrid.js +80 -44
  166. package/dist/SelectableGrid.mjs +5 -5
  167. package/dist/Separator.d.mts +4 -2
  168. package/dist/Separator.d.ts +4 -2
  169. package/dist/Separator.js +29 -1
  170. package/dist/Separator.mjs +3 -2
  171. package/dist/Sheet.d.mts +10 -10
  172. package/dist/Sheet.d.ts +10 -10
  173. package/dist/Sheet.js +59 -44
  174. package/dist/Sheet.mjs +4 -3
  175. package/dist/SheetSelect.d.mts +2 -2
  176. package/dist/SheetSelect.d.ts +2 -2
  177. package/dist/SheetSelect.js +30 -27
  178. package/dist/SheetSelect.mjs +5 -5
  179. package/dist/Skeleton.d.mts +5 -5
  180. package/dist/Skeleton.d.ts +5 -5
  181. package/dist/Skeleton.mjs +3 -3
  182. package/dist/Slider.d.mts +2 -2
  183. package/dist/Slider.d.ts +2 -2
  184. package/dist/Slider.js +23 -14
  185. package/dist/Slider.mjs +3 -3
  186. package/dist/Spinner.d.mts +2 -2
  187. package/dist/Spinner.d.ts +2 -2
  188. package/dist/Spinner.mjs +2 -2
  189. package/dist/Stats.d.mts +6 -6
  190. package/dist/Stats.d.ts +6 -6
  191. package/dist/Stats.js +30 -27
  192. package/dist/Stats.mjs +5 -5
  193. package/dist/Switch.d.mts +2 -2
  194. package/dist/Switch.d.ts +2 -2
  195. package/dist/Switch.js +23 -14
  196. package/dist/Switch.mjs +3 -3
  197. package/dist/TabBar.d.mts +3 -3
  198. package/dist/TabBar.d.ts +3 -3
  199. package/dist/TabBar.js +30 -27
  200. package/dist/TabBar.mjs +4 -4
  201. package/dist/Tabs.d.mts +13 -13
  202. package/dist/Tabs.d.ts +13 -13
  203. package/dist/Tabs.js +23 -14
  204. package/dist/Tabs.mjs +3 -3
  205. package/dist/Text.d.mts +4 -4
  206. package/dist/Text.d.ts +4 -4
  207. package/dist/Text.js +20 -2
  208. package/dist/Text.mjs +3 -3
  209. package/dist/Textarea.d.mts +3 -3
  210. package/dist/Textarea.d.ts +3 -3
  211. package/dist/Textarea.js +7 -13
  212. package/dist/Textarea.mjs +3 -3
  213. package/dist/Toast.d.mts +4 -4
  214. package/dist/Toast.d.ts +4 -4
  215. package/dist/Toast.mjs +2 -2
  216. package/dist/Toggle.d.mts +4 -4
  217. package/dist/Toggle.d.ts +4 -4
  218. package/dist/Toggle.js +30 -27
  219. package/dist/Toggle.mjs +4 -4
  220. package/dist/VirtualizedList.d.mts +28 -0
  221. package/dist/VirtualizedList.d.ts +28 -0
  222. package/dist/VirtualizedList.js +130 -0
  223. package/dist/VirtualizedList.mjs +3 -0
  224. package/dist/{chunk-MZ6WRTD2.mjs → chunk-24JTXQ2M.mjs} +7 -13
  225. package/dist/{chunk-OBV72JD4.mjs → chunk-2DDJ53DK.mjs} +9 -11
  226. package/dist/{chunk-H6MQL7PS.mjs → chunk-2J5OZOMX.mjs} +12 -6
  227. package/dist/{chunk-4NQFTHN3.mjs → chunk-3GE4UFV5.mjs} +2 -2
  228. package/dist/{chunk-KAGADD2O.mjs → chunk-3RIZCKRM.mjs} +2 -2
  229. package/dist/{chunk-DE25XTVQ.mjs → chunk-3VHFOSZR.mjs} +2 -2
  230. package/dist/{chunk-C5ZRMR2E.mjs → chunk-4PF4LKNT.mjs} +2 -2
  231. package/dist/{chunk-5MYNAAFE.mjs → chunk-5J7VKFSZ.mjs} +4 -4
  232. package/dist/{chunk-E2PONRJG.mjs → chunk-5TNQ573V.mjs} +2 -2
  233. package/dist/{chunk-CZN6L2QU.mjs → chunk-6T2DVIQT.mjs} +4 -4
  234. package/dist/{chunk-L3YKPTJQ.mjs → chunk-7CE6PDCQ.mjs} +2 -2
  235. package/dist/{chunk-Y6YS33GM.mjs → chunk-AHFEAY6M.mjs} +4 -4
  236. package/dist/{chunk-77UOVFIS.mjs → chunk-AZRATPNP.mjs} +2 -2
  237. package/dist/{chunk-UMZTPUB3.mjs → chunk-BGXOEFDM.mjs} +6 -31
  238. package/dist/{chunk-KC5QDYGZ.mjs → chunk-BMAAAJWN.mjs} +2 -2
  239. package/dist/{chunk-IJCMPVW5.mjs → chunk-BQMJQMWY.mjs} +2 -2
  240. package/dist/{chunk-COA2YZOX.mjs → chunk-BTPCY4C7.mjs} +4 -4
  241. package/dist/chunk-BVJAYPAD.mjs +55 -0
  242. package/dist/{chunk-RA6SAAFE.mjs → chunk-BWLVX2SQ.mjs} +4 -4
  243. package/dist/{chunk-HHOOFDBA.mjs → chunk-CCEM3HIJ.mjs} +5 -5
  244. package/dist/chunk-CTUFFKGS.mjs +30 -0
  245. package/dist/{chunk-EHGBHFMH.mjs → chunk-CYGYC7XT.mjs} +8 -4
  246. package/dist/{chunk-ESQDPO5E.mjs → chunk-DLAOTHHS.mjs} +7 -6
  247. package/dist/{chunk-QY3X2UYR.mjs → chunk-DYYPDQA2.mjs} +21 -7
  248. package/dist/{chunk-S44XWTTC.mjs → chunk-E4BJ5WXG.mjs} +3 -3
  249. package/dist/{chunk-HUSSF6TF.mjs → chunk-EQNCMDZC.mjs} +1 -1
  250. package/dist/{chunk-PI6RULJX.mjs → chunk-EQYTDFDD.mjs} +1 -1
  251. package/dist/{chunk-BULKGOIZ.mjs → chunk-FE26TPCI.mjs} +4 -4
  252. package/dist/{chunk-DBHSUUKU.mjs → chunk-FOUSI6JD.mjs} +1 -1
  253. package/dist/{chunk-KPTY7UYQ.mjs → chunk-GR7PKEKD.mjs} +1 -1
  254. package/dist/{chunk-RRKM4MKB.mjs → chunk-HLWGFBIF.mjs} +3 -3
  255. package/dist/chunk-HMKJGVXA.mjs +35 -0
  256. package/dist/{chunk-U6DEBYU5.mjs → chunk-IFGZUJFH.mjs} +3 -3
  257. package/dist/{chunk-2VIDP72N.mjs → chunk-K3V6OTVB.mjs} +1 -1
  258. package/dist/{chunk-K7TKID3V.mjs → chunk-K4YFTUMC.mjs} +3 -3
  259. package/dist/{chunk-NGEN2EES.mjs → chunk-MQAK2W6L.mjs} +14 -22
  260. package/dist/{chunk-CM2DG4MR.mjs → chunk-MSS3CD6F.mjs} +4 -4
  261. package/dist/{chunk-2QXJDRVU.mjs → chunk-NQYS6RPX.mjs} +4 -4
  262. package/dist/{chunk-62BBSSUF.mjs → chunk-P5KC3RTG.mjs} +1 -1
  263. package/dist/{chunk-K3QX2M26.mjs → chunk-PPKCGCZ3.mjs} +5 -5
  264. package/dist/{chunk-ITG4JQM3.mjs → chunk-QEE3EQ3N.mjs} +2 -2
  265. package/dist/{chunk-IDVUZIVY.mjs → chunk-RLPPRIJ7.mjs} +17 -33
  266. package/dist/{chunk-XCIG6HT2.mjs → chunk-S433IOQE.mjs} +2 -2
  267. package/dist/{chunk-IGU223UM.mjs → chunk-SWUZKVYO.mjs} +1 -1
  268. package/dist/{chunk-NPCBNGNE.mjs → chunk-T4KMKHTI.mjs} +55 -22
  269. package/dist/{chunk-7BZJRB77.mjs → chunk-UBTP4NPP.mjs} +4 -30
  270. package/dist/{chunk-TMH263OK.mjs → chunk-UEA2VYGW.mjs} +3 -3
  271. package/dist/chunk-VISIOH33.mjs +37 -0
  272. package/dist/{chunk-SZEKQAOY.mjs → chunk-VSKBODEY.mjs} +1 -1
  273. package/dist/{chunk-FTTI6T5Q.mjs → chunk-W422TEH2.mjs} +3 -3
  274. package/dist/{chunk-WIPEDNSD.mjs → chunk-WD5LBXPR.mjs} +4 -4
  275. package/dist/chunk-WFNGSYS4.mjs +111 -0
  276. package/dist/chunk-WR6DCNAE.mjs +65 -0
  277. package/dist/{chunk-ERWJPVX7.mjs → chunk-XKBB2UZU.mjs} +2 -2
  278. package/dist/{chunk-422IVD3H.mjs → chunk-Y5TPAKOS.mjs} +13 -17
  279. package/dist/{chunk-AZV7KNJI.mjs → chunk-YKWIMVGU.mjs} +2 -2
  280. package/dist/{chunk-ZKDKKQCE.mjs → chunk-YOXSXHDE.mjs} +4 -4
  281. package/dist/{chunk-PGQ6FMXS.mjs → chunk-ZO5BRTCW.mjs} +2 -2
  282. package/dist/{chunk-KSSVIFYR.mjs → chunk-ZQGCQ7SA.mjs} +14 -34
  283. package/dist/{chunk-ZTPYUU5C.mjs → chunk-ZRUUUVOO.mjs} +3 -3
  284. package/dist/{index-CY34hxPN.d.mts → index-CinAt5Uo.d.mts} +3 -3
  285. package/dist/{index-CY34hxPN.d.ts → index-CinAt5Uo.d.ts} +3 -3
  286. package/dist/index.d.mts +68 -18
  287. package/dist/index.d.ts +68 -18
  288. package/dist/index.js +965 -825
  289. package/dist/index.mjs +76 -69
  290. package/package.json +3 -2
  291. package/src/components/Accordion/Accordion.tsx +9 -18
  292. package/src/components/AppHeader/AppHeader.tsx +9 -1
  293. package/src/components/ConfirmDialog/ConfirmDialog.tsx +4 -34
  294. package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -2
  295. package/src/components/Image/Image.tsx +50 -0
  296. package/src/components/Image/index.ts +2 -0
  297. package/src/components/ImageUpload/ImageUpload.tsx +1 -1
  298. package/src/components/{ListGroup/ListGroup.tsx → ItemGroup/ItemGroup.tsx} +15 -29
  299. package/src/components/ItemGroup/index.ts +2 -0
  300. package/src/components/ListGroup/index.tsx +20 -0
  301. package/src/components/MenuGroup/index.tsx +20 -0
  302. package/src/components/Pressable/Pressable.tsx +0 -24
  303. package/src/components/ScreenContainer/ScreenContainer.tsx +94 -0
  304. package/src/components/ScreenContainer/index.ts +2 -0
  305. package/src/components/Select/Select.tsx +22 -30
  306. package/src/components/SelectableGrid/SelectableGrid.tsx +51 -19
  307. package/src/components/Separator/Separator.tsx +35 -2
  308. package/src/components/Sheet/Sheet.tsx +3 -34
  309. package/src/components/Tabs/Tabs.tsx +9 -9
  310. package/src/components/Tabs/index.ts +1 -1
  311. package/src/components/Text/Text.tsx +6 -0
  312. package/src/components/VirtualizedList/VirtualizedList.tsx +154 -0
  313. package/src/components/VirtualizedList/index.ts +2 -0
  314. package/src/hooks/useConfirmDialog.ts +2 -11
  315. package/src/hooks/useSheetModal.ts +40 -0
  316. package/src/index.ts +5 -1
  317. package/src/theme/colors.ts +19 -57
  318. package/src/tokens.ts +21 -7
  319. package/src/utils/curatedIcons.ts +9 -18
  320. package/src/utils/haptics.ts +10 -21
  321. package/src/utils/icons.ts +7 -14
  322. package/dist/ListGroup.d.mts +0 -34
  323. package/dist/ListGroup.d.ts +0 -34
  324. package/dist/ListGroup.mjs +0 -4
  325. package/dist/MenuGroup.d.mts +0 -34
  326. package/dist/MenuGroup.d.ts +0 -34
  327. package/dist/MenuGroup.js +0 -106
  328. package/dist/MenuGroup.mjs +0 -4
  329. package/dist/chunk-ARONDO7M.mjs +0 -40
  330. package/dist/chunk-EW2FIDSM.mjs +0 -29
  331. package/dist/chunk-S2VGME7X.mjs +0 -82
  332. package/src/components/ListGroup/index.ts +0 -1
  333. package/src/components/MenuGroup/MenuGroup.tsx +0 -145
  334. package/src/components/MenuGroup/index.ts +0 -1
@@ -0,0 +1,94 @@
1
+ import React from 'react'
2
+ import { View, ScrollView, KeyboardAvoidingView, Platform, StyleSheet, ViewStyle } from 'react-native'
3
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
4
+ import { useTheme } from '../../theme'
5
+ import { BREAKPOINTS } from '../../tokens'
6
+
7
+ export interface ScreenContainerProps {
8
+ children: React.ReactNode
9
+ /** Maximum width in points. Defaults to BREAKPOINTS.wide (700). Set to 0 to disable. */
10
+ maxWidth?: number
11
+ /** Enable scroll. Default false. */
12
+ scroll?: boolean
13
+ /** Background color. Defaults to theme `background`. */
14
+ backgroundColor?: string
15
+ /** Padding top override. Defaults to safe area top. */
16
+ paddingTop?: number
17
+ /** Padding bottom override. Defaults to safe area bottom. */
18
+ paddingBottom?: number
19
+ /** Horizontal padding. Defaults to 0. */
20
+ paddingHorizontal?: number
21
+ /** Enable keyboard avoidance. Default false. */
22
+ keyboard?: boolean
23
+ style?: ViewStyle
24
+ }
25
+
26
+ export function ScreenContainer({
27
+ children,
28
+ maxWidth = BREAKPOINTS.wide,
29
+ scroll = false,
30
+ backgroundColor,
31
+ paddingTop,
32
+ paddingBottom,
33
+ paddingHorizontal,
34
+ keyboard = false,
35
+ style: _style,
36
+ }: ScreenContainerProps) {
37
+ const insets = useSafeAreaInsets()
38
+ const { colors } = useTheme()
39
+
40
+ const wrapperStyle: ViewStyle = {
41
+ flex: 1,
42
+ backgroundColor: backgroundColor ?? colors.background,
43
+ paddingTop: paddingTop ?? insets.top,
44
+ paddingBottom: paddingBottom ?? insets.bottom,
45
+ paddingHorizontal: paddingHorizontal ?? 0,
46
+ ..._style,
47
+ }
48
+
49
+ const inner = (
50
+ <View style={[styles.inner, { maxWidth: maxWidth || undefined }]}>
51
+ {children}
52
+ </View>
53
+ )
54
+
55
+ const content = scroll ? (
56
+ <ScrollView
57
+ style={styles.scroll}
58
+ contentContainerStyle={styles.scrollContent}
59
+ keyboardShouldPersistTaps="handled"
60
+ >
61
+ {inner}
62
+ </ScrollView>
63
+ ) : (
64
+ inner
65
+ )
66
+
67
+ if (keyboard) {
68
+ return (
69
+ <KeyboardAvoidingView
70
+ style={wrapperStyle}
71
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}
72
+ keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
73
+ >
74
+ {content}
75
+ </KeyboardAvoidingView>
76
+ )
77
+ }
78
+
79
+ return <View style={wrapperStyle}>{content}</View>
80
+ }
81
+
82
+ const styles = StyleSheet.create({
83
+ scroll: {
84
+ flex: 1,
85
+ },
86
+ scrollContent: {
87
+ flexGrow: 1,
88
+ },
89
+ inner: {
90
+ flex: 1,
91
+ alignSelf: 'center',
92
+ width: '100%',
93
+ },
94
+ })
@@ -0,0 +1,2 @@
1
+ export { ScreenContainer } from './ScreenContainer'
2
+ export type { ScreenContainerProps } from './ScreenContainer'
@@ -51,6 +51,25 @@ export function Select({
51
51
 
52
52
  const selected = options.find((o) => o.value === value)
53
53
 
54
+ function renderPickerItems(includePlaceholder: boolean, itemColor?: string, disabledColor?: string) {
55
+ return (
56
+ <>
57
+ {includePlaceholder ? (
58
+ <Picker.Item label={placeholder} value="" enabled={false} color={disabledColor} />
59
+ ) : null}
60
+ {options.map((o) => (
61
+ <Picker.Item
62
+ key={o.value}
63
+ label={o.label}
64
+ value={o.value}
65
+ enabled={!o.disabled}
66
+ color={o.disabled ? disabledColor : itemColor}
67
+ />
68
+ ))}
69
+ </>
70
+ )
71
+ }
72
+
54
73
  const handleOpen = () => {
55
74
  if (disabled) return
56
75
  hapticSelection()
@@ -142,18 +161,7 @@ export function Select({
142
161
  onValueChange={(val) => setPendingValue(val as string)}
143
162
  itemStyle={{ color: colors.foreground }}
144
163
  >
145
- {!value ? (
146
- <Picker.Item label={placeholder} value="" color={colors.foregroundMuted} enabled={false} />
147
- ) : null}
148
- {options.map((o) => (
149
- <Picker.Item
150
- key={o.value}
151
- label={o.label}
152
- value={o.value}
153
- enabled={!o.disabled}
154
- color={o.disabled ? colors.foregroundMuted : colors.foreground}
155
- />
156
- ))}
164
+ {renderPickerItems(!value, colors.foreground, colors.foregroundMuted)}
157
165
  </Picker>
158
166
  </View>
159
167
  </Modal>
@@ -175,15 +183,7 @@ export function Select({
175
183
  prompt={label}
176
184
  style={styles.androidHiddenPicker}
177
185
  >
178
- {!value ? <Picker.Item label={placeholder} value="" enabled={false} /> : null}
179
- {options.map((o) => (
180
- <Picker.Item
181
- key={o.value}
182
- label={o.label}
183
- value={o.value}
184
- enabled={!o.disabled}
185
- />
186
- ))}
186
+ {renderPickerItems(!value)}
187
187
  </Picker>
188
188
  ) : null}
189
189
 
@@ -207,15 +207,7 @@ export function Select({
207
207
  },
208
208
  ]}
209
209
  >
210
- <Picker.Item label={placeholder} value="" enabled={false} />
211
- {options.map((o) => (
212
- <Picker.Item
213
- key={o.value}
214
- label={o.label}
215
- value={o.value}
216
- enabled={!o.disabled}
217
- />
218
- ))}
210
+ {renderPickerItems(true)}
219
211
  </Picker>
220
212
  ) : null}
221
213
 
@@ -1,5 +1,6 @@
1
1
  import React, { useState } from 'react'
2
- import { View, Text, StyleSheet, ViewStyle, ScrollView } from 'react-native'
2
+ import { View, Text, StyleSheet, ViewStyle, ScrollView, ImageSourcePropType } from 'react-native'
3
+ import { Image } from 'expo-image'
3
4
  import { useTheme } from '../../theme'
4
5
  import { Icon } from '../../utils/icons'
5
6
  import { PressableChip } from '../../utils/pressable'
@@ -12,6 +13,7 @@ export interface SelectableGridItem<T extends string | number = string> {
12
13
  label?: string
13
14
  iconName?: string
14
15
  icon?: React.ReactNode
16
+ imageUrl?: ImageSourcePropType
15
17
  disabled?: boolean
16
18
  }
17
19
 
@@ -40,10 +42,13 @@ interface CellProps<T extends string | number> {
40
42
 
41
43
  function Cell<T extends string | number>({ item, selected, width, onPress }: CellProps<T>) {
42
44
  const { colors } = useTheme()
45
+ const hasImage = !!item.imageUrl
43
46
 
44
47
  const iconColor = selected ? colors.primary : colors.foregroundSubtle
45
48
  const iconNode = item.icon ?? (item.iconName ? <Icon name={item.iconName} size={ms(24)} color={iconColor} /> : null)
46
49
 
50
+ const imageSize = width - 4
51
+
47
52
  return (
48
53
  <PressableChip
49
54
  onPress={onPress}
@@ -54,25 +59,40 @@ function Cell<T extends string | number>({ item, selected, width, onPress }: Cel
54
59
  accessibilityRole="button"
55
60
  accessibilityState={{ selected, disabled: item.disabled }}
56
61
  accessibilityLabel={item.label ?? String(item.value)}
57
- style={[
58
- { width },
59
- styles.cell,
60
- {
61
- borderColor: selected ? colors.primary : 'transparent',
62
- },
63
- item.disabled && styles.cellDisabled,
64
- ]}
65
62
  >
66
- {iconNode}
67
- {item.label ? (
68
- <Text
69
- style={[styles.label, { color: selected ? colors.primary : colors.foreground }]}
70
- numberOfLines={1}
71
- allowFontScaling={true}
72
- >
73
- {item.label}
74
- </Text>
75
- ) : null}
63
+ <View
64
+ style={[
65
+ { width },
66
+ styles.cell,
67
+ hasImage && styles.cellImage,
68
+ {
69
+ borderColor: selected ? colors.primary : 'transparent',
70
+ },
71
+ item.disabled && styles.cellDisabled,
72
+ ]}
73
+ >
74
+ {hasImage ? (
75
+ <Image
76
+ source={item.imageUrl}
77
+ style={{ width: imageSize, height: imageSize }}
78
+ contentFit="cover"
79
+ />
80
+ ) : null}
81
+ {iconNode && !hasImage ? iconNode : null}
82
+ {item.label ? (
83
+ <Text
84
+ style={[
85
+ styles.label,
86
+ hasImage && styles.labelImage,
87
+ { color: selected ? colors.primary : colors.foreground },
88
+ ]}
89
+ numberOfLines={2}
90
+ allowFontScaling={true}
91
+ >
92
+ {item.label}
93
+ </Text>
94
+ ) : null}
95
+ </View>
76
96
  </PressableChip>
77
97
  )
78
98
  }
@@ -158,6 +178,13 @@ const styles = StyleSheet.create({
158
178
  paddingHorizontal: s(8),
159
179
  paddingVertical: vs(10),
160
180
  },
181
+ cellImage: {
182
+ padding: 0,
183
+ gap: 0,
184
+ overflow: 'hidden',
185
+ alignItems: 'stretch',
186
+ justifyContent: 'flex-start',
187
+ },
161
188
  cellDisabled: {
162
189
  opacity: 0.4,
163
190
  },
@@ -167,4 +194,9 @@ const styles = StyleSheet.create({
167
194
  lineHeight: mvs(14),
168
195
  textAlign: 'center',
169
196
  },
197
+ labelImage: {
198
+ paddingHorizontal: s(6),
199
+ paddingBottom: vs(6),
200
+ paddingTop: vs(3),
201
+ },
170
202
  })
@@ -1,15 +1,33 @@
1
1
  import React from 'react'
2
- import { View, StyleSheet, ViewStyle } from 'react-native'
2
+ import { View, Text, StyleSheet, ViewStyle } from 'react-native'
3
3
  import { useTheme } from '../../theme'
4
+ import { s } from '../../utils/scaling'
4
5
 
5
6
  export interface SeparatorProps {
6
7
  orientation?: 'horizontal' | 'vertical'
8
+ /** Label shown in the center of a horizontal separator — renders as "── label ──". */
9
+ label?: string
7
10
  style?: ViewStyle
8
11
  }
9
12
 
10
- export function Separator({ orientation = 'horizontal', style }: SeparatorProps) {
13
+ export function Separator({ orientation = 'horizontal', label, style }: SeparatorProps) {
11
14
  const { colors } = useTheme()
12
15
 
16
+ if (label && orientation !== 'vertical') {
17
+ return (
18
+ <View style={[styles.row, style]} accessibilityRole="none">
19
+ <View style={[styles.line, { backgroundColor: colors.separator }]} />
20
+ <Text
21
+ style={[styles.label, { color: colors.foregroundMuted }]}
22
+ allowFontScaling={true}
23
+ >
24
+ {label}
25
+ </Text>
26
+ <View style={[styles.line, { backgroundColor: colors.separator }]} />
27
+ </View>
28
+ )
29
+ }
30
+
13
31
  return (
14
32
  <View
15
33
  style={[
@@ -30,4 +48,19 @@ const styles = StyleSheet.create({
30
48
  width: 1,
31
49
  height: '100%',
32
50
  },
51
+ row: {
52
+ flexDirection: 'row',
53
+ alignItems: 'center',
54
+ gap: s(12),
55
+ },
56
+ line: {
57
+ flex: 1,
58
+ height: 1,
59
+ },
60
+ label: {
61
+ fontFamily: 'Sohne-Medium',
62
+ fontSize: 13,
63
+ letterSpacing: 0.32,
64
+ textTransform: 'uppercase',
65
+ },
33
66
  })
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useRef, useState, useId } from 'react'
1
+ import React, { useCallback, useId } from 'react'
2
2
  import { View, Text, TouchableOpacity, StyleSheet, ViewStyle } from 'react-native'
3
3
  import {
4
4
  BottomSheetModal,
@@ -13,8 +13,8 @@ import {
13
13
  } from '@gorhom/bottom-sheet'
14
14
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
15
15
  import { AntDesign } from '@expo/vector-icons'
16
- import { impactMedium } from '../../utils/haptics'
17
16
  import { useTheme } from '../../theme'
17
+ import { useSheetModal } from '../../hooks/useSheetModal'
18
18
  import { s, vs, ms, mvs } from '../../utils/scaling'
19
19
 
20
20
  export { BottomSheetModalProvider }
@@ -95,41 +95,10 @@ export function Sheet({
95
95
  footer,
96
96
  snapPoints,
97
97
  }: SheetProps) {
98
- type SheetState = 'idle' | 'presenting' | 'presented' | 'dismissing'
99
-
100
98
  const { colors } = useTheme()
101
99
  const insets = useSafeAreaInsets()
102
- const ref = useRef<BottomSheetModal>(null)
103
- const sheetState = useRef<SheetState>('idle')
104
- const onCloseRef = useRef(onClose)
100
+ const { ref, handleDismiss } = useSheetModal(open, onClose)
105
101
  const name = useId()
106
- const [tick, setTick] = useState(0)
107
-
108
- useEffect(() => {
109
- onCloseRef.current = onClose
110
- }, [onClose])
111
-
112
- const handleDismiss = useCallback(() => {
113
- sheetState.current = 'idle'
114
- onCloseRef.current?.()
115
- setTick((t) => t + 1)
116
- }, [])
117
-
118
- useEffect(() => {
119
- if (open) {
120
- if (sheetState.current === 'idle') {
121
- sheetState.current = 'presenting'
122
- impactMedium()
123
- ref.current?.present()
124
- sheetState.current = 'presented'
125
- }
126
- } else {
127
- if (sheetState.current === 'presented' || sheetState.current === 'presenting') {
128
- sheetState.current = 'dismissing'
129
- ref.current?.dismiss()
130
- }
131
- }
132
- }, [open, tick])
133
102
 
134
103
  const renderBackdrop = useCallback(
135
104
  (props: BottomSheetBackdropProps) => (
@@ -11,19 +11,19 @@ import { s, vs, ms } from '../../utils/scaling'
11
11
  import { SPRINGS } from '../../utils/animations'
12
12
  import { PressableTab } from '../../utils/pressable'
13
13
 
14
- export interface TabItem {
14
+ export interface TabItem<T extends string = string> {
15
15
  label: string
16
- value: string
16
+ value: T
17
17
  icon?: React.ReactNode | ((active: boolean) => React.ReactNode)
18
18
  }
19
19
 
20
20
  export type TabsVariant = 'pill' | 'underline'
21
21
 
22
- export interface TabsProps {
23
- tabs: TabItem[]
22
+ export interface TabsProps<T extends string = string> {
23
+ tabs: TabItem<T>[]
24
24
  variant?: TabsVariant
25
- value?: string
26
- onValueChange?: (value: string) => void
25
+ value?: T
26
+ onValueChange?: (value: T) => void
27
27
  children?: React.ReactNode
28
28
  style?: ViewStyle
29
29
  }
@@ -89,8 +89,8 @@ function TabTrigger({
89
89
  )
90
90
  }
91
91
 
92
- export function Tabs({ tabs, variant = 'pill', value, onValueChange, children, style }: TabsProps) {
93
- const [internal, setInternal] = useState(tabs[0]?.value ?? '')
92
+ export function Tabs<T extends string = string>({ tabs, variant = 'pill', value, onValueChange, children, style }: TabsProps<T>) {
93
+ const [internal, setInternal] = useState<T>((tabs[0]?.value ?? '') as T)
94
94
  const { colors } = useTheme()
95
95
  const active = value ?? internal
96
96
 
@@ -117,7 +117,7 @@ export function Tabs({ tabs, variant = 'pill', value, onValueChange, children, s
117
117
  if (initialised.current) animatePill(active, true)
118
118
  }, [active, animatePill])
119
119
 
120
- const handlePress = (v: string) => {
120
+ const handlePress = (v: T) => {
121
121
  hapticSelection()
122
122
  if (!value) setInternal(v)
123
123
  onValueChange?.(v)
@@ -1,2 +1,2 @@
1
1
  export { Tabs, TabsContent } from './Tabs'
2
- export type { TabsProps, TabsContentProps, TabItem } from './Tabs'
2
+ export type { TabsProps, TabsContentProps, TabItem, TabsVariant } from './Tabs'
@@ -24,6 +24,8 @@ export type TextVariant =
24
24
  | 'uppercase-tag'
25
25
  | 'button-lg'
26
26
  | 'button-sm'
27
+ | 'code-sm'
28
+ | 'code-md'
27
29
 
28
30
  export interface TextProps extends RNTextProps {
29
31
  variant?: TextVariant
@@ -50,6 +52,8 @@ const variantStyles: Record<TextVariant, TextStyle> = {
50
52
  'uppercase-tag':{ ...TYPOGRAPHY['uppercase-tag'],fontSize: ms(TYPOGRAPHY['uppercase-tag'].fontSize),lineHeight: mvs(TYPOGRAPHY['uppercase-tag'].lineHeight) },
51
53
  'button-lg': { ...TYPOGRAPHY['button-lg'], fontSize: ms(TYPOGRAPHY['button-lg'].fontSize), lineHeight: mvs(TYPOGRAPHY['button-lg'].lineHeight) },
52
54
  'button-sm': { ...TYPOGRAPHY['button-sm'], fontSize: ms(TYPOGRAPHY['button-sm'].fontSize), lineHeight: mvs(TYPOGRAPHY['button-sm'].lineHeight) },
55
+ 'code-sm': { ...TYPOGRAPHY['code-sm'], fontSize: ms(TYPOGRAPHY['code-sm'].fontSize), lineHeight: mvs(TYPOGRAPHY['code-sm'].lineHeight) },
56
+ 'code-md': { ...TYPOGRAPHY['code-md'], fontSize: ms(TYPOGRAPHY['code-md'].fontSize), lineHeight: mvs(TYPOGRAPHY['code-md'].lineHeight) },
53
57
  }
54
58
 
55
59
  // Default color by variant — hierarchy matches Airbnb ink/body/muted pattern
@@ -70,6 +74,8 @@ const defaultColorVariant: Partial<Record<TextVariant, 'foreground' | 'foregroun
70
74
  'uppercase-tag':'foregroundMuted',
71
75
  'button-lg': 'foreground',
72
76
  'button-sm': 'foreground',
77
+ 'code-sm': 'foreground',
78
+ 'code-md': 'foreground',
73
79
  }
74
80
 
75
81
  let fontWarned = false
@@ -0,0 +1,154 @@
1
+ import React, { useCallback } from 'react'
2
+ import {
3
+ SectionList,
4
+ type SectionListProps,
5
+ type SectionListRenderItemInfo,
6
+ RefreshControl,
7
+ View,
8
+ Text,
9
+ StyleSheet,
10
+ type ViewStyle,
11
+ } from 'react-native'
12
+ import { useTheme } from '../../theme'
13
+ import { s, vs } from '../../utils/scaling'
14
+
15
+ export interface VirtualizedListSection<T> {
16
+ title?: string
17
+ data: readonly T[]
18
+ }
19
+
20
+ export interface VirtualizedListProps<T>
21
+ extends Omit<SectionListProps<T>, 'sections' | 'renderItem' | 'ListEmptyComponent'> {
22
+ sections: VirtualizedListSection<T>[]
23
+ renderItem: (info: SectionListRenderItemInfo<T>) => React.ReactElement | null
24
+ /** Empty state shown when no data. */
25
+ emptyTitle?: string
26
+ /** Empty state description. */
27
+ emptyDescription?: string
28
+ /** Custom empty state component. Overrides emptyTitle/emptyDescription. */
29
+ emptyComponent?: React.ReactNode
30
+ /** Enable pull-to-refresh. */
31
+ refreshing?: boolean
32
+ onRefresh?: () => void
33
+ /** Sticky section headers. Default true. */
34
+ stickyHeaders?: boolean
35
+ /** Override section header color. Defaults to foregroundMuted. */
36
+ headerColor?: string
37
+ style?: ViewStyle
38
+ }
39
+
40
+ export function VirtualizedList<T>({
41
+ sections,
42
+ renderItem,
43
+ emptyTitle = 'Sin contenido',
44
+ emptyDescription,
45
+ emptyComponent,
46
+ refreshing = false,
47
+ onRefresh,
48
+ stickyHeaders = true,
49
+ headerColor,
50
+ style,
51
+ ...props
52
+ }: VirtualizedListProps<T>) {
53
+ const { colors } = useTheme()
54
+
55
+ const renderSectionHeader = useCallback(
56
+ ({ section }: { section: VirtualizedListSection<T> }) => {
57
+ if (!section.title) return null
58
+ return (
59
+ <View style={[styles.sectionHeader, { backgroundColor: colors.background }]}>
60
+ <Text
61
+ style={[styles.sectionHeaderText, { color: headerColor ?? colors.foregroundMuted }]}
62
+ allowFontScaling={true}
63
+ >
64
+ {section.title}
65
+ </Text>
66
+ </View>
67
+ )
68
+ },
69
+ [colors.background, colors.foregroundMuted, headerColor],
70
+ )
71
+
72
+ const keyExtractor = useCallback(
73
+ (_item: T, index: number) => String(index),
74
+ [],
75
+ )
76
+
77
+ const flatSections = sections.filter((s) => s.data.length > 0)
78
+ const hasData = flatSections.length > 0
79
+
80
+ if (!hasData) {
81
+ if (emptyComponent) return <View style={style}>{emptyComponent}</View>
82
+ return (
83
+ <View style={[styles.empty, style]}>
84
+ <Text
85
+ style={[styles.emptyTitle, { color: colors.foregroundMuted }]}
86
+ allowFontScaling={true}
87
+ >
88
+ {emptyTitle}
89
+ </Text>
90
+ {emptyDescription ? (
91
+ <Text
92
+ style={[styles.emptyDescription, { color: colors.foregroundMuted }]}
93
+ allowFontScaling={true}
94
+ >
95
+ {emptyDescription}
96
+ </Text>
97
+ ) : null}
98
+ </View>
99
+ )
100
+ }
101
+
102
+ return (
103
+ <SectionList
104
+ sections={flatSections}
105
+ renderItem={renderItem}
106
+ renderSectionHeader={renderSectionHeader}
107
+ keyExtractor={keyExtractor}
108
+ stickySectionHeadersEnabled={stickyHeaders}
109
+ refreshControl={
110
+ onRefresh ? (
111
+ <RefreshControl
112
+ refreshing={refreshing}
113
+ onRefresh={onRefresh}
114
+ tintColor={colors.primary}
115
+ />
116
+ ) : undefined
117
+ }
118
+ style={style}
119
+ showsVerticalScrollIndicator={false}
120
+ {...props}
121
+ />
122
+ )
123
+ }
124
+
125
+ const styles = StyleSheet.create({
126
+ sectionHeader: {
127
+ paddingHorizontal: s(16),
128
+ paddingTop: vs(16),
129
+ paddingBottom: vs(6),
130
+ },
131
+ sectionHeaderText: {
132
+ fontFamily: 'Sohne-SemiBold',
133
+ fontSize: 13,
134
+ letterSpacing: 0.32,
135
+ textTransform: 'uppercase',
136
+ },
137
+ empty: {
138
+ flex: 1,
139
+ alignItems: 'center',
140
+ justifyContent: 'center',
141
+ paddingHorizontal: s(32),
142
+ gap: vs(8),
143
+ },
144
+ emptyTitle: {
145
+ fontFamily: 'Sohne-Medium',
146
+ fontSize: 16,
147
+ textAlign: 'center',
148
+ },
149
+ emptyDescription: {
150
+ fontFamily: 'Sohne-Regular',
151
+ fontSize: 14,
152
+ textAlign: 'center',
153
+ },
154
+ })
@@ -0,0 +1,2 @@
1
+ export { VirtualizedList } from './VirtualizedList'
2
+ export type { VirtualizedListProps, VirtualizedListSection } from './VirtualizedList'
@@ -16,7 +16,6 @@ export interface UseConfirmDialogResult {
16
16
  export function useConfirmDialog(options: UseConfirmDialogOptions): UseConfirmDialogResult {
17
17
  const [visible, setVisible] = useState(false)
18
18
  const [loading, setLoading] = useState(false)
19
- const mountedRef = useRef(true)
20
19
  const onConfirmRef = useRef(options.onConfirm)
21
20
  const onCancelRef = useRef(options.onCancel)
22
21
 
@@ -25,12 +24,6 @@ export function useConfirmDialog(options: UseConfirmDialogOptions): UseConfirmDi
25
24
  onCancelRef.current = options.onCancel
26
25
  })
27
26
 
28
- useEffect(() => {
29
- return () => {
30
- mountedRef.current = false
31
- }
32
- }, [])
33
-
34
27
  const open = useCallback(() => setVisible(true), [])
35
28
 
36
29
  const handleConfirm = useCallback(async () => {
@@ -40,10 +33,8 @@ export function useConfirmDialog(options: UseConfirmDialogOptions): UseConfirmDi
40
33
  } catch {
41
34
  /* consumer handles error in onConfirm */
42
35
  } finally {
43
- if (mountedRef.current) {
44
- setLoading(false)
45
- setVisible(false)
46
- }
36
+ setLoading(false)
37
+ setVisible(false)
47
38
  }
48
39
  }, [])
49
40