feat(05-02): extend PdfViewer with pageInfo state and FieldPlacer integration
- Add pageInfo state (PageInfo | null) to track rendered PDF page dimensions - Wire onLoadSuccess callback on Page to capture originalWidth/Height using Math.max mediaBox pattern - Import and wrap Document/Page tree inside FieldPlacer component - Pass docId, pageInfo, currentPage to FieldPlacer for coordinate conversion - Only scale prop used on Page (not both width+scale — avoids double scaling) - All existing controls (Prev, Next, Zoom In/Out, Download) preserved unchanged
This commit is contained in:
@@ -3,6 +3,7 @@ import { useState } from 'react';
|
|||||||
import { Document, Page, pdfjs } from 'react-pdf';
|
import { Document, Page, pdfjs } from 'react-pdf';
|
||||||
import 'react-pdf/dist/Page/AnnotationLayer.css';
|
import 'react-pdf/dist/Page/AnnotationLayer.css';
|
||||||
import 'react-pdf/dist/Page/TextLayer.css';
|
import 'react-pdf/dist/Page/TextLayer.css';
|
||||||
|
import { FieldPlacer } from './FieldPlacer';
|
||||||
|
|
||||||
// Worker setup — must use import.meta.url for local/Docker environments (no CDN)
|
// Worker setup — must use import.meta.url for local/Docker environments (no CDN)
|
||||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||||
@@ -10,10 +11,19 @@ pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
|||||||
import.meta.url,
|
import.meta.url,
|
||||||
).toString();
|
).toString();
|
||||||
|
|
||||||
|
interface PageInfo {
|
||||||
|
originalWidth: number;
|
||||||
|
originalHeight: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
scale: number;
|
||||||
|
}
|
||||||
|
|
||||||
export function PdfViewer({ docId }: { docId: string }) {
|
export function PdfViewer({ docId }: { docId: string }) {
|
||||||
const [numPages, setNumPages] = useState(0);
|
const [numPages, setNumPages] = useState(0);
|
||||||
const [pageNumber, setPageNumber] = useState(1);
|
const [pageNumber, setPageNumber] = useState(1);
|
||||||
const [scale, setScale] = useState(1.0);
|
const [scale, setScale] = useState(1.0);
|
||||||
|
const [pageInfo, setPageInfo] = useState<PageInfo | null>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center gap-4">
|
<div className="flex flex-col items-center gap-4">
|
||||||
@@ -54,13 +64,29 @@ export function PdfViewer({ docId }: { docId: string }) {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* PDF canvas wrapped in FieldPlacer for drag-and-drop field placement */}
|
||||||
|
<FieldPlacer docId={docId} pageInfo={pageInfo} currentPage={pageNumber}>
|
||||||
<Document
|
<Document
|
||||||
file={`/api/documents/${docId}/file`}
|
file={`/api/documents/${docId}/file`}
|
||||||
onLoadSuccess={({ numPages }) => setNumPages(numPages)}
|
onLoadSuccess={({ numPages }) => setNumPages(numPages)}
|
||||||
className="shadow-lg"
|
className="shadow-lg"
|
||||||
>
|
>
|
||||||
<Page pageNumber={pageNumber} scale={scale} />
|
<Page
|
||||||
|
pageNumber={pageNumber}
|
||||||
|
scale={scale}
|
||||||
|
onLoadSuccess={(page) => {
|
||||||
|
setPageInfo({
|
||||||
|
// Math.max handles non-standard mediaBox ordering (view is [x1, y1, x2, y2])
|
||||||
|
originalWidth: Math.max(page.view[0], page.view[2]),
|
||||||
|
originalHeight: Math.max(page.view[1], page.view[3]),
|
||||||
|
width: page.width,
|
||||||
|
height: page.height,
|
||||||
|
scale: page.scale,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Document>
|
</Document>
|
||||||
|
</FieldPlacer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user