"use client"; import { type RefObject, useEffect, useState } from "react"; interface InViewOptions { /** Fração do elemento (0 a 1) que precisa estar à vista; em elementos altos, vale também essa fração da altura da janela. */ amount?: number; /** Verdadeiro (padrão): depois de aparecer uma vez, continua verdadeiro. Falso: acompanha a entrada e a saída. */ once?: boolean; } function isShown(entry: IntersectionObserverEntry, amount: number) { return ( entry.isIntersecting && (entry.intersectionRatio >= amount || entry.intersectionRect.height >= window.innerHeight * amount) ); } /** * O elemento está à vista? Usa IntersectionObserver (sem ouvir a rolagem). Sem IntersectionObserver devolve * verdadeiro logo na montagem: o conteúdo nunca fica escondido por falta do recurso. */ export function useInView( ref: RefObject, { amount = 0.2, once = true }: InViewOptions = {}, ): boolean { const [inView, setInView] = useState(false); useEffect(() => { const element = ref.current; if (!element) return; if (typeof IntersectionObserver === "undefined") { setInView(true); return; } const observer = new IntersectionObserver( (entries) => { for (const entry of entries) { const shown = isShown(entry, amount); if (shown) { setInView(true); if (once) observer.disconnect(); } else if (!once) setInView(false); } }, { threshold: [0, amount] }, ); observer.observe(element); return () => observer.disconnect(); }, [ref, amount, once]); return inView; }