@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,12 +1,51 @@
1
- import React, { useRef, useEffect } from 'react'
2
- import { TouchableOpacity, Animated, StyleSheet, TouchableOpacityProps, ViewStyle, View, Easing, Platform } from 'react-native'
3
-
4
- const nativeDriver = Platform.OS !== 'web'
1
+ import React from 'react'
2
+ import { TouchableOpacity, StyleSheet, TouchableOpacityProps, ViewStyle, View } from 'react-native'
3
+ import Animated, {
4
+ useAnimatedStyle,
5
+ interpolateColor,
6
+ } from 'react-native-reanimated'
5
7
  import { FontAwesome5 } from '@expo/vector-icons'
6
8
  import { selectionAsync as hapticSelection } from '../../utils/haptics'
7
9
  import { useTheme } from '../../theme'
8
10
  import { s, vs, ms } from '../../utils/scaling'
9
11
  import { renderIcon } from '../../utils/icons'
12
+ import { usePressScale } from '../../utils/usePressScale'
13
+ import { useColorTransition } from '../../utils/useColorTransition'
14
+ import { PRESS_SCALE } from '../../utils/animations'
15
+
16
+ interface ToggleIconProps {
17
+ pressed: boolean
18
+ iconName?: string
19
+ activeIconName?: string
20
+ icon?: React.ReactNode | ((pressed: boolean) => React.ReactNode)
21
+ activeIcon?: React.ReactNode | ((pressed: boolean) => React.ReactNode)
22
+ iconColor?: string
23
+ activeIconColor?: string
24
+ iconSize: number
25
+ primaryColor: string
26
+ mutedColor: string
27
+ }
28
+
29
+ function ToggleIcon({ pressed, iconName, activeIconName, icon, activeIcon, iconColor, activeIconColor, iconSize, primaryColor, mutedColor }: ToggleIconProps) {
30
+ const renderProp = (prop?: React.ReactNode | ((p: boolean) => React.ReactNode)) => {
31
+ if (!prop) return null
32
+ if (typeof prop === 'function') return (prop as (p: boolean) => React.ReactNode)(pressed)
33
+ return prop
34
+ }
35
+
36
+ if (pressed) {
37
+ if (activeIconName) return <>{renderIcon(activeIconName, iconSize, activeIconColor ?? primaryColor)}</>
38
+ const active = renderProp(activeIcon)
39
+ if (active) return <>{active}</>
40
+ return <FontAwesome5 name="check-circle" size={iconSize} color={primaryColor} />
41
+ }
42
+
43
+ if (iconName) return <>{renderIcon(iconName, iconSize, iconColor ?? mutedColor)}</>
44
+ const custom = renderProp(icon)
45
+ if (custom) return <>{custom}</>
46
+
47
+ return <FontAwesome5 name="circle" size={iconSize} color={mutedColor} />
48
+ }
10
49
 
11
50
  export type ToggleVariant = 'default' | 'outline'
12
51
  export type ToggleSize = 'sm' | 'md' | 'lg'
@@ -59,96 +98,75 @@ export function Toggle({
59
98
  activeIconColor,
60
99
  disabled,
61
100
  style,
101
+ accessibilityLabel,
62
102
  ...props
63
103
  }: ToggleProps) {
64
104
  const { colors } = useTheme()
65
- const scale = useRef(new Animated.Value(1)).current
66
- // 0 = unpressed, 1 = pressed — used to interpolate colors (JS thread)
67
- const pressAnim = useRef(new Animated.Value(pressed ? 1 : 0)).current
68
-
69
- useEffect(() => {
70
- Animated.timing(pressAnim, {
71
- toValue: pressed ? 1 : 0,
72
- duration: 150,
73
- easing: Easing.out(Easing.ease),
74
- useNativeDriver: false,
75
- }).start()
76
- }, [pressed, pressAnim])
77
-
78
- const handlePressIn = () => {
79
- if (disabled) return
80
- Animated.spring(scale, { toValue: 0.95, useNativeDriver: nativeDriver, stiffness: 600, damping: 35, mass: 0.8 }).start()
81
- }
82
-
83
- const handlePressOut = () => {
84
- Animated.spring(scale, { toValue: 1, useNativeDriver: nativeDriver, stiffness: 280, damping: 22, mass: 0.8 }).start()
85
- }
86
-
87
- // Keep borderWidth constant at 2 to prevent layout jumps when pressing.
88
- // Animate borderColor and backgroundColor instead.
89
- const borderColor = pressAnim.interpolate({
90
- inputRange: [0, 1],
91
- outputRange: [variant === 'outline' ? colors.border : 'transparent', colors.primary],
105
+ const { animatedStyle: scaleStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
106
+ pressScale: PRESS_SCALE.button,
107
+ disabled,
92
108
  })
109
+ const progress = useColorTransition(pressed)
93
110
 
94
- const backgroundColor = pressAnim.interpolate({
95
- inputRange: [0, 1],
96
- outputRange: ['transparent', colors.surfaceStrong],
97
- })
111
+ const inactiveBorder = variant === 'outline' ? colors.border : 'transparent'
98
112
 
99
- const textColor = pressAnim.interpolate({
100
- inputRange: [0, 1],
101
- outputRange: [colors.foreground, colors.primary],
102
- })
113
+ const surfaceStyle = useAnimatedStyle(() => ({
114
+ borderColor: interpolateColor(progress.value, [0, 1], [inactiveBorder, colors.primary]),
115
+ backgroundColor: interpolateColor(progress.value, [0, 1], ['transparent', colors.surfaceStrong]),
116
+ }))
103
117
 
104
- const iconSize = iconSizeMap[size]
118
+ const textStyle = useAnimatedStyle(() => ({
119
+ color: interpolateColor(progress.value, [0, 1], [colors.foreground, colors.primary]),
120
+ }))
105
121
 
106
- const LeftIcon = () => {
107
- const renderProp = (prop?: any) => {
108
- if (!prop) return null
109
- if (typeof prop === 'function') return prop(pressed)
110
- return prop
111
- }
112
-
113
- if (pressed) {
114
- if (activeIconName) return <>{renderIcon(activeIconName, iconSize, activeIconColor ?? colors.primary)}</>
115
- const active = renderProp(activeIcon)
116
- if (active) return <>{active}</>
117
- return <FontAwesome5 name="check-circle" size={iconSize} color={colors.primary} />
118
- }
119
-
120
- if (iconName) return <>{renderIcon(iconName, iconSize, iconColor ?? colors.foregroundMuted)}</>
121
- const custom = renderProp(icon)
122
- if (custom) return <>{custom}</>
123
-
124
- // Default: empty circle to signal an action is available
125
- return <FontAwesome5 name="circle" size={iconSize} color={colors.foregroundMuted} />
126
- }
122
+ const iconSize = iconSizeMap[size]
127
123
 
128
124
  return (
129
- <Animated.View style={[{ transform: [{ scale }] }, disabled && styles.disabled, style]}>
125
+ <Animated.View
126
+ style={[scaleStyle, disabled && styles.disabled, style]}
127
+ {...hoverHandlers}
128
+ >
130
129
  <TouchableOpacity
131
130
  onPress={() => {
132
131
  hapticSelection()
133
132
  onPressedChange?.(!pressed)
134
133
  }}
135
- onPressIn={handlePressIn}
136
- onPressOut={handlePressOut}
134
+ onPressIn={onPressIn}
135
+ onPressOut={onPressOut}
137
136
  disabled={disabled}
138
137
  activeOpacity={1}
139
138
  touchSoundDisabled={true}
139
+ accessibilityRole="button"
140
+ accessibilityLabel={accessibilityLabel ?? label}
141
+ accessibilityState={{ selected: pressed, disabled: !!disabled }}
140
142
  {...props}
141
143
  >
142
144
  <Animated.View
143
145
  style={[
144
146
  styles.base,
145
147
  sizeStyles[size],
146
- { borderColor, backgroundColor, borderWidth: 2 },
148
+ { borderWidth: 2 },
149
+ surfaceStyle,
147
150
  ]}
148
151
  >
149
152
  <View style={styles.inner}>
150
- <LeftIcon />
151
- {label ? <Animated.Text style={[styles.label, { color: textColor }]} allowFontScaling={true}>{label}</Animated.Text> : null}
153
+ <ToggleIcon
154
+ pressed={pressed}
155
+ iconName={iconName}
156
+ activeIconName={activeIconName}
157
+ icon={icon}
158
+ activeIcon={activeIcon}
159
+ iconColor={iconColor}
160
+ activeIconColor={activeIconColor}
161
+ iconSize={iconSize}
162
+ primaryColor={colors.primary}
163
+ mutedColor={colors.foregroundMuted}
164
+ />
165
+ {label ? (
166
+ <Animated.Text style={[styles.label, textStyle]} allowFontScaling={true}>
167
+ {label}
168
+ </Animated.Text>
169
+ ) : null}
152
170
  </View>
153
171
  </Animated.View>
154
172
  </TouchableOpacity>
@@ -170,7 +188,7 @@ const styles = StyleSheet.create({
170
188
  opacity: 0.45,
171
189
  },
172
190
  label: {
173
- fontFamily: 'Poppins-Medium',
191
+ fontFamily: 'Sohne-Medium',
174
192
  fontSize: ms(14),
175
193
  },
176
194
  })
@@ -0,0 +1,60 @@
1
+ import React, { useCallback } from 'react'
2
+ import { FlatList, FlatListProps, ListRenderItem } from 'react-native'
3
+
4
+ export interface VirtualListItem {
5
+ id?: string | number
6
+ }
7
+
8
+ export interface VirtualListProps<T> extends Omit<FlatListProps<T>, 'getItemLayout'> {
9
+ /**
10
+ * Fixed row height in px. When provided, enables `getItemLayout` so the list
11
+ * skips async measurement — large datasets scroll and `scrollToIndex` jump
12
+ * without layout passes. Omit only for variable-height rows.
13
+ */
14
+ itemHeight?: number
15
+ }
16
+
17
+ const defaultKeyExtractor = <T,>(item: T, index: number): string => {
18
+ const id = (item as VirtualListItem | null)?.id
19
+ return id !== undefined ? String(id) : String(index)
20
+ }
21
+
22
+ /**
23
+ * Virtualized list primitive. Thin wrapper over `FlatList` with sane defaults
24
+ * for the common case: stable keys + optional fixed-height fast path.
25
+ *
26
+ * For 10k+ rows, pass `itemHeight` and a `React.memo`-wrapped `renderItem` so
27
+ * only on-screen rows mount and re-render.
28
+ *
29
+ * @example
30
+ * const renderItem = useCallback(({ item }) => <ListItem title={item.title} onPress={...} />, [])
31
+ * <VirtualList data={rows} renderItem={renderItem} itemHeight={64} />
32
+ */
33
+ function VirtualListInner<T>(
34
+ { itemHeight, keyExtractor, renderItem, ...props }: VirtualListProps<T>,
35
+ ref: React.Ref<FlatList<T>>,
36
+ ) {
37
+ const getItemLayout = useCallback(
38
+ (_data: ArrayLike<T> | null | undefined, index: number) => ({
39
+ length: itemHeight ?? 0,
40
+ offset: (itemHeight ?? 0) * index,
41
+ index,
42
+ }),
43
+ [itemHeight],
44
+ )
45
+
46
+ return (
47
+ <FlatList<T>
48
+ ref={ref}
49
+ keyExtractor={keyExtractor ?? defaultKeyExtractor}
50
+ renderItem={renderItem as ListRenderItem<T>}
51
+ getItemLayout={itemHeight !== undefined ? getItemLayout : undefined}
52
+ removeClippedSubviews
53
+ {...props}
54
+ />
55
+ )
56
+ }
57
+
58
+ export const VirtualList = React.forwardRef(VirtualListInner) as <T>(
59
+ props: VirtualListProps<T> & { ref?: React.Ref<FlatList<T>> },
60
+ ) => React.ReactElement
@@ -0,0 +1 @@
1
+ export * from './VirtualList'
package/src/fonts.ts CHANGED
@@ -1,30 +1,48 @@
1
1
  /**
2
- * Poppins font family required by @retray-dev/ui-kit components.
3
- *
2
+ * Sohne font family required by @retray-dev/ui-kit components.
3
+ *
4
4
  * Consumer apps must load these fonts at app root using expo-font:
5
- *
5
+ *
6
6
  * @example
7
7
  * import { useFonts } from 'expo-font'
8
- * import { PoppinsFonts } from '@retray-dev/ui-kit'
9
- *
8
+ * import { SohneFonts } from '@retray-dev/ui-kit/fonts'
9
+ *
10
10
  * function App() {
11
- * const [fontsLoaded] = useFonts(PoppinsFonts)
11
+ * const [fontsLoaded] = useFonts(SohneFonts)
12
12
  * if (!fontsLoaded) return null
13
13
  * // render app
14
14
  * }
15
15
  */
16
- export const PoppinsFonts = {
17
- 'Poppins-Thin': require('./assets/fonts/Poppins-Thin.ttf'),
18
- 'Poppins-ExtraLight': require('./assets/fonts/Poppins-ExtraLight.ttf'),
19
- 'Poppins-Light': require('./assets/fonts/Poppins-Light.ttf'),
20
- 'Poppins-Regular': require('./assets/fonts/Poppins-Regular.ttf'),
21
- 'Poppins-Medium': require('./assets/fonts/Poppins-Medium.ttf'),
22
- 'Poppins-SemiBold': require('./assets/fonts/Poppins-SemiBold.ttf'),
23
- 'Poppins-Bold': require('./assets/fonts/Poppins-Bold.ttf'),
24
- 'Poppins-ExtraBold': require('./assets/fonts/Poppins-ExtraBold.ttf'),
25
- 'Poppins-Black': require('./assets/fonts/Poppins-Black.ttf'),
26
- 'Poppins-Italic': require('./assets/fonts/Poppins-Italic.ttf'),
27
- 'Poppins-MediumItalic': require('./assets/fonts/Poppins-MediumItalic.ttf'),
28
- 'Poppins-SemiBoldItalic': require('./assets/fonts/Poppins-SemiBoldItalic.ttf'),
29
- 'Poppins-BoldItalic': require('./assets/fonts/Poppins-BoldItalic.ttf'),
16
+ /* eslint-disable @typescript-eslint/no-require-imports */
17
+ export const SohneFonts = {
18
+ // Sohne base
19
+ 'Sohne-ExtraLight': require('./assets/fonts/Sohne-ExtraLight.otf'),
20
+ 'Sohne-ExtraLightItalic': require('./assets/fonts/Sohne-ExtraLightItalic.otf'),
21
+ 'Sohne-Light': require('./assets/fonts/Sohne-Light.otf'),
22
+ 'Sohne-LightItalic': require('./assets/fonts/Sohne-LightItalic.otf'),
23
+ 'Sohne-Regular': require('./assets/fonts/Sohne-Regular.otf'),
24
+ 'Sohne-Italic': require('./assets/fonts/Sohne-Italic.otf'),
25
+ 'Sohne-Medium': require('./assets/fonts/Sohne-Medium.otf'),
26
+ 'Sohne-MediumItalic': require('./assets/fonts/Sohne-MediumItalic.otf'),
27
+ 'Sohne-SemiBold': require('./assets/fonts/Sohne-SemiBold.otf'),
28
+ 'Sohne-SemiBoldItalic': require('./assets/fonts/Sohne-SemiBoldItalic.otf'),
29
+ 'Sohne-Bold': require('./assets/fonts/Sohne-Bold.otf'),
30
+ 'Sohne-BoldItalic': require('./assets/fonts/Sohne-BoldItalic.otf'),
31
+ 'Sohne-ExtraBold': require('./assets/fonts/Sohne-ExtraBold.otf'),
32
+ 'Sohne-ExtraBoldItalic': require('./assets/fonts/Sohne-ExtraBoldItalic.otf'),
33
+ // SohneMono
34
+ 'SohneMono-ExtraLight': require('./assets/fonts/SohneMono-ExtraLight.otf'),
35
+ 'SohneMono-ExtraLightItalic': require('./assets/fonts/SohneMono-ExtraLightItalic.otf'),
36
+ 'SohneMono-Light': require('./assets/fonts/SohneMono-Light.otf'),
37
+ 'SohneMono-LightItalic': require('./assets/fonts/SohneMono-LightItalic.otf'),
38
+ 'SohneMono-Regular': require('./assets/fonts/SohneMono-Regular.otf'),
39
+ 'SohneMono-Italic': require('./assets/fonts/SohneMono-Italic.otf'),
40
+ 'SohneMono-Medium': require('./assets/fonts/SohneMono-Medium.otf'),
41
+ 'SohneMono-MediumItalic': require('./assets/fonts/SohneMono-MediumItalic.otf'),
42
+ 'SohneMono-SemiBold': require('./assets/fonts/SohneMono-SemiBold.otf'),
43
+ 'SohneMono-SemiBoldItalic': require('./assets/fonts/SohneMono-SemiBoldItalic.otf'),
44
+ 'SohneMono-Bold': require('./assets/fonts/SohneMono-Bold.otf'),
45
+ 'SohneMono-BoldItalic': require('./assets/fonts/SohneMono-BoldItalic.otf'),
46
+ 'SohneMono-ExtraBold': require('./assets/fonts/SohneMono-ExtraBold.otf'),
47
+ 'SohneMono-ExtraBoldItalic': require('./assets/fonts/SohneMono-ExtraBoldItalic.otf'),
30
48
  } as const
package/src/index.ts CHANGED
@@ -34,7 +34,9 @@ export * from './components/CurrencyDisplay'
34
34
  // CurrencyInputLarge is deprecated — use <CurrencyInput size="large" /> instead
35
35
  export { CurrencyInput as CurrencyInputLarge } from './components/CurrencyInput'
36
36
  export * from './components/ListItem'
37
+ export * from './components/ListGroup'
37
38
  export * from './components/MenuItem'
39
+ export * from './components/MenuGroup'
38
40
  export * from './components/Chip'
39
41
  export * from './components/ConfirmDialog'
40
42
  export * from './components/LabelValue'
@@ -43,9 +45,11 @@ export * from './components/MediaCard'
43
45
  export * from './components/CategoryStrip'
44
46
  export * from './components/Pressable'
45
47
  export * from './components/DetailRow'
48
+ export * from './components/Form'
49
+ export * from './components/VirtualList'
46
50
 
47
51
  // Icon utility
48
- export { Icon, renderIcon } from './utils/icons'
52
+ export { Icon, renderIcon, configureIconFamilies } from './utils/icons'
49
53
 
50
54
  // Typography utilities
51
55
  export { getResponsiveFontSize } from './utils/typography'
@@ -1,61 +1,74 @@
1
1
  import { ThemeColors, ResolvedColors } from './types'
2
- import { mixWithBackground, withAlphaOnWhite, withAlphaOnDark, lighten, darken, isDark } from './colorUtils'
2
+ import { mixWithBackground, withAlphaOnWhite, withAlphaOnDark, lighten, darken } from './colorUtils'
3
3
 
4
- // ─── Default palettes (12 tokens each) ───────────────────────────────────────
5
- // These are the only values consumers need to override.
6
- // Designed to look great out of the box with no customization.
4
+ // ─── Default palettes ─────────────────────────────────────────────────────────
5
+ // AUDIT FIXES applied:
6
+ // · accent + accentForeground added system was fully achromatic
7
+ // · warning darkened: #e67e00 → #9a5200 (white text: 2.86:1 → 5.86:1 ✓ AA)
8
+ // · destructive darkened: #e53935 → #c72828 (white text: 4.22:1 → 5.59:1 ✓ AA)
9
+ // · foreground tweaked #222222 → #1a1a1a (matches primary, 16.1:1 on white ✓)
7
10
 
8
11
  export const defaultLight: ThemeColors = {
9
- background: '#ffffff',
10
- foreground: '#222222', // Airbnb ink — deep near-black, never pure black
11
- card: '#ffffff',
12
- primary: '#1a1a1a', // Near-black primary — clean, premium default
13
- primaryForeground: '#ffffff',
14
- border: '#dddddd', // Airbnb hairline light, airy
15
- destructive: '#e53935',
12
+ background: '#ffffff',
13
+ foreground: '#1a1a1a',
14
+ card: '#ffffff',
15
+ primary: '#1a1a1a',
16
+ primaryForeground: '#ffffff',
17
+ // AUDIT FIX: brand accentwas undefined; falls back to primary when omitted
18
+ accent: '#d4561d',
19
+ accentForeground: '#ffffff',
20
+ border: '#dddddd',
21
+ // AUDIT FIX: was #e53935 (4.22:1 on white — fails AA); #c72828 = 5.59:1 ✓
22
+ destructive: '#c72828',
16
23
  destructiveForeground: '#ffffff',
17
- success: '#1a7a45',
18
- successForeground: '#ffffff',
19
- warning: '#e67e00',
20
- warningForeground: '#ffffff',
24
+ success: '#1a7a45',
25
+ successForeground: '#ffffff',
26
+ // AUDIT FIX: was #e67e00 (2.86:1 — severe fail); #9a5200 = 5.86:1 ✓ AAA-near
27
+ warning: '#9a5200',
28
+ warningForeground: '#ffffff',
21
29
  }
22
30
 
23
31
  export const defaultDark: ThemeColors = {
24
- background: '#0f0f0f',
25
- foreground: '#fafafa',
26
- card: '#1c1c1c',
27
- primary: '#fafafa',
28
- primaryForeground: '#0f0f0f',
29
- border: '#303030',
30
- destructive: '#ef5350',
32
+ background: '#0f0f0f',
33
+ foreground: '#fafafa',
34
+ card: '#1c1c1c',
35
+ primary: '#fafafa',
36
+ primaryForeground: '#0f0f0f',
37
+ // AUDIT FIX: lighter accent for dark surfaces (warm amber-orange)
38
+ accent: '#e87645',
39
+ accentForeground: '#ffffff',
40
+ border: '#303030',
41
+ destructive: '#ef5350',
31
42
  destructiveForeground: '#ffffff',
32
- success: '#2e7d52',
33
- successForeground: '#ffffff',
34
- warning: '#f57c00',
35
- warningForeground: '#ffffff',
43
+ success: '#2e7d52',
44
+ successForeground: '#ffffff',
45
+ // AUDIT FIX: brighter amber for dark-bg visibility; dark text for contrast
46
+ // #f5a623 on #0f0f0f = 8.6:1 ✓ as indicator; #0f0f0f text on #f5a623 = 8.6:1 ✓
47
+ warning: '#f5a623',
48
+ warningForeground: '#0f0f0f',
36
49
  }
37
50
 
38
51
  // ─── Color derivation ─────────────────────────────────────────────────────────
39
- // Takes 12 public tokens → produces full ResolvedColors for component consumption.
40
- // Dark mode uses bg-blended tints instead of white-blended to stay on-palette.
41
-
42
52
  export function deriveColors(t: ThemeColors, scheme: 'light' | 'dark'): ResolvedColors {
43
53
  const dark = scheme === 'dark'
44
54
  const bg = t.background
45
55
 
46
- // Text hierarchy: foreground mixed with background at different opacities
47
- const foregroundSubtle = mixWithBackground(t.foreground, bg, 0.55)
48
- const foregroundMuted = mixWithBackground(t.foreground, bg, 0.38)
56
+ // AUDIT FIX: Text hierarchy opacities raised to pass WCAG AA.
57
+ // foregroundSubtle was 0.55 → ~#858585 (3.5:1 fail on white)
58
+ // foregroundMuted was 0.38 → ~#ababab (2.2:1 critical fail on white)
59
+ // New values on light (#1a1a1a on #ffffff):
60
+ // foregroundSubtle 0.70 → ~#646464 (5.9:1 ✓ AA)
61
+ // foregroundMuted 0.62 → ~#767676 (4.5:1 ✓ AA minimum)
62
+ const foregroundSubtle = mixWithBackground(t.foreground, bg, 0.70)
63
+ const foregroundMuted = mixWithBackground(t.foreground, bg, 0.62)
49
64
 
50
- // Surface fills: slight offset from background
51
65
  const surface = dark
52
- ? lighten(bg, -0.06) // slightly lighter than dark bg
53
- : darken(bg, 0.04) // slightly darker than white → #f7f7f7 equivalent
66
+ ? lighten(bg, -0.06)
67
+ : darken(bg, 0.04)
54
68
  const surfaceStrong = dark
55
69
  ? lighten(bg, -0.12)
56
- : darken(bg, 0.08) // #ebebeb equivalent
70
+ : darken(bg, 0.08)
57
71
 
58
- // Semantic tints: color blended toward background
59
72
  const destructiveTint = dark
60
73
  ? withAlphaOnDark(t.destructive, 0.15, bg)
61
74
  : withAlphaOnWhite(t.destructive, 0.08)
@@ -92,7 +105,8 @@ export function deriveColors(t: ThemeColors, scheme: 'light' | 'dark'): Resolved
92
105
  overlay: t.overlay ?? 'rgba(0,0,0,0.45)',
93
106
  accentResolved: t.accent ?? t.primary,
94
107
  accentForegroundResolved: t.accentForeground ?? t.primaryForeground,
95
- ring: t.primary, // focus ring always = primary
96
- input: t.border, // input border always = border
108
+ ring: t.accent ?? t.primary,
109
+ input: t.border,
110
+ separator: dark ? lighten(t.border, 0.22) : darken(t.border, 0.16),
97
111
  }
98
112
  }
@@ -49,6 +49,9 @@ export type ResolvedColors = ThemeColors & {
49
49
  // Aliases (ring + input always equal primary + border for coherence)
50
50
  ring: string // = primary
51
51
  input: string // = border
52
+
53
+ // Divider/separator line — deliberately darker than border for visibility
54
+ separator: string
52
55
  }
53
56
 
54
57
  export type Theme = {