From 0576820755fe12e32e50f03259035c863913b29f Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 4 May 2026 13:50:30 -0700 Subject: [PATCH] Prevent focus-ring clipping in AnimatedHeight (#2503) --- apps/web/src/components/AnimatedHeight.tsx | 42 +++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/AnimatedHeight.tsx b/apps/web/src/components/AnimatedHeight.tsx index dd404c49dffbc8f9855623e4d94765a6a0516727..d0e21b3907eebffbb328e72927d35a2e9fdc9dfe 100644 --- a/apps/web/src/components/AnimatedHeight.tsx +++ b/apps/web/src/components/AnimatedHeight.tsx @@ -1,10 +1,25 @@ "use client"; -import { type ReactNode, useLayoutEffect, useRef, useState } from "react"; +import { type ReactNode, useEffect, useLayoutEffect, useRef, useState } from "react"; + +const HEIGHT_TRANSITION_FALLBACK_MS = 250; export function AnimatedHeight({ children }: { readonly children: ReactNode }) { const contentRef = useRef(null); - const [height, setHeight] = useState(null); + const [heightState, setHeightState] = useState<{ + readonly height: number | null; + readonly isClipping: boolean; + }>({ height: null, isClipping: false }); + + useEffect(() => { + if (!heightState.isClipping) return; + const timeoutId = window.setTimeout(() => { + setHeightState((currentState) => + currentState.isClipping ? { ...currentState, isClipping: false } : currentState, + ); + }, HEIGHT_TRANSITION_FALLBACK_MS); + return () => window.clearTimeout(timeoutId); + }, [heightState.height, heightState.isClipping]); useLayoutEffect(() => { const element = contentRef.current; @@ -14,7 +29,13 @@ export function AnimatedHeight({ children }: { readonly children: ReactNode }) { const updateHeight = () => { const nextHeight = Math.ceil(element.scrollHeight || element.getBoundingClientRect().height); - setHeight((currentHeight) => (currentHeight === nextHeight ? currentHeight : nextHeight)); + setHeightState((currentState) => { + if (currentState.height === nextHeight) return currentState; + return { + height: nextHeight, + isClipping: currentState.height !== null, + }; + }); }; const cancelPendingFrames = () => { if (firstFrameId !== null) { @@ -50,8 +71,19 @@ export function AnimatedHeight({ children }: { readonly children: ReactNode }) { return (
{ + if (event.target !== event.currentTarget || event.propertyName !== "height") return; + setHeightState((currentState) => + currentState.isClipping ? { ...currentState, isClipping: false } : currentState, + ); + }} >
{children}
-- 2.54.0