mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 10:29:58 +02:00
53 lines
1.3 KiB
TypeScript
53 lines
1.3 KiB
TypeScript
"use client";
|
|
|
|
import type { CSSProperties, ReactNode } from "react";
|
|
import { useEffect, useRef } from "react";
|
|
|
|
type RevealProps = {
|
|
children: ReactNode;
|
|
className?: string;
|
|
delay?: number;
|
|
};
|
|
|
|
export function Reveal({ children, className = "", delay = 0 }: RevealProps) {
|
|
const targetRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
const root = document.documentElement;
|
|
root.classList.add("motion-ready");
|
|
const target = targetRef.current;
|
|
if (!target) return;
|
|
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
|
|
if (reduceMotion || !("IntersectionObserver" in window)) {
|
|
target.classList.add("is-visible");
|
|
return;
|
|
}
|
|
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
if (!entry.isIntersecting) return;
|
|
entry.target.classList.add("is-visible");
|
|
observer.unobserve(entry.target);
|
|
});
|
|
},
|
|
{ rootMargin: "0px 0px -10%", threshold: 0.08 },
|
|
);
|
|
|
|
observer.observe(target);
|
|
return () => observer.disconnect();
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
ref={targetRef}
|
|
className={`reveal ${className}`.trim()}
|
|
data-reveal
|
|
style={{ "--reveal-delay": `${delay}ms` } as CSSProperties}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|