{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"interactive-grid-pattern","type":"registry:ui","title":"InteractiveGridPattern","description":"SVG grid of squares that highlight on hover with smooth fade transitions","author":"fancy-ui <https://fancy-ui.rama.app>","categories":["effects"],"registryDependencies":["https://fancy-ui.rama.app/r/utils.json"],"files":[{"path":"react/src/components/interactive-grid-pattern/InteractiveGridPattern.tsx","content":"\"use client\";\n\nimport { memo, useCallback, useState } from \"react\";\nimport { cn } from \"../../utils\";\nimport \"./interactive-grid-pattern.css\";\n\nexport interface InteractiveGridPatternProps {\n\t/** Additional CSS classes for the SVG container */\n\tclassName?: string;\n\t/** Additional CSS classes for individual squares */\n\tsquaresClassName?: string;\n\t/** Additional CSS classes controlling the square outline color */\n\tstrokeClassName?: string;\n\t/** Width of each square in pixels */\n\twidth?: number;\n\t/** Height of each square in pixels */\n\theight?: number;\n\t/** Grid dimensions [columns, rows] */\n\tsquares?: [number, number];\n\t/** Whether squares respond to hover. When false, renders a static graph-paper grid with no listeners */\n\tinteractive?: boolean;\n}\n\ninterface SquareProps {\n\tindex: number;\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n\thovered: boolean;\n\tstrokeClassName?: string;\n\tsquaresClassName?: string;\n\tonEnter?: (index: number) => void;\n\tonLeave?: () => void;\n}\n\n/**\n * One grid cell. Memoised so a pointer move re-renders only the squares whose fill\n * actually flipped instead of the whole grid (cols * rows rects on every event).\n */\nconst Square = memo(function Square({\n\tindex,\n\tx,\n\ty,\n\twidth,\n\theight,\n\thovered,\n\tstrokeClassName,\n\tsquaresClassName,\n\tonEnter,\n\tonLeave,\n}: SquareProps) {\n\treturn (\n\t\t<rect\n\t\t\tx={x}\n\t\t\ty={y}\n\t\t\twidth={width}\n\t\t\theight={height}\n\t\t\tclassName={cn(\n\t\t\t\t\"interactive-grid-square motion-safe:duration-100 ease-in-out motion-safe:transition-all\",\n\t\t\t\tstrokeClassName,\n\t\t\t\thovered ? \"fill-gray-300/30\" : \"fill-transparent\",\n\t\t\t\tsquaresClassName\n\t\t\t)}\n\t\t\tonMouseEnter={onEnter ? () => onEnter(index) : undefined}\n\t\t\tonMouseLeave={onLeave}\n\t\t/>\n\t);\n});\n\nexport function InteractiveGridPattern({\n\tclassName,\n\tsquaresClassName,\n\tstrokeClassName = \"stroke-gray-400/30\",\n\twidth = 40,\n\theight = 40,\n\tsquares = [24, 24],\n\tinteractive = true,\n}: InteractiveGridPatternProps) {\n\tconst [hoveredSquare, setHoveredSquare] = useState<number | null>(null);\n\n\tconst cols = squares[0];\n\tconst rows = squares[1];\n\tconst totalSquares = cols * rows;\n\tconst gridWidth = width * cols;\n\tconst gridHeight = height * rows;\n\n\tfunction getX(index: number) {\n\t\treturn (index % cols) * width;\n\t}\n\n\tfunction getY(index: number) {\n\t\treturn Math.floor(index / cols) * height;\n\t}\n\n\tconst handleEnter = useCallback((index: number) => setHoveredSquare(index), []);\n\tconst handleLeave = useCallback(() => setHoveredSquare(null), []);\n\n\treturn (\n\t\t<svg\n\t\t\twidth={gridWidth}\n\t\t\theight={gridHeight}\n\t\t\tclassName={cn(\n\t\t\t\t\"interactive-grid-pattern absolute inset-0 h-full w-full border border-gray-400/30\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t>\n\t\t\t{Array.from({ length: totalSquares }, (_, index) => (\n\t\t\t\t<Square\n\t\t\t\t\tkey={index}\n\t\t\t\t\tindex={index}\n\t\t\t\t\tx={getX(index)}\n\t\t\t\t\ty={getY(index)}\n\t\t\t\t\twidth={width}\n\t\t\t\t\theight={height}\n\t\t\t\t\thovered={hoveredSquare === index}\n\t\t\t\t\tstrokeClassName={strokeClassName}\n\t\t\t\t\tsquaresClassName={squaresClassName}\n\t\t\t\t\tonEnter={interactive ? handleEnter : undefined}\n\t\t\t\t\tonLeave={interactive ? handleLeave : undefined}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</svg>\n\t);\n}\n","type":"registry:ui","target":"@ui/fancy/components/interactive-grid-pattern/InteractiveGridPattern.tsx"},{"path":"react/src/components/interactive-grid-pattern/index.ts","content":"\"use client\";\n\nexport { InteractiveGridPattern } from \"./InteractiveGridPattern\";\nexport type { InteractiveGridPatternProps } from \"./InteractiveGridPattern\";\n","type":"registry:ui","target":"@ui/fancy/components/interactive-grid-pattern/index.ts"},{"path":"react/src/components/interactive-grid-pattern/interactive-grid-pattern.css","content":"/* .interactive-grid-pattern is a port-added anchor: the source's scoped rule had no\n   root class, so the slug anchors it to keep the selector out of the global namespace. */\n@media (prefers-reduced-motion: no-preference) {\n\t.interactive-grid-pattern .interactive-grid-square:not(:hover) {\n\t\ttransition-duration: 1000ms;\n\t}\n}\n","type":"registry:ui","target":"@ui/fancy/components/interactive-grid-pattern/interactive-grid-pattern.css"}],"meta":{"license":"MIT","package":"fancy-ui-react","version":"0.7.0","source":"https://fancy-ui.rama.app/docs/components/interactive-grid-pattern?framework=react"}}