{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "qr-code",
  "title": "Qr Code",
  "type": "registry:ui",
  "description": "QR code renderer — pass `value` and a size, get a canvas PNG or SVG. Useful for sign-in links, share URLs, and Wi-Fi credentials. Depends on the `qrcode` package.",
  "categories": [
    "data-display"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/qr-code/qr-code.ts",
      "content": "import { LitElement, css, html, isServer, nothing } from 'lit'\nimport { unsafeSVG } from 'lit/directives/unsafe-svg.js'\nimport { Check, Loader2, RotateCcw, ScanLine } from 'lucide'\nimport { cn } from '../../lib/utils'\nimport { icon } from '../../lib/icon'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\n\nexport type QRCodeType = 'canvas' | 'svg'\nexport type QRCodeStatus = 'active' | 'expired' | 'loading' | 'scanned'\nexport type QRCodeErrorLevel = 'L' | 'M' | 'Q' | 'H'\n\n/**\n * <uip-qr-code> — QR Code generator supporting Canvas and SVG rendering modes,\n * error correction levels, status overlays (active, expired, loading, scanned),\n * center logos/icons, and PNG downloads.\n */\nexport class UipQrCode extends LitElement {\n  static styles = [\n    tailwind,\n    css`\n      :host {\n        display: inline-flex;\n      }\n    `,\n  ]\n\n  static properties = {\n    value: { type: String },\n    type: { type: String },\n    size: { type: Number },\n    color: { type: String },\n    bgColor: { type: String, attribute: 'bg-color' },\n    icon: { type: String },\n    iconSize: { type: Number, attribute: 'icon-size' },\n    errorLevel: { type: String, attribute: 'error-level' },\n    bordered: {\n      type: Boolean,\n      converter: { fromAttribute: (v: string | null) => v !== null && v !== 'false' },\n    },\n    status: { type: String },\n    marginSize: { type: Number, attribute: 'margin-size' },\n    qrDataUrl: { state: true },\n    qrSvg: { state: true },\n  }\n\n  value = ''\n  type: QRCodeType = 'canvas'\n  size = 160\n  color = '#000000'\n  bgColor = '#ffffff'\n  icon = ''\n  iconSize = 40\n  errorLevel: QRCodeErrorLevel = 'M'\n  bordered = true\n  status: QRCodeStatus = 'active'\n  marginSize = 0\n\n  qrDataUrl = ''\n  qrSvg = ''\n\n  private isGenerating = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'qr-code')\n    this.generateQR()\n  }\n\n  updated(changedProperties: Map<string, unknown>) {\n    if (\n      changedProperties.has('value') ||\n      changedProperties.has('type') ||\n      changedProperties.has('size') ||\n      changedProperties.has('color') ||\n      changedProperties.has('bgColor') ||\n      changedProperties.has('errorLevel') ||\n      changedProperties.has('marginSize') ||\n      changedProperties.has('status')\n    ) {\n      this.generateQR()\n    }\n  }\n\n  private async generateQR() {\n    if (isServer || !this.value || this.status === 'loading' || this.isGenerating) return\n    this.isGenerating = true\n    try {\n      // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n      // @ts-ignore\n      const mod = await import('qrcode')\n      const QRCodeLib = mod.default || mod\n\n      const options = {\n        width: this.size,\n        margin: this.marginSize,\n        color: { dark: this.color, light: this.bgColor },\n        errorCorrectionLevel: this.errorLevel,\n      }\n\n      if (this.type === 'svg') {\n        const svg = await QRCodeLib.toString(this.value, { type: 'svg', ...options })\n        this.qrSvg = svg\n      } else {\n        const url = await QRCodeLib.toDataURL(this.value, options)\n        this.qrDataUrl = url\n      }\n    } catch (e) {\n      console.error('QR Code generation failed:', e)\n    } finally {\n      this.isGenerating = false\n    }\n  }\n\n  private downloadQR() {\n    if (!this.qrDataUrl) return\n    const link = document.createElement('a')\n    link.download = `qrcode-${this.value.slice(0, 20)}.png`\n    link.href = this.qrDataUrl\n    link.click()\n  }\n\n  private onRefreshClick() {\n    this.dispatchEvent(new CustomEvent('refresh', { bubbles: true, composed: true }))\n  }\n\n  render() {\n    const statusOverlay = (() => {\n      switch (this.status) {\n        case 'expired':\n          return {\n            icon: icon(RotateCcw, 'rotate-ccw', 'size-8'),\n            text: 'Expired',\n            canRefresh: true,\n          }\n        case 'scanned':\n          return {\n            icon: icon(Check, 'check', 'size-8'),\n            text: 'Scanned',\n            canRefresh: false,\n          }\n        case 'loading':\n          return {\n            icon: icon(Loader2, 'loader-2', 'size-8 animate-spin'),\n            text: 'Loading...',\n            canRefresh: false,\n          }\n        default:\n          return null\n      }\n    })()\n\n    return html`\n      <div\n        part=\"base\"\n        aria-busy=${this.status === 'loading' ? 'true' : nothing}\n        class=${cn(\n          'inline-flex flex-col items-center gap-2',\n          this.bordered && 'bg-background rounded-lg border p-4',\n        )}\n      >\n        <div\n          class=\"relative inline-flex items-center justify-center overflow-hidden\"\n          style=\"width: ${this.size}px; height: ${this.size}px;\"\n        >\n          <!-- QR Code Content -->\n          ${this.type === 'svg' && this.qrSvg\n            ? html`<div class=\"size-full [&_svg]:size-full\">${unsafeSVG(this.qrSvg)}</div>`\n            : this.qrDataUrl\n              ? html`<img src=${this.qrDataUrl} alt=\"QR Code for ${this.value}\" class=\"size-full\" />`\n              : nothing}\n\n          <!-- Center Icon Overlay -->\n          ${this.icon && this.status === 'active'\n            ? html`\n                <div class=\"absolute inset-0 flex items-center justify-center\">\n                  <div\n                    class=\"bg-background overflow-hidden rounded-md shadow-sm\"\n                    style=\"width: ${this.iconSize}px; height: ${this.iconSize}px;\"\n                  >\n                    <img src=${this.icon} alt=\"\" class=\"size-full object-cover\" />\n                  </div>\n                </div>\n              `\n            : nothing}\n\n          <!-- Status Overlay -->\n          ${statusOverlay\n            ? html`\n                <div\n                  class=\"bg-background/90 absolute inset-0 flex flex-col items-center justify-center gap-2 backdrop-blur-sm\"\n                >\n                  ${statusOverlay.icon}\n                  <span class=\"text-foreground text-sm font-medium\">${statusOverlay.text}</span>\n                  ${statusOverlay.canRefresh\n                    ? html`\n                        <button\n                          type=\"button\"\n                          class=\"bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-ring inline-flex items-center gap-1 rounded-md px-3 py-1 text-xs font-medium focus-visible:ring-2 focus-visible:outline-none\"\n                          @click=${this.onRefreshClick}\n                        >\n                          ${icon(ScanLine, 'scan-line', 'size-3')}\n                          Refresh\n                        </button>\n                      `\n                    : nothing}\n                </div>\n              `\n            : nothing}\n        </div>\n\n        <!-- Download button / Extra slot -->\n        <slot name=\"extra\">\n          ${this.type === 'canvas' && this.status === 'active' && this.qrDataUrl\n            ? html`\n                <button\n                  type=\"button\"\n                  class=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring text-xs underline-offset-2 hover:underline focus-visible:ring-2 focus-visible:outline-none\"\n                  @click=${this.downloadQR}\n                >\n                  Download\n                </button>\n              `\n            : nothing}\n        </slot>\n      </div>\n    `\n  }\n}\n\ncustomElements.get('uip-qr-code') || customElements.define('uip-qr-code', UipQrCode)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-qr-code': UipQrCode\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/qr-code/qr-code.ts"
    }
  ],
  "dependencies": [
    "lit",
    "lucide",
    "qrcode"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
