Skip to content

组件 API

本库导出三个预览组件:

组件场景渲染方式
FilePreviewModal全屏弹窗预览Portal 挂载到 document.body,带半透明遮罩
FilePreviewEmbed内联嵌入到任意 div 容器普通 DOM,填充父容器
FilePreviewContent底层组件,自定义包装由你决定包装方式

FilePreviewModalFilePreviewEmbed 都是基于底层 FilePreviewContent 的薄包装,核心功能(工具栏、缩放、旋转、导航、渲染器分发)完全一致。

FilePreviewModal

主要的文件预览模态框组件。

Props

files

  • 类型: PreviewFileInput[]
  • 必需: 是
  • 描述: 要预览的文件列表,支持三种输入格式:
    • File 对象(原生浏览器 File)
    • PreviewFileLink 对象(包含 name, url, type 等属性)
    • string(HTTP URL)
tsx
// 方式 1: URL 字符串
const files1 = ['https://example.com/image.jpg']

// 方式 2: 文件对象
const files2 = [
  {
    name: 'document.pdf',
    url: '/files/doc.pdf',
    type: 'application/pdf'
  }
]

// 方式 3: File 对象
const files3 = [new File(['content'], 'text.txt')]

// 方式 4: 混合使用
const files4 = [
  'https://example.com/image.jpg',
  { name: 'doc.pdf', url: '/doc.pdf', type: 'application/pdf' },
  fileObject
]

currentIndex

  • 类型: number
  • 必需: 是
  • 描述: 当前显示的文件索引(从 0 开始)
tsx
<FilePreviewModal currentIndex={0} ... />

isOpen

  • 类型: boolean
  • 必需: 是
  • 描述: 控制模态框的显示/隐藏状态
tsx
<FilePreviewModal isOpen={true} ... />

onClose

  • 类型: () => void
  • 必需: 是
  • 描述: 关闭模态框时的回调函数
tsx
<FilePreviewModal onClose={() => setIsOpen(false)} ... />

onNavigate

  • 类型: (index: number) => void
  • 必需: 否
  • 描述: 文件切换时的回调函数,参数为新的文件索引
tsx
<FilePreviewModal
  onNavigate={(index) => {
    console.log('切换到文件:', index)
    setCurrentIndex(index)
  }}
  ...
/>

customRenderers

  • 类型: CustomRenderer[]
  • 必需: 否
  • 描述: 自定义渲染器数组,用于扩展或覆盖默认的文件渲染逻辑

每个 CustomRenderer 对象包含:

  • test: (file: PreviewFile) => boolean - 文件匹配函数,返回 true 表示使用此渲染器
  • render: (file: PreviewFile) => React.ReactNode - 渲染函数,返回要显示的 React 组件

自定义渲染器会优先于内置渲染器执行。如果多个自定义渲染器匹配同一文件,将使用第一个匹配的渲染器。

tsx
import type { CustomRenderer } from '@eternalheart/react-file-preview'

const customRenderers: CustomRenderer[] = [
  {
    // 为 JSON 文件添加格式化显示
    test: (file) => file.name.endsWith('.json'),
    render: (file) => (
      <div className="p-8">
        <pre className="bg-gray-900 text-white p-4 rounded">
          <JsonViewer url={file.url} />
        </pre>
      </div>
    ),
  },
  {
    // 为特定 MIME 类型添加自定义渲染
    test: (file) => file.type === 'application/x-custom',
    render: (file) => <CustomFileViewer file={file} />,
  },
]

<FilePreviewModal
  customRenderers={customRenderers}
  ...
/>

locale

  • 类型: Locale'zh-CN' | 'en-US' | string
  • 必需: 否
  • 默认值: 'zh-CN'
  • 描述: 界面语言。内置支持 'zh-CN'(中文)和 'en-US'(英文),也可传入任意 locale 字符串并通过 messages prop 提供字典。
tsx
<FilePreviewModal locale="en-US" ... />

messages

  • 类型: Partial<Record<Locale, Partial<Messages>>>
  • 必需: 否
  • 描述: 用户自定义翻译字典。浅合并到对应语言的内置字典之上,可覆盖任何翻译值。详见 国际化指南
tsx
<FilePreviewModal
  locale="en-US"
  messages={{ 'en-US': { 'toolbar.zoom_in': 'ZOOM++' } }}
  ...
/>

完整示例

tsx
import { useState } from 'react'
import { FilePreviewModal } from '@eternalheart/react-file-preview'
import type { PreviewFileInput } from '@eternalheart/react-file-preview'
import '@eternalheart/react-file-preview/style.css'

function App() {
  const [isOpen, setIsOpen] = useState(false)
  const [currentIndex, setCurrentIndex] = useState(0)

  const files: PreviewFileInput[] = [
    'https://example.com/image.jpg',
    {
      name: 'document.pdf',
      url: 'https://example.com/document.pdf',
      type: 'application/pdf'
    }
  ]

  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        打开预览
      </button>

      <FilePreviewModal
        files={files}
        currentIndex={currentIndex}
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        onNavigate={setCurrentIndex}
      />
    </>
  )
}

FilePreviewEmbed

嵌入式文件预览组件。与 FilePreviewModal 共用同一套渲染逻辑,但以内联方式渲染到你指定的容器内,而不是全屏弹出。

适用场景:

  • 详情页中的文件预览面板
  • 左右分栏的文件浏览器
  • 仪表盘卡片里的文件预览
  • 任何不希望遮挡页面其他内容的场景

基础用法

tsx
import { FilePreviewEmbed } from '@eternalheart/react-file-preview'
import '@eternalheart/react-file-preview/style.css'

function Panel() {
  const files = [
    'https://example.com/image.jpg',
    { name: 'doc.pdf', url: '/doc.pdf', type: 'application/pdf' }
  ]

  return (
    // 嵌入预览默认填充父容器,父容器需要有明确的高度
    <div style={{ width: '100%', height: 520 }}>
      <FilePreviewEmbed files={files} />
    </div>
  )
}

Props

属性类型必需默认值描述
filesPreviewFileInput[]-要预览的文件列表,格式与 FilePreviewModal.files 一致
currentIndexnumber0当前显示的文件索引
onNavigate(index: number) => void-切换文件时的回调
customRenderersCustomRenderer[]-自定义渲染器数组
widthnumber | string'100%'容器宽度,填充父容器或显式指定
heightnumber | string'100%'容器高度
classNamestring-根节点额外 className
styleCSSProperties-根节点额外内联样式

尺寸说明

FilePreviewEmbed 默认使用 width: 100%; height: 100% 填充父容器,因此 父容器必须具有明确的高度(如 height: 520px 或通过 flex/grid 布局给定高度),否则组件会塌陷为 0 高度。

也可以直接在组件上显式指定尺寸:

tsx
<FilePreviewEmbed files={files} width={800} height={500} />

与 FilePreviewModal 的区别

特性FilePreviewModalFilePreviewEmbed
渲染位置Portal 到 document.body组件树内联
背景遮罩半透明黑色全屏遮罩
isOpen / onClose必填不存在,由父组件控制是否渲染
工具栏"关闭"按钮✅ 显示❌ 不显示
Esc 键关闭✅ 支持(全局监听)❌ 不支持
← → 键导航✅ 全局 window 监听✅ 仅容器 focus 时响应
body 滚动锁定✅ 打开时锁定❌ 不锁定
z-index9999(最高层)跟随组件树,由外层决定

完整示例

tsx
import { useState } from 'react'
import { FilePreviewEmbed } from '@eternalheart/react-file-preview'
import type { PreviewFileInput } from '@eternalheart/react-file-preview'
import '@eternalheart/react-file-preview/style.css'

function DetailPanel() {
  const [index, setIndex] = useState(0)

  const files: PreviewFileInput[] = [
    'https://example.com/image.jpg',
    { name: 'document.pdf', url: 'https://example.com/doc.pdf', type: 'application/pdf' },
    { name: 'data.xlsx', url: 'https://example.com/data.xlsx', type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' },
  ]

  return (
    <div className="grid grid-cols-2 gap-4">
      <aside>
        {files.map((f, i) => (
          <button key={i} onClick={() => setIndex(i)}>
            文件 {i + 1}
          </button>
        ))}
      </aside>

      <div style={{ height: 600 }}>
        <FilePreviewEmbed
          files={files}
          currentIndex={index}
          onNavigate={setIndex}
        />
      </div>
    </div>
  )
}

FilePreviewContent

底层文件预览组件。FilePreviewModalFilePreviewEmbed 都是基于它的薄包装。

当你需要构建完全自定义的外壳(例如自定义抽屉、浮层、Tab 切换容器等)时,可以直接使用它。

Props

属性类型必需默认值描述
filesPreviewFileInput[]-文件列表
currentIndexnumber-当前文件索引
onNavigate(index: number) => void-导航回调
customRenderersCustomRenderer[]-自定义渲染器
mode'modal' | 'embed''modal'运行模式,控制细节差异
onClose() => void-关闭回调,仅在 mode='modal' 时显示关闭按钮

mode 差异:

  • mode='modal': 显示工具栏"关闭"按钮、全局监听键盘(Esc 关闭、←/→ 导航)
  • mode='embed': 不显示"关闭"按钮、键盘事件绑定到组件根节点(需 focus),不监听 Esc

使用示例

tsx
import { FilePreviewContent } from '@eternalheart/react-file-preview'

function CustomDrawer({ files, currentIndex, onNavigate, onClose }) {
  return (
    <div className="my-drawer-wrapper" style={{ width: 720, height: '100vh' }}>
      <div className="my-drawer-header">
        <button onClick={onClose}>自定义关闭按钮</button>
      </div>
      <div style={{ flex: 1 }}>
        <FilePreviewContent
          mode="embed"
          files={files}
          currentIndex={currentIndex}
          onNavigate={onNavigate}
        />
      </div>
    </div>
  )
}

渲染机制

Portal 渲染

FilePreviewModal 使用 React Portal (createPortal) 将模态框渲染到 document.body,而不是在组件树的当前位置渲染。

优势:

  • 最高层级: 模态框的 z-index 设置为 9999,确保始终显示在页面最上层
  • 样式隔离: 不受父元素的 CSS 样式影响(如 overflow: hiddentransformfilter 等)
  • 定位准确: 模态框使用 fixed 定位相对于视口,不受父元素定位上下文影响
  • 无需配置: 开箱即用,无需担心层级和定位问题

示例:

tsx
// 即使在复杂的嵌套结构中使用也没问题
<div style={{ position: 'relative', overflow: 'hidden', zIndex: 100 }}>
  <div style={{ transform: 'scale(0.9)' }}>
    <FilePreviewModal
      isOpen={isOpen}
      onClose={() => setIsOpen(false)}
      files={files}
      currentIndex={0}
    />
  </div>
</div>

模态框会自动渲染到 document.body,完全不受上述样式影响。

功能特性

工具栏控制

根据文件类型,组件提供不同的工具栏控制:

图片预览

  • 缩放控制: 放大/缩小按钮(步进 10%,范围 0.01x - 10x),鼠标滚轮缩放(步进 0.05)
  • 旋转控制: 顺时针/逆时针旋转(每次 90°)
  • 拖拽移动: 缩放后可拖拽图片
  • 重置按钮: 恢复到原始状态

PDF 预览

  • 缩放控制: 放大/缩小按钮(范围 0.01x - 10x)
  • 页面导航: 上一页/下一页按钮
  • 页码显示: 当前页/总页数
  • 连续滚动: 支持滚动浏览所有页面

Office 文档

  • Word (DOCX): 通过 mammoth 库渲染为 HTML
  • Excel (XLSX): 多工作表切换,表格渲染
  • PowerPoint (PPT/PPTX): 平铺/幻灯片两种显示模式,16:9 宽高比

Outlook 邮件

  • MSG: 解析邮件头信息(发件人、收件人、主题、日期)
  • 邮件正文渲染
  • 附件列表展示

视频

  • 基于 Video.js 播放器
  • 支持播放控制、音量调节、进度条、全屏播放
  • 支持 MP4、WebM、OGG、MOV、AVI、MKV 等格式

音频

  • 自定义播放器界面(紫粉渐变主题)
  • 播放/暂停控制、进度条、音量调节
  • 快进/快退按钮(±10 秒)
  • 支持 MP3、WAV、OGG、M4A、AAC、FLAC 格式

Markdown

  • 实时渲染 Markdown 内容
  • 支持 GFM (GitHub Flavored Markdown)
  • 支持表格、代码块、任务列表等

代码文件

  • 语法高亮显示(40+ 种语言)
  • 自动检测语言类型
  • VS Code Dark+ 主题

通用功能

  • 文件导航: 上一个/下一个文件按钮
  • 下载功能: 下载当前文件
  • 关闭操作: 关闭按钮、ESC 键、点击背景

键盘快捷键

按键功能
Escape关闭预览
上一个文件
下一个文件

响应式设计

  • 桌面端: 完整工具栏和控制按钮
  • 移动端: 优化的触摸操作和简化的 UI
  • 平板: 介于两者之间的体验

触摸手势

  • 左滑 (>50px): 切换到下一个文件
  • 右滑 (>50px): 切换到上一个文件

动画效果

基于 Framer Motion 提供入场/退场动画,包括模态框、导航按钮和工具栏的过渡动画。

滚动锁定

预览打开时自动锁定页面滚动,关闭后恢复,并自动处理滚动条宽度补偿。

下一步

Released under the MIT License.