{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-line-chart",
  "title": "Animated Line Chart",
  "description": "Line chart whose SVG path draws on from left to right with a leading dot.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/animated-line-chart/index.tsx",
      "content": "\"use client\";\n\nimport { spring, useCurrentFrame, useVideoConfig } from \"remotion\";\n\nexport interface AnimatedLineChartProps {\n  data?: number[];\n  width?: number;\n  height?: number;\n  strokeColor?: string;\n  strokeWidth?: number;\n  gridColor?: string;\n  showDot?: boolean;\n  speed?: number;\n  className?: string;\n}\n\nexport function AnimatedLineChart({\n  data = [12, 19, 8, 15, 22, 18, 28, 25, 32],\n  width = 1000,\n  height = 500,\n  strokeColor = \"#22c55e\",\n  strokeWidth = 4,\n  gridColor = \"#27272a\",\n  showDot = true,\n  speed = 1,\n  className,\n}: AnimatedLineChartProps) {\n  const frame = useCurrentFrame() * speed;\n  const { fps, durationInFrames } = useVideoConfig();\n\n  const padding = 60;\n  const innerWidth = width - padding * 2;\n  const innerHeight = height - padding * 2;\n\n  const min = Math.min(...data);\n  const max = Math.max(...data);\n  const range = max - min || 1;\n\n  const points = data.map((value, index) => {\n    const x = padding + (index / (data.length - 1)) * innerWidth;\n    const y = padding + innerHeight - ((value - min) / range) * innerHeight;\n    return { x, y };\n  });\n\n  // Analytical path length: sum of segment distances\n  let pathLength = 0;\n  for (let i = 1; i < points.length; i++) {\n    const dx = points[i].x - points[i - 1].x;\n    const dy = points[i].y - points[i - 1].y;\n    pathLength += Math.sqrt(dx * dx + dy * dy);\n  }\n\n  const d = points\n    .map((p, i) => `${i === 0 ? \"M\" : \"L\"}${p.x.toFixed(2)},${p.y.toFixed(2)}`)\n    .join(\" \");\n\n  // Spring-driven progress: smooth, no bounce. Matches the \"smooth\" preset\n  // from remotion-best-practices/timing.md.\n  const progress = spring({\n    frame,\n    fps,\n    durationInFrames: Math.round(durationInFrames * 0.85),\n    config: { damping: 200 },\n  });\n\n  const dashOffset = pathLength * (1 - progress);\n\n  // Find dot position along the path at current progress\n  const targetLen = pathLength * progress;\n  let traveled = 0;\n  let dotX = points[0].x;\n  let dotY = points[0].y;\n  for (let i = 1; i < points.length; i++) {\n    const dx = points[i].x - points[i - 1].x;\n    const dy = points[i].y - points[i - 1].y;\n    const segLen = Math.sqrt(dx * dx + dy * dy);\n    if (traveled + segLen >= targetLen) {\n      const t = (targetLen - traveled) / segLen;\n      dotX = points[i - 1].x + dx * t;\n      dotY = points[i - 1].y + dy * t;\n      break;\n    }\n    traveled += segLen;\n    dotX = points[i].x;\n    dotY = points[i].y;\n  }\n\n  const gridRows = 4;\n  const gridCols = data.length - 1;\n\n  return (\n    <div\n      className={className}\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        fontFamily:\n          \"var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif\",\n      }}\n    >\n      <svg\n        width={width}\n        height={height}\n        viewBox={`0 0 ${width} ${height}`}\n        style={{ overflow: \"visible\" }}\n      >\n        {/* Horizontal grid */}\n        {Array.from({ length: gridRows + 1 }).map((_, i) => {\n          const y = padding + (i / gridRows) * innerHeight;\n          return (\n            <line\n              key={`h-${i}`}\n              x1={padding}\n              x2={padding + innerWidth}\n              y1={y}\n              y2={y}\n              stroke={gridColor}\n              strokeWidth={1}\n            />\n          );\n        })}\n        {/* Vertical grid */}\n        {Array.from({ length: gridCols + 1 }).map((_, i) => {\n          const x = padding + (i / gridCols) * innerWidth;\n          return (\n            <line\n              key={`v-${i}`}\n              x1={x}\n              x2={x}\n              y1={padding}\n              y2={padding + innerHeight}\n              stroke={gridColor}\n              strokeWidth={1}\n            />\n          );\n        })}\n        {/* Axis */}\n        <line\n          x1={padding}\n          x2={padding}\n          y1={padding}\n          y2={padding + innerHeight}\n          stroke={gridColor}\n          strokeWidth={2}\n        />\n        <line\n          x1={padding}\n          x2={padding + innerWidth}\n          y1={padding + innerHeight}\n          y2={padding + innerHeight}\n          stroke={gridColor}\n          strokeWidth={2}\n        />\n\n        {/* Animated line */}\n        <path\n          d={d}\n          fill=\"none\"\n          stroke={strokeColor}\n          strokeWidth={strokeWidth}\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n          strokeDasharray={pathLength}\n          strokeDashoffset={dashOffset}\n          style={{\n            filter: `drop-shadow(0 0 12px ${strokeColor}55)`,\n          }}\n        />\n\n        {showDot && progress > 0 && progress < 1 && (\n          <circle\n            cx={dotX}\n            cy={dotY}\n            r={strokeWidth * 2}\n            fill={strokeColor}\n            style={{\n              filter: `drop-shadow(0 0 8px ${strokeColor})`,\n            }}\n          />\n        )}\n      </svg>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/animated-line-chart.tsx"
    }
  ],
  "type": "registry:component"
}
