2025-05-15 23:12:39 +01:00
|
|
|
import React, { useEffect, useState } from "react";
|
|
|
|
import { Paper, Stack, Text, ScrollArea, Loader, Center, Button, Group } from "@mantine/core";
|
2025-05-21 21:47:44 +01:00
|
|
|
import { getDocument, GlobalWorkerOptions } from "pdfjs-dist";
|
2025-05-15 23:12:39 +01:00
|
|
|
|
|
|
|
GlobalWorkerOptions.workerSrc = `${process.env.PUBLIC_URL}/pdf.worker.js`;
|
2025-05-15 20:07:33 +01:00
|
|
|
|
2025-05-21 21:47:44 +01:00
|
|
|
export interface ViewerProps {
|
|
|
|
pdfFile: { file: File; url: string } | null;
|
|
|
|
setPdfFile: (file: { file: File; url: string } | null) => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Viewer: React.FC<ViewerProps> = ({ pdfFile, setPdfFile }) => {
|
|
|
|
const [numPages, setNumPages] = useState<number>(0);
|
|
|
|
const [pageImages, setPageImages] = useState<string[]>([]);
|
|
|
|
const [loading, setLoading] = useState<boolean>(false);
|
2025-05-15 23:12:39 +01:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
let cancelled = false;
|
|
|
|
async function renderPages() {
|
|
|
|
if (!pdfFile || !pdfFile.url) {
|
|
|
|
setNumPages(0);
|
|
|
|
setPageImages([]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setLoading(true);
|
|
|
|
try {
|
|
|
|
const pdf = await getDocument(pdfFile.url).promise;
|
|
|
|
setNumPages(pdf.numPages);
|
2025-05-21 21:47:44 +01:00
|
|
|
const images: string[] = [];
|
2025-05-15 23:12:39 +01:00
|
|
|
for (let i = 1; i <= pdf.numPages; i++) {
|
|
|
|
const page = await pdf.getPage(i);
|
|
|
|
const viewport = page.getViewport({ scale: 1.2 });
|
|
|
|
const canvas = document.createElement("canvas");
|
|
|
|
canvas.width = viewport.width;
|
|
|
|
canvas.height = viewport.height;
|
|
|
|
const ctx = canvas.getContext("2d");
|
2025-05-21 21:47:44 +01:00
|
|
|
if (ctx) {
|
|
|
|
await page.render({ canvasContext: ctx, viewport }).promise;
|
|
|
|
images.push(canvas.toDataURL());
|
|
|
|
}
|
2025-05-15 23:12:39 +01:00
|
|
|
}
|
|
|
|
if (!cancelled) setPageImages(images);
|
|
|
|
} catch {
|
|
|
|
if (!cancelled) setPageImages([]);
|
|
|
|
}
|
|
|
|
if (!cancelled) setLoading(false);
|
|
|
|
}
|
|
|
|
renderPages();
|
|
|
|
return () => { cancelled = true; };
|
|
|
|
}, [pdfFile]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Paper shadow="xs" radius="md" p="md" style={{ height: "100%", minHeight: 400, display: "flex", flexDirection: "column" }}>
|
|
|
|
{!pdfFile ? (
|
|
|
|
<Center style={{ flex: 1 }}>
|
|
|
|
<Stack align="center">
|
|
|
|
<Text color="dimmed">No PDF loaded. Click to upload a PDF.</Text>
|
|
|
|
<Button
|
|
|
|
component="label"
|
|
|
|
variant="outline"
|
|
|
|
color="blue"
|
|
|
|
>
|
|
|
|
Choose PDF
|
|
|
|
<input
|
|
|
|
type="file"
|
|
|
|
accept="application/pdf"
|
|
|
|
hidden
|
|
|
|
onChange={(e) => {
|
2025-05-21 21:47:44 +01:00
|
|
|
const file = e.target.files?.[0];
|
2025-05-15 23:12:39 +01:00
|
|
|
if (file && file.type === "application/pdf") {
|
|
|
|
const fileUrl = URL.createObjectURL(file);
|
|
|
|
setPdfFile({ file, url: fileUrl });
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</Button>
|
|
|
|
</Stack>
|
|
|
|
</Center>
|
|
|
|
) : loading ? (
|
|
|
|
<Center style={{ flex: 1 }}>
|
|
|
|
<Loader size="lg" />
|
|
|
|
</Center>
|
|
|
|
) : (
|
|
|
|
<ScrollArea style={{ flex: 1, height: "100%" }}>
|
2025-05-21 21:47:44 +01:00
|
|
|
<Stack gap="xl" align="center">
|
2025-05-15 23:12:39 +01:00
|
|
|
{pageImages.length === 0 && (
|
|
|
|
<Text color="dimmed">No pages to display.</Text>
|
|
|
|
)}
|
|
|
|
{pageImages.map((img, idx) => (
|
|
|
|
<img
|
|
|
|
key={idx}
|
|
|
|
src={img}
|
|
|
|
alt={`Page ${idx + 1}`}
|
|
|
|
style={{
|
|
|
|
width: "100%",
|
|
|
|
maxWidth: 700,
|
|
|
|
boxShadow: "0 2px 8px rgba(0,0,0,0.08)",
|
|
|
|
borderRadius: 8,
|
|
|
|
background: "#fff"
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</Stack>
|
|
|
|
</ScrollArea>
|
|
|
|
)}
|
|
|
|
{pdfFile && (
|
2025-05-21 21:47:44 +01:00
|
|
|
<Group justify="flex-end" mt="md">
|
2025-05-15 23:12:39 +01:00
|
|
|
<Button
|
|
|
|
variant="light"
|
|
|
|
color="red"
|
|
|
|
onClick={() => setPdfFile(null)}
|
|
|
|
>
|
|
|
|
Close PDF
|
|
|
|
</Button>
|
|
|
|
</Group>
|
|
|
|
)}
|
|
|
|
</Paper>
|
2025-05-15 20:07:33 +01:00
|
|
|
);
|
2025-05-21 21:47:44 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
export default Viewer;
|