{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "ClickSpark-JS-CSS",
	"title": "ClickSpark",
	"description": "Creates particle spark bursts at click position.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "ClickSpark/ClickSpark.jsx",
			"content": "import { useRef, useEffect, useCallback } from 'react';\n\nconst ClickSpark = ({\n  sparkColor = '#fff',\n  sparkSize = 10,\n  sparkRadius = 15,\n  sparkCount = 8,\n  duration = 400,\n  easing = 'ease-out',\n  extraScale = 1.0,\n  children\n}) => {\n  const canvasRef = useRef(null);\n  const sparksRef = useRef([]);\n  const startTimeRef = useRef(null);\n\n  useEffect(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    const parent = canvas.parentElement;\n    if (!parent) return;\n\n    let resizeTimeout;\n\n    const resizeCanvas = () => {\n      const { width, height } = parent.getBoundingClientRect();\n      if (canvas.width !== width || canvas.height !== height) {\n        canvas.width = width;\n        canvas.height = height;\n      }\n    };\n\n    const handleResize = () => {\n      clearTimeout(resizeTimeout);\n      resizeTimeout = setTimeout(resizeCanvas, 100);\n    };\n\n    const ro = new ResizeObserver(handleResize);\n    ro.observe(parent);\n\n    resizeCanvas();\n\n    return () => {\n      ro.disconnect();\n      clearTimeout(resizeTimeout);\n    };\n  }, []);\n\n  const easeFunc = useCallback(\n    t => {\n      switch (easing) {\n        case 'linear':\n          return t;\n        case 'ease-in':\n          return t * t;\n        case 'ease-in-out':\n          return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;\n        default:\n          return t * (2 - t);\n      }\n    },\n    [easing]\n  );\n\n  useEffect(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n    const ctx = canvas.getContext('2d');\n\n    let animationId;\n\n    const draw = timestamp => {\n      if (!startTimeRef.current) {\n        startTimeRef.current = timestamp;\n      }\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n      sparksRef.current = sparksRef.current.filter(spark => {\n        const elapsed = timestamp - spark.startTime;\n        if (elapsed >= duration) {\n          return false;\n        }\n\n        const progress = elapsed / duration;\n        const eased = easeFunc(progress);\n\n        const distance = eased * sparkRadius * extraScale;\n        const lineLength = sparkSize * (1 - eased);\n\n        const x1 = spark.x + distance * Math.cos(spark.angle);\n        const y1 = spark.y + distance * Math.sin(spark.angle);\n        const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle);\n        const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle);\n\n        ctx.strokeStyle = sparkColor;\n        ctx.lineWidth = 2;\n        ctx.beginPath();\n        ctx.moveTo(x1, y1);\n        ctx.lineTo(x2, y2);\n        ctx.stroke();\n\n        return true;\n      });\n\n      animationId = requestAnimationFrame(draw);\n    };\n\n    animationId = requestAnimationFrame(draw);\n\n    return () => {\n      cancelAnimationFrame(animationId);\n    };\n  }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]);\n\n  const handleClick = e => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n    const rect = canvas.getBoundingClientRect();\n    const x = e.clientX - rect.left;\n    const y = e.clientY - rect.top;\n\n    const now = performance.now();\n    const newSparks = Array.from({ length: sparkCount }, (_, i) => ({\n      x,\n      y,\n      angle: (2 * Math.PI * i) / sparkCount,\n      startTime: now\n    }));\n\n    sparksRef.current.push(...newSparks);\n  };\n\n  return (\n    <div\n      style={{\n        position: 'relative',\n        width: '100%',\n        height: '100%'\n      }}\n      onClick={handleClick}\n    >\n      <canvas\n        ref={canvasRef}\n        style={{\n          width: '100%',\n          height: '100%',\n          display: 'block',\n          userSelect: 'none',\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          pointerEvents: 'none'\n        }}\n      />\n      {children}\n    </div>\n  );\n};\n\nexport default ClickSpark;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": []
}