{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "qr-code-generator",
  "type": "registry:component",
  "title": "QR Code Generator",
  "description": "A text input with a live QR code preview, style presets and download buttons.",
  "author": "bucharitesh <https://qrdx.dev>",
  "dependencies": ["qrdx", "lucide-react"],
  "registryDependencies": ["button", "input", "label"],
  "files": [
    {
      "path": "registry/qrdx/qr-code-generator.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport { Download } from \"lucide-react\";\nimport { type DownloadFormat, downloadQRCode, getQRData, QRCode } from \"qrdx\";\nimport { type ComponentProps, useId, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\n\n/** qrdx style options: colors, gradients, patterns, logo, template. */\nexport type QrCodeStyle = Omit<ComponentProps<typeof QRCode>, \"value\" | \"size\">;\n\nexport interface QrCodePreset {\n  id: string;\n  name: string;\n  style: QrCodeStyle;\n}\n\nexport const QR_CODE_PRESETS: QrCodePreset[] = [\n  {\n    id: \"classic\",\n    name: \"Classic\",\n    style: {\n      fgColor: \"#09090b\",\n      bodyPattern: \"square\",\n      cornerEyePattern: \"square\",\n      cornerEyeDotPattern: \"square\",\n    },\n  },\n  {\n    id: \"rounded\",\n    name: \"Rounded\",\n    style: {\n      fgColor: \"#18181b\",\n      bodyPattern: \"rounded\",\n      cornerEyePattern: \"extra-rounded\",\n      cornerEyeDotPattern: \"rounded\",\n    },\n  },\n  {\n    id: \"dots\",\n    name: \"Dots\",\n    style: {\n      fgColor: \"#1d4ed8\",\n      bodyPattern: \"circle\",\n      cornerEyePattern: \"circle\",\n      cornerEyeDotPattern: \"circle\",\n    },\n  },\n  {\n    id: \"ocean\",\n    name: \"Ocean\",\n    style: {\n      fgColor: {\n        type: \"linear\",\n        angle: 45,\n        stops: [\n          { color: \"#0369a1\", offset: 0 },\n          { color: \"#4338ca\", offset: 100 },\n        ],\n      },\n      bodyPattern: \"circle-large\",\n      cornerEyePattern: \"rounded\",\n      cornerEyeDotPattern: \"circle\",\n    },\n  },\n  {\n    id: \"sunset\",\n    name: \"Sunset\",\n    style: {\n      fgColor: {\n        type: \"linear\",\n        angle: 135,\n        stops: [\n          { color: \"#c2410c\", offset: 0 },\n          { color: \"#9d174d\", offset: 100 },\n        ],\n      },\n      bodyPattern: \"diamond\",\n      cornerEyePattern: \"rounded\",\n      cornerEyeDotPattern: \"diamond\",\n    },\n  },\n  {\n    id: \"forest\",\n    name: \"Forest\",\n    style: {\n      fgColor: \"#166534\",\n      eyeColor: \"#14532d\",\n      bodyPattern: \"small-square\",\n      cornerEyePattern: \"square\",\n      cornerEyeDotPattern: \"rounded\",\n    },\n  },\n];\n\nexport interface QrCodeGeneratorProps\n  extends Omit<\n    ComponentProps<\"div\">,\n    \"children\" | \"defaultValue\" | \"onChange\"\n  > {\n  /**\n   * Initial text or URL.\n   * @default \"https://qrdx.dev\"\n   */\n  defaultValue?: string;\n  /**\n   * Style presets to pick from.\n   * @default QR_CODE_PRESETS\n   */\n  presets?: QrCodePreset[];\n  /** Id of the preset selected first. Defaults to the first preset. */\n  defaultPreset?: string;\n  /**\n   * Width and height of the preview in pixels.\n   * @default 200\n   */\n  size?: number;\n  /**\n   * Download buttons under the preview. Pass `[]` to hide them.\n   * @default [\"png\", \"svg\"]\n   */\n  formats?: DownloadFormat[];\n  /**\n   * Download file name without extension.\n   * @default \"qr-code\"\n   */\n  filename?: string;\n  /** Called when the text or the preset changes. */\n  onChange?: (state: { value: string; preset: QrCodePreset }) => void;\n}\n\nexport function QrCodeGenerator({\n  defaultValue = \"https://qrdx.dev\",\n  presets = QR_CODE_PRESETS,\n  defaultPreset,\n  size = 200,\n  formats = [\"png\", \"svg\"],\n  filename = \"qr-code\",\n  onChange,\n  className,\n  ...props\n}: QrCodeGeneratorProps) {\n  const inputId = useId();\n  const [value, setValue] = useState(defaultValue);\n  const [presetId, setPresetId] = useState(\n    defaultPreset ?? presets[0]?.id ?? \"\",\n  );\n  const [pending, setPending] = useState(false);\n  const [error, setError] = useState(\"\");\n  const preset = presets.find((item) => item.id === presetId) ?? presets[0];\n  const style = preset?.style;\n\n  function update(nextValue: string, nextPreset = preset) {\n    setValue(nextValue);\n    if (nextPreset) {\n      setPresetId(nextPreset.id);\n      onChange?.({ value: nextValue, preset: nextPreset });\n    }\n  }\n\n  async function download(format: DownloadFormat) {\n    setPending(true);\n    setError(\"\");\n    try {\n      // getQRData lays a logo out for 1024px, the size of this file.\n      await downloadQRCode(getQRData({ margin: 2, ...style, value }), {\n        format,\n        size: { width: 1024, height: 1024 },\n        filename: `${filename}.${format}`,\n      });\n    } catch (cause) {\n      setError(\n        cause instanceof Error ? cause.message : \"Could not download the file.\",\n      );\n    } finally {\n      setPending(false);\n    }\n  }\n\n  return (\n    <div\n      className={cn(\n        \"grid w-full max-w-2xl gap-6 rounded-xl border bg-card p-4 text-card-foreground shadow-sm sm:grid-cols-[minmax(0,1fr)_auto] sm:p-6\",\n        className,\n      )}\n      data-slot=\"qr-code-generator\"\n      {...props}\n    >\n      <div className=\"flex min-w-0 flex-col gap-5\">\n        <div className=\"grid gap-2\">\n          <Label htmlFor={inputId}>Text or URL</Label>\n          <Input\n            id={inputId}\n            onChange={(event) => update(event.target.value)}\n            placeholder=\"https://example.com\"\n            value={value}\n          />\n        </div>\n        <fieldset className=\"grid gap-2\">\n          <legend className=\"mb-2 font-medium text-sm leading-none\">\n            Style\n          </legend>\n          <div className=\"grid grid-cols-3 gap-2\">\n            {presets.map((item) => {\n              const active = item.id === preset?.id;\n              return (\n                <button\n                  aria-pressed={active}\n                  className={cn(\n                    \"flex flex-col items-center gap-1.5 rounded-lg border p-2 font-medium text-xs outline-none transition-colors hover:bg-accent focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n                    active && \"border-foreground/40 bg-accent\",\n                  )}\n                  key={item.id}\n                  onClick={() => update(value, item)}\n                  type=\"button\"\n                >\n                  <span\n                    className=\"overflow-hidden rounded-[3px]\"\n                    aria-hidden=\"true\"\n                  >\n                    <QRCode margin={1} {...item.style} size={44} value=\"qrdx\" />\n                  </span>\n                  {item.name}\n                </button>\n              );\n            })}\n          </div>\n        </fieldset>\n      </div>\n      <div className=\"flex flex-col items-center gap-3\">\n        <div\n          aria-label={value ? `QR code: ${value}` : \"Empty QR code preview\"}\n          className=\"overflow-hidden rounded-[10px] border\"\n          role=\"img\"\n          style={{ width: size, height: size }}\n        >\n          {value ? (\n            <QRCode margin={2} {...style} size={size} value={value} />\n          ) : (\n            <div className=\"flex size-full items-center justify-center bg-muted p-6 text-center text-muted-foreground text-xs\">\n              Type some text to generate a code\n            </div>\n          )}\n        </div>\n        {formats.length > 0 ? (\n          <div className=\"flex flex-wrap justify-center gap-2\">\n            {formats.map((format) => (\n              <Button\n                disabled={!value || pending}\n                key={format}\n                onClick={() => download(format)}\n                size=\"sm\"\n                type=\"button\"\n                variant=\"outline\"\n              >\n                <Download aria-hidden=\"true\" />\n                {format.toUpperCase()}\n              </Button>\n            ))}\n          </div>\n        ) : null}\n        {error ? (\n          <p\n            className=\"max-w-[200px] text-center text-destructive text-xs\"\n            role=\"alert\"\n          >\n            {error}\n          </p>\n        ) : null}\n      </div>\n    </div>\n  );\n}\n"
    }
  ],
  "docs": "Import it with `import { QrCodeGenerator } from \"@/components/qr-code-generator\"`. Props and examples: https://qrdx.dev/docs/components/qr-code-generator",
  "categories": ["qr-code"]
}
