Spaces:
Running
Running
File size: 4,966 Bytes
8af739b |
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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
'use client'
import React, { useState } from 'react'
import dynamic from 'next/dynamic'
import { MagnifyingGlassPlus, MagnifyingGlassMinus, ArrowLeft, ArrowRight } from '@phosphor-icons/react'
// Dynamically import react-pdf with no SSR
const Document = dynamic(
() => import('react-pdf').then((mod) => mod.Document),
{ ssr: false }
)
const Page = dynamic(
() => import('react-pdf').then((mod) => mod.Page),
{ ssr: false }
)
// Configure PDF.js worker
if (typeof window !== 'undefined') {
import('react-pdf').then((pdfjs) => {
pdfjs.pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.pdfjs.version}/build/pdf.worker.min.mjs`
})
}
interface PDFViewerProps {
url: string
scale: number
onZoomIn: () => void
onZoomOut: () => void
}
export function PDFViewer({ url, scale, onZoomIn, onZoomOut }: PDFViewerProps) {
const [numPages, setNumPages] = useState<number | null>(null)
const [pageNumber, setPageNumber] = useState(1)
const [error, setError] = useState<string | null>(null)
const [isLoading, setIsLoading] = useState(true)
const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {
setNumPages(numPages)
setPageNumber(1)
setError(null)
setIsLoading(false)
}
const changePage = (offset: number) => {
setPageNumber(prevPageNumber => {
const newPageNumber = prevPageNumber + offset
if (newPageNumber < 1 || newPageNumber > (numPages || 1)) {
return prevPageNumber
}
return newPageNumber
})
}
if (!url) {
return <div className="flex items-center justify-center h-full text-gray-500">No PDF to display</div>
}
return (
<div className="flex flex-col h-full">
<div className="flex items-center gap-2 p-3 border-b border-gray-200 bg-gray-50">
<button
onClick={onZoomOut}
className="p-2 hover:bg-gray-200 rounded-lg transition-colors"
title="Zoom Out"
>
<MagnifyingGlassMinus className="h-5 w-5" />
</button>
<span className="text-sm text-gray-600 min-w-[60px] text-center">{Math.round(scale * 100)}%</span>
<button
onClick={onZoomIn}
className="p-2 hover:bg-gray-200 rounded-lg transition-colors"
title="Zoom In"
>
<MagnifyingGlassPlus className="h-5 w-5" />
</button>
{numPages && (
<>
<div className="mx-4 h-6 w-px bg-gray-300" />
<button
onClick={() => changePage(-1)}
disabled={pageNumber <= 1}
className="p-2 hover:bg-gray-200 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Previous Page"
>
<ArrowLeft className="h-5 w-5" />
</button>
<span className="text-sm text-gray-600 min-w-[80px] text-center">
Page {pageNumber} of {numPages}
</span>
<button
onClick={() => changePage(1)}
disabled={pageNumber >= numPages}
className="p-2 hover:bg-gray-200 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Next Page"
>
<ArrowRight className="h-5 w-5" />
</button>
</>
)}
</div>
<div className="flex-1 overflow-auto p-4 bg-gray-50">
{typeof window !== 'undefined' && url && (
<Document
file={url}
onLoadSuccess={onDocumentLoadSuccess}
onLoadError={(err) => {
console.warn('PDF load error:', err)
setError('Unable to load PDF. The file might be corrupted or in an unsupported format.')
setIsLoading(false)
}}
loading={
<div className="flex items-center justify-center h-full">
<div className="text-gray-600">Loading PDF...</div>
</div>
}
error={
<div className="flex flex-col items-center justify-center h-full">
<div className="text-red-500 mb-2">Failed to load PDF</div>
<div className="text-sm text-gray-600">{error || 'The file might be corrupted or in an unsupported format.'}</div>
</div>
}
className="flex justify-center"
>
{!isLoading && !error && (
<Page
pageNumber={pageNumber}
scale={scale}
renderTextLayer={false}
renderAnnotationLayer={false}
className="shadow-lg bg-white"
loading={
<div className="flex items-center justify-center h-96 bg-white shadow-lg">
<div className="text-gray-400">Loading page...</div>
</div>
}
/>
)}
</Document>
)}
</div>
</div>
)
} |