{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "CardSwap-TS-CSS",
	"title": "CardSwap",
	"description": "Cards animate position swapping with smooth layout transitions.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "CardSwap.css",
			"target": "@components/CardSwap.css",
			"content": ".card-swap-container {\n  position: absolute;\n  bottom: 0;\n  right: 0;\n  transform: translate(5%, 20%);\n  transform-origin: bottom right;\n\n  perspective: 900px;\n  overflow: visible;\n}\n\n.card {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  border-radius: 12px;\n  border: 1px solid #fff;\n  background: #000;\n\n  transform-style: preserve-3d;\n  will-change: transform;\n  backface-visibility: hidden;\n  -webkit-backface-visibility: hidden;\n}\n\n@media (max-width: 768px) {\n  .card-swap-container {\n    transform: scale(0.75) translate(25%, 25%);\n  }\n}\n\n@media (max-width: 480px) {\n  .card-swap-container {\n    transform: scale(0.55) translate(25%, 25%);\n  }\n}\n"
		},
		{
			"type": "registry:component",
			"path": "CardSwap.tsx",
			"content": "import React, {\n  Children,\n  cloneElement,\n  forwardRef,\n  isValidElement,\n  type ReactElement,\n  type ReactNode,\n  type RefObject,\n  useEffect,\n  useMemo,\n  useRef\n} from 'react';\nimport gsap from 'gsap';\nimport './CardSwap.css';\n\nexport interface CardSwapProps {\n  width?: number | string;\n  height?: number | string;\n  cardDistance?: number;\n  verticalDistance?: number;\n  delay?: number;\n  pauseOnHover?: boolean;\n  onCardClick?: (idx: number) => void;\n  skewAmount?: number;\n  easing?: 'linear' | 'elastic';\n  children: ReactNode;\n}\n\nexport interface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n  customClass?: string;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(({ customClass, ...rest }, ref) => (\n  <div ref={ref} {...rest} className={`card ${customClass ?? ''} ${rest.className ?? ''}`.trim()} />\n));\nCard.displayName = 'Card';\n\ntype CardRef = RefObject<HTMLDivElement | null>;\ninterface Slot {\n  x: number;\n  y: number;\n  z: number;\n  zIndex: number;\n}\n\nconst makeSlot = (i: number, distX: number, distY: number, total: number): Slot => ({\n  x: i * distX,\n  y: -i * distY,\n  z: -i * distX * 1.5,\n  zIndex: total - i\n});\n\nconst placeNow = (el: HTMLElement, slot: Slot, skew: number) =>\n  gsap.set(el, {\n    x: slot.x,\n    y: slot.y,\n    z: slot.z,\n    xPercent: -50,\n    yPercent: -50,\n    skewY: skew,\n    transformOrigin: 'center center',\n    zIndex: slot.zIndex,\n    force3D: true\n  });\n\nconst CardSwap: React.FC<CardSwapProps> = ({\n  width = 500,\n  height = 400,\n  cardDistance = 60,\n  verticalDistance = 70,\n  delay = 5000,\n  pauseOnHover = false,\n  onCardClick,\n  skewAmount = 6,\n  easing = 'elastic',\n  children\n}) => {\n  const config =\n    easing === 'elastic'\n      ? {\n          ease: 'elastic.out(0.6,0.9)',\n          durDrop: 2,\n          durMove: 2,\n          durReturn: 2,\n          promoteOverlap: 0.9,\n          returnDelay: 0.05\n        }\n      : {\n          ease: 'power1.inOut',\n          durDrop: 0.8,\n          durMove: 0.8,\n          durReturn: 0.8,\n          promoteOverlap: 0.45,\n          returnDelay: 0.2\n        };\n\n  const childArr = useMemo(() => Children.toArray(children) as ReactElement<CardProps>[], [children]);\n  const refs = useMemo<CardRef[]>(() => childArr.map(() => React.createRef<HTMLDivElement>()), [childArr.length]);\n\n  const order = useRef<number[]>(Array.from({ length: childArr.length }, (_, i) => i));\n\n  const tlRef = useRef<gsap.core.Timeline | null>(null);\n  const intervalRef = useRef<number>(0);\n  const container = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    const total = refs.length;\n    refs.forEach((r, i) => placeNow(r.current!, makeSlot(i, cardDistance, verticalDistance, total), skewAmount));\n\n    const swap = () => {\n      if (order.current.length < 2) return;\n\n      const [front, ...rest] = order.current;\n      const elFront = refs[front].current!;\n      const tl = gsap.timeline();\n      tlRef.current = tl;\n\n      tl.to(elFront, {\n        y: '+=500',\n        duration: config.durDrop,\n        ease: config.ease\n      });\n\n      tl.addLabel('promote', `-=${config.durDrop * config.promoteOverlap}`);\n      rest.forEach((idx, i) => {\n        const el = refs[idx].current!;\n        const slot = makeSlot(i, cardDistance, verticalDistance, refs.length);\n        tl.set(el, { zIndex: slot.zIndex }, 'promote');\n        tl.to(\n          el,\n          {\n            x: slot.x,\n            y: slot.y,\n            z: slot.z,\n            duration: config.durMove,\n            ease: config.ease\n          },\n          `promote+=${i * 0.15}`\n        );\n      });\n\n      const backSlot = makeSlot(refs.length - 1, cardDistance, verticalDistance, refs.length);\n      tl.addLabel('return', `promote+=${config.durMove * config.returnDelay}`);\n      tl.call(\n        () => {\n          gsap.set(elFront, { zIndex: backSlot.zIndex });\n        },\n        undefined,\n        'return'\n      );\n      tl.to(\n        elFront,\n        {\n          x: backSlot.x,\n          y: backSlot.y,\n          z: backSlot.z,\n          duration: config.durReturn,\n          ease: config.ease\n        },\n        'return'\n      );\n\n      tl.call(() => {\n        order.current = [...rest, front];\n      });\n    };\n\n    swap();\n    intervalRef.current = window.setInterval(swap, delay);\n\n    if (pauseOnHover) {\n      const node = container.current!;\n      const pause = () => {\n        tlRef.current?.pause();\n        clearInterval(intervalRef.current);\n      };\n      const resume = () => {\n        tlRef.current?.play();\n        intervalRef.current = window.setInterval(swap, delay);\n      };\n      node.addEventListener('mouseenter', pause);\n      node.addEventListener('mouseleave', resume);\n      return () => {\n        node.removeEventListener('mouseenter', pause);\n        node.removeEventListener('mouseleave', resume);\n        clearInterval(intervalRef.current);\n      };\n    }\n    return () => clearInterval(intervalRef.current);\n  }, [cardDistance, verticalDistance, delay, pauseOnHover, skewAmount, easing]);\n\n  const rendered = childArr.map((child, i) =>\n    isValidElement<CardProps>(child)\n      ? cloneElement(child, {\n          key: i,\n          ref: refs[i],\n          style: { width, height, ...(child.props.style ?? {}) },\n          onClick: e => {\n            child.props.onClick?.(e as React.MouseEvent<HTMLDivElement>);\n            onCardClick?.(i);\n          }\n        } as CardProps & React.RefAttributes<HTMLDivElement>)\n      : child\n  );\n\n  return (\n    <div ref={container} className=\"card-swap-container\" style={{ width, height }}>\n      {rendered}\n    </div>\n  );\n};\n\nexport default CardSwap;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"gsap@^3.13.0"
	]
}