{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-grid-pattern",
  "type": "registry:ui",
  "title": "Animated Grid Pattern",
  "description": "A animated background grid pattern made with SVGs, fully customizable using Tailwind CSS.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/magicui/animated-grid-pattern.tsx",
      "content": "\"use client\"\n\nimport {\n  useCallback,\n  useEffect,\n  useId,\n  useRef,\n  useState,\n  type ComponentPropsWithoutRef,\n} from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface AnimatedGridPatternProps extends ComponentPropsWithoutRef<\"svg\"> {\n  width?: number\n  height?: number\n  x?: number\n  y?: number\n  strokeDasharray?: number\n  numSquares?: number\n  maxOpacity?: number\n  duration?: number\n  repeatDelay?: number\n}\n\ntype Square = {\n  id: number\n  pos: [number, number]\n  iteration: number\n}\n\nexport function AnimatedGridPattern({\n  width = 40,\n  height = 40,\n  x = -1,\n  y = -1,\n  strokeDasharray = 0,\n  numSquares = 50,\n  className,\n  maxOpacity = 0.5,\n  duration = 4,\n  repeatDelay = 0.5,\n  ...props\n}: AnimatedGridPatternProps) {\n  const id = useId()\n  const containerRef = useRef<SVGSVGElement | null>(null)\n  const [dimensions, setDimensions] = useState({ width: 0, height: 0 })\n  const [squares, setSquares] = useState<Array<Square>>([])\n\n  const getPos = useCallback((): [number, number] => {\n    return [\n      Math.floor((Math.random() * dimensions.width) / width),\n      Math.floor((Math.random() * dimensions.height) / height),\n    ]\n  }, [dimensions.height, dimensions.width, height, width])\n\n  const generateSquares = useCallback(\n    (count: number) => {\n      return Array.from({ length: count }, (_, i) => ({\n        id: i,\n        pos: getPos(),\n        iteration: 0,\n      }))\n    },\n    [getPos]\n  )\n\n  const updateSquarePosition = useCallback(\n    (squareId: number) => {\n      setSquares((currentSquares) => {\n        const current = currentSquares[squareId]\n        if (!current || current.id !== squareId) return currentSquares\n\n        const nextSquares = currentSquares.slice()\n        nextSquares[squareId] = {\n          ...current,\n          pos: getPos(),\n          iteration: current.iteration + 1,\n        }\n\n        return nextSquares\n      })\n    },\n    [getPos]\n  )\n\n  useEffect(() => {\n    if (dimensions.width && dimensions.height) {\n      setSquares(generateSquares(numSquares))\n    }\n  }, [dimensions.width, dimensions.height, generateSquares, numSquares])\n\n  useEffect(() => {\n    const element = containerRef.current\n    let resizeObserver: ResizeObserver | null = null\n\n    if (element) {\n      resizeObserver = new ResizeObserver((entries) => {\n        for (const entry of entries) {\n          setDimensions((currentDimensions) => {\n            const nextWidth = entry.contentRect.width\n            const nextHeight = entry.contentRect.height\n            if (\n              currentDimensions.width === nextWidth &&\n              currentDimensions.height === nextHeight\n            ) {\n              return currentDimensions\n            }\n            return { width: nextWidth, height: nextHeight }\n          })\n        }\n      })\n\n      resizeObserver.observe(element)\n    }\n\n    return () => {\n      if (resizeObserver) {\n        resizeObserver.disconnect()\n      }\n    }\n  }, [])\n\n  return (\n    <svg\n      ref={containerRef}\n      aria-hidden=\"true\"\n      className={cn(\n        \"pointer-events-none absolute inset-0 h-full w-full fill-gray-400/30 stroke-gray-400/30\",\n        className\n      )}\n      {...props}\n    >\n      <defs>\n        <pattern\n          id={id}\n          width={width}\n          height={height}\n          patternUnits=\"userSpaceOnUse\"\n          x={x}\n          y={y}\n        >\n          <path\n            d={`M.5 ${height}V.5H${width}`}\n            fill=\"none\"\n            strokeDasharray={strokeDasharray}\n          />\n        </pattern>\n      </defs>\n      <rect width=\"100%\" height=\"100%\" fill={`url(#${id})`} />\n      <svg x={x} y={y} className=\"overflow-visible\">\n        {squares.map(({ pos: [squareX, squareY], id, iteration }, index) => (\n          <motion.rect\n            initial={{ opacity: 0 }}\n            animate={{ opacity: maxOpacity }}\n            transition={{\n              duration,\n              repeat: 1,\n              delay: index * 0.1,\n              repeatType: \"reverse\",\n              repeatDelay,\n            }}\n            onAnimationComplete={() => updateSquarePosition(id)}\n            key={`${id}-${iteration}`}\n            width={width - 1}\n            height={height - 1}\n            x={squareX * width + 1}\n            y={squareY * height + 1}\n            fill=\"currentColor\"\n            strokeWidth=\"0\"\n          />\n        ))}\n      </svg>\n    </svg>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}