@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
@@ -1,58 +1,48 @@
1
- import React, { useState, useRef } from 'react'
2
- import { TextInput, View, Text, Animated, StyleSheet, TextInputProps, ViewStyle, TextStyle, TouchableOpacity, Platform, Easing } from 'react-native'
1
+ import React, { useState } from 'react'
2
+ import { TextInput, View, Text, StyleSheet, TextInputProps, ViewStyle, TextStyle, TouchableOpacity, Platform } from 'react-native'
3
+ import Animated, {
4
+ useAnimatedStyle,
5
+ interpolateColor,
6
+ interpolate,
7
+ } from 'react-native-reanimated'
3
8
  import { AntDesign } from '@expo/vector-icons'
4
9
  import { useTheme } from '../../theme'
5
10
  import { s, vs, ms } from '../../utils/scaling'
6
11
  import { renderIcon } from '../../utils/icons'
12
+ import { useColorTransition } from '../../utils/useColorTransition'
13
+ import { TIMINGS } from '../../utils/animations'
7
14
 
8
- const webInputResetStyle: any =
15
+ const webInputResetStyle: Record<string, unknown> =
9
16
  Platform.OS === 'web'
10
17
  ? { outlineStyle: 'none', outlineWidth: 0, outlineColor: 'transparent', boxShadow: 'none' }
11
18
  : {}
12
19
 
13
20
  export interface InputProps extends TextInputProps {
14
21
  label?: string
15
- /** Red helper text below the input; also changes border to `destructive` color. Takes priority over `hint`. */
16
22
  error?: string
17
- /** Helper text shown below the input when there is no error. */
18
23
  hint?: string
19
- /** Disabled visual state — dimmed appearance, not editable. Also sets `editable={false}`. */
20
24
  disabled?: boolean
21
- /** Text or component rendered before the input text. */
22
25
  prefix?: React.ReactNode
23
- /** Text or component rendered after the input text. */
24
26
  suffix?: React.ReactNode
25
- /** Style applied to prefix text if prefix is a string. */
26
27
  prefixStyle?: TextStyle
27
- /** Style applied to suffix text if suffix is a string. */
28
28
  suffixStyle?: TextStyle
29
- /**
30
- * Icon name from `@expo/vector-icons` rendered before the input text.
31
- * See https://icons.expo.fyi. Takes precedence over `prefix`.
32
- */
33
29
  prefixIcon?: string
34
- /**
35
- * Icon name from `@expo/vector-icons` rendered after the input text.
36
- * See https://icons.expo.fyi. Takes precedence over `suffix` (unless `type="password"`).
37
- */
38
30
  suffixIcon?: string
39
- /** Override the resolved prefix icon color. Defaults to `mutedForeground`. */
40
31
  prefixIconColor?: string
41
- /** Override the resolved suffix icon color. Defaults to `mutedForeground`. */
42
32
  suffixIconColor?: string
43
- /** Input type. When set to \`'password'\`, shows a toggle button to reveal/hide text. */
44
33
  type?: 'text' | 'password'
45
- /** Style for the outer container \`View\`. Use \`style\` (from \`TextInputProps\`) to style the \`TextInput\` itself. */
46
34
  containerStyle?: ViewStyle
47
- /** Style for the inner border wrapper (overrides padding, etc). */
48
35
  inputWrapperStyle?: ViewStyle
49
36
  }
50
37
 
51
- export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type = 'text', containerStyle, inputWrapperStyle, style, onFocus, onBlur, secureTextEntry, editable, ...props }: InputProps) {
38
+ export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyle, suffixStyle, prefixIcon, suffixIcon, prefixIconColor, suffixIconColor, type = 'text', containerStyle, inputWrapperStyle, style, onFocus, onBlur, secureTextEntry, editable, accessibilityLabel, ...props }: InputProps) {
52
39
  const { colors } = useTheme()
53
40
  const [focused, setFocused] = useState(false)
54
41
  const [showPassword, setShowPassword] = useState(false)
55
- const focusAnim = useRef(new Animated.Value(0)).current
42
+
43
+ const focusProgress = useColorTransition(focused, {
44
+ duration: focused ? TIMINGS.focusIn.duration : TIMINGS.focusOut.duration,
45
+ })
56
46
 
57
47
  const isDisabled = disabled || editable === false
58
48
  const isPassword = type === 'password'
@@ -62,33 +52,45 @@ export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyl
62
52
  ? renderIcon(prefixIcon, 20, prefixIconColor ?? colors.foregroundMuted)
63
53
  : prefix
64
54
 
65
- // If type is password and no suffix override is provided, add the toggle button
66
55
  const effectiveSuffix: React.ReactNode = isPassword && !suffix && !suffixIcon ? (
67
- <TouchableOpacity onPress={() => setShowPassword(!showPassword)} style={styles.passwordToggle} activeOpacity={0.6}>
56
+ <TouchableOpacity
57
+ onPress={() => setShowPassword(!showPassword)}
58
+ // AUDIT FIX: was padding: s(4) → ~28px target (below 44px minimum).
59
+ // padding: s(12) with negative margin keeps visual size but expands hit area.
60
+ style={styles.passwordToggle}
61
+ activeOpacity={0.6}
62
+ accessibilityRole="button"
63
+ accessibilityLabel={showPassword ? 'Hide password' : 'Show password'}
64
+ >
68
65
  <AntDesign name={showPassword ? 'eye' : 'eye-invisible'} size={20} color={colors.foregroundMuted} />
69
66
  </TouchableOpacity>
70
67
  ) : suffixIcon
71
68
  ? renderIcon(suffixIcon, 20, suffixIconColor ?? colors.foregroundMuted)
72
69
  : suffix
73
70
 
71
+ // Border drawn on an absolute overlay so the 1px→2px weight change never
72
+ // resizes the layout box (which would reflow content / shift the interface).
73
+ // Wrapper keeps borderWidth: 0; overlay grows inward and is non-interactive.
74
+ const borderAnimStyle = useAnimatedStyle(() => ({
75
+ borderColor: error
76
+ ? colors.destructive
77
+ : interpolateColor(focusProgress.value, [0, 1], [colors.border, colors.primary]),
78
+ borderWidth: error
79
+ ? 2
80
+ : interpolate(focusProgress.value, [0, 1], [1, 2]),
81
+ }))
82
+
74
83
  return (
75
84
  <View style={[styles.container, isDisabled && styles.containerDisabled, containerStyle]}>
76
85
  {label ? <Text style={[styles.label, { color: colors.foreground }]} allowFontScaling={true}>{label}</Text> : null}
77
86
  <Animated.View
78
87
  style={[
79
88
  styles.inputWrapper,
80
- {
81
- borderColor: error
82
- ? colors.destructive
83
- : focusAnim.interpolate({
84
- inputRange: [0, 1],
85
- outputRange: [colors.border, colors.primary],
86
- }),
87
- backgroundColor: isDisabled ? colors.surface : colors.background,
88
- },
89
+ { backgroundColor: isDisabled ? colors.surface : colors.background },
89
90
  inputWrapperStyle,
90
91
  ]}
91
92
  >
93
+ <Animated.View style={[styles.borderOverlay, borderAnimStyle]} pointerEvents="none" />
92
94
  {effectivePrefix ? (
93
95
  typeof effectivePrefix === 'string' ? (
94
96
  <Text style={[styles.prefixText, { color: colors.foregroundMuted }, prefixStyle]} allowFontScaling={true}>
@@ -101,26 +103,23 @@ export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyl
101
103
  <TextInput
102
104
  style={[
103
105
  styles.input,
104
- {
105
- color: colors.foreground,
106
- },
106
+ { color: colors.foreground },
107
107
  webInputResetStyle,
108
108
  style,
109
109
  ]}
110
110
  onFocus={(e) => {
111
111
  setFocused(true)
112
- Animated.timing(focusAnim, { toValue: 1, duration: 120, easing: Easing.out(Easing.ease), useNativeDriver: false }).start()
113
112
  onFocus?.(e)
114
113
  }}
115
114
  onBlur={(e) => {
116
115
  setFocused(false)
117
- Animated.timing(focusAnim, { toValue: 0, duration: 80, easing: Easing.out(Easing.ease), useNativeDriver: false }).start()
118
116
  onBlur?.(e)
119
117
  }}
120
118
  placeholderTextColor={colors.foregroundMuted}
121
119
  allowFontScaling={true}
122
120
  secureTextEntry={effectiveSecure}
123
121
  editable={isDisabled ? false : editable}
122
+ accessibilityLabel={accessibilityLabel ?? label}
124
123
  {...props}
125
124
  />
126
125
  {effectiveSuffix ? (
@@ -134,7 +133,13 @@ export function Input({ label, error, hint, disabled, prefix, suffix, prefixStyl
134
133
  ) : null}
135
134
  </Animated.View>
136
135
  {error ? (
137
- <Text style={[styles.helperText, { color: colors.destructive }]} allowFontScaling={true}>{error}</Text>
136
+ <Text
137
+ style={[styles.helperText, { color: colors.destructive }]}
138
+ allowFontScaling={true}
139
+ accessibilityLiveRegion="polite"
140
+ >
141
+ {error}
142
+ </Text>
138
143
  ) : null}
139
144
  {!error && hint ? (
140
145
  <Text style={[styles.helperText, { color: colors.foregroundMuted }]} allowFontScaling={true}>{hint}</Text>
@@ -151,20 +156,25 @@ const styles = StyleSheet.create({
151
156
  opacity: 0.6,
152
157
  },
153
158
  label: {
154
- fontFamily: 'Poppins-Medium',
155
- fontSize: ms(14), // caption size for input labels
159
+ fontFamily: 'Sohne-Medium',
160
+ fontSize: ms(14),
156
161
  },
157
162
  inputWrapper: {
158
163
  flexDirection: 'row',
159
164
  alignItems: 'center',
160
- borderWidth: 2,
165
+ // Border lives on borderOverlay (absolute) so its 1px→2px focus change
166
+ // never resizes this box. Wrapper itself carries no border.
161
167
  borderRadius: 8,
162
168
  paddingHorizontal: s(14),
163
169
  paddingVertical: vs(11),
164
170
  minHeight: 48,
165
171
  },
172
+ borderOverlay: {
173
+ ...StyleSheet.absoluteFillObject,
174
+ borderRadius: 8,
175
+ },
166
176
  input: {
167
- fontFamily: 'Poppins-Regular',
177
+ fontFamily: 'Sohne-Regular',
168
178
  flex: 1,
169
179
  fontSize: ms(16),
170
180
  paddingVertical: vs(2),
@@ -174,7 +184,7 @@ const styles = StyleSheet.create({
174
184
  marginRight: s(8),
175
185
  },
176
186
  prefixText: {
177
- fontFamily: 'Poppins-Regular',
187
+ fontFamily: 'Sohne-Regular',
178
188
  fontSize: ms(15),
179
189
  marginRight: s(8),
180
190
  },
@@ -182,15 +192,18 @@ const styles = StyleSheet.create({
182
192
  marginLeft: s(8),
183
193
  },
184
194
  suffixText: {
185
- fontFamily: 'Poppins-Regular',
195
+ fontFamily: 'Sohne-Regular',
186
196
  fontSize: ms(15),
187
197
  marginLeft: s(8),
188
198
  },
199
+ // AUDIT FIX: was padding: s(4) → ~28px tap target. Now 12px padding → ~44px.
200
+ // Negative margin compensates so the visual icon position is unchanged.
189
201
  passwordToggle: {
190
- padding: s(4),
202
+ padding: s(12),
203
+ margin: -s(8),
191
204
  },
192
205
  helperText: {
193
- fontFamily: 'Poppins-Regular',
206
+ fontFamily: 'Sohne-Regular',
194
207
  fontSize: ms(13),
195
208
  },
196
209
  })
@@ -1,7 +1,7 @@
1
1
  import React from 'react'
2
2
  import { View, Text, StyleSheet, ViewStyle } from 'react-native'
3
3
  import { useTheme } from '../../theme'
4
- import { s, vs, ms, mvs } from '../../utils/scaling'
4
+ import { s, ms, mvs } from '../../utils/scaling'
5
5
  import { renderIcon } from '../../utils/icons'
6
6
 
7
7
  export interface LabelValueProps {
@@ -14,7 +14,7 @@ export interface LabelValueProps {
14
14
  style?: ViewStyle
15
15
  }
16
16
 
17
- export function LabelValue({ label, value, iconName, iconColor, style }: LabelValueProps) {
17
+ function LabelValueBase({ label, value, iconName, iconColor, style }: LabelValueProps) {
18
18
  const { colors } = useTheme()
19
19
 
20
20
  const resolvedIcon = iconName
@@ -40,6 +40,8 @@ export function LabelValue({ label, value, iconName, iconColor, style }: LabelVa
40
40
  )
41
41
  }
42
42
 
43
+ export const LabelValue = React.memo(LabelValueBase)
44
+
43
45
  const styles = StyleSheet.create({
44
46
  container: {
45
47
  flexDirection: 'row',
@@ -57,12 +59,12 @@ const styles = StyleSheet.create({
57
59
  justifyContent: 'center',
58
60
  },
59
61
  label: {
60
- fontFamily: 'Poppins-Regular',
62
+ fontFamily: 'Sohne-Regular',
61
63
  fontSize: ms(13),
62
64
  lineHeight: mvs(18),
63
65
  },
64
66
  value: {
65
- fontFamily: 'Poppins-Medium',
67
+ fontFamily: 'Sohne-Medium',
66
68
  fontSize: ms(14),
67
69
  lineHeight: mvs(20),
68
70
  textAlign: 'right',
@@ -0,0 +1,145 @@
1
+ import React from 'react'
2
+ import { View, Text, StyleSheet, ViewStyle } from 'react-native'
3
+ import { useTheme } from '../../theme'
4
+ import { s, vs } from '../../utils/scaling'
5
+ import { RADIUS } from '../../tokens'
6
+
7
+ export type ListGroupVariant = 'plain' | 'card'
8
+
9
+ export interface ListGroupProps {
10
+ children: React.ReactNode
11
+ /**
12
+ * - `plain` (default): no background, plain ListItems inside.
13
+ * - `card`: card surface with background + border wrapping plain ListItems.
14
+ */
15
+ variant?: ListGroupVariant
16
+ style?: ViewStyle
17
+ }
18
+
19
+ export interface ListGroupHeaderProps {
20
+ children: React.ReactNode
21
+ style?: ViewStyle
22
+ }
23
+
24
+ export interface ListGroupFooterProps {
25
+ children: React.ReactNode
26
+ style?: ViewStyle
27
+ }
28
+
29
+ /**
30
+ * ListGroup wraps multiple ListItems and auto-adds separators between them.
31
+ * Use variant="card" for a standalone surface or "plain" for items inside another container.
32
+ */
33
+ export function ListGroup({ children, variant = 'plain', style }: ListGroupProps) {
34
+ const { colors } = useTheme()
35
+
36
+ // Auto-inject showSeparator={true} to all ListItem children except the last
37
+ const processedChildren = React.Children.map(children, (child, index) => {
38
+ if (!React.isValidElement(child)) return child
39
+
40
+ // Skip ListGroup.Header and ListGroup.Footer
41
+ if (child.type === ListGroupHeader || child.type === ListGroupFooter) {
42
+ return child
43
+ }
44
+
45
+ // Check if it's a ListItem (has title prop as a heuristic)
46
+ const childProps = child.props as Record<string, unknown>
47
+ const isListItem = 'title' in childProps
48
+ if (!isListItem) return child
49
+
50
+ const isLast = index === React.Children.count(children) - 1
51
+
52
+ // Only add separator if not already explicitly set and not last item
53
+ if (childProps['showSeparator'] === undefined && !isLast) {
54
+ return React.cloneElement(child as React.ReactElement<Record<string, unknown>>, {
55
+ showSeparator: true,
56
+ })
57
+ }
58
+
59
+ return child
60
+ })
61
+
62
+ const cardStyle: ViewStyle =
63
+ variant === 'card'
64
+ ? {
65
+ backgroundColor: colors.card,
66
+ borderRadius: RADIUS.md,
67
+ borderWidth: 1,
68
+ borderColor: colors.border,
69
+ shadowColor: '#000',
70
+ shadowOffset: { width: 0, height: 2 },
71
+ shadowOpacity: 0.06,
72
+ shadowRadius: 6,
73
+ elevation: 2,
74
+ paddingVertical: vs(4),
75
+ }
76
+ : {}
77
+
78
+ return (
79
+ <View style={[styles.container, cardStyle, style]}>
80
+ {processedChildren}
81
+ </View>
82
+ )
83
+ }
84
+
85
+ export function ListGroupHeader({ children, style }: ListGroupHeaderProps) {
86
+ const { colors } = useTheme()
87
+
88
+ if (typeof children === 'string') {
89
+ return (
90
+ <View style={[styles.header, { borderBottomColor: colors.separator }, style]}>
91
+ <Text style={[styles.headerText, { color: colors.foregroundMuted }]} allowFontScaling={true}>
92
+ {children}
93
+ </Text>
94
+ </View>
95
+ )
96
+ }
97
+
98
+ return <View style={[styles.header, { borderBottomColor: colors.separator }, style]}>{children}</View>
99
+ }
100
+
101
+ export function ListGroupFooter({ children, style }: ListGroupFooterProps) {
102
+ const { colors } = useTheme()
103
+
104
+ if (typeof children === 'string') {
105
+ return (
106
+ <View style={[styles.footer, style]}>
107
+ <Text style={[styles.footerText, { color: colors.foregroundMuted }]} allowFontScaling={true}>
108
+ {children}
109
+ </Text>
110
+ </View>
111
+ )
112
+ }
113
+
114
+ return <View style={[styles.footer, style]}>{children}</View>
115
+ }
116
+
117
+ ListGroup.Header = ListGroupHeader
118
+ ListGroup.Footer = ListGroupFooter
119
+
120
+ const styles = StyleSheet.create({
121
+ container: {
122
+ overflow: 'hidden',
123
+ },
124
+ header: {
125
+ paddingHorizontal: s(16),
126
+ paddingTop: vs(12),
127
+ paddingBottom: vs(8),
128
+ borderBottomWidth: StyleSheet.hairlineWidth,
129
+ },
130
+ headerText: {
131
+ fontFamily: 'Sohne-SemiBold',
132
+ fontSize: 13,
133
+ letterSpacing: 0.32,
134
+ textTransform: 'uppercase',
135
+ },
136
+ footer: {
137
+ paddingHorizontal: s(16),
138
+ paddingTop: vs(8),
139
+ paddingBottom: vs(12),
140
+ },
141
+ footerText: {
142
+ fontFamily: 'Sohne-Regular',
143
+ fontSize: 12,
144
+ },
145
+ })
@@ -0,0 +1 @@
1
+ export * from './ListGroup'
@@ -1,22 +1,21 @@
1
- import React, { useRef } from 'react'
1
+ import React from 'react'
2
2
  import {
3
3
  TouchableOpacity,
4
- Animated,
5
4
  View,
6
5
  Text,
7
6
  StyleSheet,
8
7
  ViewStyle,
9
8
  TextStyle,
10
- Platform,
11
9
  } from 'react-native'
10
+ import Animated from 'react-native-reanimated'
12
11
  import { Entypo } from '@expo/vector-icons'
13
12
  import { selectionAsync as hapticSelection } from '../../utils/haptics'
14
13
  import { useTheme } from '../../theme'
15
14
  import { s, vs, ms, mvs } from '../../utils/scaling'
16
15
  import { renderIcon } from '../../utils/icons'
17
16
  import { RADIUS } from '../../tokens'
18
-
19
- const nativeDriver = Platform.OS !== 'web'
17
+ import { usePressScale } from '../../utils/usePressScale'
18
+ import { SPRINGS, PRESS_SCALE } from '../../utils/animations'
20
19
 
21
20
  export type ListItemVariant = 'plain' | 'card'
22
21
 
@@ -78,9 +77,11 @@ export interface ListItemProps {
78
77
  subtitleStyle?: TextStyle
79
78
  /** Style applied to the caption Text. */
80
79
  captionStyle?: TextStyle
80
+ /** Accessibility label override. Defaults to the title. */
81
+ accessibilityLabel?: string
81
82
  }
82
83
 
83
- export function ListItem({
84
+ function ListItemBase({
84
85
  leftRender,
85
86
  rightRender,
86
87
  trailing,
@@ -101,30 +102,15 @@ export function ListItem({
101
102
  titleStyle,
102
103
  subtitleStyle,
103
104
  captionStyle,
105
+ accessibilityLabel,
104
106
  }: ListItemProps) {
105
107
  const { colors } = useTheme()
106
- const scale = useRef(new Animated.Value(1)).current
107
-
108
- const handlePressIn = () => {
109
- if (!onPress || disabled) return
110
- Animated.spring(scale, {
111
- toValue: 0.97,
112
- useNativeDriver: nativeDriver,
113
- stiffness: 350,
114
- damping: 28,
115
- mass: 0.9,
116
- }).start()
117
- }
118
-
119
- const handlePressOut = () => {
120
- Animated.spring(scale, {
121
- toValue: 1,
122
- useNativeDriver: nativeDriver,
123
- stiffness: 220,
124
- damping: 20,
125
- mass: 0.9,
126
- }).start()
127
- }
108
+ const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
109
+ pressScale: PRESS_SCALE.row,
110
+ pressInSpring: SPRINGS.surfacePressIn,
111
+ pressOutSpring: SPRINGS.surfacePressOut,
112
+ disabled: !onPress || disabled,
113
+ })
128
114
 
129
115
  const handlePress = () => {
130
116
  hapticSelection()
@@ -154,16 +140,21 @@ export function ListItem({
154
140
  }
155
141
  : {}
156
142
 
143
+ const a11yLabel = accessibilityLabel ?? [title, subtitle, caption].filter(Boolean).join('. ')
144
+
157
145
  return (
158
- <Animated.View style={[{ transform: [{ scale }] }, disabled && styles.disabled]}>
146
+ <Animated.View style={[animatedStyle, disabled && styles.disabled]} {...hoverHandlers}>
159
147
  <TouchableOpacity
160
148
  style={[styles.container, cardStyle, style]}
161
149
  onPress={onPress ? handlePress : undefined}
162
- onPressIn={handlePressIn}
163
- onPressOut={handlePressOut}
150
+ onPressIn={onPressIn}
151
+ onPressOut={onPressOut}
164
152
  disabled={disabled}
165
153
  activeOpacity={1}
166
154
  touchSoundDisabled={true}
155
+ accessibilityRole={onPress ? 'button' : undefined}
156
+ accessibilityLabel={onPress ? a11yLabel : undefined}
157
+ accessibilityState={onPress ? { disabled: !!disabled } : undefined}
167
158
  >
168
159
  {effectiveLeft ? (
169
160
  <View style={styles.leftContainer}>{effectiveLeft}</View>
@@ -219,10 +210,7 @@ export function ListItem({
219
210
  <View
220
211
  style={[
221
212
  styles.separator,
222
- {
223
- backgroundColor: colors.border,
224
- marginLeft: effectiveLeft ? s(44) + s(12) : 0
225
- },
213
+ { backgroundColor: colors.separator },
226
214
  ]}
227
215
  />
228
216
  ) : null}
@@ -230,11 +218,13 @@ export function ListItem({
230
218
  )
231
219
  }
232
220
 
221
+ export const ListItem = React.memo(ListItemBase)
222
+
233
223
  const styles = StyleSheet.create({
234
224
  container: {
235
225
  flexDirection: 'row',
236
226
  alignItems: 'center',
237
- paddingHorizontal: 0,
227
+ paddingHorizontal: s(16),
238
228
  paddingVertical: vs(10),
239
229
  gap: s(12),
240
230
  },
@@ -250,17 +240,17 @@ const styles = StyleSheet.create({
250
240
  gap: vs(4),
251
241
  },
252
242
  title: {
253
- fontFamily: 'Poppins-Medium',
243
+ fontFamily: 'Sohne-Medium',
254
244
  fontSize: ms(15),
255
245
  lineHeight: mvs(22),
256
246
  },
257
247
  subtitle: {
258
- fontFamily: 'Poppins-Regular',
248
+ fontFamily: 'Sohne-Regular',
259
249
  fontSize: ms(13),
260
250
  lineHeight: mvs(18),
261
251
  },
262
252
  caption: {
263
- fontFamily: 'Poppins-Regular',
253
+ fontFamily: 'Sohne-Regular',
264
254
  fontSize: ms(12),
265
255
  lineHeight: mvs(16),
266
256
  opacity: 0.7,
@@ -272,12 +262,9 @@ const styles = StyleSheet.create({
272
262
  maxWidth: s(160),
273
263
  },
274
264
  rightText: {
275
- fontFamily: 'Poppins-Regular',
265
+ fontFamily: 'Sohne-Regular',
276
266
  fontSize: ms(14),
277
267
  },
278
- chevron: {
279
- marginLeft: s(4),
280
- },
281
268
  separator: {
282
269
  height: StyleSheet.hairlineWidth,
283
270
  marginRight: 0,