@retray-dev/ui-kit 1.0.0 → 1.6.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
1
+ # @retray-dev/ui-kit — Component Reference (v1.6.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
 
@@ -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 |
@@ -59,7 +74,7 @@ function MyComponent() {
59
74
 
60
75
  ## Theme Tokens
61
76
 
62
- All 18 tokens are available via `useTheme().colors`.
77
+ All 20 tokens are available via `useTheme().colors`.
63
78
 
64
79
  | Token | Light | Dark | Semantic Role |
65
80
  |-------|-------|------|---------------|
@@ -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 |
@@ -80,6 +95,8 @@ All 18 tokens are available via `useTheme().colors`.
80
95
  | `border` | `#e5e5e5` | `#2a2a2a` | Borders and dividers |
81
96
  | `input` | `#e5e5e5` | `#2a2a2a` | Input field border color |
82
97
  | `ring` | `#a3a3a3` | `#d4d4d4` | Focus ring color |
98
+ | `success` | `#16a34a` | `#22c55e` | Success state (Toast success variant) |
99
+ | `successForeground` | `#ffffff` | `#ffffff` | Text on success background |
83
100
 
84
101
  ---
85
102
 
@@ -91,12 +108,12 @@ All 18 tokens are available via `useTheme().colors`.
91
108
 
92
109
  **Import:** `import { Text } from '@retray-dev/ui-kit'`
93
110
  **When to use:** All text in the app. Replaces React Native's `Text` with semantic variants.
94
- **Extends:** `TextProps` from React Native.
111
+ **Extends:** `TextProps` from React Native — all native props pass through.
95
112
 
96
113
  | Prop | Type | Default | Notes |
97
114
  |------|------|---------|-------|
98
115
  | 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` |
116
+ | color | `string` | — | Override the color. Defaults to `foreground`, except `caption` which uses `mutedForeground` |
100
117
 
101
118
  **Sizes:**
102
119
  - `h1`: 32px / 700 weight
@@ -127,22 +144,27 @@ All 18 tokens are available via `useTheme().colors`.
127
144
  | label | `string` | required | Button text |
128
145
  | variant | `'primary' \| 'secondary' \| 'outline' \| 'ghost'` | `'primary'` | Visual style |
129
146
  | size | `'sm' \| 'md' \| 'lg'` | `'md'` | — |
130
- | loading | `boolean` | `false` | Replaces label with spinner and forces disabled |
131
- | fullWidth | `boolean` | `false` | Stretches to container width |
147
+ | loading | `boolean` | `false` | Replaces label with a spinner and forces disabled state |
148
+ | fullWidth | `boolean` | `false` | Stretches to container width (`alignSelf: 'stretch'`) |
132
149
  | disabled | `boolean` | — | Reduces opacity to 0.45 |
150
+ | icon | `React.ReactNode` | — | Icon rendered alongside the label |
151
+ | iconPosition | `'left' \| 'right'` | `'left'` | Side the icon appears on |
133
152
 
134
153
  **Variants:**
135
154
  - `primary`: filled with `primary` token — main actions
136
155
  - `secondary`: filled with `secondary` token — less prominent actions
137
- - `outline`: transparent with `border` — alternative/secondary without fill
156
+ - `outline`: transparent with `border` — alternative without fill
138
157
  - `ghost`: fully transparent — in-context or low-emphasis actions
139
158
 
159
+ **Animations:** Scale springs to 0.97 on `onPressIn`, back to 1.0 on `onPressOut`.
160
+
140
161
  **Example:**
141
162
  ```tsx
142
163
  <Button label="Save changes" onPress={handleSave} />
143
164
  <Button label="Cancel" variant="ghost" onPress={onCancel} />
144
165
  <Button label="Delete" variant="outline" size="sm" />
145
166
  <Button label="Submitting..." loading fullWidth />
167
+ <Button label="Share" icon={<ShareIcon size={16} />} iconPosition="right" />
146
168
  ```
147
169
 
148
170
  ---
@@ -151,13 +173,15 @@ All 18 tokens are available via `useTheme().colors`.
151
173
 
152
174
  **Import:** `import { Input } from '@retray-dev/ui-kit'`
153
175
  **When to use:** Single-line text entry. Includes built-in label, error, and hint support.
154
- **Extends:** `TextInputProps` from React Native.
176
+ **Extends:** `TextInputProps` from React Native — all native props pass through.
155
177
 
156
178
  | Prop | Type | Default | Notes |
157
179
  |------|------|---------|-------|
158
180
  | 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) |
181
+ | error | `string` | — | Shows error text below; turns border red (`destructive` token) |
182
+ | hint | `string` | — | Helper text below (hidden when `error` is set) |
183
+
184
+ **Border colors:** `destructive` when `error` is set, `ring` when focused, `border` otherwise.
161
185
 
162
186
  **Example:**
163
187
  ```tsx
@@ -168,18 +192,62 @@ All 18 tokens are available via `useTheme().colors`.
168
192
 
169
193
  ---
170
194
 
195
+ ### CurrencyInput
196
+
197
+ **Import:** `import { CurrencyInput } from '@retray-dev/ui-kit'`
198
+ **When to use:** Monetary or numeric inputs that need thousands formatting while typing. Wraps `Input` — shares the same visual design, label, error, and hint behavior.
199
+
200
+ | Prop | Type | Default | Notes |
201
+ |------|------|---------|-------|
202
+ | value | `string` | — | Controlled display value (includes prefix, e.g. `'$1,234'`) |
203
+ | onChangeText | `(formatted: string) => void` | — | Called with the formatted display string |
204
+ | onChangeValue | `(raw: number) => void` | — | Called with the parsed number (no separators, no prefix) |
205
+ | prefix | `string` | `'$'` | Symbol prepended to the formatted value |
206
+ | thousandsSeparator | `'.' \| ','` | `'.'` | Character used to separate groups of three digits |
207
+ | label | `string` | — | Label above the input |
208
+ | error | `string` | — | Error text below; turns border red |
209
+ | hint | `string` | — | Helper text below (hidden when `error` is set) |
210
+ | placeholder | `string` | `'$0'` | Defaults to `prefix + '0'` |
211
+ | editable | `boolean` | — | Pass `false` to disable editing |
212
+ | containerStyle | `ViewStyle` | — | Style for the outer container |
213
+
214
+ **Example:**
215
+ ```tsx
216
+ const [display, setDisplay] = useState('')
217
+ const [amount, setAmount] = useState(0)
218
+
219
+ <CurrencyInput
220
+ label="Amount"
221
+ value={display}
222
+ onChangeText={setDisplay}
223
+ onChangeValue={setAmount}
224
+ hint={`Parsed: ${amount}`}
225
+ />
226
+
227
+ // European format (dot as thousands separator)
228
+ <CurrencyInput
229
+ prefix="€"
230
+ thousandsSeparator="."
231
+ value={display}
232
+ onChangeText={setDisplay}
233
+ onChangeValue={setAmount}
234
+ />
235
+ ```
236
+
237
+ ---
238
+
171
239
  ### Textarea
172
240
 
173
241
  **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.
242
+ **When to use:** Multi-line text entry. Same API as `Input` plus `rows`.
243
+ **Extends:** `TextInputProps` from React Native — all native props pass through.
176
244
 
177
245
  | Prop | Type | Default | Notes |
178
246
  |------|------|---------|-------|
179
247
  | label | `string` | — | Label above |
180
248
  | error | `string` | — | Error text below; red border |
181
249
  | hint | `string` | — | Helper text below |
182
- | rows | `number` | `4` | Sets minimum height (each row ≈ 24px) |
250
+ | rows | `number` | `4` | Sets `minHeight` (each row ≈ 28px) |
183
251
 
184
252
  **Example:**
185
253
  ```tsx
@@ -197,6 +265,7 @@ All 18 tokens are available via `useTheme().colors`.
197
265
  |------|------|---------|-------|
198
266
  | label | `string` | required | — |
199
267
  | variant | `'default' \| 'secondary' \| 'destructive' \| 'outline'` | `'default'` | — |
268
+ | style | `ViewStyle` | — | — |
200
269
 
201
270
  **Example:**
202
271
  ```tsx
@@ -216,8 +285,9 @@ All 18 tokens are available via `useTheme().colors`.
216
285
  | Prop | Type | Default | Notes |
217
286
  |------|------|---------|-------|
218
287
  | 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 |
288
+ | fallback | `string` | — | Text shown when image fails or `src` is absent — first 2 chars, uppercased |
289
+ | size | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | sm=24, md=32, lg=48, xl=64 (diameter in pt) |
290
+ | style | `ViewStyle` | — | — |
221
291
 
222
292
  **Example:**
223
293
  ```tsx
@@ -234,7 +304,8 @@ All 18 tokens are available via `useTheme().colors`.
234
304
 
235
305
  | Prop | Type | Default | Notes |
236
306
  |------|------|---------|-------|
237
- | orientation | `'horizontal' \| 'vertical'` | `'horizontal'` | Vertical requires a parent with defined height |
307
+ | orientation | `'horizontal' \| 'vertical'` | `'horizontal'` | Vertical requires a parent with a defined height |
308
+ | style | `ViewStyle` | — | — |
238
309
 
239
310
  **Example:**
240
311
  ```tsx
@@ -251,11 +322,12 @@ All 18 tokens are available via `useTheme().colors`.
251
322
  ### Spinner
252
323
 
253
324
  **Import:** `import { Spinner } from '@retray-dev/ui-kit'`
254
- **When to use:** Loading state indicator.
325
+ **When to use:** Loading state indicator. Wraps React Native's `ActivityIndicator`.
326
+ **Extends:** `ActivityIndicatorProps` (except `size`).
255
327
 
256
328
  | Prop | Type | Default | Notes |
257
329
  |------|------|---------|-------|
258
- | size | `'sm' \| 'md' \| 'lg'` | `'md'` | sm/md map to RN `'small'`, lg maps to `'large'` |
330
+ | size | `'sm' \| 'md' \| 'lg'` | `'md'` | `sm`/`md` map to RN `'small'`, `lg` maps to `'large'` |
259
331
  | color | `string` | `primary` token | Override spinner color |
260
332
 
261
333
  **Example:**
@@ -269,13 +341,14 @@ All 18 tokens are available via `useTheme().colors`.
269
341
  ### Skeleton
270
342
 
271
343
  **Import:** `import { Skeleton } from '@retray-dev/ui-kit'`
272
- **When to use:** Placeholder while content is loading. Pulses with animation.
344
+ **When to use:** Placeholder while content is loading. Pulses with a looping opacity animation.
273
345
 
274
346
  | Prop | Type | Default | Notes |
275
347
  |------|------|---------|-------|
276
348
  | width | `number \| string` | `'100%'` | — |
277
349
  | height | `number` | `16` | — |
278
350
  | borderRadius | `number` | `6` | — |
351
+ | style | `ViewStyle` | — | — |
279
352
 
280
353
  **Example:**
281
354
  ```tsx
@@ -294,6 +367,9 @@ All 18 tokens are available via `useTheme().colors`.
294
367
  |------|------|---------|-------|
295
368
  | value | `number` | `0` | Current value |
296
369
  | max | `number` | `100` | Maximum value |
370
+ | style | `ViewStyle` | — | — |
371
+
372
+ **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
373
 
298
374
  **Example:**
299
375
  ```tsx
@@ -326,21 +402,26 @@ All sub-components accept a `style` prop for overrides.
326
402
  </Card>
327
403
  ```
328
404
 
329
- **Notes:** `CardHeader` and `CardContent` have `padding: 24`. `CardFooter` has `paddingTop: 0` so it connects naturally to `CardContent`.
405
+ **Notes:**
406
+ - `CardHeader` and `CardContent` have `padding: 24`
407
+ - `CardFooter` has `paddingTop: 0` so it connects naturally to `CardContent`
408
+ - `CardTitle`: 18px / 600 weight, `cardForeground` color
409
+ - `CardDescription`: 14px, `mutedForeground` color
330
410
 
331
411
  ---
332
412
 
333
413
  ### Alert
334
414
 
335
415
  **Import:** `import { Alert } from '@retray-dev/ui-kit'`
336
- **When to use:** Inline feedback messages (info, success, warning, error). Not for toasts.
416
+ **When to use:** Inline feedback messages (info, success, warning, error). Not for transient toasts.
337
417
 
338
418
  | Prop | Type | Default | Notes |
339
419
  |------|------|---------|-------|
340
420
  | title | `string` | — | Bold heading |
341
421
  | description | `string` | — | Detail text |
342
- | variant | `'default' \| 'destructive'` | `'default'` | `destructive` turns border and text red |
343
- | icon | `ReactNode` | — | Icon placed to the left |
422
+ | variant | `'default' \| 'destructive'` | `'default'` | `destructive` turns border and text to `destructive` token |
423
+ | icon | `ReactNode` | — | Icon placed to the left of the text content |
424
+ | style | `ViewStyle` | — | — |
344
425
 
345
426
  **Example:**
346
427
  ```tsx
@@ -359,8 +440,9 @@ All sub-components accept a `style` prop for overrides.
359
440
  |------|------|---------|-------|
360
441
  | title | `string` | required | — |
361
442
  | description | `string` | — | — |
362
- | icon | `ReactNode` | — | Shown in a muted square above the text |
443
+ | icon | `ReactNode` | — | Shown in a 48×48 muted square above the text |
363
444
  | action | `ReactNode` | — | Usually a `Button`, placed below the text |
445
+ | style | `ViewStyle` | — | — |
364
446
 
365
447
  **Example:**
366
448
  ```tsx
@@ -383,6 +465,7 @@ All sub-components accept a `style` prop for overrides.
383
465
  | onCheckedChange | `(checked: boolean) => void` | — | — |
384
466
  | label | `string` | — | Text to the right of the box |
385
467
  | disabled | `boolean` | — | — |
468
+ | style | `ViewStyle` | — | — |
386
469
 
387
470
  **Example:**
388
471
  ```tsx
@@ -395,13 +478,18 @@ const [accepted, setAccepted] = useState(false)
395
478
  ### Switch
396
479
 
397
480
  **Import:** `import { Switch } from '@retray-dev/ui-kit'`
398
- **When to use:** Binary on/off settings. Animated thumb.
481
+ **When to use:** Binary on/off settings.
399
482
 
400
483
  | Prop | Type | Default | Notes |
401
484
  |------|------|---------|-------|
402
485
  | checked | `boolean` | `false` | — |
403
486
  | onCheckedChange | `(checked: boolean) => void` | — | — |
404
- | disabled | `boolean` | — | |
487
+ | disabled | `boolean` | — | Reduces opacity to 0.45 |
488
+ | style | `ViewStyle` | — | — |
489
+
490
+ **Dimensions:** Track 56×32pt, Thumb 24×24pt with 4pt offset from edges.
491
+
492
+ **Animation:** Thumb translates via spring (bounciness: 4); track color transitions via opacity timing (150ms).
405
493
 
406
494
  **Example:**
407
495
  ```tsx
@@ -413,16 +501,16 @@ const [accepted, setAccepted] = useState(false)
413
501
  ### Toggle
414
502
 
415
503
  **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.
504
+ **When to use:** Toggleable button (e.g., bold/italic in a toolbar). Looks like a button, unlike `Switch`.
417
505
 
418
506
  | Prop | Type | Default | Notes |
419
507
  |------|------|---------|-------|
420
508
  | pressed | `boolean` | `false` | — |
421
509
  | onPressedChange | `(pressed: boolean) => void` | — | — |
422
510
  | variant | `'default' \| 'outline'` | `'default'` | `outline` adds a border when unpressed |
423
- | size | `'sm' \| 'md' \| 'lg'` | `'md'` | |
511
+ | size | `'sm' \| 'md' \| 'lg'` | `'md'` | sm=minH 40pt, md=minH 44pt, lg=minH 48pt |
424
512
  | label | `string` | — | Text label |
425
- | icon | `ReactNode` | — | Icon (can be combined with label) |
513
+ | icon | `ReactNode` | — | Icon can be combined with `label` |
426
514
 
427
515
  **Example:**
428
516
  ```tsx
@@ -437,10 +525,11 @@ const [accepted, setAccepted] = useState(false)
437
525
 
438
526
  | Prop | Type | Default | Notes |
439
527
  |------|------|---------|-------|
440
- | options | `RadioOption[]` | required | Each option: `{ label, value, disabled? }` |
441
- | value | `string` | — | Selected value |
528
+ | options | `RadioOption[]` | required | Each option: `{ label: string, value: string, disabled?: boolean }` |
529
+ | value | `string` | — | Currently selected value |
442
530
  | onValueChange | `(value: string) => void` | — | — |
443
531
  | orientation | `'vertical' \| 'horizontal'` | `'vertical'` | — |
532
+ | style | `ViewStyle` | — | — |
444
533
 
445
534
  **Example:**
446
535
  ```tsx
@@ -464,13 +553,14 @@ const [accepted, setAccepted] = useState(false)
464
553
 
465
554
  | Prop | Type | Default | Notes |
466
555
  |------|------|---------|-------|
467
- | options | `SelectOption[]` | required | Each option: `{ label, value, disabled? }` |
556
+ | options | `SelectOption[]` | required | Each option: `{ label: string, value: string, disabled?: boolean }` |
468
557
  | value | `string` | — | Selected value |
469
558
  | onValueChange | `(value: string) => void` | — | — |
470
- | placeholder | `string` | `'Select an option'` | |
559
+ | placeholder | `string` | `'Select an option'` | Shown when no value is selected |
471
560
  | label | `string` | — | Label above the trigger |
472
- | error | `string` | — | Error text below |
561
+ | error | `string` | — | Error text below the trigger |
473
562
  | disabled | `boolean` | — | — |
563
+ | style | `ViewStyle` | — | — |
474
564
 
475
565
  **Example:**
476
566
  ```tsx
@@ -495,10 +585,13 @@ const [accepted, setAccepted] = useState(false)
495
585
  | value | `number` | `0` | — |
496
586
  | minimumValue | `number` | `0` | — |
497
587
  | maximumValue | `number` | `1` | — |
498
- | step | `number` | `0` | `0` means continuous |
588
+ | step | `number` | `0` | `0` means continuous (no snapping) |
499
589
  | onValueChange | `(value: number) => void` | — | Fires while dragging |
500
- | onSlidingComplete | `(value: number) => void` | — | Fires on release |
590
+ | onSlidingComplete | `(value: number) => void` | — | Fires on finger release |
501
591
  | disabled | `boolean` | — | — |
592
+ | style | `ViewStyle` | — | — |
593
+
594
+ **Dimensions:** Container height=32pt, track height=6pt, thumb 28×28pt. Uses `PanResponder` internally.
502
595
 
503
596
  **Example:**
504
597
  ```tsx
@@ -507,8 +600,6 @@ const [accepted, setAccepted] = useState(false)
507
600
 
508
601
  ---
509
602
 
510
-
511
-
512
603
  ### Tabs
513
604
 
514
605
  **Import:** `import { Tabs, TabsContent } from '@retray-dev/ui-kit'`
@@ -516,18 +607,22 @@ const [accepted, setAccepted] = useState(false)
516
607
 
517
608
  | Prop | Type | Default | Notes |
518
609
  |------|------|---------|-------|
519
- | tabs | `TabItem[]` | required | Each item: `{ label, value }` |
610
+ | tabs | `TabItem[]` | required | Each item: `{ label: string, value: string }` |
520
611
  | value | `string` | — | Controlled active tab |
521
612
  | onValueChange | `(value: string) => void` | — | — |
522
613
  | children | `ReactNode` | — | `TabsContent` components |
614
+ | style | `ViewStyle` | — | — |
523
615
 
524
616
  **`TabsContent` Props:**
525
617
 
526
618
  | Prop | Type | Notes |
527
619
  |------|------|-------|
528
- | value | `string` | Must match a tab value |
529
- | activeValue | `string` | Pass the current active tab value |
620
+ | value | `string` | Must match a tab value in `tabs` |
621
+ | activeValue | `string` | Pass the current active tab content is hidden when not active |
530
622
  | children | `ReactNode` | — |
623
+ | style | `ViewStyle` | — |
624
+
625
+ **Animation:** An absolutely-positioned pill slides and resizes via spring (speed: 20, bounciness: 0) to track the active tab.
531
626
 
532
627
  **Example:**
533
628
  ```tsx
@@ -556,9 +651,12 @@ const [tab, setTab] = useState('profile')
556
651
 
557
652
  | Prop | Type | Default | Notes |
558
653
  |------|------|---------|-------|
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) |
654
+ | items | `AccordionItem[]` | required | Each: `{ value: string, trigger: string, content: ReactNode }` |
655
+ | type | `'single' \| 'multiple'` | `'single'` | `single`: only one open at a time. `multiple`: any number can be open |
656
+ | defaultValue | `string \| string[]` | — | Initially open item(s). Use `string[]` with `type='multiple'` |
657
+ | style | `ViewStyle` | — | — |
658
+
659
+ **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
660
 
563
661
  **Example:**
564
662
  ```tsx
@@ -573,38 +671,38 @@ const [tab, setTab] = useState('profile')
573
671
 
574
672
  ---
575
673
 
576
-
577
-
578
674
  ### Sheet
579
675
 
580
676
  **Import:** `import { Sheet, BottomSheetModalProvider } from '@retray-dev/ui-kit'`
581
677
  **When to use:** Bottom sheet with physics-based gestures, rubber-band overscroll, and snap points. Powered by `@gorhom/bottom-sheet`.
582
678
 
583
- **Setup:** Wrap your app root with `BottomSheetModalProvider` (alongside `ThemeProvider`):
679
+ **Required setup** — add to your app root (see Setup section above):
584
680
  ```tsx
585
- import { ThemeProvider, BottomSheetModalProvider } from '@retray-dev/ui-kit'
681
+ import { GestureHandlerRootView } from 'react-native-gesture-handler'
682
+ import { BottomSheetModalProvider } from '@retray-dev/ui-kit'
586
683
 
587
- <ThemeProvider>
684
+ <GestureHandlerRootView style={{ flex: 1 }}>
588
685
  <BottomSheetModalProvider>
589
686
  {/* rest of app */}
590
687
  </BottomSheetModalProvider>
591
- </ThemeProvider>
688
+ </GestureHandlerRootView>
592
689
  ```
593
690
 
594
691
  **Peer dependencies** (install in your app):
595
692
  ```bash
596
- pnpm add @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler
693
+ pnpm add @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-worklets
597
694
  ```
598
- Add `react-native-reanimated/plugin` to your `babel.config.js` plugins.
695
+ Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to your `babel.config.js` plugins.
599
696
 
600
697
  | Prop | Type | Default | Notes |
601
698
  |------|------|---------|-------|
602
- | open | `boolean` | required | |
699
+ | open | `boolean` | required | `true` presents the sheet, `false` dismisses it |
603
700
  | onClose | `() => void` | required | Called on swipe-dismiss or backdrop press |
604
701
  | snapPoints | `(string \| number)[]` | `['50%']` | Snap positions, e.g. `['40%', '80%']` |
605
702
  | title | `string` | — | — |
606
703
  | description | `string` | — | — |
607
704
  | children | `ReactNode` | — | — |
705
+ | style | `ViewStyle` | — | Applied to the inner content container |
608
706
 
609
707
  **Example:**
610
708
  ```tsx
@@ -615,14 +713,14 @@ Add `react-native-reanimated/plugin` to your `babel.config.js` plugins.
615
713
 
616
714
  ---
617
715
 
618
-
619
-
620
716
  ### Toast / useToast
621
717
 
622
718
  **Import:** `import { ToastProvider, useToast } from '@retray-dev/ui-kit'`
623
719
  **When to use:** Ephemeral feedback messages (save success, network error, copy confirmation).
624
720
 
625
- **Setup:** `ToastProvider` must be in the app root (see Setup section above).
721
+ **Required setup** — `ToastProvider` must wrap your app inside `SafeAreaProvider` (see Setup section above).
722
+
723
+ **Peer dependency:** `react-native-safe-area-context` — required for `useSafeAreaInsets` inside `ToastProvider`.
626
724
 
627
725
  ```tsx
628
726
  import { useToast } from '@retray-dev/ui-kit'
@@ -651,4 +749,11 @@ function MyComponent() {
651
749
  | variant | `'default' \| 'destructive' \| 'success'` | `'default'` | `default`: dark background. `destructive`: red. `success`: green |
652
750
  | duration | `number` (ms) | `3000` | Auto-dismiss after this delay |
653
751
 
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).
752
+ **`dismiss(id)`:** Dismiss a toast programmatically. The `id` is returned by the `toast()` call — store it if you need programmatic dismissal.
753
+
754
+ **Notes:**
755
+ - Max 3 toasts shown simultaneously (oldest is removed when a 4th arrives)
756
+ - Toasts appear at the top of the screen, below the status bar (dynamic safe area inset)
757
+ - Entrance: `withTiming(120ms, Easing.out(Easing.exp))` slide-down + opacity fade — fast, sharp feel
758
+ - Exit: `withTiming(200ms)` slide-up + opacity fade
759
+ - Swipe left or right to dismiss early (threshold: 80px or 800 pt/s velocity)
package/README.md CHANGED
@@ -1,8 +1,8 @@
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
 
5
- - 23 components across 5 categories
5
+ - 24 components across 5 categories
6
6
  - Light/dark theme with 18 color tokens and full customization
7
7
  - Apple HIG–compliant touch targets and haptic feedback
8
8
  - Animated interactions: spring press, sliding tabs, accordion easing, animated progress
@@ -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`, `CurrencyInput`, `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