11'use client' ;
22
3- import { RefObject } from 'react' ;
3+ import { RefObject , useCallback , useState , useEffect , useRef } from 'react' ;
44import { Document , Page } from 'react-pdf' ;
5+ import { FixedSizeList } from 'react-window' ;
56import 'react-pdf/dist/Page/AnnotationLayer.css' ;
67import 'react-pdf/dist/Page/TextLayer.css' ;
7- import { useState , useEffect , useRef } from 'react' ;
88import { PDFSkeleton } from './PDFSkeleton' ;
99import { useTTS } from '@/contexts/TTSContext' ;
1010import { usePDF } from '@/contexts/PDFContext' ;
11+ import { pdfjs } from 'react-pdf' ;
1112
1213interface PDFViewerProps {
1314 pdfData : Blob | undefined ;
1415 zoomLevel : number ;
1516}
1617
18+ interface PageItemData {
19+ numPages : number ;
20+ scale : number ;
21+ containerWidth : number ;
22+ pageWidth : number ;
23+ pageHeight : number ;
24+ }
25+
26+ const PageComponent = ( { index, style, data } : { index : number ; style : any ; data : PageItemData } ) => {
27+ const { numPages, scale, pageHeight } = data ;
28+
29+ return (
30+ < div style = { {
31+ ...style ,
32+ height : pageHeight ,
33+ display : 'flex' ,
34+ flexDirection : 'column' ,
35+ alignItems : 'center'
36+ } } >
37+ { index == 0 ? ( < div className = "bg-offbase m-4 px-2 py-0.5 rounded-full w-fit" >
38+ < p className = "text-xs" >
39+ { index + 1 } / { numPages }
40+ </ p >
41+ </ div > ) :
42+ ( < div className = "bg-offbase m-4 px-2 py-0.5 rounded-full w-fit" >
43+ < p className = "text-xs" >
44+ { index + 1 } / { numPages }
45+ </ p >
46+ </ div > ) }
47+ < div className = "flex justify-center" >
48+ < Page
49+ pageNumber = { index + 1 }
50+ renderAnnotationLayer = { true }
51+ renderTextLayer = { true }
52+ className = "shadow-lg"
53+ scale = { scale }
54+ />
55+ </ div >
56+ </ div >
57+ ) ;
58+ } ;
59+
1760export function PDFViewer ( { pdfData, zoomLevel } : PDFViewerProps ) {
1861 const [ numPages , setNumPages ] = useState < number > ( ) ;
1962 const [ containerWidth , setContainerWidth ] = useState < number > ( 0 ) ;
@@ -23,6 +66,8 @@ export function PDFViewer({ pdfData, zoomLevel }: PDFViewerProps) {
2366 const [ loadingError , setLoadingError ] = useState < string > ( ) ;
2467 const containerRef = useRef < HTMLDivElement > ( null ) ;
2568 const { extractTextFromPDF, highlightPattern, clearHighlights, handleTextClick } = usePDF ( ) ;
69+ const [ pageHeight , setPageHeight ] = useState ( 800 ) ; // Default height
70+ const [ pageSize , setPageSize ] = useState ( { width : 595 , height : 842 } ) ; // A4 default
2671
2772 // Add static styles once during component initialization
2873 const styleElement = document . createElement ( 'style' ) ;
@@ -146,12 +191,18 @@ export function PDFViewer({ pdfData, zoomLevel }: PDFViewerProps) {
146191 } , [ pdfText , currentSentence , highlightPattern , clearHighlights ] ) ;
147192
148193 // Add scale calculation function
149- const calculateScale = ( pageWidth : number = 595 ) => { // 595 is default PDF width in points
150- const margin = 24 ; // 24px padding on each side
151- const targetWidth = containerWidth - margin ;
152- const baseScale = targetWidth / pageWidth ;
194+ const calculateScale = useCallback ( ( ) => {
195+ // const margin = 48 ; // 24px padding on each side
196+ // const targetWidth = containerWidth - margin;
197+ const baseScale = containerWidth / pageSize . width ;
153198 return baseScale * ( zoomLevel / 100 ) ;
154- } ;
199+ } , [ containerWidth , zoomLevel , pageSize . width ] ) ;
200+
201+ const calculatePageHeight = useCallback ( ( viewport : { width : number ; height : number } ) => {
202+ const scale = calculateScale ( ) ;
203+ const scaledHeight = viewport . height * scale ;
204+ return scaledHeight + 55 ; // 40px padding top and bottom
205+ } , [ calculateScale ] ) ;
155206
156207 // Add resize observer effect
157208 useEffect ( ( ) => {
@@ -168,9 +219,37 @@ export function PDFViewer({ pdfData, zoomLevel }: PDFViewerProps) {
168219 return ( ) => observer . disconnect ( ) ;
169220 } , [ ] ) ;
170221
171- function onDocumentLoadSuccess ( { numPages } : { numPages : number } ) : void {
222+ const handleLoadSuccess = useCallback ( async ( { numPages } : { numPages : number } ) => {
172223 setNumPages ( numPages ) ;
173- }
224+
225+ if ( pdfData ) {
226+ try {
227+ // Convert Blob to ArrayBuffer
228+ const arrayBuffer = await pdfData . arrayBuffer ( ) ;
229+ const pdf = await pdfjs . getDocument ( arrayBuffer ) . promise ;
230+ const page = await pdf . getPage ( 1 ) ;
231+ const viewport = page . getViewport ( { scale : 1 } ) ;
232+
233+ setPageSize ( {
234+ width : viewport . width ,
235+ height : viewport . height
236+ } ) ;
237+
238+ const newPageHeight = calculatePageHeight ( viewport ) ;
239+ setPageHeight ( newPageHeight ) ;
240+ } catch ( error ) {
241+ console . error ( 'Error measuring page:' , error ) ;
242+ }
243+ }
244+ } , [ pdfData , calculatePageHeight ] ) ;
245+
246+ // Update page height when zoom level changes
247+ useEffect ( ( ) => {
248+ if ( pageSize . width && pageSize . height ) {
249+ const newPageHeight = calculatePageHeight ( pageSize ) ;
250+ setPageHeight ( newPageHeight ) ;
251+ }
252+ } , [ zoomLevel , pageSize , calculatePageHeight ] ) ;
174253
175254 return (
176255 < div
@@ -185,30 +264,28 @@ export function PDFViewer({ pdfData, zoomLevel }: PDFViewerProps) {
185264 loading = { < PDFSkeleton /> }
186265 noData = { < PDFSkeleton /> }
187266 file = { pdfDataUrl }
188- onLoadSuccess = { onDocumentLoadSuccess }
189- className = "flex flex-col items-center m-0 "
267+ onLoadSuccess = { handleLoadSuccess }
268+ className = "flex flex-col items-center"
190269 >
191- { Array . from (
192- new Array ( numPages ) ,
193- ( el , index ) => (
194- < div key = { `page_${ index + 1 } ` } >
195- < div className = "bg-offbase my-4 px-2 py-0.5 rounded-full w-fit" >
196- < p className = "text-xs" >
197- { index + 1 } / { numPages }
198- </ p >
199- </ div >
200- < div className = "flex justify-center" >
201- < Page
202- pageNumber = { index + 1 }
203- renderAnnotationLayer = { true }
204- renderTextLayer = { true }
205- className = "shadow-lg"
206- scale = { calculateScale ( ) }
207- />
208- </ div >
209- </ div >
210- ) ,
211- ) }
270+ { numPages && containerWidth ? (
271+ < FixedSizeList
272+ height = { Math . min ( window . innerHeight - 100 , numPages * pageHeight ) }
273+ width = { containerWidth }
274+ itemCount = { numPages }
275+ itemSize = { pageHeight }
276+ itemData = { {
277+ numPages,
278+ scale : calculateScale ( ) ,
279+ containerWidth,
280+ pageWidth : pageSize . width ,
281+ pageHeight : pageHeight ,
282+ } }
283+ className = "pdf-list"
284+ overscanCount = { 2 } // Add overscanning for smoother scrolling
285+ >
286+ { PageComponent }
287+ </ FixedSizeList >
288+ ) : null }
212289 </ Document >
213290 </ div >
214291 ) ;
0 commit comments