{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "draggable-stack",
  "title": "Draggable Stack",
  "description": "A spring-driven stack with threshold-based card promotion.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "@rivelle/utils"
  ],
  "files": [
    {
      "path": "registry/nova/effects/draggable-stack.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useReducedMotion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype DraggableStackProps = React.ComponentProps<\"div\"> & {\n  cardClassName?: string;\n  onOrderChange?: (order: number[]) => void;\n  threshold?: number;\n};\n\nfunction DraggableStack({\n  cardClassName,\n  children,\n  className,\n  onOrderChange,\n  threshold = 110,\n  ...props\n}: DraggableStackProps) {\n  const cards = React.Children.toArray(children);\n  const [order, setOrder] = React.useState(() =>\n    cards.map((_, index) => index),\n  );\n  const reducedMotion = useReducedMotion();\n\n  React.useEffect(() => {\n    setOrder(cards.map((_, index) => index));\n  }, [cards.length]);\n\n  const sendToBack = () => {\n    setOrder((current) => {\n      const next = [...current.slice(1), current[0]];\n      onOrderChange?.(next);\n      return next;\n    });\n  };\n\n  return (\n    <div\n      className={cn(\"relative isolate min-h-80 w-full\", className)}\n      data-slot=\"draggable-stack\"\n      {...props}\n    >\n      {[...order].reverse().map((cardIndex, reverseIndex) => {\n        const depth = order.length - 1 - reverseIndex;\n        const isTop = depth === 0;\n        return (\n          <motion.div\n            animate={{\n              rotate: depth % 2 === 0 ? depth * -1.5 : depth * 1.5,\n              scale: 1 - depth * 0.035,\n              y: depth * 12,\n            }}\n            className={cn(\n              \"absolute inset-x-0 top-0 mx-auto w-[min(82%,340px)] cursor-grab touch-none overflow-hidden rounded-[1.75rem] border border-foreground/10 bg-card shadow-[0_28px_80px_-45px_var(--foreground)] active:cursor-grabbing\",\n              !isTop && \"pointer-events-none\",\n              cardClassName,\n            )}\n            drag={isTop && !reducedMotion}\n            dragConstraints={{ bottom: 150, left: -170, right: 170, top: -150 }}\n            dragElastic={0.18}\n            key={cardIndex}\n            onDragEnd={(_, info) => {\n              if (Math.hypot(info.offset.x, info.offset.y) >= threshold) {\n                sendToBack();\n              }\n            }}\n            style={{ zIndex: order.length - depth }}\n            transition={{ damping: 24, stiffness: 280, type: \"spring\" }}\n            whileDrag={{ rotate: 0, scale: 1.04, zIndex: order.length + 1 }}\n          >\n            {cards[cardIndex]}\n          </motion.div>\n        );\n      })}\n    </div>\n  );\n}\n\nexport { DraggableStack };\nexport type { DraggableStackProps };\n",
      "type": "registry:ui",
      "target": "@effects/draggable-stack.tsx"
    }
  ],
  "type": "registry:ui"
}