@campxdev/react-native-blueprint 0.1.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.
Files changed (178) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +358 -0
  3. package/lib/module/app/_layout.js +23 -0
  4. package/lib/module/app/_layout.js.map +1 -0
  5. package/lib/module/assets/icons/weather_icons/drizzle.png +0 -0
  6. package/lib/module/assets/icons/weather_icons/foggy.png +0 -0
  7. package/lib/module/assets/icons/weather_icons/freezing_rain.png +0 -0
  8. package/lib/module/assets/icons/weather_icons/partly_cloudy.png +0 -0
  9. package/lib/module/assets/icons/weather_icons/rainy.png +0 -0
  10. package/lib/module/assets/icons/weather_icons/showers.png +0 -0
  11. package/lib/module/assets/icons/weather_icons/sunny_weather.png +0 -0
  12. package/lib/module/assets/icons/weather_icons/thunderstorm.png +0 -0
  13. package/lib/module/assets/icons/weather_icons/thunderstorm_hail.png +0 -0
  14. package/lib/module/components/theme-config.js +265 -0
  15. package/lib/module/components/theme-config.js.map +1 -0
  16. package/lib/module/components/ui/Accordion.js +228 -0
  17. package/lib/module/components/ui/Accordion.js.map +1 -0
  18. package/lib/module/components/ui/Alert-Dialog.js +266 -0
  19. package/lib/module/components/ui/Alert-Dialog.js.map +1 -0
  20. package/lib/module/components/ui/Alert.js +107 -0
  21. package/lib/module/components/ui/Alert.js.map +1 -0
  22. package/lib/module/components/ui/AppBar.js +403 -0
  23. package/lib/module/components/ui/AppBar.js.map +1 -0
  24. package/lib/module/components/ui/Aspect-Ratio.js +27 -0
  25. package/lib/module/components/ui/Aspect-Ratio.js.map +1 -0
  26. package/lib/module/components/ui/Avatar.js +97 -0
  27. package/lib/module/components/ui/Avatar.js.map +1 -0
  28. package/lib/module/components/ui/Badge.js +127 -0
  29. package/lib/module/components/ui/Badge.js.map +1 -0
  30. package/lib/module/components/ui/Bottom-Sheet.js +144 -0
  31. package/lib/module/components/ui/Bottom-Sheet.js.map +1 -0
  32. package/lib/module/components/ui/Button.js +88 -0
  33. package/lib/module/components/ui/Button.js.map +1 -0
  34. package/lib/module/components/ui/Card.js +176 -0
  35. package/lib/module/components/ui/Card.js.map +1 -0
  36. package/lib/module/components/ui/Checkbox.js +65 -0
  37. package/lib/module/components/ui/Checkbox.js.map +1 -0
  38. package/lib/module/components/ui/Collapsible.js +42 -0
  39. package/lib/module/components/ui/Collapsible.js.map +1 -0
  40. package/lib/module/components/ui/Context-Menu.js +287 -0
  41. package/lib/module/components/ui/Context-Menu.js.map +1 -0
  42. package/lib/module/components/ui/Custom-Card.js +202 -0
  43. package/lib/module/components/ui/Custom-Card.js.map +1 -0
  44. package/lib/module/components/ui/Dialog.js +202 -0
  45. package/lib/module/components/ui/Dialog.js.map +1 -0
  46. package/lib/module/components/ui/Dropdown-Menu.js +421 -0
  47. package/lib/module/components/ui/Dropdown-Menu.js.map +1 -0
  48. package/lib/module/components/ui/Floating-Action.js +50 -0
  49. package/lib/module/components/ui/Floating-Action.js.map +1 -0
  50. package/lib/module/components/ui/Greeting-Card.js +392 -0
  51. package/lib/module/components/ui/Greeting-Card.js.map +1 -0
  52. package/lib/module/components/ui/Hover-Card.js +96 -0
  53. package/lib/module/components/ui/Hover-Card.js.map +1 -0
  54. package/lib/module/components/ui/Icon.js +73 -0
  55. package/lib/module/components/ui/Icon.js.map +1 -0
  56. package/lib/module/components/ui/Input.js +74 -0
  57. package/lib/module/components/ui/Input.js.map +1 -0
  58. package/lib/module/components/ui/Label.js +44 -0
  59. package/lib/module/components/ui/Label.js.map +1 -0
  60. package/lib/module/components/ui/Menubar.js +375 -0
  61. package/lib/module/components/ui/Menubar.js.map +1 -0
  62. package/lib/module/components/ui/Native-Only-Animated-View.js +41 -0
  63. package/lib/module/components/ui/Native-Only-Animated-View.js.map +1 -0
  64. package/lib/module/components/ui/NavBar.js +352 -0
  65. package/lib/module/components/ui/NavBar.js.map +1 -0
  66. package/lib/module/components/ui/Popover.js +101 -0
  67. package/lib/module/components/ui/Popover.js.map +1 -0
  68. package/lib/module/components/ui/Progress.js +124 -0
  69. package/lib/module/components/ui/Progress.js.map +1 -0
  70. package/lib/module/components/ui/Radio-Group.js +75 -0
  71. package/lib/module/components/ui/Radio-Group.js.map +1 -0
  72. package/lib/module/components/ui/Select.js +269 -0
  73. package/lib/module/components/ui/Select.js.map +1 -0
  74. package/lib/module/components/ui/Separator.js +58 -0
  75. package/lib/module/components/ui/Separator.js.map +1 -0
  76. package/lib/module/components/ui/SizedBox.js +101 -0
  77. package/lib/module/components/ui/SizedBox.js.map +1 -0
  78. package/lib/module/components/ui/Skeleton.js +57 -0
  79. package/lib/module/components/ui/Skeleton.js.map +1 -0
  80. package/lib/module/components/ui/Slider.js +169 -0
  81. package/lib/module/components/ui/Slider.js.map +1 -0
  82. package/lib/module/components/ui/Switch.js +55 -0
  83. package/lib/module/components/ui/Switch.js.map +1 -0
  84. package/lib/module/components/ui/Table.js +150 -0
  85. package/lib/module/components/ui/Table.js.map +1 -0
  86. package/lib/module/components/ui/Tabs.js +106 -0
  87. package/lib/module/components/ui/Tabs.js.map +1 -0
  88. package/lib/module/components/ui/Text.js +69 -0
  89. package/lib/module/components/ui/Text.js.map +1 -0
  90. package/lib/module/components/ui/Textarea.js +88 -0
  91. package/lib/module/components/ui/Textarea.js.map +1 -0
  92. package/lib/module/components/ui/Theme-Toggle.js +156 -0
  93. package/lib/module/components/ui/Theme-Toggle.js.map +1 -0
  94. package/lib/module/components/ui/Toast.js +101 -0
  95. package/lib/module/components/ui/Toast.js.map +1 -0
  96. package/lib/module/components/ui/Toggle-Group.js +129 -0
  97. package/lib/module/components/ui/Toggle-Group.js.map +1 -0
  98. package/lib/module/components/ui/Toggle.js +106 -0
  99. package/lib/module/components/ui/Toggle.js.map +1 -0
  100. package/lib/module/components/ui/Tooltip.js +106 -0
  101. package/lib/module/components/ui/Tooltip.js.map +1 -0
  102. package/lib/module/components/ui/index.js +45 -0
  103. package/lib/module/components/ui/index.js.map +1 -0
  104. package/lib/module/index.js +19 -0
  105. package/lib/module/index.js.map +1 -0
  106. package/lib/module/lib/ThemeProvider.js +173 -0
  107. package/lib/module/lib/ThemeProvider.js.map +1 -0
  108. package/lib/module/lib/cornerRadius.js +164 -0
  109. package/lib/module/lib/cornerRadius.js.map +1 -0
  110. package/lib/module/lib/fonts.js +25 -0
  111. package/lib/module/lib/fonts.js.map +1 -0
  112. package/lib/module/lib/theme.js +212 -0
  113. package/lib/module/lib/theme.js.map +1 -0
  114. package/lib/module/lib/utils.js +137 -0
  115. package/lib/module/lib/utils.js.map +1 -0
  116. package/lib/module/package.json +1 -0
  117. package/package.json +208 -0
  118. package/src/app/_layout.tsx +25 -0
  119. package/src/assets/icons/weather_icons/drizzle.png +0 -0
  120. package/src/assets/icons/weather_icons/foggy.png +0 -0
  121. package/src/assets/icons/weather_icons/freezing_rain.png +0 -0
  122. package/src/assets/icons/weather_icons/partly_cloudy.png +0 -0
  123. package/src/assets/icons/weather_icons/rainy.png +0 -0
  124. package/src/assets/icons/weather_icons/showers.png +0 -0
  125. package/src/assets/icons/weather_icons/sunny_weather.png +0 -0
  126. package/src/assets/icons/weather_icons/thunderstorm.png +0 -0
  127. package/src/assets/icons/weather_icons/thunderstorm_hail.png +0 -0
  128. package/src/components/theme-config.ts +331 -0
  129. package/src/components/ui/Accordion.tsx +253 -0
  130. package/src/components/ui/Alert-Dialog.tsx +295 -0
  131. package/src/components/ui/Alert.tsx +137 -0
  132. package/src/components/ui/AppBar.tsx +551 -0
  133. package/src/components/ui/Aspect-Ratio.tsx +25 -0
  134. package/src/components/ui/Avatar.tsx +103 -0
  135. package/src/components/ui/Badge.tsx +121 -0
  136. package/src/components/ui/Bottom-Sheet.tsx +224 -0
  137. package/src/components/ui/Button.tsx +100 -0
  138. package/src/components/ui/Card.tsx +185 -0
  139. package/src/components/ui/Checkbox.tsx +81 -0
  140. package/src/components/ui/Collapsible.tsx +40 -0
  141. package/src/components/ui/Context-Menu.tsx +407 -0
  142. package/src/components/ui/Custom-Card.tsx +226 -0
  143. package/src/components/ui/Dialog.tsx +240 -0
  144. package/src/components/ui/Dropdown-Menu.tsx +544 -0
  145. package/src/components/ui/Floating-Action.tsx +54 -0
  146. package/src/components/ui/Greeting-Card.tsx +471 -0
  147. package/src/components/ui/Hover-Card.tsx +101 -0
  148. package/src/components/ui/Icon.tsx +75 -0
  149. package/src/components/ui/Input.tsx +90 -0
  150. package/src/components/ui/Label.tsx +48 -0
  151. package/src/components/ui/Menubar.tsx +509 -0
  152. package/src/components/ui/Native-Only-Animated-View.tsx +37 -0
  153. package/src/components/ui/NavBar.tsx +397 -0
  154. package/src/components/ui/Popover.tsx +110 -0
  155. package/src/components/ui/Progress.tsx +138 -0
  156. package/src/components/ui/Radio-Group.tsx +79 -0
  157. package/src/components/ui/Select.tsx +344 -0
  158. package/src/components/ui/Separator.tsx +68 -0
  159. package/src/components/ui/SizedBox.tsx +116 -0
  160. package/src/components/ui/Skeleton.tsx +55 -0
  161. package/src/components/ui/Slider.tsx +222 -0
  162. package/src/components/ui/Switch.tsx +67 -0
  163. package/src/components/ui/Table.tsx +170 -0
  164. package/src/components/ui/Tabs.tsx +119 -0
  165. package/src/components/ui/Text.tsx +73 -0
  166. package/src/components/ui/Textarea.tsx +93 -0
  167. package/src/components/ui/Theme-Toggle.tsx +204 -0
  168. package/src/components/ui/Toast.tsx +127 -0
  169. package/src/components/ui/Toggle-Group.tsx +160 -0
  170. package/src/components/ui/Toggle.tsx +122 -0
  171. package/src/components/ui/Tooltip.tsx +117 -0
  172. package/src/components/ui/index.ts +42 -0
  173. package/src/index.tsx +24 -0
  174. package/src/lib/ThemeProvider.tsx +204 -0
  175. package/src/lib/cornerRadius.ts +160 -0
  176. package/src/lib/fonts.ts +28 -0
  177. package/src/lib/theme.ts +151 -0
  178. package/src/lib/utils.ts +146 -0
@@ -0,0 +1,79 @@
1
+ import { cn } from '../../lib/utils';
2
+ import * as RadioGroupPrimitive from '@rn-primitives/radio-group';
3
+ import { Platform } from 'react-native';
4
+
5
+ /**
6
+ * Radio group component for single-choice selection
7
+ *
8
+ * Groups radio buttons together to ensure only one can be selected at a time.
9
+ *
10
+ * @component
11
+ * @example
12
+ * ```tsx
13
+ * <RadioGroup value={selectedValue} onValueChange={setSelectedValue}>
14
+ * <View className="flex-row items-center gap-2">
15
+ * <RadioGroupItem value="option1" />
16
+ * <Label>Option 1</Label>
17
+ * </View>
18
+ * <View className="flex-row items-center gap-2">
19
+ * <RadioGroupItem value="option2" />
20
+ * <Label>Option 2</Label>
21
+ * </View>
22
+ * </RadioGroup>
23
+ * ```
24
+ *
25
+ * @accessibility
26
+ * - Uses proper ARIA role for radio group
27
+ * - Keyboard navigation support (arrow keys)
28
+ * - Screen reader friendly with proper labeling
29
+ */
30
+ function RadioGroup({
31
+ className,
32
+ ...props
33
+ }: RadioGroupPrimitive.RootProps &
34
+ React.RefAttributes<RadioGroupPrimitive.RootRef>) {
35
+ return (
36
+ <RadioGroupPrimitive.Root className={cn('gap-3', className)} {...props} />
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Individual radio button item
42
+ *
43
+ * Displays as a circular button with an inner indicator when selected.
44
+ *
45
+ * @component
46
+ * @example
47
+ * ```tsx
48
+ * <RadioGroupItem value="dark" />
49
+ * <RadioGroupItem value="light" disabled />
50
+ * ```
51
+ *
52
+ * @accessibility
53
+ * - Disabled state prevents interaction
54
+ * - Focus visible states on web
55
+ * - Invalid state styling for form validation
56
+ */
57
+ function RadioGroupItem({
58
+ className,
59
+ ...props
60
+ }: RadioGroupPrimitive.ItemProps &
61
+ React.RefAttributes<RadioGroupPrimitive.ItemRef>) {
62
+ return (
63
+ <RadioGroupPrimitive.Item
64
+ className={cn(
65
+ 'border-input dark:bg-input/30 aspect-square size-4 shrink-0 items-center justify-center rounded-full border shadow-sm shadow-black/5',
66
+ Platform.select({
67
+ web: 'focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive outline-none transition-all focus-visible:ring-[3px] disabled:cursor-not-allowed',
68
+ }),
69
+ props.disabled && 'opacity-50',
70
+ className
71
+ )}
72
+ {...props}
73
+ >
74
+ <RadioGroupPrimitive.Indicator className="bg-primary size-2 rounded-full" />
75
+ </RadioGroupPrimitive.Item>
76
+ );
77
+ }
78
+
79
+ export { RadioGroup, RadioGroupItem };
@@ -0,0 +1,344 @@
1
+ import { Icon } from './Icon';
2
+ import { NativeOnlyAnimatedView } from './Native-Only-Animated-View';
3
+ import { TextClassContext } from './Text';
4
+ import { cn } from '../../lib/utils';
5
+ import * as SelectPrimitive from '@rn-primitives/select';
6
+ import {
7
+ Check,
8
+ ChevronDown,
9
+ ChevronDownIcon,
10
+ ChevronUpIcon,
11
+ } from 'lucide-react-native';
12
+ import * as React from 'react';
13
+ import { Platform, ScrollView, StyleSheet, View } from 'react-native';
14
+ import { cssInterop } from 'nativewind';
15
+ import { FadeIn, FadeOut } from 'react-native-reanimated';
16
+ import { FullWindowOverlay as RNFullWindowOverlay } from 'react-native-screens';
17
+
18
+ cssInterop(View, { className: 'style' });
19
+ cssInterop(ScrollView, { className: 'style' });
20
+
21
+ /**
22
+ * Option type for select items
23
+ */
24
+ type Option = SelectPrimitive.Option;
25
+
26
+ /**
27
+ * Root select component for dropdown selection
28
+ *
29
+ * @component
30
+ * @example
31
+ * ```tsx
32
+ * <Select value={value} onValueChange={setValue}>
33
+ * <SelectTrigger>
34
+ * <SelectValue placeholder="Select an option" />
35
+ * </SelectTrigger>
36
+ * <SelectContent>
37
+ * <SelectItem value="option1">
38
+ * <Text>Option 1</Text>
39
+ * </SelectItem>
40
+ * </SelectContent>
41
+ * </Select>
42
+ * ```
43
+ */
44
+ const Select = SelectPrimitive.Root;
45
+
46
+ /**
47
+ * Groups related select items together
48
+ * @component
49
+ */
50
+ const SelectGroup = SelectPrimitive.Group;
51
+
52
+ /**
53
+ * Displays the selected value or placeholder text
54
+ * @component
55
+ */
56
+ function SelectValue({
57
+ ref,
58
+ className,
59
+ ...props
60
+ }: SelectPrimitive.ValueProps &
61
+ React.RefAttributes<SelectPrimitive.ValueRef> & {
62
+ className?: string;
63
+ }) {
64
+ const { value } = SelectPrimitive.useRootContext();
65
+ return (
66
+ <SelectPrimitive.Value
67
+ ref={ref}
68
+ className={cn(
69
+ 'text-foreground line-clamp-1 flex flex-row items-center gap-2 text-sm',
70
+ !value && 'text-muted-foreground',
71
+ className
72
+ )}
73
+ {...props}
74
+ />
75
+ );
76
+ }
77
+
78
+ /**
79
+ * Trigger button that opens the select dropdown
80
+ * @component
81
+ */
82
+ function SelectTrigger({
83
+ ref,
84
+ className,
85
+ children,
86
+ size = 'default',
87
+ ...props
88
+ }: SelectPrimitive.TriggerProps &
89
+ React.RefAttributes<SelectPrimitive.TriggerRef> & {
90
+ children?: React.ReactNode;
91
+ size?: 'default' | 'sm';
92
+ }) {
93
+ return (
94
+ <SelectPrimitive.Trigger
95
+ ref={ref}
96
+ className={cn(
97
+ 'border-input dark:bg-input/30 dark:active:bg-input/50 bg-background flex h-10 flex-row items-center justify-between gap-2 rounded-md border px-3 py-2 shadow-sm shadow-black/5 sm:h-9',
98
+ Platform.select({
99
+ web: 'focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:hover:bg-input/50 w-fit whitespace-nowrap text-sm outline-none transition-[color,box-shadow] focus-visible:ring-[3px] disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0',
100
+ }),
101
+ props.disabled && 'opacity-50',
102
+ size === 'sm' && 'h-8 py-2 sm:py-1.5',
103
+ className
104
+ )}
105
+ {...props}
106
+ >
107
+ <>{children}</>
108
+ <Icon
109
+ as={ChevronDown}
110
+ aria-hidden={true}
111
+ className="text-muted-foreground size-4"
112
+ />
113
+ </SelectPrimitive.Trigger>
114
+ );
115
+ }
116
+
117
+ /**
118
+ * Full window overlay wrapper for iOS
119
+ */
120
+ const FullWindowOverlay =
121
+ Platform.OS === 'ios' ? RNFullWindowOverlay : React.Fragment;
122
+
123
+ /**
124
+ * Content container for select items
125
+ * @component
126
+ */
127
+ function SelectContent({
128
+ className,
129
+ children,
130
+ position = 'popper',
131
+ portalHost,
132
+ ...props
133
+ }: SelectPrimitive.ContentProps &
134
+ React.RefAttributes<SelectPrimitive.ContentRef> & {
135
+ className?: string;
136
+ portalHost?: string;
137
+ }) {
138
+ return (
139
+ <SelectPrimitive.Portal hostName={portalHost}>
140
+ <FullWindowOverlay>
141
+ <SelectPrimitive.Overlay
142
+ style={Platform.select({ native: StyleSheet.absoluteFill })}
143
+ >
144
+ <TextClassContext.Provider value="text-popover-foreground">
145
+ <NativeOnlyAnimatedView
146
+ className="z-50"
147
+ entering={FadeIn}
148
+ exiting={FadeOut}
149
+ >
150
+ <SelectPrimitive.Content
151
+ className={cn(
152
+ 'bg-popover border-border relative z-50 min-w-[8rem] rounded-md border shadow-md shadow-black/5',
153
+ Platform.select({
154
+ web: cn(
155
+ 'animate-in fade-in-0 zoom-in-95 origin-(--radix-select-content-transform-origin) max-h-52 overflow-y-auto overflow-x-hidden',
156
+ props.side === 'bottom' && 'slide-in-from-top-2',
157
+ props.side === 'top' && 'slide-in-from-bottom-2'
158
+ ),
159
+ native: 'p-1',
160
+ }),
161
+ position === 'popper' &&
162
+ Platform.select({
163
+ web: cn(
164
+ props.side === 'bottom' && 'translate-y-1',
165
+ props.side === 'top' && '-translate-y-1'
166
+ ),
167
+ }),
168
+ className
169
+ )}
170
+ position={position}
171
+ {...props}
172
+ >
173
+ <SelectScrollUpButton />
174
+ <SelectPrimitive.Viewport
175
+ className={cn(
176
+ 'p-1',
177
+ position === 'popper' &&
178
+ cn(
179
+ 'w-full',
180
+ Platform.select({
181
+ web: 'h-[var(--radix-select-trigger-height)] min-w-[var(--radix-select-trigger-width)]',
182
+ })
183
+ )
184
+ )}
185
+ >
186
+ {children}
187
+ </SelectPrimitive.Viewport>
188
+ <SelectScrollDownButton />
189
+ </SelectPrimitive.Content>
190
+ </NativeOnlyAnimatedView>
191
+ </TextClassContext.Provider>
192
+ </SelectPrimitive.Overlay>
193
+ </FullWindowOverlay>
194
+ </SelectPrimitive.Portal>
195
+ );
196
+ }
197
+
198
+ /**
199
+ * Label for grouping select items
200
+ * @component
201
+ */
202
+ function SelectLabel({
203
+ className,
204
+ ...props
205
+ }: SelectPrimitive.LabelProps & React.RefAttributes<SelectPrimitive.LabelRef>) {
206
+ return (
207
+ <SelectPrimitive.Label
208
+ className={cn(
209
+ 'text-muted-foreground px-2 py-2 text-xs sm:py-1.5',
210
+ className
211
+ )}
212
+ {...props}
213
+ />
214
+ );
215
+ }
216
+
217
+ /**
218
+ * Individual selectable item
219
+ * @component
220
+ */
221
+ function SelectItem({
222
+ className,
223
+ children: _children,
224
+ ...props
225
+ }: SelectPrimitive.ItemProps & React.RefAttributes<SelectPrimitive.ItemRef>) {
226
+ return (
227
+ <SelectPrimitive.Item
228
+ className={cn(
229
+ 'active:bg-accent group relative flex w-full flex-row items-center gap-2 rounded-sm py-2 pl-2 pr-8 sm:py-1.5',
230
+ Platform.select({
231
+ web: 'focus:bg-accent focus:text-accent-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 cursor-default outline-none data-[disabled]:pointer-events-none [&_svg]:pointer-events-none',
232
+ }),
233
+ props.disabled && 'opacity-50',
234
+ className
235
+ )}
236
+ {...props}
237
+ >
238
+ <View className="absolute right-2 flex size-3.5 items-center justify-center">
239
+ <SelectPrimitive.ItemIndicator>
240
+ <Icon as={Check} className="text-muted-foreground size-4 shrink-0" />
241
+ </SelectPrimitive.ItemIndicator>
242
+ </View>
243
+ <SelectPrimitive.ItemText className="text-foreground group-active:text-accent-foreground select-none text-sm" />
244
+ </SelectPrimitive.Item>
245
+ );
246
+ }
247
+
248
+ /**
249
+ * Visual separator between select items
250
+ * @component
251
+ */
252
+ function SelectSeparator({
253
+ className,
254
+ ...props
255
+ }: SelectPrimitive.SeparatorProps &
256
+ React.RefAttributes<SelectPrimitive.SeparatorRef>) {
257
+ return (
258
+ <SelectPrimitive.Separator
259
+ className={cn(
260
+ 'bg-border -mx-1 my-1 h-px',
261
+ Platform.select({ web: 'pointer-events-none' }),
262
+ className
263
+ )}
264
+ {...props}
265
+ />
266
+ );
267
+ }
268
+
269
+ /**
270
+ * @platform Web only
271
+ * Returns null on native platforms
272
+ */
273
+ function SelectScrollUpButton({
274
+ className,
275
+ ...props
276
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
277
+ if (Platform.OS !== 'web') {
278
+ return null;
279
+ }
280
+ return (
281
+ <SelectPrimitive.ScrollUpButton
282
+ className={cn(
283
+ 'flex cursor-default items-center justify-center py-1',
284
+ className
285
+ )}
286
+ {...props}
287
+ >
288
+ <Icon as={ChevronUpIcon} className="size-4" />
289
+ </SelectPrimitive.ScrollUpButton>
290
+ );
291
+ }
292
+
293
+ /**
294
+ * @platform Web only
295
+ * Returns null on native platforms
296
+ */
297
+ function SelectScrollDownButton({
298
+ className,
299
+ ...props
300
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
301
+ if (Platform.OS !== 'web') {
302
+ return null;
303
+ }
304
+ return (
305
+ <SelectPrimitive.ScrollDownButton
306
+ className={cn(
307
+ 'flex cursor-default items-center justify-center py-1',
308
+ className
309
+ )}
310
+ {...props}
311
+ >
312
+ <Icon as={ChevronDownIcon} className="size-4" />
313
+ </SelectPrimitive.ScrollDownButton>
314
+ );
315
+ }
316
+
317
+ /**
318
+ * @platform Native only
319
+ * Returns the children on the web
320
+ */
321
+ function NativeSelectScrollView({
322
+ className,
323
+ ...props
324
+ }: React.ComponentProps<typeof ScrollView>) {
325
+ if (Platform.OS === 'web') {
326
+ return <>{props.children}</>;
327
+ }
328
+ return <ScrollView className={cn('max-h-52', className)} {...props} />;
329
+ }
330
+
331
+ export {
332
+ NativeSelectScrollView,
333
+ Select,
334
+ SelectContent,
335
+ SelectGroup,
336
+ SelectItem,
337
+ SelectLabel,
338
+ SelectScrollDownButton,
339
+ SelectScrollUpButton,
340
+ SelectSeparator,
341
+ SelectTrigger,
342
+ SelectValue,
343
+ type Option,
344
+ };
@@ -0,0 +1,68 @@
1
+ import * as React from 'react';
2
+ import { View } from 'react-native';
3
+ import { cssInterop } from 'nativewind';
4
+ import { cn } from '../../lib/utils';
5
+ import type { ViewRef, SlottableViewProps } from '@rn-primitives/types';
6
+
7
+ cssInterop(View, { className: 'style' });
8
+
9
+ /**
10
+ * Visual divider for separating content sections
11
+ *
12
+ * A thin line that creates visual separation between content areas. Can be oriented
13
+ * horizontally or vertically and supports decorative or semantic roles.
14
+ *
15
+ * @component
16
+ * @example
17
+ * ```tsx
18
+ * // Horizontal separator (default)
19
+ * <View>
20
+ * <Text>Section 1</Text>
21
+ * <Separator className="my-4" />
22
+ * <Text>Section 2</Text>
23
+ * </View>
24
+ *
25
+ * // Vertical separator
26
+ * <View className="flex-row items-center gap-4">
27
+ * <Text>Left</Text>
28
+ * <Separator orientation="vertical" className="h-4" />
29
+ * <Text>Right</Text>
30
+ * </View>
31
+ *
32
+ * // Semantic separator for screen readers
33
+ * <Separator decorative={false} />
34
+ * ```
35
+ *
36
+ * @accessibility
37
+ * - Decorative separators (default) have role="none" for screen readers
38
+ * - Non-decorative separators have role="separator" for semantic meaning
39
+ */
40
+ const Separator = React.forwardRef<
41
+ ViewRef,
42
+ SlottableViewProps & {
43
+ className?: string;
44
+ orientation?: 'horizontal' | 'vertical';
45
+ decorative?: boolean;
46
+ }
47
+ >(
48
+ (
49
+ { className, orientation = 'horizontal', decorative = true, ...props },
50
+ ref
51
+ ) => {
52
+ return (
53
+ <View
54
+ role={decorative ? 'none' : 'separator'}
55
+ className={cn(
56
+ 'shrink-0 bg-border dark:bg-border',
57
+ orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
58
+ className
59
+ )}
60
+ ref={ref}
61
+ {...props}
62
+ />
63
+ );
64
+ }
65
+ );
66
+ Separator.displayName = 'Separator';
67
+
68
+ export { Separator };
@@ -0,0 +1,116 @@
1
+ import * as React from 'react';
2
+ import { View, type ViewStyle } from 'react-native';
3
+
4
+ interface SizedBoxProps {
5
+ /**
6
+ * The width of the box
7
+ */
8
+ width?: number;
9
+
10
+ /**
11
+ * The height of the box
12
+ */
13
+ height?: number;
14
+
15
+ /**
16
+ * Optional child element to render inside the box
17
+ */
18
+ children?: React.ReactNode;
19
+
20
+ /**
21
+ * Additional styles to apply
22
+ */
23
+ style?: ViewStyle;
24
+ }
25
+
26
+ /**
27
+ * A box with a specified size.
28
+ *
29
+ * If a child is provided, this widget forces its child to have a specific width and/or height.
30
+ * If either the width or height is not specified, this widget will size itself to match the child's size in that dimension.
31
+ *
32
+ * If no child is provided, this widget will size itself to the specified width and height,
33
+ * treating null values as zero.
34
+ *
35
+ * This is similar to Flutter's SizedBox widget and is useful for:
36
+ * - Adding fixed spacing between widgets
37
+ * - Constraining child widget dimensions
38
+ * - Creating empty space
39
+ *
40
+ * @example
41
+ * // Create spacing
42
+ * <SizedBox height={16} />
43
+ *
44
+ * @example
45
+ * // Constrain child size
46
+ * <SizedBox width={100} height={100}>
47
+ * <Image source={...} />
48
+ * </SizedBox>
49
+ *
50
+ * @example
51
+ * // Create horizontal spacing
52
+ * <View style={{ flexDirection: 'row' }}>
53
+ * <Button />
54
+ * <SizedBox width={12} />
55
+ * <Button />
56
+ * </View>
57
+ */
58
+ export function SizedBox({ width, height, children, style }: SizedBoxProps) {
59
+ const boxStyle: ViewStyle = {
60
+ ...(width !== undefined && { width }),
61
+ ...(height !== undefined && { height }),
62
+ ...style,
63
+ };
64
+
65
+ return <View style={boxStyle}>{children}</View>;
66
+ }
67
+
68
+ // Convenience components for common spacing values
69
+ export namespace SizedBox {
70
+ /**
71
+ * Creates a square SizedBox with equal width and height
72
+ */
73
+ export function Square({
74
+ size,
75
+ children,
76
+ style,
77
+ }: {
78
+ size: number;
79
+ children?: React.ReactNode;
80
+ style?: ViewStyle;
81
+ }) {
82
+ return (
83
+ <SizedBox width={size} height={size} style={style}>
84
+ {children}
85
+ </SizedBox>
86
+ );
87
+ }
88
+
89
+ /**
90
+ * Creates a SizedBox that expands to fill available space
91
+ */
92
+ export function Expand({
93
+ children,
94
+ style,
95
+ }: {
96
+ children?: React.ReactNode;
97
+ style?: ViewStyle;
98
+ }) {
99
+ const expandStyle: ViewStyle = { flex: 1 };
100
+ return <View style={[expandStyle, style]}>{children}</View>;
101
+ }
102
+
103
+ /**
104
+ * Creates a SizedBox that shrinks to fit its content
105
+ */
106
+ export function Shrink({
107
+ children,
108
+ style,
109
+ }: {
110
+ children?: React.ReactNode;
111
+ style?: ViewStyle;
112
+ }) {
113
+ const shrinkStyle: ViewStyle = { flexShrink: 1 };
114
+ return <View style={[shrinkStyle, style]}>{children}</View>;
115
+ }
116
+ }
@@ -0,0 +1,55 @@
1
+ import { cn } from '../../lib/utils';
2
+ import { View } from 'react-native';
3
+ import { cssInterop } from 'nativewind';
4
+
5
+ cssInterop(View, { className: 'style' });
6
+
7
+ /**
8
+ * Skeleton loading placeholder component
9
+ *
10
+ * Displays an animated placeholder during content loading states. Provides visual feedback
11
+ * to users while data is being fetched, improving perceived performance.
12
+ *
13
+ * @component
14
+ * @example
15
+ * ```tsx
16
+ * // Text skeleton
17
+ * <Skeleton className="h-4 w-[250px]" />
18
+ *
19
+ * // Avatar skeleton
20
+ * <Skeleton className="h-12 w-12 rounded-full" />
21
+ *
22
+ * // Card skeleton
23
+ * <View className="flex gap-3">
24
+ * <Skeleton className="h-[125px] w-full rounded-xl" />
25
+ * <View className="gap-2">
26
+ * <Skeleton className="h-4 w-full" />
27
+ * <Skeleton className="h-4 w-[80%]" />
28
+ * </View>
29
+ * </View>
30
+ *
31
+ * // List skeleton
32
+ * {[1, 2, 3].map((i) => (
33
+ * <View key={i} className="flex-row items-center gap-4">
34
+ * <Skeleton className="h-12 w-12 rounded-full" />
35
+ * <View className="flex-1 gap-2">
36
+ * <Skeleton className="h-4 w-full" />
37
+ * <Skeleton className="h-4 w-3/4" />
38
+ * </View>
39
+ * </View>
40
+ * ))}
41
+ * ```
42
+ */
43
+ function Skeleton({
44
+ className,
45
+ ...props
46
+ }: React.ComponentProps<typeof View> & React.RefAttributes<View>) {
47
+ return (
48
+ <View
49
+ className={cn('bg-accent animate-pulse rounded-md', className)}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ export { Skeleton };