@retray-dev/ui-kit 5.2.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +500 -140
- package/EXAMPLES.md +666 -0
- package/README.md +3 -3
- package/dist/index.d.mts +253 -49
- package/dist/index.d.ts +253 -49
- package/dist/index.js +955 -610
- package/dist/index.mjs +886 -552
- package/package.json +9 -3
- package/src/components/Accordion/Accordion.tsx +31 -4
- package/src/components/AlertBanner/AlertBanner.tsx +16 -33
- package/src/components/Avatar/Avatar.tsx +21 -7
- package/src/components/Button/Button.tsx +34 -13
- package/src/components/ButtonGroup/ButtonGroup.tsx +60 -0
- package/src/components/ButtonGroup/index.ts +1 -0
- package/src/components/Card/Card.tsx +12 -9
- package/src/components/Chip/Chip.tsx +8 -1
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +4 -4
- package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +38 -5
- package/src/components/DetailRow/DetailRow.tsx +140 -0
- package/src/components/DetailRow/index.ts +1 -0
- package/src/components/EmptyState/EmptyState.tsx +21 -6
- package/src/components/Input/Input.tsx +21 -10
- package/src/components/LabelValue/LabelValue.tsx +25 -4
- package/src/components/ListItem/ListItem.tsx +14 -8
- package/src/components/MediaCard/MediaCard.tsx +1 -0
- package/src/components/MenuItem/MenuItem.tsx +206 -0
- package/src/components/MenuItem/index.ts +2 -0
- package/src/components/MonthPicker/MonthPicker.tsx +18 -6
- package/src/components/Select/Select.tsx +1 -1
- package/src/components/Separator/Separator.tsx +2 -0
- package/src/components/Sheet/Sheet.tsx +165 -36
- package/src/components/Sheet/index.ts +1 -1
- package/src/components/Tabs/Tabs.tsx +4 -4
- package/src/components/Textarea/Textarea.tsx +66 -29
- package/src/components/Toast/Toast.tsx +41 -267
- package/src/components/Toast/index.ts +1 -2
- package/src/components/Toggle/Toggle.tsx +2 -2
- package/src/index.ts +6 -0
- package/src/theme/colors.ts +3 -0
- package/src/theme/types.ts +11 -0
- package/src/tokens.ts +4 -4
- package/src/utils/typography.ts +24 -0
package/COMPONENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @retray-dev/ui-kit — Component Reference (
|
|
1
|
+
# @retray-dev/ui-kit — Component Reference (v6.0.0)
|
|
2
2
|
|
|
3
3
|
This file is the AI reference for this package. It is shipped inside the npm package so consuming projects can import it into their `CLAUDE.md` with:
|
|
4
4
|
|
|
@@ -36,11 +36,11 @@ export default function App() {
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
**Provider order is mandatory:**
|
|
39
|
-
- `SafeAreaProvider` must be outermost — required by `
|
|
39
|
+
- `SafeAreaProvider` must be outermost — required by `@gorhom/bottom-sheet`
|
|
40
40
|
- `initialMetrics={initialWindowMetrics}` is required on Android to avoid a "No safe area value available" crash on first render
|
|
41
41
|
- `GestureHandlerRootView` must wrap everything — required by `@gorhom/bottom-sheet`
|
|
42
42
|
- `BottomSheetModalProvider` must be inside `GestureHandlerRootView`
|
|
43
|
-
- `ToastProvider`
|
|
43
|
+
- `ToastProvider` wraps children and renders `Toaster` (from `sonner-native`) internally
|
|
44
44
|
|
|
45
45
|
## Typography — Poppins (Required)
|
|
46
46
|
|
|
@@ -136,6 +136,9 @@ These are the only values you need to supply when customizing the theme. The lib
|
|
|
136
136
|
| `successForeground` | `#ffffff` | `#ffffff` | Text/icon on success backgrounds |
|
|
137
137
|
| `warning` | `#e67e00` | `#f57c00` | Warning / caution states |
|
|
138
138
|
| `warningForeground` | `#ffffff` | `#ffffff` | Text/icon on warning backgrounds |
|
|
139
|
+
| `overlay` *(optional)* | `rgba(0,0,0,0.45)` | `rgba(0,0,0,0.45)` | Backdrop/overlay color behind sheets and dialogs |
|
|
140
|
+
| `accent` *(optional)* | same as `primary` | same as `primary` | Secondary brand accent (e.g. Airbnb coral). Falls back to `primary` |
|
|
141
|
+
| `accentForeground` *(optional)* | same as `primaryForeground` | same as `primaryForeground` | Text/icon on accent backgrounds. Falls back to `primaryForeground` |
|
|
139
142
|
|
|
140
143
|
### Derived Tokens (ResolvedColors) — read-only via useTheme().colors
|
|
141
144
|
|
|
@@ -155,6 +158,9 @@ The full palette components consume. Never supply these directly — they are co
|
|
|
155
158
|
| `warningBorder` | `warning` @ 30% | Warning banner border |
|
|
156
159
|
| `ring` | `= primary` | Focus ring color (always matches primary) |
|
|
157
160
|
| `input` | `= border` | Input field border (always matches border) |
|
|
161
|
+
| `overlay` | `overlay` token or `rgba(0,0,0,0.45)` | Backdrop behind sheets and dialogs |
|
|
162
|
+
| `accentResolved` | `accent` token or `= primary` | Resolved accent color — always present |
|
|
163
|
+
| `accentForegroundResolved` | `accentForeground` token or `= primaryForeground` | Resolved text on accent — always present |
|
|
158
164
|
|
|
159
165
|
**Usage example — building a custom component using derived tokens:**
|
|
160
166
|
```tsx
|
|
@@ -231,10 +237,10 @@ import { SPACING, ICON_SIZES, RADIUS, SHADOWS, BREAKPOINTS, TYPOGRAPHY } from '@
|
|
|
231
237
|
| `none` | 0 | No rounding |
|
|
232
238
|
| `xs` | 4 | Micro chips, tags |
|
|
233
239
|
| `sm` | 8 | Inputs, Textarea, Select, Checkbox |
|
|
234
|
-
| `md` | 14 | Cards, MediaCard, AlertBanner, Toast, EmptyState |
|
|
240
|
+
| `md` | 14 | Cards, Buttons (all variants), MediaCard, AlertBanner, Toast, EmptyState |
|
|
235
241
|
| `lg` | 20 | Sheet top corners |
|
|
236
|
-
| `xl` | 32 |
|
|
237
|
-
| `full` | 9999 |
|
|
242
|
+
| `xl` | 32 | Large decorative elements |
|
|
243
|
+
| `full` | 9999 | IconButton (circle), CategoryStrip chips |
|
|
238
244
|
|
|
239
245
|
**Types:** `Radius`, `RadiusKey`
|
|
240
246
|
|
|
@@ -286,7 +292,7 @@ All components use these tokens for text styling. Import and use in custom compo
|
|
|
286
292
|
| `caption-sm` | 13 | 400 | 16 | 0 | Timestamps, metadata |
|
|
287
293
|
| `badge-text` | 11 | 600 | 13 | 0 | Badge labels, small tags |
|
|
288
294
|
| `micro-label` | 12 | 700 | 16 | 0 | Micro labels, overlines |
|
|
289
|
-
| `uppercase-tag` |
|
|
295
|
+
| `uppercase-tag` | 10 | 700 | 13 | 0.8 | Uppercase decorative tags (auto-uppercase) |
|
|
290
296
|
| `button-lg` | 16 | 500 | 20 | 0 | Button labels (md/lg size) |
|
|
291
297
|
| `button-sm` | 14 | 500 | 18 | 0 | Button labels (sm size) |
|
|
292
298
|
|
|
@@ -344,6 +350,59 @@ const styles = StyleSheet.create({
|
|
|
344
350
|
|
|
345
351
|
---
|
|
346
352
|
|
|
353
|
+
## Migration Guide: v5 → v6
|
|
354
|
+
|
|
355
|
+
### New Components
|
|
356
|
+
|
|
357
|
+
**`MenuItem`** — Navigation row with icon, label, and optional right slot (`rightRender`). Replaces ad-hoc `ListItem` usage for settings/nav menus. Zero horizontal padding by design — consumer controls spacing.
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
import { MenuItem } from '@retray-dev/ui-kit'
|
|
361
|
+
// variants: 'plain' (default) | 'card'
|
|
362
|
+
<MenuItem label="Profile" iconName="user" onPress={() => {}} />
|
|
363
|
+
<MenuItem label="Notifications" iconName="bell" rightRender={<Switch value={on} onValueChange={setOn} />} showChevron={false} onPress={() => {}} />
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Breaking Changes
|
|
367
|
+
|
|
368
|
+
**Sheet keyboard defaults changed:**
|
|
369
|
+
| Prop | v5 default | v6 default |
|
|
370
|
+
|------|-----------|-----------|
|
|
371
|
+
| `keyboardBehavior` | `'fillParent'` (Android) / `'interactive'` (iOS) | `'interactive'` (both) |
|
|
372
|
+
| `android_keyboardInputMode` | `'adjustResize'` | `'adjustPan'` |
|
|
373
|
+
|
|
374
|
+
If you relied on `adjustResize`, set it explicitly: `android_keyboardInputMode="adjustResize"`.
|
|
375
|
+
|
|
376
|
+
**Toast API simplified:**
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
379
|
+
// v5 — hook-only API
|
|
380
|
+
const { toast } = useToast()
|
|
381
|
+
toast.success('Done')
|
|
382
|
+
|
|
383
|
+
// v6 — direct import preferred (hook still works for compat)
|
|
384
|
+
import { toast } from '@retray-dev/ui-kit'
|
|
385
|
+
toast.success('Done')
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### New Theme Tokens
|
|
389
|
+
|
|
390
|
+
Three optional `ThemeColors` tokens — fall back gracefully if omitted:
|
|
391
|
+
|
|
392
|
+
| Token | Default | Purpose |
|
|
393
|
+
|-------|---------|---------|
|
|
394
|
+
| `overlay` | `rgba(0,0,0,0.45)` | Backdrop color behind sheets and dialogs |
|
|
395
|
+
| `accent` | `= primary` | Secondary brand accent color |
|
|
396
|
+
| `accentForeground` | `= primaryForeground` | Text on accent backgrounds |
|
|
397
|
+
|
|
398
|
+
Access resolved values via `useTheme().colors.overlay`, `.accentResolved`, `.accentForegroundResolved`.
|
|
399
|
+
|
|
400
|
+
### Token Corrections
|
|
401
|
+
|
|
402
|
+
- `TYPOGRAPHY['uppercase-tag']` — size corrected to `10` (was documented as `8`, actual value was always `10`)
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
347
406
|
## Components
|
|
348
407
|
|
|
349
408
|
---
|
|
@@ -381,7 +440,7 @@ const styles = StyleSheet.create({
|
|
|
381
440
|
| `caption-sm` | 13 | 400 | `foregroundMuted` | Timestamps, metadata, helper text |
|
|
382
441
|
| `badge-text` | 11 | 600 | `foreground` | Badge labels, small status tags |
|
|
383
442
|
| `micro-label` | 12 | 700 | `foreground` | Micro labels, overlines |
|
|
384
|
-
| `uppercase-tag` |
|
|
443
|
+
| `uppercase-tag` | 10 | 700 | `foreground` | Decorative uppercase category labels (auto-transforms text) |
|
|
385
444
|
| `button-lg` | 16 | 500 | `foreground` | Internal use in Button component (md/lg) |
|
|
386
445
|
| `button-sm` | 14 | 500 | `foreground` | Internal use in Button component (sm) |
|
|
387
446
|
|
|
@@ -456,7 +515,7 @@ const styles = StyleSheet.create({
|
|
|
456
515
|
| `md` | 48px | 24px | 18px | `button-lg` (16pt/500) |
|
|
457
516
|
| `lg` | 56px | 28px | 20px | `button-lg` (16pt/500) |
|
|
458
517
|
|
|
459
|
-
**Shape:** `borderRadius: RADIUS.
|
|
518
|
+
**Shape:** `borderRadius: RADIUS.md = 14px` — Airbnb-aligned rounded rect on all variants. Exception: `IconButton` uses `RADIUS.full` (perfect circle).
|
|
460
519
|
|
|
461
520
|
**Animations:** Scale springs to 0.95 on `onPressIn`, back to 1.0 on `onPressOut`.
|
|
462
521
|
|
|
@@ -499,6 +558,67 @@ const styles = StyleSheet.create({
|
|
|
499
558
|
|
|
500
559
|
---
|
|
501
560
|
|
|
561
|
+
### ButtonGroup
|
|
562
|
+
|
|
563
|
+
**Import:** `import { ButtonGroup } from '@retray-dev/ui-kit'`
|
|
564
|
+
|
|
565
|
+
**When to use:** Auto-distribute space equally between buttons in horizontal or vertical layouts. Perfect for side-by-side CTAs (Cancel/Confirm, Back/Next) where both buttons should occupy equal width.
|
|
566
|
+
|
|
567
|
+
| Prop | Type | Default | Notes |
|
|
568
|
+
|------|------|---------|-------|
|
|
569
|
+
| children | `React.ReactNode` | required | Button components to lay out |
|
|
570
|
+
| gap | `number` | `12` | Spacing between buttons (in points) |
|
|
571
|
+
| vertical | `boolean` | `false` | Stack buttons vertically instead of horizontally |
|
|
572
|
+
| style | `ViewStyle` | — | Override container style |
|
|
573
|
+
|
|
574
|
+
**Behavior:** Automatically applies `flex: 1` to all direct children. Children share space equally regardless of label length.
|
|
575
|
+
|
|
576
|
+
**Mobile best practice:** When using 2 buttons with icons in a ButtonGroup, use `size="sm"` to prevent text clipping on narrow screens:
|
|
577
|
+
```tsx
|
|
578
|
+
// ✅ Good — Small size prevents overflow
|
|
579
|
+
<ButtonGroup>
|
|
580
|
+
<Button label="Cancel" variant="secondary" size="sm" iconName="x" onPress={handleCancel} />
|
|
581
|
+
<Button label="Confirm" size="sm" iconName="check" onPress={handleConfirm} />
|
|
582
|
+
</ButtonGroup>
|
|
583
|
+
|
|
584
|
+
// ❌ Avoid — Default size may clip text when icons present
|
|
585
|
+
<ButtonGroup>
|
|
586
|
+
<Button label="Cancel" variant="secondary" iconName="x" onPress={handleCancel} />
|
|
587
|
+
<Button label="Confirm" iconName="check" onPress={handleConfirm} />
|
|
588
|
+
</ButtonGroup>
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
**Examples:**
|
|
592
|
+
```tsx
|
|
593
|
+
// Horizontal pair (50%/50%)
|
|
594
|
+
<ButtonGroup>
|
|
595
|
+
<Button label="Cancel" variant="secondary" onPress={handleCancel} />
|
|
596
|
+
<Button label="Confirm" onPress={handleConfirm} />
|
|
597
|
+
</ButtonGroup>
|
|
598
|
+
|
|
599
|
+
// Custom gap
|
|
600
|
+
<ButtonGroup gap={8}>
|
|
601
|
+
<Button label="Back" variant="text" onPress={handleBack} />
|
|
602
|
+
<Button label="Next" onPress={handleNext} />
|
|
603
|
+
</ButtonGroup>
|
|
604
|
+
|
|
605
|
+
// Vertical stack
|
|
606
|
+
<ButtonGroup vertical gap={8}>
|
|
607
|
+
<Button label="Primary Action" onPress={handlePrimary} />
|
|
608
|
+
<Button label="Secondary Action" variant="secondary" onPress={handleSecondary} />
|
|
609
|
+
<Button label="Delete" variant="destructive" onPress={handleDelete} />
|
|
610
|
+
</ButtonGroup>
|
|
611
|
+
|
|
612
|
+
// Three buttons (33%/33%/33%)
|
|
613
|
+
<ButtonGroup gap={6}>
|
|
614
|
+
<Button label="1D" variant="secondary" size="sm" onPress={() => setRange('1d')} />
|
|
615
|
+
<Button label="1W" variant="secondary" size="sm" onPress={() => setRange('1w')} />
|
|
616
|
+
<Button label="1M" size="sm" onPress={() => setRange('1m')} />
|
|
617
|
+
</ButtonGroup>
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
502
622
|
### IconButton
|
|
503
623
|
|
|
504
624
|
**Import:** `import { IconButton } from '@retray-dev/ui-kit'`
|
|
@@ -682,6 +802,9 @@ const styles = StyleSheet.create({
|
|
|
682
802
|
| error | `string` | — | Error text below; turns border destructive (2px) |
|
|
683
803
|
| hint | `string` | — | Helper text below (hidden when `error` is set) |
|
|
684
804
|
| rows | `number` | `4` | Minimum row count — each row ≈ 30px. Sets `numberOfLines` |
|
|
805
|
+
| prefixIcon | `string` | — | Icon name from `@expo/vector-icons` rendered inside top-left corner |
|
|
806
|
+
| prefixIconNode | `ReactNode` | — | Custom icon node rendered top-left |
|
|
807
|
+
| prefixIconColor | `string` | — | Override prefix icon color. Defaults to `foregroundMuted` |
|
|
685
808
|
| containerStyle | `ViewStyle` | — | Outer container style |
|
|
686
809
|
| style | `TextStyle` | — | TextInput element style |
|
|
687
810
|
|
|
@@ -689,9 +812,9 @@ const styles = StyleSheet.create({
|
|
|
689
812
|
|
|
690
813
|
**Examples:**
|
|
691
814
|
```tsx
|
|
692
|
-
<Textarea label="Bio" placeholder="Tell us about yourself" rows={5} />
|
|
693
|
-
<Textarea label="Review" hint="Be honest and helpful" />
|
|
694
|
-
<Textarea label="Notes" error="Notes are required" rows={3} />
|
|
815
|
+
<Textarea label="Bio" placeholder="Tell us about yourself" rows={5} prefixIcon="user" />
|
|
816
|
+
<Textarea label="Review" hint="Be honest and helpful" prefixIcon="message-circle" />
|
|
817
|
+
<Textarea label="Notes" error="Notes are required" rows={3} prefixIcon="edit-3" />
|
|
695
818
|
```
|
|
696
819
|
|
|
697
820
|
---
|
|
@@ -776,6 +899,9 @@ const [amount, setAmount] = useState(0)
|
|
|
776
899
|
| prefix | `string` | `'$'` | Symbol prepended to formatted value |
|
|
777
900
|
| showDecimals | `boolean` | `false` | Show two decimal places with comma separator (e.g. `$25.000,00`) |
|
|
778
901
|
| textColor | `string` | — | Override text color. Defaults to `foreground` token |
|
|
902
|
+
| variant | `'hero' \| 'large' \| 'medium' \| 'small'` | — | Predefined size (48/32/18/14pt). Overrides default 56pt |
|
|
903
|
+
| autoScale | `boolean` | `false` | Enable `adjustsFontSizeToFit` — long values shrink to fit one line |
|
|
904
|
+
| maxFontSize | `number` | — | Max font size when `autoScale` true. Defaults to variant size or 56pt |
|
|
779
905
|
| style | `ViewStyle` | — | Outer container style |
|
|
780
906
|
|
|
781
907
|
**Format:** Dot (`.`) as thousands separator, comma (`,`) as decimal separator — Latin American / European format.
|
|
@@ -874,9 +1000,10 @@ const [amount, setAmount] = useState(0)
|
|
|
874
1000
|
|
|
875
1001
|
| Prop | Type | Default | Notes |
|
|
876
1002
|
|------|------|---------|-------|
|
|
877
|
-
| src | `string` | — | Image URI (remote or local) |
|
|
878
|
-
| fallback | `string` | — |
|
|
879
|
-
|
|
|
1003
|
+
| src | `string \| null` | — | Image URI (remote or local). `null` forces fallback |
|
|
1004
|
+
| fallback | `string` | — | Manual initials (max 2 chars, uppercased) |
|
|
1005
|
+
| fallbackText | `string` | — | Full name — extracts up to 2 initials (e.g. `"Julian Cruz"` → `"JC"`) |
|
|
1006
|
+
| size | `'sm' \| 'md' \| 'lg' \| 'xl' \| number` | `'md'` | Named size or custom diameter in points |
|
|
880
1007
|
| status | `'online' \| 'offline' \| 'busy' \| 'away'` | — | Status indicator dot, bottom-right |
|
|
881
1008
|
| style | `ViewStyle` | — | — |
|
|
882
1009
|
|
|
@@ -903,6 +1030,8 @@ const [amount, setAmount] = useState(0)
|
|
|
903
1030
|
<Avatar fallback="AN" size="md" />
|
|
904
1031
|
<Avatar src={user.avatar} fallback={user.initials} size="xl" status="online" />
|
|
905
1032
|
<Avatar fallback="BU" size="sm" status="busy" />
|
|
1033
|
+
<Avatar fallbackText="Julian Cruz" size="md" /> // → "JC"
|
|
1034
|
+
<Avatar src={avatarUrl} size={64} /> // custom numeric size
|
|
906
1035
|
|
|
907
1036
|
// In a ListItem
|
|
908
1037
|
<ListItem
|
|
@@ -1773,7 +1902,14 @@ const [tab, setTab] = useState('profile')
|
|
|
1773
1902
|
|
|
1774
1903
|
**AccordionItem type:**
|
|
1775
1904
|
```ts
|
|
1776
|
-
{
|
|
1905
|
+
{
|
|
1906
|
+
value: string
|
|
1907
|
+
trigger: string
|
|
1908
|
+
content: ReactNode
|
|
1909
|
+
iconName?: string // Icon name from @expo/vector-icons
|
|
1910
|
+
icon?: ReactNode // Custom icon node
|
|
1911
|
+
iconColor?: string // Override icon color (defaults to foregroundMuted)
|
|
1912
|
+
}
|
|
1777
1913
|
```
|
|
1778
1914
|
|
|
1779
1915
|
**Animation:** `react-native-reanimated` `withTiming` (220ms) for height and chevron rotation (180°). Press scale uses `withSpring`. Runs on UI thread at 60fps.
|
|
@@ -1787,11 +1923,13 @@ const [tab, setTab] = useState('profile')
|
|
|
1787
1923
|
{
|
|
1788
1924
|
value: 'q1',
|
|
1789
1925
|
trigger: 'What payment methods do you accept?',
|
|
1926
|
+
iconName: 'credit-card',
|
|
1790
1927
|
content: <Text variant="body-sm">We accept Visa, Mastercard, and bank transfers.</Text>,
|
|
1791
1928
|
},
|
|
1792
1929
|
{
|
|
1793
1930
|
value: 'q2',
|
|
1794
1931
|
trigger: 'Can I cancel my booking?',
|
|
1932
|
+
iconName: 'x-circle',
|
|
1795
1933
|
content: <Text variant="body-sm">Yes — free cancellation within 48 hours of booking.</Text>,
|
|
1796
1934
|
},
|
|
1797
1935
|
]}
|
|
@@ -1808,7 +1946,7 @@ const [tab, setTab] = useState('profile')
|
|
|
1808
1946
|
|
|
1809
1947
|
### Sheet
|
|
1810
1948
|
|
|
1811
|
-
**Import:** `import { Sheet, BottomSheetModalProvider } from '@retray-dev/ui-kit'`
|
|
1949
|
+
**Import:** `import { Sheet, SheetTextInput, BottomSheetModalProvider } from '@retray-dev/ui-kit'`
|
|
1812
1950
|
|
|
1813
1951
|
**When to use:** Bottom sheet for contextual actions, filters, pickers, or detail views that don't need a full screen. Auto-sizes to content — no snap points needed.
|
|
1814
1952
|
|
|
@@ -1825,158 +1963,156 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
|
|
|
1825
1963
|
| open | `boolean` | required | `true` presents the sheet, `false` dismisses it |
|
|
1826
1964
|
| onClose | `() => void` | required | Called on swipe-dismiss or backdrop press |
|
|
1827
1965
|
| title | `string` | — | Sheet heading |
|
|
1828
|
-
|
|
|
1966
|
+
| subtitle | `string` | — | Supporting text below title (replaces deprecated `description`) |
|
|
1967
|
+
| showCloseButton | `boolean` | `false` | Show X close button in the header |
|
|
1829
1968
|
| children | `ReactNode` | — | Sheet content |
|
|
1830
|
-
| style | `ViewStyle` | — | Inner content container style |
|
|
1831
|
-
|
|
|
1832
|
-
|
|
|
1969
|
+
| style | `ViewStyle` | — | Inner scroll/content container style |
|
|
1970
|
+
| contentStyle | `ViewStyle` | — | Outer content wrapper style |
|
|
1971
|
+
| scrollable | `boolean` | `false` | Wraps children in `BottomSheetScrollView` |
|
|
1972
|
+
| maxHeight | `number` | `~85% screen` | Caps sheet height (dp). Defaults to 85% of screen height. Enables scrolling when content overflows |
|
|
1973
|
+
| keyboardBehavior | `'extend' \| 'fillParent' \| 'interactive'` | `'interactive'` | How sheet responds to keyboard. `'interactive'` (default) offsets sheet by keyboard size — works on both platforms. `'fillParent'` extends to fill parent view. `'extend'` extends to max snap point |
|
|
1974
|
+
| keyboardBlurBehavior | `'none' \| 'restore'` | `'restore'` | What happens when keyboard dismisses: `'restore'` → return to pre-keyboard position (recommended), `'none'` → stay at keyboard-adjusted position |
|
|
1975
|
+
| enableBlurKeyboardOnGesture | `boolean` | `true` | Dismiss keyboard when user starts dragging sheet down (recommended for better UX) |
|
|
1976
|
+
| 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) |
|
|
1977
|
+
| footer | `ReactNode` | — | Sticky footer below scroll area (sticky above keyboard) |
|
|
1978
|
+
| snapPoints | `(string \| number)[]` | — | Optional snap points (e.g., `['50%', '85%']`). When omitted, uses dynamic sizing (auto-fits content) |
|
|
1833
1979
|
|
|
1834
1980
|
**Features:**
|
|
1835
|
-
- `enableDynamicSizing` — height auto-fits content, no `snapPoints` needed
|
|
1981
|
+
- `enableDynamicSizing` — height auto-fits content, no `snapPoints` needed (default behavior when `snapPoints` is omitted)
|
|
1982
|
+
- `snapPoints` — optionally provide custom snap points (e.g., `['50%', '85%']`). Disables dynamic sizing when provided
|
|
1836
1983
|
- `enablePanDownToClose` — swipe down to dismiss
|
|
1837
1984
|
- Backdrop press dismisses
|
|
1838
|
-
- **Scrollable content:** use `scrollable` prop
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1985
|
+
- **Scrollable content:** use `scrollable` prop or `maxHeight`. Both use `BottomSheetScrollView` — do NOT use plain `ScrollView` inside Sheet
|
|
1986
|
+
- **Keyboard handling:** Full keyboard awareness via `@gorhom/bottom-sheet` v5. Professional defaults:
|
|
1987
|
+
- `keyboardBehavior="interactive"` — sheet offsets by keyboard size on both platforms
|
|
1988
|
+
- `android_keyboardInputMode="adjustPan"` — moves window instead of resizing (fixes restore issues on Android)
|
|
1989
|
+
- `keyboardBlurBehavior="restore"` — returns to pre-keyboard position when keyboard dismisses
|
|
1990
|
+
- `enableBlurKeyboardOnGesture={true}` — dismisses keyboard when dragging sheet down
|
|
1991
|
+
- `topInset` — automatically applied from safe area context to prevent going above notch
|
|
1992
|
+
- **Text inputs inside sheet:** **MUST use `SheetTextInput`** (re-exported `BottomSheetTextInput`) — handles focus/blur internally, communicates with sheet's keyboard system. **Never use regular `TextInput`** inside sheets — keyboard handling will break
|
|
1993
|
+
- **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)
|
|
1994
|
+
|
|
1995
|
+
**Haptics:** `impactMedium` on open.
|
|
1843
1996
|
|
|
1844
1997
|
**Examples:**
|
|
1845
1998
|
```tsx
|
|
1846
1999
|
const [open, setOpen] = useState(false)
|
|
1847
2000
|
|
|
2001
|
+
// Basic
|
|
1848
2002
|
<Sheet open={open} onClose={() => setOpen(false)} title="Sort by">
|
|
1849
|
-
<RadioGroup
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
2003
|
+
<RadioGroup options={sortOptions} value={sort} onValueChange={setSort} />
|
|
2004
|
+
</Sheet>
|
|
2005
|
+
|
|
2006
|
+
// Scrollable list
|
|
2007
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Results" scrollable maxHeight={500}>
|
|
2008
|
+
{items.map((r) => <ListItem key={r.id} title={r.name} showSeparator />)}
|
|
2009
|
+
</Sheet>
|
|
2010
|
+
|
|
2011
|
+
// With text input — keyboard handling works automatically (platform-optimized defaults)
|
|
2012
|
+
<Sheet
|
|
2013
|
+
open={open}
|
|
2014
|
+
onClose={() => setOpen(false)}
|
|
2015
|
+
title="Add note"
|
|
2016
|
+
subtitle="Keyboard handling is automatic with platform-optimized defaults"
|
|
2017
|
+
>
|
|
2018
|
+
<SheetTextInput
|
|
2019
|
+
placeholder="Write your note..."
|
|
2020
|
+
multiline
|
|
2021
|
+
style={{
|
|
2022
|
+
borderWidth: 1,
|
|
2023
|
+
borderColor: '#ddd',
|
|
2024
|
+
borderRadius: 8,
|
|
2025
|
+
padding: 12,
|
|
2026
|
+
minHeight: 80,
|
|
2027
|
+
}}
|
|
1858
2028
|
/>
|
|
2029
|
+
<Button label="Save" fullWidth style={{ marginTop: 12 }} onPress={() => setOpen(false)} />
|
|
1859
2030
|
</Sheet>
|
|
1860
2031
|
|
|
1861
|
-
//
|
|
1862
|
-
<Sheet
|
|
1863
|
-
{
|
|
1864
|
-
|
|
1865
|
-
|
|
2032
|
+
// With custom snap points (manual sizing)
|
|
2033
|
+
<Sheet
|
|
2034
|
+
open={open}
|
|
2035
|
+
onClose={() => setOpen(false)}
|
|
2036
|
+
title="Filters"
|
|
2037
|
+
snapPoints={['50%', '90%']}
|
|
2038
|
+
>
|
|
2039
|
+
<RadioGroup options={filterOptions} value={filter} onValueChange={setFilter} />
|
|
1866
2040
|
</Sheet>
|
|
1867
2041
|
|
|
1868
|
-
//
|
|
1869
|
-
<Sheet open={open} onClose={() => setOpen(false)} title="
|
|
1870
|
-
{
|
|
2042
|
+
// With sticky footer
|
|
2043
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Filters" scrollable
|
|
2044
|
+
footer={<View style={{ padding: 16 }}><Button label="Apply" fullWidth onPress={() => setOpen(false)} /></View>}>
|
|
2045
|
+
{/* long filter content */}
|
|
1871
2046
|
</Sheet>
|
|
1872
2047
|
|
|
1873
|
-
//
|
|
1874
|
-
<Sheet open={
|
|
1875
|
-
<
|
|
1876
|
-
<View>
|
|
1877
|
-
<Text variant="title-sm">Price range</Text>
|
|
1878
|
-
<Slider value={maxPrice} minimumValue={0} maximumValue={1000} step={50} onValueChange={setMaxPrice} showValue formatValue={(v) => `$${v}`} />
|
|
1879
|
-
</View>
|
|
1880
|
-
<Separator />
|
|
1881
|
-
<View>
|
|
1882
|
-
<Text variant="title-sm">Category</Text>
|
|
1883
|
-
<ChipGroup options={categoryOptions} value={selectedCategory} onValueChange={setSelectedCategory} />
|
|
1884
|
-
</View>
|
|
1885
|
-
<Button label="Apply filters" fullWidth onPress={() => { applyFilters(); setFilterOpen(false) }} />
|
|
1886
|
-
</View>
|
|
2048
|
+
// With close button
|
|
2049
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Details" subtitle="Product info" showCloseButton>
|
|
2050
|
+
<Text>Content here...</Text>
|
|
1887
2051
|
</Sheet>
|
|
1888
2052
|
```
|
|
1889
2053
|
|
|
2054
|
+
**Keyboard handling notes:**
|
|
2055
|
+
- No `KeyboardAvoidingView` needed — `@gorhom/bottom-sheet` handles everything
|
|
2056
|
+
- Always use `SheetTextInput` (not plain `TextInput`) for auto-focus/blur handling
|
|
2057
|
+
- Default `keyboardBehavior="interactive"` works on both platforms
|
|
2058
|
+
- Default `android_keyboardInputMode="adjustPan"` fixes the transparent gap that occurs with `adjustResize` when keyboard dismisses
|
|
2059
|
+
- `enableBlurKeyboardOnGesture={true}` (default) dismisses keyboard when dragging sheet
|
|
2060
|
+
|
|
1890
2061
|
---
|
|
1891
2062
|
|
|
1892
2063
|
### Toast / useToast
|
|
1893
2064
|
|
|
1894
|
-
**Import:** `import { ToastProvider, useToast } from '@retray-dev/ui-kit'`
|
|
2065
|
+
**Import:** `import { toast, ToastProvider, useToast } from '@retray-dev/ui-kit'`
|
|
1895
2066
|
|
|
1896
2067
|
**When to use:** Ephemeral feedback messages — save confirmations, errors, copy notifications, background process updates. Auto-dismiss after duration.
|
|
1897
2068
|
|
|
1898
|
-
**
|
|
2069
|
+
**Powered by:** `sonner-native` — white/black background, colored icon marks the variant (not the background), swipe-to-dismiss, promise support, stacking.
|
|
2070
|
+
|
|
2071
|
+
**Required setup:** `ToastProvider` must wrap the app. See Setup section above. Also requires `react-native-svg` and `react-native-screens` as peer deps.
|
|
1899
2072
|
|
|
1900
|
-
**
|
|
2073
|
+
**API — direct function (preferred):**
|
|
1901
2074
|
```tsx
|
|
1902
|
-
|
|
1903
|
-
|
|
2075
|
+
import { toast } from '@retray-dev/ui-kit'
|
|
2076
|
+
|
|
2077
|
+
// Variants:
|
|
2078
|
+
toast.success('Saved successfully')
|
|
2079
|
+
toast.error('Connection error', { description: 'Check your network' })
|
|
2080
|
+
toast.warning('Slow connection detected')
|
|
2081
|
+
toast('Neutral message')
|
|
2082
|
+
toast.loading('Processing...')
|
|
2083
|
+
|
|
2084
|
+
// Promise — auto-resolves:
|
|
2085
|
+
toast.promise(saveData(), {
|
|
2086
|
+
loading: 'Saving...',
|
|
2087
|
+
success: 'Saved',
|
|
2088
|
+
error: 'Failed to save',
|
|
2089
|
+
})
|
|
1904
2090
|
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
toast({ title: 'Saved', variant: 'success' })
|
|
1911
|
-
}}
|
|
1912
|
-
/>
|
|
1913
|
-
)
|
|
1914
|
-
}
|
|
2091
|
+
// Programmatic dismiss:
|
|
2092
|
+
const id = toast.loading('Uploading...')
|
|
2093
|
+
await upload()
|
|
2094
|
+
toast.dismiss(id)
|
|
2095
|
+
toast.success('Upload complete')
|
|
1915
2096
|
```
|
|
1916
2097
|
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
| Field | Type | Default | Notes |
|
|
1920
|
-
|-------|------|---------|-------|
|
|
1921
|
-
| title | `string` | — | Bold heading |
|
|
1922
|
-
| description | `string` | — | Detail text below title |
|
|
1923
|
-
| variant | `'default' \| 'destructive' \| 'success' \| 'warning'` | `'default'` | Background and icon color |
|
|
1924
|
-
| duration | `number` (ms) | `3000` | Auto-dismiss delay. Pass `Infinity` to prevent auto-dismiss |
|
|
1925
|
-
| icon | `ReactNode` | — | Custom icon. Defaults to variant symbol |
|
|
1926
|
-
| iconName | `string` | — | Icon name from `@expo/vector-icons`. Takes precedence over `icon` |
|
|
1927
|
-
| iconColor | `string` | — | Override icon color |
|
|
1928
|
-
| action | `{ label: string, onPress: () => void }` | — | Optional action button beside dismiss |
|
|
1929
|
-
|
|
1930
|
-
**`dismiss(id)`:** Dismiss a specific toast programmatically. `id` is returned by the `toast()` call.
|
|
1931
|
-
|
|
1932
|
-
**Variant details:**
|
|
1933
|
-
- `default` — dark/primary background, `impactLight` haptic
|
|
1934
|
-
- `success` — `success` token background, `notificationSuccess` haptic
|
|
1935
|
-
- `destructive` — `destructive` token background, `notificationError` haptic
|
|
1936
|
-
- `warning` — `warning` token background, `notificationError` haptic
|
|
1937
|
-
|
|
1938
|
-
**Behavior:**
|
|
1939
|
-
- Max 3 toasts shown simultaneously — oldest removed when 4th arrives
|
|
1940
|
-
- Appear at top of screen below status bar (dynamic safe area inset)
|
|
1941
|
-
- Swipe left or right to dismiss early (threshold: 80px or 800pt/s velocity)
|
|
1942
|
-
- **Web:** 400px max width, centered
|
|
1943
|
-
|
|
1944
|
-
**Animation:** Entrance: `withTiming(120ms, Easing.out(Easing.exp))` slide-down + opacity. Exit: `withTiming(200ms)` slide-up + opacity fade.
|
|
1945
|
-
|
|
1946
|
-
**Examples:**
|
|
2098
|
+
**API — hook (backward compat):**
|
|
1947
2099
|
```tsx
|
|
1948
2100
|
const { toast, dismiss } = useToast()
|
|
1949
|
-
|
|
1950
|
-
// Basic variants
|
|
1951
|
-
toast({ title: 'Saved', variant: 'success' })
|
|
1952
|
-
toast({ title: 'Connection error', variant: 'destructive' })
|
|
1953
|
-
toast({ title: 'Check your email', variant: 'warning' })
|
|
1954
|
-
toast({ title: 'Link copied' })
|
|
1955
|
-
|
|
1956
|
-
// With description
|
|
1957
|
-
toast({
|
|
1958
|
-
title: 'Payment sent',
|
|
1959
|
-
description: '$250 sent to John Doe',
|
|
1960
|
-
variant: 'success',
|
|
1961
|
-
iconName: 'check-circle',
|
|
1962
|
-
})
|
|
1963
|
-
|
|
1964
|
-
// With action button
|
|
1965
|
-
toast({
|
|
1966
|
-
title: 'Message deleted',
|
|
1967
|
-
action: { label: 'Undo', onPress: () => restoreMessage() },
|
|
1968
|
-
})
|
|
1969
|
-
|
|
1970
|
-
// Custom duration (longer)
|
|
1971
|
-
toast({ title: 'Processing your request...', duration: 8000 })
|
|
1972
|
-
|
|
1973
|
-
// Programmatic dismiss
|
|
1974
|
-
const id = toast({ title: 'Uploading...', duration: Infinity })
|
|
1975
|
-
await upload()
|
|
2101
|
+
toast.success('Saved')
|
|
1976
2102
|
dismiss(id)
|
|
1977
|
-
toast({ title: 'Upload complete', variant: 'success' })
|
|
1978
2103
|
```
|
|
1979
2104
|
|
|
2105
|
+
**Behavior:**
|
|
2106
|
+
- Max 3 toasts shown simultaneously
|
|
2107
|
+
- Appear top-center with safe area insets (respects notch/status bar)
|
|
2108
|
+
- Swipe up to dismiss early
|
|
2109
|
+
- Auto-dismiss after 4000ms
|
|
2110
|
+
- Follows `colorScheme` from `ThemeProvider` (light/dark background)
|
|
2111
|
+
|
|
2112
|
+
**Notes:**
|
|
2113
|
+
- `richColors: false` — background stays white (light) / black (dark). Variant shown via icon color only.
|
|
2114
|
+
- `closeButton: false` — swipe-to-dismiss is the gesture.
|
|
2115
|
+
|
|
1980
2116
|
---
|
|
1981
2117
|
|
|
1982
2118
|
### ConfirmDialog
|
|
@@ -2049,6 +2185,8 @@ toast({ title: 'Upload complete', variant: 'success' })
|
|
|
2049
2185
|
|
|
2050
2186
|
**When to use:** Rows in lists, feeds, menus, settings screens. The most compositional component — composes left slot, text block, and right slot in a standard horizontal layout. Supports plain (list items) and card (standalone cards) variants.
|
|
2051
2187
|
|
|
2188
|
+
**Important:** ListItem has **zero horizontal padding** by design — the consumer controls spacing. Wrap in a container with padding for standalone use, or use `variant="card"` for auto-styled surfaces.
|
|
2189
|
+
|
|
2052
2190
|
| Prop | Type | Default | Notes |
|
|
2053
2191
|
|------|------|---------|-------|
|
|
2054
2192
|
| title | `string` | required | Primary text |
|
|
@@ -2159,6 +2297,84 @@ toast({ title: 'Upload complete', variant: 'success' })
|
|
|
2159
2297
|
|
|
2160
2298
|
---
|
|
2161
2299
|
|
|
2300
|
+
### MenuItem
|
|
2301
|
+
|
|
2302
|
+
**Import:** `import { MenuItem } from '@retray-dev/ui-kit'`
|
|
2303
|
+
|
|
2304
|
+
**When to use:** Settings screens, navigation lists, menu entries — icon + label + optional right content. Purpose-built for Airbnb-style settings rows. Use `ListItem` when you need subtitle, caption, or more complex layouts.
|
|
2305
|
+
|
|
2306
|
+
**Important:** MenuItem has **zero horizontal padding** by design — the consumer controls spacing. Wrap in a container with padding for standalone use, or use `variant="card"` for auto-styled surfaces.
|
|
2307
|
+
|
|
2308
|
+
| Prop | Type | Default | Notes |
|
|
2309
|
+
|------|------|---------|-------|
|
|
2310
|
+
| label | `string` | required | Row label |
|
|
2311
|
+
| iconName | `string` | — | Icon from `@expo/vector-icons`. Auto-resolved across all 6 families |
|
|
2312
|
+
| icon | `ReactNode` | — | Custom icon (used if `iconName` not set) |
|
|
2313
|
+
| iconColor | `string` | — | Override icon color. Defaults to `foreground` |
|
|
2314
|
+
| rightRender | `ReactNode` | — | Custom content on the right. Replaces default chevron. Use for Switch, Checkbox, Badge, etc. |
|
|
2315
|
+
| showChevron | `boolean` | `true` | Show chevron on the right. Ignored when `rightRender` is set |
|
|
2316
|
+
| onPress | `() => void` | required | Press handler |
|
|
2317
|
+
| variant | `'plain' \| 'card'` | `'plain'` | `plain` sits inside parent surface. `card` has own background + border |
|
|
2318
|
+
| disabled | `boolean` | `false` | Dims and disables press |
|
|
2319
|
+
| showSeparator | `boolean` | `false` | Hairline separator at bottom |
|
|
2320
|
+
| style | `ViewStyle` | — | Container style override |
|
|
2321
|
+
| labelStyle | `TextStyle` | — | Label text style override |
|
|
2322
|
+
|
|
2323
|
+
**Features:**
|
|
2324
|
+
- Height: 54dp — consistent settings row
|
|
2325
|
+
- Chevron: shown by default (`showChevron={true}`). Can be replaced via `rightRender` prop
|
|
2326
|
+
- `rightRender` — arbitrary right slot content (Switch, Checkbox, Badge, etc). When set, chevron is hidden
|
|
2327
|
+
- Press scale: 0.97 spring (`stiffness: 350, damping: 28, mass: 0.9`)
|
|
2328
|
+
- Haptics: `selectionAsync` on press
|
|
2329
|
+
|
|
2330
|
+
**Example:**
|
|
2331
|
+
```tsx
|
|
2332
|
+
// Settings screen (wrap in container with padding)
|
|
2333
|
+
<View style={{ padding: 16, backgroundColor: colors.card, borderRadius: 12 }}>
|
|
2334
|
+
<MenuItem label="Personal info" iconName="user" onPress={() => navigate('profile')} showSeparator />
|
|
2335
|
+
<MenuItem label="Security" iconName="shield" onPress={() => navigate('security')} showSeparator />
|
|
2336
|
+
<MenuItem label="Privacy" iconName="lock" onPress={() => navigate('privacy')} showSeparator />
|
|
2337
|
+
<MenuItem
|
|
2338
|
+
label="Notifications"
|
|
2339
|
+
iconName="bell"
|
|
2340
|
+
onPress={() => setNotifs(!notifs)}
|
|
2341
|
+
rightRender={<Switch value={notifs} onValueChange={setNotifs} />}
|
|
2342
|
+
showSeparator
|
|
2343
|
+
/>
|
|
2344
|
+
<MenuItem
|
|
2345
|
+
label="Dark Mode"
|
|
2346
|
+
iconName="moon"
|
|
2347
|
+
onPress={() => setDark(!dark)}
|
|
2348
|
+
rightRender={<Checkbox checked={dark} onPress={() => setDark(!dark)} />}
|
|
2349
|
+
showSeparator
|
|
2350
|
+
/>
|
|
2351
|
+
<MenuItem
|
|
2352
|
+
label="Payments"
|
|
2353
|
+
iconName="credit-card"
|
|
2354
|
+
onPress={() => navigate('payments')}
|
|
2355
|
+
rightRender={<Badge label="New" variant="warning" size="sm" />}
|
|
2356
|
+
/>
|
|
2357
|
+
</View>
|
|
2358
|
+
|
|
2359
|
+
// Card variant (standalone)
|
|
2360
|
+
<MenuItem
|
|
2361
|
+
variant="card"
|
|
2362
|
+
label="Refer a friend"
|
|
2363
|
+
iconName="gift"
|
|
2364
|
+
onPress={() => navigate('referral')}
|
|
2365
|
+
/>
|
|
2366
|
+
|
|
2367
|
+
// No chevron (custom right content)
|
|
2368
|
+
<MenuItem
|
|
2369
|
+
label="Language"
|
|
2370
|
+
iconName="globe"
|
|
2371
|
+
rightRender={<Text>English</Text>}
|
|
2372
|
+
onPress={() => navigate('language')}
|
|
2373
|
+
/>
|
|
2374
|
+
```
|
|
2375
|
+
|
|
2376
|
+
---
|
|
2377
|
+
|
|
2162
2378
|
### Chip / ChipGroup
|
|
2163
2379
|
|
|
2164
2380
|
**Import:** `import { Chip, ChipGroup } from '@retray-dev/ui-kit'`
|
|
@@ -2180,7 +2396,7 @@ toast({ title: 'Upload complete', variant: 'success' })
|
|
|
2180
2396
|
|
|
2181
2397
|
| Prop | Type | Default | Notes |
|
|
2182
2398
|
|------|------|---------|-------|
|
|
2183
|
-
| options | `ChipOption[]` | required | `{ label
|
|
2399
|
+
| options | `ChipOption[]` | required | `{ label, value, iconName?, iconColor?, disabled? }` |
|
|
2184
2400
|
| value | `string \| number \| (string \| number)[]` | — | Selected value(s) |
|
|
2185
2401
|
| onValueChange | `(value: ...) => void` | — | Returns single value or array depending on `multiSelect` |
|
|
2186
2402
|
| multiSelect | `boolean` | `false` | Allow multiple chips selected simultaneously |
|
|
@@ -2325,29 +2541,98 @@ const [categories, setCategories] = useState<number[]>([1, 3])
|
|
|
2325
2541
|
|------|------|---------|-------|
|
|
2326
2542
|
| label | `string` | required | Caption label on the left |
|
|
2327
2543
|
| value | `string \| ReactNode` | required | Value on the right. Strings auto-styled; pass `ReactNode` for custom content |
|
|
2544
|
+
| iconName | `string` | — | Icon name from `@expo/vector-icons` rendered left of label |
|
|
2545
|
+
| iconColor | `string` | — | Override icon color. Defaults to `foregroundMuted` |
|
|
2328
2546
|
| style | `ViewStyle` | — | — |
|
|
2329
2547
|
|
|
2330
2548
|
**Styling:** Row layout, `justifyContent: 'space-between'`. Label: 13pt / Regular / `foregroundMuted`. Value: 14pt / Medium / `foreground`. 12px gap.
|
|
2331
2549
|
|
|
2332
2550
|
**Examples:**
|
|
2333
2551
|
```tsx
|
|
2334
|
-
<LabelValue label="Date" value="12 mar 2025" />
|
|
2335
|
-
<LabelValue label="Category" value="Food & drink" />
|
|
2336
|
-
<LabelValue label="Status" value={<Badge label="Pending" variant="warningOutline" size="sm" />} />
|
|
2337
|
-
<LabelValue label="Amount" value="$45.000" />
|
|
2552
|
+
<LabelValue label="Date" value="12 mar 2025" iconName="calendar" />
|
|
2553
|
+
<LabelValue label="Category" value="Food & drink" iconName="tag" />
|
|
2554
|
+
<LabelValue label="Status" value={<Badge label="Pending" variant="warningOutline" size="sm" />} iconName="clock" />
|
|
2555
|
+
<LabelValue label="Amount" value="$45.000" iconName="dollar-sign" />
|
|
2338
2556
|
|
|
2339
2557
|
// Transaction detail
|
|
2340
2558
|
<View style={{ gap: SPACING.sm }}>
|
|
2341
|
-
<LabelValue label="Merchant" value={transaction.merchant} />
|
|
2342
|
-
<LabelValue label="Date" value={formatDate(transaction.date)} />
|
|
2343
|
-
<LabelValue label="Category" value={transaction.category} />
|
|
2559
|
+
<LabelValue label="Merchant" value={transaction.merchant} iconName="shopping-bag" />
|
|
2560
|
+
<LabelValue label="Date" value={formatDate(transaction.date)} iconName="calendar" />
|
|
2561
|
+
<LabelValue label="Category" value={transaction.category} iconName="tag" />
|
|
2344
2562
|
<Separator />
|
|
2345
|
-
<LabelValue label="Amount" value={`$${transaction.amount}`} />
|
|
2563
|
+
<LabelValue label="Amount" value={`$${transaction.amount}`} iconName="dollar-sign" />
|
|
2346
2564
|
</View>
|
|
2347
2565
|
```
|
|
2348
2566
|
|
|
2349
2567
|
---
|
|
2350
2568
|
|
|
2569
|
+
### DetailRow
|
|
2570
|
+
|
|
2571
|
+
**Import:** `import { DetailRow } from '@retray-dev/ui-kit'`
|
|
2572
|
+
|
|
2573
|
+
**When to use:** "Label ··· Value" ticket/receipt rows — financial summaries, split bills, transaction breakdowns. Dotted separator fills the space between label and value.
|
|
2574
|
+
|
|
2575
|
+
| Prop | Type | Default | Notes |
|
|
2576
|
+
|------|------|---------|-------|
|
|
2577
|
+
| label | `string \| ReactNode` | required | Left side. Strings auto-styled in `foregroundMuted` caption |
|
|
2578
|
+
| value | `string` | required | Right side value text |
|
|
2579
|
+
| separator | `'dotted' \| 'solid' \| 'dashed' \| 'none'` | `'dotted'` | Fill line between label and value |
|
|
2580
|
+
| labelWeight | `'normal' \| 'medium' \| 'semibold' \| 'bold'` | `'normal'` | Font weight of label text |
|
|
2581
|
+
| valueColor | `string` | — | Override value text color (hex or theme token value) |
|
|
2582
|
+
| leftIcon | `ReactNode` | — | Node rendered left of label (e.g. `Avatar`, `Icon`) |
|
|
2583
|
+
| leftIconName | `string` | — | Icon name from `@expo/vector-icons` rendered left of label. Takes precedence over `leftIcon` |
|
|
2584
|
+
| leftIconColor | `string` | — | Override left icon color. Defaults to `foregroundMuted` |
|
|
2585
|
+
| rightIconName | `string` | — | Icon name from `@expo/vector-icons` rendered right of value |
|
|
2586
|
+
| rightIconColor | `string` | — | Override right icon color. Defaults to `foregroundMuted` |
|
|
2587
|
+
| style | `ViewStyle` | — | Row container style |
|
|
2588
|
+
| labelStyle | `TextStyle` | — | Label text style override |
|
|
2589
|
+
| valueStyle | `TextStyle` | — | Value text style override |
|
|
2590
|
+
|
|
2591
|
+
**Examples:**
|
|
2592
|
+
```tsx
|
|
2593
|
+
// Basic
|
|
2594
|
+
<DetailRow label="Total" value="$50.000" />
|
|
2595
|
+
|
|
2596
|
+
// With icons
|
|
2597
|
+
<DetailRow label="Food" value="$380.000" leftIconName="coffee" />
|
|
2598
|
+
<DetailRow label="Transport" value="$95.000" leftIconName="truck" />
|
|
2599
|
+
<DetailRow label="Total" value="$1.250.000" labelWeight="bold" rightIconName="trending-up" />
|
|
2600
|
+
|
|
2601
|
+
// Dotted separator with bold total
|
|
2602
|
+
<DetailRow label="Total" value="$50.000" labelWeight="bold" />
|
|
2603
|
+
|
|
2604
|
+
// With avatar left icon
|
|
2605
|
+
<DetailRow
|
|
2606
|
+
label="Juliana"
|
|
2607
|
+
value="$25.000"
|
|
2608
|
+
leftIcon={<Avatar src={avatarUrl} size={20} />}
|
|
2609
|
+
/>
|
|
2610
|
+
|
|
2611
|
+
// Destructive value
|
|
2612
|
+
<DetailRow label="Deuda" value="$150.000" valueColor={colors.destructive} />
|
|
2613
|
+
|
|
2614
|
+
// No separator
|
|
2615
|
+
<DetailRow label="Subtotal" value="$45.000" separator="none" />
|
|
2616
|
+
|
|
2617
|
+
// Full receipt block
|
|
2618
|
+
<View style={{ gap: SPACING.xs }}>
|
|
2619
|
+
<DetailRow label="Juliana" value="$25.000" leftIcon={<Avatar fallback="J" size={20} />} />
|
|
2620
|
+
<DetailRow label="Julián" value="$25.000" leftIcon={<Avatar fallback="JC" size={20} />} />
|
|
2621
|
+
<Separator />
|
|
2622
|
+
<DetailRow label="Total" value="$50.000" labelWeight="bold" />
|
|
2623
|
+
</View>
|
|
2624
|
+
```
|
|
2625
|
+
|
|
2626
|
+
**Output:**
|
|
2627
|
+
```
|
|
2628
|
+
Juliana ············ $25.000
|
|
2629
|
+
Julián ············· $25.000
|
|
2630
|
+
─────────────────────────────
|
|
2631
|
+
Total ·············· $50.000
|
|
2632
|
+
```
|
|
2633
|
+
|
|
2634
|
+
---
|
|
2635
|
+
|
|
2351
2636
|
### MonthPicker
|
|
2352
2637
|
|
|
2353
2638
|
**Import:** `import { MonthPicker } from '@retray-dev/ui-kit'`
|
|
@@ -2358,6 +2643,8 @@ const [categories, setCategories] = useState<number[]>([1, 3])
|
|
|
2358
2643
|
|------|------|---------|-------|
|
|
2359
2644
|
| value | `MonthPickerValue` | required | `{ month: 1–12, year: number }` |
|
|
2360
2645
|
| onChange | `(value: MonthPickerValue) => void` | required | Called on navigation |
|
|
2646
|
+
| locale | `string` | `'en'` | BCP 47 locale. Built-in: `'en'`, `'es'`, `'pt'`, `'fr'`. Other locales: use `formatLabel` |
|
|
2647
|
+
| formatLabel | `(value: MonthPickerValue) => string` | — | Custom label formatter. Takes precedence over `locale` |
|
|
2361
2648
|
| style | `ViewStyle` | — | — |
|
|
2362
2649
|
|
|
2363
2650
|
**MonthPickerValue type:**
|
|
@@ -2381,6 +2668,16 @@ const [period, setPeriod] = useState({
|
|
|
2381
2668
|
<MonthPicker value={period} onChange={setPeriod} />
|
|
2382
2669
|
// Displays: "May 2026"
|
|
2383
2670
|
|
|
2671
|
+
<MonthPicker value={period} onChange={setPeriod} locale="es" />
|
|
2672
|
+
// Displays: "mayo 2026"
|
|
2673
|
+
|
|
2674
|
+
<MonthPicker
|
|
2675
|
+
value={period}
|
|
2676
|
+
onChange={setPeriod}
|
|
2677
|
+
formatLabel={({ month, year }) => `${month}/${year}`}
|
|
2678
|
+
/>
|
|
2679
|
+
// Displays: "5/2026"
|
|
2680
|
+
|
|
2384
2681
|
// In a transactions header
|
|
2385
2682
|
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
2386
2683
|
<Text variant="display-md">Transactions</Text>
|
|
@@ -2528,6 +2825,43 @@ All components with icon slots accept `iconName` — auto-resolved size and colo
|
|
|
2528
2825
|
|
|
2529
2826
|
---
|
|
2530
2827
|
|
|
2828
|
+
### `getResponsiveFontSize` utility
|
|
2829
|
+
|
|
2830
|
+
Calculates a stepped-down font size for long numeric strings (currency amounts, large numbers) that would overflow at full size.
|
|
2831
|
+
|
|
2832
|
+
```tsx
|
|
2833
|
+
import { getResponsiveFontSize } from '@retray-dev/ui-kit'
|
|
2834
|
+
|
|
2835
|
+
// Default steps: ≤10 chars → max, ≤12 → max-4, ≤14 → max-6, >14 → max-8
|
|
2836
|
+
const fontSize = getResponsiveFontSize(formatCOP(amount), 48)
|
|
2837
|
+
|
|
2838
|
+
<Text style={{ fontSize }} numberOfLines={1} adjustsFontSizeToFit>
|
|
2839
|
+
{formatCOP(amount)}
|
|
2840
|
+
</Text>
|
|
2841
|
+
```
|
|
2842
|
+
|
|
2843
|
+
**Signature:**
|
|
2844
|
+
```ts
|
|
2845
|
+
getResponsiveFontSize(
|
|
2846
|
+
text: string,
|
|
2847
|
+
maxSize: number,
|
|
2848
|
+
steps?: { maxLen: number; subtract: number }[]
|
|
2849
|
+
): number
|
|
2850
|
+
```
|
|
2851
|
+
|
|
2852
|
+
Custom steps example:
|
|
2853
|
+
```tsx
|
|
2854
|
+
getResponsiveFontSize(text, 48, [
|
|
2855
|
+
{ maxLen: 8, subtract: 0 },
|
|
2856
|
+
{ maxLen: 11, subtract: 6 },
|
|
2857
|
+
{ maxLen: 14, subtract: 10 },
|
|
2858
|
+
])
|
|
2859
|
+
```
|
|
2860
|
+
|
|
2861
|
+
**Alternative:** Use `<CurrencyDisplay autoScale maxFontSize={48} />` for currency values — no manual sizing needed.
|
|
2862
|
+
|
|
2863
|
+
---
|
|
2864
|
+
|
|
2531
2865
|
## Hover Support (Web)
|
|
2532
2866
|
|
|
2533
2867
|
```tsx
|
|
@@ -2592,3 +2926,29 @@ All `Text` and `TextInput` components have `allowFontScaling={true}` — respect
|
|
|
2592
2926
|
|
|
2593
2927
|
### Scaling utilities (internal)
|
|
2594
2928
|
Used internally — not exported. `s()` horizontal scale, `vs()` vertical scale, `ms()` moderate scale relative to 350×680 base.
|
|
2929
|
+
|
|
2930
|
+
---
|
|
2931
|
+
|
|
2932
|
+
## Full Composition Examples
|
|
2933
|
+
|
|
2934
|
+
The package includes **EXAMPLES.md** with complete, working code for 3 real-world app screens:
|
|
2935
|
+
|
|
2936
|
+
1. **Finance Dashboard** — MonthPicker, CurrencyDisplay, Progress, CategoryStrip, ListItem, DetailRow
|
|
2937
|
+
2. **Edit Profile** — Avatar, Badge, Input, Select, Switch, Card, AlertBanner
|
|
2938
|
+
3. **Onboarding Flow** — Badge, Progress, Input, RadioGroup, EmptyState (multi-step wizard)
|
|
2939
|
+
|
|
2940
|
+
**For AI agents:** Import EXAMPLES.md from the package for full source code and patterns:
|
|
2941
|
+
|
|
2942
|
+
```markdown
|
|
2943
|
+
## UI Kit Composition Examples
|
|
2944
|
+
@./node_modules/@retray-dev/ui-kit/EXAMPLES.md
|
|
2945
|
+
```
|
|
2946
|
+
|
|
2947
|
+
Each example includes:
|
|
2948
|
+
- Complete component code (copy-paste ready)
|
|
2949
|
+
- State management setup
|
|
2950
|
+
- Proper imports and theme usage
|
|
2951
|
+
- Common patterns (spacing, colors, navigation, toast feedback)
|
|
2952
|
+
- StyleSheet definitions
|
|
2953
|
+
|
|
2954
|
+
**For human developers:** Examples are also live in the `example/` app. Clone the repo, run `pnpm install && pnpm build`, then `cd example && pnpm start` to see them in action. Access via "🖥 App Screen Compositions" accordion.
|