@retray-dev/ui-kit 5.2.0 → 5.4.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 CHANGED
@@ -1,4 +1,4 @@
1
- # @retray-dev/ui-kit — Component Reference (v5.2.0)
1
+ # @retray-dev/ui-kit — Component Reference (v5.4.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
 
@@ -499,6 +499,67 @@ const styles = StyleSheet.create({
499
499
 
500
500
  ---
501
501
 
502
+ ### ButtonGroup
503
+
504
+ **Import:** `import { ButtonGroup } from '@retray-dev/ui-kit'`
505
+
506
+ **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.
507
+
508
+ | Prop | Type | Default | Notes |
509
+ |------|------|---------|-------|
510
+ | children | `React.ReactNode` | required | Button components to lay out |
511
+ | gap | `number` | `12` | Spacing between buttons (in points) |
512
+ | vertical | `boolean` | `false` | Stack buttons vertically instead of horizontally |
513
+ | style | `ViewStyle` | — | Override container style |
514
+
515
+ **Behavior:** Automatically applies `flex: 1` to all direct children. Children share space equally regardless of label length.
516
+
517
+ **Mobile best practice:** When using 2 buttons with icons in a ButtonGroup, use `size="sm"` to prevent text clipping on narrow screens:
518
+ ```tsx
519
+ // ✅ Good — Small size prevents overflow
520
+ <ButtonGroup>
521
+ <Button label="Cancel" variant="secondary" size="sm" iconName="x" onPress={handleCancel} />
522
+ <Button label="Confirm" size="sm" iconName="check" onPress={handleConfirm} />
523
+ </ButtonGroup>
524
+
525
+ // ❌ Avoid — Default size may clip text when icons present
526
+ <ButtonGroup>
527
+ <Button label="Cancel" variant="secondary" iconName="x" onPress={handleCancel} />
528
+ <Button label="Confirm" iconName="check" onPress={handleConfirm} />
529
+ </ButtonGroup>
530
+ ```
531
+
532
+ **Examples:**
533
+ ```tsx
534
+ // Horizontal pair (50%/50%)
535
+ <ButtonGroup>
536
+ <Button label="Cancel" variant="secondary" onPress={handleCancel} />
537
+ <Button label="Confirm" onPress={handleConfirm} />
538
+ </ButtonGroup>
539
+
540
+ // Custom gap
541
+ <ButtonGroup gap={8}>
542
+ <Button label="Back" variant="text" onPress={handleBack} />
543
+ <Button label="Next" onPress={handleNext} />
544
+ </ButtonGroup>
545
+
546
+ // Vertical stack
547
+ <ButtonGroup vertical gap={8}>
548
+ <Button label="Primary Action" onPress={handlePrimary} />
549
+ <Button label="Secondary Action" variant="secondary" onPress={handleSecondary} />
550
+ <Button label="Delete" variant="destructive" onPress={handleDelete} />
551
+ </ButtonGroup>
552
+
553
+ // Three buttons (33%/33%/33%)
554
+ <ButtonGroup gap={6}>
555
+ <Button label="1D" variant="secondary" size="sm" onPress={() => setRange('1d')} />
556
+ <Button label="1W" variant="secondary" size="sm" onPress={() => setRange('1w')} />
557
+ <Button label="1M" size="sm" onPress={() => setRange('1m')} />
558
+ </ButtonGroup>
559
+ ```
560
+
561
+ ---
562
+
502
563
  ### IconButton
503
564
 
504
565
  **Import:** `import { IconButton } from '@retray-dev/ui-kit'`
@@ -682,6 +743,9 @@ const styles = StyleSheet.create({
682
743
  | error | `string` | — | Error text below; turns border destructive (2px) |
683
744
  | hint | `string` | — | Helper text below (hidden when `error` is set) |
684
745
  | rows | `number` | `4` | Minimum row count — each row ≈ 30px. Sets `numberOfLines` |
746
+ | prefixIcon | `string` | — | Icon name from `@expo/vector-icons` rendered inside top-left corner |
747
+ | prefixIconNode | `ReactNode` | — | Custom icon node rendered top-left |
748
+ | prefixIconColor | `string` | — | Override prefix icon color. Defaults to `foregroundMuted` |
685
749
  | containerStyle | `ViewStyle` | — | Outer container style |
686
750
  | style | `TextStyle` | — | TextInput element style |
687
751
 
@@ -689,9 +753,9 @@ const styles = StyleSheet.create({
689
753
 
690
754
  **Examples:**
691
755
  ```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} />
756
+ <Textarea label="Bio" placeholder="Tell us about yourself" rows={5} prefixIcon="user" />
757
+ <Textarea label="Review" hint="Be honest and helpful" prefixIcon="message-circle" />
758
+ <Textarea label="Notes" error="Notes are required" rows={3} prefixIcon="edit-3" />
695
759
  ```
696
760
 
697
761
  ---
@@ -776,6 +840,9 @@ const [amount, setAmount] = useState(0)
776
840
  | prefix | `string` | `'$'` | Symbol prepended to formatted value |
777
841
  | showDecimals | `boolean` | `false` | Show two decimal places with comma separator (e.g. `$25.000,00`) |
778
842
  | textColor | `string` | — | Override text color. Defaults to `foreground` token |
843
+ | variant | `'hero' \| 'large' \| 'medium' \| 'small'` | — | Predefined size (48/32/18/14pt). Overrides default 56pt |
844
+ | autoScale | `boolean` | `false` | Enable `adjustsFontSizeToFit` — long values shrink to fit one line |
845
+ | maxFontSize | `number` | — | Max font size when `autoScale` true. Defaults to variant size or 56pt |
779
846
  | style | `ViewStyle` | — | Outer container style |
780
847
 
781
848
  **Format:** Dot (`.`) as thousands separator, comma (`,`) as decimal separator — Latin American / European format.
@@ -874,9 +941,10 @@ const [amount, setAmount] = useState(0)
874
941
 
875
942
  | Prop | Type | Default | Notes |
876
943
  |------|------|---------|-------|
877
- | src | `string` | — | Image URI (remote or local) |
878
- | fallback | `string` | — | Text shown when image fails or `src` is absent — first 2 characters shown, uppercased |
879
- | size | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Diameter in points |
944
+ | src | `string \| null` | — | Image URI (remote or local). `null` forces fallback |
945
+ | fallback | `string` | — | Manual initials (max 2 chars, uppercased) |
946
+ | fallbackText | `string` | | Full name extracts up to 2 initials (e.g. `"Julian Cruz"` `"JC"`) |
947
+ | size | `'sm' \| 'md' \| 'lg' \| 'xl' \| number` | `'md'` | Named size or custom diameter in points |
880
948
  | status | `'online' \| 'offline' \| 'busy' \| 'away'` | — | Status indicator dot, bottom-right |
881
949
  | style | `ViewStyle` | — | — |
882
950
 
@@ -903,6 +971,8 @@ const [amount, setAmount] = useState(0)
903
971
  <Avatar fallback="AN" size="md" />
904
972
  <Avatar src={user.avatar} fallback={user.initials} size="xl" status="online" />
905
973
  <Avatar fallback="BU" size="sm" status="busy" />
974
+ <Avatar fallbackText="Julian Cruz" size="md" /> // → "JC"
975
+ <Avatar src={avatarUrl} size={64} /> // custom numeric size
906
976
 
907
977
  // In a ListItem
908
978
  <ListItem
@@ -1773,7 +1843,14 @@ const [tab, setTab] = useState('profile')
1773
1843
 
1774
1844
  **AccordionItem type:**
1775
1845
  ```ts
1776
- { value: string; trigger: string; content: ReactNode }
1846
+ {
1847
+ value: string
1848
+ trigger: string
1849
+ content: ReactNode
1850
+ iconName?: string // Icon name from @expo/vector-icons
1851
+ icon?: ReactNode // Custom icon node
1852
+ iconColor?: string // Override icon color (defaults to foregroundMuted)
1853
+ }
1777
1854
  ```
1778
1855
 
1779
1856
  **Animation:** `react-native-reanimated` `withTiming` (220ms) for height and chevron rotation (180°). Press scale uses `withSpring`. Runs on UI thread at 60fps.
@@ -1787,11 +1864,13 @@ const [tab, setTab] = useState('profile')
1787
1864
  {
1788
1865
  value: 'q1',
1789
1866
  trigger: 'What payment methods do you accept?',
1867
+ iconName: 'credit-card',
1790
1868
  content: <Text variant="body-sm">We accept Visa, Mastercard, and bank transfers.</Text>,
1791
1869
  },
1792
1870
  {
1793
1871
  value: 'q2',
1794
1872
  trigger: 'Can I cancel my booking?',
1873
+ iconName: 'x-circle',
1795
1874
  content: <Text variant="body-sm">Yes — free cancellation within 48 hours of booking.</Text>,
1796
1875
  },
1797
1876
  ]}
@@ -1830,6 +1909,8 @@ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to `ba
1830
1909
  | style | `ViewStyle` | — | Inner content container style |
1831
1910
  | scrollable | `boolean` | `false` | Wraps children in `BottomSheetScrollView` — fixes gesture conflict on both platforms when content needs to scroll |
1832
1911
  | maxHeight | `number` | — | Caps sheet height (dp). Automatically enables scrolling when content exceeds this value |
1912
+ | keyboardBehavior | `'padding' \| 'height' \| 'position' \| 'none'` | — | Wraps content in `KeyboardAvoidingView` when set. Use when sheet contains text inputs |
1913
+ | keyboardOffset | `number` | `0` | Extra vertical offset for `KeyboardAvoidingView` |
1833
1914
 
1834
1915
  **Features:**
1835
1916
  - `enableDynamicSizing` — height auto-fits content, no `snapPoints` needed
@@ -2180,7 +2261,7 @@ toast({ title: 'Upload complete', variant: 'success' })
2180
2261
 
2181
2262
  | Prop | Type | Default | Notes |
2182
2263
  |------|------|---------|-------|
2183
- | options | `ChipOption[]` | required | `{ label: string, value: string \| number }` |
2264
+ | options | `ChipOption[]` | required | `{ label, value, iconName?, iconColor?, disabled? }` |
2184
2265
  | value | `string \| number \| (string \| number)[]` | — | Selected value(s) |
2185
2266
  | onValueChange | `(value: ...) => void` | — | Returns single value or array depending on `multiSelect` |
2186
2267
  | multiSelect | `boolean` | `false` | Allow multiple chips selected simultaneously |
@@ -2325,29 +2406,98 @@ const [categories, setCategories] = useState<number[]>([1, 3])
2325
2406
  |------|------|---------|-------|
2326
2407
  | label | `string` | required | Caption label on the left |
2327
2408
  | value | `string \| ReactNode` | required | Value on the right. Strings auto-styled; pass `ReactNode` for custom content |
2409
+ | iconName | `string` | — | Icon name from `@expo/vector-icons` rendered left of label |
2410
+ | iconColor | `string` | — | Override icon color. Defaults to `foregroundMuted` |
2328
2411
  | style | `ViewStyle` | — | — |
2329
2412
 
2330
2413
  **Styling:** Row layout, `justifyContent: 'space-between'`. Label: 13pt / Regular / `foregroundMuted`. Value: 14pt / Medium / `foreground`. 12px gap.
2331
2414
 
2332
2415
  **Examples:**
2333
2416
  ```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" />
2417
+ <LabelValue label="Date" value="12 mar 2025" iconName="calendar" />
2418
+ <LabelValue label="Category" value="Food & drink" iconName="tag" />
2419
+ <LabelValue label="Status" value={<Badge label="Pending" variant="warningOutline" size="sm" />} iconName="clock" />
2420
+ <LabelValue label="Amount" value="$45.000" iconName="dollar-sign" />
2338
2421
 
2339
2422
  // Transaction detail
2340
2423
  <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} />
2424
+ <LabelValue label="Merchant" value={transaction.merchant} iconName="shopping-bag" />
2425
+ <LabelValue label="Date" value={formatDate(transaction.date)} iconName="calendar" />
2426
+ <LabelValue label="Category" value={transaction.category} iconName="tag" />
2344
2427
  <Separator />
2345
- <LabelValue label="Amount" value={`$${transaction.amount}`} />
2428
+ <LabelValue label="Amount" value={`$${transaction.amount}`} iconName="dollar-sign" />
2346
2429
  </View>
2347
2430
  ```
2348
2431
 
2349
2432
  ---
2350
2433
 
2434
+ ### DetailRow
2435
+
2436
+ **Import:** `import { DetailRow } from '@retray-dev/ui-kit'`
2437
+
2438
+ **When to use:** "Label ··· Value" ticket/receipt rows — financial summaries, split bills, transaction breakdowns. Dotted separator fills the space between label and value.
2439
+
2440
+ | Prop | Type | Default | Notes |
2441
+ |------|------|---------|-------|
2442
+ | label | `string \| ReactNode` | required | Left side. Strings auto-styled in `foregroundMuted` caption |
2443
+ | value | `string` | required | Right side value text |
2444
+ | separator | `'dotted' \| 'solid' \| 'dashed' \| 'none'` | `'dotted'` | Fill line between label and value |
2445
+ | labelWeight | `'normal' \| 'medium' \| 'semibold' \| 'bold'` | `'normal'` | Font weight of label text |
2446
+ | valueColor | `string` | — | Override value text color (hex or theme token value) |
2447
+ | leftIcon | `ReactNode` | — | Node rendered left of label (e.g. `Avatar`, `Icon`) |
2448
+ | leftIconName | `string` | — | Icon name from `@expo/vector-icons` rendered left of label. Takes precedence over `leftIcon` |
2449
+ | leftIconColor | `string` | — | Override left icon color. Defaults to `foregroundMuted` |
2450
+ | rightIconName | `string` | — | Icon name from `@expo/vector-icons` rendered right of value |
2451
+ | rightIconColor | `string` | — | Override right icon color. Defaults to `foregroundMuted` |
2452
+ | style | `ViewStyle` | — | Row container style |
2453
+ | labelStyle | `TextStyle` | — | Label text style override |
2454
+ | valueStyle | `TextStyle` | — | Value text style override |
2455
+
2456
+ **Examples:**
2457
+ ```tsx
2458
+ // Basic
2459
+ <DetailRow label="Total" value="$50.000" />
2460
+
2461
+ // With icons
2462
+ <DetailRow label="Food" value="$380.000" leftIconName="coffee" />
2463
+ <DetailRow label="Transport" value="$95.000" leftIconName="truck" />
2464
+ <DetailRow label="Total" value="$1.250.000" labelWeight="bold" rightIconName="trending-up" />
2465
+
2466
+ // Dotted separator with bold total
2467
+ <DetailRow label="Total" value="$50.000" labelWeight="bold" />
2468
+
2469
+ // With avatar left icon
2470
+ <DetailRow
2471
+ label="Juliana"
2472
+ value="$25.000"
2473
+ leftIcon={<Avatar src={avatarUrl} size={20} />}
2474
+ />
2475
+
2476
+ // Destructive value
2477
+ <DetailRow label="Deuda" value="$150.000" valueColor={colors.destructive} />
2478
+
2479
+ // No separator
2480
+ <DetailRow label="Subtotal" value="$45.000" separator="none" />
2481
+
2482
+ // Full receipt block
2483
+ <View style={{ gap: SPACING.xs }}>
2484
+ <DetailRow label="Juliana" value="$25.000" leftIcon={<Avatar fallback="J" size={20} />} />
2485
+ <DetailRow label="Julián" value="$25.000" leftIcon={<Avatar fallback="JC" size={20} />} />
2486
+ <Separator />
2487
+ <DetailRow label="Total" value="$50.000" labelWeight="bold" />
2488
+ </View>
2489
+ ```
2490
+
2491
+ **Output:**
2492
+ ```
2493
+ Juliana ············ $25.000
2494
+ Julián ············· $25.000
2495
+ ─────────────────────────────
2496
+ Total ·············· $50.000
2497
+ ```
2498
+
2499
+ ---
2500
+
2351
2501
  ### MonthPicker
2352
2502
 
2353
2503
  **Import:** `import { MonthPicker } from '@retray-dev/ui-kit'`
@@ -2358,6 +2508,8 @@ const [categories, setCategories] = useState<number[]>([1, 3])
2358
2508
  |------|------|---------|-------|
2359
2509
  | value | `MonthPickerValue` | required | `{ month: 1–12, year: number }` |
2360
2510
  | onChange | `(value: MonthPickerValue) => void` | required | Called on navigation |
2511
+ | locale | `string` | `'en'` | BCP 47 locale. Built-in: `'en'`, `'es'`, `'pt'`, `'fr'`. Other locales: use `formatLabel` |
2512
+ | formatLabel | `(value: MonthPickerValue) => string` | — | Custom label formatter. Takes precedence over `locale` |
2361
2513
  | style | `ViewStyle` | — | — |
2362
2514
 
2363
2515
  **MonthPickerValue type:**
@@ -2381,6 +2533,16 @@ const [period, setPeriod] = useState({
2381
2533
  <MonthPicker value={period} onChange={setPeriod} />
2382
2534
  // Displays: "May 2026"
2383
2535
 
2536
+ <MonthPicker value={period} onChange={setPeriod} locale="es" />
2537
+ // Displays: "mayo 2026"
2538
+
2539
+ <MonthPicker
2540
+ value={period}
2541
+ onChange={setPeriod}
2542
+ formatLabel={({ month, year }) => `${month}/${year}`}
2543
+ />
2544
+ // Displays: "5/2026"
2545
+
2384
2546
  // In a transactions header
2385
2547
  <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
2386
2548
  <Text variant="display-md">Transactions</Text>
@@ -2528,6 +2690,43 @@ All components with icon slots accept `iconName` — auto-resolved size and colo
2528
2690
 
2529
2691
  ---
2530
2692
 
2693
+ ### `getResponsiveFontSize` utility
2694
+
2695
+ Calculates a stepped-down font size for long numeric strings (currency amounts, large numbers) that would overflow at full size.
2696
+
2697
+ ```tsx
2698
+ import { getResponsiveFontSize } from '@retray-dev/ui-kit'
2699
+
2700
+ // Default steps: ≤10 chars → max, ≤12 → max-4, ≤14 → max-6, >14 → max-8
2701
+ const fontSize = getResponsiveFontSize(formatCOP(amount), 48)
2702
+
2703
+ <Text style={{ fontSize }} numberOfLines={1} adjustsFontSizeToFit>
2704
+ {formatCOP(amount)}
2705
+ </Text>
2706
+ ```
2707
+
2708
+ **Signature:**
2709
+ ```ts
2710
+ getResponsiveFontSize(
2711
+ text: string,
2712
+ maxSize: number,
2713
+ steps?: { maxLen: number; subtract: number }[]
2714
+ ): number
2715
+ ```
2716
+
2717
+ Custom steps example:
2718
+ ```tsx
2719
+ getResponsiveFontSize(text, 48, [
2720
+ { maxLen: 8, subtract: 0 },
2721
+ { maxLen: 11, subtract: 6 },
2722
+ { maxLen: 14, subtract: 10 },
2723
+ ])
2724
+ ```
2725
+
2726
+ **Alternative:** Use `<CurrencyDisplay autoScale maxFontSize={48} />` for currency values — no manual sizing needed.
2727
+
2728
+ ---
2729
+
2531
2730
  ## Hover Support (Web)
2532
2731
 
2533
2732
  ```tsx
@@ -2592,3 +2791,29 @@ All `Text` and `TextInput` components have `allowFontScaling={true}` — respect
2592
2791
 
2593
2792
  ### Scaling utilities (internal)
2594
2793
  Used internally — not exported. `s()` horizontal scale, `vs()` vertical scale, `ms()` moderate scale relative to 350×680 base.
2794
+
2795
+ ---
2796
+
2797
+ ## Full Composition Examples
2798
+
2799
+ The package includes **EXAMPLES.md** with complete, working code for 3 real-world app screens:
2800
+
2801
+ 1. **Finance Dashboard** — MonthPicker, CurrencyDisplay, Progress, CategoryStrip, ListItem, DetailRow
2802
+ 2. **Edit Profile** — Avatar, Badge, Input, Select, Switch, Card, AlertBanner
2803
+ 3. **Onboarding Flow** — Badge, Progress, Input, RadioGroup, EmptyState (multi-step wizard)
2804
+
2805
+ **For AI agents:** Import EXAMPLES.md from the package for full source code and patterns:
2806
+
2807
+ ```markdown
2808
+ ## UI Kit Composition Examples
2809
+ @./node_modules/@retray-dev/ui-kit/EXAMPLES.md
2810
+ ```
2811
+
2812
+ Each example includes:
2813
+ - Complete component code (copy-paste ready)
2814
+ - State management setup
2815
+ - Proper imports and theme usage
2816
+ - Common patterns (spacing, colors, navigation, toast feedback)
2817
+ - StyleSheet definitions
2818
+
2819
+ **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.