@retray-dev/ui-kit 10.1.0 → 12.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +419 -38
- package/README.md +14 -5
- package/dist/Accordion.js +1 -1
- package/dist/Accordion.mjs +3 -3
- package/dist/AlertBanner.js +1 -1
- package/dist/AlertBanner.mjs +3 -3
- package/dist/AppHeader.js +1 -1
- package/dist/AppHeader.mjs +4 -4
- package/dist/Avatar.mjs +2 -2
- package/dist/Badge.js +1 -1
- package/dist/Badge.mjs +3 -3
- package/dist/Button.js +1 -1
- package/dist/Button.mjs +3 -3
- package/dist/Card.mjs +2 -2
- package/dist/CategoryStrip.js +1 -1
- package/dist/CategoryStrip.mjs +3 -3
- package/dist/Checkbox.mjs +2 -2
- package/dist/Chip.js +1 -1
- package/dist/Chip.mjs +3 -3
- package/dist/ConfirmDialog.d.mts +1 -6
- package/dist/ConfirmDialog.d.ts +1 -6
- package/dist/ConfirmDialog.js +30 -24
- package/dist/ConfirmDialog.mjs +4 -4
- package/dist/CurrencyDisplay.mjs +2 -2
- package/dist/CurrencyInput.d.mts +3 -8
- package/dist/CurrencyInput.d.ts +3 -8
- package/dist/CurrencyInput.js +4 -2
- package/dist/CurrencyInput.mjs +4 -4
- package/dist/DetailRow.d.mts +1 -1
- package/dist/DetailRow.d.ts +1 -1
- package/dist/DetailRow.js +1 -1
- package/dist/DetailRow.mjs +3 -3
- package/dist/EmptyState.js +1 -1
- package/dist/EmptyState.mjs +4 -4
- package/dist/ErrorBoundary.js +1 -1
- package/dist/ErrorBoundary.mjs +3 -3
- package/dist/Form.mjs +2 -2
- package/dist/IconButton.js +1 -1
- package/dist/IconButton.mjs +3 -3
- package/dist/IconPicker.d.mts +17 -0
- package/dist/IconPicker.d.ts +17 -0
- package/dist/IconPicker.js +1424 -0
- package/dist/IconPicker.mjs +8 -0
- package/dist/ImageUpload.d.mts +3 -1
- package/dist/ImageUpload.d.ts +3 -1
- package/dist/ImageUpload.js +28 -10
- package/dist/ImageUpload.mjs +3 -3
- package/dist/ImageViewer.js +1 -1
- package/dist/ImageViewer.mjs +5 -5
- package/dist/Input.js +1 -1
- package/dist/Input.mjs +3 -3
- package/dist/LabelValue.js +1 -1
- package/dist/LabelValue.mjs +3 -3
- package/dist/ListGroup.mjs +2 -2
- package/dist/ListItem.d.mts +7 -7
- package/dist/ListItem.d.ts +7 -7
- package/dist/ListItem.js +13 -8
- package/dist/ListItem.mjs +3 -3
- package/dist/MediaCard.js +1 -1
- package/dist/MediaCard.mjs +3 -3
- package/dist/MenuGroup.mjs +2 -2
- package/dist/MenuItem.js +1 -1
- package/dist/MenuItem.mjs +3 -3
- package/dist/MonthPicker.mjs +2 -2
- package/dist/NumberStepper.d.mts +19 -0
- package/dist/NumberStepper.d.ts +19 -0
- package/dist/NumberStepper.js +410 -0
- package/dist/NumberStepper.mjs +9 -0
- package/dist/PagerDots.js +1 -1
- package/dist/PagerDots.mjs +3 -3
- 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 +1 -1
- package/dist/PricingCard.mjs +5 -5
- package/dist/Progress.mjs +2 -2
- package/dist/RadioGroup.mjs +2 -2
- package/dist/RetrayProvider.mjs +3 -3
- package/dist/Select.mjs +2 -2
- package/dist/SelectableGrid.js +1 -1
- package/dist/SelectableGrid.mjs +3 -3
- package/dist/Separator.mjs +2 -2
- package/dist/Sheet.d.mts +4 -46
- package/dist/Sheet.d.ts +4 -46
- package/dist/Sheet.js +46 -114
- package/dist/Sheet.mjs +2 -3
- package/dist/SheetSelect.js +1 -1
- package/dist/SheetSelect.mjs +3 -3
- package/dist/Skeleton.mjs +2 -2
- package/dist/Slider.mjs +2 -2
- package/dist/Spinner.mjs +2 -2
- package/dist/Stats.d.mts +30 -0
- package/dist/Stats.d.ts +30 -0
- package/dist/Stats.js +429 -0
- package/dist/Stats.mjs +9 -0
- package/dist/Switch.mjs +2 -2
- package/dist/TabBar.js +1 -1
- package/dist/TabBar.mjs +3 -3
- package/dist/Tabs.mjs +2 -2
- package/dist/Text.d.mts +3 -1
- package/dist/Text.d.ts +3 -1
- package/dist/Text.js +3 -3
- package/dist/Text.mjs +2 -2
- package/dist/Textarea.js +1 -1
- package/dist/Textarea.mjs +3 -3
- package/dist/Toast.mjs +2 -2
- package/dist/Toggle.js +1 -1
- package/dist/Toggle.mjs +3 -3
- package/dist/{chunk-DJ7RN37L.mjs → chunk-265G6A46.mjs} +2 -2
- package/dist/{chunk-WOEYDUJZ.mjs → chunk-2A2LEFZG.mjs} +2 -2
- package/dist/{chunk-ID72TK46.mjs → chunk-2CBQKU7H.mjs} +1 -1
- package/dist/{chunk-OB4JUQ3O.mjs → chunk-2I2AYECM.mjs} +1 -1
- package/dist/{chunk-WJLKJMKR.mjs → chunk-357YO24D.mjs} +4 -4
- package/dist/{chunk-GQYFLP3D.mjs → chunk-3GEYJ7I5.mjs} +1 -1
- package/dist/{chunk-AV4EMIRH.mjs → chunk-3N2M3WZL.mjs} +1 -1
- package/dist/{chunk-TERDKCLE.mjs → chunk-3UYAZ7I4.mjs} +2 -2
- package/dist/{chunk-JMOZEC77.mjs → chunk-4WFMPFZB.mjs} +1 -1
- package/dist/chunk-5OLNXP3S.mjs +144 -0
- package/dist/{chunk-6OAZJ577.mjs → chunk-7HSILTC4.mjs} +3 -3
- package/dist/{chunk-IRRY3CRZ.mjs → chunk-AKM4EPOT.mjs} +1 -1
- package/dist/{chunk-VGTDN7SW.mjs → chunk-AQEVCEXV.mjs} +2 -2
- package/dist/{chunk-WBOOUHSS.mjs → chunk-BCWEHE34.mjs} +1 -1
- package/dist/{chunk-AJ7ZDNBT.mjs → chunk-BOVUP27T.mjs} +1 -1
- package/dist/{chunk-BRKYVJVV.mjs → chunk-BQZE3HAW.mjs} +1 -1
- package/dist/{chunk-MLF3EZFW.mjs → chunk-D3Y2T42P.mjs} +2 -2
- package/dist/{chunk-3U4SSNWP.mjs → chunk-DF6DU42P.mjs} +2 -2
- package/dist/{chunk-ZJKGQMYH.mjs → chunk-DI7CBDL6.mjs} +2 -2
- package/dist/{chunk-2TFTAWVJ.mjs → chunk-DOGIPOF5.mjs} +2 -2
- package/dist/{chunk-MBMXYJJV.mjs → chunk-E7NEHHXV.mjs} +7 -3
- package/dist/{chunk-MX6HRKMI.mjs → chunk-EFLFRAHD.mjs} +1 -1
- package/dist/{chunk-SOYNZDVY.mjs → chunk-EMUWGDWC.mjs} +6 -1
- package/dist/{chunk-4I7D47FH.mjs → chunk-F4V6XLP4.mjs} +4 -4
- package/dist/{chunk-UREA2GYY.mjs → chunk-FA2KMTH5.mjs} +2 -2
- package/dist/{chunk-Y2NS74WS.mjs → chunk-FFTYLPSB.mjs} +46 -98
- package/dist/{chunk-OHBNABL5.mjs → chunk-FUVYSVGR.mjs} +14 -9
- package/dist/{chunk-KIHCWCWL.mjs → chunk-FVTVCJAH.mjs} +2 -2
- package/dist/{chunk-Y4GL2MHX.mjs → chunk-GK4VRMNE.mjs} +30 -12
- package/dist/{chunk-6Q64UFIA.mjs → chunk-HJ46DTJE.mjs} +1 -1
- package/dist/{chunk-WF2XDFRK.mjs → chunk-HLMPMUK2.mjs} +1 -1
- package/dist/{chunk-GD6KXMG5.mjs → chunk-I4V5XZPS.mjs} +1 -1
- package/dist/{chunk-AZJF2BLK.mjs → chunk-ISY26JQJ.mjs} +2 -2
- package/dist/{chunk-X4G6APW6.mjs → chunk-J6Q2YJEV.mjs} +1 -1
- package/dist/{chunk-KZL5VTYK.mjs → chunk-JCZQOY4O.mjs} +31 -24
- package/dist/{chunk-CZCQZHG6.mjs → chunk-JNVAIDLK.mjs} +2 -2
- package/dist/{chunk-SOA2Z4RB.mjs → chunk-JULSIZDM.mjs} +1 -1
- package/dist/{chunk-T7XZ7H7Y.mjs → chunk-KA7LTET3.mjs} +17 -3
- package/dist/chunk-KHYX4IOM.mjs +1114 -0
- package/dist/{chunk-LXJIIOYQ.mjs → chunk-LRM4AVYY.mjs} +2 -2
- package/dist/{chunk-VQ57HWPL.mjs → chunk-MYZ2EDYU.mjs} +2 -2
- package/dist/chunk-N4ZPVCJH.mjs +126 -0
- package/dist/{chunk-NA7PARID.mjs → chunk-NXI4YDZ2.mjs} +2 -2
- package/dist/{chunk-4K625MVM.mjs → chunk-OULVKTWL.mjs} +2 -2
- package/dist/{chunk-A4MDAP7G.mjs → chunk-P64WHW4A.mjs} +2 -2
- package/dist/{chunk-URI2WBIV.mjs → chunk-P73V2EKS.mjs} +2 -2
- package/dist/{chunk-ZUR7AU5R.mjs → chunk-PGERH3P7.mjs} +2 -2
- package/dist/{chunk-2UYENBLV.mjs → chunk-QSFV2P7O.mjs} +1 -1
- package/dist/{chunk-JT7HKXRB.mjs → chunk-S3KJCPEJ.mjs} +1 -1
- package/dist/{chunk-6MKGPAR2.mjs → chunk-V6NFJXKO.mjs} +2 -2
- package/dist/{chunk-A3A6KNQN.mjs → chunk-WOEWGSTU.mjs} +1 -1
- package/dist/{chunk-JUXSWN54.mjs → chunk-X26S5EVZ.mjs} +4 -2
- package/dist/{chunk-YFZ3ELX5.mjs → chunk-XBAGGKLW.mjs} +2 -2
- package/dist/{chunk-JB67UOB5.mjs → chunk-ZHMSAYLT.mjs} +2 -2
- 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.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +1831 -475
- package/dist/index.mjs +54 -51
- package/package.json +3 -3
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +39 -30
- package/src/components/CurrencyInput/CurrencyInput.tsx +4 -7
- package/src/components/DetailRow/DetailRow.tsx +1 -1
- package/src/components/IconPicker/IconPicker.tsx +395 -0
- package/src/components/IconPicker/index.ts +1 -0
- package/src/components/ImageUpload/ImageUpload.tsx +34 -12
- package/src/components/ListItem/ListItem.tsx +43 -28
- package/src/components/NumberStepper/NumberStepper.tsx +147 -0
- package/src/components/NumberStepper/index.ts +1 -0
- package/src/components/Pressable/Pressable.tsx +20 -8
- package/src/components/Sheet/Sheet.tsx +64 -172
- package/src/components/Stats/Stats.tsx +226 -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 +7 -1
- package/src/theme/colorUtils.ts +9 -0
- package/src/utils/curatedIcons.ts +849 -0
- package/src/utils/fontGuard.ts +2 -1
- package/src/utils/icons.ts +20 -2
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
|
|
|
@@ -317,6 +317,24 @@ const resolved = deriveColors(myThemeColors, 'light')
|
|
|
317
317
|
|
|
318
318
|
---
|
|
319
319
|
|
|
320
|
+
### Color Utilities
|
|
321
|
+
|
|
322
|
+
**Import:** `import { withAlpha } from '@retray-dev/ui-kit'`
|
|
323
|
+
|
|
324
|
+
Convert a hex color to rgba with the given alpha. Useful for semi-transparent backgrounds, borders, and overlays derived from theme colors.
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
import { useTheme, withAlpha } from '@retray-dev/ui-kit'
|
|
328
|
+
|
|
329
|
+
const { colors } = useTheme()
|
|
330
|
+
|
|
331
|
+
<View style={{ backgroundColor: withAlpha(colors.primary, 0.15) }}>
|
|
332
|
+
<Text style={{ color: colors.primary }}>Tinted background</Text>
|
|
333
|
+
</View>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
320
338
|
## Design Tokens
|
|
321
339
|
|
|
322
340
|
Static structural constants — no context or provider needed.
|
|
@@ -684,6 +702,132 @@ assets/fonts/sohne/
|
|
|
684
702
|
|
|
685
703
|
---
|
|
686
704
|
|
|
705
|
+
## Migration Guide: v10 → v11
|
|
706
|
+
|
|
707
|
+
### New — public utility
|
|
708
|
+
|
|
709
|
+
**`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.
|
|
710
|
+
|
|
711
|
+
```tsx
|
|
712
|
+
import { useTheme, withAlpha } from '@retray-dev/ui-kit'
|
|
713
|
+
|
|
714
|
+
const { colors } = useTheme()
|
|
715
|
+
<View style={{ backgroundColor: withAlpha(colors.primary, 0.15) }} />
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
### Updated
|
|
719
|
+
|
|
720
|
+
- `Stats` component promoted from internal/experimental to public (`Stats` + `Stats.Group`).
|
|
721
|
+
- Documentation refreshed for `IconPicker` and `NumberStepper`.
|
|
722
|
+
|
|
723
|
+
No breaking changes in v11. Safe minor upgrade from v10.
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
## Migration Guide: v11 → v12
|
|
728
|
+
|
|
729
|
+
### Breaking Changes
|
|
730
|
+
|
|
731
|
+
`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.
|
|
732
|
+
|
|
733
|
+
**1. Removed `responsive` / `dialogMaxWidth` props from `Sheet` and `ConfirmDialog`**
|
|
734
|
+
|
|
735
|
+
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.
|
|
736
|
+
|
|
737
|
+
```diff
|
|
738
|
+
<Sheet
|
|
739
|
+
open={open}
|
|
740
|
+
onClose={() => setOpen(false)}
|
|
741
|
+
title="Options"
|
|
742
|
+
- responsive
|
|
743
|
+
- dialogMaxWidth={600}
|
|
744
|
+
/>
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
**2. `onClose` is the only close handler**
|
|
748
|
+
|
|
749
|
+
`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.
|
|
750
|
+
|
|
751
|
+
**3. `Sheet` requires `BottomSheetModalProvider` at app root**
|
|
752
|
+
|
|
753
|
+
`RetrayProvider` already wires it. If you assemble providers manually, ensure `BottomSheetModalProvider` sits inside `GestureHandlerRootView`:
|
|
754
|
+
|
|
755
|
+
```tsx
|
|
756
|
+
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
|
757
|
+
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
758
|
+
<ThemeProvider>
|
|
759
|
+
<BottomSheetModalProvider>
|
|
760
|
+
<ToastProvider>{/* app */}</ToastProvider>
|
|
761
|
+
</BottomSheetModalProvider>
|
|
762
|
+
</ThemeProvider>
|
|
763
|
+
</GestureHandlerRootView>
|
|
764
|
+
</SafeAreaProvider>
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
**4. Keyboard prop renames (no consumer action — defaults match v11)**
|
|
768
|
+
|
|
769
|
+
| Prop | v11 | v12 default |
|
|
770
|
+
|------|-----|-------------|
|
|
771
|
+
| `keyboardBehavior` | `'interactive'` | `'interactive'` (unchanged) |
|
|
772
|
+
| `android_keyboardInputMode` | `'adjustPan'` | `'adjustPan'` (unchanged) |
|
|
773
|
+
|
|
774
|
+
**5. Removed top-level imports of `Modal`, `ScrollView`, `useWindowDimensions`, `BREAKPOINTS` from `Sheet.tsx`**
|
|
775
|
+
|
|
776
|
+
Internal only — no public API.
|
|
777
|
+
|
|
778
|
+
### Behavioral changes (no code change required)
|
|
779
|
+
|
|
780
|
+
- **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.
|
|
781
|
+
- **`topInset={insets.top}`** is now applied automatically from safe-area context — sheet never crosses the notch / status bar on iOS or Android.
|
|
782
|
+
- **`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).
|
|
783
|
+
|
|
784
|
+
### New — recommended pattern
|
|
785
|
+
|
|
786
|
+
**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").
|
|
787
|
+
|
|
788
|
+
```tsx
|
|
789
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Add note">
|
|
790
|
+
<Input
|
|
791
|
+
label="Note"
|
|
792
|
+
placeholder="Type your note..."
|
|
793
|
+
value={note}
|
|
794
|
+
onChangeText={setNote}
|
|
795
|
+
sheetMode
|
|
796
|
+
/>
|
|
797
|
+
<Button label="Save" fullWidth onPress={handleSave} />
|
|
798
|
+
</Sheet>
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
`SheetTextInput` is still re-exported for low-level use.
|
|
802
|
+
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
## Migration Guide: v12.0 → v12.1
|
|
806
|
+
|
|
807
|
+
### New — IconPicker feedback pattern (REGLA 4)
|
|
808
|
+
|
|
809
|
+
`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.
|
|
810
|
+
|
|
811
|
+
This is now the canonical pattern for any sheet whose content needs to measure or load before rendering — apply it to new overlay components.
|
|
812
|
+
|
|
813
|
+
### New — race-condition fix in `Sheet` and `ConfirmDialog`
|
|
814
|
+
|
|
815
|
+
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.
|
|
816
|
+
|
|
817
|
+
No consumer action required — the fix is internal.
|
|
818
|
+
|
|
819
|
+
### New — expanded curated icon library
|
|
820
|
+
|
|
821
|
+
`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).
|
|
822
|
+
|
|
823
|
+
### Updated
|
|
824
|
+
|
|
825
|
+
- `Sheet` and `ConfirmDialog` pass a stable `name` prop to `BottomSheetModal` (from `useId()`) for correct gorhom modal registry behavior when multiple modals are mounted.
|
|
826
|
+
- `Input` `sheetMode` prop documented in the Setup section (replaces the `SheetTextInput` boilerplate inside sheets).
|
|
827
|
+
- `CompositionScreen` example now uses `<Input sheetMode />` inside a `Sheet` to demonstrate the keyboard-friendly pattern.
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
|
|
687
831
|
## Components
|
|
688
832
|
|
|
689
833
|
---
|
|
@@ -700,6 +844,7 @@ assets/fonts/sohne/
|
|
|
700
844
|
|------|------|---------|-------|
|
|
701
845
|
| variant | `TextVariant` | `'body-md'` | Sets font, size, weight, line height, letter spacing |
|
|
702
846
|
| color | `string` | — | Override text color. Each variant has a semantic default (see table below) |
|
|
847
|
+
| uppercase | `boolean` | `false` | Force text-transform: uppercase on any variant |
|
|
703
848
|
| children | `ReactNode` | required | — |
|
|
704
849
|
| style | `TextStyle` | — | Additional styles (merged after variant styles) |
|
|
705
850
|
| (all TextProps) | — | — | `numberOfLines`, `ellipsizeMode`, `onPress`, etc. all pass through |
|
|
@@ -1073,6 +1218,23 @@ assets/fonts/sohne/
|
|
|
1073
1218
|
</View>
|
|
1074
1219
|
```
|
|
1075
1220
|
|
|
1221
|
+
**Composition — inside a Sheet (preferred pattern, v12+):**
|
|
1222
|
+
|
|
1223
|
+
Use `sheetMode` to opt the inner `TextInput` into `BottomSheetTextInput` so keyboard handling works correctly. This is the canonical replacement for raw `SheetTextInput` inside sheets.
|
|
1224
|
+
|
|
1225
|
+
```tsx
|
|
1226
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Add note">
|
|
1227
|
+
<Input
|
|
1228
|
+
label="Note"
|
|
1229
|
+
placeholder="Write your note..."
|
|
1230
|
+
value={note}
|
|
1231
|
+
onChangeText={setNote}
|
|
1232
|
+
sheetMode
|
|
1233
|
+
/>
|
|
1234
|
+
<Button label="Save" fullWidth onPress={handleSave} />
|
|
1235
|
+
</Sheet>
|
|
1236
|
+
```
|
|
1237
|
+
|
|
1076
1238
|
---
|
|
1077
1239
|
|
|
1078
1240
|
### Textarea
|
|
@@ -1125,9 +1287,12 @@ assets/fonts/sohne/
|
|
|
1125
1287
|
| hint | `string` | — | Helper text below (hidden when `error` is set) |
|
|
1126
1288
|
| placeholder | `string` | `'$0'` | Defaults to `prefix + '0'` |
|
|
1127
1289
|
| editable | `boolean` | — | Pass `false` to disable |
|
|
1290
|
+
| autoFocus | `boolean` | — | Auto-focus on mount (inherited from TextInputProps) |
|
|
1128
1291
|
| containerStyle | `ViewStyle` | — | Outer container style |
|
|
1129
1292
|
| sheetMode | `boolean` | `false` | Use inside a Sheet — forwards `sheetMode` to underlying `Input` |
|
|
1130
1293
|
|
|
1294
|
+
**Extends `TextInputProps` from React Native** — all TextInput props (autoFocus, onFocus, onBlur, etc.) pass through to the underlying input.
|
|
1295
|
+
|
|
1131
1296
|
**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
1297
|
|
|
1133
1298
|
**Examples:**
|
|
@@ -1175,6 +1340,69 @@ const [amount, setAmount] = useState(0)
|
|
|
1175
1340
|
|
|
1176
1341
|
---
|
|
1177
1342
|
|
|
1343
|
+
### NumberStepper
|
|
1344
|
+
|
|
1345
|
+
**Import:** `import { NumberStepper } from '@retray-dev/ui-kit'`
|
|
1346
|
+
|
|
1347
|
+
**When to use:** Quantity +/- controls — cart item counts, guest selectors, inventory adjustments, portion sizes. Use when the user needs to increment/decrement a discrete numeric value within a constrained range (min–max).
|
|
1348
|
+
|
|
1349
|
+
| Prop | Type | Default | Notes |
|
|
1350
|
+
|------|------|---------|-------|
|
|
1351
|
+
| value | `number` | required | Current numeric value |
|
|
1352
|
+
| onValueChange | `(value: number) => void` | required | Called with clamped value on each step |
|
|
1353
|
+
| min | `number` | `1` | Minimum allowed value |
|
|
1354
|
+
| max | `number` | `99` | Maximum allowed value |
|
|
1355
|
+
| step | `number` | `1` | Increment/decrement amount. Supports decimals (e.g. `0.5`) |
|
|
1356
|
+
| size | `'sm' \| 'md' \| 'lg'` | `'md'` | Controls button and value text size |
|
|
1357
|
+
| disabled | `boolean` | `false` | Disables both buttons |
|
|
1358
|
+
| accessibilityLabel | `string` | — | Custom a11y label for value text. Default: `"Quantity: {value}"` |
|
|
1359
|
+
| style | `ViewStyle` | — | Outer container style |
|
|
1360
|
+
|
|
1361
|
+
**Styling:** Row layout with two square buttons flanking a centered value label. Buttons use `RADIUS.md` (14pt), `borderWidth: 1.5`, `surface` background with `border` outline. Value text: `Sohne-Medium`, centered, `foreground` color.
|
|
1362
|
+
|
|
1363
|
+
**Button states:** The decrement button disables (opacity 0.35, non-pressable) when `value ≤ min`. The increment button disables when `value ≥ max`. Both disable when the `disabled` prop is `true`.
|
|
1364
|
+
|
|
1365
|
+
**Haptics:** `impactLight` on each press.
|
|
1366
|
+
|
|
1367
|
+
**Accessibility:** Each button has a descriptive label (`"Decrease, current value X"` / `"Increase, current value X"`). The value text has `accessibilityRole="text"`. Disabled state passed to `accessibilityState`.
|
|
1368
|
+
|
|
1369
|
+
**Animation:** `PressableButton` (scale 0.95) on each button — matches the Button/IconButton press feel.
|
|
1370
|
+
|
|
1371
|
+
**Examples:**
|
|
1372
|
+
```tsx
|
|
1373
|
+
// Basic quantity stepper
|
|
1374
|
+
const [qty, setQty] = useState(1)
|
|
1375
|
+
<NumberStepper value={qty} onValueChange={setQty} />
|
|
1376
|
+
|
|
1377
|
+
// Custom range and step
|
|
1378
|
+
const [rating, setRating] = useState(3)
|
|
1379
|
+
<NumberStepper value={rating} onValueChange={setRating} min={0} max={5} step={0.5} />
|
|
1380
|
+
|
|
1381
|
+
// Large size for prominent use
|
|
1382
|
+
const [guests, setGuests] = useState(2)
|
|
1383
|
+
<NumberStepper value={guests} onValueChange={setGuests} min={1} max={16} size="lg" />
|
|
1384
|
+
|
|
1385
|
+
// Disabled during async operation
|
|
1386
|
+
<NumberStepper
|
|
1387
|
+
value={qty}
|
|
1388
|
+
onValueChange={setQty}
|
|
1389
|
+
disabled={isUpdating}
|
|
1390
|
+
/>
|
|
1391
|
+
```
|
|
1392
|
+
|
|
1393
|
+
**Composition — cart item row:**
|
|
1394
|
+
```tsx
|
|
1395
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
1396
|
+
<View>
|
|
1397
|
+
<Text variant="body-md">Margherita Pizza</Text>
|
|
1398
|
+
<Text variant="caption-sm" color={colors.foregroundMuted}>$12.000</Text>
|
|
1399
|
+
</View>
|
|
1400
|
+
<NumberStepper value={quantity} onValueChange={setQuantity} />
|
|
1401
|
+
</View>
|
|
1402
|
+
```
|
|
1403
|
+
|
|
1404
|
+
---
|
|
1405
|
+
|
|
1178
1406
|
### CurrencyDisplay
|
|
1179
1407
|
|
|
1180
1408
|
**Import:** `import { CurrencyDisplay } from '@retray-dev/ui-kit'`
|
|
@@ -2337,7 +2565,6 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
|
|
|
2337
2565
|
| onClose | `() => void` | required | Called on swipe-dismiss or backdrop press |
|
|
2338
2566
|
| title | `string` | — | Sheet heading |
|
|
2339
2567
|
| subtitle | `string` | — | Supporting text below title |
|
|
2340
|
-
| description | `string` | — | **Deprecated alias** for `subtitle` — prefer `subtitle` |
|
|
2341
2568
|
| showCloseButton | `boolean` | `false` | Show X close button in the header |
|
|
2342
2569
|
| children | `ReactNode` | — | Sheet content |
|
|
2343
2570
|
| style | `ViewStyle` | — | Inner scroll/content container style |
|
|
@@ -2350,14 +2577,11 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
|
|
|
2350
2577
|
| 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) |
|
|
2351
2578
|
| footer | `ReactNode` | — | Sticky footer below scroll area (sticky above keyboard) |
|
|
2352
2579
|
| snapPoints | `(string \| number)[]` | — | Optional snap points (e.g., `['50%', '85%']`). When omitted, uses dynamic sizing (auto-fits content) |
|
|
2353
|
-
| 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 |
|
|
2354
|
-
| dialogMaxWidth | `number` | `480` | Max width of the centered dialog. Only applies when `responsive` |
|
|
2355
2580
|
|
|
2356
2581
|
**Features:**
|
|
2357
2582
|
- `enableDynamicSizing` — height auto-fits content, no `snapPoints` needed (default behavior when `snapPoints` is omitted)
|
|
2358
2583
|
- `snapPoints` — optionally provide custom snap points (e.g., `['50%', '85%']`). Disables dynamic sizing when provided
|
|
2359
|
-
- `
|
|
2360
|
-
- `enablePanDownToClose` — swipe down to dismiss
|
|
2584
|
+
- `enablePanDownToClose` — swipe down to dismiss (always enabled)
|
|
2361
2585
|
- Backdrop press dismisses
|
|
2362
2586
|
- **Scrollable content:** use `scrollable` prop or `maxHeight`. Both use `BottomSheetScrollView` — do NOT use plain `ScrollView` inside Sheet
|
|
2363
2587
|
- **Keyboard handling:** Full keyboard awareness via `@gorhom/bottom-sheet` v5. Professional defaults:
|
|
@@ -2366,7 +2590,7 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
|
|
|
2366
2590
|
- `keyboardBlurBehavior="restore"` — returns to pre-keyboard position when keyboard dismisses
|
|
2367
2591
|
- `enableBlurKeyboardOnGesture={true}` — dismisses keyboard when dragging sheet down
|
|
2368
2592
|
- `topInset` — automatically applied from safe area context to prevent going above notch
|
|
2369
|
-
- **Text inputs inside sheet:**
|
|
2593
|
+
- **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.
|
|
2370
2594
|
- **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)
|
|
2371
2595
|
|
|
2372
2596
|
**Haptics:** `impactMedium` on open.
|
|
@@ -2385,23 +2609,39 @@ const [open, setOpen] = useState(false)
|
|
|
2385
2609
|
{items.map((r) => <ListItem key={r.id} title={r.name} showSeparator />)}
|
|
2386
2610
|
</Sheet>
|
|
2387
2611
|
|
|
2388
|
-
// With text input —
|
|
2389
|
-
<Sheet
|
|
2390
|
-
open={open}
|
|
2391
|
-
onClose={() => setOpen(false)}
|
|
2612
|
+
// With text input — recommended pattern (v12+): Input sheetMode
|
|
2613
|
+
<Sheet
|
|
2614
|
+
open={open}
|
|
2615
|
+
onClose={() => setOpen(false)}
|
|
2616
|
+
title="Add note"
|
|
2617
|
+
subtitle="Keyboard handling is automatic with platform-optimized defaults"
|
|
2618
|
+
>
|
|
2619
|
+
<Input
|
|
2620
|
+
label="Note"
|
|
2621
|
+
placeholder="Write your note..."
|
|
2622
|
+
value={note}
|
|
2623
|
+
onChangeText={setNote}
|
|
2624
|
+
sheetMode
|
|
2625
|
+
/>
|
|
2626
|
+
<Button label="Save" fullWidth onPress={handleSave} />
|
|
2627
|
+
</Sheet>
|
|
2628
|
+
|
|
2629
|
+
// Low-level alternative: SheetTextInput
|
|
2630
|
+
<Sheet
|
|
2631
|
+
open={open}
|
|
2632
|
+
onClose={() => setOpen(false)}
|
|
2392
2633
|
title="Add note"
|
|
2393
|
-
subtitle="Keyboard handling is automatic with platform-optimized defaults"
|
|
2394
2634
|
>
|
|
2395
|
-
<SheetTextInput
|
|
2396
|
-
placeholder="Write your note..."
|
|
2635
|
+
<SheetTextInput
|
|
2636
|
+
placeholder="Write your note..."
|
|
2397
2637
|
multiline
|
|
2398
|
-
style={{
|
|
2399
|
-
borderWidth: 1,
|
|
2638
|
+
style={{
|
|
2639
|
+
borderWidth: 1,
|
|
2400
2640
|
borderColor: '#ddd',
|
|
2401
2641
|
borderRadius: 8,
|
|
2402
2642
|
padding: 12,
|
|
2403
2643
|
minHeight: 80,
|
|
2404
|
-
}}
|
|
2644
|
+
}}
|
|
2405
2645
|
/>
|
|
2406
2646
|
<Button label="Save" fullWidth style={{ marginTop: 12 }} onPress={() => setOpen(false)} />
|
|
2407
2647
|
</Sheet>
|
|
@@ -2430,7 +2670,7 @@ const [open, setOpen] = useState(false)
|
|
|
2430
2670
|
|
|
2431
2671
|
**Keyboard handling notes:**
|
|
2432
2672
|
- No `KeyboardAvoidingView` needed — `@gorhom/bottom-sheet` handles everything
|
|
2433
|
-
-
|
|
2673
|
+
- Use `<Input sheetMode />` (preferred) or `SheetTextInput` (low-level) for text inputs inside the sheet
|
|
2434
2674
|
- Default `keyboardBehavior="interactive"` works on both platforms
|
|
2435
2675
|
- Default `android_keyboardInputMode="adjustPan"` fixes the transparent gap that occurs with `adjustResize` when keyboard dismisses
|
|
2436
2676
|
- `enableBlurKeyboardOnGesture={true}` (default) dismisses keyboard when dragging sheet
|
|
@@ -2694,7 +2934,6 @@ dismiss(id)
|
|
|
2694
2934
|
| visible | `boolean` | required | Controls dialog visibility |
|
|
2695
2935
|
| title | `string` | required | Dialog heading |
|
|
2696
2936
|
| subtitle | `string` | — | Secondary text below title |
|
|
2697
|
-
| description | `string` | — | **Deprecated** — use `subtitle` instead |
|
|
2698
2937
|
| confirmLabel | `string` | `'Confirm'` | Confirm button text |
|
|
2699
2938
|
| cancelLabel | `string` | `'Cancel'` | Cancel button text |
|
|
2700
2939
|
| confirmVariant | `'primary' \| 'destructive'` | `'primary'` | Use `'destructive'` for delete/remove actions |
|
|
@@ -2704,12 +2943,15 @@ dismiss(id)
|
|
|
2704
2943
|
| onCancel | `() => void` | required | Called when cancel is tapped or backdrop pressed |
|
|
2705
2944
|
|
|
2706
2945
|
**Notes:**
|
|
2707
|
-
- Powered by `@gorhom/bottom-sheet` with `enableDynamicSizing`
|
|
2946
|
+
- Powered by `@gorhom/bottom-sheet` `BottomSheetModal` with `enableDynamicSizing` (v12 refactor — `present()` / `dismiss()` driven, lazy-mounted, no `index={-1}` + `snapToIndex(0)` timing traps)
|
|
2947
|
+
- `topInset={insets.top}` applied automatically — dialog never crosses the notch / status bar
|
|
2948
|
+
- Internal `wasOpened` ref prevents `dismiss()` being called before the sheet is mounted (race-condition fix from v12.1)
|
|
2949
|
+
- A stable `name` prop (from `useId()`) is passed to `BottomSheetModal` for correct gorhom modal registry behavior when multiple modals are open
|
|
2708
2950
|
- Buttons are full-width, stacked vertically (confirm on top)
|
|
2709
2951
|
- Cancel shows X icon, confirm shows check (primary) or trash-2 (destructive) icon
|
|
2710
2952
|
- Swipe down or backdrop press calls `onCancel`
|
|
2711
2953
|
|
|
2712
|
-
**Haptics:** `
|
|
2954
|
+
**Haptics:** `impactMedium` on open.
|
|
2713
2955
|
|
|
2714
2956
|
**Examples:**
|
|
2715
2957
|
```tsx
|
|
@@ -2717,7 +2959,7 @@ dismiss(id)
|
|
|
2717
2959
|
<ConfirmDialog
|
|
2718
2960
|
visible={showDelete}
|
|
2719
2961
|
title="Delete transaction?"
|
|
2720
|
-
|
|
2962
|
+
subtitle="$45.000 · Mercado · 12 mar · This action cannot be undone."
|
|
2721
2963
|
confirmLabel="Delete"
|
|
2722
2964
|
confirmVariant="destructive"
|
|
2723
2965
|
onConfirm={handleDelete}
|
|
@@ -2728,7 +2970,7 @@ dismiss(id)
|
|
|
2728
2970
|
<ConfirmDialog
|
|
2729
2971
|
visible={showConfirm}
|
|
2730
2972
|
title="Send payment?"
|
|
2731
|
-
|
|
2973
|
+
subtitle={`Send $${amount} to ${recipientName}`}
|
|
2732
2974
|
confirmLabel="Send"
|
|
2733
2975
|
onConfirm={handleSend}
|
|
2734
2976
|
onCancel={() => setShowConfirm(false)}
|
|
@@ -2738,7 +2980,7 @@ dismiss(id)
|
|
|
2738
2980
|
<ConfirmDialog
|
|
2739
2981
|
visible={showDiscard}
|
|
2740
2982
|
title="Discard changes?"
|
|
2741
|
-
|
|
2983
|
+
subtitle="All unsaved changes will be lost."
|
|
2742
2984
|
confirmLabel="Discard"
|
|
2743
2985
|
confirmVariant="destructive"
|
|
2744
2986
|
onConfirm={() => { setShowDiscard(false); goBack() }}
|
|
@@ -2769,7 +3011,7 @@ dismiss(id)
|
|
|
2769
3011
|
| leftIconColor | `string` | — | Override left icon color (default: `foreground`) |
|
|
2770
3012
|
| rightIconColor | `string` | — | Override right icon color (default: `foregroundMuted`) |
|
|
2771
3013
|
| variant | `'plain' \| 'card'` | `'plain'` | `plain`: no background. `card`: surface with border and shadow |
|
|
2772
|
-
| showChevron | `boolean` | `false` | Right-pointing chevron. Ignored when `rightRender` is set |
|
|
3014
|
+
| showChevron | `boolean` | `false` | Right-pointing chevron. Ignored when `rightActions`, `rightRender`, or `rightIcon` is set |
|
|
2773
3015
|
| showSeparator | `boolean` | `false` | Hairline separator at bottom. Useful for stacking plain items |
|
|
2774
3016
|
| onPress | `() => void` | — | Makes row pressable (scale animation + haptics) |
|
|
2775
3017
|
| disabled | `boolean` | — | Reduces opacity to 0.45 |
|
|
@@ -2778,13 +3020,12 @@ dismiss(id)
|
|
|
2778
3020
|
| subtitleStyle | `TextStyle` | — | Override subtitle text style |
|
|
2779
3021
|
| subtitleNumberOfLines | `number` | `2` | Max lines for subtitle before truncation |
|
|
2780
3022
|
| captionStyle | `TextStyle` | — | Override caption text style |
|
|
2781
|
-
|
|
|
2782
|
-
| trailing | `string \| ReactNode` | — | **Deprecated** — use `rightRender` |
|
|
3023
|
+
| rightActions | `ReactNode[]` | — | Multiple action buttons on the right with 8pt gap. Takes precedence over `rightRender` |
|
|
2783
3024
|
|
|
2784
3025
|
**Slots:**
|
|
2785
3026
|
- `leftRender` / `leftIcon` — 44×44pt fixed container, centered. Good for Avatar, icons, thumbnails
|
|
2786
3027
|
- `rightRender` / `rightIcon` — max 160pt wide, right-aligned. Good for Badge, price text, Switch
|
|
2787
|
-
- `showChevron` — `›` chevron, 24pt, `foregroundMuted` color. Only shows when `rightRender`
|
|
3028
|
+
- `showChevron` — `›` chevron, 24pt, `foregroundMuted` color. Only shows when `rightRender`, `rightActions`, and `rightIcon` are absent
|
|
2788
3029
|
|
|
2789
3030
|
**Separator inset:** Aligns to text block — `marginLeft` adjusts to skip over left slot when present.
|
|
2790
3031
|
|
|
@@ -3077,7 +3318,7 @@ dismiss(id)
|
|
|
3077
3318
|
|
|
3078
3319
|
**Import:** `import { Chip, ChipGroup } from '@retray-dev/ui-kit'`
|
|
3079
3320
|
|
|
3080
|
-
**When to use:** Inline filter options, quick selections, multi-select toggles. Use `Chip` for standalone custom logic; use `ChipGroup` for managed selection (single or multi).
|
|
3321
|
+
**When to use:** Inline filter options, quick selections, multi-select toggles. Use `Chip` for standalone custom logic; use `ChipGroup` for managed selection (single or multi). **ChipGroup vs CategoryStrip:** ChipGroup wraps to multiple rows and supports disabled items — best for filter tags, feature toggles, and tray-style selections. CategoryStrip scrolls horizontally and supports badge counts — best for top-of-screen category browsers.
|
|
3081
3322
|
|
|
3082
3323
|
**`Chip` Props:**
|
|
3083
3324
|
|
|
@@ -3149,7 +3390,7 @@ const [categories, setCategories] = useState<number[]>([1, 3])
|
|
|
3149
3390
|
|
|
3150
3391
|
**Import:** `import { CategoryStrip } from '@retray-dev/ui-kit'`
|
|
3151
3392
|
|
|
3152
|
-
**When to use:** Horizontal scrollable filter/category bar at the top of browse screens — marketplace categories, content type filters, location tabs. Airbnb-style pill chips that scroll horizontally.
|
|
3393
|
+
**When to use:** Horizontal scrollable filter/category bar at the top of browse screens — marketplace categories, content type filters, location tabs. Airbnb-style pill chips that scroll horizontally. **CategoryStrip vs ChipGroup:** CategoryStrip scrolls horizontally and supports badge counts — best for top-of-screen category browsers. ChipGroup wraps to multiple rows and supports disabled items — best for filter tags and tray-style selections.
|
|
3153
3394
|
|
|
3154
3395
|
| Prop | Type | Default | Notes |
|
|
3155
3396
|
|------|------|---------|-------|
|
|
@@ -3274,7 +3515,7 @@ const [categories, setCategories] = useState<number[]>([1, 3])
|
|
|
3274
3515
|
| Prop | Type | Default | Notes |
|
|
3275
3516
|
|------|------|---------|-------|
|
|
3276
3517
|
| label | `string \| ReactNode` | required | Left side. Strings auto-styled in `foregroundMuted` caption |
|
|
3277
|
-
| value | `string` | required | Right side value
|
|
3518
|
+
| value | `string \| number \| ReactNode` | required | Right side value. Strings and numbers auto-styled in `Sohne-SemiBold`; pass `ReactNode` for custom content |
|
|
3278
3519
|
| separator | `'dotted' \| 'solid' \| 'dashed' \| 'none'` | `'dotted'` | Fill line between label and value |
|
|
3279
3520
|
| labelWeight | `'normal' \| 'medium' \| 'semibold' \| 'bold'` | `'normal'` | Font weight of label text |
|
|
3280
3521
|
| valueColor | `string` | — | Override value text color (hex or theme token value) |
|
|
@@ -3402,22 +3643,22 @@ const [period, setPeriod] = useState({
|
|
|
3402
3643
|
|
|
3403
3644
|
**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.
|
|
3404
3645
|
|
|
3405
|
-
**Extends:** `TouchableOpacityProps` (all native props pass through except `activeOpacity`)
|
|
3406
|
-
|
|
3407
3646
|
| Prop | Type | Default | Notes |
|
|
3408
3647
|
|------|------|---------|-------|
|
|
3409
3648
|
| children | `ReactNode` | required | Content to render inside the pressable |
|
|
3410
3649
|
| onPress | `() => void` | — | Press handler |
|
|
3411
3650
|
| pressScale | `number` | `0.98` | Scale value on press (MediaCard-style) |
|
|
3412
|
-
| bounciness | `number` | `4` | Spring bounciness on release |
|
|
3413
3651
|
| haptics | `boolean` | `true` | Enable haptic feedback on press |
|
|
3414
3652
|
| hoverScale | `number` | `1.02` | Hover scale (web only). Set to `1` to disable |
|
|
3415
3653
|
| disabled | `boolean` | `false` | Disable interaction |
|
|
3654
|
+
| accessibilityRole | `AccessibilityRole` | `'button'` | Override the accessibility role for screen readers |
|
|
3655
|
+
| accessibilityState | `Record<string, unknown>` | `{ disabled: !!disabled }` | Accessibility state for selected/expanded/checked |
|
|
3656
|
+
| accessibilityLabel | `string` | — | Accessibility label for screen readers |
|
|
3416
3657
|
| style | `ViewStyle` | — | Animated wrapper style |
|
|
3417
3658
|
|
|
3418
3659
|
**Behavior:**
|
|
3419
3660
|
- Press: springs to `pressScale` (default 0.98) with `speed: 40, bounciness: 0`
|
|
3420
|
-
- Release: springs back to 1.0 with `speed: 40
|
|
3661
|
+
- Release: springs back to 1.0 with `speed: 40`
|
|
3421
3662
|
- Web: optional hover scale (default 1.02)
|
|
3422
3663
|
- Haptics: `impactLight` on press (unless `haptics={false}`)
|
|
3423
3664
|
|
|
@@ -3436,7 +3677,7 @@ const [period, setPeriod] = useState({
|
|
|
3436
3677
|
</Pressable>
|
|
3437
3678
|
|
|
3438
3679
|
// Wrapping complex layout
|
|
3439
|
-
<Pressable onPress={handleSelect} pressScale={0.96}
|
|
3680
|
+
<Pressable onPress={handleSelect} pressScale={0.96}>
|
|
3440
3681
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, padding: 16 }}>
|
|
3441
3682
|
<Avatar src={user.avatar} size="md" />
|
|
3442
3683
|
<View style={{ flex: 1 }}>
|
|
@@ -3453,7 +3694,7 @@ const [period, setPeriod] = useState({
|
|
|
3453
3694
|
</Pressable>
|
|
3454
3695
|
|
|
3455
3696
|
// Custom press scale (deeper press)
|
|
3456
|
-
<Pressable onPress={handlePress} pressScale={0.92}
|
|
3697
|
+
<Pressable onPress={handlePress} pressScale={0.92}>
|
|
3457
3698
|
{/* content */}
|
|
3458
3699
|
</Pressable>
|
|
3459
3700
|
```
|
|
@@ -3725,6 +3966,76 @@ export default function TabLayout() {
|
|
|
3725
3966
|
|
|
3726
3967
|
---
|
|
3727
3968
|
|
|
3969
|
+
### Stats
|
|
3970
|
+
|
|
3971
|
+
**Import:** `import { Stats } from '@retray-dev/ui-kit'`
|
|
3972
|
+
|
|
3973
|
+
**When to use:** Dashboards and analytics screens — single-stat card with a large value, icon, label, and optional description. Tappable cards wire haptics automatically.
|
|
3974
|
+
|
|
3975
|
+
| Prop | Type | Default | Notes |
|
|
3976
|
+
|------|------|---------|-------|
|
|
3977
|
+
| value | `string` | required | Large display value, e.g. `"$12,450"` or `"847"` |
|
|
3978
|
+
| label | `string` | required | Label below the value, e.g. `"Monthly Revenue"` |
|
|
3979
|
+
| description | `string` | — | Third line — smaller, muted text for trend/context |
|
|
3980
|
+
| icon | `React.ReactNode` | — | Custom icon node |
|
|
3981
|
+
| iconName | `string` | — | Icon from `@expo/vector-icons` (left of value, `colors.primary`) |
|
|
3982
|
+
| iconColor | `string` | `colors.primary` | Override icon color |
|
|
3983
|
+
| variant | `'elevated' \| 'outlined' \| 'filled'` | `'elevated'` | Card surface variant |
|
|
3984
|
+
| onPress | `() => void` | — | Makes the card pressable (haptic `impactLight()`) |
|
|
3985
|
+
| style | `ViewStyle` | — | — |
|
|
3986
|
+
| accessibilityLabel | `string` | — | — |
|
|
3987
|
+
|
|
3988
|
+
**Design notes:** Value uses `Sohne-Bold` at 28px. Label uses `Sohne-Regular` at 13px in `foregroundSubtle`. Description uses `Sohne-Regular` at 12px in `foregroundMuted`. Icon renders at 22px, 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
|
+
|
|
3990
|
+
**`Stats.Group`** — horizontal layout wrapper that distributes children equally:
|
|
3991
|
+
|
|
3992
|
+
| Prop | Type | Default | Notes |
|
|
3993
|
+
|------|------|---------|-------|
|
|
3994
|
+
| children | `React.ReactNode` | required | `Stats` components to arrange horizontally |
|
|
3995
|
+
| gap | `number` | `s(12)` | Spacing between cards |
|
|
3996
|
+
| style | `ViewStyle` | — | — |
|
|
3997
|
+
|
|
3998
|
+
**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.
|
|
3999
|
+
|
|
4000
|
+
**Example:**
|
|
4001
|
+
```tsx
|
|
4002
|
+
// Standalone
|
|
4003
|
+
<Stats
|
|
4004
|
+
value="$12,450"
|
|
4005
|
+
label="Monthly Revenue"
|
|
4006
|
+
description="+12% from last month"
|
|
4007
|
+
iconName="trending-up"
|
|
4008
|
+
/>
|
|
4009
|
+
|
|
4010
|
+
// 3-up — short values only, no description
|
|
4011
|
+
<Stats.Group>
|
|
4012
|
+
<Stats value="847" label="Users" variant="elevated" />
|
|
4013
|
+
<Stats value="98%" label="Uptime" variant="outlined" />
|
|
4014
|
+
<Stats value="12" label="Alerts" variant="filled" />
|
|
4015
|
+
</Stats.Group>
|
|
4016
|
+
|
|
4017
|
+
// 2-up with icons, long values, and description
|
|
4018
|
+
<Stats.Group gap={s(16)}>
|
|
4019
|
+
<Stats
|
|
4020
|
+
value="$8,200"
|
|
4021
|
+
label="Spent"
|
|
4022
|
+
description="This month"
|
|
4023
|
+
iconName="credit-card"
|
|
4024
|
+
variant="elevated"
|
|
4025
|
+
onPress={() => navigateToBilling()}
|
|
4026
|
+
/>
|
|
4027
|
+
<Stats
|
|
4028
|
+
value="$2,150"
|
|
4029
|
+
label="Saved"
|
|
4030
|
+
description="12% of budget"
|
|
4031
|
+
iconName="trending-up"
|
|
4032
|
+
variant="outlined"
|
|
4033
|
+
/>
|
|
4034
|
+
</Stats.Group>
|
|
4035
|
+
```
|
|
4036
|
+
|
|
4037
|
+
---
|
|
4038
|
+
|
|
3728
4039
|
### ErrorBoundary
|
|
3729
4040
|
|
|
3730
4041
|
**Import:** `import { ErrorBoundary } from '@retray-dev/ui-kit'`
|
|
@@ -3920,6 +4231,7 @@ import { HolographicCard, FOIL_PRESETS } from '@retray-dev/ui-kit/HolographicCar
|
|
|
3920
4231
|
| onChange | `(uri: string \| null) => void` | — | Called with selected URI after picker completes |
|
|
3921
4232
|
| loading | `boolean` | `false` | Show spinner overlay (e.g. while uploading to server) |
|
|
3922
4233
|
| placeholder | `string` | `'Tap to add image'` | Text shown when no image selected |
|
|
4234
|
+
| showPlaceholderText | `boolean` | `true` | Whether to show the placeholder text. Use `false` for compact/avatar variants |
|
|
3923
4235
|
| width | `number` | — | Width of the upload area. Defaults to full width |
|
|
3924
4236
|
| height | `number` | `200` | Height of the upload area |
|
|
3925
4237
|
| borderRadius | `number` | `RADIUS.lg` | — |
|
|
@@ -3949,18 +4261,72 @@ const handleChange = async (uri: string | null) => {
|
|
|
3949
4261
|
height={200}
|
|
3950
4262
|
/>
|
|
3951
4263
|
|
|
3952
|
-
// Avatar upload (square)
|
|
4264
|
+
// Avatar upload (square, no text)
|
|
3953
4265
|
<ImageUpload
|
|
3954
4266
|
value={avatarUri}
|
|
3955
4267
|
onChange={setAvatarUri}
|
|
3956
4268
|
width={100}
|
|
3957
4269
|
height={100}
|
|
3958
4270
|
borderRadius={50}
|
|
4271
|
+
showPlaceholderText={false}
|
|
3959
4272
|
/>
|
|
3960
4273
|
```
|
|
3961
4274
|
|
|
3962
4275
|
---
|
|
3963
4276
|
|
|
4277
|
+
### IconPicker
|
|
4278
|
+
|
|
4279
|
+
**Import:** `import { IconPicker } from '@retray-dev/ui-kit'`
|
|
4280
|
+
|
|
4281
|
+
**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.
|
|
4282
|
+
|
|
4283
|
+
**Requires:** `@gorhom/bottom-sheet` (already a peer dependency of the UI kit).
|
|
4284
|
+
|
|
4285
|
+
| Prop | Type | Default | Notes |
|
|
4286
|
+
|------|------|---------|-------|
|
|
4287
|
+
| value | `string \| null` | — | Currently selected icon name |
|
|
4288
|
+
| onChange | `(iconName: string) => void` | — | Called when an icon is selected |
|
|
4289
|
+
| label | `string` | — | Optional label above the trigger |
|
|
4290
|
+
| error | `string` | — | Error message (red border + helper text) |
|
|
4291
|
+
| hint | `string` | — | Hint text below trigger |
|
|
4292
|
+
| disabled | `boolean` | `false` | Prevents opening |
|
|
4293
|
+
| numColumns | `number` | `6` | Icons per row in the sheet grid |
|
|
4294
|
+
| gap | `number` | `6` | Gap between cells (dp) |
|
|
4295
|
+
| style | `ViewStyle` | — | — |
|
|
4296
|
+
|
|
4297
|
+
**Examples:**
|
|
4298
|
+
```tsx
|
|
4299
|
+
const [icon, setIcon] = useState<string | null>(null)
|
|
4300
|
+
|
|
4301
|
+
// Basic usage
|
|
4302
|
+
<IconPicker
|
|
4303
|
+
label="Icon"
|
|
4304
|
+
value={icon}
|
|
4305
|
+
onChange={setIcon}
|
|
4306
|
+
/>
|
|
4307
|
+
|
|
4308
|
+
// With validation error
|
|
4309
|
+
<IconPicker
|
|
4310
|
+
label="Icon"
|
|
4311
|
+
value={icon}
|
|
4312
|
+
onChange={setIcon}
|
|
4313
|
+
error={!icon ? 'Please select an icon' : undefined}
|
|
4314
|
+
/>
|
|
4315
|
+
```
|
|
4316
|
+
|
|
4317
|
+
**Notes:**
|
|
4318
|
+
- 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.
|
|
4319
|
+
- 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.
|
|
4320
|
+
- **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.
|
|
4321
|
+
- 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.
|
|
4322
|
+
- Grid cells show only icons — clean, fast visual scanning. Cell size adapts to container width.
|
|
4323
|
+
- Sheet uses `enableDynamicSizing` with `maxDynamicContentSize` (70% screen height) — height auto-fits content up to that cap. `topInset={insets.top}` is applied automatically.
|
|
4324
|
+
- Category strip is a horizontal `ScrollView` of pill-shaped chips. The grid is rendered in rows inside a `BottomSheetScrollView`.
|
|
4325
|
+
- Selection closes the sheet immediately and resets category to "Todos".
|
|
4326
|
+
- Haptics: medium impact on open, selection on icon tap.
|
|
4327
|
+
|
|
4328
|
+
---
|
|
4329
|
+
|
|
3964
4330
|
### useConfirmDialog
|
|
3965
4331
|
|
|
3966
4332
|
**Import:** `import { useConfirmDialog } from '@retray-dev/ui-kit'`
|
|
@@ -4063,6 +4429,21 @@ import { renderIcon } from '@retray-dev/ui-kit'
|
|
|
4063
4429
|
const icon = renderIcon('check', 18, colors.primary)
|
|
4064
4430
|
```
|
|
4065
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.
|
|
4446
|
+
|
|
4066
4447
|
### `iconName` props on components
|
|
4067
4448
|
|
|
4068
4449
|
All components with icon slots accept `iconName` — auto-resolved size and color:
|