@retray-dev/ui-kit 0.1.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 +710 -0
- package/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/index.d.mts +345 -4
- package/dist/index.d.ts +345 -4
- package/dist/index.js +1644 -58
- package/dist/index.mjs +1590 -58
- package/package.json +44 -5
- package/src/components/Accordion/Accordion.tsx +173 -0
- package/src/components/Accordion/index.ts +2 -0
- package/src/components/Alert/Alert.tsx +59 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/Avatar/Avatar.tsx +71 -0
- package/src/components/Avatar/index.ts +2 -0
- package/src/components/Badge/Badge.tsx +48 -0
- package/src/components/Badge/index.ts +2 -0
- package/src/components/Button/Button.tsx +94 -45
- package/src/components/Card/Card.tsx +103 -0
- package/src/components/Card/index.ts +9 -0
- package/src/components/Checkbox/Checkbox.tsx +98 -0
- package/src/components/Checkbox/index.ts +2 -0
- package/src/components/EmptyState/EmptyState.tsx +67 -0
- package/src/components/EmptyState/index.ts +2 -0
- package/src/components/Input/Input.tsx +28 -35
- package/src/components/Progress/Progress.tsx +52 -0
- package/src/components/Progress/index.ts +2 -0
- package/src/components/RadioGroup/RadioGroup.tsx +132 -0
- package/src/components/RadioGroup/index.ts +2 -0
- package/src/components/Select/Select.tsx +232 -0
- package/src/components/Select/index.ts +2 -0
- package/src/components/Separator/Separator.tsx +33 -0
- package/src/components/Separator/index.ts +2 -0
- package/src/components/Sheet/Sheet.tsx +115 -0
- package/src/components/Sheet/index.ts +2 -0
- package/src/components/Skeleton/Skeleton.tsx +63 -0
- package/src/components/Skeleton/index.ts +2 -0
- package/src/components/Slider/Slider.tsx +143 -0
- package/src/components/Slider/index.ts +2 -0
- package/src/components/Spinner/Spinner.tsx +21 -0
- package/src/components/Spinner/index.ts +2 -0
- package/src/components/Switch/Switch.tsx +86 -0
- package/src/components/Switch/index.ts +2 -0
- package/src/components/Tabs/Tabs.tsx +196 -0
- package/src/components/Tabs/index.ts +2 -0
- package/src/components/Text/Text.tsx +10 -4
- package/src/components/Textarea/Textarea.tsx +89 -0
- package/src/components/Textarea/index.ts +2 -0
- package/src/components/Toast/Toast.tsx +200 -0
- package/src/components/Toast/index.ts +2 -0
- package/src/components/Toggle/Toggle.tsx +92 -0
- package/src/components/Toggle/index.ts +2 -0
- package/src/index.ts +26 -0
- package/src/theme/ThemeProvider.tsx +47 -0
- package/src/theme/colors.ts +45 -0
- package/src/theme/index.ts +4 -0
- package/src/theme/types.ts +33 -0
package/COMPONENTS.md
ADDED
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
# @retray-dev/ui-kit — Component Reference
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
```markdown
|
|
6
|
+
## UI Components
|
|
7
|
+
@./node_modules/@retray-dev/ui-kit/COMPONENTS.md
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Setup (Required)
|
|
13
|
+
|
|
14
|
+
Wrap your app root with all required providers in this exact order:
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
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'
|
|
20
|
+
|
|
21
|
+
export default function App() {
|
|
22
|
+
return (
|
|
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>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
```
|
|
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
|
+
|
|
45
|
+
### ThemeProvider Props
|
|
46
|
+
|
|
47
|
+
| Prop | Type | Default | Notes |
|
|
48
|
+
|------|------|---------|-------|
|
|
49
|
+
| colorScheme | `'light' \| 'dark' \| 'system'` | `'system'` | `'system'` auto-detects the device setting and updates when it changes |
|
|
50
|
+
| theme | `{ light?: Partial<ThemeColors>, dark?: Partial<ThemeColors> }` | — | Override any subset of color tokens per scheme |
|
|
51
|
+
|
|
52
|
+
**Custom theme example:**
|
|
53
|
+
```tsx
|
|
54
|
+
const myTheme = {
|
|
55
|
+
light: { primary: '#6366f1', primaryForeground: '#ffffff' },
|
|
56
|
+
dark: { primary: '#818cf8', primaryForeground: '#ffffff' },
|
|
57
|
+
}
|
|
58
|
+
<ThemeProvider theme={myTheme} colorScheme="system">
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### useTheme Hook
|
|
62
|
+
|
|
63
|
+
Access the active color tokens and scheme inside any component:
|
|
64
|
+
```tsx
|
|
65
|
+
import { useTheme } from '@retray-dev/ui-kit'
|
|
66
|
+
|
|
67
|
+
function MyComponent() {
|
|
68
|
+
const { colors, colorScheme } = useTheme()
|
|
69
|
+
return <View style={{ backgroundColor: colors.background }} />
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Theme Tokens
|
|
76
|
+
|
|
77
|
+
All 18 tokens are available via `useTheme().colors`.
|
|
78
|
+
|
|
79
|
+
| Token | Light | Dark | Semantic Role |
|
|
80
|
+
|-------|-------|------|---------------|
|
|
81
|
+
| `background` | `#ffffff` | `#171717` | Screen / page background |
|
|
82
|
+
| `foreground` | `#171717` | `#fafafa` | Primary text color |
|
|
83
|
+
| `card` | `#ffffff` | `#1f1f1f` | Card / surface background |
|
|
84
|
+
| `cardForeground` | `#171717` | `#fafafa` | Text on cards |
|
|
85
|
+
| `primary` | `#1a1a1a` | `#fafafa` | Primary action (buttons, selected states) |
|
|
86
|
+
| `primaryForeground` | `#fafafa` | `#1a1a1a` | Text/icon on primary background |
|
|
87
|
+
| `secondary` | `#f5f5f5` | `#2a2a2a` | Secondary surfaces |
|
|
88
|
+
| `secondaryForeground` | `#1a1a1a` | `#fafafa` | Text on secondary |
|
|
89
|
+
| `muted` | `#f5f5f5` | `#2a2a2a` | Muted backgrounds, skeleton fills, track fills |
|
|
90
|
+
| `mutedForeground` | `#646464` | `#a3a3a3` | Placeholder text, helper text, captions (WCAG AA ≥4.5:1) |
|
|
91
|
+
| `accent` | `#f5f5f5` | `#2a2a2a` | Hover / pressed state fills |
|
|
92
|
+
| `accentForeground` | `#1a1a1a` | `#fafafa` | Text on accent |
|
|
93
|
+
| `destructive` | `#ef4444` | `#dc2626` | Error / danger / delete actions |
|
|
94
|
+
| `destructiveForeground` | `#fafafa` | `#fafafa` | Text on destructive |
|
|
95
|
+
| `border` | `#e5e5e5` | `#2a2a2a` | Borders and dividers |
|
|
96
|
+
| `input` | `#e5e5e5` | `#2a2a2a` | Input field border color |
|
|
97
|
+
| `ring` | `#a3a3a3` | `#d4d4d4` | Focus ring color |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Components
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Text
|
|
106
|
+
|
|
107
|
+
**Import:** `import { Text } from '@retray-dev/ui-kit'`
|
|
108
|
+
**When to use:** All text in the app. Replaces React Native's `Text` with semantic variants.
|
|
109
|
+
**Extends:** `TextProps` from React Native — all native props pass through.
|
|
110
|
+
|
|
111
|
+
| Prop | Type | Default | Notes |
|
|
112
|
+
|------|------|---------|-------|
|
|
113
|
+
| variant | `'h1' \| 'h2' \| 'h3' \| 'body' \| 'caption' \| 'label'` | `'body'` | Sets font size, weight, and line height |
|
|
114
|
+
| color | `string` | — | Override the color. Defaults to `foreground`, except `caption` which uses `mutedForeground` |
|
|
115
|
+
|
|
116
|
+
**Sizes:**
|
|
117
|
+
- `h1`: 32px / 700 weight
|
|
118
|
+
- `h2`: 24px / 700 weight
|
|
119
|
+
- `h3`: 20px / 600 weight
|
|
120
|
+
- `body`: 16px / 400 weight
|
|
121
|
+
- `label`: 14px / 500 weight
|
|
122
|
+
- `caption`: 12px / 400 weight / `mutedForeground` color by default
|
|
123
|
+
|
|
124
|
+
**Example:**
|
|
125
|
+
```tsx
|
|
126
|
+
<Text variant="h2">Welcome back</Text>
|
|
127
|
+
<Text variant="body">Your account is ready.</Text>
|
|
128
|
+
<Text variant="caption">Last updated 2 hours ago</Text>
|
|
129
|
+
<Text variant="label" color="#6366f1">Pro plan</Text>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### Button
|
|
135
|
+
|
|
136
|
+
**Import:** `import { Button } from '@retray-dev/ui-kit'`
|
|
137
|
+
**When to use:** Any interactive action. Use `variant` to communicate intent.
|
|
138
|
+
**Extends:** `TouchableOpacityProps` — all RN TouchableOpacity props pass through.
|
|
139
|
+
|
|
140
|
+
| Prop | Type | Default | Notes |
|
|
141
|
+
|------|------|---------|-------|
|
|
142
|
+
| label | `string` | required | Button text |
|
|
143
|
+
| variant | `'primary' \| 'secondary' \| 'outline' \| 'ghost'` | `'primary'` | Visual style |
|
|
144
|
+
| size | `'sm' \| 'md' \| 'lg'` | `'md'` | — |
|
|
145
|
+
| loading | `boolean` | `false` | Replaces label with a spinner and forces disabled state |
|
|
146
|
+
| fullWidth | `boolean` | `false` | Stretches to container width (`alignSelf: 'stretch'`) |
|
|
147
|
+
| disabled | `boolean` | — | Reduces opacity to 0.45 |
|
|
148
|
+
|
|
149
|
+
**Variants:**
|
|
150
|
+
- `primary`: filled with `primary` token — main actions
|
|
151
|
+
- `secondary`: filled with `secondary` token — less prominent actions
|
|
152
|
+
- `outline`: transparent with `border` — alternative without fill
|
|
153
|
+
- `ghost`: fully transparent — in-context or low-emphasis actions
|
|
154
|
+
|
|
155
|
+
**Animations:** Scale springs to 0.97 on `onPressIn`, back to 1.0 on `onPressOut`.
|
|
156
|
+
|
|
157
|
+
**Example:**
|
|
158
|
+
```tsx
|
|
159
|
+
<Button label="Save changes" onPress={handleSave} />
|
|
160
|
+
<Button label="Cancel" variant="ghost" onPress={onCancel} />
|
|
161
|
+
<Button label="Delete" variant="outline" size="sm" />
|
|
162
|
+
<Button label="Submitting..." loading fullWidth />
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### Input
|
|
168
|
+
|
|
169
|
+
**Import:** `import { Input } from '@retray-dev/ui-kit'`
|
|
170
|
+
**When to use:** Single-line text entry. Includes built-in label, error, and hint support.
|
|
171
|
+
**Extends:** `TextInputProps` from React Native — all native props pass through.
|
|
172
|
+
|
|
173
|
+
| Prop | Type | Default | Notes |
|
|
174
|
+
|------|------|---------|-------|
|
|
175
|
+
| label | `string` | — | Label above the input |
|
|
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.
|
|
180
|
+
|
|
181
|
+
**Example:**
|
|
182
|
+
```tsx
|
|
183
|
+
<Input label="Email" placeholder="you@example.com" keyboardType="email-address" />
|
|
184
|
+
<Input label="Password" secureTextEntry error="Incorrect password" />
|
|
185
|
+
<Input label="Username" hint="Must be at least 4 characters" />
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### Textarea
|
|
191
|
+
|
|
192
|
+
**Import:** `import { Textarea } from '@retray-dev/ui-kit'`
|
|
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.
|
|
195
|
+
|
|
196
|
+
| Prop | Type | Default | Notes |
|
|
197
|
+
|------|------|---------|-------|
|
|
198
|
+
| label | `string` | — | Label above |
|
|
199
|
+
| error | `string` | — | Error text below; red border |
|
|
200
|
+
| hint | `string` | — | Helper text below |
|
|
201
|
+
| rows | `number` | `4` | Sets `minHeight` (each row ≈ 28px) |
|
|
202
|
+
|
|
203
|
+
**Example:**
|
|
204
|
+
```tsx
|
|
205
|
+
<Textarea label="Bio" placeholder="Tell us about yourself" rows={5} />
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### Badge
|
|
211
|
+
|
|
212
|
+
**Import:** `import { Badge } from '@retray-dev/ui-kit'`
|
|
213
|
+
**When to use:** Status labels, tags, counts.
|
|
214
|
+
|
|
215
|
+
| Prop | Type | Default | Notes |
|
|
216
|
+
|------|------|---------|-------|
|
|
217
|
+
| label | `string` | required | — |
|
|
218
|
+
| variant | `'default' \| 'secondary' \| 'destructive' \| 'outline'` | `'default'` | — |
|
|
219
|
+
| style | `ViewStyle` | — | — |
|
|
220
|
+
|
|
221
|
+
**Example:**
|
|
222
|
+
```tsx
|
|
223
|
+
<Badge label="New" />
|
|
224
|
+
<Badge label="Error" variant="destructive" />
|
|
225
|
+
<Badge label="Draft" variant="secondary" />
|
|
226
|
+
<Badge label="Beta" variant="outline" />
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
### Avatar
|
|
232
|
+
|
|
233
|
+
**Import:** `import { Avatar } from '@retray-dev/ui-kit'`
|
|
234
|
+
**When to use:** User profile pictures with automatic fallback to initials.
|
|
235
|
+
|
|
236
|
+
| Prop | Type | Default | Notes |
|
|
237
|
+
|------|------|---------|-------|
|
|
238
|
+
| src | `string` | — | Image URI |
|
|
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` | — | — |
|
|
242
|
+
|
|
243
|
+
**Example:**
|
|
244
|
+
```tsx
|
|
245
|
+
<Avatar src="https://..." fallback="JC" size="lg" />
|
|
246
|
+
<Avatar fallback="AN" size="md" />
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### Separator
|
|
252
|
+
|
|
253
|
+
**Import:** `import { Separator } from '@retray-dev/ui-kit'`
|
|
254
|
+
**When to use:** Visual dividers between sections.
|
|
255
|
+
|
|
256
|
+
| Prop | Type | Default | Notes |
|
|
257
|
+
|------|------|---------|-------|
|
|
258
|
+
| orientation | `'horizontal' \| 'vertical'` | `'horizontal'` | Vertical requires a parent with a defined height |
|
|
259
|
+
| style | `ViewStyle` | — | — |
|
|
260
|
+
|
|
261
|
+
**Example:**
|
|
262
|
+
```tsx
|
|
263
|
+
<Separator />
|
|
264
|
+
<View style={{ flexDirection: 'row', height: 40 }}>
|
|
265
|
+
<Text>Left</Text>
|
|
266
|
+
<Separator orientation="vertical" style={{ marginHorizontal: 12 }} />
|
|
267
|
+
<Text>Right</Text>
|
|
268
|
+
</View>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### Spinner
|
|
274
|
+
|
|
275
|
+
**Import:** `import { Spinner } from '@retray-dev/ui-kit'`
|
|
276
|
+
**When to use:** Loading state indicator. Wraps React Native's `ActivityIndicator`.
|
|
277
|
+
**Extends:** `ActivityIndicatorProps` (except `size`).
|
|
278
|
+
|
|
279
|
+
| Prop | Type | Default | Notes |
|
|
280
|
+
|------|------|---------|-------|
|
|
281
|
+
| size | `'sm' \| 'md' \| 'lg'` | `'md'` | `sm`/`md` map to RN `'small'`, `lg` maps to `'large'` |
|
|
282
|
+
| color | `string` | `primary` token | Override spinner color |
|
|
283
|
+
|
|
284
|
+
**Example:**
|
|
285
|
+
```tsx
|
|
286
|
+
<Spinner />
|
|
287
|
+
<Spinner size="lg" color="#6366f1" />
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### Skeleton
|
|
293
|
+
|
|
294
|
+
**Import:** `import { Skeleton } from '@retray-dev/ui-kit'`
|
|
295
|
+
**When to use:** Placeholder while content is loading. Pulses with a looping opacity animation.
|
|
296
|
+
|
|
297
|
+
| Prop | Type | Default | Notes |
|
|
298
|
+
|------|------|---------|-------|
|
|
299
|
+
| width | `number \| string` | `'100%'` | — |
|
|
300
|
+
| height | `number` | `16` | — |
|
|
301
|
+
| borderRadius | `number` | `6` | — |
|
|
302
|
+
| style | `ViewStyle` | — | — |
|
|
303
|
+
|
|
304
|
+
**Example:**
|
|
305
|
+
```tsx
|
|
306
|
+
<Skeleton height={20} width="60%" />
|
|
307
|
+
<Skeleton height={80} borderRadius={10} />
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
### Progress
|
|
313
|
+
|
|
314
|
+
**Import:** `import { Progress } from '@retray-dev/ui-kit'`
|
|
315
|
+
**When to use:** Show completion percentage for a task or upload.
|
|
316
|
+
|
|
317
|
+
| Prop | Type | Default | Notes |
|
|
318
|
+
|------|------|---------|-------|
|
|
319
|
+
| value | `number` | `0` | Current value |
|
|
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: '%'`).
|
|
324
|
+
|
|
325
|
+
**Example:**
|
|
326
|
+
```tsx
|
|
327
|
+
<Progress value={60} />
|
|
328
|
+
<Progress value={3} max={10} />
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
### Card
|
|
334
|
+
|
|
335
|
+
**Import:** `import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@retray-dev/ui-kit'`
|
|
336
|
+
**When to use:** Grouped content with a surface background, border, and shadow.
|
|
337
|
+
|
|
338
|
+
All sub-components accept a `style` prop for overrides.
|
|
339
|
+
|
|
340
|
+
**Example:**
|
|
341
|
+
```tsx
|
|
342
|
+
<Card>
|
|
343
|
+
<CardHeader>
|
|
344
|
+
<CardTitle>Account</CardTitle>
|
|
345
|
+
<CardDescription>Manage your profile settings</CardDescription>
|
|
346
|
+
</CardHeader>
|
|
347
|
+
<CardContent>
|
|
348
|
+
<Input label="Name" />
|
|
349
|
+
</CardContent>
|
|
350
|
+
<CardFooter>
|
|
351
|
+
<Button label="Save" fullWidth />
|
|
352
|
+
</CardFooter>
|
|
353
|
+
</Card>
|
|
354
|
+
```
|
|
355
|
+
|
|
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
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
### Alert
|
|
365
|
+
|
|
366
|
+
**Import:** `import { Alert } from '@retray-dev/ui-kit'`
|
|
367
|
+
**When to use:** Inline feedback messages (info, success, warning, error). Not for transient toasts.
|
|
368
|
+
|
|
369
|
+
| Prop | Type | Default | Notes |
|
|
370
|
+
|------|------|---------|-------|
|
|
371
|
+
| title | `string` | — | Bold heading |
|
|
372
|
+
| description | `string` | — | Detail text |
|
|
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` | — | — |
|
|
376
|
+
|
|
377
|
+
**Example:**
|
|
378
|
+
```tsx
|
|
379
|
+
<Alert title="Success" description="Your profile has been updated." />
|
|
380
|
+
<Alert variant="destructive" title="Error" description="Failed to save. Try again." />
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
### EmptyState
|
|
386
|
+
|
|
387
|
+
**Import:** `import { EmptyState } from '@retray-dev/ui-kit'`
|
|
388
|
+
**When to use:** When a list or section has no content yet.
|
|
389
|
+
|
|
390
|
+
| Prop | Type | Default | Notes |
|
|
391
|
+
|------|------|---------|-------|
|
|
392
|
+
| title | `string` | required | — |
|
|
393
|
+
| description | `string` | — | — |
|
|
394
|
+
| icon | `ReactNode` | — | Shown in a 48×48 muted square above the text |
|
|
395
|
+
| action | `ReactNode` | — | Usually a `Button`, placed below the text |
|
|
396
|
+
| style | `ViewStyle` | — | — |
|
|
397
|
+
|
|
398
|
+
**Example:**
|
|
399
|
+
```tsx
|
|
400
|
+
<EmptyState
|
|
401
|
+
title="No notifications"
|
|
402
|
+
description="You're all caught up!"
|
|
403
|
+
action={<Button label="Refresh" variant="outline" size="sm" />}
|
|
404
|
+
/>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
### Checkbox
|
|
410
|
+
|
|
411
|
+
**Import:** `import { Checkbox } from '@retray-dev/ui-kit'`
|
|
412
|
+
|
|
413
|
+
| Prop | Type | Default | Notes |
|
|
414
|
+
|------|------|---------|-------|
|
|
415
|
+
| checked | `boolean` | `false` | — |
|
|
416
|
+
| onCheckedChange | `(checked: boolean) => void` | — | — |
|
|
417
|
+
| label | `string` | — | Text to the right of the box |
|
|
418
|
+
| disabled | `boolean` | — | — |
|
|
419
|
+
| style | `ViewStyle` | — | — |
|
|
420
|
+
|
|
421
|
+
**Example:**
|
|
422
|
+
```tsx
|
|
423
|
+
const [accepted, setAccepted] = useState(false)
|
|
424
|
+
<Checkbox checked={accepted} onCheckedChange={setAccepted} label="I agree to the terms" />
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
### Switch
|
|
430
|
+
|
|
431
|
+
**Import:** `import { Switch } from '@retray-dev/ui-kit'`
|
|
432
|
+
**When to use:** Binary on/off settings.
|
|
433
|
+
|
|
434
|
+
| Prop | Type | Default | Notes |
|
|
435
|
+
|------|------|---------|-------|
|
|
436
|
+
| checked | `boolean` | `false` | — |
|
|
437
|
+
| onCheckedChange | `(checked: boolean) => void` | — | — |
|
|
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).
|
|
444
|
+
|
|
445
|
+
**Example:**
|
|
446
|
+
```tsx
|
|
447
|
+
<Switch checked={notifications} onCheckedChange={setNotifications} />
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
### Toggle
|
|
453
|
+
|
|
454
|
+
**Import:** `import { Toggle } from '@retray-dev/ui-kit'`
|
|
455
|
+
**When to use:** Toggleable button (e.g., bold/italic in a toolbar). Looks like a button, unlike `Switch`.
|
|
456
|
+
|
|
457
|
+
| Prop | Type | Default | Notes |
|
|
458
|
+
|------|------|---------|-------|
|
|
459
|
+
| pressed | `boolean` | `false` | — |
|
|
460
|
+
| onPressedChange | `(pressed: boolean) => void` | — | — |
|
|
461
|
+
| variant | `'default' \| 'outline'` | `'default'` | `outline` adds a border when unpressed |
|
|
462
|
+
| size | `'sm' \| 'md' \| 'lg'` | `'md'` | sm=minH 40pt, md=minH 44pt, lg=minH 48pt |
|
|
463
|
+
| label | `string` | — | Text label |
|
|
464
|
+
| icon | `ReactNode` | — | Icon — can be combined with `label` |
|
|
465
|
+
|
|
466
|
+
**Example:**
|
|
467
|
+
```tsx
|
|
468
|
+
<Toggle pressed={bold} onPressedChange={setBold} label="Bold" variant="outline" />
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
### RadioGroup
|
|
474
|
+
|
|
475
|
+
**Import:** `import { RadioGroup } from '@retray-dev/ui-kit'`
|
|
476
|
+
|
|
477
|
+
| Prop | Type | Default | Notes |
|
|
478
|
+
|------|------|---------|-------|
|
|
479
|
+
| options | `RadioOption[]` | required | Each option: `{ label: string, value: string, disabled?: boolean }` |
|
|
480
|
+
| value | `string` | — | Currently selected value |
|
|
481
|
+
| onValueChange | `(value: string) => void` | — | — |
|
|
482
|
+
| orientation | `'vertical' \| 'horizontal'` | `'vertical'` | — |
|
|
483
|
+
| style | `ViewStyle` | — | — |
|
|
484
|
+
|
|
485
|
+
**Example:**
|
|
486
|
+
```tsx
|
|
487
|
+
<RadioGroup
|
|
488
|
+
options={[
|
|
489
|
+
{ label: 'Free', value: 'free' },
|
|
490
|
+
{ label: 'Pro', value: 'pro' },
|
|
491
|
+
{ label: 'Enterprise', value: 'enterprise', disabled: true },
|
|
492
|
+
]}
|
|
493
|
+
value={plan}
|
|
494
|
+
onValueChange={setPlan}
|
|
495
|
+
/>
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
### Select
|
|
501
|
+
|
|
502
|
+
**Import:** `import { Select } from '@retray-dev/ui-kit'`
|
|
503
|
+
**When to use:** Dropdown picker. Tapping the trigger opens a modal with a scrollable list.
|
|
504
|
+
|
|
505
|
+
| Prop | Type | Default | Notes |
|
|
506
|
+
|------|------|---------|-------|
|
|
507
|
+
| options | `SelectOption[]` | required | Each option: `{ label: string, value: string, disabled?: boolean }` |
|
|
508
|
+
| value | `string` | — | Selected value |
|
|
509
|
+
| onValueChange | `(value: string) => void` | — | — |
|
|
510
|
+
| placeholder | `string` | `'Select an option'` | Shown when no value is selected |
|
|
511
|
+
| label | `string` | — | Label above the trigger |
|
|
512
|
+
| error | `string` | — | Error text below the trigger |
|
|
513
|
+
| disabled | `boolean` | — | — |
|
|
514
|
+
| style | `ViewStyle` | — | — |
|
|
515
|
+
|
|
516
|
+
**Example:**
|
|
517
|
+
```tsx
|
|
518
|
+
<Select
|
|
519
|
+
label="Country"
|
|
520
|
+
options={[{ label: 'Argentina', value: 'AR' }, { label: 'Spain', value: 'ES' }]}
|
|
521
|
+
value={country}
|
|
522
|
+
onValueChange={setCountry}
|
|
523
|
+
placeholder="Pick a country"
|
|
524
|
+
/>
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
### Slider
|
|
530
|
+
|
|
531
|
+
**Import:** `import { Slider } from '@retray-dev/ui-kit'`
|
|
532
|
+
**When to use:** Select a numeric value within a range by dragging.
|
|
533
|
+
|
|
534
|
+
| Prop | Type | Default | Notes |
|
|
535
|
+
|------|------|---------|-------|
|
|
536
|
+
| value | `number` | `0` | — |
|
|
537
|
+
| minimumValue | `number` | `0` | — |
|
|
538
|
+
| maximumValue | `number` | `1` | — |
|
|
539
|
+
| step | `number` | `0` | `0` means continuous (no snapping) |
|
|
540
|
+
| onValueChange | `(value: number) => void` | — | Fires while dragging |
|
|
541
|
+
| onSlidingComplete | `(value: number) => void` | — | Fires on finger release |
|
|
542
|
+
| disabled | `boolean` | — | — |
|
|
543
|
+
| style | `ViewStyle` | — | — |
|
|
544
|
+
|
|
545
|
+
**Dimensions:** Container height=32pt, track height=6pt, thumb 28×28pt. Uses `PanResponder` internally.
|
|
546
|
+
|
|
547
|
+
**Example:**
|
|
548
|
+
```tsx
|
|
549
|
+
<Slider value={volume} minimumValue={0} maximumValue={100} step={1} onValueChange={setVolume} />
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
### Tabs
|
|
555
|
+
|
|
556
|
+
**Import:** `import { Tabs, TabsContent } from '@retray-dev/ui-kit'`
|
|
557
|
+
**When to use:** Switching between categorized sections on the same screen.
|
|
558
|
+
|
|
559
|
+
| Prop | Type | Default | Notes |
|
|
560
|
+
|------|------|---------|-------|
|
|
561
|
+
| tabs | `TabItem[]` | required | Each item: `{ label: string, value: string }` |
|
|
562
|
+
| value | `string` | — | Controlled active tab |
|
|
563
|
+
| onValueChange | `(value: string) => void` | — | — |
|
|
564
|
+
| children | `ReactNode` | — | `TabsContent` components |
|
|
565
|
+
| style | `ViewStyle` | — | — |
|
|
566
|
+
|
|
567
|
+
**`TabsContent` Props:**
|
|
568
|
+
|
|
569
|
+
| Prop | Type | Notes |
|
|
570
|
+
|------|------|-------|
|
|
571
|
+
| value | `string` | Must match a tab value in `tabs` |
|
|
572
|
+
| activeValue | `string` | Pass the current active tab — content is hidden when not active |
|
|
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.
|
|
577
|
+
|
|
578
|
+
**Example:**
|
|
579
|
+
```tsx
|
|
580
|
+
const [tab, setTab] = useState('profile')
|
|
581
|
+
|
|
582
|
+
<Tabs
|
|
583
|
+
tabs={[{ label: 'Profile', value: 'profile' }, { label: 'Security', value: 'security' }]}
|
|
584
|
+
value={tab}
|
|
585
|
+
onValueChange={setTab}
|
|
586
|
+
>
|
|
587
|
+
<TabsContent value="profile" activeValue={tab}>
|
|
588
|
+
<Text>Profile content</Text>
|
|
589
|
+
</TabsContent>
|
|
590
|
+
<TabsContent value="security" activeValue={tab}>
|
|
591
|
+
<Text>Security content</Text>
|
|
592
|
+
</TabsContent>
|
|
593
|
+
</Tabs>
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
### Accordion
|
|
599
|
+
|
|
600
|
+
**Import:** `import { Accordion } from '@retray-dev/ui-kit'`
|
|
601
|
+
**When to use:** FAQs, collapsible sections. Animated expand/collapse.
|
|
602
|
+
|
|
603
|
+
| Prop | Type | Default | Notes |
|
|
604
|
+
|------|------|---------|-------|
|
|
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).
|
|
611
|
+
|
|
612
|
+
**Example:**
|
|
613
|
+
```tsx
|
|
614
|
+
<Accordion
|
|
615
|
+
type="single"
|
|
616
|
+
items={[
|
|
617
|
+
{ value: 'q1', trigger: 'What is this?', content: <Text>It's a UI kit.</Text> },
|
|
618
|
+
{ value: 'q2', trigger: 'Is it free?', content: <Text>Yes.</Text> },
|
|
619
|
+
]}
|
|
620
|
+
/>
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
---
|
|
624
|
+
|
|
625
|
+
### Sheet
|
|
626
|
+
|
|
627
|
+
**Import:** `import { Sheet, BottomSheetModalProvider } from '@retray-dev/ui-kit'`
|
|
628
|
+
**When to use:** Bottom sheet with physics-based gestures, rubber-band overscroll, and snap points. Powered by `@gorhom/bottom-sheet`.
|
|
629
|
+
|
|
630
|
+
**Required setup** — add to your app root (see Setup section above):
|
|
631
|
+
```tsx
|
|
632
|
+
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
|
633
|
+
import { BottomSheetModalProvider } from '@retray-dev/ui-kit'
|
|
634
|
+
|
|
635
|
+
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
636
|
+
<BottomSheetModalProvider>
|
|
637
|
+
{/* rest of app */}
|
|
638
|
+
</BottomSheetModalProvider>
|
|
639
|
+
</GestureHandlerRootView>
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
**Peer dependencies** (install in your app):
|
|
643
|
+
```bash
|
|
644
|
+
pnpm add @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-worklets
|
|
645
|
+
```
|
|
646
|
+
Add `react-native-worklets/plugin` (not `react-native-reanimated/plugin`) to your `babel.config.js` plugins.
|
|
647
|
+
|
|
648
|
+
| Prop | Type | Default | Notes |
|
|
649
|
+
|------|------|---------|-------|
|
|
650
|
+
| open | `boolean` | required | `true` presents the sheet, `false` dismisses it |
|
|
651
|
+
| onClose | `() => void` | required | Called on swipe-dismiss or backdrop press |
|
|
652
|
+
| snapPoints | `(string \| number)[]` | `['50%']` | Snap positions, e.g. `['40%', '80%']` |
|
|
653
|
+
| title | `string` | — | — |
|
|
654
|
+
| description | `string` | — | — |
|
|
655
|
+
| children | `ReactNode` | — | — |
|
|
656
|
+
| style | `ViewStyle` | — | Applied to the inner content container |
|
|
657
|
+
|
|
658
|
+
**Example:**
|
|
659
|
+
```tsx
|
|
660
|
+
<Sheet open={open} onClose={() => setOpen(false)} title="Filters" snapPoints={['50%']}>
|
|
661
|
+
<RadioGroup options={sortOptions} value={sort} onValueChange={setSort} />
|
|
662
|
+
</Sheet>
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
### Toast / useToast
|
|
668
|
+
|
|
669
|
+
**Import:** `import { ToastProvider, useToast } from '@retray-dev/ui-kit'`
|
|
670
|
+
**When to use:** Ephemeral feedback messages (save success, network error, copy confirmation).
|
|
671
|
+
|
|
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`.
|
|
675
|
+
|
|
676
|
+
```tsx
|
|
677
|
+
import { useToast } from '@retray-dev/ui-kit'
|
|
678
|
+
|
|
679
|
+
function MyComponent() {
|
|
680
|
+
const { toast, dismiss } = useToast()
|
|
681
|
+
|
|
682
|
+
return (
|
|
683
|
+
<Button
|
|
684
|
+
label="Save"
|
|
685
|
+
onPress={async () => {
|
|
686
|
+
await save()
|
|
687
|
+
toast({ title: 'Saved', description: 'Your changes were saved.', variant: 'success' })
|
|
688
|
+
}}
|
|
689
|
+
/>
|
|
690
|
+
)
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
**`toast()` options:**
|
|
695
|
+
|
|
696
|
+
| Field | Type | Default | Notes |
|
|
697
|
+
|-------|------|---------|-------|
|
|
698
|
+
| title | `string` | — | Bold heading |
|
|
699
|
+
| description | `string` | — | Detail text |
|
|
700
|
+
| variant | `'default' \| 'destructive' \| 'success'` | `'default'` | `default`: dark background. `destructive`: red. `success`: green |
|
|
701
|
+
| duration | `number` (ms) | `3000` | Auto-dismiss after this delay |
|
|
702
|
+
|
|
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)
|