(feat) connect PiP to room and options menu

wire room-level PiP state to the options menu
This commit is contained in:
Cyril
2026-01-22 11:29:26 +01:00
parent e49355985a
commit 94488f31cf
16 changed files with 14 additions and 32 deletions
@@ -153,4 +153,3 @@ export const DocumentPiPPortal = ({
return portal return portal
} }
@@ -71,4 +71,3 @@ const PipControlsRight = styled('div', {
right: '1.35rem', right: '1.35rem',
}, },
}) })
@@ -107,4 +107,3 @@ const PipGridWrapper = styled('div', {
height: '100%', height: '100%',
}, },
}) })
@@ -15,4 +15,3 @@ export const RoomPiP = () => {
</DocumentPiPPortal> </DocumentPiPPortal>
) )
} }
@@ -9,8 +9,7 @@ import { PipOptionsMenu } from './PipOptionsMenu'
export const OptionsButton = () => { export const OptionsButton = () => {
const { t } = useTranslation('rooms') const { t } = useTranslation('rooms')
const portalContainer = useOverlayPortalContainer() const portalContainer = useOverlayPortalContainer()
const isInPiP = const isInPiP = portalContainer && portalContainer.ownerDocument !== document
portalContainer && portalContainer.ownerDocument !== document
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const wrapperRef = useRef<HTMLDivElement>(null) const wrapperRef = useRef<HTMLDivElement>(null)
@@ -33,8 +32,6 @@ export const OptionsButton = () => {
} }
}, [isInPiP, isOpen]) }, [isInPiP, isOpen])
if (isInPiP) { if (isInPiP) {
return ( return (
<PipOptionsMenu <PipOptionsMenu
@@ -21,4 +21,3 @@ export const PictureInPictureMenuItem = () => {
</MenuItem> </MenuItem>
) )
} }
@@ -30,10 +30,10 @@ export const PipOptionsMenu = ({
const target = event.target as HTMLElement | null const target = event.target as HTMLElement | null
const wrapper = wrapperRef.current const wrapper = wrapperRef.current
if (!wrapper || !target) return if (!wrapper || !target) return
// Don't close if clicking the trigger button // Don't close if clicking the trigger button
if (wrapper.querySelector('button')?.contains(target)) return if (wrapper.querySelector('button')?.contains(target)) return
// Close if clicking a menu item (action will have fired) // Close if clicking a menu item (action will have fired)
if (target.closest('[role="menuitem"]')) { if (target.closest('[role="menuitem"]')) {
// Use requestAnimationFrame to ensure action completes first, without visible delay // Use requestAnimationFrame to ensure action completes first, without visible delay
@@ -83,4 +83,4 @@ export const PipOptionsMenu = ({
)} )}
</div> </div>
) )
} }
@@ -64,4 +64,3 @@ export const PipLateralMenu = () => {
</DialogTrigger> </DialogTrigger>
) )
} }
@@ -19,10 +19,7 @@ export const RoomPiPProvider = ({
const open = useCallback(() => setIsOpen(true), []) const open = useCallback(() => setIsOpen(true), [])
const close = useCallback(() => setIsOpen(false), []) const close = useCallback(() => setIsOpen(false), [])
const toggle = useCallback( const toggle = useCallback(() => setIsOpen((current) => !current), [])
() => setIsOpen((current) => !current),
[]
)
const value = useMemo( const value = useMemo(
() => ({ () => ({
@@ -36,9 +33,6 @@ export const RoomPiPProvider = ({
) )
return ( return (
<RoomPiPContext.Provider value={value}> <RoomPiPContext.Provider value={value}>{children}</RoomPiPContext.Provider>
{children}
</RoomPiPContext.Provider>
) )
} }
@@ -9,4 +9,3 @@ export type RoomPiPContextValue = {
} }
export const RoomPiPContext = createContext<RoomPiPContextValue | null>(null) export const RoomPiPContext = createContext<RoomPiPContextValue | null>(null)
@@ -1,7 +1,10 @@
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
type DocumentPictureInPicture = { type DocumentPictureInPicture = {
requestWindow: (options?: { width?: number; height?: number }) => Promise<Window> requestWindow: (options?: {
width?: number
height?: number
}) => Promise<Window>
} }
type WindowWithDocumentPiP = Window & { type WindowWithDocumentPiP = Window & {
@@ -67,4 +70,3 @@ export const useDocumentPiP = ({
closePiP, closePiP,
} }
} }
@@ -8,4 +8,3 @@ export const useRoomPiP = () => {
} }
return context return context
} }
+1 -1
View File
@@ -24,7 +24,7 @@ export const Menu = ({
const [trigger, menu] = children const [trigger, menu] = children
const boundaryElement = useOverlayBoundaryElement() const boundaryElement = useOverlayBoundaryElement()
const portalContainer = useOverlayPortalContainer() const portalContainer = useOverlayPortalContainer()
// Detect if we're in PiP: portal container is in a different document than the main window // Detect if we're in PiP: portal container is in a different document than the main window
const isInPiP = useMemo( const isInPiP = useMemo(
() => () =>
@@ -134,10 +134,7 @@ const Tooltip = ({
children: ReactNode children: ReactNode
} & Partial<Omit<TooltipProps, 'children'>>) => { } & Partial<Omit<TooltipProps, 'children'>>) => {
return ( return (
<StyledTooltip <StyledTooltip arrowBoundaryOffset={arrowBoundaryOffset ?? 0} {...props}>
arrowBoundaryOffset={arrowBoundaryOffset ?? 0}
{...props}
>
<TooltipArrow /> <TooltipArrow />
{children} {children}
</StyledTooltip> </StyledTooltip>
@@ -82,7 +82,8 @@ export const VisualOnlyTooltip = ({
> >
{wrappedChild} {wrappedChild}
</div> </div>
{tooltipData && portalContainer && {tooltipData &&
portalContainer &&
createPortal( createPortal(
<div <div
aria-hidden="true" aria-hidden="true"
@@ -22,4 +22,3 @@ export const useOverlayBoundaryElement = () => {
return undefined return undefined
}, [portalContainer]) }, [portalContainer])
} }