@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.
- package/LICENSE +18 -0
- package/README.md +35 -0
- package/dist/components/chat/chat-box.js +109 -0
- package/dist/components/chat/chat-message.js +76 -0
- package/dist/components/chat/chat.js +56 -0
- package/dist/components/chat/mention-suggestions.js +39 -0
- package/dist/components/chat/mod-view.js +33 -0
- package/dist/components/mobile-player/fullscreen.js +69 -0
- package/dist/components/mobile-player/fullscreen.native.js +151 -0
- package/dist/components/mobile-player/player.js +103 -0
- package/dist/components/mobile-player/props.js +1 -0
- package/dist/components/mobile-player/shared.js +51 -0
- package/dist/components/mobile-player/ui/countdown.js +79 -0
- package/dist/components/mobile-player/ui/index.js +5 -0
- package/dist/components/mobile-player/ui/input.js +38 -0
- package/dist/components/mobile-player/ui/metrics.js +40 -0
- package/dist/components/mobile-player/ui/streamer-context-menu.js +4 -0
- package/dist/components/mobile-player/ui/viewer-context-menu.js +20 -0
- package/dist/components/mobile-player/use-webrtc.js +232 -0
- package/dist/components/mobile-player/video.js +375 -0
- package/dist/components/mobile-player/video.native.js +238 -0
- package/dist/components/mobile-player/webrtc-diagnostics.js +106 -0
- package/dist/components/mobile-player/webrtc-primitives.js +25 -0
- package/dist/components/mobile-player/webrtc-primitives.native.js +1 -0
- package/dist/components/ui/button.js +220 -0
- package/dist/components/ui/dialog.js +203 -0
- package/dist/components/ui/dropdown.js +148 -0
- package/dist/components/ui/icons.js +22 -0
- package/dist/components/ui/index.js +22 -0
- package/dist/components/ui/input.js +202 -0
- package/dist/components/ui/loader.js +7 -0
- package/dist/components/ui/primitives/button.js +121 -0
- package/dist/components/ui/primitives/input.js +202 -0
- package/dist/components/ui/primitives/modal.js +203 -0
- package/dist/components/ui/primitives/text.js +286 -0
- package/dist/components/ui/resizeable.js +101 -0
- package/dist/components/ui/text.js +175 -0
- package/dist/components/ui/textarea.js +17 -0
- package/dist/components/ui/toast.js +129 -0
- package/dist/components/ui/view.js +250 -0
- package/dist/hooks/index.js +9 -0
- package/dist/hooks/useAvatars.js +32 -0
- package/dist/hooks/useCameraToggle.js +9 -0
- package/dist/hooks/useKeyboard.js +33 -0
- package/dist/hooks/useKeyboardSlide.js +11 -0
- package/dist/hooks/useLivestreamInfo.js +62 -0
- package/dist/hooks/useOuterAndInnerDimensions.js +27 -0
- package/dist/hooks/usePlayerDimensions.js +19 -0
- package/dist/hooks/useSegmentTiming.js +62 -0
- package/dist/index.js +16 -0
- package/dist/lib/facet.js +88 -0
- package/dist/lib/theme/atoms.js +620 -0
- package/dist/lib/theme/atoms.types.js +5 -0
- package/dist/lib/theme/index.js +9 -0
- package/dist/lib/theme/theme.js +248 -0
- package/dist/lib/theme/tokens.js +383 -0
- package/dist/lib/utils.js +94 -0
- package/dist/livestream-provider/index.js +25 -0
- package/dist/livestream-provider/websocket.js +41 -0
- package/dist/livestream-store/chat.js +186 -0
- package/dist/livestream-store/context.js +2 -0
- package/dist/livestream-store/index.js +4 -0
- package/dist/livestream-store/livestream-state.js +1 -0
- package/dist/livestream-store/livestream-store.js +42 -0
- package/dist/livestream-store/stream-key.js +115 -0
- package/dist/livestream-store/websocket-consumer.js +55 -0
- package/dist/player-store/context.js +2 -0
- package/dist/player-store/index.js +6 -0
- package/dist/player-store/player-provider.js +52 -0
- package/dist/player-store/player-state.js +22 -0
- package/dist/player-store/player-store.js +159 -0
- package/dist/player-store/single-player-provider.js +109 -0
- package/dist/streamplace-provider/context.js +2 -0
- package/dist/streamplace-provider/index.js +16 -0
- package/dist/streamplace-provider/poller.js +46 -0
- package/dist/streamplace-provider/xrpc.js +0 -0
- package/dist/streamplace-store/block.js +23 -0
- package/dist/streamplace-store/index.js +3 -0
- package/dist/streamplace-store/stream.js +193 -0
- package/dist/streamplace-store/streamplace-store.js +37 -0
- package/dist/streamplace-store/user.js +47 -0
- package/dist/streamplace-store/xrpc.js +12 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/56540125 +0 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/67b1eb60 +0 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/7c275f90 +0 -0
- package/package.json +50 -8
- package/src/components/chat/chat-box.tsx +195 -0
- package/src/components/chat/chat-message.tsx +192 -0
- package/src/components/chat/chat.tsx +128 -0
- package/src/components/chat/mention-suggestions.tsx +71 -0
- package/src/components/chat/mod-view.tsx +118 -0
- package/src/components/mobile-player/fullscreen.native.tsx +193 -0
- package/src/components/mobile-player/fullscreen.tsx +79 -0
- package/src/components/mobile-player/player.tsx +134 -0
- package/src/components/mobile-player/props.tsx +11 -0
- package/src/components/mobile-player/shared.tsx +56 -0
- package/src/components/mobile-player/ui/countdown.tsx +119 -0
- package/src/components/mobile-player/ui/index.ts +5 -0
- package/src/components/mobile-player/ui/input.tsx +85 -0
- package/src/components/mobile-player/ui/metrics.tsx +69 -0
- package/src/components/mobile-player/ui/streamer-context-menu.tsx +3 -0
- package/src/components/mobile-player/ui/viewer-context-menu.tsx +70 -0
- package/src/components/mobile-player/use-webrtc.tsx +282 -0
- package/src/components/mobile-player/video.native.tsx +360 -0
- package/src/components/mobile-player/video.tsx +557 -0
- package/src/components/mobile-player/webrtc-diagnostics.tsx +149 -0
- package/src/components/mobile-player/webrtc-primitives.native.tsx +6 -0
- package/src/components/mobile-player/webrtc-primitives.tsx +33 -0
- package/src/components/ui/button.tsx +309 -0
- package/src/components/ui/dialog.tsx +376 -0
- package/src/components/ui/dropdown.tsx +399 -0
- package/src/components/ui/icons.tsx +50 -0
- package/src/components/ui/index.ts +33 -0
- package/src/components/ui/input.tsx +350 -0
- package/src/components/ui/loader.tsx +9 -0
- package/src/components/ui/primitives/button.tsx +292 -0
- package/src/components/ui/primitives/input.tsx +422 -0
- package/src/components/ui/primitives/modal.tsx +421 -0
- package/src/components/ui/primitives/text.tsx +499 -0
- package/src/components/ui/resizeable.tsx +169 -0
- package/src/components/ui/text.tsx +330 -0
- package/src/components/ui/textarea.tsx +34 -0
- package/src/components/ui/toast.tsx +203 -0
- package/src/components/ui/view.tsx +344 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/useAvatars.tsx +44 -0
- package/src/hooks/useCameraToggle.ts +12 -0
- package/src/hooks/useKeyboard.tsx +41 -0
- package/src/hooks/useKeyboardSlide.ts +12 -0
- package/src/hooks/useLivestreamInfo.ts +67 -0
- package/src/hooks/useOuterAndInnerDimensions.tsx +32 -0
- package/src/hooks/usePlayerDimensions.ts +23 -0
- package/src/hooks/useSegmentTiming.tsx +88 -0
- package/src/index.tsx +27 -0
- package/src/lib/facet.ts +131 -0
- package/src/lib/theme/atoms.ts +760 -0
- package/src/lib/theme/atoms.types.ts +258 -0
- package/src/lib/theme/index.ts +48 -0
- package/src/lib/theme/theme.tsx +436 -0
- package/src/lib/theme/tokens.ts +409 -0
- package/src/lib/utils.ts +132 -0
- package/src/livestream-provider/index.tsx +48 -0
- package/src/livestream-provider/websocket.tsx +47 -0
- package/src/livestream-store/chat.tsx +261 -0
- package/src/livestream-store/context.tsx +10 -0
- package/src/livestream-store/index.tsx +4 -0
- package/src/livestream-store/livestream-state.tsx +21 -0
- package/src/livestream-store/livestream-store.tsx +59 -0
- package/src/livestream-store/stream-key.tsx +124 -0
- package/src/livestream-store/websocket-consumer.tsx +62 -0
- package/src/player-store/context.tsx +11 -0
- package/src/player-store/index.tsx +6 -0
- package/src/player-store/player-provider.tsx +89 -0
- package/src/player-store/player-state.tsx +187 -0
- package/src/player-store/player-store.tsx +239 -0
- package/src/player-store/single-player-provider.tsx +181 -0
- package/src/streamplace-provider/context.tsx +10 -0
- package/src/streamplace-provider/index.tsx +32 -0
- package/src/streamplace-provider/poller.tsx +55 -0
- package/src/streamplace-provider/xrpc.tsx +0 -0
- package/src/streamplace-store/block.tsx +29 -0
- package/src/streamplace-store/index.tsx +3 -0
- package/src/streamplace-store/stream.tsx +262 -0
- package/src/streamplace-store/streamplace-store.tsx +89 -0
- package/src/streamplace-store/user.tsx +57 -0
- package/src/streamplace-store/xrpc.tsx +15 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design tokens for React Native components
|
|
3
|
+
* Inspired by shadcn/ui but adapted for React Native styling
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const colors = {
|
|
7
|
+
// Primary colors
|
|
8
|
+
primary: {
|
|
9
|
+
50: "#eff6ff",
|
|
10
|
+
100: "#dbeafe",
|
|
11
|
+
200: "#bfdbfe",
|
|
12
|
+
300: "#93c5fd",
|
|
13
|
+
400: "#60a5fa",
|
|
14
|
+
500: "#3b82f6",
|
|
15
|
+
600: "#2563eb",
|
|
16
|
+
700: "#1d4ed8",
|
|
17
|
+
800: "#1e40af",
|
|
18
|
+
900: "#1e3a8a",
|
|
19
|
+
950: "#172554",
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// Grayscale
|
|
23
|
+
gray: {
|
|
24
|
+
50: "#fafafa",
|
|
25
|
+
100: "#f5f5f5",
|
|
26
|
+
200: "#e5e5e5",
|
|
27
|
+
300: "#d4d4d4",
|
|
28
|
+
400: "#a3a3a3",
|
|
29
|
+
500: "#737373",
|
|
30
|
+
600: "#525252",
|
|
31
|
+
700: "#404040",
|
|
32
|
+
800: "#262626",
|
|
33
|
+
900: "#171717",
|
|
34
|
+
950: "#0d0d0d",
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
// Semantic colors
|
|
38
|
+
destructive: {
|
|
39
|
+
50: "#fef2f2",
|
|
40
|
+
100: "#fee2e2",
|
|
41
|
+
200: "#fecaca",
|
|
42
|
+
300: "#fca5a5",
|
|
43
|
+
400: "#f87171",
|
|
44
|
+
500: "#ef4444",
|
|
45
|
+
600: "#dc2626",
|
|
46
|
+
700: "#b91c1c",
|
|
47
|
+
800: "#991b1b",
|
|
48
|
+
900: "#7f1d1d",
|
|
49
|
+
950: "#450a0a",
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
success: {
|
|
53
|
+
50: "#f0fdf4",
|
|
54
|
+
100: "#dcfce7",
|
|
55
|
+
200: "#bbf7d0",
|
|
56
|
+
300: "#86efac",
|
|
57
|
+
400: "#4ade80",
|
|
58
|
+
500: "#22c55e",
|
|
59
|
+
600: "#16a34a",
|
|
60
|
+
700: "#15803d",
|
|
61
|
+
800: "#166534",
|
|
62
|
+
900: "#14532d",
|
|
63
|
+
950: "#052e16",
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
warning: {
|
|
67
|
+
50: "#fffbeb",
|
|
68
|
+
100: "#fef3c7",
|
|
69
|
+
200: "#fde68a",
|
|
70
|
+
300: "#fcd34d",
|
|
71
|
+
400: "#fbbf24",
|
|
72
|
+
500: "#f59e0b",
|
|
73
|
+
600: "#d97706",
|
|
74
|
+
700: "#b45309",
|
|
75
|
+
800: "#92400e",
|
|
76
|
+
900: "#78350f",
|
|
77
|
+
950: "#451a03",
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// iOS system colors (adaptive)
|
|
81
|
+
ios: {
|
|
82
|
+
systemBlue: "#007AFF",
|
|
83
|
+
systemGreen: "#34C759",
|
|
84
|
+
systemRed: "#FF3B30",
|
|
85
|
+
systemOrange: "#FF9500",
|
|
86
|
+
systemYellow: "#FFCC00",
|
|
87
|
+
systemPurple: "#AF52DE",
|
|
88
|
+
systemPink: "#FF2D92",
|
|
89
|
+
systemTeal: "#5AC8FA",
|
|
90
|
+
systemIndigo: "#5856D6",
|
|
91
|
+
systemGray: "#8E8E93",
|
|
92
|
+
systemGray2: "#AEAEB2",
|
|
93
|
+
systemGray3: "#C7C7CC",
|
|
94
|
+
systemGray4: "#D1D1D6",
|
|
95
|
+
systemGray5: "#E5E5EA",
|
|
96
|
+
systemGray6: "#F2F2F7",
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
// Android Material colors
|
|
100
|
+
android: {
|
|
101
|
+
primary: "#6200EE",
|
|
102
|
+
primaryVariant: "#3700B3",
|
|
103
|
+
secondary: "#03DAC6",
|
|
104
|
+
secondaryVariant: "#018786",
|
|
105
|
+
background: "#FFFFFF",
|
|
106
|
+
surface: "#FFFFFF",
|
|
107
|
+
error: "#B00020",
|
|
108
|
+
onPrimary: "#FFFFFF",
|
|
109
|
+
onSecondary: "#000000",
|
|
110
|
+
onBackground: "#000000",
|
|
111
|
+
onSurface: "#000000",
|
|
112
|
+
onError: "#FFFFFF",
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// Transparent colors
|
|
116
|
+
transparent: "transparent",
|
|
117
|
+
black: "#000000",
|
|
118
|
+
white: "#FFFFFF",
|
|
119
|
+
} as const;
|
|
120
|
+
|
|
121
|
+
export const spacing = {
|
|
122
|
+
0: 0,
|
|
123
|
+
1: 4,
|
|
124
|
+
2: 8,
|
|
125
|
+
3: 12,
|
|
126
|
+
4: 16,
|
|
127
|
+
5: 20,
|
|
128
|
+
6: 24,
|
|
129
|
+
7: 28,
|
|
130
|
+
8: 32,
|
|
131
|
+
9: 36,
|
|
132
|
+
10: 40,
|
|
133
|
+
11: 44,
|
|
134
|
+
12: 48,
|
|
135
|
+
14: 56,
|
|
136
|
+
16: 64,
|
|
137
|
+
20: 80,
|
|
138
|
+
24: 96,
|
|
139
|
+
28: 112,
|
|
140
|
+
32: 128,
|
|
141
|
+
36: 144,
|
|
142
|
+
40: 160,
|
|
143
|
+
44: 176,
|
|
144
|
+
48: 192,
|
|
145
|
+
52: 208,
|
|
146
|
+
56: 224,
|
|
147
|
+
60: 240,
|
|
148
|
+
64: 256,
|
|
149
|
+
72: 288,
|
|
150
|
+
80: 320,
|
|
151
|
+
96: 384,
|
|
152
|
+
auto: "auto",
|
|
153
|
+
} as const;
|
|
154
|
+
|
|
155
|
+
export const borderRadius = {
|
|
156
|
+
none: 0,
|
|
157
|
+
sm: 4,
|
|
158
|
+
md: 8,
|
|
159
|
+
lg: 12,
|
|
160
|
+
xl: 16,
|
|
161
|
+
"2xl": 20,
|
|
162
|
+
"3xl": 24,
|
|
163
|
+
full: 999,
|
|
164
|
+
} as const;
|
|
165
|
+
|
|
166
|
+
export const typography = {
|
|
167
|
+
// iOS system font sizes
|
|
168
|
+
ios: {
|
|
169
|
+
largeTitle: {
|
|
170
|
+
fontSize: 34,
|
|
171
|
+
lineHeight: 41,
|
|
172
|
+
fontWeight: "700" as const,
|
|
173
|
+
},
|
|
174
|
+
title1: {
|
|
175
|
+
fontSize: 28,
|
|
176
|
+
lineHeight: 34,
|
|
177
|
+
fontWeight: "700" as const,
|
|
178
|
+
},
|
|
179
|
+
title2: {
|
|
180
|
+
fontSize: 22,
|
|
181
|
+
lineHeight: 28,
|
|
182
|
+
fontWeight: "700" as const,
|
|
183
|
+
},
|
|
184
|
+
title3: {
|
|
185
|
+
fontSize: 20,
|
|
186
|
+
lineHeight: 25,
|
|
187
|
+
fontWeight: "600" as const,
|
|
188
|
+
},
|
|
189
|
+
headline: {
|
|
190
|
+
fontSize: 17,
|
|
191
|
+
lineHeight: 22,
|
|
192
|
+
fontWeight: "600" as const,
|
|
193
|
+
},
|
|
194
|
+
body: {
|
|
195
|
+
fontSize: 17,
|
|
196
|
+
lineHeight: 22,
|
|
197
|
+
fontWeight: "400" as const,
|
|
198
|
+
},
|
|
199
|
+
callout: {
|
|
200
|
+
fontSize: 16,
|
|
201
|
+
lineHeight: 21,
|
|
202
|
+
fontWeight: "400" as const,
|
|
203
|
+
},
|
|
204
|
+
subhead: {
|
|
205
|
+
fontSize: 15,
|
|
206
|
+
lineHeight: 20,
|
|
207
|
+
fontWeight: "400" as const,
|
|
208
|
+
},
|
|
209
|
+
footnote: {
|
|
210
|
+
fontSize: 13,
|
|
211
|
+
lineHeight: 18,
|
|
212
|
+
fontWeight: "400" as const,
|
|
213
|
+
},
|
|
214
|
+
caption1: {
|
|
215
|
+
fontSize: 12,
|
|
216
|
+
lineHeight: 16,
|
|
217
|
+
fontWeight: "400" as const,
|
|
218
|
+
},
|
|
219
|
+
caption2: {
|
|
220
|
+
fontSize: 11,
|
|
221
|
+
lineHeight: 13,
|
|
222
|
+
fontWeight: "400" as const,
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
// Android Material typography
|
|
227
|
+
android: {
|
|
228
|
+
headline1: {
|
|
229
|
+
fontSize: 96,
|
|
230
|
+
lineHeight: 112,
|
|
231
|
+
fontWeight: "300" as const,
|
|
232
|
+
},
|
|
233
|
+
headline2: {
|
|
234
|
+
fontSize: 60,
|
|
235
|
+
lineHeight: 72,
|
|
236
|
+
fontWeight: "300" as const,
|
|
237
|
+
},
|
|
238
|
+
headline3: {
|
|
239
|
+
fontSize: 48,
|
|
240
|
+
lineHeight: 56,
|
|
241
|
+
fontWeight: "400" as const,
|
|
242
|
+
},
|
|
243
|
+
headline4: {
|
|
244
|
+
fontSize: 34,
|
|
245
|
+
lineHeight: 42,
|
|
246
|
+
fontWeight: "400" as const,
|
|
247
|
+
},
|
|
248
|
+
headline5: {
|
|
249
|
+
fontSize: 24,
|
|
250
|
+
lineHeight: 32,
|
|
251
|
+
fontWeight: "400" as const,
|
|
252
|
+
},
|
|
253
|
+
headline6: {
|
|
254
|
+
fontSize: 20,
|
|
255
|
+
lineHeight: 28,
|
|
256
|
+
fontWeight: "500" as const,
|
|
257
|
+
},
|
|
258
|
+
subtitle1: {
|
|
259
|
+
fontSize: 16,
|
|
260
|
+
lineHeight: 24,
|
|
261
|
+
fontWeight: "400" as const,
|
|
262
|
+
},
|
|
263
|
+
subtitle2: {
|
|
264
|
+
fontSize: 14,
|
|
265
|
+
lineHeight: 22,
|
|
266
|
+
fontWeight: "500" as const,
|
|
267
|
+
},
|
|
268
|
+
body1: {
|
|
269
|
+
fontSize: 16,
|
|
270
|
+
lineHeight: 24,
|
|
271
|
+
fontWeight: "400" as const,
|
|
272
|
+
},
|
|
273
|
+
body2: {
|
|
274
|
+
fontSize: 14,
|
|
275
|
+
lineHeight: 20,
|
|
276
|
+
fontWeight: "400" as const,
|
|
277
|
+
},
|
|
278
|
+
button: {
|
|
279
|
+
fontSize: 14,
|
|
280
|
+
lineHeight: 16,
|
|
281
|
+
fontWeight: "500" as const,
|
|
282
|
+
},
|
|
283
|
+
caption: {
|
|
284
|
+
fontSize: 12,
|
|
285
|
+
lineHeight: 16,
|
|
286
|
+
fontWeight: "400" as const,
|
|
287
|
+
},
|
|
288
|
+
overline: {
|
|
289
|
+
fontSize: 10,
|
|
290
|
+
lineHeight: 16,
|
|
291
|
+
fontWeight: "400" as const,
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
// Universal typography scale
|
|
296
|
+
universal: {
|
|
297
|
+
xs: {
|
|
298
|
+
fontSize: 12,
|
|
299
|
+
lineHeight: 16,
|
|
300
|
+
fontWeight: "400" as const,
|
|
301
|
+
},
|
|
302
|
+
sm: {
|
|
303
|
+
fontSize: 14,
|
|
304
|
+
lineHeight: 20,
|
|
305
|
+
fontWeight: "400" as const,
|
|
306
|
+
},
|
|
307
|
+
base: {
|
|
308
|
+
fontSize: 16,
|
|
309
|
+
lineHeight: 24,
|
|
310
|
+
fontWeight: "400" as const,
|
|
311
|
+
},
|
|
312
|
+
lg: {
|
|
313
|
+
fontSize: 18,
|
|
314
|
+
lineHeight: 28,
|
|
315
|
+
fontWeight: "400" as const,
|
|
316
|
+
},
|
|
317
|
+
xl: {
|
|
318
|
+
fontSize: 20,
|
|
319
|
+
lineHeight: 28,
|
|
320
|
+
fontWeight: "500" as const,
|
|
321
|
+
},
|
|
322
|
+
"2xl": {
|
|
323
|
+
fontSize: 24,
|
|
324
|
+
lineHeight: 32,
|
|
325
|
+
fontWeight: "600" as const,
|
|
326
|
+
},
|
|
327
|
+
"3xl": {
|
|
328
|
+
fontSize: 30,
|
|
329
|
+
lineHeight: 36,
|
|
330
|
+
fontWeight: "700" as const,
|
|
331
|
+
},
|
|
332
|
+
"4xl": {
|
|
333
|
+
fontSize: 36,
|
|
334
|
+
lineHeight: 40,
|
|
335
|
+
fontWeight: "700" as const,
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
} as const;
|
|
339
|
+
|
|
340
|
+
export const shadows = {
|
|
341
|
+
none: {
|
|
342
|
+
shadowColor: "transparent",
|
|
343
|
+
shadowOffset: { width: 0, height: 0 },
|
|
344
|
+
shadowOpacity: 0,
|
|
345
|
+
shadowRadius: 0,
|
|
346
|
+
elevation: 0,
|
|
347
|
+
},
|
|
348
|
+
sm: {
|
|
349
|
+
shadowColor: colors.black,
|
|
350
|
+
shadowOffset: { width: 0, height: 1 },
|
|
351
|
+
shadowOpacity: 0.05,
|
|
352
|
+
shadowRadius: 2,
|
|
353
|
+
elevation: 2,
|
|
354
|
+
},
|
|
355
|
+
md: {
|
|
356
|
+
shadowColor: colors.black,
|
|
357
|
+
shadowOffset: { width: 0, height: 2 },
|
|
358
|
+
shadowOpacity: 0.1,
|
|
359
|
+
shadowRadius: 4,
|
|
360
|
+
elevation: 4,
|
|
361
|
+
},
|
|
362
|
+
lg: {
|
|
363
|
+
shadowColor: colors.black,
|
|
364
|
+
shadowOffset: { width: 0, height: 4 },
|
|
365
|
+
shadowOpacity: 0.15,
|
|
366
|
+
shadowRadius: 8,
|
|
367
|
+
elevation: 8,
|
|
368
|
+
},
|
|
369
|
+
xl: {
|
|
370
|
+
shadowColor: colors.black,
|
|
371
|
+
shadowOffset: { width: 0, height: 8 },
|
|
372
|
+
shadowOpacity: 0.2,
|
|
373
|
+
shadowRadius: 16,
|
|
374
|
+
elevation: 16,
|
|
375
|
+
},
|
|
376
|
+
} as const;
|
|
377
|
+
|
|
378
|
+
// Touch targets (iOS Human Interface Guidelines)
|
|
379
|
+
export const touchTargets = {
|
|
380
|
+
minimum: 44, // Minimum touch target size
|
|
381
|
+
comfortable: 48, // Comfortable touch target size
|
|
382
|
+
large: 56, // Large touch target size
|
|
383
|
+
} as const;
|
|
384
|
+
|
|
385
|
+
// Animation durations
|
|
386
|
+
export const animations = {
|
|
387
|
+
fast: 150,
|
|
388
|
+
normal: 200,
|
|
389
|
+
slow: 300,
|
|
390
|
+
slower: 500,
|
|
391
|
+
} as const;
|
|
392
|
+
|
|
393
|
+
// Breakpoints for responsive design
|
|
394
|
+
export const breakpoints = {
|
|
395
|
+
sm: 640,
|
|
396
|
+
md: 768,
|
|
397
|
+
lg: 1024,
|
|
398
|
+
xl: 1280,
|
|
399
|
+
"2xl": 1536,
|
|
400
|
+
} as const;
|
|
401
|
+
|
|
402
|
+
export type Colors = typeof colors;
|
|
403
|
+
export type Spacing = typeof spacing;
|
|
404
|
+
export type BorderRadius = typeof borderRadius;
|
|
405
|
+
export type Typography = typeof typography;
|
|
406
|
+
export type Shadows = typeof shadows;
|
|
407
|
+
export type TouchTargets = typeof touchTargets;
|
|
408
|
+
export type Animations = typeof animations;
|
|
409
|
+
export type Breakpoints = typeof breakpoints;
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { ImageStyle, StyleSheet, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
|
|
3
|
+
// React Native style utilities
|
|
4
|
+
type Style = ViewStyle | TextStyle | ImageStyle;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Merges React Native styles similar to how cn() merges CSS classes
|
|
8
|
+
* Handles arrays, objects, and falsy values
|
|
9
|
+
*/
|
|
10
|
+
export function mergeStyles(
|
|
11
|
+
...styles: (Style | Style[] | undefined | null | false)[]
|
|
12
|
+
): Style {
|
|
13
|
+
const validStyles = styles.filter(Boolean).flat() as Style[];
|
|
14
|
+
return StyleSheet.flatten(validStyles) || {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a style merger function that includes base styles
|
|
19
|
+
* Useful for component variants
|
|
20
|
+
*/
|
|
21
|
+
export function createStyleMerger(baseStyle: Style) {
|
|
22
|
+
return (...styles: (Style | Style[] | undefined | null | false)[]) => {
|
|
23
|
+
return mergeStyles(baseStyle, ...styles);
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Conditionally applies styles based on boolean conditions
|
|
29
|
+
*/
|
|
30
|
+
export function conditionalStyle(
|
|
31
|
+
condition: boolean,
|
|
32
|
+
trueStyle: Style,
|
|
33
|
+
falseStyle?: Style,
|
|
34
|
+
): Style | undefined {
|
|
35
|
+
return condition ? trueStyle : falseStyle;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates responsive values based on screen dimensions
|
|
40
|
+
*/
|
|
41
|
+
export function responsiveValue<T>(
|
|
42
|
+
values: {
|
|
43
|
+
sm?: T;
|
|
44
|
+
md?: T;
|
|
45
|
+
lg?: T;
|
|
46
|
+
xl?: T;
|
|
47
|
+
default: T;
|
|
48
|
+
},
|
|
49
|
+
screenWidth: number,
|
|
50
|
+
): T {
|
|
51
|
+
if (screenWidth >= 1280 && values.xl !== undefined) return values.xl;
|
|
52
|
+
if (screenWidth >= 1024 && values.lg !== undefined) return values.lg;
|
|
53
|
+
if (screenWidth >= 768 && values.md !== undefined) return values.md;
|
|
54
|
+
if (screenWidth >= 640 && values.sm !== undefined) return values.sm;
|
|
55
|
+
return values.default;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates platform-specific styles
|
|
60
|
+
*/
|
|
61
|
+
export function platformStyle(styles: {
|
|
62
|
+
ios?: Style;
|
|
63
|
+
android?: Style;
|
|
64
|
+
web?: Style;
|
|
65
|
+
default?: Style;
|
|
66
|
+
}): Style {
|
|
67
|
+
const Platform = require("react-native").Platform;
|
|
68
|
+
|
|
69
|
+
if (Platform.OS === "ios" && styles.ios) return styles.ios;
|
|
70
|
+
if (Platform.OS === "android" && styles.android) return styles.android;
|
|
71
|
+
if (Platform.OS === "web" && styles.web) return styles.web;
|
|
72
|
+
return styles.default || {};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Converts hex color to rgba
|
|
77
|
+
*/
|
|
78
|
+
export function hexToRgba(hex: string, alpha: number = 1): string {
|
|
79
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
80
|
+
if (!result) return hex;
|
|
81
|
+
|
|
82
|
+
const r = parseInt(result[1], 16);
|
|
83
|
+
const g = parseInt(result[2], 16);
|
|
84
|
+
const b = parseInt(result[3], 16);
|
|
85
|
+
|
|
86
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Creates a debounced function for performance
|
|
91
|
+
*/
|
|
92
|
+
export function debounce<T extends (...args: any[]) => any>(
|
|
93
|
+
func: T,
|
|
94
|
+
delay: number,
|
|
95
|
+
): (...args: Parameters<T>) => void {
|
|
96
|
+
let timeoutId: NodeJS.Timeout;
|
|
97
|
+
|
|
98
|
+
return (...args: Parameters<T>) => {
|
|
99
|
+
clearTimeout(timeoutId);
|
|
100
|
+
timeoutId = setTimeout(() => func(...args), delay);
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Creates a throttled function for performance
|
|
106
|
+
*/
|
|
107
|
+
export function throttle<T extends (...args: any[]) => any>(
|
|
108
|
+
func: T,
|
|
109
|
+
delay: number,
|
|
110
|
+
): (...args: Parameters<T>) => void {
|
|
111
|
+
let lastCall = 0;
|
|
112
|
+
|
|
113
|
+
return (...args: Parameters<T>) => {
|
|
114
|
+
const now = Date.now();
|
|
115
|
+
if (now - lastCall >= delay) {
|
|
116
|
+
lastCall = now;
|
|
117
|
+
func(...args);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Type-safe component prop forwarding
|
|
124
|
+
*/
|
|
125
|
+
export function forwardProps<T extends Record<string, any>>(
|
|
126
|
+
props: T,
|
|
127
|
+
omit: (keyof T)[],
|
|
128
|
+
): Omit<T, keyof T extends string ? keyof T : never> {
|
|
129
|
+
const result = { ...props };
|
|
130
|
+
omit.forEach((key) => delete result[key]);
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { useContext, useRef } from "react";
|
|
2
|
+
import { LivestreamContext, makeLivestreamStore } from "../livestream-store";
|
|
3
|
+
import { useLivestreamWebsocket } from "./websocket";
|
|
4
|
+
|
|
5
|
+
export function LivestreamProvider({
|
|
6
|
+
children,
|
|
7
|
+
src,
|
|
8
|
+
}: {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
src: string;
|
|
11
|
+
}) {
|
|
12
|
+
const context = useContext(LivestreamContext);
|
|
13
|
+
const store = useRef(makeLivestreamStore()).current;
|
|
14
|
+
if (context) {
|
|
15
|
+
// this is ok, there's use cases for having one in another
|
|
16
|
+
// like having a player component that's independently usable
|
|
17
|
+
// but can also be embedded within an entire livestream page
|
|
18
|
+
return <>{children}</>;
|
|
19
|
+
}
|
|
20
|
+
(window as any).livestreamStore = store;
|
|
21
|
+
return (
|
|
22
|
+
<LivestreamContext.Provider value={{ store: store }}>
|
|
23
|
+
<LivestreamPoller src={src}>{children}</LivestreamPoller>
|
|
24
|
+
</LivestreamContext.Provider>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function WebsocketWatcher({ src }: { src: string }) {
|
|
29
|
+
useLivestreamWebsocket(src);
|
|
30
|
+
return <></>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function LivestreamPoller({
|
|
34
|
+
children,
|
|
35
|
+
src,
|
|
36
|
+
}: {
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
src: string;
|
|
39
|
+
}) {
|
|
40
|
+
// Websocket watcher is a sibling instead of a parent to avoid
|
|
41
|
+
// re-rendering when the websocket does stuff
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<WebsocketWatcher src={src} />
|
|
45
|
+
{children}
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
import useWebSocket from "react-use-websocket";
|
|
3
|
+
import { useHandleWebsocketMessages } from "../livestream-store";
|
|
4
|
+
import { useUrl } from "../streamplace-store";
|
|
5
|
+
|
|
6
|
+
export function useLivestreamWebsocket(src: string) {
|
|
7
|
+
const url = useUrl();
|
|
8
|
+
const handleWebSocketMessages = useHandleWebsocketMessages();
|
|
9
|
+
|
|
10
|
+
let wsUrl = url.replace(/^http\:/, "ws:");
|
|
11
|
+
wsUrl = wsUrl.replace(/^https\:/, "wss:");
|
|
12
|
+
|
|
13
|
+
const ref = useRef<any[]>([]);
|
|
14
|
+
const handle = useRef<NodeJS.Timeout | null>(null);
|
|
15
|
+
|
|
16
|
+
const { readyState } = useWebSocket(`${wsUrl}/api/websocket/${src}`, {
|
|
17
|
+
reconnectInterval: 1000,
|
|
18
|
+
shouldReconnect: () => true,
|
|
19
|
+
|
|
20
|
+
onOpen: () => {
|
|
21
|
+
ref.current = [];
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
onError: (e) => {
|
|
25
|
+
console.log("onError", e);
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
// spamming the redux store with messages causes a zillion re-renders,
|
|
29
|
+
// so we batch them up a bit
|
|
30
|
+
onMessage: (msg) => {
|
|
31
|
+
try {
|
|
32
|
+
const data = JSON.parse(msg.data);
|
|
33
|
+
ref.current.push(data);
|
|
34
|
+
if (handle.current) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
handle.current = setTimeout(() => {
|
|
38
|
+
handleWebSocketMessages(ref.current);
|
|
39
|
+
ref.current = [];
|
|
40
|
+
handle.current = null;
|
|
41
|
+
}, 250);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.log("onMessage parse error", e);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|