@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,117 @@
1
+ import { NativeOnlyAnimatedView } from './Native-Only-Animated-View';
2
+ import { TextClassContext } from './Text';
3
+ import { cn } from '../../lib/utils';
4
+ import * as TooltipPrimitive from '@rn-primitives/tooltip';
5
+ import * as React from 'react';
6
+ import { Platform, StyleSheet } from 'react-native';
7
+ import { FadeInDown, FadeInUp, FadeOut } from 'react-native-reanimated';
8
+ import { FullWindowOverlay as RNFullWindowOverlay } from 'react-native-screens';
9
+
10
+ /**
11
+ * Root tooltip component - provides context for trigger and content
12
+ *
13
+ * @component
14
+ * @example
15
+ * ```tsx
16
+ * <Tooltip>
17
+ * <TooltipTrigger>
18
+ * <Button variant="outline">
19
+ * <Text>Hover me</Text>
20
+ * </Button>
21
+ * </TooltipTrigger>
22
+ * <TooltipContent>
23
+ * <Text>Helpful tooltip text</Text>
24
+ * </TooltipContent>
25
+ * </Tooltip>
26
+ * ```
27
+ */
28
+ const Tooltip = TooltipPrimitive.Root;
29
+
30
+ /**
31
+ * Trigger component that shows tooltip on hover/press
32
+ *
33
+ * @component
34
+ */
35
+ const TooltipTrigger = TooltipPrimitive.Trigger;
36
+
37
+ /**
38
+ * Full window overlay wrapper for iOS, Fragment for other platforms
39
+ */
40
+ const FullWindowOverlay =
41
+ Platform.OS === 'ios' ? RNFullWindowOverlay : React.Fragment;
42
+
43
+ /**
44
+ * Tooltip content component
45
+ *
46
+ * Displays helpful text in a small popup when hovering or pressing the trigger.
47
+ * Includes directional animations based on positioning.
48
+ *
49
+ * @component
50
+ * @example
51
+ * ```tsx
52
+ * <TooltipContent side="top" sideOffset={8}>
53
+ * <Text>Add to favorites</Text>
54
+ * </TooltipContent>
55
+ * ```
56
+ *
57
+ * @accessibility
58
+ * - Content is announced by screen readers
59
+ * - Proper ARIA attributes for tooltips
60
+ * - Dismissible on escape key (web)
61
+ */
62
+ function TooltipContent({
63
+ className,
64
+ sideOffset = 4,
65
+ portalHost,
66
+ side = 'top',
67
+ ...props
68
+ }: TooltipPrimitive.ContentProps &
69
+ React.RefAttributes<TooltipPrimitive.ContentRef> & {
70
+ portalHost?: string;
71
+ }) {
72
+ return (
73
+ <TooltipPrimitive.Portal hostName={portalHost}>
74
+ <FullWindowOverlay>
75
+ <TooltipPrimitive.Overlay
76
+ style={Platform.select({ native: StyleSheet.absoluteFill })}
77
+ >
78
+ <NativeOnlyAnimatedView
79
+ entering={
80
+ side === 'top'
81
+ ? FadeInDown.withInitialValues({
82
+ transform: [{ translateY: 3 }],
83
+ }).duration(150)
84
+ : FadeInUp.withInitialValues({
85
+ transform: [{ translateY: -5 }],
86
+ })
87
+ }
88
+ exiting={FadeOut}
89
+ >
90
+ <TextClassContext.Provider value="text-xs text-primary-foreground">
91
+ <TooltipPrimitive.Content
92
+ sideOffset={sideOffset}
93
+ className={cn(
94
+ 'bg-primary z-50 rounded-md px-3 py-2 sm:py-1.5',
95
+ Platform.select({
96
+ web: cn(
97
+ 'animate-in fade-in-0 zoom-in-95 origin-(--radix-tooltip-content-transform-origin) w-fit text-balance',
98
+ side === 'bottom' && 'slide-in-from-top-2',
99
+ side === 'left' && 'slide-in-from-right-2',
100
+ side === 'right' && 'slide-in-from-left-2',
101
+ side === 'top' && 'slide-in-from-bottom-2'
102
+ ),
103
+ }),
104
+ className
105
+ )}
106
+ side={side}
107
+ {...props}
108
+ />
109
+ </TextClassContext.Provider>
110
+ </NativeOnlyAnimatedView>
111
+ </TooltipPrimitive.Overlay>
112
+ </FullWindowOverlay>
113
+ </TooltipPrimitive.Portal>
114
+ );
115
+ }
116
+
117
+ export { Tooltip, TooltipContent, TooltipTrigger };
@@ -0,0 +1,42 @@
1
+ export * from './Accordion';
2
+ export * from './Alert';
3
+ export * from './Alert-Dialog';
4
+ export * from './AppBar';
5
+ export * from './Aspect-Ratio';
6
+ export * from './Avatar';
7
+ export * from './Badge';
8
+ export * from './Bottom-Sheet';
9
+ export * from './Button';
10
+ export * from './Card';
11
+ export * from './Checkbox';
12
+ export * from './Collapsible';
13
+ export * from './Context-Menu';
14
+ export * from './Custom-Card';
15
+ export * from './Dialog';
16
+ export * from './Dropdown-Menu';
17
+ export * from './Floating-Action';
18
+ export * from './Greeting-Card';
19
+ export * from './Hover-Card';
20
+ export * from './Icon';
21
+ export * from './Input';
22
+ export * from './Label';
23
+ export * from './Menubar';
24
+ export * from './NavBar';
25
+ export * from './Popover';
26
+ export * from './Progress';
27
+ export * from './Radio-Group';
28
+ export * from './Select';
29
+ export * from './Separator';
30
+ export * from './SizedBox';
31
+ export * from './Skeleton';
32
+ export * from './Slider';
33
+ export * from './Switch';
34
+ export * from './Table';
35
+ export * from './Tabs';
36
+ export * from './Text';
37
+ export * from './Textarea';
38
+ export * from './Theme-Toggle';
39
+ export * from './Toast';
40
+ export * from './Toggle';
41
+ export * from './Toggle-Group';
42
+ export * from './Tooltip';
package/src/index.tsx ADDED
@@ -0,0 +1,24 @@
1
+ // Export components
2
+ export * from './components/ui';
3
+
4
+ // Export layout
5
+ export { default as RootLayout } from './app/_layout';
6
+
7
+ // Export utilities
8
+ export { cn } from './lib/utils';
9
+ export { THEME, NAV_THEME } from './lib/theme';
10
+ export { ThemeProvider, useTheme } from './lib/ThemeProvider';
11
+
12
+ // Corner radius utilities
13
+ export {
14
+ getRadiusForHeight,
15
+ getRadiusForDimensions,
16
+ getCustomRadius,
17
+ RADIUS,
18
+ RADIUS_PRESET,
19
+ CORNER_SMOOTHING,
20
+ } from './lib/cornerRadius';
21
+
22
+ // Font configuration
23
+ export { fonts } from './lib/fonts';
24
+ export type { FontName } from './lib/fonts';
@@ -0,0 +1,204 @@
1
+ import * as React from 'react';
2
+ import { useColorScheme } from 'react-native';
3
+ import { useColorScheme as useNativeWindColorScheme } from 'nativewind';
4
+
5
+ /**
6
+ * Available theme modes for the application
7
+ * @typedef {'light' | 'dark' | 'system'} ThemeMode
8
+ * - `light`: Force light theme regardless of system preference
9
+ * - `dark`: Force dark theme regardless of system preference
10
+ * - `system`: Automatically follow device system theme preference (default)
11
+ */
12
+ export type ThemeMode = 'light' | 'dark' | 'system';
13
+
14
+ /**
15
+ * Context type for theme management
16
+ * @internal
17
+ */
18
+ type ThemeContextType = {
19
+ /** Current theme mode setting */
20
+ mode: ThemeMode;
21
+ /** Whether dark mode is currently active (computed from mode and system preference) */
22
+ isDark: boolean;
23
+ /** Function to change the theme mode */
24
+ setMode: (mode: ThemeMode) => void;
25
+ /** Theme color tokens for the current mode */
26
+ colors: typeof import('./theme').THEME.light;
27
+ };
28
+
29
+ const ThemeContext = React.createContext<ThemeContextType | undefined>(
30
+ undefined
31
+ );
32
+
33
+ /**
34
+ * Props for ThemeProvider component
35
+ */
36
+ type ThemeProviderProps = {
37
+ /** Application content to provide theme context to */
38
+ children: React.ReactNode;
39
+ /**
40
+ * Initial theme mode
41
+ * @default 'system'
42
+ */
43
+ defaultMode?: ThemeMode;
44
+ /**
45
+ * Storage key for persisting theme preference (not yet implemented)
46
+ * @deprecated Currently not used - will be implemented in future version
47
+ */
48
+ storageKey?: string;
49
+ };
50
+
51
+ /**
52
+ * Theme Provider component that manages application theme state
53
+ *
54
+ * Provides theme context to all child components, enabling dark mode support
55
+ * throughout the application. Automatically responds to system theme changes
56
+ * when mode is set to 'system'.
57
+ *
58
+ * @component
59
+ * @example
60
+ * ```tsx
61
+ * // Basic usage with system theme
62
+ * function App() {
63
+ * return (
64
+ * <ThemeProvider>
65
+ * <YourApp />
66
+ * </ThemeProvider>
67
+ * );
68
+ * }
69
+ *
70
+ * // Start with dark mode
71
+ * function App() {
72
+ * return (
73
+ * <ThemeProvider defaultMode="dark">
74
+ * <YourApp />
75
+ * </ThemeProvider>
76
+ * );
77
+ * }
78
+ * ```
79
+ *
80
+ * @param {ThemeProviderProps} props - Component props
81
+ * @returns {JSX.Element} Provider component wrapping children with theme context
82
+ *
83
+ * @see {@link useTheme} for accessing theme in child components
84
+ * @see {@link useThemeMode} for optional theme access without error
85
+ */
86
+ export function ThemeProvider({
87
+ children,
88
+ defaultMode = 'system',
89
+ }: ThemeProviderProps) {
90
+ const systemColorScheme = useColorScheme();
91
+ const { setColorScheme: setNativeWindColorScheme } =
92
+ useNativeWindColorScheme();
93
+ const [mode, setModeState] = React.useState<ThemeMode>(defaultMode);
94
+
95
+ const isDark = React.useMemo(() => {
96
+ if (mode === 'system') {
97
+ return systemColorScheme === 'dark';
98
+ }
99
+ return mode === 'dark';
100
+ }, [mode, systemColorScheme]);
101
+
102
+ // Sync with nativewind when mode or isDark changes
103
+ React.useEffect(() => {
104
+ if (mode === 'system') {
105
+ setNativeWindColorScheme(systemColorScheme === 'dark' ? 'dark' : 'light');
106
+ } else {
107
+ setNativeWindColorScheme(mode);
108
+ }
109
+ }, [mode, systemColorScheme, setNativeWindColorScheme]);
110
+
111
+ const setMode = React.useCallback((newMode: ThemeMode) => {
112
+ setModeState(newMode);
113
+ }, []);
114
+
115
+ const { THEME } = require('./theme');
116
+ const colors = React.useMemo(
117
+ () => (isDark ? THEME.dark : THEME.light),
118
+ [isDark, THEME]
119
+ );
120
+
121
+ const value = React.useMemo(
122
+ () => ({
123
+ mode,
124
+ isDark,
125
+ setMode,
126
+ colors,
127
+ }),
128
+ [mode, isDark, setMode, colors]
129
+ );
130
+
131
+ return (
132
+ <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
133
+ );
134
+ }
135
+
136
+ /**
137
+ * Hook to access theme context and controls
138
+ *
139
+ * Provides access to the current theme state and methods to change the theme.
140
+ * Must be used within a ThemeProvider component tree.
141
+ *
142
+ * @returns {ThemeContextType} Theme context object
143
+ * @throws {Error} If used outside of ThemeProvider
144
+ *
145
+ * @example
146
+ * ```tsx
147
+ * function MyComponent() {
148
+ * const { mode, isDark, setMode, colors } = useTheme();
149
+ *
150
+ * return (
151
+ * <View style={{ backgroundColor: colors.background }}>
152
+ * <Text style={{ color: colors.foreground }}>
153
+ * Current mode: {mode} (Dark: {isDark ? 'Yes' : 'No'})
154
+ * </Text>
155
+ * <Button onPress={() => setMode(isDark ? 'light' : 'dark')}>
156
+ * <Text>Toggle Theme</Text>
157
+ * </Button>
158
+ * </View>
159
+ * );
160
+ * }
161
+ * ```
162
+ *
163
+ * @see {@link ThemeProvider} for setup
164
+ * @see {@link useThemeMode} for optional access without error throwing
165
+ */
166
+ export function useTheme() {
167
+ const context = React.useContext(ThemeContext);
168
+ if (context === undefined) {
169
+ throw new Error('useTheme must be used within a ThemeProvider');
170
+ }
171
+ return context;
172
+ }
173
+
174
+ /**
175
+ * Optional hook to get current dark mode state
176
+ *
177
+ * Similar to useTheme but only returns the isDark boolean and doesn't throw
178
+ * an error if used outside ThemeProvider. Useful for components that want
179
+ * to adapt to theme but don't require it.
180
+ *
181
+ * @returns {boolean | undefined} Whether dark mode is active, or undefined if no provider
182
+ *
183
+ * @example
184
+ * ```tsx
185
+ * function OptionalThemedComponent() {
186
+ * const isDark = useThemeMode();
187
+ *
188
+ * // Component works with or without ThemeProvider
189
+ * const backgroundColor = isDark ? '#000' : '#fff';
190
+ *
191
+ * return (
192
+ * <View style={{ backgroundColor }}>
193
+ * <Text>This component adapts if theme is available</Text>
194
+ * </View>
195
+ * );
196
+ * }
197
+ * ```
198
+ *
199
+ * @see {@link useTheme} for full theme access (requires provider)
200
+ */
201
+ export function useThemeMode(): boolean | undefined {
202
+ const context = React.useContext(ThemeContext);
203
+ return context?.isDark;
204
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Corner Radius System
3
+ *
4
+ * Automatically calculates the appropriate corner radius based on element dimensions.
5
+ *
6
+ * Design Rule:
7
+ * - Elements < 80px height → 12px radius
8
+ * - Elements ≥ 80px height → 20px radius
9
+ * - All corners use 100% smoothing (Apple-style squircles)
10
+ *
11
+ * Examples:
12
+ * - Button (50px height) → 12px
13
+ * - Chip (24px height) → 12px
14
+ * - Checkbox (16px height) → 4px (special case for very small)
15
+ * - Small card (60px height) → 12px
16
+ * - Large card (200px height) → 20px
17
+ * - Dialog (400px height) → 20px
18
+ */
19
+
20
+ /**
21
+ * Primitive radius values from your design system
22
+ * Your exact values: 0, 2, 4, 8, 12, 20, 24, 32, 48
23
+ */
24
+ export const RADIUS = {
25
+ R0: 0, // None
26
+ R1: 2, // Tiny
27
+ R2: 4, // Extra small
28
+ R3: 8, // Small
29
+ R4: 12, // Medium
30
+ R5: 20, // Large
31
+ R6: 24, // Extra large
32
+ R7: 32, // 2X large
33
+ R8: 48, // 3X large / Pill
34
+ } as const;
35
+
36
+ /**
37
+ * Height thresholds for radius calculation
38
+ */
39
+ const HEIGHT_THRESHOLDS = {
40
+ TINY: 20, // < 20px height → Use R2 (4px) for very small elements
41
+ SMALL: 80, // < 80px height → Use R4 (12px)
42
+ LARGE: 80, // ≥ 80px height → Use R5 (20px)
43
+ } as const;
44
+
45
+ /**
46
+ * Calculate corner radius based on element height
47
+ *
48
+ * @param height - Element height in pixels
49
+ * @returns Corner radius in pixels
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * // Button (50px height)
54
+ * const radius = getRadiusForHeight(50); // Returns 12
55
+ *
56
+ * // Checkbox (16px height)
57
+ * const radius = getRadiusForHeight(16); // Returns 4
58
+ *
59
+ * // Card (160px height)
60
+ * const radius = getRadiusForHeight(160); // Returns 20
61
+ * ```
62
+ */
63
+ export function getRadiusForHeight(height: number): number {
64
+ if (height < HEIGHT_THRESHOLDS.TINY) {
65
+ return RADIUS.R2; // 4px for very small elements (checkbox, badge, etc.)
66
+ }
67
+
68
+ if (height < HEIGHT_THRESHOLDS.SMALL) {
69
+ return RADIUS.R4; // 12px for small-medium elements (button, chip, input, etc.)
70
+ }
71
+
72
+ return RADIUS.R5; // 20px for large elements (card, dialog, sheet, etc.)
73
+ }
74
+
75
+ /**
76
+ * Calculate corner radius based on smallest dimension (width or height)
77
+ * Useful for square or nearly-square elements
78
+ *
79
+ * @param width - Element width in pixels
80
+ * @param height - Element height in pixels
81
+ * @returns Corner radius in pixels
82
+ *
83
+ * @example
84
+ * ```tsx
85
+ * // Square checkbox (16×16)
86
+ * const radius = getRadiusForDimensions(16, 16); // Returns 4
87
+ *
88
+ * // Wide button (200×50)
89
+ * const radius = getRadiusForDimensions(200, 50); // Returns 12 (based on height)
90
+ *
91
+ * // Tall card (300×200)
92
+ * const radius = getRadiusForDimensions(300, 200); // Returns 20 (based on width)
93
+ * ```
94
+ */
95
+ export function getRadiusForDimensions(width: number, height: number): number {
96
+ const smallestDimension = Math.min(width, height);
97
+ return getRadiusForHeight(smallestDimension);
98
+ }
99
+
100
+ /**
101
+ * Get corner radius with custom thresholds
102
+ * Advanced usage when you need different breakpoints
103
+ *
104
+ * @param height - Element height in pixels
105
+ * @param options - Custom threshold options
106
+ * @returns Corner radius in pixels
107
+ *
108
+ * @example
109
+ * ```tsx
110
+ * // Use 20px radius for elements ≥ 100px instead of 80px
111
+ * const radius = getCustomRadius(90, { largeThreshold: 100 });
112
+ * ```
113
+ */
114
+ export function getCustomRadius(
115
+ height: number,
116
+ options?: {
117
+ tinyThreshold?: number;
118
+ smallThreshold?: number;
119
+ tinyRadius?: number;
120
+ smallRadius?: number;
121
+ largeRadius?: number;
122
+ }
123
+ ): number {
124
+ const {
125
+ tinyThreshold = HEIGHT_THRESHOLDS.TINY,
126
+ smallThreshold = HEIGHT_THRESHOLDS.SMALL,
127
+ tinyRadius = RADIUS.R2,
128
+ smallRadius = RADIUS.R4,
129
+ largeRadius = RADIUS.R5,
130
+ } = options || {};
131
+
132
+ if (height < tinyThreshold) {
133
+ return tinyRadius;
134
+ }
135
+
136
+ if (height < smallThreshold) {
137
+ return smallRadius;
138
+ }
139
+
140
+ return largeRadius;
141
+ }
142
+
143
+ /**
144
+ * Corner smoothing value (0-1)
145
+ * 1.0 = 100% smoothing (Apple-style squircles)
146
+ */
147
+ export const CORNER_SMOOTHING = 1.0;
148
+
149
+ /**
150
+ * Preset radius values for common use cases
151
+ * Use these when you know the exact size you want
152
+ */
153
+ export const RADIUS_PRESET = {
154
+ none: RADIUS.R0, // 0px - No radius
155
+ tiny: RADIUS.R2, // 4px - Checkbox, badge, tooltip
156
+ small: RADIUS.R4, // 12px - Button, chip, input, small card
157
+ large: RADIUS.R5, // 20px - Large card, dialog, sheet
158
+ pill: RADIUS.R6, // 48px - Fully rounded
159
+ circle: 9999, // Full circle - Avatar, radio button
160
+ } as const;
@@ -0,0 +1,28 @@
1
+ import {
2
+ Poppins_400Regular,
3
+ Poppins_600SemiBold,
4
+ Poppins_800ExtraBold,
5
+ } from '@expo-google-fonts/poppins';
6
+ import { Heebo_400Regular, Heebo_600SemiBold } from '@expo-google-fonts/heebo';
7
+
8
+ /**
9
+ * Font configuration for CampX Design System
10
+ *
11
+ * Fonts:
12
+ * - Poppins: Used for headings, buttons, labels, subtitles
13
+ * - Heebo: Used for body text, inputs, captions
14
+ *
15
+ * Weights:
16
+ * - 400 (Regular): Body text, labels, captions
17
+ * - 600 (Semi Bold): Subtitles, buttons, emphasized body
18
+ * - 800 (Extra Bold): Headings
19
+ */
20
+ export const fonts = {
21
+ Poppins_400Regular,
22
+ Poppins_600SemiBold,
23
+ Poppins_800ExtraBold,
24
+ Heebo_400Regular,
25
+ Heebo_600SemiBold,
26
+ };
27
+
28
+ export type FontName = keyof typeof fonts;