{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "DecayCard-TS-CSS",
	"title": "DecayCard",
	"description": "Hover parallax effect that disintegrates the content of a card.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "DecayCard.css",
			"target": "@components/DecayCard.css",
			"content": ".content {\n  position: relative;\n}\n\n.svg {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  display: block;\n  will-change: transform;\n}\n\n.card-text {\n  position: absolute;\n  bottom: 1.2em;\n  letter-spacing: -0.5px;\n  font-weight: 900;\n  left: 1em;\n  font-size: 2.5rem;\n  line-height: 1.5em;\n}\n\n.card-text::first-line {\n  font-size: 6rem;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "DecayCard.tsx",
			"content": "import React, { useEffect, useRef, type ReactNode } from 'react';\nimport { gsap } from 'gsap';\nimport './DecayCard.css';\n\ninterface DecayCardProps {\n  width?: number;\n  height?: number;\n  image?: string;\n  baseFrequency?: number;\n  numOctaves?: number;\n  seed?: number;\n  maxDisplacement?: number;\n  movementBound?: number;\n  children?: ReactNode;\n}\n\nconst DecayCard: React.FC<DecayCardProps> = ({\n  width = 300,\n  height = 400,\n  image = 'https://picsum.photos/300/400?grayscale',\n  baseFrequency = 0.015,\n  numOctaves = 5,\n  seed = 4,\n  maxDisplacement = 400,\n  movementBound = 50,\n  children\n}) => {\n  const svgRef = useRef<HTMLDivElement>(null);\n  const displacementMapRef = useRef<SVGFEDisplacementMapElement>(null);\n  const cursor = useRef<{ x: number; y: number }>({\n    x: typeof window !== 'undefined' ? window.innerWidth / 2 : 0,\n    y: typeof window !== 'undefined' ? window.innerHeight / 2 : 0\n  });\n  const cachedCursor = useRef<{ x: number; y: number }>({ ...cursor.current });\n  const winsize = useRef<{ width: number; height: number }>({\n    width: typeof window !== 'undefined' ? window.innerWidth : 0,\n    height: typeof window !== 'undefined' ? window.innerHeight : 0\n  });\n\n  useEffect(() => {\n    const lerp = (a: number, b: number, n: number): number => (1 - n) * a + n * b;\n\n    const map = (x: number, a: number, b: number, c: number, d: number): number => ((x - a) * (d - c)) / (b - a) + c;\n\n    const distance = (x1: number, x2: number, y1: number, y2: number): number => {\n      const a = x1 - x2;\n      const b = y1 - y2;\n      return Math.hypot(a, b);\n    };\n\n    const handleResize = (): void => {\n      winsize.current = {\n        width: window.innerWidth,\n        height: window.innerHeight\n      };\n    };\n\n    const handleMouseMove = (ev: MouseEvent): void => {\n      cursor.current = { x: ev.clientX, y: ev.clientY };\n    };\n\n    window.addEventListener('resize', handleResize);\n    window.addEventListener('mousemove', handleMouseMove);\n\n    const imgValues = {\n      imgTransforms: { x: 0, y: 0, rz: 0 },\n      displacementScale: 0\n    };\n\n    const render = () => {\n      let targetX = lerp(imgValues.imgTransforms.x, map(cursor.current.x, 0, winsize.current.width, -120, 120), 0.1);\n      let targetY = lerp(imgValues.imgTransforms.y, map(cursor.current.y, 0, winsize.current.height, -120, 120), 0.1);\n      let targetRz = lerp(imgValues.imgTransforms.rz, map(cursor.current.x, 0, winsize.current.width, -10, 10), 0.1);\n\n      if (targetX > movementBound) targetX = movementBound + (targetX - movementBound) * 0.2;\n      if (targetX < -movementBound) targetX = -movementBound + (targetX + movementBound) * 0.2;\n      if (targetY > movementBound) targetY = movementBound + (targetY - movementBound) * 0.2;\n      if (targetY < -movementBound) targetY = -movementBound + (targetY + movementBound) * 0.2;\n\n      imgValues.imgTransforms.x = targetX;\n      imgValues.imgTransforms.y = targetY;\n      imgValues.imgTransforms.rz = targetRz;\n\n      if (svgRef.current) {\n        gsap.set(svgRef.current, {\n          x: imgValues.imgTransforms.x,\n          y: imgValues.imgTransforms.y,\n          rotateZ: imgValues.imgTransforms.rz\n        });\n      }\n\n      const cursorTravelledDistance = distance(\n        cachedCursor.current.x,\n        cursor.current.x,\n        cachedCursor.current.y,\n        cursor.current.y\n      );\n      imgValues.displacementScale = lerp(\n        imgValues.displacementScale,\n        map(cursorTravelledDistance, 0, 200, 0, maxDisplacement),\n        0.06\n      );\n\n      if (displacementMapRef.current) {\n        gsap.set(displacementMapRef.current, {\n          attr: { scale: imgValues.displacementScale }\n        });\n      }\n\n      cachedCursor.current = { ...cursor.current };\n\n      rafId = requestAnimationFrame(render);\n    };\n\n    let rafId = requestAnimationFrame(render);\n\n    return () => {\n      cancelAnimationFrame(rafId);\n      window.removeEventListener('resize', handleResize);\n      window.removeEventListener('mousemove', handleMouseMove);\n    };\n  }, [maxDisplacement, movementBound]);\n\n  return (\n    <div className=\"content\" style={{ width: `${width}px`, height: `${height}px` }} ref={svgRef}>\n      <svg viewBox=\"-60 -75 720 900\" preserveAspectRatio=\"xMidYMid slice\" className=\"svg\">\n        <filter id=\"imgFilter\">\n          <feTurbulence\n            type=\"turbulence\"\n            baseFrequency={baseFrequency}\n            numOctaves={numOctaves}\n            seed={seed}\n            stitchTiles=\"stitch\"\n            x=\"0%\"\n            y=\"0%\"\n            width=\"100%\"\n            height=\"100%\"\n            result=\"turbulence1\"\n          />\n          <feDisplacementMap\n            ref={displacementMapRef}\n            in=\"SourceGraphic\"\n            in2=\"turbulence1\"\n            scale=\"0\"\n            xChannelSelector=\"R\"\n            yChannelSelector=\"B\"\n            x=\"0%\"\n            y=\"0%\"\n            width=\"100%\"\n            height=\"100%\"\n            result=\"displacementMap3\"\n          />\n        </filter>\n        <g>\n          <image\n            href={image}\n            x=\"0\"\n            y=\"0\"\n            width=\"600\"\n            height=\"750\"\n            filter=\"url(#imgFilter)\"\n            preserveAspectRatio=\"xMidYMid slice\"\n          />\n        </g>\n      </svg>\n      <div className=\"card-text\">{children}</div>\n    </div>\n  );\n};\n\nexport default DecayCard;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"gsap@^3.13.0"
	]
}