@retray-dev/ui-kit 12.1.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +183 -147
- package/CONSUMER.md +2 -2
- package/DESIGN.md +2 -2
- package/README.md +13 -8
- package/dist/Accordion.d.mts +6 -0
- package/dist/Accordion.d.ts +6 -0
- package/dist/Accordion.js +62 -208
- package/dist/Accordion.mjs +6 -5
- package/dist/AlertBanner.js +29 -151
- package/dist/AlertBanner.mjs +3 -3
- package/dist/AppHeader.js +37 -233
- package/dist/AppHeader.mjs +6 -7
- package/dist/Avatar.d.mts +17 -1
- package/dist/Avatar.d.ts +17 -1
- package/dist/Avatar.js +80 -113
- package/dist/Avatar.mjs +2 -2
- package/dist/Badge.js +24 -147
- package/dist/Badge.mjs +3 -3
- package/dist/Button.js +86 -274
- package/dist/Button.mjs +6 -6
- package/dist/Card.js +15 -198
- package/dist/Card.mjs +4 -5
- package/dist/CategoryStrip.d.mts +0 -5
- package/dist/CategoryStrip.d.ts +0 -5
- package/dist/CategoryStrip.js +47 -263
- package/dist/CategoryStrip.mjs +6 -6
- package/dist/Checkbox.js +15 -198
- package/dist/Checkbox.mjs +5 -5
- package/dist/Chip.js +44 -234
- package/dist/Chip.mjs +7 -6
- package/dist/ConfirmDialog.js +100 -296
- package/dist/ConfirmDialog.mjs +7 -7
- package/dist/CurrencyDisplay.js +1 -112
- package/dist/CurrencyDisplay.mjs +2 -2
- package/dist/CurrencyInput.js +35 -160
- package/dist/CurrencyInput.mjs +5 -5
- package/dist/DetailRow.js +25 -148
- package/dist/DetailRow.mjs +3 -3
- package/dist/EmptyState.js +87 -275
- package/dist/EmptyState.mjs +7 -7
- package/dist/ErrorBoundary.js +32 -197
- package/dist/ErrorBoundary.mjs +4 -4
- package/dist/Form.js +1 -112
- package/dist/Form.mjs +2 -2
- package/dist/HolographicCard.d.mts +0 -28
- package/dist/HolographicCard.d.ts +0 -28
- package/dist/HolographicCard.js +20 -130
- package/dist/HolographicCard.mjs +9 -32
- package/dist/IconButton.js +36 -232
- package/dist/IconButton.mjs +5 -6
- package/dist/IconPicker.js +222 -927
- package/dist/IconPicker.mjs +5 -5
- package/dist/ImageUpload.d.mts +5 -1
- package/dist/ImageUpload.d.ts +5 -1
- package/dist/ImageUpload.js +32 -215
- package/dist/ImageUpload.mjs +5 -6
- package/dist/ImageViewer.js +75 -264
- package/dist/ImageViewer.mjs +8 -8
- package/dist/Input.d.mts +1 -1
- package/dist/Input.d.ts +1 -1
- package/dist/Input.js +35 -160
- package/dist/Input.mjs +4 -4
- package/dist/LabelValue.js +24 -147
- package/dist/LabelValue.mjs +3 -3
- package/dist/ListGroup.js +1 -112
- package/dist/ListGroup.mjs +2 -2
- package/dist/ListItem.js +38 -233
- package/dist/ListItem.mjs +5 -6
- package/dist/MediaCard.d.mts +0 -14
- package/dist/MediaCard.d.ts +0 -14
- package/dist/MediaCard.js +69 -313
- package/dist/MediaCard.mjs +5 -6
- package/dist/MenuGroup.js +1 -112
- package/dist/MenuGroup.mjs +2 -2
- package/dist/MenuItem.js +36 -232
- package/dist/MenuItem.mjs +5 -6
- package/dist/MonthPicker.js +8 -161
- package/dist/MonthPicker.mjs +3 -3
- package/dist/NumberStepper.js +40 -236
- package/dist/NumberStepper.mjs +5 -6
- package/dist/PagerDots.d.mts +1 -1
- package/dist/PagerDots.d.ts +1 -1
- package/dist/PagerDots.js +69 -222
- package/dist/PagerDots.mjs +6 -5
- package/dist/Pressable.js +14 -85
- package/dist/Pressable.mjs +4 -4
- package/dist/PricingCard.js +94 -279
- package/dist/PricingCard.mjs +8 -8
- package/dist/Progress.js +3 -121
- package/dist/Progress.mjs +3 -3
- package/dist/RadioGroup.js +52 -263
- package/dist/RadioGroup.mjs +5 -5
- package/dist/RetrayProvider.d.mts +1 -1
- package/dist/RetrayProvider.d.ts +1 -1
- package/dist/RetrayProvider.js +5 -6
- package/dist/RetrayProvider.mjs +3 -3
- package/dist/Select.d.mts +2 -1
- package/dist/Select.d.ts +2 -1
- package/dist/Select.js +24 -230
- package/dist/Select.mjs +4 -5
- package/dist/SelectableCard.d.mts +27 -0
- package/dist/SelectableCard.d.ts +27 -0
- package/dist/SelectableCard.js +335 -0
- package/dist/SelectableCard.mjs +8 -0
- package/dist/SelectableGrid.d.mts +0 -21
- package/dist/SelectableGrid.d.ts +0 -21
- package/dist/SelectableGrid.js +49 -269
- package/dist/SelectableGrid.mjs +5 -6
- package/dist/Separator.js +1 -112
- package/dist/Separator.mjs +2 -2
- package/dist/Sheet.js +16 -163
- package/dist/Sheet.mjs +3 -3
- package/dist/SheetSelect.js +39 -234
- package/dist/SheetSelect.mjs +6 -6
- package/dist/Skeleton.d.mts +3 -1
- package/dist/Skeleton.d.ts +3 -1
- package/dist/Skeleton.js +7 -124
- package/dist/Skeleton.mjs +3 -3
- package/dist/Slider.js +6 -159
- package/dist/Slider.mjs +3 -3
- package/dist/Spinner.js +3 -114
- package/dist/Spinner.mjs +2 -2
- package/dist/Stats.d.mts +4 -1
- package/dist/Stats.d.ts +4 -1
- package/dist/Stats.js +60 -234
- package/dist/Stats.mjs +5 -6
- package/dist/Switch.js +24 -173
- package/dist/Switch.mjs +5 -4
- package/dist/TabBar.js +43 -198
- package/dist/TabBar.mjs +5 -4
- package/dist/Tabs.js +15 -197
- package/dist/Tabs.mjs +5 -5
- package/dist/Text.js +9 -128
- package/dist/Text.mjs +2 -2
- package/dist/Textarea.d.mts +2 -1
- package/dist/Textarea.d.ts +2 -1
- package/dist/Textarea.js +71 -217
- package/dist/Textarea.mjs +4 -4
- package/dist/Toast.js +1 -112
- package/dist/Toast.mjs +2 -2
- package/dist/Toggle.js +39 -234
- package/dist/Toggle.mjs +6 -6
- package/dist/{chunk-FFTYLPSB.mjs → chunk-2QOHHBJC.mjs} +13 -7
- package/dist/{chunk-BCWEHE34.mjs → chunk-2VIDP72N.mjs} +3 -3
- package/dist/{chunk-PGERH3P7.mjs → chunk-4NQFTHN3.mjs} +13 -7
- package/dist/{chunk-3N2M3WZL.mjs → chunk-4ZO5PTKF.mjs} +4 -4
- package/dist/{chunk-MYZ2EDYU.mjs → chunk-5MYNAAFE.mjs} +13 -17
- package/dist/{chunk-E7NEHHXV.mjs → chunk-62BBSSUF.mjs} +3 -3
- package/dist/{chunk-ISY26JQJ.mjs → chunk-6CR4S6W2.mjs} +3 -3
- package/dist/{chunk-FUVYSVGR.mjs → chunk-6QLBHUEG.mjs} +8 -7
- package/dist/chunk-ARONDO7M.mjs +40 -0
- package/dist/{chunk-3UYAZ7I4.mjs → chunk-AZV7KNJI.mjs} +3 -3
- package/dist/{chunk-HLMPMUK2.mjs → chunk-BTUW5LSG.mjs} +11 -8
- package/dist/chunk-BULKGOIZ.mjs +235 -0
- package/dist/{chunk-265G6A46.mjs → chunk-CBIZLRYH.mjs} +29 -12
- package/dist/chunk-CM2DG4MR.mjs +142 -0
- package/dist/{chunk-2I2AYECM.mjs → chunk-DBHSUUKU.mjs} +2 -2
- package/dist/{chunk-P64WHW4A.mjs → chunk-DE25XTVQ.mjs} +3 -3
- package/dist/{chunk-DI7CBDL6.mjs → chunk-E4EQSCKR.mjs} +5 -5
- package/dist/{chunk-357YO24D.mjs → chunk-EHGBHFMH.mjs} +9 -17
- package/dist/{chunk-GK4VRMNE.mjs → chunk-EROPDCB5.mjs} +24 -27
- package/dist/{chunk-XBAGGKLW.mjs → chunk-ERWJPVX7.mjs} +2 -2
- package/dist/{chunk-LRM4AVYY.mjs → chunk-ESQDPO5E.mjs} +7 -7
- package/dist/{chunk-EFLFRAHD.mjs → chunk-EW2FIDSM.mjs} +1 -1
- package/dist/{chunk-7HSILTC4.mjs → chunk-FTTI6T5Q.mjs} +4 -4
- package/dist/{chunk-X26S5EVZ.mjs → chunk-HUSSF6TF.mjs} +1 -1
- package/dist/chunk-IFYMBOEN.mjs +14 -0
- package/dist/{chunk-S3KJCPEJ.mjs → chunk-IGU223UM.mjs} +80 -4
- package/dist/chunk-IJCMPVW5.mjs +121 -0
- package/dist/{chunk-I4V5XZPS.mjs → chunk-ITG4JQM3.mjs} +4 -4
- package/dist/{chunk-F4V6XLP4.mjs → chunk-K3QX2M26.mjs} +11 -8
- package/dist/{chunk-V6NFJXKO.mjs → chunk-K7TKID3V.mjs} +8 -7
- package/dist/{chunk-ZHMSAYLT.mjs → chunk-KAGADD2O.mjs} +4 -4
- package/dist/{chunk-3GEYJ7I5.mjs → chunk-KC5QDYGZ.mjs} +4 -4
- package/dist/{chunk-HJ46DTJE.mjs → chunk-KPTY7UYQ.mjs} +1 -1
- package/dist/{chunk-EMUWGDWC.mjs → chunk-KSSVIFYR.mjs} +11 -12
- package/dist/chunk-L3YKPTJQ.mjs +119 -0
- package/dist/chunk-M53LC4Q7.mjs +35 -0
- package/dist/{chunk-NXI4YDZ2.mjs → chunk-MP7GLMIR.mjs} +17 -25
- package/dist/chunk-MZ6WRTD2.mjs +40 -0
- package/dist/chunk-NGEN2EES.mjs +581 -0
- package/dist/{chunk-JULSIZDM.mjs → chunk-OBV72JD4.mjs} +1 -1
- package/dist/{chunk-2A2LEFZG.mjs → chunk-PGQ6FMXS.mjs} +6 -5
- package/dist/{chunk-BQZE3HAW.mjs → chunk-PI6RULJX.mjs} +1 -1
- package/dist/{chunk-FA2KMTH5.mjs → chunk-RA6SAAFE.mjs} +9 -8
- package/dist/{chunk-FVTVCJAH.mjs → chunk-RRKM4MKB.mjs} +7 -7
- package/dist/{chunk-AKM4EPOT.mjs → chunk-S2VGME7X.mjs} +1 -1
- package/dist/{chunk-OULVKTWL.mjs → chunk-S44XWTTC.mjs} +35 -25
- package/dist/{chunk-QSFV2P7O.mjs → chunk-SZEKQAOY.mjs} +1 -1
- package/dist/{chunk-N4ZPVCJH.mjs → chunk-TETMEKZE.mjs} +9 -9
- package/dist/{chunk-2CBQKU7H.mjs → chunk-TMH263OK.mjs} +5 -4
- package/dist/{chunk-D3Y2T42P.mjs → chunk-U6DEBYU5.mjs} +10 -9
- package/dist/{chunk-4WFMPFZB.mjs → chunk-UOKFSFNJ.mjs} +2 -2
- package/dist/{chunk-WOEWGSTU.mjs → chunk-URIH43IJ.mjs} +13 -21
- package/dist/{chunk-JCZQOY4O.mjs → chunk-V2ZB2XNS.mjs} +16 -10
- package/dist/{chunk-P73V2EKS.mjs → chunk-WIPEDNSD.mjs} +7 -7
- package/dist/{chunk-BOVUP27T.mjs → chunk-XCIG6HT2.mjs} +6 -5
- package/dist/chunk-Y6YS33GM.mjs +131 -0
- package/dist/{chunk-5OLNXP3S.mjs → chunk-ZKDKKQCE.mjs} +29 -7
- package/dist/{chunk-DF6DU42P.mjs → chunk-ZTPYUU5C.mjs} +5 -5
- package/dist/{index-wt-orHUi.d.ts → index-CY34hxPN.d.mts} +1 -0
- package/dist/{index-wt-orHUi.d.mts → index-CY34hxPN.d.ts} +1 -0
- package/dist/index.d.mts +15 -74
- package/dist/index.d.ts +15 -74
- package/dist/index.js +1055 -1562
- package/dist/index.mjs +81 -84
- package/package.json +8 -10
- package/src/components/Accordion/Accordion.tsx +32 -9
- package/src/components/AlertBanner/AlertBanner.tsx +7 -6
- package/src/components/AppHeader/AppHeader.tsx +1 -1
- package/src/components/Avatar/Avatar.tsx +92 -1
- package/src/components/Avatar/index.ts +2 -2
- package/src/components/Badge/Badge.tsx +2 -2
- package/src/components/Button/Button.tsx +64 -57
- package/src/components/Card/Card.tsx +1 -0
- package/src/components/CategoryStrip/CategoryStrip.tsx +36 -49
- package/src/components/Chip/Chip.tsx +5 -4
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +13 -6
- package/src/components/DetailRow/DetailRow.tsx +3 -3
- package/src/components/EmptyState/EmptyState.tsx +2 -2
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +6 -6
- package/src/components/HolographicCard/HolographicCard.tsx +14 -95
- package/src/components/IconButton/IconButton.tsx +2 -2
- package/src/components/IconPicker/IconPicker.tsx +13 -12
- package/src/components/ImageUpload/ImageUpload.tsx +24 -28
- package/src/components/ImageViewer/ImageViewer.tsx +3 -3
- package/src/components/Input/Input.tsx +11 -5
- package/src/components/LabelValue/LabelValue.tsx +2 -2
- package/src/components/ListItem/ListItem.tsx +4 -4
- package/src/components/MediaCard/MediaCard.tsx +21 -59
- package/src/components/MenuItem/MenuItem.tsx +2 -2
- package/src/components/MonthPicker/MonthPicker.tsx +2 -2
- package/src/components/NumberStepper/NumberStepper.tsx +6 -6
- package/src/components/PagerDots/PagerDots.tsx +38 -28
- package/src/components/PricingCard/PricingCard.tsx +6 -6
- package/src/components/RadioGroup/RadioGroup.tsx +18 -31
- package/src/components/Select/Select.tsx +32 -39
- package/src/components/SelectableCard/SelectableCard.tsx +302 -0
- package/src/components/SelectableCard/index.ts +1 -0
- package/src/components/SelectableGrid/SelectableGrid.tsx +38 -72
- package/src/components/Sheet/Sheet.tsx +11 -4
- package/src/components/SheetSelect/SheetSelect.tsx +3 -3
- package/src/components/Skeleton/Skeleton.tsx +6 -3
- package/src/components/Spinner/Spinner.tsx +2 -2
- package/src/components/Stats/Stats.tsx +36 -8
- package/src/components/Switch/Switch.tsx +9 -6
- package/src/components/TabBar/TabBar.tsx +9 -8
- package/src/components/Text/Text.tsx +12 -1
- package/src/components/Textarea/Textarea.tsx +18 -32
- package/src/components/Toggle/Toggle.tsx +3 -3
- package/src/hooks/useConfirmDialog.ts +31 -42
- package/src/index.ts +4 -4
- package/src/theme/ThemeProvider.tsx +1 -4
- package/src/theme/colorUtils.ts +1 -72
- package/src/theme/colors.ts +47 -1
- package/src/theme/types.ts +6 -3
- package/src/utils/animations.ts +0 -47
- package/src/utils/curatedIcons.ts +93 -801
- package/src/utils/haptics.ts +13 -208
- package/src/utils/icons.ts +27 -91
- package/src/utils/pressable.ts +10 -61
- package/dist/VirtualList.d.mts +0 -19
- package/dist/VirtualList.d.ts +0 -19
- package/dist/VirtualList.js +0 -38
- package/dist/VirtualList.mjs +0 -2
- package/dist/chunk-3DKJ2GIC.mjs +0 -30
- package/dist/chunk-AQEVCEXV.mjs +0 -164
- package/dist/chunk-DOGIPOF5.mjs +0 -131
- package/dist/chunk-DVK4G2GT.mjs +0 -59
- package/dist/chunk-EJ7ZPXOH.mjs +0 -163
- package/dist/chunk-J6Q2YJEV.mjs +0 -134
- package/dist/chunk-JNVAIDLK.mjs +0 -136
- package/dist/chunk-KA7LTET3.mjs +0 -71
- package/dist/chunk-KHYX4IOM.mjs +0 -1114
- package/dist/chunk-NC5ZTR2Y.mjs +0 -32
- package/dist/chunk-YNROWHQJ.mjs +0 -46
- package/src/components/VirtualList/VirtualList.tsx +0 -60
- package/src/components/VirtualList/index.ts +0 -1
- package/src/utils/fontGuard.ts +0 -35
- package/src/utils/hover.ts +0 -25
- package/src/utils/useColorTransition.ts +0 -40
- package/src/utils/usePressScale.ts +0 -75
package/COMPONENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @retray-dev/ui-kit — Component Reference (
|
|
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
|
-
|
|
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
|
-
|
|
105
|
+
|
|
106
106
|
|
|
107
107
|
### Troubleshooting: `react-native-screens` codegen on RN 0.83
|
|
108
108
|
|
|
@@ -275,8 +275,9 @@ The full palette components consume. Never supply these directly — they are co
|
|
|
275
275
|
|-------|-------------|---------|
|
|
276
276
|
| `foregroundSubtle` | `foreground` @ ~70% | Body text, subtitles, secondary content |
|
|
277
277
|
| `foregroundMuted` | `foreground` @ ~62% | Captions, timestamps, placeholders |
|
|
278
|
-
| `surface` | `background` slightly off-canvas | Chip backgrounds, input fills, tag backgrounds
|
|
278
|
+
| `surface` | `background` slightly off-canvas | Chip backgrounds, input fills, tag backgrounds |
|
|
279
279
|
| `surfaceStrong` | `background` stronger offset | Pressed/hover fill states |
|
|
280
|
+
| `skeleton` | `background` @ ±10% | Skeleton placeholder — higher contrast than surface for visibility |
|
|
280
281
|
| `destructiveTint` | `destructive` blended to bg | Alert banner background, toast background |
|
|
281
282
|
| `destructiveBorder` | `destructive` @ 30% | Alert banner border, badge outline |
|
|
282
283
|
| `successTint` | `success` blended to bg | Success banner background |
|
|
@@ -285,6 +286,7 @@ The full palette components consume. Never supply these directly — they are co
|
|
|
285
286
|
| `warningBorder` | `warning` @ 30% | Warning banner border |
|
|
286
287
|
| `ring` | `= primary` | Focus ring color (always matches primary) |
|
|
287
288
|
| `input` | `= border` | Input field border (always matches border) |
|
|
289
|
+
| `separator` | `border` @ ±16-22% | Divider/separator line — deliberately darker than border |
|
|
288
290
|
| `overlay` | `overlay` token or `rgba(0,0,0,0.45)` | Backdrop behind sheets and dialogs |
|
|
289
291
|
| `accentResolved` | `accent` token or `= primary` | Resolved accent color — always present |
|
|
290
292
|
| `accentForegroundResolved` | `accentForeground` token or `= primaryForeground` | Resolved text on accent — always present |
|
|
@@ -312,7 +314,7 @@ const { colors } = useTheme()
|
|
|
312
314
|
import { deriveColors } from '@retray-dev/ui-kit'
|
|
313
315
|
|
|
314
316
|
const resolved = deriveColors(myThemeColors, 'light')
|
|
315
|
-
// resolved contains all
|
|
317
|
+
// resolved contains all 26 ResolvedColors tokens
|
|
316
318
|
```
|
|
317
319
|
|
|
318
320
|
---
|
|
@@ -917,7 +919,7 @@ No consumer action required — the fix is internal.
|
|
|
917
919
|
| size | `'sm' \| 'md' \| 'lg'` | `'md'` | Controls height, padding, and icon size |
|
|
918
920
|
| loading | `boolean` | `false` | Replaces label with spinner, forces disabled state |
|
|
919
921
|
| fullWidth | `boolean` | `false` | Stretches to container width (`alignSelf: 'stretch'`) |
|
|
920
|
-
| disabled | `boolean` | — |
|
|
922
|
+
| disabled | `boolean` | — | Per-variant explicit colors (no opacity). `filled`→`surface` bg, `secondary`→`border` border, `text`/`outline`→`foregroundMuted` text |
|
|
921
923
|
| icon | `React.ReactNode \| ((props: { label, size, variant }) => React.ReactNode)` | — | Icon rendered alongside label |
|
|
922
924
|
| iconName | `string` | — | Icon name from `@expo/vector-icons`. Takes precedence over `icon` |
|
|
923
925
|
| iconColor | `string` | — | Override icon color. Defaults to variant label color |
|
|
@@ -1559,6 +1561,65 @@ const [guests, setGuests] = useState(2)
|
|
|
1559
1561
|
|
|
1560
1562
|
---
|
|
1561
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
|
+
|
|
1562
1623
|
### Card
|
|
1563
1624
|
|
|
1564
1625
|
**Import:** `import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@retray-dev/ui-kit'`
|
|
@@ -1763,73 +1824,7 @@ const [guests, setGuests] = useState(2)
|
|
|
1763
1824
|
|
|
1764
1825
|
---
|
|
1765
1826
|
|
|
1766
|
-
### VirtualList
|
|
1767
1827
|
|
|
1768
|
-
**Import:** `import { VirtualList } from '@retray-dev/ui-kit'`
|
|
1769
|
-
|
|
1770
|
-
**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.
|
|
1771
|
-
|
|
1772
|
-
| Prop | Type | Default | Notes |
|
|
1773
|
-
|------|------|---------|-------|
|
|
1774
|
-
| data | `T[]` | — | Array of items to render |
|
|
1775
|
-
| renderItem | `ListRenderItem<T>` | — | Render function for each item |
|
|
1776
|
-
| itemHeight | `number` | — | Fixed row height in px. Enables `getItemLayout` for performance with large datasets |
|
|
1777
|
-
| keyExtractor | `(item: T, index: number) => string` | Auto | Defaults to `item.id` or index. Override for custom keys |
|
|
1778
|
-
| ...FlatListProps | — | — | All standard FlatList props pass through |
|
|
1779
|
-
|
|
1780
|
-
**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.
|
|
1781
|
-
|
|
1782
|
-
**Default key extraction:** Uses `item.id` (converted to string) or falls back to index. Override with `keyExtractor` for custom logic.
|
|
1783
|
-
|
|
1784
|
-
**Example — simple list:**
|
|
1785
|
-
```tsx
|
|
1786
|
-
<VirtualList
|
|
1787
|
-
data={items}
|
|
1788
|
-
renderItem={({ item }) => (
|
|
1789
|
-
<ListItem
|
|
1790
|
-
title={item.title}
|
|
1791
|
-
subtitle={item.subtitle}
|
|
1792
|
-
onPress={() => handlePress(item.id)}
|
|
1793
|
-
/>
|
|
1794
|
-
)}
|
|
1795
|
-
itemHeight={56}
|
|
1796
|
-
/>
|
|
1797
|
-
```
|
|
1798
|
-
|
|
1799
|
-
**Example — large dataset with memoized render:**
|
|
1800
|
-
```tsx
|
|
1801
|
-
const renderItem = useCallback(({ item }) => (
|
|
1802
|
-
<ListItem
|
|
1803
|
-
title={item.title}
|
|
1804
|
-
subtitle={item.subtitle}
|
|
1805
|
-
leftIcon="circle"
|
|
1806
|
-
showSeparator
|
|
1807
|
-
onPress={() => navigate('detail', { id: item.id })}
|
|
1808
|
-
/>
|
|
1809
|
-
), [])
|
|
1810
|
-
|
|
1811
|
-
<VirtualList
|
|
1812
|
-
data={thousands}
|
|
1813
|
-
renderItem={renderItem}
|
|
1814
|
-
itemHeight={64}
|
|
1815
|
-
/>
|
|
1816
|
-
```
|
|
1817
|
-
|
|
1818
|
-
**Example — variable height rows (omit itemHeight):**
|
|
1819
|
-
```tsx
|
|
1820
|
-
<VirtualList
|
|
1821
|
-
data={posts}
|
|
1822
|
-
renderItem={({ item }) => (
|
|
1823
|
-
<Card style={{ margin: SPACING.sm }}>
|
|
1824
|
-
<CardContent>
|
|
1825
|
-
<Text>{item.content}</Text>
|
|
1826
|
-
</CardContent>
|
|
1827
|
-
</Card>
|
|
1828
|
-
)}
|
|
1829
|
-
/>
|
|
1830
|
-
```
|
|
1831
|
-
|
|
1832
|
-
---
|
|
1833
1828
|
|
|
1834
1829
|
### Separator
|
|
1835
1830
|
|
|
@@ -1902,6 +1897,7 @@ const renderItem = useCallback(({ item }) => (
|
|
|
1902
1897
|
| width | `number \| string` | `'100%'` | Width of placeholder |
|
|
1903
1898
|
| height | `number` | `16` | Height of placeholder |
|
|
1904
1899
|
| borderRadius | `number` | `6` | Corner radius |
|
|
1900
|
+
| backgroundColor | `string` | `colors.skeleton` | Override placeholder background color |
|
|
1905
1901
|
| preset | `'base' \| 'circle' \| 'text'` | `'base'` | Convenience preset |
|
|
1906
1902
|
| diameter | `number` | `40` | Used by `'circle'` preset — overrides width/height |
|
|
1907
1903
|
| style | `ViewStyle` | — | — |
|
|
@@ -1943,7 +1939,7 @@ const renderItem = useCallback(({ item }) => (
|
|
|
1943
1939
|
// Matches <ListItem> — leading circle + title/subtitle lines
|
|
1944
1940
|
<Skeleton.ListItem /> // props: showAvatar, showSubtitle, style
|
|
1945
1941
|
|
|
1946
|
-
// Repeated list/grid placeholder — for
|
|
1942
|
+
// Repeated list/grid placeholder — for FlatList while loading.
|
|
1947
1943
|
// columns=1 → stacked ListItemSkeleton; columns>1 → grid of MediaCardSkeleton.
|
|
1948
1944
|
<Skeleton.List count={6} /> // stacked list
|
|
1949
1945
|
<Skeleton.List count={6} columns={2} /> // 2-col card grid
|
|
@@ -1951,8 +1947,8 @@ const renderItem = useCallback(({ item }) => (
|
|
|
1951
1947
|
|
|
1952
1948
|
// Also exported as named components: MediaCardSkeleton, ListItemSkeleton, ListSkeleton
|
|
1953
1949
|
|
|
1954
|
-
// As a
|
|
1955
|
-
<
|
|
1950
|
+
// As a FlatList loading state
|
|
1951
|
+
<FlatList
|
|
1956
1952
|
data={loading ? [] : rows}
|
|
1957
1953
|
renderItem={renderItem}
|
|
1958
1954
|
ListEmptyComponent={loading ? <Skeleton.List count={8} /> : <EmptyState .../>}
|
|
@@ -2349,6 +2345,94 @@ const [accepted, setAccepted] = useState(false)
|
|
|
2349
2345
|
|
|
2350
2346
|
---
|
|
2351
2347
|
|
|
2348
|
+
### SelectableCard
|
|
2349
|
+
|
|
2350
|
+
**Import:** `import { SelectableCard, SelectableCardGroup } from '@retray-dev/ui-kit'`
|
|
2351
|
+
|
|
2352
|
+
**When to use:** Selectable cards with radio (single-select) or checkbox (multi-select) behavior. Each card supports an icon, title, description, and disabled state. Perfect for role selectors, plan pickers, feature/module configuration — anywhere you need descriptive options that communicate more than a simple label.
|
|
2353
|
+
|
|
2354
|
+
| Prop | Type | Default | Notes |
|
|
2355
|
+
|------|------|---------|-------|
|
|
2356
|
+
| value | `string` | required | The value this card represents |
|
|
2357
|
+
| title | `string` | required | Card title |
|
|
2358
|
+
| description | `string` | — | Secondary text below title |
|
|
2359
|
+
| iconName | `string` | — | Left icon from @expo/vector-icons |
|
|
2360
|
+
| icon | `ReactNode` | — | Custom left icon |
|
|
2361
|
+
| disabled | `boolean` | `false` | Grayed out, no press/haptic |
|
|
2362
|
+
| style | `ViewStyle` | — | — |
|
|
2363
|
+
|
|
2364
|
+
**SelectableCardGroup Props:**
|
|
2365
|
+
|
|
2366
|
+
| Prop | Type | Default | Notes |
|
|
2367
|
+
|------|------|---------|-------|
|
|
2368
|
+
| type | `'radio' \| 'checkbox'` | `'radio'` | Selection mode |
|
|
2369
|
+
| value | `string \| string[]` | required | Selected value(s) |
|
|
2370
|
+
| onValueChange | `(value: string \| string[]) => void` | required | — |
|
|
2371
|
+
| variant | `'elevated' \| 'outlined' \| 'filled'` | `'elevated'` | Card surface |
|
|
2372
|
+
| gap | `number` | `s(8)` | Spacing between cards |
|
|
2373
|
+
| style | `ViewStyle` | — | — |
|
|
2374
|
+
|
|
2375
|
+
**Card variants:** `elevated` (shadow depth, 2px border matches background when unselected — no layout shift on select), `outlined` (2px border), `filled` (surfaceStrong background + 2px border). All variants use a consistent 2px border width so cards don't resize when selected.
|
|
2376
|
+
|
|
2377
|
+
**Selection visual:** Selected cards get a 2px `colors.primary` border + `EaseView` animated selector indicator. Radio shows a 24×24 circle with spring-animated inner dot. Checkbox shows a 24×24 box with opacity-animated checkmark. Both follow the same visual patterns as `RadioGroup` and `Checkbox`.
|
|
2378
|
+
|
|
2379
|
+
**Haptics:** `impactLight` on selection.
|
|
2380
|
+
|
|
2381
|
+
**Disabled:** Dims content to `foregroundMuted`, removes shadow/elevation, keeps border at `colors.border`. No press, no haptic.
|
|
2382
|
+
|
|
2383
|
+
**Examples:**
|
|
2384
|
+
```tsx
|
|
2385
|
+
// Single select (radio)
|
|
2386
|
+
<SelectableCardGroup
|
|
2387
|
+
type="radio"
|
|
2388
|
+
value={selectedRole}
|
|
2389
|
+
onValueChange={setSelectedRole}
|
|
2390
|
+
>
|
|
2391
|
+
<SelectableCard
|
|
2392
|
+
value="admin"
|
|
2393
|
+
iconName="shield"
|
|
2394
|
+
title="Administrator"
|
|
2395
|
+
description="Full access to reports, settings, and business management"
|
|
2396
|
+
/>
|
|
2397
|
+
<SelectableCard
|
|
2398
|
+
value="waiter"
|
|
2399
|
+
iconName="user-check"
|
|
2400
|
+
title="Waiter"
|
|
2401
|
+
description="Take orders from tables and manage requests"
|
|
2402
|
+
/>
|
|
2403
|
+
<SelectableCard
|
|
2404
|
+
value="cook"
|
|
2405
|
+
iconName="chef-hat"
|
|
2406
|
+
title="Cook"
|
|
2407
|
+
description="Receive and prepare items from each order in the kitchen"
|
|
2408
|
+
disabled
|
|
2409
|
+
/>
|
|
2410
|
+
</SelectableCardGroup>
|
|
2411
|
+
|
|
2412
|
+
// Multi select (checkbox)
|
|
2413
|
+
<SelectableCardGroup
|
|
2414
|
+
type="checkbox"
|
|
2415
|
+
value={selectedRoles}
|
|
2416
|
+
onValueChange={setSelectedRoles}
|
|
2417
|
+
variant="outlined"
|
|
2418
|
+
>
|
|
2419
|
+
<SelectableCard
|
|
2420
|
+
value="reports"
|
|
2421
|
+
iconName="bar-chart-2"
|
|
2422
|
+
title="Reports"
|
|
2423
|
+
description="View sales, inventory, and performance analytics"
|
|
2424
|
+
/>
|
|
2425
|
+
<SelectableCard
|
|
2426
|
+
value="inventory"
|
|
2427
|
+
iconName="package"
|
|
2428
|
+
title="Inventory"
|
|
2429
|
+
description="Manage stock levels and supplier orders"
|
|
2430
|
+
/>
|
|
2431
|
+
</SelectableCardGroup>
|
|
2432
|
+
```
|
|
2433
|
+
|
|
2434
|
+
---
|
|
2435
|
+
|
|
2352
2436
|
### Slider
|
|
2353
2437
|
|
|
2354
2438
|
**Import:** `import { Slider } from '@retray-dev/ui-kit'`
|
|
@@ -2510,6 +2594,7 @@ const [tab, setTab] = useState('profile')
|
|
|
2510
2594
|
iconName?: string // Icon name from @expo/vector-icons
|
|
2511
2595
|
icon?: ReactNode // Custom icon node
|
|
2512
2596
|
iconColor?: string // Override icon color (defaults to foregroundMuted)
|
|
2597
|
+
triggerActions?: ReactNode // Action buttons rendered between trigger and chevron. Touch-isolated — taps on actions won't toggle the accordion.
|
|
2513
2598
|
}
|
|
2514
2599
|
```
|
|
2515
2600
|
|
|
@@ -3980,12 +4065,13 @@ export default function TabLayout() {
|
|
|
3980
4065
|
| icon | `React.ReactNode` | — | Custom icon node |
|
|
3981
4066
|
| iconName | `string` | — | Icon from `@expo/vector-icons` (left of value, `colors.primary`) |
|
|
3982
4067
|
| iconColor | `string` | `colors.primary` | Override icon color |
|
|
4068
|
+
| size | `'default' \| 'compact'` | `'default'` | `compact`: 16px SemiBold value, 11px label, reduced padding |
|
|
3983
4069
|
| variant | `'elevated' \| 'outlined' \| 'filled'` | `'elevated'` | Card surface variant |
|
|
3984
4070
|
| onPress | `() => void` | — | Makes the card pressable (haptic `impactLight()`) |
|
|
3985
4071
|
| style | `ViewStyle` | — | — |
|
|
3986
4072
|
| accessibilityLabel | `string` | — | — |
|
|
3987
4073
|
|
|
3988
|
-
**Design notes:** Value uses `Sohne-Bold` at
|
|
4074
|
+
**Design notes:** Value uses `Sohne-Bold` at 21px. Label uses `Sohne-Regular` at 13px in `foregroundSubtle`. Description uses `Sohne-Regular` at 12px in `foregroundMuted`. Icon renders at 20px, left-aligned with the value. All content is centered vertically and horizontally. The card shrinks to fit its content by default (`alignSelf: 'flex-start'`). When placed inside `Stats.Group`, cards stretch to equal width. When the card is narrow (< 150dp) and has an icon, the layout switches to vertical stacking (icon → value → label → description) to avoid overflow.
|
|
3989
4075
|
|
|
3990
4076
|
**`Stats.Group`** — horizontal layout wrapper that distributes children equally:
|
|
3991
4077
|
|
|
@@ -4237,6 +4323,7 @@ import { HolographicCard, FOIL_PRESETS } from '@retray-dev/ui-kit/HolographicCar
|
|
|
4237
4323
|
| borderRadius | `number` | `RADIUS.lg` | — |
|
|
4238
4324
|
| resizeMode | `'cover' \| 'contain' \| 'stretch'` | `'cover'` | Image resize mode |
|
|
4239
4325
|
| disabled | `boolean` | `false` | Prevents pressing |
|
|
4326
|
+
| allowsEditing | `boolean` | `true` | When `true`, iOS opens the crop/editing screen after selecting an image. Set `false` to accept the image directly without cropping |
|
|
4240
4327
|
| style | `ViewStyle` | — | — |
|
|
4241
4328
|
| accessibilityLabel | `string` | — | — |
|
|
4242
4329
|
|
|
@@ -4331,10 +4418,12 @@ const [icon, setIcon] = useState<string | null>(null)
|
|
|
4331
4418
|
|
|
4332
4419
|
**Import:** `import { useConfirmDialog } from '@retray-dev/ui-kit'`
|
|
4333
4420
|
|
|
4334
|
-
**When to use:** Manage `ConfirmDialog` state without boilerplate. Replaces the `
|
|
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.
|
|
4335
4424
|
|
|
4336
4425
|
```ts
|
|
4337
|
-
function useConfirmDialog
|
|
4426
|
+
function useConfirmDialog(options: UseConfirmDialogOptions): UseConfirmDialogResult
|
|
4338
4427
|
|
|
4339
4428
|
interface UseConfirmDialogOptions {
|
|
4340
4429
|
onConfirm: () => void | Promise<void>
|
|
@@ -4347,34 +4436,30 @@ interface UseConfirmDialogOptions {
|
|
|
4347
4436
|
| Field | Type | Notes |
|
|
4348
4437
|
|-------|------|-------|
|
|
4349
4438
|
| visible | `boolean` | Pass to `ConfirmDialog.visible` |
|
|
4350
|
-
| target | `T \| null` | The value passed to `open()` — e.g. the item being deleted |
|
|
4351
4439
|
| loading | `boolean` | Pass to `ConfirmDialog.loading` |
|
|
4352
|
-
| open | `(
|
|
4353
|
-
|
|
|
4440
|
+
| open | `() => void` | Call to show the dialog |
|
|
4441
|
+
| onConfirm | `() => void` | Pass directly to `ConfirmDialog.onConfirm` |
|
|
4442
|
+
| onCancel | `() => void` | Pass directly to `ConfirmDialog.onCancel` |
|
|
4354
4443
|
|
|
4355
4444
|
**Example:**
|
|
4356
4445
|
```tsx
|
|
4357
|
-
const { open,
|
|
4446
|
+
const { visible, loading, open, onConfirm, onCancel } = useConfirmDialog({
|
|
4358
4447
|
onConfirm: async () => {
|
|
4359
|
-
await deleteProduct(
|
|
4448
|
+
await deleteProduct(productId)
|
|
4360
4449
|
toast.success('Product deleted')
|
|
4361
4450
|
},
|
|
4362
4451
|
})
|
|
4363
4452
|
|
|
4364
|
-
// Somewhere in your
|
|
4365
|
-
<ListItem
|
|
4366
|
-
title={product.name}
|
|
4367
|
-
rightIcon="trash-2"
|
|
4368
|
-
onPress={() => open(product)}
|
|
4369
|
-
/>
|
|
4370
|
-
|
|
4371
|
-
// Once in your screen's JSX:
|
|
4453
|
+
// Somewhere in your screen:
|
|
4372
4454
|
<ConfirmDialog
|
|
4373
|
-
|
|
4374
|
-
|
|
4455
|
+
visible={visible}
|
|
4456
|
+
title="Delete product?"
|
|
4457
|
+
subtitle="This action cannot be undone."
|
|
4375
4458
|
confirmLabel="Delete"
|
|
4376
4459
|
confirmVariant="destructive"
|
|
4377
|
-
{
|
|
4460
|
+
loading={loading}
|
|
4461
|
+
onConfirm={onConfirm}
|
|
4462
|
+
onCancel={onCancel}
|
|
4378
4463
|
/>
|
|
4379
4464
|
```
|
|
4380
4465
|
|
|
@@ -4388,12 +4473,12 @@ The library ships a built-in icon resolver — pass any icon name string to comp
|
|
|
4388
4473
|
|
|
4389
4474
|
| Priority | Family | Best for |
|
|
4390
4475
|
|---|---|---|
|
|
4391
|
-
| 1
|
|
4476
|
+
| 1 | `Feather` | Clean line icons, UI essentials (first match wins) |
|
|
4392
4477
|
| 2 | `AntDesign` | Semantic UI icons |
|
|
4393
4478
|
| 3 | `Entypo` | Social, media, navigation icons |
|
|
4394
4479
|
| 4 | `FontAwesome5` | Wide coverage |
|
|
4395
4480
|
| 5 | `MaterialIcons` | Material-style icons |
|
|
4396
|
-
| 6
|
|
4481
|
+
| 6 | `Ionicons` | Fallback |
|
|
4397
4482
|
|
|
4398
4483
|
Browse all available icons: **https://icons.expo.fyi**
|
|
4399
4484
|
|
|
@@ -4420,29 +4505,7 @@ import { Icon } from '@retray-dev/ui-kit'
|
|
|
4420
4505
|
|
|
4421
4506
|
Returns `null` (no crash) if name not found in any family.
|
|
4422
4507
|
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
```tsx
|
|
4426
|
-
import { renderIcon } from '@retray-dev/ui-kit'
|
|
4427
|
-
|
|
4428
|
-
// Returns ReactNode or null
|
|
4429
|
-
const icon = renderIcon('check', 18, colors.primary)
|
|
4430
|
-
```
|
|
4431
|
-
|
|
4432
|
-
### `getValidIconNames` utility
|
|
4433
|
-
|
|
4434
|
-
```tsx
|
|
4435
|
-
import { getValidIconNames } from '@retray-dev/ui-kit'
|
|
4436
|
-
|
|
4437
|
-
// All icon names across all configured families
|
|
4438
|
-
const allIcons: string[] = getValidIconNames()
|
|
4439
|
-
// => ["home", "user", "heart", "star", ...]
|
|
4440
|
-
|
|
4441
|
-
// Scoped to specific families (does not mutate global config)
|
|
4442
|
-
const featherOnly: string[] = getValidIconNames(['Feather'])
|
|
4443
|
-
```
|
|
4444
|
-
|
|
4445
|
-
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.
|
|
4446
4509
|
|
|
4447
4510
|
### `iconName` props on components
|
|
4448
4511
|
|
|
@@ -4506,33 +4569,6 @@ getResponsiveFontSize(text, 48, [
|
|
|
4506
4569
|
|
|
4507
4570
|
---
|
|
4508
4571
|
|
|
4509
|
-
## Hover Support (Web)
|
|
4510
|
-
|
|
4511
|
-
```tsx
|
|
4512
|
-
import { useHover } from '@retray-dev/ui-kit'
|
|
4513
|
-
|
|
4514
|
-
function MyHoverableComponent() {
|
|
4515
|
-
const { hovered, hoverHandlers } = useHover()
|
|
4516
|
-
|
|
4517
|
-
return (
|
|
4518
|
-
<View
|
|
4519
|
-
{...hoverHandlers}
|
|
4520
|
-
style={[
|
|
4521
|
-
styles.container,
|
|
4522
|
-
hovered && { backgroundColor: colors.surfaceStrong }
|
|
4523
|
-
]}
|
|
4524
|
-
/>
|
|
4525
|
-
)
|
|
4526
|
-
}
|
|
4527
|
-
```
|
|
4528
|
-
|
|
4529
|
-
- **Web:** Returns `{ hovered: boolean, hoverHandlers: { onMouseEnter, onMouseLeave } }`
|
|
4530
|
-
- **Native:** Always returns `{ hovered: false, hoverHandlers: {} }` — no-op, no crashes
|
|
4531
|
-
|
|
4532
|
-
Built-in web hover: `MediaCard` (shadow lift), interactive components use this internally.
|
|
4533
|
-
|
|
4534
|
-
---
|
|
4535
|
-
|
|
4536
4572
|
## Design System Conventions
|
|
4537
4573
|
|
|
4538
4574
|
### Touch targets
|
|
@@ -4562,7 +4598,7 @@ All interactive elements maintain ≥44pt touch height per Apple HIG:
|
|
|
4562
4598
|
- `useNativeDriver` — `Platform.OS !== 'web'` everywhere (native driver disabled on web)
|
|
4563
4599
|
- `Select` — wheel modal on iOS, dialog on Android, `<select>` on web
|
|
4564
4600
|
- `Toast` — full width on mobile, 400px centered on web
|
|
4565
|
-
- Hover states — web only
|
|
4601
|
+
- Hover states — web only
|
|
4566
4602
|
- `Skeleton` shimmer highlight — adapts opacity for light/dark mode
|
|
4567
4603
|
|
|
4568
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 `
|
|
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` — `
|
|
605
|
+
All haptics via `src/utils/haptics.ts` — `expo-haptics`.
|
|
606
606
|
|
|
607
607
|
| Function | Use |
|
|
608
608
|
|---|---|
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
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
5
|
- 54 components across 9 categories (plus the deep-import `HolographicCard`)
|
|
6
|
-
- Light/dark theme with 12 public tokens (
|
|
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
|
|
9
9
|
- Built with TypeScript — full type declarations included
|
|
@@ -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
|
-
**
|
|
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
|
|
|
@@ -156,7 +158,7 @@ const { colors, colorScheme } = useTheme()
|
|
|
156
158
|
|
|
157
159
|
**Public tokens (12 — override these):** `background`, `foreground`, `card`, `primary`, `primaryForeground`, `border`, `destructive`, `destructiveForeground`, `success`, `successForeground`, `warning`, `warningForeground`. Optional: `overlay`, `accent`, `accentForeground`.
|
|
158
160
|
|
|
159
|
-
**Derived tokens (read-only via `useTheme()`):** `foregroundSubtle`, `foregroundMuted`, `surface`, `surfaceStrong`, `destructiveTint`, `destructiveBorder`, `successTint`, `successBorder`, `warningTint`, `warningBorder`, `ring`, `input`, `separator`, `overlay`, `accentResolved`, `accentForegroundResolved`
|
|
161
|
+
**Derived tokens (read-only via `useTheme()`):** `foregroundSubtle`, `foregroundMuted`, `surface`, `surfaceStrong`, `skeleton`, `destructiveTint`, `destructiveBorder`, `successTint`, `successBorder`, `warningTint`, `warningBorder`, `ring`, `input`, `separator`, `overlay`, `accentResolved`, `accentForegroundResolved`
|
|
160
162
|
|
|
161
163
|
## Design Tokens
|
|
162
164
|
|
|
@@ -179,7 +181,8 @@ import { SPACING, ICON_SIZES, RADIUS, SHADOWS, BREAKPOINTS, TYPOGRAPHY } from '@
|
|
|
179
181
|
|
|
180
182
|
### Color Utilities
|
|
181
183
|
|
|
182
|
-
- **`withAlpha(
|
|
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
|
|
|
@@ -187,12 +190,12 @@ import { SPACING, ICON_SIZES, RADIUS, SHADOWS, BREAKPOINTS, TYPOGRAPHY } from '@
|
|
|
187
190
|
| ----------- | ----------------------------------------------------------------------------------------------- |
|
|
188
191
|
| Display | `Text`, `Badge`, `Avatar`, `Separator`, `Spinner`, `Skeleton`, `Progress`, `CurrencyDisplay`, `Stats` |
|
|
189
192
|
| Surfaces | `Card`, `AlertBanner`, `EmptyState`, `MediaCard`, `PricingCard` |
|
|
190
|
-
| Form | `Form` (+ `Form.Field` / `Form.Section` / `Form.Footer`), `Button`, `ButtonGroup`, `IconButton`, `Input`, `CurrencyInput`, `Textarea`, `Checkbox`, `Switch`, `Toggle`, `RadioGroup`, `Select`, `Slider`, `SelectableGrid`, `SheetSelect`, `ImageUpload`, `IconPicker`, `NumberStepper` |
|
|
193
|
+
| Form | `Form` (+ `Form.Field` / `Form.Section` / `Form.Footer`), `Button`, `ButtonGroup`, `IconButton`, `Input`, `CurrencyInput`, `Textarea`, `Checkbox`, `Switch`, `Toggle`, `RadioGroup`, `Select`, `Slider`, `SelectableGrid`, `SelectableCard` (+ `SelectableCardGroup`), `SheetSelect`, `ImageUpload`, `IconPicker`, `NumberStepper` |
|
|
191
194
|
| Composition | `Tabs`, `Accordion` |
|
|
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`), `
|
|
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:
|
package/dist/Accordion.d.mts
CHANGED
|
@@ -11,6 +11,12 @@ interface AccordionItem {
|
|
|
11
11
|
icon?: React.ReactNode;
|
|
12
12
|
/** Override icon color. Defaults to foregroundMuted. */
|
|
13
13
|
iconColor?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Action buttons rendered after the trigger content but before the chevron.
|
|
16
|
+
* Automatically touch-isolated — taps on actions won't toggle the accordion.
|
|
17
|
+
* Use this instead of embedding interactive elements inside `trigger`.
|
|
18
|
+
*/
|
|
19
|
+
triggerActions?: React.ReactNode;
|
|
14
20
|
}
|
|
15
21
|
interface AccordionProps {
|
|
16
22
|
items: AccordionItem[];
|
package/dist/Accordion.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ interface AccordionItem {
|
|
|
11
11
|
icon?: React.ReactNode;
|
|
12
12
|
/** Override icon color. Defaults to foregroundMuted. */
|
|
13
13
|
iconColor?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Action buttons rendered after the trigger content but before the chevron.
|
|
16
|
+
* Automatically touch-isolated — taps on actions won't toggle the accordion.
|
|
17
|
+
* Use this instead of embedding interactive elements inside `trigger`.
|
|
18
|
+
*/
|
|
19
|
+
triggerActions?: React.ReactNode;
|
|
14
20
|
}
|
|
15
21
|
interface AccordionProps {
|
|
16
22
|
items: AccordionItem[];
|