{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "specials-swipeable-stack",
  "type": "registry:component",
  "title": "Swipeable Stack",
  "description": "A deck of interactive cards with swipe gestures",
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/new-york/animations/specials/swipeable-stack.tsx",
      "content": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport { motion, useMotionValue, useTransform, useAnimation, PanInfo, AnimatePresence } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface SwipeableStackProps {\n    children: React.ReactNode[];\n    className?: string;\n    onSwipeRight?: (index: number) => void;\n    onSwipeLeft?: (index: number) => void;\n}\n\nexport function SwipeableStack({ children, className, onSwipeRight, onSwipeLeft }: SwipeableStackProps) {\n    const [cards, setCards] = useState(React.Children.toArray(children));\n    const [history, setHistory] = useState<React.ReactNode[]>([]);\n\n    const activeCard = cards[cards.length - 1];\n\n    const removeCard = (direction: \"left\" | \"right\") => {\n        const removed = cards.pop();\n        if (removed) {\n            setHistory([...history, removed]);\n            const index = cards.length;\n            if (direction === \"right\" && onSwipeRight) onSwipeRight(index);\n            if (direction === \"left\" && onSwipeLeft) onSwipeLeft(index);\n            setCards([...cards]);\n        }\n    };\n\n    return (\n        <div className={cn(\"relative h-full w-full flex items-center justify-center\", className)}>\n            <AnimatePresence>\n                {cards.map((child, index) => {\n                    const isFront = index === cards.length - 1;\n                    return (\n                        <Card\n                            key={index}\n                            index={index}\n                            isFront={isFront}\n                            drag={isFront}\n                            total={cards.length}\n                            onRemove={removeCard}\n                        >\n                            {child}\n                        </Card>\n                    );\n                })}\n            </AnimatePresence>\n            {cards.length === 0 && (\n                <div className=\"text-center text-neutral-500\">\n                    No more items\n                    <button\n                        onClick={() => {\n                            setCards(React.Children.toArray(children));\n                            setHistory([]);\n                        }}\n                        className=\"mt-4 block mx-auto text-blue-500 hover:underline\"\n                    >\n                        Reset\n                    </button>\n                </div>\n            )}\n        </div>\n    );\n}\n\nfunction Card({\n    children,\n    index,\n    isFront,\n    drag,\n    total,\n    onRemove\n}: {\n    children: React.ReactNode;\n    index: number;\n    isFront: boolean;\n    drag: boolean;\n    total: number;\n    onRemove: (dir: \"left\" | \"right\") => void;\n}) {\n    const x = useMotionValue(0);\n    const rotate = useTransform(x, [-200, 200], [-25, 25]);\n    const opacity = useTransform(x, [-150, 0, 150], [0.5, 1, 0.5]);\n\n    // Stacking logic\n    const reverseIndex = total - 1 - index;\n    const scale = 1 - reverseIndex * 0.05;\n    const y = reverseIndex * 15;\n    const zIndex = total - reverseIndex;\n\n    const controls = useAnimation();\n\n    const handleDragEnd = async (_: any, info: PanInfo) => {\n        if (info.offset.x > 100) {\n            await controls.start({ x: 500, opacity: 0 });\n            onRemove(\"right\");\n        } else if (info.offset.x < -100) {\n            await controls.start({ x: -500, opacity: 0 });\n            onRemove(\"left\");\n        } else {\n            controls.start({ x: 0 });\n        }\n    };\n\n    return (\n        <motion.div\n            style={{\n                x,\n                rotate,\n                scale: isFront ? 1 : scale,\n                y: isFront ? 0 : y,\n                zIndex,\n            }}\n            initial={{ scale, y }}\n            animate={controls}\n            drag={drag ? \"x\" : false}\n            dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}\n            dragElastic={0.7}\n            onDragEnd={handleDragEnd}\n            className=\"absolute left-0 right-0 top-0 bottom-0 mx-auto cursor-grab active:cursor-grabbing origin-bottom\"\n            whileDrag={{ scale: 1.05 }}\n        >\n            <div className=\"h-full w-full rounded-2xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 shadow-xl overflow-hidden\">\n                {children}\n            </div>\n        </motion.div>\n    );\n}\n",
      "type": "registry:component"
    }
  ]
}