{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "grid-pixelate-wipe",
  "title": "Grid Pixelate Wipe",
  "description": "Dissolve from one scene to the next through a deterministic grid of mask cells.",
  "dependencies": [
    "remotion"
  ],
  "files": [
    {
      "path": "registry/bases/editframe/components/grid-pixelate-wipe/index.tsx",
      "content": "\"use client\";\n\nimport { Timegroup } from \"@editframe/react\";\nimport type { CSSProperties, ReactElement } from \"react\";\n\nconst FONT_FAMILY =\n  \"var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif\";\n\nconst DefaultPanel = ({ label, color }: { label: string; color: string }) => (\n  <div\n    style={{\n      alignItems: \"center\",\n      background: color,\n      color: \"white\",\n      display: \"flex\",\n      fontFamily: FONT_FAMILY,\n      fontSize: 96,\n      fontWeight: 700,\n      inset: 0,\n      justifyContent: \"center\",\n      letterSpacing: \"-0.05em\",\n      position: \"absolute\",\n    }}\n  >\n    {label}\n  </div>\n);\n\nexport interface GridPixelateWipeProps {\n  from?: React.ReactNode;\n  to?: React.ReactNode;\n  cols?: number;\n  rows?: number;\n  pattern?: \"wave\" | \"diagonal\" | \"spiral\";\n  transitionStart?: number;\n  transitionDuration?: number;\n  cellFadeFrames?: number;\n  speed?: number;\n  fps?: number;\n  durationInFrames?: number;\n  width?: number;\n  height?: number;\n  background?: string;\n  className?: string;\n}\n\nconst computeDelays = (\n  cols: number,\n  rows: number,\n  pattern: string,\n  transitionDuration: number,\n  cellFadeFrames: number\n) => {\n  const raw: number[][] = Array.from({ length: rows }, () =>\n    Array.from({ length: cols }, () => 0)\n  );\n  let max = -Infinity;\n  let min = Infinity;\n\n  if (pattern === \"spiral\") {\n    let bottom = rows - 1;\n    let i = 0;\n    let left = 0;\n    let right = cols - 1;\n    let top = 0;\n    while (top <= bottom && left <= right) {\n      for (let x = left; x <= right; x += 1) {\n        raw[top][x] = i;\n        i += 1;\n      }\n      top += 1;\n      for (let y = top; y <= bottom; y += 1) {\n        raw[y][right] = i;\n        i += 1;\n      }\n      right -= 1;\n      if (top <= bottom) {\n        for (let x = right; x >= left; x -= 1) {\n          raw[bottom][x] = i;\n          i += 1;\n        }\n        bottom -= 1;\n      }\n      if (left <= right) {\n        for (let y = bottom; y >= top; y -= 1) {\n          raw[y][left] = i;\n          i += 1;\n        }\n        left -= 1;\n      }\n    }\n  } else {\n    for (let y = 0; y < rows; y += 1) {\n      for (let x = 0; x < cols; x += 1) {\n        raw[y][x] =\n          pattern === \"wave\"\n            ? Math.hypot(x - (cols - 1) / 2, y - (rows - 1) / 2)\n            : x + y;\n      }\n    }\n  }\n\n  for (let y = 0; y < rows; y += 1) {\n    for (let x = 0; x < cols; x += 1) {\n      if (raw[y][x] < min) {\n        min = raw[y][x];\n      }\n      if (raw[y][x] > max) {\n        max = raw[y][x];\n      }\n    }\n  }\n\n  const span = Math.max(0, transitionDuration - cellFadeFrames);\n  const range = max - min || 1;\n  const normalized: number[][] = Array.from({ length: rows }, () =>\n    Array.from({ length: cols }, () => 0)\n  );\n  for (let y = 0; y < rows; y += 1) {\n    for (let x = 0; x < cols; x += 1) {\n      normalized[y][x] = ((raw[y][x] - min) / range) * span;\n    }\n  }\n  return normalized;\n};\n\nexport const GridPixelateWipe = ({\n  from,\n  to,\n  cols = 12,\n  rows = 7,\n  pattern = \"wave\",\n  transitionStart = 36,\n  transitionDuration = 30,\n  cellFadeFrames = 4,\n  speed = 1,\n  fps = 30,\n  durationInFrames = 90,\n  width = 1280,\n  height = 720,\n  background = \"black\",\n  className,\n}: GridPixelateWipeProps) => {\n  const safeSpeed = Math.max(0.01, speed);\n  const durationMs = (durationInFrames / fps) * 1000;\n  const frameMs = 1000 / fps;\n  const startMs = transitionStart * frameMs;\n\n  const delays = computeDelays(\n    cols,\n    rows,\n    pattern,\n    transitionDuration,\n    cellFadeFrames\n  );\n  const cellFadeMs = ((cellFadeFrames / fps) * 1000) / safeSpeed;\n\n  const fromContent = from ?? <DefaultPanel label=\"Scene A\" color=\"#0f172a\" />;\n  const toContent = to ?? <DefaultPanel label=\"Scene B\" color=\"#ec4899\" />;\n\n  const containerStyle: CSSProperties = {\n    background,\n    height,\n    overflow: \"hidden\",\n    position: \"relative\",\n    width,\n  };\n\n  // Build grid cells\n  const cells: ReactElement[] = [];\n  for (let y = 0; y < rows; y += 1) {\n    for (let x = 0; x < cols; x += 1) {\n      const delayMs = (delays[y][x] * frameMs) / safeSpeed;\n      cells.push(\n        <div\n          key={`${x}-${y}`}\n          style={{\n            animation: `framecn-grid-pixel-fade ${cellFadeMs}ms ease-out ${startMs + delayMs}ms backwards`,\n            background: \"black\",\n          }}\n        />\n      );\n    }\n  }\n\n  return (\n    <Timegroup\n      className={className}\n      duration={`${durationMs}ms`}\n      mode=\"fixed\"\n      style={containerStyle}\n    >\n      <>\n        <style>{`\n          @keyframes framecn-grid-pixel-fade {\n            from { opacity: 1; }\n            to { opacity: 0; }\n          }\n        `}</style>\n        <div style={{ inset: 0, position: \"absolute\" }}>{toContent}</div>\n        <div\n          style={{\n            display: \"grid\",\n            gridTemplateColumns: `repeat(${cols}, 1fr)`,\n            gridTemplateRows: `repeat(${rows}, 1fr)`,\n            inset: 0,\n            position: \"absolute\",\n          }}\n        >\n          {cells}\n        </div>\n        <div style={{ inset: 0, opacity: 1, position: \"absolute\" }}>\n          {fromContent}\n        </div>\n      </>\n    </Timegroup>\n  );\n};\n",
      "type": "registry:component",
      "target": "components/framecn/grid-pixelate-wipe.tsx"
    }
  ],
  "type": "registry:component"
}