@retray-dev/ui-kit 12.2.0 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/COMPONENTS.md +85 -143
  2. package/CONSUMER.md +2 -2
  3. package/DESIGN.md +2 -2
  4. package/README.md +11 -6
  5. package/dist/Accordion.js +48 -210
  6. package/dist/Accordion.mjs +6 -5
  7. package/dist/AlertBanner.js +29 -153
  8. package/dist/AlertBanner.mjs +3 -3
  9. package/dist/AppHeader.js +37 -235
  10. package/dist/AppHeader.mjs +6 -7
  11. package/dist/Avatar.d.mts +17 -1
  12. package/dist/Avatar.d.ts +17 -1
  13. package/dist/Avatar.js +80 -115
  14. package/dist/Avatar.mjs +2 -2
  15. package/dist/Badge.js +24 -149
  16. package/dist/Badge.mjs +3 -3
  17. package/dist/Button.js +79 -267
  18. package/dist/Button.mjs +6 -6
  19. package/dist/Card.js +15 -200
  20. package/dist/Card.mjs +4 -5
  21. package/dist/CategoryStrip.d.mts +0 -5
  22. package/dist/CategoryStrip.d.ts +0 -5
  23. package/dist/CategoryStrip.js +47 -265
  24. package/dist/CategoryStrip.mjs +6 -6
  25. package/dist/Checkbox.js +15 -200
  26. package/dist/Checkbox.mjs +5 -5
  27. package/dist/Chip.js +44 -236
  28. package/dist/Chip.mjs +7 -6
  29. package/dist/ConfirmDialog.js +84 -286
  30. package/dist/ConfirmDialog.mjs +7 -7
  31. package/dist/CurrencyDisplay.js +1 -114
  32. package/dist/CurrencyDisplay.mjs +2 -2
  33. package/dist/CurrencyInput.js +35 -162
  34. package/dist/CurrencyInput.mjs +5 -5
  35. package/dist/DetailRow.js +25 -150
  36. package/dist/DetailRow.mjs +3 -3
  37. package/dist/EmptyState.js +80 -268
  38. package/dist/EmptyState.mjs +7 -7
  39. package/dist/ErrorBoundary.js +32 -199
  40. package/dist/ErrorBoundary.mjs +4 -4
  41. package/dist/Form.js +1 -114
  42. package/dist/Form.mjs +2 -2
  43. package/dist/HolographicCard.d.mts +0 -28
  44. package/dist/HolographicCard.d.ts +0 -28
  45. package/dist/HolographicCard.js +20 -130
  46. package/dist/HolographicCard.mjs +9 -32
  47. package/dist/IconButton.js +36 -234
  48. package/dist/IconButton.mjs +5 -6
  49. package/dist/IconPicker.js +222 -929
  50. package/dist/IconPicker.mjs +5 -5
  51. package/dist/ImageUpload.d.mts +3 -1
  52. package/dist/ImageUpload.d.ts +3 -1
  53. package/dist/ImageUpload.js +25 -215
  54. package/dist/ImageUpload.mjs +5 -6
  55. package/dist/ImageViewer.js +75 -266
  56. package/dist/ImageViewer.mjs +8 -8
  57. package/dist/Input.d.mts +1 -1
  58. package/dist/Input.d.ts +1 -1
  59. package/dist/Input.js +35 -162
  60. package/dist/Input.mjs +4 -4
  61. package/dist/LabelValue.js +24 -149
  62. package/dist/LabelValue.mjs +3 -3
  63. package/dist/ListGroup.js +1 -114
  64. package/dist/ListGroup.mjs +2 -2
  65. package/dist/ListItem.js +38 -235
  66. package/dist/ListItem.mjs +5 -6
  67. package/dist/MediaCard.d.mts +0 -14
  68. package/dist/MediaCard.d.ts +0 -14
  69. package/dist/MediaCard.js +69 -315
  70. package/dist/MediaCard.mjs +5 -6
  71. package/dist/MenuGroup.js +1 -114
  72. package/dist/MenuGroup.mjs +2 -2
  73. package/dist/MenuItem.js +36 -234
  74. package/dist/MenuItem.mjs +5 -6
  75. package/dist/MonthPicker.js +8 -163
  76. package/dist/MonthPicker.mjs +3 -3
  77. package/dist/NumberStepper.js +40 -238
  78. package/dist/NumberStepper.mjs +5 -6
  79. package/dist/PagerDots.d.mts +1 -1
  80. package/dist/PagerDots.d.ts +1 -1
  81. package/dist/PagerDots.js +69 -224
  82. package/dist/PagerDots.mjs +6 -5
  83. package/dist/Pressable.js +14 -85
  84. package/dist/Pressable.mjs +4 -4
  85. package/dist/PricingCard.js +87 -272
  86. package/dist/PricingCard.mjs +8 -8
  87. package/dist/Progress.js +3 -123
  88. package/dist/Progress.mjs +3 -3
  89. package/dist/RadioGroup.js +52 -265
  90. package/dist/RadioGroup.mjs +5 -5
  91. package/dist/RetrayProvider.js +3 -6
  92. package/dist/RetrayProvider.mjs +3 -3
  93. package/dist/Select.d.mts +2 -1
  94. package/dist/Select.d.ts +2 -1
  95. package/dist/Select.js +24 -232
  96. package/dist/Select.mjs +4 -5
  97. package/dist/SelectableCard.js +33 -209
  98. package/dist/SelectableCard.mjs +5 -5
  99. package/dist/SelectableGrid.d.mts +0 -21
  100. package/dist/SelectableGrid.d.ts +0 -21
  101. package/dist/SelectableGrid.js +49 -271
  102. package/dist/SelectableGrid.mjs +5 -6
  103. package/dist/Separator.js +1 -114
  104. package/dist/Separator.mjs +2 -2
  105. package/dist/Sheet.js +7 -162
  106. package/dist/Sheet.mjs +3 -3
  107. package/dist/SheetSelect.js +39 -236
  108. package/dist/SheetSelect.mjs +6 -6
  109. package/dist/Skeleton.js +4 -124
  110. package/dist/Skeleton.mjs +3 -3
  111. package/dist/Slider.js +6 -161
  112. package/dist/Slider.mjs +3 -3
  113. package/dist/Spinner.js +3 -116
  114. package/dist/Spinner.mjs +2 -2
  115. package/dist/Stats.js +36 -234
  116. package/dist/Stats.mjs +5 -6
  117. package/dist/Switch.js +24 -175
  118. package/dist/Switch.mjs +5 -4
  119. package/dist/TabBar.js +43 -200
  120. package/dist/TabBar.mjs +5 -4
  121. package/dist/Tabs.js +15 -199
  122. package/dist/Tabs.mjs +5 -5
  123. package/dist/Text.js +9 -130
  124. package/dist/Text.mjs +2 -2
  125. package/dist/Textarea.d.mts +2 -1
  126. package/dist/Textarea.d.ts +2 -1
  127. package/dist/Textarea.js +71 -219
  128. package/dist/Textarea.mjs +4 -4
  129. package/dist/Toast.js +1 -114
  130. package/dist/Toast.mjs +2 -2
  131. package/dist/Toggle.js +39 -236
  132. package/dist/Toggle.mjs +6 -6
  133. package/dist/{chunk-M3C7XM2M.mjs → chunk-2QOHHBJC.mjs} +3 -3
  134. package/dist/{chunk-LIS6I5UP.mjs → chunk-2VIDP72N.mjs} +3 -3
  135. package/dist/{chunk-DF7JA72E.mjs → chunk-4NQFTHN3.mjs} +13 -7
  136. package/dist/{chunk-UBUXUMER.mjs → chunk-4ZO5PTKF.mjs} +4 -4
  137. package/dist/{chunk-3XCFYSX4.mjs → chunk-5MYNAAFE.mjs} +13 -17
  138. package/dist/{chunk-E7NEHHXV.mjs → chunk-62BBSSUF.mjs} +3 -3
  139. package/dist/{chunk-MVMGPZN6.mjs → chunk-6CR4S6W2.mjs} +3 -3
  140. package/dist/{chunk-EDLCGYIO.mjs → chunk-6QLBHUEG.mjs} +8 -7
  141. package/dist/chunk-ARONDO7M.mjs +40 -0
  142. package/dist/{chunk-GH67YXG6.mjs → chunk-AZV7KNJI.mjs} +3 -3
  143. package/dist/{chunk-RMRS44MQ.mjs → chunk-BTUW5LSG.mjs} +11 -8
  144. package/dist/{chunk-2P2CB235.mjs → chunk-BULKGOIZ.mjs} +7 -8
  145. package/dist/{chunk-NHDI3VQB.mjs → chunk-CBIZLRYH.mjs} +15 -12
  146. package/dist/chunk-CM2DG4MR.mjs +142 -0
  147. package/dist/{chunk-TS7DGUIR.mjs → chunk-DBHSUUKU.mjs} +2 -2
  148. package/dist/{chunk-57V2LXCK.mjs → chunk-DE25XTVQ.mjs} +3 -3
  149. package/dist/{chunk-UQ4742ET.mjs → chunk-E4EQSCKR.mjs} +5 -5
  150. package/dist/{chunk-GUTDFUNF.mjs → chunk-EHGBHFMH.mjs} +9 -17
  151. package/dist/{chunk-CF27NBXO.mjs → chunk-EROPDCB5.mjs} +16 -24
  152. package/dist/{chunk-ZIMY2QUM.mjs → chunk-ERWJPVX7.mjs} +2 -2
  153. package/dist/{chunk-NLZY4TXU.mjs → chunk-ESQDPO5E.mjs} +7 -7
  154. package/dist/{chunk-VJBUCITV.mjs → chunk-EW2FIDSM.mjs} +1 -1
  155. package/dist/{chunk-HC4VVCWY.mjs → chunk-FTTI6T5Q.mjs} +4 -4
  156. package/dist/{chunk-2HFD4IHU.mjs → chunk-HUSSF6TF.mjs} +1 -1
  157. package/dist/chunk-IFYMBOEN.mjs +14 -0
  158. package/dist/{chunk-QOLWA2PW.mjs → chunk-IGU223UM.mjs} +80 -4
  159. package/dist/chunk-IJCMPVW5.mjs +121 -0
  160. package/dist/{chunk-AENAVIKT.mjs → chunk-ITG4JQM3.mjs} +4 -4
  161. package/dist/{chunk-E5UKLSJZ.mjs → chunk-K3QX2M26.mjs} +11 -8
  162. package/dist/{chunk-4OORJ2DY.mjs → chunk-K7TKID3V.mjs} +8 -7
  163. package/dist/{chunk-2LG326TT.mjs → chunk-KAGADD2O.mjs} +4 -4
  164. package/dist/{chunk-IVSRW4HS.mjs → chunk-KC5QDYGZ.mjs} +4 -4
  165. package/dist/{chunk-7AFZWSCI.mjs → chunk-KPTY7UYQ.mjs} +1 -1
  166. package/dist/{chunk-YTXRIXNZ.mjs → chunk-KSSVIFYR.mjs} +9 -12
  167. package/dist/chunk-L3YKPTJQ.mjs +119 -0
  168. package/dist/chunk-M53LC4Q7.mjs +35 -0
  169. package/dist/{chunk-ZR6HSEAB.mjs → chunk-MP7GLMIR.mjs} +17 -25
  170. package/dist/chunk-MZ6WRTD2.mjs +40 -0
  171. package/dist/chunk-NGEN2EES.mjs +581 -0
  172. package/dist/{chunk-C43HRKXH.mjs → chunk-OBV72JD4.mjs} +1 -1
  173. package/dist/{chunk-LPV4NJJK.mjs → chunk-PGQ6FMXS.mjs} +6 -5
  174. package/dist/{chunk-MEPSKGBO.mjs → chunk-PI6RULJX.mjs} +1 -1
  175. package/dist/{chunk-F3YTWO3T.mjs → chunk-RA6SAAFE.mjs} +9 -8
  176. package/dist/{chunk-UNNRUJTM.mjs → chunk-RRKM4MKB.mjs} +7 -7
  177. package/dist/{chunk-ULGNQPNE.mjs → chunk-S2VGME7X.mjs} +1 -1
  178. package/dist/{chunk-OLVJFKXS.mjs → chunk-S44XWTTC.mjs} +35 -25
  179. package/dist/{chunk-YMYIEVZP.mjs → chunk-SZEKQAOY.mjs} +1 -1
  180. package/dist/{chunk-ELGEOM7I.mjs → chunk-TETMEKZE.mjs} +9 -9
  181. package/dist/{chunk-BXF4AMHY.mjs → chunk-TMH263OK.mjs} +5 -4
  182. package/dist/{chunk-NJG7DHVF.mjs → chunk-U6DEBYU5.mjs} +10 -9
  183. package/dist/{chunk-RJNLAH76.mjs → chunk-UOKFSFNJ.mjs} +2 -2
  184. package/dist/{chunk-HEDQPK4I.mjs → chunk-URIH43IJ.mjs} +13 -21
  185. package/dist/{chunk-QXDGGOLC.mjs → chunk-V2ZB2XNS.mjs} +6 -6
  186. package/dist/{chunk-KSUWPU2F.mjs → chunk-WIPEDNSD.mjs} +7 -7
  187. package/dist/{chunk-QDAZGZUF.mjs → chunk-XCIG6HT2.mjs} +3 -3
  188. package/dist/{chunk-4J2PXL36.mjs → chunk-Y6YS33GM.mjs} +40 -38
  189. package/dist/{chunk-4XOB5TTD.mjs → chunk-ZKDKKQCE.mjs} +5 -5
  190. package/dist/{chunk-LOBLCFMN.mjs → chunk-ZTPYUU5C.mjs} +5 -5
  191. package/dist/index.d.mts +12 -72
  192. package/dist/index.d.ts +12 -72
  193. package/dist/index.js +1051 -1838
  194. package/dist/index.mjs +81 -85
  195. package/package.json +8 -10
  196. package/src/components/Accordion/Accordion.tsx +12 -9
  197. package/src/components/AlertBanner/AlertBanner.tsx +7 -6
  198. package/src/components/AppHeader/AppHeader.tsx +1 -1
  199. package/src/components/Avatar/Avatar.tsx +92 -1
  200. package/src/components/Avatar/index.ts +2 -2
  201. package/src/components/Badge/Badge.tsx +2 -2
  202. package/src/components/Button/Button.tsx +50 -46
  203. package/src/components/Card/Card.tsx +1 -0
  204. package/src/components/CategoryStrip/CategoryStrip.tsx +36 -49
  205. package/src/components/Chip/Chip.tsx +5 -4
  206. package/src/components/ConfirmDialog/ConfirmDialog.tsx +3 -3
  207. package/src/components/DetailRow/DetailRow.tsx +3 -3
  208. package/src/components/EmptyState/EmptyState.tsx +2 -2
  209. package/src/components/ErrorBoundary/ErrorBoundary.tsx +6 -6
  210. package/src/components/HolographicCard/HolographicCard.tsx +14 -95
  211. package/src/components/IconButton/IconButton.tsx +2 -2
  212. package/src/components/IconPicker/IconPicker.tsx +13 -12
  213. package/src/components/ImageUpload/ImageUpload.tsx +14 -25
  214. package/src/components/ImageViewer/ImageViewer.tsx +3 -3
  215. package/src/components/Input/Input.tsx +11 -5
  216. package/src/components/LabelValue/LabelValue.tsx +2 -2
  217. package/src/components/ListItem/ListItem.tsx +4 -4
  218. package/src/components/MediaCard/MediaCard.tsx +21 -59
  219. package/src/components/MenuItem/MenuItem.tsx +2 -2
  220. package/src/components/MonthPicker/MonthPicker.tsx +2 -2
  221. package/src/components/NumberStepper/NumberStepper.tsx +6 -6
  222. package/src/components/PagerDots/PagerDots.tsx +38 -28
  223. package/src/components/PricingCard/PricingCard.tsx +6 -6
  224. package/src/components/RadioGroup/RadioGroup.tsx +18 -31
  225. package/src/components/Select/Select.tsx +32 -39
  226. package/src/components/SelectableCard/SelectableCard.tsx +4 -6
  227. package/src/components/SelectableGrid/SelectableGrid.tsx +38 -72
  228. package/src/components/Sheet/Sheet.tsx +1 -1
  229. package/src/components/SheetSelect/SheetSelect.tsx +3 -3
  230. package/src/components/Skeleton/Skeleton.tsx +1 -1
  231. package/src/components/Spinner/Spinner.tsx +2 -2
  232. package/src/components/Stats/Stats.tsx +2 -2
  233. package/src/components/Switch/Switch.tsx +9 -6
  234. package/src/components/TabBar/TabBar.tsx +9 -8
  235. package/src/components/Text/Text.tsx +12 -1
  236. package/src/components/Textarea/Textarea.tsx +18 -32
  237. package/src/components/Toggle/Toggle.tsx +3 -3
  238. package/src/hooks/useConfirmDialog.ts +31 -42
  239. package/src/index.ts +3 -4
  240. package/src/theme/ThemeProvider.tsx +1 -4
  241. package/src/theme/colorUtils.ts +1 -72
  242. package/src/theme/colors.ts +40 -1
  243. package/src/theme/types.ts +2 -2
  244. package/src/utils/animations.ts +0 -47
  245. package/src/utils/curatedIcons.ts +93 -801
  246. package/src/utils/haptics.ts +13 -208
  247. package/src/utils/icons.ts +27 -91
  248. package/src/utils/pressable.ts +10 -61
  249. package/dist/VirtualList.d.mts +0 -19
  250. package/dist/VirtualList.d.ts +0 -19
  251. package/dist/VirtualList.js +0 -38
  252. package/dist/VirtualList.mjs +0 -2
  253. package/dist/chunk-2BA3JMKK.mjs +0 -136
  254. package/dist/chunk-3DKJ2GIC.mjs +0 -30
  255. package/dist/chunk-7ELGZ66G.mjs +0 -164
  256. package/dist/chunk-DVK4G2GT.mjs +0 -59
  257. package/dist/chunk-EJ7ZPXOH.mjs +0 -163
  258. package/dist/chunk-KA7LTET3.mjs +0 -71
  259. package/dist/chunk-LNPKGWBG.mjs +0 -134
  260. package/dist/chunk-NC5ZTR2Y.mjs +0 -32
  261. package/dist/chunk-SAWUXP3A.mjs +0 -1114
  262. package/dist/chunk-YNROWHQJ.mjs +0 -46
  263. package/src/components/VirtualList/VirtualList.tsx +0 -60
  264. package/src/components/VirtualList/index.ts +0 -1
  265. package/src/utils/fontGuard.ts +0 -35
  266. package/src/utils/hover.ts +0 -25
  267. package/src/utils/useColorTransition.ts +0 -40
  268. package/src/utils/usePressScale.ts +0 -75
package/COMPONENTS.md CHANGED
@@ -1,4 +1,4 @@
1
- # @retray-dev/ui-kit — Component Reference (v12.1.0)
1
+ # @retray-dev/ui-kit — Component Reference (v13.0.0)
2
2
 
3
3
  This file is the AI reference for this package. Add all three lines below to your project's `CLAUDE.md` to give Claude full context — components, setup guide, and usage examples:
4
4
 
@@ -99,10 +99,10 @@ module.exports = function (api) {
99
99
  | `@expo/vector-icons` | **Required** | Icons everywhere | |
100
100
  | `react-native-size-matters` | **Required** | Responsive scaling | |
101
101
  | `react-native-ease` | **Required** | `Checkbox`, `Chip`, `CategoryStrip`, `Switch`, `RadioGroup`, `Toggle` | Declarative `EaseView` animation layer. Static import in source — omitting it crashes the module load. `pnpm add react-native-ease` |
102
- | `react-native-pulsar` | **Optional** | Rich haptics on dev builds | Loaded via dynamic `import()` + try/catch; falls back to `expo-haptics`. Skip in Expo Go. `pnpm add react-native-pulsar` |
102
+
103
103
  | `@shopify/react-native-skia` + `expo-sensors` | **Optional** | Deep-import `HolographicCard` only | `pnpm add @shopify/react-native-skia expo-sensors` |
104
104
 
105
- > **Dev build vs Expo Go:** Everything in the kit runs in **Expo Go** — `react-native-pulsar` is the only native module that needs a custom dev build, and it degrades gracefully (basic haptics) when absent. No component is broken by running in Expo Go.
105
+
106
106
 
107
107
  ### Troubleshooting: `react-native-screens` codegen on RN 0.83
108
108
 
@@ -1561,6 +1561,65 @@ const [guests, setGuests] = useState(2)
1561
1561
 
1562
1562
  ---
1563
1563
 
1564
+ ### AvatarGroup
1565
+
1566
+ **Import:** `import { AvatarGroup } from '@retray-dev/ui-kit'`
1567
+
1568
+ **When to use:** Stacked/overlapping avatars to show multiple users in a compact space. Common for collaborator indicators, multi-waiter tables, and team members.
1569
+
1570
+ | Prop | Type | Default | Notes |
1571
+ |------|------|---------|-------|
1572
+ | users | `{ name: string; src?: string }[]` | required | User data — name used for fallback initials |
1573
+ | max | `number` | `3` | Max avatars visible before `+N` overflow badge |
1574
+ | size | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'sm'` | Avatar size preset, passed to each `Avatar` |
1575
+ | overlap | `number` | `vs(8)` | Overlap in points between consecutive avatars |
1576
+ | onOverflowPress | `() => void` | — | Tap handler for the `+N` overflow badge |
1577
+ | style | `ViewStyle` | — | Outer container style |
1578
+
1579
+ **Sizes:** Same as `Avatar` — `sm` (28px), `md` (40px), `lg` (56px), `xl` (72px).
1580
+
1581
+ **Notes:**
1582
+ - Overflow badge uses `surfaceStrong` background with `foregroundMuted` text.
1583
+ - Individual avatars are not tappable — only the `+N` badge fires `onOverflowPress`.
1584
+ - `overlap` follows the spacing grid (defaults to `spacing.sm` = 8pt).
1585
+
1586
+ **Examples:**
1587
+ ```tsx
1588
+ // Basic — 5 waiters, show max 3
1589
+ <AvatarGroup
1590
+ users={[
1591
+ { name: 'Ana', src: 'https://...' },
1592
+ { name: 'Bob', src: 'https://...' },
1593
+ { name: 'Carlos' },
1594
+ { name: 'Diana', src: 'https://...' },
1595
+ { name: 'Elena' },
1596
+ ]}
1597
+ max={3}
1598
+ />
1599
+
1600
+ // With overflow tap
1601
+ <AvatarGroup
1602
+ users={waiters}
1603
+ max={3}
1604
+ size="sm"
1605
+ onOverflowPress={() => showAllWaiters()}
1606
+ />
1607
+
1608
+ // In a ListItem
1609
+ <ListItem
1610
+ title="Table 4 — 5 waiters"
1611
+ leftRender={
1612
+ <AvatarGroup
1613
+ users={table.waiters}
1614
+ max={3}
1615
+ size="sm"
1616
+ />
1617
+ }
1618
+ />
1619
+ ```
1620
+
1621
+ ---
1622
+
1564
1623
  ### Card
1565
1624
 
1566
1625
  **Import:** `import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@retray-dev/ui-kit'`
@@ -1765,73 +1824,7 @@ const [guests, setGuests] = useState(2)
1765
1824
 
1766
1825
  ---
1767
1826
 
1768
- ### VirtualList
1769
-
1770
- **Import:** `import { VirtualList } from '@retray-dev/ui-kit'`
1771
-
1772
- **When to use:** Large lists (100+ items) where you need efficient rendering. Thin wrapper over `FlatList` with sane defaults for stable keys and optional fixed-height fast path.
1773
-
1774
- | Prop | Type | Default | Notes |
1775
- |------|------|---------|-------|
1776
- | data | `T[]` | — | Array of items to render |
1777
- | renderItem | `ListRenderItem<T>` | — | Render function for each item |
1778
- | itemHeight | `number` | — | Fixed row height in px. Enables `getItemLayout` for performance with large datasets |
1779
- | keyExtractor | `(item: T, index: number) => string` | Auto | Defaults to `item.id` or index. Override for custom keys |
1780
- | ...FlatListProps | — | — | All standard FlatList props pass through |
1781
-
1782
- **Performance optimization:** When `itemHeight` is provided, `VirtualList` enables `getItemLayout` so FlatList skips async measurement. For 10k+ rows, combine with `React.memo`-wrapped `renderItem` so only on-screen rows mount and re-render.
1783
-
1784
- **Default key extraction:** Uses `item.id` (converted to string) or falls back to index. Override with `keyExtractor` for custom logic.
1785
-
1786
- **Example — simple list:**
1787
- ```tsx
1788
- <VirtualList
1789
- data={items}
1790
- renderItem={({ item }) => (
1791
- <ListItem
1792
- title={item.title}
1793
- subtitle={item.subtitle}
1794
- onPress={() => handlePress(item.id)}
1795
- />
1796
- )}
1797
- itemHeight={56}
1798
- />
1799
- ```
1800
-
1801
- **Example — large dataset with memoized render:**
1802
- ```tsx
1803
- const renderItem = useCallback(({ item }) => (
1804
- <ListItem
1805
- title={item.title}
1806
- subtitle={item.subtitle}
1807
- leftIcon="circle"
1808
- showSeparator
1809
- onPress={() => navigate('detail', { id: item.id })}
1810
- />
1811
- ), [])
1812
-
1813
- <VirtualList
1814
- data={thousands}
1815
- renderItem={renderItem}
1816
- itemHeight={64}
1817
- />
1818
- ```
1819
1827
 
1820
- **Example — variable height rows (omit itemHeight):**
1821
- ```tsx
1822
- <VirtualList
1823
- data={posts}
1824
- renderItem={({ item }) => (
1825
- <Card style={{ margin: SPACING.sm }}>
1826
- <CardContent>
1827
- <Text>{item.content}</Text>
1828
- </CardContent>
1829
- </Card>
1830
- )}
1831
- />
1832
- ```
1833
-
1834
- ---
1835
1828
 
1836
1829
  ### Separator
1837
1830
 
@@ -1946,7 +1939,7 @@ const renderItem = useCallback(({ item }) => (
1946
1939
  // Matches <ListItem> — leading circle + title/subtitle lines
1947
1940
  <Skeleton.ListItem /> // props: showAvatar, showSubtitle, style
1948
1941
 
1949
- // Repeated list/grid placeholder — for VirtualList / FlatList while loading.
1942
+ // Repeated list/grid placeholder — for FlatList while loading.
1950
1943
  // columns=1 → stacked ListItemSkeleton; columns>1 → grid of MediaCardSkeleton.
1951
1944
  <Skeleton.List count={6} /> // stacked list
1952
1945
  <Skeleton.List count={6} columns={2} /> // 2-col card grid
@@ -1954,8 +1947,8 @@ const renderItem = useCallback(({ item }) => (
1954
1947
 
1955
1948
  // Also exported as named components: MediaCardSkeleton, ListItemSkeleton, ListSkeleton
1956
1949
 
1957
- // As a VirtualList loading state
1958
- <VirtualList
1950
+ // As a FlatList loading state
1951
+ <FlatList
1959
1952
  data={loading ? [] : rows}
1960
1953
  renderItem={renderItem}
1961
1954
  ListEmptyComponent={loading ? <Skeleton.List count={8} /> : <EmptyState .../>}
@@ -4425,10 +4418,12 @@ const [icon, setIcon] = useState<string | null>(null)
4425
4418
 
4426
4419
  **Import:** `import { useConfirmDialog } from '@retray-dev/ui-kit'`
4427
4420
 
4428
- **When to use:** Manage `ConfirmDialog` state without boilerplate. Replaces the `[target, setTarget]` + `visible` + `loading` state pattern that each screen would otherwise duplicate.
4421
+ **When to use:** Manage `ConfirmDialog` state without boilerplate. Replaces the `visible` + `loading` state pattern that each screen would otherwise duplicate.
4422
+
4423
+ **Note (v13):** The generic `T`, `target`, and `dialogProps` convenience object were removed. Returns `onConfirm`/`onCancel` directly. Uses refs for callback stability and `mountedRef` for safe async cleanup.
4429
4424
 
4430
4425
  ```ts
4431
- function useConfirmDialog<T>(options: UseConfirmDialogOptions): UseConfirmDialogResult<T>
4426
+ function useConfirmDialog(options: UseConfirmDialogOptions): UseConfirmDialogResult
4432
4427
 
4433
4428
  interface UseConfirmDialogOptions {
4434
4429
  onConfirm: () => void | Promise<void>
@@ -4441,34 +4436,30 @@ interface UseConfirmDialogOptions {
4441
4436
  | Field | Type | Notes |
4442
4437
  |-------|------|-------|
4443
4438
  | visible | `boolean` | Pass to `ConfirmDialog.visible` |
4444
- | target | `T \| null` | The value passed to `open()` — e.g. the item being deleted |
4445
4439
  | loading | `boolean` | Pass to `ConfirmDialog.loading` |
4446
- | open | `(target?: T) => void` | Call to show the dialog |
4447
- | dialogProps | `object` | Spread directly onto `<ConfirmDialog>` (contains `visible`, `loading`, `onConfirm`, `onCancel`) |
4440
+ | open | `() => void` | Call to show the dialog |
4441
+ | onConfirm | `() => void` | Pass directly to `ConfirmDialog.onConfirm` |
4442
+ | onCancel | `() => void` | Pass directly to `ConfirmDialog.onCancel` |
4448
4443
 
4449
4444
  **Example:**
4450
4445
  ```tsx
4451
- const { open, target, dialogProps } = useConfirmDialog<Product>({
4446
+ const { visible, loading, open, onConfirm, onCancel } = useConfirmDialog({
4452
4447
  onConfirm: async () => {
4453
- await deleteProduct(target!.id)
4448
+ await deleteProduct(productId)
4454
4449
  toast.success('Product deleted')
4455
4450
  },
4456
4451
  })
4457
4452
 
4458
- // Somewhere in your list:
4459
- <ListItem
4460
- title={product.name}
4461
- rightIcon="trash-2"
4462
- onPress={() => open(product)}
4463
- />
4464
-
4465
- // Once in your screen's JSX:
4453
+ // Somewhere in your screen:
4466
4454
  <ConfirmDialog
4467
- title={`Delete "${target?.name}"?`}
4468
- description="This action cannot be undone."
4455
+ visible={visible}
4456
+ title="Delete product?"
4457
+ subtitle="This action cannot be undone."
4469
4458
  confirmLabel="Delete"
4470
4459
  confirmVariant="destructive"
4471
- {...dialogProps}
4460
+ loading={loading}
4461
+ onConfirm={onConfirm}
4462
+ onCancel={onCancel}
4472
4463
  />
4473
4464
  ```
4474
4465
 
@@ -4482,12 +4473,12 @@ The library ships a built-in icon resolver — pass any icon name string to comp
4482
4473
 
4483
4474
  | Priority | Family | Best for |
4484
4475
  |---|---|---|
4485
- | 1 (highest) | `Feather` | Clean line icons, UI essentials |
4476
+ | 1 | `Feather` | Clean line icons, UI essentials (first match wins) |
4486
4477
  | 2 | `AntDesign` | Semantic UI icons |
4487
4478
  | 3 | `Entypo` | Social, media, navigation icons |
4488
4479
  | 4 | `FontAwesome5` | Wide coverage |
4489
4480
  | 5 | `MaterialIcons` | Material-style icons |
4490
- | 6 (lowest) | `Ionicons` | Fallback |
4481
+ | 6 | `Ionicons` | Fallback |
4491
4482
 
4492
4483
  Browse all available icons: **https://icons.expo.fyi**
4493
4484
 
@@ -4514,29 +4505,7 @@ import { Icon } from '@retray-dev/ui-kit'
4514
4505
 
4515
4506
  Returns `null` (no crash) if name not found in any family.
4516
4507
 
4517
- ### `renderIcon` helper
4518
-
4519
- ```tsx
4520
- import { renderIcon } from '@retray-dev/ui-kit'
4521
-
4522
- // Returns ReactNode or null
4523
- const icon = renderIcon('check', 18, colors.primary)
4524
- ```
4525
-
4526
- ### `getValidIconNames` utility
4527
-
4528
- ```tsx
4529
- import { getValidIconNames } from '@retray-dev/ui-kit'
4530
-
4531
- // All icon names across all configured families
4532
- const allIcons: string[] = getValidIconNames()
4533
- // => ["home", "user", "heart", "star", ...]
4534
-
4535
- // Scoped to specific families (does not mutate global config)
4536
- const featherOnly: string[] = getValidIconNames(['Feather'])
4537
- ```
4538
-
4539
- Returns `string[]` — all resolvable icon names from the configured families. Respects `configureIconFamilies()` global config. The optional `families` parameter builds a temporary cache scoped to those families without mutating global state. Use to build icon pickers, search, and galleries.
4508
+ > **v13:** `renderIcon`, `getValidIconNames`, and `configureIconFamilies` were removed. Use `Icon` component directly for all icon rendering.
4540
4509
 
4541
4510
  ### `iconName` props on components
4542
4511
 
@@ -4600,33 +4569,6 @@ getResponsiveFontSize(text, 48, [
4600
4569
 
4601
4570
  ---
4602
4571
 
4603
- ## Hover Support (Web)
4604
-
4605
- ```tsx
4606
- import { useHover } from '@retray-dev/ui-kit'
4607
-
4608
- function MyHoverableComponent() {
4609
- const { hovered, hoverHandlers } = useHover()
4610
-
4611
- return (
4612
- <View
4613
- {...hoverHandlers}
4614
- style={[
4615
- styles.container,
4616
- hovered && { backgroundColor: colors.surfaceStrong }
4617
- ]}
4618
- />
4619
- )
4620
- }
4621
- ```
4622
-
4623
- - **Web:** Returns `{ hovered: boolean, hoverHandlers: { onMouseEnter, onMouseLeave } }`
4624
- - **Native:** Always returns `{ hovered: false, hoverHandlers: {} }` — no-op, no crashes
4625
-
4626
- Built-in web hover: `MediaCard` (shadow lift), interactive components use this internally.
4627
-
4628
- ---
4629
-
4630
4572
  ## Design System Conventions
4631
4573
 
4632
4574
  ### Touch targets
@@ -4656,7 +4598,7 @@ All interactive elements maintain ≥44pt touch height per Apple HIG:
4656
4598
  - `useNativeDriver` — `Platform.OS !== 'web'` everywhere (native driver disabled on web)
4657
4599
  - `Select` — wheel modal on iOS, dialog on Android, `<select>` on web
4658
4600
  - `Toast` — full width on mobile, 400px centered on web
4659
- - Hover states — web only via `useHover()`
4601
+ - Hover states — web only
4660
4602
  - `Skeleton` shimmer highlight — adapts opacity for light/dark mode
4661
4603
 
4662
4604
  ### Dynamic Type
package/CONSUMER.md CHANGED
@@ -32,6 +32,7 @@ npx expo install \
32
32
  expo-haptics \
33
33
  expo-linear-gradient \
34
34
  expo-font \
35
+ expo-image \
35
36
  react-native-reanimated \
36
37
  react-native-gesture-handler \
37
38
  react-native-worklets \
@@ -48,7 +49,7 @@ npx expo install \
48
49
  pressto
49
50
  ```
50
51
 
51
- > `react-native-ease` and `pressto` are hard required. Omitting either crashes every component at module load — you will see a blank screen with no error message on screen.
52
+ > `react-native-ease` and `pressto` are hard required. Omitting either crashes every component at module load — you will see a blank screen with no error message on screen. `expo-image` is required for image-based components (Avatar, MediaCard, ListItem, ImageUpload, ImageViewer).
52
53
 
53
54
  ---
54
55
 
@@ -56,7 +57,6 @@ npx expo install \
56
57
 
57
58
  ```bash
58
59
  npx expo install expo-image-picker # Required only for ImageUpload component
59
- npx expo install react-native-pulsar # Richer haptic feedback (graceful fallback to expo-haptics when absent)
60
60
 
61
61
  # HolographicCard only — deep-import, NOT in main barrel:
62
62
  npx expo install @shopify/react-native-skia expo-sensors
package/DESIGN.md CHANGED
@@ -324,7 +324,7 @@ Shape language is **soft everywhere**. Buttons use `{rounded.md}` (14px) — a f
324
324
  - Sohne type family. Display weights 600–700. Body weight 400. Modest weight is intentional.
325
325
  - 8pt spacing grid with 4pt micro-steps. Section bands at `{spacing.section}` (64px).
326
326
  - Maximum two elevation tiers — flat baseline (95% of surfaces) plus one card float tier.
327
- - Haptics on every interaction via `react-native-pulsar` with `expo-haptics` fallback.
327
+ - Haptics on every interaction via `expo-haptics`.
328
328
  - Press animations via `pressto` — main-thread, gesture-handler worklets. Color transitions via `react-native-reanimated` v4.
329
329
 
330
330
  ## Colors
@@ -602,7 +602,7 @@ All pressables: `enabled={!disabled}`, `rippleColor="transparent"`, `touchSoundD
602
602
 
603
603
  ## Haptic Feedback
604
604
 
605
- All haptics via `src/utils/haptics.ts` — `react-native-pulsar` with `expo-haptics` fallback. Pulsar loads dynamically via `import()` inside a try/catch; absent in Expo Go, graceful fallback.
605
+ All haptics via `src/utils/haptics.ts` — `expo-haptics`.
606
606
 
607
607
  | Function | Use |
608
608
  |---|---|
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A personal React Native / Expo UI component library with a built-in design system, dark mode support, haptic feedback, and smooth animations.
4
4
 
5
- - 55 components across 9 categories (plus the deep-import `HolographicCard`)
5
+ - 54 components across 9 categories (plus the deep-import `HolographicCard`)
6
6
  - Light/dark theme with 12 public tokens (26 resolved) and full customization
7
7
  - Apple HIG–compliant touch targets and haptic feedback
8
8
  - Animated interactions: spring press, sliding tabs, accordion easing, animated progress
@@ -23,14 +23,16 @@ pnpm add @retray-dev/ui-kit
23
23
  Install these in your app if not already present:
24
24
 
25
25
  ```bash
26
- pnpm add expo-font expo-haptics expo-linear-gradient react-native-safe-area-context @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-worklets @react-native-picker/picker @react-native-community/slider @expo/vector-icons react-native-size-matters react-native-svg react-native-screens sonner-native pressto react-native-ease expo-image-picker
26
+ pnpm add expo-font expo-haptics expo-linear-gradient expo-image react-native-safe-area-context @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-worklets @react-native-picker/picker @react-native-community/slider @expo/vector-icons react-native-size-matters react-native-svg react-native-screens sonner-native pressto react-native-ease expo-image-picker
27
27
  ```
28
28
 
29
29
  For Expo projects, run `npx expo install` instead to get SDK-compatible versions.
30
30
 
31
31
  `pressto` is **required** — it powers the press animations on every interactive component. Omitting it crashes the import. `sonner-native` is **required** for `Toast`.
32
32
 
33
- **Optional:** for richer haptics in a custom dev build (not Expo Go), also `pnpm add react-native-pulsar`. The kit falls back to `expo-haptics` automatically when it is absent. For `ImageUpload`, add `expo-image-picker`. For the deep-import `HolographicCard`, add `@shopify/react-native-skia expo-sensors`.
33
+ **Required:** `expo-image` is required for `Avatar`, `ImageUpload`, `ImageViewer`, `ListItem`, and `MediaCard` image rendering. It replaces React Native's built-in `Image` with caching, blurhash placeholders, and cross-fade transitions.
34
+
35
+ **Optional:** for `ImageUpload`, add `expo-image-picker`. For the deep-import `HolographicCard`, add `@shopify/react-native-skia expo-sensors`.
34
36
 
35
37
  Add the Worklets Babel plugin to `babel.config.js` (required by `@gorhom/bottom-sheet`):
36
38
 
@@ -179,7 +181,8 @@ import { SPACING, ICON_SIZES, RADIUS, SHADOWS, BREAKPOINTS, TYPOGRAPHY } from '@
179
181
 
180
182
  ### Color Utilities
181
183
 
182
- - **`withAlpha(color: string, alpha: number)`** — takes a hex color string (e.g., `"#6366f1"`) and an opacity value (0–1), returns an `rgba()` string. Useful for semi-transparent overlays without adding a separate token.
184
+ - **`withAlpha(hex: string, alpha: number)`** — takes a hex color string (e.g., `"#6366f1"`) and an opacity value (0–1), returns an `rgba()` string. Useful for semi-transparent overlays without adding a separate token.
185
+ - **`hexToRgb(hex: string)`** — returns `{ r, g, b } | null`. Converts hex color to RGB components.
183
186
 
184
187
  ## Components
185
188
 
@@ -192,7 +195,7 @@ import { SPACING, ICON_SIZES, RADIUS, SHADOWS, BREAKPOINTS, TYPOGRAPHY } from '@
192
195
  | Navigation | `AppHeader`, `TabBar`, `PagerDots` |
193
196
  | Overlays | `Sheet`, `ConfirmDialog`, `ImageViewer` |
194
197
  | Feedback | `Toast` / `ToastProvider` / `useToast` |
195
- | Data | `ListItem`, `ListGroup` (+ `.Header` / `.Footer`), `MenuItem`, `MenuGroup` (+ `.Header` / `.Footer`), `VirtualList`, `Chip` / `ChipGroup`, `LabelValue`, `MonthPicker`, `CategoryStrip`, `DetailRow` |
198
+ | Data | `ListItem`, `ListGroup` (+ `.Header` / `.Footer`), `MenuItem`, `MenuGroup` (+ `.Header` / `.Footer`), `Chip` / `ChipGroup`, `LabelValue`, `MonthPicker`, `CategoryStrip`, `DetailRow` |
196
199
  | Utilities | `Pressable`, `Icon`, `RetrayProvider`, `ErrorBoundary` |
197
200
 
198
201
  Deep-import only: `HolographicCard` — `import { HolographicCard } from '@retray-dev/ui-kit/HolographicCard'`.
@@ -226,9 +229,11 @@ toast({ title: 'Saved', variant: 'success' })
226
229
  />
227
230
 
228
231
  // Color utilities
229
- import { useTheme, withAlpha } from '@retray-dev/ui-kit'
232
+ import { useTheme, withAlpha, hexToRgb } from '@retray-dev/ui-kit'
230
233
  const { colors } = useTheme()
231
234
  ;<View style={{ backgroundColor: withAlpha(colors.primary, 0.15) }} />
235
+ // hexToRgb — converts hex to RGB components
236
+ const rgb = hexToRgb('#6366f1') // { r: 99, g: 102, b: 241 }
232
237
  ```
233
238
 
234
239
  Full props reference and more examples are available in [COMPONENTS.md](./COMPONENTS.md), which is also shipped inside the npm package for use with AI tools: