{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "loader-dot-matrix",
  "title": "Dot Matrix Loader",
  "description": "A grid of dots with ripple, wave, and cascade animation effects",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/loaders/loader-dot-matrix/components/elements/loader-dot-matrix.tsx",
      "content": "\"use client\";\n\nimport { useMemo } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport type LoaderDotMatrixProps = {\n  rows?: number;\n  cols?: number;\n  pattern?: \"ripple\" | \"wave\" | \"rain\";\n  speed?: number;\n  dotSize?: number;\n  className?: string;\n};\n\nexport function LoaderDotMatrix({\n  rows = 5,\n  cols = 7,\n  pattern = \"ripple\",\n  speed = 1.5,\n  dotSize = 3,\n  className,\n}: LoaderDotMatrixProps) {\n  const dots = useMemo(() => {\n    const result = [];\n    const centerX = (cols - 1) / 2;\n    const centerY = (rows - 1) / 2;\n    const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);\n\n    for (let row = 0; row < rows; row++) {\n      for (let col = 0; col < cols; col++) {\n        let delay = 0;\n\n        if (pattern === \"ripple\") {\n          const distance = Math.sqrt(\n            (col - centerX) ** 2 + (row - centerY) ** 2,\n          );\n          delay = (distance / maxDist) * speed;\n        } else if (pattern === \"wave\") {\n          delay = ((row + col) / (rows + cols - 2)) * speed;\n        } else if (pattern === \"rain\") {\n          delay = row * 0.1 + (col / cols) * speed;\n        }\n\n        result.push({ row, col, delay });\n      }\n    }\n\n    return result;\n  }, [rows, cols, pattern, speed]);\n\n  const gap = dotSize * 1.5;\n\n  return (\n    <output\n      data-slot=\"loader-dot-matrix\"\n      aria-live=\"polite\"\n      aria-label=\"Loading\"\n      className={cn(\"inline-flex\", className)}\n    >\n      <style>\n        {`\n          @keyframes dot-matrix-pulse {\n            0%, 100% { opacity: 0.15; transform: scale(0.3); }\n            50% { opacity: 1; transform: scale(1); }\n          }\n        `}\n      </style>\n      <span className=\"sr-only\">Loading</span>\n      <div\n        style={{\n          display: \"grid\",\n          gridTemplateColumns: `repeat(${cols}, 1fr)`,\n          gap: `${gap}px`,\n        }}\n      >\n        {dots.map(({ delay }, index) => (\n          <span\n            // biome-ignore lint/suspicious/noArrayIndexKey: stable list derived from fixed grid dimensions\n            key={index}\n            className=\"rounded-full bg-foreground will-change-transform\"\n            style={{\n              width: `${dotSize}px`,\n              height: `${dotSize}px`,\n              animation: `dot-matrix-pulse ${speed}s ease-in-out infinite`,\n              animationDelay: `${delay}s`,\n            }}\n          />\n        ))}\n      </div>\n    </output>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Dot grid loader with configurable rows, columns, dot size, and animation patterns (ripple, wave, rain). CSS-only animations with computed stagger delays.",
  "categories": [
    "loaders"
  ],
  "type": "registry:block"
}