You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
47 lines
1.3 KiB
47 lines
1.3 KiB
'use client';
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
|
|
interface Props {
|
|
data: string;
|
|
size?: number;
|
|
}
|
|
|
|
/**
|
|
* Renders a styled QR code for `data`. `qr-code-styling` touches the DOM, so it
|
|
* is imported lazily inside the effect to stay clear of SSR.
|
|
*/
|
|
export function QrCode({ data, size = 240 }: Props) {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
// biome-ignore lint/suspicious/noExplicitAny: qr-code-styling instance type loaded lazily
|
|
const instanceRef = useRef<any>(null);
|
|
|
|
// biome-ignore lint/correctness/useExhaustiveDependencies: initial data is read once here; later changes go through update() in the next effect
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
(async () => {
|
|
const { default: QRCodeStyling } = await import('qr-code-styling');
|
|
if (cancelled) return;
|
|
instanceRef.current = new QRCodeStyling({
|
|
width: size,
|
|
height: size,
|
|
type: 'svg',
|
|
data,
|
|
dotsOptions: { type: 'rounded' },
|
|
});
|
|
if (containerRef.current) {
|
|
containerRef.current.replaceChildren();
|
|
instanceRef.current.append(containerRef.current);
|
|
}
|
|
})();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [size]);
|
|
|
|
useEffect(() => {
|
|
instanceRef.current?.update({ data });
|
|
}, [data]);
|
|
|
|
return <div ref={containerRef} role="img" aria-label="QR code" />;
|
|
}
|
|
|