All files / web/src/components/common AbacusQRCode.tsx

63.04% Statements 58/92
100% Branches 0/0
0% Functions 0/1
63.04% Lines 58/92

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 931x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                      
import type { ComponentProps } from 'react'
import { QRCodeSVG } from 'qrcode.react'
 
type QRCodeSVGProps = ComponentProps<typeof QRCodeSVG>
 
export interface AbacusQRCodeProps extends Omit<QRCodeSVGProps, 'imageSettings'> {
  /**
   * Override the default abacus logo with a custom image
   * If not provided, uses the abacus icon at /icon (only on QR codes >= 150px)
   */
  imageSettings?: QRCodeSVGProps['imageSettings']
 
  /**
   * Minimum size (in pixels) to show the logo
   * Below this threshold, QR code will be plain for better scannability
   * @default 150
   */
  minLogoSize?: number
}
 
/**
 * QR Code component with the abacus logo in the middle
 *
 * This is a thin wrapper around QRCodeSVG that adds our branding by default.
 * Use this as the standard QR code component throughout the app.
 *
 * **Smart logo sizing:** The abacus logo only appears on QR codes >= 150px.
 * Smaller QR codes show plain dots for better scannability.
 *
 * @example
 * ```tsx
 * // Large QR code - shows abacus logo
 * <AbacusQRCode
 *   value="https://abaci.one/share/abc123"
 *   size={200}
 * />
 * ```
 *
 * @example
 * ```tsx
 * // Small QR code - no logo (too small)
 * <AbacusQRCode
 *   value="https://abaci.one/room/xyz"
 *   size={84}
 * />
 * ```
 *
 * @example With custom styling
 * ```tsx
 * <AbacusQRCode
 *   value="https://abaci.one/room/xyz"
 *   size={300}
 *   fgColor="#1a1a2e"
 *   level="H"  // High error correction for better scanning with logo
 * />
 * ```
 */
export function AbacusQRCode({
  imageSettings,
  minLogoSize = 150,
  size = 128,
  level = 'H', // Default to high error correction for logo
  fgColor = '#111827',
  bgColor = '#ffffff',
  ...props
}: AbacusQRCodeProps) {
  // Only show logo on QR codes large enough for it to scan reliably
  const showLogo = typeof size === 'number' && size >= minLogoSize

  // Calculate logo size as 22% of QR code size (scales nicely)
  const logoSize = typeof size === 'number' ? Math.round(size * 0.22) : 48

  return (
    <QRCodeSVG
      {...props}
      size={size}
      level={level}
      fgColor={fgColor}
      bgColor={bgColor}
      imageSettings={
        showLogo
          ? (imageSettings ?? {
              src: '/icon',
              height: logoSize,
              width: logoSize,
              excavate: true, // Clear space behind logo for better scanning
            })
          : undefined // No logo on small QR codes
      }
    />
  )
}