| import { useEffect, useState } from "react"; | |
| import type { RefObject } from "react"; | |
| import { cn } from "../../lib/utils"; | |
| type SpotlightProps = { | |
| parentRef: RefObject<HTMLDivElement|null>; | |
| className?: string; | |
| }; | |
| export const Spotlight = ({ parentRef, className }: SpotlightProps) => { | |
| const [pos, setPos] = useState({ x: -9999, y: -9999 }); | |
| useEffect(() => { | |
| const parent = parentRef.current; | |
| if (!parent) return; | |
| const handleMove = (e: MouseEvent) => { | |
| const rect = parent.getBoundingClientRect(); | |
| setPos({ | |
| x: e.clientX - rect.left, | |
| y: e.clientY - rect.top, | |
| }); | |
| }; | |
| const handleLeave = () => { | |
| setPos({ x: -9999, y: -9999 }); | |
| }; | |
| parent.addEventListener("mousemove", handleMove); | |
| parent.addEventListener("mouseleave", handleLeave); | |
| return () => { | |
| parent.removeEventListener("mousemove", handleMove); | |
| parent.removeEventListener("mouseleave", handleLeave); | |
| }; | |
| }, [parentRef]); | |
| return ( | |
| <div | |
| className={cn( | |
| "pointer-events-none absolute inset-0 z-0 transition-transform duration-75 ease-out", | |
| className | |
| )} | |
| style={{ | |
| transform: `translate(${pos.x - 600}px, ${pos.y - 600}px)`, | |
| }} | |
| > | |
| <svg | |
| className="w-[1200px] h-[1200px] opacity-40 blur-3xl" | |
| viewBox="0 0 1200 1200" | |
| fill="none" | |
| > | |
| <defs> | |
| <radialGradient id="spot" cx="50%" cy="50%" r="50%"> | |
| <stop offset="0%" stopColor="white" stopOpacity="0.35" /> | |
| <stop offset="100%" stopColor="white" stopOpacity="0" /> | |
| </radialGradient> | |
| </defs> | |
| <circle cx="600" cy="600" r="600" fill="url(#spot)" /> | |
| </svg> | |
| </div> | |
| ); | |
| }; | |