{
  "name": "draggable-card",
  "type": "registry:ui",
  "files": [
    {
      "path": "components/ui/draggable-card.tsx",
      "content": "\"use client\";\nimport { cn } from \"@/lib/utils\";\nimport React, { useRef, useState, useEffect } from \"react\";\nimport {\n  motion,\n  useMotionValue,\n  useSpring,\n  useTransform,\n  animate,\n  useVelocity,\n  useAnimationControls,\n} from \"motion/react\";\n\nexport const DraggableCardBody = ({\n  className,\n  children,\n}: {\n  className?: string;\n  children?: React.ReactNode;\n}) => {\n  const mouseX = useMotionValue(0);\n  const mouseY = useMotionValue(0);\n  const cardRef = useRef<HTMLDivElement>(null);\n  const controls = useAnimationControls();\n  const [constraints, setConstraints] = useState({\n    top: 0,\n    left: 0,\n    right: 0,\n    bottom: 0,\n  });\n\n  // physics biatch\n  const velocityX = useVelocity(mouseX);\n  const velocityY = useVelocity(mouseY);\n\n  const springConfig = {\n    stiffness: 100,\n    damping: 20,\n    mass: 0.5,\n  };\n\n  const rotateX = useSpring(\n    useTransform(mouseY, [-300, 300], [25, -25]),\n    springConfig,\n  );\n  const rotateY = useSpring(\n    useTransform(mouseX, [-300, 300], [-25, 25]),\n    springConfig,\n  );\n\n  const opacity = useSpring(\n    useTransform(mouseX, [-300, 0, 300], [0.8, 1, 0.8]),\n    springConfig,\n  );\n\n  const glareOpacity = useSpring(\n    useTransform(mouseX, [-300, 0, 300], [0.2, 0, 0.2]),\n    springConfig,\n  );\n\n  useEffect(() => {\n    // Update constraints when component mounts or window resizes\n    const updateConstraints = () => {\n      if (typeof window !== \"undefined\") {\n        setConstraints({\n          top: -window.innerHeight / 2,\n          left: -window.innerWidth / 2,\n          right: window.innerWidth / 2,\n          bottom: window.innerHeight / 2,\n        });\n      }\n    };\n\n    updateConstraints();\n\n    // Add resize listener\n    window.addEventListener(\"resize\", updateConstraints);\n\n    // Clean up\n    return () => {\n      window.removeEventListener(\"resize\", updateConstraints);\n    };\n  }, []);\n\n  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {\n    const { clientX, clientY } = e;\n    const { width, height, left, top } =\n      cardRef.current?.getBoundingClientRect() ?? {\n        width: 0,\n        height: 0,\n        left: 0,\n        top: 0,\n      };\n    const centerX = left + width / 2;\n    const centerY = top + height / 2;\n    const deltaX = clientX - centerX;\n    const deltaY = clientY - centerY;\n    mouseX.set(deltaX);\n    mouseY.set(deltaY);\n  };\n\n  const handleMouseLeave = () => {\n    mouseX.set(0);\n    mouseY.set(0);\n  };\n\n  return (\n    <motion.div\n      ref={cardRef}\n      drag\n      dragConstraints={constraints}\n      onDragStart={() => {\n        document.body.style.cursor = \"grabbing\";\n      }}\n      onDragEnd={(event, info) => {\n        document.body.style.cursor = \"default\";\n\n        controls.start({\n          rotateX: 0,\n          rotateY: 0,\n          transition: {\n            type: \"spring\",\n            ...springConfig,\n          },\n        });\n        const currentVelocityX = velocityX.get();\n        const currentVelocityY = velocityY.get();\n\n        const velocityMagnitude = Math.sqrt(\n          currentVelocityX * currentVelocityX +\n            currentVelocityY * currentVelocityY,\n        );\n        const bounce = Math.min(0.8, velocityMagnitude / 1000);\n\n        animate(info.point.x, info.point.x + currentVelocityX * 0.3, {\n          duration: 0.8,\n          ease: [0.2, 0, 0, 1],\n          bounce,\n          type: \"spring\",\n          stiffness: 50,\n          damping: 15,\n          mass: 0.8,\n        });\n\n        animate(info.point.y, info.point.y + currentVelocityY * 0.3, {\n          duration: 0.8,\n          ease: [0.2, 0, 0, 1],\n          bounce,\n          type: \"spring\",\n          stiffness: 50,\n          damping: 15,\n          mass: 0.8,\n        });\n      }}\n      style={{\n        rotateX,\n        rotateY,\n        opacity,\n        willChange: \"transform\",\n      }}\n      animate={controls}\n      whileHover={{ scale: 1.02 }}\n      onMouseMove={handleMouseMove}\n      onMouseLeave={handleMouseLeave}\n      className={cn(\n        \"relative min-h-96 w-80 overflow-hidden rounded-md bg-neutral-100 p-6 shadow-2xl transform-3d dark:bg-neutral-900\",\n        className,\n      )}\n    >\n      {children}\n      <motion.div\n        style={{\n          opacity: glareOpacity,\n        }}\n        className=\"pointer-events-none absolute inset-0 bg-white select-none\"\n      />\n    </motion.div>\n  );\n};\n\nexport const DraggableCardContainer = ({\n  className,\n  children,\n}: {\n  className?: string;\n  children?: React.ReactNode;\n}) => {\n  return (\n    <div className={cn(\"[perspective:3000px]\", className)}>{children}</div>\n  );\n};\n",
      "type": "registry:ui",
      "target": "components/ui/draggable-card.tsx"
    }
  ],
  "author": "Manu Arora <hi@manuarora.in>",
  "title": "Draggable Card"
}