Spaces:
Running
Running
File size: 4,251 Bytes
26a0c00 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | "use client";
import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Loader2 } from "lucide-react";
import { API_BASE } from "@/lib/api";
interface Props {
documentId: string;
currentPage: number;
onPageChange: (page: number) => void;
totalPages: number;
}
export default function PDFViewer({ documentId, currentPage, onPageChange, totalPages }: Props) {
const [scale, setScale] = useState(1.0);
const [loading, setLoading] = useState(true);
const [pageInput, setPageInput] = useState(String(currentPage));
useEffect(() => {
setPageInput(String(currentPage));
}, [currentPage]);
const token = typeof window !== "undefined" ? localStorage.getItem("token") : null;
const pdfUrl = `${API_BASE}/api/v1/documents/${documentId}/pdf`;
// Use an iframe to render the PDF with the browser's native viewer
// We append a page fragment for navigation
const iframeSrc = `${pdfUrl}#page=${currentPage}`;
const handlePageSubmit = (e: React.FormEvent) => {
e.preventDefault();
const num = parseInt(pageInput.trim());
if (!isNaN(num) && num >= 1 && num <= totalPages) {
onPageChange(num);
} else {
setPageInput(String(currentPage));
}
};
return (
<div className="h-full flex flex-col bg-background">
{/* ββ Toolbar βββββββββββββββββββββββββββββββββββ */}
<div className="flex items-center justify-between px-3 py-2 border-b border-border/50 bg-card/50 shrink-0">
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="icon"
className="h-7 w-7"
onClick={() => onPageChange(Math.max(1, currentPage - 1))}
disabled={currentPage <= 1}
>
<ChevronLeft className="w-4 h-4" />
</Button>
<form
onSubmit={handlePageSubmit}
className="flex items-center gap-1 text-xs"
>
<Input
value={pageInput}
onChange={(e) => setPageInput(e.target.value)}
className="w-10 h-7 text-center text-xs p-0 bg-background/50"
/>
<span className="text-muted-foreground">/ {totalPages}</span>
</form>
<Button
variant="ghost"
size="icon"
className="h-7 w-7"
onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
disabled={currentPage >= totalPages}
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="icon"
className="h-7 w-7"
onClick={() => setScale((s) => Math.max(0.5, s - 0.15))}
>
<ZoomOut className="w-3.5 h-3.5" />
</Button>
<span className="text-[10px] text-muted-foreground min-w-[36px] text-center">
{Math.round(scale * 100)}%
</span>
<Button
variant="ghost"
size="icon"
className="h-7 w-7"
onClick={() => setScale((s) => Math.min(2.5, s + 0.15))}
>
<ZoomIn className="w-3.5 h-3.5" />
</Button>
</div>
</div>
{/* ββ PDF Render ββββββββββββββββββββββββββββββββ */}
<div className="flex-1 overflow-auto relative">
{loading && (
<div className="absolute inset-0 flex items-center justify-center bg-background/80 z-10">
<Loader2 className="w-6 h-6 animate-spin text-primary" />
</div>
)}
<iframe
key={`${documentId}-${currentPage}`}
src={iframeSrc}
className="w-full h-full border-0"
style={{ transform: `scale(${scale})`, transformOrigin: "top left", width: `${100/scale}%`, height: `${100/scale}%` }}
onLoad={() => setLoading(false)}
title="PDF Viewer"
/>
</div>
</div>
);
}
|