Files
sign/apps/web/src/components/(dashboard)/pdf-viewer/pdf-viewer.tsx

150 lines
3.6 KiB
TypeScript
Raw Normal View History

2023-06-09 18:21:18 +10:00
'use client';
2023-06-10 22:33:12 +10:00
import React, { useEffect, useRef, useState } from 'react';
2023-06-09 18:21:18 +10:00
import { Loader } from 'lucide-react';
import { Document as PDFDocument, Page as PDFPage, pdfjs } from 'react-pdf';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css';
import { cn } from '@documenso/ui/lib/utils';
type LoadedPDFDocument = pdfjs.PDFDocumentProxy;
2023-06-10 22:33:12 +10:00
/**
* This imports the worker from the `pdfjs-dist` package.
*/
2023-06-09 18:21:18 +10:00
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.js',
import.meta.url,
).toString();
2023-06-10 22:33:12 +10:00
export type OnPDFViewerPageClick = (_event: {
pageNumber: number;
numPages: number;
originalEvent: React.MouseEvent<HTMLDivElement, MouseEvent>;
pageHeight: number;
pageWidth: number;
pageX: number;
pageY: number;
}) => void | Promise<void>;
2023-06-09 18:21:18 +10:00
export type PDFViewerProps = {
className?: string;
document: string;
2023-06-10 22:33:12 +10:00
onPageClick?: OnPDFViewerPageClick;
2023-06-09 18:21:18 +10:00
[key: string]: unknown;
};
2023-06-10 22:33:12 +10:00
export const PDFViewer = ({ className, document, onPageClick, ...props }: PDFViewerProps) => {
2023-06-09 18:21:18 +10:00
const $el = useRef<HTMLDivElement>(null);
const [width, setWidth] = useState(0);
const [numPages, setNumPages] = useState(0);
const onDocumentLoaded = (doc: LoadedPDFDocument) => {
setNumPages(doc.numPages);
};
2023-06-10 22:33:12 +10:00
const onDocumentPageClick = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
pageNumber: number,
) => {
const $el = event.target instanceof HTMLElement ? event.target : null;
if (!$el) {
return;
}
const $page = $el.closest('.react-pdf__Page');
if (!$page) {
return;
}
const { height, width, top, left } = $page.getBoundingClientRect();
const pageX = event.clientX - left;
const pageY = event.clientY - top;
console.log({
pageNumber,
numPages,
originalEvent: event,
pageHeight: height,
pageWidth: width,
pageX,
pageY,
});
if (onPageClick) {
onPageClick({
pageNumber,
numPages,
originalEvent: event,
pageHeight: height,
pageWidth: width,
pageX,
pageY,
});
}
};
2023-06-09 18:21:18 +10:00
useEffect(() => {
if ($el.current) {
const $current = $el.current;
const { width } = $current.getBoundingClientRect();
setWidth(width);
const onResize = () => {
const { width } = $current.getBoundingClientRect();
setWidth(width);
};
window.addEventListener('resize', onResize);
return () => {
window.removeEventListener('resize', onResize);
};
}
}, []);
return (
2023-06-10 22:33:12 +10:00
<div ref={$el} className={cn('overflow-hidden', className)} {...props}>
2023-06-09 18:21:18 +10:00
<PDFDocument
2023-06-10 22:33:12 +10:00
file={document}
2023-06-09 18:21:18 +10:00
className="w-full overflow-hidden rounded"
onLoadSuccess={(d) => onDocumentLoaded(d)}
externalLinkTarget="_blank"
loading={
<div className="dark:bg-background flex min-h-[80vh] flex-col items-center justify-center bg-white/50">
<Loader className="text-documenso h-12 w-12 animate-spin" />
2023-06-09 18:21:18 +10:00
<p className="text-muted-foreground mt-4">Loading document...</p>
2023-06-09 18:21:18 +10:00
</div>
}
>
{Array(numPages)
.fill(null)
.map((_, i) => (
<div
key={i}
2023-06-11 01:50:19 -04:00
className="border-border my-8 overflow-hidden rounded border first:mt-0 last:mb-0"
2023-06-09 18:21:18 +10:00
>
2023-06-10 22:33:12 +10:00
<PDFPage
pageNumber={i + 1}
width={width}
onClick={(e) => onDocumentPageClick(e, i + 1)}
/>
2023-06-09 18:21:18 +10:00
</div>
))}
</PDFDocument>
</div>
);
};
export default PDFViewer;