{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "minimal-viewer", "dependencies": [ "react-pdf", "@wojtekmaj/react-hooks", "lucide-react" ], "registryDependencies": [ "button" ], "files": [ { "path": "registry/reusables/pdf-viewers/minimal-viewer.tsx", "content": "\"use client\"\n\nimport React, { useCallback, useState } from \"react\"\nimport { useResizeObserver } from \"@wojtekmaj/react-hooks\"\nimport { ChevronLeft, ChevronRight } from \"lucide-react\"\nimport { Document, Page, pdfjs } from \"react-pdf\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\n\nimport \"react-pdf/dist/esm/Page/AnnotationLayer.css\"\nimport \"react-pdf/dist/esm/Page/TextLayer.css\"\nimport \"react-pdf/dist/Page/TextLayer.css\"\n\nimport { LoadingSpinner } from \"./loading-spinner\"\n\n// Important: Set worker source\npdfjs.GlobalWorkerOptions.workerSrc = new URL(\n \"pdfjs-dist/build/pdf.worker.min.mjs\",\n import.meta.url\n).toString()\n\nconst resizeObserverOptions = {}\nconst maxWidth = 800\n\ninterface MinimalViewerProps {\n url: string\n}\n\nexport const MinimalViewer = ({ url }: MinimalViewerProps) => {\n const [numPages, setNumPages] = useState(null)\n const [pageNumber, setPageNumber] = useState(1)\n const [containerRef, setContainerRef] = useState(null)\n const [containerWidth, setContainerWidth] = useState()\n const [pageHeight, setPageHeight] = useState(0)\n\n const onResize = useCallback((entries) => {\n const [entry] = entries\n if (entry) {\n setContainerWidth(entry.contentRect.width)\n }\n }, [])\n\n useResizeObserver(containerRef, resizeObserverOptions, onResize)\n\n const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {\n setNumPages(numPages)\n }\n\n const changePage = (offset: number) => {\n setPageNumber((prevPageNumber) => {\n const newPageNumber = prevPageNumber + offset\n return numPages ?\n Math.min(Math.max(1, newPageNumber), numPages)\n : prevPageNumber\n })\n }\n\n const previousPage = () => changePage(-1)\n const nextPage = () => changePage(1)\n\n // Keyboard navigation\n React.useEffect(() => {\n const handleKeyPress = (event: KeyboardEvent) => {\n if (event.key === \"ArrowLeft\") {\n previousPage()\n } else if (event.key === \"ArrowRight\") {\n nextPage()\n }\n }\n\n window.addEventListener(\"keydown\", handleKeyPress)\n return () => window.removeEventListener(\"keydown\", handleKeyPress)\n }, [])\n\n const pageWidth =\n containerWidth ? Math.min(containerWidth - 48, maxWidth) : maxWidth\n\n return (\n
\n
\n
\n \n }\n className={cn(\n \"flex w-full flex-col items-center gap-2 md:gap-4\",\n \"[&_.react-pdf__message]:p-5\",\n \"[&_.react-pdf__message]:text-white\",\n \"[&_.react-pdf__Page]:my-0\"\n )}\n >\n \n }\n onLoadSuccess={(page) => {\n setPageHeight(page.height * (pageWidth / page.width))\n }}\n />\n \n
\n\n \n \n \n \n\n
\n {pageNumber} / {numPages || \"-\"}\n
\n\n = numPages}\n className=\"rounded-full\"\n >\n \n \n
\n
\n \n )\n}\n\nexport default MinimalViewer\n", "type": "registry:component", "target": "components/pdf-viewers/minimal-viewer.tsx" }, { "path": "registry/reusables/pdf-viewers/loading-spinner.tsx", "content": "import { Loader2 } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface LoadingSpinnerProps {\n minHeight?: string\n minWidth?: string\n className?: string\n spinnerClassName?: string\n}\n\nexport const LoadingSpinner = ({\n minHeight = \"600px\",\n minWidth = \"800px\",\n className,\n spinnerClassName,\n}: LoadingSpinnerProps) => {\n return (\n \n \n \n )\n}\n", "type": "registry:component", "target": "components/pdf-viewers/loading-spinner.tsx" } ], "type": "registry:component" }