{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "wiggling-cards",
  "type": "registry:component",
  "title": "Wiggling Cards",
  "description": "An animated card carousel with a playful wiggling effect during navigation.",
  "dependencies": [
    "lucide-react",
    "motion",
    "react-icons"
  ],
  "files": [
    {
      "path": "components/watermelon/wiggling-cards.tsx",
      "type": "registry:component",
      "content": "import React, { useState, useEffect } from 'react';\nimport {\n  motion,\n  useMotionValue,\n  useTransform,\n  useMotionTemplate,\n  type PanInfo,\n} from 'motion/react';\nimport {\n  ArrowUpRight,\n  ShoppingCart,\n  Users,\n  CreditCard,\n  BarChart3,\n} from 'lucide-react';\nimport { FaArrowUpLong } from 'react-icons/fa6';\n\nexport interface CardData {\n  id: number;\n  icon: React.ElementType;\n  percentage: string;\n  value: string;\n  label: string;\n}\n\nconst DEFAULT_CARDS: CardData[] = [\n  {\n    id: 0,\n    icon: CreditCard,\n    percentage: '2.15%',\n    value: '$2,374',\n    label: 'Weekly Expense',\n  },\n  {\n    id: 1,\n    icon: ShoppingCart,\n    percentage: '1.20%',\n    value: '$1,589',\n    label: 'Weekly Orders',\n  },\n  {\n    id: 2,\n    icon: Users,\n    percentage: '2.33%',\n    value: '$976',\n    label: 'Weekly Users',\n  },\n  {\n    id: 3,\n    icon: BarChart3,\n    percentage: '3.82%',\n    value: '$46,748',\n    label: 'Weekly Sales',\n  },\n];\n\nconst DRAG_BUFFER = 60;\nconst VELOCITY_THRESHOLD = 500;\n\nconst WigglingCard = ({ card, i, x, cardWidth, gap }: any) => {\n  const Icon = card.icon;\n  const center = -(i * (cardWidth + gap));\n\n  const distance = useTransform(x, (v: number) => v - center);\n\n  const rotate = useTransform(\n    distance,\n    [-cardWidth, -cardWidth * 0.1, 0, cardWidth * 0.1, cardWidth],\n    [10, 10, 0, -10, -10],\n  );\n\n  const blur = useTransform(\n    distance,\n    [-cardWidth, -cardWidth * 0.2, 0, cardWidth * 0.2, cardWidth],\n    [4, 2, 0, 2, 4],\n  );\n\n  const opacity = useTransform(\n    distance,\n    [-cardWidth, -cardWidth * 0.2, 0, cardWidth * 0.2, cardWidth],\n    [0, 0.8, 1, 0.8, 0],\n  );\n\n  const filter = useMotionTemplate`blur(${blur}px)`;\n\n  return (\n    <motion.div\n      key={card.id}\n      style={{\n        opacity,\n        rotate,\n        filter,\n        minWidth: cardWidth,\n      }}\n      className=\"relative flex h-72 flex-col justify-between rounded-[32px] border border-neutral-200 bg-white p-5 sm:h-80 sm:rounded-[40px] sm:p-6 dark:border-neutral-800 dark:bg-neutral-900\"\n    >\n      <div className=\"flex flex-col gap-6 sm:gap-10\">\n        <div className=\"flex h-16 w-16 items-center justify-center rounded-2xl bg-neutral-100 sm:h-20 sm:w-20 dark:bg-neutral-800\">\n          <Icon\n            className=\"h-10 w-10 text-neutral-900 sm:h-14 sm:w-14 dark:text-neutral-100\"\n            strokeWidth={1.5}\n          />\n        </div>\n\n        <div className=\"flex flex-col gap-1.5\">\n          <div className=\"flex w-fit items-center rounded-2xl bg-neutral-200 px-3 py-0.5 text-base font-medium text-neutral-600 sm:text-lg dark:bg-neutral-800 dark:text-neutral-300\">\n            <FaArrowUpLong className=\"mr-1 h-3 w-3\" />\n            {card.percentage}\n          </div>\n\n          <h2 className=\"text-3xl font-bold text-neutral-900 sm:text-[42px] dark:text-neutral-100\">\n            {card.value}\n          </h2>\n\n          <p className=\"text-lg font-medium text-neutral-700 sm:text-[20px] dark:text-neutral-300\">\n            {card.label}\n          </p>\n        </div>\n      </div>\n\n      <div className=\"absolute right-6 bottom-7 sm:right-7 sm:bottom-9\">\n        <div className=\"flex h-10 w-10 items-center justify-center rounded-full bg-neutral-200 sm:h-12 sm:w-12 dark:bg-neutral-800\">\n          <ArrowUpRight className=\"h-5 w-5 text-neutral-900 sm:h-6 sm:w-6 dark:text-neutral-100\" />\n        </div>\n      </div>\n    </motion.div>\n  );\n};\n\nexport function WigglingCards({ cards }: { cards?: CardData[] }) {\n  const data = cards ?? DEFAULT_CARDS;\n  const [index, setIndex] = useState(1);\n  const [dimensions, setDimensions] = useState({ cardWidth: 320, gap: 200 });\n\n  useEffect(() => {\n    const updateDimensions = () => {\n      const width = window.innerWidth;\n      if (width < 640) {\n        setDimensions({\n          cardWidth: Math.min(width - 64, 300),\n          gap: 40,\n        });\n      } else {\n        setDimensions({\n          cardWidth: 320,\n          gap: 200,\n        });\n      }\n    };\n\n    updateDimensions();\n    window.addEventListener('resize', updateDimensions);\n    return () => window.removeEventListener('resize', updateDimensions);\n  }, []);\n\n  const { cardWidth, gap } = dimensions;\n  const x = useMotionValue(-(index * (cardWidth + gap)));\n\n  const handleDragEnd = (_: any, info: PanInfo) => {\n    const offset = info.offset.x;\n    const velocity = info.velocity.x;\n\n    if (offset < -DRAG_BUFFER || velocity < -VELOCITY_THRESHOLD) {\n      setIndex((prev) => Math.min(prev + 1, data.length - 1));\n    } else if (offset > DRAG_BUFFER || velocity > VELOCITY_THRESHOLD) {\n      setIndex((prev) => Math.max(prev - 1, 0));\n    }\n  };\n\n  return (\n    <div className=\"flex flex-col items-center py-10\">\n      <div style={{ width: cardWidth + 40 }} className=\"relative mt-2\">\n        <motion.div\n          className=\"flex touch-pan-y\"\n          drag=\"x\"\n          dragConstraints={{\n            left: -(data.length - 1) * (cardWidth + gap),\n            right: 0,\n          }}\n          style={{\n            x,\n            gap: `${gap}px`,\n            perspective: 1000,\n          }}\n          animate={{\n            x: -(index * (cardWidth + gap)),\n          }}\n          transition={{\n            type: 'spring',\n            stiffness: 300,\n            damping: 40,\n          }}\n          onDragEnd={handleDragEnd}\n        >\n          {data.map((card, i) => (\n            <WigglingCard\n              key={card.id}\n              card={card}\n              i={i}\n              x={x}\n              cardWidth={cardWidth}\n              gap={gap}\n            />\n          ))}\n        </motion.div>\n      </div>\n\n      <div className=\"mt-8 flex gap-3\">\n        {data.map((_, i) => (\n          <button\n            key={i}\n            onClick={() => setIndex(i)}\n            className={`h-3 w-3 rounded-full transition-colors duration-200 ease-out ${\n              i === index\n                ? 'bg-neutral-500 dark:bg-neutral-400'\n                : 'bg-neutral-300 dark:bg-neutral-700'\n            }`}\n          />\n        ))}\n      </div>\n    </div>\n  );\n}\n"
    }
  ]
}
