{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "CardSwap-TS-TW",
	"title": "CardSwap",
	"description": "Cards animate position swapping with smooth layout transitions.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "CardSwap/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';\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\n    ref={ref}\n    {...rest}\n    className={`absolute top-1/2 left-1/2 rounded-xl border border-white bg-black [transform-style:preserve-3d] [will-change:transform] [backface-visibility:hidden] ${customClass ?? ''} ${rest.className ?? ''}`.trim()}\n  />\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\n      ref={container}\n      className=\"absolute bottom-0 right-0 transform translate-x-[5%] translate-y-[20%] origin-bottom-right perspective-[900px] overflow-visible max-[768px]:translate-x-[25%] max-[768px]:translate-y-[25%] max-[768px]:scale-[0.75] max-[480px]:translate-x-[25%] max-[480px]:translate-y-[25%] max-[480px]:scale-[0.55]\"\n      style={{ width, height }}\n    >\n      {rendered}\n    </div>\n  );\n};\n\nexport default CardSwap;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"gsap@^3.13.0"
	]
}