@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,222 @@
1
+ import * as React from 'react';
2
+ import { Platform, View, ViewProps } from 'react-native';
3
+ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
4
+ import Animated, {
5
+ runOnJS,
6
+ useAnimatedStyle,
7
+ useSharedValue,
8
+ withSpring,
9
+ } from 'react-native-reanimated';
10
+ import { cn } from '../../lib/utils';
11
+
12
+ /**
13
+ * Props for the Slider component
14
+ *
15
+ * @property {number} value - Current slider value
16
+ * @property {function} onValueChange - Callback when value changes
17
+ * @property {number} min - Minimum value (default: 0)
18
+ * @property {number} max - Maximum value (default: 100)
19
+ * @property {number} step - Step increment (default: 1)
20
+ * @property {boolean} disabled - Whether the slider is disabled
21
+ */
22
+ export interface SliderProps extends Omit<ViewProps, 'children'> {
23
+ value: number;
24
+ onValueChange?: (value: number[]) => void;
25
+ min?: number;
26
+ max?: number;
27
+ step?: number;
28
+ disabled?: boolean;
29
+ className?: string;
30
+ }
31
+
32
+ /**
33
+ * Ref interface for Slider component
34
+ *
35
+ * @property {function} reset - Resets slider to minimum value
36
+ */
37
+ export interface SliderRef {
38
+ reset: () => void;
39
+ }
40
+
41
+ /**
42
+ * Interactive slider component for selecting numeric values
43
+ *
44
+ * Provides smooth gesture-based interaction with spring animations.
45
+ * Supports min/max ranges, step increments, and tap-to-select.
46
+ *
47
+ * @component
48
+ * @example
49
+ * ```tsx
50
+ * <Slider
51
+ * value={volume}
52
+ * onValueChange={(values) => setVolume(values[0])}
53
+ * min={0}
54
+ * max={100}
55
+ * step={5}
56
+ * />
57
+ * ```
58
+ *
59
+ * @accessibility
60
+ * - Disabled state prevents interaction
61
+ * - Focus visible states on web
62
+ * - Value changes are announced by screen readers
63
+ */
64
+ const Slider = React.forwardRef<SliderRef, SliderProps>(
65
+ (
66
+ {
67
+ value,
68
+ onValueChange,
69
+ min = 0,
70
+ max = 100,
71
+ step = 1,
72
+ disabled = false,
73
+ className,
74
+ style,
75
+ ...props
76
+ },
77
+ ref
78
+ ) => {
79
+ const sliderWidth = useSharedValue(0);
80
+ const translationX = useSharedValue(0);
81
+ const startPosition = useSharedValue(0);
82
+ const [sliderDimensions, setSliderDimensions] = React.useState({
83
+ width: 0,
84
+ x: 0,
85
+ });
86
+
87
+ // Calculate thumb position from value
88
+ const getThumbPosition = React.useCallback(
89
+ (val: number) => {
90
+ const range = max - min;
91
+ const percentage = (val - min) / range;
92
+ return percentage * sliderDimensions.width;
93
+ },
94
+ [max, min, sliderDimensions.width]
95
+ );
96
+
97
+ // Calculate value from position
98
+ const getValueFromPosition = React.useCallback(
99
+ (position: number) => {
100
+ if (sliderWidth.value === 0) return min;
101
+ const percentage = Math.max(
102
+ 0,
103
+ Math.min(1, position / sliderWidth.value)
104
+ );
105
+ const range = max - min;
106
+ let newValue = min + percentage * range;
107
+
108
+ // Apply step
109
+ if (step > 0) {
110
+ newValue = Math.round(newValue / step) * step;
111
+ }
112
+
113
+ return Math.max(min, Math.min(max, newValue));
114
+ },
115
+ [max, min, step, sliderWidth]
116
+ );
117
+
118
+ // Initialize thumb position based on value
119
+ React.useEffect(() => {
120
+ if (sliderDimensions.width > 0) {
121
+ const newPosition = getThumbPosition(value);
122
+ translationX.value = newPosition;
123
+ }
124
+ }, [value, sliderDimensions.width, getThumbPosition, translationX]);
125
+
126
+ // Expose reset method via ref
127
+ React.useImperativeHandle(ref, () => ({
128
+ reset: () => {
129
+ translationX.value = withSpring(getThumbPosition(min));
130
+ onValueChange?.([min]);
131
+ },
132
+ }));
133
+
134
+ const handleValueChange = React.useCallback(
135
+ (newValue: number) => {
136
+ onValueChange?.([newValue]);
137
+ },
138
+ [onValueChange]
139
+ );
140
+
141
+ const pan = Gesture.Pan()
142
+ .enabled(!disabled)
143
+ .onBegin(() => {
144
+ startPosition.value = translationX.value;
145
+ })
146
+ .onUpdate((event: { translationX: number }) => {
147
+ if (sliderWidth.value === 0) return;
148
+ const newPosition = Math.max(
149
+ 0,
150
+ Math.min(sliderWidth.value, event.translationX + startPosition.value)
151
+ );
152
+ translationX.value = newPosition;
153
+ })
154
+ .onEnd(() => {
155
+ if (sliderWidth.value === 0) return;
156
+ const newValue = getValueFromPosition(translationX.value);
157
+ translationX.value = withSpring(getThumbPosition(newValue));
158
+ runOnJS(handleValueChange)(newValue);
159
+ });
160
+
161
+ const tap = Gesture.Tap()
162
+ .enabled(!disabled)
163
+ .onEnd((event: { x: number }) => {
164
+ if (sliderWidth.value === 0) return;
165
+ const newPosition = Math.max(0, Math.min(sliderWidth.value, event.x));
166
+ const newValue = getValueFromPosition(newPosition);
167
+ translationX.value = withSpring(getThumbPosition(newValue));
168
+ runOnJS(handleValueChange)(newValue);
169
+ });
170
+
171
+ const composed = Gesture.Race(tap, pan);
172
+
173
+ const thumbAnimatedStyle = useAnimatedStyle(() => ({
174
+ transform: [{ translateX: translationX.value }],
175
+ }));
176
+
177
+ const rangeAnimatedStyle = useAnimatedStyle(() => ({
178
+ width: translationX.value,
179
+ }));
180
+
181
+ return (
182
+ <View
183
+ className={cn(
184
+ 'relative flex h-10 w-full flex-row items-center justify-center',
185
+ disabled && 'opacity-50',
186
+ className
187
+ )}
188
+ style={style}
189
+ {...props}
190
+ >
191
+ <GestureDetector gesture={composed}>
192
+ <View
193
+ className="relative h-2 w-full overflow-hidden rounded-full bg-primary/20"
194
+ onLayout={(event) => {
195
+ const { width, x } = event.nativeEvent.layout;
196
+ sliderWidth.value = width;
197
+ setSliderDimensions({ width, x });
198
+ }}
199
+ >
200
+ <Animated.View
201
+ className="absolute left-0 h-full rounded-full bg-primary"
202
+ style={rangeAnimatedStyle}
203
+ />
204
+ <Animated.View
205
+ className={cn(
206
+ 'absolute -top-1.5 size-5 -ml-2.5 rounded-full border-2 border-primary bg-background shadow-sm shadow-black/5',
207
+ Platform.select({
208
+ web: 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-none focus-visible:ring-[3px] disabled:pointer-events-none',
209
+ })
210
+ )}
211
+ style={thumbAnimatedStyle}
212
+ />
213
+ </View>
214
+ </GestureDetector>
215
+ </View>
216
+ );
217
+ }
218
+ );
219
+
220
+ Slider.displayName = 'Slider';
221
+
222
+ export { Slider };
@@ -0,0 +1,67 @@
1
+ import { cn } from '../../lib/utils';
2
+ import * as SwitchPrimitives from '@rn-primitives/switch';
3
+ import { Platform } from 'react-native';
4
+
5
+ /**
6
+ * A toggle switch component for binary on/off states
7
+ *
8
+ * Provides a visual toggle control with smooth animations and platform-specific styling.
9
+ * Automatically handles checked/unchecked states and disabled interactions.
10
+ *
11
+ * @component
12
+ * @example
13
+ * ```tsx
14
+ * const [enabled, setEnabled] = useState(false);
15
+ *
16
+ * <Switch
17
+ * checked={enabled}
18
+ * onCheckedChange={setEnabled}
19
+ * />
20
+ *
21
+ * // Disabled switch
22
+ * <Switch checked={true} disabled />
23
+ * ```
24
+ *
25
+ * @param {boolean} checked - Current state of the switch
26
+ * @param {(checked: boolean) => void} onCheckedChange - Callback when switch is toggled
27
+ * @param {boolean} [disabled] - Whether the switch is disabled
28
+ * @param {string} [className] - Additional Tailwind classes
29
+ *
30
+ * @accessibility
31
+ * - Proper focus states on web for keyboard navigation
32
+ * - Disabled state prevents interaction
33
+ * - Visual feedback for checked/unchecked states
34
+ */
35
+ function Switch({
36
+ className,
37
+ ...props
38
+ }: SwitchPrimitives.RootProps & React.RefAttributes<SwitchPrimitives.RootRef>) {
39
+ return (
40
+ <SwitchPrimitives.Root
41
+ className={cn(
42
+ 'flex h-[1.15rem] w-8 shrink-0 flex-row items-center rounded-full border border-transparent shadow-sm shadow-black/5',
43
+ Platform.select({
44
+ web: 'focus-visible:border-ring focus-visible:ring-ring/50 peer inline-flex outline-none transition-all focus-visible:ring-[3px] disabled:cursor-not-allowed',
45
+ }),
46
+ props.checked ? 'bg-primary' : 'bg-input dark:bg-input/80',
47
+ props.disabled && 'opacity-50',
48
+ className
49
+ )}
50
+ {...props}
51
+ >
52
+ <SwitchPrimitives.Thumb
53
+ className={cn(
54
+ 'bg-background size-4 rounded-full transition-transform',
55
+ Platform.select({
56
+ web: 'pointer-events-none block ring-0',
57
+ }),
58
+ props.checked
59
+ ? 'dark:bg-primary-foreground translate-x-3.5'
60
+ : 'dark:bg-foreground translate-x-0'
61
+ )}
62
+ />
63
+ </SwitchPrimitives.Root>
64
+ );
65
+ }
66
+
67
+ export { Switch };
@@ -0,0 +1,170 @@
1
+ import { cn } from '../../lib/utils';
2
+ import { Text } from './Text';
3
+ import { View } from 'react-native';
4
+ import { cssInterop } from 'nativewind';
5
+ import * as React from 'react';
6
+
7
+ cssInterop(View, { className: 'style' });
8
+
9
+ /**
10
+ * Root table container component
11
+ *
12
+ * @component
13
+ * @example
14
+ * ```tsx
15
+ * <Table>
16
+ * <TableHeader>
17
+ * <TableRow>
18
+ * <TableHead><Text>Name</Text></TableHead>
19
+ * <TableHead><Text>Email</Text></TableHead>
20
+ * </TableRow>
21
+ * </TableHeader>
22
+ * <TableBody>
23
+ * <TableRow>
24
+ * <TableCell><Text>John Doe</Text></TableCell>
25
+ * <TableCell><Text>john@example.com</Text></TableCell>
26
+ * </TableRow>
27
+ * </TableBody>
28
+ * </Table>
29
+ * ```
30
+ */
31
+ function Table({
32
+ className,
33
+ ...props
34
+ }: React.ComponentProps<typeof View> & React.RefAttributes<View>) {
35
+ return <View className={cn('w-full', className)} {...props} />;
36
+ }
37
+
38
+ /**
39
+ * Table header container
40
+ *
41
+ * @component
42
+ */
43
+ function TableHeader({
44
+ className,
45
+ ...props
46
+ }: React.ComponentProps<typeof View> & React.RefAttributes<View>) {
47
+ return (
48
+ <View
49
+ className={cn('border-border flex-row border-b', className)}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ /**
56
+ * Table body container for data rows
57
+ *
58
+ * @component
59
+ */
60
+ function TableBody({
61
+ className,
62
+ ...props
63
+ }: React.ComponentProps<typeof View> & React.RefAttributes<View>) {
64
+ return <View className={cn('flex-col', className)} {...props} />;
65
+ }
66
+
67
+ /**
68
+ * Table footer container
69
+ *
70
+ * @component
71
+ */
72
+ function TableFooter({
73
+ className,
74
+ ...props
75
+ }: React.ComponentProps<typeof View> & React.RefAttributes<View>) {
76
+ return (
77
+ <View
78
+ className={cn(
79
+ 'border-border bg-muted/50 flex-row border-t font-medium',
80
+ className
81
+ )}
82
+ {...props}
83
+ />
84
+ );
85
+ }
86
+
87
+ /**
88
+ * Table row component
89
+ *
90
+ * @component
91
+ */
92
+ function TableRow({
93
+ className,
94
+ ...props
95
+ }: React.ComponentProps<typeof View> & React.RefAttributes<View>) {
96
+ return (
97
+ <View
98
+ className={cn('border-border flex-row border-b', className)}
99
+ {...props}
100
+ />
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Table header cell component
106
+ *
107
+ * @component
108
+ */
109
+ function TableHead({
110
+ className,
111
+ ...props
112
+ }: React.ComponentProps<typeof Text> & React.RefAttributes<Text>) {
113
+ return (
114
+ <Text
115
+ className={cn(
116
+ 'text-muted-foreground flex-1 p-2 text-left align-middle text-xs font-medium',
117
+ className
118
+ )}
119
+ {...props}
120
+ />
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Table data cell component
126
+ *
127
+ * @component
128
+ */
129
+ function TableCell({
130
+ className,
131
+ ...props
132
+ }: React.ComponentProps<typeof Text> & React.RefAttributes<Text>) {
133
+ return (
134
+ <Text
135
+ className={cn('flex-1 p-2 align-middle text-sm', className)}
136
+ {...props}
137
+ />
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Table caption for describing the table
143
+ *
144
+ * @component
145
+ */
146
+ function TableCaption({
147
+ className,
148
+ ...props
149
+ }: React.ComponentProps<typeof Text> & React.RefAttributes<Text>) {
150
+ return (
151
+ <Text
152
+ className={cn(
153
+ 'text-muted-foreground mt-2 text-center text-sm',
154
+ className
155
+ )}
156
+ {...props}
157
+ />
158
+ );
159
+ }
160
+
161
+ export {
162
+ Table,
163
+ TableBody,
164
+ TableCaption,
165
+ TableCell,
166
+ TableFooter,
167
+ TableHead,
168
+ TableHeader,
169
+ TableRow,
170
+ };
@@ -0,0 +1,119 @@
1
+ import { TextClassContext } from './Text';
2
+ import { cn } from '../../lib/utils';
3
+ import * as TabsPrimitive from '@rn-primitives/tabs';
4
+ import { Platform } from 'react-native';
5
+
6
+ /**
7
+ * Root tabs component for organizing content into panels
8
+ *
9
+ * @component
10
+ * @example
11
+ * ```tsx
12
+ * <Tabs value={activeTab} onValueChange={setActiveTab}>
13
+ * <TabsList>
14
+ * <TabsTrigger value="account">
15
+ * <Text>Account</Text>
16
+ * </TabsTrigger>
17
+ * <TabsTrigger value="password">
18
+ * <Text>Password</Text>
19
+ * </TabsTrigger>
20
+ * </TabsList>
21
+ * <TabsContent value="account">
22
+ * <Text>Account settings content</Text>
23
+ * </TabsContent>
24
+ * <TabsContent value="password">
25
+ * <Text>Password settings content</Text>
26
+ * </TabsContent>
27
+ * </Tabs>
28
+ * ```
29
+ *
30
+ * @accessibility
31
+ * - Keyboard navigation support (arrow keys)
32
+ * - Proper ARIA attributes for tab structure
33
+ * - Screen reader friendly
34
+ */
35
+ function Tabs({
36
+ className,
37
+ ...props
38
+ }: TabsPrimitive.RootProps & React.RefAttributes<TabsPrimitive.RootRef>) {
39
+ return (
40
+ <TabsPrimitive.Root
41
+ className={cn('flex flex-col gap-2', className)}
42
+ {...props}
43
+ />
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Container for tab triggers
49
+ *
50
+ * @component
51
+ */
52
+ function TabsList({
53
+ className,
54
+ ...props
55
+ }: TabsPrimitive.ListProps & React.RefAttributes<TabsPrimitive.ListRef>) {
56
+ return (
57
+ <TabsPrimitive.List
58
+ className={cn(
59
+ 'bg-muted flex h-9 flex-row items-center justify-center rounded-lg p-[3px]',
60
+ Platform.select({ web: 'inline-flex w-fit', native: 'mr-auto' }),
61
+ className
62
+ )}
63
+ {...props}
64
+ />
65
+ );
66
+ }
67
+
68
+ /**
69
+ * Individual tab trigger button
70
+ *
71
+ * @component
72
+ */
73
+ function TabsTrigger({
74
+ className,
75
+ ...props
76
+ }: TabsPrimitive.TriggerProps & React.RefAttributes<TabsPrimitive.TriggerRef>) {
77
+ const { value } = TabsPrimitive.useRootContext();
78
+ return (
79
+ <TextClassContext.Provider
80
+ value={cn(
81
+ 'text-foreground dark:text-muted-foreground text-sm font-medium',
82
+ value === props.value && 'dark:text-foreground'
83
+ )}
84
+ >
85
+ <TabsPrimitive.Trigger
86
+ className={cn(
87
+ 'flex h-[calc(100%-1px)] flex-row items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 shadow-none shadow-black/5',
88
+ Platform.select({
89
+ web: 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring inline-flex cursor-default whitespace-nowrap transition-[color,box-shadow] focus-visible:outline-1 focus-visible:ring-[3px] disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0',
90
+ }),
91
+ props.disabled && 'opacity-50',
92
+ props.value === value &&
93
+ 'bg-background dark:border-foreground/10 dark:bg-input/30',
94
+ className
95
+ )}
96
+ {...props}
97
+ />
98
+ </TextClassContext.Provider>
99
+ );
100
+ }
101
+
102
+ /**
103
+ * Tab content panel
104
+ *
105
+ * @component
106
+ */
107
+ function TabsContent({
108
+ className,
109
+ ...props
110
+ }: TabsPrimitive.ContentProps & React.RefAttributes<TabsPrimitive.ContentRef>) {
111
+ return (
112
+ <TabsPrimitive.Content
113
+ className={cn(Platform.select({ web: 'flex-1 outline-none' }), className)}
114
+ {...props}
115
+ />
116
+ );
117
+ }
118
+
119
+ export { Tabs, TabsContent, TabsList, TabsTrigger };
@@ -0,0 +1,73 @@
1
+ import * as Slot from '@rn-primitives/slot';
2
+ import type { SlottableTextProps, TextRef } from '@rn-primitives/types';
3
+ import { cssInterop } from 'nativewind';
4
+ import * as React from 'react';
5
+ import { Text as RNText } from 'react-native';
6
+ import { cn } from '../../lib/utils';
7
+
8
+ cssInterop(RNText, { className: 'style' });
9
+
10
+ /**
11
+ * Context for sharing text class styles with child Text components
12
+ * Used by components like Button, Card, Badge to automatically style nested Text
13
+ */
14
+ const TextClassContext = React.createContext<string | undefined>(undefined);
15
+
16
+ /**
17
+ * Enhanced Text component with NativeWind styling support
18
+ *
19
+ * Extends React Native's Text with:
20
+ * - Automatic style inheritance from TextClassContext
21
+ * - NativeWind className support
22
+ * - Slot pattern support for composition
23
+ * - Web-specific selection styling
24
+ *
25
+ * @component
26
+ * @example
27
+ * ```tsx
28
+ * // Basic text
29
+ * <Text>Hello World</Text>
30
+ *
31
+ * // With custom styling
32
+ * <Text className="text-lg font-bold">Title</Text>
33
+ *
34
+ * // Inside a styled context (e.g., Button)
35
+ * <Button variant="default">
36
+ * <Text>Button Text</Text> // Automatically inherits button text styling
37
+ * </Button>
38
+ *
39
+ * // Using asChild for composition
40
+ * <Text asChild>
41
+ * <Link href="/home">Home</Link>
42
+ * </Text>
43
+ * ```
44
+ *
45
+ * @property {string} [className] - Additional Tailwind classes
46
+ * @property {boolean} [asChild] - When true, merges props into immediate child
47
+ */
48
+ const Text = React.forwardRef<
49
+ TextRef,
50
+ SlottableTextProps & {
51
+ className?: string;
52
+ children?: React.ReactNode;
53
+ }
54
+ >(({ className, asChild = false, style, ...props }, ref) => {
55
+ const textClass = React.useContext(TextClassContext);
56
+ const Component = asChild ? Slot.Text : RNText;
57
+
58
+ const finalClassName = textClass
59
+ ? cn('text-base web:select-text', textClass, className)
60
+ : cn('text-base text-foreground web:select-text', className);
61
+
62
+ return (
63
+ <Component
64
+ className={finalClassName}
65
+ style={style}
66
+ ref={ref as any}
67
+ {...props}
68
+ />
69
+ );
70
+ });
71
+ Text.displayName = 'Text';
72
+
73
+ export { Text, TextClassContext };