mirror of
https://github.com/suitenumerique/meet.git
synced 2026-07-27 20:29:09 +00:00
♻️ decouple PiP behavior from shared primitives
Remove PiP-specific branching from Menu/Tooltip primitives via explicit props and context.
This commit is contained in:
@@ -5,6 +5,7 @@ import { useDocumentPiP } from '../hooks/useDocumentPiP'
|
||||
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
|
||||
import { useRestoreFocus } from '@/hooks/useRestoreFocus'
|
||||
import { UNSAFE_PortalProvider } from '@react-aria/overlays'
|
||||
import { VisualOnlyTooltipsContext } from '@/primitives/VisualOnlyTooltipsContext'
|
||||
|
||||
// Minimal base styles so the PiP window renders correctly on first paint.
|
||||
const ensureBaseStyles = (target: Document) => {
|
||||
@@ -224,7 +225,9 @@ export const DocumentPiPPortal = ({
|
||||
if (!container) return null
|
||||
return createPortal(
|
||||
<UNSAFE_PortalProvider getContainer={() => container}>
|
||||
{children}
|
||||
<VisualOnlyTooltipsContext.Provider value={true}>
|
||||
{children}
|
||||
</VisualOnlyTooltipsContext.Provider>
|
||||
</UNSAFE_PortalProvider>,
|
||||
container
|
||||
)
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
import { ReactNode } from 'react'
|
||||
import { MenuTrigger } from 'react-aria-components'
|
||||
import { StyledPopover } from './Popover'
|
||||
import { Box } from './Box'
|
||||
import {
|
||||
useOverlayBoundaryElement,
|
||||
useOverlayPortalContainer,
|
||||
} from './useOverlayPortalContainer'
|
||||
import { useOverlayBoundaryElement } 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
|
||||
@@ -15,33 +12,22 @@ import {
|
||||
export const Menu = ({
|
||||
children,
|
||||
variant = 'light',
|
||||
placement,
|
||||
placement = 'top',
|
||||
shouldFlip,
|
||||
}: {
|
||||
children: [trigger: ReactNode, menu: ReactNode]
|
||||
variant?: 'dark' | 'light'
|
||||
placement?: 'bottom' | 'top' | 'left' | 'right'
|
||||
shouldFlip?: boolean
|
||||
}) => {
|
||||
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?.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 (
|
||||
<MenuTrigger>
|
||||
{trigger}
|
||||
<StyledPopover
|
||||
placement={placement ?? defaultPlacement}
|
||||
placement={placement}
|
||||
shouldFlip={shouldFlip}
|
||||
boundaryElement={boundaryElement}
|
||||
>
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
type TooltipProps,
|
||||
} from 'react-aria-components'
|
||||
import { styled } from '@/styled-system/jsx'
|
||||
import { useOverlayPortalContainer } from './useOverlayPortalContainer'
|
||||
import { VisualOnlyTooltip } from './VisualOnlyTooltip'
|
||||
import { useVisualOnlyTooltips } from './VisualOnlyTooltipsContext'
|
||||
|
||||
export type TooltipWrapperProps = {
|
||||
tooltip?: string
|
||||
@@ -29,28 +29,23 @@ export const TooltipWrapper = ({
|
||||
}: {
|
||||
children: ReactElement
|
||||
} & TooltipWrapperProps) => {
|
||||
const portalContainer = useOverlayPortalContainer()
|
||||
const isExternalDocument =
|
||||
!!portalContainer?.ownerDocument &&
|
||||
portalContainer.ownerDocument !== document
|
||||
const visualOnly = useVisualOnlyTooltips()
|
||||
const tooltipDelay =
|
||||
tooltipType === 'instant'
|
||||
? INSTANT_TOOLTIP_DELAY_MS
|
||||
: DELAYED_TOOLTIP_DELAY_MS
|
||||
|
||||
return tooltip ? (
|
||||
isExternalDocument ? (
|
||||
<VisualOnlyTooltip tooltip={tooltip}>{children}</VisualOnlyTooltip>
|
||||
) : (
|
||||
<TooltipTrigger
|
||||
delay={
|
||||
tooltipType === 'instant'
|
||||
? INSTANT_TOOLTIP_DELAY_MS
|
||||
: DELAYED_TOOLTIP_DELAY_MS
|
||||
}
|
||||
>
|
||||
{children}
|
||||
<Tooltip>{tooltip}</Tooltip>
|
||||
</TooltipTrigger>
|
||||
)
|
||||
) : (
|
||||
children
|
||||
if (!tooltip) return children
|
||||
|
||||
if (visualOnly) {
|
||||
return <VisualOnlyTooltip tooltip={tooltip}>{children}</VisualOnlyTooltip>
|
||||
}
|
||||
|
||||
return (
|
||||
<TooltipTrigger delay={tooltipDelay}>
|
||||
{children}
|
||||
<Tooltip>{tooltip}</Tooltip>
|
||||
</TooltipTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createContext, useContext } from 'react'
|
||||
|
||||
/**
|
||||
* When true, tooltips render as visual-only (no aria-describedby).
|
||||
* Provided by surfaces where React Aria TooltipTrigger doesn't work
|
||||
* correctly (e.g. cross-document portals).
|
||||
*/
|
||||
export const VisualOnlyTooltipsContext = createContext(false)
|
||||
|
||||
export const useVisualOnlyTooltips = () =>
|
||||
useContext(VisualOnlyTooltipsContext)
|
||||
Reference in New Issue
Block a user