@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
JavaScript
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 };