@retray-dev/ui-kit 12.1.0 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/COMPONENTS.md +183 -147
  2. package/CONSUMER.md +2 -2
  3. package/DESIGN.md +2 -2
  4. package/README.md +13 -8
  5. package/dist/Accordion.d.mts +6 -0
  6. package/dist/Accordion.d.ts +6 -0
  7. package/dist/Accordion.js +62 -208
  8. package/dist/Accordion.mjs +6 -5
  9. package/dist/AlertBanner.js +29 -151
  10. package/dist/AlertBanner.mjs +3 -3
  11. package/dist/AppHeader.js +37 -233
  12. package/dist/AppHeader.mjs +6 -7
  13. package/dist/Avatar.d.mts +17 -1
  14. package/dist/Avatar.d.ts +17 -1
  15. package/dist/Avatar.js +80 -113
  16. package/dist/Avatar.mjs +2 -2
  17. package/dist/Badge.js +24 -147
  18. package/dist/Badge.mjs +3 -3
  19. package/dist/Button.js +86 -274
  20. package/dist/Button.mjs +6 -6
  21. package/dist/Card.js +15 -198
  22. package/dist/Card.mjs +4 -5
  23. package/dist/CategoryStrip.d.mts +0 -5
  24. package/dist/CategoryStrip.d.ts +0 -5
  25. package/dist/CategoryStrip.js +47 -263
  26. package/dist/CategoryStrip.mjs +6 -6
  27. package/dist/Checkbox.js +15 -198
  28. package/dist/Checkbox.mjs +5 -5
  29. package/dist/Chip.js +44 -234
  30. package/dist/Chip.mjs +7 -6
  31. package/dist/ConfirmDialog.js +100 -296
  32. package/dist/ConfirmDialog.mjs +7 -7
  33. package/dist/CurrencyDisplay.js +1 -112
  34. package/dist/CurrencyDisplay.mjs +2 -2
  35. package/dist/CurrencyInput.js +35 -160
  36. package/dist/CurrencyInput.mjs +5 -5
  37. package/dist/DetailRow.js +25 -148
  38. package/dist/DetailRow.mjs +3 -3
  39. package/dist/EmptyState.js +87 -275
  40. package/dist/EmptyState.mjs +7 -7
  41. package/dist/ErrorBoundary.js +32 -197
  42. package/dist/ErrorBoundary.mjs +4 -4
  43. package/dist/Form.js +1 -112
  44. package/dist/Form.mjs +2 -2
  45. package/dist/HolographicCard.d.mts +0 -28
  46. package/dist/HolographicCard.d.ts +0 -28
  47. package/dist/HolographicCard.js +20 -130
  48. package/dist/HolographicCard.mjs +9 -32
  49. package/dist/IconButton.js +36 -232
  50. package/dist/IconButton.mjs +5 -6
  51. package/dist/IconPicker.js +222 -927
  52. package/dist/IconPicker.mjs +5 -5
  53. package/dist/ImageUpload.d.mts +5 -1
  54. package/dist/ImageUpload.d.ts +5 -1
  55. package/dist/ImageUpload.js +32 -215
  56. package/dist/ImageUpload.mjs +5 -6
  57. package/dist/ImageViewer.js +75 -264
  58. package/dist/ImageViewer.mjs +8 -8
  59. package/dist/Input.d.mts +1 -1
  60. package/dist/Input.d.ts +1 -1
  61. package/dist/Input.js +35 -160
  62. package/dist/Input.mjs +4 -4
  63. package/dist/LabelValue.js +24 -147
  64. package/dist/LabelValue.mjs +3 -3
  65. package/dist/ListGroup.js +1 -112
  66. package/dist/ListGroup.mjs +2 -2
  67. package/dist/ListItem.js +38 -233
  68. package/dist/ListItem.mjs +5 -6
  69. package/dist/MediaCard.d.mts +0 -14
  70. package/dist/MediaCard.d.ts +0 -14
  71. package/dist/MediaCard.js +69 -313
  72. package/dist/MediaCard.mjs +5 -6
  73. package/dist/MenuGroup.js +1 -112
  74. package/dist/MenuGroup.mjs +2 -2
  75. package/dist/MenuItem.js +36 -232
  76. package/dist/MenuItem.mjs +5 -6
  77. package/dist/MonthPicker.js +8 -161
  78. package/dist/MonthPicker.mjs +3 -3
  79. package/dist/NumberStepper.js +40 -236
  80. package/dist/NumberStepper.mjs +5 -6
  81. package/dist/PagerDots.d.mts +1 -1
  82. package/dist/PagerDots.d.ts +1 -1
  83. package/dist/PagerDots.js +69 -222
  84. package/dist/PagerDots.mjs +6 -5
  85. package/dist/Pressable.js +14 -85
  86. package/dist/Pressable.mjs +4 -4
  87. package/dist/PricingCard.js +94 -279
  88. package/dist/PricingCard.mjs +8 -8
  89. package/dist/Progress.js +3 -121
  90. package/dist/Progress.mjs +3 -3
  91. package/dist/RadioGroup.js +52 -263
  92. package/dist/RadioGroup.mjs +5 -5
  93. package/dist/RetrayProvider.d.mts +1 -1
  94. package/dist/RetrayProvider.d.ts +1 -1
  95. package/dist/RetrayProvider.js +5 -6
  96. package/dist/RetrayProvider.mjs +3 -3
  97. package/dist/Select.d.mts +2 -1
  98. package/dist/Select.d.ts +2 -1
  99. package/dist/Select.js +24 -230
  100. package/dist/Select.mjs +4 -5
  101. package/dist/SelectableCard.d.mts +27 -0
  102. package/dist/SelectableCard.d.ts +27 -0
  103. package/dist/SelectableCard.js +335 -0
  104. package/dist/SelectableCard.mjs +8 -0
  105. package/dist/SelectableGrid.d.mts +0 -21
  106. package/dist/SelectableGrid.d.ts +0 -21
  107. package/dist/SelectableGrid.js +49 -269
  108. package/dist/SelectableGrid.mjs +5 -6
  109. package/dist/Separator.js +1 -112
  110. package/dist/Separator.mjs +2 -2
  111. package/dist/Sheet.js +16 -163
  112. package/dist/Sheet.mjs +3 -3
  113. package/dist/SheetSelect.js +39 -234
  114. package/dist/SheetSelect.mjs +6 -6
  115. package/dist/Skeleton.d.mts +3 -1
  116. package/dist/Skeleton.d.ts +3 -1
  117. package/dist/Skeleton.js +7 -124
  118. package/dist/Skeleton.mjs +3 -3
  119. package/dist/Slider.js +6 -159
  120. package/dist/Slider.mjs +3 -3
  121. package/dist/Spinner.js +3 -114
  122. package/dist/Spinner.mjs +2 -2
  123. package/dist/Stats.d.mts +4 -1
  124. package/dist/Stats.d.ts +4 -1
  125. package/dist/Stats.js +60 -234
  126. package/dist/Stats.mjs +5 -6
  127. package/dist/Switch.js +24 -173
  128. package/dist/Switch.mjs +5 -4
  129. package/dist/TabBar.js +43 -198
  130. package/dist/TabBar.mjs +5 -4
  131. package/dist/Tabs.js +15 -197
  132. package/dist/Tabs.mjs +5 -5
  133. package/dist/Text.js +9 -128
  134. package/dist/Text.mjs +2 -2
  135. package/dist/Textarea.d.mts +2 -1
  136. package/dist/Textarea.d.ts +2 -1
  137. package/dist/Textarea.js +71 -217
  138. package/dist/Textarea.mjs +4 -4
  139. package/dist/Toast.js +1 -112
  140. package/dist/Toast.mjs +2 -2
  141. package/dist/Toggle.js +39 -234
  142. package/dist/Toggle.mjs +6 -6
  143. package/dist/{chunk-FFTYLPSB.mjs → chunk-2QOHHBJC.mjs} +13 -7
  144. package/dist/{chunk-BCWEHE34.mjs → chunk-2VIDP72N.mjs} +3 -3
  145. package/dist/{chunk-PGERH3P7.mjs → chunk-4NQFTHN3.mjs} +13 -7
  146. package/dist/{chunk-3N2M3WZL.mjs → chunk-4ZO5PTKF.mjs} +4 -4
  147. package/dist/{chunk-MYZ2EDYU.mjs → chunk-5MYNAAFE.mjs} +13 -17
  148. package/dist/{chunk-E7NEHHXV.mjs → chunk-62BBSSUF.mjs} +3 -3
  149. package/dist/{chunk-ISY26JQJ.mjs → chunk-6CR4S6W2.mjs} +3 -3
  150. package/dist/{chunk-FUVYSVGR.mjs → chunk-6QLBHUEG.mjs} +8 -7
  151. package/dist/chunk-ARONDO7M.mjs +40 -0
  152. package/dist/{chunk-3UYAZ7I4.mjs → chunk-AZV7KNJI.mjs} +3 -3
  153. package/dist/{chunk-HLMPMUK2.mjs → chunk-BTUW5LSG.mjs} +11 -8
  154. package/dist/chunk-BULKGOIZ.mjs +235 -0
  155. package/dist/{chunk-265G6A46.mjs → chunk-CBIZLRYH.mjs} +29 -12
  156. package/dist/chunk-CM2DG4MR.mjs +142 -0
  157. package/dist/{chunk-2I2AYECM.mjs → chunk-DBHSUUKU.mjs} +2 -2
  158. package/dist/{chunk-P64WHW4A.mjs → chunk-DE25XTVQ.mjs} +3 -3
  159. package/dist/{chunk-DI7CBDL6.mjs → chunk-E4EQSCKR.mjs} +5 -5
  160. package/dist/{chunk-357YO24D.mjs → chunk-EHGBHFMH.mjs} +9 -17
  161. package/dist/{chunk-GK4VRMNE.mjs → chunk-EROPDCB5.mjs} +24 -27
  162. package/dist/{chunk-XBAGGKLW.mjs → chunk-ERWJPVX7.mjs} +2 -2
  163. package/dist/{chunk-LRM4AVYY.mjs → chunk-ESQDPO5E.mjs} +7 -7
  164. package/dist/{chunk-EFLFRAHD.mjs → chunk-EW2FIDSM.mjs} +1 -1
  165. package/dist/{chunk-7HSILTC4.mjs → chunk-FTTI6T5Q.mjs} +4 -4
  166. package/dist/{chunk-X26S5EVZ.mjs → chunk-HUSSF6TF.mjs} +1 -1
  167. package/dist/chunk-IFYMBOEN.mjs +14 -0
  168. package/dist/{chunk-S3KJCPEJ.mjs → chunk-IGU223UM.mjs} +80 -4
  169. package/dist/chunk-IJCMPVW5.mjs +121 -0
  170. package/dist/{chunk-I4V5XZPS.mjs → chunk-ITG4JQM3.mjs} +4 -4
  171. package/dist/{chunk-F4V6XLP4.mjs → chunk-K3QX2M26.mjs} +11 -8
  172. package/dist/{chunk-V6NFJXKO.mjs → chunk-K7TKID3V.mjs} +8 -7
  173. package/dist/{chunk-ZHMSAYLT.mjs → chunk-KAGADD2O.mjs} +4 -4
  174. package/dist/{chunk-3GEYJ7I5.mjs → chunk-KC5QDYGZ.mjs} +4 -4
  175. package/dist/{chunk-HJ46DTJE.mjs → chunk-KPTY7UYQ.mjs} +1 -1
  176. package/dist/{chunk-EMUWGDWC.mjs → chunk-KSSVIFYR.mjs} +11 -12
  177. package/dist/chunk-L3YKPTJQ.mjs +119 -0
  178. package/dist/chunk-M53LC4Q7.mjs +35 -0
  179. package/dist/{chunk-NXI4YDZ2.mjs → chunk-MP7GLMIR.mjs} +17 -25
  180. package/dist/chunk-MZ6WRTD2.mjs +40 -0
  181. package/dist/chunk-NGEN2EES.mjs +581 -0
  182. package/dist/{chunk-JULSIZDM.mjs → chunk-OBV72JD4.mjs} +1 -1
  183. package/dist/{chunk-2A2LEFZG.mjs → chunk-PGQ6FMXS.mjs} +6 -5
  184. package/dist/{chunk-BQZE3HAW.mjs → chunk-PI6RULJX.mjs} +1 -1
  185. package/dist/{chunk-FA2KMTH5.mjs → chunk-RA6SAAFE.mjs} +9 -8
  186. package/dist/{chunk-FVTVCJAH.mjs → chunk-RRKM4MKB.mjs} +7 -7
  187. package/dist/{chunk-AKM4EPOT.mjs → chunk-S2VGME7X.mjs} +1 -1
  188. package/dist/{chunk-OULVKTWL.mjs → chunk-S44XWTTC.mjs} +35 -25
  189. package/dist/{chunk-QSFV2P7O.mjs → chunk-SZEKQAOY.mjs} +1 -1
  190. package/dist/{chunk-N4ZPVCJH.mjs → chunk-TETMEKZE.mjs} +9 -9
  191. package/dist/{chunk-2CBQKU7H.mjs → chunk-TMH263OK.mjs} +5 -4
  192. package/dist/{chunk-D3Y2T42P.mjs → chunk-U6DEBYU5.mjs} +10 -9
  193. package/dist/{chunk-4WFMPFZB.mjs → chunk-UOKFSFNJ.mjs} +2 -2
  194. package/dist/{chunk-WOEWGSTU.mjs → chunk-URIH43IJ.mjs} +13 -21
  195. package/dist/{chunk-JCZQOY4O.mjs → chunk-V2ZB2XNS.mjs} +16 -10
  196. package/dist/{chunk-P73V2EKS.mjs → chunk-WIPEDNSD.mjs} +7 -7
  197. package/dist/{chunk-BOVUP27T.mjs → chunk-XCIG6HT2.mjs} +6 -5
  198. package/dist/chunk-Y6YS33GM.mjs +131 -0
  199. package/dist/{chunk-5OLNXP3S.mjs → chunk-ZKDKKQCE.mjs} +29 -7
  200. package/dist/{chunk-DF6DU42P.mjs → chunk-ZTPYUU5C.mjs} +5 -5
  201. package/dist/{index-wt-orHUi.d.ts → index-CY34hxPN.d.mts} +1 -0
  202. package/dist/{index-wt-orHUi.d.mts → index-CY34hxPN.d.ts} +1 -0
  203. package/dist/index.d.mts +15 -74
  204. package/dist/index.d.ts +15 -74
  205. package/dist/index.js +1055 -1562
  206. package/dist/index.mjs +81 -84
  207. package/package.json +8 -10
  208. package/src/components/Accordion/Accordion.tsx +32 -9
  209. package/src/components/AlertBanner/AlertBanner.tsx +7 -6
  210. package/src/components/AppHeader/AppHeader.tsx +1 -1
  211. package/src/components/Avatar/Avatar.tsx +92 -1
  212. package/src/components/Avatar/index.ts +2 -2
  213. package/src/components/Badge/Badge.tsx +2 -2
  214. package/src/components/Button/Button.tsx +64 -57
  215. package/src/components/Card/Card.tsx +1 -0
  216. package/src/components/CategoryStrip/CategoryStrip.tsx +36 -49
  217. package/src/components/Chip/Chip.tsx +5 -4
  218. package/src/components/ConfirmDialog/ConfirmDialog.tsx +13 -6
  219. package/src/components/DetailRow/DetailRow.tsx +3 -3
  220. package/src/components/EmptyState/EmptyState.tsx +2 -2
  221. package/src/components/ErrorBoundary/ErrorBoundary.tsx +6 -6
  222. package/src/components/HolographicCard/HolographicCard.tsx +14 -95
  223. package/src/components/IconButton/IconButton.tsx +2 -2
  224. package/src/components/IconPicker/IconPicker.tsx +13 -12
  225. package/src/components/ImageUpload/ImageUpload.tsx +24 -28
  226. package/src/components/ImageViewer/ImageViewer.tsx +3 -3
  227. package/src/components/Input/Input.tsx +11 -5
  228. package/src/components/LabelValue/LabelValue.tsx +2 -2
  229. package/src/components/ListItem/ListItem.tsx +4 -4
  230. package/src/components/MediaCard/MediaCard.tsx +21 -59
  231. package/src/components/MenuItem/MenuItem.tsx +2 -2
  232. package/src/components/MonthPicker/MonthPicker.tsx +2 -2
  233. package/src/components/NumberStepper/NumberStepper.tsx +6 -6
  234. package/src/components/PagerDots/PagerDots.tsx +38 -28
  235. package/src/components/PricingCard/PricingCard.tsx +6 -6
  236. package/src/components/RadioGroup/RadioGroup.tsx +18 -31
  237. package/src/components/Select/Select.tsx +32 -39
  238. package/src/components/SelectableCard/SelectableCard.tsx +302 -0
  239. package/src/components/SelectableCard/index.ts +1 -0
  240. package/src/components/SelectableGrid/SelectableGrid.tsx +38 -72
  241. package/src/components/Sheet/Sheet.tsx +11 -4
  242. package/src/components/SheetSelect/SheetSelect.tsx +3 -3
  243. package/src/components/Skeleton/Skeleton.tsx +6 -3
  244. package/src/components/Spinner/Spinner.tsx +2 -2
  245. package/src/components/Stats/Stats.tsx +36 -8
  246. package/src/components/Switch/Switch.tsx +9 -6
  247. package/src/components/TabBar/TabBar.tsx +9 -8
  248. package/src/components/Text/Text.tsx +12 -1
  249. package/src/components/Textarea/Textarea.tsx +18 -32
  250. package/src/components/Toggle/Toggle.tsx +3 -3
  251. package/src/hooks/useConfirmDialog.ts +31 -42
  252. package/src/index.ts +4 -4
  253. package/src/theme/ThemeProvider.tsx +1 -4
  254. package/src/theme/colorUtils.ts +1 -72
  255. package/src/theme/colors.ts +47 -1
  256. package/src/theme/types.ts +6 -3
  257. package/src/utils/animations.ts +0 -47
  258. package/src/utils/curatedIcons.ts +93 -801
  259. package/src/utils/haptics.ts +13 -208
  260. package/src/utils/icons.ts +27 -91
  261. package/src/utils/pressable.ts +10 -61
  262. package/dist/VirtualList.d.mts +0 -19
  263. package/dist/VirtualList.d.ts +0 -19
  264. package/dist/VirtualList.js +0 -38
  265. package/dist/VirtualList.mjs +0 -2
  266. package/dist/chunk-3DKJ2GIC.mjs +0 -30
  267. package/dist/chunk-AQEVCEXV.mjs +0 -164
  268. package/dist/chunk-DOGIPOF5.mjs +0 -131
  269. package/dist/chunk-DVK4G2GT.mjs +0 -59
  270. package/dist/chunk-EJ7ZPXOH.mjs +0 -163
  271. package/dist/chunk-J6Q2YJEV.mjs +0 -134
  272. package/dist/chunk-JNVAIDLK.mjs +0 -136
  273. package/dist/chunk-KA7LTET3.mjs +0 -71
  274. package/dist/chunk-KHYX4IOM.mjs +0 -1114
  275. package/dist/chunk-NC5ZTR2Y.mjs +0 -32
  276. package/dist/chunk-YNROWHQJ.mjs +0 -46
  277. package/src/components/VirtualList/VirtualList.tsx +0 -60
  278. package/src/components/VirtualList/index.ts +0 -1
  279. package/src/utils/fontGuard.ts +0 -35
  280. package/src/utils/hover.ts +0 -25
  281. package/src/utils/useColorTransition.ts +0 -40
  282. package/src/utils/usePressScale.ts +0 -75
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo } from 'react'
2
- import { StyleSheet, ViewStyle, TouchableOpacity, Platform } from 'react-native'
2
+ import { ViewStyle, Platform } from 'react-native'
3
3
  import {
4
4
  Canvas,
5
5
  Group,
@@ -15,14 +15,10 @@ import Animated, {
15
15
  useAnimatedStyle,
16
16
  withTiming,
17
17
  } from 'react-native-reanimated'
18
- import { usePressScale } from '../../utils/usePressScale'
19
- import { PRESS_SCALE } from '../../utils/animations'
18
+ import { PressableCard } from '../../utils/pressable'
20
19
  import { impactLight } from '../../utils/haptics'
21
20
  import { RADIUS } from '../../tokens'
22
21
 
23
- // ─── Foil Color Presets ───────────────────────────────────────────────────────
24
-
25
- /** Available foil color preset names */
26
22
  export type FoilPreset =
27
23
  | 'rainbow'
28
24
  | 'gold'
@@ -35,9 +31,7 @@ export type FoilPreset =
35
31
  | 'aurora'
36
32
  | 'neon'
37
33
 
38
- /** Foil color presets — each is an array of RGBA colors for the gradient */
39
34
  export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
40
- // Classic holographic rainbow
41
35
  rainbow: [
42
36
  'rgba(255, 0, 128, 0.45)',
43
37
  'rgba(255, 200, 0, 0.40)',
@@ -45,7 +39,6 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
45
39
  'rgba(0, 170, 255, 0.45)',
46
40
  'rgba(180, 0, 255, 0.45)',
47
41
  ],
48
- // Premium gold foil
49
42
  gold: [
50
43
  'rgba(255, 215, 0, 0.50)',
51
44
  'rgba(255, 180, 0, 0.45)',
@@ -53,7 +46,6 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
53
46
  'rgba(255, 223, 128, 0.50)',
54
47
  'rgba(184, 134, 11, 0.45)',
55
48
  ],
56
- // Chrome silver foil
57
49
  silver: [
58
50
  'rgba(192, 192, 192, 0.50)',
59
51
  'rgba(220, 220, 220, 0.45)',
@@ -61,7 +53,6 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
61
53
  'rgba(240, 240, 240, 0.50)',
62
54
  'rgba(128, 128, 128, 0.45)',
63
55
  ],
64
- // Deep space cosmic
65
56
  cosmic: [
66
57
  'rgba(75, 0, 130, 0.50)',
67
58
  'rgba(138, 43, 226, 0.45)',
@@ -69,7 +60,6 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
69
60
  'rgba(0, 191, 255, 0.45)',
70
61
  'rgba(148, 0, 211, 0.50)',
71
62
  ],
72
- // Emerald green luxury
73
63
  emerald: [
74
64
  'rgba(0, 201, 87, 0.50)',
75
65
  'rgba(46, 139, 87, 0.45)',
@@ -77,7 +67,6 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
77
67
  'rgba(60, 179, 113, 0.45)',
78
68
  'rgba(0, 128, 0, 0.50)',
79
69
  ],
80
- // Rose gold / pink
81
70
  rose: [
82
71
  'rgba(255, 105, 180, 0.50)',
83
72
  'rgba(255, 182, 193, 0.45)',
@@ -85,7 +74,6 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
85
74
  'rgba(255, 20, 147, 0.45)',
86
75
  'rgba(199, 21, 133, 0.50)',
87
76
  ],
88
- // Deep ocean blue
89
77
  ocean: [
90
78
  'rgba(0, 119, 190, 0.50)',
91
79
  'rgba(0, 180, 216, 0.45)',
@@ -93,7 +81,6 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
93
81
  'rgba(144, 224, 239, 0.45)',
94
82
  'rgba(0, 150, 199, 0.50)',
95
83
  ],
96
- // Hot fire gradient
97
84
  fire: [
98
85
  'rgba(255, 69, 0, 0.50)',
99
86
  'rgba(255, 140, 0, 0.45)',
@@ -101,7 +88,6 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
101
88
  'rgba(255, 99, 71, 0.45)',
102
89
  'rgba(220, 20, 60, 0.50)',
103
90
  ],
104
- // Aurora borealis
105
91
  aurora: [
106
92
  'rgba(0, 255, 127, 0.45)',
107
93
  'rgba(127, 255, 212, 0.40)',
@@ -109,7 +95,6 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
109
95
  'rgba(138, 43, 226, 0.40)',
110
96
  'rgba(255, 20, 147, 0.45)',
111
97
  ],
112
- // Neon cyberpunk
113
98
  neon: [
114
99
  'rgba(255, 0, 255, 0.55)',
115
100
  'rgba(0, 255, 255, 0.50)',
@@ -119,64 +104,29 @@ export const FOIL_PRESETS: Record<FoilPreset, string[]> = {
119
104
  ],
120
105
  }
121
106
 
122
- // Default preset
123
107
  const DEFAULT_FOIL_COLORS = FOIL_PRESETS.rainbow
124
-
125
- // ─── Tilt Configuration ───────────────────────────────────────────────────────
126
-
127
- /** Default max tilt in degrees */
128
108
  const DEFAULT_TILT_DEG = 10
129
- /** Default normalization factor (~30° of device rotation = full tilt) */
130
109
  const DEFAULT_NORM_FACTOR = Math.PI / 6
131
110
 
132
111
  export interface HolographicCardProps {
133
- /** Card art — `require()` asset or `{ uri }`. Omitted = gradient-only foil surface. */
134
112
  source?: Parameters<typeof useImage>[0]
135
- /** Card width (dp). Defaults to 300. */
136
113
  width?: number
137
- /** Card height (dp). Defaults to `width * 1.4` (trading-card ratio). */
138
114
  height?: number
139
- /** Corner radius (dp). Defaults to `RADIUS.md`. */
140
115
  borderRadius?: number
141
- /** React to device motion (gyroscope) for parallax tilt + sheen. Defaults to true. */
142
116
  enableTilt?: boolean
143
- /** Strength of the foil sheen, 0–1. Defaults to 1. */
144
117
  intensity?: number
145
- /** Called when the card is pressed. */
146
118
  onPress?: () => void
147
119
  style?: ViewStyle
148
-
149
- // ─── New Customization Props ────────────────────────────────────────────────
150
-
151
- /** Foil color preset. Defaults to 'rainbow'. Ignored if `foilColors` is provided. */
152
120
  foilPreset?: FoilPreset
153
- /** Custom foil gradient colors (array of RGBA strings). Overrides `foilPreset`. */
154
121
  foilColors?: string[]
155
- /** Maximum tilt angle in degrees (0–45). Defaults to 10. */
156
122
  maxTiltDegrees?: number
157
- /** Sensitivity of tilt to device motion (0.1–2). Higher = more responsive. Defaults to 1. */
158
123
  tiltSensitivity?: number
159
- /** How far the sheen moves relative to tilt (0–1). Defaults to 0.6. */
160
124
  sheenSpread?: number
161
- /** Animation duration for tilt smoothing in ms. Defaults to 80. */
162
125
  tiltAnimationDuration?: number
163
- /** Perspective depth for 3D effect (200–2000). Defaults to 800. */
164
126
  perspective?: number
165
- /** Blend mode for the foil overlay. Defaults to 'plus'. */
166
127
  blendMode?: 'plus' | 'screen' | 'overlay' | 'softLight' | 'hardLight'
167
128
  }
168
129
 
169
- /**
170
- * Holographic / foil trading-card surface (Pokémon-TCG style). Renders the art
171
- * on a Skia canvas with an animated rainbow sheen, and tilts in 3D toward device
172
- * motion. The sheen position tracks the tilt so the foil "catches the light".
173
- *
174
- * Deep-import only — keeps Skia out of the main bundle:
175
- * `import { HolographicCard } from '@retray-dev/ui-kit/HolographicCard'`
176
- *
177
- * Requires the optional peers `@shopify/react-native-skia` and (for tilt)
178
- * `expo-sensors`. Without `expo-sensors` the card renders with a static sheen.
179
- */
180
130
  export function HolographicCard({
181
131
  source,
182
132
  width = 300,
@@ -186,7 +136,6 @@ export function HolographicCard({
186
136
  intensity = 1,
187
137
  onPress,
188
138
  style,
189
- // New customization props
190
139
  foilPreset = 'rainbow',
191
140
  foilColors,
192
141
  maxTiltDegrees = DEFAULT_TILT_DEG,
@@ -197,25 +146,20 @@ export function HolographicCard({
197
146
  blendMode = 'plus',
198
147
  }: HolographicCardProps) {
199
148
  const h = height ?? width * 1.4
200
- // Called unconditionally (rules of hooks); useImage returns null for a nullish source.
201
149
  const image = useImage((source ?? null) as Parameters<typeof useImage>[0])
202
150
 
203
- // Clamp and compute values
204
151
  const clampedTilt = Math.max(0, Math.min(45, maxTiltDegrees))
205
152
  const clampedSensitivity = Math.max(0.1, Math.min(2, tiltSensitivity))
206
153
  const clampedSpread = Math.max(0, Math.min(1, sheenSpread))
207
154
  const clampedPerspective = Math.max(200, Math.min(2000, perspective))
208
-
209
- // Resolve foil colors: custom > preset > default
155
+
210
156
  const resolvedFoilColors = useMemo(() => {
211
157
  if (foilColors && foilColors.length >= 2) return foilColors
212
158
  return FOIL_PRESETS[foilPreset] ?? DEFAULT_FOIL_COLORS
213
159
  }, [foilColors, foilPreset])
214
160
 
215
- // Normalization factor adjusted by sensitivity
216
161
  const normFactor = DEFAULT_NORM_FACTOR / clampedSensitivity
217
162
 
218
- // Tilt in normalized [-1, 1] on each axis.
219
163
  const tiltX = useSharedValue(0)
220
164
  const tiltY = useSharedValue(0)
221
165
 
@@ -223,7 +167,6 @@ export function HolographicCard({
223
167
  if (!enableTilt || Platform.OS === 'web') return
224
168
  let remove: (() => void) | undefined
225
169
  let cancelled = false
226
- // Dynamic import: expo-sensors is an optional peer. Absence = static sheen.
227
170
  import('expo-sensors')
228
171
  .then(({ DeviceMotion }) => {
229
172
  if (cancelled) return
@@ -237,21 +180,13 @@ export function HolographicCard({
237
180
  })
238
181
  remove = () => sub.remove()
239
182
  })
240
- .catch(() => {
241
- // expo-sensors not installed — leave tilt at rest.
242
- })
183
+ .catch(() => {})
243
184
  return () => {
244
185
  cancelled = true
245
186
  remove?.()
246
187
  }
247
188
  }, [enableTilt, tiltX, tiltY, normFactor, tiltAnimationDuration])
248
189
 
249
- // 3D parallax — perspective tilt of the whole card toward the device motion.
250
- const { animatedStyle: pressStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
251
- pressScale: PRESS_SCALE.card,
252
- disabled: !onPress,
253
- })
254
-
255
190
  const tiltStyle = useAnimatedStyle(() => ({
256
191
  transform: [
257
192
  { perspective: clampedPerspective },
@@ -260,7 +195,6 @@ export function HolographicCard({
260
195
  ],
261
196
  }))
262
197
 
263
- // Sheen sweeps across the card as it tilts.
264
198
  const start = useDerivedValue(() => vec(width * (0.5 - tiltX.value * clampedSpread), h * (0.5 - tiltY.value * clampedSpread)))
265
199
  const end = useDerivedValue(() => vec(width * (0.5 + tiltX.value * clampedSpread), h * (0.5 + tiltY.value * clampedSpread)))
266
200
 
@@ -268,13 +202,11 @@ export function HolographicCard({
268
202
 
269
203
  const canvas = (
270
204
  <Canvas style={{ width, height: h }}>
271
- {/* Art clipped to the rounded card. */}
272
205
  {image ? (
273
206
  <Group clip={{ rect: rrct, rx: borderRadius, ry: borderRadius }}>
274
207
  <SkiaImage image={image} x={0} y={0} width={width} height={h} fit="cover" />
275
208
  </Group>
276
209
  ) : null}
277
- {/* Foil sheen — additive rainbow gradient that tracks the tilt. */}
278
210
  <RoundedRect x={0} y={0} width={width} height={h} r={borderRadius} opacity={intensity} blendMode={blendMode}>
279
211
  <LinearGradient start={start} end={end} colors={resolvedFoilColors} />
280
212
  </RoundedRect>
@@ -288,28 +220,15 @@ export function HolographicCard({
288
220
  if (!onPress) return card
289
221
 
290
222
  return (
291
- <Animated.View style={pressStyle}>
292
- <TouchableOpacity
293
- onPress={() => {
294
- impactLight()
295
- onPress()
296
- }}
297
- onPressIn={onPressIn}
298
- onPressOut={onPressOut}
299
- activeOpacity={1}
300
- touchSoundDisabled={true}
301
- accessibilityRole="imagebutton"
302
- {...hoverHandlers}
303
- style={styles.touch}
304
- >
305
- {card}
306
- </TouchableOpacity>
307
- </Animated.View>
223
+ <PressableCard
224
+ onPress={() => { impactLight(); onPress() }}
225
+ enabled
226
+ rippleColor="transparent"
227
+ touchSoundDisabled
228
+ accessibilityRole="imagebutton"
229
+ style={{ alignSelf: 'flex-start' }}
230
+ >
231
+ {card}
232
+ </PressableCard>
308
233
  )
309
234
  }
310
-
311
- const styles = StyleSheet.create({
312
- touch: {
313
- alignSelf: 'flex-start',
314
- },
315
- })
@@ -9,7 +9,7 @@ import {
9
9
  import { impactLight } from '../../utils/haptics'
10
10
  import { useTheme } from '../../theme'
11
11
  import { s, ms } from '../../utils/scaling'
12
- import { renderIcon } from '../../utils/icons'
12
+ import { Icon } from '../../utils/icons'
13
13
  import { PressableButton } from '../../utils/pressable'
14
14
 
15
15
  // primary: filled primary
@@ -92,7 +92,7 @@ function IconButtonBase({
92
92
  const { container: containerSize, icon: iconSize } = sizeMap[size]
93
93
 
94
94
  const resolvedIcon: React.ReactNode = iconName
95
- ? renderIcon(iconName, iconSize, iconColor ?? defaultIconColor)
95
+ ? <Icon name={iconName} size={iconSize} color={iconColor ?? defaultIconColor} />
96
96
  : icon
97
97
 
98
98
  // Badge rendering
@@ -8,8 +8,8 @@ import {
8
8
  } from '@gorhom/bottom-sheet'
9
9
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
10
10
  import { useTheme } from '../../theme'
11
- import { renderIcon } from '../../utils/icons'
12
- import { CURATED_ICONS, ALL_CURATED_ICONS } from '../../utils/curatedIcons'
11
+ import { Icon } from '../../utils/icons'
12
+ import { getCuratedCategories, getAllCuratedIcons } from '../../utils/curatedIcons'
13
13
  import { selectionAsync as hapticSelection, impactMedium } from '../../utils/haptics'
14
14
  import { s, vs, ms } from '../../utils/scaling'
15
15
  import { RADIUS } from '../../tokens'
@@ -54,7 +54,7 @@ function IconCell({ name, selected, size, onPress }: { name: string; selected: b
54
54
  accessibilityLabel={name}
55
55
  style={[styles.cell, { width: size, height: size, backgroundColor: bg }]}
56
56
  >
57
- {renderIcon(name, ms(20), iconColor)}
57
+ <Icon name={name} size={ms(20)} color={iconColor} />
58
58
  </TouchableOpacity>
59
59
  )
60
60
  }
@@ -83,10 +83,11 @@ export function IconPicker({
83
83
  const sheetName = useId()
84
84
 
85
85
  const activeIcons = useMemo(() => {
86
+ const allIcons = getAllCuratedIcons()
86
87
  if (activeCategory) {
87
- return CURATED_ICONS.find((c) => c.name === activeCategory)?.icons ?? ALL_CURATED_ICONS
88
+ return getCuratedCategories().find((c) => c.name === activeCategory)?.icons ?? allIcons
88
89
  }
89
- return ALL_CURATED_ICONS
90
+ return allIcons
90
91
  }, [activeCategory])
91
92
 
92
93
  const gapPx = s(gap)
@@ -129,7 +130,7 @@ export function IconPicker({
129
130
  [],
130
131
  )
131
132
 
132
- const selectedIcon = value ? renderIcon(value, ms(28), colors.foreground) : null
133
+ const selectedIcon = value ? <Icon name={value} size={ms(28)} color={colors.foreground} /> : null
133
134
 
134
135
  return (
135
136
  <View style={[styles.triggerContainer, style]}>
@@ -157,7 +158,7 @@ export function IconPicker({
157
158
  disabled && styles.triggerDisabled,
158
159
  ]}
159
160
  >
160
- {selectedIcon ?? renderIcon('plus', ms(24), colors.foregroundMuted)}
161
+ {selectedIcon ?? <Icon name="plus" size={ms(24)} color={colors.foregroundMuted} />}
161
162
  </TouchableOpacity>
162
163
  {error ? (
163
164
  <Text
@@ -223,7 +224,7 @@ export function IconPicker({
223
224
  activeOpacity={0.7}
224
225
  touchSoundDisabled={true}
225
226
  accessibilityRole="button"
226
- accessibilityLabel="Todos"
227
+ accessibilityLabel="Todas"
227
228
  accessibilityState={{ selected: activeCategory === null }}
228
229
  style={[
229
230
  styles.categoryChip,
@@ -234,7 +235,7 @@ export function IconPicker({
234
235
  ]}
235
236
  >
236
237
  <View style={styles.categoryChipInner}>
237
- {renderIcon('grid', ms(14), activeCategory === null ? colors.primaryForeground : colors.foregroundSubtle)}
238
+ <Icon name="grid" size={ms(14)} color={activeCategory === null ? colors.primaryForeground : colors.foregroundSubtle} />
238
239
  <Text
239
240
  style={[
240
241
  styles.categoryChipText,
@@ -243,11 +244,11 @@ export function IconPicker({
243
244
  allowFontScaling={true}
244
245
  numberOfLines={1}
245
246
  >
246
- Todos
247
+ Todas
247
248
  </Text>
248
249
  </View>
249
250
  </TouchableOpacity>
250
- {CURATED_ICONS.map((cat) => (
251
+ {getCuratedCategories().map((cat) => (
251
252
  <TouchableOpacity
252
253
  key={cat.name}
253
254
  onPress={() => setActiveCategory(cat.name)}
@@ -265,7 +266,7 @@ export function IconPicker({
265
266
  ]}
266
267
  >
267
268
  <View style={styles.categoryChipInner}>
268
- {renderIcon(cat.categoryIcon, ms(14), activeCategory === cat.name ? colors.primaryForeground : colors.foregroundSubtle)}
269
+ <Icon name={cat.categoryIcon} size={ms(14)} color={activeCategory === cat.name ? colors.primaryForeground : colors.foregroundSubtle} />
269
270
  <Text
270
271
  style={[
271
272
  styles.categoryChipText,
@@ -1,5 +1,6 @@
1
- import React from 'react'
2
- import { View, Text, Image, StyleSheet, ViewStyle, Platform } from 'react-native'
1
+ import React, { useState } from 'react'
2
+ import { Image } from 'expo-image'
3
+ import { View, Text, StyleSheet, ViewStyle } from 'react-native'
3
4
  import { Feather } from '@expo/vector-icons'
4
5
  import { impactLight } from '../../utils/haptics'
5
6
  import { useTheme } from '../../theme'
@@ -27,65 +28,58 @@ export interface ImageUploadProps {
27
28
  borderRadius?: number
28
29
  /** Aspect ratio for the selected image. Defaults to 'cover'. */
29
30
  resizeMode?: 'cover' | 'contain' | 'stretch'
31
+ /** Whether to allow the user to crop the image after selecting. Defaults to true. */
32
+ allowsEditing?: boolean
30
33
  disabled?: boolean
31
34
  style?: ViewStyle
32
35
  accessibilityLabel?: string
36
+ /** Called synchronously when user taps the upload area, before dynamic import and permission request. */
37
+ onPickerStarting?: () => void
33
38
  }
34
39
 
35
40
  export function ImageUpload({
36
41
  value,
37
42
  onChange,
38
43
  loading = false,
39
- placeholder = 'Tap to add image',
44
+ placeholder = 'Toca para añadir imagen',
40
45
  showPlaceholderText = true,
41
46
  width,
42
47
  height = 200,
43
48
  borderRadius = RADIUS.lg,
44
49
  resizeMode = 'cover',
50
+ allowsEditing = true,
45
51
  disabled = false,
46
52
  style,
47
53
  accessibilityLabel,
54
+ onPickerStarting,
48
55
  }: ImageUploadProps) {
49
56
  const { colors } = useTheme()
57
+ const [imageLoaded, setImageLoaded] = useState(false)
50
58
 
51
59
  const handlePress = async () => {
52
60
  if (disabled || loading) return
53
61
  impactLight()
54
62
 
55
- // Dynamic import so expo-image-picker is optional at module load time.
56
- // Import ExponentImagePicker (the raw native module proxy) directly to
57
- // avoid expo-image-picker's top-level createPermissionHook dependency.
63
+ onPickerStarting?.()
64
+
58
65
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
66
  let picker: any
60
67
  try {
61
- const mod = await import('expo-image-picker/build/ExponentImagePicker')
62
- picker = (mod as { default: unknown }).default
68
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
69
+ picker = require('expo-image-picker')
63
70
  } catch {
64
- // Fallback: try the main module
65
- try {
66
- picker = await import('expo-image-picker')
67
- } catch {
68
- if (__DEV__) console.warn('[ImageUpload] expo-image-picker not installed.')
69
- return
70
- }
71
- }
72
-
73
- if (Platform.OS !== 'web') {
74
- try {
75
- const { status } = await picker.requestMediaLibraryPermissionsAsync()
76
- if (status !== 'granted') return
77
- } catch {
78
- // Permission check failed — try picker anyway
79
- }
71
+ if (__DEV__) console.warn('[ImageUpload] expo-image-picker not installed.')
72
+ return
80
73
  }
81
74
 
82
75
  const result = await picker.launchImageLibraryAsync({
83
76
  mediaTypes: ['images'],
84
- allowsEditing: true,
77
+ allowsEditing,
85
78
  quality: 0.8,
86
79
  })
87
80
 
88
81
  if (!result.canceled && result.assets?.[0]) {
82
+ setImageLoaded(false)
89
83
  onChange?.(result.assets[0].uri)
90
84
  }
91
85
  }
@@ -97,7 +91,7 @@ export function ImageUpload({
97
91
  borderWidth: value ? 0 : 1,
98
92
  borderStyle: 'dashed',
99
93
  borderColor: colors.border,
100
- backgroundColor: value ? 'transparent' : colors.surface,
94
+ backgroundColor: (value && imageLoaded) ? 'transparent' : colors.surface,
101
95
  overflow: 'hidden',
102
96
  }
103
97
 
@@ -108,7 +102,7 @@ export function ImageUpload({
108
102
  rippleColor="transparent"
109
103
  touchSoundDisabled
110
104
  accessibilityRole="button"
111
- accessibilityLabel={accessibilityLabel ?? (value ? 'Change image' : placeholder)}
105
+ accessibilityLabel={accessibilityLabel ?? (value ? 'Cambiar imagen' : placeholder)}
112
106
  accessibilityState={{ disabled: disabled || loading }}
113
107
  style={[containerStyle, style]}
114
108
  >
@@ -116,7 +110,9 @@ export function ImageUpload({
116
110
  <Image
117
111
  source={{ uri: value }}
118
112
  style={[StyleSheet.absoluteFillObject, { borderRadius }]}
119
- resizeMode={resizeMode}
113
+ contentFit={resizeMode === 'stretch' ? 'fill' : resizeMode}
114
+ onLoad={() => setImageLoaded(true)}
115
+ onError={() => setImageLoaded(true)}
120
116
  />
121
117
  ) : (
122
118
  <View style={styles.placeholder}>
@@ -1,8 +1,8 @@
1
1
  import React, { useState, useCallback } from 'react'
2
+ import { Image } from 'expo-image'
2
3
  import {
3
4
  Modal,
4
5
  View,
5
- Image,
6
6
  Dimensions,
7
7
  StyleSheet,
8
8
  useWindowDimensions,
@@ -105,7 +105,7 @@ function ZoomableImage({ source, width, height, onZoomChange }: ZoomableImagePro
105
105
  <GestureDetector gesture={composed}>
106
106
  <View style={[{ width, height }, styles.imageWrap]} collapsable={false}>
107
107
  <Animated.View style={[{ width, height }, animatedStyle]}>
108
- <Image source={source} style={{ width, height }} resizeMode="contain" />
108
+ <Image source={source} style={{ width, height }} contentFit="contain" />
109
109
  </Animated.View>
110
110
  </View>
111
111
  </GestureDetector>
@@ -235,7 +235,7 @@ export function ImageViewer({ images, visible, onClose, initialIndex = 0 }: Imag
235
235
  style={{ backgroundColor: 'rgba(255,255,255,0.18)' }}
236
236
  iconColor="#fff"
237
237
  onPress={onClose}
238
- accessibilityLabel="Close"
238
+ accessibilityLabel="Cerrar"
239
239
  />
240
240
  </View>
241
241
 
@@ -5,7 +5,7 @@ import { EaseView } from 'react-native-ease'
5
5
  import { AntDesign } from '@expo/vector-icons'
6
6
  import { useTheme } from '../../theme'
7
7
  import { s, vs, ms } from '../../utils/scaling'
8
- import { renderIcon } from '../../utils/icons'
8
+ import { Icon } from '../../utils/icons'
9
9
  import { COLOR_TRANSITION } from '../../utils/animations'
10
10
 
11
11
  const webInputResetStyle: Record<string, unknown> =
@@ -33,7 +33,7 @@ export interface InputProps extends TextInputProps {
33
33
  sheetMode?: boolean
34
34
  }
35
35
 
36
- export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type = 'text', containerStyle, inputWrapperStyle, sheetMode = false, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }: InputProps) {
36
+ export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type = 'text', containerStyle, inputWrapperStyle, sheetMode = false, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, autoCapitalize, autoCorrect, ...props }: InputProps) {
37
37
  const { colors } = useTheme()
38
38
  const [focused, setFocused] = useState(false)
39
39
  const [showPassword, setShowPassword] = useState(false)
@@ -41,9 +41,11 @@ export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyl
41
41
  const isDisabled = disabled || editable === false
42
42
  const isPassword = type === 'password'
43
43
  const effectiveSecure = isPassword ? !showPassword : secureTextEntry
44
+ const effectiveAutoCapitalize = isPassword ? 'none' : autoCapitalize
45
+ const effectiveAutoCorrect = isPassword ? false : autoCorrect
44
46
 
45
47
  const effectivePrefix: React.ReactNode = prefixIcon
46
- ? renderIcon(prefixIcon, 20, prefixIconColor ?? colors.foregroundMuted)
48
+ ? <Icon name={prefixIcon} size={20} color={prefixIconColor ?? colors.foregroundMuted} />
47
49
  : prefix
48
50
 
49
51
  const effectiveSuffix: React.ReactNode = isPassword && !suffix && !suffixIcon ? (
@@ -54,12 +56,12 @@ export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyl
54
56
  style={styles.passwordToggle}
55
57
  activeOpacity={0.6}
56
58
  accessibilityRole="button"
57
- accessibilityLabel={showPassword ? 'Hide password' : 'Show password'}
59
+ accessibilityLabel={showPassword ? 'Ocultar contraseña' : 'Mostrar contraseña'}
58
60
  >
59
61
  <AntDesign name={showPassword ? 'eye' : 'eye-invisible'} size={20} color={colors.foregroundMuted} />
60
62
  </TouchableOpacity>
61
63
  ) : suffixIcon
62
- ? renderIcon(suffixIcon, 20, suffixIconColor ?? colors.foregroundMuted)
64
+ ? <Icon name={suffixIcon} size={20} color={suffixIconColor ?? colors.foregroundMuted} />
63
65
  : suffix
64
66
 
65
67
  // Border color animated via EaseView. Width is static (1px normal, 2px error)
@@ -109,6 +111,8 @@ export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyl
109
111
  secureTextEntry={effectiveSecure}
110
112
  editable={isDisabled ? false : editable}
111
113
  accessibilityLabel={accessibilityLabel ?? label}
114
+ autoCapitalize={effectiveAutoCapitalize}
115
+ autoCorrect={effectiveAutoCorrect}
112
116
  {...props}
113
117
  />
114
118
  ) : (
@@ -132,6 +136,8 @@ export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyl
132
136
  secureTextEntry={effectiveSecure}
133
137
  editable={isDisabled ? false : editable}
134
138
  accessibilityLabel={accessibilityLabel ?? label}
139
+ autoCapitalize={effectiveAutoCapitalize}
140
+ autoCorrect={effectiveAutoCorrect}
135
141
  {...props}
136
142
  />
137
143
  )}
@@ -2,7 +2,7 @@ import React from 'react'
2
2
  import { View, Text, StyleSheet, ViewStyle } from 'react-native'
3
3
  import { useTheme } from '../../theme'
4
4
  import { s, ms, mvs } from '../../utils/scaling'
5
- import { renderIcon } from '../../utils/icons'
5
+ import { Icon } from '../../utils/icons'
6
6
 
7
7
  export interface LabelValueProps {
8
8
  label: string
@@ -18,7 +18,7 @@ function LabelValueBase({ label, value, iconName, iconColor, style }: LabelValue
18
18
  const { colors } = useTheme()
19
19
 
20
20
  const resolvedIcon = iconName
21
- ? renderIcon(iconName, ms(14), iconColor ?? colors.foregroundMuted)
21
+ ? <Icon name={iconName} size={ms(14)} color={iconColor ?? colors.foregroundMuted} />
22
22
  : null
23
23
 
24
24
  return (
@@ -1,18 +1,18 @@
1
1
  import React from 'react'
2
+ import { Image } from 'expo-image'
2
3
  import {
3
4
  View,
4
5
  Text,
5
6
  StyleSheet,
6
7
  ViewStyle,
7
8
  TextStyle,
8
- Image,
9
9
  ImageSourcePropType,
10
10
  } from 'react-native'
11
11
  import { Entypo } from '@expo/vector-icons'
12
12
  import { selectionAsync as hapticSelection } from '../../utils/haptics'
13
13
  import { useTheme } from '../../theme'
14
14
  import { s, vs, ms, mvs } from '../../utils/scaling'
15
- import { renderIcon } from '../../utils/icons'
15
+ import { Icon } from '../../utils/icons'
16
16
  import { RADIUS } from '../../tokens'
17
17
  import { PressableRow } from '../../utils/pressable'
18
18
 
@@ -122,7 +122,7 @@ function ListItemBase({
122
122
  const effectiveLeft: React.ReactNode = imageSource
123
123
  ? <Image source={imageSource} style={styles.image} />
124
124
  : leftIcon
125
- ? renderIcon(leftIcon, 24, leftIconColor ?? colors.foreground)
125
+ ? <Icon name={leftIcon} size={24} color={leftIconColor ?? colors.foreground} />
126
126
  : leftRender
127
127
 
128
128
  const hasRightContent = !!(rightIcon || (rightActions && rightActions.length > 0) || rightRender !== undefined || showChevron)
@@ -181,7 +181,7 @@ function ListItemBase({
181
181
  {hasRightContent ? (
182
182
  rightIcon ? (
183
183
  <View style={styles.rightContainer}>
184
- {renderIcon(rightIcon, 24, rightIconColor ?? colors.foregroundMuted)}
184
+ <Icon name={rightIcon} size={24} color={rightIconColor ?? colors.foregroundMuted} />
185
185
  </View>
186
186
  ) : rightActions && rightActions.length > 0 ? (
187
187
  <View style={styles.rightActionsContainer}>