{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "qr-code",
  "title": "QR Code",
  "description": "A rounded QR code generator with dot modules, custom foreground/background colors, configurable error correction, and a matching loading skeleton.",
  "dependencies": [
    "qrcode"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/base/ui/qr-code.tsx",
      "content": "import * as React from \"react\"\n// qrcode does not bundle TypeScript declarations.\n// @ts-expect-error qrcode has no bundled TypeScript declarations.\nimport QRCodePackage from \"qrcode\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype ErrorCorrectionLevel = \"L\" | \"M\" | \"Q\" | \"H\"\n\ninterface QrModules {\n  size: number\n  get(row: number, col: number): boolean\n}\n\ninterface QrData {\n  modules: QrModules\n}\n\ninterface CirclePoint {\n  cx: number\n  cy: number\n}\n\nconst QRCodeLib = QRCodePackage as {\n  create(\n    value: string,\n    options: { errorCorrectionLevel: ErrorCorrectionLevel }\n  ): QrData\n}\n\nfunction isInFinderPattern(row: number, col: number, size: number) {\n  return (\n    (row < 7 && col < 7) ||\n    (row < 7 && col >= size - 7) ||\n    (row >= size - 7 && col < 7)\n  )\n}\n\nexport interface QrCodeProps extends Omit<\n  React.ComponentProps<\"svg\">,\n  \"color\"\n> {\n  /** The value to encode. */\n  value: string\n  /** The width and height of the rendered code in pixels. @default 268 */\n  size?: number\n  /** The color of the QR modules. @default \"var(--foreground)\" */\n  fgColor?: string\n  /** The color of the QR code background. @default \"var(--background)\" */\n  bgColor?: string\n  /** The QR encoding error correction level. @default \"M\" */\n  errorCorrectionLevel?: ErrorCorrectionLevel\n  /** An element rendered centered over the code, on a background chip matching `bgColor`. Prefer `errorCorrectionLevel=\"H\"` so the code stays scannable. */\n  logo?: React.ReactNode\n}\n\nconst LOGO_SIZE_RATIO = 0.25\n\nfunction QrCode({\n  value,\n  size = 268,\n  fgColor = \"var(--foreground)\",\n  bgColor = \"var(--background)\",\n  errorCorrectionLevel = \"M\",\n  logo,\n  className,\n  ...props\n}: QrCodeProps) {\n  const qrData = React.useMemo(() => {\n    try {\n      return QRCodeLib.create(value, { errorCorrectionLevel })\n    } catch {\n      return null\n    }\n  }, [errorCorrectionLevel, value])\n\n  const moduleCount = qrData?.modules.size ?? 0\n  const moduleSize = moduleCount ? size / moduleCount : 0\n  const circleRadius = moduleSize / 3\n  const finderPositions = [\n    [0, 0],\n    [0, moduleCount - 7],\n    [moduleCount - 7, 0],\n  ] as const\n\n  const circles = React.useMemo(() => {\n    if (!qrData) return [] as CirclePoint[]\n\n    const nextCircles: CirclePoint[] = []\n\n    for (let row = 0; row < moduleCount; row++) {\n      for (let col = 0; col < moduleCount; col++) {\n        if (\n          qrData.modules.get(row, col) &&\n          !isInFinderPattern(row, col, moduleCount)\n        ) {\n          nextCircles.push({\n            cx: (col + 0.5) * moduleSize,\n            cy: (row + 0.5) * moduleSize,\n          })\n        }\n      }\n    }\n\n    return nextCircles\n  }, [moduleCount, moduleSize, qrData])\n\n  if (!qrData) return null\n\n  const finderSize = 7 * moduleSize\n  const innerPadding = moduleSize\n  const innerWhiteSize = 5 * moduleSize\n  const innerBlackSize = 3 * moduleSize\n\n  const logoSize = size * LOGO_SIZE_RATIO\n  const logoX = (size - logoSize) / 2\n  const logoY = (size - logoSize) / 2\n\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox={`0 0 ${size} ${size}`}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-label={`QR code for ${value}`}\n      className={cn(\"block\", className)}\n      data-slot=\"qr-code\"\n      {...props}\n    >\n      <rect width={size} height={size} fill={bgColor} rx=\"12\" ry=\"12\" />\n\n      {finderPositions.map(([row, col]) => {\n        const x = col * moduleSize\n        const y = row * moduleSize\n\n        return (\n          <g key={`${row}-${col}`}>\n            <rect\n              x={x}\n              y={y}\n              width={finderSize}\n              height={finderSize}\n              fill={fgColor}\n              rx=\"12\"\n              ry=\"12\"\n            />\n            <rect\n              x={x + innerPadding}\n              y={y + innerPadding}\n              width={innerWhiteSize}\n              height={innerWhiteSize}\n              fill={bgColor}\n              rx=\"8\"\n              ry=\"8\"\n            />\n            <rect\n              x={x + innerPadding * 2}\n              y={y + innerPadding * 2}\n              width={innerBlackSize}\n              height={innerBlackSize}\n              fill={fgColor}\n              rx=\"3\"\n              ry=\"3\"\n            />\n          </g>\n        )\n      })}\n\n      {circles.map(({ cx, cy }, index) => (\n        <circle\n          key={`${cx}-${cy}-${index}`}\n          cx={cx}\n          cy={cy}\n          r={circleRadius}\n          fill={fgColor}\n        />\n      ))}\n\n      {logo ? (\n        <>\n          <rect\n            x={logoX}\n            y={logoY}\n            width={logoSize}\n            height={logoSize}\n            fill={bgColor}\n            rx={logoSize / 5}\n            ry={logoSize / 5}\n          />\n          <foreignObject x={logoX} y={logoY} width={logoSize} height={logoSize}>\n            <div\n              aria-hidden=\"true\"\n              style={{\n                display: \"flex\",\n                alignItems: \"center\",\n                justifyContent: \"center\",\n                width: \"100%\",\n                height: \"100%\",\n              }}\n            >\n              {logo}\n            </div>\n          </foreignObject>\n        </>\n      ) : null}\n    </svg>\n  )\n}\n\n/**\n * A loading placeholder rendered as an SVG with the same footprint as\n * `QrCode`, so any CSS that scales the code (e.g. `[&_svg]:w-full`) styles\n * both identically and swapping between them causes zero layout shift.\n */\nfunction QrCodeSkeleton({\n  size = 268,\n  className,\n  ...props\n}: Omit<React.ComponentProps<\"svg\">, \"viewBox\"> & Pick<QrCodeProps, \"size\">) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      viewBox={`0 0 ${size} ${size}`}\n      className={cn(\"block animate-pulse text-muted\", className)}\n      data-slot=\"qr-code-skeleton\"\n      width={size}\n      height={size}\n      {...props}\n    >\n      <rect width={size} height={size} rx=\"12\" ry=\"12\" fill=\"currentColor\" />\n    </svg>\n  )\n}\n\nexport { QrCode, QrCodeSkeleton }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}