import { type ReactNode, useEffect, useMemo, useRef } from "react"; import QRCodeStyling, { type Options } from "qr-code-styling"; import { BitcoinPaymentIcon } from "src/components/icons/BitcoinPayment"; import { LightningIcon } from "src/components/icons/Lightning"; import { cn } from "src/lib/utils"; export type Props = { value: string; size?: number; className?: string; showAvatar?: boolean; frameType?: "lightning" | "onchain"; paymentType?: "lightning" | "onchain"; centerContent?: ReactNode; // Use Q when an external overlay covers part of the QR code. level?: "Q" | undefined; }; function QRCode({ value, size = 256, level, className, showAvatar = false, frameType, paymentType, centerContent, }: Props) { const resolvedFrameType = paymentType ?? frameType; const hasCenterContent = Boolean(centerContent); const containerRef = useRef(null); const options = useMemo( () => ({ type: "svg", width: size, height: size, data: value, image: showAvatar ? "/icon-lightmode.svg" : undefined, margin: 0, qrOptions: { errorCorrectionLevel: level ?? (showAvatar || paymentType || hasCenterContent ? "Q" : "M"), }, imageOptions: { crossOrigin: "anonymous", hideBackgroundDots: true, imageSize: 0.15, margin: 4, }, dotsOptions: { color: "var(--qr-foreground)", type: "dots", roundSize: false, }, cornersSquareOptions: { color: "var(--qr-foreground)", type: "extra-rounded", }, cornersDotOptions: { color: "var(--qr-foreground)", type: "dot", }, backgroundOptions: { color: "var(--qr-background)", }, }), [hasCenterContent, level, paymentType, showAvatar, size, value] ); useEffect(() => { const container = containerRef.current; if (!container) { return; } const qrCode = new QRCodeStyling(options); qrCode.append(container); return () => { container.replaceChildren(); }; }, [options]); return (
{(paymentType || centerContent) && (
{centerContent ?? (paymentType === "lightning" ? ( ) : ( ))}
)}
); } export default QRCode;