{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "morphing-card",
  "type": "registry:ui",
  "title": "Morphing Card",
  "description": "The MorphingCard component creates an engaging, interactive card that smoothly transitions between different shapes and content. It features elegant 3D rotations, customizable gradients, and a clean design that's perfect for showcasing key information or features in a visually striking manner.",
  "author": "Michael (https://github.com/lappemic)",
  "dependencies": [
    "lucide-react",
    "motion",
    "next-themes"
  ],
  "files": [
    {
      "path": "components/demo/card/morphing-card.tsx",
      "content": "import React, { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"motion/react\";\nimport { ArrowRight, Pause, Play } from \"lucide-react\";\nimport { useTheme } from \"next-themes\";\n\ntype Shape = \"rectangle\" | \"circle\" | \"hexagon\";\n\ninterface MorphingCardProps {\n  width?: string;\n  height?: string;\n  contents: Array<{\n    shape: Shape;\n    title: string;\n    description: string;\n  }>;\n  colorScheme?: {\n    from: string;\n    to: string;\n  };\n  autoPlay?: boolean;\n  interval?: number;\n}\n\nconst shapeVariants = {\n  rectangle: { borderRadius: \"16px\", rotate: 0 },\n  circle: { borderRadius: \"50%\", rotate: 120 },\n  hexagon: { borderRadius: \"24% 76% 24% 76% / 32% 32% 68% 68%\", rotate: 240 },\n};\n\nconst MorphingCard: React.FC<MorphingCardProps> = ({\n  width = \"300px\",\n  height = \"300px\",\n  contents,\n  colorScheme = { from: \"#4F46E5\", to: \"#7C3AED\" },\n  autoPlay = true,\n  interval = 3000,\n}) => {\n  const [currentIndex, setCurrentIndex] = useState(0);\n  const [isPlaying, setIsPlaying] = useState(autoPlay);\n\n  const nextShape = useCallback(() => {\n    setCurrentIndex((prevIndex) => (prevIndex + 1) % contents.length);\n  }, [contents.length]);\n\n  useEffect(() => {\n    let timer: NodeJS.Timeout;\n    if (isPlaying) {\n      timer = setInterval(nextShape, interval);\n    }\n    return () => clearInterval(timer);\n  }, [isPlaying, interval, nextShape]);\n\n  const currentContent = contents[currentIndex];\n  const { theme, systemTheme } = useTheme();\n\n  // `contents` is caller-supplied; an empty array would otherwise crash here.\n  if (!currentContent) return null;\n\n  const currentTheme = theme === \"system\" ? systemTheme : theme;\n\n  return (\n    <div className=\"relative\" style={{ width, height }}>\n      <motion.div\n        className=\"absolute inset-0 cursor-pointer overflow-hidden rounded-2xl shadow-lg\"\n        style={{\n          background: `linear-gradient(135deg, ${colorScheme.from}, ${colorScheme.to})`,\n        }}\n        animate={shapeVariants[currentContent.shape]}\n        transition={{ duration: 0.8, ease: [0.4, 0, 0.2, 1] }}\n        whileHover={{ scale: 1.05 }}\n        whileTap={{ scale: 0.95 }}\n      >\n        {/* This empty div will handle the shape morphing */}\n      </motion.div>\n      <div className=\"absolute inset-0 flex items-center justify-center\">\n        <AnimatePresence mode=\"wait\">\n          <motion.div\n            key={currentIndex}\n            className=\"flex h-full w-full flex-col items-center justify-center p-6\"\n            initial={{ opacity: 0, y: 20 }}\n            animate={{ opacity: 1, y: 0 }}\n            exit={{ opacity: 0, y: -20 }}\n            transition={{ duration: 0.5 }}\n          >\n            <h3 className=\"mb-4 text-center text-xl font-semibold text-white\">\n              {currentContent.title}\n            </h3>\n            <p className=\"text-center text-sm text-white\">\n              {currentContent.description}\n            </p>\n          </motion.div>\n        </AnimatePresence>\n      </div>\n      <div className=\"absolute bottom-4 left-1/2 flex -translate-x-1/2 space-x-1\">\n        {contents.map((_, index) => (\n          <motion.div\n            key={index}\n            className=\"h-1 w-3 rounded-full bg-white\"\n            initial={{ opacity: 0.3 }}\n            animate={{ opacity: index === currentIndex ? 1 : 0.3 }}\n            transition={{ duration: 0.3 }}\n          />\n        ))}\n      </div>\n      <button\n        className={`absolute top-4 right-4 rounded-full p-2 text-white ${currentTheme !== \"dark\" ? \"bg-gray-800 hover:bg-gray-500\" : \"bg-white/20 hover:bg-white/30\"}`}\n        onClick={(e) => {\n          e.stopPropagation();\n          setIsPlaying(!isPlaying);\n        }}\n      >\n        {isPlaying ? <Pause size={14} /> : <Play size={14} />}\n      </button>\n      <button\n        className={`absolute right-4 bottom-4 rounded-full p-2 text-white ${currentTheme !== \"dark\" ? \"bg-gray-800 hover:bg-gray-500\" : \"bg-white/20 hover:bg-white/30\"}`}\n        onClick={(e) => {\n          e.stopPropagation();\n          nextShape();\n        }}\n      >\n        <ArrowRight size={14} />\n      </button>\n    </div>\n  );\n};\n\nexport default MorphingCard;\n",
      "type": "registry:ui",
      "target": "components/ui/morphing-card.tsx"
    }
  ]
}
