UNPKG

@llamaindex/ui

Version:

A comprehensive UI component library built with React, TypeScript, and Tailwind CSS for LlamaIndex applications

290 lines (287 loc) 10.2 kB
import { BoundingBoxOverlay, PdfNavigator } from './chunk-IHCRL4VW.mjs'; import './chunk-EWMUBFVD.mjs'; import './chunk-NLLOGSY3.mjs'; import './chunk-MG2ARK3A.mjs'; import { __spreadProps, __spreadValues } from './chunk-FWCSY2DS.mjs'; import { useState, useRef, useEffect } from 'react'; import { pdfjs, Document, Page } from 'react-pdf'; import { jsx, jsxs } from 'react/jsx-runtime'; if (typeof window !== "undefined") { pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`; } import('react-pdf/dist/Page/AnnotationLayer.css'); import('react-pdf/dist/Page/TextLayer.css'); var pdfOptions = { cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`, wasmUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/wasm/` }; var FILE_SIZE_THRESHOLD = 10 * 1024 * 1024; var PdfPreviewImpl = ({ url, onDownload, highlight }) => { const [numPages, setNumPages] = useState(); const [currentPage, setCurrentPage] = useState(1); const [scale, setScale] = useState(1); const [file, setFile] = useState(null); const [isLoading, setIsLoading] = useState(false); const [renderedPages, setRenderedPages] = useState(0); const [isRendering, setIsRendering] = useState(true); const containerRef = useRef(null); const pageRefs = useRef({}); const isInitialScaleSet = useRef(false); const [pageBaseDims, setPageBaseDims] = useState({}); const [showHighlight, setShowHighlight] = useState(false); const boundingBoxes = [ { id: "highlight", x: (highlight == null ? void 0 : highlight.x) || 0, y: (highlight == null ? void 0 : highlight.y) || 0, width: (highlight == null ? void 0 : highlight.width) || 0, height: (highlight == null ? void 0 : highlight.height) || 0, color: "rgba(255, 215, 0, 0.25)" } ]; function onDocumentLoadSuccess({ numPages: numPages2 }) { setNumPages(numPages2); setRenderedPages(0); setIsRendering(true); } function handlePageRenderSuccess() { setRenderedPages((prev) => { const next = prev + 1; if (next === numPages) { setIsRendering(false); } return next; }); } useEffect(() => { if (!highlight) return; if (!numPages) return; const pageEl = pageRefs.current[highlight.page]; if (pageEl) { goToPage(highlight.page); setShowHighlight(true); } }, [highlight, numPages]); const handleLoadPage = (page) => { const viewport = page.getViewport({ scale: 1 }); setPageBaseDims((prev) => __spreadProps(__spreadValues({}, prev), { [page.pageNumber]: { width: viewport.width, height: viewport.height } })); if (!isInitialScaleSet.current && page.pageNumber === 1 && containerRef.current) { const containerWidth = containerRef.current.clientWidth; const newScale = containerWidth / viewport.width; setScale(newScale); isInitialScaleSet.current = true; } }; const handleClickOnPage = () => { if (showHighlight) { setShowHighlight(false); } }; useEffect(() => { const handleScroll = () => { if (!containerRef.current) return; const containerRect = containerRef.current.getBoundingClientRect(); const containerCenter = containerRect.top + containerRect.height / 2; let closestPage = 1; let closestDistance = Infinity; Object.entries(pageRefs.current).forEach(([pageNumber, element]) => { if (element) { const rect = element.getBoundingClientRect(); const pageCenter = rect.top + rect.height / 2; const distance = Math.abs(pageCenter - containerCenter); if (distance < closestDistance) { closestDistance = distance; closestPage = parseInt(pageNumber); } } }); setCurrentPage(closestPage); }; const container = containerRef.current; if (container) { container.addEventListener("scroll", handleScroll); return () => container.removeEventListener("scroll", handleScroll); } }, [numPages]); const lastLoadedUrl = useRef(null); useEffect(() => { if (lastLoadedUrl.current === url) { return; } lastLoadedUrl.current = url; const fetchFile = async () => { setIsLoading(true); const response = await fetch(url); const blob = await response.blob(); setFile(new File([blob], "document.pdf", { type: "application/pdf" })); setIsLoading(false); }; fetchFile(); return () => { setFile(null); }; }, [url]); const goToPage = (pageNumber) => { const pageElement = pageRefs.current[pageNumber]; if (pageElement && containerRef.current) { pageElement.scrollIntoView({ behavior: "instant", block: "center" }); } }; useEffect(() => { const container = containerRef.current; if (!container) return; const handleKeyDown = (event) => { if (event.key === "ArrowLeft") { event.preventDefault(); if (currentPage > 1) { goToPage(currentPage - 1); } } else if (event.key === "ArrowRight") { event.preventDefault(); if (currentPage < (numPages || 1)) { goToPage(currentPage + 1); } } else if (event.key === "=" || event.key === "+") { event.preventDefault(); setScale((prev) => Math.min(prev + 0.25, 3)); } else if (event.key === "-") { event.preventDefault(); setScale((prev) => Math.max(prev - 0.25, 0.5)); } }; container.addEventListener("keydown", handleKeyDown); container.tabIndex = 0; return () => { container.removeEventListener("keydown", handleKeyDown); }; }, [currentPage, numPages]); const handleDownload = () => { if (onDownload) { onDownload(); } else { if (file) { const blobUrl = URL.createObjectURL(file); const link = document.createElement("a"); link.href = blobUrl; link.download = file.name; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(blobUrl); } else { window.open(url, "_blank"); } } }; const handleReset = () => { setCurrentPage(1); goToPage(1); }; if (isLoading) { return /* @__PURE__ */ jsx("div", { className: "relative h-full flex items-center justify-center bg-gray-50", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [ /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto mb-2" }), /* @__PURE__ */ jsx("p", { className: "text-gray-600", children: "Loading PDF..." }) ] }) }); } return /* @__PURE__ */ jsxs("div", { className: "relative h-full", children: [ isRendering && file && file.size > FILE_SIZE_THRESHOLD && /* @__PURE__ */ jsx( PdfRenderingProgress, { renderedPages, numPages } ), /* @__PURE__ */ jsx("div", { ref: containerRef, className: "overflow-auto h-full", children: /* @__PURE__ */ jsx( Document, { file, onLoadSuccess: onDocumentLoadSuccess, loading: isLoading, options: pdfOptions, children: Array.from(new Array(numPages), (_, index) => /* @__PURE__ */ jsx( "div", { ref: (el) => { pageRefs.current[index + 1] = el; }, className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsxs("div", { className: "relative inline-block", children: [ /* @__PURE__ */ jsx( Page, { pageNumber: index + 1, scale, renderTextLayer: true, renderAnnotationLayer: true, onLoadSuccess: handleLoadPage, onClick: handleClickOnPage, onRenderSuccess: handlePageRenderSuccess } ), highlight && showHighlight && highlight.page === index + 1 && pageBaseDims[index + 1] && /* @__PURE__ */ jsx( BoundingBoxOverlay, { boundingBoxes, zoom: scale, containerWidth: pageBaseDims[index + 1].width, containerHeight: pageBaseDims[index + 1].height } ) ] }) }, `page_${index + 1}` )) } ) }), numPages && /* @__PURE__ */ jsx( PdfNavigator, { currentPage, totalPages: numPages, scale, onPageChange: goToPage, onScaleChange: setScale, onDownload: handleDownload, onReset: handleReset } ) ] }); }; function PdfRenderingProgress({ renderedPages, numPages }) { return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-50 bg-opacity-90 z-50", children: /* @__PURE__ */ jsxs("div", { className: "bg-white shadow-lg rounded-2xl p-6 w-80 text-center", children: [ /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-10 w-10 border-4 border-primary border-t-transparent" }) }), /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-800 mb-2", children: "Rendering PDF\u2026" }), /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-600 mb-3", children: [ "Page ", renderedPages, " of ", numPages ] }), /* @__PURE__ */ jsx("div", { className: "w-full bg-gray-200 rounded-full h-2.5 mb-4", children: /* @__PURE__ */ jsx( "div", { className: "bg-primary h-2.5 rounded-full transition-all duration-300", style: { width: numPages && numPages > 0 ? `${Math.round(renderedPages / numPages * 100)}%` : "0%" } } ) }), /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500", children: "Large PDFs or those with heavy images may take longer to render." }) ] }) }); } export { PdfPreviewImpl };