{
  "name": "attract-button",
  "type": "registry:component",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "button"
  ],
  "files": [
    {
      "type": "registry:component",
      "content": "\"use client\";\n\n/**\n * @author: @dorianbaffier\n * @description: Attract Button\n * @version: 1.0.0\n * @date: 2025-06-26\n * @license: MIT\n * @website: https://kokonutui.com\n * @github: https://github.com/kokonut-labs/kokonutui\n */\n\nimport { Magnet } from \"lucide-react\";\nimport { motion, useAnimation } from \"motion/react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\ninterface AttractButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  particleCount?: number;\n  attractRadius?: number;\n}\n\ninterface Particle {\n  id: number;\n  x: number;\n  y: number;\n}\n\nexport default function AttractButton({\n  className,\n  particleCount = 12,\n  attractRadius = 50,\n  ...props\n}: AttractButtonProps) {\n  const [isAttracting, setIsAttracting] = useState(false);\n  const [particles, setParticles] = useState<Particle[]>([]);\n  const particlesControl = useAnimation();\n\n  useEffect(() => {\n    const newParticles = Array.from({ length: particleCount }, (_, i) => ({\n      id: i,\n      x: Math.random() * 360 - 180,\n      y: Math.random() * 360 - 180,\n    }));\n    setParticles(newParticles);\n  }, [particleCount]);\n\n  const handleInteractionStart = useCallback(async () => {\n    setIsAttracting(true);\n    await particlesControl.start({\n      x: 0,\n      y: 0,\n      transition: {\n        type: \"spring\",\n        stiffness: 50,\n        damping: 10,\n      },\n    });\n  }, [particlesControl]);\n\n  const handleInteractionEnd = useCallback(async () => {\n    setIsAttracting(false);\n    await particlesControl.start((i) => ({\n      x: particles[i].x,\n      y: particles[i].y,\n      transition: {\n        type: \"spring\",\n        stiffness: 100,\n        damping: 15,\n      },\n    }));\n  }, [particlesControl, particles]);\n\n  return (\n    <Button\n      className={cn(\n        \"relative min-w-40 touch-none\",\n        \"bg-violet-100 dark:bg-violet-900\",\n        \"hover:bg-violet-200 dark:hover:bg-violet-800\",\n        \"text-violet-600 dark:text-violet-300\",\n        \"border border-violet-300 dark:border-violet-700\",\n        \"transition-all duration-300\",\n        className\n      )}\n      onMouseEnter={handleInteractionStart}\n      onMouseLeave={handleInteractionEnd}\n      onTouchEnd={handleInteractionEnd}\n      onTouchStart={handleInteractionStart}\n      {...props}\n    >\n      {particles.map((_, index) => (\n        <motion.div\n          animate={particlesControl}\n          className={cn(\n            \"absolute h-1.5 w-1.5 rounded-full\",\n            \"bg-violet-400 dark:bg-violet-300\",\n            \"transition-opacity duration-300\",\n            isAttracting ? \"opacity-100\" : \"opacity-40\"\n          )}\n          custom={index}\n          initial={{ x: particles[index].x, y: particles[index].y }}\n          key={index}\n        />\n      ))}\n      <span className=\"relative flex w-full items-center justify-center gap-2\">\n        <Magnet\n          className={cn(\n            \"h-4 w-4 transition-transform duration-300\",\n            isAttracting && \"scale-110\"\n          )}\n        />\n        {isAttracting ? \"Attracting\" : \"Hover me\"}\n      </span>\n    </Button>\n  );\n}\n",
      "path": "/components/kokonutui/attract-button.tsx",
      "target": "components/kokonutui/attract-button.tsx"
    }
  ]
}