fix(web): portal overlays into fullscreenElement so they render in voice fullscreen
requestFullscreen() on the voice container puts only its descendants in the browser's top layer; overlays portaled to document.body were rendered outside that layer and stayed invisible — most visibly the right-click context menu on stream tiles and voice user panels. Add usePortalContainer() hook returning document.fullscreenElement ?? document.body and re-rendering on fullscreenchange. Migrate every overlay reachable during a call: ContextMenuRenderer (desktop, submenu, mobile sheet), Tooltip, ConfirmDialog, ConnectionInfoPopover, ScreenShareSettingsPopover, and ScreenSharePicker (which previously rendered inline at App root).
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { usePortalContainer } from '../../hooks/usePortalContainer';
|
||||
|
||||
interface ConfirmDialogProps {
|
||||
isOpen: boolean;
|
||||
@@ -38,6 +39,8 @@ export function ConfirmDialog({
|
||||
}
|
||||
}, [isOpen, handleKeyDown]);
|
||||
|
||||
const portalContainer = usePortalContainer();
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
const isDanger = variant === 'danger';
|
||||
@@ -74,6 +77,6 @@ export function ConfirmDialog({
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
portalContainer,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
type ContextMenuSubmenu,
|
||||
} from '../../stores/contextMenuStore';
|
||||
import { useUIStore } from '../../stores/uiStore';
|
||||
import { usePortalContainer } from '../../hooks/usePortalContainer';
|
||||
|
||||
// ── Desktop item button ──────────────────────────────────────────────────────
|
||||
|
||||
@@ -83,6 +84,7 @@ interface SubmenuFlyoutProps {
|
||||
|
||||
function SubmenuFlyout({ submenu, triggerRef, onMouseEnter, onMouseLeave, close }: SubmenuFlyoutProps) {
|
||||
const flyoutRef = useRef<HTMLDivElement>(null);
|
||||
const portalContainer = usePortalContainer();
|
||||
const filteredChildren = filterMenuItems(submenu.children);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
@@ -125,7 +127,7 @@ function SubmenuFlyout({ submenu, triggerRef, onMouseEnter, onMouseLeave, close
|
||||
<DesktopLeafItem key={child.key} item={child} close={close} />
|
||||
))}
|
||||
</div>,
|
||||
document.body,
|
||||
portalContainer,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -281,6 +283,7 @@ interface DesktopMenuProps {
|
||||
|
||||
function DesktopMenu({ items, position, close, closeGuard }: DesktopMenuProps) {
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const portalContainer = usePortalContainer();
|
||||
|
||||
// Viewport-aware positioning via direct DOM mutation
|
||||
useLayoutEffect(() => {
|
||||
@@ -411,7 +414,7 @@ function DesktopMenu({ items, position, close, closeGuard }: DesktopMenuProps) {
|
||||
))}
|
||||
</div>
|
||||
</>,
|
||||
document.body,
|
||||
portalContainer,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -463,6 +466,7 @@ interface MobileMenuProps {
|
||||
|
||||
function MobileMenu({ items, close }: MobileMenuProps) {
|
||||
const [submenuStack, setSubmenuStack] = useState<ContextMenuSubmenu | null>(null);
|
||||
const portalContainer = usePortalContainer();
|
||||
|
||||
// Dismiss on scroll/resize
|
||||
useEffect(() => {
|
||||
@@ -527,7 +531,7 @@ function MobileMenu({ items, close }: MobileMenuProps) {
|
||||
</div>
|
||||
</div>
|
||||
</>,
|
||||
document.body,
|
||||
portalContainer,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useFloatingPosition } from '../../hooks/useFloatingPosition';
|
||||
import { usePortalContainer } from '../../hooks/usePortalContainer';
|
||||
import { useUIStore } from '../../stores/uiStore';
|
||||
|
||||
interface TooltipProps {
|
||||
@@ -19,6 +20,7 @@ export function Tooltip({ content, children, position = 'right', delay = 200 }:
|
||||
const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const anchorRef = useRef<HTMLDivElement>(null);
|
||||
const floatingRef = useRef<HTMLDivElement>(null);
|
||||
const portalContainer = usePortalContainer();
|
||||
|
||||
const { style } = useFloatingPosition(anchorRef, floatingRef, {
|
||||
placement: position,
|
||||
@@ -52,7 +54,7 @@ export function Tooltip({ content, children, position = 'right', delay = 200 }:
|
||||
>
|
||||
{content}
|
||||
</div>,
|
||||
document.body,
|
||||
portalContainer,
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user