"use client"; import { useEffect, useRef } from "react"; /** Telas dos projetos (vitrine e capas): nenhum efeito pode ficar por cima delas. */ const PROJECT_SCREENS = ".ds-reel, .ds-frame, .ds-bframe, .ds-phone"; /** * Avisa `onChange(true)` enquanto qualquer tela de projeto está à vista. Usa IntersectionObserver (nada mede a * página a cada quadro de rolagem) e um MutationObserver só para pegar as telas que a página cria ou remove * depois (a vitrine fixa troca a lista ao hidratar). Devolve a função que desfaz tudo. */ function watchProjectScreens(onChange: (over: boolean) => void) { if (typeof IntersectionObserver === "undefined") return () => {}; const watched = new Set(); const inView = new Set(); const observer = new IntersectionObserver((entries) => { for (const entry of entries) { if (entry.isIntersecting && entry.boundingClientRect.height > 0) inView.add(entry.target); else inView.delete(entry.target); } onChange(inView.size > 0); }); const scan = () => { for (const screen of document.querySelectorAll(PROJECT_SCREENS)) { if (watched.has(screen)) continue; watched.add(screen); observer.observe(screen); } for (const screen of watched) { if (screen.isConnected) continue; observer.unobserve(screen); watched.delete(screen); inView.delete(screen); } onChange(inView.size > 0); }; scan(); const mutations = new MutationObserver(scan); mutations.observe(document.body, { childList: true, subtree: true }); return () => { observer.disconnect(); mutations.disconnect(); }; } /** Grão de filme sobre a página. Some enquanto qualquer tela de projeto está à vista (data-over-projects). */ export default function FilmGrain() { const ref = useRef(null); useEffect(() => { const grain = ref.current; if (!grain) return; return watchProjectScreens((over) => { if (over) grain.dataset.overProjects = ""; else delete grain.dataset.overProjects; }); }, []); return