PetroMind_AI / frontend /components /PdfViewer.tsx
gauthamnairy's picture
Upload 41 files
609c821 verified
import React, { useState, useEffect } from 'react';
import { Document, Page, pdfjs } from 'react-pdf';
import { Icons } from '../constants';
// Set worker source
pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
interface PdfViewerProps {
fileBase64: string;
mimeType: string;
currentPage?: number;
}
const PdfViewer: React.FC<PdfViewerProps> = ({ fileBase64, mimeType, currentPage = 1 }) => {
const [numPages, setNumPages] = useState<number>(0);
const [pageNumber, setPageNumber] = useState(currentPage);
const [scale, setScale] = useState(1.0);
const [containerWidth, setContainerWidth] = useState<number>(0);
// Sync with prop
useEffect(() => {
if (currentPage && currentPage !== pageNumber) {
setPageNumber(currentPage);
}
}, [currentPage]);
// Handle Resize
const containerRef = React.useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
setContainerWidth(entry.contentRect.width);
}
});
observer.observe(containerRef.current);
return () => observer.disconnect();
}, []);
function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
setNumPages(numPages);
}
const handlePrevPage = () => setPageNumber(p => Math.max(p - 1, 1));
const handleNextPage = () => setPageNumber(p => Math.min(p + 1, numPages));
const handleZoomIn = () => setScale(s => Math.min(s + 0.25, 2.0));
const handleZoomOut = () => setScale(s => Math.max(s - 0.25, 0.5));
const dataUrl = `data:${mimeType};base64,${fileBase64}`;
// If not PDF, show image
if (mimeType !== 'application/pdf') {
return (
<div className="h-full w-full bg-industrial-900 rounded-lg border border-industrial-800 flex flex-col overflow-hidden">
<div className="flex-1 relative bg-gray-800 overflow-auto flex items-center justify-center p-4">
<img src={dataUrl} alt="Source" className="max-w-full shadow-lg" />
</div>
</div>
);
}
return (
<div className="h-full w-full bg-industrial-900 rounded-lg border border-industrial-800 flex flex-col overflow-hidden">
{/* Controls */}
<div className="bg-industrial-950 px-4 py-2 border-b border-industrial-800 flex justify-between items-center shrink-0">
<span className="text-xs font-bold text-gray-500 uppercase">Document Viewer</span>
<div className="flex items-center gap-2">
{/* Page Nav */}
<div className="flex items-center gap-1 bg-industrial-900 rounded px-2 py-1">
<button onClick={handlePrevPage} disabled={pageNumber <= 1} className="p-1 hover:bg-industrial-800 rounded disabled:opacity-30">
<Icons.ChevronLeft />
</button>
<span className="text-xs text-gray-400 px-2 min-w-[60px] text-center">
{pageNumber} / {numPages || '--'}
</span>
<button onClick={handleNextPage} disabled={pageNumber >= numPages} className="p-1 hover:bg-industrial-800 rounded disabled:opacity-30">
<Icons.ChevronRight />
</button>
</div>
{/* Zoom */}
<div className="flex items-center gap-1 bg-industrial-900 rounded px-2 py-1">
<button onClick={handleZoomOut} className="p-1 hover:bg-industrial-800 rounded">
<Icons.ZoomOut />
</button>
<span className="text-xs text-gray-400 px-2">{Math.round(scale * 100)}%</span>
<button onClick={handleZoomIn} className="p-1 hover:bg-industrial-800 rounded">
<Icons.ZoomIn />
</button>
</div>
</div>
</div>
{/* PDF Canvas */}
<div ref={containerRef} className="flex-1 relative bg-gray-800 overflow-auto flex justify-center p-8">
<Document
file={dataUrl}
onLoadSuccess={onDocumentLoadSuccess}
loading={<div className="text-white text-sm">Loading PDF...</div>}
error={<div className="text-red-400 text-sm">Failed to load PDF.</div>}
className="shadow-2xl"
>
<Page
pageNumber={pageNumber}
width={containerWidth ? Math.min(containerWidth - 64, 800) : 600}
scale={scale}
renderTextLayer={false}
renderAnnotationLayer={false}
className="shadow-lg"
loading={null}
/>
</Document>
</div>
</div>
);
};
export default PdfViewer;