From 61877d23bc3ccddc7155e3a16a4f02e57ffe9019 Mon Sep 17 00:00:00 2001 From: Cyril Date: Wed, 22 Apr 2026 18:00:37 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(frontend)=20share=20a=20PiP-?= =?UTF-8?q?aware=20element=20size=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuse a single ResizeObserver hook in the PiP control bar. --- .../features/pip/components/PipControlBar.tsx | 35 ++-------------- .../features/pip/hooks/usePipElementSize.ts | 42 +++++++++++++++++++ 2 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 src/frontend/src/features/pip/hooks/usePipElementSize.ts diff --git a/src/frontend/src/features/pip/components/PipControlBar.tsx b/src/frontend/src/features/pip/components/PipControlBar.tsx index 23c9b067..0f6534ae 100644 --- a/src/frontend/src/features/pip/components/PipControlBar.tsx +++ b/src/frontend/src/features/pip/components/PipControlBar.tsx @@ -1,5 +1,5 @@ import { styled } from '@/styled-system/jsx' -import { useRef, useMemo, useState, useEffect, useCallback } from 'react' +import { useRef, useMemo } from 'react' import { AudioDevicesControl } from '@/features/rooms/livekit/components/controls/Device/AudioDevicesControl' import { VideoDeviceControl } from '@/features/rooms/livekit/components/controls/Device/VideoDeviceControl' import { ScreenShareToggle } from '@/features/rooms/livekit/components/controls/ScreenShareToggle' @@ -7,6 +7,7 @@ import { LeaveButton } from '@/features/rooms/livekit/components/controls/LeaveB import { SubtitlesToggle } from '@/features/rooms/livekit/components/controls/SubtitlesToggle' import { HandToggle } from '@/features/rooms/livekit/components/controls/HandToggle' import { StartMediaButton } from '@/features/rooms/livekit/components/controls/StartMediaButton' +import { usePipElementSize } from '../hooks/usePipElementSize' import { PipOptionsMenu } from './controls/PipOptionsMenu' import { PipReactionsToggle } from './PipReactionsToggle' @@ -46,43 +47,13 @@ function getHiddenControls( return hidden } -/** - * ResizeObserver that works inside the PiP document context - * (the global singleton from the main window cannot observe PiP elements). - */ -function usePipSize(ref: React.RefObject) { - const [width, setWidth] = useState(0) - - const measure = useCallback(() => { - if (ref.current) setWidth(ref.current.getBoundingClientRect().width) - }, [ref]) - - useEffect(() => { - const el = ref.current - if (!el) return - - measure() - - const RO = - el.ownerDocument.defaultView?.ResizeObserver ?? window.ResizeObserver - const observer = new RO((entries) => { - const entry = entries[0] - if (entry) setWidth(entry.contentRect.width) - }) - observer.observe(el) - return () => observer.disconnect() - }, [ref, measure]) - - return width -} - export const PipControlBar = ({ showScreenShare, }: { showScreenShare: boolean }) => { const containerRef = useRef(null) - const width = usePipSize(containerRef) + const { width } = usePipElementSize(containerRef) const hidden = useMemo( () => getHiddenControls(width, showScreenShare), diff --git a/src/frontend/src/features/pip/hooks/usePipElementSize.ts b/src/frontend/src/features/pip/hooks/usePipElementSize.ts new file mode 100644 index 00000000..7008085c --- /dev/null +++ b/src/frontend/src/features/pip/hooks/usePipElementSize.ts @@ -0,0 +1,42 @@ +import { useCallback, useEffect, useState, type RefObject } from 'react' + +type Size = { width: number; height: number } + +/** + * Observes an element's size, even when mounted in the PiP document. + * Resolves `ResizeObserver` from the element's own window. + */ +export const usePipElementSize = ( + ref: RefObject +): Size => { + const [size, setSize] = useState({ width: 0, height: 0 }) + + const measure = useCallback(() => { + const el = ref.current + if (!el) return + const rect = el.getBoundingClientRect() + setSize({ width: rect.width, height: rect.height }) + }, [ref]) + + useEffect(() => { + const el = ref.current + if (!el) return + + measure() + + const RO = + el.ownerDocument.defaultView?.ResizeObserver ?? window.ResizeObserver + if (!RO) return + + const observer = new RO((entries) => { + const entry = entries[0] + if (!entry) return + const { width, height } = entry.contentRect + setSize({ width, height }) + }) + observer.observe(el) + return () => observer.disconnect() + }, [ref, measure]) + + return size +}