@retray-dev/ui-kit 7.0.1 → 9.1.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 (234) hide show
  1. package/COMPONENTS.md +567 -14
  2. package/EXAMPLES.md +21 -14
  3. package/README.md +14 -8
  4. package/dist/Accordion.js +57 -5
  5. package/dist/Accordion.mjs +4 -3
  6. package/dist/AlertBanner.js +4 -1
  7. package/dist/AlertBanner.mjs +3 -2
  8. package/dist/AppHeader.d.mts +40 -0
  9. package/dist/AppHeader.d.ts +40 -0
  10. package/dist/AppHeader.js +515 -0
  11. package/dist/AppHeader.mjs +10 -0
  12. package/dist/Avatar.js +39 -29
  13. package/dist/Avatar.mjs +2 -1
  14. package/dist/Badge.js +11 -1
  15. package/dist/Badge.mjs +2 -1
  16. package/dist/Button.d.mts +8 -3
  17. package/dist/Button.d.ts +8 -3
  18. package/dist/Button.js +126 -108
  19. package/dist/Button.mjs +6 -5
  20. package/dist/ButtonGroup.mjs +1 -0
  21. package/dist/Card.js +90 -70
  22. package/dist/Card.mjs +5 -4
  23. package/dist/CategoryStrip.js +79 -22
  24. package/dist/CategoryStrip.mjs +6 -6
  25. package/dist/Checkbox.js +118 -86
  26. package/dist/Checkbox.mjs +5 -5
  27. package/dist/Chip.js +113 -80
  28. package/dist/Chip.mjs +5 -5
  29. package/dist/ConfirmDialog.js +140 -110
  30. package/dist/ConfirmDialog.mjs +7 -6
  31. package/dist/CurrencyDisplay.mjs +1 -0
  32. package/dist/CurrencyInput.d.mts +1 -1
  33. package/dist/CurrencyInput.d.ts +1 -1
  34. package/dist/CurrencyInput.js +9 -5
  35. package/dist/CurrencyInput.mjs +5 -4
  36. package/dist/DetailRow.mjs +1 -0
  37. package/dist/EmptyState.js +131 -111
  38. package/dist/EmptyState.mjs +7 -6
  39. package/dist/ErrorBoundary.d.mts +42 -0
  40. package/dist/ErrorBoundary.d.ts +42 -0
  41. package/dist/ErrorBoundary.js +351 -0
  42. package/dist/ErrorBoundary.mjs +7 -0
  43. package/dist/Form.mjs +1 -0
  44. package/dist/HolographicCard.d.mts +55 -0
  45. package/dist/HolographicCard.d.ts +55 -0
  46. package/dist/HolographicCard.js +316 -0
  47. package/dist/HolographicCard.mjs +191 -0
  48. package/dist/IconButton.d.mts +8 -3
  49. package/dist/IconButton.d.ts +8 -3
  50. package/dist/IconButton.js +115 -98
  51. package/dist/IconButton.mjs +5 -4
  52. package/dist/ImageViewer.d.mts +23 -0
  53. package/dist/ImageViewer.d.ts +23 -0
  54. package/dist/ImageViewer.js +582 -0
  55. package/dist/ImageViewer.mjs +8 -0
  56. package/dist/Input.mjs +4 -3
  57. package/dist/LabelValue.mjs +1 -0
  58. package/dist/ListGroup.mjs +1 -0
  59. package/dist/ListItem.js +131 -117
  60. package/dist/ListItem.mjs +6 -5
  61. package/dist/MediaCard.js +54 -6
  62. package/dist/MediaCard.mjs +6 -5
  63. package/dist/MenuGroup.mjs +1 -0
  64. package/dist/MenuItem.js +91 -79
  65. package/dist/MenuItem.mjs +6 -5
  66. package/dist/MonthPicker.d.mts +10 -2
  67. package/dist/MonthPicker.d.ts +10 -2
  68. package/dist/MonthPicker.js +80 -17
  69. package/dist/MonthPicker.mjs +3 -2
  70. package/dist/PagerDots.d.mts +35 -0
  71. package/dist/PagerDots.d.ts +35 -0
  72. package/dist/PagerDots.js +392 -0
  73. package/dist/PagerDots.mjs +7 -0
  74. package/dist/Pressable.d.mts +5 -5
  75. package/dist/Pressable.d.ts +5 -5
  76. package/dist/Pressable.js +97 -86
  77. package/dist/Pressable.mjs +5 -4
  78. package/dist/PricingCard.d.mts +50 -0
  79. package/dist/PricingCard.d.ts +50 -0
  80. package/dist/PricingCard.js +636 -0
  81. package/dist/PricingCard.mjs +11 -0
  82. package/dist/Progress.mjs +3 -2
  83. package/dist/RadioGroup.js +81 -30
  84. package/dist/RadioGroup.mjs +5 -5
  85. package/dist/RetrayProvider.d.mts +2 -0
  86. package/dist/RetrayProvider.d.ts +2 -0
  87. package/dist/RetrayProvider.js +214 -0
  88. package/dist/RetrayProvider.mjs +5 -0
  89. package/dist/Select.js +51 -4
  90. package/dist/Select.mjs +5 -4
  91. package/dist/SelectableGrid.d.mts +44 -0
  92. package/dist/SelectableGrid.d.ts +44 -0
  93. package/dist/SelectableGrid.js +448 -0
  94. package/dist/SelectableGrid.mjs +9 -0
  95. package/dist/Separator.mjs +1 -0
  96. package/dist/Sheet.d.mts +13 -1
  97. package/dist/Sheet.d.ts +13 -1
  98. package/dist/Sheet.js +115 -5
  99. package/dist/Sheet.mjs +4 -2
  100. package/dist/Skeleton.d.mts +50 -0
  101. package/dist/Skeleton.d.ts +50 -0
  102. package/dist/Skeleton.js +61 -0
  103. package/dist/Skeleton.mjs +4 -2
  104. package/dist/Slider.js +51 -4
  105. package/dist/Slider.mjs +3 -2
  106. package/dist/Spinner.js +28 -7
  107. package/dist/Spinner.mjs +2 -1
  108. package/dist/Switch.js +98 -48
  109. package/dist/Switch.mjs +4 -3
  110. package/dist/TabBar.d.mts +42 -0
  111. package/dist/TabBar.d.ts +42 -0
  112. package/dist/TabBar.js +361 -0
  113. package/dist/TabBar.mjs +6 -0
  114. package/dist/Tabs.js +92 -62
  115. package/dist/Tabs.mjs +5 -4
  116. package/dist/Text.js +16 -0
  117. package/dist/Text.mjs +2 -1
  118. package/dist/Textarea.mjs +4 -3
  119. package/dist/Toast.d.mts +7 -7
  120. package/dist/Toast.d.ts +7 -7
  121. package/dist/Toast.mjs +1 -0
  122. package/dist/Toggle.d.mts +6 -3
  123. package/dist/Toggle.d.ts +6 -3
  124. package/dist/Toggle.js +135 -120
  125. package/dist/Toggle.mjs +5 -5
  126. package/dist/VirtualList.mjs +1 -0
  127. package/dist/{chunk-7H2OR44A.mjs → chunk-26BCI223.mjs} +1 -1
  128. package/dist/{chunk-CRYBX2CM.mjs → chunk-2TFTAWVJ.mjs} +44 -59
  129. package/dist/chunk-3DKJ2GIC.mjs +30 -0
  130. package/dist/{chunk-KWCPOM6W.mjs → chunk-3U4SSNWP.mjs} +32 -48
  131. package/dist/chunk-4I7D47FH.mjs +139 -0
  132. package/dist/chunk-4K625MVM.mjs +142 -0
  133. package/dist/{chunk-MN7OG7IY.mjs → chunk-6OAZJ577.mjs} +6 -4
  134. package/dist/{chunk-L7E7TVEZ.mjs → chunk-756RAKE4.mjs} +2 -2
  135. package/dist/{chunk-HSPSMN6U.mjs → chunk-7QHVVCB3.mjs} +2 -2
  136. package/dist/{chunk-URLL5JBR.mjs → chunk-A3A6KNQN.mjs} +3 -3
  137. package/dist/chunk-AJ7ZDNBT.mjs +120 -0
  138. package/dist/{chunk-FTLJOUOQ.mjs → chunk-AV4EMIRH.mjs} +25 -28
  139. package/dist/chunk-AZJF2BLK.mjs +115 -0
  140. package/dist/chunk-BNP626TY.mjs +159 -0
  141. package/dist/{chunk-5IKW3VNC.mjs → chunk-DVK4G2GT.mjs} +17 -1
  142. package/dist/{chunk-6LQYY7HC.mjs → chunk-EH745HE5.mjs} +2 -2
  143. package/dist/chunk-EJ7ZPXOH.mjs +163 -0
  144. package/dist/{chunk-RKLHUDZS.mjs → chunk-GD6KXMG5.mjs} +29 -15
  145. package/dist/{chunk-RR2VQLKE.mjs → chunk-GQYFLP3D.mjs} +14 -17
  146. package/dist/{chunk-Y6MXOREN.mjs → chunk-ID72TK46.mjs} +8 -17
  147. package/dist/{chunk-NQGVLMWG.mjs → chunk-JMOZEC77.mjs} +1 -1
  148. package/dist/{chunk-GCWOGZYL.mjs → chunk-JT7HKXRB.mjs} +39 -29
  149. package/dist/{chunk-LWG526VX.mjs → chunk-KIHCWCWL.mjs} +47 -62
  150. package/dist/chunk-LXJIIOYQ.mjs +104 -0
  151. package/dist/{chunk-SBZYEV4S.mjs → chunk-M6ZXVBTK.mjs} +5 -2
  152. package/dist/{chunk-XDMN67KV.mjs → chunk-MAC465BB.mjs} +10 -8
  153. package/dist/chunk-MBMXYJJV.mjs +36 -0
  154. package/dist/chunk-MLF3EZFW.mjs +119 -0
  155. package/dist/chunk-NA7PARID.mjs +147 -0
  156. package/dist/{chunk-QXGYKWI7.mjs → chunk-O3HA6TYM.mjs} +9 -4
  157. package/dist/{chunk-63357L2X.mjs → chunk-OB4JUQ3O.mjs} +1 -1
  158. package/dist/{chunk-AU2VDY4P.mjs → chunk-PFZTM6D5.mjs} +52 -4
  159. package/dist/chunk-QKH5ZOD5.mjs +97 -0
  160. package/dist/{chunk-KZJRQOIU.mjs → chunk-TERDKCLE.mjs} +11 -1
  161. package/dist/{chunk-U4N7WF4Z.mjs → chunk-UREA2GYY.mjs} +28 -23
  162. package/dist/{chunk-TAJ2PQ2O.mjs → chunk-VGTDN7SW.mjs} +7 -6
  163. package/dist/{chunk-URDE3EUU.mjs → chunk-VQ57HWPL.mjs} +27 -15
  164. package/dist/chunk-WBOOUHSS.mjs +62 -0
  165. package/dist/{chunk-GNGLDL6Z.mjs → chunk-WJLKJMKR.mjs} +18 -0
  166. package/dist/{chunk-YZJAFS4P.mjs → chunk-X4G6APW6.mjs} +22 -19
  167. package/dist/chunk-Y6FXYEAI.mjs +8 -0
  168. package/dist/chunk-YFZ3ELX5.mjs +16 -0
  169. package/dist/{chunk-QCNARS3X.mjs → chunk-YNROWHQJ.mjs} +1 -1
  170. package/dist/chunk-Z4BVUWW6.mjs +196 -0
  171. package/dist/{chunk-GPOUINK5.mjs → chunk-ZJKGQMYH.mjs} +10 -27
  172. package/dist/index-wt-orHUi.d.mts +85 -0
  173. package/dist/index-wt-orHUi.d.ts +85 -0
  174. package/dist/index.d.mts +59 -51
  175. package/dist/index.d.ts +59 -51
  176. package/dist/index.js +1940 -744
  177. package/dist/index.mjs +49 -39
  178. package/package.json +35 -5
  179. package/src/components/Accordion/Accordion.tsx +12 -1
  180. package/src/components/AlertBanner/AlertBanner.tsx +5 -0
  181. package/src/components/AppHeader/AppHeader.tsx +172 -0
  182. package/src/components/AppHeader/index.ts +1 -0
  183. package/src/components/Avatar/Avatar.tsx +10 -2
  184. package/src/components/Badge/Badge.tsx +8 -1
  185. package/src/components/Button/Button.tsx +20 -27
  186. package/src/components/Card/Card.tsx +12 -23
  187. package/src/components/CategoryStrip/CategoryStrip.tsx +17 -21
  188. package/src/components/Checkbox/Checkbox.tsx +26 -40
  189. package/src/components/Chip/Chip.tsx +24 -33
  190. package/src/components/CurrencyInput/CurrencyInput.tsx +10 -8
  191. package/src/components/EmptyState/EmptyState.tsx +2 -1
  192. package/src/components/ErrorBoundary/ErrorBoundary.tsx +153 -0
  193. package/src/components/ErrorBoundary/index.ts +1 -0
  194. package/src/components/HolographicCard/HolographicCard.tsx +315 -0
  195. package/src/components/HolographicCard/index.ts +1 -0
  196. package/src/components/IconButton/IconButton.tsx +19 -27
  197. package/src/components/ImageViewer/ImageViewer.tsx +290 -0
  198. package/src/components/ImageViewer/index.ts +1 -0
  199. package/src/components/ListItem/ListItem.tsx +70 -67
  200. package/src/components/MediaCard/MediaCard.tsx +8 -2
  201. package/src/components/MenuItem/MenuItem.tsx +10 -25
  202. package/src/components/MonthPicker/MonthPicker.tsx +39 -13
  203. package/src/components/MonthPicker/index.ts +1 -1
  204. package/src/components/PagerDots/PagerDots.tsx +200 -0
  205. package/src/components/PagerDots/index.ts +1 -0
  206. package/src/components/Pressable/Pressable.tsx +19 -35
  207. package/src/components/PricingCard/PricingCard.tsx +220 -0
  208. package/src/components/PricingCard/index.ts +1 -0
  209. package/src/components/RadioGroup/RadioGroup.tsx +14 -27
  210. package/src/components/RetrayProvider/RetrayProvider.tsx +59 -0
  211. package/src/components/RetrayProvider/index.ts +1 -0
  212. package/src/components/SelectableGrid/SelectableGrid.tsx +205 -0
  213. package/src/components/SelectableGrid/index.ts +1 -0
  214. package/src/components/Sheet/Sheet.tsx +65 -1
  215. package/src/components/Skeleton/Skeleton.tsx +142 -1
  216. package/src/components/Spinner/Spinner.tsx +17 -2
  217. package/src/components/Switch/Switch.tsx +30 -58
  218. package/src/components/TabBar/TabBar.tsx +169 -0
  219. package/src/components/TabBar/index.ts +1 -0
  220. package/src/components/Tabs/Tabs.tsx +23 -26
  221. package/src/components/Text/Text.tsx +2 -0
  222. package/src/components/Toggle/Toggle.tsx +35 -51
  223. package/src/fonts.ts +4 -1
  224. package/src/index.ts +23 -2
  225. package/src/utils/animations.ts +29 -1
  226. package/src/utils/fontGuard.ts +34 -0
  227. package/src/utils/haptics.ts +211 -9
  228. package/src/utils/pressable.ts +66 -0
  229. package/dist/chunk-76PFOSM2.mjs +0 -41
  230. package/dist/chunk-DITNP6PL.mjs +0 -106
  231. package/dist/chunk-JBLL7U3U.mjs +0 -64
  232. package/dist/chunk-LG4DO3DK.mjs +0 -174
  233. package/dist/chunk-RMMK64W5.mjs +0 -54
  234. package/dist/chunk-RTC3CFXF.mjs +0 -29
package/dist/index.mjs CHANGED
@@ -1,52 +1,62 @@
1
- export { Toggle } from './chunk-LWG526VX.mjs';
1
+ export { Toggle } from './chunk-KIHCWCWL.mjs';
2
2
  export { VirtualList } from './chunk-NC5ZTR2Y.mjs';
3
- export { Skeleton } from './chunk-JBLL7U3U.mjs';
4
- export { Slider } from './chunk-NQGVLMWG.mjs';
5
- export { Spinner } from './chunk-76PFOSM2.mjs';
6
- export { Switch } from './chunk-DITNP6PL.mjs';
7
- export { Tabs, TabsContent } from './chunk-RR2VQLKE.mjs';
8
- export { Text } from './chunk-GNGLDL6Z.mjs';
9
- export { Textarea } from './chunk-6LQYY7HC.mjs';
3
+ export { Skeleton } from './chunk-AJ7ZDNBT.mjs';
4
+ export { Slider } from './chunk-JMOZEC77.mjs';
5
+ export { Spinner } from './chunk-WBOOUHSS.mjs';
6
+ export { Switch } from './chunk-QKH5ZOD5.mjs';
7
+ export { TabBar } from './chunk-MLF3EZFW.mjs';
8
+ export { Tabs, TabsContent } from './chunk-GQYFLP3D.mjs';
9
+ export { Text } from './chunk-WJLKJMKR.mjs';
10
+ export { Textarea } from './chunk-EH745HE5.mjs';
11
+ export { PricingCard } from './chunk-4I7D47FH.mjs';
12
+ export { Progress } from './chunk-OB4JUQ3O.mjs';
13
+ export { RadioGroup } from './chunk-X4G6APW6.mjs';
14
+ export { RetrayProvider } from './chunk-YFZ3ELX5.mjs';
10
15
  export { ToastProvider, sonnerToast as toast, useToast } from './chunk-2UYENBLV.mjs';
11
- export { MenuItem } from './chunk-GPOUINK5.mjs';
12
- export { MonthPicker } from './chunk-RKLHUDZS.mjs';
13
- export { Pressable } from './chunk-RMMK64W5.mjs';
14
- export { Progress } from './chunk-63357L2X.mjs';
15
- export { RadioGroup } from './chunk-YZJAFS4P.mjs';
16
- export { Select } from './chunk-URLL5JBR.mjs';
16
+ export { Select } from './chunk-A3A6KNQN.mjs';
17
+ export { SelectableGrid } from './chunk-NA7PARID.mjs';
17
18
  export { Separator } from './chunk-MX6HRKMI.mjs';
18
- export { BottomSheetModalProvider, Sheet, BottomSheetTextInput as SheetTextInput } from './chunk-AU2VDY4P.mjs';
19
- export { Form, FormField, FormFooter, FormSection } from './chunk-6Q64UFIA.mjs';
20
- export { IconButton } from './chunk-KWCPOM6W.mjs';
21
- export { LabelValue } from './chunk-A4MDAP7G.mjs';
19
+ export { BottomSheetModalProvider, Sheet, BottomSheetTextInput as SheetTextInput } from './chunk-PFZTM6D5.mjs';
22
20
  export { ListGroup, ListGroupFooter, ListGroupHeader } from './chunk-SOA2Z4RB.mjs';
23
- export { ListItem } from './chunk-LG4DO3DK.mjs';
24
- export { MediaCard } from './chunk-TAJ2PQ2O.mjs';
21
+ export { ListItem } from './chunk-BNP626TY.mjs';
22
+ export { MediaCard } from './chunk-VGTDN7SW.mjs';
25
23
  export { MenuGroup, MenuGroupFooter, MenuGroupHeader } from './chunk-IRRY3CRZ.mjs';
26
- export { CategoryStrip } from './chunk-URDE3EUU.mjs';
27
- export { Checkbox } from './chunk-FTLJOUOQ.mjs';
28
- export { Chip, ChipGroup } from './chunk-U4N7WF4Z.mjs';
29
- export { ConfirmDialog } from './chunk-HSPSMN6U.mjs';
24
+ export { MenuItem } from './chunk-ZJKGQMYH.mjs';
25
+ export { MonthPicker, dateToMonthPickerValue, monthPickerValueToDate } from './chunk-GD6KXMG5.mjs';
26
+ export { Pressable } from './chunk-MBMXYJJV.mjs';
27
+ export { EmptyState } from './chunk-6OAZJ577.mjs';
28
+ export { ErrorBoundary } from './chunk-LXJIIOYQ.mjs';
29
+ export { Form, FormField, FormFooter, FormSection } from './chunk-6Q64UFIA.mjs';
30
+ export { ImageViewer } from './chunk-Z4BVUWW6.mjs';
31
+ export { PagerDots } from './chunk-4K625MVM.mjs';
32
+ export { LabelValue } from './chunk-A4MDAP7G.mjs';
33
+ export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './chunk-ID72TK46.mjs';
34
+ export { CategoryStrip } from './chunk-VQ57HWPL.mjs';
35
+ import './chunk-YNROWHQJ.mjs';
36
+ export { Checkbox } from './chunk-AV4EMIRH.mjs';
37
+ export { Chip, ChipGroup } from './chunk-UREA2GYY.mjs';
38
+ export { ConfirmDialog } from './chunk-7QHVVCB3.mjs';
30
39
  export { CurrencyDisplay } from './chunk-BRKYVJVV.mjs';
31
- export { CurrencyInput, CurrencyInput as CurrencyInputLarge } from './chunk-XDMN67KV.mjs';
32
- export { Input } from './chunk-L7E7TVEZ.mjs';
33
- import './chunk-7H2OR44A.mjs';
40
+ export { CurrencyInput } from './chunk-MAC465BB.mjs';
41
+ export { Input } from './chunk-756RAKE4.mjs';
42
+ import './chunk-26BCI223.mjs';
34
43
  export { DetailRow } from './chunk-JB67UOB5.mjs';
35
- export { EmptyState } from './chunk-MN7OG7IY.mjs';
36
- export { Accordion } from './chunk-QXGYKWI7.mjs';
37
- export { AlertBanner } from './chunk-SBZYEV4S.mjs';
38
- export { Avatar } from './chunk-GCWOGZYL.mjs';
39
- export { Badge } from './chunk-KZJRQOIU.mjs';
40
- export { Button } from './chunk-CRYBX2CM.mjs';
41
- export { Icon, configureIconFamilies, renderIcon } from './chunk-T7XZ7H7Y.mjs';
42
- export { ButtonGroup } from './chunk-3BBOZ3OQ.mjs';
43
- export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './chunk-Y6MXOREN.mjs';
44
- import './chunk-QCNARS3X.mjs';
45
- import './chunk-RTC3CFXF.mjs';
46
- import './chunk-5IKW3VNC.mjs';
44
+ export { Accordion } from './chunk-O3HA6TYM.mjs';
45
+ export { AlertBanner } from './chunk-M6ZXVBTK.mjs';
46
+ export { AppHeader } from './chunk-AZJF2BLK.mjs';
47
+ export { IconButton } from './chunk-3U4SSNWP.mjs';
48
+ export { Avatar } from './chunk-JT7HKXRB.mjs';
49
+ export { Badge } from './chunk-TERDKCLE.mjs';
50
+ export { Button } from './chunk-2TFTAWVJ.mjs';
51
+ import './chunk-3DKJ2GIC.mjs';
52
+ export { impactHeavy, impactLight, impactMedium, notificationError, notificationSuccess, notificationWarning, richHaptics, selectionAsync } from './chunk-EJ7ZPXOH.mjs';
53
+ import './chunk-DVK4G2GT.mjs';
47
54
  export { BREAKPOINTS, ICON_SIZES, RADIUS, SHADOWS, SPACING, TYPOGRAPHY } from './chunk-QY3X2UYR.mjs';
55
+ export { Icon, configureIconFamilies, renderIcon } from './chunk-T7XZ7H7Y.mjs';
48
56
  export { ThemeProvider, defaultDark, defaultLight, deriveColors, useTheme } from './chunk-SOYNZDVY.mjs';
57
+ export { ButtonGroup } from './chunk-3BBOZ3OQ.mjs';
49
58
  import './chunk-2CE3TQVY.mjs';
59
+ import './chunk-Y6FXYEAI.mjs';
50
60
 
51
61
  // src/utils/typography.ts
52
62
  function getResponsiveFontSize(text, maxSize, steps = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retray-dev/ui-kit",
3
- "version": "7.0.1",
3
+ "version": "9.1.0",
4
4
  "description": "Personal UI Kit for React Native / Expo",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -28,13 +28,16 @@
28
28
  "build": "tsup",
29
29
  "dev": "tsup --watch",
30
30
  "typecheck": "tsc --noEmit",
31
+ "test": "jest",
32
+ "test:watch": "jest --watch",
33
+ "test:coverage": "jest --coverage",
31
34
  "lint": "eslint src",
32
35
  "lint:fix": "eslint src --fix",
33
36
  "format": "prettier --write src",
34
37
  "format:check": "prettier --check src",
35
38
  "lint:all": "pnpm lint && pnpm --filter retray-ui-kit-example lint",
36
39
  "format:all": "pnpm format && pnpm --filter retray-ui-kit-example format",
37
- "verify": "pnpm typecheck && pnpm lint && pnpm build",
40
+ "verify": "pnpm typecheck && pnpm lint && pnpm test && pnpm build",
38
41
  "deploy": "pnpm typecheck && pnpm build && npm publish --access public"
39
42
  },
40
43
  "keywords": [
@@ -46,21 +49,38 @@
46
49
  "license": "MIT",
47
50
  "peerDependencies": {
48
51
  "@expo/vector-icons": ">=14.0.0",
49
- "@gorhom/bottom-sheet": ">=5.0.0",
52
+ "@gorhom/bottom-sheet": ">=5.2.0",
50
53
  "@react-native-community/slider": ">=4.0.0",
51
54
  "@react-native-picker/picker": ">=2.0.0",
55
+ "@shopify/react-native-skia": ">=1.0.0",
52
56
  "expo-font": ">=14.0.0",
53
57
  "expo-haptics": ">=14.0.0",
54
58
  "expo-linear-gradient": ">=13.0.0",
59
+ "expo-sensors": ">=13.0.0",
60
+ "pressto": ">=0.6.0",
55
61
  "react": ">=17",
56
- "react-native": ">=0.70",
62
+ "react-native": ">=0.76",
63
+ "react-native-ease": ">=0.7.0",
57
64
  "react-native-gesture-handler": ">=2.0.0",
65
+ "react-native-pulsar": ">=1.6.0",
58
66
  "react-native-reanimated": ">=4.0.0",
59
67
  "react-native-safe-area-context": ">=4.0.0",
60
68
  "react-native-screens": ">=3.0.0",
61
69
  "react-native-size-matters": ">=0.4.0",
62
70
  "react-native-svg": ">=15.0.0",
63
- "react-native-worklets": ">=0.5.0"
71
+ "react-native-worklets": ">=0.5.0",
72
+ "sonner-native": ">=0.20.0"
73
+ },
74
+ "peerDependenciesMeta": {
75
+ "@shopify/react-native-skia": {
76
+ "optional": true
77
+ },
78
+ "expo-sensors": {
79
+ "optional": true
80
+ },
81
+ "react-native-pulsar": {
82
+ "optional": true
83
+ }
64
84
  },
65
85
  "pnpm": {
66
86
  "overrides": {
@@ -82,7 +102,11 @@
82
102
  "@gorhom/bottom-sheet": "5.2.8",
83
103
  "@react-native-community/slider": "5.0.1",
84
104
  "@react-native-picker/picker": "2.11.1",
105
+ "@shopify/react-native-skia": "2.2.12",
106
+ "@testing-library/react-native": "^14.0.0",
107
+ "@types/jest": "^30.0.0",
85
108
  "@types/react": "19.1.17",
109
+ "babel-preset-expo": "~54.0.11",
86
110
  "eslint": "^9.0.0",
87
111
  "eslint-config-prettier": "^10.0.0",
88
112
  "eslint-plugin-react": "^7.37.0",
@@ -90,10 +114,15 @@
90
114
  "expo-font": "~14.0.11",
91
115
  "expo-haptics": "~15.0.8",
92
116
  "expo-linear-gradient": "~15.0.8",
117
+ "expo-sensors": "~15.0.7",
118
+ "jest-expo": "~54.0.17",
119
+ "pressto": "^0.6.1",
93
120
  "prettier": "^3.8.3",
94
121
  "react": "19.1.0",
95
122
  "react-native": "0.81.5",
123
+ "react-native-ease": "^0.7.2",
96
124
  "react-native-gesture-handler": "~2.28.0",
125
+ "react-native-pulsar": "^1.6.1",
97
126
  "react-native-reanimated": "~4.1.1",
98
127
  "react-native-safe-area-context": "5.6.2",
99
128
  "react-native-screens": "4.16.0",
@@ -101,6 +130,7 @@
101
130
  "react-native-svg": "15.12.1",
102
131
  "react-native-worklets": "~0.5.1",
103
132
  "sonner-native": "0.23.1",
133
+ "test-renderer": "^1.2.0",
104
134
  "tsup": "^8.0.0",
105
135
  "typescript": "^5.4.0",
106
136
  "typescript-eslint": "^8.60.0"
@@ -126,7 +126,13 @@ function AccordionItemComponent({
126
126
  height.value = e.nativeEvent.layout.height
127
127
  }}
128
128
  >
129
- {item.content}
129
+ {typeof item.content === 'string' || typeof item.content === 'number' ? (
130
+ <Text style={[styles.contentText, { color: colors.foregroundMuted }]} allowFontScaling={true}>
131
+ {item.content}
132
+ </Text>
133
+ ) : (
134
+ item.content
135
+ )}
130
136
  </View>
131
137
  </Animated.View>
132
138
  </View>
@@ -204,4 +210,9 @@ const styles = StyleSheet.create({
204
210
  position: 'absolute',
205
211
  width: '100%',
206
212
  },
213
+ contentText: {
214
+ fontFamily: 'Sohne-Regular',
215
+ fontSize: ms(14),
216
+ lineHeight: ms(20),
217
+ },
207
218
  })
@@ -61,6 +61,9 @@ export function AlertBanner({ title, description, variant = 'default', icon, ico
61
61
  ? renderIcon(iconName, ms(16), iconColor ?? accentColor)
62
62
  : icon ?? defaultIcon
63
63
 
64
+ // Accessibility: AlertBanner is a notification — "alert" role announces it.
65
+ const a11yLabel = description ? `${title}. ${description}` : title
66
+
64
67
  return (
65
68
  <View
66
69
  style={[
@@ -68,6 +71,8 @@ export function AlertBanner({ title, description, variant = 'default', icon, ico
68
71
  { backgroundColor: bgColor, borderWidth: 1, borderColor },
69
72
  style,
70
73
  ]}
74
+ accessibilityRole="alert"
75
+ accessibilityLabel={a11yLabel}
71
76
  >
72
77
  <View style={styles.iconSlot}>{effectiveIcon}</View>
73
78
  <View style={styles.content}>
@@ -0,0 +1,172 @@
1
+ import React from 'react'
2
+ import { View, Text, StyleSheet, ViewStyle, useWindowDimensions } from 'react-native'
3
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
4
+ import { useTheme } from '../../theme'
5
+ import { IconButton } from '../IconButton'
6
+ import { s, vs, ms, mvs } from '../../utils/scaling'
7
+ import { BREAKPOINTS } from '../../tokens'
8
+
9
+ export interface AppHeaderProps {
10
+ /** Primary title. */
11
+ title?: string
12
+ /** Secondary text below the title. */
13
+ subtitle?: string
14
+ /** Show a back button on the left and call this when pressed. */
15
+ onBack?: () => void
16
+ /** Icon name for the back button. Defaults to `'chevron-left'`. */
17
+ backIconName?: string
18
+ /** Custom left content — overrides the back button. */
19
+ left?: React.ReactNode
20
+ /** Custom right content (actions). */
21
+ right?: React.ReactNode
22
+ /**
23
+ * Title alignment.
24
+ * - `'auto'` (default): left on narrow screens, centered when width ≥ `BREAKPOINTS.wide`.
25
+ * - `'left'` / `'center'`: force alignment.
26
+ */
27
+ titleAlign?: 'auto' | 'left' | 'center'
28
+ /** Render a hairline border under the header. Defaults to true. */
29
+ bordered?: boolean
30
+ /** Apply the top safe-area inset as padding. Defaults to true. */
31
+ withSafeArea?: boolean
32
+ /** Background color. Defaults to theme `background`. */
33
+ backgroundColor?: string
34
+ style?: ViewStyle
35
+ }
36
+
37
+ /**
38
+ * Top app bar / screen chrome — back button, title/subtitle, and a right action
39
+ * slot. Responsive: the title left-aligns on phones and centers on wide layouts.
40
+ *
41
+ * @example
42
+ * <AppHeader title="Settings" onBack={navigation.goBack} right={<IconButton iconName="more-horizontal" variant="text" />} />
43
+ */
44
+ export function AppHeader({
45
+ title,
46
+ subtitle,
47
+ onBack,
48
+ backIconName = 'chevron-left',
49
+ left,
50
+ right,
51
+ titleAlign = 'auto',
52
+ bordered = true,
53
+ withSafeArea = true,
54
+ backgroundColor,
55
+ style,
56
+ }: AppHeaderProps) {
57
+ const { colors } = useTheme()
58
+ const insets = useSafeAreaInsets()
59
+ const { width } = useWindowDimensions()
60
+
61
+ const isWide = width >= BREAKPOINTS.wide
62
+ const centered = titleAlign === 'center' || (titleAlign === 'auto' && isWide)
63
+
64
+ const leftNode = left ?? (onBack ? (
65
+ <IconButton
66
+ iconName={backIconName}
67
+ variant="text"
68
+ size="md"
69
+ onPress={onBack}
70
+ accessibilityLabel="Go back"
71
+ />
72
+ ) : null)
73
+
74
+ const titleBlock = (
75
+ <View style={[styles.titleBlock, centered && styles.titleBlockCentered]} pointerEvents="none">
76
+ {title ? (
77
+ <Text
78
+ style={[styles.title, { color: colors.foreground }, centered && styles.textCentered]}
79
+ numberOfLines={1}
80
+ allowFontScaling={true}
81
+ accessibilityRole="header"
82
+ >
83
+ {title}
84
+ </Text>
85
+ ) : null}
86
+ {subtitle ? (
87
+ <Text
88
+ style={[styles.subtitle, { color: colors.foregroundMuted }, centered && styles.textCentered]}
89
+ numberOfLines={1}
90
+ allowFontScaling={true}
91
+ >
92
+ {subtitle}
93
+ </Text>
94
+ ) : null}
95
+ </View>
96
+ )
97
+
98
+ return (
99
+ <View
100
+ style={[
101
+ styles.container,
102
+ {
103
+ backgroundColor: backgroundColor ?? colors.background,
104
+ paddingTop: withSafeArea ? insets.top : 0,
105
+ borderBottomWidth: bordered ? StyleSheet.hairlineWidth : 0,
106
+ borderBottomColor: colors.border,
107
+ },
108
+ style,
109
+ ]}
110
+ >
111
+ <View style={styles.bar}>
112
+ {/* Centered layout: absolutely-positioned title so it stays centered regardless of slot widths. */}
113
+ {centered ? (
114
+ <>
115
+ <View style={StyleSheet.absoluteFill}>{titleBlock}</View>
116
+ <View style={styles.side}>{leftNode}</View>
117
+ <View style={[styles.side, styles.sideRight]}>{right}</View>
118
+ </>
119
+ ) : (
120
+ <>
121
+ <View style={styles.side}>{leftNode}</View>
122
+ {titleBlock}
123
+ <View style={[styles.side, styles.sideRight]}>{right}</View>
124
+ </>
125
+ )}
126
+ </View>
127
+ </View>
128
+ )
129
+ }
130
+
131
+ const styles = StyleSheet.create({
132
+ container: {
133
+ width: '100%',
134
+ },
135
+ bar: {
136
+ minHeight: vs(48),
137
+ flexDirection: 'row',
138
+ alignItems: 'center',
139
+ paddingHorizontal: s(8),
140
+ },
141
+ side: {
142
+ minWidth: s(44),
143
+ flexDirection: 'row',
144
+ alignItems: 'center',
145
+ justifyContent: 'flex-start',
146
+ },
147
+ sideRight: {
148
+ justifyContent: 'flex-end',
149
+ },
150
+ titleBlock: {
151
+ flex: 1,
152
+ justifyContent: 'center',
153
+ paddingHorizontal: s(8),
154
+ gap: vs(1),
155
+ },
156
+ titleBlockCentered: {
157
+ alignItems: 'center',
158
+ },
159
+ title: {
160
+ fontFamily: 'Sohne-SemiBold',
161
+ fontSize: ms(18),
162
+ lineHeight: mvs(24),
163
+ },
164
+ subtitle: {
165
+ fontFamily: 'Sohne-Regular',
166
+ fontSize: ms(13),
167
+ lineHeight: mvs(16),
168
+ },
169
+ textCentered: {
170
+ textAlign: 'center',
171
+ },
172
+ })
@@ -0,0 +1 @@
1
+ export * from './AppHeader'
@@ -59,8 +59,9 @@ function AvatarBase({ src, fallback, fallbackText, size = 'md', status, style }:
59
59
 
60
60
  const statusSize = typeof size === 'number' ? size * 0.25 : statusSizeMap[size as AvatarSize]
61
61
 
62
+ // AUDIT FIX: online was hardcoded '#22c55e' — now uses theme token.
62
63
  const statusColor: Record<AvatarStatus, string> = {
63
- online: '#22c55e',
64
+ online: colors.success,
64
65
  offline: 'transparent',
65
66
  busy: colors.destructive,
66
67
  away: colors.warning,
@@ -74,8 +75,15 @@ function AvatarBase({ src, fallback, fallbackText, size = 'md', status, style }:
74
75
  overflow: 'hidden',
75
76
  }
76
77
 
78
+ // Compute accessible label: prefer explicit fallback text, then initials.
79
+ const a11yLabel = fallbackText || fallback || 'Avatar'
80
+
77
81
  return (
78
- <View style={[styles.wrapper, style]}>
82
+ <View
83
+ style={[styles.wrapper, style]}
84
+ accessibilityRole="image"
85
+ accessibilityLabel={a11yLabel}
86
+ >
79
87
  <View style={[styles.base, containerStyle]}>
80
88
  {!showFallback ? (
81
89
  <Image
@@ -78,8 +78,15 @@ function BadgeBase({ label, children, variant = 'default', size = 'md', icon, ic
78
78
 
79
79
  const content = children ?? label
80
80
 
81
+ // Accessibility: badges are status indicators — role helps screen readers.
82
+ const a11yLabel = typeof content === 'string' ? content : label
83
+
81
84
  return (
82
- <View style={[styles.container, containerStyle, sizePadding[size], { gap: sizeIconGap[size] }, style]}>
85
+ <View
86
+ style={[styles.container, containerStyle, sizePadding[size], { gap: sizeIconGap[size] }, style]}
87
+ accessibilityRole="text"
88
+ accessibilityLabel={a11yLabel}
89
+ >
83
90
  {effectiveIcon}
84
91
  {typeof content === 'string' ? (
85
92
  <Text style={[styles.label, { color: textColor }, sizeFontSize[size]]} allowFontScaling={true}>
@@ -1,26 +1,23 @@
1
1
  import React from 'react'
2
2
  import {
3
- TouchableOpacity,
3
+ View,
4
4
  Text,
5
5
  ActivityIndicator,
6
6
  StyleSheet,
7
- TouchableOpacityProps,
8
7
  ViewStyle,
9
8
  TextStyle,
10
9
  } from 'react-native'
11
- import Animated from 'react-native-reanimated'
12
10
  import { impactMedium } from '../../utils/haptics'
13
11
  import { useTheme } from '../../theme'
14
12
  import { s, vs, ms, mvs } from '../../utils/scaling'
15
13
  import { renderIcon } from '../../utils/icons'
16
14
  import { RADIUS, TYPOGRAPHY } from '../../tokens'
17
- import { usePressScale } from '../../utils/usePressScale'
18
- import { PRESS_SCALE } from '../../utils/animations'
15
+ import { PressableButton } from '../../utils/pressable'
19
16
 
20
17
  export type ButtonVariant = 'primary' | 'secondary' | 'text' | 'destructive'
21
18
  export type ButtonSize = 'sm' | 'md' | 'lg'
22
19
 
23
- export interface ButtonProps extends TouchableOpacityProps {
20
+ export interface ButtonProps {
24
21
  label: string
25
22
  variant?: ButtonVariant
26
23
  size?: ButtonSize
@@ -30,10 +27,16 @@ export interface ButtonProps extends TouchableOpacityProps {
30
27
  iconName?: string
31
28
  iconColor?: string
32
29
  iconPosition?: 'left' | 'right'
30
+ disabled?: boolean
31
+ style?: ViewStyle
32
+ onPress?: () => void
33
+ accessibilityLabel?: string
34
+ accessibilityHint?: string
33
35
  }
34
36
 
35
37
  const containerSizeStyles: Record<ButtonSize, ViewStyle> = {
36
- sm: { paddingHorizontal: s(16), paddingVertical: vs(10), minHeight: 40 },
38
+ // AUDIT FIX: sm was 40pt below Apple HIG 44pt minimum touch target.
39
+ sm: { paddingHorizontal: s(16), paddingVertical: vs(12), minHeight: 44 },
37
40
  md: { paddingHorizontal: s(24), paddingVertical: vs(14), minHeight: 48 },
38
41
  lg: { paddingHorizontal: s(28), paddingVertical: vs(16), minHeight: 56 },
39
42
  }
@@ -61,18 +64,13 @@ function ButtonBase({
61
64
  onPress,
62
65
  accessibilityLabel,
63
66
  accessibilityHint,
64
- ...props
65
67
  }: ButtonProps) {
66
68
  const { colors } = useTheme()
67
69
  const isDisabled = disabled || loading
68
- const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
69
- pressScale: PRESS_SCALE.button,
70
- disabled: isDisabled,
71
- })
72
70
 
73
- const handlePress: TouchableOpacityProps['onPress'] = (e) => {
71
+ const handlePress = () => {
74
72
  impactMedium()
75
- onPress?.(e)
73
+ onPress?.()
76
74
  }
77
75
 
78
76
  const containerVariantStyle: ViewStyle = {
@@ -108,11 +106,8 @@ function ButtonBase({
108
106
  const { flex, ...restStyle } = flatStyle || {}
109
107
 
110
108
  return (
111
- <Animated.View
112
- style={[fullWidth && styles.fullWidth, flex !== undefined && { flex }, animatedStyle]}
113
- {...hoverHandlers}
114
- >
115
- <TouchableOpacity
109
+ <View style={[fullWidth && styles.fullWidth, flex !== undefined && { flex }]}>
110
+ <PressableButton
116
111
  style={[
117
112
  styles.base,
118
113
  containerVariantStyle,
@@ -121,17 +116,15 @@ function ButtonBase({
121
116
  isDisabled && styles.disabled,
122
117
  restStyle,
123
118
  ]}
124
- disabled={isDisabled}
125
- activeOpacity={1}
126
- touchSoundDisabled={true}
119
+ enabled={!isDisabled}
127
120
  onPress={handlePress}
128
- onPressIn={onPressIn}
129
- onPressOut={onPressOut}
121
+ rippleColor="transparent"
122
+ touchSoundDisabled
123
+ activateOnHover
130
124
  accessibilityRole="button"
131
125
  accessibilityLabel={accessibilityLabel ?? label}
132
126
  accessibilityHint={accessibilityHint}
133
127
  accessibilityState={{ disabled: isDisabled, busy: loading }}
134
- {...props}
135
128
  >
136
129
  {loading ? (
137
130
  <>
@@ -157,8 +150,8 @@ function ButtonBase({
157
150
  {effectiveIcon && iconPosition === 'right' && <>{effectiveIcon}</>}
158
151
  </>
159
152
  )}
160
- </TouchableOpacity>
161
- </Animated.View>
153
+ </PressableButton>
154
+ </View>
162
155
  )
163
156
  }
164
157
 
@@ -1,12 +1,10 @@
1
1
  import React from 'react'
2
- import { View, Text, TouchableOpacity, StyleSheet, ViewStyle, TextStyle } from 'react-native'
3
- import Animated from 'react-native-reanimated'
2
+ import { View, Text, StyleSheet, ViewStyle, TextStyle } from 'react-native'
4
3
  import { impactLight } from '../../utils/haptics'
5
4
  import { useTheme } from '../../theme'
6
5
  import { s, vs, ms, mvs } from '../../utils/scaling'
7
6
  import { RADIUS } from '../../tokens'
8
- import { usePressScale } from '../../utils/usePressScale'
9
- import { SPRINGS, PRESS_SCALE } from '../../utils/animations'
7
+ import { PressableCard } from '../../utils/pressable'
10
8
 
11
9
  export type CardVariant = 'elevated' | 'outlined' | 'filled'
12
10
 
@@ -26,12 +24,6 @@ export interface CardFooterProps { children: React.ReactNode; style?: ViewStyle
26
24
 
27
25
  export function Card({ children, variant = 'elevated', onPress, style, accessibilityLabel }: CardProps) {
28
26
  const { colors } = useTheme()
29
- const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
30
- pressScale: PRESS_SCALE.card,
31
- pressInSpring: SPRINGS.surfacePressIn,
32
- pressOutSpring: SPRINGS.surfacePressOut,
33
- disabled: !onPress,
34
- })
35
27
 
36
28
  const handlePress = () => {
37
29
  if (!onPress) return
@@ -74,19 +66,16 @@ export function Card({ children, variant = 'elevated', onPress, style, accessibi
74
66
 
75
67
  if (onPress) {
76
68
  return (
77
- <Animated.View style={animatedStyle} {...hoverHandlers}>
78
- <TouchableOpacity
79
- onPress={handlePress}
80
- onPressIn={onPressIn}
81
- onPressOut={onPressOut}
82
- activeOpacity={1}
83
- touchSoundDisabled={true}
84
- accessibilityRole="button"
85
- accessibilityLabel={accessibilityLabel}
86
- >
87
- {cardContent}
88
- </TouchableOpacity>
89
- </Animated.View>
69
+ <PressableCard
70
+ onPress={handlePress}
71
+ rippleColor="transparent"
72
+ touchSoundDisabled
73
+ activateOnHover
74
+ accessibilityRole="button"
75
+ accessibilityLabel={accessibilityLabel}
76
+ >
77
+ {cardContent}
78
+ </PressableCard>
90
79
  )
91
80
  }
92
81