From 90c0442d357c9b43df7d28678f0f0220aee93560 Mon Sep 17 00:00:00 2001 From: Cyril Date: Tue, 16 Dec 2025 10:15:30 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20fix=20focus=20scroll=20ju?= =?UTF-8?q?mp=20during=20side=20panel=20animation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit preventScroll avoids layout shift that broke the slide-in chat animation Signed-off-by: Cyril --- .../src/features/rooms/livekit/prefabs/Chat.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/features/rooms/livekit/prefabs/Chat.tsx b/src/frontend/src/features/rooms/livekit/prefabs/Chat.tsx index ddc5983d..7c4e67f4 100644 --- a/src/frontend/src/features/rooms/livekit/prefabs/Chat.tsx +++ b/src/frontend/src/features/rooms/livekit/prefabs/Chat.tsx @@ -48,14 +48,17 @@ export function Chat({ ...props }: ChatProps) { // Chat just opened if (!wasChatOpen && isChatPanelOpen) { chatTriggerRef.current = document.activeElement as HTMLElement | null - inputRef.current?.focus() + // Avoid layout "jump" during the side panel slide-in animation. + // Focusing can trigger scroll into view; preventScroll keeps the animation smooth. + requestAnimationFrame(() => { + inputRef.current?.focus({ preventScroll: true }) + }) } - // Chat just closed if (wasChatOpen && !isChatPanelOpen) { const trigger = chatTriggerRef.current if (trigger && document.contains(trigger)) { - trigger.focus() + trigger.focus({ preventScroll: true }) } chatTriggerRef.current = null } @@ -72,7 +75,7 @@ export function Chat({ ...props }: ChatProps) { async function handleSubmit(text: string) { if (!send || !text) return await send(text) - inputRef?.current?.focus() + inputRef?.current?.focus({ preventScroll: true }) } // TEMPORARY: This is a brittle workaround that relies on message count tracking