@cannymindstech/file-viewers 0.28.0 → 0.28.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 (91) hide show
  1. package/dist/chunk-2BLHLWNN.js +299 -0
  2. package/dist/chunk-2BLHLWNN.js.map +1 -0
  3. package/dist/chunk-4HVU224A.js +2454 -0
  4. package/dist/chunk-4HVU224A.js.map +1 -0
  5. package/dist/chunk-6BRYDA3B.js +329 -0
  6. package/dist/chunk-6BRYDA3B.js.map +1 -0
  7. package/dist/chunk-6L72TJIW.mjs +2454 -0
  8. package/dist/chunk-6L72TJIW.mjs.map +1 -0
  9. package/dist/chunk-7PMZ4GN5.mjs +290 -0
  10. package/dist/chunk-7PMZ4GN5.mjs.map +1 -0
  11. package/dist/chunk-B5NNB4KD.mjs +268 -0
  12. package/dist/chunk-B5NNB4KD.mjs.map +1 -0
  13. package/dist/chunk-CIWCHSAA.js +309 -0
  14. package/dist/chunk-CIWCHSAA.js.map +1 -0
  15. package/dist/chunk-D7SDEVDM.js +268 -0
  16. package/dist/chunk-D7SDEVDM.js.map +1 -0
  17. package/dist/chunk-EKNP342T.mjs +299 -0
  18. package/dist/chunk-EKNP342T.mjs.map +1 -0
  19. package/dist/chunk-EM63H4SA.js +290 -0
  20. package/dist/chunk-EM63H4SA.js.map +1 -0
  21. package/dist/chunk-IJMNPAXX.mjs +309 -0
  22. package/dist/chunk-IJMNPAXX.mjs.map +1 -0
  23. package/dist/chunk-JKX5RSLF.mjs +871 -0
  24. package/dist/chunk-JKX5RSLF.mjs.map +1 -0
  25. package/dist/chunk-KJNOBIUZ.mjs +36 -0
  26. package/dist/chunk-KJNOBIUZ.mjs.map +1 -0
  27. package/dist/chunk-KQLIG6WK.js +871 -0
  28. package/dist/chunk-KQLIG6WK.js.map +1 -0
  29. package/dist/chunk-KWHXS7I4.mjs +615 -0
  30. package/dist/chunk-KWHXS7I4.mjs.map +1 -0
  31. package/dist/chunk-NELCZBZV.js +489 -0
  32. package/dist/chunk-NELCZBZV.js.map +1 -0
  33. package/dist/chunk-OPJOCUSL.js +36 -0
  34. package/dist/chunk-OPJOCUSL.js.map +1 -0
  35. package/dist/chunk-S5KXYKBX.js +615 -0
  36. package/dist/chunk-S5KXYKBX.js.map +1 -0
  37. package/dist/chunk-W2P6JHTQ.mjs +489 -0
  38. package/dist/chunk-W2P6JHTQ.mjs.map +1 -0
  39. package/dist/chunk-XJQ5J2UF.mjs +329 -0
  40. package/dist/chunk-XJQ5J2UF.mjs.map +1 -0
  41. package/dist/components/viewers/AudioViewer.d.mts +6 -0
  42. package/dist/components/viewers/AudioViewer.d.ts +6 -0
  43. package/dist/components/viewers/AudioViewer.js +10 -0
  44. package/dist/components/viewers/AudioViewer.js.map +1 -0
  45. package/dist/components/viewers/AudioViewer.mjs +10 -0
  46. package/dist/components/viewers/AudioViewer.mjs.map +1 -0
  47. package/dist/components/viewers/DefaultViewer.d.mts +6 -0
  48. package/dist/components/viewers/DefaultViewer.d.ts +6 -0
  49. package/dist/components/viewers/DefaultViewer.js +10 -0
  50. package/dist/components/viewers/DefaultViewer.js.map +1 -0
  51. package/dist/components/viewers/DefaultViewer.mjs +10 -0
  52. package/dist/components/viewers/DefaultViewer.mjs.map +1 -0
  53. package/dist/components/viewers/ImageViewer.d.mts +6 -0
  54. package/dist/components/viewers/ImageViewer.d.ts +6 -0
  55. package/dist/components/viewers/ImageViewer.js +10 -0
  56. package/dist/components/viewers/ImageViewer.js.map +1 -0
  57. package/dist/components/viewers/ImageViewer.mjs +10 -0
  58. package/dist/components/viewers/ImageViewer.mjs.map +1 -0
  59. package/dist/components/viewers/PDFViewer.d.mts +51 -0
  60. package/dist/components/viewers/PDFViewer.d.ts +51 -0
  61. package/dist/components/viewers/PDFViewer.js +11 -0
  62. package/dist/components/viewers/PDFViewer.js.map +1 -0
  63. package/dist/components/viewers/PDFViewer.mjs +11 -0
  64. package/dist/components/viewers/PDFViewer.mjs.map +1 -0
  65. package/dist/components/viewers/TIFFViewer.d.mts +6 -0
  66. package/dist/components/viewers/TIFFViewer.d.ts +6 -0
  67. package/dist/components/viewers/TIFFViewer.js +10 -0
  68. package/dist/components/viewers/TIFFViewer.js.map +1 -0
  69. package/dist/components/viewers/TIFFViewer.mjs +10 -0
  70. package/dist/components/viewers/TIFFViewer.mjs.map +1 -0
  71. package/dist/components/viewers/TextViewer.d.mts +6 -0
  72. package/dist/components/viewers/TextViewer.d.ts +6 -0
  73. package/dist/components/viewers/TextViewer.js +10 -0
  74. package/dist/components/viewers/TextViewer.js.map +1 -0
  75. package/dist/components/viewers/TextViewer.mjs +10 -0
  76. package/dist/components/viewers/TextViewer.mjs.map +1 -0
  77. package/dist/components/viewers/VideoViewer.d.mts +6 -0
  78. package/dist/components/viewers/VideoViewer.d.ts +6 -0
  79. package/dist/components/viewers/VideoViewer.js +10 -0
  80. package/dist/components/viewers/VideoViewer.js.map +1 -0
  81. package/dist/components/viewers/VideoViewer.mjs +10 -0
  82. package/dist/components/viewers/VideoViewer.mjs.map +1 -0
  83. package/dist/index.d.mts +75 -0
  84. package/dist/index.d.ts +75 -0
  85. package/dist/index.js +305 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/index.mjs +305 -0
  88. package/dist/index.mjs.map +1 -0
  89. package/dist/types-DMF9gANJ.d.mts +101 -0
  90. package/dist/types-DMF9gANJ.d.ts +101 -0
  91. package/package.json +3 -2
@@ -0,0 +1,871 @@
1
+ "use client";
2
+ import {
3
+ getFileExtension
4
+ } from "./chunk-KJNOBIUZ.mjs";
5
+ import {
6
+ FileIcon_default,
7
+ mergeToolbarConfig
8
+ } from "./chunk-7PMZ4GN5.mjs";
9
+
10
+ // src/components/viewers/TIFFViewer.tsx
11
+ import {
12
+ useCallback,
13
+ useEffect,
14
+ useMemo,
15
+ useRef,
16
+ useState
17
+ } from "react";
18
+ import {
19
+ Box,
20
+ Card,
21
+ CardContent,
22
+ CardHeader,
23
+ Divider,
24
+ LinearProgress,
25
+ Stack,
26
+ Typography,
27
+ IconButton
28
+ } from "@mui/material";
29
+ import DownloadIcon from "@mui/icons-material/Download";
30
+ import PrintIcon from "@mui/icons-material/Print";
31
+ import FullscreenIcon from "@mui/icons-material/Fullscreen";
32
+ import FirstPageIcon from "@mui/icons-material/FirstPage";
33
+ import LastPageIcon from "@mui/icons-material/LastPage";
34
+ import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
35
+ import ChevronRightIcon from "@mui/icons-material/ChevronRight";
36
+ import ZoomInIcon from "@mui/icons-material/ZoomIn";
37
+ import ZoomOutIcon from "@mui/icons-material/ZoomOut";
38
+ import FitScreenIcon from "@mui/icons-material/FitScreen";
39
+ import AspectRatioIcon from "@mui/icons-material/AspectRatio";
40
+ import InfoIcon from "@mui/icons-material/Info";
41
+ import DescriptionIcon from "@mui/icons-material/Description";
42
+ import HistoryIcon from "@mui/icons-material/History";
43
+ import { LocalOffer } from "@mui/icons-material";
44
+ import * as UTIF from "utif";
45
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
46
+ var resolveDocumentName = (file, fileName, url) => {
47
+ if (file?.name) {
48
+ return file.name;
49
+ }
50
+ if (fileName) {
51
+ return fileName;
52
+ }
53
+ if (url) {
54
+ const parts = url.split("?")[0]?.split("#")[0]?.split("/") ?? [];
55
+ return parts[parts.length - 1] || "document.tiff";
56
+ }
57
+ return "document.tiff";
58
+ };
59
+ var TIFFViewerContent = ({
60
+ sourceDescription,
61
+ file,
62
+ url,
63
+ fileName,
64
+ totalPages = 1,
65
+ className = "",
66
+ style = {},
67
+ width = "100%",
68
+ height = "100%",
69
+ onLoad,
70
+ onError,
71
+ onDownloadClick,
72
+ onPrintClick,
73
+ onTagsClick,
74
+ onMetadataClick,
75
+ onPropertiesClick,
76
+ onHistoryClick,
77
+ pageLoader,
78
+ showDownload = true,
79
+ showPrint = true,
80
+ showMetadata = false,
81
+ showTags = true,
82
+ showProperties = false,
83
+ showHistory = true,
84
+ // Extract props that shouldn't be passed to DOM elements
85
+ mimeType,
86
+ fileSize,
87
+ showPageCount,
88
+ showPageNavigation,
89
+ showZoomControls,
90
+ showSearch,
91
+ customRegistry,
92
+ initialSearchText,
93
+ initialSearchPages,
94
+ autoOpenSearch,
95
+ autoExecuteSearch,
96
+ onSearchComplete,
97
+ toolbarActions,
98
+ // Annotation-related FileViewerProps (PDF only) — extract so they don't
99
+ // leak onto the DOM via {...props} on <Box>.
100
+ showAnnotations,
101
+ userDetails,
102
+ annotationSelectionMenu,
103
+ isHighlighterActive,
104
+ isStampActive,
105
+ isNoteActive,
106
+ onHighlighterClick,
107
+ onStampClick,
108
+ onSignatureClick,
109
+ onNoteClick,
110
+ permissions,
111
+ isAnnotationForeign,
112
+ onAnnotationNoteClick,
113
+ isAnnotationInteractive,
114
+ hasUnsavedAnnotations,
115
+ isSavingAnnotations,
116
+ canSaveAnnotations,
117
+ onSaveAnnotations,
118
+ onDownloadWithAnnotations,
119
+ onDownloadWithoutAnnotations,
120
+ onPrintWithAnnotations,
121
+ onPrintWithoutAnnotations,
122
+ showDownloadOriginal,
123
+ showDownloadWithAnnotations,
124
+ showPrintOriginal,
125
+ showPrintWithAnnotations,
126
+ showAnnotate,
127
+ disabledAnnotate,
128
+ showRotation,
129
+ defaultFitToPage,
130
+ loading,
131
+ onPasswordRequest,
132
+ ...props
133
+ }) => {
134
+ const [currentPage, setCurrentPage] = useState(1);
135
+ const [isLoading, setIsLoading] = useState(false);
136
+ const [error, setError] = useState(null);
137
+ const [fitMode, setFitMode] = useState(
138
+ "fit-page"
139
+ );
140
+ const [zoom, setZoom] = useState(100);
141
+ const [viewportWidth, setViewportWidth] = useState(0);
142
+ const [viewportHeight, setViewportHeight] = useState(0);
143
+ const [, setIsFullScreen] = useState(false);
144
+ const [imageUrl, setImageUrl] = useState(null);
145
+ const [pageImages, setPageImages] = useState(/* @__PURE__ */ new Map());
146
+ const [internalLoading, setInternalLoading] = useState(false);
147
+ const [imageSize, setImageSize] = useState(null);
148
+ const viewportRef = useRef(null);
149
+ const containerRef = useRef(null);
150
+ const imgRef = useRef(null);
151
+ const pageLoaderRef = useRef(pageLoader);
152
+ useEffect(() => {
153
+ pageLoaderRef.current = pageLoader;
154
+ }, [pageLoader]);
155
+ const [localTotalPages, setLocalTotalPages] = useState(0);
156
+ const tiffSourceRef = useRef(null);
157
+ const effectiveTotalPages = localTotalPages || totalPages;
158
+ const updatePageImage = useCallback(
159
+ (pageData, pageNumber) => {
160
+ const blob = new Blob([pageData]);
161
+ const imageUrl2 = URL.createObjectURL(blob);
162
+ setPageImages((prev) => {
163
+ const oldUrl = prev.get(pageNumber);
164
+ if (oldUrl && oldUrl !== imageUrl2) {
165
+ URL.revokeObjectURL(oldUrl);
166
+ }
167
+ return new Map(prev).set(pageNumber, imageUrl2);
168
+ });
169
+ setCurrentPage(pageNumber);
170
+ setImageUrl(imageUrl2);
171
+ },
172
+ []
173
+ );
174
+ const decodeLocalPage = useCallback(
175
+ async (pageIndex) => {
176
+ const source = tiffSourceRef.current;
177
+ if (!source) return null;
178
+ const image = source.ifds[pageIndex];
179
+ if (!image) return null;
180
+ UTIF.decodeImage(source.buffer, image);
181
+ const rgba = UTIF.toRGBA8(image);
182
+ const width2 = Number(image.width) || 0;
183
+ const height2 = Number(image.height) || 0;
184
+ if (!width2 || !height2) return null;
185
+ const canvas = document.createElement("canvas");
186
+ canvas.width = width2;
187
+ canvas.height = height2;
188
+ const ctx = canvas.getContext("2d");
189
+ if (!ctx) return null;
190
+ ctx.putImageData(
191
+ new ImageData(new Uint8ClampedArray(rgba), width2, height2),
192
+ 0,
193
+ 0
194
+ );
195
+ return await new Promise((resolve) => {
196
+ canvas.toBlob(async (blob) => {
197
+ if (!blob) {
198
+ resolve(null);
199
+ return;
200
+ }
201
+ resolve(await blob.arrayBuffer());
202
+ }, "image/png");
203
+ });
204
+ },
205
+ []
206
+ );
207
+ useEffect(() => {
208
+ const loadInitialPage = async () => {
209
+ if (pageLoaderRef.current && !pageImages.has(1) && currentPage === 1) {
210
+ setInternalLoading(true);
211
+ setError(null);
212
+ try {
213
+ const pageData = await pageLoaderRef.current(1);
214
+ if (pageData) {
215
+ updatePageImage(pageData, 1);
216
+ } else {
217
+ setError("Failed to load page 1");
218
+ }
219
+ } catch (err) {
220
+ const message = err instanceof Error ? err.message : "Unable to load page 1";
221
+ setError(message);
222
+ onError?.(new Error(message));
223
+ } finally {
224
+ setInternalLoading(false);
225
+ }
226
+ }
227
+ };
228
+ loadInitialPage();
229
+ }, [pageImages, currentPage, updatePageImage, onError]);
230
+ useEffect(() => {
231
+ if (!file && !url) {
232
+ tiffSourceRef.current = null;
233
+ setLocalTotalPages(0);
234
+ return;
235
+ }
236
+ let cancelled = false;
237
+ (async () => {
238
+ try {
239
+ let buffer = null;
240
+ if (file) {
241
+ buffer = await file.arrayBuffer();
242
+ } else if (url && !pageLoader) {
243
+ buffer = await (await fetch(url)).arrayBuffer();
244
+ }
245
+ if (cancelled || !buffer) return;
246
+ const magic = new Uint8Array(buffer, 0, 4);
247
+ const isTiffBytes = magic[0] === 73 && magic[1] === 73 && magic[2] === 42 && magic[3] === 0 || magic[0] === 77 && magic[1] === 77 && magic[2] === 0 && magic[3] === 42;
248
+ if (!isTiffBytes) return;
249
+ const ifds = UTIF.decode(buffer);
250
+ if (!ifds.length) {
251
+ throw new Error("No pages found in TIFF");
252
+ }
253
+ tiffSourceRef.current = { buffer, ifds };
254
+ setLocalTotalPages(ifds.length);
255
+ if (pageLoader) return;
256
+ setInternalLoading(true);
257
+ setError(null);
258
+ setImageUrl(null);
259
+ setImageSize(null);
260
+ setPageImages((prev) => {
261
+ prev.forEach((u) => URL.revokeObjectURL(u));
262
+ return /* @__PURE__ */ new Map();
263
+ });
264
+ const firstPage = await decodeLocalPage(0);
265
+ if (cancelled) return;
266
+ if (!firstPage) {
267
+ throw new Error("Failed to decode TIFF page 1");
268
+ }
269
+ updatePageImage(firstPage, 1);
270
+ onLoad?.();
271
+ } catch (err) {
272
+ if (cancelled) return;
273
+ const message = err instanceof Error ? err.message : "Failed to decode TIFF";
274
+ if (!pageLoader) {
275
+ setError(message);
276
+ onError?.(new Error(message));
277
+ } else {
278
+ console.warn(`[TIFFViewer] Local IFD count failed: ${message}`);
279
+ }
280
+ } finally {
281
+ if (!cancelled && !pageLoader) setInternalLoading(false);
282
+ }
283
+ })();
284
+ return () => {
285
+ cancelled = true;
286
+ };
287
+ }, [file, url, pageLoader, decodeLocalPage, updatePageImage, onLoad, onError]);
288
+ const handlePageChange = useCallback(
289
+ async (newPageNumber) => {
290
+ const hasExternalLoader = !!pageLoaderRef.current;
291
+ const hasLocalSource = !!tiffSourceRef.current;
292
+ if (!hasExternalLoader && !hasLocalSource) {
293
+ console.warn("pageLoader not provided - page navigation disabled");
294
+ return;
295
+ }
296
+ const cachedUrl = pageImages.get(newPageNumber);
297
+ if (cachedUrl) {
298
+ setCurrentPage(newPageNumber);
299
+ setImageUrl(cachedUrl);
300
+ return;
301
+ }
302
+ setError(null);
303
+ try {
304
+ const pageData = hasExternalLoader ? await pageLoaderRef.current(newPageNumber) : await decodeLocalPage(newPageNumber - 1);
305
+ if (pageData) {
306
+ updatePageImage(pageData, newPageNumber);
307
+ } else {
308
+ setError(`Failed to load page ${newPageNumber}`);
309
+ }
310
+ } catch (err) {
311
+ const message = err instanceof Error ? err.message : `Unable to load page ${newPageNumber}`;
312
+ setError(message);
313
+ onError?.(new Error(message));
314
+ }
315
+ },
316
+ [pageImages, onError, updatePageImage, decodeLocalPage]
317
+ );
318
+ const resolvedFileName = useMemo(
319
+ () => fileName || sourceDescription,
320
+ [fileName, sourceDescription]
321
+ );
322
+ const fileExtension = useMemo(
323
+ () => getFileExtension(resolvedFileName || ""),
324
+ [resolvedFileName]
325
+ );
326
+ const loadImage = useCallback(
327
+ async (resetPage = false) => {
328
+ if (!file && !url) {
329
+ setImageUrl(null);
330
+ if (resetPage) setCurrentPage(1);
331
+ setError(null);
332
+ return;
333
+ }
334
+ setIsLoading(true);
335
+ setError(null);
336
+ if (resetPage) {
337
+ setCurrentPage(1);
338
+ setFitMode("fit-page");
339
+ setZoom(100);
340
+ }
341
+ setIsLoading(false);
342
+ },
343
+ [file, url]
344
+ );
345
+ const [previousFileSource, setPreviousFileSource] = useState({});
346
+ useEffect(() => {
347
+ const currentFileSource = { file, url };
348
+ const isNewFile = previousFileSource.file !== file || previousFileSource.url !== url;
349
+ if (isNewFile) {
350
+ setPreviousFileSource(currentFileSource);
351
+ void loadImage(true);
352
+ } else {
353
+ void loadImage(false);
354
+ }
355
+ }, [file, url, loadImage, previousFileSource.file, previousFileSource.url]);
356
+ useEffect(() => {
357
+ return () => {
358
+ pageImages.forEach((url2) => {
359
+ URL.revokeObjectURL(url2);
360
+ });
361
+ if (imageUrl && file && !pageImages.has(currentPage)) {
362
+ URL.revokeObjectURL(imageUrl);
363
+ }
364
+ };
365
+ }, []);
366
+ const hasImage = !!imageUrl;
367
+ const hasMultiplePages = effectiveTotalPages > 1;
368
+ const MARGIN = 40;
369
+ const MIN_ZOOM = 25;
370
+ const MAX_ZOOM = 400;
371
+ const ZOOM_LEVELS = [25, 50, 75, 100, 125, 150, 200, 300, 400];
372
+ const calculateFitZoom = useCallback(
373
+ (mode) => {
374
+ if (!imageSize || viewportWidth <= 0) return null;
375
+ const availableWidth = viewportWidth - MARGIN;
376
+ if (mode === "fit-width") {
377
+ const calculatedZoom = Math.round(
378
+ availableWidth / imageSize.width * 100
379
+ );
380
+ return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, calculatedZoom));
381
+ }
382
+ if (mode === "fit-page" && viewportHeight > 0) {
383
+ const availableHeight = viewportHeight - MARGIN;
384
+ const scaleW = availableWidth / imageSize.width;
385
+ const scaleH = availableHeight / imageSize.height;
386
+ const calculatedZoom = Math.round(Math.min(scaleW, scaleH) * 100);
387
+ return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, calculatedZoom));
388
+ }
389
+ return null;
390
+ },
391
+ [imageSize, viewportWidth, viewportHeight]
392
+ );
393
+ useEffect(() => {
394
+ if (!hasImage || !imageSize) return;
395
+ const newZoom = calculateFitZoom(fitMode);
396
+ if (newZoom !== null) {
397
+ setZoom(newZoom);
398
+ }
399
+ }, [currentPage, hasImage, imageSize, fitMode, calculateFitZoom]);
400
+ useEffect(() => {
401
+ if (typeof window === "undefined" || typeof ResizeObserver === "undefined") {
402
+ return void 0;
403
+ }
404
+ if (!viewportRef.current) {
405
+ return void 0;
406
+ }
407
+ const updateSize = () => {
408
+ const bounds = viewportRef.current?.getBoundingClientRect();
409
+ if (bounds) {
410
+ setViewportWidth(bounds.width);
411
+ setViewportHeight(bounds.height);
412
+ }
413
+ };
414
+ updateSize();
415
+ const observer = new ResizeObserver(updateSize);
416
+ observer.observe(viewportRef.current);
417
+ return () => {
418
+ observer.disconnect();
419
+ };
420
+ }, []);
421
+ const handleZoomIn = useCallback(() => {
422
+ setFitMode("zoom");
423
+ setZoom((value) => {
424
+ const nextLevel = ZOOM_LEVELS.find((level) => level > value);
425
+ return nextLevel || MAX_ZOOM;
426
+ });
427
+ }, []);
428
+ const handleZoomOut = useCallback(() => {
429
+ setFitMode("zoom");
430
+ setZoom((value) => {
431
+ const prevLevel = [...ZOOM_LEVELS].reverse().find((level) => level < value);
432
+ return prevLevel || MIN_ZOOM;
433
+ });
434
+ }, []);
435
+ const handleFitWidth = useCallback(() => {
436
+ setFitMode("fit-width");
437
+ const newZoom = calculateFitZoom("fit-width");
438
+ if (newZoom !== null) {
439
+ setZoom(newZoom);
440
+ }
441
+ }, [calculateFitZoom]);
442
+ const handleFitPage = useCallback(() => {
443
+ setFitMode("fit-page");
444
+ const newZoom = calculateFitZoom("fit-page");
445
+ if (newZoom !== null) {
446
+ setZoom(newZoom);
447
+ }
448
+ }, [calculateFitZoom]);
449
+ const handleFirstPage = useCallback(() => {
450
+ handlePageChange(1);
451
+ }, [handlePageChange]);
452
+ const handleLastPage = useCallback(() => {
453
+ handlePageChange(effectiveTotalPages);
454
+ }, [handlePageChange, effectiveTotalPages]);
455
+ const handlePreviousPage = useCallback(() => {
456
+ const newPage = Math.max(currentPage - 1, 1);
457
+ if (newPage !== currentPage) {
458
+ handlePageChange(newPage);
459
+ }
460
+ }, [currentPage, handlePageChange]);
461
+ const handleNextPage = useCallback(() => {
462
+ const newPage = Math.min(currentPage + 1, effectiveTotalPages);
463
+ if (newPage !== currentPage) {
464
+ handlePageChange(newPage);
465
+ }
466
+ }, [currentPage, effectiveTotalPages, handlePageChange]);
467
+ const handleToggleFullScreen = useCallback(() => {
468
+ if (!document.fullscreenElement && containerRef.current?.requestFullscreen) {
469
+ containerRef.current.requestFullscreen();
470
+ } else if (document.fullscreenElement && document.exitFullscreen) {
471
+ document.exitFullscreen();
472
+ }
473
+ }, []);
474
+ useEffect(() => {
475
+ const onFullscreenChange = () => {
476
+ setIsFullScreen(!!document.fullscreenElement);
477
+ };
478
+ document.addEventListener("fullscreenchange", onFullscreenChange);
479
+ return () => {
480
+ document.removeEventListener("fullscreenchange", onFullscreenChange);
481
+ };
482
+ }, []);
483
+ const handlePrint = useCallback(() => {
484
+ if (!imageUrl) return;
485
+ const printWindow = window.open("", "_blank", "width=900,height=1200");
486
+ if (!printWindow) return;
487
+ const doc = printWindow.document;
488
+ doc.title = resolvedFileName || "Document";
489
+ const style2 = doc.createElement("style");
490
+ style2.textContent = "@page { margin: 0.5cm; }html, body { margin: 0; padding: 0; }img { display: block; width: 100%; height: auto; page-break-after: always; }";
491
+ doc.head.appendChild(style2);
492
+ const img = doc.createElement("img");
493
+ img.alt = resolvedFileName || "Document";
494
+ img.onload = () => {
495
+ window.setTimeout(() => printWindow.print(), 100);
496
+ };
497
+ img.src = imageUrl;
498
+ doc.body.appendChild(img);
499
+ }, [imageUrl, resolvedFileName]);
500
+ const toolbar = mergeToolbarConfig({
501
+ showDownload,
502
+ showPrint,
503
+ showMetadata,
504
+ showTags,
505
+ showProperties,
506
+ showHistory,
507
+ onDownloadClick,
508
+ onPrintClick,
509
+ onTagsClick,
510
+ onMetadataClick,
511
+ onPropertiesClick,
512
+ onHistoryClick,
513
+ toolbarActions
514
+ });
515
+ const displayScale = zoom;
516
+ const handleImageLoad = useCallback(
517
+ (e) => {
518
+ const img = e.currentTarget;
519
+ setImageSize({ width: img.naturalWidth, height: img.naturalHeight });
520
+ if (!hasImage) {
521
+ onLoad?.();
522
+ }
523
+ },
524
+ [hasImage, onLoad]
525
+ );
526
+ const renderImage = () => {
527
+ if (!imageUrl) {
528
+ return /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: "No image available." });
529
+ }
530
+ let imgStyle = {
531
+ display: "block",
532
+ borderRadius: "4px"
533
+ };
534
+ if (imageSize) {
535
+ const zoomFactor = zoom / 100;
536
+ const scaledWidth = imageSize.width * zoomFactor;
537
+ const scaledHeight = imageSize.height * zoomFactor;
538
+ imgStyle = {
539
+ ...imgStyle,
540
+ width: `${scaledWidth}px`,
541
+ height: `${scaledHeight}px`,
542
+ maxWidth: "none",
543
+ maxHeight: "none"
544
+ };
545
+ }
546
+ return /* @__PURE__ */ jsx(
547
+ Box,
548
+ {
549
+ component: "img",
550
+ ref: imgRef,
551
+ src: imageUrl,
552
+ alt: `${resolvedFileName} page ${currentPage}`,
553
+ onLoad: handleImageLoad,
554
+ onError: () => {
555
+ setError("Failed to load image");
556
+ onError?.(new Error("Failed to load image"));
557
+ },
558
+ sx: {
559
+ ...imgStyle,
560
+ boxShadow: 1,
561
+ backgroundColor: "#fff",
562
+ margin: "20px"
563
+ }
564
+ }
565
+ );
566
+ };
567
+ return /* @__PURE__ */ jsx(
568
+ Box,
569
+ {
570
+ ref: containerRef,
571
+ className: `tiff-viewer ${className}`,
572
+ sx: {
573
+ width,
574
+ height,
575
+ display: "flex",
576
+ flexDirection: "column",
577
+ overflow: "hidden",
578
+ ...style
579
+ },
580
+ ...props,
581
+ children: /* @__PURE__ */ jsxs(
582
+ Card,
583
+ {
584
+ sx: {
585
+ height: "100%",
586
+ width: "100%",
587
+ display: "flex",
588
+ flexDirection: "column",
589
+ overflow: "hidden"
590
+ },
591
+ elevation: 1,
592
+ children: [
593
+ /* @__PURE__ */ jsx(
594
+ CardHeader,
595
+ {
596
+ avatar: /* @__PURE__ */ jsx(FileIcon_default, { ext: fileExtension, size: 32 }),
597
+ title: /* @__PURE__ */ jsx(Typography, { variant: "subtitle1", fontWeight: 500, children: resolvedFileName }),
598
+ sx: { pb: 1 }
599
+ }
600
+ ),
601
+ /* @__PURE__ */ jsx(Box, { sx: { px: 2, pb: 1, display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsxs(
602
+ Stack,
603
+ {
604
+ direction: "row",
605
+ spacing: 1,
606
+ alignItems: "center",
607
+ flexWrap: "wrap",
608
+ gap: 1,
609
+ children: [
610
+ hasMultiplePages && /* @__PURE__ */ jsxs(Fragment, { children: [
611
+ /* @__PURE__ */ jsx(
612
+ IconButton,
613
+ {
614
+ size: "small",
615
+ onClick: handleFirstPage,
616
+ disabled: currentPage === 1,
617
+ title: "First Page",
618
+ "aria-label": "Go to first page",
619
+ children: /* @__PURE__ */ jsx(FirstPageIcon, { fontSize: "small" })
620
+ }
621
+ ),
622
+ /* @__PURE__ */ jsx(
623
+ IconButton,
624
+ {
625
+ size: "small",
626
+ onClick: handlePreviousPage,
627
+ disabled: currentPage === 1,
628
+ title: "Previous Page",
629
+ "aria-label": "Go to previous page",
630
+ children: /* @__PURE__ */ jsx(ChevronLeftIcon, { fontSize: "small" })
631
+ }
632
+ ),
633
+ /* @__PURE__ */ jsxs(
634
+ Typography,
635
+ {
636
+ variant: "body2",
637
+ color: "text.secondary",
638
+ sx: { minWidth: 100, textAlign: "center" },
639
+ children: [
640
+ "Page ",
641
+ currentPage,
642
+ " of ",
643
+ effectiveTotalPages
644
+ ]
645
+ }
646
+ ),
647
+ /* @__PURE__ */ jsx(
648
+ IconButton,
649
+ {
650
+ size: "small",
651
+ onClick: handleNextPage,
652
+ disabled: currentPage === effectiveTotalPages,
653
+ title: "Next Page",
654
+ "aria-label": "Go to next page",
655
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, { fontSize: "small" })
656
+ }
657
+ ),
658
+ /* @__PURE__ */ jsx(
659
+ IconButton,
660
+ {
661
+ size: "small",
662
+ onClick: handleLastPage,
663
+ disabled: currentPage === effectiveTotalPages,
664
+ title: "Last Page",
665
+ "aria-label": "Go to last page",
666
+ children: /* @__PURE__ */ jsx(LastPageIcon, { fontSize: "small" })
667
+ }
668
+ ),
669
+ /* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true, sx: { mx: 1 } })
670
+ ] }),
671
+ /* @__PURE__ */ jsx(
672
+ IconButton,
673
+ {
674
+ size: "small",
675
+ onClick: handleZoomOut,
676
+ disabled: !hasImage || zoom <= 25,
677
+ title: "Zoom Out",
678
+ "aria-label": "Zoom out",
679
+ children: /* @__PURE__ */ jsx(ZoomOutIcon, { fontSize: "small" })
680
+ }
681
+ ),
682
+ /* @__PURE__ */ jsx(
683
+ Typography,
684
+ {
685
+ variant: "body2",
686
+ color: "text.secondary",
687
+ sx: { minWidth: 64, textAlign: "center" },
688
+ children: hasImage ? `${displayScale}%` : "\u2014"
689
+ }
690
+ ),
691
+ /* @__PURE__ */ jsx(
692
+ IconButton,
693
+ {
694
+ size: "small",
695
+ onClick: handleZoomIn,
696
+ disabled: !hasImage || zoom >= 400,
697
+ title: "Zoom In",
698
+ "aria-label": "Zoom in",
699
+ children: /* @__PURE__ */ jsx(ZoomInIcon, { fontSize: "small" })
700
+ }
701
+ ),
702
+ /* @__PURE__ */ jsx(
703
+ IconButton,
704
+ {
705
+ size: "small",
706
+ onClick: handleFitWidth,
707
+ disabled: !hasImage,
708
+ title: "Fit to Width",
709
+ "aria-label": "Fit to width",
710
+ color: fitMode === "fit-width" ? "primary" : "default",
711
+ children: /* @__PURE__ */ jsx(AspectRatioIcon, { fontSize: "small" })
712
+ }
713
+ ),
714
+ /* @__PURE__ */ jsx(
715
+ IconButton,
716
+ {
717
+ size: "small",
718
+ onClick: handleFitPage,
719
+ disabled: !hasImage,
720
+ title: "Fit to Page",
721
+ "aria-label": "Fit to page",
722
+ color: fitMode === "fit-page" ? "primary" : "default",
723
+ children: /* @__PURE__ */ jsx(FitScreenIcon, { fontSize: "small" })
724
+ }
725
+ ),
726
+ /* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true, sx: { mx: 1 } }),
727
+ !toolbar.isHidden("download") && /* @__PURE__ */ jsx(
728
+ IconButton,
729
+ {
730
+ size: "small",
731
+ onClick: toolbar.getHandler("download", onDownloadClick),
732
+ disabled: toolbar.isDisabled("download"),
733
+ title: toolbar.getLabel("download") || "Download",
734
+ "aria-label": "Download TIFF",
735
+ children: toolbar.getIcon("download") || /* @__PURE__ */ jsx(DownloadIcon, { fontSize: "small" })
736
+ }
737
+ ),
738
+ !toolbar.isHidden("print") && /* @__PURE__ */ jsx(
739
+ IconButton,
740
+ {
741
+ size: "small",
742
+ onClick: toolbar.getHandler("print", onPrintClick || handlePrint),
743
+ disabled: toolbar.isDisabled("print"),
744
+ title: toolbar.getLabel("print") || "Print",
745
+ "aria-label": "Print TIFF",
746
+ children: toolbar.getIcon("print") || /* @__PURE__ */ jsx(PrintIcon, { fontSize: "small" })
747
+ }
748
+ ),
749
+ !toolbar.isHidden("fullscreen") && /* @__PURE__ */ jsx(
750
+ IconButton,
751
+ {
752
+ size: "small",
753
+ onClick: toolbar.getHandler(
754
+ "fullscreen",
755
+ handleToggleFullScreen
756
+ ),
757
+ disabled: toolbar.isDisabled("fullscreen"),
758
+ title: toolbar.getLabel("fullscreen") || "Fullscreen",
759
+ "aria-label": "Toggle fullscreen",
760
+ children: toolbar.getIcon("fullscreen") || /* @__PURE__ */ jsx(FullscreenIcon, { fontSize: "small" })
761
+ }
762
+ ),
763
+ !toolbar.isHidden("metadata") && /* @__PURE__ */ jsx(
764
+ IconButton,
765
+ {
766
+ size: "small",
767
+ onClick: toolbar.getHandler("metadata", onMetadataClick),
768
+ disabled: toolbar.isDisabled("metadata"),
769
+ title: toolbar.getLabel("metadata") || "Document Metadata",
770
+ "aria-label": "View document metadata",
771
+ children: toolbar.getIcon("metadata") || /* @__PURE__ */ jsx(DescriptionIcon, { fontSize: "small" })
772
+ }
773
+ ),
774
+ !toolbar.isHidden("properties") && /* @__PURE__ */ jsx(
775
+ IconButton,
776
+ {
777
+ size: "small",
778
+ onClick: toolbar.getHandler("properties", onPropertiesClick),
779
+ disabled: toolbar.isDisabled("properties"),
780
+ title: toolbar.getLabel("properties") || "Document Properties",
781
+ "aria-label": "View document properties",
782
+ children: toolbar.getIcon("properties") || /* @__PURE__ */ jsx(InfoIcon, { fontSize: "small" })
783
+ }
784
+ ),
785
+ !toolbar.isHidden("tags") && /* @__PURE__ */ jsx(
786
+ IconButton,
787
+ {
788
+ size: "small",
789
+ onClick: toolbar.getHandler("tags", onTagsClick),
790
+ disabled: toolbar.isDisabled("tags"),
791
+ title: toolbar.getLabel("tags") || "Document Tags",
792
+ "aria-label": "View document tags",
793
+ children: toolbar.getIcon("tags") || /* @__PURE__ */ jsx(LocalOffer, { fontSize: "small" })
794
+ }
795
+ ),
796
+ !toolbar.isHidden("history") && /* @__PURE__ */ jsx(
797
+ IconButton,
798
+ {
799
+ size: "small",
800
+ onClick: toolbar.getHandler("history", onHistoryClick),
801
+ disabled: toolbar.isDisabled("history"),
802
+ title: toolbar.getLabel("history") || "Document History",
803
+ "aria-label": "View document history",
804
+ children: toolbar.getIcon("history") || /* @__PURE__ */ jsx(HistoryIcon, { fontSize: "small" })
805
+ }
806
+ )
807
+ ]
808
+ }
809
+ ) }),
810
+ (isLoading || internalLoading) && /* @__PURE__ */ jsx(LinearProgress, { sx: { mx: 3, mb: 1 } }),
811
+ error && /* @__PURE__ */ jsx(Typography, { color: "error", variant: "body2", sx: { px: 3, pb: 1 }, children: error }),
812
+ /* @__PURE__ */ jsx(Divider, {}),
813
+ /* @__PURE__ */ jsx(
814
+ CardContent,
815
+ {
816
+ sx: {
817
+ flexGrow: 1,
818
+ flexShrink: 1,
819
+ minHeight: 0,
820
+ p: 0,
821
+ overflow: "hidden",
822
+ backgroundColor: "#f6f8fa",
823
+ position: "relative"
824
+ },
825
+ children: /* @__PURE__ */ jsx(
826
+ Box,
827
+ {
828
+ ref: viewportRef,
829
+ sx: {
830
+ position: "absolute",
831
+ top: 0,
832
+ left: 0,
833
+ right: 0,
834
+ bottom: 0,
835
+ overflow: "auto"
836
+ },
837
+ children: /* @__PURE__ */ jsx(
838
+ Box,
839
+ {
840
+ sx: {
841
+ minWidth: "100%",
842
+ minHeight: "100%",
843
+ display: "flex",
844
+ alignItems: "center",
845
+ justifyContent: "center"
846
+ },
847
+ children: renderImage()
848
+ }
849
+ )
850
+ }
851
+ )
852
+ }
853
+ )
854
+ ]
855
+ }
856
+ )
857
+ }
858
+ );
859
+ };
860
+ var TIFFViewer = (props) => {
861
+ const sourceDescription = useMemo(
862
+ () => resolveDocumentName(props.file, props.fileName, props.url),
863
+ [props.file, props.fileName, props.url]
864
+ );
865
+ return /* @__PURE__ */ jsx(TIFFViewerContent, { ...props, sourceDescription });
866
+ };
867
+
868
+ export {
869
+ TIFFViewer
870
+ };
871
+ //# sourceMappingURL=chunk-JKX5RSLF.mjs.map