@retray-dev/ui-kit 1.0.0 → 1.5.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
@@ -11,22 +11,37 @@ This file is the AI reference for this package. It is shipped inside the npm pac
11
11
 
12
12
  ## Setup (Required)
13
13
 
14
- Wrap your app root with both providers. `ThemeProvider` enables theming; `ToastProvider` enables the toast notification system.
14
+ Wrap your app root with all required providers in this exact order:
15
15
 
16
16
  ```tsx
17
- import { ThemeProvider, ToastProvider } from '@retray-dev/ui-kit'
17
+ import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context'
18
+ import { GestureHandlerRootView } from 'react-native-gesture-handler'
19
+ import { ThemeProvider, BottomSheetModalProvider, ToastProvider } from '@retray-dev/ui-kit'
18
20
 
19
- export default function RootLayout({ children }) {
21
+ export default function App() {
20
22
  return (
21
- <ThemeProvider colorScheme="system">
22
- <ToastProvider>
23
- {children}
24
- </ToastProvider>
25
- </ThemeProvider>
23
+ <SafeAreaProvider initialMetrics={initialWindowMetrics}>
24
+ <GestureHandlerRootView style={{ flex: 1 }}>
25
+ <ThemeProvider colorScheme="system">
26
+ <BottomSheetModalProvider>
27
+ <ToastProvider>
28
+ {/* your app */}
29
+ </ToastProvider>
30
+ </BottomSheetModalProvider>
31
+ </ThemeProvider>
32
+ </GestureHandlerRootView>
33
+ </SafeAreaProvider>
26
34
  )
27
35
  }
28
36
  ```
29
37
 
38
+ **Provider order is mandatory:**
39
+ - `SafeAreaProvider` must be outermost — required by `useSafeAreaInsets` in `ToastProvider`
40
+ - `initialMetrics={initialWindowMetrics}` is required on Android to avoid a "No safe area value available" crash on first render
41
+ - `GestureHandlerRootView` must wrap everything — required by `@gorhom/bottom-sheet`
42
+ - `BottomSheetModalProvider` must be inside `GestureHandlerRootView`
43
+ - `ToastProvider` must be inside `SafeAreaProvider`
44
+
30
45
  ### ThemeProvider Props
31
46
 
32
47
  | Prop | Type | Default | Notes |
@@ -72,7 +87,7 @@ All 18 tokens are available via `useTheme().colors`.
72
87
  | `secondary` | `#f5f5f5` | `#2a2a2a` | Secondary surfaces |
73
88
  | `secondaryForeground` | `#1a1a1a` | `#fafafa` | Text on secondary |
74
89
  | `muted` | `#f5f5f5` | `#2a2a2a` | Muted backgrounds, skeleton fills, track fills |
75
- | `mutedForeground` | `#737373` | `#a3a3a3` | Placeholder text, helper text, captions |
90
+ | `mutedForeground` | `#646464` | `#a3a3a3` | Placeholder text, helper text, captions (WCAG AA ≥4.5:1) |
76
91
  | `accent` | `#f5f5f5` | `#2a2a2a` | Hover / pressed state fills |
77
92
  | `accentForeground` | `#1a1a1a` | `#fafafa` | Text on accent |
78
93
  | `destructive` | `#ef4444` | `#dc2626` | Error / danger / delete actions |
@@ -91,12 +106,12 @@ All 18 tokens are available via `useTheme().colors`.
91
106
 
92
107
  **Import:** `import { Text } from '@retray-dev/ui-kit'`
93
108
  **When to use:** All text in the app. Replaces React Native's `Text` with semantic variants.
94
- **Extends:** `TextProps` from React Native.
109
+ **Extends:** `TextProps` from React Native — all native props pass through.
95
110
 
96
111
  | Prop | Type | Default | Notes |
97
112
  |------|------|---------|-------|
98
113
  | variant | `'h1' \| 'h2' \| 'h3' \| 'body' \| 'caption' \| 'label'` | `'body'` | Sets font size, weight, and line height |
99
- | color | `string` | — | Override the color. Defaults to `foreground`, except `caption` which defaults to `mutedForeground` |
114
+ | color | `string` | — | Override the color. Defaults to `foreground`, except `caption` which uses `mutedForeground` |
100
115
 
101
116
  **Sizes:**
102
117
  - `h1`: 32px / 700 weight
@@ -127,16 +142,18 @@ All 18 tokens are available via `useTheme().colors`.
127
142
  | label | `string` | required | Button text |
128
143
  | variant | `'primary' \| 'secondary' \| 'outline' \| 'ghost'` | `'primary'` | Visual style |
129
144
  | size | `'sm' \| 'md' \| 'lg'` | `'md'` | — |
130
- | loading | `boolean` | `false` | Replaces label with spinner and forces disabled |
131
- | fullWidth | `boolean` | `false` | Stretches to container width |
145
+ | loading | `boolean` | `false` | Replaces label with a spinner and forces disabled state |
146
+ | fullWidth | `boolean` | `false` | Stretches to container width (`alignSelf: 'stretch'`) |
132
147
  | disabled | `boolean` | — | Reduces opacity to 0.45 |
133
148
 
134
149
  **Variants:**
135
150
  - `primary`: filled with `primary` token — main actions
136
151
  - `secondary`: filled with `secondary` token — less prominent actions
137
- - `outline`: transparent with `border` — alternative/secondary without fill
152
+ - `outline`: transparent with `border` — alternative without fill
138
153
  - `ghost`: fully transparent — in-context or low-emphasis actions
139
154
 
155
+ **Animations:** Scale springs to 0.97 on `onPressIn`, back to 1.0 on `onPressOut`.
156
+
140
157
  **Example:**
141
158
  ```tsx
142
159
  <Button label="Save changes" onPress={handleSave} />
@@ -151,13 +168,15 @@ All 18 tokens are available via `useTheme().colors`.
151
168
 
152
169
  **Import:** `import { Input } from '@retray-dev/ui-kit'`
153
170
  **When to use:** Single-line text entry. Includes built-in label, error, and hint support.
154
- **Extends:** `TextInputProps` from React Native.
171
+ **Extends:** `TextInputProps` from React Native — all native props pass through.
155
172
 
156
173
  | Prop | Type | Default | Notes |
157
174
  |------|------|---------|-------|
158
175
  | label | `string` | — | Label above the input |
159
- | error | `string` | — | Shows error text below; turns border red |
160
- | hint | `string` | — | Helper text below (hidden when error is set) |
176
+ | error | `string` | — | Shows error text below; turns border red (`destructive` token) |
177
+ | hint | `string` | — | Helper text below (hidden when `error` is set) |
178
+
179
+ **Border colors:** `destructive` when `error` is set, `ring` when focused, `border` otherwise.
161
180
 
162
181
  **Example:**
163
182
  ```tsx
@@ -171,15 +190,15 @@ All 18 tokens are available via `useTheme().colors`.
171
190
  ### Textarea
172
191
 
173
192
  **Import:** `import { Textarea } from '@retray-dev/ui-kit'`
174
- **When to use:** Multi-line text entry. Same API as Input plus `rows`.
175
- **Extends:** `TextInputProps` from React Native.
193
+ **When to use:** Multi-line text entry. Same API as `Input` plus `rows`.
194
+ **Extends:** `TextInputProps` from React Native — all native props pass through.
176
195
 
177
196
  | Prop | Type | Default | Notes |
178
197
  |------|------|---------|-------|
179
198
  | label | `string` | — | Label above |
180
199
  | error | `string` | — | Error text below; red border |
181
200
  | hint | `string` | — | Helper text below |
182
- | rows | `number` | `4` | Sets minimum height (each row ≈ 24px) |
201
+ | rows | `number` | `4` | Sets `minHeight` (each row ≈ 28px) |
183
202
 
184
203
  **Example:**
185
204
  ```tsx
@@ -197,6 +216,7 @@ All 18 tokens are available via `useTheme().colors`.
197
216
  |------|------|---------|-------|
198
217
  | label | `string` | required | — |
199
218
  | variant | `'default' \| 'secondary' \| 'destructive' \| 'outline'` | `'default'` | — |
219
+ | style | `ViewStyle` | — | — |
200
220
 
201
221
  **Example:**
202
222
  ```tsx
@@ -216,8 +236,9 @@ All 18 tokens are available via `useTheme().colors`.
216
236
  | Prop | Type | Default | Notes |
217
237
  |------|------|---------|-------|
218
238
  | src | `string` | — | Image URI |
219
- | fallback | `string` | — | Text for fallback (first 2 chars shown, uppercased) |
220
- | size | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | sm=24, md=32, lg=48, xl=64 |
239
+ | fallback | `string` | — | Text shown when image fails or `src` is absent — first 2 chars, uppercased |
240
+ | size | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | sm=24, md=32, lg=48, xl=64 (diameter in pt) |
241
+ | style | `ViewStyle` | — | — |
221
242
 
222
243
  **Example:**
223
244
  ```tsx
@@ -234,7 +255,8 @@ All 18 tokens are available via `useTheme().colors`.
234
255
 
235
256
  | Prop | Type | Default | Notes |
236
257
  |------|------|---------|-------|
237
- | orientation | `'horizontal' \| 'vertical'` | `'horizontal'` | Vertical requires a parent with defined height |
258
+ | orientation | `'horizontal' \| 'vertical'` | `'horizontal'` | Vertical requires a parent with a defined height |
259
+ | style | `ViewStyle` | — | — |
238
260
 
239
261
  **Example:**
240
262
  ```tsx
@@ -251,11 +273,12 @@ All 18 tokens are available via `useTheme().colors`.
251
273
  ### Spinner
252
274
 
253
275
  **Import:** `import { Spinner } from '@retray-dev/ui-kit'`
254
- **When to use:** Loading state indicator.
276
+ **When to use:** Loading state indicator. Wraps React Native's `ActivityIndicator`.
277
+ **Extends:** `ActivityIndicatorProps` (except `size`).
255
278
 
256
279
  | Prop | Type | Default | Notes |
257
280
  |------|------|---------|-------|
258
- | size | `'sm' \| 'md' \| 'lg'` | `'md'` | sm/md map to RN `'small'`, lg maps to `'large'` |
281
+ | size | `'sm' \| 'md' \| 'lg'` | `'md'` | `sm`/`md` map to RN `'small'`, `lg` maps to `'large'` |
259
282
  | color | `string` | `primary` token | Override spinner color |
260
283
 
261
284
  **Example:**
@@ -269,13 +292,14 @@ All 18 tokens are available via `useTheme().colors`.
269
292
  ### Skeleton
270
293
 
271
294
  **Import:** `import { Skeleton } from '@retray-dev/ui-kit'`
272
- **When to use:** Placeholder while content is loading. Pulses with animation.
295
+ **When to use:** Placeholder while content is loading. Pulses with a looping opacity animation.
273
296
 
274
297
  | Prop | Type | Default | Notes |
275
298
  |------|------|---------|-------|
276
299
  | width | `number \| string` | `'100%'` | — |
277
300
  | height | `number` | `16` | — |
278
301
  | borderRadius | `number` | `6` | — |
302
+ | style | `ViewStyle` | — | — |
279
303
 
280
304
  **Example:**
281
305
  ```tsx
@@ -294,6 +318,9 @@ All 18 tokens are available via `useTheme().colors`.
294
318
  |------|------|---------|-------|
295
319
  | value | `number` | `0` | Current value |
296
320
  | max | `number` | `100` | Maximum value |
321
+ | style | `ViewStyle` | — | — |
322
+
323
+ **Animation:** Spring-animates the fill width on `value` changes (JS thread — `useNativeDriver: false`). Uses `onLayout` to capture track pixel width and interpolates to pixels (cannot animate `width: '%'`).
297
324
 
298
325
  **Example:**
299
326
  ```tsx
@@ -326,21 +353,26 @@ All sub-components accept a `style` prop for overrides.
326
353
  </Card>
327
354
  ```
328
355
 
329
- **Notes:** `CardHeader` and `CardContent` have `padding: 24`. `CardFooter` has `paddingTop: 0` so it connects naturally to `CardContent`.
356
+ **Notes:**
357
+ - `CardHeader` and `CardContent` have `padding: 24`
358
+ - `CardFooter` has `paddingTop: 0` so it connects naturally to `CardContent`
359
+ - `CardTitle`: 18px / 600 weight, `cardForeground` color
360
+ - `CardDescription`: 14px, `mutedForeground` color
330
361
 
331
362
  ---
332
363
 
333
364
  ### Alert
334
365
 
335
366
  **Import:** `import { Alert } from '@retray-dev/ui-kit'`
336
- **When to use:** Inline feedback messages (info, success, warning, error). Not for toasts.
367
+ **When to use:** Inline feedback messages (info, success, warning, error). Not for transient toasts.
337
368
 
338
369
  | Prop | Type | Default | Notes |
339
370
  |------|------|---------|-------|
340
371
  | title | `string` | — | Bold heading |
341
372
  | description | `string` | — | Detail text |
342
- | variant | `'default' \| 'destructive'` | `'default'` | `destructive` turns border and text red |
343
- | icon | `ReactNode` | — | Icon placed to the left |
373
+ | variant | `'default' \| 'destructive'` | `'default'` | `destructive` turns border and text to `destructive` token |
374
+ | icon | `ReactNode` | — | Icon placed to the left of the text content |
375
+ | style | `ViewStyle` | — | — |
344
376
 
345
377
  **Example:**
346
378
  ```tsx
@@ -359,8 +391,9 @@ All sub-components accept a `style` prop for overrides.
359
391
  |------|------|---------|-------|
360
392
  | title | `string` | required | — |
361
393
  | description | `string` | — | — |
362
- | icon | `ReactNode` | — | Shown in a muted square above the text |
394
+ | icon | `ReactNode` | — | Shown in a 48×48 muted square above the text |
363
395
  | action | `ReactNode` | — | Usually a `Button`, placed below the text |
396
+ | style | `ViewStyle` | — | — |
364
397
 
365
398
  **Example:**
366
399
  ```tsx
@@ -383,6 +416,7 @@ All sub-components accept a `style` prop for overrides.
383
416
  | onCheckedChange | `(checked: boolean) => void` | — | — |
384
417
  | label | `string` | — | Text to the right of the box |
385
418
  | disabled | `boolean` | — | — |
419
+ | style | `ViewStyle` | — | — |
386
420
 
387
421
  **Example:**
388
422
  ```tsx
@@ -395,13 +429,18 @@ const [accepted, setAccepted] = useState(false)
395
429
  ### Switch
396
430
 
397
431
  **Import:** `import { Switch } from '@retray-dev/ui-kit'`
398
- **When to use:** Binary on/off settings. Animated thumb.
432
+ **When to use:** Binary on/off settings.
399
433
 
400
434
  | Prop | Type | Default | Notes |
401
435
  |------|------|---------|-------|
402
436
  | checked | `boolean` | `false` | — |
403
437
  | onCheckedChange | `(checked: boolean) => void` | — | — |
404
- | disabled | `boolean` | — | |
438
+ | disabled | `boolean` | — | Reduces opacity to 0.45 |
439
+ | style | `ViewStyle` | — | — |
440
+
441
+ **Dimensions:** Track 56×32pt, Thumb 24×24pt with 4pt offset from edges.
442
+
443
+ **Animation:** Thumb translates via spring (bounciness: 4); track color transitions via opacity timing (150ms).
405
444
 
406
445
  **Example:**
407
446
  ```tsx
@@ -413,16 +452,16 @@ const [accepted, setAccepted] = useState(false)
413
452
  ### Toggle
414
453
 
415
454
  **Import:** `import { Toggle } from '@retray-dev/ui-kit'`
416
- **When to use:** Toggleable button (e.g., bold/italic in a toolbar). Unlike Switch, it looks like a button.
455
+ **When to use:** Toggleable button (e.g., bold/italic in a toolbar). Looks like a button, unlike `Switch`.
417
456
 
418
457
  | Prop | Type | Default | Notes |
419
458
  |------|------|---------|-------|
420
459
  | pressed | `boolean` | `false` | — |
421
460
  | onPressedChange | `(pressed: boolean) => void` | — | — |
422
461
  | variant | `'default' \| 'outline'` | `'default'` | `outline` adds a border when unpressed |
423
- | size | `'sm' \| 'md' \| 'lg'` | `'md'` | |
462
+ | size | `'sm' \| 'md' \| 'lg'` | `'md'` | sm=minH 40pt, md=minH 44pt, lg=minH 48pt |
424
463
  | label | `string` | — | Text label |
425
- | icon | `ReactNode` | — | Icon (can be combined with label) |
464
+ | icon | `ReactNode` | — | Icon can be combined with `label` |
426
465
 
427
466
  **Example:**
428
467
  ```tsx
@@ -437,10 +476,11 @@ const [accepted, setAccepted] = useState(false)
437
476
 
438
477
  | Prop | Type | Default | Notes |
439
478
  |------|------|---------|-------|
440
- | options | `RadioOption[]` | required | Each option: `{ label, value, disabled? }` |
441
- | value | `string` | — | Selected value |
479
+ | options | `RadioOption[]` | required | Each option: `{ label: string, value: string, disabled?: boolean }` |
480
+ | value | `string` | — | Currently selected value |
442
481
  | onValueChange | `(value: string) => void` | — | — |
443
482
  | orientation | `'vertical' \| 'horizontal'` | `'vertical'` | — |
483
+ | style | `ViewStyle` | — | — |
444
484
 
445
485
  **Example:**
446
486
  ```tsx
@@ -464,13 +504,14 @@ const [accepted, setAccepted] = useState(false)
464
504
 
465
505
  | Prop | Type | Default | Notes |
466
506
  |------|------|---------|-------|
467
- | options | `SelectOption[]` | required | Each option: `{ label, value, disabled? }` |
507
+ | options | `SelectOption[]` | required | Each option: `{ label: string, value: string, disabled?: boolean }` |
468
508
  | value | `string` | — | Selected value |
469
509
  | onValueChange | `(value: string) => void` | — | — |
470
- | placeholder | `string` | `'Select an option'` | |
510
+ | placeholder | `string` | `'Select an option'` | Shown when no value is selected |
471
511
  | label | `string` | — | Label above the trigger |
472
- | error | `string` | — | Error text below |
512
+ | error | `string` | — | Error text below the trigger |
473
513
  | disabled | `boolean` | — | — |
514
+ | style | `ViewStyle` | — | — |
474
515
 
475
516
  **Example:**
476
517
  ```tsx
@@ -495,10 +536,13 @@ const [accepted, setAccepted] = useState(false)
495
536
  | value | `number` | `0` | — |
496
537
  | minimumValue | `number` | `0` | — |
497
538
  | maximumValue | `number` | `1` | — |
498
- | step | `number` | `0` | `0` means continuous |
539
+ | step | `number` | `0` | `0` means continuous (no snapping) |
499
540
  | onValueChange | `(value: number) => void` | — | Fires while dragging |
500
- | onSlidingComplete | `(value: number) => void` | — | Fires on release |
541
+ | onSlidingComplete | `(value: number) => void` | — | Fires on finger release |
501
542
  | disabled | `boolean` | — | — |
543
+ | style | `ViewStyle` | — | — |
544
+
545
+ **Dimensions:** Container height=32pt, track height=6pt, thumb 28×28pt. Uses `PanResponder` internally.
502
546
 
503
547
  **Example:**
504
548
  ```tsx
@@ -507,8 +551,6 @@ const [accepted, setAccepted] = useState(false)
507
551
 
508
552
  ---
509
553
 
510
-
511
-
512
554
  ### Tabs
513
555
 
514
556
  **Import:** `import { Tabs, TabsContent } from '@retray-dev/ui-kit'`
@@ -516,18 +558,22 @@ const [accepted, setAccepted] = useState(false)
516
558
 
517
559
  | Prop | Type | Default | Notes |
518
560
  |------|------|---------|-------|
519
- | tabs | `TabItem[]` | required | Each item: `{ label, value }` |
561
+ | tabs | `TabItem[]` | required | Each item: `{ label: string, value: string }` |
520
562
  | value | `string` | — | Controlled active tab |
521
563
  | onValueChange | `(value: string) => void` | — | — |
522
564
  | children | `ReactNode` | — | `TabsContent` components |
565
+ | style | `ViewStyle` | — | — |
523
566
 
524
567
  **`TabsContent` Props:**
525
568
 
526
569
  | Prop | Type | Notes |
527
570
  |------|------|-------|
528
- | value | `string` | Must match a tab value |
529
- | activeValue | `string` | Pass the current active tab value |
571
+ | value | `string` | Must match a tab value in `tabs` |
572
+ | activeValue | `string` | Pass the current active tab content is hidden when not active |
530
573
  | children | `ReactNode` | — |
574
+ | style | `ViewStyle` | — |
575
+
576
+ **Animation:** An absolutely-positioned pill slides and resizes via spring (speed: 20, bounciness: 0) to track the active tab.
531
577
 
532
578
  **Example:**
533
579
  ```tsx
@@ -556,9 +602,12 @@ const [tab, setTab] = useState('profile')
556
602
 
557
603
  | Prop | Type | Default | Notes |
558
604
  |------|------|---------|-------|
559
- | items | `AccordionItem[]` | required | Each: `{ value, trigger: string, content: ReactNode }` |
560
- | type | `'single' \| 'multiple'` | `'single'` | `single`: only one open at a time. `multiple`: many can be open |
561
- | defaultValue | `string \| string[]` | — | Initially open item(s) |
605
+ | items | `AccordionItem[]` | required | Each: `{ value: string, trigger: string, content: ReactNode }` |
606
+ | type | `'single' \| 'multiple'` | `'single'` | `single`: only one open at a time. `multiple`: any number can be open |
607
+ | defaultValue | `string \| string[]` | — | Initially open item(s). Use `string[]` with `type='multiple'` |
608
+ | style | `ViewStyle` | — | — |
609
+
610
+ **Animation:** Height and chevron rotation are animated on the UI thread via `react-native-reanimated` (`withTiming`, `useSharedValue`). `Easing.out(Easing.ease)` for expand, `Easing.in(Easing.ease)` for collapse (220ms, 60 fps).
562
611
 
563
612
  **Example:**
564
613
  ```tsx
@@ -573,38 +622,38 @@ const [tab, setTab] = useState('profile')
573
622
 
574
623
  ---
575
624
 
576
-
577
-
578
625
  ### Sheet
579
626
 
580
627
  **Import:** `import { Sheet, BottomSheetModalProvider } from '@retray-dev/ui-kit'`
581
628
  **When to use:** Bottom sheet with physics-based gestures, rubber-band overscroll, and snap points. Powered by `@gorhom/bottom-sheet`.
582
629
 
583
- **Setup:** Wrap your app root with `BottomSheetModalProvider` (alongside `ThemeProvider`):
630
+ **Required setup** — add to your app root (see Setup section above):
584
631
  ```tsx
585
- import { ThemeProvider, BottomSheetModalProvider } from '@retray-dev/ui-kit'
632
+ import { GestureHandlerRootView } from 'react-native-gesture-handler'
633
+ import { BottomSheetModalProvider } from '@retray-dev/ui-kit'
586
634
 
587
- <ThemeProvider>
635
+ <GestureHandlerRootView style={{ flex: 1 }}>
588
636
  <BottomSheetModalProvider>
589
637
  {/* rest of app */}
590
638
  </BottomSheetModalProvider>
591
- </ThemeProvider>
639
+ </GestureHandlerRootView>
592
640
  ```
593
641
 
594
642
  **Peer dependencies** (install in your app):
595
643
  ```bash
596
- pnpm add @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler
644
+ pnpm add @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-worklets
597
645
  ```
598
- Add `react-native-reanimated/plugin` to your `babel.config.js` plugins.
646
+ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to your `babel.config.js` plugins.
599
647
 
600
648
  | Prop | Type | Default | Notes |
601
649
  |------|------|---------|-------|
602
- | open | `boolean` | required | |
650
+ | open | `boolean` | required | `true` presents the sheet, `false` dismisses it |
603
651
  | onClose | `() => void` | required | Called on swipe-dismiss or backdrop press |
604
652
  | snapPoints | `(string \| number)[]` | `['50%']` | Snap positions, e.g. `['40%', '80%']` |
605
653
  | title | `string` | — | — |
606
654
  | description | `string` | — | — |
607
655
  | children | `ReactNode` | — | — |
656
+ | style | `ViewStyle` | — | Applied to the inner content container |
608
657
 
609
658
  **Example:**
610
659
  ```tsx
@@ -615,14 +664,14 @@ Add `react-native-reanimated/plugin` to your `babel.config.js` plugins.
615
664
 
616
665
  ---
617
666
 
618
-
619
-
620
667
  ### Toast / useToast
621
668
 
622
669
  **Import:** `import { ToastProvider, useToast } from '@retray-dev/ui-kit'`
623
670
  **When to use:** Ephemeral feedback messages (save success, network error, copy confirmation).
624
671
 
625
- **Setup:** `ToastProvider` must be in the app root (see Setup section above).
672
+ **Required setup** — `ToastProvider` must wrap your app inside `SafeAreaProvider` (see Setup section above).
673
+
674
+ **Peer dependency:** `react-native-safe-area-context` — required for `useSafeAreaInsets` inside `ToastProvider`.
626
675
 
627
676
  ```tsx
628
677
  import { useToast } from '@retray-dev/ui-kit'
@@ -651,4 +700,11 @@ function MyComponent() {
651
700
  | variant | `'default' \| 'destructive' \| 'success'` | `'default'` | `default`: dark background. `destructive`: red. `success`: green |
652
701
  | duration | `number` (ms) | `3000` | Auto-dismiss after this delay |
653
702
 
654
- **Notes:** Max 3 toasts shown simultaneously. Toasts appear at the top of the screen. `dismiss(id)` dismisses programmatically (the `id` is returned by `toast()`... track it if needed).
703
+ **`dismiss(id)`:** Dismiss a toast programmatically. The `id` is returned by the `toast()` call — store it if you need programmatic dismissal.
704
+
705
+ **Notes:**
706
+ - Max 3 toasts shown simultaneously (oldest is removed when a 4th arrives)
707
+ - Toasts appear at the top of the screen, below the status bar (dynamic safe area inset)
708
+ - Entrance: `withTiming(120ms, Easing.out(Easing.exp))` slide-down + opacity fade — fast, sharp feel
709
+ - Exit: `withTiming(200ms)` slide-up + opacity fade
710
+ - Swipe left or right to dismiss early (threshold: 80px or 800 pt/s velocity)
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @retray-dev/ui-kit
1
+ # 📦 @retray-dev/ui-kit
2
2
 
3
3
  A personal React Native / Expo UI component library with a built-in design system, dark mode support, haptic feedback, and smooth animations.
4
4
 
@@ -55,9 +55,7 @@ export default function App() {
55
55
  <GestureHandlerRootView style={{ flex: 1 }}>
56
56
  <ThemeProvider colorScheme="system">
57
57
  <BottomSheetModalProvider>
58
- <ToastProvider>
59
- {/* your app */}
60
- </ToastProvider>
58
+ <ToastProvider>{/* your app */}</ToastProvider>
61
59
  </BottomSheetModalProvider>
62
60
  </ThemeProvider>
63
61
  </GestureHandlerRootView>
@@ -66,13 +64,13 @@ export default function App() {
66
64
  }
67
65
  ```
68
66
 
69
- | Provider | Required by |
70
- |----------|-------------|
71
- | `SafeAreaProvider` | `ToastProvider` (uses `useSafeAreaInsets`) |
72
- | `GestureHandlerRootView` | `Sheet` (uses `@gorhom/bottom-sheet`) |
73
- | `ThemeProvider` | All components |
74
- | `BottomSheetModalProvider` | `Sheet` |
75
- | `ToastProvider` | `useToast` hook |
67
+ | Provider | Required by |
68
+ | -------------------------- | ------------------------------------------ |
69
+ | `SafeAreaProvider` | `ToastProvider` (uses `useSafeAreaInsets`) |
70
+ | `GestureHandlerRootView` | `Sheet` (uses `@gorhom/bottom-sheet`) |
71
+ | `ThemeProvider` | All components |
72
+ | `BottomSheetModalProvider` | `Sheet` |
73
+ | `ToastProvider` | `useToast` hook |
76
74
 
77
75
  ## Theme
78
76
 
@@ -99,14 +97,14 @@ const { colors, colorScheme } = useTheme()
99
97
 
100
98
  ## Components
101
99
 
102
- | Category | Components |
103
- |----------|------------|
104
- | Display | `Text`, `Badge`, `Avatar`, `Separator`, `Spinner`, `Skeleton`, `Progress` |
105
- | Surfaces | `Card`, `Alert`, `EmptyState` |
106
- | Form | `Button`, `Input`, `Textarea`, `Checkbox`, `Switch`, `Toggle`, `RadioGroup`, `Select`, `Slider` |
107
- | Composition | `Tabs`, `Accordion` |
108
- | Overlays | `Sheet` |
109
- | Feedback | `Toast` / `ToastProvider` / `useToast` |
100
+ | Category | Components |
101
+ | ----------- | ----------------------------------------------------------------------------------------------- |
102
+ | Display | `Text`, `Badge`, `Avatar`, `Separator`, `Spinner`, `Skeleton`, `Progress` |
103
+ | Surfaces | `Card`, `Alert`, `EmptyState` |
104
+ | Form | `Button`, `Input`, `Textarea`, `Checkbox`, `Switch`, `Toggle`, `RadioGroup`, `Select`, `Slider` |
105
+ | Composition | `Tabs`, `Accordion` |
106
+ | Overlays | `Sheet` |
107
+ | Feedback | `Toast` / `ToastProvider` / `useToast` |
110
108
 
111
109
  ### Quick examples
112
110
 
@@ -143,6 +141,7 @@ Full props reference and more examples are available in [COMPONENTS.md](./COMPON
143
141
 
144
142
  ```markdown
145
143
  ## UI Components
144
+
146
145
  @./node_modules/@retray-dev/ui-kit/COMPONENTS.md
147
146
  ```
148
147
 
package/dist/index.d.mts CHANGED
@@ -20,6 +20,8 @@ type ThemeColors = {
20
20
  border: string;
21
21
  input: string;
22
22
  ring: string;
23
+ success: string;
24
+ successForeground: string;
23
25
  };
24
26
  type Theme = {
25
27
  light?: Partial<ThemeColors>;
@@ -68,8 +70,12 @@ interface ButtonProps extends TouchableOpacityProps {
68
70
  /** Replaces the label with a spinner and forces `disabled`. */
69
71
  loading?: boolean;
70
72
  fullWidth?: boolean;
73
+ /** Icon rendered alongside the label. */
74
+ icon?: React.ReactNode;
75
+ /** Side the icon appears on. Defaults to `'left'`. */
76
+ iconPosition?: 'left' | 'right';
71
77
  }
72
- declare function Button({ label, variant, size, loading, fullWidth, disabled, style, onPress, ...props }: ButtonProps): React.JSX.Element;
78
+ declare function Button({ label, variant, size, loading, fullWidth, icon, iconPosition, disabled, style, onPress, ...props }: ButtonProps): React.JSX.Element;
73
79
 
74
80
  type TextVariant = 'h1' | 'h2' | 'h3' | 'body' | 'caption' | 'label';
75
81
  interface TextProps extends TextProps$1 {
@@ -80,10 +86,14 @@ declare function Text({ variant, color, style, children, ...props }: TextProps):
80
86
 
81
87
  interface InputProps extends TextInputProps {
82
88
  label?: string;
89
+ /** Red helper text below the input; also changes border to `destructive` color. Takes priority over `hint`. */
83
90
  error?: string;
91
+ /** Helper text shown below the input when there is no error. */
84
92
  hint?: string;
93
+ /** Style for the outer container `View`. Use `style` (from `TextInputProps`) to style the `TextInput` itself. */
94
+ containerStyle?: ViewStyle;
85
95
  }
86
- declare function Input({ label, error, hint, style, onFocus, onBlur, ...props }: InputProps): React.JSX.Element;
96
+ declare function Input({ label, error, hint, containerStyle, style, onFocus, onBlur, ...props }: InputProps): React.JSX.Element;
87
97
 
88
98
  type BadgeVariant = 'default' | 'secondary' | 'destructive' | 'outline';
89
99
  interface BadgeProps {
@@ -147,7 +157,9 @@ declare function Skeleton({ width, height, borderRadius, style }: SkeletonProps)
147
157
 
148
158
  type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';
149
159
  interface AvatarProps {
160
+ /** Remote image URI. Falls back to `fallback` initials on error or when omitted. */
150
161
  src?: string;
162
+ /** Up to 2 characters shown when the image is unavailable. Auto-uppercased. Defaults to `'?'`. */
151
163
  fallback?: string;
152
164
  size?: AvatarSize;
153
165
  style?: ViewStyle;
@@ -165,7 +177,9 @@ interface AlertProps {
165
177
  declare function Alert({ title, description, variant, icon, style }: AlertProps): React.JSX.Element;
166
178
 
167
179
  interface ProgressProps {
180
+ /** Current progress value. Clamped to `[0, max]`. Defaults to `0`. */
168
181
  value?: number;
182
+ /** Maximum value. Defaults to `100`. */
169
183
  max?: number;
170
184
  style?: ViewStyle;
171
185
  }
@@ -182,11 +196,16 @@ declare function EmptyState({ icon, title, description, action, style }: EmptySt
182
196
 
183
197
  interface TextareaProps extends TextInputProps {
184
198
  label?: string;
199
+ /** Red helper text below the textarea; also changes border to `destructive` color. Takes priority over `hint`. */
185
200
  error?: string;
201
+ /** Helper text shown below the textarea when there is no error. */
186
202
  hint?: string;
203
+ /** Number of visible text rows. Defaults to `4`. Controls `numberOfLines` and `minHeight`. */
187
204
  rows?: number;
205
+ /** Style for the outer container `View`. Use `style` (from `TextInputProps`) to style the `TextInput` itself. */
206
+ containerStyle?: ViewStyle;
188
207
  }
189
- declare function Textarea({ label, error, hint, rows, style, onFocus, onBlur, ...props }: TextareaProps): React.JSX.Element;
208
+ declare function Textarea({ label, error, hint, rows, containerStyle, style, onFocus, onBlur, ...props }: TextareaProps): React.JSX.Element;
190
209
 
191
210
  interface CheckboxProps {
192
211
  checked?: boolean;
@@ -195,7 +214,7 @@ interface CheckboxProps {
195
214
  disabled?: boolean;
196
215
  style?: ViewStyle;
197
216
  }
198
- declare function Checkbox({ checked, onCheckedChange, label, disabled, style }: CheckboxProps): React.JSX.Element;
217
+ declare function Checkbox({ checked, onCheckedChange, label, disabled, style, }: CheckboxProps): React.JSX.Element;
199
218
 
200
219
  interface SwitchProps {
201
220
  checked?: boolean;
@@ -237,6 +256,10 @@ interface TabItem {
237
256
  }
238
257
  interface TabsProps {
239
258
  tabs: TabItem[];
259
+ /**
260
+ * Controlled active tab value. When omitted the component manages state internally
261
+ * (uncontrolled), defaulting to the first tab.
262
+ */
240
263
  value?: string;
241
264
  onValueChange?: (value: string) => void;
242
265
  children?: React.ReactNode;
@@ -270,11 +293,15 @@ interface AccordionProps {
270
293
  declare function Accordion({ items, type, defaultValue, style }: AccordionProps): React.JSX.Element;
271
294
 
272
295
  interface SliderProps {
296
+ /** Current value. Controlled when provided; falls back to internal state otherwise. */
273
297
  value?: number;
274
298
  minimumValue?: number;
275
299
  maximumValue?: number;
300
+ /** Snap interval. `0` (default) means continuous (no snapping). */
276
301
  step?: number;
302
+ /** Called on every move while dragging. */
277
303
  onValueChange?: (value: number) => void;
304
+ /** Called once when the user releases the thumb. */
278
305
  onSlidingComplete?: (value: number) => void;
279
306
  disabled?: boolean;
280
307
  style?: ViewStyle;
@@ -287,7 +314,12 @@ interface SheetProps {
287
314
  title?: string;
288
315
  description?: string;
289
316
  children?: React.ReactNode;
317
+ /**
318
+ * Heights the sheet can snap to. Accepts percentage strings (`'50%'`) or
319
+ * absolute point values (`300`). Defaults to `['50%']`.
320
+ */
290
321
  snapPoints?: (string | number)[];
322
+ /** Style for the inner `BottomSheetView` content container. */
291
323
  style?: ViewStyle;
292
324
  }
293
325
  declare function Sheet({ open, onClose, title, description, children, snapPoints, style, }: SheetProps): React.JSX.Element;
@@ -301,8 +333,10 @@ interface SelectProps {
301
333
  options: SelectOption[];
302
334
  value?: string;
303
335
  onValueChange?: (value: string) => void;
336
+ /** Text shown when no option is selected. Defaults to `'Select an option'`. */
304
337
  placeholder?: string;
305
338
  label?: string;
339
+ /** Red helper text; also changes trigger border to `destructive` color. */
306
340
  error?: string;
307
341
  disabled?: boolean;
308
342
  style?: ViewStyle;
@@ -315,6 +349,7 @@ interface ToastItem {
315
349
  title?: string;
316
350
  description?: string;
317
351
  variant?: ToastVariant;
352
+ /** Auto-dismiss delay in milliseconds. Defaults to `3000`. */
318
353
  duration?: number;
319
354
  }
320
355
  interface ToastContextValue {