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:
@@ -3,6 +3,7 @@ import { createPortal } from 'react-dom';
|
||||
import { useTrackStats, AudioTrackStat, VideoTrackStat } from '../../hooks/useTrackStats';
|
||||
import { getActiveRoom } from '../../hooks/useLiveKit';
|
||||
import { useFloatingPosition } from '../../hooks/useFloatingPosition';
|
||||
import { usePortalContainer } from '../../hooks/usePortalContainer';
|
||||
|
||||
interface ConnectionInfoPopoverProps {
|
||||
open: boolean;
|
||||
@@ -118,6 +119,7 @@ function VideoTrackRow({ track }: { track: VideoTrackStat }) {
|
||||
|
||||
export function ConnectionInfoPopover({ open, onClose, anchorRef }: ConnectionInfoPopoverProps) {
|
||||
const popoverRef = useRef<HTMLDivElement>(null);
|
||||
const portalContainer = usePortalContainer();
|
||||
const stats = useTrackStats(open);
|
||||
|
||||
const { style } = useFloatingPosition(anchorRef, popoverRef, {
|
||||
@@ -211,6 +213,6 @@ export function ConnectionInfoPopover({ open, onClose, anchorRef }: ConnectionIn
|
||||
)}
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
portalContainer,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { create } from 'zustand';
|
||||
import { getElectronAPI } from '../../platform/platform';
|
||||
import { useVoiceStore } from '../../stores/voiceStore';
|
||||
import { usePortalContainer } from '../../hooks/usePortalContainer';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Zustand micro-store — bridges the event-driven API to React state
|
||||
@@ -40,6 +42,7 @@ export function ScreenSharePicker() {
|
||||
const [search, setSearch] = useState('');
|
||||
const shareAudio = useVoiceStore((s) => s.screenShareConfig.shareAudio);
|
||||
const setScreenShareConfig = useVoiceStore((s) => s.setScreenShareConfig);
|
||||
const portalContainer = usePortalContainer();
|
||||
|
||||
// Register listener for sources from main process (once on mount)
|
||||
useEffect(() => {
|
||||
@@ -100,7 +103,7 @@ export function ScreenSharePicker() {
|
||||
|
||||
const activeSources = activeTab === 'screens' ? screens : windows;
|
||||
|
||||
return (
|
||||
return createPortal(
|
||||
<div className="fixed inset-0 z-[200] flex items-center justify-center animate-fade-in">
|
||||
{/* Backdrop */}
|
||||
<div
|
||||
@@ -211,7 +214,8 @@ export function ScreenSharePicker() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
portalContainer,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ScreenShareConfig } from '../../stores/voiceStore';
|
||||
import { useSettingsStore } from '../../stores/settingsStore';
|
||||
import { buildScreenShareOptions } from '../../utils/screenShare';
|
||||
import { useFloatingPosition } from '../../hooks/useFloatingPosition';
|
||||
import { usePortalContainer } from '../../hooks/usePortalContainer';
|
||||
import { Toggle } from '../ui/Toggle';
|
||||
import { isElectron } from '../../platform/platform';
|
||||
import { RESOLUTION_LABELS } from '@backspace/shared/src/constants';
|
||||
@@ -46,6 +47,7 @@ function formatKbps(kbps: number): string {
|
||||
|
||||
export function ScreenShareSettingsPopover({ open, onClose, anchorRef }: ScreenShareSettingsPopoverProps) {
|
||||
const popoverRef = useRef<HTMLDivElement>(null);
|
||||
const portalContainer = usePortalContainer();
|
||||
const config = useVoiceStore((s) => s.screenShareConfig);
|
||||
const setConfig = useVoiceStore((s) => s.setScreenShareConfig);
|
||||
const hwOverdrive = useVoiceStore((s) => s.hwOverdrive);
|
||||
@@ -296,6 +298,6 @@ export function ScreenShareSettingsPopover({ open, onClose, anchorRef }: ScreenS
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
portalContainer,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user