import { useEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react'; import '../companion.css'; // A history card mounted again must not replay its delivery transition. const revealedResults = new Set(); export function MotionAssembly({ active, reducedMotion, revealKey, children }: { active: boolean; reducedMotion: boolean; revealKey?: string; children?: ReactNode }) { const host = useRef(null); const previous = useRef({ active, revealKey }); const [revealing, setRevealing] = useState(false); const [visible, setVisible] = useState(!document.hidden); useEffect(() => { const before = previous.current; previous.current = { active, revealKey }; if (active || reducedMotion) { setRevealing(false); return; } if (before.active) { const alreadyRevealed = revealKey ? revealedResults.has(revealKey) : false; if (revealKey) { revealedResults.add(revealKey); if (revealedResults.size > 256) revealedResults.delete(revealedResults.values().next().value!); } setRevealing(!alreadyRevealed); } else if (before.revealKey !== revealKey) setRevealing(false); }, [active, reducedMotion, revealKey]); useEffect(() => { const element = host.current; if (!element) return; let inViewport = true; const sync = () => setVisible(inViewport && !document.hidden); const observer = new IntersectionObserver(entries => { inViewport = entries.some(entry => entry.isIntersecting); sync(); }); observer.observe(element); document.addEventListener('visibilitychange', sync); return () => { observer.disconnect(); document.removeEventListener('visibilitychange', sync); }; }, []); return
{active && } {children != null &&
setRevealing(false)}>{children}
}
; }