@retray-dev/ui-kit 10.2.0 → 12.2.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 +384 -40
- package/README.md +14 -5
- package/dist/Accordion.d.mts +6 -0
- package/dist/Accordion.d.ts +6 -0
- package/dist/Accordion.js +16 -0
- package/dist/Accordion.mjs +2 -2
- package/dist/AlertBanner.js +2 -0
- package/dist/AlertBanner.mjs +2 -2
- package/dist/AppHeader.js +2 -0
- package/dist/AppHeader.mjs +3 -3
- package/dist/Avatar.js +2 -0
- package/dist/Avatar.mjs +2 -2
- package/dist/Badge.js +2 -0
- package/dist/Badge.mjs +2 -2
- package/dist/Button.js +17 -17
- package/dist/Button.mjs +2 -2
- package/dist/Card.js +2 -0
- package/dist/Card.mjs +2 -2
- package/dist/CategoryStrip.js +2 -0
- package/dist/CategoryStrip.mjs +2 -2
- package/dist/Checkbox.js +2 -0
- package/dist/Checkbox.mjs +2 -2
- package/dist/Chip.js +2 -0
- package/dist/Chip.mjs +2 -2
- package/dist/ConfirmDialog.d.mts +1 -6
- package/dist/ConfirmDialog.d.ts +1 -6
- package/dist/ConfirmDialog.js +53 -41
- package/dist/ConfirmDialog.mjs +3 -3
- package/dist/CurrencyDisplay.js +2 -0
- package/dist/CurrencyDisplay.mjs +2 -2
- package/dist/CurrencyInput.d.mts +3 -8
- package/dist/CurrencyInput.d.ts +3 -8
- package/dist/CurrencyInput.js +5 -1
- package/dist/CurrencyInput.mjs +3 -3
- package/dist/DetailRow.js +2 -0
- package/dist/DetailRow.mjs +2 -2
- package/dist/EmptyState.js +17 -17
- package/dist/EmptyState.mjs +3 -3
- package/dist/ErrorBoundary.js +2 -0
- package/dist/ErrorBoundary.mjs +2 -2
- package/dist/Form.js +2 -0
- package/dist/Form.mjs +2 -2
- package/dist/IconButton.js +2 -0
- package/dist/IconButton.mjs +2 -2
- package/dist/IconPicker.js +677 -248
- package/dist/IconPicker.mjs +3 -2
- package/dist/ImageUpload.d.mts +3 -1
- package/dist/ImageUpload.d.ts +3 -1
- package/dist/ImageUpload.js +10 -3
- package/dist/ImageUpload.mjs +3 -3
- package/dist/ImageViewer.js +2 -0
- package/dist/ImageViewer.mjs +4 -4
- package/dist/Input.js +2 -0
- package/dist/Input.mjs +2 -2
- package/dist/LabelValue.js +2 -0
- package/dist/LabelValue.mjs +2 -2
- package/dist/ListGroup.js +2 -0
- package/dist/ListGroup.mjs +2 -2
- package/dist/ListItem.d.mts +7 -7
- package/dist/ListItem.d.ts +7 -7
- package/dist/ListItem.js +14 -7
- package/dist/ListItem.mjs +2 -2
- package/dist/MediaCard.js +2 -0
- package/dist/MediaCard.mjs +2 -2
- package/dist/MenuGroup.js +2 -0
- package/dist/MenuGroup.mjs +2 -2
- package/dist/MenuItem.js +2 -0
- package/dist/MenuItem.mjs +2 -2
- package/dist/MonthPicker.js +2 -0
- package/dist/MonthPicker.mjs +2 -2
- package/dist/NumberStepper.js +2 -0
- package/dist/NumberStepper.mjs +2 -2
- package/dist/PagerDots.js +2 -0
- package/dist/PagerDots.mjs +2 -2
- package/dist/Pressable.d.mts +15 -7
- package/dist/Pressable.d.ts +15 -7
- package/dist/Pressable.js +7 -3
- package/dist/Pressable.mjs +1 -1
- package/dist/PricingCard.js +17 -17
- package/dist/PricingCard.mjs +4 -4
- package/dist/Progress.js +2 -0
- package/dist/Progress.mjs +2 -2
- package/dist/RadioGroup.js +2 -0
- package/dist/RadioGroup.mjs +2 -2
- package/dist/RetrayProvider.d.mts +1 -1
- package/dist/RetrayProvider.d.ts +1 -1
- package/dist/RetrayProvider.js +2 -0
- package/dist/RetrayProvider.mjs +3 -3
- package/dist/Select.js +2 -0
- package/dist/Select.mjs +2 -2
- package/dist/SelectableCard.d.mts +27 -0
- package/dist/SelectableCard.d.ts +27 -0
- package/dist/SelectableCard.js +511 -0
- package/dist/SelectableCard.mjs +8 -0
- package/dist/SelectableGrid.js +2 -0
- package/dist/SelectableGrid.mjs +2 -2
- package/dist/Separator.js +2 -0
- package/dist/Separator.mjs +2 -2
- package/dist/Sheet.d.mts +4 -46
- package/dist/Sheet.d.ts +4 -46
- package/dist/Sheet.js +55 -115
- package/dist/Sheet.mjs +2 -3
- package/dist/SheetSelect.js +2 -0
- package/dist/SheetSelect.mjs +2 -2
- package/dist/Skeleton.d.mts +3 -1
- package/dist/Skeleton.d.ts +3 -1
- package/dist/Skeleton.js +5 -2
- package/dist/Skeleton.mjs +2 -2
- package/dist/Slider.js +2 -0
- package/dist/Slider.mjs +2 -2
- package/dist/Spinner.js +2 -0
- package/dist/Spinner.mjs +2 -2
- package/dist/Stats.d.mts +33 -0
- package/dist/Stats.d.ts +33 -0
- package/dist/Stats.js +453 -0
- package/dist/Stats.mjs +9 -0
- package/dist/Switch.js +2 -0
- package/dist/Switch.mjs +2 -2
- package/dist/TabBar.js +2 -0
- package/dist/TabBar.mjs +2 -2
- package/dist/Tabs.js +2 -0
- package/dist/Tabs.mjs +2 -2
- package/dist/Text.d.mts +3 -1
- package/dist/Text.d.ts +3 -1
- package/dist/Text.js +5 -3
- package/dist/Text.mjs +2 -2
- package/dist/Textarea.js +2 -0
- package/dist/Textarea.mjs +2 -2
- package/dist/Toast.js +2 -0
- package/dist/Toast.mjs +2 -2
- package/dist/Toggle.js +2 -0
- package/dist/Toggle.mjs +2 -2
- package/dist/{chunk-U2XJFYED.mjs → chunk-2BA3JMKK.mjs} +1 -1
- package/dist/{chunk-NMU5FMQJ.mjs → chunk-2HFD4IHU.mjs} +4 -2
- package/dist/{chunk-S2R7UVOE.mjs → chunk-2LG326TT.mjs} +1 -1
- package/dist/chunk-2P2CB235.mjs +236 -0
- package/dist/{chunk-6L4G6PBT.mjs → chunk-3XCFYSX4.mjs} +1 -1
- package/dist/{chunk-HTHGSXFG.mjs → chunk-4J2PXL36.mjs} +16 -18
- package/dist/{chunk-BEMIQXXU.mjs → chunk-4OORJ2DY.mjs} +1 -1
- package/dist/chunk-4XOB5TTD.mjs +166 -0
- package/dist/{chunk-FCSSQK3L.mjs → chunk-57V2LXCK.mjs} +1 -1
- package/dist/{chunk-6Q64UFIA.mjs → chunk-7AFZWSCI.mjs} +1 -1
- package/dist/{chunk-IX3NYLYQ.mjs → chunk-7ELGZ66G.mjs} +1 -1
- package/dist/{chunk-GD6KXMG5.mjs → chunk-AENAVIKT.mjs} +1 -1
- package/dist/{chunk-ID72TK46.mjs → chunk-BXF4AMHY.mjs} +1 -1
- package/dist/{chunk-SOA2Z4RB.mjs → chunk-C43HRKXH.mjs} +1 -1
- package/dist/{chunk-TZDGAP5N.mjs → chunk-CF27NBXO.mjs} +11 -6
- package/dist/{chunk-SXLKNTA4.mjs → chunk-DF7JA72E.mjs} +1 -1
- package/dist/{chunk-AJRVDP2H.mjs → chunk-E5UKLSJZ.mjs} +3 -3
- package/dist/{chunk-MBMXYJJV.mjs → chunk-E7NEHHXV.mjs} +7 -3
- package/dist/{chunk-VKID2D2I.mjs → chunk-EDLCGYIO.mjs} +13 -8
- package/dist/{chunk-BUMAMSTZ.mjs → chunk-ELGEOM7I.mjs} +1 -1
- package/dist/{chunk-DYT7BG5I.mjs → chunk-F3YTWO3T.mjs} +1 -1
- package/dist/{chunk-VF2ATYN3.mjs → chunk-GH67YXG6.mjs} +1 -1
- package/dist/{chunk-WJLKJMKR.mjs → chunk-GUTDFUNF.mjs} +4 -4
- package/dist/{chunk-6SECQ2ZF.mjs → chunk-HC4VVCWY.mjs} +2 -2
- package/dist/{chunk-A3A6KNQN.mjs → chunk-HEDQPK4I.mjs} +1 -1
- package/dist/{chunk-GQYFLP3D.mjs → chunk-IVSRW4HS.mjs} +1 -1
- package/dist/{chunk-KOO4WITD.mjs → chunk-KSUWPU2F.mjs} +1 -1
- package/dist/{chunk-WBOOUHSS.mjs → chunk-LIS6I5UP.mjs} +1 -1
- package/dist/{chunk-X4G6APW6.mjs → chunk-LNPKGWBG.mjs} +1 -1
- package/dist/{chunk-T2KCAHOS.mjs → chunk-LOBLCFMN.mjs} +1 -1
- package/dist/{chunk-ELXBDILQ.mjs → chunk-LPV4NJJK.mjs} +2 -2
- package/dist/{chunk-Y2NS74WS.mjs → chunk-M3C7XM2M.mjs} +53 -99
- package/dist/{chunk-BRKYVJVV.mjs → chunk-MEPSKGBO.mjs} +1 -1
- package/dist/{chunk-TBNZHU6C.mjs → chunk-MVMGPZN6.mjs} +2 -2
- package/dist/{chunk-YJ7I257J.mjs → chunk-NHDI3VQB.mjs} +15 -1
- package/dist/{chunk-Z6SFHN6T.mjs → chunk-NJG7DHVF.mjs} +1 -1
- package/dist/{chunk-RYZC432S.mjs → chunk-NLZY4TXU.mjs} +1 -1
- package/dist/{chunk-ZZ2R6KZ3.mjs → chunk-OLVJFKXS.mjs} +1 -1
- package/dist/{chunk-AJ7ZDNBT.mjs → chunk-QDAZGZUF.mjs} +4 -3
- package/dist/{chunk-JT7HKXRB.mjs → chunk-QOLWA2PW.mjs} +1 -1
- package/dist/{chunk-WYEUNUTP.mjs → chunk-QXDGGOLC.mjs} +38 -25
- package/dist/{chunk-JMOZEC77.mjs → chunk-RJNLAH76.mjs} +1 -1
- package/dist/{chunk-WF2XDFRK.mjs → chunk-RMRS44MQ.mjs} +1 -1
- package/dist/chunk-SAWUXP3A.mjs +1114 -0
- package/dist/{chunk-OB4JUQ3O.mjs → chunk-TS7DGUIR.mjs} +1 -1
- package/dist/{chunk-AV4EMIRH.mjs → chunk-UBUXUMER.mjs} +1 -1
- package/dist/{chunk-IRRY3CRZ.mjs → chunk-ULGNQPNE.mjs} +1 -1
- package/dist/{chunk-7LWRKMF5.mjs → chunk-UNNRUJTM.mjs} +1 -1
- package/dist/{chunk-TB6SD2FT.mjs → chunk-UQ4742ET.mjs} +1 -1
- package/dist/{chunk-MX6HRKMI.mjs → chunk-VJBUCITV.mjs} +1 -1
- package/dist/{chunk-2UYENBLV.mjs → chunk-YMYIEVZP.mjs} +1 -1
- package/dist/{chunk-SOYNZDVY.mjs → chunk-YTXRIXNZ.mjs} +8 -1
- package/dist/{chunk-YFZ3ELX5.mjs → chunk-ZIMY2QUM.mjs} +2 -2
- package/dist/{chunk-Z4VHZ7B5.mjs → chunk-ZR6HSEAB.mjs} +1 -1
- package/dist/fonts.d.mts +1 -7
- package/dist/fonts.d.ts +1 -7
- package/dist/fonts.js +0 -2
- package/dist/fonts.mjs +1 -2
- 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 +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +1517 -761
- package/dist/index.mjs +54 -52
- package/package.json +3 -3
- package/src/components/Accordion/Accordion.tsx +20 -0
- package/src/components/Button/Button.tsx +29 -26
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +47 -31
- package/src/components/CurrencyInput/CurrencyInput.tsx +4 -7
- package/src/components/IconPicker/IconPicker.tsx +124 -112
- package/src/components/ImageUpload/ImageUpload.tsx +10 -3
- package/src/components/ListItem/ListItem.tsx +43 -28
- package/src/components/Pressable/Pressable.tsx +20 -8
- package/src/components/SelectableCard/SelectableCard.tsx +304 -0
- package/src/components/SelectableCard/index.ts +1 -0
- package/src/components/Sheet/Sheet.tsx +72 -173
- package/src/components/Skeleton/Skeleton.tsx +5 -2
- package/src/components/Stats/Stats.tsx +254 -0
- package/src/components/Stats/index.ts +2 -0
- package/src/components/Text/Text.tsx +4 -2
- package/src/fonts.ts +0 -7
- package/src/index.ts +5 -0
- package/src/theme/colorUtils.ts +9 -0
- package/src/theme/colors.ts +7 -0
- package/src/theme/types.ts +4 -1
- package/src/utils/curatedIcons.ts +698 -135
- package/src/utils/fontGuard.ts +2 -1
- package/dist/chunk-53Z3NYGE.mjs +0 -742
package/COMPONENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @retray-dev/ui-kit — Component Reference (
|
|
1
|
+
# @retray-dev/ui-kit — Component Reference (v12.1.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
|
|
|
@@ -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,25 @@ 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
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
### Color Utilities
|
|
323
|
+
|
|
324
|
+
**Import:** `import { withAlpha } from '@retray-dev/ui-kit'`
|
|
325
|
+
|
|
326
|
+
Convert a hex color to rgba with the given alpha. Useful for semi-transparent backgrounds, borders, and overlays derived from theme colors.
|
|
327
|
+
|
|
328
|
+
```tsx
|
|
329
|
+
import { useTheme, withAlpha } from '@retray-dev/ui-kit'
|
|
330
|
+
|
|
331
|
+
const { colors } = useTheme()
|
|
332
|
+
|
|
333
|
+
<View style={{ backgroundColor: withAlpha(colors.primary, 0.15) }}>
|
|
334
|
+
<Text style={{ color: colors.primary }}>Tinted background</Text>
|
|
335
|
+
</View>
|
|
316
336
|
```
|
|
317
337
|
|
|
318
338
|
---
|
|
@@ -684,6 +704,132 @@ assets/fonts/sohne/
|
|
|
684
704
|
|
|
685
705
|
---
|
|
686
706
|
|
|
707
|
+
## Migration Guide: v10 → v11
|
|
708
|
+
|
|
709
|
+
### New — public utility
|
|
710
|
+
|
|
711
|
+
**`withAlpha(hex, alpha)`** — hex-to-rgba color helper, now exported from the package root. Useful for semi-transparent overlays derived from theme colors without adding a new token.
|
|
712
|
+
|
|
713
|
+
```tsx
|
|
714
|
+
import { useTheme, withAlpha } from '@retray-dev/ui-kit'
|
|
715
|
+
|
|
716
|
+
const { colors } = useTheme()
|
|
717
|
+
<View style={{ backgroundColor: withAlpha(colors.primary, 0.15) }} />
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
### Updated
|
|
721
|
+
|
|
722
|
+
- `Stats` component promoted from internal/experimental to public (`Stats` + `Stats.Group`).
|
|
723
|
+
- Documentation refreshed for `IconPicker` and `NumberStepper`.
|
|
724
|
+
|
|
725
|
+
No breaking changes in v11. Safe minor upgrade from v10.
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
## Migration Guide: v11 → v12
|
|
730
|
+
|
|
731
|
+
### Breaking Changes
|
|
732
|
+
|
|
733
|
+
`Sheet` and `ConfirmDialog` were rewritten on top of `@gorhom/bottom-sheet`'s **`BottomSheetModal`** (lazy-mounted, `present()` / `dismiss()` driven) — replacing the old `BottomSheet` + `index={-1}` + `snapToIndex(0)` pattern. This fixes timing issues with `enableDynamicSizing` and unifies the API with the rest of the gorhom ecosystem.
|
|
734
|
+
|
|
735
|
+
**1. Removed `responsive` / `dialogMaxWidth` props from `Sheet` and `ConfirmDialog`**
|
|
736
|
+
|
|
737
|
+
The previous wide-screen fallback that bypassed gorhom with a native `Modal` + `ScrollView` was a partial workaround (see REGLA 1). It has been deleted. `@gorhom/bottom-sheet` handles responsive behavior natively — the modal simply renders inside its modal layer at the device width.
|
|
738
|
+
|
|
739
|
+
```diff
|
|
740
|
+
<Sheet
|
|
741
|
+
open={open}
|
|
742
|
+
onClose={() => setOpen(false)}
|
|
743
|
+
title="Options"
|
|
744
|
+
- responsive
|
|
745
|
+
- dialogMaxWidth={600}
|
|
746
|
+
/>
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
**2. `onClose` is the only close handler**
|
|
750
|
+
|
|
751
|
+
`onClose` is called from `BottomSheetModal.onDismiss` — the native gorhom callback. The previous `onClose` prop (passed directly to `BottomSheet`) was redundant. No public-API change for consumers; this is an internal alignment.
|
|
752
|
+
|
|
753
|
+
**3. `Sheet` requires `BottomSheetModalProvider` at app root**
|
|
754
|
+
|
|
755
|
+
`RetrayProvider` already wires it. If you assemble providers manually, ensure `BottomSheetModalProvider` sits inside `GestureHandlerRootView`:
|
|
756
|
+
|
|
757
|
+
```tsx
|
|
758
|
+
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
|
759
|
+
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
760
|
+
<ThemeProvider>
|
|
761
|
+
<BottomSheetModalProvider>
|
|
762
|
+
<ToastProvider>{/* app */}</ToastProvider>
|
|
763
|
+
</BottomSheetModalProvider>
|
|
764
|
+
</ThemeProvider>
|
|
765
|
+
</GestureHandlerRootView>
|
|
766
|
+
</SafeAreaProvider>
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**4. Keyboard prop renames (no consumer action — defaults match v11)**
|
|
770
|
+
|
|
771
|
+
| Prop | v11 | v12 default |
|
|
772
|
+
|------|-----|-------------|
|
|
773
|
+
| `keyboardBehavior` | `'interactive'` | `'interactive'` (unchanged) |
|
|
774
|
+
| `android_keyboardInputMode` | `'adjustPan'` | `'adjustPan'` (unchanged) |
|
|
775
|
+
|
|
776
|
+
**5. Removed top-level imports of `Modal`, `ScrollView`, `useWindowDimensions`, `BREAKPOINTS` from `Sheet.tsx`**
|
|
777
|
+
|
|
778
|
+
Internal only — no public API.
|
|
779
|
+
|
|
780
|
+
### Behavioral changes (no code change required)
|
|
781
|
+
|
|
782
|
+
- **Backdrop / swipe close is now driven by gorhom's modal lifecycle** — `onClose` fires once on full dismiss instead of on every interaction. State-setter closures (e.g. `setOpen(false)`) are safe to call from `onClose` without causing "dismiss on unmounted" loops.
|
|
783
|
+
- **`topInset={insets.top}`** is now applied automatically from safe-area context — sheet never crosses the notch / status bar on iOS or Android.
|
|
784
|
+
- **`snapPoints` + `enableDynamicSizing` are mutually exclusive.** If you pass `snapPoints`, dynamic sizing is disabled and the sheet snaps to those points. Omit `snapPoints` to use dynamic sizing (default).
|
|
785
|
+
|
|
786
|
+
### New — recommended pattern
|
|
787
|
+
|
|
788
|
+
**Use `Input` with `sheetMode` inside a Sheet** instead of `SheetTextInput` directly. The new `sheetMode` prop on `Input` swaps in `BottomSheetTextInput` for keyboard-aware focus/blur handling while preserving the full `Input` API (label, error, hint, prefix/suffix, icons, type="password").
|
|
789
|
+
|
|
790
|
+
```tsx
|
|
791
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Add note">
|
|
792
|
+
<Input
|
|
793
|
+
label="Note"
|
|
794
|
+
placeholder="Type your note..."
|
|
795
|
+
value={note}
|
|
796
|
+
onChangeText={setNote}
|
|
797
|
+
sheetMode
|
|
798
|
+
/>
|
|
799
|
+
<Button label="Save" fullWidth onPress={handleSave} />
|
|
800
|
+
</Sheet>
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
`SheetTextInput` is still re-exported for low-level use.
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
807
|
+
## Migration Guide: v12.0 → v12.1
|
|
808
|
+
|
|
809
|
+
### New — IconPicker feedback pattern (REGLA 4)
|
|
810
|
+
|
|
811
|
+
`IconPicker` now follows the "no frozen screen" rule. When the user taps the trigger, the sheet presents **immediately** (no `useEffect` delay). While the inner grid measures its container, a centered `<Spinner />` is shown inside the sheet so the user sees visible feedback. The grid fades in as soon as `onLayout` fires.
|
|
812
|
+
|
|
813
|
+
This is now the canonical pattern for any sheet whose content needs to measure or load before rendering — apply it to new overlay components.
|
|
814
|
+
|
|
815
|
+
### New — race-condition fix in `Sheet` and `ConfirmDialog`
|
|
816
|
+
|
|
817
|
+
Both components now track a `wasOpened` ref so `dismiss()` is only called after the sheet has been presented at least once. This eliminates a class of crashes that occurred when the parent component unmounted (or the `open`/`visible` prop flipped) before the gorham modal had a chance to mount.
|
|
818
|
+
|
|
819
|
+
No consumer action required — the fix is internal.
|
|
820
|
+
|
|
821
|
+
### New — expanded curated icon library
|
|
822
|
+
|
|
823
|
+
`src/utils/curatedIcons.ts` has been expanded: every category now carries 42+ icons (some up to 66), totaling ~600 themed icons across the 12 categories. Selection rules are now codified in REGLA 5 — only Feather, Ionicons `-outline`, and themed FA5/Entypo/AntDesign icons are eligible (no filled variants).
|
|
824
|
+
|
|
825
|
+
### Updated
|
|
826
|
+
|
|
827
|
+
- `Sheet` and `ConfirmDialog` pass a stable `name` prop to `BottomSheetModal` (from `useId()`) for correct gorhom modal registry behavior when multiple modals are mounted.
|
|
828
|
+
- `Input` `sheetMode` prop documented in the Setup section (replaces the `SheetTextInput` boilerplate inside sheets).
|
|
829
|
+
- `CompositionScreen` example now uses `<Input sheetMode />` inside a `Sheet` to demonstrate the keyboard-friendly pattern.
|
|
830
|
+
|
|
831
|
+
---
|
|
832
|
+
|
|
687
833
|
## Components
|
|
688
834
|
|
|
689
835
|
---
|
|
@@ -700,6 +846,7 @@ assets/fonts/sohne/
|
|
|
700
846
|
|------|------|---------|-------|
|
|
701
847
|
| variant | `TextVariant` | `'body-md'` | Sets font, size, weight, line height, letter spacing |
|
|
702
848
|
| color | `string` | — | Override text color. Each variant has a semantic default (see table below) |
|
|
849
|
+
| uppercase | `boolean` | `false` | Force text-transform: uppercase on any variant |
|
|
703
850
|
| children | `ReactNode` | required | — |
|
|
704
851
|
| style | `TextStyle` | — | Additional styles (merged after variant styles) |
|
|
705
852
|
| (all TextProps) | — | — | `numberOfLines`, `ellipsizeMode`, `onPress`, etc. all pass through |
|
|
@@ -772,7 +919,7 @@ assets/fonts/sohne/
|
|
|
772
919
|
| size | `'sm' \| 'md' \| 'lg'` | `'md'` | Controls height, padding, and icon size |
|
|
773
920
|
| loading | `boolean` | `false` | Replaces label with spinner, forces disabled state |
|
|
774
921
|
| fullWidth | `boolean` | `false` | Stretches to container width (`alignSelf: 'stretch'`) |
|
|
775
|
-
| disabled | `boolean` | — |
|
|
922
|
+
| disabled | `boolean` | — | Per-variant explicit colors (no opacity). `filled`→`surface` bg, `secondary`→`border` border, `text`/`outline`→`foregroundMuted` text |
|
|
776
923
|
| icon | `React.ReactNode \| ((props: { label, size, variant }) => React.ReactNode)` | — | Icon rendered alongside label |
|
|
777
924
|
| iconName | `string` | — | Icon name from `@expo/vector-icons`. Takes precedence over `icon` |
|
|
778
925
|
| iconColor | `string` | — | Override icon color. Defaults to variant label color |
|
|
@@ -1073,6 +1220,23 @@ assets/fonts/sohne/
|
|
|
1073
1220
|
</View>
|
|
1074
1221
|
```
|
|
1075
1222
|
|
|
1223
|
+
**Composition — inside a Sheet (preferred pattern, v12+):**
|
|
1224
|
+
|
|
1225
|
+
Use `sheetMode` to opt the inner `TextInput` into `BottomSheetTextInput` so keyboard handling works correctly. This is the canonical replacement for raw `SheetTextInput` inside sheets.
|
|
1226
|
+
|
|
1227
|
+
```tsx
|
|
1228
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Add note">
|
|
1229
|
+
<Input
|
|
1230
|
+
label="Note"
|
|
1231
|
+
placeholder="Write your note..."
|
|
1232
|
+
value={note}
|
|
1233
|
+
onChangeText={setNote}
|
|
1234
|
+
sheetMode
|
|
1235
|
+
/>
|
|
1236
|
+
<Button label="Save" fullWidth onPress={handleSave} />
|
|
1237
|
+
</Sheet>
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1076
1240
|
---
|
|
1077
1241
|
|
|
1078
1242
|
### Textarea
|
|
@@ -1125,9 +1289,12 @@ assets/fonts/sohne/
|
|
|
1125
1289
|
| hint | `string` | — | Helper text below (hidden when `error` is set) |
|
|
1126
1290
|
| placeholder | `string` | `'$0'` | Defaults to `prefix + '0'` |
|
|
1127
1291
|
| editable | `boolean` | — | Pass `false` to disable |
|
|
1292
|
+
| autoFocus | `boolean` | — | Auto-focus on mount (inherited from TextInputProps) |
|
|
1128
1293
|
| containerStyle | `ViewStyle` | — | Outer container style |
|
|
1129
1294
|
| sheetMode | `boolean` | `false` | Use inside a Sheet — forwards `sheetMode` to underlying `Input` |
|
|
1130
1295
|
|
|
1296
|
+
**Extends `TextInputProps` from React Native** — all TextInput props (autoFocus, onFocus, onBlur, etc.) pass through to the underlying input.
|
|
1297
|
+
|
|
1131
1298
|
**Formatting behavior:** Strips all non-digit characters from input, then re-applies thousands separator every 3 digits from the right, then prepends `prefix`. Decimal point input is not supported — this is for whole-number monetary amounts.
|
|
1132
1299
|
|
|
1133
1300
|
**Examples:**
|
|
@@ -1737,6 +1904,7 @@ const renderItem = useCallback(({ item }) => (
|
|
|
1737
1904
|
| width | `number \| string` | `'100%'` | Width of placeholder |
|
|
1738
1905
|
| height | `number` | `16` | Height of placeholder |
|
|
1739
1906
|
| borderRadius | `number` | `6` | Corner radius |
|
|
1907
|
+
| backgroundColor | `string` | `colors.skeleton` | Override placeholder background color |
|
|
1740
1908
|
| preset | `'base' \| 'circle' \| 'text'` | `'base'` | Convenience preset |
|
|
1741
1909
|
| diameter | `number` | `40` | Used by `'circle'` preset — overrides width/height |
|
|
1742
1910
|
| style | `ViewStyle` | — | — |
|
|
@@ -2184,6 +2352,94 @@ const [accepted, setAccepted] = useState(false)
|
|
|
2184
2352
|
|
|
2185
2353
|
---
|
|
2186
2354
|
|
|
2355
|
+
### SelectableCard
|
|
2356
|
+
|
|
2357
|
+
**Import:** `import { SelectableCard, SelectableCardGroup } from '@retray-dev/ui-kit'`
|
|
2358
|
+
|
|
2359
|
+
**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.
|
|
2360
|
+
|
|
2361
|
+
| Prop | Type | Default | Notes |
|
|
2362
|
+
|------|------|---------|-------|
|
|
2363
|
+
| value | `string` | required | The value this card represents |
|
|
2364
|
+
| title | `string` | required | Card title |
|
|
2365
|
+
| description | `string` | — | Secondary text below title |
|
|
2366
|
+
| iconName | `string` | — | Left icon from @expo/vector-icons |
|
|
2367
|
+
| icon | `ReactNode` | — | Custom left icon |
|
|
2368
|
+
| disabled | `boolean` | `false` | Grayed out, no press/haptic |
|
|
2369
|
+
| style | `ViewStyle` | — | — |
|
|
2370
|
+
|
|
2371
|
+
**SelectableCardGroup Props:**
|
|
2372
|
+
|
|
2373
|
+
| Prop | Type | Default | Notes |
|
|
2374
|
+
|------|------|---------|-------|
|
|
2375
|
+
| type | `'radio' \| 'checkbox'` | `'radio'` | Selection mode |
|
|
2376
|
+
| value | `string \| string[]` | required | Selected value(s) |
|
|
2377
|
+
| onValueChange | `(value: string \| string[]) => void` | required | — |
|
|
2378
|
+
| variant | `'elevated' \| 'outlined' \| 'filled'` | `'elevated'` | Card surface |
|
|
2379
|
+
| gap | `number` | `s(8)` | Spacing between cards |
|
|
2380
|
+
| style | `ViewStyle` | — | — |
|
|
2381
|
+
|
|
2382
|
+
**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.
|
|
2383
|
+
|
|
2384
|
+
**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`.
|
|
2385
|
+
|
|
2386
|
+
**Haptics:** `impactLight` on selection.
|
|
2387
|
+
|
|
2388
|
+
**Disabled:** Dims content to `foregroundMuted`, removes shadow/elevation, keeps border at `colors.border`. No press, no haptic.
|
|
2389
|
+
|
|
2390
|
+
**Examples:**
|
|
2391
|
+
```tsx
|
|
2392
|
+
// Single select (radio)
|
|
2393
|
+
<SelectableCardGroup
|
|
2394
|
+
type="radio"
|
|
2395
|
+
value={selectedRole}
|
|
2396
|
+
onValueChange={setSelectedRole}
|
|
2397
|
+
>
|
|
2398
|
+
<SelectableCard
|
|
2399
|
+
value="admin"
|
|
2400
|
+
iconName="shield"
|
|
2401
|
+
title="Administrator"
|
|
2402
|
+
description="Full access to reports, settings, and business management"
|
|
2403
|
+
/>
|
|
2404
|
+
<SelectableCard
|
|
2405
|
+
value="waiter"
|
|
2406
|
+
iconName="user-check"
|
|
2407
|
+
title="Waiter"
|
|
2408
|
+
description="Take orders from tables and manage requests"
|
|
2409
|
+
/>
|
|
2410
|
+
<SelectableCard
|
|
2411
|
+
value="cook"
|
|
2412
|
+
iconName="chef-hat"
|
|
2413
|
+
title="Cook"
|
|
2414
|
+
description="Receive and prepare items from each order in the kitchen"
|
|
2415
|
+
disabled
|
|
2416
|
+
/>
|
|
2417
|
+
</SelectableCardGroup>
|
|
2418
|
+
|
|
2419
|
+
// Multi select (checkbox)
|
|
2420
|
+
<SelectableCardGroup
|
|
2421
|
+
type="checkbox"
|
|
2422
|
+
value={selectedRoles}
|
|
2423
|
+
onValueChange={setSelectedRoles}
|
|
2424
|
+
variant="outlined"
|
|
2425
|
+
>
|
|
2426
|
+
<SelectableCard
|
|
2427
|
+
value="reports"
|
|
2428
|
+
iconName="bar-chart-2"
|
|
2429
|
+
title="Reports"
|
|
2430
|
+
description="View sales, inventory, and performance analytics"
|
|
2431
|
+
/>
|
|
2432
|
+
<SelectableCard
|
|
2433
|
+
value="inventory"
|
|
2434
|
+
iconName="package"
|
|
2435
|
+
title="Inventory"
|
|
2436
|
+
description="Manage stock levels and supplier orders"
|
|
2437
|
+
/>
|
|
2438
|
+
</SelectableCardGroup>
|
|
2439
|
+
```
|
|
2440
|
+
|
|
2441
|
+
---
|
|
2442
|
+
|
|
2187
2443
|
### Slider
|
|
2188
2444
|
|
|
2189
2445
|
**Import:** `import { Slider } from '@retray-dev/ui-kit'`
|
|
@@ -2345,6 +2601,7 @@ const [tab, setTab] = useState('profile')
|
|
|
2345
2601
|
iconName?: string // Icon name from @expo/vector-icons
|
|
2346
2602
|
icon?: ReactNode // Custom icon node
|
|
2347
2603
|
iconColor?: string // Override icon color (defaults to foregroundMuted)
|
|
2604
|
+
triggerActions?: ReactNode // Action buttons rendered between trigger and chevron. Touch-isolated — taps on actions won't toggle the accordion.
|
|
2348
2605
|
}
|
|
2349
2606
|
```
|
|
2350
2607
|
|
|
@@ -2400,7 +2657,6 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
|
|
|
2400
2657
|
| onClose | `() => void` | required | Called on swipe-dismiss or backdrop press |
|
|
2401
2658
|
| title | `string` | — | Sheet heading |
|
|
2402
2659
|
| subtitle | `string` | — | Supporting text below title |
|
|
2403
|
-
| description | `string` | — | **Deprecated alias** for `subtitle` — prefer `subtitle` |
|
|
2404
2660
|
| showCloseButton | `boolean` | `false` | Show X close button in the header |
|
|
2405
2661
|
| children | `ReactNode` | — | Sheet content |
|
|
2406
2662
|
| style | `ViewStyle` | — | Inner scroll/content container style |
|
|
@@ -2413,14 +2669,11 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
|
|
|
2413
2669
|
| android_keyboardInputMode | `'adjustPan' \| 'adjustResize'` | `'adjustPan'` | Android-only: `'adjustPan'` moves the window (default — fixes restore issues with dynamic sizing). `'adjustResize'` resizes the container (can cause transparent gap when keyboard dismisses) |
|
|
2414
2670
|
| footer | `ReactNode` | — | Sticky footer below scroll area (sticky above keyboard) |
|
|
2415
2671
|
| snapPoints | `(string \| number)[]` | — | Optional snap points (e.g., `['50%', '85%']`). When omitted, uses dynamic sizing (auto-fits content) |
|
|
2416
|
-
| responsive | `boolean` | `false` | **(v8)** On wide screens (width ≥ `BREAKPOINTS.wide`) render a centered modal dialog instead of a bottom sheet. Stays a bottom sheet on phones |
|
|
2417
|
-
| dialogMaxWidth | `number` | `480` | Max width of the centered dialog. Only applies when `responsive` |
|
|
2418
2672
|
|
|
2419
2673
|
**Features:**
|
|
2420
2674
|
- `enableDynamicSizing` — height auto-fits content, no `snapPoints` needed (default behavior when `snapPoints` is omitted)
|
|
2421
2675
|
- `snapPoints` — optionally provide custom snap points (e.g., `['50%', '85%']`). Disables dynamic sizing when provided
|
|
2422
|
-
- `
|
|
2423
|
-
- `enablePanDownToClose` — swipe down to dismiss
|
|
2676
|
+
- `enablePanDownToClose` — swipe down to dismiss (always enabled)
|
|
2424
2677
|
- Backdrop press dismisses
|
|
2425
2678
|
- **Scrollable content:** use `scrollable` prop or `maxHeight`. Both use `BottomSheetScrollView` — do NOT use plain `ScrollView` inside Sheet
|
|
2426
2679
|
- **Keyboard handling:** Full keyboard awareness via `@gorhom/bottom-sheet` v5. Professional defaults:
|
|
@@ -2429,7 +2682,7 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
|
|
|
2429
2682
|
- `keyboardBlurBehavior="restore"` — returns to pre-keyboard position when keyboard dismisses
|
|
2430
2683
|
- `enableBlurKeyboardOnGesture={true}` — dismisses keyboard when dragging sheet down
|
|
2431
2684
|
- `topInset` — automatically applied from safe area context to prevent going above notch
|
|
2432
|
-
- **Text inputs inside sheet:**
|
|
2685
|
+
- **Text inputs inside sheet:** use `<Input sheetMode />` — it transparently swaps in `BottomSheetTextInput` for keyboard-aware focus/blur handling, while preserving the full `Input` API (label, error, hint, prefix/suffix, icons, type="password"). For low-level access, `SheetTextInput` (re-exported `BottomSheetTextInput`) is also available. **Never use regular `<TextInput>`** inside sheets — keyboard handling will break.
|
|
2433
2686
|
- **Custom TextInput:** If using custom input components, you must copy `handleOnFocus`/`handleOnBlur` from [BottomSheetTextInput source](https://github.com/gorhom/react-native-bottom-sheet/blob/master/src/components/bottomSheetTextInput/BottomSheetTextInput.tsx)
|
|
2434
2687
|
|
|
2435
2688
|
**Haptics:** `impactMedium` on open.
|
|
@@ -2448,23 +2701,39 @@ const [open, setOpen] = useState(false)
|
|
|
2448
2701
|
{items.map((r) => <ListItem key={r.id} title={r.name} showSeparator />)}
|
|
2449
2702
|
</Sheet>
|
|
2450
2703
|
|
|
2451
|
-
// With text input —
|
|
2452
|
-
<Sheet
|
|
2453
|
-
open={open}
|
|
2454
|
-
onClose={() => setOpen(false)}
|
|
2704
|
+
// With text input — recommended pattern (v12+): Input sheetMode
|
|
2705
|
+
<Sheet
|
|
2706
|
+
open={open}
|
|
2707
|
+
onClose={() => setOpen(false)}
|
|
2708
|
+
title="Add note"
|
|
2709
|
+
subtitle="Keyboard handling is automatic with platform-optimized defaults"
|
|
2710
|
+
>
|
|
2711
|
+
<Input
|
|
2712
|
+
label="Note"
|
|
2713
|
+
placeholder="Write your note..."
|
|
2714
|
+
value={note}
|
|
2715
|
+
onChangeText={setNote}
|
|
2716
|
+
sheetMode
|
|
2717
|
+
/>
|
|
2718
|
+
<Button label="Save" fullWidth onPress={handleSave} />
|
|
2719
|
+
</Sheet>
|
|
2720
|
+
|
|
2721
|
+
// Low-level alternative: SheetTextInput
|
|
2722
|
+
<Sheet
|
|
2723
|
+
open={open}
|
|
2724
|
+
onClose={() => setOpen(false)}
|
|
2455
2725
|
title="Add note"
|
|
2456
|
-
subtitle="Keyboard handling is automatic with platform-optimized defaults"
|
|
2457
2726
|
>
|
|
2458
|
-
<SheetTextInput
|
|
2459
|
-
placeholder="Write your note..."
|
|
2727
|
+
<SheetTextInput
|
|
2728
|
+
placeholder="Write your note..."
|
|
2460
2729
|
multiline
|
|
2461
|
-
style={{
|
|
2462
|
-
borderWidth: 1,
|
|
2730
|
+
style={{
|
|
2731
|
+
borderWidth: 1,
|
|
2463
2732
|
borderColor: '#ddd',
|
|
2464
2733
|
borderRadius: 8,
|
|
2465
2734
|
padding: 12,
|
|
2466
2735
|
minHeight: 80,
|
|
2467
|
-
}}
|
|
2736
|
+
}}
|
|
2468
2737
|
/>
|
|
2469
2738
|
<Button label="Save" fullWidth style={{ marginTop: 12 }} onPress={() => setOpen(false)} />
|
|
2470
2739
|
</Sheet>
|
|
@@ -2493,7 +2762,7 @@ const [open, setOpen] = useState(false)
|
|
|
2493
2762
|
|
|
2494
2763
|
**Keyboard handling notes:**
|
|
2495
2764
|
- No `KeyboardAvoidingView` needed — `@gorhom/bottom-sheet` handles everything
|
|
2496
|
-
-
|
|
2765
|
+
- Use `<Input sheetMode />` (preferred) or `SheetTextInput` (low-level) for text inputs inside the sheet
|
|
2497
2766
|
- Default `keyboardBehavior="interactive"` works on both platforms
|
|
2498
2767
|
- Default `android_keyboardInputMode="adjustPan"` fixes the transparent gap that occurs with `adjustResize` when keyboard dismisses
|
|
2499
2768
|
- `enableBlurKeyboardOnGesture={true}` (default) dismisses keyboard when dragging sheet
|
|
@@ -2757,7 +3026,6 @@ dismiss(id)
|
|
|
2757
3026
|
| visible | `boolean` | required | Controls dialog visibility |
|
|
2758
3027
|
| title | `string` | required | Dialog heading |
|
|
2759
3028
|
| subtitle | `string` | — | Secondary text below title |
|
|
2760
|
-
| description | `string` | — | **Deprecated** — use `subtitle` instead |
|
|
2761
3029
|
| confirmLabel | `string` | `'Confirm'` | Confirm button text |
|
|
2762
3030
|
| cancelLabel | `string` | `'Cancel'` | Cancel button text |
|
|
2763
3031
|
| confirmVariant | `'primary' \| 'destructive'` | `'primary'` | Use `'destructive'` for delete/remove actions |
|
|
@@ -2767,12 +3035,15 @@ dismiss(id)
|
|
|
2767
3035
|
| onCancel | `() => void` | required | Called when cancel is tapped or backdrop pressed |
|
|
2768
3036
|
|
|
2769
3037
|
**Notes:**
|
|
2770
|
-
- Powered by `@gorhom/bottom-sheet` with `enableDynamicSizing`
|
|
3038
|
+
- Powered by `@gorhom/bottom-sheet` `BottomSheetModal` with `enableDynamicSizing` (v12 refactor — `present()` / `dismiss()` driven, lazy-mounted, no `index={-1}` + `snapToIndex(0)` timing traps)
|
|
3039
|
+
- `topInset={insets.top}` applied automatically — dialog never crosses the notch / status bar
|
|
3040
|
+
- Internal `wasOpened` ref prevents `dismiss()` being called before the sheet is mounted (race-condition fix from v12.1)
|
|
3041
|
+
- A stable `name` prop (from `useId()`) is passed to `BottomSheetModal` for correct gorhom modal registry behavior when multiple modals are open
|
|
2771
3042
|
- Buttons are full-width, stacked vertically (confirm on top)
|
|
2772
3043
|
- Cancel shows X icon, confirm shows check (primary) or trash-2 (destructive) icon
|
|
2773
3044
|
- Swipe down or backdrop press calls `onCancel`
|
|
2774
3045
|
|
|
2775
|
-
**Haptics:** `
|
|
3046
|
+
**Haptics:** `impactMedium` on open.
|
|
2776
3047
|
|
|
2777
3048
|
**Examples:**
|
|
2778
3049
|
```tsx
|
|
@@ -2780,7 +3051,7 @@ dismiss(id)
|
|
|
2780
3051
|
<ConfirmDialog
|
|
2781
3052
|
visible={showDelete}
|
|
2782
3053
|
title="Delete transaction?"
|
|
2783
|
-
|
|
3054
|
+
subtitle="$45.000 · Mercado · 12 mar · This action cannot be undone."
|
|
2784
3055
|
confirmLabel="Delete"
|
|
2785
3056
|
confirmVariant="destructive"
|
|
2786
3057
|
onConfirm={handleDelete}
|
|
@@ -2791,7 +3062,7 @@ dismiss(id)
|
|
|
2791
3062
|
<ConfirmDialog
|
|
2792
3063
|
visible={showConfirm}
|
|
2793
3064
|
title="Send payment?"
|
|
2794
|
-
|
|
3065
|
+
subtitle={`Send $${amount} to ${recipientName}`}
|
|
2795
3066
|
confirmLabel="Send"
|
|
2796
3067
|
onConfirm={handleSend}
|
|
2797
3068
|
onCancel={() => setShowConfirm(false)}
|
|
@@ -2801,7 +3072,7 @@ dismiss(id)
|
|
|
2801
3072
|
<ConfirmDialog
|
|
2802
3073
|
visible={showDiscard}
|
|
2803
3074
|
title="Discard changes?"
|
|
2804
|
-
|
|
3075
|
+
subtitle="All unsaved changes will be lost."
|
|
2805
3076
|
confirmLabel="Discard"
|
|
2806
3077
|
confirmVariant="destructive"
|
|
2807
3078
|
onConfirm={() => { setShowDiscard(false); goBack() }}
|
|
@@ -2832,7 +3103,7 @@ dismiss(id)
|
|
|
2832
3103
|
| leftIconColor | `string` | — | Override left icon color (default: `foreground`) |
|
|
2833
3104
|
| rightIconColor | `string` | — | Override right icon color (default: `foregroundMuted`) |
|
|
2834
3105
|
| variant | `'plain' \| 'card'` | `'plain'` | `plain`: no background. `card`: surface with border and shadow |
|
|
2835
|
-
| showChevron | `boolean` | `false` | Right-pointing chevron. Ignored when `rightRender` is set |
|
|
3106
|
+
| showChevron | `boolean` | `false` | Right-pointing chevron. Ignored when `rightActions`, `rightRender`, or `rightIcon` is set |
|
|
2836
3107
|
| showSeparator | `boolean` | `false` | Hairline separator at bottom. Useful for stacking plain items |
|
|
2837
3108
|
| onPress | `() => void` | — | Makes row pressable (scale animation + haptics) |
|
|
2838
3109
|
| disabled | `boolean` | — | Reduces opacity to 0.45 |
|
|
@@ -2841,13 +3112,12 @@ dismiss(id)
|
|
|
2841
3112
|
| subtitleStyle | `TextStyle` | — | Override subtitle text style |
|
|
2842
3113
|
| subtitleNumberOfLines | `number` | `2` | Max lines for subtitle before truncation |
|
|
2843
3114
|
| captionStyle | `TextStyle` | — | Override caption text style |
|
|
2844
|
-
|
|
|
2845
|
-
| trailing | `string \| ReactNode` | — | **Deprecated** — use `rightRender` |
|
|
3115
|
+
| rightActions | `ReactNode[]` | — | Multiple action buttons on the right with 8pt gap. Takes precedence over `rightRender` |
|
|
2846
3116
|
|
|
2847
3117
|
**Slots:**
|
|
2848
3118
|
- `leftRender` / `leftIcon` — 44×44pt fixed container, centered. Good for Avatar, icons, thumbnails
|
|
2849
3119
|
- `rightRender` / `rightIcon` — max 160pt wide, right-aligned. Good for Badge, price text, Switch
|
|
2850
|
-
- `showChevron` — `›` chevron, 24pt, `foregroundMuted` color. Only shows when `rightRender`
|
|
3120
|
+
- `showChevron` — `›` chevron, 24pt, `foregroundMuted` color. Only shows when `rightRender`, `rightActions`, and `rightIcon` are absent
|
|
2851
3121
|
|
|
2852
3122
|
**Separator inset:** Aligns to text block — `marginLeft` adjusts to skip over left slot when present.
|
|
2853
3123
|
|
|
@@ -3465,22 +3735,22 @@ const [period, setPeriod] = useState({
|
|
|
3465
3735
|
|
|
3466
3736
|
**When to use:** Custom interactive content that needs beautiful spring bounce effect matching MediaCard. Use when you need a pressable wrapper around custom layouts that aren't covered by existing button components.
|
|
3467
3737
|
|
|
3468
|
-
**Extends:** `TouchableOpacityProps` (all native props pass through except `activeOpacity`)
|
|
3469
|
-
|
|
3470
3738
|
| Prop | Type | Default | Notes |
|
|
3471
3739
|
|------|------|---------|-------|
|
|
3472
3740
|
| children | `ReactNode` | required | Content to render inside the pressable |
|
|
3473
3741
|
| onPress | `() => void` | — | Press handler |
|
|
3474
3742
|
| pressScale | `number` | `0.98` | Scale value on press (MediaCard-style) |
|
|
3475
|
-
| bounciness | `number` | `4` | Spring bounciness on release |
|
|
3476
3743
|
| haptics | `boolean` | `true` | Enable haptic feedback on press |
|
|
3477
3744
|
| hoverScale | `number` | `1.02` | Hover scale (web only). Set to `1` to disable |
|
|
3478
3745
|
| disabled | `boolean` | `false` | Disable interaction |
|
|
3746
|
+
| accessibilityRole | `AccessibilityRole` | `'button'` | Override the accessibility role for screen readers |
|
|
3747
|
+
| accessibilityState | `Record<string, unknown>` | `{ disabled: !!disabled }` | Accessibility state for selected/expanded/checked |
|
|
3748
|
+
| accessibilityLabel | `string` | — | Accessibility label for screen readers |
|
|
3479
3749
|
| style | `ViewStyle` | — | Animated wrapper style |
|
|
3480
3750
|
|
|
3481
3751
|
**Behavior:**
|
|
3482
3752
|
- Press: springs to `pressScale` (default 0.98) with `speed: 40, bounciness: 0`
|
|
3483
|
-
- Release: springs back to 1.0 with `speed: 40
|
|
3753
|
+
- Release: springs back to 1.0 with `speed: 40`
|
|
3484
3754
|
- Web: optional hover scale (default 1.02)
|
|
3485
3755
|
- Haptics: `impactLight` on press (unless `haptics={false}`)
|
|
3486
3756
|
|
|
@@ -3499,7 +3769,7 @@ const [period, setPeriod] = useState({
|
|
|
3499
3769
|
</Pressable>
|
|
3500
3770
|
|
|
3501
3771
|
// Wrapping complex layout
|
|
3502
|
-
<Pressable onPress={handleSelect} pressScale={0.96}
|
|
3772
|
+
<Pressable onPress={handleSelect} pressScale={0.96}>
|
|
3503
3773
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, padding: 16 }}>
|
|
3504
3774
|
<Avatar src={user.avatar} size="md" />
|
|
3505
3775
|
<View style={{ flex: 1 }}>
|
|
@@ -3516,7 +3786,7 @@ const [period, setPeriod] = useState({
|
|
|
3516
3786
|
</Pressable>
|
|
3517
3787
|
|
|
3518
3788
|
// Custom press scale (deeper press)
|
|
3519
|
-
<Pressable onPress={handlePress} pressScale={0.92}
|
|
3789
|
+
<Pressable onPress={handlePress} pressScale={0.92}>
|
|
3520
3790
|
{/* content */}
|
|
3521
3791
|
</Pressable>
|
|
3522
3792
|
```
|
|
@@ -3788,6 +4058,77 @@ export default function TabLayout() {
|
|
|
3788
4058
|
|
|
3789
4059
|
---
|
|
3790
4060
|
|
|
4061
|
+
### Stats
|
|
4062
|
+
|
|
4063
|
+
**Import:** `import { Stats } from '@retray-dev/ui-kit'`
|
|
4064
|
+
|
|
4065
|
+
**When to use:** Dashboards and analytics screens — single-stat card with a large value, icon, label, and optional description. Tappable cards wire haptics automatically.
|
|
4066
|
+
|
|
4067
|
+
| Prop | Type | Default | Notes |
|
|
4068
|
+
|------|------|---------|-------|
|
|
4069
|
+
| value | `string` | required | Large display value, e.g. `"$12,450"` or `"847"` |
|
|
4070
|
+
| label | `string` | required | Label below the value, e.g. `"Monthly Revenue"` |
|
|
4071
|
+
| description | `string` | — | Third line — smaller, muted text for trend/context |
|
|
4072
|
+
| icon | `React.ReactNode` | — | Custom icon node |
|
|
4073
|
+
| iconName | `string` | — | Icon from `@expo/vector-icons` (left of value, `colors.primary`) |
|
|
4074
|
+
| iconColor | `string` | `colors.primary` | Override icon color |
|
|
4075
|
+
| size | `'default' \| 'compact'` | `'default'` | `compact`: 16px SemiBold value, 11px label, reduced padding |
|
|
4076
|
+
| variant | `'elevated' \| 'outlined' \| 'filled'` | `'elevated'` | Card surface variant |
|
|
4077
|
+
| onPress | `() => void` | — | Makes the card pressable (haptic `impactLight()`) |
|
|
4078
|
+
| style | `ViewStyle` | — | — |
|
|
4079
|
+
| accessibilityLabel | `string` | — | — |
|
|
4080
|
+
|
|
4081
|
+
**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.
|
|
4082
|
+
|
|
4083
|
+
**`Stats.Group`** — horizontal layout wrapper that distributes children equally:
|
|
4084
|
+
|
|
4085
|
+
| Prop | Type | Default | Notes |
|
|
4086
|
+
|------|------|---------|-------|
|
|
4087
|
+
| children | `React.ReactNode` | required | `Stats` components to arrange horizontally |
|
|
4088
|
+
| gap | `number` | `s(12)` | Spacing between cards |
|
|
4089
|
+
| style | `ViewStyle` | — | — |
|
|
4090
|
+
|
|
4091
|
+
**Layout guidance:** Prefer 2-up for values longer than ~3 digits (e.g. `"$12,450"`, `"1,234"`) or with descriptions. Use 3-up only for very compact metrics with short values (e.g. `"847"`, `"98%"`) and no description — otherwise values will wrap and look broken.
|
|
4092
|
+
|
|
4093
|
+
**Example:**
|
|
4094
|
+
```tsx
|
|
4095
|
+
// Standalone
|
|
4096
|
+
<Stats
|
|
4097
|
+
value="$12,450"
|
|
4098
|
+
label="Monthly Revenue"
|
|
4099
|
+
description="+12% from last month"
|
|
4100
|
+
iconName="trending-up"
|
|
4101
|
+
/>
|
|
4102
|
+
|
|
4103
|
+
// 3-up — short values only, no description
|
|
4104
|
+
<Stats.Group>
|
|
4105
|
+
<Stats value="847" label="Users" variant="elevated" />
|
|
4106
|
+
<Stats value="98%" label="Uptime" variant="outlined" />
|
|
4107
|
+
<Stats value="12" label="Alerts" variant="filled" />
|
|
4108
|
+
</Stats.Group>
|
|
4109
|
+
|
|
4110
|
+
// 2-up with icons, long values, and description
|
|
4111
|
+
<Stats.Group gap={s(16)}>
|
|
4112
|
+
<Stats
|
|
4113
|
+
value="$8,200"
|
|
4114
|
+
label="Spent"
|
|
4115
|
+
description="This month"
|
|
4116
|
+
iconName="credit-card"
|
|
4117
|
+
variant="elevated"
|
|
4118
|
+
onPress={() => navigateToBilling()}
|
|
4119
|
+
/>
|
|
4120
|
+
<Stats
|
|
4121
|
+
value="$2,150"
|
|
4122
|
+
label="Saved"
|
|
4123
|
+
description="12% of budget"
|
|
4124
|
+
iconName="trending-up"
|
|
4125
|
+
variant="outlined"
|
|
4126
|
+
/>
|
|
4127
|
+
</Stats.Group>
|
|
4128
|
+
```
|
|
4129
|
+
|
|
4130
|
+
---
|
|
4131
|
+
|
|
3791
4132
|
### ErrorBoundary
|
|
3792
4133
|
|
|
3793
4134
|
**Import:** `import { ErrorBoundary } from '@retray-dev/ui-kit'`
|
|
@@ -3989,6 +4330,7 @@ import { HolographicCard, FOIL_PRESETS } from '@retray-dev/ui-kit/HolographicCar
|
|
|
3989
4330
|
| borderRadius | `number` | `RADIUS.lg` | — |
|
|
3990
4331
|
| resizeMode | `'cover' \| 'contain' \| 'stretch'` | `'cover'` | Image resize mode |
|
|
3991
4332
|
| disabled | `boolean` | `false` | Prevents pressing |
|
|
4333
|
+
| allowsEditing | `boolean` | `true` | When `true`, iOS opens the crop/editing screen after selecting an image. Set `false` to accept the image directly without cropping |
|
|
3992
4334
|
| style | `ViewStyle` | — | — |
|
|
3993
4335
|
| accessibilityLabel | `string` | — | — |
|
|
3994
4336
|
|
|
@@ -4030,7 +4372,7 @@ const handleChange = async (uri: string | null) => {
|
|
|
4030
4372
|
|
|
4031
4373
|
**Import:** `import { IconPicker } from '@retray-dev/ui-kit'`
|
|
4032
4374
|
|
|
4033
|
-
**When to use:** Selecting an icon from a curated catalog of ~
|
|
4375
|
+
**When to use:** Selecting an icon from a curated catalog of ~600 themed icons (across Feather, Ionicons `-outline`, FA5 Brands/regular, Entypo, AntDesign — only outlined variants) organized by 12 categories. The trigger is a simple tappable square showing the selected icon (or a + placeholder). Tapping opens a bottom sheet with category chips (icon + label) and a scrollable grid. No search, no text clutter — purely visual selection.
|
|
4034
4376
|
|
|
4035
4377
|
**Requires:** `@gorhom/bottom-sheet` (already a peer dependency of the UI kit).
|
|
4036
4378
|
|
|
@@ -4067,10 +4409,12 @@ const [icon, setIcon] = useState<string | null>(null)
|
|
|
4067
4409
|
```
|
|
4068
4410
|
|
|
4069
4411
|
**Notes:**
|
|
4070
|
-
- Uses a static curated list of ~
|
|
4412
|
+
- Uses a static curated list of ~600 outlined icons in 12 categories (food, sports, business, objects, status, actions, communication, navigation, media, layout, nature, brands), with at least 42 icons per category — instant load, no runtime glyphMap scanning.
|
|
4413
|
+
- Icon selection rules are strict: only Feather, Ionicons `-outline`, and themed FA5/Entypo/AntDesign icons are eligible. Filled variants and FA5-only-solid icons are excluded to keep the visual style consistent. See `src/utils/curatedIcons.ts` for the source of truth.
|
|
4414
|
+
- **Feedback pattern (v12.1):** the sheet presents **immediately** on trigger tap (no `useEffect` delay). While the grid container measures its width via `onLayout`, a centered `<Spinner />` is shown inside the sheet so the user always sees visible feedback. The grid transitions in as soon as measurement is complete. This is the canonical REGLA 4 pattern — apply it to any overlay whose content needs to measure before rendering.
|
|
4071
4415
|
- Category chips use representative icons (coffee, activity, briefcase, folder, alert-circle, edit-3, message-circle, compass, image, grid, sun, globe) with Spanish labels and a "Todos" (All) chip.
|
|
4072
4416
|
- Grid cells show only icons — clean, fast visual scanning. Cell size adapts to container width.
|
|
4073
|
-
- Sheet uses `enableDynamicSizing` with `maxDynamicContentSize` (70% screen height) — height auto-fits content up to that cap.
|
|
4417
|
+
- Sheet uses `enableDynamicSizing` with `maxDynamicContentSize` (70% screen height) — height auto-fits content up to that cap. `topInset={insets.top}` is applied automatically.
|
|
4074
4418
|
- Category strip is a horizontal `ScrollView` of pill-shaped chips. The grid is rendered in rows inside a `BottomSheetScrollView`.
|
|
4075
4419
|
- Selection closes the sheet immediately and resets category to "Todos".
|
|
4076
4420
|
- Haptics: medium impact on open, selection on icon tap.
|