diff --git a/web/src/components/magicui/magic-card.tsx b/web/src/components/magicui/magic-card.tsx new file mode 100644 index 00000000..40c2c0ae --- /dev/null +++ b/web/src/components/magicui/magic-card.tsx @@ -0,0 +1,108 @@ +"use client"; + +import { motion, useMotionTemplate, useMotionValue } from "motion/react"; +import React, { useCallback, useEffect, useRef } from "react"; + +import { cn } from "@/lib/utils"; + +interface MagicCardProps { + children?: React.ReactNode; + className?: string; + gradientSize?: number; + gradientColor?: string; + gradientOpacity?: number; + gradientFrom?: string; + gradientTo?: string; +} + +export function MagicCard({ + children, + className, + gradientSize = 200, + gradientColor = "", + gradientOpacity = 0.8, + gradientFrom = "#ff0a54", + gradientTo = "#f9bec7", +}: MagicCardProps) { + const cardRef = useRef(null); + const mouseX = useMotionValue(-gradientSize); + const mouseY = useMotionValue(-gradientSize); + + const handleMouseMove = useCallback( + (e: MouseEvent) => { + if (cardRef.current) { + const { left, top } = cardRef.current.getBoundingClientRect(); + const clientX = e.clientX; + const clientY = e.clientY; + mouseX.set(clientX - left); + mouseY.set(clientY - top); + } + }, + [mouseX, mouseY], + ); + + const handleMouseOut = useCallback( + (e: MouseEvent) => { + if (!e.relatedTarget) { + document.removeEventListener("mousemove", handleMouseMove); + mouseX.set(-gradientSize); + mouseY.set(-gradientSize); + } + }, + [handleMouseMove, mouseX, gradientSize, mouseY], + ); + + const handleMouseEnter = useCallback(() => { + document.addEventListener("mousemove", handleMouseMove); + mouseX.set(-gradientSize); + mouseY.set(-gradientSize); + }, [handleMouseMove, mouseX, gradientSize, mouseY]); + + useEffect(() => { + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseout", handleMouseOut); + document.addEventListener("mouseenter", handleMouseEnter); + + return () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseout", handleMouseOut); + document.removeEventListener("mouseenter", handleMouseEnter); + }; + }, [handleMouseEnter, handleMouseMove, handleMouseOut]); + + useEffect(() => { + mouseX.set(-gradientSize); + mouseY.set(-gradientSize); + }, [gradientSize, mouseX, mouseY]); + + return ( +
+ +
+ +
{children}
+
+ ); +}