import { useEffect, useRef, useState } from "react"; import type { PacketPage } from "../api/types"; import { openPdf } from "../lib/pdf"; const ZOOMS = [0.5, 0.75, 1, 1.25, 1.5, 2, 3]; /** * The drawing pages, one at a time, fitted to the width. Zoom buttons and a page list. No marking yet. * `onPrintSheet` adds "Print this sheet" for the shown page (its packet page number). */ export function DrawingViewer({ pages, onPrintSheet }: { pages: PacketPage[]; onPrintSheet?: (n: number) => void }) { const [sel, setSel] = useState(0); const [zi, setZi] = useState(2); const [width, setWidth] = useState(0); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); const wrapRef = useRef(null); const canvasRef = useRef(null); const page = pages[Math.min(sel, pages.length - 1)]; const zoom = ZOOMS[zi] ?? 1; const url = page?.url ?? ""; const pn = page?.pn ?? 1; // Fit to width: follow the frame's width. useEffect(() => { const el = wrapRef.current; if (!el) return; const measure = () => setWidth(el.clientWidth); measure(); if (typeof ResizeObserver === "undefined") return; const ro = new ResizeObserver(measure); ro.observe(el); return () => ro.disconnect(); }, []); useEffect(() => { if (!url || !width) return; let cancelled = false; let task: { cancel: () => void; promise: Promise } | null = null; setBusy(true); setError(null); openPdf(url) .then((doc) => doc.getPage(Math.max(1, Math.min(pn || 1, doc.numPages)))) .then(async (pg) => { if (cancelled) return; const canvas = canvasRef.current; if (!canvas) return; const base = pg.getViewport({ scale: 1 }); const scale = (Math.max(200, width - 2) / base.width) * zoom; const ratio = window.devicePixelRatio || 1; const vp = pg.getViewport({ scale: scale * ratio }); canvas.width = Math.floor(vp.width); canvas.height = Math.floor(vp.height); canvas.style.width = `${Math.floor(vp.width / ratio)}px`; canvas.style.height = `${Math.floor(vp.height / ratio)}px`; task = pg.render({ canvas, viewport: vp }); await task.promise; if (!cancelled) setBusy(false); }) .catch((err: unknown) => { if (cancelled) return; if (err && typeof err === "object" && "name" in err && (err as { name: string }).name === "RenderingCancelledException") return; setBusy(false); setError("The drawing could not be shown. Open it in a new tab instead."); }); return () => { cancelled = true; task?.cancel(); }; }, [url, pn, width, zoom]); if (!page) return
No drawing attached yet.
; return (
{Math.round(zoom * 100)}% {onPrintSheet ? ( ) : null} Open in a new tab
    {pages.map((p, i) => (
  1. ))}
{error ?
{error}
: null} {busy && !error ?
Drawing the page
: null}
); }