File size: 1,825 Bytes
65d68f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | "use client";
import {
useRef,
type CSSProperties,
type PointerEvent,
type ReactNode,
} from "react";
import { motion, useReducedMotion, type HTMLMotionProps } from "framer-motion";
import "./SpotlightCard.css";
type SpotlightCardProps = Omit<
HTMLMotionProps<"article">,
"children" | "onMouseLeave" | "onPointerMove" | "onPointerLeave" | "style"
> & {
children: ReactNode;
className?: string;
spotlightColor?: string;
style?: CSSProperties;
};
export default function SpotlightCard({
children,
className = "",
spotlightColor = "rgba(255, 255, 255, 0.095)",
style,
...props
}: SpotlightCardProps) {
const cardRef = useRef<HTMLElement | null>(null);
const shouldReduceMotion = useReducedMotion();
function handlePointerMove(event: PointerEvent<HTMLElement>) {
if (shouldReduceMotion || event.pointerType === "touch") return;
const card = cardRef.current;
if (!card) return;
const bounds = card.getBoundingClientRect();
card.style.setProperty("--spotlight-x", `${event.clientX - bounds.left}px`);
card.style.setProperty("--spotlight-y", `${event.clientY - bounds.top}px`);
card.style.setProperty("--spotlight-visible", "1");
}
function handlePointerLeave() {
cardRef.current?.style.setProperty("--spotlight-visible", "0");
}
return (
<motion.article
ref={cardRef}
className={`reactbits-spotlight-card ${className}`.trim()}
onPointerMove={handlePointerMove}
onPointerLeave={handlePointerLeave}
onMouseLeave={handlePointerLeave}
style={
{
...style,
"--reactbits-spotlight-color": spotlightColor,
} as CSSProperties
}
{...props}
>
<span className="reactbits-spotlight-card__glow" aria-hidden="true" />
{children}
</motion.article>
);
}
|