"use client"; import { useEffect, useRef, useState } from "react"; /** Shared singleton IntersectionObserver for all Reveal instances */ const callbacks = new Map void>(); let sharedObserver: IntersectionObserver | null = null; function getObserver(): IntersectionObserver { if (!sharedObserver) { sharedObserver = new IntersectionObserver( (entries) => { for (const entry of entries) { if (entry.isIntersecting) { const cb = callbacks.get(entry.target); if (cb) { cb(); callbacks.delete(entry.target); sharedObserver!.unobserve(entry.target); } } } }, { threshold: 0.1, rootMargin: "0px 0px -50px 0px" }, ); } return sharedObserver; } interface RevealProps { children: React.ReactNode; className?: string; } export function Reveal({ children, className = "" }: RevealProps) { const ref = useRef(null); const [visible, setVisible] = useState(false); const [reducedMotion, setReducedMotion] = useState(false); useEffect(() => { if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) { setReducedMotion(true); setVisible(true); return; } const el = ref.current; if (!el) return; const observer = getObserver(); callbacks.set(el, () => setVisible(true)); observer.observe(el); return () => { callbacks.delete(el); observer.unobserve(el); }; }, []); return (
{children}
); }