@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,121 @@
1
+ import * as Slot from '@rn-primitives/slot';
2
+ import type { SlottableViewProps, ViewRef } from '@rn-primitives/types';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+ import { cssInterop } from 'nativewind';
5
+ import * as React from 'react';
6
+ import { View } from 'react-native';
7
+ import { cn } from '../../lib/utils';
8
+ import { TextClassContext } from './Text';
9
+
10
+ cssInterop(View, { className: 'style' });
11
+ cssInterop(Slot.View, { className: 'style' });
12
+
13
+ /**
14
+ * Badge container style variants
15
+ *
16
+ * @variant default - Primary badge with solid background
17
+ * @variant secondary - Secondary badge with muted background
18
+ * @variant destructive - Destructive badge with danger/error styling
19
+ * @variant outline - Badge with border and transparent background
20
+ */
21
+ const badgeVariants = cva(
22
+ 'flex-row items-center rounded-full border border-border px-2.5 py-0.5 web:inline-flex',
23
+ {
24
+ variants: {
25
+ variant: {
26
+ default: 'border-transparent bg-primary',
27
+ secondary: 'border-transparent bg-secondary',
28
+ destructive: 'border-transparent bg-destructive',
29
+ outline: 'border-border',
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ variant: 'default',
34
+ },
35
+ }
36
+ );
37
+
38
+ /**
39
+ * Badge text style variants for matching text colors with badge variants
40
+ * Applied through TextClassContext to child Text components
41
+ *
42
+ * Variants match badgeVariants to ensure proper contrast
43
+ * @see badgeVariants
44
+ */
45
+ const badgeTextVariants = cva('text-xs font-semibold', {
46
+ variants: {
47
+ variant: {
48
+ default: 'text-primary-foreground',
49
+ secondary: 'text-secondary-foreground',
50
+ destructive: 'text-destructive-foreground',
51
+ outline: 'text-foreground',
52
+ },
53
+ },
54
+ defaultVariants: {
55
+ variant: 'default',
56
+ },
57
+ });
58
+
59
+ /**
60
+ * Props for Badge component
61
+ *
62
+ * @extends SlottableViewProps - Includes all View props plus asChild for slot composition
63
+ * @extends VariantProps - Includes variant styling options from badgeVariants
64
+ *
65
+ * @property {string} [className] - Additional Tailwind classes for custom styling
66
+ * @property {boolean} [asChild] - When true, merges props into immediate child
67
+ * @property {'default' | 'secondary' | 'destructive' | 'outline'} [variant='default'] - Badge style variant
68
+ *
69
+ * @example
70
+ * ```tsx
71
+ * <Badge variant="default">
72
+ * <Text>New</Text>
73
+ * </Badge>
74
+ * ```
75
+ */
76
+ type BadgeProps = SlottableViewProps &
77
+ VariantProps<typeof badgeVariants> & { className?: string };
78
+
79
+ /**
80
+ * A compact badge component for displaying status, labels, or counts
81
+ *
82
+ * Small, pill-shaped component for highlighting information. Supports multiple
83
+ * variants for different contexts and automatically styles child Text components.
84
+ *
85
+ * @component
86
+ * @example
87
+ * ```tsx
88
+ * // Status badge
89
+ * <Badge variant="default">
90
+ * <Text>Active</Text>
91
+ * </Badge>
92
+ *
93
+ * // Count badge
94
+ * <Badge variant="destructive">
95
+ * <Text>5</Text>
96
+ * </Badge>
97
+ *
98
+ * // Outline badge
99
+ * <Badge variant="outline">
100
+ * <Text>Draft</Text>
101
+ * </Badge>
102
+ * ```
103
+ */
104
+ const Badge = React.forwardRef<ViewRef, BadgeProps>(
105
+ ({ className, variant, asChild = false, ...props }, ref) => {
106
+ const Component = asChild ? Slot.View : View;
107
+ return (
108
+ <TextClassContext.Provider value={cn(badgeTextVariants({ variant }))}>
109
+ <Component
110
+ className={cn(badgeVariants({ variant, className }))}
111
+ ref={ref as any}
112
+ {...props}
113
+ />
114
+ </TextClassContext.Provider>
115
+ );
116
+ }
117
+ );
118
+ Badge.displayName = 'Badge';
119
+
120
+ export { Badge, badgeTextVariants, badgeVariants };
121
+ export type { BadgeProps };
@@ -0,0 +1,224 @@
1
+ import React, {
2
+ forwardRef,
3
+ useCallback,
4
+ useImperativeHandle,
5
+ useMemo,
6
+ useRef,
7
+ } from 'react';
8
+ import { StyleSheet, View, ViewStyle } from 'react-native';
9
+ import BottomSheetPrimitive, {
10
+ BottomSheetBackdrop,
11
+ BottomSheetBackdropProps,
12
+ BottomSheetView,
13
+ } from '@gorhom/bottom-sheet';
14
+ import { cn } from '../../lib/utils';
15
+ import { useTheme } from '../../lib/ThemeProvider';
16
+ import { hslToRgb } from '../theme-config';
17
+
18
+ type BottomSheetRef = {
19
+ open: () => void;
20
+ close: () => void;
21
+ expand: () => void;
22
+ collapse: () => void;
23
+ snapToIndex: (index: number) => void;
24
+ };
25
+
26
+ interface BottomSheetProps {
27
+ children: React.ReactNode;
28
+ snapPoints?: (string | number)[];
29
+ initialSnapIndex?: number;
30
+ enablePanDownToClose?: boolean;
31
+ enableDynamicSizing?: boolean;
32
+ backdropOpacity?: number;
33
+ enableBackdropDismiss?: boolean;
34
+ handleIndicatorStyle?: ViewStyle;
35
+ backgroundStyle?: ViewStyle;
36
+ className?: string;
37
+ containerClassName?: string;
38
+ onChange?: (index: number) => void;
39
+ onClose?: () => void;
40
+ }
41
+
42
+ const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>(
43
+ (
44
+ {
45
+ children,
46
+ snapPoints: customSnapPoints,
47
+ initialSnapIndex = -1,
48
+ enablePanDownToClose = true,
49
+ enableDynamicSizing = false,
50
+ backdropOpacity = 0.5,
51
+ enableBackdropDismiss = true,
52
+ handleIndicatorStyle,
53
+ backgroundStyle,
54
+ className,
55
+ containerClassName,
56
+ onChange,
57
+ onClose,
58
+ },
59
+ ref
60
+ ) => {
61
+ const bottomSheetRef = useRef<BottomSheetPrimitive>(null);
62
+ const { colors } = useTheme();
63
+
64
+ // Default snap points if not provided
65
+ const snapPoints = useMemo(
66
+ () => customSnapPoints ?? ['25%', '50%', '90%'],
67
+ [customSnapPoints]
68
+ );
69
+
70
+ // Expose methods via ref (similar to Flutter's showModalBottomSheet)
71
+ useImperativeHandle(ref, () => ({
72
+ open: () => bottomSheetRef.current?.expand(),
73
+ close: () => bottomSheetRef.current?.close(),
74
+ expand: () => bottomSheetRef.current?.expand(),
75
+ collapse: () => bottomSheetRef.current?.collapse(),
76
+ snapToIndex: (index: number) =>
77
+ bottomSheetRef.current?.snapToIndex(index),
78
+ }));
79
+
80
+ // Custom backdrop component
81
+ const renderBackdrop = useCallback(
82
+ (props: BottomSheetBackdropProps) => (
83
+ <BottomSheetBackdrop
84
+ {...props}
85
+ opacity={backdropOpacity}
86
+ appearsOnIndex={0}
87
+ disappearsOnIndex={-1}
88
+ pressBehavior={enableBackdropDismiss ? 'close' : 'none'}
89
+ />
90
+ ),
91
+ [backdropOpacity, enableBackdropDismiss]
92
+ );
93
+
94
+ // Handle sheet changes
95
+ const handleSheetChanges = useCallback(
96
+ (index: number) => {
97
+ onChange?.(index);
98
+ if (index === -1) {
99
+ onClose?.();
100
+ }
101
+ },
102
+ [onChange, onClose]
103
+ );
104
+
105
+ // Theme-aware background style - convert HSL to RGB for React Native
106
+ const themedBackgroundStyle = useMemo(
107
+ () => [
108
+ styles.background,
109
+ { backgroundColor: hslToRgb(colors.card) },
110
+ backgroundStyle,
111
+ ],
112
+ [colors.card, backgroundStyle]
113
+ );
114
+
115
+ // Theme-aware handle indicator style - convert HSL to RGB for React Native
116
+ const themedHandleStyle = useMemo(
117
+ () => [
118
+ styles.handleIndicator,
119
+ { backgroundColor: hslToRgb(colors.border) },
120
+ handleIndicatorStyle,
121
+ ],
122
+ [colors.border, handleIndicatorStyle]
123
+ );
124
+
125
+ return (
126
+ <BottomSheetPrimitive
127
+ ref={bottomSheetRef}
128
+ index={initialSnapIndex}
129
+ snapPoints={snapPoints}
130
+ enablePanDownToClose={enablePanDownToClose}
131
+ enableDynamicSizing={enableDynamicSizing}
132
+ backdropComponent={renderBackdrop}
133
+ onChange={handleSheetChanges}
134
+ handleIndicatorStyle={themedHandleStyle}
135
+ backgroundStyle={themedBackgroundStyle}
136
+ style={styles.container}
137
+ animateOnMount={false}
138
+ >
139
+ <BottomSheetView
140
+ style={[styles.contentContainer]}
141
+ className={cn('flex-1 bg-card', containerClassName)}
142
+ >
143
+ <View className={cn('flex-1', className)}>{children}</View>
144
+ </BottomSheetView>
145
+ </BottomSheetPrimitive>
146
+ );
147
+ }
148
+ );
149
+
150
+ BottomSheet.displayName = 'BottomSheet';
151
+
152
+ const styles = StyleSheet.create({
153
+ container: {
154
+ zIndex: 9999,
155
+ },
156
+ contentContainer: {
157
+ flex: 1,
158
+ },
159
+ background: {
160
+ borderTopLeftRadius: 20,
161
+ borderTopRightRadius: 20,
162
+ },
163
+ handleIndicator: {
164
+ width: 40,
165
+ height: 4,
166
+ },
167
+ });
168
+
169
+ // Helper component for bottom sheet header
170
+ interface BottomSheetHeaderProps {
171
+ children: React.ReactNode;
172
+ className?: string;
173
+ }
174
+
175
+ const BottomSheetHeader = ({ children, className }: BottomSheetHeaderProps) => {
176
+ return (
177
+ <View
178
+ className={cn('border-b border-border px-4 py-3 web:px-6', className)}
179
+ >
180
+ {children}
181
+ </View>
182
+ );
183
+ };
184
+
185
+ // Helper component for bottom sheet content
186
+ interface BottomSheetContentProps {
187
+ children: React.ReactNode;
188
+ className?: string;
189
+ }
190
+
191
+ const BottomSheetContent = ({
192
+ children,
193
+ className,
194
+ }: BottomSheetContentProps) => {
195
+ return (
196
+ <View className={cn('flex-1 px-4 py-4 web:px-6', className)}>
197
+ {children}
198
+ </View>
199
+ );
200
+ };
201
+
202
+ // Helper component for bottom sheet footer
203
+ interface BottomSheetFooterProps {
204
+ children: React.ReactNode;
205
+ className?: string;
206
+ }
207
+
208
+ const BottomSheetFooter = ({ children, className }: BottomSheetFooterProps) => {
209
+ return (
210
+ <View
211
+ className={cn('border-t border-border px-4 py-3 web:px-6', className)}
212
+ >
213
+ {children}
214
+ </View>
215
+ );
216
+ };
217
+
218
+ export {
219
+ BottomSheet,
220
+ BottomSheetHeader,
221
+ BottomSheetContent,
222
+ BottomSheetFooter,
223
+ };
224
+ export type { BottomSheetRef, BottomSheetProps };
@@ -0,0 +1,100 @@
1
+ import * as Slot from '@rn-primitives/slot';
2
+ import type {
3
+ PressableRef,
4
+ SlottablePressableProps,
5
+ } from '@rn-primitives/types';
6
+ import { cva, type VariantProps } from 'class-variance-authority';
7
+ import { cssInterop } from 'nativewind';
8
+ import * as React from 'react';
9
+ import { Pressable } from 'react-native';
10
+ import { cn } from '../../lib/utils';
11
+ import { TextClassContext } from './Text';
12
+
13
+ cssInterop(Pressable, { className: 'style' });
14
+ cssInterop(Slot.Pressable, { className: 'style' });
15
+
16
+ const buttonVariants = cva(
17
+ 'group flex items-center justify-center rounded-4 web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2',
18
+ {
19
+ variants: {
20
+ variant: {
21
+ default: 'bg-primary active:opacity-20',
22
+ destructive: 'bg-destructive active:opacity-90',
23
+ outline: 'border border-input bg-background active:bg-accent',
24
+ secondary: 'bg-secondary active:opacity-80',
25
+ ghost: 'active:bg-accent',
26
+ link: 'web:underline-offset-4',
27
+ },
28
+ size: {
29
+ default:
30
+ 'h-10 px-button-x py-button-y native:h-12 native:px-button-x native:py-button-y',
31
+ sm: 'h-9 px-3',
32
+ lg: 'h-11 px-8 native:h-14',
33
+ icon: 'h-10 w-10',
34
+ },
35
+ },
36
+ defaultVariants: {
37
+ variant: 'default',
38
+ size: 'default',
39
+ },
40
+ }
41
+ );
42
+
43
+ const buttonTextVariants = cva(
44
+ 'web:whitespace-nowrap text-sm native:text-base font-medium web:transition-colors',
45
+ {
46
+ variants: {
47
+ variant: {
48
+ default: 'text-primary-foreground',
49
+ destructive: 'text-destructive-foreground',
50
+ outline: 'text-foreground group-active:text-accent-foreground',
51
+ secondary:
52
+ 'text-secondary-foreground group-active:text-secondary-foreground',
53
+ ghost: 'text-foreground group-active:text-accent-foreground',
54
+ link: 'text-primary group-active:underline',
55
+ },
56
+ size: {
57
+ default: '',
58
+ sm: '',
59
+ lg: 'native:text-lg',
60
+ icon: '',
61
+ },
62
+ },
63
+ defaultVariants: {
64
+ variant: 'default',
65
+ size: 'default',
66
+ },
67
+ }
68
+ );
69
+
70
+ type ButtonProps = SlottablePressableProps &
71
+ VariantProps<typeof buttonVariants> & {
72
+ className?: string;
73
+ };
74
+
75
+ const Button = React.forwardRef<PressableRef, ButtonProps>(
76
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
77
+ const Component = asChild ? Slot.Pressable : Pressable;
78
+
79
+ return (
80
+ <TextClassContext.Provider
81
+ value={cn(buttonTextVariants({ variant, size }))}
82
+ >
83
+ <Component
84
+ ref={ref}
85
+ role="button"
86
+ className={cn(
87
+ buttonVariants({ variant, size, className }),
88
+ props.disabled && 'opacity-50 web:pointer-events-none'
89
+ )}
90
+ {...props}
91
+ />
92
+ </TextClassContext.Provider>
93
+ );
94
+ }
95
+ );
96
+
97
+ Button.displayName = 'Button';
98
+
99
+ export { Button, buttonTextVariants, buttonVariants };
100
+ export type { ButtonProps };
@@ -0,0 +1,185 @@
1
+ import * as Slot from '@rn-primitives/slot';
2
+ import type { SlottableViewProps, ViewRef } from '@rn-primitives/types';
3
+ import { cssInterop } from 'nativewind';
4
+ import * as React from 'react';
5
+ import { View } from 'react-native';
6
+ import { cn } from '../../lib/utils';
7
+ import { TextClassContext } from './Text';
8
+
9
+ cssInterop(View, { className: 'style' });
10
+ cssInterop(Slot.View, { className: 'style' });
11
+
12
+ /**
13
+ * Card container component - Main wrapper for card content
14
+ *
15
+ * Provides elevated container with border, rounded corners, and shadow.
16
+ * Use with CardHeader, CardTitle, CardDescription, CardContent, and CardFooter
17
+ * for a complete card structure.
18
+ *
19
+ * @component
20
+ * @example
21
+ * ```tsx
22
+ * <Card>
23
+ * <CardHeader>
24
+ * <CardTitle><Text>Title</Text></CardTitle>
25
+ * <CardDescription><Text>Description</Text></CardDescription>
26
+ * </CardHeader>
27
+ * <CardContent>
28
+ * <Text>Card content goes here</Text>
29
+ * </CardContent>
30
+ * <CardFooter>
31
+ * <Button>Action</Button>
32
+ * </CardFooter>
33
+ * </Card>
34
+ * ```
35
+ *
36
+ * @property {boolean} [asChild] - When true, merges props into immediate child
37
+ * @property {string} [className] - Additional Tailwind classes
38
+ */
39
+ const Card = React.forwardRef<
40
+ ViewRef,
41
+ SlottableViewProps & { className?: string }
42
+ >(({ className, asChild = false, ...props }, ref) => {
43
+ const Component = asChild ? Slot.View : View;
44
+ return (
45
+ <Component
46
+ className={cn(
47
+ 'rounded-lg border border-border dark:border-border bg-card dark:bg-card p-6 shadow-sm shadow-foreground/10 dark:shadow-white/10',
48
+ className
49
+ )}
50
+ ref={ref as any}
51
+ {...props}
52
+ />
53
+ );
54
+ });
55
+ Card.displayName = 'Card';
56
+
57
+ /**
58
+ * CardHeader - Container for card title and description
59
+ *
60
+ * Provides proper spacing for title and description elements at the top of a card.
61
+ *
62
+ * @property {boolean} [asChild] - When true, merges props into immediate child
63
+ * @property {string} [className] - Additional Tailwind classes
64
+ */
65
+ const CardHeader = React.forwardRef<
66
+ ViewRef,
67
+ SlottableViewProps & { className?: string }
68
+ >(({ className, asChild = false, ...props }, ref) => {
69
+ const Component = asChild ? Slot.View : View;
70
+ return (
71
+ <Component
72
+ className={cn('flex flex-col gap-1.5 p-6', className)}
73
+ ref={ref as any}
74
+ {...props}
75
+ />
76
+ );
77
+ });
78
+ CardHeader.displayName = 'CardHeader';
79
+
80
+ /**
81
+ * CardTitle - Main title text for the card
82
+ *
83
+ * Automatically styles child Text components with large, semibold typography.
84
+ * Uses TextClassContext to apply consistent styling to nested Text elements.
85
+ *
86
+ * @property {boolean} [asChild] - When true, merges props into immediate child
87
+ * @property {string} [className] - Additional Tailwind classes for text styling
88
+ */
89
+ const CardTitle = React.forwardRef<
90
+ ViewRef,
91
+ SlottableViewProps & { className?: string }
92
+ >(({ className, asChild = false, ...props }, ref) => {
93
+ const Component = asChild ? Slot.View : View;
94
+ return (
95
+ <TextClassContext.Provider
96
+ value={cn(
97
+ 'text-2xl font-semibold leading-none tracking-tight text-card-foreground dark:text-card-foreground',
98
+ className
99
+ )}
100
+ >
101
+ <Component ref={ref as any} {...props} />
102
+ </TextClassContext.Provider>
103
+ );
104
+ });
105
+ CardTitle.displayName = 'CardTitle';
106
+
107
+ /**
108
+ * CardDescription - Subtitle or description text for the card
109
+ *
110
+ * Automatically styles child Text components with muted, smaller typography.
111
+ * Uses TextClassContext to apply consistent styling to nested Text elements.
112
+ *
113
+ * @property {boolean} [asChild] - When true, merges props into immediate child
114
+ * @property {string} [className] - Additional Tailwind classes for text styling
115
+ */
116
+ const CardDescription = React.forwardRef<
117
+ ViewRef,
118
+ SlottableViewProps & { className?: string }
119
+ >(({ className, asChild = false, ...props }, ref) => {
120
+ const Component = asChild ? Slot.View : View;
121
+ return (
122
+ <TextClassContext.Provider
123
+ value={cn(
124
+ 'text-sm text-muted-foreground dark:text-muted-foreground',
125
+ className
126
+ )}
127
+ >
128
+ <Component ref={ref as any} {...props} />
129
+ </TextClassContext.Provider>
130
+ );
131
+ });
132
+ CardDescription.displayName = 'CardDescription';
133
+
134
+ /**
135
+ * CardContent - Main content area of the card
136
+ *
137
+ * Container for the primary card content with appropriate padding.
138
+ * Top padding is removed to flow naturally after CardHeader.
139
+ *
140
+ * @property {boolean} [asChild] - When true, merges props into immediate child
141
+ * @property {string} [className] - Additional Tailwind classes
142
+ */
143
+ const CardContent = React.forwardRef<
144
+ ViewRef,
145
+ SlottableViewProps & { className?: string }
146
+ >(({ className, asChild = false, ...props }, ref) => {
147
+ const Component = asChild ? Slot.View : View;
148
+ return (
149
+ <Component className={cn('p-6 pt-0', className)} ref={ref} {...props} />
150
+ );
151
+ });
152
+ CardContent.displayName = 'CardContent';
153
+
154
+ /**
155
+ * CardFooter - Footer area for actions or additional info
156
+ *
157
+ * Typically contains buttons or other interactive elements.
158
+ * Arranged as a row with centered items.
159
+ *
160
+ * @property {boolean} [asChild] - When true, merges props into immediate child
161
+ * @property {string} [className] - Additional Tailwind classes
162
+ */
163
+ const CardFooter = React.forwardRef<
164
+ ViewRef,
165
+ SlottableViewProps & { className?: string }
166
+ >(({ className, asChild = false, ...props }, ref) => {
167
+ const Component = asChild ? Slot.View : View;
168
+ return (
169
+ <Component
170
+ className={cn('flex flex-row items-center p-6 pt-0', className)}
171
+ ref={ref as any}
172
+ {...props}
173
+ />
174
+ );
175
+ });
176
+ CardFooter.displayName = 'CardFooter';
177
+
178
+ export {
179
+ Card,
180
+ CardContent,
181
+ CardDescription,
182
+ CardFooter,
183
+ CardHeader,
184
+ CardTitle,
185
+ };
@@ -0,0 +1,81 @@
1
+ import { Icon } from './Icon';
2
+ import { cn } from '../../lib/utils';
3
+ import * as CheckboxPrimitive from '@rn-primitives/checkbox';
4
+ import { Check } from 'lucide-react-native';
5
+ import { Platform } from 'react-native';
6
+
7
+ const DEFAULT_HIT_SLOP = 24;
8
+
9
+ /**
10
+ * Interactive checkbox for boolean selections
11
+ *
12
+ * A control that allows users to toggle between checked and unchecked states.
13
+ * Features a checkmark indicator, configurable styling, and accessible touch targets.
14
+ *
15
+ * @component
16
+ * @example
17
+ * ```tsx
18
+ * // Basic checkbox
19
+ * <Checkbox checked={isChecked} onCheckedChange={setIsChecked} />
20
+ *
21
+ * // Checkbox with label
22
+ * <View className="flex-row items-center gap-2">
23
+ * <Checkbox id="terms" checked={accepted} onCheckedChange={setAccepted} />
24
+ * <Label htmlFor="terms">Accept terms and conditions</Label>
25
+ * </View>
26
+ *
27
+ * // Disabled checkbox
28
+ * <Checkbox checked={true} disabled />
29
+ * ```
30
+ *
31
+ * @accessibility
32
+ * - Uses 24px hit slop for easier touch interaction
33
+ * - Supports keyboard navigation on web
34
+ * - Proper ARIA attributes for screen readers
35
+ * - Visual focus indicators on web
36
+ */
37
+ function Checkbox({
38
+ className,
39
+ checkedClassName,
40
+ indicatorClassName,
41
+ iconClassName,
42
+ ...props
43
+ }: CheckboxPrimitive.RootProps &
44
+ React.RefAttributes<CheckboxPrimitive.RootRef> & {
45
+ checkedClassName?: string;
46
+ indicatorClassName?: string;
47
+ iconClassName?: string;
48
+ }) {
49
+ return (
50
+ <CheckboxPrimitive.Root
51
+ className={cn(
52
+ 'border-input dark:bg-input/30 size-4 shrink-0 rounded-[4px] border shadow-sm shadow-black/5',
53
+ Platform.select({
54
+ 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 peer cursor-default outline-none transition-shadow focus-visible:ring-[3px] disabled:cursor-not-allowed',
55
+ native: 'overflow-hidden',
56
+ }),
57
+ props.checked && cn('border-primary', checkedClassName),
58
+ props.disabled && 'opacity-50',
59
+ className
60
+ )}
61
+ hitSlop={DEFAULT_HIT_SLOP}
62
+ {...props}
63
+ >
64
+ <CheckboxPrimitive.Indicator
65
+ className={cn(
66
+ 'bg-primary h-full w-full items-center justify-center',
67
+ indicatorClassName
68
+ )}
69
+ >
70
+ <Icon
71
+ as={Check}
72
+ size={12}
73
+ strokeWidth={Platform.OS === 'web' ? 2.5 : 3.5}
74
+ className={cn('text-primary-foreground', iconClassName)}
75
+ />
76
+ </CheckboxPrimitive.Indicator>
77
+ </CheckboxPrimitive.Root>
78
+ );
79
+ }
80
+
81
+ export { Checkbox };