@turinhub/atomix-common-ui 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/dist/AuthPanel-C_2JBE7t.cjs +2 -0
- package/dist/AuthPanel-C_2JBE7t.cjs.map +1 -0
- package/dist/AuthPanel-D2HFX8eN.js +656 -0
- package/dist/AuthPanel-D2HFX8eN.js.map +1 -0
- package/dist/auth.cjs +2 -0
- package/dist/auth.cjs.map +1 -0
- package/dist/auth.d.ts +11 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +9 -0
- package/dist/auth.js.map +1 -0
- package/dist/components/AuthLoginPanel.d.ts +55 -0
- package/dist/components/AuthLoginPanel.d.ts.map +1 -0
- package/dist/components/AuthPageShell.d.ts +11 -0
- package/dist/components/AuthPageShell.d.ts.map +1 -0
- package/dist/components/AuthPanel.d.ts +20 -0
- package/dist/components/AuthPanel.d.ts.map +1 -0
- package/dist/components/AuthRegisterPanel.d.ts +34 -0
- package/dist/components/AuthRegisterPanel.d.ts.map +1 -0
- package/dist/components/AuthVisualCarousel.d.ts +20 -0
- package/dist/components/AuthVisualCarousel.d.ts.map +1 -0
- package/dist/components/DataTable.d.ts +2 -2
- package/dist/components/DataTable.d.ts.map +1 -1
- package/dist/components/ImageReader.d.ts +44 -0
- package/dist/components/ImageReader.d.ts.map +1 -0
- package/dist/components/VideoReader.d.ts +39 -0
- package/dist/components/VideoReader.d.ts.map +1 -0
- package/dist/components/media-utils.d.ts +9 -0
- package/dist/components/media-utils.d.ts.map +1 -0
- package/dist/data-table.cjs +1 -1
- package/dist/data-table.cjs.map +1 -1
- package/dist/data-table.js +83 -73
- package/dist/data-table.js.map +1 -1
- package/dist/image-reader.cjs +2 -0
- package/dist/image-reader.cjs.map +1 -0
- package/dist/image-reader.d.ts +3 -0
- package/dist/image-reader.d.ts.map +1 -0
- package/dist/image-reader.js +214 -0
- package/dist/image-reader.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/media-utils-5UPuocc1.js +23 -0
- package/dist/media-utils-5UPuocc1.js.map +1 -0
- package/dist/media-utils-X1dDYP9W.cjs +2 -0
- package/dist/media-utils-X1dDYP9W.cjs.map +1 -0
- package/dist/pdf-reader.cjs +1 -1
- package/dist/pdf-reader.cjs.map +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/pdf-reader.js.map +1 -1
- package/dist/simple-pdf-reader.cjs +1 -1
- package/dist/simple-pdf-reader.cjs.map +1 -1
- package/dist/simple-pdf-reader.js +1 -1
- package/dist/simple-pdf-reader.js.map +1 -1
- package/dist/types/component-types.d.ts +1 -0
- package/dist/types/component-types.d.ts.map +1 -1
- package/dist/video-reader.cjs +2 -0
- package/dist/video-reader.cjs.map +1 -0
- package/dist/video-reader.d.ts +3 -0
- package/dist/video-reader.d.ts.map +1 -0
- package/dist/video-reader.js +158 -0
- package/dist/video-reader.js.map +1 -0
- package/package.json +31 -1
- package/dist/index-BiA_tnaq.cjs +0 -13
- package/dist/index-BiA_tnaq.cjs.map +0 -1
- package/dist/index-BypbGNpR.js +0 -18821
- package/dist/index-BypbGNpR.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simple-pdf-reader.cjs","sources":["../src/components/SimplePDFReader.tsx"],"sourcesContent":["import {\n ChevronLeft as ChevronLeftIcon,\n ChevronRight as ChevronRightIcon,\n Maximize2 as Maximize2Icon,\n Minimize2 as Minimize2Icon,\n ZoomIn as ZoomInIcon,\n ZoomOut as ZoomOutIcon,\n} from 'lucide-react';\nimport { useState, useMemo, useCallback, useEffect, useRef } from 'react';\nimport type { HTMLAttributes } from 'react';\n\nimport type {\n CardComponent,\n ButtonComponent,\n InputComponent,\n SkeletonComponent,\n} from '../types/component-types';\n\n/**\n * react-pdf 类型定义\n * 这些类型由 @types/react-pdf 提供,这里我们定义必要的接口\n */\nexport interface PDFDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<any>;\n}\n\n/**\n * SimplePDFReader UI 组件接口\n */\nexport interface SimplePDFReaderUIComponents {\n Card: CardComponent;\n CardContent: React.ComponentType<HTMLAttributes<HTMLDivElement>>;\n Button: ButtonComponent;\n Input?: InputComponent;\n Skeleton: SkeletonComponent;\n}\n\n/**\n * SimplePDFReader 组件 Props\n */\nexport interface SimplePDFReaderProps {\n // PDF 数据源\n url: string;\n\n // 初始状态\n initialPage?: number;\n initialScale?: number;\n\n // 缩放控制\n scale?: number;\n onScaleChange?: (scale: number) => void;\n minScale?: number;\n maxScale?: number;\n\n // 页面导航\n currentPage?: number;\n onPageChange?: (page: number) => void;\n\n // 功能开关\n showToolbar?: boolean; // 默认 true\n showPagination?: boolean; // 默认 true\n enableHotkeys?: boolean; // 默认 true\n\n // 样式定制\n className?: string;\n containerClassName?: string;\n pageClassName?: string;\n\n // UI 组件注入\n components: SimplePDFReaderUIComponents;\n\n // 回调函数\n onLoadSuccess?: (pdf: PDFDocumentProxy) => void;\n onLoadError?: (error: Error) => void;\n\n // 加载状态文本\n loadingText?: string; // 默认 \"加载中...\"\n errorText?: string; // 默认 \"加载失败\"\n}\n\n/**\n * SimplePDFReader 组件\n *\n * 用于在 React 应用中展示和浏览 PDF 文档。\n * 支持从 URL 加载 PDF,提供基础的浏览功能(翻页、缩放),并包含性能优化。\n *\n * @example\n * ```tsx\n * import { SimplePDFReader } from '@turinhub/atomix-common-ui/simple-pdf-reader';\n * import { Card, Button, Skeleton } from '@/components/ui';\n *\n * <SimplePDFReader\n * url=\"/documents/sample.pdf\"\n * components={{\n * Card,\n * CardContent: Card.Content,\n * Button,\n * Skeleton,\n * }}\n * initialPage={1}\n * initialScale={1.0}\n * showToolbar={true}\n * showPagination={true}\n * enableHotkeys={true}\n * onPageChange={(page) => console.log('Current page:', page)}\n * />\n *\n * Keyboard Shortcuts:\n * - Arrow Left/Right: Navigate pages\n * - Ctrl/Cmd + +/-: Zoom in/out\n * - F: Toggle fullscreen\n * ```\n */\nexport function SimplePDFReader({\n url,\n initialPage = 1,\n initialScale = 1.0,\n scale: controlledScale,\n onScaleChange,\n minScale = 0.5,\n maxScale = 3.0,\n currentPage: controlledPage,\n onPageChange,\n showToolbar = true,\n showPagination = true,\n enableHotkeys = true,\n className,\n containerClassName,\n pageClassName,\n components,\n onLoadSuccess,\n onLoadError,\n loadingText = '加载中...',\n errorText = '加载失败',\n}: SimplePDFReaderProps) {\n // ==================== React Hooks (必须在最顶部) ====================\n\n // 状态管理\n const [pdfDocument, setPdfDocument] = useState<PDFDocumentProxy | null>(null);\n const [internalPage, setInternalPage] = useState(initialPage);\n const [internalScale, setInternalScale] = useState(initialScale);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [totalPages, setTotalPages] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const readerRef = useRef<HTMLDivElement>(null);\n const loadingTaskRef = useRef<any>(null);\n\n // 动态导入 react-pdf\n const [ReactPDF, setReactPDF] = useState<any>(null);\n\n // 使用受控或非受控模式\n const currentPage = controlledPage ?? internalPage;\n const scale = controlledScale ?? internalScale;\n\n // 预渲染相邻页面以提升翻页体验\n const pagesToPreload = useMemo(() => {\n const pages = [currentPage];\n if (currentPage > 1) pages.push(currentPage - 1);\n if (currentPage < totalPages) pages.push(currentPage + 1);\n return pages;\n }, [currentPage, totalPages]);\n\n // 页面导航处理\n const goToPage = useCallback(\n (page: number) => {\n const maxPage = Math.max(totalPages, 1);\n const targetPage = Math.min(Math.max(page, 1), maxPage);\n if (controlledPage === undefined) {\n setInternalPage(targetPage);\n }\n onPageChange?.(targetPage);\n },\n [totalPages, controlledPage, onPageChange]\n );\n\n const handlePreviousPage = useCallback(() => {\n if (currentPage > 1) {\n goToPage(currentPage - 1);\n }\n }, [currentPage, goToPage]);\n\n const handleNextPage = useCallback(() => {\n if (currentPage < totalPages) {\n goToPage(currentPage + 1);\n }\n }, [currentPage, totalPages, goToPage]);\n\n // 缩放控制处理\n const handleZoomIn = useCallback(() => {\n const newScale = Math.min(scale + 0.25, maxScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, maxScale, controlledScale, onScaleChange]);\n\n const handleZoomOut = useCallback(() => {\n const newScale = Math.max(scale - 0.25, minScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, minScale, controlledScale, onScaleChange]);\n\n const handleToggleFullscreen = useCallback(async () => {\n if (typeof document === 'undefined') return;\n\n if (!document.fullscreenElement) {\n await readerRef.current?.requestFullscreen?.();\n return;\n }\n\n await document.exitFullscreen?.();\n }, []);\n\n // 动态导入 react-pdf 和设置 worker\n useEffect(() => {\n let isMounted = true;\n\n const loadReactPDF = async () => {\n try {\n // 动态导入 react-pdf\n const pdfModule = await import('react-pdf');\n // 设置 worker\n if (typeof window !== 'undefined') {\n const pdfjs = (pdfModule as any).pdfjs;\n const workerVersion = pdfjs?.version;\n if (pdfjs?.GlobalWorkerOptions && workerVersion) {\n pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${workerVersion}/build/pdf.worker.min.mjs`;\n }\n }\n if (isMounted) {\n setReactPDF(pdfModule);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('无法加载 react-pdf 库');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadReactPDF();\n\n return () => {\n isMounted = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // 加载 PDF 文档\n useEffect(() => {\n if (!ReactPDF || !url) return;\n\n let isMounted = true;\n\n const loadPDF = async () => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { Document } = ReactPDF;\n if (!Document) {\n throw new Error('react-pdf Document 组件不可用');\n }\n\n // 使用 react-pdf 的 Document 组件内部加载逻辑\n // 我们创建一个加载器来获取 PDF 文档信息\n const loadingTask = (ReactPDF.pdfjs as any).getDocument(url);\n loadingTaskRef.current = loadingTask;\n const pdf = await loadingTask.promise;\n\n if (isMounted) {\n setPdfDocument(pdf as PDFDocumentProxy);\n setTotalPages(pdf.numPages);\n if (controlledPage === undefined) {\n setInternalPage((prev) =>\n Math.max(1, Math.min(prev, pdf.numPages))\n );\n }\n setIsLoading(false);\n onLoadSuccess?.(pdf as PDFDocumentProxy);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('PDF 加载失败');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadPDF();\n\n return () => {\n isMounted = false;\n if (\n loadingTaskRef.current &&\n typeof loadingTaskRef.current.destroy === 'function'\n ) {\n loadingTaskRef.current.destroy();\n loadingTaskRef.current = null;\n }\n };\n }, [ReactPDF, url, controlledPage, onLoadSuccess, onLoadError]);\n\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n const handleFullscreenChange = () => {\n setIsFullscreen(document.fullscreenElement === readerRef.current);\n };\n\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange);\n };\n }, []);\n\n // ==================== 键盘快捷键 ====================\n useEffect(() => {\n if (!enableHotkeys) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Don't handle hotkeys if input is focused\n const activeElement = document.activeElement;\n if (\n activeElement &&\n (activeElement.tagName === 'INPUT' ||\n activeElement.getAttribute('role') === 'input')\n ) {\n return;\n }\n\n // Ctrl/Cmd + 加号: 放大\n if ((e.ctrlKey || e.metaKey) && (e.key === '=' || e.key === '+')) {\n e.preventDefault();\n handleZoomIn();\n }\n // Ctrl/Cmd + 减号: 缩小\n else if ((e.ctrlKey || e.metaKey) && e.key === '-') {\n e.preventDefault();\n handleZoomOut();\n }\n // 左箭头: 上一页\n else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n handlePreviousPage();\n }\n // 右箭头: 下一页\n else if (e.key === 'ArrowRight') {\n e.preventDefault();\n handleNextPage();\n }\n // F 键: 全屏切换\n else if (e.key === 'f' || e.key === 'F') {\n e.preventDefault();\n void handleToggleFullscreen();\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [\n enableHotkeys,\n handleZoomIn,\n handleZoomOut,\n handlePreviousPage,\n handleNextPage,\n handleToggleFullscreen,\n ]);\n\n // ==================== 组件验证和渲染 ====================\n\n // 验证 components\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const {\n Card,\n CardContent,\n Button,\n Input: InputComponentInjected,\n Skeleton,\n } = components;\n\n // 渲染工具栏\n const renderToolbar = () => {\n if (!showToolbar) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomOut}\n disabled={scale <= minScale}\n >\n <ZoomOutIcon />\n </Button>\n <span className=\"text-sm\">{Math.round(scale * 100)}%</span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomIn}\n disabled={scale >= maxScale}\n >\n <ZoomInIcon />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => {\n void handleToggleFullscreen();\n }}\n >\n {isFullscreen ? <Minimize2Icon /> : <Maximize2Icon />}\n </Button>\n </div>\n );\n };\n\n // 渲染分页控制\n const renderPagination = () => {\n if (!showPagination) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handlePreviousPage}\n disabled={currentPage <= 1}\n >\n <ChevronLeftIcon />\n </Button>\n {InputComponentInjected ? (\n <InputComponentInjected\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 text-center\"\n />\n ) : (\n <input\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 rounded-md border border-input bg-background px-2 text-center text-sm\"\n />\n )}\n <span className=\"text-sm text-muted-foreground\">/ {totalPages}</span>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handleNextPage}\n disabled={currentPage >= totalPages}\n >\n <ChevronRightIcon />\n </Button>\n </div>\n );\n };\n\n const renderOperations = () => {\n if (!showToolbar && !showPagination) return null;\n\n return (\n <div\n data-testid=\"pdf-operations-bar\"\n className=\"flex items-center justify-between gap-4 border-b px-4 py-2\"\n >\n {renderToolbar()}\n {renderPagination()}\n </div>\n );\n };\n\n // 渲染加载状态\n const renderLoading = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <Skeleton className=\"h-8 w-32\" />\n <Skeleton className=\"h-64 w-full max-w-2xl\" />\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n </div>\n );\n\n // 渲染错误状态\n const renderError = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <div className=\"text-center text-destructive\">\n <p className=\"font-medium\">{errorText}</p>\n {error && (\n <p className=\"mt-2 text-sm text-muted-foreground\">{error.message}</p>\n )}\n </div>\n </div>\n );\n\n // 渲染 PDF 文档\n const renderPDFDocument = () => {\n if (!ReactPDF || !pdfDocument) return null;\n\n const { Page } = ReactPDF;\n\n return (\n <div\n className={`flex flex-col items-center justify-center ${\n isFullscreen\n ? 'h-[calc(100vh-56px)] overflow-auto'\n : 'overflow-visible'\n }`}\n >\n {pagesToPreload.map((pageNum) => (\n <div\n key={pageNum}\n className={pageClassName}\n style={{\n display: pageNum === currentPage ? 'block' : 'none',\n }}\n >\n <Page\n pdf={pdfDocument}\n pageNumber={pageNum}\n scale={scale}\n renderTextLayer={false}\n renderAnnotationLayer={false}\n className=\"shadow-md\"\n />\n </div>\n ))}\n </div>\n );\n };\n\n return (\n <div ref={readerRef}>\n <Card className={className}>\n {renderOperations()}\n <CardContent className={containerClassName}>\n {isLoading\n ? renderLoading()\n : error\n ? renderError()\n : renderPDFDocument()}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"names":["SimplePDFReader","url","initialPage","initialScale","controlledScale","onScaleChange","minScale","maxScale","controlledPage","onPageChange","showToolbar","showPagination","enableHotkeys","className","containerClassName","pageClassName","components","onLoadSuccess","onLoadError","loadingText","errorText","pdfDocument","setPdfDocument","useState","internalPage","setInternalPage","internalScale","setInternalScale","isLoading","setIsLoading","error","setError","totalPages","setTotalPages","isFullscreen","setIsFullscreen","readerRef","useRef","loadingTaskRef","ReactPDF","setReactPDF","currentPage","scale","pagesToPreload","useMemo","pages","goToPage","useCallback","page","maxPage","targetPage","handlePreviousPage","handleNextPage","handleZoomIn","newScale","handleZoomOut","handleToggleFullscreen","_b","_a","_c","useEffect","isMounted","pdfModule","pdfjs","workerVersion","err","loadError","Document","loadingTask","pdf","prev","handleFullscreenChange","handleKeyDown","e","activeElement","jsx","Card","CardContent","Button","InputComponentInjected","Skeleton","renderToolbar","jsxs","ZoomOutIcon","ZoomInIcon","Minimize2Icon","Maximize2Icon","renderPagination","ChevronLeftIcon","ChevronRightIcon","renderOperations","renderLoading","renderError","renderPDFDocument","Page","pageNum"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"simple-pdf-reader.cjs","sources":["../src/components/SimplePDFReader.tsx"],"sourcesContent":["import {\n ChevronLeft as ChevronLeftIcon,\n ChevronRight as ChevronRightIcon,\n Maximize2 as Maximize2Icon,\n Minimize2 as Minimize2Icon,\n ZoomIn as ZoomInIcon,\n ZoomOut as ZoomOutIcon,\n} from 'lucide-react';\nimport { useState, useMemo, useCallback, useEffect, useRef } from 'react';\nimport type { HTMLAttributes } from 'react';\n\nimport type {\n CardComponent,\n ButtonComponent,\n InputComponent,\n SkeletonComponent,\n} from '../types/component-types';\n\n/**\n * react-pdf 类型定义\n * 这些类型由 @types/react-pdf 提供,这里我们定义必要的接口\n */\nexport interface PDFDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<any>;\n}\n\n/**\n * SimplePDFReader UI 组件接口\n */\nexport interface SimplePDFReaderUIComponents {\n Card: CardComponent;\n CardContent: React.ComponentType<HTMLAttributes<HTMLDivElement>>;\n Button: ButtonComponent;\n Input?: InputComponent;\n Skeleton: SkeletonComponent;\n}\n\n/**\n * SimplePDFReader 组件 Props\n */\nexport interface SimplePDFReaderProps {\n // PDF 数据源\n url: string;\n\n // 初始状态\n initialPage?: number;\n initialScale?: number;\n\n // 缩放控制\n scale?: number;\n onScaleChange?: (scale: number) => void;\n minScale?: number;\n maxScale?: number;\n\n // 页面导航\n currentPage?: number;\n onPageChange?: (page: number) => void;\n\n // 功能开关\n showToolbar?: boolean; // 默认 true\n showPagination?: boolean; // 默认 true\n enableHotkeys?: boolean; // 默认 true\n\n // 样式定制\n className?: string;\n containerClassName?: string;\n pageClassName?: string;\n\n // UI 组件注入\n components: SimplePDFReaderUIComponents;\n\n // 回调函数\n onLoadSuccess?: (pdf: PDFDocumentProxy) => void;\n onLoadError?: (error: Error) => void;\n\n // 加载状态文本\n loadingText?: string; // 默认 \"加载中...\"\n errorText?: string; // 默认 \"加载失败\"\n}\n\n/**\n * SimplePDFReader 组件\n *\n * 用于在 React 应用中展示和浏览 PDF 文档。\n * 支持从 URL 加载 PDF,提供基础的浏览功能(翻页、缩放),并包含性能优化。\n *\n * @example\n * ```tsx\n * import { SimplePDFReader } from '@turinhub/atomix-common-ui/simple-pdf-reader';\n * import { Card, Button, Skeleton } from '@/components/ui';\n *\n * <SimplePDFReader\n * url=\"/documents/sample.pdf\"\n * components={{\n * Card,\n * CardContent: Card.Content,\n * Button,\n * Skeleton,\n * }}\n * initialPage={1}\n * initialScale={1.0}\n * showToolbar={true}\n * showPagination={true}\n * enableHotkeys={true}\n * onPageChange={(page) => console.log('Current page:', page)}\n * />\n *\n * Keyboard Shortcuts:\n * - Arrow Left/Right: Navigate pages\n * - Ctrl/Cmd + +/-: Zoom in/out\n * - F: Toggle fullscreen\n * ```\n */\nexport function SimplePDFReader({\n url,\n initialPage = 1,\n initialScale = 1.0,\n scale: controlledScale,\n onScaleChange,\n minScale = 0.5,\n maxScale = 3.0,\n currentPage: controlledPage,\n onPageChange,\n showToolbar = true,\n showPagination = true,\n enableHotkeys = true,\n className,\n containerClassName,\n pageClassName,\n components,\n onLoadSuccess,\n onLoadError,\n loadingText = '加载中...',\n errorText = '加载失败',\n}: SimplePDFReaderProps) {\n // ==================== React Hooks (必须在最顶部) ====================\n\n // 状态管理\n const [pdfDocument, setPdfDocument] = useState<PDFDocumentProxy | null>(null);\n const [internalPage, setInternalPage] = useState(initialPage);\n const [internalScale, setInternalScale] = useState(initialScale);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [totalPages, setTotalPages] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const readerRef = useRef<HTMLDivElement>(null);\n const loadingTaskRef = useRef<any>(null);\n\n // 动态导入 react-pdf\n const [ReactPDF, setReactPDF] = useState<any>(null);\n\n // 使用受控或非受控模式\n const currentPage = controlledPage ?? internalPage;\n const scale = controlledScale ?? internalScale;\n\n // 预渲染相邻页面以提升翻页体验\n const pagesToPreload = useMemo(() => {\n const pages = [currentPage];\n if (currentPage > 1) pages.push(currentPage - 1);\n if (currentPage < totalPages) pages.push(currentPage + 1);\n return pages;\n }, [currentPage, totalPages]);\n\n // 页面导航处理\n const goToPage = useCallback(\n (page: number) => {\n const maxPage = Math.max(totalPages, 1);\n const targetPage = Math.min(Math.max(page, 1), maxPage);\n if (controlledPage === undefined) {\n setInternalPage(targetPage);\n }\n onPageChange?.(targetPage);\n },\n [totalPages, controlledPage, onPageChange]\n );\n\n const handlePreviousPage = useCallback(() => {\n if (currentPage > 1) {\n goToPage(currentPage - 1);\n }\n }, [currentPage, goToPage]);\n\n const handleNextPage = useCallback(() => {\n if (currentPage < totalPages) {\n goToPage(currentPage + 1);\n }\n }, [currentPage, totalPages, goToPage]);\n\n // 缩放控制处理\n const handleZoomIn = useCallback(() => {\n const newScale = Math.min(scale + 0.25, maxScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, maxScale, controlledScale, onScaleChange]);\n\n const handleZoomOut = useCallback(() => {\n const newScale = Math.max(scale - 0.25, minScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, minScale, controlledScale, onScaleChange]);\n\n const handleToggleFullscreen = useCallback(async () => {\n if (typeof document === 'undefined') return;\n\n if (!document.fullscreenElement) {\n await readerRef.current?.requestFullscreen?.();\n return;\n }\n\n await document.exitFullscreen?.();\n }, []);\n\n // 动态导入 react-pdf 和设置 worker\n useEffect(() => {\n let isMounted = true;\n\n const loadReactPDF = async () => {\n try {\n // 动态导入 react-pdf\n const pdfModule = await import('react-pdf');\n // 设置 worker\n if (typeof window !== 'undefined') {\n const pdfjs = (pdfModule as any).pdfjs;\n const workerVersion = pdfjs?.version;\n if (pdfjs?.GlobalWorkerOptions && workerVersion) {\n pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${workerVersion}/build/pdf.worker.min.mjs`;\n }\n }\n if (isMounted) {\n setReactPDF(pdfModule);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('无法加载 react-pdf 库');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadReactPDF();\n\n return () => {\n isMounted = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // 加载 PDF 文档\n useEffect(() => {\n if (!ReactPDF || !url) return;\n\n let isMounted = true;\n\n const loadPDF = async () => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { Document } = ReactPDF;\n if (!Document) {\n throw new Error('react-pdf Document 组件不可用');\n }\n\n // 使用 react-pdf 的 Document 组件内部加载逻辑\n // 我们创建一个加载器来获取 PDF 文档信息\n const loadingTask = (ReactPDF.pdfjs as any).getDocument(url);\n loadingTaskRef.current = loadingTask;\n const pdf = await loadingTask.promise;\n\n if (isMounted) {\n setPdfDocument(pdf as PDFDocumentProxy);\n setTotalPages(pdf.numPages);\n if (controlledPage === undefined) {\n setInternalPage((prev) =>\n Math.max(1, Math.min(prev, pdf.numPages))\n );\n }\n setIsLoading(false);\n onLoadSuccess?.(pdf as PDFDocumentProxy);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('PDF 加载失败');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadPDF();\n\n return () => {\n isMounted = false;\n if (\n loadingTaskRef.current &&\n typeof loadingTaskRef.current.destroy === 'function'\n ) {\n loadingTaskRef.current.destroy();\n loadingTaskRef.current = null;\n }\n };\n }, [ReactPDF, url, controlledPage, onLoadSuccess, onLoadError]);\n\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n const handleFullscreenChange = () => {\n setIsFullscreen(document.fullscreenElement === readerRef.current);\n };\n\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange);\n };\n }, []);\n\n // ==================== 键盘快捷键 ====================\n useEffect(() => {\n if (!enableHotkeys) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Don't handle hotkeys if input is focused\n const activeElement = document.activeElement;\n if (\n activeElement &&\n (activeElement.tagName === 'INPUT' ||\n activeElement.getAttribute('role') === 'input')\n ) {\n return;\n }\n\n // Ctrl/Cmd + 加号: 放大\n if ((e.ctrlKey || e.metaKey) && (e.key === '=' || e.key === '+')) {\n e.preventDefault();\n handleZoomIn();\n }\n // Ctrl/Cmd + 减号: 缩小\n else if ((e.ctrlKey || e.metaKey) && e.key === '-') {\n e.preventDefault();\n handleZoomOut();\n }\n // 左箭头: 上一页\n else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n handlePreviousPage();\n }\n // 右箭头: 下一页\n else if (e.key === 'ArrowRight') {\n e.preventDefault();\n handleNextPage();\n }\n // F 键: 全屏切换\n else if (e.key === 'f' || e.key === 'F') {\n e.preventDefault();\n void handleToggleFullscreen();\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [\n enableHotkeys,\n handleZoomIn,\n handleZoomOut,\n handlePreviousPage,\n handleNextPage,\n handleToggleFullscreen,\n ]);\n\n // ==================== 组件验证和渲染 ====================\n\n // 验证 components\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const {\n Card,\n CardContent,\n Button,\n Input: InputComponentInjected,\n Skeleton,\n } = components;\n\n // 渲染工具栏\n const renderToolbar = () => {\n if (!showToolbar) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomOut}\n disabled={scale <= minScale}\n >\n <ZoomOutIcon />\n </Button>\n <span className=\"text-sm\">{Math.round(scale * 100)}%</span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomIn}\n disabled={scale >= maxScale}\n >\n <ZoomInIcon />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => {\n void handleToggleFullscreen();\n }}\n >\n {isFullscreen ? <Minimize2Icon /> : <Maximize2Icon />}\n </Button>\n </div>\n );\n };\n\n // 渲染分页控制\n const renderPagination = () => {\n if (!showPagination) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handlePreviousPage}\n disabled={currentPage <= 1}\n >\n <ChevronLeftIcon />\n </Button>\n {InputComponentInjected ? (\n <InputComponentInjected\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 text-center\"\n />\n ) : (\n <input\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 rounded-md border border-input bg-background px-2 text-center text-sm\"\n />\n )}\n <span className=\"text-sm text-muted-foreground\">/ {totalPages}</span>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handleNextPage}\n disabled={currentPage >= totalPages}\n >\n <ChevronRightIcon />\n </Button>\n </div>\n );\n };\n\n const renderOperations = () => {\n if (!showToolbar && !showPagination) return null;\n\n return (\n <div\n data-testid=\"pdf-operations-bar\"\n className=\"flex items-center justify-between gap-4 border-b px-4 py-2\"\n >\n {renderToolbar()}\n {renderPagination()}\n </div>\n );\n };\n\n // 渲染加载状态\n const renderLoading = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <Skeleton className=\"h-8 w-32\" />\n <Skeleton className=\"h-64 w-full max-w-2xl\" />\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n </div>\n );\n\n // 渲染错误状态\n const renderError = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <div className=\"text-center text-destructive\">\n <p className=\"font-medium\">{errorText}</p>\n {error && (\n <p className=\"mt-2 text-sm text-muted-foreground\">{error.message}</p>\n )}\n </div>\n </div>\n );\n\n // 渲染 PDF 文档\n const renderPDFDocument = () => {\n if (!ReactPDF || !pdfDocument) return null;\n\n const { Page } = ReactPDF;\n\n return (\n <div\n className={`flex flex-col items-center justify-center ${\n isFullscreen\n ? 'h-[calc(100vh-56px)] overflow-auto'\n : 'overflow-visible'\n }`}\n >\n {pagesToPreload.map((pageNum) => (\n <div\n key={pageNum}\n className={pageClassName}\n style={{\n display: pageNum === currentPage ? 'block' : 'none',\n }}\n >\n <Page\n pdf={pdfDocument}\n pageNumber={pageNum}\n scale={scale}\n renderTextLayer={false}\n renderAnnotationLayer={false}\n className=\"shadow-md\"\n />\n </div>\n ))}\n </div>\n );\n };\n\n return (\n <div ref={readerRef}>\n <Card className={className}>\n {renderOperations()}\n <CardContent className={containerClassName}>\n {isLoading\n ? renderLoading()\n : error\n ? renderError()\n : renderPDFDocument()}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"names":["SimplePDFReader","url","initialPage","initialScale","controlledScale","onScaleChange","minScale","maxScale","controlledPage","onPageChange","showToolbar","showPagination","enableHotkeys","className","containerClassName","pageClassName","components","onLoadSuccess","onLoadError","loadingText","errorText","pdfDocument","setPdfDocument","useState","internalPage","setInternalPage","internalScale","setInternalScale","isLoading","setIsLoading","error","setError","totalPages","setTotalPages","isFullscreen","setIsFullscreen","readerRef","useRef","loadingTaskRef","ReactPDF","setReactPDF","currentPage","scale","pagesToPreload","useMemo","pages","goToPage","useCallback","page","maxPage","targetPage","handlePreviousPage","handleNextPage","handleZoomIn","newScale","handleZoomOut","handleToggleFullscreen","_b","_a","_c","useEffect","isMounted","pdfModule","pdfjs","workerVersion","err","loadError","Document","loadingTask","pdf","prev","handleFullscreenChange","handleKeyDown","e","activeElement","jsx","Card","CardContent","Button","InputComponentInjected","Skeleton","renderToolbar","jsxs","ZoomOutIcon","ZoomInIcon","Minimize2Icon","Maximize2Icon","renderPagination","ChevronLeftIcon","ChevronRightIcon","renderOperations","renderLoading","renderError","renderPDFDocument","Page","pageNum"],"mappings":"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"}
|
|
@@ -56,7 +56,7 @@ function ge({
|
|
|
56
56
|
let e = !0;
|
|
57
57
|
return (async () => {
|
|
58
58
|
try {
|
|
59
|
-
const s = await import("
|
|
59
|
+
const s = await import("react-pdf");
|
|
60
60
|
if (typeof window < "u") {
|
|
61
61
|
const i = s.pdfjs, d = i == null ? void 0 : i.version;
|
|
62
62
|
i != null && i.GlobalWorkerOptions && d && (i.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${d}/build/pdf.worker.min.mjs`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simple-pdf-reader.js","sources":["../src/components/SimplePDFReader.tsx"],"sourcesContent":["import {\n ChevronLeft as ChevronLeftIcon,\n ChevronRight as ChevronRightIcon,\n Maximize2 as Maximize2Icon,\n Minimize2 as Minimize2Icon,\n ZoomIn as ZoomInIcon,\n ZoomOut as ZoomOutIcon,\n} from 'lucide-react';\nimport { useState, useMemo, useCallback, useEffect, useRef } from 'react';\nimport type { HTMLAttributes } from 'react';\n\nimport type {\n CardComponent,\n ButtonComponent,\n InputComponent,\n SkeletonComponent,\n} from '../types/component-types';\n\n/**\n * react-pdf 类型定义\n * 这些类型由 @types/react-pdf 提供,这里我们定义必要的接口\n */\nexport interface PDFDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<any>;\n}\n\n/**\n * SimplePDFReader UI 组件接口\n */\nexport interface SimplePDFReaderUIComponents {\n Card: CardComponent;\n CardContent: React.ComponentType<HTMLAttributes<HTMLDivElement>>;\n Button: ButtonComponent;\n Input?: InputComponent;\n Skeleton: SkeletonComponent;\n}\n\n/**\n * SimplePDFReader 组件 Props\n */\nexport interface SimplePDFReaderProps {\n // PDF 数据源\n url: string;\n\n // 初始状态\n initialPage?: number;\n initialScale?: number;\n\n // 缩放控制\n scale?: number;\n onScaleChange?: (scale: number) => void;\n minScale?: number;\n maxScale?: number;\n\n // 页面导航\n currentPage?: number;\n onPageChange?: (page: number) => void;\n\n // 功能开关\n showToolbar?: boolean; // 默认 true\n showPagination?: boolean; // 默认 true\n enableHotkeys?: boolean; // 默认 true\n\n // 样式定制\n className?: string;\n containerClassName?: string;\n pageClassName?: string;\n\n // UI 组件注入\n components: SimplePDFReaderUIComponents;\n\n // 回调函数\n onLoadSuccess?: (pdf: PDFDocumentProxy) => void;\n onLoadError?: (error: Error) => void;\n\n // 加载状态文本\n loadingText?: string; // 默认 \"加载中...\"\n errorText?: string; // 默认 \"加载失败\"\n}\n\n/**\n * SimplePDFReader 组件\n *\n * 用于在 React 应用中展示和浏览 PDF 文档。\n * 支持从 URL 加载 PDF,提供基础的浏览功能(翻页、缩放),并包含性能优化。\n *\n * @example\n * ```tsx\n * import { SimplePDFReader } from '@turinhub/atomix-common-ui/simple-pdf-reader';\n * import { Card, Button, Skeleton } from '@/components/ui';\n *\n * <SimplePDFReader\n * url=\"/documents/sample.pdf\"\n * components={{\n * Card,\n * CardContent: Card.Content,\n * Button,\n * Skeleton,\n * }}\n * initialPage={1}\n * initialScale={1.0}\n * showToolbar={true}\n * showPagination={true}\n * enableHotkeys={true}\n * onPageChange={(page) => console.log('Current page:', page)}\n * />\n *\n * Keyboard Shortcuts:\n * - Arrow Left/Right: Navigate pages\n * - Ctrl/Cmd + +/-: Zoom in/out\n * - F: Toggle fullscreen\n * ```\n */\nexport function SimplePDFReader({\n url,\n initialPage = 1,\n initialScale = 1.0,\n scale: controlledScale,\n onScaleChange,\n minScale = 0.5,\n maxScale = 3.0,\n currentPage: controlledPage,\n onPageChange,\n showToolbar = true,\n showPagination = true,\n enableHotkeys = true,\n className,\n containerClassName,\n pageClassName,\n components,\n onLoadSuccess,\n onLoadError,\n loadingText = '加载中...',\n errorText = '加载失败',\n}: SimplePDFReaderProps) {\n // ==================== React Hooks (必须在最顶部) ====================\n\n // 状态管理\n const [pdfDocument, setPdfDocument] = useState<PDFDocumentProxy | null>(null);\n const [internalPage, setInternalPage] = useState(initialPage);\n const [internalScale, setInternalScale] = useState(initialScale);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [totalPages, setTotalPages] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const readerRef = useRef<HTMLDivElement>(null);\n const loadingTaskRef = useRef<any>(null);\n\n // 动态导入 react-pdf\n const [ReactPDF, setReactPDF] = useState<any>(null);\n\n // 使用受控或非受控模式\n const currentPage = controlledPage ?? internalPage;\n const scale = controlledScale ?? internalScale;\n\n // 预渲染相邻页面以提升翻页体验\n const pagesToPreload = useMemo(() => {\n const pages = [currentPage];\n if (currentPage > 1) pages.push(currentPage - 1);\n if (currentPage < totalPages) pages.push(currentPage + 1);\n return pages;\n }, [currentPage, totalPages]);\n\n // 页面导航处理\n const goToPage = useCallback(\n (page: number) => {\n const maxPage = Math.max(totalPages, 1);\n const targetPage = Math.min(Math.max(page, 1), maxPage);\n if (controlledPage === undefined) {\n setInternalPage(targetPage);\n }\n onPageChange?.(targetPage);\n },\n [totalPages, controlledPage, onPageChange]\n );\n\n const handlePreviousPage = useCallback(() => {\n if (currentPage > 1) {\n goToPage(currentPage - 1);\n }\n }, [currentPage, goToPage]);\n\n const handleNextPage = useCallback(() => {\n if (currentPage < totalPages) {\n goToPage(currentPage + 1);\n }\n }, [currentPage, totalPages, goToPage]);\n\n // 缩放控制处理\n const handleZoomIn = useCallback(() => {\n const newScale = Math.min(scale + 0.25, maxScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, maxScale, controlledScale, onScaleChange]);\n\n const handleZoomOut = useCallback(() => {\n const newScale = Math.max(scale - 0.25, minScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, minScale, controlledScale, onScaleChange]);\n\n const handleToggleFullscreen = useCallback(async () => {\n if (typeof document === 'undefined') return;\n\n if (!document.fullscreenElement) {\n await readerRef.current?.requestFullscreen?.();\n return;\n }\n\n await document.exitFullscreen?.();\n }, []);\n\n // 动态导入 react-pdf 和设置 worker\n useEffect(() => {\n let isMounted = true;\n\n const loadReactPDF = async () => {\n try {\n // 动态导入 react-pdf\n const pdfModule = await import('react-pdf');\n // 设置 worker\n if (typeof window !== 'undefined') {\n const pdfjs = (pdfModule as any).pdfjs;\n const workerVersion = pdfjs?.version;\n if (pdfjs?.GlobalWorkerOptions && workerVersion) {\n pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${workerVersion}/build/pdf.worker.min.mjs`;\n }\n }\n if (isMounted) {\n setReactPDF(pdfModule);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('无法加载 react-pdf 库');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadReactPDF();\n\n return () => {\n isMounted = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // 加载 PDF 文档\n useEffect(() => {\n if (!ReactPDF || !url) return;\n\n let isMounted = true;\n\n const loadPDF = async () => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { Document } = ReactPDF;\n if (!Document) {\n throw new Error('react-pdf Document 组件不可用');\n }\n\n // 使用 react-pdf 的 Document 组件内部加载逻辑\n // 我们创建一个加载器来获取 PDF 文档信息\n const loadingTask = (ReactPDF.pdfjs as any).getDocument(url);\n loadingTaskRef.current = loadingTask;\n const pdf = await loadingTask.promise;\n\n if (isMounted) {\n setPdfDocument(pdf as PDFDocumentProxy);\n setTotalPages(pdf.numPages);\n if (controlledPage === undefined) {\n setInternalPage((prev) =>\n Math.max(1, Math.min(prev, pdf.numPages))\n );\n }\n setIsLoading(false);\n onLoadSuccess?.(pdf as PDFDocumentProxy);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('PDF 加载失败');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadPDF();\n\n return () => {\n isMounted = false;\n if (\n loadingTaskRef.current &&\n typeof loadingTaskRef.current.destroy === 'function'\n ) {\n loadingTaskRef.current.destroy();\n loadingTaskRef.current = null;\n }\n };\n }, [ReactPDF, url, controlledPage, onLoadSuccess, onLoadError]);\n\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n const handleFullscreenChange = () => {\n setIsFullscreen(document.fullscreenElement === readerRef.current);\n };\n\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange);\n };\n }, []);\n\n // ==================== 键盘快捷键 ====================\n useEffect(() => {\n if (!enableHotkeys) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Don't handle hotkeys if input is focused\n const activeElement = document.activeElement;\n if (\n activeElement &&\n (activeElement.tagName === 'INPUT' ||\n activeElement.getAttribute('role') === 'input')\n ) {\n return;\n }\n\n // Ctrl/Cmd + 加号: 放大\n if ((e.ctrlKey || e.metaKey) && (e.key === '=' || e.key === '+')) {\n e.preventDefault();\n handleZoomIn();\n }\n // Ctrl/Cmd + 减号: 缩小\n else if ((e.ctrlKey || e.metaKey) && e.key === '-') {\n e.preventDefault();\n handleZoomOut();\n }\n // 左箭头: 上一页\n else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n handlePreviousPage();\n }\n // 右箭头: 下一页\n else if (e.key === 'ArrowRight') {\n e.preventDefault();\n handleNextPage();\n }\n // F 键: 全屏切换\n else if (e.key === 'f' || e.key === 'F') {\n e.preventDefault();\n void handleToggleFullscreen();\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [\n enableHotkeys,\n handleZoomIn,\n handleZoomOut,\n handlePreviousPage,\n handleNextPage,\n handleToggleFullscreen,\n ]);\n\n // ==================== 组件验证和渲染 ====================\n\n // 验证 components\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const {\n Card,\n CardContent,\n Button,\n Input: InputComponentInjected,\n Skeleton,\n } = components;\n\n // 渲染工具栏\n const renderToolbar = () => {\n if (!showToolbar) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomOut}\n disabled={scale <= minScale}\n >\n <ZoomOutIcon />\n </Button>\n <span className=\"text-sm\">{Math.round(scale * 100)}%</span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomIn}\n disabled={scale >= maxScale}\n >\n <ZoomInIcon />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => {\n void handleToggleFullscreen();\n }}\n >\n {isFullscreen ? <Minimize2Icon /> : <Maximize2Icon />}\n </Button>\n </div>\n );\n };\n\n // 渲染分页控制\n const renderPagination = () => {\n if (!showPagination) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handlePreviousPage}\n disabled={currentPage <= 1}\n >\n <ChevronLeftIcon />\n </Button>\n {InputComponentInjected ? (\n <InputComponentInjected\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 text-center\"\n />\n ) : (\n <input\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 rounded-md border border-input bg-background px-2 text-center text-sm\"\n />\n )}\n <span className=\"text-sm text-muted-foreground\">/ {totalPages}</span>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handleNextPage}\n disabled={currentPage >= totalPages}\n >\n <ChevronRightIcon />\n </Button>\n </div>\n );\n };\n\n const renderOperations = () => {\n if (!showToolbar && !showPagination) return null;\n\n return (\n <div\n data-testid=\"pdf-operations-bar\"\n className=\"flex items-center justify-between gap-4 border-b px-4 py-2\"\n >\n {renderToolbar()}\n {renderPagination()}\n </div>\n );\n };\n\n // 渲染加载状态\n const renderLoading = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <Skeleton className=\"h-8 w-32\" />\n <Skeleton className=\"h-64 w-full max-w-2xl\" />\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n </div>\n );\n\n // 渲染错误状态\n const renderError = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <div className=\"text-center text-destructive\">\n <p className=\"font-medium\">{errorText}</p>\n {error && (\n <p className=\"mt-2 text-sm text-muted-foreground\">{error.message}</p>\n )}\n </div>\n </div>\n );\n\n // 渲染 PDF 文档\n const renderPDFDocument = () => {\n if (!ReactPDF || !pdfDocument) return null;\n\n const { Page } = ReactPDF;\n\n return (\n <div\n className={`flex flex-col items-center justify-center ${\n isFullscreen\n ? 'h-[calc(100vh-56px)] overflow-auto'\n : 'overflow-visible'\n }`}\n >\n {pagesToPreload.map((pageNum) => (\n <div\n key={pageNum}\n className={pageClassName}\n style={{\n display: pageNum === currentPage ? 'block' : 'none',\n }}\n >\n <Page\n pdf={pdfDocument}\n pageNumber={pageNum}\n scale={scale}\n renderTextLayer={false}\n renderAnnotationLayer={false}\n className=\"shadow-md\"\n />\n </div>\n ))}\n </div>\n );\n };\n\n return (\n <div ref={readerRef}>\n <Card className={className}>\n {renderOperations()}\n <CardContent className={containerClassName}>\n {isLoading\n ? renderLoading()\n : error\n ? renderError()\n : renderPDFDocument()}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"names":["SimplePDFReader","url","initialPage","initialScale","controlledScale","onScaleChange","minScale","maxScale","controlledPage","onPageChange","showToolbar","showPagination","enableHotkeys","className","containerClassName","pageClassName","components","onLoadSuccess","onLoadError","loadingText","errorText","pdfDocument","setPdfDocument","useState","internalPage","setInternalPage","internalScale","setInternalScale","isLoading","setIsLoading","error","setError","totalPages","setTotalPages","isFullscreen","setIsFullscreen","readerRef","useRef","loadingTaskRef","ReactPDF","setReactPDF","currentPage","scale","pagesToPreload","useMemo","pages","goToPage","useCallback","page","maxPage","targetPage","handlePreviousPage","handleNextPage","handleZoomIn","newScale","handleZoomOut","handleToggleFullscreen","_b","_a","_c","useEffect","isMounted","pdfModule","pdfjs","workerVersion","err","loadError","Document","loadingTask","pdf","prev","handleFullscreenChange","handleKeyDown","e","activeElement","jsx","Card","CardContent","Button","InputComponentInjected","Skeleton","renderToolbar","jsxs","ZoomOutIcon","ZoomInIcon","Minimize2Icon","Maximize2Icon","renderPagination","ChevronLeftIcon","ChevronRightIcon","renderOperations","renderLoading","renderError","renderPDFDocument","Page","pageNum"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"simple-pdf-reader.js","sources":["../src/components/SimplePDFReader.tsx"],"sourcesContent":["import {\n ChevronLeft as ChevronLeftIcon,\n ChevronRight as ChevronRightIcon,\n Maximize2 as Maximize2Icon,\n Minimize2 as Minimize2Icon,\n ZoomIn as ZoomInIcon,\n ZoomOut as ZoomOutIcon,\n} from 'lucide-react';\nimport { useState, useMemo, useCallback, useEffect, useRef } from 'react';\nimport type { HTMLAttributes } from 'react';\n\nimport type {\n CardComponent,\n ButtonComponent,\n InputComponent,\n SkeletonComponent,\n} from '../types/component-types';\n\n/**\n * react-pdf 类型定义\n * 这些类型由 @types/react-pdf 提供,这里我们定义必要的接口\n */\nexport interface PDFDocumentProxy {\n numPages: number;\n getPage(pageNumber: number): Promise<any>;\n}\n\n/**\n * SimplePDFReader UI 组件接口\n */\nexport interface SimplePDFReaderUIComponents {\n Card: CardComponent;\n CardContent: React.ComponentType<HTMLAttributes<HTMLDivElement>>;\n Button: ButtonComponent;\n Input?: InputComponent;\n Skeleton: SkeletonComponent;\n}\n\n/**\n * SimplePDFReader 组件 Props\n */\nexport interface SimplePDFReaderProps {\n // PDF 数据源\n url: string;\n\n // 初始状态\n initialPage?: number;\n initialScale?: number;\n\n // 缩放控制\n scale?: number;\n onScaleChange?: (scale: number) => void;\n minScale?: number;\n maxScale?: number;\n\n // 页面导航\n currentPage?: number;\n onPageChange?: (page: number) => void;\n\n // 功能开关\n showToolbar?: boolean; // 默认 true\n showPagination?: boolean; // 默认 true\n enableHotkeys?: boolean; // 默认 true\n\n // 样式定制\n className?: string;\n containerClassName?: string;\n pageClassName?: string;\n\n // UI 组件注入\n components: SimplePDFReaderUIComponents;\n\n // 回调函数\n onLoadSuccess?: (pdf: PDFDocumentProxy) => void;\n onLoadError?: (error: Error) => void;\n\n // 加载状态文本\n loadingText?: string; // 默认 \"加载中...\"\n errorText?: string; // 默认 \"加载失败\"\n}\n\n/**\n * SimplePDFReader 组件\n *\n * 用于在 React 应用中展示和浏览 PDF 文档。\n * 支持从 URL 加载 PDF,提供基础的浏览功能(翻页、缩放),并包含性能优化。\n *\n * @example\n * ```tsx\n * import { SimplePDFReader } from '@turinhub/atomix-common-ui/simple-pdf-reader';\n * import { Card, Button, Skeleton } from '@/components/ui';\n *\n * <SimplePDFReader\n * url=\"/documents/sample.pdf\"\n * components={{\n * Card,\n * CardContent: Card.Content,\n * Button,\n * Skeleton,\n * }}\n * initialPage={1}\n * initialScale={1.0}\n * showToolbar={true}\n * showPagination={true}\n * enableHotkeys={true}\n * onPageChange={(page) => console.log('Current page:', page)}\n * />\n *\n * Keyboard Shortcuts:\n * - Arrow Left/Right: Navigate pages\n * - Ctrl/Cmd + +/-: Zoom in/out\n * - F: Toggle fullscreen\n * ```\n */\nexport function SimplePDFReader({\n url,\n initialPage = 1,\n initialScale = 1.0,\n scale: controlledScale,\n onScaleChange,\n minScale = 0.5,\n maxScale = 3.0,\n currentPage: controlledPage,\n onPageChange,\n showToolbar = true,\n showPagination = true,\n enableHotkeys = true,\n className,\n containerClassName,\n pageClassName,\n components,\n onLoadSuccess,\n onLoadError,\n loadingText = '加载中...',\n errorText = '加载失败',\n}: SimplePDFReaderProps) {\n // ==================== React Hooks (必须在最顶部) ====================\n\n // 状态管理\n const [pdfDocument, setPdfDocument] = useState<PDFDocumentProxy | null>(null);\n const [internalPage, setInternalPage] = useState(initialPage);\n const [internalScale, setInternalScale] = useState(initialScale);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [totalPages, setTotalPages] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const readerRef = useRef<HTMLDivElement>(null);\n const loadingTaskRef = useRef<any>(null);\n\n // 动态导入 react-pdf\n const [ReactPDF, setReactPDF] = useState<any>(null);\n\n // 使用受控或非受控模式\n const currentPage = controlledPage ?? internalPage;\n const scale = controlledScale ?? internalScale;\n\n // 预渲染相邻页面以提升翻页体验\n const pagesToPreload = useMemo(() => {\n const pages = [currentPage];\n if (currentPage > 1) pages.push(currentPage - 1);\n if (currentPage < totalPages) pages.push(currentPage + 1);\n return pages;\n }, [currentPage, totalPages]);\n\n // 页面导航处理\n const goToPage = useCallback(\n (page: number) => {\n const maxPage = Math.max(totalPages, 1);\n const targetPage = Math.min(Math.max(page, 1), maxPage);\n if (controlledPage === undefined) {\n setInternalPage(targetPage);\n }\n onPageChange?.(targetPage);\n },\n [totalPages, controlledPage, onPageChange]\n );\n\n const handlePreviousPage = useCallback(() => {\n if (currentPage > 1) {\n goToPage(currentPage - 1);\n }\n }, [currentPage, goToPage]);\n\n const handleNextPage = useCallback(() => {\n if (currentPage < totalPages) {\n goToPage(currentPage + 1);\n }\n }, [currentPage, totalPages, goToPage]);\n\n // 缩放控制处理\n const handleZoomIn = useCallback(() => {\n const newScale = Math.min(scale + 0.25, maxScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, maxScale, controlledScale, onScaleChange]);\n\n const handleZoomOut = useCallback(() => {\n const newScale = Math.max(scale - 0.25, minScale);\n if (controlledScale === undefined) {\n setInternalScale(newScale);\n }\n onScaleChange?.(newScale);\n }, [scale, minScale, controlledScale, onScaleChange]);\n\n const handleToggleFullscreen = useCallback(async () => {\n if (typeof document === 'undefined') return;\n\n if (!document.fullscreenElement) {\n await readerRef.current?.requestFullscreen?.();\n return;\n }\n\n await document.exitFullscreen?.();\n }, []);\n\n // 动态导入 react-pdf 和设置 worker\n useEffect(() => {\n let isMounted = true;\n\n const loadReactPDF = async () => {\n try {\n // 动态导入 react-pdf\n const pdfModule = await import('react-pdf');\n // 设置 worker\n if (typeof window !== 'undefined') {\n const pdfjs = (pdfModule as any).pdfjs;\n const workerVersion = pdfjs?.version;\n if (pdfjs?.GlobalWorkerOptions && workerVersion) {\n pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${workerVersion}/build/pdf.worker.min.mjs`;\n }\n }\n if (isMounted) {\n setReactPDF(pdfModule);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('无法加载 react-pdf 库');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadReactPDF();\n\n return () => {\n isMounted = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // 加载 PDF 文档\n useEffect(() => {\n if (!ReactPDF || !url) return;\n\n let isMounted = true;\n\n const loadPDF = async () => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { Document } = ReactPDF;\n if (!Document) {\n throw new Error('react-pdf Document 组件不可用');\n }\n\n // 使用 react-pdf 的 Document 组件内部加载逻辑\n // 我们创建一个加载器来获取 PDF 文档信息\n const loadingTask = (ReactPDF.pdfjs as any).getDocument(url);\n loadingTaskRef.current = loadingTask;\n const pdf = await loadingTask.promise;\n\n if (isMounted) {\n setPdfDocument(pdf as PDFDocumentProxy);\n setTotalPages(pdf.numPages);\n if (controlledPage === undefined) {\n setInternalPage((prev) =>\n Math.max(1, Math.min(prev, pdf.numPages))\n );\n }\n setIsLoading(false);\n onLoadSuccess?.(pdf as PDFDocumentProxy);\n }\n } catch (err) {\n if (isMounted) {\n const loadError =\n err instanceof Error ? err : new Error('PDF 加载失败');\n setError(loadError);\n setIsLoading(false);\n onLoadError?.(loadError);\n }\n }\n };\n\n loadPDF();\n\n return () => {\n isMounted = false;\n if (\n loadingTaskRef.current &&\n typeof loadingTaskRef.current.destroy === 'function'\n ) {\n loadingTaskRef.current.destroy();\n loadingTaskRef.current = null;\n }\n };\n }, [ReactPDF, url, controlledPage, onLoadSuccess, onLoadError]);\n\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n const handleFullscreenChange = () => {\n setIsFullscreen(document.fullscreenElement === readerRef.current);\n };\n\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange);\n };\n }, []);\n\n // ==================== 键盘快捷键 ====================\n useEffect(() => {\n if (!enableHotkeys) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n // Don't handle hotkeys if input is focused\n const activeElement = document.activeElement;\n if (\n activeElement &&\n (activeElement.tagName === 'INPUT' ||\n activeElement.getAttribute('role') === 'input')\n ) {\n return;\n }\n\n // Ctrl/Cmd + 加号: 放大\n if ((e.ctrlKey || e.metaKey) && (e.key === '=' || e.key === '+')) {\n e.preventDefault();\n handleZoomIn();\n }\n // Ctrl/Cmd + 减号: 缩小\n else if ((e.ctrlKey || e.metaKey) && e.key === '-') {\n e.preventDefault();\n handleZoomOut();\n }\n // 左箭头: 上一页\n else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n handlePreviousPage();\n }\n // 右箭头: 下一页\n else if (e.key === 'ArrowRight') {\n e.preventDefault();\n handleNextPage();\n }\n // F 键: 全屏切换\n else if (e.key === 'f' || e.key === 'F') {\n e.preventDefault();\n void handleToggleFullscreen();\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [\n enableHotkeys,\n handleZoomIn,\n handleZoomOut,\n handlePreviousPage,\n handleNextPage,\n handleToggleFullscreen,\n ]);\n\n // ==================== 组件验证和渲染 ====================\n\n // 验证 components\n if (!components) {\n return (\n <div className=\"p-4 text-center text-destructive\">\n 错误:请通过 components prop 注入 UI 组件\n </div>\n );\n }\n\n const {\n Card,\n CardContent,\n Button,\n Input: InputComponentInjected,\n Skeleton,\n } = components;\n\n // 渲染工具栏\n const renderToolbar = () => {\n if (!showToolbar) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomOut}\n disabled={scale <= minScale}\n >\n <ZoomOutIcon />\n </Button>\n <span className=\"text-sm\">{Math.round(scale * 100)}%</span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleZoomIn}\n disabled={scale >= maxScale}\n >\n <ZoomInIcon />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => {\n void handleToggleFullscreen();\n }}\n >\n {isFullscreen ? <Minimize2Icon /> : <Maximize2Icon />}\n </Button>\n </div>\n );\n };\n\n // 渲染分页控制\n const renderPagination = () => {\n if (!showPagination) return null;\n\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handlePreviousPage}\n disabled={currentPage <= 1}\n >\n <ChevronLeftIcon />\n </Button>\n {InputComponentInjected ? (\n <InputComponentInjected\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 text-center\"\n />\n ) : (\n <input\n type=\"number\"\n min={1}\n max={Math.max(totalPages, 1)}\n value={currentPage}\n onChange={(e) => goToPage(parseInt(e.target.value, 10) || 1)}\n className=\"w-16 rounded-md border border-input bg-background px-2 text-center text-sm\"\n />\n )}\n <span className=\"text-sm text-muted-foreground\">/ {totalPages}</span>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={handleNextPage}\n disabled={currentPage >= totalPages}\n >\n <ChevronRightIcon />\n </Button>\n </div>\n );\n };\n\n const renderOperations = () => {\n if (!showToolbar && !showPagination) return null;\n\n return (\n <div\n data-testid=\"pdf-operations-bar\"\n className=\"flex items-center justify-between gap-4 border-b px-4 py-2\"\n >\n {renderToolbar()}\n {renderPagination()}\n </div>\n );\n };\n\n // 渲染加载状态\n const renderLoading = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <Skeleton className=\"h-8 w-32\" />\n <Skeleton className=\"h-64 w-full max-w-2xl\" />\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n </div>\n );\n\n // 渲染错误状态\n const renderError = () => (\n <div className=\"flex flex-col items-center justify-center space-y-4 p-8\">\n <div className=\"text-center text-destructive\">\n <p className=\"font-medium\">{errorText}</p>\n {error && (\n <p className=\"mt-2 text-sm text-muted-foreground\">{error.message}</p>\n )}\n </div>\n </div>\n );\n\n // 渲染 PDF 文档\n const renderPDFDocument = () => {\n if (!ReactPDF || !pdfDocument) return null;\n\n const { Page } = ReactPDF;\n\n return (\n <div\n className={`flex flex-col items-center justify-center ${\n isFullscreen\n ? 'h-[calc(100vh-56px)] overflow-auto'\n : 'overflow-visible'\n }`}\n >\n {pagesToPreload.map((pageNum) => (\n <div\n key={pageNum}\n className={pageClassName}\n style={{\n display: pageNum === currentPage ? 'block' : 'none',\n }}\n >\n <Page\n pdf={pdfDocument}\n pageNumber={pageNum}\n scale={scale}\n renderTextLayer={false}\n renderAnnotationLayer={false}\n className=\"shadow-md\"\n />\n </div>\n ))}\n </div>\n );\n };\n\n return (\n <div ref={readerRef}>\n <Card className={className}>\n {renderOperations()}\n <CardContent className={containerClassName}>\n {isLoading\n ? renderLoading()\n : error\n ? renderError()\n : renderPDFDocument()}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"names":["SimplePDFReader","url","initialPage","initialScale","controlledScale","onScaleChange","minScale","maxScale","controlledPage","onPageChange","showToolbar","showPagination","enableHotkeys","className","containerClassName","pageClassName","components","onLoadSuccess","onLoadError","loadingText","errorText","pdfDocument","setPdfDocument","useState","internalPage","setInternalPage","internalScale","setInternalScale","isLoading","setIsLoading","error","setError","totalPages","setTotalPages","isFullscreen","setIsFullscreen","readerRef","useRef","loadingTaskRef","ReactPDF","setReactPDF","currentPage","scale","pagesToPreload","useMemo","pages","goToPage","useCallback","page","maxPage","targetPage","handlePreviousPage","handleNextPage","handleZoomIn","newScale","handleZoomOut","handleToggleFullscreen","_b","_a","_c","useEffect","isMounted","pdfModule","pdfjs","workerVersion","err","loadError","Document","loadingTask","pdf","prev","handleFullscreenChange","handleKeyDown","e","activeElement","jsx","Card","CardContent","Button","InputComponentInjected","Skeleton","renderToolbar","jsxs","ZoomOutIcon","ZoomInIcon","Minimize2Icon","Maximize2Icon","renderPagination","ChevronLeftIcon","ChevronRightIcon","renderOperations","renderLoading","renderError","renderPDFDocument","Page","pageNum"],"mappings":"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"}
|
|
@@ -110,6 +110,7 @@ export type TabsComponent = UIComponent<{
|
|
|
110
110
|
onValueChange?: (value: string) => void;
|
|
111
111
|
children?: React.ReactNode;
|
|
112
112
|
defaultValue?: string;
|
|
113
|
+
className?: string;
|
|
113
114
|
}>;
|
|
114
115
|
export type TabsListComponent = UIComponent<HTMLAttributes<HTMLDivElement>>;
|
|
115
116
|
export type TabsTriggerComponent = UIComponent<HTMLAttributes<HTMLButtonElement> & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-types.d.ts","sourceRoot":"","sources":["../../src/types/component-types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"component-types.d.ts","sourceRoot":"","sources":["../../src/types/component-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./jsx-runtime-BB_1_6y_.cjs"),r=require("react"),W=require("lucide-react"),p=require("./utils-IjLH3w2e.cjs"),O=require("./media-utils-X1dDYP9W.cjs"),P=["mp4","webm","ogg","ogv","mov","m4v"],_=["video/mp4","video/webm","video/ogg","video/quicktime","video/x-m4v"],Z=s=>s?s instanceof Error?s:new Error(s):null;function ee({src:s,fileName:o,mimeType:i,title:j,components:y,tracks:u,loading:I=!1,error:V,className:E,containerClassName:M,videoClassName:q,toolbarClassName:U,loadingText:D="正在加载视频...",errorText:f="视频加载失败",unsupportedText:T="暂不支持该视频格式",showToolbar:C=!0,showOpenInNewTab:$=!0,allowUnsupportedFormat:k=!1,supportedExtensions:L=P,supportedMimeTypes:z=_,controls:B=!0,preload:F="metadata",playsInline:X=!0,onLoadedData:l,onError:x,...Y}){const{Card:R,CardContent:g,Button:h,Skeleton:a}=y||{},[A,d]=r.useState(!0),[G,c]=r.useState(null),n=Z(V)||G,v=k||O.isSupportedMediaSource({src:s,fileName:o,mimeType:i,supportedExtensions:L,supportedMimeTypes:z}),b=I||A&&!n,H=r.useMemo(()=>{const t=O.getMediaExtension(s,o);return t?t.toUpperCase():i||"视频"},[o,i,s]);r.useEffect(()=>{d(!0),c(null)},[s]);const J=(t,S,N)=>{const Q="inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground";return h?e.jsxRuntimeExports.jsx(h,{"aria-label":t,className:"h-8 w-8",onClick:N,size:"icon",title:t,type:"button",variant:"outline",children:S}):e.jsxRuntimeExports.jsx("button",{"aria-label":t,className:Q,onClick:N,title:t,type:"button",children:S})},K=()=>e.jsxRuntimeExports.jsx("div",{className:"w-full space-y-3 p-4",role:"status","aria-live":"polite",children:a?e.jsxRuntimeExports.jsxs(e.jsxRuntimeExports.Fragment,{children:[e.jsxRuntimeExports.jsx(a,{className:"h-4 w-32"}),e.jsxRuntimeExports.jsx(a,{className:"aspect-video w-full"})]}):e.jsxRuntimeExports.jsx("p",{className:"text-sm text-muted-foreground",children:D})}),w=t=>e.jsxRuntimeExports.jsxs("div",{className:"p-4 text-center text-sm text-destructive",role:"alert",children:[e.jsxRuntimeExports.jsx("p",{className:"font-medium",children:t||f}),n!=null&&n.message?e.jsxRuntimeExports.jsx("p",{className:"mt-1 opacity-80",children:n.message}):null]}),m=e.jsxRuntimeExports.jsxs("div",{className:p.cn("flex h-full min-h-[360px] flex-col overflow-hidden rounded-md border bg-background",M),children:[C?e.jsxRuntimeExports.jsxs("div",{className:p.cn("flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2",U),children:[e.jsxRuntimeExports.jsx("span",{className:"text-xs font-medium text-muted-foreground",children:j||H}),$?J("新窗口打开",e.jsxRuntimeExports.jsx(W.ExternalLink,{className:"h-4 w-4"}),()=>window.open(s,"_blank","noreferrer")):null]}):null,e.jsxRuntimeExports.jsxs("div",{className:"relative flex min-h-0 flex-1 items-center justify-center bg-muted/20 p-4",children:[v?n?w():null:w(T),v&&!n?e.jsxRuntimeExports.jsxs(e.jsxRuntimeExports.Fragment,{children:[b?K():null,r.createElement("video",{...Y,key:s,className:p.cn("max-h-full w-full max-w-full bg-black",b?"invisible absolute":"visible",q),controls:B,onError:()=>{const t=new Error(f);d(!1),c(t),x==null||x(t)},onLoadedData:()=>{d(!1),c(null),l==null||l()},playsInline:X,preload:F,title:j},e.jsxRuntimeExports.jsx("source",{src:s,type:i}),u==null?void 0:u.map(t=>e.jsxRuntimeExports.jsx("track",{...t},`${t.src}-${t.kind||""}`)))]}):null]})]});return R?e.jsxRuntimeExports.jsx(R,{className:E,children:g?e.jsxRuntimeExports.jsx(g,{children:m}):m}):e.jsxRuntimeExports.jsx("div",{className:E,children:m})}exports.SUPPORTED_VIDEO_EXTENSIONS=P;exports.SUPPORTED_VIDEO_MIME_TYPES=_;exports.VideoReader=ee;
|
|
2
|
+
//# sourceMappingURL=video-reader.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-reader.cjs","sources":["../src/components/VideoReader.tsx"],"sourcesContent":["import { ExternalLink as ExternalLinkIcon } from 'lucide-react';\nimport { useEffect, useMemo, useState } from 'react';\nimport type {\n HTMLAttributes,\n ReactNode,\n TrackHTMLAttributes,\n VideoHTMLAttributes,\n} from 'react';\n\nimport { cn } from '../lib/utils';\nimport type {\n ButtonComponent,\n CardComponent,\n SkeletonComponent,\n UIComponent,\n} from '../types/component-types';\n\nimport { getMediaExtension, isSupportedMediaSource } from './media-utils';\n\nexport const SUPPORTED_VIDEO_EXTENSIONS = [\n 'mp4',\n 'webm',\n 'ogg',\n 'ogv',\n 'mov',\n 'm4v',\n] as const;\n\nexport const SUPPORTED_VIDEO_MIME_TYPES = [\n 'video/mp4',\n 'video/webm',\n 'video/ogg',\n 'video/quicktime',\n 'video/x-m4v',\n] as const;\n\nexport interface VideoReaderUIComponents {\n Card?: CardComponent;\n CardContent?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n Button?: ButtonComponent;\n Skeleton?: SkeletonComponent;\n}\n\nexport interface VideoReaderTrack\n extends Omit<TrackHTMLAttributes<HTMLTrackElement>, 'children'> {\n src: string;\n}\n\nexport interface VideoReaderProps\n extends Omit<\n VideoHTMLAttributes<HTMLVideoElement>,\n 'children' | 'className' | 'onError' | 'onLoadedData' | 'src'\n > {\n src: string;\n fileName?: string;\n mimeType?: string;\n title?: string;\n components?: VideoReaderUIComponents;\n tracks?: VideoReaderTrack[];\n loading?: boolean;\n error?: Error | string | null;\n className?: string;\n containerClassName?: string;\n videoClassName?: string;\n toolbarClassName?: string;\n loadingText?: string;\n errorText?: string;\n unsupportedText?: string;\n showToolbar?: boolean;\n showOpenInNewTab?: boolean;\n allowUnsupportedFormat?: boolean;\n supportedExtensions?: readonly string[];\n supportedMimeTypes?: readonly string[];\n onLoadedData?: () => void;\n onError?: (error: Error) => void;\n}\n\nconst normalizeError = (error: Error | string | null | undefined) => {\n if (!error) return null;\n return error instanceof Error ? error : new Error(error);\n};\n\nexport function VideoReader({\n src,\n fileName,\n mimeType,\n title,\n components,\n tracks,\n loading = false,\n error,\n className,\n containerClassName,\n videoClassName,\n toolbarClassName,\n loadingText = '正在加载视频...',\n errorText = '视频加载失败',\n unsupportedText = '暂不支持该视频格式',\n showToolbar = true,\n showOpenInNewTab = true,\n allowUnsupportedFormat = false,\n supportedExtensions = SUPPORTED_VIDEO_EXTENSIONS,\n supportedMimeTypes = SUPPORTED_VIDEO_MIME_TYPES,\n controls = true,\n preload = 'metadata',\n playsInline = true,\n onLoadedData,\n onError,\n ...videoProps\n}: VideoReaderProps) {\n const { Card, CardContent, Button, Skeleton } = components || {};\n const [isVideoLoading, setIsVideoLoading] = useState(true);\n const [videoError, setVideoError] = useState<Error | null>(null);\n\n const displayedError = normalizeError(error) || videoError;\n const isSupported =\n allowUnsupportedFormat ||\n isSupportedMediaSource({\n src,\n fileName,\n mimeType,\n supportedExtensions,\n supportedMimeTypes,\n });\n const isLoading = loading || (isVideoLoading && !displayedError);\n\n const formatLabel = useMemo(() => {\n const extension = getMediaExtension(src, fileName);\n return extension ? extension.toUpperCase() : mimeType || '视频';\n }, [fileName, mimeType, src]);\n\n useEffect(() => {\n setIsVideoLoading(true);\n setVideoError(null);\n }, [src]);\n\n const renderButton = (\n label: string,\n icon: ReactNode,\n onClick: () => void\n ) => {\n const buttonClassName =\n 'inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground';\n\n if (Button) {\n return (\n <Button\n aria-label={label}\n className=\"h-8 w-8\"\n onClick={onClick}\n size=\"icon\"\n title={label}\n type=\"button\"\n variant=\"outline\"\n >\n {icon}\n </Button>\n );\n }\n\n return (\n <button\n aria-label={label}\n className={buttonClassName}\n onClick={onClick}\n title={label}\n type=\"button\"\n >\n {icon}\n </button>\n );\n };\n\n const renderLoading = () => (\n <div className=\"w-full space-y-3 p-4\" role=\"status\" aria-live=\"polite\">\n {Skeleton ? (\n <>\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"aspect-video w-full\" />\n </>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n )}\n </div>\n );\n\n const renderError = (message?: string) => (\n <div className=\"p-4 text-center text-sm text-destructive\" role=\"alert\">\n <p className=\"font-medium\">{message || errorText}</p>\n {displayedError?.message ? (\n <p className=\"mt-1 opacity-80\">{displayedError.message}</p>\n ) : null}\n </div>\n );\n\n const body = (\n <div\n className={cn(\n 'flex h-full min-h-[360px] flex-col overflow-hidden rounded-md border bg-background',\n containerClassName\n )}\n >\n {showToolbar ? (\n <div\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2',\n toolbarClassName\n )}\n >\n <span className=\"text-xs font-medium text-muted-foreground\">\n {title || formatLabel}\n </span>\n {showOpenInNewTab\n ? renderButton('新窗口打开', <ExternalLinkIcon className=\"h-4 w-4\" />, () =>\n window.open(src, '_blank', 'noreferrer')\n )\n : null}\n </div>\n ) : null}\n <div className=\"relative flex min-h-0 flex-1 items-center justify-center bg-muted/20 p-4\">\n {!isSupported\n ? renderError(unsupportedText)\n : displayedError\n ? renderError()\n : null}\n {isSupported && !displayedError ? (\n <>\n {isLoading ? renderLoading() : null}\n <video\n {...videoProps}\n key={src}\n className={cn(\n 'max-h-full w-full max-w-full bg-black',\n isLoading ? 'invisible absolute' : 'visible',\n videoClassName\n )}\n controls={controls}\n onError={() => {\n const nextError = new Error(errorText);\n setIsVideoLoading(false);\n setVideoError(nextError);\n onError?.(nextError);\n }}\n onLoadedData={() => {\n setIsVideoLoading(false);\n setVideoError(null);\n onLoadedData?.();\n }}\n playsInline={playsInline}\n preload={preload}\n title={title}\n >\n <source src={src} type={mimeType} />\n {tracks?.map((track) => (\n <track key={`${track.src}-${track.kind || ''}`} {...track} />\n ))}\n </video>\n </>\n ) : null}\n </div>\n </div>\n );\n\n if (Card) {\n return (\n <Card className={className}>\n {CardContent ? <CardContent>{body}</CardContent> : body}\n </Card>\n );\n }\n\n return <div className={className}>{body}</div>;\n}\n"],"names":["SUPPORTED_VIDEO_EXTENSIONS","SUPPORTED_VIDEO_MIME_TYPES","normalizeError","error","VideoReader","src","fileName","mimeType","title","components","tracks","loading","className","containerClassName","videoClassName","toolbarClassName","loadingText","errorText","unsupportedText","showToolbar","showOpenInNewTab","allowUnsupportedFormat","supportedExtensions","supportedMimeTypes","controls","preload","playsInline","onLoadedData","onError","videoProps","Card","CardContent","Button","Skeleton","isVideoLoading","setIsVideoLoading","useState","videoError","setVideoError","displayedError","isSupported","isSupportedMediaSource","isLoading","formatLabel","useMemo","extension","getMediaExtension","useEffect","renderButton","label","icon","onClick","buttonClassName","jsx","renderLoading","jsxs","Fragment","renderError","message","body","cn","ExternalLinkIcon","createElement","nextError","track"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-reader.d.ts","sourceRoot":"","sources":["../src/video-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { j as e } from "./jsx-runtime-B4hRZ52C.js";
|
|
2
|
+
import { useState as y, useMemo as K, useEffect as Q, createElement as T } from "react";
|
|
3
|
+
import { ExternalLink as W } from "lucide-react";
|
|
4
|
+
import { c as x } from "./utils-B6yFEsav.js";
|
|
5
|
+
import { i as Z, g as ee } from "./media-utils-5UPuocc1.js";
|
|
6
|
+
const te = [
|
|
7
|
+
"mp4",
|
|
8
|
+
"webm",
|
|
9
|
+
"ogg",
|
|
10
|
+
"ogv",
|
|
11
|
+
"mov",
|
|
12
|
+
"m4v"
|
|
13
|
+
], se = [
|
|
14
|
+
"video/mp4",
|
|
15
|
+
"video/webm",
|
|
16
|
+
"video/ogg",
|
|
17
|
+
"video/quicktime",
|
|
18
|
+
"video/x-m4v"
|
|
19
|
+
], ne = (s) => s ? s instanceof Error ? s : new Error(s) : null;
|
|
20
|
+
function de({
|
|
21
|
+
src: s,
|
|
22
|
+
fileName: o,
|
|
23
|
+
mimeType: r,
|
|
24
|
+
title: f,
|
|
25
|
+
components: V,
|
|
26
|
+
tracks: i,
|
|
27
|
+
loading: _ = !1,
|
|
28
|
+
error: I,
|
|
29
|
+
className: p,
|
|
30
|
+
containerClassName: M,
|
|
31
|
+
videoClassName: O,
|
|
32
|
+
toolbarClassName: P,
|
|
33
|
+
loadingText: C = "正在加载视频...",
|
|
34
|
+
errorText: h = "视频加载失败",
|
|
35
|
+
unsupportedText: R = "暂不支持该视频格式",
|
|
36
|
+
showToolbar: k = !0,
|
|
37
|
+
showOpenInNewTab: L = !0,
|
|
38
|
+
allowUnsupportedFormat: U = !1,
|
|
39
|
+
supportedExtensions: z = te,
|
|
40
|
+
supportedMimeTypes: B = se,
|
|
41
|
+
controls: F = !0,
|
|
42
|
+
preload: $ = "metadata",
|
|
43
|
+
playsInline: q = !0,
|
|
44
|
+
onLoadedData: l,
|
|
45
|
+
onError: a,
|
|
46
|
+
...D
|
|
47
|
+
}) {
|
|
48
|
+
const { Card: g, CardContent: v, Button: j, Skeleton: d } = V || {}, [X, u] = y(!0), [Y, c] = y(null), n = ne(I) || Y, b = U || Z({
|
|
49
|
+
src: s,
|
|
50
|
+
fileName: o,
|
|
51
|
+
mimeType: r,
|
|
52
|
+
supportedExtensions: z,
|
|
53
|
+
supportedMimeTypes: B
|
|
54
|
+
}), w = _ || X && !n, A = K(() => {
|
|
55
|
+
const t = ee(s, o);
|
|
56
|
+
return t ? t.toUpperCase() : r || "视频";
|
|
57
|
+
}, [o, r, s]);
|
|
58
|
+
Q(() => {
|
|
59
|
+
u(!0), c(null);
|
|
60
|
+
}, [s]);
|
|
61
|
+
const G = (t, N, S) => {
|
|
62
|
+
const J = "inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground";
|
|
63
|
+
return j ? /* @__PURE__ */ e.jsx(
|
|
64
|
+
j,
|
|
65
|
+
{
|
|
66
|
+
"aria-label": t,
|
|
67
|
+
className: "h-8 w-8",
|
|
68
|
+
onClick: S,
|
|
69
|
+
size: "icon",
|
|
70
|
+
title: t,
|
|
71
|
+
type: "button",
|
|
72
|
+
variant: "outline",
|
|
73
|
+
children: N
|
|
74
|
+
}
|
|
75
|
+
) : /* @__PURE__ */ e.jsx(
|
|
76
|
+
"button",
|
|
77
|
+
{
|
|
78
|
+
"aria-label": t,
|
|
79
|
+
className: J,
|
|
80
|
+
onClick: S,
|
|
81
|
+
title: t,
|
|
82
|
+
type: "button",
|
|
83
|
+
children: N
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}, H = () => /* @__PURE__ */ e.jsx("div", { className: "w-full space-y-3 p-4", role: "status", "aria-live": "polite", children: d ? /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
|
|
87
|
+
/* @__PURE__ */ e.jsx(d, { className: "h-4 w-32" }),
|
|
88
|
+
/* @__PURE__ */ e.jsx(d, { className: "aspect-video w-full" })
|
|
89
|
+
] }) : /* @__PURE__ */ e.jsx("p", { className: "text-sm text-muted-foreground", children: C }) }), E = (t) => /* @__PURE__ */ e.jsxs("div", { className: "p-4 text-center text-sm text-destructive", role: "alert", children: [
|
|
90
|
+
/* @__PURE__ */ e.jsx("p", { className: "font-medium", children: t || h }),
|
|
91
|
+
n != null && n.message ? /* @__PURE__ */ e.jsx("p", { className: "mt-1 opacity-80", children: n.message }) : null
|
|
92
|
+
] }), m = /* @__PURE__ */ e.jsxs(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
className: x(
|
|
96
|
+
"flex h-full min-h-[360px] flex-col overflow-hidden rounded-md border bg-background",
|
|
97
|
+
M
|
|
98
|
+
),
|
|
99
|
+
children: [
|
|
100
|
+
k ? /* @__PURE__ */ e.jsxs(
|
|
101
|
+
"div",
|
|
102
|
+
{
|
|
103
|
+
className: x(
|
|
104
|
+
"flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2",
|
|
105
|
+
P
|
|
106
|
+
),
|
|
107
|
+
children: [
|
|
108
|
+
/* @__PURE__ */ e.jsx("span", { className: "text-xs font-medium text-muted-foreground", children: f || A }),
|
|
109
|
+
L ? G(
|
|
110
|
+
"新窗口打开",
|
|
111
|
+
/* @__PURE__ */ e.jsx(W, { className: "h-4 w-4" }),
|
|
112
|
+
() => window.open(s, "_blank", "noreferrer")
|
|
113
|
+
) : null
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
) : null,
|
|
117
|
+
/* @__PURE__ */ e.jsxs("div", { className: "relative flex min-h-0 flex-1 items-center justify-center bg-muted/20 p-4", children: [
|
|
118
|
+
b ? n ? E() : null : E(R),
|
|
119
|
+
b && !n ? /* @__PURE__ */ e.jsxs(e.Fragment, { children: [
|
|
120
|
+
w ? H() : null,
|
|
121
|
+
/* @__PURE__ */ T(
|
|
122
|
+
"video",
|
|
123
|
+
{
|
|
124
|
+
...D,
|
|
125
|
+
key: s,
|
|
126
|
+
className: x(
|
|
127
|
+
"max-h-full w-full max-w-full bg-black",
|
|
128
|
+
w ? "invisible absolute" : "visible",
|
|
129
|
+
O
|
|
130
|
+
),
|
|
131
|
+
controls: F,
|
|
132
|
+
onError: () => {
|
|
133
|
+
const t = new Error(h);
|
|
134
|
+
u(!1), c(t), a == null || a(t);
|
|
135
|
+
},
|
|
136
|
+
onLoadedData: () => {
|
|
137
|
+
u(!1), c(null), l == null || l();
|
|
138
|
+
},
|
|
139
|
+
playsInline: q,
|
|
140
|
+
preload: $,
|
|
141
|
+
title: f
|
|
142
|
+
},
|
|
143
|
+
/* @__PURE__ */ e.jsx("source", { src: s, type: r }),
|
|
144
|
+
i == null ? void 0 : i.map((t) => /* @__PURE__ */ e.jsx("track", { ...t }, `${t.src}-${t.kind || ""}`))
|
|
145
|
+
)
|
|
146
|
+
] }) : null
|
|
147
|
+
] })
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
return g ? /* @__PURE__ */ e.jsx(g, { className: p, children: v ? /* @__PURE__ */ e.jsx(v, { children: m }) : m }) : /* @__PURE__ */ e.jsx("div", { className: p, children: m });
|
|
152
|
+
}
|
|
153
|
+
export {
|
|
154
|
+
te as SUPPORTED_VIDEO_EXTENSIONS,
|
|
155
|
+
se as SUPPORTED_VIDEO_MIME_TYPES,
|
|
156
|
+
de as VideoReader
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=video-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-reader.js","sources":["../src/components/VideoReader.tsx"],"sourcesContent":["import { ExternalLink as ExternalLinkIcon } from 'lucide-react';\nimport { useEffect, useMemo, useState } from 'react';\nimport type {\n HTMLAttributes,\n ReactNode,\n TrackHTMLAttributes,\n VideoHTMLAttributes,\n} from 'react';\n\nimport { cn } from '../lib/utils';\nimport type {\n ButtonComponent,\n CardComponent,\n SkeletonComponent,\n UIComponent,\n} from '../types/component-types';\n\nimport { getMediaExtension, isSupportedMediaSource } from './media-utils';\n\nexport const SUPPORTED_VIDEO_EXTENSIONS = [\n 'mp4',\n 'webm',\n 'ogg',\n 'ogv',\n 'mov',\n 'm4v',\n] as const;\n\nexport const SUPPORTED_VIDEO_MIME_TYPES = [\n 'video/mp4',\n 'video/webm',\n 'video/ogg',\n 'video/quicktime',\n 'video/x-m4v',\n] as const;\n\nexport interface VideoReaderUIComponents {\n Card?: CardComponent;\n CardContent?: UIComponent<HTMLAttributes<HTMLDivElement>>;\n Button?: ButtonComponent;\n Skeleton?: SkeletonComponent;\n}\n\nexport interface VideoReaderTrack\n extends Omit<TrackHTMLAttributes<HTMLTrackElement>, 'children'> {\n src: string;\n}\n\nexport interface VideoReaderProps\n extends Omit<\n VideoHTMLAttributes<HTMLVideoElement>,\n 'children' | 'className' | 'onError' | 'onLoadedData' | 'src'\n > {\n src: string;\n fileName?: string;\n mimeType?: string;\n title?: string;\n components?: VideoReaderUIComponents;\n tracks?: VideoReaderTrack[];\n loading?: boolean;\n error?: Error | string | null;\n className?: string;\n containerClassName?: string;\n videoClassName?: string;\n toolbarClassName?: string;\n loadingText?: string;\n errorText?: string;\n unsupportedText?: string;\n showToolbar?: boolean;\n showOpenInNewTab?: boolean;\n allowUnsupportedFormat?: boolean;\n supportedExtensions?: readonly string[];\n supportedMimeTypes?: readonly string[];\n onLoadedData?: () => void;\n onError?: (error: Error) => void;\n}\n\nconst normalizeError = (error: Error | string | null | undefined) => {\n if (!error) return null;\n return error instanceof Error ? error : new Error(error);\n};\n\nexport function VideoReader({\n src,\n fileName,\n mimeType,\n title,\n components,\n tracks,\n loading = false,\n error,\n className,\n containerClassName,\n videoClassName,\n toolbarClassName,\n loadingText = '正在加载视频...',\n errorText = '视频加载失败',\n unsupportedText = '暂不支持该视频格式',\n showToolbar = true,\n showOpenInNewTab = true,\n allowUnsupportedFormat = false,\n supportedExtensions = SUPPORTED_VIDEO_EXTENSIONS,\n supportedMimeTypes = SUPPORTED_VIDEO_MIME_TYPES,\n controls = true,\n preload = 'metadata',\n playsInline = true,\n onLoadedData,\n onError,\n ...videoProps\n}: VideoReaderProps) {\n const { Card, CardContent, Button, Skeleton } = components || {};\n const [isVideoLoading, setIsVideoLoading] = useState(true);\n const [videoError, setVideoError] = useState<Error | null>(null);\n\n const displayedError = normalizeError(error) || videoError;\n const isSupported =\n allowUnsupportedFormat ||\n isSupportedMediaSource({\n src,\n fileName,\n mimeType,\n supportedExtensions,\n supportedMimeTypes,\n });\n const isLoading = loading || (isVideoLoading && !displayedError);\n\n const formatLabel = useMemo(() => {\n const extension = getMediaExtension(src, fileName);\n return extension ? extension.toUpperCase() : mimeType || '视频';\n }, [fileName, mimeType, src]);\n\n useEffect(() => {\n setIsVideoLoading(true);\n setVideoError(null);\n }, [src]);\n\n const renderButton = (\n label: string,\n icon: ReactNode,\n onClick: () => void\n ) => {\n const buttonClassName =\n 'inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground';\n\n if (Button) {\n return (\n <Button\n aria-label={label}\n className=\"h-8 w-8\"\n onClick={onClick}\n size=\"icon\"\n title={label}\n type=\"button\"\n variant=\"outline\"\n >\n {icon}\n </Button>\n );\n }\n\n return (\n <button\n aria-label={label}\n className={buttonClassName}\n onClick={onClick}\n title={label}\n type=\"button\"\n >\n {icon}\n </button>\n );\n };\n\n const renderLoading = () => (\n <div className=\"w-full space-y-3 p-4\" role=\"status\" aria-live=\"polite\">\n {Skeleton ? (\n <>\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"aspect-video w-full\" />\n </>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{loadingText}</p>\n )}\n </div>\n );\n\n const renderError = (message?: string) => (\n <div className=\"p-4 text-center text-sm text-destructive\" role=\"alert\">\n <p className=\"font-medium\">{message || errorText}</p>\n {displayedError?.message ? (\n <p className=\"mt-1 opacity-80\">{displayedError.message}</p>\n ) : null}\n </div>\n );\n\n const body = (\n <div\n className={cn(\n 'flex h-full min-h-[360px] flex-col overflow-hidden rounded-md border bg-background',\n containerClassName\n )}\n >\n {showToolbar ? (\n <div\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2',\n toolbarClassName\n )}\n >\n <span className=\"text-xs font-medium text-muted-foreground\">\n {title || formatLabel}\n </span>\n {showOpenInNewTab\n ? renderButton('新窗口打开', <ExternalLinkIcon className=\"h-4 w-4\" />, () =>\n window.open(src, '_blank', 'noreferrer')\n )\n : null}\n </div>\n ) : null}\n <div className=\"relative flex min-h-0 flex-1 items-center justify-center bg-muted/20 p-4\">\n {!isSupported\n ? renderError(unsupportedText)\n : displayedError\n ? renderError()\n : null}\n {isSupported && !displayedError ? (\n <>\n {isLoading ? renderLoading() : null}\n <video\n {...videoProps}\n key={src}\n className={cn(\n 'max-h-full w-full max-w-full bg-black',\n isLoading ? 'invisible absolute' : 'visible',\n videoClassName\n )}\n controls={controls}\n onError={() => {\n const nextError = new Error(errorText);\n setIsVideoLoading(false);\n setVideoError(nextError);\n onError?.(nextError);\n }}\n onLoadedData={() => {\n setIsVideoLoading(false);\n setVideoError(null);\n onLoadedData?.();\n }}\n playsInline={playsInline}\n preload={preload}\n title={title}\n >\n <source src={src} type={mimeType} />\n {tracks?.map((track) => (\n <track key={`${track.src}-${track.kind || ''}`} {...track} />\n ))}\n </video>\n </>\n ) : null}\n </div>\n </div>\n );\n\n if (Card) {\n return (\n <Card className={className}>\n {CardContent ? <CardContent>{body}</CardContent> : body}\n </Card>\n );\n }\n\n return <div className={className}>{body}</div>;\n}\n"],"names":["SUPPORTED_VIDEO_EXTENSIONS","SUPPORTED_VIDEO_MIME_TYPES","normalizeError","error","VideoReader","src","fileName","mimeType","title","components","tracks","loading","className","containerClassName","videoClassName","toolbarClassName","loadingText","errorText","unsupportedText","showToolbar","showOpenInNewTab","allowUnsupportedFormat","supportedExtensions","supportedMimeTypes","controls","preload","playsInline","onLoadedData","onError","videoProps","Card","CardContent","Button","Skeleton","isVideoLoading","setIsVideoLoading","useState","videoError","setVideoError","displayedError","isSupported","isSupportedMediaSource","isLoading","formatLabel","useMemo","extension","getMediaExtension","useEffect","renderButton","label","icon","onClick","buttonClassName","jsx","renderLoading","jsxs","Fragment","renderError","message","body","cn","ExternalLinkIcon","createElement","nextError","track"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@turinhub/atomix-common-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Common UI components for TurinHub projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -17,6 +17,16 @@
|
|
|
17
17
|
"default": "./dist/index.cjs"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
+
"./auth": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/auth.d.ts",
|
|
23
|
+
"default": "./dist/auth.js"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./dist/auth.d.ts",
|
|
27
|
+
"default": "./dist/auth.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
20
30
|
"./data-table": {
|
|
21
31
|
"import": {
|
|
22
32
|
"types": "./dist/data-table.d.ts",
|
|
@@ -87,6 +97,26 @@
|
|
|
87
97
|
"default": "./dist/file-upload.cjs"
|
|
88
98
|
}
|
|
89
99
|
},
|
|
100
|
+
"./image-reader": {
|
|
101
|
+
"import": {
|
|
102
|
+
"types": "./dist/image-reader.d.ts",
|
|
103
|
+
"default": "./dist/image-reader.js"
|
|
104
|
+
},
|
|
105
|
+
"require": {
|
|
106
|
+
"types": "./dist/image-reader.d.ts",
|
|
107
|
+
"default": "./dist/image-reader.cjs"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"./video-reader": {
|
|
111
|
+
"import": {
|
|
112
|
+
"types": "./dist/video-reader.d.ts",
|
|
113
|
+
"default": "./dist/video-reader.js"
|
|
114
|
+
},
|
|
115
|
+
"require": {
|
|
116
|
+
"types": "./dist/video-reader.d.ts",
|
|
117
|
+
"default": "./dist/video-reader.cjs"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
90
120
|
"./simple-pdf-reader": {
|
|
91
121
|
"import": {
|
|
92
122
|
"types": "./dist/simple-pdf-reader.d.ts",
|