@file-viewer/core 3.3.0 → 3.3.1
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.en.md +14 -14
- package/README.md +14 -14
- package/README.zh-CN.md +4 -0
- package/dist/assets.d.ts +2 -2
- package/dist/assets.js +5 -1
- package/dist/browser/controller.d.ts +2 -1
- package/dist/browser/controller.js +109 -26
- package/dist/browser/source-feedback.d.ts +4 -0
- package/dist/browser/source-feedback.js +40 -0
- package/dist/browser/toolbar-focus.d.ts +1 -0
- package/dist/browser/toolbar-focus.js +3 -0
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +2 -2
- package/dist/config/options.d.ts +1 -1
- package/dist/contracts/types.d.ts +470 -1
- package/dist/contracts/xml.d.ts +11 -0
- package/dist/export.d.ts +10 -3
- package/dist/export.js +9 -2
- package/dist/exportDocument.d.ts +12 -1
- package/dist/exportDocument.js +31 -2
- package/dist/features/document/dom/anchors.d.ts +1 -1
- package/dist/features/document/dom/index.d.ts +5 -5
- package/dist/features/document/dom/index.js +3 -3
- package/dist/features/document/dom/providers.d.ts +1 -1
- package/dist/features/document/events.d.ts +2 -2
- package/dist/features/document/events.js +3 -3
- package/dist/features/document/fit.d.ts +1 -1
- package/dist/features/document/fit.js +1 -1
- package/dist/features/document/imageHandTool.d.ts +2 -0
- package/dist/features/document/imageHandTool.js +5 -1
- package/dist/features/document/model.d.ts +1 -1
- package/dist/features/document/search.d.ts +1 -1
- package/dist/features/document/search.js +2 -2
- package/dist/features/document/viewState.d.ts +1 -1
- package/dist/features/document/viewState.js +2 -2
- package/dist/features/document/zoom.d.ts +1 -1
- package/dist/features/document/zoom.js +3 -3
- package/dist/features/document/zoomGestures.d.ts +2 -2
- package/dist/features/document/zoomGestures.js +2 -0
- package/dist/features/printMask.d.ts +1 -1
- package/dist/features/printMaskDesigner.d.ts +6 -2
- package/dist/features/printMaskDesigner.js +11 -2
- package/dist/features/printMaskLoader.d.ts +7 -1
- package/dist/features/printMaskLoader.js +7 -1
- package/dist/features/watermark.d.ts +1 -1
- package/dist/headless.d.ts +29 -29
- package/dist/headless.js +21 -16
- package/dist/i18n/messages.d.ts +1 -1
- package/dist/i18n/messages.de.d.ts +5 -1
- package/dist/i18n/messages.de.js +4 -0
- package/dist/i18n/messages.ja.d.ts +6 -1
- package/dist/i18n/messages.ja.js +5 -0
- package/dist/i18n/messages.js +2 -2
- package/dist/index.d.ts +77 -77
- package/dist/index.js +39 -39
- package/dist/lifecycle/facade.d.ts +4 -4
- package/dist/lifecycle/facade.js +2 -2
- package/dist/lifecycle/operations.d.ts +5 -4
- package/dist/lifecycle/operations.js +10 -4
- package/dist/output/canvasImage.d.ts +2 -1
- package/dist/output/canvasImage.js +4 -1
- package/dist/output/export.d.ts +3 -1
- package/dist/output/export.js +12 -0
- package/dist/output/printLayout.d.ts +5 -0
- package/dist/output/printLayout.js +4 -0
- package/dist/output/snapshotDownload.d.ts +2 -1
- package/dist/output/snapshotDownload.js +3 -2
- package/dist/platform/assets.d.ts +8 -1
- package/dist/platform/assets.js +14 -0
- package/dist/platform/copiedAssets.d.ts +1 -0
- package/dist/platform/copiedAssets.js +2 -1
- package/dist/presentation/colorScheme.d.ts +2 -1
- package/dist/presentation/colorScheme.js +2 -1
- package/dist/presentation/state.d.ts +1 -1
- package/dist/presentation/state.js +3 -3
- package/dist/print-mask.d.ts +2 -2
- package/dist/print-mask.js +1 -1
- package/dist/registry/capabilities.d.ts +14 -1
- package/dist/registry/capabilities.js +15 -2
- package/dist/registry/formats.d.ts +1 -1
- package/dist/registry/formats.generated.d.ts +1 -0
- package/dist/registry/formats.generated.js +1 -0
- package/dist/registry/formats.js +2 -2
- package/dist/registry/registry.d.ts +15 -1
- package/dist/registry/registry.js +10 -2
- package/dist/renderers/image.d.ts +1 -1
- package/dist/renderers/image.js +7 -5
- package/dist/renderers/index.d.ts +1 -1
- package/dist/renderers/index.js +11 -5
- package/dist/rendering/dispatcher.d.ts +6 -1
- package/dist/rendering/dispatcher.js +3 -3
- package/dist/rendering/handler.d.ts +13 -3
- package/dist/rendering/handler.js +21 -9
- package/dist/security/svgResources.d.ts +10 -0
- package/dist/security/svgResources.js +12 -0
- package/dist/source/index.d.ts +3 -3
- package/dist/source/index.js +1 -1
- package/dist/source/loading.d.ts +4 -4
- package/dist/source/loading.js +7 -4
- package/dist/source/precheck.d.ts +13 -1
- package/dist/source/precheck.js +9 -2
- package/dist/source/textEncoding.d.ts +18 -0
- package/dist/source/textEncoding.js +15 -0
- package/dist/viewer/createViewer.d.ts +1 -1
- package/dist/viewer/createViewer.js +28 -17
- package/dist/viewer/loading.d.ts +6 -2
- package/dist/viewer/loading.js +9 -1
- package/dist/viewer/operations.d.ts +3 -3
- package/dist/viewer/operations.js +14 -6
- package/dist/viewer/state.d.ts +6 -2
- package/dist/viewer/state.js +8 -3
- package/package.json +5 -3
package/README.en.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[English](https://github.com/flyfish-dev/file-viewer/blob/main/packages/core/README.md) · [简体中文](https://github.com/flyfish-dev/file-viewer/blob/main/packages/core/README.zh-CN.md)
|
|
4
4
|
|
|
5
|
+
Framework-neutral TypeScript APIs for browser-side file preview. Build a viewer in Vanilla JavaScript, or use the Vue, React, Svelte and jQuery components with the same loading, search, zoom, page navigation, print and export controls. Files are processed locally, without server-side conversion.
|
|
6
|
+
|
|
5
7
|
<!-- FILE_VIEWER_PACKAGE_HEADER -->
|
|
6
8
|
<p>
|
|
7
9
|
<a href="https://www.npmjs.com/package/@file-viewer/core"><img src="https://img.shields.io/npm/v/@file-viewer%2Fcore?logo=npm" alt="npm version"></a>
|
|
@@ -20,20 +22,22 @@
|
|
|
20
22
|
|
|
21
23
|
> ⭐ If File Viewer helps you, please [star the repo on GitHub](https://github.com/flyfish-dev/file-viewer).
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
This package is the migration base for the next architecture: one pure TypeScript core package with internal `headless`, `browser`, and `renderers` layers, plus multiple native standard component packages for Vue, React, pure JavaScript, jQuery, and Svelte. Core owns the shared format matrix, source detection, renderer protocol, capability calculation, lifecycle context, operation guards, operation/event/search/location/zoom/print/export contracts, heavy renderer asset manifests, option normalization, pure state models, and framework-neutral browser rendering engine.
|
|
25
|
+
File Viewer has a catalog of 275 file extensions through optional renderers. **This package is the lightweight foundation, not an all-format viewer.** Add only the engines your application needs; stable and experimental support are listed separately in the [format matrix](https://doc.file-viewer.app/guide/formats).
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
## Start here
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
- [Create a configured application](https://doc.file-viewer.app/guide/cli): `npm create file-viewer@latest my-viewer`.
|
|
30
|
+
- [Use a Web Component](https://doc.file-viewer.app/guide/quickstart-web), or choose a [framework integration](https://github.com/flyfish-dev/file-viewer#readme).
|
|
31
|
+
- For a custom integration, use `core/headless` for metadata and contracts, or `core/browser` for DOM rendering. PDF and Office require an explicit renderer or preset and local runtime assets.
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
npm install @file-viewer/core
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
## Browser source feedback (unreleased)
|
|
38
|
+
|
|
39
|
+
The browser `mountViewer` shell shows localized download feedback while buffering a URL, then reading feedback until the renderer takes over. File and buffer inputs use reading feedback. The status follows the configured locale/theme, reports acquisition failures, and is cleared when a source is replaced or the viewer is destroyed.
|
|
40
|
+
|
|
37
41
|
## Preflight Before Rendering
|
|
38
42
|
|
|
39
43
|
`precheckFileViewerSource()` does not create a viewer or load a heavy renderer. It checks whether the extension is available and validates known PDF, OpenXML / OpenDocument, legacy OLE Office, and RTF signatures or package parts. `valid: null` explicitly means capability-only detection, not a full parse; the real renderer remains the final authority for malformed content.
|
|
@@ -71,10 +75,7 @@ const pdfAssets = resolveFileViewerRendererAssets('pdf', {
|
|
|
71
75
|
Use `@file-viewer/core/browser` when a native component package or pure browser integration needs the shared browser shell and toolbar, direct DOM engine, renderer registry, search/zoom providers, print/export adapters, and framework-neutral Canvas/Worker/WASM renderers.
|
|
72
76
|
|
|
73
77
|
```ts
|
|
74
|
-
import {
|
|
75
|
-
createViewer,
|
|
76
|
-
coreBrowserRendererHandlers
|
|
77
|
-
} from '@file-viewer/core/browser'
|
|
78
|
+
import { createViewer } from '@file-viewer/core/browser'
|
|
78
79
|
|
|
79
80
|
const viewer = createViewer(document.querySelector('#viewer')!, {
|
|
80
81
|
options: {
|
|
@@ -83,7 +84,8 @@ const viewer = createViewer(document.querySelector('#viewer')!, {
|
|
|
83
84
|
}
|
|
84
85
|
})
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
// Images use the built-in renderer; document formats need an optional preset.
|
|
88
|
+
await viewer.load({ url: '/files/photo.png' })
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
Zoom buttons keep their positions and become disabled at their limits. Configure the existing `toolbar.items` map to hide reset controls while retaining the `resetZoom()` API. Set `pdf.zoomToolbar` to `false` to hide the PDF renderer's zoom group while keeping its page navigation and rotation controls:
|
|
@@ -149,8 +151,6 @@ Public source repositories:
|
|
|
149
151
|
- GitHub: https://github.com/flyfish-dev/file-viewer-core
|
|
150
152
|
- Gitee: https://gitee.com/flyfish-dev/file-viewer-core
|
|
151
153
|
|
|
152
|
-
The GitHub/Gitee repositories are distributed public source packages. The private Gitea aggregate remains useful for the complete workspace, release automation, integration history, sponsorship, and priority support.
|
|
153
|
-
|
|
154
154
|
License: Apache-2.0. For second development or commercial use, keep clear Flyfish Viewer attribution and contribute shared compatibility improvements where possible.
|
|
155
155
|
|
|
156
156
|
## Formats and related packages
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[English](https://github.com/flyfish-dev/file-viewer/blob/main/packages/core/README.md) · [简体中文](https://github.com/flyfish-dev/file-viewer/blob/main/packages/core/README.zh-CN.md)
|
|
4
4
|
|
|
5
|
+
Framework-neutral TypeScript APIs for browser-side file preview. Build a viewer in Vanilla JavaScript, or use the Vue, React, Svelte and jQuery components with the same loading, search, zoom, page navigation, print and export controls. Files are processed locally, without server-side conversion.
|
|
6
|
+
|
|
5
7
|
<!-- FILE_VIEWER_PACKAGE_HEADER -->
|
|
6
8
|
<p>
|
|
7
9
|
<a href="https://www.npmjs.com/package/@file-viewer/core"><img src="https://img.shields.io/npm/v/@file-viewer%2Fcore?logo=npm" alt="npm version"></a>
|
|
@@ -20,20 +22,22 @@
|
|
|
20
22
|
|
|
21
23
|
> ⭐ If File Viewer helps you, please [star the repo on GitHub](https://github.com/flyfish-dev/file-viewer).
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
This package is the migration base for the next architecture: one pure TypeScript core package with internal `headless`, `browser`, and `renderers` layers, plus multiple native standard component packages for Vue, React, pure JavaScript, jQuery, and Svelte. Core owns the shared format matrix, source detection, renderer protocol, capability calculation, lifecycle context, operation guards, operation/event/search/location/zoom/print/export contracts, heavy renderer asset manifests, option normalization, pure state models, and framework-neutral browser rendering engine.
|
|
25
|
+
File Viewer has a catalog of 275 file extensions through optional renderers. **This package is the lightweight foundation, not an all-format viewer.** Add only the engines your application needs; stable and experimental support are listed separately in the [format matrix](https://doc.file-viewer.app/guide/formats).
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
## Start here
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
- [Create a configured application](https://doc.file-viewer.app/guide/cli): `npm create file-viewer@latest my-viewer`.
|
|
30
|
+
- [Use a Web Component](https://doc.file-viewer.app/guide/quickstart-web), or choose a [framework integration](https://github.com/flyfish-dev/file-viewer#readme).
|
|
31
|
+
- For a custom integration, use `core/headless` for metadata and contracts, or `core/browser` for DOM rendering. PDF and Office require an explicit renderer or preset and local runtime assets.
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
npm install @file-viewer/core
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
## Browser source feedback (unreleased)
|
|
38
|
+
|
|
39
|
+
The browser `mountViewer` shell shows localized download feedback while buffering a URL, then reading feedback until the renderer takes over. File and buffer inputs use reading feedback. The status follows the configured locale/theme, reports acquisition failures, and is cleared when a source is replaced or the viewer is destroyed.
|
|
40
|
+
|
|
37
41
|
## Preflight Before Rendering
|
|
38
42
|
|
|
39
43
|
`precheckFileViewerSource()` does not create a viewer or load a heavy renderer. It checks whether the extension is available and validates known PDF, OpenXML / OpenDocument, legacy OLE Office, and RTF signatures or package parts. `valid: null` explicitly means capability-only detection, not a full parse; the real renderer remains the final authority for malformed content.
|
|
@@ -71,10 +75,7 @@ const pdfAssets = resolveFileViewerRendererAssets('pdf', {
|
|
|
71
75
|
Use `@file-viewer/core/browser` when a native component package or pure browser integration needs the shared browser shell and toolbar, direct DOM engine, renderer registry, search/zoom providers, print/export adapters, and framework-neutral Canvas/Worker/WASM renderers.
|
|
72
76
|
|
|
73
77
|
```ts
|
|
74
|
-
import {
|
|
75
|
-
createViewer,
|
|
76
|
-
coreBrowserRendererHandlers
|
|
77
|
-
} from '@file-viewer/core/browser'
|
|
78
|
+
import { createViewer } from '@file-viewer/core/browser'
|
|
78
79
|
|
|
79
80
|
const viewer = createViewer(document.querySelector('#viewer')!, {
|
|
80
81
|
options: {
|
|
@@ -83,7 +84,8 @@ const viewer = createViewer(document.querySelector('#viewer')!, {
|
|
|
83
84
|
}
|
|
84
85
|
})
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
// Images use the built-in renderer; document formats need an optional preset.
|
|
88
|
+
await viewer.load({ url: '/files/photo.png' })
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
Zoom buttons keep their positions and become disabled at their limits. Configure the existing `toolbar.items` map to hide reset controls while retaining the `resetZoom()` API. Set `pdf.zoomToolbar` to `false` to hide the PDF renderer's zoom group while keeping its page navigation and rotation controls:
|
|
@@ -149,8 +151,6 @@ Public source repositories:
|
|
|
149
151
|
- GitHub: https://github.com/flyfish-dev/file-viewer-core
|
|
150
152
|
- Gitee: https://gitee.com/flyfish-dev/file-viewer-core
|
|
151
153
|
|
|
152
|
-
The GitHub/Gitee repositories are distributed public source packages. The private Gitea aggregate remains useful for the complete workspace, release automation, integration history, sponsorship, and priority support.
|
|
153
|
-
|
|
154
154
|
License: Apache-2.0. For second development or commercial use, keep clear Flyfish Viewer attribution and contribute shared compatibility improvements where possible.
|
|
155
155
|
|
|
156
156
|
## Formats and related packages
|
package/README.zh-CN.md
CHANGED
|
@@ -34,6 +34,10 @@ Current framework-neutral browser renderers still inside core are limited to the
|
|
|
34
34
|
npm install @file-viewer/core
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
## 浏览器源加载反馈(未发布)
|
|
38
|
+
|
|
39
|
+
浏览器 `mountViewer` 在缓冲 URL 时显示本地化的下载提示,随后显示读取提示,直至渲染器接管。File 和 buffer 输入显示读取提示。状态遵循配置的语言和主题,展示获取失败,并在切源或销毁时清理。
|
|
40
|
+
|
|
37
41
|
## 渲染前预检
|
|
38
42
|
|
|
39
43
|
`precheckFileViewerSource()` 不会创建 viewer,也不会加载重型 renderer。它先检查当前扩展名是否在能力范围内,并对 PDF、OpenXML / OpenDocument、旧版 OLE Office、RTF 等格式执行签名或包结构检查。`valid: null` 明确表示该格式只有能力检查、没有做完整解析;真实 renderer 仍是畸形文件的最终判断者。
|
package/dist/assets.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export type * from
|
|
1
|
+
export * from './platform/assets.js';
|
|
2
|
+
export type * from './platform/assets.js';
|
package/dist/assets.js
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// Public subpath facade for `@file-viewer/core/assets`.
|
|
2
|
+
//
|
|
3
|
+
// Asset resolution belongs to the platform layer, but this root-level file is
|
|
4
|
+
// intentionally kept as the stable package export declared in package.json.
|
|
5
|
+
export * from './platform/assets.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FileViewerAiOptions, type FileViewerApplyViewStateOptions, type FileViewerArchiveOptions, type FileViewerCadOptions, type FileViewerDocxOptions, type FileViewerDocumentAnchor, type FileViewerDocumentChunk, type FileViewerEvent, type FileViewerEventHandler, type FileViewerEventType, type FileViewerFileRef, type FileViewerFitMode, type FileViewerFitOptions, type FileViewerFitResult, type FileViewerInstance, type FileViewerLifecycleContext, type FileViewerOperationAvailability, type FileViewerOptions, type FileViewerPdfOptions, type FileViewerPresentationOptions, type FileViewerSpreadsheetOptions, type FileViewerPublicApi, type FileViewerSearchOptions, type FileViewerSearchState, type FileViewerThemeMode, type FileViewerToolbarOptions, type FileViewerToolbarPosition, type FileViewerTypstOptions, type FileViewerUiDensity, type FileViewerUiOptions, type FileViewerViewState, type FileViewerWatermarkOptions, type FileViewerPrintOptions, type FileViewerZoomState, type RendererRegistry } from
|
|
1
|
+
import { type FileViewerAiOptions, type FileViewerApplyViewStateOptions, type FileViewerArchiveOptions, type FileViewerCadOptions, type FileViewerDocxOptions, type FileViewerDocumentAnchor, type FileViewerDocumentChunk, type FileViewerEvent, type FileViewerEventHandler, type FileViewerEventType, type FileViewerFileRef, type FileViewerFitMode, type FileViewerFitOptions, type FileViewerFitResult, type FileViewerInstance, type FileViewerLifecycleContext, type FileViewerOperationAvailability, type FileViewerOptions, type FileViewerPdfOptions, type FileViewerPresentationOptions, type FileViewerSpreadsheetOptions, type FileViewerPublicApi, type FileViewerSearchOptions, type FileViewerSearchState, type FileViewerThemeMode, type FileViewerToolbarOptions, type FileViewerToolbarPosition, type FileViewerTypstOptions, type FileViewerUiDensity, type FileViewerUiOptions, type FileViewerViewState, type FileViewerWatermarkOptions, type FileViewerPrintOptions, type FileViewerZoomState, type RendererRegistry } from '../index.js';
|
|
2
2
|
export type FileRef = FileViewerFileRef;
|
|
3
3
|
export type ViewerWatermarkOptions = FileViewerWatermarkOptions;
|
|
4
4
|
export type ViewerToolbarPosition = FileViewerToolbarPosition;
|
|
@@ -34,6 +34,7 @@ export interface ViewerState {
|
|
|
34
34
|
search: FileViewerSearchState | null;
|
|
35
35
|
zoom: FileViewerZoomState | null;
|
|
36
36
|
location: FileViewerDocumentAnchor | null;
|
|
37
|
+
/** Added in 2.2; optional so existing state fixtures remain source-compatible. */
|
|
37
38
|
viewState?: FileViewerViewState | null;
|
|
38
39
|
}
|
|
39
40
|
export type ViewerStateListener = (state: ViewerState, event?: ViewerEvent) => void;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { appendFileViewerStyle, cloneFileViewerViewState, applyFileViewerZoomAvailability, createViewer, isFileViewerShadowRoot, normalizeFileViewerUiDensity, normalizeFileViewerTheme, normalizeFileViewerStyleIsolation, syncFileViewerRenderSurfaceBackground, } from
|
|
2
|
-
import { DEFAULT_FILE_VIEWER_SOURCE_FILENAME, createFileViewerTranslator, getExtension, normalizeFileViewerSourceUrl, hasVisibleFileViewerToolbarActions, isFileViewerZoomButtonDisabled, normalizeFileViewerToolbar, normalizeFilename, readFileViewerBuffer, resolveFileViewerSourceFilename, resolveFileViewerRemoteSourcePlan, resolveFileViewerColorScheme, resolveFileViewerToolbarOrder, resolveFileViewerToolbarPosition, resolveVisibleFileViewerToolbar, toggleFileViewerColorScheme, wrapFileViewerFileRef, } from
|
|
3
|
-
import { resolveFileViewerToolbarZoomButtons } from
|
|
4
|
-
import { createToolbarFocusRestorer } from
|
|
1
|
+
import { appendFileViewerStyle, cloneFileViewerViewState, applyFileViewerZoomAvailability, createViewer, isFileViewerShadowRoot, normalizeFileViewerUiDensity, normalizeFileViewerTheme, normalizeFileViewerStyleIsolation, syncFileViewerRenderSurfaceBackground, } from '../index.js';
|
|
2
|
+
import { DEFAULT_FILE_VIEWER_SOURCE_FILENAME, createFileViewerTranslator, getExtension, normalizeFileViewerSourceUrl, hasVisibleFileViewerToolbarActions, isFileViewerZoomButtonDisabled, normalizeFileViewerToolbar, normalizeFilename, readFileViewerBuffer, resolveFileViewerSourceFilename, resolveFileViewerRemoteSourcePlan, resolveFileViewerColorScheme, resolveFileViewerToolbarOrder, resolveFileViewerToolbarPosition, resolveVisibleFileViewerToolbar, toggleFileViewerColorScheme, wrapFileViewerFileRef, } from '../index.js';
|
|
3
|
+
import { resolveFileViewerToolbarZoomButtons } from '../lifecycle/operations.js';
|
|
4
|
+
import { createToolbarFocusRestorer } from './toolbar-focus.js';
|
|
5
|
+
import { createViewerSourceFeedback } from './source-feedback.js';
|
|
5
6
|
const isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
6
7
|
const hasSource = (options = {}) => {
|
|
7
8
|
return !!(options.url || options.file || options.buffer);
|
|
@@ -15,6 +16,8 @@ const toViewerSourceInput = (options = {}) => ({
|
|
|
15
16
|
type: options.type,
|
|
16
17
|
size: options.size,
|
|
17
18
|
});
|
|
19
|
+
// These options feed renderer selection or rendering itself. Viewer chrome,
|
|
20
|
+
// fitting, watermark, and operation callbacks update in place instead.
|
|
18
21
|
const RENDER_AFFECTING_VIEWER_OPTION_KEYS = [
|
|
19
22
|
'theme',
|
|
20
23
|
'rendererMode',
|
|
@@ -116,26 +119,28 @@ const resolveViewerSourceFilename = (source) => {
|
|
|
116
119
|
}), source.type ? `preview.${source.type}` : DEFAULT_FILE_VIEWER_SOURCE_FILENAME);
|
|
117
120
|
};
|
|
118
121
|
const resolveViewerLoadSource = async (source, options = {}) => {
|
|
119
|
-
var _a, _b, _c;
|
|
122
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
120
123
|
const filename = resolveViewerSourceFilename(source);
|
|
121
124
|
const type = source.type || getExtension(filename);
|
|
122
125
|
if (source.buffer) {
|
|
126
|
+
(_a = options.onPhase) === null || _a === void 0 ? void 0 : _a.call(options, 'reading');
|
|
123
127
|
return {
|
|
124
128
|
buffer: source.buffer,
|
|
125
129
|
filename,
|
|
126
130
|
type,
|
|
127
|
-
size: (
|
|
131
|
+
size: (_b = source.size) !== null && _b !== void 0 ? _b : source.buffer.byteLength,
|
|
128
132
|
url: source.url,
|
|
129
133
|
};
|
|
130
134
|
}
|
|
131
135
|
if (source.file) {
|
|
136
|
+
(_c = options.onPhase) === null || _c === void 0 ? void 0 : _c.call(options, 'reading');
|
|
132
137
|
const file = wrapFileViewerFileRef(source.file, filename);
|
|
133
138
|
return {
|
|
134
139
|
file,
|
|
135
140
|
buffer: await readFileViewerBuffer(file),
|
|
136
141
|
filename: file.name || filename,
|
|
137
142
|
type: type || getExtension(file.name),
|
|
138
|
-
size: (
|
|
143
|
+
size: (_d = source.size) !== null && _d !== void 0 ? _d : file.size,
|
|
139
144
|
url: source.url,
|
|
140
145
|
};
|
|
141
146
|
}
|
|
@@ -144,9 +149,13 @@ const resolveViewerLoadSource = async (source, options = {}) => {
|
|
|
144
149
|
url: source.url, filename, type,
|
|
145
150
|
pageHref: options.pageHref, streaming: options.streaming,
|
|
146
151
|
});
|
|
152
|
+
// A supplied transport owns authentication/acquisition and keeps its normal
|
|
153
|
+
// buffered contract. Only the default PDF URL path defers bytes to PDF.js.
|
|
147
154
|
if (!options.fetchFile && options.acceptsPdfUrl && plan.streamPdf) {
|
|
155
|
+
(_e = options.onPhase) === null || _e === void 0 ? void 0 : _e.call(options, 'streamingPdf');
|
|
148
156
|
return { url: plan.url, filename: plan.filename, type: plan.extension, size: source.size };
|
|
149
157
|
}
|
|
158
|
+
(_f = options.onPhase) === null || _f === void 0 ? void 0 : _f.call(options, 'downloading');
|
|
150
159
|
const fileRef = await (options.fetchFile || defaultFetchFile)({
|
|
151
160
|
url: source.url,
|
|
152
161
|
signal: options.signal,
|
|
@@ -156,12 +165,13 @@ const resolveViewerLoadSource = async (source, options = {}) => {
|
|
|
156
165
|
throw new Error('Downloaded file is empty.');
|
|
157
166
|
}
|
|
158
167
|
const file = wrapFileViewerFileRef(fileRef, filename);
|
|
168
|
+
(_g = options.onPhase) === null || _g === void 0 ? void 0 : _g.call(options, 'reading');
|
|
159
169
|
return {
|
|
160
170
|
file,
|
|
161
171
|
buffer: await readFileViewerBuffer(file),
|
|
162
172
|
filename: file.name || filename,
|
|
163
173
|
type: type || getExtension(file.name),
|
|
164
|
-
size: (
|
|
174
|
+
size: (_h = source.size) !== null && _h !== void 0 ? _h : file.size,
|
|
165
175
|
url: source.url,
|
|
166
176
|
};
|
|
167
177
|
}
|
|
@@ -306,6 +316,9 @@ const DEFAULT_TOOLBAR_ZOOM_STATE = {
|
|
|
306
316
|
canZoomOut: false,
|
|
307
317
|
canReset: false,
|
|
308
318
|
};
|
|
319
|
+
// A ShadowRoot cannot be detached. Track the exact roots created on viewer
|
|
320
|
+
// hosts so a later light-DOM remount can expose its children through a slot,
|
|
321
|
+
// without mistaking and clearing a ShadowRoot owned by the host application.
|
|
309
322
|
const ownedViewerShadowRoots = new WeakMap();
|
|
310
323
|
const WEB_VIEWER_STYLE = `
|
|
311
324
|
:host,.file-viewer-web-shell{display:block;width:100%;height:100%;min-width:0;min-height:0;contain:content;--file-viewer-bg:transparent;--file-viewer-content-bg:transparent;--file-viewer-text:#172033;--file-viewer-muted:#607282;--file-viewer-font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;--file-viewer-border:rgba(20,35,53,.08);--file-viewer-toolbar-bg:rgba(255,255,255,.92);--file-viewer-toolbar-border:rgba(20,35,53,.06);--file-viewer-toolbar-shadow:0 18px 44px rgba(15,23,42,.16);--file-viewer-toolbar-radius:999px;--file-viewer-toolbar-gap:6px;--file-viewer-toolbar-min-height:45px;--file-viewer-toolbar-padding:6px 10px;--file-viewer-toolbar-floating-min-height:42px;--file-viewer-toolbar-floating-padding:6px;--file-viewer-toolbar-floating-offset:16px;--file-viewer-group-bg:rgba(20,35,53,.035);--file-viewer-group-border:rgba(20,35,53,.08);--file-viewer-group-gap:2px;--file-viewer-group-padding:2px;--file-viewer-button-color:#40546a;--file-viewer-button-hover-bg:rgba(33,163,102,.1);--file-viewer-button-hover-color:#16774c;--file-viewer-button-disabled-color:#aab5c0;--file-viewer-button-radius:8px;--file-viewer-button-min-width:42px;--file-viewer-button-height:30px;--file-viewer-button-padding:0 10px;--file-viewer-icon-button-size:30px;--file-viewer-zoom-meter-min-width:48px;--file-viewer-zoom-meter-padding:0 8px;--file-viewer-floating-button-min-width:48px;--file-viewer-floating-button-height:32px;--file-viewer-floating-icon-button-size:32px;--file-viewer-floating-zoom-meter-min-width:54px;--file-viewer-search-input-height:30px;--file-viewer-search-input-padding:0 10px;--file-viewer-search-button-min-width:32px;--file-viewer-search-button-padding:0 8px;--file-viewer-search-count-min-width:42px;--file-viewer-floating-search-input-height:32px;--file-viewer-focus-ring:rgba(31,157,103,.22);--file-viewer-z-toolbar:20;--file-viewer-z-floating-toolbar:30}
|
|
@@ -314,6 +327,9 @@ const WEB_VIEWER_STYLE = `
|
|
|
314
327
|
.file-viewer-web-shell{position:relative;width:100%;height:100%;min-height:0;display:flex;flex-direction:column;overflow:hidden;background:var(--file-viewer-render-surface-background,var(--file-viewer-bg));color:var(--file-viewer-text);font:var(--file-viewer-font);letter-spacing:0;box-sizing:border-box;contain:content}
|
|
315
328
|
.file-viewer-web-shell[data-viewer-density="compact"]{--file-viewer-toolbar-gap:3px;--file-viewer-toolbar-min-height:34px;--file-viewer-toolbar-padding:3px 5px;--file-viewer-toolbar-floating-min-height:32px;--file-viewer-toolbar-floating-padding:3px;--file-viewer-toolbar-floating-offset:10px;--file-viewer-group-gap:2px;--file-viewer-group-padding:2px;--file-viewer-button-radius:6px;--file-viewer-button-min-width:34px;--file-viewer-button-height:26px;--file-viewer-button-padding:0 6px;--file-viewer-icon-button-size:26px;--file-viewer-zoom-meter-min-width:42px;--file-viewer-zoom-meter-padding:0 5px;--file-viewer-floating-button-min-width:38px;--file-viewer-floating-button-height:28px;--file-viewer-floating-icon-button-size:28px;--file-viewer-floating-zoom-meter-min-width:46px;--file-viewer-search-input-height:26px;--file-viewer-search-input-padding:0 8px;--file-viewer-search-button-min-width:28px;--file-viewer-search-button-padding:0 6px;--file-viewer-search-count-min-width:36px;--file-viewer-floating-search-input-height:28px}
|
|
316
329
|
.file-viewer-web-content{position:relative;flex:1 1 auto;min-height:0;min-width:0;overflow:auto;overscroll-behavior:contain;background:var(--file-viewer-render-surface-background,var(--file-viewer-content-bg))}
|
|
330
|
+
.file-viewer-web-source-status{position:absolute;inset:0;z-index:10;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:24px;background:var(--file-viewer-toolbar-bg);color:var(--file-viewer-text);text-align:center;overflow:auto}
|
|
331
|
+
.file-viewer-web-source-status p{margin:0;max-width:560px;white-space:pre-wrap;overflow-wrap:anywhere}
|
|
332
|
+
.file-viewer-web-source-status strong{font-size:18px}
|
|
317
333
|
.file-viewer-web-toolbar{flex:0 0 auto;min-height:var(--file-viewer-toolbar-min-height);display:inline-flex;align-items:center;justify-content:flex-end;gap:var(--file-viewer-toolbar-gap);padding:var(--file-viewer-toolbar-padding);border-bottom:1px solid var(--file-viewer-toolbar-border);background:var(--file-viewer-toolbar-bg);box-sizing:border-box;z-index:var(--file-viewer-z-toolbar)}
|
|
318
334
|
.file-viewer-web-toolbar[hidden]{display:none!important}
|
|
319
335
|
.file-viewer-web-toolbar[data-toolbar-position="top-center"]{justify-content:center}
|
|
@@ -406,14 +422,26 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
406
422
|
}
|
|
407
423
|
else if (shouldUseShadowRoot && canAttachShadowRoot) {
|
|
408
424
|
try {
|
|
425
|
+
// The viewer owns this root even when its host already lives inside a
|
|
426
|
+
// customer's ShadowRoot. Reusing the ancestor would let that component's
|
|
427
|
+
// resets override the toolbar and would make our :host rules mutate the
|
|
428
|
+
// customer's outer custom element.
|
|
429
|
+
// Delegated focus makes Safari taps on renderer controls focus the first
|
|
430
|
+
// toolbar input instead, triggering keyboard zoom and viewport scrolling.
|
|
409
431
|
renderRoot = container.attachShadow({ mode: 'open' });
|
|
410
432
|
ownedViewerShadowRoots.set(container, renderRoot);
|
|
411
433
|
}
|
|
412
434
|
catch {
|
|
435
|
+
// Some HTMLElement subclasses cannot host Shadow DOM, and a closed root
|
|
436
|
+
// is intentionally invisible through `shadowRoot`. Keep the legacy
|
|
437
|
+
// scoped path usable instead of surfacing a raw NotSupportedError.
|
|
413
438
|
renderRoot = container;
|
|
414
439
|
}
|
|
415
440
|
}
|
|
416
441
|
else if (existingHostShadowRoot && !hasOwnedHostShadowRoot) {
|
|
442
|
+
// The application owns this root. Preserve every existing child and mount
|
|
443
|
+
// the viewer in a dedicated boundary. Shadow mode gets a nested root so
|
|
444
|
+
// customer resets in the outer component cannot reach the viewer.
|
|
417
445
|
mountBoundary = documentRef.createElement('div');
|
|
418
446
|
mountBoundary.dataset.fileViewerMountBoundary = 'true';
|
|
419
447
|
existingHostShadowRoot.appendChild(mountBoundary);
|
|
@@ -430,6 +458,9 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
430
458
|
}
|
|
431
459
|
}
|
|
432
460
|
else if (!shouldUseShadowRoot && hasOwnedHostShadowRoot && existingHostShadowRoot) {
|
|
461
|
+
// Light-DOM children of a shadow host are not painted unless the root has
|
|
462
|
+
// a slot. Keep the immutable owned root, but project the real light-DOM
|
|
463
|
+
// viewer so host selectors and legacy deep overrides work as documented.
|
|
433
464
|
const slot = documentRef.createElement('slot');
|
|
434
465
|
slot.dataset.fileViewerLightDomSlot = 'true';
|
|
435
466
|
existingHostShadowRoot.replaceChildren(slot);
|
|
@@ -437,6 +468,9 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
437
468
|
}
|
|
438
469
|
renderRoot.replaceChildren();
|
|
439
470
|
const styleHandle = appendFileViewerStyle(renderRoot, WEB_VIEWER_STYLE, {
|
|
471
|
+
// Adopt only into a root owned by the viewer. Adopting into an ancestor
|
|
472
|
+
// ShadowRoot would leak toolbar selectors and apply :host rules to the
|
|
473
|
+
// customer's custom element.
|
|
440
474
|
adoptedStyleSheet: isFileViewerShadowRoot(renderRoot),
|
|
441
475
|
});
|
|
442
476
|
const shell = documentRef.createElement('div');
|
|
@@ -462,7 +496,10 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
462
496
|
let currentSource = hasSource(currentOptions)
|
|
463
497
|
? toViewerSourceInput(currentOptions)
|
|
464
498
|
: null;
|
|
465
|
-
let
|
|
499
|
+
let sourceRequest = null;
|
|
500
|
+
let renderingRequest = null;
|
|
501
|
+
let lifecycleVersion = -1;
|
|
502
|
+
const updateSourceFeedback = createViewerSourceFeedback(shell, contentEl);
|
|
466
503
|
const listeners = new Set();
|
|
467
504
|
const state = {
|
|
468
505
|
loading: false,
|
|
@@ -647,6 +684,12 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
647
684
|
group.appendChild(createReadonlyMeter(documentRef, zoomState.label, 'file-viewer-web-zoom-meter'));
|
|
648
685
|
}
|
|
649
686
|
if (zoomButtons.zoomReset) {
|
|
687
|
+
// Placed before the zoom-in button (not after) so that toggling this
|
|
688
|
+
// button's visibility never shifts the zoom-in button's position: the
|
|
689
|
+
// toolbar group is right-aligned, and both icon buttons share the same
|
|
690
|
+
// width, so inserting "1:1" after "+" would make it land exactly on
|
|
691
|
+
// top of the previous "+" position, turning a repeated zoom-in click
|
|
692
|
+
// into an accidental reset (see GitHub issue #88).
|
|
650
693
|
const button = createButton(documentRef, '1:1', 'file-viewer-web-icon-button', () => controller === null || controller === void 0 ? void 0 : controller.resetZoom());
|
|
651
694
|
button.title = t('toolbar.zoomReset');
|
|
652
695
|
button.dataset.viewerToolbarKey = 'zoom-reset';
|
|
@@ -804,11 +847,20 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
804
847
|
var _a;
|
|
805
848
|
const snapshot = snapshotState();
|
|
806
849
|
renderToolbar();
|
|
850
|
+
updateSourceFeedback((sourceRequest === null || sourceRequest === void 0 ? void 0 : sourceRequest.pending) ? sourceRequest.phase : null, state.error, currentSource ? currentSource.type || getExtension(resolveViewerSourceFilename(currentSource)) : '', currentOptions.options, Boolean((sourceRequest === null || sourceRequest === void 0 ? void 0 : sourceRequest.rendering) && contentEl.querySelector('.file-viewer-render-error')));
|
|
807
851
|
(_a = currentOptions.onStateChange) === null || _a === void 0 ? void 0 : _a.call(currentOptions, snapshot, event);
|
|
808
852
|
listeners.forEach(listener => listener(snapshot, event));
|
|
809
853
|
};
|
|
810
854
|
const applyViewerEvent = (event) => {
|
|
811
855
|
var _a;
|
|
856
|
+
if (disposed || sourceRequest !== renderingRequest)
|
|
857
|
+
return;
|
|
858
|
+
if (event.type === 'load-start' || event.type === 'load-complete' ||
|
|
859
|
+
event.type === 'unload-start' || event.type === 'unload-complete') {
|
|
860
|
+
if (event.payload.version < lifecycleVersion)
|
|
861
|
+
return;
|
|
862
|
+
lifecycleVersion = event.payload.version;
|
|
863
|
+
}
|
|
812
864
|
state.lastEvent = event;
|
|
813
865
|
if (event.type === 'load-start') {
|
|
814
866
|
state.loading = true;
|
|
@@ -819,6 +871,8 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
819
871
|
searchDraft = '';
|
|
820
872
|
}
|
|
821
873
|
else if (event.type === 'load-complete') {
|
|
874
|
+
if (sourceRequest)
|
|
875
|
+
sourceRequest.pending = false;
|
|
822
876
|
state.loading = false;
|
|
823
877
|
state.ready = true;
|
|
824
878
|
state.lifecycle = event.payload;
|
|
@@ -829,7 +883,7 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
829
883
|
state.lifecycle = event.payload;
|
|
830
884
|
}
|
|
831
885
|
else if (event.type === 'unload-complete') {
|
|
832
|
-
state.loading =
|
|
886
|
+
state.loading = Boolean(sourceRequest === null || sourceRequest === void 0 ? void 0 : sourceRequest.pending);
|
|
833
887
|
state.ready = false;
|
|
834
888
|
state.lifecycle = event.payload;
|
|
835
889
|
}
|
|
@@ -860,21 +914,27 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
860
914
|
(_e = viewerColorSchemeQuery === null || viewerColorSchemeQuery === void 0 ? void 0 : viewerColorSchemeQuery.addEventListener) === null || _e === void 0 ? void 0 : _e.call(viewerColorSchemeQuery, 'change', handleViewerColorSchemeChange);
|
|
861
915
|
renderToolbar();
|
|
862
916
|
const cancel = () => {
|
|
863
|
-
|
|
864
|
-
|
|
917
|
+
var _a;
|
|
918
|
+
const previous = sourceRequest;
|
|
919
|
+
sourceRequest = null;
|
|
920
|
+
(_a = previous === null || previous === void 0 ? void 0 : previous.abort) === null || _a === void 0 ? void 0 : _a.abort();
|
|
865
921
|
};
|
|
866
922
|
const loadSource = async (nextSource) => {
|
|
867
|
-
var _a, _b, _c, _d, _e, _f;
|
|
923
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
868
924
|
cancel();
|
|
869
925
|
currentSource = nextSource;
|
|
870
|
-
|
|
871
|
-
|
|
926
|
+
const request = {
|
|
927
|
+
abort: typeof AbortController !== 'undefined' ? new AbortController() : null,
|
|
928
|
+
phase: 'reading', pending: true, rendering: false,
|
|
929
|
+
};
|
|
930
|
+
sourceRequest = request;
|
|
931
|
+
const isCurrent = () => { var _a; return !disposed && sourceRequest === request && !((_a = request.abort) === null || _a === void 0 ? void 0 : _a.signal.aborted); };
|
|
872
932
|
try {
|
|
873
933
|
state.loading = true;
|
|
874
934
|
state.error = null;
|
|
875
935
|
notifyState();
|
|
876
936
|
await ((_a = instance.prepare) === null || _a === void 0 ? void 0 : _a.call(instance));
|
|
877
|
-
if (
|
|
937
|
+
if (!isCurrent()) {
|
|
878
938
|
return null;
|
|
879
939
|
}
|
|
880
940
|
const extension = nextSource.type || getExtension(resolveViewerSourceFilename(nextSource));
|
|
@@ -882,29 +942,43 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
882
942
|
const resolvedSource = await resolveViewerLoadSource(nextSource, {
|
|
883
943
|
acceptsPdfUrl: (renderer === null || renderer === void 0 ? void 0 : renderer.sourceAccess) === 'stream-preferred' && Boolean(renderer.load),
|
|
884
944
|
fetchFile: coreOptions.fetchFile,
|
|
885
|
-
signal:
|
|
886
|
-
streaming: (
|
|
887
|
-
pageHref: (
|
|
945
|
+
signal: (_b = request.abort) === null || _b === void 0 ? void 0 : _b.signal,
|
|
946
|
+
streaming: (_d = (_c = currentOptions.options) === null || _c === void 0 ? void 0 : _c.pdf) === null || _d === void 0 ? void 0 : _d.streaming,
|
|
947
|
+
pageHref: (_f = (_e = documentRef.defaultView) === null || _e === void 0 ? void 0 : _e.location) === null || _f === void 0 ? void 0 : _f.href,
|
|
948
|
+
onPhase(phase) {
|
|
949
|
+
if (!isCurrent())
|
|
950
|
+
return;
|
|
951
|
+
request.phase = phase;
|
|
952
|
+
updateSourceFeedback(phase, null, extension, currentOptions.options);
|
|
953
|
+
},
|
|
888
954
|
});
|
|
889
|
-
if (
|
|
955
|
+
if (!isCurrent()) {
|
|
890
956
|
return null;
|
|
891
957
|
}
|
|
892
|
-
|
|
958
|
+
if (resolvedSource.buffer || resolvedSource.file)
|
|
959
|
+
request.phase = 'reading';
|
|
960
|
+
request.rendering = true;
|
|
961
|
+
renderingRequest = request;
|
|
962
|
+
notifyState();
|
|
963
|
+
return await instance.load(resolvedSource, { signal: (_g = request.abort) === null || _g === void 0 ? void 0 : _g.signal });
|
|
893
964
|
}
|
|
894
965
|
catch (error) {
|
|
895
|
-
if (
|
|
966
|
+
if (!isCurrent()) {
|
|
896
967
|
return null;
|
|
897
968
|
}
|
|
898
969
|
state.loading = false;
|
|
899
970
|
state.ready = false;
|
|
900
|
-
state.error = error;
|
|
971
|
+
state.error = error !== null && error !== void 0 ? error : new Error(createFileViewerTranslator(currentOptions.options)('error.unknown'));
|
|
972
|
+
request.pending = false;
|
|
901
973
|
notifyState();
|
|
902
|
-
(
|
|
974
|
+
(_h = coreOptions.onError) === null || _h === void 0 ? void 0 : _h.call(coreOptions, error, nextSource);
|
|
903
975
|
throw error;
|
|
904
976
|
}
|
|
905
977
|
finally {
|
|
906
|
-
if (
|
|
907
|
-
|
|
978
|
+
if (isCurrent() && request.pending) {
|
|
979
|
+
request.pending = false;
|
|
980
|
+
state.loading = false;
|
|
981
|
+
notifyState();
|
|
908
982
|
}
|
|
909
983
|
}
|
|
910
984
|
};
|
|
@@ -922,6 +996,9 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
922
996
|
}
|
|
923
997
|
};
|
|
924
998
|
if (currentSource) {
|
|
999
|
+
// The initial declarative source has no caller awaiting it. State changes
|
|
1000
|
+
// and the visible error surface remain authoritative without a global
|
|
1001
|
+
// unhandled-rejection event for an expected rendering failure.
|
|
925
1002
|
void loadSource(currentSource).catch(() => undefined);
|
|
926
1003
|
}
|
|
927
1004
|
controller = {
|
|
@@ -969,7 +1046,13 @@ export const mountViewer = (container, initialOptions = {}, coreOptions = {}) =>
|
|
|
969
1046
|
}
|
|
970
1047
|
}
|
|
971
1048
|
else {
|
|
1049
|
+
cancel();
|
|
1050
|
+
renderingRequest = null;
|
|
972
1051
|
currentSource = null;
|
|
1052
|
+
state.loading = false;
|
|
1053
|
+
state.ready = false;
|
|
1054
|
+
state.error = null;
|
|
1055
|
+
notifyState();
|
|
973
1056
|
await instance.load({ filename: DEFAULT_FILE_VIEWER_SOURCE_FILENAME });
|
|
974
1057
|
}
|
|
975
1058
|
},
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type FileViewerI18nInput } from '../i18n/messages.js';
|
|
2
|
+
export type ViewerSourcePhase = 'downloading' | 'reading' | 'streamingPdf';
|
|
3
|
+
/** The shell owns acquisition feedback; renderers retain their own error surface. */
|
|
4
|
+
export declare const createViewerSourceFeedback: (shell: HTMLElement, content: HTMLElement) => (phase: ViewerSourcePhase | null, error: unknown, extension: string, options?: FileViewerI18nInput, rendererHasError?: boolean) => void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createFileViewerTranslator } from '../i18n/messages.js';
|
|
2
|
+
import { createFileViewerErrorState } from '../viewer/state.js';
|
|
3
|
+
/** The shell owns acquisition feedback; renderers retain their own error surface. */
|
|
4
|
+
export const createViewerSourceFeedback = (shell, content) => {
|
|
5
|
+
let surface = null;
|
|
6
|
+
let title = null;
|
|
7
|
+
let message = null;
|
|
8
|
+
return (phase, error, extension, options, rendererHasError = false) => {
|
|
9
|
+
content.setAttribute('aria-busy', String(Boolean(phase)));
|
|
10
|
+
content.toggleAttribute('inert', Boolean(phase));
|
|
11
|
+
if (!phase && (error == null || rendererHasError)) {
|
|
12
|
+
surface === null || surface === void 0 ? void 0 : surface.remove();
|
|
13
|
+
surface = title = message = null;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!surface) {
|
|
17
|
+
surface = shell.ownerDocument.createElement('div');
|
|
18
|
+
surface.className = 'file-viewer-web-source-status';
|
|
19
|
+
surface.setAttribute('part', 'source-status');
|
|
20
|
+
title = shell.ownerDocument.createElement('strong');
|
|
21
|
+
message = shell.ownerDocument.createElement('p');
|
|
22
|
+
surface.append(title, message);
|
|
23
|
+
shell.append(surface);
|
|
24
|
+
}
|
|
25
|
+
surface.dataset.fileViewerSourceState = phase ? 'loading' : 'error';
|
|
26
|
+
surface.setAttribute('role', phase ? 'status' : 'alert');
|
|
27
|
+
surface.setAttribute('aria-live', phase ? 'polite' : 'assertive');
|
|
28
|
+
surface.setAttribute('aria-atomic', 'true');
|
|
29
|
+
const failure = phase ? null : createFileViewerErrorState(extension, error, undefined, options);
|
|
30
|
+
const nextTitle = (failure === null || failure === void 0 ? void 0 : failure.title) || '';
|
|
31
|
+
const nextMessage = phase
|
|
32
|
+
? createFileViewerTranslator(options)(`preview.${phase}`)
|
|
33
|
+
: failure.message;
|
|
34
|
+
title.hidden = Boolean(phase);
|
|
35
|
+
if (title.textContent !== nextTitle)
|
|
36
|
+
title.textContent = nextTitle;
|
|
37
|
+
if (message.textContent !== nextMessage)
|
|
38
|
+
message.textContent = nextMessage;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Preserve the active logical action across state-driven toolbar rebuilds. */
|
|
1
2
|
export function createToolbarFocusRestorer(toolbar, content) {
|
|
2
3
|
let pendingKey = null;
|
|
3
4
|
const controls = () => Array.from(toolbar.querySelectorAll('[data-viewer-toolbar-key]'));
|
|
@@ -34,6 +35,8 @@ export function createToolbarFocusRestorer(toolbar, content) {
|
|
|
34
35
|
target.setSelectionRange(selection[0], selection[1], selection[2] || undefined);
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
38
|
+
// All actions may be temporarily disabled during a load. Restore only
|
|
39
|
+
// while the user remains here; moving elsewhere clears this pending key.
|
|
37
40
|
pendingKey = key;
|
|
38
41
|
toolbar.tabIndex = -1;
|
|
39
42
|
toolbar.focus({ preventScroll: true });
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from './index.js';
|
|
2
|
+
export * from './browser/controller.js';
|
package/dist/browser.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from './index.js';
|
|
2
|
+
export * from './browser/controller.js';
|
package/dist/config/options.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FileViewerArchiveOptions, FileViewerCadOptions, FileViewerOptions, FileViewerResolvedThemeMode, FileViewerThemeMode, FileViewerToolbarOptions, FileViewerUiDensity } from
|
|
1
|
+
import type { FileViewerArchiveOptions, FileViewerCadOptions, FileViewerOptions, FileViewerResolvedThemeMode, FileViewerThemeMode, FileViewerToolbarOptions, FileViewerUiDensity } from '../contracts/types.js';
|
|
2
2
|
export type FileViewerSerializableToolbarOptions = Omit<FileViewerToolbarOptions, 'beforeOperation' | 'beforeDownload' | 'beforePrint' | 'beforeExportHtml'>;
|
|
3
3
|
export type FileViewerSerializableCadOptions = Omit<FileViewerCadOptions, 'workerUrl'> & {
|
|
4
4
|
workerUrl?: string;
|