{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "flip-card",
  "type": "registry:component",
  "title": "Flip Card",
  "author": "Toby Belhome",
  "description": "Experience smooth, fluid card navigation with natural drag interactions and beautiful animations.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "http://localhost:3000/r/flip-card.json"
  ],
  "files": [
    {
      "path": "examples/motion/animations/flip-card/01/flip-card.tsx",
      "content": "\"use client\";\n\nimport React, { useState, useCallback, useMemo, ReactNode } from \"react\";\nimport { motion, AnimatePresence, PanInfo } from \"motion/react\";\n\ntype FlipCardProps = {\n  children: ReactNode[];\n  stackOffset?: number;\n  stackRotation?: number;\n  dragThreshold?: number;\n  borderRadius?: number;\n  shadowIntensity?: number;\n};\n\nexport default function FlipCard({\n  children,\n  stackOffset = 20,\n  stackRotation = 8,\n  dragThreshold = 120,\n  borderRadius = 10,\n  shadowIntensity = 0.2\n}: FlipCardProps) {\n  const childCount = React.Children.count(children);\n  const [cardOrder, setCardOrder] = useState(() => Array.from({ length: childCount }, (_, i) => i));\n\n  const getCardTransform = useCallback(\n    (index: number, childIndex: number) => {\n      const stackPosition = cardOrder.indexOf(childIndex);\n      const positionFromBottom = cardOrder.length - 1 - stackPosition;\n\n      return {\n        zIndex: stackPosition,\n        y: -positionFromBottom * stackOffset,\n        rotate: positionFromBottom * stackRotation,\n        scale: 1 - positionFromBottom * 0.02,\n        opacity: 1\n      };\n    },\n    [cardOrder, stackOffset, stackRotation]\n  );\n\n  const handleDragEnd = useCallback(\n    (event: any, info: PanInfo, cardIndex: number) => {\n      const dragDistance = Math.abs(info.offset.x) + Math.abs(info.offset.y);\n      const velocity = Math.abs(info.velocity.x) + Math.abs(info.velocity.y);\n\n      if (dragDistance > dragThreshold || velocity > 800) {\n        setCardOrder((prevOrder) => {\n          const newOrder = [...prevOrder];\n          const draggedCardPosition = newOrder.indexOf(cardIndex);\n          const draggedCard = newOrder.splice(draggedCardPosition, 1)[0];\n          newOrder.unshift(draggedCard);\n          return newOrder;\n        });\n      }\n    },\n    [dragThreshold]\n  );\n\n  const cardVariants = {\n    initial: (custom: any) => ({ ...custom, x: 0, y: custom.y }),\n    animate: (custom: any) => ({\n      ...custom,\n      x: 0,\n      y: custom.y,\n      transition: {\n        type: \"spring\",\n        damping: 30,\n        stiffness: 500,\n        mass: 0.5,\n        restDelta: 0.01,\n        restSpeed: 0.01\n      }\n    }),\n    drag: {\n      scale: 1.05,\n      rotate: 0,\n      transition: { duration: 0.05 }\n    }\n  };\n\n  const renderedCards = useMemo(() => {\n    return React.Children.map(children, (child, index) => {\n      const transform = getCardTransform(index, index);\n      const isTopCard = cardOrder.indexOf(index) === cardOrder.length - 1;\n\n      return (\n        <motion.div\n          key={`card-${index}`}\n          custom={transform}\n          variants={cardVariants}\n          initial=\"initial\"\n          animate=\"animate\"\n          exit=\"exit\"\n          whileDrag=\"drag\"\n          drag={isTopCard}\n          dragConstraints={{\n            top: -150,\n            bottom: 150,\n            left: -150,\n            right: 150\n          }}\n          dragElastic={0.2}\n          dragSnapToOrigin={true}\n          dragTransition={{\n            power: 0.3,\n            timeConstant: 125,\n            bounceStiffness: 500,\n            bounceDamping: 30\n          }}\n          onDragEnd={(event, info) => handleDragEnd(event, info, index)}\n          className={`absolute overflow-hidden ${isTopCard ? \"cursor-grab\" : \"cursor-default\"}`}\n          style={{\n            borderRadius,\n            zIndex: transform.zIndex,\n            boxShadow: `0px ${4 + transform.zIndex * 2}px ${\n              8 + transform.zIndex * 4\n            }px rgba(0,0,0,${shadowIntensity})`\n          }}>\n          {child}\n        </motion.div>\n      );\n    });\n  }, [children, cardOrder, borderRadius, shadowIntensity, getCardTransform]);\n\n  return <AnimatePresence>{renderedCards}</AnimatePresence>;\n}\n",
      "type": "registry:component",
      "target": "components/flip-card/flip-card.tsx"
    },
    {
      "path": "examples/motion/animations/flip-card/01/page.tsx",
      "content": "import FlipCard from \"./flip-card\";\n\nconst images = [\n  \"https://images.unsplash.com/photo-1629394661462-13ea8fe156ef?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\n  \"https://images.unsplash.com/photo-1535083783855-76ae62b2914e?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\n  \"https://images.unsplash.com/photo-1497206365907-f5e630693df0?q=80&w=2080&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\n  \"https://images.unsplash.com/photo-1500479694472-551d1fb6258d?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\"\n];\n\nexport default function FlipCardExample() {\n  return (\n    <div className=\"py-20\">\n      <div className=\"h-72 w-60\">\n        <FlipCard>\n          {images.map((src, index) => (\n            <div key={index} className=\"h-72 w-60\">\n              <img\n                className=\"pointer-events-none h-full w-full object-cover\"\n                src={src}\n                alt={`Card ${index + 1}`}\n              />\n            </div>\n          ))}\n        </FlipCard>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/flip-card/flip-card-example.tsx"
    }
  ],
  "meta": {
    "isPro": false
  }
}