@streamplace/components 0.0.1 → 0.7.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 (169) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +35 -0
  3. package/dist/components/chat/chat-box.js +109 -0
  4. package/dist/components/chat/chat-message.js +76 -0
  5. package/dist/components/chat/chat.js +56 -0
  6. package/dist/components/chat/mention-suggestions.js +39 -0
  7. package/dist/components/chat/mod-view.js +33 -0
  8. package/dist/components/mobile-player/fullscreen.js +69 -0
  9. package/dist/components/mobile-player/fullscreen.native.js +151 -0
  10. package/dist/components/mobile-player/player.js +103 -0
  11. package/dist/components/mobile-player/props.js +1 -0
  12. package/dist/components/mobile-player/shared.js +51 -0
  13. package/dist/components/mobile-player/ui/countdown.js +79 -0
  14. package/dist/components/mobile-player/ui/index.js +5 -0
  15. package/dist/components/mobile-player/ui/input.js +38 -0
  16. package/dist/components/mobile-player/ui/metrics.js +40 -0
  17. package/dist/components/mobile-player/ui/streamer-context-menu.js +4 -0
  18. package/dist/components/mobile-player/ui/viewer-context-menu.js +20 -0
  19. package/dist/components/mobile-player/use-webrtc.js +232 -0
  20. package/dist/components/mobile-player/video.js +375 -0
  21. package/dist/components/mobile-player/video.native.js +238 -0
  22. package/dist/components/mobile-player/webrtc-diagnostics.js +106 -0
  23. package/dist/components/mobile-player/webrtc-primitives.js +25 -0
  24. package/dist/components/mobile-player/webrtc-primitives.native.js +1 -0
  25. package/dist/components/ui/button.js +220 -0
  26. package/dist/components/ui/dialog.js +203 -0
  27. package/dist/components/ui/dropdown.js +148 -0
  28. package/dist/components/ui/icons.js +22 -0
  29. package/dist/components/ui/index.js +22 -0
  30. package/dist/components/ui/input.js +202 -0
  31. package/dist/components/ui/loader.js +7 -0
  32. package/dist/components/ui/primitives/button.js +121 -0
  33. package/dist/components/ui/primitives/input.js +202 -0
  34. package/dist/components/ui/primitives/modal.js +203 -0
  35. package/dist/components/ui/primitives/text.js +286 -0
  36. package/dist/components/ui/resizeable.js +101 -0
  37. package/dist/components/ui/text.js +175 -0
  38. package/dist/components/ui/textarea.js +17 -0
  39. package/dist/components/ui/toast.js +129 -0
  40. package/dist/components/ui/view.js +250 -0
  41. package/dist/hooks/index.js +9 -0
  42. package/dist/hooks/useAvatars.js +32 -0
  43. package/dist/hooks/useCameraToggle.js +9 -0
  44. package/dist/hooks/useKeyboard.js +33 -0
  45. package/dist/hooks/useKeyboardSlide.js +11 -0
  46. package/dist/hooks/useLivestreamInfo.js +62 -0
  47. package/dist/hooks/useOuterAndInnerDimensions.js +27 -0
  48. package/dist/hooks/usePlayerDimensions.js +19 -0
  49. package/dist/hooks/useSegmentTiming.js +62 -0
  50. package/dist/index.js +16 -0
  51. package/dist/lib/facet.js +88 -0
  52. package/dist/lib/theme/atoms.js +620 -0
  53. package/dist/lib/theme/atoms.types.js +5 -0
  54. package/dist/lib/theme/index.js +9 -0
  55. package/dist/lib/theme/theme.js +248 -0
  56. package/dist/lib/theme/tokens.js +383 -0
  57. package/dist/lib/utils.js +94 -0
  58. package/dist/livestream-provider/index.js +25 -0
  59. package/dist/livestream-provider/websocket.js +41 -0
  60. package/dist/livestream-store/chat.js +186 -0
  61. package/dist/livestream-store/context.js +2 -0
  62. package/dist/livestream-store/index.js +4 -0
  63. package/dist/livestream-store/livestream-state.js +1 -0
  64. package/dist/livestream-store/livestream-store.js +42 -0
  65. package/dist/livestream-store/stream-key.js +115 -0
  66. package/dist/livestream-store/websocket-consumer.js +55 -0
  67. package/dist/player-store/context.js +2 -0
  68. package/dist/player-store/index.js +6 -0
  69. package/dist/player-store/player-provider.js +52 -0
  70. package/dist/player-store/player-state.js +22 -0
  71. package/dist/player-store/player-store.js +159 -0
  72. package/dist/player-store/single-player-provider.js +109 -0
  73. package/dist/streamplace-provider/context.js +2 -0
  74. package/dist/streamplace-provider/index.js +16 -0
  75. package/dist/streamplace-provider/poller.js +46 -0
  76. package/dist/streamplace-provider/xrpc.js +0 -0
  77. package/dist/streamplace-store/block.js +23 -0
  78. package/dist/streamplace-store/index.js +3 -0
  79. package/dist/streamplace-store/stream.js +193 -0
  80. package/dist/streamplace-store/streamplace-store.js +37 -0
  81. package/dist/streamplace-store/user.js +47 -0
  82. package/dist/streamplace-store/xrpc.js +12 -0
  83. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  84. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/56540125 +0 -0
  85. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/67b1eb60 +0 -0
  86. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/7c275f90 +0 -0
  87. package/package.json +50 -8
  88. package/src/components/chat/chat-box.tsx +195 -0
  89. package/src/components/chat/chat-message.tsx +192 -0
  90. package/src/components/chat/chat.tsx +128 -0
  91. package/src/components/chat/mention-suggestions.tsx +71 -0
  92. package/src/components/chat/mod-view.tsx +118 -0
  93. package/src/components/mobile-player/fullscreen.native.tsx +193 -0
  94. package/src/components/mobile-player/fullscreen.tsx +79 -0
  95. package/src/components/mobile-player/player.tsx +134 -0
  96. package/src/components/mobile-player/props.tsx +11 -0
  97. package/src/components/mobile-player/shared.tsx +56 -0
  98. package/src/components/mobile-player/ui/countdown.tsx +119 -0
  99. package/src/components/mobile-player/ui/index.ts +5 -0
  100. package/src/components/mobile-player/ui/input.tsx +85 -0
  101. package/src/components/mobile-player/ui/metrics.tsx +69 -0
  102. package/src/components/mobile-player/ui/streamer-context-menu.tsx +3 -0
  103. package/src/components/mobile-player/ui/viewer-context-menu.tsx +70 -0
  104. package/src/components/mobile-player/use-webrtc.tsx +282 -0
  105. package/src/components/mobile-player/video.native.tsx +360 -0
  106. package/src/components/mobile-player/video.tsx +557 -0
  107. package/src/components/mobile-player/webrtc-diagnostics.tsx +149 -0
  108. package/src/components/mobile-player/webrtc-primitives.native.tsx +6 -0
  109. package/src/components/mobile-player/webrtc-primitives.tsx +33 -0
  110. package/src/components/ui/button.tsx +309 -0
  111. package/src/components/ui/dialog.tsx +376 -0
  112. package/src/components/ui/dropdown.tsx +399 -0
  113. package/src/components/ui/icons.tsx +50 -0
  114. package/src/components/ui/index.ts +33 -0
  115. package/src/components/ui/input.tsx +350 -0
  116. package/src/components/ui/loader.tsx +9 -0
  117. package/src/components/ui/primitives/button.tsx +292 -0
  118. package/src/components/ui/primitives/input.tsx +422 -0
  119. package/src/components/ui/primitives/modal.tsx +421 -0
  120. package/src/components/ui/primitives/text.tsx +499 -0
  121. package/src/components/ui/resizeable.tsx +169 -0
  122. package/src/components/ui/text.tsx +330 -0
  123. package/src/components/ui/textarea.tsx +34 -0
  124. package/src/components/ui/toast.tsx +203 -0
  125. package/src/components/ui/view.tsx +344 -0
  126. package/src/hooks/index.ts +9 -0
  127. package/src/hooks/useAvatars.tsx +44 -0
  128. package/src/hooks/useCameraToggle.ts +12 -0
  129. package/src/hooks/useKeyboard.tsx +41 -0
  130. package/src/hooks/useKeyboardSlide.ts +12 -0
  131. package/src/hooks/useLivestreamInfo.ts +67 -0
  132. package/src/hooks/useOuterAndInnerDimensions.tsx +32 -0
  133. package/src/hooks/usePlayerDimensions.ts +23 -0
  134. package/src/hooks/useSegmentTiming.tsx +88 -0
  135. package/src/index.tsx +27 -0
  136. package/src/lib/facet.ts +131 -0
  137. package/src/lib/theme/atoms.ts +760 -0
  138. package/src/lib/theme/atoms.types.ts +258 -0
  139. package/src/lib/theme/index.ts +48 -0
  140. package/src/lib/theme/theme.tsx +436 -0
  141. package/src/lib/theme/tokens.ts +409 -0
  142. package/src/lib/utils.ts +132 -0
  143. package/src/livestream-provider/index.tsx +48 -0
  144. package/src/livestream-provider/websocket.tsx +47 -0
  145. package/src/livestream-store/chat.tsx +261 -0
  146. package/src/livestream-store/context.tsx +10 -0
  147. package/src/livestream-store/index.tsx +4 -0
  148. package/src/livestream-store/livestream-state.tsx +21 -0
  149. package/src/livestream-store/livestream-store.tsx +59 -0
  150. package/src/livestream-store/stream-key.tsx +124 -0
  151. package/src/livestream-store/websocket-consumer.tsx +62 -0
  152. package/src/player-store/context.tsx +11 -0
  153. package/src/player-store/index.tsx +6 -0
  154. package/src/player-store/player-provider.tsx +89 -0
  155. package/src/player-store/player-state.tsx +187 -0
  156. package/src/player-store/player-store.tsx +239 -0
  157. package/src/player-store/single-player-provider.tsx +181 -0
  158. package/src/streamplace-provider/context.tsx +10 -0
  159. package/src/streamplace-provider/index.tsx +32 -0
  160. package/src/streamplace-provider/poller.tsx +55 -0
  161. package/src/streamplace-provider/xrpc.tsx +0 -0
  162. package/src/streamplace-store/block.tsx +29 -0
  163. package/src/streamplace-store/index.tsx +3 -0
  164. package/src/streamplace-store/stream.tsx +262 -0
  165. package/src/streamplace-store/streamplace-store.tsx +89 -0
  166. package/src/streamplace-store/user.tsx +57 -0
  167. package/src/streamplace-store/xrpc.tsx +15 -0
  168. package/tsconfig.json +9 -0
  169. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,248 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PortalHost } from "@rn-primitives/portal";
3
+ import { createContext, useContext, useMemo, useState, } from "react";
4
+ import { Platform, useColorScheme } from "react-native";
5
+ import { animations, borderRadius, colors, shadows, spacing, touchTargets, typography, } from "./tokens";
6
+ import { GestureHandlerRootView } from "react-native-gesture-handler";
7
+ // Create theme colors based on dark mode
8
+ const createThemeColors = (isDark) => ({
9
+ background: isDark ? colors.gray[950] : colors.white,
10
+ foreground: isDark ? colors.gray[50] : colors.gray[950],
11
+ card: isDark ? colors.gray[900] : colors.white,
12
+ cardForeground: isDark ? colors.gray[50] : colors.gray[950],
13
+ popover: isDark ? colors.gray[900] : colors.white,
14
+ popoverForeground: isDark ? colors.gray[50] : colors.gray[950],
15
+ primary: Platform.OS === "ios" ? colors.ios.systemBlue : colors.primary[500],
16
+ primaryForeground: colors.white,
17
+ secondary: isDark ? colors.gray[800] : colors.gray[100],
18
+ secondaryForeground: isDark ? colors.gray[50] : colors.gray[900],
19
+ muted: isDark ? colors.gray[800] : colors.gray[100],
20
+ mutedForeground: isDark ? colors.gray[400] : colors.gray[500],
21
+ accent: isDark ? colors.gray[800] : colors.gray[100],
22
+ accentForeground: isDark ? colors.gray[50] : colors.gray[900],
23
+ destructive: Platform.OS === "ios" ? colors.ios.systemRed : colors.destructive[500],
24
+ destructiveForeground: colors.white,
25
+ success: Platform.OS === "ios" ? colors.ios.systemGreen : colors.success[500],
26
+ successForeground: colors.white,
27
+ warning: Platform.OS === "ios" ? colors.ios.systemOrange : colors.warning[500],
28
+ warningForeground: colors.white,
29
+ border: isDark ? colors.gray[500] + "30" : colors.gray[200] + "30",
30
+ input: isDark ? colors.gray[800] : colors.gray[200],
31
+ ring: Platform.OS === "ios" ? colors.ios.systemBlue : colors.primary[500],
32
+ text: isDark ? colors.gray[50] : colors.gray[950],
33
+ textMuted: isDark ? colors.gray[400] : colors.gray[500],
34
+ textDisabled: isDark ? colors.gray[600] : colors.gray[400],
35
+ });
36
+ // Create theme styles based on colors
37
+ const createThemeStyles = (themeColors) => ({
38
+ shadow: {
39
+ sm: shadows.sm,
40
+ md: shadows.md,
41
+ lg: shadows.lg,
42
+ xl: shadows.xl,
43
+ },
44
+ button: {
45
+ primary: {
46
+ backgroundColor: themeColors.primary,
47
+ borderWidth: 0,
48
+ ...shadows.sm,
49
+ },
50
+ secondary: {
51
+ backgroundColor: themeColors.secondary,
52
+ borderWidth: 0,
53
+ },
54
+ outline: {
55
+ backgroundColor: "transparent",
56
+ borderWidth: 1,
57
+ borderColor: themeColors.border,
58
+ },
59
+ ghost: {
60
+ backgroundColor: "transparent",
61
+ borderWidth: 0,
62
+ },
63
+ },
64
+ text: {
65
+ primary: {
66
+ color: themeColors.text,
67
+ },
68
+ muted: {
69
+ color: themeColors.textMuted,
70
+ },
71
+ disabled: {
72
+ color: themeColors.textDisabled,
73
+ },
74
+ },
75
+ input: {
76
+ base: {
77
+ backgroundColor: themeColors.background,
78
+ borderWidth: 1,
79
+ borderColor: themeColors.border,
80
+ borderRadius: borderRadius.md,
81
+ paddingHorizontal: spacing[3],
82
+ paddingVertical: spacing[3],
83
+ minHeight: touchTargets.minimum,
84
+ },
85
+ focused: {
86
+ borderColor: themeColors.ring,
87
+ borderWidth: 2,
88
+ },
89
+ error: {
90
+ borderColor: themeColors.destructive,
91
+ borderWidth: 2,
92
+ },
93
+ },
94
+ card: {
95
+ base: {
96
+ backgroundColor: themeColors.card,
97
+ borderRadius: borderRadius.lg,
98
+ ...shadows.sm,
99
+ },
100
+ },
101
+ });
102
+ // Create theme icons based on colors
103
+ const createThemeIcons = (themeColors) => ({
104
+ color: {
105
+ default: themeColors.text,
106
+ muted: themeColors.textMuted,
107
+ primary: themeColors.primary,
108
+ secondary: themeColors.secondary,
109
+ destructive: themeColors.destructive,
110
+ success: themeColors.success,
111
+ warning: themeColors.warning,
112
+ },
113
+ size: {
114
+ sm: 16,
115
+ md: 20,
116
+ lg: 24,
117
+ xl: 32,
118
+ },
119
+ });
120
+ // Create the theme context
121
+ const ThemeContext = createContext(null);
122
+ // Theme provider component
123
+ export function ThemeProvider({ children, defaultTheme = "system", forcedTheme, }) {
124
+ const systemColorScheme = useColorScheme();
125
+ const [currentTheme, setCurrentTheme] = useState(defaultTheme);
126
+ // Determine if dark mode should be active
127
+ const isDark = useMemo(() => {
128
+ if (forcedTheme === "light")
129
+ return false;
130
+ if (forcedTheme === "dark")
131
+ return true;
132
+ if (currentTheme === "light")
133
+ return false;
134
+ if (currentTheme === "dark")
135
+ return true;
136
+ if (currentTheme === "system")
137
+ return systemColorScheme === "dark";
138
+ return systemColorScheme === "dark";
139
+ }, [forcedTheme, currentTheme, systemColorScheme]);
140
+ // Create theme based on dark mode
141
+ const theme = useMemo(() => {
142
+ const themeColors = createThemeColors(isDark);
143
+ return {
144
+ colors: themeColors,
145
+ spacing,
146
+ borderRadius,
147
+ typography,
148
+ shadows,
149
+ touchTargets,
150
+ animations,
151
+ };
152
+ }, [isDark]);
153
+ // Create utility styles
154
+ const styles = useMemo(() => {
155
+ return createThemeStyles(theme.colors);
156
+ }, [theme.colors]);
157
+ // Create icon utilities
158
+ const icons = useMemo(() => {
159
+ return createThemeIcons(theme.colors);
160
+ }, [theme.colors]);
161
+ // Theme controls
162
+ const setTheme = (newTheme) => {
163
+ if (!forcedTheme) {
164
+ setCurrentTheme(newTheme);
165
+ }
166
+ };
167
+ const toggleTheme = () => {
168
+ if (!forcedTheme) {
169
+ setCurrentTheme((prev) => {
170
+ if (prev === "light")
171
+ return "dark";
172
+ if (prev === "dark")
173
+ return "system";
174
+ return "light";
175
+ });
176
+ }
177
+ };
178
+ const value = useMemo(() => ({
179
+ theme,
180
+ styles,
181
+ icons,
182
+ isDark,
183
+ currentTheme: forcedTheme || currentTheme,
184
+ systemTheme: systemColorScheme || "light",
185
+ setTheme,
186
+ toggleTheme,
187
+ }), [
188
+ theme,
189
+ styles,
190
+ icons,
191
+ isDark,
192
+ forcedTheme,
193
+ currentTheme,
194
+ systemColorScheme,
195
+ setTheme,
196
+ toggleTheme,
197
+ ]);
198
+ return (_jsx(ThemeContext.Provider, { value: value, children: _jsxs(GestureHandlerRootView, { children: [children, _jsx(PortalHost, {})] }) }));
199
+ }
200
+ // Hook to use theme
201
+ export function useTheme() {
202
+ const context = useContext(ThemeContext);
203
+ if (!context) {
204
+ throw new Error("useTheme must be used within a ThemeProvider");
205
+ }
206
+ return context;
207
+ }
208
+ // Hook to get current platform's typography
209
+ export function usePlatformTypography() {
210
+ const { theme } = useTheme();
211
+ return useMemo(() => {
212
+ if (Platform.OS === "ios") {
213
+ return theme.typography.ios;
214
+ }
215
+ else if (Platform.OS === "android") {
216
+ return theme.typography.android;
217
+ }
218
+ return theme.typography.universal;
219
+ }, [theme.typography]);
220
+ }
221
+ // Utility function to create theme-aware styles
222
+ export function createThemedStyles(styleCreator) {
223
+ return function useThemedStyles() {
224
+ const { theme, styles, icons } = useTheme();
225
+ return useMemo(() => styleCreator(theme, styles, icons), [theme, styles, icons]);
226
+ };
227
+ }
228
+ // Create light and dark theme instances for external use
229
+ export const lightTheme = {
230
+ colors: createThemeColors(false),
231
+ spacing,
232
+ borderRadius,
233
+ typography,
234
+ shadows,
235
+ touchTargets,
236
+ animations,
237
+ };
238
+ export const darkTheme = {
239
+ colors: createThemeColors(true),
240
+ spacing,
241
+ borderRadius,
242
+ typography,
243
+ shadows,
244
+ touchTargets,
245
+ animations,
246
+ };
247
+ // Export individual theme utilities for convenience
248
+ export { createThemeColors, createThemeIcons, createThemeStyles };
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Design tokens for React Native components
3
+ * Inspired by shadcn/ui but adapted for React Native styling
4
+ */
5
+ export const colors = {
6
+ // Primary colors
7
+ primary: {
8
+ 50: "#eff6ff",
9
+ 100: "#dbeafe",
10
+ 200: "#bfdbfe",
11
+ 300: "#93c5fd",
12
+ 400: "#60a5fa",
13
+ 500: "#3b82f6",
14
+ 600: "#2563eb",
15
+ 700: "#1d4ed8",
16
+ 800: "#1e40af",
17
+ 900: "#1e3a8a",
18
+ 950: "#172554",
19
+ },
20
+ // Grayscale
21
+ gray: {
22
+ 50: "#fafafa",
23
+ 100: "#f5f5f5",
24
+ 200: "#e5e5e5",
25
+ 300: "#d4d4d4",
26
+ 400: "#a3a3a3",
27
+ 500: "#737373",
28
+ 600: "#525252",
29
+ 700: "#404040",
30
+ 800: "#262626",
31
+ 900: "#171717",
32
+ 950: "#0d0d0d",
33
+ },
34
+ // Semantic colors
35
+ destructive: {
36
+ 50: "#fef2f2",
37
+ 100: "#fee2e2",
38
+ 200: "#fecaca",
39
+ 300: "#fca5a5",
40
+ 400: "#f87171",
41
+ 500: "#ef4444",
42
+ 600: "#dc2626",
43
+ 700: "#b91c1c",
44
+ 800: "#991b1b",
45
+ 900: "#7f1d1d",
46
+ 950: "#450a0a",
47
+ },
48
+ success: {
49
+ 50: "#f0fdf4",
50
+ 100: "#dcfce7",
51
+ 200: "#bbf7d0",
52
+ 300: "#86efac",
53
+ 400: "#4ade80",
54
+ 500: "#22c55e",
55
+ 600: "#16a34a",
56
+ 700: "#15803d",
57
+ 800: "#166534",
58
+ 900: "#14532d",
59
+ 950: "#052e16",
60
+ },
61
+ warning: {
62
+ 50: "#fffbeb",
63
+ 100: "#fef3c7",
64
+ 200: "#fde68a",
65
+ 300: "#fcd34d",
66
+ 400: "#fbbf24",
67
+ 500: "#f59e0b",
68
+ 600: "#d97706",
69
+ 700: "#b45309",
70
+ 800: "#92400e",
71
+ 900: "#78350f",
72
+ 950: "#451a03",
73
+ },
74
+ // iOS system colors (adaptive)
75
+ ios: {
76
+ systemBlue: "#007AFF",
77
+ systemGreen: "#34C759",
78
+ systemRed: "#FF3B30",
79
+ systemOrange: "#FF9500",
80
+ systemYellow: "#FFCC00",
81
+ systemPurple: "#AF52DE",
82
+ systemPink: "#FF2D92",
83
+ systemTeal: "#5AC8FA",
84
+ systemIndigo: "#5856D6",
85
+ systemGray: "#8E8E93",
86
+ systemGray2: "#AEAEB2",
87
+ systemGray3: "#C7C7CC",
88
+ systemGray4: "#D1D1D6",
89
+ systemGray5: "#E5E5EA",
90
+ systemGray6: "#F2F2F7",
91
+ },
92
+ // Android Material colors
93
+ android: {
94
+ primary: "#6200EE",
95
+ primaryVariant: "#3700B3",
96
+ secondary: "#03DAC6",
97
+ secondaryVariant: "#018786",
98
+ background: "#FFFFFF",
99
+ surface: "#FFFFFF",
100
+ error: "#B00020",
101
+ onPrimary: "#FFFFFF",
102
+ onSecondary: "#000000",
103
+ onBackground: "#000000",
104
+ onSurface: "#000000",
105
+ onError: "#FFFFFF",
106
+ },
107
+ // Transparent colors
108
+ transparent: "transparent",
109
+ black: "#000000",
110
+ white: "#FFFFFF",
111
+ };
112
+ export const spacing = {
113
+ 0: 0,
114
+ 1: 4,
115
+ 2: 8,
116
+ 3: 12,
117
+ 4: 16,
118
+ 5: 20,
119
+ 6: 24,
120
+ 7: 28,
121
+ 8: 32,
122
+ 9: 36,
123
+ 10: 40,
124
+ 11: 44,
125
+ 12: 48,
126
+ 14: 56,
127
+ 16: 64,
128
+ 20: 80,
129
+ 24: 96,
130
+ 28: 112,
131
+ 32: 128,
132
+ 36: 144,
133
+ 40: 160,
134
+ 44: 176,
135
+ 48: 192,
136
+ 52: 208,
137
+ 56: 224,
138
+ 60: 240,
139
+ 64: 256,
140
+ 72: 288,
141
+ 80: 320,
142
+ 96: 384,
143
+ auto: "auto",
144
+ };
145
+ export const borderRadius = {
146
+ none: 0,
147
+ sm: 4,
148
+ md: 8,
149
+ lg: 12,
150
+ xl: 16,
151
+ "2xl": 20,
152
+ "3xl": 24,
153
+ full: 999,
154
+ };
155
+ export const typography = {
156
+ // iOS system font sizes
157
+ ios: {
158
+ largeTitle: {
159
+ fontSize: 34,
160
+ lineHeight: 41,
161
+ fontWeight: "700",
162
+ },
163
+ title1: {
164
+ fontSize: 28,
165
+ lineHeight: 34,
166
+ fontWeight: "700",
167
+ },
168
+ title2: {
169
+ fontSize: 22,
170
+ lineHeight: 28,
171
+ fontWeight: "700",
172
+ },
173
+ title3: {
174
+ fontSize: 20,
175
+ lineHeight: 25,
176
+ fontWeight: "600",
177
+ },
178
+ headline: {
179
+ fontSize: 17,
180
+ lineHeight: 22,
181
+ fontWeight: "600",
182
+ },
183
+ body: {
184
+ fontSize: 17,
185
+ lineHeight: 22,
186
+ fontWeight: "400",
187
+ },
188
+ callout: {
189
+ fontSize: 16,
190
+ lineHeight: 21,
191
+ fontWeight: "400",
192
+ },
193
+ subhead: {
194
+ fontSize: 15,
195
+ lineHeight: 20,
196
+ fontWeight: "400",
197
+ },
198
+ footnote: {
199
+ fontSize: 13,
200
+ lineHeight: 18,
201
+ fontWeight: "400",
202
+ },
203
+ caption1: {
204
+ fontSize: 12,
205
+ lineHeight: 16,
206
+ fontWeight: "400",
207
+ },
208
+ caption2: {
209
+ fontSize: 11,
210
+ lineHeight: 13,
211
+ fontWeight: "400",
212
+ },
213
+ },
214
+ // Android Material typography
215
+ android: {
216
+ headline1: {
217
+ fontSize: 96,
218
+ lineHeight: 112,
219
+ fontWeight: "300",
220
+ },
221
+ headline2: {
222
+ fontSize: 60,
223
+ lineHeight: 72,
224
+ fontWeight: "300",
225
+ },
226
+ headline3: {
227
+ fontSize: 48,
228
+ lineHeight: 56,
229
+ fontWeight: "400",
230
+ },
231
+ headline4: {
232
+ fontSize: 34,
233
+ lineHeight: 42,
234
+ fontWeight: "400",
235
+ },
236
+ headline5: {
237
+ fontSize: 24,
238
+ lineHeight: 32,
239
+ fontWeight: "400",
240
+ },
241
+ headline6: {
242
+ fontSize: 20,
243
+ lineHeight: 28,
244
+ fontWeight: "500",
245
+ },
246
+ subtitle1: {
247
+ fontSize: 16,
248
+ lineHeight: 24,
249
+ fontWeight: "400",
250
+ },
251
+ subtitle2: {
252
+ fontSize: 14,
253
+ lineHeight: 22,
254
+ fontWeight: "500",
255
+ },
256
+ body1: {
257
+ fontSize: 16,
258
+ lineHeight: 24,
259
+ fontWeight: "400",
260
+ },
261
+ body2: {
262
+ fontSize: 14,
263
+ lineHeight: 20,
264
+ fontWeight: "400",
265
+ },
266
+ button: {
267
+ fontSize: 14,
268
+ lineHeight: 16,
269
+ fontWeight: "500",
270
+ },
271
+ caption: {
272
+ fontSize: 12,
273
+ lineHeight: 16,
274
+ fontWeight: "400",
275
+ },
276
+ overline: {
277
+ fontSize: 10,
278
+ lineHeight: 16,
279
+ fontWeight: "400",
280
+ },
281
+ },
282
+ // Universal typography scale
283
+ universal: {
284
+ xs: {
285
+ fontSize: 12,
286
+ lineHeight: 16,
287
+ fontWeight: "400",
288
+ },
289
+ sm: {
290
+ fontSize: 14,
291
+ lineHeight: 20,
292
+ fontWeight: "400",
293
+ },
294
+ base: {
295
+ fontSize: 16,
296
+ lineHeight: 24,
297
+ fontWeight: "400",
298
+ },
299
+ lg: {
300
+ fontSize: 18,
301
+ lineHeight: 28,
302
+ fontWeight: "400",
303
+ },
304
+ xl: {
305
+ fontSize: 20,
306
+ lineHeight: 28,
307
+ fontWeight: "500",
308
+ },
309
+ "2xl": {
310
+ fontSize: 24,
311
+ lineHeight: 32,
312
+ fontWeight: "600",
313
+ },
314
+ "3xl": {
315
+ fontSize: 30,
316
+ lineHeight: 36,
317
+ fontWeight: "700",
318
+ },
319
+ "4xl": {
320
+ fontSize: 36,
321
+ lineHeight: 40,
322
+ fontWeight: "700",
323
+ },
324
+ },
325
+ };
326
+ export const shadows = {
327
+ none: {
328
+ shadowColor: "transparent",
329
+ shadowOffset: { width: 0, height: 0 },
330
+ shadowOpacity: 0,
331
+ shadowRadius: 0,
332
+ elevation: 0,
333
+ },
334
+ sm: {
335
+ shadowColor: colors.black,
336
+ shadowOffset: { width: 0, height: 1 },
337
+ shadowOpacity: 0.05,
338
+ shadowRadius: 2,
339
+ elevation: 2,
340
+ },
341
+ md: {
342
+ shadowColor: colors.black,
343
+ shadowOffset: { width: 0, height: 2 },
344
+ shadowOpacity: 0.1,
345
+ shadowRadius: 4,
346
+ elevation: 4,
347
+ },
348
+ lg: {
349
+ shadowColor: colors.black,
350
+ shadowOffset: { width: 0, height: 4 },
351
+ shadowOpacity: 0.15,
352
+ shadowRadius: 8,
353
+ elevation: 8,
354
+ },
355
+ xl: {
356
+ shadowColor: colors.black,
357
+ shadowOffset: { width: 0, height: 8 },
358
+ shadowOpacity: 0.2,
359
+ shadowRadius: 16,
360
+ elevation: 16,
361
+ },
362
+ };
363
+ // Touch targets (iOS Human Interface Guidelines)
364
+ export const touchTargets = {
365
+ minimum: 44, // Minimum touch target size
366
+ comfortable: 48, // Comfortable touch target size
367
+ large: 56, // Large touch target size
368
+ };
369
+ // Animation durations
370
+ export const animations = {
371
+ fast: 150,
372
+ normal: 200,
373
+ slow: 300,
374
+ slower: 500,
375
+ };
376
+ // Breakpoints for responsive design
377
+ export const breakpoints = {
378
+ sm: 640,
379
+ md: 768,
380
+ lg: 1024,
381
+ xl: 1280,
382
+ "2xl": 1536,
383
+ };
@@ -0,0 +1,94 @@
1
+ import { StyleSheet } from "react-native";
2
+ /**
3
+ * Merges React Native styles similar to how cn() merges CSS classes
4
+ * Handles arrays, objects, and falsy values
5
+ */
6
+ export function mergeStyles(...styles) {
7
+ const validStyles = styles.filter(Boolean).flat();
8
+ return StyleSheet.flatten(validStyles) || {};
9
+ }
10
+ /**
11
+ * Creates a style merger function that includes base styles
12
+ * Useful for component variants
13
+ */
14
+ export function createStyleMerger(baseStyle) {
15
+ return (...styles) => {
16
+ return mergeStyles(baseStyle, ...styles);
17
+ };
18
+ }
19
+ /**
20
+ * Conditionally applies styles based on boolean conditions
21
+ */
22
+ export function conditionalStyle(condition, trueStyle, falseStyle) {
23
+ return condition ? trueStyle : falseStyle;
24
+ }
25
+ /**
26
+ * Creates responsive values based on screen dimensions
27
+ */
28
+ export function responsiveValue(values, screenWidth) {
29
+ if (screenWidth >= 1280 && values.xl !== undefined)
30
+ return values.xl;
31
+ if (screenWidth >= 1024 && values.lg !== undefined)
32
+ return values.lg;
33
+ if (screenWidth >= 768 && values.md !== undefined)
34
+ return values.md;
35
+ if (screenWidth >= 640 && values.sm !== undefined)
36
+ return values.sm;
37
+ return values.default;
38
+ }
39
+ /**
40
+ * Creates platform-specific styles
41
+ */
42
+ export function platformStyle(styles) {
43
+ const Platform = require("react-native").Platform;
44
+ if (Platform.OS === "ios" && styles.ios)
45
+ return styles.ios;
46
+ if (Platform.OS === "android" && styles.android)
47
+ return styles.android;
48
+ if (Platform.OS === "web" && styles.web)
49
+ return styles.web;
50
+ return styles.default || {};
51
+ }
52
+ /**
53
+ * Converts hex color to rgba
54
+ */
55
+ export function hexToRgba(hex, alpha = 1) {
56
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
57
+ if (!result)
58
+ return hex;
59
+ const r = parseInt(result[1], 16);
60
+ const g = parseInt(result[2], 16);
61
+ const b = parseInt(result[3], 16);
62
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
63
+ }
64
+ /**
65
+ * Creates a debounced function for performance
66
+ */
67
+ export function debounce(func, delay) {
68
+ let timeoutId;
69
+ return (...args) => {
70
+ clearTimeout(timeoutId);
71
+ timeoutId = setTimeout(() => func(...args), delay);
72
+ };
73
+ }
74
+ /**
75
+ * Creates a throttled function for performance
76
+ */
77
+ export function throttle(func, delay) {
78
+ let lastCall = 0;
79
+ return (...args) => {
80
+ const now = Date.now();
81
+ if (now - lastCall >= delay) {
82
+ lastCall = now;
83
+ func(...args);
84
+ }
85
+ };
86
+ }
87
+ /**
88
+ * Type-safe component prop forwarding
89
+ */
90
+ export function forwardProps(props, omit) {
91
+ const result = { ...props };
92
+ omit.forEach((key) => delete result[key]);
93
+ return result;
94
+ }