/* eslint-disable no-param-reassign */ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ import React, { FormEventHandler, MouseEventHandler, useEffect, useRef, useState } from 'react'; import classNames from 'classnames'; import { Box, Button, Chip, Header, Icon, IconButton, Icons, Input, Menu, PopOut, RectCords, Scroll, Spinner, Text, as, config, } from 'folds'; import FocusTrap from 'focus-trap-react'; import FileSaver from 'file-saver'; import * as css from './PdfViewer.css'; import { AsyncStatus } from '../../hooks/useAsyncCallback'; import { useZoom } from '../../hooks/useZoom'; import { createPage, usePdfDocumentLoader, usePdfJSLoader } from '../../plugins/pdfjs-dist'; import { stopPropagation } from '../../utils/keyboard'; export type PdfViewerProps = { name: string; src: string; requestClose: () => void; }; export const PdfViewer = as<'div', PdfViewerProps>( ({ className, name, src, requestClose, ...props }, ref) => { const containerRef = useRef(null); const scrollRef = useRef(null); const { zoom, zoomIn, zoomOut, setZoom } = useZoom(0.2); const [pdfJSState, loadPdfJS] = usePdfJSLoader(); const [docState, loadPdfDocument] = usePdfDocumentLoader( pdfJSState.status === AsyncStatus.Success ? pdfJSState.data : undefined, src ); const isLoading = pdfJSState.status === AsyncStatus.Loading || docState.status === AsyncStatus.Loading; const isError = pdfJSState.status === AsyncStatus.Error || docState.status === AsyncStatus.Error; const [pageNo, setPageNo] = useState(1); const [jumpAnchor, setJumpAnchor] = useState(); useEffect(() => { loadPdfJS(); }, [loadPdfJS]); useEffect(() => { if (pdfJSState.status === AsyncStatus.Success) { loadPdfDocument(); } }, [pdfJSState, loadPdfDocument]); useEffect(() => { if (docState.status === AsyncStatus.Success) { const doc = docState.data; if (pageNo < 0 || pageNo > doc.numPages) return; createPage(doc, pageNo, { scale: zoom }).then((canvas) => { const container = containerRef.current; if (!container) return; container.textContent = ''; container.append(canvas); scrollRef.current?.scrollTo({ top: 0, }); }); } }, [docState, pageNo, zoom]); const handleDownload = () => { FileSaver.saveAs(src, name); }; const handleJumpSubmit: FormEventHandler = (evt) => { evt.preventDefault(); if (docState.status !== AsyncStatus.Success) return; const jumpInput = evt.currentTarget.jumpInput as HTMLInputElement; if (!jumpInput) return; const jumpTo = parseInt(jumpInput.value, 10); setPageNo(Math.max(1, Math.min(docState.data.numPages, jumpTo))); setJumpAnchor(undefined); }; const handlePrevPage = () => { setPageNo((n) => Math.max(n - 1, 1)); }; const handleNextPage = () => { if (docState.status !== AsyncStatus.Success) return; setPageNo((n) => Math.min(n + 1, docState.data.numPages)); }; const handleOpenJump: MouseEventHandler = (evt) => { setJumpAnchor(evt.currentTarget.getBoundingClientRect()); }; return (
{name} setZoom(zoom === 1 ? 2 : 1)}> {Math.round(zoom * 100)}% 1 ? 'Success' : 'SurfaceVariant'} outlined={zoom > 1} size="300" radii="Pill" onClick={zoomIn} aria-label="Zoom In" > } > Download
{isLoading && } {isError && ( <> Failed to load PDF )} {docState.status === AsyncStatus.Success && (
)} {docState.status === AsyncStatus.Success && (
} onClick={handlePrevPage} aria-disabled={pageNo <= 1} > Previous setJumpAnchor(undefined), clickOutsideDeactivates: true, escapeDeactivates: stopPropagation, }} > } > {`${pageNo}/${docState.data.numPages}`} } onClick={handleNextPage} aria-disabled={pageNo >= docState.data.numPages} > Next
)} ); } );