integrate-file-viewer
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseIntegrate CogniteFileViewer
集成CogniteFileViewer
Add to this Dune app to preview CDF files (PDF, image, text).
CogniteFileViewer将添加到Dune应用中,以预览CDF文件(PDF、图片、文本)。
CogniteFileViewerYour job
任务要求
Complete these steps in order. Read each file before modifying it.
按顺序完成以下步骤,修改文件前请先阅读相关内容。
Step 1 — Understand the app
步骤1 — 了解应用
Read these files before touching anything:
- — detect package manager (
package.jsonfield or lock file) and existing depspackageManager - — understand current Vite setup
vite.config.ts - The component where the viewer should be added
修改任何内容前,请先阅读以下文件:
- — 检测包管理器(
package.json字段或锁文件)及已存在的依赖packageManager - — 了解当前Vite配置
vite.config.ts - 需要添加文件查看器的组件
Step 2 — Install dependencies
步骤2 — 安装依赖
- pnpm →
pnpm add "github:cognitedata/dune-industrial-components#semver:*" react-pdf - npm →
npm install "github:cognitedata/dune-industrial-components#semver:*" react-pdf - yarn →
yarn add "github:cognitedata/dune-industrial-components#semver:*" react-pdf
pdfjs-distreact-pdf- pnpm →
pnpm add "github:cognitedata/dune-industrial-components#semver:*" react-pdf - npm →
npm install "github:cognitedata/dune-industrial-components#semver:*" react-pdf - yarn →
yarn add "github:cognitedata/dune-industrial-components#semver:*" react-pdf
pdfjs-distreact-pdfStep 3 — Configure the PDF.js worker
步骤3 — 配置PDF.js Worker
The consumer app must configure the PDF.js worker. This ensures the worker version matches the version shipped with your install.
pdfjs-distreact-pdfAdd this setup in the same file where is used (module execution order matters):
CogniteFileViewertsx
import { pdfjs } from 'react-pdf';
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.mjs',
import.meta.url,
).toString();pnpm users: pnpm's strict linking may prevent the browser from resolving. Either addpdfjs-distas a direct dependency (pdfjs-dist), or addpnpm add pdfjs-disttopublic-hoist-pattern[]=pdfjs-dist..npmrc
消费应用必须配置PDF.js Worker,确保Worker版本与附带的版本匹配。
react-pdfpdfjs-dist在使用的同一文件中添加以下配置(模块执行顺序很重要):
CogniteFileViewertsx
import { pdfjs } from 'react-pdf';
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.mjs',
import.meta.url,
).toString();pnpm用户注意: pnpm的严格链接机制可能导致浏览器无法解析。可选择将pdfjs-dist添加为直接依赖(pdfjs-dist),或在pnpm add pdfjs-dist中添加.npmrc。public-hoist-pattern[]=pdfjs-dist
Step 4 — Configure Vite
步骤4 — 配置Vite
Add to to prevent Vite from pre-bundling pdfjs-dist (which breaks the worker):
optimizeDeps.exclude: ['pdfjs-dist']vite.config.tsts
export default defineConfig({
// ... existing config ...
optimizeDeps: {
exclude: ['pdfjs-dist'],
},
});在中添加,防止Vite预打包pdfjs-dist(这会导致Worker失效):
vite.config.tsoptimizeDeps.exclude: ['pdfjs-dist']ts
export default defineConfig({
// ... 现有配置 ...
optimizeDeps: {
exclude: ['pdfjs-dist'],
},
});Step 5 — Use the component
步骤5 — 使用组件
Import and render wherever a file preview is needed.
CogniteFileViewertsx
import { CogniteFileViewer } from '@cognite/dune-industrial-components/file-viewer';Get the from the hook (already available in every Dune app):
sdkuseDune()tsx
import { useDune } from '@cognite/dune';
const { sdk } = useDune();在需要文件预览的地方导入并渲染。
CogniteFileViewertsx
import { CogniteFileViewer } from '@cognite/dune-industrial-components/file-viewer';从钩子中获取(每个Dune应用中都已内置该钩子):
useDune()sdktsx
import { useDune } from '@cognite/dune';
const { sdk } = useDune();Supported file types
支持的文件类型
| Type | Formats |
|---|---|
| |
| Office documents | Word, PowerPoint, Excel, ODS, ODP, ODT, RTF, TSV — converted to PDF via the CDF Document Preview API, then rendered identically to PDF |
| Image | JPEG, PNG, WebP, SVG, TIFF — zoom, pan, rotation |
| Text | |
| Other | Falls back to |
| 类型 | 格式 |
|---|---|
| |
| 办公文档 | Word、PowerPoint、Excel、ODS、ODP、ODT、RTF、TSV — 通过CDF文档预览API转换为PDF,然后以PDF格式渲染 |
| 图片 | JPEG、PNG、WebP、SVG、TIFF — 支持缩放、平移、旋转 |
| 文本 | |
| 其他 | 回退到 |
Minimal usage
最简使用示例
This is all you need — zoom, pan, and touch gestures are handled internally:
tsx
<CogniteFileViewer
source={{ type: 'internalId', id: file.id }}
client={sdk}
style={{ width: '100%', height: '600px' }}
/>The component needs a defined height. If the parent has no explicit height, the viewer will collapse to zero. Always set aviaheight,style, or the parent container.className
以下代码即可实现基础功能——缩放、平移及触摸手势均已内置:
tsx
<CogniteFileViewer
source={{ type: 'internalId', id: file.id }}
client={sdk}
style={{ width: '100%', height: '600px' }}
/>组件需要设置明确高度。如果父容器没有显式高度,查看器会收缩为零高度。请始终通过、style或父容器设置className。height
File source
文件源
Pass any of three source types:
tsx
// By instance ID (data-modelled file — enables annotations)
<CogniteFileViewer
source={{ type: 'instanceId', space: 'my-space', externalId: 'my-file' }}
client={sdk}
/>
// By CDF internal ID
<CogniteFileViewer
source={{ type: 'internalId', id: 12345 }}
client={sdk}
/>
// By direct URL
<CogniteFileViewer
source={{ type: 'url', url: 'https://...', mimeType: 'application/pdf' }}
/>Prefer when available — it's the only source type that enables the diagram annotation overlay. When listing files via , check first:
instanceIdsdk.files.list()file.instanceIdtsx
source={
file.instanceId
? { type: 'instanceId', space: file.instanceId.space, externalId: file.instanceId.externalId }
: { type: 'internalId', id: file.id }
}支持三种文件源类型:
tsx
// 通过实例ID(数据建模文件——支持批注)
<CogniteFileViewer
source={{ type: 'instanceId', space: 'my-space', externalId: 'my-file' }}
client={sdk}
/>
// 通过CDF内部ID
<CogniteFileViewer
source={{ type: 'internalId', id: 12345 }}
client={sdk}
/>
// 通过直接URL
<CogniteFileViewer
source={{ type: 'url', url: 'https://...', mimeType: 'application/pdf' }}
/>优先使用(如果可用)——这是唯一支持图表批注叠加的文件源类型。通过列出文件时,请优先检查:
instanceIdsdk.files.list()file.instanceIdtsx
source={
file.instanceId
? { type: 'instanceId', space: file.instanceId.space, externalId: file.instanceId.externalId }
: { type: 'internalId', id: file.id }
}Full props reference
完整属性参考
tsx
<CogniteFileViewer
// Required
source={source}
client={sdk} // required for instanceId and internalId sources
// PDF pagination
page={page} // controlled current page (1-indexed)
onPageChange={setPage}
onDocumentLoad={({ numPages }) => setNumPages(numPages)}
// Zoom & pan (works on PDF and images)
zoom={zoom} // 1 = 100%; Ctrl/Cmd+wheel, pinch-to-zoom, and middle-click drag built in
onZoomChange={setZoom}
minZoom={0.25} // default
maxZoom={5} // default
panOffset={pan} // controlled pan offset; resets on page change
onPanChange={setPan}
// Fit mode
fitMode="width" // 'width' fits to container width; 'page' fits entire page in container
// Rotation (PDFs and images)
rotation={rotation} // 0 | 90 | 180 | 270
// Diagram annotations (instanceId sources only)
showAnnotations={true} // default
onAnnotationClick={(annotation) => { /* annotation.linkedResource has space + externalId */ }}
onAnnotationHover={(annotation) => {}}
// Custom annotation tooltip (replaces native <title> tooltip)
renderAnnotationTooltip={(annotation, rect) => (
<div style={{
position: 'absolute',
left: rect.x + rect.width,
top: rect.y,
zIndex: 11,
}}>
{annotation.text}
</div>
)}
// Custom overlay (SVG paths, highlights, drawings — works on PDF and images)
renderOverlay={({ width, height, originalWidth, originalHeight, pageNumber, rotation }) => (
<svg
width={width}
height={height}
viewBox={`0 0 ${originalWidth} ${originalHeight}`}
preserveAspectRatio="none"
style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'all' }}
>
<path d="..." stroke="cyan" fill="none" />
</svg>
)}
// Custom renderers (all optional)
renderLoading={() => <MySpinner />}
renderError={(error) => <MyError message={error.message} />}
renderUnsupported={(mimeType) => <div>Cannot preview {mimeType}</div>}
// Layout
className="..."
style={{ width: '100%', height: '100%' }}
/>tsx
<CogniteFileViewer
// 必填项
source={source}
client={sdk} // 使用instanceId和internalId源时必填
// PDF分页
page={page} // 受控当前页码(从1开始)
onPageChange={setPage}
onDocumentLoad={({ numPages }) => setNumPages(numPages)}
// 缩放与平移(支持PDF和图片)
zoom={zoom} // 1 = 100%;内置Ctrl/Cmd+滚轮、捏合缩放、中键拖动功能
onZoomChange={setZoom}
minZoom={0.25} // 默认值
maxZoom={5} // 默认值
panOffset={pan} // 受控平移偏移量;切换页面时会重置
onPanChange={setPan}
// 适配模式
fitMode="width" // 'width' 适配容器宽度;'page' 使整个页面适配容器
// 旋转(支持PDF和图片)
rotation={rotation} // 0 | 90 | 180 | 270
// 图表批注(仅instanceId源支持)
showAnnotations={true} // 默认值
onAnnotationClick={(annotation) => { /* annotation.linkedResource包含space + externalId */ }}
onAnnotationHover={(annotation) => {}}
// 自定义批注提示框(替换原生<title>提示框)
renderAnnotationTooltip={(annotation, rect) => (
<div style={{
position: 'absolute',
left: rect.x + rect.width,
top: rect.y,
zIndex: 11,
}}>
{annotation.text}
</div>
)}
// 自定义叠加层(SVG路径、高亮、绘图——支持PDF和图片)
renderOverlay={({ width, height, originalWidth, originalHeight, pageNumber, rotation }) => (
<svg
width={width}
height={height}
viewBox={`0 0 ${originalWidth} ${originalHeight}`}
preserveAspectRatio="none"
style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'all' }}
>
<path d="..." stroke="cyan" fill="none" />
</svg>
)}
// 自定义渲染器(均为可选)
renderLoading={() => <MySpinner />}
renderError={(error) => <MyError message={error.message} />}
renderUnsupported={(mimeType) => <div>无法预览 {mimeType}</div>}
// 布局
className="..."
style={{ width: '100%', height: '100%' }}
/>Tips & tricks
技巧与提示
Reset page, zoom and rotation when the source changes.
The component does not reset these automatically when you switch files — do it yourself:
ts
const navigateToFile = (file: FileInfo) => {
setSelectedFile(file);
setPage(1);
setZoom(1);
setRotation(0);
};Gate pagination UI on .
only fires for PDFs. Don't render pagination controls until you know there are pages to paginate:
numPages > 0onDocumentLoadtsx
{numPages > 0 && (
<>
<button disabled={page <= 1} onClick={() => setPage(p => p - 1)}>‹</button>
<span>{page} / {numPages}</span>
<button disabled={page >= numPages} onClick={() => setPage(p => p + 1)}>›</button>
</>
)}Annotation click → navigate to linked file.
contains the and of the linked CDF instance. Match it against to navigate:
annotation.linkedResourcespaceexternalIdfile.instanceIdts
onAnnotationClick={(annotation) => {
if (!annotation.linkedResource) return;
const { space, externalId } = annotation.linkedResource;
const linked = files.find(
f => f.instanceId?.space === space && f.instanceId?.externalId === externalId
);
if (linked) navigateToFile(linked);
}}Touch support is built in. Two-finger pinch-to-zoom and two-finger drag-to-pan work on touch devices automatically. No configuration needed.
Pan is middle-click drag (when zoomed in) on desktop. Left-click remains free for annotation clicks and text selection.
Ctrl/Cmd + wheel zooms toward the cursor — also built in. Wire / if you want programmatic zoom buttons or to persist zoom state; otherwise it works fully uncontrolled.
zoomonZoomChangerenderOverlayoriginalWidthoriginalHeightviewBox切换文件源时重置页码、缩放和旋转。组件不会自动重置这些状态,请手动处理:
ts
const navigateToFile = (file: FileInfo) => {
setSelectedFile(file);
setPage(1);
setZoom(1);
setRotation(0);
};仅当时渲染分页UI。仅针对PDF触发。在确认有可分页的页面之前,请勿渲染分页控件:
numPages > 0onDocumentLoadtsx
{numPages > 0 && (
<>
<button disabled={page <= 1} onClick={() => setPage(p => p - 1)}>‹</button>
<span>{page} / {numPages}</span>
<button disabled={page >= numPages} onClick={() => setPage(p => p + 1)}>›</button>
</>
)}点击批注→跳转到关联文件。包含关联CDF实例的和。将其与匹配以实现跳转:
annotation.linkedResourcespaceexternalIdfile.instanceIdts
onAnnotationClick={(annotation) => {
if (!annotation.linkedResource) return;
const { space, externalId } = annotation.linkedResource;
const linked = files.find(
f => f.instanceId?.space === space && f.instanceId?.externalId === externalId
);
if (linked) navigateToFile(linked);
}}内置触摸支持。触摸设备上自动支持双指捏合缩放和双指拖动平移,无需额外配置。
桌面端平移为中键拖动(缩放后)。左键可用于点击批注和选择文本。
Ctrl/Cmd + 滚轮可向光标位置缩放——该功能已内置。如果需要程序化缩放按钮或保存缩放状态,请绑定/;否则可完全使用无控模式。
zoomonZoomChangerenderOverlayoriginalWidthoriginalHeightviewBoxCommon pitfalls
常见问题
| Problem | Cause | Fix |
|---|---|---|
| Worker not configured | Add the worker setup from Step 3 in the same file that uses |
| | Do not install |
| Annotations never show | | Use |
| Annotations show but are empty | File has no | Expected — only P&ID/diagram files synced to the data model have annotations |
| Viewer collapses to zero height | Parent has no explicit height | Set |
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 未配置Worker | 在使用 |
| 应用与 | 请勿单独安装 |
| 批注始终不显示 | | 使用 |
| 批注显示但为空 | CDF中该文件没有 | 属于正常情况——仅同步到数据模型的P&ID/图表文件才有批注 |
| 查看器收缩为零高度 | 父容器没有显式高度 | 通过 |