@apollo-design/react-docx-preview 0.1.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/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright (c) 2016-2023 Volodymyr Baydalka
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,20 @@
1
+ import { CSSProperties } from 'react';
2
+ import { Options } from '@apollo-design/docx-preview';
3
+ export interface DocxViewerProps {
4
+ /** document data — Blob / ArrayBuffer / Uint8Array; re-renders on change */
5
+ data?: Blob | ArrayBuffer | Uint8Array | null;
6
+ /** document name for the download button */
7
+ name?: string;
8
+ /** 'fit' | number (0.75, 75, …) — initial zoom; mount-time only */
9
+ initialScale?: 'fit' | number;
10
+ /** initial thumbnail sidebar visibility; mount-time only */
11
+ showThumbs?: boolean;
12
+ /** library render options, merged over { paginate, experimental } */
13
+ renderOptions?: Partial<Options>;
14
+ className?: string;
15
+ style?: CSSProperties;
16
+ onRendered?: (result: unknown) => void;
17
+ onError?: (error: unknown) => void;
18
+ }
19
+ export declare function DocxViewer({ data, name, initialScale, showThumbs, renderOptions, className, style, onRendered, onError, }: DocxViewerProps): import("react").JSX.Element;
20
+ export default DocxViewer;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function DocumentStage(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function ErrorBanner(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function ThumbnailsSidebar(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function Toolbar(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function IconChevronDown(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function IconDownload(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function IconNextPage(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function IconOpenFile(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function IconPrevPage(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function IconThumbs(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function IconZoomIn(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare function IconZoomOut(): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { DocxViewerApi } from './hooks/useDocViewer';
2
+ export declare const DocxViewerContext: import('react').Context<{
3
+ loading: boolean;
4
+ error: {
5
+ title: string;
6
+ detail: string;
7
+ };
8
+ docLoaded: boolean;
9
+ currentPage: number;
10
+ pageCount: number;
11
+ currentScale: number;
12
+ scaleMode: number | "fit";
13
+ thumbsVisible: boolean;
14
+ dropdownOpen: boolean;
15
+ setDropdownOpen: import('react').Dispatch<import('react').SetStateAction<boolean>>;
16
+ pagerShown: boolean;
17
+ thumbsShown: boolean;
18
+ canPrev: boolean;
19
+ canNext: boolean;
20
+ canZoomOut: boolean;
21
+ canZoomIn: boolean;
22
+ scaleLabel: string;
23
+ open: (data: ArrayBuffer | Uint8Array | Blob, name?: string) => Promise<void>;
24
+ goToPage: (index: number) => void;
25
+ jumpToPage: (inputValue: string) => void;
26
+ zoomStep: (direction: 1 | -1) => void;
27
+ setScale: (value: string) => void;
28
+ toggleThumbs: () => void;
29
+ download: () => void;
30
+ stage: import('react').RefObject<HTMLElement>;
31
+ docBox: import('react').RefObject<HTMLElement>;
32
+ thumbs: import('react').RefObject<HTMLElement>;
33
+ }>;
34
+ export declare function useDocxViewer(): DocxViewerApi;
@@ -0,0 +1,50 @@
1
+ import { RefObject } from 'react';
2
+ import { Options } from '@apollo-design/docx-preview';
3
+ export interface DocxViewerRefs {
4
+ stage: RefObject<HTMLElement | null>;
5
+ docBox: RefObject<HTMLElement | null>;
6
+ thumbs: RefObject<HTMLElement | null>;
7
+ }
8
+ export interface DocxViewerOptions {
9
+ /** 'fit' (fit-width, never upscales) or a number like 0.75 / 75 */
10
+ initialScale?: 'fit' | number;
11
+ /** initial thumbnail sidebar visibility (default true) */
12
+ showThumbs?: boolean;
13
+ /** library render options, merged over { hideWrapperOnPrint, paginate, experimental } */
14
+ renderOptions?: Partial<Options>;
15
+ onRendered?: (result: unknown) => void;
16
+ onError?: (error: unknown) => void;
17
+ }
18
+ export declare function useDocViewer(refs: DocxViewerRefs, options?: DocxViewerOptions): {
19
+ loading: boolean;
20
+ error: {
21
+ title: string;
22
+ detail: string;
23
+ };
24
+ docLoaded: boolean;
25
+ currentPage: number;
26
+ pageCount: number;
27
+ currentScale: number;
28
+ scaleMode: number | "fit";
29
+ thumbsVisible: boolean;
30
+ dropdownOpen: boolean;
31
+ setDropdownOpen: import('react').Dispatch<import('react').SetStateAction<boolean>>;
32
+ pagerShown: boolean;
33
+ thumbsShown: boolean;
34
+ canPrev: boolean;
35
+ canNext: boolean;
36
+ canZoomOut: boolean;
37
+ canZoomIn: boolean;
38
+ scaleLabel: string;
39
+ open: (data: Blob | ArrayBuffer | Uint8Array | null | undefined, name?: string) => Promise<void>;
40
+ goToPage: (index: number) => void;
41
+ jumpToPage: (inputValue: string) => void;
42
+ zoomStep: (direction: 1 | -1) => void;
43
+ setScale: (value: string) => void;
44
+ toggleThumbs: () => void;
45
+ download: () => void;
46
+ stage: RefObject<HTMLElement>;
47
+ docBox: RefObject<HTMLElement>;
48
+ thumbs: RefObject<HTMLElement>;
49
+ };
50
+ export type DocxViewerApi = ReturnType<typeof useDocViewer>;
@@ -0,0 +1,5 @@
1
+ export { DocxViewer, DocxViewer as DocxPreview, default } from './DocxViewer';
2
+ export type { DocxViewerProps } from './DocxViewer';
3
+ export { useDocViewer } from './hooks/useDocViewer';
4
+ export type { DocxViewerApi, DocxViewerOptions, DocxViewerRefs } from './hooks/useDocViewer';
5
+ export { renderAsync, parseAsync, renderDocument, defaultOptions } from '@apollo-design/docx-preview';
package/dist/index.js ADDED
@@ -0,0 +1,684 @@
1
+ import { createContext as e, useContext as t, useEffect as n, useRef as r, useState as i } from "react";
2
+ import { defaultOptions as a, parseAsync as o, renderAsync as s, renderAsync as c, renderDocument as l } from "@apollo-design/docx-preview";
3
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
4
+ //#region src/hooks/useDocViewer.ts
5
+ var f = .1, p = 4, m = .25, h = 1, g = [
6
+ "tl",
7
+ "tr",
8
+ "bl",
9
+ "br"
10
+ ];
11
+ function _(e) {
12
+ if (!e || e.byteLength < 4) return !1;
13
+ let t = new Uint8Array(e, 0, 4);
14
+ return t[0] === 208 && t[1] === 207 && t[2] === 17 && t[3] === 224;
15
+ }
16
+ function v(e) {
17
+ return e && typeof e == "object" && "message" in e ? String(e.message) : String(e);
18
+ }
19
+ function y(e) {
20
+ if (typeof e == "number" && isFinite(e) && e > 0) {
21
+ let t = e > 10 ? e / 100 : e;
22
+ return Math.max(f, Math.min(p, Math.round(t * 100) / 100));
23
+ }
24
+ return "fit";
25
+ }
26
+ function b(e, t = {}) {
27
+ let [a, o] = i(!1), [s, l] = i(null), [u, d] = i(!1), [b, x] = i(0), [S, C] = i(0), [w, T] = i(1), [E, D] = i(() => y(t.initialScale)), [O, k] = i(t.showThumbs !== !1), [A, j] = i(!1), M = r({
28
+ docName: "",
29
+ docBlob: null,
30
+ wrapperEl: null,
31
+ sections: [],
32
+ naturalWidth: 0,
33
+ naturalHeight: 0,
34
+ currentPage: 0,
35
+ pageObserver: null,
36
+ thumbPageSynced: -1
37
+ }), N = r({
38
+ docLoaded: u,
39
+ currentPage: b,
40
+ scaleMode: E,
41
+ thumbsVisible: O,
42
+ currentScale: w
43
+ });
44
+ N.current = {
45
+ docLoaded: u,
46
+ currentPage: b,
47
+ scaleMode: E,
48
+ thumbsVisible: O,
49
+ currentScale: w
50
+ };
51
+ let P = r(t);
52
+ P.current = t;
53
+ let F = S > 1, I = O && S > 1 && u, L = u && b > 0, ee = u && b < S - 1, te = u && w > .100001, R = u && w < 3.999999, z = E === "fit" ? "适应宽度" : Math.round(E * 100) + "%";
54
+ function B() {
55
+ let t = e.thumbs.current;
56
+ if (!t) return;
57
+ let n = t.children, r = M.current.currentPage;
58
+ for (let e = 0; e < n.length; e++) n[e].classList.toggle("current", Number(n[e].getAttribute("data-page")) === r);
59
+ N.current.thumbsVisible && n.length > 1 && N.current.docLoaded && M.current.thumbPageSynced !== r && n[r] && (M.current.thumbPageSynced = r, n[r].scrollIntoView({ block: "nearest" }));
60
+ }
61
+ function V() {
62
+ let t = e.thumbs.current;
63
+ if (!t) return;
64
+ t.innerHTML = "", M.current.thumbPageSynced = -1;
65
+ let n = M.current.sections;
66
+ if (n.length <= 1 || !M.current.naturalWidth) return;
67
+ let r = 94 / M.current.naturalWidth;
68
+ for (let e = 0; e < n.length; e++) {
69
+ let i = document.createElement("div");
70
+ i.className = "thumb", i.setAttribute("data-page", String(e));
71
+ let a = document.createElement("div");
72
+ a.className = "thumb-page", a.style.height = Math.ceil(n[e].offsetHeight * r) + "px";
73
+ let o = n[e].cloneNode(!0);
74
+ o.removeAttribute("data-page"), o.style.transform = "scale(" + r + ")", o.style.transformOrigin = "top left", a.appendChild(o);
75
+ let s = document.createElement("div");
76
+ s.className = "thumb-label", s.textContent = String(e + 1), i.appendChild(a), i.appendChild(s), t.appendChild(i);
77
+ }
78
+ B();
79
+ }
80
+ function H() {
81
+ let t = e.stage.current;
82
+ if (!M.current.naturalWidth || !t) return 1;
83
+ let n = t.clientWidth - 48;
84
+ return Math.min(h, n / M.current.naturalWidth);
85
+ }
86
+ function U() {
87
+ let t = e.stage.current, n = e.docBox.current, r = M.current.wrapperEl, i = M.current.naturalWidth;
88
+ if (!N.current.docLoaded || !r || !i || !t || !n) return;
89
+ let a = t.scrollHeight - t.clientHeight, o = t.scrollWidth - t.clientWidth, s = a > 0 ? t.scrollTop / a : 0, c = o > 0 ? t.scrollLeft / o : 0, l = N.current.scaleMode, u = l === "fit" ? H() : l;
90
+ T(u), n.style.width = Math.ceil(i * u) + "px", n.style.height = Math.ceil(M.current.naturalHeight * u) + "px", r.style.width = i + "px", r.style.transform = "scale(" + u + ")", r.style.transformOrigin = "top left";
91
+ let d = t.scrollHeight - t.clientHeight, f = t.scrollWidth - t.clientWidth;
92
+ d > 0 && (t.scrollTop = s * d), f > 0 && (t.scrollLeft = c * f);
93
+ }
94
+ function W(e) {
95
+ let t = N.current.currentScale / m, n = Math.round(t);
96
+ Math.abs(t - n) < .01 && (t = n);
97
+ let r = e > 0 ? (Math.floor(t) + 1) * m : (Math.ceil(t) - 1) * m;
98
+ G(Math.max(f, Math.min(p, Math.round(r * 100) / 100)));
99
+ }
100
+ function G(e) {
101
+ N.current.scaleMode = e, D(e), U();
102
+ }
103
+ function K(e) {
104
+ j(!1), G(e === "fit" ? "fit" : parseFloat(e));
105
+ }
106
+ function q() {
107
+ let e = !N.current.thumbsVisible;
108
+ k(e), N.current.thumbsVisible = e, N.current.docLoaded && N.current.scaleMode === "fit" && U();
109
+ }
110
+ function J(e) {
111
+ let t = getComputedStyle(e), n = parseFloat(t.paddingTop) || 0, r = parseFloat(t.paddingRight) || 0, i = parseFloat(t.paddingBottom) || 0, a = parseFloat(t.paddingLeft) || 0, o = e.offsetWidth - r, s = e.offsetHeight - i, c = Math.max(4, Math.round(Math.min(n, r, i, a) * .25)), l = {
112
+ tl: {
113
+ left: a - c,
114
+ top: n - c
115
+ },
116
+ tr: {
117
+ left: o,
118
+ top: n - c
119
+ },
120
+ bl: {
121
+ left: a - c,
122
+ top: s
123
+ },
124
+ br: {
125
+ left: o,
126
+ top: s
127
+ }
128
+ };
129
+ for (let t of g) {
130
+ let n = document.createElement("i");
131
+ n.className = "page-corner " + t, n.setAttribute("aria-hidden", "true"), n.style.width = c + "px", n.style.height = c + "px", n.style.left = l[t].left + "px", n.style.top = l[t].top + "px", e.appendChild(n);
132
+ }
133
+ }
134
+ function Y(t) {
135
+ let n = e.stage.current;
136
+ if (!n) return;
137
+ let r = parseFloat(getComputedStyle(n).paddingTop) || 0;
138
+ n.scrollTop = n.scrollTop + (t.getBoundingClientRect().top - n.getBoundingClientRect().top) - r;
139
+ }
140
+ function X(e) {
141
+ let t = M.current.sections;
142
+ if (!N.current.docLoaded || t.length === 0) return;
143
+ let n = Math.max(0, Math.min(e, t.length - 1));
144
+ M.current.currentPage = n, x(n), Y(t[n]);
145
+ }
146
+ function Z(e) {
147
+ let t = parseInt(e, 10);
148
+ Number.isNaN(t) || X(t - 1);
149
+ }
150
+ function ne() {
151
+ M.current.pageObserver && (M.current.pageObserver.disconnect(), M.current.pageObserver = null);
152
+ let t = e.stage.current, n = M.current.sections;
153
+ if (typeof window.IntersectionObserver != "function" || n.length <= 1 || !t) return;
154
+ let r = [];
155
+ M.current.pageObserver = new IntersectionObserver((e) => {
156
+ for (let t of e) {
157
+ let e = Number(t.target.getAttribute("data-page"));
158
+ Number.isNaN(e) || (r[e] = t.intersectionRatio);
159
+ }
160
+ let t = -1, n = -1;
161
+ for (let e = 0; e < r.length; e++) r[e] > n && (n = r[e], t = e);
162
+ t >= 0 && n > 0 && (M.current.currentPage = t, x(t));
163
+ }, {
164
+ root: t,
165
+ threshold: [
166
+ 0,
167
+ .25,
168
+ .5,
169
+ .75,
170
+ 1
171
+ ]
172
+ });
173
+ for (let e of n) M.current.pageObserver.observe(e);
174
+ }
175
+ function Q() {
176
+ M.current.pageObserver && (M.current.pageObserver.disconnect(), M.current.pageObserver = null);
177
+ let t = e.docBox.current, n = e.thumbs.current;
178
+ t && (t.innerHTML = "", t.style.width = "", t.style.height = ""), n && (n.innerHTML = ""), M.current.thumbPageSynced = -1, M.current.wrapperEl = null, M.current.sections = [], M.current.naturalWidth = 0, M.current.naturalHeight = 0, M.current.currentPage = 0, x(0), C(0), d(!1);
179
+ }
180
+ async function $(t, n) {
181
+ if (Q(), l(null), j(!1), o(!0), _(t)) {
182
+ o(!1);
183
+ let e = /* @__PURE__ */ Error("legacy binary document (.wps/.doc), not OOXML");
184
+ l({
185
+ title: "无法打开" + (n ? "「" + n + "」" : "文档"),
186
+ detail: "这是旧版二进制文档格式(.wps / .doc),不是 OOXML(.docx)。即使是新版 WPS Office,默认保存的 .wps 仍是二进制格式。请用 WPS 或 Word 将其另存为 .docx 后重试。"
187
+ }), P.current.onError?.(e);
188
+ return;
189
+ }
190
+ let r = {
191
+ hideWrapperOnPrint: !0,
192
+ paginate: !0,
193
+ experimental: !0,
194
+ ...P.current.renderOptions
195
+ };
196
+ try {
197
+ let n = e.docBox.current, i = e.stage.current;
198
+ if (!n || !i) throw Error("viewer is not mounted");
199
+ let a = await c(t, n, void 0, r), o = n.querySelector(".docx-wrapper") || n;
200
+ M.current.wrapperEl = o;
201
+ let s = o.querySelectorAll("section"), l = [];
202
+ for (let e = 0; e < s.length; e++) {
203
+ let t = s[e];
204
+ t.setAttribute("data-page", String(e)), J(t), l.push(t);
205
+ }
206
+ M.current.sections = l;
207
+ let u = 0;
208
+ for (let e of l) u = Math.max(u, e.offsetWidth);
209
+ u || (u = o.scrollWidth), M.current.naturalWidth = u, M.current.naturalHeight = o.offsetHeight, M.current.currentPage = 0, N.current.docLoaded = !0, d(!0), C(l.length), x(0), U(), V(), ne(), i.scrollTop = 0, i.scrollLeft = 0, P.current.onRendered?.(a);
210
+ } catch (e) {
211
+ console.error(e), Q(), l({
212
+ title: "无法打开" + (n ? "「" + n + "」" : "文档"),
213
+ detail: v(e) + " —— 请确认这是有效且未加密的 .docx 文件。"
214
+ }), P.current.onError?.(e);
215
+ } finally {
216
+ o(!1);
217
+ }
218
+ }
219
+ async function re(e, t) {
220
+ if (!e) return;
221
+ let n = t || typeof File < "u" && e instanceof File && e.name || "document.docx";
222
+ if (M.current.docName = n, M.current.docBlob = e instanceof Blob ? e : new Blob([e]), e instanceof Blob) {
223
+ let t;
224
+ try {
225
+ t = await new Promise((t, n) => {
226
+ let r = new FileReader();
227
+ r.onload = () => t(r.result), r.onerror = () => n(r.error || /* @__PURE__ */ Error("FileReader failed")), r.readAsArrayBuffer(e);
228
+ });
229
+ } catch (e) {
230
+ l({
231
+ title: "读取文件失败",
232
+ detail: "浏览器无法读取「" + n + "」,请重试。"
233
+ }), P.current.onError?.(e);
234
+ return;
235
+ }
236
+ return $(t, n);
237
+ }
238
+ return $(e, n);
239
+ }
240
+ function ie() {
241
+ let e = M.current.docBlob;
242
+ if (!e) return;
243
+ let t = URL.createObjectURL(e), n = document.createElement("a");
244
+ n.href = t, n.download = M.current.docName || "document.docx", document.body.appendChild(n), n.click(), document.body.removeChild(n), setTimeout(() => URL.revokeObjectURL(t), 1e4);
245
+ }
246
+ return n(() => {
247
+ function t() {
248
+ N.current.docLoaded && N.current.scaleMode === "fit" && U();
249
+ }
250
+ function n(e) {
251
+ e.target.closest(".docx-viewer .scale-select") || j(!1);
252
+ }
253
+ function r(t) {
254
+ t.key === "Escape" && j(!1);
255
+ let n = t.target && t.target.tagName;
256
+ n !== "INPUT" && n !== "TEXTAREA" && n !== "SELECT" && (!e.stage.current || !e.stage.current.contains(t.target) || (t.key === "ArrowLeft" || t.key === "ArrowUp" || t.key === "PageUp" ? (t.preventDefault(), X(N.current.currentPage - 1)) : t.key === "ArrowRight" || t.key === "ArrowDown" || t.key === "PageDown" || t.key === " " ? (t.preventDefault(), X(N.current.currentPage + 1)) : t.key === "Home" ? X(0) : t.key === "End" && X(M.current.sections.length - 1)));
257
+ }
258
+ return window.addEventListener("resize", t), document.addEventListener("click", n), document.addEventListener("keydown", r), () => {
259
+ window.removeEventListener("resize", t), document.removeEventListener("click", n), document.removeEventListener("keydown", r), M.current.pageObserver && (M.current.pageObserver.disconnect(), M.current.pageObserver = null);
260
+ };
261
+ }, []), n(() => {
262
+ B();
263
+ }, [b, I]), {
264
+ ...e,
265
+ loading: a,
266
+ error: s,
267
+ docLoaded: u,
268
+ currentPage: b,
269
+ pageCount: S,
270
+ currentScale: w,
271
+ scaleMode: E,
272
+ thumbsVisible: O,
273
+ dropdownOpen: A,
274
+ setDropdownOpen: j,
275
+ pagerShown: F,
276
+ thumbsShown: I,
277
+ canPrev: L,
278
+ canNext: ee,
279
+ canZoomOut: te,
280
+ canZoomIn: R,
281
+ scaleLabel: z,
282
+ open: re,
283
+ goToPage: X,
284
+ jumpToPage: Z,
285
+ zoomStep: W,
286
+ setScale: K,
287
+ toggleThumbs: q,
288
+ download: ie
289
+ };
290
+ }
291
+ //#endregion
292
+ //#region src/context.ts
293
+ var x = e(null);
294
+ function S() {
295
+ let e = t(x);
296
+ if (!e) throw Error("DocxViewer components must be used inside <DocxViewer>");
297
+ return e;
298
+ }
299
+ //#endregion
300
+ //#region src/components/icons/IconThumbs.tsx
301
+ function C() {
302
+ return /* @__PURE__ */ u("svg", {
303
+ width: "16",
304
+ height: "16",
305
+ fill: "none",
306
+ viewBox: "0 0 24 24",
307
+ "aria-hidden": "true",
308
+ children: /* @__PURE__ */ u("path", {
309
+ fill: "currentColor",
310
+ fillRule: "evenodd",
311
+ d: "M21 5H11v14h10zM3 5h6v14H3zm0-2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm2 4.25a.75.75 0 0 0 0 1.5h2a.75.75 0 0 0 0-1.5zm-.75 3.5A.75.75 0 0 1 5 10h2a.75.75 0 0 1 0 1.5H5a.75.75 0 0 1-.75-.75m.75 2a.75.75 0 0 0 0 1.5h2a.75.75 0 0 0 0-1.5z",
312
+ clipRule: "evenodd"
313
+ })
314
+ });
315
+ }
316
+ //#endregion
317
+ //#region src/components/icons/IconPrevPage.tsx
318
+ function w() {
319
+ return /* @__PURE__ */ u("svg", {
320
+ width: "16",
321
+ height: "16",
322
+ viewBox: "0 0 16 16",
323
+ fill: "none",
324
+ "aria-hidden": "true",
325
+ children: /* @__PURE__ */ u("path", {
326
+ d: "M3 10l5-5 5 5",
327
+ stroke: "currentColor",
328
+ strokeWidth: "1.5",
329
+ strokeLinecap: "round",
330
+ strokeLinejoin: "round"
331
+ })
332
+ });
333
+ }
334
+ //#endregion
335
+ //#region src/components/icons/IconNextPage.tsx
336
+ function T() {
337
+ return /* @__PURE__ */ u("svg", {
338
+ width: "16",
339
+ height: "16",
340
+ viewBox: "0 0 16 16",
341
+ fill: "none",
342
+ "aria-hidden": "true",
343
+ children: /* @__PURE__ */ u("path", {
344
+ d: "M3 6l5 5 5-5",
345
+ stroke: "currentColor",
346
+ strokeWidth: "1.5",
347
+ strokeLinecap: "round",
348
+ strokeLinejoin: "round"
349
+ })
350
+ });
351
+ }
352
+ //#endregion
353
+ //#region src/components/icons/IconZoomOut.tsx
354
+ function E() {
355
+ return /* @__PURE__ */ d("svg", {
356
+ width: "16",
357
+ height: "16",
358
+ viewBox: "0 0 16 16",
359
+ fill: "none",
360
+ "aria-hidden": "true",
361
+ children: [/* @__PURE__ */ u("circle", {
362
+ cx: "7",
363
+ cy: "7",
364
+ r: "4.5",
365
+ stroke: "currentColor",
366
+ strokeWidth: "1.5"
367
+ }), /* @__PURE__ */ u("path", {
368
+ d: "M4.8 7h4.4M10.2 10.2L14 14",
369
+ stroke: "currentColor",
370
+ strokeWidth: "1.5",
371
+ strokeLinecap: "round",
372
+ strokeLinejoin: "round"
373
+ })]
374
+ });
375
+ }
376
+ //#endregion
377
+ //#region src/components/icons/IconZoomIn.tsx
378
+ function D() {
379
+ return /* @__PURE__ */ d("svg", {
380
+ width: "16",
381
+ height: "16",
382
+ viewBox: "0 0 16 16",
383
+ fill: "none",
384
+ "aria-hidden": "true",
385
+ children: [/* @__PURE__ */ u("circle", {
386
+ cx: "7",
387
+ cy: "7",
388
+ r: "4.5",
389
+ stroke: "currentColor",
390
+ strokeWidth: "1.5"
391
+ }), /* @__PURE__ */ u("path", {
392
+ d: "M4.8 7h4.4M7 4.8v4.4M10.2 10.2L14 14",
393
+ stroke: "currentColor",
394
+ strokeWidth: "1.5",
395
+ strokeLinecap: "round",
396
+ strokeLinejoin: "round"
397
+ })]
398
+ });
399
+ }
400
+ //#endregion
401
+ //#region src/components/icons/IconChevronDown.tsx
402
+ function O() {
403
+ return /* @__PURE__ */ u("svg", {
404
+ width: "10",
405
+ height: "10",
406
+ viewBox: "0 0 10 10",
407
+ fill: "none",
408
+ "aria-hidden": "true",
409
+ children: /* @__PURE__ */ u("path", {
410
+ d: "M2 3.5l3 3 3-3",
411
+ stroke: "currentColor",
412
+ strokeWidth: "1.5",
413
+ strokeLinecap: "round",
414
+ strokeLinejoin: "round"
415
+ })
416
+ });
417
+ }
418
+ //#endregion
419
+ //#region src/components/icons/IconOpenFile.tsx
420
+ function k() {
421
+ return /* @__PURE__ */ u("svg", {
422
+ width: "16",
423
+ height: "16",
424
+ viewBox: "0 0 16 16",
425
+ fill: "none",
426
+ "aria-hidden": "true",
427
+ children: /* @__PURE__ */ u("path", {
428
+ d: "M2 5.2A1.7 1.7 0 0 1 3.7 3.5h2.5l1.5 1.7h4.6a1.7 1.7 0 0 1 1.7 1.7v4.4a1.7 1.7 0 0 1-1.7 1.7H3.7A1.7 1.7 0 0 1 2 11.3V5.2Z",
429
+ stroke: "currentColor",
430
+ strokeWidth: "1.5",
431
+ strokeLinejoin: "round"
432
+ })
433
+ });
434
+ }
435
+ //#endregion
436
+ //#region src/components/icons/IconDownload.tsx
437
+ function A() {
438
+ return /* @__PURE__ */ u("svg", {
439
+ width: "16",
440
+ height: "16",
441
+ viewBox: "0 0 16 16",
442
+ fill: "none",
443
+ "aria-hidden": "true",
444
+ children: /* @__PURE__ */ u("path", {
445
+ d: "M8 2.5V10M8 10L4.8 6.8 8 10l3.2-3.2M2.5 13.5h11",
446
+ stroke: "currentColor",
447
+ strokeWidth: "1.5",
448
+ strokeLinecap: "round",
449
+ strokeLinejoin: "round"
450
+ })
451
+ });
452
+ }
453
+ //#endregion
454
+ //#region src/components/Toolbar.tsx
455
+ var j = [
456
+ "fit",
457
+ "0.5",
458
+ "0.75",
459
+ "1",
460
+ "1.25",
461
+ "1.5",
462
+ "2"
463
+ ];
464
+ function M() {
465
+ let e = S(), t = r(null);
466
+ function n(t) {
467
+ let n = t.currentTarget;
468
+ e.jumpToPage(n.value), n.blur();
469
+ }
470
+ function i(t) {
471
+ let n = t.currentTarget, r = n.files && n.files[0];
472
+ r && e.open(r), n.value = "";
473
+ }
474
+ return /* @__PURE__ */ d("header", {
475
+ className: "toolbar",
476
+ hidden: !e.docLoaded,
477
+ children: [
478
+ /* @__PURE__ */ d("div", {
479
+ className: "toolbar-left",
480
+ children: [
481
+ /* @__PURE__ */ u("button", {
482
+ className: e.thumbsShown ? "tbtn active" : "tbtn",
483
+ type: "button",
484
+ title: "缩略图",
485
+ disabled: !e.docLoaded || e.pageCount <= 1,
486
+ onClick: e.toggleThumbs,
487
+ children: /* @__PURE__ */ u(C, {})
488
+ }),
489
+ /* @__PURE__ */ u("span", { className: "toolbar-divider" }),
490
+ /* @__PURE__ */ d("span", {
491
+ className: "pager",
492
+ hidden: !e.pagerShown,
493
+ children: [
494
+ /* @__PURE__ */ u("button", {
495
+ className: "tbtn",
496
+ type: "button",
497
+ title: "上一页",
498
+ disabled: !e.canPrev,
499
+ onClick: () => e.goToPage(e.currentPage - 1),
500
+ children: /* @__PURE__ */ u(w, {})
501
+ }),
502
+ /* @__PURE__ */ d("span", {
503
+ className: "page-info",
504
+ children: [/* @__PURE__ */ u("input", {
505
+ className: "page-input",
506
+ type: "number",
507
+ min: 1,
508
+ step: 1,
509
+ defaultValue: e.pageCount > 0 ? e.currentPage + 1 : "",
510
+ "aria-label": "当前页码",
511
+ onKeyDown: (e) => {
512
+ e.key === "Enter" && n(e);
513
+ },
514
+ onBlur: n
515
+ }, e.currentPage), /* @__PURE__ */ d("span", {
516
+ className: "page-total",
517
+ children: ["/ ", e.pageCount > 0 ? e.pageCount : "–"]
518
+ })]
519
+ }),
520
+ /* @__PURE__ */ u("button", {
521
+ className: "tbtn",
522
+ type: "button",
523
+ title: "下一页",
524
+ disabled: !e.canNext,
525
+ onClick: () => e.goToPage(e.currentPage + 1),
526
+ children: /* @__PURE__ */ u(T, {})
527
+ })
528
+ ]
529
+ })
530
+ ]
531
+ }),
532
+ /* @__PURE__ */ u("div", {
533
+ className: "toolbar-center",
534
+ children: /* @__PURE__ */ d("div", {
535
+ className: "zoom-group",
536
+ children: [
537
+ /* @__PURE__ */ u("button", {
538
+ className: "tbtn",
539
+ type: "button",
540
+ title: "缩小",
541
+ disabled: !e.canZoomOut,
542
+ onClick: () => e.zoomStep(-1),
543
+ children: /* @__PURE__ */ u(E, {})
544
+ }),
545
+ /* @__PURE__ */ d("div", {
546
+ className: "scale-select",
547
+ children: [/* @__PURE__ */ d("button", {
548
+ className: "tbtn scale-display",
549
+ type: "button",
550
+ onClick: (t) => {
551
+ t.stopPropagation(), e.setDropdownOpen(!e.dropdownOpen);
552
+ },
553
+ children: [/* @__PURE__ */ u("span", { children: e.scaleLabel }), /* @__PURE__ */ u(O, {})]
554
+ }), /* @__PURE__ */ u("div", {
555
+ className: "dropdown",
556
+ hidden: !e.dropdownOpen,
557
+ children: j.map((t) => /* @__PURE__ */ u("button", {
558
+ type: "button",
559
+ className: e.scaleMode === "fit" && t === "fit" || String(e.scaleMode) === t ? "selected" : void 0,
560
+ onClick: () => e.setScale(t),
561
+ children: t === "fit" ? "适应宽度" : Math.round(parseFloat(t) * 100) + "%"
562
+ }, t))
563
+ })]
564
+ }),
565
+ /* @__PURE__ */ u("button", {
566
+ className: "tbtn",
567
+ type: "button",
568
+ title: "放大",
569
+ disabled: !e.canZoomIn,
570
+ onClick: () => e.zoomStep(1),
571
+ children: /* @__PURE__ */ u(D, {})
572
+ })
573
+ ]
574
+ })
575
+ }),
576
+ /* @__PURE__ */ d("div", {
577
+ className: "toolbar-right",
578
+ children: [
579
+ /* @__PURE__ */ u("button", {
580
+ className: "tbtn",
581
+ type: "button",
582
+ title: "打开文档",
583
+ onClick: () => t.current?.click(),
584
+ children: /* @__PURE__ */ u(k, {})
585
+ }),
586
+ /* @__PURE__ */ u("button", {
587
+ className: "tbtn",
588
+ type: "button",
589
+ title: "下载文档",
590
+ disabled: !e.docLoaded,
591
+ onClick: e.download,
592
+ children: /* @__PURE__ */ u(A, {})
593
+ }),
594
+ /* @__PURE__ */ u("input", {
595
+ ref: t,
596
+ type: "file",
597
+ accept: ".docx",
598
+ hidden: !0,
599
+ onChange: i
600
+ })
601
+ ]
602
+ })
603
+ ]
604
+ });
605
+ }
606
+ //#endregion
607
+ //#region src/components/ErrorBanner.tsx
608
+ function N() {
609
+ let e = S();
610
+ return /* @__PURE__ */ d("div", {
611
+ className: "error-banner",
612
+ hidden: !e.error,
613
+ children: [/* @__PURE__ */ u("strong", { children: e.error?.title }), /* @__PURE__ */ u("span", { children: e.error?.detail })]
614
+ });
615
+ }
616
+ //#endregion
617
+ //#region src/components/ThumbnailsSidebar.tsx
618
+ function P() {
619
+ let e = S();
620
+ function t(t) {
621
+ let n = t.target.closest(".thumb");
622
+ n && e.goToPage(Number(n.getAttribute("data-page")));
623
+ }
624
+ return /* @__PURE__ */ u("aside", {
625
+ ref: e.thumbs,
626
+ className: "thumbs",
627
+ hidden: !e.thumbsShown,
628
+ "aria-label": "页面缩略图",
629
+ onClick: t
630
+ });
631
+ }
632
+ //#endregion
633
+ //#region src/components/DocumentStage.tsx
634
+ function F() {
635
+ let e = S();
636
+ return /* @__PURE__ */ d("main", {
637
+ ref: e.stage,
638
+ className: "stage",
639
+ children: [/* @__PURE__ */ d("div", {
640
+ className: "loading",
641
+ hidden: !e.loading,
642
+ children: [/* @__PURE__ */ u("div", { className: "spinner" }), /* @__PURE__ */ u("p", { children: "加载中…" })]
643
+ }), /* @__PURE__ */ u("div", {
644
+ ref: e.docBox,
645
+ className: "document"
646
+ })]
647
+ });
648
+ }
649
+ //#endregion
650
+ //#region src/DocxViewer.tsx
651
+ function I({ data: e, name: t, initialScale: i, showThumbs: a, renderOptions: o, className: s, style: c, onRendered: l, onError: f }) {
652
+ let p = b({
653
+ stage: r(null),
654
+ docBox: r(null),
655
+ thumbs: r(null)
656
+ }, {
657
+ initialScale: i,
658
+ showThumbs: a,
659
+ renderOptions: o,
660
+ onRendered: l,
661
+ onError: f
662
+ });
663
+ return n(() => {
664
+ e && p.open(e, t);
665
+ }, [e, t]), /* @__PURE__ */ u(x.Provider, {
666
+ value: p,
667
+ children: /* @__PURE__ */ d("div", {
668
+ className: s ? `docx-viewer ${s}` : "docx-viewer",
669
+ style: c,
670
+ children: [
671
+ /* @__PURE__ */ u(M, {}),
672
+ /* @__PURE__ */ u(N, {}),
673
+ /* @__PURE__ */ d("div", {
674
+ className: "body",
675
+ children: [/* @__PURE__ */ u(P, {}), /* @__PURE__ */ u(F, {})]
676
+ })
677
+ ]
678
+ })
679
+ });
680
+ }
681
+ //#endregion
682
+ export { I as DocxPreview, I as DocxViewer, I as default, a as defaultOptions, o as parseAsync, s as renderAsync, l as renderDocument, b as useDocViewer };
683
+
684
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/hooks/useDocViewer.ts","../src/context.ts","../src/components/icons/IconThumbs.tsx","../src/components/icons/IconPrevPage.tsx","../src/components/icons/IconNextPage.tsx","../src/components/icons/IconZoomOut.tsx","../src/components/icons/IconZoomIn.tsx","../src/components/icons/IconChevronDown.tsx","../src/components/icons/IconOpenFile.tsx","../src/components/icons/IconDownload.tsx","../src/components/Toolbar.tsx","../src/components/ErrorBanner.tsx","../src/components/ThumbnailsSidebar.tsx","../src/components/DocumentStage.tsx","../src/DocxViewer.tsx"],"sourcesContent":["// Core viewer logic, ported from apps/viewer/viewer.js. UI-facing values are\n// React state; everything else (library render output, thumbnail clones, page\n// corners, zoom transforms) is imperative DOM work against element refs.\n// A ref mirror (stateRef) lets the once-registered global listeners read\n// fresh values.\nimport { useEffect, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\nimport { renderAsync } from '@apollo-design/docx-preview';\nimport type { Options } from '@apollo-design/docx-preview';\n\n// Zoom-button range. Stepping clamps here so a step never strands the\n// buttons in a disabled state.\nconst SCALE_MIN = 0.1;\nconst SCALE_MAX = 4;\nconst SCALE_STEP = 0.25;\n// Fit-width never upscales: a document reads best at its natural size.\nconst FIT_MAX = 1;\nconst STAGE_PADDING = 24; // .stage padding on each side, see viewer.css\nconst THUMB_WIDTH = 96; // matches .thumb-page width in viewer.css\nconst CORNER_NAMES = ['tl', 'tr', 'bl', 'br'] as const;\n\nexport interface DocxViewerRefs {\n stage: RefObject<HTMLElement | null>;\n docBox: RefObject<HTMLElement | null>;\n thumbs: RefObject<HTMLElement | null>;\n}\n\nexport interface DocxViewerOptions {\n /** 'fit' (fit-width, never upscales) or a number like 0.75 / 75 */\n initialScale?: 'fit' | number;\n /** initial thumbnail sidebar visibility (default true) */\n showThumbs?: boolean;\n /** library render options, merged over { hideWrapperOnPrint, paginate, experimental } */\n renderOptions?: Partial<Options>;\n onRendered?: (result: unknown) => void;\n onError?: (error: unknown) => void;\n}\n\n// OLE2 compound-file magic (D0 CF 11 E0 ...): legacy binary documents —\n// old .doc, and .wps as still saved by default even by recent WPS Office.\nfunction isLegacyBinary(buffer: ArrayBuffer): boolean {\n if (!buffer || buffer.byteLength < 4) return false;\n const b = new Uint8Array(buffer, 0, 4);\n return b[0] === 0xd0 && b[1] === 0xcf && b[2] === 0x11 && b[3] === 0xe0;\n}\n\nfunction errorText(err: unknown): string {\n if (err && typeof err === 'object' && 'message' in err) return String((err as Error).message);\n return String(err);\n}\n\nfunction initialScaleMode(value: 'fit' | number | undefined): 'fit' | number {\n if (typeof value === 'number' && isFinite(value) && value > 0) {\n const n = value > 10 ? value / 100 : value;\n return Math.max(SCALE_MIN, Math.min(SCALE_MAX, Math.round(n * 100) / 100));\n }\n return 'fit';\n}\n\nexport function useDocViewer(refs: DocxViewerRefs, options: DocxViewerOptions = {}) {\n // ── UI state ───────────────────────────────────────────────────────────\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<{ title: string; detail: string } | null>(null);\n const [docLoaded, setDocLoaded] = useState(false);\n const [currentPage, setCurrentPage] = useState(0);\n const [pageCount, setPageCount] = useState(0);\n const [currentScale, setCurrentScale] = useState(1);\n const [scaleMode, setScaleMode] = useState<'fit' | number>(() => initialScaleMode(options.initialScale));\n const [thumbsVisible, setThumbsVisible] = useState(options.showThumbs !== false);\n const [dropdownOpen, setDropdownOpen] = useState(false);\n\n // ── Mutable internals (live DOM produced by the library) ───────────────\n const it = useRef({\n docName: '',\n // The opened document kept as a Blob so the download button works\n // regardless of the input type (Blob / ArrayBuffer / Uint8Array).\n docBlob: null as Blob | null,\n wrapperEl: null as HTMLElement | null,\n sections: [] as HTMLElement[],\n naturalWidth: 0,\n naturalHeight: 0,\n currentPage: 0, // authoritative copy (state mirrors it for rendering)\n pageObserver: null as IntersectionObserver | null,\n thumbPageSynced: -1,\n });\n // Latest-value mirror for the once-registered global listeners.\n const stateRef = useRef({ docLoaded, currentPage, scaleMode, thumbsVisible, currentScale });\n stateRef.current = { docLoaded, currentPage, scaleMode, thumbsVisible, currentScale };\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const pagerShown = pageCount > 1;\n const thumbsShown = thumbsVisible && pageCount > 1 && docLoaded;\n const canPrev = docLoaded && currentPage > 0;\n const canNext = docLoaded && currentPage < pageCount - 1;\n const canZoomOut = docLoaded && currentScale > SCALE_MIN + 1e-6;\n const canZoomIn = docLoaded && currentScale < SCALE_MAX - 1e-6;\n const scaleLabel = scaleMode === 'fit' ? '适应宽度' : Math.round(scaleMode * 100) + '%';\n\n // ── Thumbnails ─────────────────────────────────────────────────────────\n // Deep clones of page sections shrunk with a CSS transform — no re-render.\n\n function syncThumbs() {\n const box = refs.thumbs.current;\n if (!box) return;\n const items = box.children;\n const page = it.current.currentPage;\n for (let i = 0; i < items.length; i++) {\n items[i].classList.toggle('current', Number(items[i].getAttribute('data-page')) === page);\n }\n const shown = stateRef.current.thumbsVisible && items.length > 1 && stateRef.current.docLoaded;\n if (shown && it.current.thumbPageSynced !== page && items[page]) {\n it.current.thumbPageSynced = page;\n (items[page] as HTMLElement).scrollIntoView({ block: 'nearest' });\n }\n }\n\n function buildThumbs() {\n const box = refs.thumbs.current;\n if (!box) return;\n box.innerHTML = '';\n it.current.thumbPageSynced = -1;\n const sections = it.current.sections;\n if (sections.length <= 1 || !it.current.naturalWidth) return;\n\n const scale = (THUMB_WIDTH - 2) / it.current.naturalWidth; // 2px = .thumb-page borders\n for (let i = 0; i < sections.length; i++) {\n const item = document.createElement('div');\n item.className = 'thumb';\n item.setAttribute('data-page', String(i));\n\n const page = document.createElement('div');\n page.className = 'thumb-page';\n page.style.height = Math.ceil(sections[i].offsetHeight * scale) + 'px';\n\n const clone = sections[i].cloneNode(true) as HTMLElement;\n clone.removeAttribute('data-page');\n clone.style.transform = 'scale(' + scale + ')';\n clone.style.transformOrigin = 'top left';\n page.appendChild(clone);\n\n const label = document.createElement('div');\n label.className = 'thumb-label';\n label.textContent = String(i + 1);\n\n item.appendChild(page);\n item.appendChild(label);\n box.appendChild(item);\n }\n syncThumbs();\n }\n\n // ── Zoom ───────────────────────────────────────────────────────────────\n // Zoom is pure CSS: the library renders once at natural size, and we scale\n // .docx-wrapper with a transform inside an explicitly-sized box.\n\n function fitScale() {\n const stage = refs.stage.current;\n if (!it.current.naturalWidth || !stage) return 1;\n const avail = stage.clientWidth - STAGE_PADDING * 2;\n return Math.min(FIT_MAX, avail / it.current.naturalWidth);\n }\n\n function applyScale() {\n const stage = refs.stage.current;\n const docBox = refs.docBox.current;\n const wrapperEl = it.current.wrapperEl;\n const naturalWidth = it.current.naturalWidth;\n if (!stateRef.current.docLoaded || !wrapperEl || !naturalWidth || !stage || !docBox) return;\n\n // Keep the same relative scroll position across the zoom change.\n const maxY = stage.scrollHeight - stage.clientHeight;\n const maxX = stage.scrollWidth - stage.clientWidth;\n const ratioY = maxY > 0 ? stage.scrollTop / maxY : 0;\n const ratioX = maxX > 0 ? stage.scrollLeft / maxX : 0;\n\n const mode = stateRef.current.scaleMode;\n const scale = mode === 'fit' ? fitScale() : mode;\n setCurrentScale(scale);\n docBox.style.width = Math.ceil(naturalWidth * scale) + 'px';\n docBox.style.height = Math.ceil(it.current.naturalHeight * scale) + 'px';\n // Pin the wrapper to the natural content width so the top-left-origin\n // transform lines up exactly with the zoom box.\n wrapperEl.style.width = naturalWidth + 'px';\n wrapperEl.style.transform = 'scale(' + scale + ')';\n wrapperEl.style.transformOrigin = 'top left';\n\n const newMaxY = stage.scrollHeight - stage.clientHeight;\n const newMaxX = stage.scrollWidth - stage.clientWidth;\n if (newMaxY > 0) stage.scrollTop = ratioY * newMaxY;\n if (newMaxX > 0) stage.scrollLeft = ratioX * newMaxX;\n }\n\n function zoomStep(direction: 1 | -1) {\n // currentScale carries sub-pixel fit error (e.g. 0.99998); snap to the\n // nearest grid line within tolerance first, or the buttons look dead.\n let steps = stateRef.current.currentScale / SCALE_STEP;\n const nearest = Math.round(steps);\n if (Math.abs(steps - nearest) < 0.01) steps = nearest;\n const next = direction > 0 ? (Math.floor(steps) + 1) * SCALE_STEP : (Math.ceil(steps) - 1) * SCALE_STEP;\n applyScaleWith(Math.max(SCALE_MIN, Math.min(SCALE_MAX, Math.round(next * 100) / 100)));\n }\n\n // applyScale reads scaleMode from stateRef; callers that just changed the\n // mode must update the mirror first (setScaleMode alone lands next render).\n function applyScaleWith(mode: 'fit' | number) {\n stateRef.current.scaleMode = mode;\n setScaleMode(mode);\n applyScale();\n }\n\n function setScale(value: string) {\n setDropdownOpen(false);\n applyScaleWith(value === 'fit' ? 'fit' : parseFloat(value));\n }\n\n function toggleThumbs() {\n const next = !stateRef.current.thumbsVisible;\n setThumbsVisible(next);\n stateRef.current.thumbsVisible = next;\n // The sidebar eats stage width — re-fit so fit-width stays correct.\n if (stateRef.current.docLoaded && stateRef.current.scaleMode === 'fit') applyScale();\n }\n\n // ── Page corners ───────────────────────────────────────────────────────\n // Office/WPS-style crop marks: four L-brackets whose joints sit exactly\n // on the text-area corners (the page's padding corners).\n\n function addPageCorners(section: HTMLElement) {\n const cs = getComputedStyle(section);\n const padTop = parseFloat(cs.paddingTop) || 0;\n const padRight = parseFloat(cs.paddingRight) || 0;\n const padBottom = parseFloat(cs.paddingBottom) || 0;\n const padLeft = parseFloat(cs.paddingLeft) || 0;\n const right = section.offsetWidth - padRight; // text-area right edge\n const bottom = section.offsetHeight - padBottom; // text-area bottom edge\n const size = Math.max(4, Math.round(Math.min(padTop, padRight, padBottom, padLeft) * 0.25));\n\n const boxes = {\n tl: { left: padLeft - size, top: padTop - size },\n tr: { left: right, top: padTop - size },\n bl: { left: padLeft - size, top: bottom },\n br: { left: right, top: bottom },\n };\n\n for (const name of CORNER_NAMES) {\n const corner = document.createElement('i');\n corner.className = 'page-corner ' + name;\n corner.setAttribute('aria-hidden', 'true');\n corner.style.width = size + 'px';\n corner.style.height = size + 'px';\n corner.style.left = boxes[name].left + 'px';\n corner.style.top = boxes[name].top + 'px';\n section.appendChild(corner);\n }\n }\n\n // ── Pages ──────────────────────────────────────────────────────────────\n\n function scrollStageTo(element: HTMLElement) {\n const stage = refs.stage.current;\n if (!stage) return;\n const paddingTop = parseFloat(getComputedStyle(stage).paddingTop) || 0;\n stage.scrollTop =\n stage.scrollTop +\n (element.getBoundingClientRect().top - stage.getBoundingClientRect().top) -\n paddingTop;\n }\n\n function goToPage(index: number) {\n const sections = it.current.sections;\n if (!stateRef.current.docLoaded || sections.length === 0) return;\n const clamped = Math.max(0, Math.min(index, sections.length - 1));\n it.current.currentPage = clamped;\n setCurrentPage(clamped);\n scrollStageTo(sections[clamped]);\n }\n\n function jumpToPage(inputValue: string) {\n const target = parseInt(inputValue, 10);\n if (!Number.isNaN(target)) goToPage(target - 1);\n }\n\n function trackPages() {\n if (it.current.pageObserver) {\n it.current.pageObserver.disconnect();\n it.current.pageObserver = null;\n }\n const stage = refs.stage.current;\n const sections = it.current.sections;\n if (typeof window.IntersectionObserver !== 'function' || sections.length <= 1 || !stage) return;\n\n const ratios: number[] = [];\n it.current.pageObserver = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const index = Number((entry.target as HTMLElement).getAttribute('data-page'));\n if (!Number.isNaN(index)) ratios[index] = entry.intersectionRatio;\n }\n let bestIndex = -1;\n let bestRatio = -1;\n for (let j = 0; j < ratios.length; j++) {\n if (ratios[j] > bestRatio) {\n bestRatio = ratios[j];\n bestIndex = j;\n }\n }\n if (bestIndex >= 0 && bestRatio > 0) {\n it.current.currentPage = bestIndex;\n setCurrentPage(bestIndex);\n }\n },\n { root: stage, threshold: [0, 0.25, 0.5, 0.75, 1.0] },\n );\n for (const section of sections) it.current.pageObserver.observe(section);\n }\n\n // ── Document loading ───────────────────────────────────────────────────\n\n function resetDocument() {\n if (it.current.pageObserver) {\n it.current.pageObserver.disconnect();\n it.current.pageObserver = null;\n }\n const docBox = refs.docBox.current;\n const thumbs = refs.thumbs.current;\n if (docBox) {\n docBox.innerHTML = '';\n docBox.style.width = '';\n docBox.style.height = '';\n }\n if (thumbs) thumbs.innerHTML = '';\n it.current.thumbPageSynced = -1;\n it.current.wrapperEl = null;\n it.current.sections = [];\n it.current.naturalWidth = 0;\n it.current.naturalHeight = 0;\n it.current.currentPage = 0;\n setCurrentPage(0);\n setPageCount(0);\n setDocLoaded(false);\n }\n\n async function openBuffer(buffer: ArrayBuffer, label: string) {\n resetDocument();\n setError(null);\n setDropdownOpen(false);\n setLoading(true);\n\n if (isLegacyBinary(buffer)) {\n setLoading(false);\n const legacyErr = new Error('legacy binary document (.wps/.doc), not OOXML');\n setError({\n title: '无法打开' + (label ? '「' + label + '」' : '文档'),\n detail:\n '这是旧版二进制文档格式(.wps / .doc),不是 OOXML(.docx)。' +\n '即使是新版 WPS Office,默认保存的 .wps 仍是二进制格式。' +\n '请用 WPS 或 Word 将其另存为 .docx 后重试。',\n });\n optionsRef.current.onError?.(legacyErr);\n return;\n }\n\n const renderOptions: Partial<Options> = {\n hideWrapperOnPrint: true,\n paginate: true,\n // 'experimental' only gates tab-stop computation, which 公文版记\n // lines (right-aligned tab stops) depend on.\n experimental: true,\n ...optionsRef.current.renderOptions,\n };\n\n try {\n const docBox = refs.docBox.current;\n const stage = refs.stage.current;\n if (!docBox || !stage) throw new Error('viewer is not mounted');\n const result = await renderAsync(buffer, docBox, undefined, renderOptions);\n\n const wrapperEl = (docBox.querySelector('.docx-wrapper') as HTMLElement) || docBox;\n it.current.wrapperEl = wrapperEl;\n\n const found = wrapperEl.querySelectorAll('section');\n const sections: HTMLElement[] = [];\n for (let i = 0; i < found.length; i++) {\n const section = found[i] as HTMLElement;\n section.setAttribute('data-page', String(i));\n addPageCorners(section);\n sections.push(section);\n }\n it.current.sections = sections;\n\n // Natural page size, measured before any transform is applied. The\n // wrapper is a full-width block, so its own width is the stage's —\n // the document's real width is the widest fixed-width page section.\n let naturalWidth = 0;\n for (const section of sections) {\n naturalWidth = Math.max(naturalWidth, section.offsetWidth);\n }\n if (!naturalWidth) naturalWidth = wrapperEl.scrollWidth;\n it.current.naturalWidth = naturalWidth;\n it.current.naturalHeight = wrapperEl.offsetHeight;\n it.current.currentPage = 0;\n\n stateRef.current.docLoaded = true;\n setDocLoaded(true);\n setPageCount(sections.length);\n setCurrentPage(0);\n applyScale();\n buildThumbs();\n trackPages();\n stage.scrollTop = 0;\n stage.scrollLeft = 0;\n optionsRef.current.onRendered?.(result);\n } catch (err) {\n console.error(err);\n resetDocument();\n setError({\n title: '无法打开' + (label ? '「' + label + '」' : '文档'),\n detail: errorText(err) + ' —— 请确认这是有效且未加密的 .docx 文件。',\n });\n optionsRef.current.onError?.(err);\n } finally {\n setLoading(false);\n }\n }\n\n /** Opens a document. `data` may be a Blob, ArrayBuffer or Uint8Array. */\n async function open(data: Blob | ArrayBuffer | Uint8Array | null | undefined, name?: string) {\n if (!data) return;\n const docName =\n name || (typeof File !== 'undefined' && data instanceof File && data.name) || 'document.docx';\n it.current.docName = docName;\n it.current.docBlob = data instanceof Blob ? data : new Blob([data as ArrayBuffer]);\n\n if (data instanceof Blob) {\n let buffer: ArrayBuffer;\n try {\n buffer = await new Promise<ArrayBuffer>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(reader.error || new Error('FileReader failed'));\n reader.readAsArrayBuffer(data);\n });\n } catch (err) {\n setError({ title: '读取文件失败', detail: '浏览器无法读取「' + docName + '」,请重试。' });\n optionsRef.current.onError?.(err);\n return;\n }\n return openBuffer(buffer, docName);\n }\n return openBuffer(data as ArrayBuffer, docName);\n }\n\n function download() {\n const docBlob = it.current.docBlob;\n if (!docBlob) return;\n const objectUrl = URL.createObjectURL(docBlob);\n const link = document.createElement('a');\n link.href = objectUrl;\n link.download = it.current.docName || 'document.docx';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n setTimeout(() => URL.revokeObjectURL(objectUrl), 10000);\n }\n\n // ── Global listeners (registered once, read fresh state via stateRef) ──\n\n useEffect(() => {\n function onWindowResize() {\n if (stateRef.current.docLoaded && stateRef.current.scaleMode === 'fit') applyScale();\n }\n function onDocumentClick(event: MouseEvent) {\n if (!(event.target as HTMLElement).closest('.docx-viewer .scale-select')) {\n setDropdownOpen(false);\n }\n }\n function onDocumentKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape') setDropdownOpen(false);\n\n const tag = event.target && (event.target as HTMLElement).tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n // Only steal navigation keys when this viewer contains the focus.\n if (!refs.stage.current || !refs.stage.current.contains(event.target as Node)) return;\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp' || event.key === 'PageUp') {\n event.preventDefault();\n goToPage(stateRef.current.currentPage - 1);\n } else if (event.key === 'ArrowRight' || event.key === 'ArrowDown' || event.key === 'PageDown' || event.key === ' ') {\n event.preventDefault();\n goToPage(stateRef.current.currentPage + 1);\n } else if (event.key === 'Home') {\n goToPage(0);\n } else if (event.key === 'End') {\n goToPage(it.current.sections.length - 1);\n }\n }\n\n window.addEventListener('resize', onWindowResize);\n document.addEventListener('click', onDocumentClick);\n document.addEventListener('keydown', onDocumentKeydown);\n return () => {\n window.removeEventListener('resize', onWindowResize);\n document.removeEventListener('click', onDocumentClick);\n document.removeEventListener('keydown', onDocumentKeydown);\n if (it.current.pageObserver) {\n it.current.pageObserver.disconnect();\n it.current.pageObserver = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // currentPage highlight in the sidebar (the Vue port uses a watcher).\n useEffect(() => {\n syncThumbs();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [currentPage, thumbsShown]);\n\n return {\n // element refs (bound by the components)\n ...refs,\n // state\n loading,\n error,\n docLoaded,\n currentPage,\n pageCount,\n currentScale,\n scaleMode,\n thumbsVisible,\n dropdownOpen,\n setDropdownOpen,\n pagerShown,\n thumbsShown,\n canPrev,\n canNext,\n canZoomOut,\n canZoomIn,\n scaleLabel,\n // actions\n open,\n goToPage,\n jumpToPage,\n zoomStep,\n setScale,\n toggleThumbs,\n download,\n };\n}\n\nexport type DocxViewerApi = ReturnType<typeof useDocViewer>;\n","import { createContext, useContext } from 'react';\nimport type { DocxViewerApi } from './hooks/useDocViewer';\n\nexport const DocxViewerContext = createContext<DocxViewerApi | null>(null);\n\nexport function useDocxViewer(): DocxViewerApi {\n const api = useContext(DocxViewerContext);\n if (!api) throw new Error('DocxViewer components must be used inside <DocxViewer>');\n return api;\n}\n","export function IconThumbs() {\n return (\n <svg width=\"16\" height=\"16\" fill=\"none\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path fill=\"currentColor\" fillRule=\"evenodd\" d=\"M21 5H11v14h10zM3 5h6v14H3zm0-2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm2 4.25a.75.75 0 0 0 0 1.5h2a.75.75 0 0 0 0-1.5zm-.75 3.5A.75.75 0 0 1 5 10h2a.75.75 0 0 1 0 1.5H5a.75.75 0 0 1-.75-.75m.75 2a.75.75 0 0 0 0 1.5h2a.75.75 0 0 0 0-1.5z\" clipRule=\"evenodd\" />\n </svg>\n );\n}\n","export function IconPrevPage() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M3 10l5-5 5 5\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n","export function IconNextPage() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M3 6l5 5 5-5\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n","export function IconZoomOut() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M4.8 7h4.4M10.2 10.2L14 14\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n","export function IconZoomIn() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"4.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M4.8 7h4.4M7 4.8v4.4M10.2 10.2L14 14\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n","export function IconChevronDown() {\n return (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M2 3.5l3 3 3-3\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n","export function IconOpenFile() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M2 5.2A1.7 1.7 0 0 1 3.7 3.5h2.5l1.5 1.7h4.6a1.7 1.7 0 0 1 1.7 1.7v4.4a1.7 1.7 0 0 1-1.7 1.7H3.7A1.7 1.7 0 0 1 2 11.3V5.2Z\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n","export function IconDownload() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M8 2.5V10M8 10L4.8 6.8 8 10l3.2-3.2M2.5 13.5h11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n","import { useRef } from 'react';\nimport { useDocxViewer } from '../context';\nimport { IconThumbs } from './icons/IconThumbs';\nimport { IconPrevPage } from './icons/IconPrevPage';\nimport { IconNextPage } from './icons/IconNextPage';\nimport { IconZoomOut } from './icons/IconZoomOut';\nimport { IconZoomIn } from './icons/IconZoomIn';\nimport { IconChevronDown } from './icons/IconChevronDown';\nimport { IconOpenFile } from './icons/IconOpenFile';\nimport { IconDownload } from './icons/IconDownload';\n\nconst SCALES = ['fit', '0.5', '0.75', '1', '1.25', '1.5', '2'];\n\nexport function Toolbar() {\n const api = useDocxViewer();\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n function onJump(event: React.SyntheticEvent<HTMLInputElement>) {\n const input = event.currentTarget;\n api.jumpToPage(input.value);\n input.blur();\n }\n\n function onFileChange(event: React.ChangeEvent<HTMLInputElement>) {\n const input = event.currentTarget;\n const file = input.files && input.files[0];\n if (file) api.open(file);\n // Reset so picking the same file twice still fires change.\n input.value = '';\n }\n\n return (\n <header className=\"toolbar\" hidden={!api.docLoaded}>\n <div className=\"toolbar-left\">\n <button\n className={api.thumbsShown ? 'tbtn active' : 'tbtn'}\n type=\"button\"\n title=\"缩略图\"\n disabled={!api.docLoaded || api.pageCount <= 1}\n onClick={api.toggleThumbs}\n >\n <IconThumbs />\n </button>\n <span className=\"toolbar-divider\"></span>\n <span className=\"pager\" hidden={!api.pagerShown}>\n <button\n className=\"tbtn\"\n type=\"button\"\n title=\"上一页\"\n disabled={!api.canPrev}\n onClick={() => api.goToPage(api.currentPage - 1)}\n >\n <IconPrevPage />\n </button>\n <span className=\"page-info\">\n {/* Uncontrolled + keyed by page: typing is free-form, and a page\n change remounts the input with the fresh page number. */}\n <input\n key={api.currentPage}\n className=\"page-input\"\n type=\"number\"\n min={1}\n step={1}\n defaultValue={api.pageCount > 0 ? api.currentPage + 1 : ''}\n aria-label=\"当前页码\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') onJump(e);\n }}\n onBlur={onJump}\n />\n <span className=\"page-total\">/ {api.pageCount > 0 ? api.pageCount : '–'}</span>\n </span>\n <button\n className=\"tbtn\"\n type=\"button\"\n title=\"下一页\"\n disabled={!api.canNext}\n onClick={() => api.goToPage(api.currentPage + 1)}\n >\n <IconNextPage />\n </button>\n </span>\n </div>\n\n <div className=\"toolbar-center\">\n <div className=\"zoom-group\">\n <button className=\"tbtn\" type=\"button\" title=\"缩小\" disabled={!api.canZoomOut} onClick={() => api.zoomStep(-1)}>\n <IconZoomOut />\n </button>\n <div className=\"scale-select\">\n <button\n className=\"tbtn scale-display\"\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n api.setDropdownOpen(!api.dropdownOpen);\n }}\n >\n <span>{api.scaleLabel}</span>\n <IconChevronDown />\n </button>\n <div className=\"dropdown\" hidden={!api.dropdownOpen}>\n {SCALES.map((s) => (\n <button\n key={s}\n type=\"button\"\n className={\n (api.scaleMode === 'fit' && s === 'fit') || String(api.scaleMode) === s ? 'selected' : undefined\n }\n onClick={() => api.setScale(s)}\n >\n {s === 'fit' ? '适应宽度' : Math.round(parseFloat(s) * 100) + '%'}\n </button>\n ))}\n </div>\n </div>\n <button className=\"tbtn\" type=\"button\" title=\"放大\" disabled={!api.canZoomIn} onClick={() => api.zoomStep(1)}>\n <IconZoomIn />\n </button>\n </div>\n </div>\n\n <div className=\"toolbar-right\">\n <button className=\"tbtn\" type=\"button\" title=\"打开文档\" onClick={() => fileInputRef.current?.click()}>\n <IconOpenFile />\n </button>\n <button className=\"tbtn\" type=\"button\" title=\"下载文档\" disabled={!api.docLoaded} onClick={api.download}>\n <IconDownload />\n </button>\n <input ref={fileInputRef} type=\"file\" accept=\".docx\" hidden onChange={onFileChange} />\n </div>\n </header>\n );\n}\n","import { useDocxViewer } from '../context';\n\nexport function ErrorBanner() {\n const api = useDocxViewer();\n\n return (\n <div className=\"error-banner\" hidden={!api.error}>\n <strong>{api.error?.title}</strong>\n <span>{api.error?.detail}</span>\n </div>\n );\n}\n","import { useDocxViewer } from '../context';\n\nexport function ThumbnailsSidebar() {\n const api = useDocxViewer();\n\n function onClick(event: React.MouseEvent) {\n const item = (event.target as HTMLElement).closest('.thumb');\n if (item) api.goToPage(Number(item.getAttribute('data-page')));\n }\n\n return (\n <aside\n ref={api.thumbs as React.RefObject<HTMLElement>}\n className=\"thumbs\"\n hidden={!api.thumbsShown}\n aria-label=\"页面缩略图\"\n onClick={onClick}\n />\n );\n}\n","import { useDocxViewer } from '../context';\n\nexport function DocumentStage() {\n const api = useDocxViewer();\n\n return (\n <main ref={api.stage as React.RefObject<HTMLElement>} className=\"stage\">\n <div className=\"loading\" hidden={!api.loading}>\n <div className=\"spinner\"></div>\n <p>加载中…</p>\n </div>\n <div ref={api.docBox as React.RefObject<HTMLDivElement>} className=\"document\"></div>\n </main>\n );\n}\n","// React DOCX viewer component — a port of apps/viewer (toolbar, pager,\n// zoom, thumbnails, page corners). The document is fed via the `data` prop\n// and re-renders when it changes; initialScale/showThumbs/renderOptions\n// describe the initial mount (like viewer.html's URL params).\nimport { useEffect, useRef } from 'react';\nimport type { CSSProperties } from 'react';\nimport type { Options } from '@apollo-design/docx-preview';\nimport { useDocViewer } from './hooks/useDocViewer';\nimport { DocxViewerContext } from './context';\nimport { Toolbar } from './components/Toolbar';\nimport { ErrorBanner } from './components/ErrorBanner';\nimport { ThumbnailsSidebar } from './components/ThumbnailsSidebar';\nimport { DocumentStage } from './components/DocumentStage';\nimport './styles/viewer.css';\n\nexport interface DocxViewerProps {\n /** document data — Blob / ArrayBuffer / Uint8Array; re-renders on change */\n data?: Blob | ArrayBuffer | Uint8Array | null;\n /** document name for the download button */\n name?: string;\n /** 'fit' | number (0.75, 75, …) — initial zoom; mount-time only */\n initialScale?: 'fit' | number;\n /** initial thumbnail sidebar visibility; mount-time only */\n showThumbs?: boolean;\n /** library render options, merged over { paginate, experimental } */\n renderOptions?: Partial<Options>;\n className?: string;\n style?: CSSProperties;\n onRendered?: (result: unknown) => void;\n onError?: (error: unknown) => void;\n}\n\nexport function DocxViewer({\n data,\n name,\n initialScale,\n showThumbs,\n renderOptions,\n className,\n style,\n onRendered,\n onError,\n}: DocxViewerProps) {\n const stage = useRef<HTMLElement>(null);\n const docBox = useRef<HTMLElement>(null);\n const thumbs = useRef<HTMLElement>(null);\n\n const api = useDocViewer(\n { stage, docBox, thumbs },\n { initialScale, showThumbs, renderOptions, onRendered, onError },\n );\n\n useEffect(() => {\n if (data) api.open(data, name);\n // open is stable (reads latest props through refs); re-run only on data.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data, name]);\n\n return (\n <DocxViewerContext.Provider value={api}>\n <div className={className ? `docx-viewer ${className}` : 'docx-viewer'} style={style}>\n <Toolbar />\n <ErrorBanner />\n <div className=\"body\">\n <ThumbnailsSidebar />\n <DocumentStage />\n </div>\n </div>\n </DocxViewerContext.Provider>\n );\n}\n\nexport default DocxViewer;\n"],"mappings":"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"}
package/dist/style.css ADDED
@@ -0,0 +1,2 @@
1
+ .docx-viewer{--bg:#f3f4f6;--surface:#fff;--border:#e5e7eb;--text:#1f2329;--text-dim:#6b7280;--accent:#2563eb;--accent-soft:#eff4ff;--error-bg:#fef2f2;--error-border:#fecaca;--error-text:#b91c1c;--font:system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans SC", "Microsoft YaHei", sans-serif}.docx-viewer,.docx-viewer *{box-sizing:border-box;margin:0;padding:0}.docx-viewer{background:var(--bg);height:100%;min-height:240px;font-family:var(--font);color:var(--text);flex-direction:column;font-size:14px;display:flex;overflow:hidden}.docx-viewer .toolbar{z-index:10;background:var(--surface);border-bottom:1px solid var(--border);-webkit-user-select:none;user-select:none;flex:none;align-items:center;padding:6px 12px;display:flex;position:relative}.docx-viewer .toolbar[hidden]{display:none}.docx-viewer .toolbar-divider{background:var(--border);width:1px;height:18px;margin-left:8px;margin-right:8px}.docx-viewer .toolbar-left,.docx-viewer .toolbar-right{flex:1 1 0;align-items:center;display:flex}.docx-viewer .toolbar-right{justify-content:flex-end}.docx-viewer .toolbar-center{align-items:center;display:flex;position:absolute;left:50%;transform:translate(-50%)}.docx-viewer .tbtn{font:inherit;min-width:30px;height:30px;color:var(--text-dim);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0 7px;display:inline-flex}.docx-viewer .tbtn svg{display:block}.docx-viewer .tbtn:hover:not(:disabled){background:var(--bg);color:var(--text)}.docx-viewer .tbtn:disabled{opacity:.35;cursor:default}.docx-viewer .tbtn.active{background:var(--accent-soft);color:var(--accent)}.docx-viewer .pager{align-items:center;display:flex}.docx-viewer .pager[hidden]{display:none}.docx-viewer .page-info{align-items:center;display:flex}.docx-viewer .page-input{font:inherit;text-align:center;width:48px;height:28px;color:var(--text);background:var(--surface);border:1px solid var(--border);-webkit-appearance:textfield;appearance:textfield;-webkit-user-select:text;user-select:text;border-radius:4px;font-size:13px}.docx-viewer .page-input::-webkit-outer-spin-button{-webkit-appearance:none;appearance:none}.docx-viewer .page-input::-webkit-inner-spin-button{-webkit-appearance:none;appearance:none}.docx-viewer .page-input:focus{border-color:var(--accent);outline:none}.docx-viewer .page-total{color:var(--text-dim);font-variant-numeric:tabular-nums;margin-left:6px;font-size:13px}.docx-viewer .zoom-group{border:1px solid var(--border);background:var(--surface);border-radius:6px;align-items:center;display:flex}.docx-viewer .zoom-group>.tbtn{border-radius:0}.docx-viewer .zoom-group>.tbtn:first-child{border-radius:6px 0 0 6px}.docx-viewer .zoom-group>.tbtn:last-child{border-radius:0 6px 6px 0}.docx-viewer .scale-select{position:relative}.docx-viewer .scale-display{border-left:1px solid var(--border);border-right:1px solid var(--border);min-width:78px;color:var(--text);border-radius:0;font-size:13px}.docx-viewer .scale-display svg{margin-left:4px}.docx-viewer .dropdown{background:var(--surface);border:1px solid var(--border);z-index:30;border-radius:6px;min-width:108px;margin-top:6px;padding:4px;position:absolute;top:100%;left:50%;transform:translate(-50%);box-shadow:0 6px 24px #1018281f}.docx-viewer .dropdown[hidden]{display:none}.docx-viewer .dropdown button{width:100%;font:inherit;text-align:left;color:var(--text);cursor:pointer;background:0 0;border:none;border-radius:4px;padding:6px 10px;font-size:13px;display:block}.docx-viewer .dropdown button:hover{background:var(--bg)}.docx-viewer .dropdown button.selected{color:var(--accent);font-weight:600}.docx-viewer .error-banner{background:var(--error-bg);border-bottom:1px solid var(--error-border);color:var(--error-text);flex:none;padding:10px 14px;font-size:13px;line-height:1.6}.docx-viewer .error-banner[hidden]{display:none}.docx-viewer .error-banner strong{margin-right:10px}.docx-viewer .error-banner span{overflow-wrap:anywhere}.docx-viewer .body{flex:auto;min-height:0;display:flex}.docx-viewer .stage{scrollbar-width:thin;flex:auto;min-width:0;padding:24px;position:relative;overflow:auto}.docx-viewer .document{margin:0 auto;position:relative}.docx-viewer .document .docx-wrapper{background:0 0;min-width:0;padding:0}.docx-viewer .document .docx-wrapper>section.docx{box-shadow:0 1px 3px #1018281a,0 0 1px #10182829}.docx-viewer .page-corner{z-index:2;pointer-events:none;border:0 solid #b9bec7;position:absolute}.docx-viewer .page-corner.tl{border-bottom-width:1.5px;border-right-width:1.5px}.docx-viewer .page-corner.tr{border-bottom-width:1.5px;border-left-width:1.5px}.docx-viewer .page-corner.bl{border-top-width:1.5px;border-right-width:1.5px}.docx-viewer .page-corner.br{border-top-width:1.5px;border-left-width:1.5px}.docx-viewer .thumbs{scrollbar-width:thin;background:var(--bg);border-right:1px solid var(--border);-webkit-user-select:none;user-select:none;flex:none;width:148px;padding:16px 0;overflow:hidden auto}.docx-viewer .thumbs[hidden]{display:none}.docx-viewer .thumb{cursor:pointer;width:104px;margin:0 auto 14px}.docx-viewer .thumb-page{background:var(--surface);border:1px solid var(--border);border-radius:2px;width:96px;margin:0 auto;position:relative;overflow:hidden;box-shadow:0 1px 2px #10182814}.docx-viewer .thumb:hover .thumb-page{border-color:#c7cdd6}.docx-viewer .thumb.current .thumb-page{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.docx-viewer .thumb-page .docx{pointer-events:none}.docx-viewer .thumbs .docx p:before{content:none!important}.docx-viewer .thumb-label{text-align:center;color:var(--text-dim);font-variant-numeric:tabular-nums;margin-top:5px;font-size:12px}.docx-viewer .thumb.current .thumb-label{color:var(--accent);font-weight:600}@media (max-width:600px){.docx-viewer .thumbs{display:none}}.docx-viewer .loading{color:var(--text-dim);z-index:5;background:#f3f4f6b8;flex-direction:column;justify-content:center;align-items:center;display:flex;position:absolute;top:0;bottom:0;left:0;right:0}.docx-viewer .loading[hidden]{display:none}.docx-viewer .spinner{border:3px solid #2563eb2e;border-top-color:var(--accent);border-radius:50%;width:30px;height:30px;margin-bottom:14px;animation:.9s linear infinite viewer-spin}@keyframes viewer-spin{to{transform:rotate(360deg)}}.docx-viewer .docx span[style*=--docx-font-ea][style*=方正仿宋_GBK]{font-weight:500!important}
2
+ /*$vite$:1*/
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@apollo-design/react-docx-preview",
3
+ "version": "0.1.0",
4
+ "description": "React DOCX viewer component (toolbar, pager, zoom, thumbnails) built on @apollo-design/docx-preview",
5
+ "keywords": [
6
+ "word",
7
+ "docx",
8
+ "preview",
9
+ "viewer",
10
+ "react"
11
+ ],
12
+ "license": "Apache-2.0",
13
+ "author": "apollocmh",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/apollocmh/docx-preview.git",
17
+ "directory": "packages/react-docx-preview"
18
+ },
19
+ "homepage": "https://apollocmh.github.io/docx-preview/",
20
+ "bugs": "https://github.com/apollocmh/docx-preview/issues",
21
+ "type": "module",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js"
26
+ },
27
+ "./style.css": "./dist/style.css",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "sideEffects": [
34
+ "**/*.css"
35
+ ],
36
+ "peerDependencies": {
37
+ "react": ">=17"
38
+ },
39
+ "dependencies": {
40
+ "@apollo-design/docx-preview": "0.6.3"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "registry": "https://registry.npmjs.org/"
45
+ },
46
+ "scripts": {
47
+ "build": "vite build"
48
+ }
49
+ }