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