import React, { useEffect, useRef } from 'react'; import type { PdfFile } from '../pdf/pdfTypes'; import * as pdfjsLib from 'pdfjs-dist'; import pdfjsWorker from 'pdfjs-dist/build/pdf.worker?worker&url'; // pdf.js worker setup // eslint-disable-next-line @typescript-eslint/no-explicit-any (pdfjsLib as any).GlobalWorkerOptions.workerSrc = pdfjsWorker; interface PagePreviewModalProps { isOpen: boolean; pdf: PdfFile | null; pageIndex: number | null; // original page index (0-based) rotation: number; // degrees onClose: () => void; } const PagePreviewModal: React.FC = ({ isOpen, pdf, pageIndex, rotation, onClose, }) => { const canvasRef = useRef(null); useEffect(() => { if (!isOpen || !pdf || pageIndex == null) return; let cancelled = false; (async () => { try { // copy data for pdf.js (avoid detaching original ArrayBuffer) const src = new Uint8Array(pdf.arrayBuffer); const copy = new Uint8Array(src.byteLength); copy.set(src); const loadingTask = pdfjsLib.getDocument({ data: copy }); const doc = await loadingTask.promise; if (cancelled) return; const page = await doc.getPage(pageIndex + 1); if (cancelled) return; const viewport = page.getViewport({ scale: 1 }); const maxWidth = Math.min(window.innerWidth * 0.9, 800); const scale = maxWidth / viewport.width; const scaledViewport = page.getViewport({ scale }); const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; // base size let canvasWidth = scaledViewport.width; let canvasHeight = scaledViewport.height; const angle = ((rotation % 360) + 360) % 360; if (angle === 90 || angle === 270) { canvasWidth = scaledViewport.height; canvasHeight = scaledViewport.width; } canvas.width = canvasWidth; canvas.height = canvasHeight; // render into an offscreen canvas first const baseCanvas = document.createElement('canvas'); const baseCtx = baseCanvas.getContext('2d'); if (!baseCtx) return; baseCanvas.width = scaledViewport.width; baseCanvas.height = scaledViewport.height; const renderTask = page.render({ canvasContext: baseCtx, viewport: scaledViewport, }); await renderTask.promise; if (cancelled) return; // draw rotated onto visible canvas ctx.save(); switch (angle) { case 90: ctx.translate(canvasWidth, 0); ctx.rotate((angle * Math.PI) / 180); break; case 180: ctx.translate(canvasWidth, canvasHeight); ctx.rotate((angle * Math.PI) / 180); break; case 270: ctx.translate(0, canvasHeight); ctx.rotate((angle * Math.PI) / 180); break; default: break; } ctx.drawImage(baseCanvas, 0, 0); ctx.restore(); } catch (e) { console.error('Error rendering preview', e); } })(); return () => { cancelled = true; }; }, [isOpen, pdf, pageIndex, rotation]); if (!isOpen || !pdf || pageIndex == null) return null; return (
e.stopPropagation()} style={{ background: '#111827', borderRadius: '0.75rem', padding: '0.75rem', maxWidth: '90vw', maxHeight: '90vh', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.5rem', }} >
Page {pageIndex + 1} · Rot {rotation}°
); }; export default PagePreviewModal;