From cf37d636dba471096d1131ca2ff6ab5a8f5adda3 Mon Sep 17 00:00:00 2001 From: Cyril Date: Wed, 21 Jan 2026 12:31:00 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=84(fix)=20align=20overlays=20in=20pip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit route menus/tooltips to the pip document --- src/frontend/src/primitives/Menu.tsx | 30 ++++++++++++++++-- src/frontend/src/primitives/Popover.tsx | 6 +++- .../src/primitives/TooltipWrapper.tsx | 31 +++++++++++++++---- .../src/primitives/VisualOnlyTooltip.tsx | 13 ++++++-- .../primitives/useOverlayPortalContainer.tsx | 22 +++++++++++++ 5 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 src/frontend/src/primitives/useOverlayPortalContainer.tsx diff --git a/src/frontend/src/primitives/Menu.tsx b/src/frontend/src/primitives/Menu.tsx index f6008716..04da8205 100644 --- a/src/frontend/src/primitives/Menu.tsx +++ b/src/frontend/src/primitives/Menu.tsx @@ -1,10 +1,16 @@ -import { ReactNode } from 'react' +import { ReactNode, useMemo } from 'react' import { MenuTrigger } from 'react-aria-components' import { StyledPopover } from './Popover' import { Box } from './Box' +import { + useOverlayBoundaryElement, + useOverlayPortalContainer, +} from './useOverlayPortalContainer' /** * a Menu is a tuple of a trigger component (most usually a Button) that toggles menu items in a tooltip around the trigger + * + * Uses UNSAFE_PortalProvider context automatically for portal container (no need for UNSTABLE_portalContainer). */ export const Menu = ({ children, @@ -16,10 +22,30 @@ export const Menu = ({ placement?: 'bottom' | 'top' | 'left' | 'right' }) => { const [trigger, menu] = children + const boundaryElement = useOverlayBoundaryElement() + const portalContainer = useOverlayPortalContainer() + + // Detect if we're in PiP: portal container is in a different document than the main window + const isInPiP = useMemo( + () => + portalContainer && + portalContainer.ownerDocument && + portalContainer.ownerDocument !== document, + [portalContainer] + ) + + // Default placement: 'bottom' in PiP, 'top' elsewhere (to match existing behavior) + const defaultPlacement = isInPiP ? 'bottom' : 'top' + const shouldFlip = isInPiP ? false : undefined + return ( {trigger} - + {menu} diff --git a/src/frontend/src/primitives/Popover.tsx b/src/frontend/src/primitives/Popover.tsx index 5ccd995f..e875c8a6 100644 --- a/src/frontend/src/primitives/Popover.tsx +++ b/src/frontend/src/primitives/Popover.tsx @@ -8,6 +8,7 @@ import { } from 'react-aria-components' import { styled } from '@/styled-system/jsx' import { Box } from './Box' +import { useOverlayBoundaryElement } from './useOverlayPortalContainer' export const StyledPopover = styled(RACPopover, { base: { @@ -65,6 +66,8 @@ const StyledOverlayArrow = styled(OverlayArrow, { * * Note: to show a list of actionable items, like a dropdown menu, prefer using a or