@streamplace/components 0.8.8 → 0.8.9
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/dist/components/mobile-player/ui/viewer-context-menu.js +7 -6
- package/dist/components/ui/dropdown.js +227 -20
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +2 -2
- package/src/components/mobile-player/ui/viewer-context-menu.tsx +49 -26
- package/src/components/ui/dropdown.tsx +391 -41
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -25,7 +25,6 @@ function ContextMenu({ dropdownPortalContainer, }) {
|
|
|
25
25
|
const setReportModalOpen = (0, player_store_1.usePlayerStore)((x) => x.setReportModalOpen);
|
|
26
26
|
const setReportSubject = (0, player_store_1.usePlayerStore)((x) => x.setReportSubject);
|
|
27
27
|
const { profile } = (0, __1.useLivestreamInfo)();
|
|
28
|
-
console.log("profile", profile);
|
|
29
28
|
const avatars = (0, __1.useAvatars)(profile?.did ? [profile?.did] : []);
|
|
30
29
|
const ls = (0, livestream_store_1.useLivestreamStore)((x) => x.livestream);
|
|
31
30
|
const segment = (0, livestream_store_1.useLivestreamStore)((x) => x.segment);
|
|
@@ -41,10 +40,7 @@ function ContextMenu({ dropdownPortalContainer, }) {
|
|
|
41
40
|
const isMobile = react_native_1.Platform.OS === "ios" || react_native_1.Platform.OS === "android";
|
|
42
41
|
// dummy portal for mobile
|
|
43
42
|
const Portal = isMobile ? react_native_1.View : ui_2.DropdownMenuPortal;
|
|
44
|
-
|
|
45
|
-
const DropdownMenuContent = isMobile
|
|
46
|
-
? ui_2.ResponsiveDropdownMenuContent
|
|
47
|
-
: ui_2.DropdownMenuContentWithoutPortal;
|
|
43
|
+
const DropdownMenuContent = ui_2.ResponsiveDropdownMenuContent;
|
|
48
44
|
return ((0, jsx_runtime_1.jsxs)(ui_2.DropdownMenu, { children: [(0, jsx_runtime_1.jsx)(ui_2.DropdownMenuTrigger, { children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.Menu, { color: theme_1.colors.gray[200] }) }), (0, jsx_runtime_1.jsx)(Portal, { container: dropdownPortalContainer, children: (0, jsx_runtime_1.jsxs)(DropdownMenuContent, { side: "top", align: "end", children: [react_native_1.Platform.OS !== "web" && ((0, jsx_runtime_1.jsxs)(ui_2.DropdownMenuGroup, { title: "Streamer", children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
49
45
|
__1.zero.layout.flex.row,
|
|
50
46
|
__1.zero.layout.flex.center,
|
|
@@ -82,7 +78,12 @@ function ContextMenu({ dropdownPortalContainer, }) {
|
|
|
82
78
|
const url = `https://bsky.app/profile/${profile.handle}`;
|
|
83
79
|
react_native_1.Linking.openURL(url);
|
|
84
80
|
}
|
|
85
|
-
}, children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "View Profile on Bluesky" }) })] })), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, {
|
|
81
|
+
}, children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "View Profile on Bluesky" }) })] })), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, { children: (0, jsx_runtime_1.jsxs)(ui_2.DropdownMenuSub, { children: [(0, jsx_runtime_1.jsx)(ui_2.DropdownMenuSubTrigger, { subMenuTitle: "Quality", children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
82
|
+
__1.zero.flex.values[1],
|
|
83
|
+
__1.zero.layout.flex.row,
|
|
84
|
+
__1.zero.layout.flex.spaceBetween,
|
|
85
|
+
__1.zero.pr[4],
|
|
86
|
+
], children: [(0, jsx_runtime_1.jsx)(ui_2.Text, { children: "Quality" }), (0, jsx_runtime_1.jsxs)(ui_2.Text, { muted: true, children: ["(", quality, ", ", lowLatency ? "low latency" : "regular latency", ")"] })] }) }), (0, jsx_runtime_1.jsxs)(ui_2.DropdownMenuSubContent, { children: [(0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, { title: "Resolution", children: (0, jsx_runtime_1.jsxs)(ui_2.DropdownMenuRadioGroup, { value: quality, onValueChange: setQuality, children: [(0, jsx_runtime_1.jsx)(ui_2.DropdownMenuRadioItem, { value: "source", children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "Source (Original Quality)" }) }), qualities.map((r) => ((0, jsx_runtime_1.jsx)(ui_2.DropdownMenuRadioItem, { value: r.name, children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: r.name }) }, r.name)))] }) }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, { children: (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuCheckboxItem, { checked: lowLatency, onCheckedChange: () => setLowLatency(!lowLatency), children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "Low Latency" }) }) }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuInfo, { description: "Reduces the delay between video and chat for a more real-time experience." })] })] }) }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, { title: "Advanced", children: (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuCheckboxItem, { checked: debugInfo, onCheckedChange: () => setShowDebugInfo(!debugInfo), children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "Show Debug Info" }) }) }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, { title: "Report", children: (0, jsx_runtime_1.jsx)(ReportButton, { livestream: livestream, setReportModalOpen: setReportModalOpen, setReportSubject: setReportSubject }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [ui_1.pt[3], ui_1.px[2], ui_1.gap.all[2]], children: [contentWarnings && contentWarnings.length > 0 && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [ui_1.gap.all[1]], children: [(0, jsx_runtime_1.jsx)(ui_2.Text, { size: "base", color: "muted", children: "Stream may contain" }), (0, jsx_runtime_1.jsx)(__1.ContentWarnings, { warnings: contentWarnings, compact: true })] })), contentRights && Object.keys(contentRights).length > 0 && ((0, jsx_runtime_1.jsx)(__1.ContentRights, { contentRights: contentRights, size: "xs", color: "muted" }))] })] }) })] }));
|
|
86
87
|
}
|
|
87
88
|
function ReportButton({ livestream, setReportModalOpen, setReportSubject, }) {
|
|
88
89
|
const { onOpenChange } = (0, dropdown_menu_1.useRootContext)();
|
|
@@ -1,40 +1,218 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DropdownMenuInfo = exports.DropdownMenuGroup = exports.DropdownMenuSeparator = exports.DropdownMenuLabel = exports.DropdownMenuRadioItem = exports.DropdownMenuCheckboxItem = exports.DropdownMenuItem = exports.ResponsiveDropdownMenuContent = exports.DropdownMenuContentWithoutPortal = exports.DropdownMenuContent = exports.DropdownMenuSubContent = exports.DropdownMenuSubTrigger = exports.DropdownMenuBottomSheet = exports.
|
|
3
|
+
exports.DropdownMenuInfo = exports.DropdownMenuGroup = exports.DropdownMenuSeparator = exports.DropdownMenuLabel = exports.DropdownMenuRadioItem = exports.DropdownMenuCheckboxItem = exports.DropdownMenuItem = exports.ResponsiveDropdownMenuContent = exports.DropdownMenuContentWithoutPortal = exports.DropdownMenuContent = exports.DropdownMenuSubContent = exports.DropdownMenuSubTrigger = exports.DropdownMenuBottomSheet = exports.DropdownMenuSub = exports.DropdownMenuRadioGroup = exports.DropdownMenuPortal = exports.DropdownMenuTrigger = exports.DropdownMenu = void 0;
|
|
4
4
|
exports.DropdownMenuShortcut = DropdownMenuShortcut;
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
6
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
7
|
const bottom_sheet_1 = tslib_1.__importStar(require("@gorhom/bottom-sheet"));
|
|
8
8
|
const DropdownMenuPrimitive = tslib_1.__importStar(require("@rn-primitives/dropdown-menu"));
|
|
9
9
|
const lucide_react_native_1 = require("lucide-react-native");
|
|
10
|
-
const react_1 = require("react");
|
|
10
|
+
const react_1 = tslib_1.__importStar(require("react"));
|
|
11
11
|
const react_native_1 = require("react-native");
|
|
12
|
-
const
|
|
12
|
+
const react_native_reanimated_1 = tslib_1.__importStar(require("react-native-reanimated"));
|
|
13
|
+
const react_native_safe_area_context_1 = require("react-native-safe-area-context");
|
|
13
14
|
const atoms_1 = require("../../lib/theme/atoms");
|
|
14
15
|
const ui_1 = require("../../ui");
|
|
15
16
|
const text_1 = require("./primitives/text");
|
|
16
17
|
const text_2 = require("./text");
|
|
18
|
+
const NavigationStackContext = (0, react_1.createContext)(null);
|
|
19
|
+
const useNavigationStack = () => {
|
|
20
|
+
const context = (0, react_1.useContext)(NavigationStackContext);
|
|
21
|
+
return context;
|
|
22
|
+
};
|
|
23
|
+
const SubMenuContext = (0, react_1.createContext)(null);
|
|
17
24
|
exports.DropdownMenu = DropdownMenuPrimitive.Root;
|
|
18
25
|
exports.DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
19
26
|
exports.DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
20
|
-
exports.DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
21
27
|
exports.DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
28
|
+
// Custom DropdownMenuSub that works with mobile navigation
|
|
29
|
+
exports.DropdownMenuSub = (0, react_1.forwardRef)(({ children, ...props }, ref) => {
|
|
30
|
+
const navStack = useNavigationStack();
|
|
31
|
+
const [subMenuTitle, setSubMenuTitle] = (0, react_1.useState)();
|
|
32
|
+
const renderContentRef = (0, react_1.useRef)(null);
|
|
33
|
+
const [subMenuKey, setSubMenuKey] = (0, react_1.useState)(null);
|
|
34
|
+
// If we're in a mobile navigation stack, use custom context
|
|
35
|
+
if (navStack) {
|
|
36
|
+
const trigger = () => {
|
|
37
|
+
if (renderContentRef.current) {
|
|
38
|
+
const key = `submenu-${Date.now()}`;
|
|
39
|
+
setSubMenuKey(key);
|
|
40
|
+
navStack.push({
|
|
41
|
+
key,
|
|
42
|
+
title: subMenuTitle,
|
|
43
|
+
// Store a function that always reads the latest content from the ref
|
|
44
|
+
content: (props) => {
|
|
45
|
+
const renderFn = renderContentRef.current;
|
|
46
|
+
return renderFn ? renderFn() : null;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const setRenderContent = (renderer) => {
|
|
52
|
+
renderContentRef.current = renderer;
|
|
53
|
+
};
|
|
54
|
+
const contextValue = react_1.default.useMemo(() => ({
|
|
55
|
+
renderContent: () => renderContentRef.current?.(),
|
|
56
|
+
setRenderContent,
|
|
57
|
+
title: subMenuTitle,
|
|
58
|
+
setTitle: setSubMenuTitle,
|
|
59
|
+
trigger,
|
|
60
|
+
key: subMenuKey,
|
|
61
|
+
}), [subMenuTitle, subMenuKey]);
|
|
62
|
+
return ((0, jsx_runtime_1.jsx)(SubMenuContext.Provider, { value: contextValue, children: children }));
|
|
63
|
+
}
|
|
64
|
+
// Web - use primitive
|
|
65
|
+
return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Sub, { ref: ref, ...props, children: children }));
|
|
66
|
+
});
|
|
22
67
|
exports.DropdownMenuBottomSheet = (0, react_1.forwardRef)(function DropdownMenuBottomSheet({ overlayStyle, portalHost, children, ...rest }, _ref) {
|
|
23
68
|
// Use the primitives' context to know if open
|
|
24
|
-
const {
|
|
25
|
-
const { zero: zt } = (0, ui_1.useTheme)();
|
|
69
|
+
const { onOpenChange } = DropdownMenuPrimitive.useRootContext();
|
|
70
|
+
const { zero: zt, theme } = (0, ui_1.useTheme)();
|
|
26
71
|
const sheetRef = (0, react_1.useRef)(null);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
72
|
+
const { width } = (0, react_native_1.useWindowDimensions)();
|
|
73
|
+
const isWide = react_native_1.Platform.OS !== "web" && width >= 800;
|
|
74
|
+
const sheetWidth = isWide ? 450 : width;
|
|
75
|
+
const horizontalMargin = isWide ? (width - sheetWidth) / 2 : 0;
|
|
76
|
+
const insets = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
|
|
77
|
+
// Navigation stack state
|
|
78
|
+
const [stack, setStack] = (0, react_1.useState)([
|
|
79
|
+
{ key: "root", content: children },
|
|
80
|
+
]);
|
|
81
|
+
// Update root content when children changes
|
|
82
|
+
react_1.default.useEffect(() => {
|
|
83
|
+
setStack((prev) => {
|
|
84
|
+
if (!Array.isArray(prev) || prev.length === 0) {
|
|
85
|
+
return [{ key: "root", content: children }];
|
|
86
|
+
}
|
|
87
|
+
// Update the root item content
|
|
88
|
+
const newStack = [...prev];
|
|
89
|
+
newStack[0] = { ...newStack[0], content: children };
|
|
90
|
+
return newStack;
|
|
91
|
+
});
|
|
92
|
+
}, [children]);
|
|
93
|
+
const slideAnim = (0, react_native_reanimated_1.useSharedValue)(0);
|
|
94
|
+
const fadeAnim = (0, react_native_reanimated_1.useSharedValue)(1);
|
|
95
|
+
const push = (item) => {
|
|
96
|
+
// First, update the stack
|
|
97
|
+
setStack((prev) => {
|
|
98
|
+
if (!Array.isArray(prev))
|
|
99
|
+
return [{ key: "root", content: children }, item];
|
|
100
|
+
return [...prev, item];
|
|
101
|
+
});
|
|
102
|
+
// Then animate from right to center with fade
|
|
103
|
+
slideAnim.value = 40;
|
|
104
|
+
fadeAnim.value = 0;
|
|
105
|
+
slideAnim.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 350 });
|
|
106
|
+
fadeAnim.value = (0, react_native_reanimated_1.withTiming)(1, { duration: 350 });
|
|
107
|
+
};
|
|
108
|
+
const popStack = () => {
|
|
109
|
+
(0, react_1.startTransition)(() => {
|
|
110
|
+
setStack((prev) => {
|
|
111
|
+
if (!Array.isArray(prev) || prev.length <= 1) {
|
|
112
|
+
return [{ key: "root", content: children }];
|
|
113
|
+
}
|
|
114
|
+
return prev.slice(0, -1);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
const resetAnimationValues = () => {
|
|
119
|
+
setTimeout(() => {
|
|
120
|
+
slideAnim.value = 0;
|
|
121
|
+
fadeAnim.value = 1;
|
|
122
|
+
}, 5);
|
|
123
|
+
};
|
|
124
|
+
const pop = () => {
|
|
125
|
+
if (stack.length <= 1)
|
|
126
|
+
return;
|
|
127
|
+
// Animate out to the right with fade
|
|
128
|
+
slideAnim.value = (0, react_native_reanimated_1.withTiming)(40, { duration: 150 });
|
|
129
|
+
fadeAnim.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 150 }, (finished) => {
|
|
130
|
+
if (finished) {
|
|
131
|
+
// Update stack first with startTransition for smoother render
|
|
132
|
+
(0, react_native_reanimated_1.runOnJS)(popStack)();
|
|
133
|
+
// Then reset animation position after a brief delay to ensure component has unmounted
|
|
134
|
+
(0, react_native_reanimated_1.runOnJS)(resetAnimationValues)();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
const animatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
|
|
139
|
+
transform: [{ translateX: slideAnim.value }],
|
|
140
|
+
opacity: fadeAnim.value,
|
|
141
|
+
}));
|
|
142
|
+
const headerAnimatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
|
|
143
|
+
opacity: fadeAnim.value,
|
|
144
|
+
}));
|
|
145
|
+
const currentLevel = stack[stack.length - 1];
|
|
146
|
+
const isNested = stack.length > 1;
|
|
147
|
+
const onBackgroundTap = () => {
|
|
148
|
+
if (sheetRef.current)
|
|
149
|
+
sheetRef.current?.close();
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
onOpenChange?.(false);
|
|
152
|
+
}, 300);
|
|
153
|
+
};
|
|
154
|
+
// Safety check - if no current level, don't render
|
|
155
|
+
if (!currentLevel) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Portal, { hostName: portalHost, children: (0, jsx_runtime_1.jsx)(NavigationStackContext.Provider, { value: { stack, push, pop, isNested }, children: (0, jsx_runtime_1.jsxs)(bottom_sheet_1.default, { ref: sheetRef, enablePanDownToClose: true, enableDynamicSizing: true, detached: isWide, bottomInset: isWide ? 0 : 0, backdropComponent: ({ style }) => ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: [style, react_native_1.StyleSheet.absoluteFill], onPress: () => onBackgroundTap() })), onClose: () => onOpenChange?.(false), style: [
|
|
159
|
+
overlayStyle,
|
|
160
|
+
react_native_1.StyleSheet.flatten(rest.style),
|
|
161
|
+
isWide && { marginHorizontal: horizontalMargin },
|
|
162
|
+
], backgroundStyle: [zt.bg.popover, atoms_1.a.radius.all.md, atoms_1.a.shadows.md, atoms_1.p[1]], handleIndicatorStyle: [
|
|
163
|
+
atoms_1.a.sizes.width[12],
|
|
164
|
+
atoms_1.a.sizes.height[1],
|
|
165
|
+
zt.bg.mutedForeground,
|
|
166
|
+
], children: [isNested && ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: [
|
|
167
|
+
headerAnimatedStyle,
|
|
168
|
+
atoms_1.a.layout.flex.row,
|
|
169
|
+
atoms_1.a.layout.flex.alignCenter,
|
|
170
|
+
atoms_1.px[4],
|
|
171
|
+
atoms_1.pb[2],
|
|
172
|
+
{
|
|
173
|
+
borderBottomWidth: 1,
|
|
174
|
+
borderBottomColor: theme.colors.border,
|
|
175
|
+
},
|
|
176
|
+
], children: (0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { onPress: pop, style: [
|
|
177
|
+
atoms_1.a.layout.flex.row,
|
|
178
|
+
atoms_1.a.layout.flex.alignCenter,
|
|
179
|
+
atoms_1.gap.all[2],
|
|
180
|
+
], hitSlop: 80, children: [(0, jsx_runtime_1.jsx)(lucide_react_native_1.ChevronLeft, { size: 20, color: theme.colors.foreground }), currentLevel?.title ? ((0, jsx_runtime_1.jsx)(text_2.Text, { size: "lg", children: currentLevel.title })) : null] }) })), (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: animatedStyle, children: (0, jsx_runtime_1.jsx)(bottom_sheet_1.BottomSheetScrollView, { style: [atoms_1.px[4]], contentContainerStyle: {
|
|
181
|
+
paddingBottom: insets.bottom + 50,
|
|
182
|
+
overflow: "hidden",
|
|
183
|
+
}, children: stack.map((level, index) => {
|
|
184
|
+
const isCurrent = index === stack.length - 1;
|
|
185
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [{ display: isCurrent ? "flex" : "none" }], children: typeof level.content === "function"
|
|
186
|
+
? level.content({ pressed: true })
|
|
187
|
+
: level.content }, level.key));
|
|
188
|
+
}) }) })] }) }) }));
|
|
34
189
|
});
|
|
35
|
-
exports.DropdownMenuSubTrigger = (0, react_1.forwardRef)(({ inset, children, ...props }, ref) => {
|
|
190
|
+
exports.DropdownMenuSubTrigger = (0, react_1.forwardRef)(({ inset, children, subMenuTitle, ...props }, ref) => {
|
|
191
|
+
const navStack = useNavigationStack();
|
|
192
|
+
const subMenuContext = (0, react_1.useContext)(SubMenuContext);
|
|
193
|
+
const { icons, theme } = (0, ui_1.useTheme)();
|
|
194
|
+
// Set the title in the submenu context if provided
|
|
195
|
+
react_1.default.useEffect(() => {
|
|
196
|
+
if (subMenuContext && subMenuTitle) {
|
|
197
|
+
subMenuContext.setTitle(subMenuTitle);
|
|
198
|
+
}
|
|
199
|
+
}, [subMenuContext, subMenuTitle]);
|
|
200
|
+
// If we're in a navigation stack (mobile bottom sheet), handle differently
|
|
201
|
+
if (navStack && subMenuContext) {
|
|
202
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => {
|
|
203
|
+
subMenuContext.trigger();
|
|
204
|
+
}, ...props, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
205
|
+
inset && atoms_1.gap[2],
|
|
206
|
+
atoms_1.layout.flex.row,
|
|
207
|
+
atoms_1.layout.flex.alignCenter,
|
|
208
|
+
atoms_1.a.radius.all.sm,
|
|
209
|
+
atoms_1.py[1],
|
|
210
|
+
atoms_1.pl[2],
|
|
211
|
+
atoms_1.pr[2],
|
|
212
|
+
], children: [typeof children === "function" ? (children({ pressed: true })) : typeof children === "string" ? ((0, jsx_runtime_1.jsx)(text_2.Text, { children: children })) : (children), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [atoms_1.a.layout.position.absolute, atoms_1.a.position.right[1]], children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.ChevronRight, { size: 18, color: icons.color.muted }) })] }) }));
|
|
213
|
+
}
|
|
214
|
+
// Web behavior - use primitive
|
|
36
215
|
const { open } = DropdownMenuPrimitive.useSubContext();
|
|
37
|
-
const { icons } = (0, ui_1.useTheme)();
|
|
38
216
|
const Icon = react_native_1.Platform.OS === "web" ? lucide_react_native_1.ChevronRight : open ? lucide_react_native_1.ChevronUp : lucide_react_native_1.ChevronDown;
|
|
39
217
|
return ((0, jsx_runtime_1.jsx)(text_1.TextContext.Provider, { value: (0, text_1.objectFromObjects)([
|
|
40
218
|
atoms_1.a.textColors.primary[500],
|
|
@@ -48,8 +226,35 @@ exports.DropdownMenuSubTrigger = (0, react_1.forwardRef)(({ inset, children, ...
|
|
|
48
226
|
atoms_1.pr[8],
|
|
49
227
|
], children: [children, (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [atoms_1.a.layout.position.absolute, atoms_1.a.position.right[1]], children: (0, jsx_runtime_1.jsx)(Icon, { size: 18, color: icons.color.muted }) })] }) }) }));
|
|
50
228
|
});
|
|
51
|
-
exports.DropdownMenuSubContent = (0, react_1.forwardRef)((props, ref) => {
|
|
229
|
+
exports.DropdownMenuSubContent = (0, react_1.forwardRef)(({ children, ...props }, ref) => {
|
|
52
230
|
const { zero: zt } = (0, ui_1.useTheme)();
|
|
231
|
+
const subMenuContext = (0, react_1.useContext)(SubMenuContext);
|
|
232
|
+
const navStack = useNavigationStack();
|
|
233
|
+
const prevChildrenRef = (0, react_1.useRef)(null);
|
|
234
|
+
// Register a render function that will be called fresh each time
|
|
235
|
+
react_1.default.useEffect(() => {
|
|
236
|
+
if (subMenuContext && navStack) {
|
|
237
|
+
// Only update if children reference actually changed
|
|
238
|
+
if (prevChildrenRef.current === children) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
prevChildrenRef.current = children;
|
|
242
|
+
// Pass a function that returns the current children
|
|
243
|
+
subMenuContext.setRenderContent(() => children);
|
|
244
|
+
// Force a stack update to trigger rerender with the actual children
|
|
245
|
+
if (subMenuContext.key) {
|
|
246
|
+
// Store the children directly so React can handle updates
|
|
247
|
+
//navStack.updateContent(subMenuContext.key, children);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}, [children, subMenuContext, navStack]);
|
|
251
|
+
// On mobile, don't render the subcontent here - it'll be rendered in the nav stack
|
|
252
|
+
// But keep the component mounted so effects run when children change
|
|
253
|
+
if (navStack && subMenuContext) {
|
|
254
|
+
// Component stays mounted to track prop changes, but renders nothing
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
// Web - use primitive
|
|
53
258
|
return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.SubContent, { ref: ref, style: [
|
|
54
259
|
atoms_1.a.zIndex[50],
|
|
55
260
|
atoms_1.a.sizes.minWidth[32],
|
|
@@ -61,10 +266,12 @@ exports.DropdownMenuSubContent = (0, react_1.forwardRef)((props, ref) => {
|
|
|
61
266
|
zt.bg.popover,
|
|
62
267
|
atoms_1.p[1],
|
|
63
268
|
atoms_1.a.shadows.md,
|
|
64
|
-
], ...props }));
|
|
269
|
+
], ...props, children: children }));
|
|
65
270
|
});
|
|
66
271
|
exports.DropdownMenuContent = (0, react_1.forwardRef)(({ overlayStyle, portalHost, style, children, ...props }, ref) => {
|
|
67
272
|
const { zero: zt } = (0, ui_1.useTheme)();
|
|
273
|
+
const { height } = (0, react_native_1.useWindowDimensions)();
|
|
274
|
+
const maxHeight = height * 0.8;
|
|
68
275
|
return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Portal, { hostName: portalHost, children: (0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Overlay, { style: [
|
|
69
276
|
react_native_1.Platform.OS !== "web" ? react_native_1.StyleSheet.absoluteFill : undefined,
|
|
70
277
|
overlayStyle,
|
|
@@ -80,7 +287,7 @@ exports.DropdownMenuContent = (0, react_1.forwardRef)(({ overlayStyle, portalHos
|
|
|
80
287
|
atoms_1.p[2],
|
|
81
288
|
atoms_1.a.shadows.md,
|
|
82
289
|
style,
|
|
83
|
-
], ...props, children: (0, jsx_runtime_1.jsx)(react_native_1.ScrollView, { showsVerticalScrollIndicator: true, children: typeof children === "function"
|
|
290
|
+
], ...props, children: (0, jsx_runtime_1.jsx)(react_native_1.ScrollView, { style: { maxHeight }, showsVerticalScrollIndicator: true, children: typeof children === "function"
|
|
84
291
|
? children({ pressed: false })
|
|
85
292
|
: children }) }) }) }));
|
|
86
293
|
});
|
|
@@ -111,9 +318,9 @@ exports.DropdownMenuContentWithoutPortal = (0, react_1.forwardRef)(({ overlaySty
|
|
|
111
318
|
exports.ResponsiveDropdownMenuContent = (0, react_1.forwardRef)(({ children, ...props }, ref) => {
|
|
112
319
|
const { width } = (0, react_native_1.useWindowDimensions)();
|
|
113
320
|
// On web, you might want to always use the normal dropdown
|
|
114
|
-
const isBottomSheet = react_native_1.Platform.OS !== "web"
|
|
321
|
+
const isBottomSheet = react_native_1.Platform.OS !== "web";
|
|
115
322
|
if (isBottomSheet) {
|
|
116
|
-
return ((0, jsx_runtime_1.jsx)(exports.DropdownMenuBottomSheet, { ref: ref, ...props, children:
|
|
323
|
+
return ((0, jsx_runtime_1.jsx)(exports.DropdownMenuBottomSheet, { ref: ref, ...props, children: children }));
|
|
117
324
|
}
|
|
118
325
|
return ((0, jsx_runtime_1.jsx)(exports.DropdownMenuContent, { ref: ref, ...props, children: children }));
|
|
119
326
|
});
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamplace/components",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
4
4
|
"description": "Streamplace React (Native) Components",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "src/index.tsx",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"start": "tsc --watch --preserveWatchOutput",
|
|
57
57
|
"prepare": "tsc"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "2ac59bf91315b2b1f848842c1048a23bd74658b7"
|
|
60
60
|
}
|
|
@@ -16,7 +16,6 @@ import { gap, pt, px } from "../../../ui";
|
|
|
16
16
|
import {
|
|
17
17
|
DropdownMenu,
|
|
18
18
|
DropdownMenuCheckboxItem,
|
|
19
|
-
DropdownMenuContentWithoutPortal,
|
|
20
19
|
DropdownMenuGroup,
|
|
21
20
|
DropdownMenuInfo,
|
|
22
21
|
DropdownMenuItem,
|
|
@@ -24,6 +23,9 @@ import {
|
|
|
24
23
|
DropdownMenuRadioGroup,
|
|
25
24
|
DropdownMenuRadioItem,
|
|
26
25
|
DropdownMenuSeparator,
|
|
26
|
+
DropdownMenuSub,
|
|
27
|
+
DropdownMenuSubContent,
|
|
28
|
+
DropdownMenuSubTrigger,
|
|
27
29
|
DropdownMenuTrigger,
|
|
28
30
|
ResponsiveDropdownMenuContent,
|
|
29
31
|
Text,
|
|
@@ -50,7 +52,6 @@ export function ContextMenu({
|
|
|
50
52
|
|
|
51
53
|
const { profile } = useLivestreamInfo();
|
|
52
54
|
|
|
53
|
-
console.log("profile", profile);
|
|
54
55
|
const avatars = useAvatars(profile?.did ? [profile?.did] : []);
|
|
55
56
|
const ls = useLivestreamStore((x) => x.livestream);
|
|
56
57
|
const segment = useLivestreamStore((x) => x.segment);
|
|
@@ -72,10 +73,7 @@ export function ContextMenu({
|
|
|
72
73
|
// dummy portal for mobile
|
|
73
74
|
const Portal = isMobile ? View : DropdownMenuPortal;
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
const DropdownMenuContent = isMobile
|
|
77
|
-
? ResponsiveDropdownMenuContent
|
|
78
|
-
: DropdownMenuContentWithoutPortal;
|
|
76
|
+
const DropdownMenuContent = ResponsiveDropdownMenuContent;
|
|
79
77
|
|
|
80
78
|
return (
|
|
81
79
|
<DropdownMenu>
|
|
@@ -175,28 +173,53 @@ export function ContextMenu({
|
|
|
175
173
|
</DropdownMenuGroup>
|
|
176
174
|
)}
|
|
177
175
|
|
|
178
|
-
<DropdownMenuGroup
|
|
179
|
-
<
|
|
180
|
-
<
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
176
|
+
<DropdownMenuGroup>
|
|
177
|
+
<DropdownMenuSub>
|
|
178
|
+
<DropdownMenuSubTrigger subMenuTitle="Quality">
|
|
179
|
+
<View
|
|
180
|
+
style={[
|
|
181
|
+
zero.flex.values[1],
|
|
182
|
+
zero.layout.flex.row,
|
|
183
|
+
zero.layout.flex.spaceBetween,
|
|
184
|
+
zero.pr[4],
|
|
185
|
+
]}
|
|
186
|
+
>
|
|
187
|
+
<Text>Quality</Text>
|
|
188
|
+
<Text muted>
|
|
189
|
+
({quality}, {lowLatency ? "low latency" : "regular latency"}
|
|
190
|
+
)
|
|
191
|
+
</Text>
|
|
192
|
+
</View>
|
|
193
|
+
</DropdownMenuSubTrigger>
|
|
194
|
+
<DropdownMenuSubContent>
|
|
195
|
+
<DropdownMenuGroup title="Resolution">
|
|
196
|
+
<DropdownMenuRadioGroup
|
|
197
|
+
value={quality}
|
|
198
|
+
onValueChange={setQuality}
|
|
199
|
+
>
|
|
200
|
+
<DropdownMenuRadioItem value="source">
|
|
201
|
+
<Text>Source (Original Quality)</Text>
|
|
202
|
+
</DropdownMenuRadioItem>
|
|
203
|
+
{qualities.map((r) => (
|
|
204
|
+
<DropdownMenuRadioItem key={r.name} value={r.name}>
|
|
205
|
+
<Text>{r.name}</Text>
|
|
206
|
+
</DropdownMenuRadioItem>
|
|
207
|
+
))}
|
|
208
|
+
</DropdownMenuRadioGroup>
|
|
209
|
+
</DropdownMenuGroup>
|
|
210
|
+
<DropdownMenuGroup>
|
|
211
|
+
<DropdownMenuCheckboxItem
|
|
212
|
+
checked={lowLatency}
|
|
213
|
+
onCheckedChange={() => setLowLatency(!lowLatency)}
|
|
214
|
+
>
|
|
215
|
+
<Text>Low Latency</Text>
|
|
216
|
+
</DropdownMenuCheckboxItem>
|
|
217
|
+
</DropdownMenuGroup>
|
|
218
|
+
<DropdownMenuInfo description="Reduces the delay between video and chat for a more real-time experience." />
|
|
219
|
+
</DropdownMenuSubContent>
|
|
220
|
+
</DropdownMenuSub>
|
|
189
221
|
</DropdownMenuGroup>
|
|
190
222
|
<DropdownMenuGroup title="Advanced">
|
|
191
|
-
<DropdownMenuCheckboxItem
|
|
192
|
-
checked={lowLatency}
|
|
193
|
-
onCheckedChange={() => setLowLatency(!lowLatency)}
|
|
194
|
-
>
|
|
195
|
-
<Text>Low Latency</Text>
|
|
196
|
-
</DropdownMenuCheckboxItem>
|
|
197
|
-
</DropdownMenuGroup>
|
|
198
|
-
<DropdownMenuInfo description="Reduces the delay between video and chat for a more real-time experience." />
|
|
199
|
-
<DropdownMenuGroup>
|
|
200
223
|
<DropdownMenuCheckboxItem
|
|
201
224
|
checked={debugInfo}
|
|
202
225
|
onCheckedChange={() => setShowDebugInfo(!debugInfo)}
|