{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "loader-dot-matrix-01",
  "title": "Loader Dot Matrix",
  "description": "A grid of dots that pulses in a ripple, wave, or rain pattern, shown four ways including inside a button",
  "dependencies": [],
  "registryDependencies": [
    "https://ui.cortexcn.dev/r/utils.json",
    "button"
  ],
  "files": [
    {
      "path": "registry/blocks/loader-dot-matrix-01/components/loader-dot-matrix-demo.tsx",
      "content": "\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { Button } from \"@/components/ui/button\";\n\nimport { LoaderDotMatrix } from \"./loader-dot-matrix\";\n\n// Cortexcn item \"loader-dot-matrix-01\", revision 8ec39664178b, MIT license.\n// https://ui.cortexcn.dev/r/v/loader-dot-matrix-01/8ec39664178b.json\n\nconst EXAMPLES: { label: string; loader: ReactNode }[] = [\n  { label: \"Ripple (default)\", loader: <LoaderDotMatrix /> },\n  { label: \"Wave\", loader: <LoaderDotMatrix pattern=\"wave\" /> },\n  {\n    label: \"Rain, 6 by 6\",\n    loader: <LoaderDotMatrix cols={6} pattern=\"rain\" rows={6} />,\n  },\n  {\n    label: \"In a button\",\n    loader: (\n      <Button className=\"gap-2\" disabled variant=\"outline\">\n        <LoaderDotMatrix cols={3} dotSize={2} rows={3} speed={1.2} />\n        Saving\n      </Button>\n    ),\n  },\n];\n\n/** The dot matrix loader four ways: its default, two other patterns, and in a button. */\nexport function LoaderDotMatrixDemo() {\n  return (\n    <div className=\"grid w-full max-w-3xl gap-3 sm:grid-cols-2\">\n      {EXAMPLES.map((example) => (\n        <figure\n          className=\"flex aspect-4/3 flex-col items-center justify-center gap-6 rounded-xl border bg-background p-6\"\n          key={example.label}\n        >\n          <div className=\"flex flex-1 items-center\">{example.loader}</div>\n          <figcaption className=\"font-mono text-muted-foreground text-xs\">\n            {example.label}\n          </figcaption>\n        </figure>\n      ))}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/loader-dot-matrix-demo.tsx"
    },
    {
      "path": "registry/blocks/loader-dot-matrix-01/components/loader-dot-matrix.tsx",
      "content": "\"use client\";\n\nimport { useMemo } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n// Cortexcn item \"loader-dot-matrix-01\", revision 8ec39664178b, MIT license.\n// https://ui.cortexcn.dev/r/v/loader-dot-matrix-01/8ec39664178b.json\n\nexport interface 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: { row: number; col: number; delay: number }[] = [];\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      aria-label=\"Loading\"\n      aria-live=\"polite\"\n      className={cn(\"inline-flex\", className)}\n      data-slot=\"loader-dot-matrix\"\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(({ row, col, delay }) => (\n          <span\n            className=\"rounded-full bg-foreground will-change-transform\"\n            key={`${row}-${col}`}\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",
      "target": "components/loader-dot-matrix.tsx"
    }
  ],
  "type": "registry:block",
  "author": "Cortexcn (https://ui.cortexcn.dev)",
  "meta": {
    "license": "MIT",
    "revision": "8ec39664178b"
  }
}