viewdoc 0.1.1 → 0.2.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.
Files changed (46) hide show
  1. package/README.md +7 -3
  2. package/dist/DocxViewer-HQFSWXQW.js +8 -0
  3. package/dist/DocxViewer-HQFSWXQW.js.map +1 -0
  4. package/dist/PdfViewer-YJOHTAPL.js +8 -0
  5. package/dist/PdfViewer-YJOHTAPL.js.map +1 -0
  6. package/dist/XlsxViewer-SQMAF5UA.js +8 -0
  7. package/dist/XlsxViewer-SQMAF5UA.js.map +1 -0
  8. package/dist/chunk-EQCFZMLL.js +114 -0
  9. package/dist/chunk-EQCFZMLL.js.map +1 -0
  10. package/dist/chunk-LE4YTMPN.js +135 -0
  11. package/dist/chunk-LE4YTMPN.js.map +1 -0
  12. package/dist/chunk-OA35SYM6.js +432 -0
  13. package/dist/chunk-OA35SYM6.js.map +1 -0
  14. package/dist/chunk-US2KTONG.js +174 -0
  15. package/dist/chunk-US2KTONG.js.map +1 -0
  16. package/dist/docx.cjs +570 -0
  17. package/dist/docx.cjs.map +1 -0
  18. package/dist/docx.d.cts +43 -0
  19. package/dist/docx.d.ts +43 -0
  20. package/dist/docx.js +8 -0
  21. package/dist/docx.js.map +1 -0
  22. package/dist/index.cjs +277 -138
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +9 -150
  25. package/dist/index.d.ts +9 -150
  26. package/dist/index.js +36 -827
  27. package/dist/index.js.map +1 -1
  28. package/dist/pdf.cjs +631 -0
  29. package/dist/pdf.cjs.map +1 -0
  30. package/dist/pdf.d.cts +45 -0
  31. package/dist/pdf.d.ts +45 -0
  32. package/dist/pdf.js +8 -0
  33. package/dist/pdf.js.map +1 -0
  34. package/dist/styles.css +5 -0
  35. package/dist/styles.css.map +1 -1
  36. package/dist/useDraggable-QvFR-4DD.d.cts +33 -0
  37. package/dist/useDraggable-QvFR-4DD.d.ts +33 -0
  38. package/dist/xlsx.cjs +591 -0
  39. package/dist/xlsx.cjs.map +1 -0
  40. package/dist/xlsx.d.cts +43 -0
  41. package/dist/xlsx.d.ts +43 -0
  42. package/dist/xlsx.js +8 -0
  43. package/dist/xlsx.js.map +1 -0
  44. package/package.json +16 -1
  45. package/dist/styles.d.cts +0 -2
  46. package/dist/styles.d.ts +0 -2
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/viewers/PdfPage.tsx","../src/viewers/usePdfDocument.ts","../src/viewers/useVisiblePage.ts","../src/viewers/PdfViewer.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport type { PDFDocumentProxy } from 'pdfjs-dist'\n\nexport interface PdfPageProps {\n doc: PDFDocumentProxy\n pageNumber: number\n /** Render resolution multiplier. Higher = crisper at deep zoom, more memory. Default: 1.5 */\n renderScale?: number\n}\n\nexport function PdfPage({ doc, pageNumber, renderScale = 1.5 }: PdfPageProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null)\n const renderTaskRef = useRef<{ cancel: () => void } | null>(null)\n\n useEffect(() => {\n let cancelled = false\n\n doc.getPage(pageNumber).then((page) => {\n if (cancelled) return\n const viewport = page.getViewport({ scale: renderScale })\n const canvas = canvasRef.current\n if (!canvas) return\n const context = canvas.getContext('2d')\n if (!context) return\n\n canvas.width = viewport.width\n canvas.height = viewport.height\n canvas.style.width = `${viewport.width / renderScale}px`\n canvas.style.height = `${viewport.height / renderScale}px`\n\n const renderTask = page.render({ canvasContext: context, viewport, canvas })\n renderTaskRef.current = renderTask\n renderTask.promise.catch(() => {\n // ignore cancellation errors from rapid re-renders\n })\n })\n\n return () => {\n cancelled = true\n renderTaskRef.current?.cancel()\n }\n }, [doc, pageNumber, renderScale])\n\n return <canvas ref={canvasRef} className=\"vd-pdf-page\" data-page-number={pageNumber} />\n}\n","import { useEffect, useState } from 'react'\nimport * as pdfjsLib from 'pdfjs-dist'\nimport type { PDFDocumentLoadingTask, PDFDocumentProxy } from 'pdfjs-dist'\n\npdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.mjs', import.meta.url).toString()\n\nexport interface UsePdfDocumentResult {\n doc: PDFDocumentProxy | null\n numPages: number\n error: Error | null\n loading: boolean\n}\n\nexport function usePdfDocument(uri: string): UsePdfDocumentResult {\n const [doc, setDoc] = useState<PDFDocumentProxy | null>(null)\n const [error, setError] = useState<Error | null>(null)\n const [loading, setLoading] = useState(true)\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n setDoc(null)\n\n const loadingTask: PDFDocumentLoadingTask = pdfjsLib.getDocument({ url: uri })\n loadingTask.promise\n .then((pdf) => {\n if (cancelled) return\n setDoc(pdf)\n setLoading(false)\n })\n .catch((err: Error) => {\n if (!cancelled) {\n setError(err)\n setLoading(false)\n }\n })\n\n return () => {\n cancelled = true\n loadingTask.destroy()\n }\n }, [uri])\n\n return { doc, numPages: doc?.numPages ?? 0, error, loading }\n}\n","import { useEffect, useState } from 'react'\n\n/** Tracks which page (by data-page-number) is most visible inside the given scroll container. */\nexport function useVisiblePage(containerRef: React.RefObject<HTMLElement>, numPages: number): number {\n const [currentPage, setCurrentPage] = useState(1)\n\n useEffect(() => {\n const root = containerRef.current\n if (!root || numPages === 0) return\n\n const observer = new IntersectionObserver(\n (entries) => {\n const mostVisible = entries\n .filter((e) => e.isIntersecting)\n .sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0]\n if (mostVisible) {\n const page = Number((mostVisible.target as HTMLElement).dataset.pageNumber)\n if (page) setCurrentPage(page)\n }\n },\n { root, threshold: [0.25, 0.5, 0.75] }\n )\n\n const pages = root.querySelectorAll('[data-page-number]')\n pages.forEach((el) => observer.observe(el))\n\n return () => observer.disconnect()\n }, [containerRef, numPages])\n\n return currentPage\n}\n","import type { CSSProperties } from 'react'\nimport { ViewerShell } from '../core/ViewerShell'\nimport type { ViewerTheme } from '../core/theme'\nimport type { Position } from '../core/useDraggable'\nimport { useZoomPan } from '../core/useZoomPan'\nimport { PdfPage } from './PdfPage'\nimport { usePdfDocument } from './usePdfDocument'\nimport { useVisiblePage } from './useVisiblePage'\n\nexport interface PdfViewerProps {\n /** URL or data URI of the PDF to display. */\n uri: string\n /** Used as the suggested filename on download. */\n fileName?: string\n className?: string\n style?: CSSProperties\n\n /** Width of the viewer. Accepts any CSS size (e.g. 1200, '1200px', '100%'). Default: 1200. */\n width?: number | string\n /** Height of the viewer. Accepts any CSS size (e.g. 700, '700px', '100vh'). Default: 700. */\n height?: number | string\n\n /** Minimum zoom scale. Default: 0.25 */\n minScale?: number\n /** Maximum zoom scale. Default: 5 */\n maxScale?: number\n /** Zoom increment per step (buttons / Ctrl+scroll). Default: 0.25 */\n zoomStep?: number\n /** Canvas render resolution multiplier (crispness at high zoom). Default: 1.5 */\n renderScale?: number\n\n /** Show zoom in/out and reset-to-100% controls. Default: true. */\n enableZoomControls?: boolean\n /** Allow Ctrl/Cmd + scroll wheel to zoom. Default: true. */\n enableWheelZoom?: boolean\n /** Show the fullscreen toggle button. Default: true. */\n enableFullscreen?: boolean\n /** Show the download button. Default: true. */\n enableDownload?: boolean\n /** Called when the download button is clicked. If omitted, downloads `uri` directly. */\n onDownload?: () => void\n /** Colors/radii to override the default look (toolbar background, text color, etc). */\n theme?: ViewerTheme\n\n /** Render as a floating window (position: fixed) that can be dragged, instead of filling the parent. Default: true. */\n floating?: boolean\n /** When floating, allow dragging the window by its toolbar. Default: true. */\n windowDraggable?: boolean\n /** Initial position when floating. Default: centered in the viewport. */\n defaultPosition?: Position\n}\n\nfunction defaultDownload(uri: string, fileName?: string) {\n const link = document.createElement('a')\n link.href = uri\n link.download = fileName ?? ''\n link.rel = 'noopener'\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n}\n\nexport function PdfViewer({\n uri,\n fileName,\n className,\n style,\n width,\n height,\n minScale = 0.25,\n maxScale = 5,\n zoomStep,\n renderScale,\n enableZoomControls = true,\n enableWheelZoom = true,\n enableFullscreen = true,\n enableDownload = true,\n onDownload,\n theme,\n floating = true,\n windowDraggable = true,\n defaultPosition,\n}: PdfViewerProps) {\n const zoomPan = useZoomPan({\n minScale,\n maxScale,\n zoomStep,\n enablePan: false,\n enableDragScroll: true,\n enableWheelZoom,\n })\n const { doc, numPages, error, loading } = usePdfDocument(uri)\n const currentPage = useVisiblePage(zoomPan.containerRef, numPages)\n\n const handleDownload = enableDownload ? onDownload ?? (() => defaultDownload(uri, fileName)) : undefined\n\n const wrapperStyle: CSSProperties = floating\n ? { ...style }\n : { display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%', ...style }\n\n return (\n <div className={className} style={wrapperStyle}>\n <ViewerShell\n zoomPan={zoomPan}\n onDownload={handleDownload}\n enableZoomControls={enableZoomControls}\n enableFullscreen={enableFullscreen}\n enablePan={false}\n theme={theme}\n width={width}\n height={height}\n floating={floating}\n draggable={windowDraggable}\n defaultPosition={defaultPosition}\n transformOrigin=\"top center\"\n toolbarExtra={\n numPages > 0 ? (\n <span className=\"vd-pdf-page-indicator\">\n Page {currentPage} / {numPages}\n </span>\n ) : undefined\n }\n >\n {loading && <span className=\"vd-doc-status\">Loading PDF…</span>}\n {error && <span className=\"vd-doc-status\">Failed to load PDF: {error.message}</span>}\n {doc && (\n <div className=\"vd-pdf-pages\">\n {Array.from({ length: numPages }, (_, i) => (\n <PdfPage key={i + 1} doc={doc} pageNumber={i + 1} renderScale={renderScale} />\n ))}\n </div>\n )}\n </ViewerShell>\n </div>\n )\n}\n"],"mappings":"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","names":["useEffect","useEffect","useState","jsx"]}
package/dist/docx.cjs ADDED
@@ -0,0 +1,570 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/entry-docx.ts
31
+ var entry_docx_exports = {};
32
+ __export(entry_docx_exports, {
33
+ DocxViewer: () => DocxViewer
34
+ });
35
+ module.exports = __toCommonJS(entry_docx_exports);
36
+
37
+ // src/core/ViewerShell.tsx
38
+ var import_react2 = require("react");
39
+
40
+ // src/core/useDraggable.ts
41
+ var import_react = require("react");
42
+ function useDraggable(options = {}) {
43
+ const { defaultPosition = { x: 80, y: 80 }, draggable = true } = options;
44
+ const [position, setPosition] = (0, import_react.useState)(defaultPosition);
45
+ const onDragHandlePointerDown = (0, import_react.useCallback)(
46
+ (e) => {
47
+ if (!draggable) return;
48
+ e.preventDefault();
49
+ const startX = e.clientX;
50
+ const startY = e.clientY;
51
+ setPosition((current) => {
52
+ const originX = current.x;
53
+ const originY = current.y;
54
+ const onMove = (ev) => {
55
+ setPosition({ x: originX + (ev.clientX - startX), y: originY + (ev.clientY - startY) });
56
+ };
57
+ const onUp = () => {
58
+ window.removeEventListener("pointermove", onMove);
59
+ window.removeEventListener("pointerup", onUp);
60
+ };
61
+ window.addEventListener("pointermove", onMove);
62
+ window.addEventListener("pointerup", onUp);
63
+ return current;
64
+ });
65
+ },
66
+ [draggable]
67
+ );
68
+ return { position, onDragHandlePointerDown };
69
+ }
70
+
71
+ // src/core/icons.tsx
72
+ var import_jsx_runtime = require("react/jsx-runtime");
73
+ function Svg(props) {
74
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
75
+ "svg",
76
+ {
77
+ width: "16",
78
+ height: "16",
79
+ viewBox: "0 0 24 24",
80
+ fill: "none",
81
+ stroke: "currentColor",
82
+ strokeWidth: "2",
83
+ strokeLinecap: "round",
84
+ strokeLinejoin: "round",
85
+ "aria-hidden": "true",
86
+ focusable: "false",
87
+ ...props
88
+ }
89
+ );
90
+ }
91
+ function ZoomOutIcon() {
92
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
93
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "11", r: "7" }),
94
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" }),
95
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "11", x2: "14", y2: "11" })
96
+ ] });
97
+ }
98
+ function ZoomInIcon() {
99
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
100
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "11", r: "7" }),
101
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" }),
102
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "11", y1: "8", x2: "11", y2: "14" }),
103
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "11", x2: "14", y2: "11" })
104
+ ] });
105
+ }
106
+ function DownloadIcon() {
107
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
108
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 3v12" }),
109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7 10l5 5 5-5" }),
110
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 21h14" })
111
+ ] });
112
+ }
113
+ function FullscreenEnterIcon() {
114
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "15 3 21 3 21 9" }),
116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "9 21 3 21 3 15" }),
117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
118
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
119
+ ] });
120
+ }
121
+ function ResetIcon() {
122
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
123
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 12a9 9 0 1 0 3-6.7" }),
124
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3 3 3 8 8 8" })
125
+ ] });
126
+ }
127
+ function GripIcon() {
128
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "5 9 2 12 5 15" }),
130
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "9 5 12 2 15 5" }),
131
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "15 19 12 22 9 19" }),
132
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "19 9 22 12 19 15" }),
133
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "2", y1: "12", x2: "22", y2: "12" }),
134
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "2", x2: "12", y2: "22" })
135
+ ] });
136
+ }
137
+ function FullscreenExitIcon() {
138
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { children: [
139
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "9 3 9 9 3 9" }),
140
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "15 21 15 15 21 15" }),
141
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "9", y1: "9", x2: "3", y2: "3" }),
142
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "15", y1: "15", x2: "21", y2: "21" })
143
+ ] });
144
+ }
145
+
146
+ // src/core/Toolbar.tsx
147
+ var import_jsx_runtime2 = require("react/jsx-runtime");
148
+ function Toolbar({
149
+ scale,
150
+ onZoomIn,
151
+ onZoomOut,
152
+ onReset,
153
+ onFullscreen,
154
+ onDownload,
155
+ isFullscreen = false,
156
+ showZoomControls = true,
157
+ dragHandle,
158
+ onDragHandlePointerDown,
159
+ extra
160
+ }) {
161
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "vd-toolbar", role: "toolbar", "aria-label": "Document viewer controls", children: [
162
+ dragHandle && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
163
+ "span",
164
+ {
165
+ className: "vd-toolbar-drag-handle",
166
+ onPointerDown: onDragHandlePointerDown,
167
+ title: "Drag to move",
168
+ "aria-label": "Drag to move",
169
+ children: dragHandle
170
+ }
171
+ ),
172
+ showZoomControls && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
173
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
174
+ "button",
175
+ {
176
+ type: "button",
177
+ className: "vd-toolbar-btn",
178
+ onClick: onZoomOut,
179
+ "aria-label": "Zoom out",
180
+ title: "Zoom out",
181
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ZoomOutIcon, {})
182
+ }
183
+ ),
184
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "vd-toolbar-scale", "aria-live": "polite", children: [
185
+ Math.round(scale * 100),
186
+ "%"
187
+ ] }),
188
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "vd-toolbar-btn", onClick: onZoomIn, "aria-label": "Zoom in", title: "Zoom in", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ZoomInIcon, {}) }),
189
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
190
+ "button",
191
+ {
192
+ type: "button",
193
+ className: "vd-toolbar-btn",
194
+ onClick: onReset,
195
+ "aria-label": "Reset zoom to 100%",
196
+ title: "Reset zoom to 100%",
197
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ResetIcon, {})
198
+ }
199
+ )
200
+ ] }),
201
+ extra,
202
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "vd-toolbar-spacer" }),
203
+ onDownload && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
204
+ "button",
205
+ {
206
+ type: "button",
207
+ className: "vd-toolbar-btn",
208
+ onClick: onDownload,
209
+ "aria-label": "Download file",
210
+ title: "Download file",
211
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DownloadIcon, {})
212
+ }
213
+ ),
214
+ onFullscreen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
215
+ "button",
216
+ {
217
+ type: "button",
218
+ className: "vd-toolbar-btn",
219
+ onClick: onFullscreen,
220
+ "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
221
+ title: isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
222
+ children: isFullscreen ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FullscreenExitIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FullscreenEnterIcon, {})
223
+ }
224
+ )
225
+ ] });
226
+ }
227
+
228
+ // src/core/theme.ts
229
+ var THEME_VAR_MAP = {
230
+ background: "--vd-bg",
231
+ toolbarBackground: "--vd-toolbar-bg",
232
+ toolbarBorderColor: "--vd-toolbar-border-color",
233
+ toolbarButtonHoverBackground: "--vd-toolbar-btn-hover-bg",
234
+ textColor: "--vd-text-color",
235
+ borderRadius: "--vd-border-radius",
236
+ toolbarButtonRadius: "--vd-toolbar-btn-radius"
237
+ };
238
+ function themeToStyle(theme) {
239
+ if (!theme) return void 0;
240
+ const style = {};
241
+ for (const key of Object.keys(theme)) {
242
+ const value = theme[key];
243
+ if (value) style[THEME_VAR_MAP[key]] = value;
244
+ }
245
+ return style;
246
+ }
247
+
248
+ // src/core/ViewerShell.tsx
249
+ var import_jsx_runtime3 = require("react/jsx-runtime");
250
+ function ViewerShell({
251
+ zoomPan,
252
+ onDownload,
253
+ toolbarExtra,
254
+ children,
255
+ enableZoomControls = true,
256
+ enableFullscreen = true,
257
+ enablePan = true,
258
+ theme,
259
+ width,
260
+ height,
261
+ floating = true,
262
+ draggable = true,
263
+ defaultPosition,
264
+ transformOrigin
265
+ }) {
266
+ const rootRef = (0, import_react2.useRef)(null);
267
+ const [isFullscreen, setIsFullscreen] = (0, import_react2.useState)(false);
268
+ const effectiveWidth = typeof width === "number" ? width : 1200;
269
+ const effectiveHeight = typeof height === "number" ? height : 700;
270
+ const resolvedDefaultPosition = defaultPosition ?? (typeof window !== "undefined" ? {
271
+ x: Math.max(0, (window.innerWidth - effectiveWidth) / 2),
272
+ y: Math.max(0, (window.innerHeight - effectiveHeight) / 2)
273
+ } : { x: 80, y: 80 });
274
+ const { position, onDragHandlePointerDown } = useDraggable({
275
+ defaultPosition: resolvedDefaultPosition,
276
+ draggable
277
+ });
278
+ (0, import_react2.useEffect)(() => {
279
+ const onChange = () => setIsFullscreen(document.fullscreenElement === rootRef.current);
280
+ document.addEventListener("fullscreenchange", onChange);
281
+ return () => document.removeEventListener("fullscreenchange", onChange);
282
+ }, []);
283
+ const onFullscreen = (0, import_react2.useCallback)(() => {
284
+ const el = rootRef.current;
285
+ if (!el) return;
286
+ if (document.fullscreenElement) {
287
+ document.exitFullscreen();
288
+ } else {
289
+ el.requestFullscreen();
290
+ }
291
+ }, []);
292
+ const sizeStyle = {
293
+ width: width ?? 1200,
294
+ height: height ?? 700
295
+ };
296
+ const floatingStyle = floating ? { position: "fixed", left: position.x, top: position.y } : void 0;
297
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
298
+ "div",
299
+ {
300
+ className: `vd-shell${floating ? " vd-shell--floating" : ""}`,
301
+ ref: rootRef,
302
+ style: { ...themeToStyle(theme), ...sizeStyle, ...floatingStyle },
303
+ children: [
304
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
305
+ Toolbar,
306
+ {
307
+ scale: zoomPan.scale,
308
+ onZoomIn: zoomPan.zoomIn,
309
+ onZoomOut: zoomPan.zoomOut,
310
+ onReset: zoomPan.resetZoom,
311
+ onFullscreen: enableFullscreen ? onFullscreen : void 0,
312
+ onDownload,
313
+ isFullscreen,
314
+ showZoomControls: enableZoomControls,
315
+ dragHandle: floating && draggable ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GripIcon, {}) : void 0,
316
+ onDragHandlePointerDown: floating && draggable ? onDragHandlePointerDown : void 0,
317
+ extra: toolbarExtra
318
+ }
319
+ ),
320
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
321
+ "div",
322
+ {
323
+ className: `vd-canvas${enablePan ? "" : " vd-canvas--no-pan"}`,
324
+ ref: zoomPan.containerRef,
325
+ onWheel: zoomPan.handlers.onWheel,
326
+ onPointerDown: zoomPan.handlers.onPointerDown,
327
+ onPointerMove: zoomPan.handlers.onPointerMove,
328
+ onPointerUp: zoomPan.handlers.onPointerUp,
329
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
330
+ "div",
331
+ {
332
+ className: "vd-canvas-content",
333
+ style: {
334
+ transform: `translate(${zoomPan.translateX}px, ${zoomPan.translateY}px) scale(${zoomPan.scale})`,
335
+ transformOrigin
336
+ },
337
+ children
338
+ }
339
+ )
340
+ }
341
+ )
342
+ ]
343
+ }
344
+ );
345
+ }
346
+
347
+ // src/core/useZoomPan.ts
348
+ var import_react3 = require("react");
349
+ var DEFAULT_MIN = 0.1;
350
+ var DEFAULT_MAX = 8;
351
+ var DEFAULT_STEP = 0.25;
352
+ function useZoomPan(options = {}) {
353
+ const {
354
+ minScale = DEFAULT_MIN,
355
+ maxScale = DEFAULT_MAX,
356
+ zoomStep = DEFAULT_STEP,
357
+ enablePan = true,
358
+ enableWheelZoom = true,
359
+ enableDragScroll = false
360
+ } = options;
361
+ const [state, setState] = (0, import_react3.useState)({ scale: 1, translateX: 0, translateY: 0 });
362
+ const containerRef = (0, import_react3.useRef)(null);
363
+ const dragState = (0, import_react3.useRef)({
364
+ dragging: false,
365
+ startX: 0,
366
+ startY: 0,
367
+ originX: 0,
368
+ originY: 0
369
+ });
370
+ const scrollDragState = (0, import_react3.useRef)(
371
+ { dragging: false, startX: 0, startY: 0, originLeft: 0, originTop: 0 }
372
+ );
373
+ const clamp = (0, import_react3.useCallback)(
374
+ (value) => Math.min(maxScale, Math.max(minScale, value)),
375
+ [minScale, maxScale]
376
+ );
377
+ const setScale = (0, import_react3.useCallback)(
378
+ (scale) => setState((prev) => ({ ...prev, scale: clamp(scale) })),
379
+ [clamp]
380
+ );
381
+ const zoomIn = (0, import_react3.useCallback)(() => setScale(state.scale + zoomStep), [setScale, state.scale, zoomStep]);
382
+ const zoomOut = (0, import_react3.useCallback)(() => setScale(state.scale - zoomStep), [setScale, state.scale, zoomStep]);
383
+ const resetZoom = (0, import_react3.useCallback)(() => setState({ scale: 1, translateX: 0, translateY: 0 }), []);
384
+ const onWheel = (0, import_react3.useCallback)(
385
+ (e) => {
386
+ if (!enableWheelZoom) return;
387
+ if (!e.ctrlKey && !e.metaKey) return;
388
+ e.preventDefault();
389
+ const delta = e.deltaY > 0 ? -zoomStep : zoomStep;
390
+ setScale(state.scale + delta);
391
+ },
392
+ [enableWheelZoom, setScale, state.scale, zoomStep]
393
+ );
394
+ const onPointerDown = (0, import_react3.useCallback)(
395
+ (e) => {
396
+ if (enablePan) {
397
+ dragState.current = {
398
+ dragging: true,
399
+ startX: e.clientX,
400
+ startY: e.clientY,
401
+ originX: state.translateX,
402
+ originY: state.translateY
403
+ };
404
+ e.target.setPointerCapture(e.pointerId);
405
+ } else if (enableDragScroll && containerRef.current) {
406
+ scrollDragState.current = {
407
+ dragging: true,
408
+ startX: e.clientX,
409
+ startY: e.clientY,
410
+ originLeft: containerRef.current.scrollLeft,
411
+ originTop: containerRef.current.scrollTop
412
+ };
413
+ e.target.setPointerCapture(e.pointerId);
414
+ }
415
+ },
416
+ [enablePan, enableDragScroll, state.translateX, state.translateY]
417
+ );
418
+ const onPointerMove = (0, import_react3.useCallback)(
419
+ (e) => {
420
+ if (enablePan) {
421
+ if (!dragState.current.dragging) return;
422
+ const dx = e.clientX - dragState.current.startX;
423
+ const dy = e.clientY - dragState.current.startY;
424
+ setState((prev) => ({
425
+ ...prev,
426
+ translateX: dragState.current.originX + dx,
427
+ translateY: dragState.current.originY + dy
428
+ }));
429
+ } else if (enableDragScroll) {
430
+ if (!scrollDragState.current.dragging || !containerRef.current) return;
431
+ const dx = e.clientX - scrollDragState.current.startX;
432
+ const dy = e.clientY - scrollDragState.current.startY;
433
+ containerRef.current.scrollLeft = scrollDragState.current.originLeft - dx;
434
+ containerRef.current.scrollTop = scrollDragState.current.originTop - dy;
435
+ }
436
+ },
437
+ [enablePan, enableDragScroll]
438
+ );
439
+ const onPointerUp = (0, import_react3.useCallback)(
440
+ (e) => {
441
+ if (enablePan) {
442
+ dragState.current.dragging = false;
443
+ e.target.releasePointerCapture(e.pointerId);
444
+ } else if (enableDragScroll) {
445
+ scrollDragState.current.dragging = false;
446
+ e.target.releasePointerCapture(e.pointerId);
447
+ }
448
+ },
449
+ [enablePan, enableDragScroll]
450
+ );
451
+ return {
452
+ ...state,
453
+ containerRef,
454
+ zoomIn,
455
+ zoomOut,
456
+ resetZoom,
457
+ setScale,
458
+ handlers: { onWheel, onPointerDown, onPointerMove, onPointerUp }
459
+ };
460
+ }
461
+
462
+ // src/viewers/useDocxHtml.ts
463
+ var import_react4 = require("react");
464
+ var import_mammoth = __toESM(require("mammoth"), 1);
465
+ function useDocxHtml(uri) {
466
+ const [html, setHtml] = (0, import_react4.useState)(null);
467
+ const [error, setError] = (0, import_react4.useState)(null);
468
+ const [loading, setLoading] = (0, import_react4.useState)(true);
469
+ (0, import_react4.useEffect)(() => {
470
+ let cancelled = false;
471
+ setLoading(true);
472
+ setError(null);
473
+ setHtml(null);
474
+ fetch(uri).then((res) => {
475
+ if (!res.ok) throw new Error(`Failed to fetch document: ${res.status} ${res.statusText}`);
476
+ return res.arrayBuffer();
477
+ }).then(
478
+ (arrayBuffer) => import_mammoth.default.convertToHtml({ arrayBuffer }).catch(() => {
479
+ throw new Error("This file could not be read as a Word document (.docx).");
480
+ })
481
+ ).then((result) => {
482
+ if (cancelled) return;
483
+ setHtml(result.value);
484
+ setLoading(false);
485
+ }).catch((err) => {
486
+ if (!cancelled) {
487
+ setError(err);
488
+ setLoading(false);
489
+ }
490
+ });
491
+ return () => {
492
+ cancelled = true;
493
+ };
494
+ }, [uri]);
495
+ return { html, error, loading };
496
+ }
497
+
498
+ // src/viewers/DocxViewer.tsx
499
+ var import_jsx_runtime4 = require("react/jsx-runtime");
500
+ function defaultDownload(uri, fileName) {
501
+ const link = document.createElement("a");
502
+ link.href = uri;
503
+ link.download = fileName ?? "";
504
+ link.rel = "noopener";
505
+ document.body.appendChild(link);
506
+ link.click();
507
+ document.body.removeChild(link);
508
+ }
509
+ function DocxViewer({
510
+ uri,
511
+ fileName,
512
+ className,
513
+ style,
514
+ width,
515
+ height,
516
+ minScale = 0.5,
517
+ maxScale = 3,
518
+ zoomStep,
519
+ enableZoomControls = true,
520
+ enableWheelZoom = true,
521
+ enableFullscreen = true,
522
+ enableDownload = true,
523
+ onDownload,
524
+ theme,
525
+ floating = true,
526
+ windowDraggable = true,
527
+ defaultPosition
528
+ }) {
529
+ const zoomPan = useZoomPan({
530
+ minScale,
531
+ maxScale,
532
+ zoomStep,
533
+ enablePan: false,
534
+ enableDragScroll: true,
535
+ enableWheelZoom
536
+ });
537
+ const { html, error, loading } = useDocxHtml(uri);
538
+ const handleDownload = enableDownload ? onDownload ?? (() => defaultDownload(uri, fileName)) : void 0;
539
+ const wrapperStyle = floating ? { ...style } : { display: "flex", alignItems: "center", justifyContent: "center", width: "100%", height: "100%", ...style };
540
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: wrapperStyle, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
541
+ ViewerShell,
542
+ {
543
+ zoomPan,
544
+ onDownload: handleDownload,
545
+ enableZoomControls,
546
+ enableFullscreen,
547
+ enablePan: false,
548
+ theme,
549
+ width,
550
+ height,
551
+ floating,
552
+ draggable: windowDraggable,
553
+ defaultPosition,
554
+ transformOrigin: "top center",
555
+ children: [
556
+ loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "vd-doc-status", children: "Loading document\u2026" }),
557
+ error && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "vd-doc-status", children: [
558
+ "Failed to load document: ",
559
+ error.message
560
+ ] }),
561
+ html && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "vd-docx-page", dangerouslySetInnerHTML: { __html: html } })
562
+ ]
563
+ }
564
+ ) });
565
+ }
566
+ // Annotate the CommonJS export names for ESM import in node:
567
+ 0 && (module.exports = {
568
+ DocxViewer
569
+ });
570
+ //# sourceMappingURL=docx.cjs.map