@streamplace/components 0.7.9 → 0.7.13

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 (48) hide show
  1. package/dist/assets/emoji-data.json +19371 -0
  2. package/dist/components/chat/chat-box.js +19 -2
  3. package/dist/components/chat/chat-message.js +12 -4
  4. package/dist/components/chat/chat.js +15 -4
  5. package/dist/components/chat/mod-view.js +15 -8
  6. package/dist/components/dashboard/chat-panel.js +38 -0
  7. package/dist/components/dashboard/header.js +80 -0
  8. package/dist/components/dashboard/index.js +14 -0
  9. package/dist/components/dashboard/information-widget.js +234 -0
  10. package/dist/components/dashboard/mod-actions.js +71 -0
  11. package/dist/components/dashboard/problems.js +74 -0
  12. package/dist/components/mobile-player/ui/viewer-context-menu.js +15 -6
  13. package/dist/components/ui/button.js +2 -2
  14. package/dist/components/ui/dropdown.js +20 -1
  15. package/dist/components/ui/index.js +2 -0
  16. package/dist/components/ui/info-box.js +31 -0
  17. package/dist/components/ui/info-row.js +23 -0
  18. package/dist/components/ui/toast.js +43 -0
  19. package/dist/index.js +3 -1
  20. package/dist/lib/theme/atoms.js +66 -45
  21. package/dist/lib/theme/tokens.js +285 -12
  22. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  23. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/56540125 +0 -0
  24. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/67b1eb60 +0 -0
  25. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/7c275f90 +0 -0
  26. package/package.json +2 -2
  27. package/src/assets/emoji-data.json +19371 -0
  28. package/src/components/chat/chat-box.tsx +19 -1
  29. package/src/components/chat/chat-message.tsx +22 -14
  30. package/src/components/chat/chat.tsx +21 -6
  31. package/src/components/chat/mod-view.tsx +24 -6
  32. package/src/components/dashboard/chat-panel.tsx +80 -0
  33. package/src/components/dashboard/header.tsx +170 -0
  34. package/src/components/dashboard/index.tsx +5 -0
  35. package/src/components/dashboard/information-widget.tsx +526 -0
  36. package/src/components/dashboard/mod-actions.tsx +133 -0
  37. package/src/components/dashboard/problems.tsx +151 -0
  38. package/src/components/mobile-player/ui/viewer-context-menu.tsx +67 -38
  39. package/src/components/ui/button.tsx +2 -2
  40. package/src/components/ui/dropdown.tsx +38 -3
  41. package/src/components/ui/index.ts +2 -0
  42. package/src/components/ui/info-box.tsx +60 -0
  43. package/src/components/ui/info-row.tsx +48 -0
  44. package/src/components/ui/toast.tsx +110 -0
  45. package/src/index.tsx +3 -0
  46. package/src/lib/theme/atoms.ts +97 -43
  47. package/src/lib/theme/tokens.ts +285 -12
  48. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,151 @@
1
+ import { ExternalLink } from "lucide-react-native";
2
+ import { useState } from "react";
3
+ import { Linking, Pressable, Text, View } from "react-native";
4
+ import { useLivestreamStore } from "../../livestream-store";
5
+ import { LivestreamProblem } from "../../livestream-store/livestream-state";
6
+ import * as zero from "../../ui";
7
+
8
+ const { bg, r, borders, p, text, layout, gap } = zero;
9
+
10
+ const Problems = ({
11
+ probs,
12
+ onIgnore,
13
+ }: {
14
+ probs: LivestreamProblem[];
15
+ onIgnore: () => void;
16
+ }) => {
17
+ return (
18
+ <View style={[gap.all[3]]}>
19
+ <View>
20
+ <Text style={[text.white, { fontSize: 24, fontWeight: "bold" }]}>
21
+ Optimize Your Stream
22
+ </Text>
23
+ <Text style={[text.gray[300]]}>
24
+ We've found a few things that could improve your stream's reliability.
25
+ </Text>
26
+ </View>
27
+ {probs.map((p) => (
28
+ <View key={p.message}>
29
+ <View
30
+ style={[
31
+ gap.all[2],
32
+ layout.flex.row,
33
+ layout.flex.alignCenter,
34
+ { gap: 8, alignItems: "flex-start" },
35
+ ]}
36
+ >
37
+ <Text
38
+ style={[
39
+ r.sm,
40
+ p[2],
41
+ {
42
+ width: 82,
43
+ textAlign: "center",
44
+ backgroundColor:
45
+ p.severity === "error"
46
+ ? "#7f1d1d"
47
+ : p.severity === "warning"
48
+ ? "#7c2d12"
49
+ : "#1e3a8a",
50
+ color: "white",
51
+ fontSize: 12,
52
+ },
53
+ ]}
54
+ >
55
+ {p.severity}
56
+ </Text>
57
+ <View style={[{ flex: 1 }, gap.all[1]]}>
58
+ <Text style={[text.white, { fontWeight: "600" }]}>{p.code}</Text>
59
+ <Text style={[text.gray[400], { fontSize: 14 }]}>
60
+ {p.message}
61
+ </Text>
62
+ {p.link && (
63
+ <Pressable onPress={() => p.link && Linking.openURL(p.link)}>
64
+ <View
65
+ style={[
66
+ layout.flex.row,
67
+ layout.flex.alignCenter,
68
+ gap.all[2],
69
+ ]}
70
+ >
71
+ <Text style={[{ color: "#3b82f6", fontSize: 14 }]}>
72
+ Learn More
73
+ </Text>
74
+ <ExternalLink size={12} color="#3b82f6" />
75
+ </View>
76
+ </Pressable>
77
+ )}
78
+ </View>
79
+ </View>
80
+ </View>
81
+ ))}
82
+
83
+ <Pressable
84
+ onPress={onIgnore}
85
+ style={[
86
+ bg.blue[600],
87
+ r.md,
88
+ p[3],
89
+ layout.flex.center,
90
+ { marginTop: 16 },
91
+ ]}
92
+ >
93
+ <Text style={[text.white, { fontWeight: "600" }]}>Ignore</Text>
94
+ </Pressable>
95
+ </View>
96
+ );
97
+ };
98
+
99
+ export const ProblemsWrapper = ({
100
+ children,
101
+ }: {
102
+ children: React.ReactElement;
103
+ }) => {
104
+ const problems = useLivestreamStore((x) => x.problems);
105
+ const [dismiss, setDismiss] = useState(false);
106
+
107
+ return (
108
+ <View
109
+ style={[
110
+ { position: "relative", flex: 1 },
111
+ layout.flex.center,
112
+ { flexBasis: 0 },
113
+ ]}
114
+ >
115
+ {children}
116
+ {problems.length > 0 && !dismiss && (
117
+ <View
118
+ style={[
119
+ {
120
+ position: "absolute",
121
+ top: 0,
122
+ left: 0,
123
+ right: 0,
124
+ bottom: 0,
125
+ backgroundColor: "rgba(0, 0, 0, 0.8)",
126
+ zIndex: 100,
127
+ },
128
+ layout.flex.center,
129
+ { justifyContent: "flex-start" },
130
+ p[8],
131
+ ]}
132
+ >
133
+ <View
134
+ style={[
135
+ bg.gray[900],
136
+ borders.color.gray[700],
137
+ borders.width.thin,
138
+ r.lg,
139
+ p[4],
140
+ { maxWidth: 700, width: "100%" },
141
+ ]}
142
+ >
143
+ <Problems probs={problems} onIgnore={() => setDismiss(true)} />
144
+ </View>
145
+ </View>
146
+ )}
147
+ </View>
148
+ );
149
+ };
150
+
151
+ export default Problems;
@@ -1,14 +1,17 @@
1
1
  import { useRootContext } from "@rn-primitives/dropdown-menu";
2
2
  import { Settings } from "lucide-react-native";
3
+ import { Platform, View } from "react-native";
3
4
  import { colors } from "../../../lib/theme";
4
5
  import { useLivestreamStore } from "../../../livestream-store";
5
6
  import { PlayerProtocol, usePlayerStore } from "../../../player-store/";
6
7
  import {
7
8
  DropdownMenu,
8
9
  DropdownMenuCheckboxItem,
10
+ DropdownMenuContentWithoutPortal,
9
11
  DropdownMenuGroup,
10
12
  DropdownMenuInfo,
11
13
  DropdownMenuItem,
14
+ DropdownMenuPortal,
12
15
  DropdownMenuRadioGroup,
13
16
  DropdownMenuRadioItem,
14
17
  DropdownMenuTrigger,
@@ -16,7 +19,11 @@ import {
16
19
  Text,
17
20
  } from "../../ui";
18
21
 
19
- export function ContextMenu() {
22
+ export function ContextMenu({
23
+ dropdownPortalContainer,
24
+ }: {
25
+ dropdownPortalContainer?: any;
26
+ }) {
20
27
  const quality = usePlayerStore((x) => x.selectedRendition);
21
28
  const setQuality = usePlayerStore((x) => x.setSelectedRendition);
22
29
  const qualities = useLivestreamStore((x) => x.renditions);
@@ -27,58 +34,80 @@ export function ContextMenu() {
27
34
  const debugInfo = usePlayerStore((x) => x.showDebugInfo);
28
35
  const setShowDebugInfo = usePlayerStore((x) => x.setShowDebugInfo);
29
36
 
37
+ const livestream = useLivestreamStore((x) => x.livestream);
38
+ const setReportModalOpen = usePlayerStore((x) => x.setReportModalOpen);
39
+ const setReportSubject = usePlayerStore((x) => x.setReportSubject);
40
+
30
41
  const lowLatency = protocol === "webrtc";
31
42
  const setLowLatency = (value: boolean) => {
32
43
  setProtocol(value ? PlayerProtocol.WEBRTC : PlayerProtocol.HLS);
33
44
  };
34
45
 
46
+ // are we on mobile? then do dropdowns
47
+ const isMobile = Platform.OS === "ios" || Platform.OS === "android";
48
+
49
+ // dummy portal for mobile
50
+ const Portal = isMobile ? View : DropdownMenuPortal;
51
+
52
+ // render the responsive version on mobile as we can't fullscreen there
53
+ const DropdownMenuContent = isMobile
54
+ ? ResponsiveDropdownMenuContent
55
+ : DropdownMenuContentWithoutPortal;
56
+
35
57
  return (
36
58
  <DropdownMenu>
37
59
  <DropdownMenuTrigger>
38
60
  <Settings color={colors.gray[200]} />
39
61
  </DropdownMenuTrigger>
40
- <ResponsiveDropdownMenuContent side="top" align="end">
41
- <DropdownMenuGroup title="Resolution">
42
- <DropdownMenuRadioGroup value={quality} onValueChange={setQuality}>
43
- <DropdownMenuRadioItem value="source">
44
- <Text>Source (Original Quality)</Text>
45
- </DropdownMenuRadioItem>
46
- {qualities.map((r) => (
47
- <DropdownMenuRadioItem value={r.name}>
48
- <Text>{r.name}</Text>
62
+ <Portal container={dropdownPortalContainer}>
63
+ <DropdownMenuContent side="top" align="end">
64
+ <DropdownMenuGroup title="Resolution">
65
+ <DropdownMenuRadioGroup value={quality} onValueChange={setQuality}>
66
+ <DropdownMenuRadioItem value="source">
67
+ <Text>Source (Original Quality)</Text>
49
68
  </DropdownMenuRadioItem>
50
- ))}
51
- </DropdownMenuRadioGroup>
52
- </DropdownMenuGroup>
53
- <DropdownMenuGroup title="Advanced">
54
- <DropdownMenuCheckboxItem
55
- checked={lowLatency}
56
- onCheckedChange={() => setLowLatency(!lowLatency)}
57
- >
58
- <Text>Low Latency</Text>
59
- </DropdownMenuCheckboxItem>
60
- </DropdownMenuGroup>
61
- <DropdownMenuInfo description="Reduces the delay between video and chat for a more real-time experience." />
62
- <DropdownMenuGroup>
63
- <DropdownMenuCheckboxItem
64
- checked={debugInfo}
65
- onCheckedChange={() => setShowDebugInfo(!debugInfo)}
66
- >
67
- <Text>Show Debug Info</Text>
68
- </DropdownMenuCheckboxItem>
69
- </DropdownMenuGroup>
70
- <DropdownMenuGroup title="Report">
71
- <ReportButton />
72
- </DropdownMenuGroup>
73
- </ResponsiveDropdownMenuContent>
69
+ {qualities.map((r) => (
70
+ <DropdownMenuRadioItem value={r.name}>
71
+ <Text>{r.name}</Text>
72
+ </DropdownMenuRadioItem>
73
+ ))}
74
+ </DropdownMenuRadioGroup>
75
+ </DropdownMenuGroup>
76
+ <DropdownMenuGroup title="Advanced">
77
+ <DropdownMenuCheckboxItem
78
+ checked={lowLatency}
79
+ onCheckedChange={() => setLowLatency(!lowLatency)}
80
+ >
81
+ <Text>Low Latency</Text>
82
+ </DropdownMenuCheckboxItem>
83
+ </DropdownMenuGroup>
84
+ <DropdownMenuInfo description="Reduces the delay between video and chat for a more real-time experience." />
85
+ <DropdownMenuGroup>
86
+ <DropdownMenuCheckboxItem
87
+ checked={debugInfo}
88
+ onCheckedChange={() => setShowDebugInfo(!debugInfo)}
89
+ >
90
+ <Text>Show Debug Info</Text>
91
+ </DropdownMenuCheckboxItem>
92
+ </DropdownMenuGroup>
93
+ <DropdownMenuGroup title="Report">
94
+ <ReportButton
95
+ livestream={livestream}
96
+ setReportModalOpen={setReportModalOpen}
97
+ setReportSubject={setReportSubject}
98
+ />
99
+ </DropdownMenuGroup>
100
+ </DropdownMenuContent>
101
+ </Portal>
74
102
  </DropdownMenu>
75
103
  );
76
104
  }
77
105
 
78
- export function ReportButton() {
79
- const livestream = useLivestreamStore((x) => x.livestream);
80
- const setReportModalOpen = usePlayerStore((x) => x.setReportModalOpen);
81
- const setReportSubject = usePlayerStore((x) => x.setReportSubject);
106
+ export function ReportButton({
107
+ livestream,
108
+ setReportModalOpen,
109
+ setReportSubject,
110
+ }) {
82
111
  const { onOpenChange } = useRootContext();
83
112
  return (
84
113
  <DropdownMenuItem
@@ -231,8 +231,8 @@ function createStyles(theme: any) {
231
231
  },
232
232
 
233
233
  pillButton: {
234
- paddingHorizontal: theme.spacing[3],
235
- paddingVertical: theme.spacing[2],
234
+ paddingHorizontal: theme.spacing[2],
235
+ paddingVertical: theme.spacing[1],
236
236
  borderRadius: tokens.borderRadius.full,
237
237
  minHeight: tokens.touchTargets.minimum / 2,
238
238
  },
@@ -8,7 +8,7 @@ import {
8
8
  ChevronUp,
9
9
  Circle,
10
10
  } from "lucide-react-native";
11
- import { forwardRef, ReactNode, useMemo, useRef } from "react";
11
+ import React, { forwardRef, ReactNode, useMemo, useRef } from "react";
12
12
  import {
13
13
  Platform,
14
14
  Pressable,
@@ -200,6 +200,43 @@ export const DropdownMenuContent = forwardRef<
200
200
  );
201
201
  });
202
202
 
203
+ export const DropdownMenuContentWithoutPortal = forwardRef<
204
+ any,
205
+ DropdownMenuPrimitive.ContentProps & {
206
+ overlayStyle?: any;
207
+ }
208
+ >(({ overlayStyle, ...props }, ref) => {
209
+ return (
210
+ <DropdownMenuPrimitive.Overlay
211
+ style={[
212
+ Platform.OS !== "web" ? StyleSheet.absoluteFill : undefined,
213
+ overlayStyle,
214
+ ]}
215
+ >
216
+ <DropdownMenuPrimitive.Content
217
+ ref={ref}
218
+ style={
219
+ [
220
+ { zIndex: 999999 },
221
+ a.sizes.minWidth[32],
222
+ a.sizes.maxWidth[64],
223
+ a.overflow.hidden,
224
+ a.radius.all.md,
225
+ a.borders.width.thin,
226
+ a.borders.color.gray[800],
227
+ bg.gray[950],
228
+ p[2],
229
+ a.shadows.md,
230
+ ] as any
231
+ }
232
+ {...props}
233
+ />
234
+ </DropdownMenuPrimitive.Overlay>
235
+ );
236
+ });
237
+
238
+ /// Responsive Dropdown Menu Content. On mobile this will render a *bottom sheet* that is **portaled to the root of the app**.
239
+ /// Prefer passing scoped content in as **otherwise it may crash the app**.
203
240
  export const ResponsiveDropdownMenuContent = forwardRef<any, any>(
204
241
  ({ children, ...props }, ref) => {
205
242
  const { width } = useWindowDimensions();
@@ -222,8 +259,6 @@ export const ResponsiveDropdownMenuContent = forwardRef<any, any>(
222
259
  },
223
260
  );
224
261
 
225
- import React from "react";
226
-
227
262
  export const DropdownMenuItem = forwardRef<
228
263
  any,
229
264
  DropdownMenuPrimitive.ItemProps & { inset?: boolean; disabled?: boolean }
@@ -9,6 +9,8 @@ export * from "./button";
9
9
  export * from "./dialog";
10
10
  export * from "./dropdown";
11
11
  export * from "./icons";
12
+ export * from "./info-box";
13
+ export * from "./info-row";
12
14
  export * from "./input";
13
15
  export * from "./loader";
14
16
  export * from "./resizeable";
@@ -0,0 +1,60 @@
1
+ import { Text, View } from "react-native";
2
+ import * as zero from "../../ui";
3
+
4
+ const { bg, r, p, text, layout, gap, flex } = zero;
5
+
6
+ interface InfoBoxProps {
7
+ icon: any;
8
+ label: string;
9
+ value: string;
10
+ status?: "good" | "warning" | "error" | "neutral";
11
+ }
12
+
13
+ export function InfoBox({
14
+ icon: Icon,
15
+ label,
16
+ value,
17
+ status = "neutral",
18
+ }: InfoBoxProps) {
19
+ const statusColors = {
20
+ good: text.green[400],
21
+ warning: text.yellow[400],
22
+ error: text.red[400],
23
+ neutral: text.white,
24
+ };
25
+
26
+ const statusColor = statusColors[status];
27
+
28
+ return (
29
+ <View
30
+ style={[
31
+ flex.values[1],
32
+ layout.flex.column,
33
+ layout.flex.spaceBetween,
34
+ layout.flex.alignCenter,
35
+ bg.neutral[700],
36
+ r.sm,
37
+ p[2],
38
+ ]}
39
+ >
40
+ <View
41
+ style={[
42
+ layout.flex.row,
43
+ layout.flex.spaceBetween,
44
+ gap.all[3],
45
+ zero.w.percent[100],
46
+ ]}
47
+ >
48
+ <Text style={[text.gray[100], { fontSize: 13, fontWeight: "500" }]}>
49
+ {label}
50
+ </Text>
51
+ <Icon size={16} color="#9ca3af" />
52
+ </View>
53
+ <View style={[layout.flex.align.end, zero.w.percent[100]]}>
54
+ <Text style={[statusColor, { fontSize: 26, fontWeight: "600" }]}>
55
+ {value}
56
+ </Text>
57
+ </View>
58
+ </View>
59
+ );
60
+ }
@@ -0,0 +1,48 @@
1
+ import { Text, View } from "react-native";
2
+ import * as zero from "../../ui";
3
+
4
+ const { text, layout, py, gap } = zero;
5
+
6
+ interface InfoRowProps {
7
+ icon: any;
8
+ label: string;
9
+ value: string;
10
+ status?: "good" | "warning" | "error" | "neutral";
11
+ }
12
+
13
+ export function InfoRow({
14
+ icon: Icon,
15
+ label,
16
+ value,
17
+ status = "neutral",
18
+ }: InfoRowProps) {
19
+ const statusColors = {
20
+ good: text.green[400],
21
+ warning: text.yellow[400],
22
+ error: text.red[400],
23
+ neutral: text.white,
24
+ };
25
+
26
+ const statusColor = statusColors[status];
27
+
28
+ return (
29
+ <View
30
+ style={[
31
+ layout.flex.row,
32
+ layout.flex.spaceBetween,
33
+ layout.flex.alignCenter,
34
+ py[2],
35
+ ]}
36
+ >
37
+ <View style={[layout.flex.row, layout.flex.alignCenter, gap.all[3]]}>
38
+ <Icon size={16} color="#9ca3af" />
39
+ <Text style={[text.gray[300], { fontSize: 13, fontWeight: "500" }]}>
40
+ {label}
41
+ </Text>
42
+ </View>
43
+ <Text style={[statusColor, { fontSize: 13, fontWeight: "600" }]}>
44
+ {value}
45
+ </Text>
46
+ </View>
47
+ );
48
+ }
@@ -13,6 +13,116 @@ import {
13
13
  import { useSafeAreaInsets } from "react-native-safe-area-context";
14
14
  import { useTheme } from "../../lib/theme/theme";
15
15
 
16
+ import { useCallback } from "react";
17
+
18
+ type ToastController = {
19
+ show: (
20
+ title: string,
21
+ description?: string,
22
+ options?: {
23
+ duration?: number;
24
+ actionLabel?: string;
25
+ onAction?: () => void;
26
+ },
27
+ ) => void;
28
+ hide: () => void;
29
+ };
30
+
31
+ type UseToastReturn = {
32
+ open: boolean;
33
+ title: string;
34
+ description?: string;
35
+ actionLabel?: string;
36
+ onAction?: () => void;
37
+ duration?: number;
38
+ setOpen: (open: boolean) => void;
39
+ setTitle: (title: string) => void;
40
+ setDescription: (description: string) => void;
41
+ setActionLabel: (label: string) => void;
42
+ setOnAction: (cb?: () => void) => void;
43
+ setDuration: (duration: number) => void;
44
+ toastController: ToastController;
45
+ };
46
+
47
+ /**
48
+ * useToast - a hook to manage Toast state and provide a toastController.
49
+ * Returns a ready-to-render ToastComponent.
50
+ */
51
+ export function useToast(
52
+ initial: {
53
+ title?: string;
54
+ description?: string;
55
+ duration?: number;
56
+ actionLabel?: string;
57
+ onAction?: () => void;
58
+ } = {},
59
+ ) {
60
+ const [open, setOpen] = useState(false);
61
+ const [title, setTitle] = useState(initial.title ?? "");
62
+ const [description, setDescription] = useState(initial.description ?? "");
63
+ const [duration, setDuration] = useState(initial.duration ?? 3);
64
+ const [actionLabel, setActionLabel] = useState(
65
+ initial.actionLabel ?? "Action",
66
+ );
67
+ const [onAction, setOnAction] = useState<(() => void) | undefined>(
68
+ initial.onAction,
69
+ );
70
+
71
+ const show = useCallback(
72
+ (
73
+ toastTitle: string,
74
+ toastDescription?: string,
75
+ options?: {
76
+ duration?: number;
77
+ actionLabel?: string;
78
+ onAction?: () => void;
79
+ },
80
+ ) => {
81
+ setTitle(toastTitle);
82
+ setDescription(toastDescription ?? "");
83
+ setDuration(options?.duration ?? 3);
84
+ setActionLabel(options?.actionLabel ?? "Action");
85
+ setOnAction(options?.onAction);
86
+ setOpen(true);
87
+ },
88
+ [],
89
+ );
90
+
91
+ const hide = useCallback(() => {
92
+ setOpen(false);
93
+ }, []);
94
+
95
+ // Ready-to-render Toast component
96
+ const ToastComponent = (
97
+ <Toast
98
+ open={open}
99
+ onOpenChange={setOpen}
100
+ title={title}
101
+ description={description}
102
+ actionLabel={actionLabel}
103
+ onAction={onAction}
104
+ duration={duration}
105
+ />
106
+ );
107
+
108
+ return {
109
+ open,
110
+ title,
111
+ description,
112
+ actionLabel,
113
+ onAction,
114
+ duration,
115
+ setOpen,
116
+ setTitle,
117
+ setDescription,
118
+ setActionLabel,
119
+ setOnAction,
120
+ setDuration,
121
+ toastController: { show, hide },
122
+ ToastComponent,
123
+ };
124
+ }
125
+
16
126
  type ToastProps = {
17
127
  open: boolean;
18
128
  onOpenChange: (open: boolean) => void;
package/src/index.tsx CHANGED
@@ -34,3 +34,6 @@ export * from "./lib/system-messages";
34
34
  export * from "./components/share/sharesheet";
35
35
 
36
36
  export * from "./components/keep-awake";
37
+
38
+ // Dashboard components
39
+ export * as Dashboard from "./components/dashboard";