@masumdev/rn-ui 0.1.6 → 0.1.7
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/README.md +47 -1752
- package/lib/commonjs/components/AlertDialog.js.map +1 -1
- package/lib/commonjs/components/AspectRatio.js.map +1 -1
- package/lib/commonjs/components/Avatar.js.map +1 -1
- package/lib/commonjs/components/Badge.js +7 -2
- package/lib/commonjs/components/Badge.js.map +1 -1
- package/lib/commonjs/components/Button.js.map +1 -1
- package/lib/commonjs/components/ButtonGroup.js +2 -4
- package/lib/commonjs/components/ButtonGroup.js.map +1 -1
- package/lib/commonjs/components/Calendar.js.map +1 -1
- package/lib/commonjs/components/FloatingActionButton.js.map +1 -1
- package/lib/commonjs/components/IconButton.js.map +1 -1
- package/lib/commonjs/components/Progress.js +3 -1
- package/lib/commonjs/components/Progress.js.map +1 -1
- package/lib/commonjs/components/index.js +7 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/components/types.js +37 -0
- package/lib/commonjs/components/types.js.map +1 -1
- package/lib/module/components/AlertDialog.js.map +1 -1
- package/lib/module/components/AspectRatio.js.map +1 -1
- package/lib/module/components/Avatar.js.map +1 -1
- package/lib/module/components/Badge.js +7 -2
- package/lib/module/components/Badge.js.map +1 -1
- package/lib/module/components/Button.js.map +1 -1
- package/lib/module/components/ButtonGroup.js +3 -5
- package/lib/module/components/ButtonGroup.js.map +1 -1
- package/lib/module/components/Calendar.js.map +1 -1
- package/lib/module/components/FloatingActionButton.js.map +1 -1
- package/lib/module/components/IconButton.js.map +1 -1
- package/lib/module/components/Progress.js +3 -1
- package/lib/module/components/Progress.js.map +1 -1
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/types.js +37 -0
- package/lib/module/components/types.js.map +1 -1
- package/lib/typescript/commonjs/components/AlertDialog.d.ts +2 -3
- package/lib/typescript/commonjs/components/AlertDialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/AspectRatio.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/Badge.d.ts +3 -6
- package/lib/typescript/commonjs/components/Badge.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/Button.d.ts +5 -11
- package/lib/typescript/commonjs/components/Button.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/ButtonGroup.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/FloatingActionButton.d.ts +2 -5
- package/lib/typescript/commonjs/components/FloatingActionButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/IconButton.d.ts +3 -6
- package/lib/typescript/commonjs/components/IconButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/Progress.d.ts +3 -2
- package/lib/typescript/commonjs/components/Progress.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/index.d.ts +2 -1
- package/lib/typescript/commonjs/components/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/components/types.d.ts +75 -0
- package/lib/typescript/commonjs/components/types.d.ts.map +1 -1
- package/lib/typescript/module/components/AlertDialog.d.ts +2 -3
- package/lib/typescript/module/components/AlertDialog.d.ts.map +1 -1
- package/lib/typescript/module/components/AspectRatio.d.ts.map +1 -1
- package/lib/typescript/module/components/Badge.d.ts +3 -6
- package/lib/typescript/module/components/Badge.d.ts.map +1 -1
- package/lib/typescript/module/components/Button.d.ts +5 -11
- package/lib/typescript/module/components/Button.d.ts.map +1 -1
- package/lib/typescript/module/components/ButtonGroup.d.ts.map +1 -1
- package/lib/typescript/module/components/FloatingActionButton.d.ts +2 -5
- package/lib/typescript/module/components/FloatingActionButton.d.ts.map +1 -1
- package/lib/typescript/module/components/IconButton.d.ts +3 -6
- package/lib/typescript/module/components/IconButton.d.ts.map +1 -1
- package/lib/typescript/module/components/Progress.d.ts +3 -2
- package/lib/typescript/module/components/Progress.d.ts.map +1 -1
- package/lib/typescript/module/components/index.d.ts +2 -1
- package/lib/typescript/module/components/index.d.ts.map +1 -1
- package/lib/typescript/module/components/types.d.ts +75 -0
- package/lib/typescript/module/components/types.d.ts.map +1 -1
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -1,95 +1,56 @@
|
|
|
1
1
|
# @masumdev/rn-ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> A modern, flat-designed React Native & Expo UI kit with 55+ typed components, dynamic light/dark theme engine, zero-shadow defaults, and pluggable architecture.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@masumdev/rn-ui)
|
|
6
|
+
[](./LICENSE)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
---
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
- Light, dark, and system color-scheme support.
|
|
11
|
-
- Typed theme tokens for colors, fonts, typography, spacing, radius, shadows, and component sizing.
|
|
12
|
-
- Pluggable theme persistence through a tiny storage adapter.
|
|
13
|
-
- Pluggable fonts through theme tokens.
|
|
14
|
-
- Pluggable icons through render functions or React nodes.
|
|
15
|
-
- Reanimated-powered component interactions.
|
|
16
|
-
- Mobile-first primitives for forms, feedback, overlays, data display, and local navigation UI.
|
|
10
|
+
## 📚 Full Documentation
|
|
17
11
|
|
|
18
|
-
|
|
12
|
+
Comprehensive guides, component previews, prop tables, and interactive examples are available in our official documentation:
|
|
19
13
|
|
|
20
|
-
|
|
14
|
+
👉 **[Explore Full Documentation & Component Catalog](https://react-native-library-docs.netlify.app/rn-ui)**
|
|
21
15
|
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
16
|
+
- 📖 **[Installation Guide](https://react-native-library-docs.netlify.app/rn-ui/installation)**
|
|
17
|
+
- 🎨 **[Theming & Dark Mode](https://react-native-library-docs.netlify.app/rn-ui/theming)**
|
|
18
|
+
- 🧩 **[55+ Component Catalog](https://react-native-library-docs.netlify.app/rn-ui/components)**
|
|
19
|
+
- 🛠️ **[Building Custom Components](https://react-native-library-docs.netlify.app/rn-ui/custom-components)**
|
|
20
|
+
- 📜 **[Architectural Rules](./RULES.md)**
|
|
21
|
+
- 🚀 **[Changelog](https://react-native-library-docs.netlify.app/rn-ui/changelog)**
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
---
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
- `@gorhom/bottom-sheet`
|
|
25
|
+
## ✨ Features
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
- **Flat Aesthetic**: Border-based, clean UI primitives with zero elevation/shadow by default.
|
|
28
|
+
- **Dynamic Theme Engine**: Light, dark, and system preference support via `ThemeProvider`.
|
|
29
|
+
- **55+ Typed Components**: Forms, feedback, overlays, navigation, data display, and chat UI primitives.
|
|
30
|
+
- **Pluggable Architecture**: Zero lock-in for fonts, icons, navigation, storage, or haptics.
|
|
31
|
+
- **Reanimated Powered**: Smooth 60fps micro-animations powered by `react-native-reanimated`.
|
|
32
|
+
- **Strict Type Safety**: 100% TypeScript with zero `any` types and shared base UI prop interfaces.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
---
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
## 📦 Quick Install
|
|
37
37
|
|
|
38
|
+
### Expo
|
|
38
39
|
```sh
|
|
39
40
|
npx expo install react-native-reanimated react-native-worklets react-native-gesture-handler react-native-calendars
|
|
40
41
|
bun add @masumdev/rn-ui @gorhom/bottom-sheet
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
If you use npm instead of Bun:
|
|
44
|
-
|
|
45
|
-
```sh
|
|
46
|
-
npx expo install react-native-reanimated react-native-worklets react-native-gesture-handler react-native-calendars
|
|
47
|
-
npm install @masumdev/rn-ui @gorhom/bottom-sheet
|
|
48
|
-
```
|
|
49
|
-
|
|
50
44
|
### React Native CLI
|
|
51
|
-
|
|
52
|
-
Install the package and peer dependencies:
|
|
53
|
-
|
|
54
45
|
```sh
|
|
55
46
|
bun add @masumdev/rn-ui react-native-reanimated react-native-worklets react-native-gesture-handler react-native-calendars @gorhom/bottom-sheet
|
|
56
47
|
```
|
|
57
48
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```sh
|
|
61
|
-
cd ios
|
|
62
|
-
pod install
|
|
63
|
-
cd ..
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Setup
|
|
67
|
-
|
|
68
|
-
### 1. Add GestureHandlerRootView
|
|
69
|
-
|
|
70
|
-
Wrap the app root with `GestureHandlerRootView`. This is required for gesture-driven components and `@gorhom/bottom-sheet`.
|
|
49
|
+
---
|
|
71
50
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
```tsx
|
|
75
|
-
import { Stack } from "expo-router";
|
|
76
|
-
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
77
|
-
import { ThemeProvider, ToastProvider } from "@masumdev/rn-ui";
|
|
78
|
-
|
|
79
|
-
export default function RootLayout() {
|
|
80
|
-
return (
|
|
81
|
-
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
82
|
-
<ThemeProvider defaultColorScheme="system">
|
|
83
|
-
<ToastProvider placement="bottom">
|
|
84
|
-
<Stack screenOptions={{ headerShown: false }} />
|
|
85
|
-
</ToastProvider>
|
|
86
|
-
</ThemeProvider>
|
|
87
|
-
</GestureHandlerRootView>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
```
|
|
51
|
+
## ⚡ Quick Setup
|
|
91
52
|
|
|
92
|
-
|
|
53
|
+
Wrap your application root with `GestureHandlerRootView` and `ThemeProvider`:
|
|
93
54
|
|
|
94
55
|
```tsx
|
|
95
56
|
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
@@ -99,7 +60,7 @@ export default function App() {
|
|
|
99
60
|
return (
|
|
100
61
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
101
62
|
<ThemeProvider defaultColorScheme="system">
|
|
102
|
-
<ToastProvider>
|
|
63
|
+
<ToastProvider placement="bottom">
|
|
103
64
|
<RootNavigator />
|
|
104
65
|
</ToastProvider>
|
|
105
66
|
</ThemeProvider>
|
|
@@ -108,1705 +69,39 @@ export default function App() {
|
|
|
108
69
|
}
|
|
109
70
|
```
|
|
110
71
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
Expo SDKs usually create the correct Babel setup, but if your app manages Babel manually, keep the Reanimated plugin last.
|
|
114
|
-
|
|
115
|
-
```js
|
|
116
|
-
module.exports = function (api) {
|
|
117
|
-
api.cache(true);
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
presets: ["babel-preset-expo"],
|
|
121
|
-
plugins: ["react-native-reanimated/plugin"],
|
|
122
|
-
};
|
|
123
|
-
};
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### 3. Wrap Once With ThemeProvider
|
|
127
|
-
|
|
128
|
-
`defaultColorScheme` accepts:
|
|
129
|
-
|
|
130
|
-
- `light`
|
|
131
|
-
- `dark`
|
|
132
|
-
- `system`
|
|
133
|
-
|
|
134
|
-
`system` follows the device color scheme through React Native `useColorScheme()`.
|
|
135
|
-
|
|
136
|
-
`ThemeProvider` should live once near the app root. Components read tokens through context, so nested providers are only needed if a subtree intentionally uses a different theme.
|
|
137
|
-
|
|
138
|
-
### 4. Add ToastProvider When Using Toast
|
|
139
|
-
|
|
140
|
-
`useToast()` requires `ToastProvider` above the screen/component that calls it.
|
|
141
|
-
|
|
142
|
-
```tsx
|
|
143
|
-
import { ToastProvider } from "@masumdev/rn-ui";
|
|
144
|
-
|
|
145
|
-
<ToastProvider placement="bottom">
|
|
146
|
-
<AppScreens />
|
|
147
|
-
</ToastProvider>;
|
|
148
|
-
```
|
|
72
|
+
---
|
|
149
73
|
|
|
150
|
-
## Usage
|
|
74
|
+
## 🚀 Basic Usage
|
|
151
75
|
|
|
152
76
|
```tsx
|
|
153
|
-
import
|
|
77
|
+
import React from "react";
|
|
78
|
+
import { Box, Card, Text, Badge, Button, useTheme } from "@masumdev/rn-ui";
|
|
154
79
|
|
|
155
|
-
export function
|
|
156
|
-
const {
|
|
80
|
+
export function ExampleScreen() {
|
|
81
|
+
const { toggleColorScheme } = useTheme();
|
|
157
82
|
|
|
158
83
|
return (
|
|
159
|
-
<Box flex={1} bg="background" p="lg" gap="md">
|
|
160
|
-
<Card>
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
84
|
+
<Box flex={1} bg="background" p="lg" gap="md" justify="center">
|
|
85
|
+
<Card elevated>
|
|
86
|
+
<Box row justify="space-between" align="center">
|
|
87
|
+
<Text variant="h3">@masumdev/rn-ui</Text>
|
|
88
|
+
<Badge tone="success">Active</Badge>
|
|
89
|
+
</Box>
|
|
90
|
+
<Text color="textMuted" style={{ marginTop: 8 }}>
|
|
91
|
+
Flat, token-driven React Native components built for speed and type-safety.
|
|
165
92
|
</Text>
|
|
166
93
|
</Card>
|
|
167
94
|
|
|
168
|
-
<Button fullWidth onPress={toggleColorScheme}>
|
|
169
|
-
Toggle Theme
|
|
170
|
-
</Button>
|
|
171
|
-
|
|
172
|
-
<Button variant="outline" onPress={() => setColorScheme("system")}>
|
|
173
|
-
Follow System
|
|
95
|
+
<Button tone="primary" variant="filled" fullWidth onPress={toggleColorScheme}>
|
|
96
|
+
Toggle Theme Mode
|
|
174
97
|
</Button>
|
|
175
98
|
</Box>
|
|
176
99
|
);
|
|
177
100
|
}
|
|
178
101
|
```
|
|
179
102
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
Override only the tokens you need. The library deep-merges your values with the default light and dark themes.
|
|
183
|
-
|
|
184
|
-
When changing `primary`, also set `primarySoft`, `primaryStrong`, and `onPrimary` so filled buttons, soft badges, sliders, switches, focus states, and selected controls stay readable in both modes.
|
|
185
|
-
|
|
186
|
-
```tsx
|
|
187
|
-
import { ThemeProvider } from "@masumdev/rn-ui";
|
|
188
|
-
|
|
189
|
-
const themes = {
|
|
190
|
-
light: {
|
|
191
|
-
colors: {
|
|
192
|
-
background: "#F6FBFC",
|
|
193
|
-
surface: "#FFFFFF",
|
|
194
|
-
text: "#06202A",
|
|
195
|
-
textMuted: "#476371",
|
|
196
|
-
primary: "#06B6D4",
|
|
197
|
-
primarySoft: "#CFFAFE",
|
|
198
|
-
primaryStrong: "#0891B2",
|
|
199
|
-
onPrimary: "#06202A",
|
|
200
|
-
accent: "#F43F5E",
|
|
201
|
-
accentSoft: "#FFE4E6",
|
|
202
|
-
border: "#B8D4DD",
|
|
203
|
-
borderMuted: "#D8E8EE",
|
|
204
|
-
},
|
|
205
|
-
fonts: {
|
|
206
|
-
regular: "OutfitRegular",
|
|
207
|
-
medium: "OutfitMedium",
|
|
208
|
-
semibold: "OutfitSemiBold",
|
|
209
|
-
bold: "OutfitBold",
|
|
210
|
-
},
|
|
211
|
-
typography: {
|
|
212
|
-
body: {
|
|
213
|
-
fontSize: 16,
|
|
214
|
-
},
|
|
215
|
-
label: {
|
|
216
|
-
letterSpacing: 0.2,
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
radii: {
|
|
220
|
-
lg: 14,
|
|
221
|
-
xl: 18,
|
|
222
|
-
},
|
|
223
|
-
},
|
|
224
|
-
dark: {
|
|
225
|
-
colors: {
|
|
226
|
-
background: "#06141B",
|
|
227
|
-
surface: "#0B202A",
|
|
228
|
-
text: "#F2FBFD",
|
|
229
|
-
textMuted: "#B7CDD5",
|
|
230
|
-
primary: "#67E8F9",
|
|
231
|
-
primarySoft: "#164E63",
|
|
232
|
-
primaryStrong: "#A5F3FC",
|
|
233
|
-
onPrimary: "#06202A",
|
|
234
|
-
accent: "#FB7185",
|
|
235
|
-
accentSoft: "#4C1220",
|
|
236
|
-
border: "#2A5260",
|
|
237
|
-
borderMuted: "#173441",
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
export function App() {
|
|
243
|
-
return (
|
|
244
|
-
<ThemeProvider defaultColorScheme="system" themes={themes}>
|
|
245
|
-
<RootNavigator />
|
|
246
|
-
</ThemeProvider>
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
Common tokens:
|
|
252
|
-
|
|
253
|
-
- `colors.background`: screen background.
|
|
254
|
-
- `colors.surface`: cards, menus, sheets, and panels.
|
|
255
|
-
- `colors.text`, `textMuted`, `textSubtle`: text hierarchy.
|
|
256
|
-
- `colors.primary`, `primarySoft`, `primaryStrong`, `onPrimary`: main action color system.
|
|
257
|
-
- `colors.border`, `borderMuted`, `divider`: flat border system.
|
|
258
|
-
- `spacing`: named spacing scale used by layout primitives.
|
|
259
|
-
- `radii`: corner radius scale.
|
|
260
|
-
- `typography`: text variants used by `Text`.
|
|
261
|
-
- `components`: component-specific sizes such as `button`, `iconButton`, `switch`, `slider`, and `floatingActionButton`.
|
|
262
|
-
|
|
263
|
-
## Fonts
|
|
264
|
-
|
|
265
|
-
Fonts are pluggable. `rn-ui` does not load font files by itself, so it works with Expo Font, local TTF files, React Native CLI font linking, or any other font-loading setup.
|
|
266
|
-
|
|
267
|
-
Load the font in your app, then pass the registered font names through the `fonts` token.
|
|
268
|
-
|
|
269
|
-
```tsx
|
|
270
|
-
import { useFonts } from "expo-font";
|
|
271
|
-
import { ThemeProvider } from "@masumdev/rn-ui";
|
|
272
|
-
|
|
273
|
-
const themes = {
|
|
274
|
-
light: {
|
|
275
|
-
fonts: {
|
|
276
|
-
regular: "OutfitRegular",
|
|
277
|
-
medium: "OutfitMedium",
|
|
278
|
-
semibold: "OutfitSemiBold",
|
|
279
|
-
bold: "OutfitBold",
|
|
280
|
-
mono: "SpaceMono",
|
|
281
|
-
},
|
|
282
|
-
},
|
|
283
|
-
dark: {
|
|
284
|
-
fonts: {
|
|
285
|
-
regular: "OutfitRegular",
|
|
286
|
-
medium: "OutfitMedium",
|
|
287
|
-
semibold: "OutfitSemiBold",
|
|
288
|
-
bold: "OutfitBold",
|
|
289
|
-
mono: "SpaceMono",
|
|
290
|
-
},
|
|
291
|
-
},
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
export function App() {
|
|
295
|
-
const [loaded] = useFonts({
|
|
296
|
-
OutfitRegular: require("./assets/fonts/Outfit-Regular.ttf"),
|
|
297
|
-
OutfitMedium: require("./assets/fonts/Outfit-Medium.ttf"),
|
|
298
|
-
OutfitSemiBold: require("./assets/fonts/Outfit-SemiBold.ttf"),
|
|
299
|
-
OutfitBold: require("./assets/fonts/Outfit-Bold.ttf"),
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
if (!loaded) return null;
|
|
303
|
-
|
|
304
|
-
return (
|
|
305
|
-
<ThemeProvider themes={themes}>
|
|
306
|
-
<RootNavigator />
|
|
307
|
-
</ThemeProvider>
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
Default mapping:
|
|
313
|
-
|
|
314
|
-
- `display`, `h1`, `h2` use `fonts.bold`
|
|
315
|
-
- `h3`, `title`, `subtitle` use `fonts.semibold`
|
|
316
|
-
- `label`, `labelSmall` use `fonts.semibold` or `fonts.medium`
|
|
317
|
-
- `body`, `bodySmall`, `caption` use `fonts.regular`
|
|
318
|
-
|
|
319
|
-
You can still override one typography variant directly:
|
|
320
|
-
|
|
321
|
-
```tsx
|
|
322
|
-
const themes = {
|
|
323
|
-
light: {
|
|
324
|
-
typography: {
|
|
325
|
-
h1: {
|
|
326
|
-
fontFamily: "CustomDisplayBold",
|
|
327
|
-
fontSize: 34,
|
|
328
|
-
lineHeight: 42,
|
|
329
|
-
},
|
|
330
|
-
},
|
|
331
|
-
},
|
|
332
|
-
};
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
## Persisting Theme Choice
|
|
336
|
-
|
|
337
|
-
The core package does not depend on AsyncStorage, SQLite, Expo SecureStore, MMKV, or Zustand. Pass a small adapter from your app.
|
|
338
|
-
|
|
339
|
-
`ThemeProvider` waits for storage hydration by default, so the app can avoid rendering light mode before the saved dark mode is loaded.
|
|
340
|
-
|
|
341
|
-
### Expo SecureStore
|
|
342
|
-
|
|
343
|
-
```tsx
|
|
344
|
-
import * as SecureStore from "expo-secure-store";
|
|
345
|
-
import { ThemeProvider } from "@masumdev/rn-ui";
|
|
346
|
-
|
|
347
|
-
const storage = {
|
|
348
|
-
getItem: (key: string) => SecureStore.getItemAsync(key),
|
|
349
|
-
setItem: (key: string, value: string) => SecureStore.setItemAsync(key, value),
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
export function App() {
|
|
353
|
-
return (
|
|
354
|
-
<ThemeProvider storage={storage} defaultColorScheme="system">
|
|
355
|
-
<RootNavigator />
|
|
356
|
-
</ThemeProvider>
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
### AsyncStorage
|
|
362
|
-
|
|
363
|
-
```tsx
|
|
364
|
-
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
365
|
-
import { ThemeProvider } from "@masumdev/rn-ui";
|
|
366
|
-
|
|
367
|
-
const storage = {
|
|
368
|
-
getItem: (key: string) => AsyncStorage.getItem(key),
|
|
369
|
-
setItem: (key: string, value: string) => AsyncStorage.setItem(key, value),
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
export function App() {
|
|
373
|
-
return (
|
|
374
|
-
<ThemeProvider storage={storage} defaultColorScheme="system">
|
|
375
|
-
<RootNavigator />
|
|
376
|
-
</ThemeProvider>
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
Expo Go support depends on whether the chosen storage native module is available in the runtime. In the native playground, SecureStore is used because it works cleanly with the Expo workflow.
|
|
382
|
-
|
|
383
|
-
## Logging
|
|
384
|
-
|
|
385
|
-
The library does not force logging. Apps can decide how noisy persistence logs should be.
|
|
386
|
-
|
|
387
|
-
Recommended app pattern:
|
|
388
|
-
|
|
389
|
-
```tsx
|
|
390
|
-
const ENABLE_THEME_DEBUG_LOGS = false;
|
|
391
|
-
|
|
392
|
-
const debugTheme = (...args: unknown[]) => {
|
|
393
|
-
if (__DEV__ && ENABLE_THEME_DEBUG_LOGS) {
|
|
394
|
-
console.debug("[rn-ui]", ...args);
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
const errorTheme = (...args: unknown[]) => {
|
|
399
|
-
console.error("[rn-ui]", ...args);
|
|
400
|
-
};
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
Use debug logs for successful hydrate/save messages and error logs for failed storage operations.
|
|
404
|
-
|
|
405
|
-
## Components
|
|
406
|
-
|
|
407
|
-
Current core components:
|
|
408
|
-
|
|
409
|
-
- `Box`
|
|
410
|
-
- `Text`
|
|
411
|
-
- `BottomSheet`
|
|
412
|
-
- `Accordion`
|
|
413
|
-
- `Alert`
|
|
414
|
-
- `AlertDialog`
|
|
415
|
-
- `Button`
|
|
416
|
-
- `IconButton`
|
|
417
|
-
- `Badge`
|
|
418
|
-
- `Card`
|
|
419
|
-
- `Divider`
|
|
420
|
-
- `AspectRatio`
|
|
421
|
-
- `Attachment`
|
|
422
|
-
- `Avatar`
|
|
423
|
-
- `Bubble`
|
|
424
|
-
- `ButtonGroup`
|
|
425
|
-
- `Calendar`
|
|
426
|
-
- `Carousel`
|
|
427
|
-
- `Checkbox`
|
|
428
|
-
- `Collapsible`
|
|
429
|
-
- `Combobox`
|
|
430
|
-
- `ContextMenu`
|
|
431
|
-
- `DropdownMenu`
|
|
432
|
-
- `Empty`
|
|
433
|
-
- `HoverCard`
|
|
434
|
-
- `Input`
|
|
435
|
-
- `InputGroup`
|
|
436
|
-
- `InputOTP`
|
|
437
|
-
- `Item`
|
|
438
|
-
- `KeyboardAvoiding`
|
|
439
|
-
- `Label`
|
|
440
|
-
- `FormField`
|
|
441
|
-
- `Switch`
|
|
442
|
-
- `RadioGroup`
|
|
443
|
-
- `Slider`
|
|
444
|
-
- `Rating`
|
|
445
|
-
- `Progress`
|
|
446
|
-
- `Skeleton`
|
|
447
|
-
- `Tabs`
|
|
448
|
-
- `Stepper`
|
|
449
|
-
- `Sheet`
|
|
450
|
-
- `Toast`
|
|
451
|
-
- `Textarea`
|
|
452
|
-
- `Select`
|
|
453
|
-
- `Command`
|
|
454
|
-
- `Popover`
|
|
455
|
-
- `Pagination`
|
|
456
|
-
- `Breadcrumb`
|
|
457
|
-
- `Table`
|
|
458
|
-
- `DataList`
|
|
459
|
-
- `Timeline`
|
|
460
|
-
- `MetricCard`
|
|
461
|
-
- `FloatingActionButton`
|
|
462
|
-
|
|
463
|
-
Icons are intentionally not tied to Ionicons or Expo. Pass any React node or render function.
|
|
464
|
-
|
|
465
|
-
```tsx
|
|
466
|
-
import { Button } from "@masumdev/rn-ui";
|
|
467
|
-
import { Ionicons } from "@expo/vector-icons";
|
|
468
|
-
|
|
469
|
-
<Button
|
|
470
|
-
leftIcon={({ color, size }) => (
|
|
471
|
-
<Ionicons name="add" color={color} size={size} />
|
|
472
|
-
)}
|
|
473
|
-
>
|
|
474
|
-
Create
|
|
475
|
-
</Button>;
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
### Accordion
|
|
479
|
-
|
|
480
|
-
Use `Accordion` for expandable flat bordered sections. It supports controlled and uncontrolled state.
|
|
481
|
-
|
|
482
|
-
```tsx
|
|
483
|
-
import { Accordion, Text } from "@masumdev/rn-ui";
|
|
484
|
-
|
|
485
|
-
<Accordion
|
|
486
|
-
defaultOpenIds={["theme"]}
|
|
487
|
-
items={[
|
|
488
|
-
{
|
|
489
|
-
id: "theme",
|
|
490
|
-
title: "Theme tokens",
|
|
491
|
-
subtitle: "Colors, fonts, spacing, and radius",
|
|
492
|
-
content: "Accordion follows the same flat token-driven style.",
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
id: "icons",
|
|
496
|
-
title: "Generic icons",
|
|
497
|
-
content: <Text color="textMuted">Icons can be render functions.</Text>,
|
|
498
|
-
},
|
|
499
|
-
]}
|
|
500
|
-
/>;
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
Controlled:
|
|
504
|
-
|
|
505
|
-
```tsx
|
|
506
|
-
const [openIds, setOpenIds] = React.useState(["theme"]);
|
|
507
|
-
|
|
508
|
-
<Accordion
|
|
509
|
-
openIds={openIds}
|
|
510
|
-
onOpenChange={setOpenIds}
|
|
511
|
-
allowMultiple
|
|
512
|
-
items={items}
|
|
513
|
-
/>;
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
Animation:
|
|
517
|
-
|
|
518
|
-
- Default animation uses Reanimated.
|
|
519
|
-
- Uses the package Reanimated peer dependency.
|
|
520
|
-
- Content animates height and opacity.
|
|
521
|
-
- Indicator animates rotation.
|
|
522
|
-
- Pass `animated={false}` to disable animation.
|
|
523
|
-
- Pass `animationDuration` to tune timing.
|
|
524
|
-
|
|
525
|
-
```tsx
|
|
526
|
-
<Accordion animationDuration={220} defaultOpenIds={["theme"]} items={items} />
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
Custom animation can be plugged in through `animationComponents`:
|
|
530
|
-
|
|
531
|
-
```tsx
|
|
532
|
-
import type {
|
|
533
|
-
AccordionAnimatedContentProps,
|
|
534
|
-
AccordionAnimatedIndicatorProps,
|
|
535
|
-
} from "@masumdev/rn-ui";
|
|
536
|
-
|
|
537
|
-
function CustomAccordionContent(props: AccordionAnimatedContentProps) {
|
|
538
|
-
return <YourAnimatedContent {...props} />;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function CustomAccordionIndicator(props: AccordionAnimatedIndicatorProps) {
|
|
542
|
-
return <YourAnimatedIndicator {...props} />;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
<Accordion
|
|
546
|
-
items={items}
|
|
547
|
-
animationComponents={{
|
|
548
|
-
Content: CustomAccordionContent,
|
|
549
|
-
Indicator: CustomAccordionIndicator,
|
|
550
|
-
}}
|
|
551
|
-
/>;
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
### Button
|
|
555
|
-
|
|
556
|
-
Use `Button` for text and icon actions. `variant` controls visual treatment, while `tone` controls semantic color.
|
|
557
|
-
|
|
558
|
-
```tsx
|
|
559
|
-
<Button variant="filled" tone="primary">
|
|
560
|
-
Continue
|
|
561
|
-
</Button>
|
|
562
|
-
|
|
563
|
-
<Button variant="outline" tone="secondary">
|
|
564
|
-
Cancel
|
|
565
|
-
</Button>
|
|
566
|
-
|
|
567
|
-
<Button tone="danger">Delete</Button>
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
`variant="danger"` is still accepted for older usage, but new code should prefer `tone="danger"` so variants stay visual-only.
|
|
571
|
-
|
|
572
|
-
### IconButton
|
|
573
|
-
|
|
574
|
-
Use `IconButton` for icon-only actions. Use `tone` for semantic token colors, or `color` for a token name/custom color override.
|
|
575
|
-
|
|
576
|
-
```tsx
|
|
577
|
-
<IconButton
|
|
578
|
-
icon={({ color, size }) => <Settings color={color} size={size} />}
|
|
579
|
-
variant="outline"
|
|
580
|
-
tone="secondary"
|
|
581
|
-
/>
|
|
582
|
-
|
|
583
|
-
<IconButton
|
|
584
|
-
icon={({ color, size }) => <Trash color={color} size={size} />}
|
|
585
|
-
tone="danger"
|
|
586
|
-
/>
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
### BottomSheet
|
|
590
|
-
|
|
591
|
-
Use `BottomSheet` for a theme-aware wrapper around `@gorhom/bottom-sheet`. The app must install Gorhom and its runtime peers.
|
|
592
|
-
|
|
593
|
-
```sh
|
|
594
|
-
bun add @gorhom/bottom-sheet react-native-gesture-handler
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
Wrap the app root with `GestureHandlerRootView` at app level:
|
|
598
|
-
|
|
599
|
-
```tsx
|
|
600
|
-
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
601
|
-
|
|
602
|
-
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
603
|
-
<ThemeProvider>
|
|
604
|
-
<App />
|
|
605
|
-
</ThemeProvider>
|
|
606
|
-
</GestureHandlerRootView>;
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
If the app uses Reanimated 4, add the Worklets Babel plugin in the app config:
|
|
610
|
-
|
|
611
|
-
```js
|
|
612
|
-
module.exports = function (api) {
|
|
613
|
-
api.cache(true);
|
|
614
|
-
|
|
615
|
-
return {
|
|
616
|
-
presets: ["babel-preset-expo"],
|
|
617
|
-
plugins: ["react-native-reanimated/plugin"],
|
|
618
|
-
};
|
|
619
|
-
};
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
Use the themed wrapper:
|
|
623
|
-
|
|
624
|
-
```tsx
|
|
625
|
-
import {
|
|
626
|
-
BottomSheet,
|
|
627
|
-
BottomSheetView,
|
|
628
|
-
Button,
|
|
629
|
-
Text,
|
|
630
|
-
type BottomSheetMethods,
|
|
631
|
-
} from '@masumdev/rn-ui';
|
|
632
|
-
|
|
633
|
-
const bottomSheetRef = React.useRef<BottomSheetMethods>(null);
|
|
634
|
-
const snapPoints = React.useMemo(() => ['35%', '70%'], []);
|
|
635
|
-
|
|
636
|
-
<Button onPress={() => bottomSheetRef.current?.snapToIndex(0)}>
|
|
637
|
-
Open Sheet
|
|
638
|
-
</Button>
|
|
639
|
-
|
|
640
|
-
<BottomSheet
|
|
641
|
-
ref={bottomSheetRef}
|
|
642
|
-
index={-1}
|
|
643
|
-
snapPoints={snapPoints}
|
|
644
|
-
enablePanDownToClose
|
|
645
|
-
>
|
|
646
|
-
<BottomSheetView>
|
|
647
|
-
<Text variant="title">Theme-aware sheet</Text>
|
|
648
|
-
</BottomSheetView>
|
|
649
|
-
</BottomSheet>
|
|
650
|
-
```
|
|
651
|
-
|
|
652
|
-
The wrapper maps theme tokens to Gorhom props:
|
|
653
|
-
|
|
654
|
-
- `backgroundStyle` uses `colors.surface`, `colors.border`, and `radii.xxl`
|
|
655
|
-
- `handleIndicatorStyle` uses `colors.border`
|
|
656
|
-
- `style` keeps shadow/elevation at zero
|
|
657
|
-
- `backdropComponent` defaults to a themed `BottomSheetBackdrop`
|
|
658
|
-
|
|
659
|
-
Useful wrapper props:
|
|
660
|
-
|
|
661
|
-
- `withBackdrop`
|
|
662
|
-
- `backdropOpacity`
|
|
663
|
-
- `backdropAppearsOnIndex`
|
|
664
|
-
- `backdropDisappearsOnIndex`
|
|
665
|
-
- `backdropPressBehavior`
|
|
666
|
-
- `backdropStyle`
|
|
667
|
-
|
|
668
|
-
Gorhom primitives are re-exported for convenience: `BottomSheetView`, `BottomSheetScrollView`, `BottomSheetFlatList`, `BottomSheetSectionList`, `BottomSheetTextInput`, `BottomSheetModal`, `BottomSheetModalProvider`, `useBottomSheet`, and `useBottomSheetModal`.
|
|
669
|
-
|
|
670
|
-
### Alert
|
|
671
|
-
|
|
672
|
-
Use `Alert` for inline feedback. It supports semantic tones, flat variants, generic icons, optional action, and optional close control.
|
|
673
|
-
|
|
674
|
-
```tsx
|
|
675
|
-
import { Alert } from "@masumdev/rn-ui";
|
|
676
|
-
|
|
677
|
-
<Alert tone="info" title="Information">
|
|
678
|
-
This message uses theme tokens and adapts to light or dark mode.
|
|
679
|
-
</Alert>;
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
With icon and action:
|
|
683
|
-
|
|
684
|
-
```tsx
|
|
685
|
-
<Alert
|
|
686
|
-
tone="success"
|
|
687
|
-
variant="outline"
|
|
688
|
-
icon={({ color, size }) => <Icon name="check" color={color} size={size} />}
|
|
689
|
-
action={{
|
|
690
|
-
label: "View details",
|
|
691
|
-
onPress: openDetails,
|
|
692
|
-
}}
|
|
693
|
-
>
|
|
694
|
-
Your changes were saved.
|
|
695
|
-
</Alert>
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
Dismissible alert:
|
|
699
|
-
|
|
700
|
-
```tsx
|
|
701
|
-
<Alert
|
|
702
|
-
tone="warning"
|
|
703
|
-
title="Unsaved changes"
|
|
704
|
-
dismissible
|
|
705
|
-
closeIcon={({ color, size }) => <Icon name="x" color={color} size={size} />}
|
|
706
|
-
>
|
|
707
|
-
This alert removes itself when the close button is pressed.
|
|
708
|
-
</Alert>
|
|
709
|
-
```
|
|
710
|
-
|
|
711
|
-
`icon`, `action.icon`, and `closeIcon` are all pluggable through `RenderIcon`.
|
|
712
|
-
|
|
713
|
-
Dismiss animations use Reanimated by default. Use `animated={false}` to disable or `animationDuration` to tune timing.
|
|
714
|
-
|
|
715
|
-
```tsx
|
|
716
|
-
<Alert dismissible animationDuration={220}>
|
|
717
|
-
Animated dismissible alert.
|
|
718
|
-
</Alert>
|
|
719
|
-
```
|
|
720
|
-
|
|
721
|
-
Action icons can be fully overridden, including custom open/close animation owned by the app:
|
|
722
|
-
|
|
723
|
-
```tsx
|
|
724
|
-
<Alert
|
|
725
|
-
action={{
|
|
726
|
-
label: expanded ? "Hide details" : "View details",
|
|
727
|
-
icon: ({ color, size }) => (
|
|
728
|
-
<AnimatedChevron expanded={expanded} color={color} size={size} />
|
|
729
|
-
),
|
|
730
|
-
onPress: () => setExpanded((value) => !value),
|
|
731
|
-
}}
|
|
732
|
-
>
|
|
733
|
-
Alert content.
|
|
734
|
-
</Alert>
|
|
735
|
-
```
|
|
736
|
-
|
|
737
|
-
### AlertDialog
|
|
738
|
-
|
|
739
|
-
Use `AlertDialog` for modal confirmation and blocking feedback. It uses React Native `Modal` and Reanimated by default.
|
|
740
|
-
|
|
741
|
-
```tsx
|
|
742
|
-
const [visible, setVisible] = React.useState(false);
|
|
743
|
-
|
|
744
|
-
<Button onPress={() => setVisible(true)}>Delete</Button>
|
|
745
|
-
|
|
746
|
-
<AlertDialog
|
|
747
|
-
visible={visible}
|
|
748
|
-
tone="danger"
|
|
749
|
-
title="Delete item?"
|
|
750
|
-
description="This action cannot be undone."
|
|
751
|
-
confirmText="Delete"
|
|
752
|
-
cancelText="Cancel"
|
|
753
|
-
onConfirm={deleteItem}
|
|
754
|
-
onCancel={() => setVisible(false)}
|
|
755
|
-
onClose={() => setVisible(false)}
|
|
756
|
-
/>
|
|
757
|
-
```
|
|
758
|
-
|
|
759
|
-
With pluggable icons:
|
|
760
|
-
|
|
761
|
-
```tsx
|
|
762
|
-
<AlertDialog
|
|
763
|
-
visible={visible}
|
|
764
|
-
icon={({ color, size }) => <Trash color={color} size={size} />}
|
|
765
|
-
closeIcon={({ color, size }) => <X color={color} size={size} />}
|
|
766
|
-
onClose={() => setVisible(false)}
|
|
767
|
-
/>
|
|
768
|
-
```
|
|
769
|
-
|
|
770
|
-
Useful props:
|
|
771
|
-
|
|
772
|
-
- `dismissOnBackdropPress`
|
|
773
|
-
- `confirmLoading`
|
|
774
|
-
- `confirmDisabled`
|
|
775
|
-
- `cancelDisabled`
|
|
776
|
-
- `animated`
|
|
777
|
-
- `animationDuration`
|
|
778
|
-
- `modalProps`
|
|
779
|
-
|
|
780
|
-
Android notes:
|
|
781
|
-
|
|
782
|
-
- `AlertDialog` enables `statusBarTranslucent`, `navigationBarTranslucent`, and `hardwareAccelerated` by default so the backdrop can cover modern Android system UI areas more consistently.
|
|
783
|
-
- If an app uses custom edge-to-edge or navigation bar handling, override native modal behavior through `modalProps`.
|
|
784
|
-
- Android navigation bar behavior is controlled by system UI, not by the modal backdrop view. If the app needs to hide or style the navigation bar while a dialog is open, handle it at app level with the system UI solution already used by that app. `rn-ui` does not depend on Expo navigation-bar APIs.
|
|
785
|
-
|
|
786
|
-
### AspectRatio
|
|
787
|
-
|
|
788
|
-
Use `AspectRatio` to display layout/media with a specific proportion. The child element automatically stretches to fill the container.
|
|
789
|
-
|
|
790
|
-
```tsx
|
|
791
|
-
import { AspectRatio } from "@masumdev/rn-ui";
|
|
792
|
-
import { Image } from "react-native";
|
|
793
|
-
|
|
794
|
-
<AspectRatio ratio={16 / 9} radius="md">
|
|
795
|
-
<Image
|
|
796
|
-
source={{
|
|
797
|
-
uri: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe",
|
|
798
|
-
}}
|
|
799
|
-
style={{ resizeMode: "cover" }}
|
|
800
|
-
/>
|
|
801
|
-
</AspectRatio>;
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
### Attachment
|
|
805
|
-
|
|
806
|
-
Use `Attachment` to represent file/image uploads or documents. Supports grid-like `card` previews and full-width list `row` views with status indicators.
|
|
807
|
-
|
|
808
|
-
```tsx
|
|
809
|
-
import { Attachment } from '@masumdev/rn-ui';
|
|
810
|
-
|
|
811
|
-
// Card layout (Image preview)
|
|
812
|
-
<Attachment
|
|
813
|
-
layout="card"
|
|
814
|
-
name="workspace.png"
|
|
815
|
-
description="PNG • 820 KB"
|
|
816
|
-
thumbnail="https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe"
|
|
817
|
-
onRemove={removeFile}
|
|
818
|
-
/>
|
|
819
|
-
|
|
820
|
-
// Row layout (Document preview)
|
|
821
|
-
<Attachment
|
|
822
|
-
layout="row"
|
|
823
|
-
name="sales-dashboard.pdf"
|
|
824
|
-
description="Uploading • 64%"
|
|
825
|
-
descriptionTone="info"
|
|
826
|
-
loading
|
|
827
|
-
onRemove={removeFile}
|
|
828
|
-
/>
|
|
829
|
-
```
|
|
830
|
-
|
|
831
|
-
`descriptionTone` accepts `default`, `info`, `success`, `warning`, and `danger`. Use it instead of deriving visual state from description copy. `thumbnail`, `fileIcon`, and `closeIcon` are pluggable through `RenderIcon`.
|
|
832
|
-
|
|
833
|
-
### Avatar
|
|
834
|
-
|
|
835
|
-
Use `Avatar` to display user profile images. Supports sizes (`sm`, `default`, `lg`), custom fallback placeholders, activity badges, and overlapping avatar groups.
|
|
836
|
-
|
|
837
|
-
```tsx
|
|
838
|
-
import {
|
|
839
|
-
Avatar,
|
|
840
|
-
AvatarImage,
|
|
841
|
-
AvatarFallback,
|
|
842
|
-
AvatarBadge,
|
|
843
|
-
AvatarGroup,
|
|
844
|
-
AvatarGroupCount,
|
|
845
|
-
} from '@masumdev/rn-ui';
|
|
846
|
-
|
|
847
|
-
// Individual Avatar with Badge
|
|
848
|
-
<Avatar size="lg">
|
|
849
|
-
<AvatarImage source={{ uri: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb' }} />
|
|
850
|
-
<AvatarFallback>JD</AvatarFallback>
|
|
851
|
-
<AvatarBadge bg={colors.success} />
|
|
852
|
-
</Avatar>
|
|
853
|
-
|
|
854
|
-
// Avatar Group
|
|
855
|
-
<AvatarGroup size="default">
|
|
856
|
-
<Avatar>
|
|
857
|
-
<AvatarImage source={{ uri: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb' }} />
|
|
858
|
-
<AvatarFallback>JD</AvatarFallback>
|
|
859
|
-
</Avatar>
|
|
860
|
-
<Avatar>
|
|
861
|
-
<AvatarImage source={{ uri: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d' }} />
|
|
862
|
-
<AvatarFallback>AM</AvatarFallback>
|
|
863
|
-
</Avatar>
|
|
864
|
-
<AvatarGroupCount count={5} />
|
|
865
|
-
</AvatarGroup>
|
|
866
|
-
```
|
|
867
|
-
|
|
868
|
-
### Bubble
|
|
869
|
-
|
|
870
|
-
Use `BubbleGroup`, `Bubble`, `BubbleContent`, and `BubbleReactions` to display conversation bubbles (chat messages) with support for variants, alignments, and overlay reaction tags.
|
|
871
|
-
|
|
872
|
-
```tsx
|
|
873
|
-
import {
|
|
874
|
-
BubbleGroup,
|
|
875
|
-
Bubble,
|
|
876
|
-
BubbleContent,
|
|
877
|
-
BubbleReactions,
|
|
878
|
-
Text,
|
|
879
|
-
} from "@masumdev/rn-ui";
|
|
880
|
-
|
|
881
|
-
<BubbleGroup>
|
|
882
|
-
{/* Incoming message */}
|
|
883
|
-
<Bubble align="start" variant="secondary">
|
|
884
|
-
<BubbleContent>Hello! How can I help you today?</BubbleContent>
|
|
885
|
-
</Bubble>
|
|
886
|
-
|
|
887
|
-
{/* Outgoing message with reactions */}
|
|
888
|
-
<Bubble align="end" variant="default">
|
|
889
|
-
<BubbleContent>I'd like to customize the colors of my theme.</BubbleContent>
|
|
890
|
-
<BubbleReactions side="bottom" align="end">
|
|
891
|
-
<Text style={{ fontSize: 11 }}>👍 1</Text>
|
|
892
|
-
</BubbleReactions>
|
|
893
|
-
</Bubble>
|
|
894
|
-
</BubbleGroup>;
|
|
895
|
-
```
|
|
896
|
-
|
|
897
|
-
### ButtonGroup
|
|
898
|
-
|
|
899
|
-
Use `ButtonGroup` to group buttons, static text containers, and separators horizontally or vertically with unified borders and border radii.
|
|
900
|
-
|
|
901
|
-
```tsx
|
|
902
|
-
import {
|
|
903
|
-
ButtonGroup,
|
|
904
|
-
ButtonGroupText,
|
|
905
|
-
ButtonGroupSeparator,
|
|
906
|
-
Button,
|
|
907
|
-
} from "@masumdev/rn-ui";
|
|
908
|
-
|
|
909
|
-
<ButtonGroup orientation="horizontal">
|
|
910
|
-
<ButtonGroupText>Prefix</ButtonGroupText>
|
|
911
|
-
<Button variant="outline">Action A</Button>
|
|
912
|
-
<ButtonGroupSeparator />
|
|
913
|
-
<Button variant="outline">Action B</Button>
|
|
914
|
-
</ButtonGroup>;
|
|
915
|
-
```
|
|
916
|
-
|
|
917
|
-
### Calendar
|
|
918
|
-
|
|
919
|
-
Use `Calendar` to render date pickers and handle single date or range select.
|
|
920
|
-
|
|
921
|
-
```tsx
|
|
922
|
-
import { Calendar } from "@masumdev/rn-ui";
|
|
923
|
-
|
|
924
|
-
<Calendar
|
|
925
|
-
markedDates={{
|
|
926
|
-
"2026-07-12": { selected: true, startingDay: true },
|
|
927
|
-
"2026-07-13": { selected: true, color: "muted" },
|
|
928
|
-
"2026-07-14": { selected: true, endingDay: true },
|
|
929
|
-
}}
|
|
930
|
-
/>;
|
|
931
|
-
```
|
|
932
|
-
|
|
933
|
-
### Carousel
|
|
934
|
-
|
|
935
|
-
Use `Carousel` for horizontally scrollable card decks. Pagination is on by default and next/previous icons are pluggable.
|
|
936
|
-
|
|
937
|
-
```tsx
|
|
938
|
-
import {
|
|
939
|
-
Carousel,
|
|
940
|
-
CarouselContent,
|
|
941
|
-
CarouselItem,
|
|
942
|
-
CarouselPrevious,
|
|
943
|
-
CarouselNext,
|
|
944
|
-
} from "@masumdev/rn-ui";
|
|
945
|
-
|
|
946
|
-
<Carousel itemWidth={280}>
|
|
947
|
-
<CarouselContent>
|
|
948
|
-
<CarouselItem>
|
|
949
|
-
<Card>
|
|
950
|
-
<Text>Slide one</Text>
|
|
951
|
-
</Card>
|
|
952
|
-
</CarouselItem>
|
|
953
|
-
<CarouselItem>
|
|
954
|
-
<Card>
|
|
955
|
-
<Text>Slide two</Text>
|
|
956
|
-
</Card>
|
|
957
|
-
</CarouselItem>
|
|
958
|
-
</CarouselContent>
|
|
959
|
-
<CarouselPrevious
|
|
960
|
-
icon={({ color, size }) => <ChevronLeft color={color} size={size} />}
|
|
961
|
-
/>
|
|
962
|
-
<CarouselNext
|
|
963
|
-
icon={({ color, size }) => <ChevronRight color={color} size={size} />}
|
|
964
|
-
/>
|
|
965
|
-
</Carousel>;
|
|
966
|
-
```
|
|
967
|
-
|
|
968
|
-
### Checkbox
|
|
969
|
-
|
|
970
|
-
Use `Checkbox` for controlled boolean state. The check icon is pluggable.
|
|
971
|
-
|
|
972
|
-
```tsx
|
|
973
|
-
<Checkbox
|
|
974
|
-
checked={checked}
|
|
975
|
-
onCheckedChange={setChecked}
|
|
976
|
-
icon={({ color, size }) => <Check color={color} size={size} />}
|
|
977
|
-
/>
|
|
978
|
-
```
|
|
979
|
-
|
|
980
|
-
### Collapsible
|
|
981
|
-
|
|
982
|
-
Use `Collapsible` for composable expandable content.
|
|
983
|
-
|
|
984
|
-
```tsx
|
|
985
|
-
<Collapsible defaultOpen>
|
|
986
|
-
<CollapsibleTrigger>
|
|
987
|
-
<Text variant="label">Toggle details</Text>
|
|
988
|
-
</CollapsibleTrigger>
|
|
989
|
-
<CollapsibleContent>
|
|
990
|
-
<Text color="textMuted">Hidden content.</Text>
|
|
991
|
-
</CollapsibleContent>
|
|
992
|
-
</Collapsible>
|
|
993
|
-
```
|
|
994
|
-
|
|
995
|
-
### Combobox
|
|
996
|
-
|
|
997
|
-
Use `Combobox` for searchable selection. It uses React Native `Modal` by default, stays flat by default, and exposes `modalProps`, `overlayStyle`, `chevronIcon`, and `checkIcon` for app-level customization.
|
|
998
|
-
|
|
999
|
-
```tsx
|
|
1000
|
-
<Combobox value={value} onValueChange={setValue}>
|
|
1001
|
-
<ComboboxInput
|
|
1002
|
-
placeholder="Select framework"
|
|
1003
|
-
chevronIcon={({ color, size }) => (
|
|
1004
|
-
<ChevronsUpDown color={color} size={size} />
|
|
1005
|
-
)}
|
|
1006
|
-
/>
|
|
1007
|
-
<ComboboxContent>
|
|
1008
|
-
<ComboboxList>
|
|
1009
|
-
<ComboboxItem value="expo" label="Expo">
|
|
1010
|
-
Expo
|
|
1011
|
-
</ComboboxItem>
|
|
1012
|
-
<ComboboxItem value="react-native" label="React Native">
|
|
1013
|
-
React Native
|
|
1014
|
-
</ComboboxItem>
|
|
1015
|
-
<ComboboxEmpty>No results.</ComboboxEmpty>
|
|
1016
|
-
</ComboboxList>
|
|
1017
|
-
</ComboboxContent>
|
|
1018
|
-
</Combobox>
|
|
1019
|
-
```
|
|
1020
|
-
|
|
1021
|
-
### ContextMenu
|
|
1022
|
-
|
|
1023
|
-
Use `ContextMenu` for long-press menus. It uses React Native `Modal` by default and keeps system UI behavior app-overridable through `modalProps`.
|
|
1024
|
-
|
|
1025
|
-
```tsx
|
|
1026
|
-
<ContextMenu>
|
|
1027
|
-
<ContextMenuTrigger>
|
|
1028
|
-
<Button variant="outline">Long press</Button>
|
|
1029
|
-
</ContextMenuTrigger>
|
|
1030
|
-
<ContextMenuContent>
|
|
1031
|
-
<ContextMenuLabel>Actions</ContextMenuLabel>
|
|
1032
|
-
<ContextMenuItem onPress={editItem}>Edit</ContextMenuItem>
|
|
1033
|
-
<ContextMenuCheckboxItem checked={pinned} onCheckedChange={setPinned}>
|
|
1034
|
-
Pinned
|
|
1035
|
-
</ContextMenuCheckboxItem>
|
|
1036
|
-
<ContextMenuSeparator />
|
|
1037
|
-
<ContextMenuItem variant="destructive" onPress={deleteItem}>
|
|
1038
|
-
Delete
|
|
1039
|
-
</ContextMenuItem>
|
|
1040
|
-
</ContextMenuContent>
|
|
1041
|
-
</ContextMenu>
|
|
1042
|
-
```
|
|
1043
|
-
|
|
1044
|
-
### DropdownMenu
|
|
1045
|
-
|
|
1046
|
-
Use `DropdownMenu` for tap-triggered action menus. It uses React Native `Modal`, Reanimated entry animation, flat bordered styling, alignment control, checkbox items, shortcuts, and app-overridable modal/backdrop props.
|
|
1047
|
-
|
|
1048
|
-
```tsx
|
|
1049
|
-
<DropdownMenu>
|
|
1050
|
-
<DropdownMenuTrigger style={triggerStyle}>
|
|
1051
|
-
<Text variant="label">Open menu</Text>
|
|
1052
|
-
</DropdownMenuTrigger>
|
|
1053
|
-
<DropdownMenuContent align="start">
|
|
1054
|
-
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
1055
|
-
<DropdownMenuItem onPress={refreshList}>
|
|
1056
|
-
Refresh
|
|
1057
|
-
<DropdownMenuShortcut>R</DropdownMenuShortcut>
|
|
1058
|
-
</DropdownMenuItem>
|
|
1059
|
-
<DropdownMenuCheckboxItem checked={compact} onCheckedChange={setCompact}>
|
|
1060
|
-
Compact mode
|
|
1061
|
-
</DropdownMenuCheckboxItem>
|
|
1062
|
-
<DropdownMenuSeparator />
|
|
1063
|
-
<DropdownMenuItem variant="destructive" onPress={deleteItem}>
|
|
1064
|
-
Delete
|
|
1065
|
-
</DropdownMenuItem>
|
|
1066
|
-
</DropdownMenuContent>
|
|
1067
|
-
</DropdownMenu>
|
|
1068
|
-
```
|
|
1069
|
-
|
|
1070
|
-
Useful props:
|
|
1071
|
-
|
|
1072
|
-
- `open`, `defaultOpen`, and `onOpenChange`
|
|
1073
|
-
- `align="start" | "end"`
|
|
1074
|
-
- `width`, `maxHeight`, and `sideOffset`
|
|
1075
|
-
- `modalProps` and `overlayStyle`
|
|
1076
|
-
- `checkIcon` on `DropdownMenuCheckboxItem`
|
|
1077
|
-
|
|
1078
|
-
Avoid nesting another `Pressable` or `Button` directly inside `DropdownMenuTrigger`; style the trigger itself or render non-pressable visual children inside it.
|
|
1079
|
-
|
|
1080
|
-
### Empty
|
|
1081
|
-
|
|
1082
|
-
Use `Empty` for empty states such as no projects, no data, or no search results. It is composable and mirrors the web pattern with React Native primitives.
|
|
1083
|
-
|
|
1084
|
-
```tsx
|
|
1085
|
-
<Empty>
|
|
1086
|
-
<EmptyHeader>
|
|
1087
|
-
<EmptyMedia variant="icon">
|
|
1088
|
-
<FileText color={colors.text} size={16} />
|
|
1089
|
-
</EmptyMedia>
|
|
1090
|
-
<EmptyTitle>Expo React Native</EmptyTitle>
|
|
1091
|
-
<EmptyDescription>
|
|
1092
|
-
Expo React Native by Ma'sum. Crafted for consistent mobile UI in 2026.
|
|
1093
|
-
</EmptyDescription>
|
|
1094
|
-
</EmptyHeader>
|
|
1095
|
-
|
|
1096
|
-
<EmptyContent>
|
|
1097
|
-
<Box row center gap="sm">
|
|
1098
|
-
<Button size="sm">Explore 2026</Button>
|
|
1099
|
-
<Button size="sm" variant="outline" tone="secondary">
|
|
1100
|
-
by Ma'sum
|
|
1101
|
-
</Button>
|
|
1102
|
-
</Box>
|
|
1103
|
-
<Button size="sm" variant="ghost" tone="secondary">
|
|
1104
|
-
Expo React Native
|
|
1105
|
-
</Button>
|
|
1106
|
-
</EmptyContent>
|
|
1107
|
-
</Empty>
|
|
1108
|
-
```
|
|
1109
|
-
|
|
1110
|
-
Parts:
|
|
1111
|
-
|
|
1112
|
-
- `Empty`
|
|
1113
|
-
- `EmptyHeader`
|
|
1114
|
-
- `EmptyMedia`
|
|
1115
|
-
- `EmptyTitle`
|
|
1116
|
-
- `EmptyDescription`
|
|
1117
|
-
- `EmptyContent`
|
|
1118
|
-
|
|
1119
|
-
### HoverCard
|
|
1120
|
-
|
|
1121
|
-
Use `HoverCard` for richer contextual previews. On mobile, the default trigger is `longPress`.
|
|
1122
|
-
|
|
1123
|
-
```tsx
|
|
1124
|
-
<HoverCard openDelay={10} closeDelay={100}>
|
|
1125
|
-
<HoverCardTrigger style={triggerStyle}>
|
|
1126
|
-
<Text variant="label" color="primary">
|
|
1127
|
-
Expo React Native
|
|
1128
|
-
</Text>
|
|
1129
|
-
</HoverCardTrigger>
|
|
1130
|
-
<HoverCardContent>
|
|
1131
|
-
<Box gap="xs">
|
|
1132
|
-
<Text variant="label">Expo React Native</Text>
|
|
1133
|
-
<Text variant="bodySmall" color="textMuted">
|
|
1134
|
-
Expo React Native by Ma'sum.
|
|
1135
|
-
</Text>
|
|
1136
|
-
<Text variant="caption" color="textSubtle">
|
|
1137
|
-
Highlight 2026
|
|
1138
|
-
</Text>
|
|
1139
|
-
</Box>
|
|
1140
|
-
</HoverCardContent>
|
|
1141
|
-
</HoverCard>
|
|
1142
|
-
```
|
|
1143
|
-
|
|
1144
|
-
Useful props:
|
|
1145
|
-
|
|
1146
|
-
- `open`, `defaultOpen`, and `onOpenChange`
|
|
1147
|
-
- `openDelay` and `closeDelay`
|
|
1148
|
-
- `triggerMode="longPress" | "press" | "manual"`
|
|
1149
|
-
- `align="start" | "center" | "end"`
|
|
1150
|
-
- `width`, `maxHeight`, `sideOffset`, `modalProps`, and `overlayStyle`
|
|
1151
|
-
|
|
1152
|
-
### Input
|
|
1153
|
-
|
|
1154
|
-
Use `Input` for token-based text fields. It wraps React Native `TextInput`, supports light/dark mode, and keeps the default style flat with a clear border.
|
|
1155
|
-
|
|
1156
|
-
```tsx
|
|
1157
|
-
const [value, setValue] = React.useState("Expo React Native by Ma'sum");
|
|
1158
|
-
|
|
1159
|
-
<Input
|
|
1160
|
-
value={value}
|
|
1161
|
-
onChangeText={setValue}
|
|
1162
|
-
placeholder="Expo React Native by Ma'sum"
|
|
1163
|
-
/>
|
|
1164
|
-
|
|
1165
|
-
<Input
|
|
1166
|
-
type="email"
|
|
1167
|
-
placeholder="expo-react-native@masum.dev"
|
|
1168
|
-
/>
|
|
1169
|
-
|
|
1170
|
-
<Input
|
|
1171
|
-
invalid
|
|
1172
|
-
value="Expo React Native by Ma'sum, 2026"
|
|
1173
|
-
onChangeText={() => undefined}
|
|
1174
|
-
/>
|
|
1175
|
-
```
|
|
1176
|
-
|
|
1177
|
-
Useful props:
|
|
1178
|
-
|
|
1179
|
-
- `type="text" | "email" | "number" | "password" | "tel" | "url"`
|
|
1180
|
-
- `size="sm" | "md" | "lg"`
|
|
1181
|
-
- `invalid`, `disabled`, and `fullWidth`
|
|
1182
|
-
- All standard React Native `TextInputProps`
|
|
1183
|
-
|
|
1184
|
-
### InputGroup
|
|
1185
|
-
|
|
1186
|
-
Use `InputGroup` when an input needs inline addons, result counts, small actions, or a multiline grouped field.
|
|
1187
|
-
|
|
1188
|
-
```tsx
|
|
1189
|
-
<InputGroup>
|
|
1190
|
-
<InputGroupAddon>
|
|
1191
|
-
<Search color={colors.textMuted} size={16} />
|
|
1192
|
-
</InputGroupAddon>
|
|
1193
|
-
<InputGroupInput placeholder="Search..." />
|
|
1194
|
-
<InputGroupAddon align="inline-end">
|
|
1195
|
-
<InputGroupText>12 results</InputGroupText>
|
|
1196
|
-
</InputGroupAddon>
|
|
1197
|
-
</InputGroup>
|
|
1198
|
-
|
|
1199
|
-
<InputGroup>
|
|
1200
|
-
<InputGroupInput placeholder="Enter password" secureTextEntry />
|
|
1201
|
-
<InputGroupAddon align="inline-end">
|
|
1202
|
-
<EyeOff color={colors.textMuted} size={16} />
|
|
1203
|
-
</InputGroupAddon>
|
|
1204
|
-
</InputGroup>
|
|
1205
|
-
|
|
1206
|
-
<InputGroup orientation="block">
|
|
1207
|
-
<InputGroupAddon align="block-start">
|
|
1208
|
-
<InputGroupText>Expo React Native by Ma'sum</InputGroupText>
|
|
1209
|
-
</InputGroupAddon>
|
|
1210
|
-
<InputGroupTextarea placeholder="Highlight 2026" />
|
|
1211
|
-
<InputGroupAddon align="block-end">
|
|
1212
|
-
<InputGroupButton size="xs">Send</InputGroupButton>
|
|
1213
|
-
</InputGroupAddon>
|
|
1214
|
-
</InputGroup>
|
|
1215
|
-
|
|
1216
|
-
<InputGroup orientation="block">
|
|
1217
|
-
<InputGroupInput placeholder="Enter amount" />
|
|
1218
|
-
<InputGroupAddon align="block-end">
|
|
1219
|
-
<InputGroupText>USD</InputGroupText>
|
|
1220
|
-
</InputGroupAddon>
|
|
1221
|
-
</InputGroup>
|
|
1222
|
-
```
|
|
1223
|
-
|
|
1224
|
-
Parts:
|
|
1225
|
-
|
|
1226
|
-
- `InputGroup`
|
|
1227
|
-
- `InputGroupAddon`
|
|
1228
|
-
- `InputGroupButton`
|
|
1229
|
-
- `InputGroupText`
|
|
1230
|
-
- `InputGroupInput`
|
|
1231
|
-
- `InputGroupTextarea`
|
|
1232
|
-
|
|
1233
|
-
Useful props:
|
|
1234
|
-
|
|
1235
|
-
- `orientation="inline" | "block"`
|
|
1236
|
-
- `InputGroupAddon align="inline-start" | "inline-end" | "block-start" | "block-end"`
|
|
1237
|
-
- `InputGroupButton size="xs" | "sm" | "icon-xs" | "icon-sm"`
|
|
1238
|
-
- `invalid` and `disabled` can be set on the group or input control
|
|
1239
|
-
|
|
1240
|
-
### InputOTP
|
|
1241
|
-
|
|
1242
|
-
Use `InputOTP` for one-time passcode fields. The root owns the hidden native `TextInput`; slots render the visual code boxes.
|
|
1243
|
-
|
|
1244
|
-
```tsx
|
|
1245
|
-
const [otp, setOtp] = React.useState("");
|
|
1246
|
-
|
|
1247
|
-
<InputOTP value={otp} onChangeText={setOtp} maxLength={6}>
|
|
1248
|
-
<InputOTPGroup>
|
|
1249
|
-
<InputOTPSlot index={0} />
|
|
1250
|
-
<InputOTPSlot index={1} />
|
|
1251
|
-
<InputOTPSlot index={2} />
|
|
1252
|
-
<InputOTPSeparator />
|
|
1253
|
-
<InputOTPSlot index={3} />
|
|
1254
|
-
<InputOTPSlot index={4} />
|
|
1255
|
-
<InputOTPSlot index={5} />
|
|
1256
|
-
</InputOTPGroup>
|
|
1257
|
-
</InputOTP>
|
|
1258
|
-
|
|
1259
|
-
<InputOTP maxLength={4} invalid>
|
|
1260
|
-
<InputOTPGroup>
|
|
1261
|
-
<InputOTPSlot index={0} />
|
|
1262
|
-
<InputOTPSlot index={1} />
|
|
1263
|
-
<InputOTPSlot index={2} />
|
|
1264
|
-
<InputOTPSlot index={3} />
|
|
1265
|
-
</InputOTPGroup>
|
|
1266
|
-
</InputOTP>
|
|
1267
|
-
```
|
|
1268
|
-
|
|
1269
|
-
Parts:
|
|
1270
|
-
|
|
1271
|
-
- `InputOTP`
|
|
1272
|
-
- `InputOTPGroup`
|
|
1273
|
-
- `InputOTPSlot`
|
|
1274
|
-
- `InputOTPSeparator`
|
|
1275
|
-
|
|
1276
|
-
Useful props:
|
|
1277
|
-
|
|
1278
|
-
- `value`, `defaultValue`, and `onChangeText`
|
|
1279
|
-
- `maxLength`, `disabled`, `invalid`, and `autoFocus`
|
|
1280
|
-
- `textInputProps` for native `TextInput` overrides
|
|
1281
|
-
|
|
1282
|
-
### Item
|
|
1283
|
-
|
|
1284
|
-
Use `Item` primitives for reusable list rows and grouped content.
|
|
1285
|
-
|
|
1286
|
-
```tsx
|
|
1287
|
-
<ItemGroup>
|
|
1288
|
-
<Item variant="outline">
|
|
1289
|
-
<ItemMedia variant="icon">
|
|
1290
|
-
<Inbox color={colors.primary} size={20} />
|
|
1291
|
-
</ItemMedia>
|
|
1292
|
-
<ItemContent>
|
|
1293
|
-
<ItemTitle>Expo React Native</ItemTitle>
|
|
1294
|
-
<ItemDescription>
|
|
1295
|
-
Reusable item row by Ma'sum for consistent 2026 mobile lists.
|
|
1296
|
-
</ItemDescription>
|
|
1297
|
-
</ItemContent>
|
|
1298
|
-
<ItemActions>
|
|
1299
|
-
<Badge tone="success" variant="soft">
|
|
1300
|
-
Active
|
|
1301
|
-
</Badge>
|
|
1302
|
-
</ItemActions>
|
|
1303
|
-
</Item>
|
|
1304
|
-
|
|
1305
|
-
<ItemSeparator />
|
|
1306
|
-
|
|
1307
|
-
<Item variant="muted" size="sm">
|
|
1308
|
-
<ItemHeader>
|
|
1309
|
-
<ItemTitle>Theme tokens</ItemTitle>
|
|
1310
|
-
<Badge tone="info" variant="outline">
|
|
1311
|
-
2026
|
|
1312
|
-
</Badge>
|
|
1313
|
-
</ItemHeader>
|
|
1314
|
-
<ItemContent>
|
|
1315
|
-
<ItemDescription>
|
|
1316
|
-
Header and footer areas stay full width.
|
|
1317
|
-
</ItemDescription>
|
|
1318
|
-
</ItemContent>
|
|
1319
|
-
<ItemFooter>
|
|
1320
|
-
<Text variant="caption" color="textMuted">
|
|
1321
|
-
by Ma'sum
|
|
1322
|
-
</Text>
|
|
1323
|
-
<Button size="xs" variant="ghost" tone="secondary">
|
|
1324
|
-
View
|
|
1325
|
-
</Button>
|
|
1326
|
-
</ItemFooter>
|
|
1327
|
-
</Item>
|
|
1328
|
-
</ItemGroup>
|
|
1329
|
-
```
|
|
1330
|
-
|
|
1331
|
-
Parts:
|
|
1332
|
-
|
|
1333
|
-
- `ItemGroup`
|
|
1334
|
-
- `Item`
|
|
1335
|
-
- `ItemMedia`
|
|
1336
|
-
- `ItemContent`
|
|
1337
|
-
- `ItemTitle`
|
|
1338
|
-
- `ItemDescription`
|
|
1339
|
-
- `ItemActions`
|
|
1340
|
-
- `ItemHeader`
|
|
1341
|
-
- `ItemFooter`
|
|
1342
|
-
- `ItemSeparator`
|
|
1343
|
-
|
|
1344
|
-
Useful props:
|
|
1345
|
-
|
|
1346
|
-
- `Item variant="default" | "outline" | "muted"`
|
|
1347
|
-
- `Item size="default" | "sm" | "xs"`
|
|
1348
|
-
- `ItemMedia variant="default" | "icon" | "image"`
|
|
1349
|
-
|
|
1350
|
-
### Toast
|
|
1351
|
-
|
|
1352
|
-
Wrap the app once with `ToastProvider`, then use `useToast()` from any screen.
|
|
1353
|
-
|
|
1354
|
-
```tsx
|
|
1355
|
-
<ThemeProvider>
|
|
1356
|
-
<ToastProvider placement="top">
|
|
1357
|
-
<App />
|
|
1358
|
-
</ToastProvider>
|
|
1359
|
-
</ThemeProvider>
|
|
1360
|
-
```
|
|
1361
|
-
|
|
1362
|
-
```tsx
|
|
1363
|
-
const toast = useToast();
|
|
1364
|
-
|
|
1365
|
-
toast.show({
|
|
1366
|
-
title: "Expo React Native",
|
|
1367
|
-
description: "Toast by Ma'sum for 2026 mobile UI.",
|
|
1368
|
-
tone: "success",
|
|
1369
|
-
icon: ({ color, size }) => <Check color={color} size={size} />,
|
|
1370
|
-
action: {
|
|
1371
|
-
label: "Undo",
|
|
1372
|
-
onPress: handleUndo,
|
|
1373
|
-
},
|
|
1374
|
-
});
|
|
1375
|
-
```
|
|
1376
|
-
|
|
1377
|
-
Parts and APIs:
|
|
1378
|
-
|
|
1379
|
-
- `ToastProvider`
|
|
1380
|
-
- `useToast`
|
|
1381
|
-
- `ToastViewport`
|
|
1382
|
-
- `Toast`
|
|
1383
|
-
- `ToastContent`
|
|
1384
|
-
- `ToastTitle`
|
|
1385
|
-
- `ToastDescription`
|
|
1386
|
-
- `ToastAction`
|
|
1387
|
-
- `ToastClose`
|
|
1388
|
-
|
|
1389
|
-
Useful props:
|
|
1390
|
-
|
|
1391
|
-
- `ToastProvider placement="top" | "bottom"`
|
|
1392
|
-
- `offset` to control distance from the top/bottom edge; Android top placement accounts for `StatusBar.currentHeight`, and Android bottom placement leaves extra room for navigation controls by default
|
|
1393
|
-
- `duration`, `maxToasts`, `swipeToDismiss`, `viewportStyle`, and `renderToast`
|
|
1394
|
-
- `toast.show({ title, description, tone, icon, closeIcon, action, duration })`
|
|
1395
|
-
- `toast.dismiss(id?)` and `toast.update(id, options)`
|
|
1396
|
-
|
|
1397
|
-
### Form And Controls
|
|
1398
|
-
|
|
1399
|
-
Use these primitives for common form, preference, loading, and local navigation UI.
|
|
1400
|
-
|
|
1401
|
-
```tsx
|
|
1402
|
-
<FormField required>
|
|
1403
|
-
<FormLabel>Expo React Native</FormLabel>
|
|
1404
|
-
<FormControl>
|
|
1405
|
-
<Input placeholder="Expo React Native by Ma'sum" />
|
|
1406
|
-
</FormControl>
|
|
1407
|
-
<FormDescription>Consistent field copy.</FormDescription>
|
|
1408
|
-
<FormMessage>Shown when the field is invalid.</FormMessage>
|
|
1409
|
-
</FormField>
|
|
1410
|
-
|
|
1411
|
-
<Label required requiredIndicator=" (required)">
|
|
1412
|
-
Expo React Native
|
|
1413
|
-
</Label>
|
|
1414
|
-
|
|
1415
|
-
<Switch value={enabled} onValueChange={setEnabled} />
|
|
1416
|
-
<Switch value={enabled} onValueChange={setEnabled} size="lg" tone="success" />
|
|
1417
|
-
|
|
1418
|
-
<RadioGroup value={value} onValueChange={setValue}>
|
|
1419
|
-
<RadioGroupItem value="expo" label="Expo React Native" />
|
|
1420
|
-
</RadioGroup>
|
|
1421
|
-
|
|
1422
|
-
<Slider
|
|
1423
|
-
value={64}
|
|
1424
|
-
tone="accent"
|
|
1425
|
-
onValueChange={setValue}
|
|
1426
|
-
onSlidingComplete={saveValue}
|
|
1427
|
-
/>
|
|
1428
|
-
<Progress value={64} />
|
|
1429
|
-
<Skeleton style={{ height: 16, width: "72%" }} />
|
|
1430
|
-
|
|
1431
|
-
<Tabs value={tab} onValueChange={setTab}>
|
|
1432
|
-
<TabsList>
|
|
1433
|
-
<TabsTrigger value="preview">Preview</TabsTrigger>
|
|
1434
|
-
<TabsTrigger value="tokens">Tokens</TabsTrigger>
|
|
1435
|
-
</TabsList>
|
|
1436
|
-
<TabsContent value="preview">
|
|
1437
|
-
<Text>Expo React Native by Ma'sum</Text>
|
|
1438
|
-
</TabsContent>
|
|
1439
|
-
</Tabs>
|
|
1440
|
-
|
|
1441
|
-
<Stepper value={2} onValueChange={setValue} min={0} max={9} />
|
|
1442
|
-
```
|
|
1443
|
-
|
|
1444
|
-
Parts and APIs:
|
|
1445
|
-
|
|
1446
|
-
- `Label` with `required`, `requiredIndicator`, and `requiredIndicatorStyle`
|
|
1447
|
-
- `FormField`, `FormLabel`, `FormControl`, `FormDescription`, `FormMessage`, and `useFormField`
|
|
1448
|
-
- `Switch` with `size`, `tone`, `activeIcon`, `inactiveIcon`, `thumbContent`, `activeThumbContent`, `inactiveThumbContent`, `renderThumb`, `trackStyle`, and `thumbStyle`
|
|
1449
|
-
- `RadioGroup` and `RadioGroupItem`
|
|
1450
|
-
- `Slider` with `tone`, `step`, tap-to-seek, hold feedback, smooth drag gestures, `trackStyle`, `activeTrackStyle`, and `thumbStyle`
|
|
1451
|
-
- `Rating` with `shape` (`star`, `heart`, `thumb`, `fire`, `smile`, `shield`), `precision` (`full`, `half`, `exact`, float number), `size`, `tone`, `color`, `emptyColor`, `readOnly`, `allowClear`, `showValue`, `renderItem`, `icon`, tap & drag gestures, and dual-layer partial fill clipping
|
|
1452
|
-
- `Progress`
|
|
1453
|
-
- `Skeleton`
|
|
1454
|
-
- `Tabs`, `TabsList`, `TabsTrigger`, and `TabsContent`
|
|
1455
|
-
- `Stepper`
|
|
1456
|
-
|
|
1457
|
-
`Switch` is a custom Reanimated control, not the platform `Switch`. It keeps a
|
|
1458
|
-
simple controlled API while rendering a flat bordered track, animated thumb,
|
|
1459
|
-
theme-aware tone, and optional thumb icons.
|
|
1460
|
-
|
|
1461
|
-
Use `thumbContent` for a simple ReactNode inside the thumb, or
|
|
1462
|
-
`activeThumbContent` / `inactiveThumbContent` when the content should change
|
|
1463
|
-
between states. Each content prop also accepts a render function with
|
|
1464
|
-
`{ checked, disabled, invalid, color, size }`.
|
|
1465
|
-
|
|
1466
|
-
`Slider` uses React Native Gesture Handler and Reanimated for tap-to-seek,
|
|
1467
|
-
press/hold feedback, smooth horizontal dragging, and spring snapping on release.
|
|
1468
|
-
Apps must keep `GestureHandlerRootView` at the native root, which is also
|
|
1469
|
-
required by BottomSheet.
|
|
1470
|
-
|
|
1471
|
-
`Rating` provides high-precision tap & drag gesture interaction for review and feedback scores.
|
|
1472
|
-
It supports preset vector shapes (`star`, `heart`, `thumb`, `fire`, `smile`, `shield`), custom `RenderIcon` components, and custom `renderItem` functions.
|
|
1473
|
-
Partial fill calculations use dual-layer overflow container clipping for 100% accurate fractional display (such as 3.4 or 4.5).
|
|
1474
|
-
Precision options include `"full"` (1), `"half"` (0.5), `"exact"` (0.1), or custom float numbers.
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
### Sheet
|
|
1478
|
-
|
|
1479
|
-
Use `Sheet` as a higher-level wrapper around the package BottomSheet integration.
|
|
1480
|
-
|
|
1481
|
-
```tsx
|
|
1482
|
-
<Sheet ref={sheetRef} snapPoints={["35%", "70%"]}>
|
|
1483
|
-
<SheetContent>
|
|
1484
|
-
<SheetHeader>
|
|
1485
|
-
<SheetTitle>Expo React Native Sheet</SheetTitle>
|
|
1486
|
-
<SheetDescription>by Ma'sum, 2026.</SheetDescription>
|
|
1487
|
-
</SheetHeader>
|
|
1488
|
-
<SheetFooter>
|
|
1489
|
-
<Button onPress={() => sheetRef.current?.close()}>Close</Button>
|
|
1490
|
-
</SheetFooter>
|
|
1491
|
-
</SheetContent>
|
|
1492
|
-
</Sheet>
|
|
1493
|
-
```
|
|
1494
|
-
|
|
1495
|
-
`Sheet` defaults to `index={-1}`, `animateOnMount={false}`, and
|
|
1496
|
-
`enablePanDownToClose` so it stays closed on initial screen render and only
|
|
1497
|
-
opens when the app calls methods such as `snapToIndex(0)`.
|
|
1498
|
-
|
|
1499
|
-
Parts:
|
|
1500
|
-
|
|
1501
|
-
- `Sheet`
|
|
1502
|
-
- `SheetContent`
|
|
1503
|
-
- `SheetHeader`
|
|
1504
|
-
- `SheetTitle`
|
|
1505
|
-
- `SheetDescription`
|
|
1506
|
-
- `SheetFooter`
|
|
1507
|
-
|
|
1508
|
-
### Data And Overlays
|
|
1509
|
-
|
|
1510
|
-
Use these primitives for multiline fields, mobile selection, command sheets, anchored content, and compact data display.
|
|
1511
|
-
|
|
1512
|
-
```tsx
|
|
1513
|
-
<Textarea placeholder="Write Expo React Native notes by Ma'sum..." />
|
|
1514
|
-
|
|
1515
|
-
<Select
|
|
1516
|
-
value={value}
|
|
1517
|
-
onValueChange={setValue}
|
|
1518
|
-
options={[{ value: "expo", label: "Expo React Native" }]}
|
|
1519
|
-
/>
|
|
1520
|
-
|
|
1521
|
-
<Command
|
|
1522
|
-
visible={visible}
|
|
1523
|
-
onClose={close}
|
|
1524
|
-
items={[{ value: "theme", label: "Theme tokens" }]}
|
|
1525
|
-
onSelect={handleSelect}
|
|
1526
|
-
/>
|
|
1527
|
-
|
|
1528
|
-
<Popover>
|
|
1529
|
-
<PopoverTrigger>
|
|
1530
|
-
<Text>Open Popover</Text>
|
|
1531
|
-
</PopoverTrigger>
|
|
1532
|
-
<PopoverContent>
|
|
1533
|
-
<Text>Expo React Native by Ma'sum</Text>
|
|
1534
|
-
</PopoverContent>
|
|
1535
|
-
</Popover>
|
|
1536
|
-
|
|
1537
|
-
<Pagination page={1} pageCount={5} onPageChange={setPage} />
|
|
1538
|
-
|
|
1539
|
-
<Breadcrumb>
|
|
1540
|
-
<BreadcrumbLink>Expo</BreadcrumbLink>
|
|
1541
|
-
<BreadcrumbSeparator />
|
|
1542
|
-
<BreadcrumbPage>Ma'sum</BreadcrumbPage>
|
|
1543
|
-
</Breadcrumb>
|
|
1544
|
-
|
|
1545
|
-
<Table>
|
|
1546
|
-
<TableRow>
|
|
1547
|
-
<TableHead>Name</TableHead>
|
|
1548
|
-
<TableHead>Status</TableHead>
|
|
1549
|
-
</TableRow>
|
|
1550
|
-
<TableRow>
|
|
1551
|
-
<TableCell>Expo React Native</TableCell>
|
|
1552
|
-
<TableCell>Active</TableCell>
|
|
1553
|
-
</TableRow>
|
|
1554
|
-
</Table>
|
|
1555
|
-
|
|
1556
|
-
<DataList>
|
|
1557
|
-
<DataListItem>
|
|
1558
|
-
<DataListLabel>Framework</DataListLabel>
|
|
1559
|
-
<DataListValue>Expo React Native</DataListValue>
|
|
1560
|
-
</DataListItem>
|
|
1561
|
-
</DataList>
|
|
1562
|
-
|
|
1563
|
-
<Timeline>
|
|
1564
|
-
<TimelineItem active>
|
|
1565
|
-
<TimelineTitle>Theme tokens</TimelineTitle>
|
|
1566
|
-
<TimelineDescription>Flat border system by Ma'sum.</TimelineDescription>
|
|
1567
|
-
</TimelineItem>
|
|
1568
|
-
</Timeline>
|
|
1569
|
-
|
|
1570
|
-
<MetricCard label="Expo React Native" value="2026" />
|
|
1571
|
-
|
|
1572
|
-
<FloatingActionButton
|
|
1573
|
-
icon={({ color, size }) => <Plus color={color} size={size} />}
|
|
1574
|
-
label="Create"
|
|
1575
|
-
tone="primary"
|
|
1576
|
-
/>
|
|
1577
|
-
```
|
|
1578
|
-
|
|
1579
|
-
Parts and APIs:
|
|
1580
|
-
|
|
1581
|
-
- `Textarea`
|
|
1582
|
-
- `Select`
|
|
1583
|
-
- `Command`
|
|
1584
|
-
- `Popover`, `PopoverTrigger`, and `PopoverContent`
|
|
1585
|
-
- `Pagination`
|
|
1586
|
-
- `Breadcrumb`, `BreadcrumbItem`, `BreadcrumbLink`, `BreadcrumbPage`, and `BreadcrumbSeparator`
|
|
1587
|
-
- `Table`, `TableRow`, `TableHead`, and `TableCell`
|
|
1588
|
-
- `DataList`, `DataListItem`, `DataListLabel`, and `DataListValue`
|
|
1589
|
-
- `Timeline`, `TimelineItem`, `TimelineTitle`, and `TimelineDescription`
|
|
1590
|
-
- `MetricCard`
|
|
1591
|
-
- `FloatingActionButton`
|
|
1592
|
-
|
|
1593
|
-
`TableHead` defaults to a transparent flat header with a subtle divider, so it
|
|
1594
|
-
inherits the surrounding card/surface color instead of rendering a mismatched
|
|
1595
|
-
filled header background.
|
|
1596
|
-
|
|
1597
|
-
### KeyboardAvoiding
|
|
1598
|
-
|
|
1599
|
-
Use `KeyboardAvoiding` for form screens that need keyboard-safe layout. It wraps React Native `KeyboardAvoidingView` and can optionally include a themed `ScrollView`.
|
|
1600
|
-
|
|
1601
|
-
```tsx
|
|
1602
|
-
<KeyboardAvoiding scroll p="lg" gap="md">
|
|
1603
|
-
<Text variant="title">Expo React Native</Text>
|
|
1604
|
-
<Input placeholder="Expo React Native by Ma'sum" />
|
|
1605
|
-
<Button>Highlight 2026</Button>
|
|
1606
|
-
</KeyboardAvoiding>
|
|
1607
|
-
|
|
1608
|
-
<KeyboardAvoiding enabled={false}>
|
|
1609
|
-
<Input placeholder="Managed by the app keyboard layer" />
|
|
1610
|
-
</KeyboardAvoiding>
|
|
1611
|
-
```
|
|
1612
|
-
|
|
1613
|
-
For Android Expo apps, set the app window to resize so the keyboard can move content predictably:
|
|
1614
|
-
|
|
1615
|
-
```json
|
|
1616
|
-
{
|
|
1617
|
-
"expo": {
|
|
1618
|
-
"android": {
|
|
1619
|
-
"softwareKeyboardLayoutMode": "resize"
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
```
|
|
1624
|
-
|
|
1625
|
-
Useful props:
|
|
1626
|
-
|
|
1627
|
-
- `scroll` to render a keyboard-friendly `ScrollView`
|
|
1628
|
-
- `bg`, `p`, `px`, `py`, and `gap` for token-based layout
|
|
1629
|
-
- `fullHeight` to control whether the wrapper uses `flex: 1`
|
|
1630
|
-
- `enabled={false}` to disable keyboard avoiding when the app uses another keyboard library
|
|
1631
|
-
- `keyboardVerticalOffset`, `behavior`, and `enabled` from React Native `KeyboardAvoidingView`
|
|
1632
|
-
- `scrollViewProps` for `keyboardShouldPersistTaps`, `contentInsetAdjustmentBehavior`, and other `ScrollView` options
|
|
1633
|
-
|
|
1634
|
-
## Common Setup Issues
|
|
1635
|
-
|
|
1636
|
-
### Gesture components do not respond
|
|
1637
|
-
|
|
1638
|
-
Make sure `GestureHandlerRootView` wraps the native app root.
|
|
1639
|
-
|
|
1640
|
-
```tsx
|
|
1641
|
-
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
1642
|
-
|
|
1643
|
-
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
1644
|
-
<App />
|
|
1645
|
-
</GestureHandlerRootView>;
|
|
1646
|
-
```
|
|
1647
|
-
|
|
1648
|
-
This affects `Slider`, Gorhom `BottomSheet`, and any future gesture-driven component.
|
|
1649
|
-
|
|
1650
|
-
### Reanimated animations do not run
|
|
1651
|
-
|
|
1652
|
-
Verify the app has installed:
|
|
1653
|
-
|
|
1654
|
-
```sh
|
|
1655
|
-
bun add react-native-reanimated react-native-worklets
|
|
1656
|
-
```
|
|
1657
|
-
|
|
1658
|
-
For manual Babel config, keep the Reanimated plugin last:
|
|
1659
|
-
|
|
1660
|
-
```js
|
|
1661
|
-
plugins: ["react-native-reanimated/plugin"];
|
|
1662
|
-
```
|
|
1663
|
-
|
|
1664
|
-
Restart Metro after changing Babel/Reanimated setup.
|
|
1665
|
-
|
|
1666
|
-
### Toast does not show
|
|
1667
|
-
|
|
1668
|
-
`useToast()` needs `ToastProvider` above the component that calls it.
|
|
1669
|
-
|
|
1670
|
-
```tsx
|
|
1671
|
-
<ThemeProvider>
|
|
1672
|
-
<ToastProvider>
|
|
1673
|
-
<App />
|
|
1674
|
-
</ToastProvider>
|
|
1675
|
-
</ThemeProvider>
|
|
1676
|
-
```
|
|
1677
|
-
|
|
1678
|
-
### Dark mode resets after reload
|
|
1679
|
-
|
|
1680
|
-
Pass a `storage` adapter to `ThemeProvider` and control `colorScheme` in the app if you need app-owned persistence.
|
|
1681
|
-
|
|
1682
|
-
```tsx
|
|
1683
|
-
<ThemeProvider
|
|
1684
|
-
storage={storage}
|
|
1685
|
-
storageKey="rn-ui-color-scheme"
|
|
1686
|
-
defaultColorScheme="system"
|
|
1687
|
-
>
|
|
1688
|
-
<App />
|
|
1689
|
-
</ThemeProvider>
|
|
1690
|
-
```
|
|
1691
|
-
|
|
1692
|
-
If the app uses Expo Go, choose storage supported by Expo Go, such as `expo-secure-store`.
|
|
1693
|
-
|
|
1694
|
-
### Missing peer dependency errors
|
|
1695
|
-
|
|
1696
|
-
Because the package currently exports all components from one root entry, install the peer dependencies listed in the install section even if your first screen only uses a few components. Missing peers usually show up as Metro import errors for packages such as:
|
|
1697
|
-
|
|
1698
|
-
- `react-native-reanimated`
|
|
1699
|
-
- `react-native-worklets`
|
|
1700
|
-
- `react-native-gesture-handler`
|
|
1701
|
-
- `react-native-calendars`
|
|
1702
|
-
- `@gorhom/bottom-sheet`
|
|
1703
|
-
|
|
1704
|
-
### Expo Go vs development builds
|
|
1705
|
-
|
|
1706
|
-
`rn-ui` avoids app-specific native system UI modules. Components should work in Expo Go as long as their peer dependencies are available in the Expo runtime. If an app chooses extra native modules outside this package, such as custom system bar libraries, those modules may require a development build.
|
|
1707
|
-
|
|
1708
|
-
## Publishing Notes
|
|
1709
|
-
|
|
1710
|
-
The npm package publishes compiled files from `dist`, `README.md`, and `LICENSE`.
|
|
1711
|
-
|
|
1712
|
-
Before publishing:
|
|
1713
|
-
|
|
1714
|
-
```sh
|
|
1715
|
-
bun run --filter @masumdev/rn-ui build
|
|
1716
|
-
cd packages/rn-ui
|
|
1717
|
-
npm pack --dry-run
|
|
1718
|
-
npm publish --access public
|
|
1719
|
-
```
|
|
1720
|
-
|
|
1721
|
-
The published package exposes:
|
|
1722
|
-
|
|
1723
|
-
- CommonJS: `dist/index.js`
|
|
1724
|
-
- ESM: `dist/index.mjs`
|
|
1725
|
-
- Types: `dist/index.d.ts`
|
|
1726
|
-
|
|
1727
|
-
## Folder Structure
|
|
1728
|
-
|
|
1729
|
-
```txt
|
|
1730
|
-
src/
|
|
1731
|
-
components/
|
|
1732
|
-
Accordion.tsx
|
|
1733
|
-
Alert.tsx
|
|
1734
|
-
AlertDialog.tsx
|
|
1735
|
-
AspectRatio.tsx
|
|
1736
|
-
Attachment.tsx
|
|
1737
|
-
Avatar.tsx
|
|
1738
|
-
BottomSheet.tsx
|
|
1739
|
-
Bubble.tsx
|
|
1740
|
-
ButtonGroup.tsx
|
|
1741
|
-
Calendar.tsx
|
|
1742
|
-
Carousel.tsx
|
|
1743
|
-
Checkbox.tsx
|
|
1744
|
-
Collapsible.tsx
|
|
1745
|
-
Combobox.tsx
|
|
1746
|
-
ContextMenu.tsx
|
|
1747
|
-
Command.tsx
|
|
1748
|
-
DataList.tsx
|
|
1749
|
-
DropdownMenu.tsx
|
|
1750
|
-
Empty.tsx
|
|
1751
|
-
FloatingActionButton.tsx
|
|
1752
|
-
HoverCard.tsx
|
|
1753
|
-
Input.tsx
|
|
1754
|
-
InputGroup.tsx
|
|
1755
|
-
InputOTP.tsx
|
|
1756
|
-
Item.tsx
|
|
1757
|
-
KeyboardAvoiding.tsx
|
|
1758
|
-
Label.tsx
|
|
1759
|
-
MetricCard.tsx
|
|
1760
|
-
Pagination.tsx
|
|
1761
|
-
Popover.tsx
|
|
1762
|
-
FormField.tsx
|
|
1763
|
-
Progress.tsx
|
|
1764
|
-
RadioGroup.tsx
|
|
1765
|
-
Sheet.tsx
|
|
1766
|
-
Skeleton.tsx
|
|
1767
|
-
Slider.tsx
|
|
1768
|
-
Stepper.tsx
|
|
1769
|
-
Switch.tsx
|
|
1770
|
-
Tabs.tsx
|
|
1771
|
-
Table.tsx
|
|
1772
|
-
Textarea.tsx
|
|
1773
|
-
Timeline.tsx
|
|
1774
|
-
Toast.tsx
|
|
1775
|
-
Badge.tsx
|
|
1776
|
-
Box.tsx
|
|
1777
|
-
Button.tsx
|
|
1778
|
-
Card.tsx
|
|
1779
|
-
Divider.tsx
|
|
1780
|
-
IconButton.tsx
|
|
1781
|
-
Text.tsx
|
|
1782
|
-
index.ts
|
|
1783
|
-
theme/
|
|
1784
|
-
ThemeProvider.tsx
|
|
1785
|
-
createTheme.ts
|
|
1786
|
-
index.ts
|
|
1787
|
-
tokens.ts
|
|
1788
|
-
types.ts
|
|
1789
|
-
useTheme.ts
|
|
1790
|
-
useThemeStyles.ts
|
|
1791
|
-
utils/
|
|
1792
|
-
color.ts
|
|
1793
|
-
index.ts
|
|
1794
|
-
index.ts
|
|
1795
|
-
```
|
|
1796
|
-
|
|
1797
|
-
## Design Notes
|
|
1798
|
-
|
|
1799
|
-
- The default visual style is flat: no shadow/elevation by default, stronger borders, semantic surfaces, and consistent rounded corners.
|
|
1800
|
-
- Default primary color uses a modern indigo palette: `#4F46E5` in light mode and `#818CF8` in dark mode.
|
|
1801
|
-
- Use semantic colors such as `background`, `surface`, `text`, `primary`, `danger`, and `border` instead of hardcoded hex values.
|
|
1802
|
-
- Use `spacing`, `radii`, `fonts`, `typography`, and `components` tokens from `useTheme()` or `useThemeStyles()`.
|
|
1803
|
-
- Use `components.borderWidth` for default, strong, focus, ring, and hairline border sizing.
|
|
1804
|
-
- Use `components.switch` for switch width, height, thumb size, and icon size.
|
|
1805
|
-
- Use `components.slider` for slider height, track height, thumb sizing, and hit slop.
|
|
1806
|
-
- Keep app-specific storage, fonts, icons, and haptics outside the UI core.
|
|
1807
|
-
- Add new components through `components/` and export them from `components/index.ts`.
|
|
1808
|
-
- When changing package APIs, tokens, default visuals, persistence behavior, or app integration examples, update this README in the same change.
|
|
103
|
+
---
|
|
1809
104
|
|
|
1810
|
-
##
|
|
105
|
+
## 📄 License
|
|
1811
106
|
|
|
1812
|
-
|
|
107
|
+
MIT © [Ma'sum](https://github.com/masumrpg)
|