ComponentsAboutContactPrivacyTerms

Built by Lacy Morrow

    Close file
    Close file
    Close file
    Close file
    'use client';
    import React, { useRef, useState, useCallback, useEffect } from 'react';
    import { motion, useSpring, useTransform, SpringOptions } from 'motion/react';
    import { cn } from '@/lib/utils';
    
    export type SpotlightProps = {
      className?: string;
      size?: number;
      springOptions?: SpringOptions;
    };
    
    export function Spotlight({
      className,
      size = 200,
      springOptions = { bounce: 0 },
    }: SpotlightProps) {
      const containerRef = useRef<HTMLDivElement>(null);
      const [isHovered, setIsHovered] = useState(false);
      const [parentElement, setParentElement] = useState<HTMLElement | null>(null);
    
      const mouseX = useSpring(0, springOptions);
      const mouseY = useSpring(0, springOptions);
    
      const spotlightLeft = useTransform(mouseX, (x) => `${x - size / 2}px`);
      const spotlightTop = useTransform(mouseY, (y) => `${y - size / 2}px`);
    
      useEffect(() => {
        if (containerRef.current) {
          const parent = containerRef.current.parentElement;
          if (parent) {
            parent.style.position = 'relative';
            parent.style.overflow = 'hidden';
            setParentElement(parent);
          }
        }
      }, []);
    
      const handleMouseMove = useCallback(
        (event: MouseEvent) => {
          if (!parentElement) return;
          const { left, top } = parentElement.getBoundingClientRect();
          mouseX.set(event.clientX - left);
          mouseY.set(event.clientY - top);
        },
        [mouseX, mouseY, parentElement]
      );
    
      useEffect(() => {
        if (!parentElement) return;
    
        const abortController = new AbortController();
    
        parentElement.addEventListener('mousemove', handleMouseMove, {
          signal: abortController.signal,
        });
        parentElement.addEventListener('mouseenter', () => setIsHovered(true), {
          signal: abortController.signal,
        });
        parentElement.addEventListener('mouseleave', () => setIsHovered(false), {
          signal: abortController.signal,
        });
    
        return () => {
          abortController.abort();
        };
      }, [parentElement, handleMouseMove]);
    
      return (
        <motion.div
          ref={containerRef}
          className={cn(
            'pointer-events-none absolute rounded-full bg-[radial-gradient(circle_at_center,var(--tw-gradient-stops),transparent_80%)] blur-xl transition-opacity duration-200',
            'from-zinc-100 via-zinc-200 to-zinc-400 dark:from-zinc-50 dark:via-zinc-100 dark:to-zinc-200',
            isHovered ? 'opacity-100' : 'opacity-0',
            className
          )}
          style={{
            width: size,
            height: size,
            left: spotlightLeft,
            top: spotlightTop,
          }}
        />
      );
    }