@streamplace/components 0.7.35 → 0.8.3

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 (45) hide show
  1. package/dist/components/content-metadata/content-metadata-form.js +467 -0
  2. package/dist/components/content-metadata/content-rights.js +78 -0
  3. package/dist/components/content-metadata/content-warnings.js +68 -0
  4. package/dist/components/content-metadata/index.js +11 -0
  5. package/dist/components/mobile-player/player.js +4 -0
  6. package/dist/components/mobile-player/ui/report-modal.js +3 -2
  7. package/dist/components/ui/checkbox.js +87 -0
  8. package/dist/components/ui/dialog.js +188 -83
  9. package/dist/components/ui/primitives/input.js +13 -1
  10. package/dist/components/ui/primitives/modal.js +2 -2
  11. package/dist/components/ui/select.js +89 -0
  12. package/dist/components/ui/textarea.js +23 -4
  13. package/dist/components/ui/toast.js +464 -114
  14. package/dist/components/ui/tooltip.js +103 -0
  15. package/dist/index.js +2 -0
  16. package/dist/lib/metadata-constants.js +157 -0
  17. package/dist/lib/theme/theme.js +5 -3
  18. package/dist/streamplace-provider/index.js +14 -4
  19. package/dist/streamplace-store/content-metadata-actions.js +124 -0
  20. package/dist/streamplace-store/streamplace-store.js +22 -5
  21. package/dist/streamplace-store/user.js +67 -7
  22. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  23. package/package.json +3 -3
  24. package/src/components/content-metadata/content-metadata-form.tsx +893 -0
  25. package/src/components/content-metadata/content-rights.tsx +104 -0
  26. package/src/components/content-metadata/content-warnings.tsx +100 -0
  27. package/src/components/content-metadata/index.tsx +10 -0
  28. package/src/components/mobile-player/player.tsx +5 -0
  29. package/src/components/mobile-player/ui/report-modal.tsx +13 -7
  30. package/src/components/ui/checkbox.tsx +147 -0
  31. package/src/components/ui/dialog.tsx +319 -99
  32. package/src/components/ui/primitives/input.tsx +19 -2
  33. package/src/components/ui/primitives/modal.tsx +4 -2
  34. package/src/components/ui/select.tsx +175 -0
  35. package/src/components/ui/textarea.tsx +47 -29
  36. package/src/components/ui/toast.tsx +785 -179
  37. package/src/components/ui/tooltip.tsx +131 -0
  38. package/src/index.tsx +3 -0
  39. package/src/lib/metadata-constants.ts +180 -0
  40. package/src/lib/theme/theme.tsx +10 -6
  41. package/src/streamplace-provider/index.tsx +20 -2
  42. package/src/streamplace-store/content-metadata-actions.tsx +145 -0
  43. package/src/streamplace-store/streamplace-store.tsx +41 -4
  44. package/src/streamplace-store/user.tsx +71 -7
  45. package/tsconfig.tsbuildinfo +1 -1
@@ -1,11 +1,21 @@
1
+ import BottomSheet, { BottomSheetScrollView } from "@gorhom/bottom-sheet";
2
+ // to get the portal
3
+ import * as Portal from "@rn-primitives/portal";
1
4
  import { cva, type VariantProps } from "class-variance-authority";
2
5
  import { X } from "lucide-react-native";
3
- import React, { forwardRef } from "react";
4
- import { Platform, Text } from "react-native";
6
+ import React, { forwardRef, useRef } from "react";
7
+ import {
8
+ Platform,
9
+ Pressable,
10
+ StyleSheet,
11
+ useWindowDimensions,
12
+ View,
13
+ } from "react-native";
14
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
5
15
  import { useTheme } from "../../lib/theme/theme";
6
- import * as zero from "../../ui";
7
16
  import { createThemedIcon } from "./icons";
8
17
  import { ModalPrimitive, ModalPrimitiveProps } from "./primitives/modal";
18
+ import { Text } from "./text";
9
19
 
10
20
  const ThemedX = createThemedIcon(X);
11
21
 
@@ -50,10 +60,138 @@ export interface DialogProps
50
60
  onClose?: () => void;
51
61
  }
52
62
 
63
+ // Bottom Sheet Dialog Component
64
+ const DialogBottomSheet = forwardRef<
65
+ any,
66
+ DialogProps & {
67
+ overlayStyle?: any;
68
+ portalHost?: string;
69
+ }
70
+ >(function DialogBottomSheet(
71
+ {
72
+ overlayStyle,
73
+ portalHost,
74
+ children,
75
+ title,
76
+ description,
77
+ showCloseButton = true,
78
+ onClose,
79
+ open = false,
80
+ onOpenChange,
81
+ ...props
82
+ },
83
+ _ref,
84
+ ) {
85
+ const { theme } = useTheme();
86
+ const sheetRef = useRef<BottomSheet>(null);
87
+ const { top } = useSafeAreaInsets();
88
+ const dims = useWindowDimensions();
89
+
90
+ const handleClose = React.useCallback(() => {
91
+ if (onClose) {
92
+ onClose();
93
+ }
94
+ if (onOpenChange) {
95
+ onOpenChange(false);
96
+ }
97
+ }, [onClose, onOpenChange]);
98
+
99
+ if (!open) {
100
+ return null;
101
+ }
102
+
103
+ return (
104
+ <Portal.Portal name="dialog">
105
+ <BottomSheet
106
+ ref={sheetRef}
107
+ index={open ? 0 : -1}
108
+ enablePanDownToClose
109
+ enableDynamicSizing={true}
110
+ maxDynamicContentSize={dims.height - top}
111
+ keyboardBehavior="interactive"
112
+ keyboardBlurBehavior="restore"
113
+ enableContentPanningGesture={false}
114
+ backdropComponent={({ style }) => (
115
+ <Pressable
116
+ style={[style, StyleSheet.absoluteFill]}
117
+ onPress={handleClose}
118
+ />
119
+ )}
120
+ onClose={handleClose}
121
+ style={[overlayStyle]}
122
+ backgroundStyle={{
123
+ backgroundColor: theme.colors.card,
124
+ borderRadius: theme.borderRadius.lg,
125
+ ...theme.shadows.lg,
126
+ }}
127
+ handleIndicatorStyle={{
128
+ width: 48,
129
+ height: 4,
130
+ backgroundColor: theme.colors.textMuted,
131
+ }}
132
+ >
133
+ <BottomSheetScrollView
134
+ style={{
135
+ flex: 1,
136
+ width: "100%",
137
+ }}
138
+ contentContainerStyle={{ flexGrow: 1 }}
139
+ >
140
+ {/* Header */}
141
+ {(title || showCloseButton) && (
142
+ <View
143
+ style={{
144
+ paddingHorizontal: theme.spacing[4],
145
+ paddingVertical: theme.spacing[4],
146
+ flexDirection: "row",
147
+ alignItems: "center",
148
+ justifyContent: "space-between",
149
+ width: "100%",
150
+ }}
151
+ >
152
+ {title && <DialogTitle>{title}</DialogTitle>}
153
+ {showCloseButton && (
154
+ <Pressable
155
+ onPress={handleClose}
156
+ style={{
157
+ width: theme.touchTargets.minimum,
158
+ height: theme.touchTargets.minimum,
159
+ alignItems: "center",
160
+ justifyContent: "center",
161
+ borderRadius: theme.borderRadius.sm,
162
+ marginLeft: theme.spacing[2],
163
+ }}
164
+ >
165
+ <DialogCloseIcon />
166
+ </Pressable>
167
+ )}
168
+ </View>
169
+ )}
170
+
171
+ {/* Scrollable Content */}
172
+ <View
173
+ style={{
174
+ paddingHorizontal: theme.spacing[4],
175
+ paddingBottom: theme.spacing[6],
176
+ flex: 1,
177
+ width: "100%",
178
+ }}
179
+ >
180
+ {description && (
181
+ <DialogDescription>{description}</DialogDescription>
182
+ )}
183
+ {children}
184
+ </View>
185
+ </BottomSheetScrollView>
186
+ </BottomSheet>
187
+ </Portal.Portal>
188
+ );
189
+ });
190
+
53
191
  export const Dialog = forwardRef<any, DialogProps>(
54
192
  (
55
193
  {
56
- variant = "left",
194
+ variant = "default",
57
195
  size = "md",
58
196
  position = "center",
59
197
  children,
@@ -68,70 +206,10 @@ export const Dialog = forwardRef<any, DialogProps>(
68
206
  },
69
207
  ref,
70
208
  ) => {
71
- const { zero: zt, theme } = useTheme();
72
-
73
- // Content styles using theme.zero
74
- const contentStyles = React.useMemo(() => {
75
- const baseStyle = [
76
- zt.bg.card,
77
- zero.r.lg,
78
- zero.shadows.lg,
79
- { maxHeight: "90%", maxWidth: "90%" },
80
- ];
81
-
82
- const variantStyle = (() => {
83
- switch (variant) {
84
- case "sheet":
85
- return [
86
- { borderRadius: zero.borderRadius.xl },
87
- {
88
- borderBottomLeftRadius: 0,
89
- borderBottomRightRadius: 0,
90
- marginTop: "auto",
91
- marginBottom: 0,
92
- maxHeight: "80%",
93
- width: "100%",
94
- maxWidth: "100%",
95
- },
96
- ];
97
- case "fullscreen":
98
- return [
99
- {
100
- width: "100%",
101
- height: "100%",
102
- maxWidth: "100%",
103
- maxHeight: "100%",
104
- borderRadius: 0,
105
- margin: 0,
106
- },
107
- ];
108
- default:
109
- return [];
110
- }
111
- })();
112
-
113
- const sizeStyle = (() => {
114
- switch (size) {
115
- case "sm":
116
- return { minWidth: 300, minHeight: 200 };
117
- case "lg":
118
- return { minWidth: 500, minHeight: 400 };
119
- case "xl":
120
- return { minWidth: 600, minHeight: 500 };
121
- case "full":
122
- return {
123
- width: "95%",
124
- height: "95%",
125
- maxWidth: "95%",
126
- maxHeight: "95%",
127
- };
128
- default:
129
- return { minWidth: 400, minHeight: 300 };
130
- }
131
- })();
132
-
133
- return [baseStyle, variantStyle, sizeStyle].flat();
134
- }, [variant, size, zero]);
209
+ const { theme } = useTheme();
210
+
211
+ // Create dynamic styles based on theme
212
+ const styles = React.useMemo(() => createStyles(theme), [theme]);
135
213
 
136
214
  const handleClose = React.useCallback(() => {
137
215
  if (onClose) {
@@ -173,38 +251,32 @@ export const Dialog = forwardRef<any, DialogProps>(
173
251
  <ModalPrimitive.Overlay
174
252
  dismissible={dismissible}
175
253
  onDismiss={handleClose}
176
- style={zt.bg.overlay}
254
+ style={styles.overlay}
177
255
  >
178
256
  <ModalPrimitive.Content
179
- position={position || "left"}
257
+ position={position || "center"}
180
258
  size={size || "md"}
181
- style={contentStyles}
259
+ style={[
260
+ styles.content,
261
+ variant === "sheet" && styles.sheetContent,
262
+ variant === "fullscreen" && styles.fullscreenContent,
263
+ size === "sm" && styles.smContent,
264
+ size === "md" && styles.mdContent,
265
+ size === "lg" && styles.lgContent,
266
+ size === "xl" && styles.xlContent,
267
+ size === "full" && styles.fullContent,
268
+ ]}
182
269
  >
183
270
  {(title || showCloseButton) && (
184
271
  <ModalPrimitive.Header
185
272
  withBorder={variant !== "sheet"}
186
- style={[
187
- zero.p[4],
188
- {
189
- flexDirection: "row",
190
- alignItems: "center",
191
- justifyContent: "space-between",
192
- },
193
- ]}
273
+ style={styles.header}
194
274
  >
195
275
  <DialogTitle>{title}</DialogTitle>
196
276
  {showCloseButton && (
197
277
  <ModalPrimitive.Close
198
278
  onClose={handleClose}
199
- style={[
200
- zero.p[2],
201
- {
202
- width: 44,
203
- height: 44,
204
- alignItems: "center",
205
- justifyContent: "center",
206
- },
207
- ]}
279
+ style={styles.closeButton}
208
280
  >
209
281
  <DialogCloseIcon />
210
282
  </ModalPrimitive.Close>
@@ -214,7 +286,7 @@ export const Dialog = forwardRef<any, DialogProps>(
214
286
 
215
287
  <ModalPrimitive.Body
216
288
  scrollable={variant !== "fullscreen"}
217
- style={[zero.p[6], { paddingTop: 0, flex: 1 }]}
289
+ style={styles.body}
218
290
  >
219
291
  {description && (
220
292
  <DialogDescription>{description}</DialogDescription>
@@ -230,6 +302,43 @@ export const Dialog = forwardRef<any, DialogProps>(
230
302
 
231
303
  Dialog.displayName = "Dialog";
232
304
 
305
+ /// Responsive Dialog Component. On mobile this will render a *bottom sheet*.
306
+ /// Prefer this over the regular Dialog component for better mobile UX.
307
+ export const ResponsiveDialog = forwardRef<any, DialogProps>(
308
+ ({ children, size, ...props }, ref) => {
309
+ const { width } = useWindowDimensions();
310
+
311
+ // On web, you might want to always use the normal dialog
312
+ // On mobile (width < 800), use the bottom sheet
313
+ const isBottomSheet = Platform.OS !== "web" && width < 800;
314
+
315
+ if (isBottomSheet) {
316
+ return (
317
+ <DialogBottomSheet
318
+ ref={ref}
319
+ {...props}
320
+ size={"full"}
321
+ showCloseButton={false}
322
+ variant="fullscreen"
323
+ >
324
+ {children}
325
+ </DialogBottomSheet>
326
+ );
327
+ }
328
+
329
+ // Use larger default size for regular dialogs to give more room
330
+ const dialogSize = size || "lg";
331
+
332
+ return (
333
+ <Dialog ref={ref} size={dialogSize} {...props}>
334
+ {children}
335
+ </Dialog>
336
+ );
337
+ },
338
+ );
339
+
340
+ ResponsiveDialog.displayName = "ResponsiveDialog";
341
+
233
342
  // Dialog Title component
234
343
  export interface DialogTitleProps {
235
344
  children?: React.ReactNode;
@@ -238,16 +347,13 @@ export interface DialogTitleProps {
238
347
 
239
348
  export const DialogTitle = forwardRef<any, DialogTitleProps>(
240
349
  ({ children, style, ...props }, ref) => {
241
- const { zero: zt } = useTheme();
350
+ const { theme } = useTheme();
351
+ const styles = React.useMemo(() => createStyles(theme), [theme]);
242
352
 
243
353
  if (!children) return null;
244
354
 
245
355
  return (
246
- <Text
247
- ref={ref}
248
- style={[zt.text.xl, { fontWeight: "600", flex: 1 }, style]}
249
- {...props}
250
- >
356
+ <Text ref={ref} style={[styles.title, style]} {...props}>
251
357
  {children}
252
358
  </Text>
253
359
  );
@@ -264,12 +370,13 @@ export interface DialogDescriptionProps {
264
370
 
265
371
  export const DialogDescription = forwardRef<any, DialogDescriptionProps>(
266
372
  ({ children, style, ...props }, ref) => {
267
- const { zero: zt } = useTheme();
373
+ const { theme } = useTheme();
374
+ const styles = React.useMemo(() => createStyles(theme), [theme]);
268
375
 
269
376
  if (!children) return null;
270
377
 
271
378
  return (
272
- <Text ref={ref} style={[zt.text.muted, zero.mb[4], style]} {...props}>
379
+ <Text ref={ref} style={[styles.description, style]} {...props}>
273
380
  {children}
274
381
  </Text>
275
382
  );
@@ -304,7 +411,8 @@ export const DialogFooter = forwardRef<any, DialogFooterProps>(
304
411
  },
305
412
  ref,
306
413
  ) => {
307
- const { zero: zt } = useTheme();
414
+ const { theme } = useTheme();
415
+ const styles = React.useMemo(() => createStyles(theme), [theme]);
308
416
 
309
417
  if (!children) return null;
310
418
 
@@ -314,7 +422,7 @@ export const DialogFooter = forwardRef<any, DialogFooterProps>(
314
422
  withBorder={withBorder}
315
423
  direction={direction}
316
424
  justify={justify}
317
- style={[zero.p[6], { gap: 8 }, style]}
425
+ style={[styles.footer, style]}
318
426
  {...props}
319
427
  >
320
428
  {children}
@@ -330,5 +438,117 @@ const DialogCloseIcon = () => {
330
438
  return <ThemedX size="md" variant="default" />;
331
439
  };
332
440
 
441
+ // Create theme-aware styles
442
+ function createStyles(theme: any) {
443
+ return StyleSheet.create({
444
+ overlay: {
445
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
446
+ },
447
+
448
+ content: {
449
+ backgroundColor: theme.colors.card,
450
+ borderRadius: theme.borderRadius.lg,
451
+ ...theme.shadows.lg,
452
+ maxHeight: "90%",
453
+ maxWidth: "90%",
454
+ },
455
+
456
+ // Variant styles
457
+ sheetContent: {
458
+ borderTopLeftRadius: theme.borderRadius.xl,
459
+ borderTopRightRadius: theme.borderRadius.xl,
460
+ borderBottomLeftRadius: 0,
461
+ borderBottomRightRadius: 0,
462
+ marginTop: "auto",
463
+ marginBottom: 0,
464
+ maxHeight: "80%",
465
+ width: "100%",
466
+ maxWidth: "100%",
467
+ },
468
+
469
+ fullscreenContent: {
470
+ width: "100%",
471
+ height: "100%",
472
+ maxWidth: "100%",
473
+ maxHeight: "100%",
474
+ borderRadius: 0,
475
+ margin: 0,
476
+ },
477
+
478
+ // Size styles
479
+ smContent: {
480
+ minWidth: 300,
481
+ minHeight: 200,
482
+ },
483
+
484
+ mdContent: {
485
+ minWidth: 400,
486
+ minHeight: 300,
487
+ },
488
+
489
+ lgContent: {
490
+ minWidth: 500,
491
+ minHeight: 400,
492
+ },
493
+
494
+ xlContent: {
495
+ minWidth: 600,
496
+ minHeight: 500,
497
+ },
498
+
499
+ fullContent: {
500
+ width: "95%",
501
+ height: "95%",
502
+ maxWidth: "95%",
503
+ maxHeight: "95%",
504
+ },
505
+
506
+ header: {
507
+ paddingHorizontal: theme.spacing[6],
508
+ paddingVertical: theme.spacing[4],
509
+ flexDirection: "row",
510
+ alignItems: "center",
511
+ justifyContent: "space-between",
512
+ },
513
+
514
+ body: {
515
+ paddingHorizontal: theme.spacing[6],
516
+ paddingBottom: theme.spacing[6],
517
+ flex: 1,
518
+ },
519
+
520
+ footer: {
521
+ paddingHorizontal: theme.spacing[6],
522
+ paddingVertical: theme.spacing[4],
523
+ gap: theme.spacing[2],
524
+ width: "100%",
525
+ },
526
+
527
+ title: {
528
+ fontSize: 20,
529
+ fontWeight: "600",
530
+ color: theme.colors.text,
531
+ flex: 1,
532
+ lineHeight: 24,
533
+ },
534
+
535
+ description: {
536
+ fontSize: 16,
537
+ color: theme.colors.textMuted,
538
+ lineHeight: 22,
539
+ marginVertical: theme.spacing[4],
540
+ },
541
+
542
+ closeButton: {
543
+ width: theme.touchTargets.minimum,
544
+ height: theme.touchTargets.minimum,
545
+ alignItems: "center",
546
+ justifyContent: "center",
547
+ borderRadius: theme.borderRadius.sm,
548
+ marginLeft: theme.spacing[2],
549
+ },
550
+ });
551
+ }
552
+
333
553
  // Export dialog variants for external use
334
554
  export { dialogVariants };
@@ -1,3 +1,7 @@
1
+ import {
2
+ BottomSheetTextInput,
3
+ useBottomSheetInternal,
4
+ } from "@gorhom/bottom-sheet";
1
5
  import React, { forwardRef } from "react";
2
6
  import {
3
7
  NativeSyntheticEvent,
@@ -24,7 +28,7 @@ export interface InputPrimitiveProps extends Omit<TextInputProps, "onChange"> {
24
28
  }
25
29
 
26
30
  // Input root primitive - the main TextInput component
27
- export const InputRoot = forwardRef<TextInput, InputPrimitiveProps>(
31
+ export const InputRoot = forwardRef<any, InputPrimitiveProps>(
28
32
  (
29
33
  {
30
34
  value,
@@ -44,6 +48,19 @@ export const InputRoot = forwardRef<TextInput, InputPrimitiveProps>(
44
48
  ) => {
45
49
  const [isFocused, setIsFocused] = React.useState(false);
46
50
 
51
+ let isInBottomSheet = false;
52
+ try {
53
+ useBottomSheetInternal();
54
+ isInBottomSheet = true;
55
+ } catch {
56
+ isInBottomSheet = false;
57
+ }
58
+
59
+ const InputComponent =
60
+ isInBottomSheet && Platform.OS !== "web"
61
+ ? BottomSheetTextInput
62
+ : TextInput;
63
+
47
64
  const handleChangeText = React.useCallback(
48
65
  (text: string) => {
49
66
  if (onChangeText) {
@@ -77,7 +94,7 @@ export const InputRoot = forwardRef<TextInput, InputPrimitiveProps>(
77
94
  );
78
95
 
79
96
  return (
80
- <TextInput
97
+ <InputComponent
81
98
  ref={ref}
82
99
  value={value}
83
100
  onChangeText={handleChangeText}
@@ -32,8 +32,9 @@ export const ModalRoot = forwardRef<View, ModalPrimitiveProps>(
32
32
  children,
33
33
  onRequestClose,
34
34
  animationType = "fade",
35
- presentationStyle = Platform.OS === "ios" ? "pageSheet" : "fullScreen",
36
- statusBarTranslucent = Platform.OS === "android",
35
+ presentationStyle = Platform.OS === "ios" ? "pageSheet" : "formSheet",
36
+ statusBarTranslucent = Platform.OS !== "ios",
37
+ transparent = true,
37
38
  ...props
38
39
  },
39
40
  ref,
@@ -57,6 +58,7 @@ export const ModalRoot = forwardRef<View, ModalPrimitiveProps>(
57
58
  animationType={animationType}
58
59
  presentationStyle={presentationStyle}
59
60
  statusBarTranslucent={statusBarTranslucent}
61
+ transparent={transparent}
60
62
  {...props}
61
63
  >
62
64
  <View ref={ref} style={primitiveStyles.container}>