@llamaindex/ui
Version:
A comprehensive UI component library built with React, TypeScript, and Tailwind CSS for LlamaIndex applications
321 lines (318 loc) • 12.8 kB
JavaScript
import { Tooltip, TooltipTrigger, TooltipContent } from './chunk-LFNFTZZW.mjs';
import { Input } from './chunk-QMXCUFNI.mjs';
import { Button } from './chunk-JLPGK5XZ.mjs';
import { cn } from './chunk-MG2ARK3A.mjs';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { File, Trash2, ChevronLeft, ChevronRight, Minus, Plus, RotateCcw, Download, Maximize } from 'lucide-react';
import { useState, useEffect } from 'react';
function BoundingBoxOverlay({
boundingBoxes,
zoom,
containerWidth,
containerHeight,
onBoundingBoxClick
}) {
if (containerWidth === 0 || containerHeight === 0) {
return null;
}
return /* @__PURE__ */ jsx(
"svg",
{
style: {
position: "absolute",
top: 0,
left: 0,
width: containerWidth * zoom,
height: containerHeight * zoom,
pointerEvents: "none"
},
viewBox: `0 0 ${containerWidth} ${containerHeight}`,
children: boundingBoxes.map((box) => /* @__PURE__ */ jsxs("g", { children: [
/* @__PURE__ */ jsx(
"rect",
{
x: box.x,
y: box.y,
width: box.width,
height: box.height,
fill: box.color || "rgba(255, 0, 0, 0.2)",
stroke: box.color || "red",
strokeWidth: 2 / zoom,
style: {
pointerEvents: "auto",
cursor: onBoundingBoxClick ? "pointer" : "default"
},
onClick: () => onBoundingBoxClick == null ? void 0 : onBoundingBoxClick(box)
}
),
box.label && /* @__PURE__ */ jsx(
"text",
{
x: box.x,
y: box.y - 5,
fill: box.color || "red",
fontSize: 12 / zoom,
fontWeight: "bold",
children: box.label
}
)
] }, box.id))
}
);
}
var FileToolbar = ({
fileName,
onFullscreen,
scale,
onScaleChange,
onReset,
onRemove,
onDownload,
currentPage,
totalPages,
onPageChange,
className,
isOverlay = false
}) => {
var _a;
const [pageInput, setPageInput] = useState(
(_a = currentPage == null ? void 0 : currentPage.toString()) != null ? _a : "1"
);
const [isEditing, setIsEditing] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const showZoomControls = typeof scale === "number" && typeof onScaleChange === "function";
const showPageNavigation = typeof currentPage === "number" && typeof totalPages === "number" && typeof onPageChange === "function";
useEffect(() => {
if (!isEditing && typeof currentPage === "number") {
setPageInput(currentPage.toString());
}
}, [currentPage, isEditing]);
const handlePageInputChange = (value) => {
setPageInput(value);
setIsEditing(true);
};
const handlePageInputSubmit = () => {
if (!showPageNavigation || !totalPages || !currentPage || !onPageChange) {
return;
}
const pageNumber = parseInt(pageInput);
if (pageNumber >= 1 && pageNumber <= totalPages) {
onPageChange(pageNumber);
} else {
setPageInput(currentPage.toString());
}
setIsEditing(false);
};
const handlePageInputKeyDown = (e) => {
if (e.key === "Enter") {
handlePageInputSubmit();
}
};
const handlePageInputFocus = () => {
setIsEditing(true);
};
const handlePrevPage = () => {
if (showPageNavigation && currentPage !== void 0 && totalPages !== void 0 && onPageChange && currentPage > 1) {
onPageChange(currentPage - 1);
}
};
const handleNextPage = () => {
if (showPageNavigation && currentPage !== void 0 && totalPages !== void 0 && onPageChange && currentPage < totalPages) {
onPageChange(currentPage + 1);
}
};
const handleZoomIn = () => {
if (showZoomControls && scale !== void 0 && onScaleChange) {
onScaleChange(Math.min(scale + 0.25, 3));
}
};
const handleZoomOut = () => {
if (showZoomControls && scale !== void 0 && onScaleChange) {
onScaleChange(Math.max(scale - 0.25, 0.5));
}
};
const handleReset = () => {
if (showZoomControls && onScaleChange) {
onScaleChange(1);
}
if (onReset) {
onReset();
}
};
const hasControls = showPageNavigation || showZoomControls || onDownload || onFullscreen || onReset;
return /* @__PURE__ */ jsxs(
"div",
{
className: cn(
"flex h-10 items-center justify-between gap-3 px-6 transition",
isOverlay ? [
"absolute left-0 right-0 top-0 z-10 border-b bg-white/70",
isHovered ? "opacity-100" : "opacity-20",
"transition-opacity duration-300 ease-in-out"
] : "border-b bg-white",
className
),
onMouseEnter: () => isOverlay && setIsHovered(true),
onMouseLeave: () => isOverlay && setIsHovered(false),
children: [
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
fileName && /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(File, { className: "size-4" }),
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: fileName })
] }),
onRemove && /* @__PURE__ */ jsxs(Tooltip, { children: [
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
Button,
{
variant: "ghost",
size: "sm",
onClick: onRemove,
className: "size-6 p-0",
children: /* @__PURE__ */ jsx(Trash2, { className: "size-4" })
}
) }),
/* @__PURE__ */ jsx(TooltipContent, { children: "Remove file" })
] })
] }),
hasControls && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
showPageNavigation && /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
/* @__PURE__ */ jsxs(Tooltip, { children: [
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
Button,
{
variant: "ghost",
size: "sm",
onClick: handlePrevPage,
disabled: currentPage === void 0 || currentPage <= 1,
className: "size-6 p-0",
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
}
) }),
/* @__PURE__ */ jsx(TooltipContent, { children: "Previous page" })
] }),
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-0.5", children: [
/* @__PURE__ */ jsx(
Input,
{
type: "number",
value: pageInput,
onChange: (e) => handlePageInputChange(e.target.value),
onFocus: handlePageInputFocus,
onBlur: handlePageInputSubmit,
onKeyDown: handlePageInputKeyDown,
className: "size-6 px-1 text-center text-xs! rounded-sm [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-inner-spin-button]:m-0 [&::-webkit-outer-spin-button]:m-0 [-moz-appearance:textfield] shadow-none border border-transparent hover:border-gray-300 focus:border-gray-500 focus:outline-none",
min: 1,
max: totalPages
}
),
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "/" }),
/* @__PURE__ */ jsx("span", { className: "flex items-center text-xs text-muted-foreground h-7 ml-1", children: totalPages })
] }),
/* @__PURE__ */ jsxs(Tooltip, { children: [
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
Button,
{
variant: "ghost",
size: "sm",
onClick: handleNextPage,
disabled: currentPage === void 0 || totalPages === void 0 || currentPage >= totalPages,
className: "size-6 p-0",
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
}
) }),
/* @__PURE__ */ jsx(TooltipContent, { children: "Next page" })
] })
] }),
(showZoomControls || onDownload || onFullscreen || onReset) && /* @__PURE__ */ jsx("div", { className: "h-6 w-px bg-border" })
] }),
showZoomControls && /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
/* @__PURE__ */ jsxs(Tooltip, { children: [
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
Button,
{
variant: "ghost",
size: "sm",
onClick: handleZoomOut,
disabled: scale !== void 0 && scale <= 0.5,
className: "size-6 p-0",
"aria-label": "Zoom Out",
children: /* @__PURE__ */ jsx(Minus, { className: "size-4" })
}
) }),
/* @__PURE__ */ jsx(TooltipContent, { children: "Zoom out" })
] }),
/* @__PURE__ */ jsxs("span", { className: "text-center text-xs text-muted-foreground", children: [
scale !== void 0 ? Math.round(scale * 100) : 0,
"%"
] }),
/* @__PURE__ */ jsxs(Tooltip, { children: [
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
Button,
{
variant: "ghost",
size: "sm",
onClick: handleZoomIn,
disabled: scale !== void 0 && scale >= 3,
className: "size-6 p-0",
"aria-label": "Zoom In",
children: /* @__PURE__ */ jsx(Plus, { className: "size-4" })
}
) }),
/* @__PURE__ */ jsx(TooltipContent, { children: "Zoom in" })
] }),
/* @__PURE__ */ jsxs(Tooltip, { children: [
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
Button,
{
variant: "ghost",
size: "sm",
onClick: handleReset,
className: "size-6 p-0",
"aria-label": "Reset Zoom",
children: /* @__PURE__ */ jsx(RotateCcw, { className: "size-4" })
}
) }),
/* @__PURE__ */ jsx(TooltipContent, { children: "Reset zoom" })
] })
] }),
(onDownload || onFullscreen) && /* @__PURE__ */ jsx("div", { className: "h-6 w-px bg-border" })
] }),
onDownload && /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsxs(Tooltip, { children: [
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
Button,
{
variant: "ghost",
size: "sm",
onClick: onDownload,
className: "size-6 p-0",
"aria-label": "Download PDF",
children: /* @__PURE__ */ jsx(Download, { className: "size-4" })
}
) }),
/* @__PURE__ */ jsx(TooltipContent, { children: "Download" })
] }),
onFullscreen && /* @__PURE__ */ jsx("div", { className: "h-6 w-px bg-border" })
] }),
onFullscreen && /* @__PURE__ */ jsxs(Tooltip, { children: [
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
Button,
{
variant: "ghost",
size: "sm",
onClick: onFullscreen,
className: "size-6 p-0",
"aria-label": "Fullscreen",
children: /* @__PURE__ */ jsx(Maximize, { className: "size-4" })
}
) }),
/* @__PURE__ */ jsx(TooltipContent, { children: "Fullscreen" })
] })
] })
]
}
);
};
export { BoundingBoxOverlay, FileToolbar };