@fdls/file-viewer 0.2.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/dist/index.js ADDED
@@ -0,0 +1,3351 @@
1
+ import "./style.css";
2
+ import { createContext, lazy, useState, useRef, useEffect, useCallback, useMemo, useId, useLayoutEffect, forwardRef, isValidElement, Suspense, useContext, cloneElement } from 'react';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { createPortal } from 'react-dom';
5
+ import { Document, Page, pdfjs } from 'react-pdf';
6
+ import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
7
+ import { useReactToPrint } from 'react-to-print';
8
+
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ function assignRef(ref, value) {
19
+ if (typeof ref === "function") {
20
+ ref(value);
21
+ return;
22
+ }
23
+ if (ref && typeof ref === "object") {
24
+ ref.current = value;
25
+ }
26
+ }
27
+ function mergeRefs(...refs) {
28
+ return (value) => {
29
+ refs.forEach((ref) => assignRef(ref, value));
30
+ };
31
+ }
32
+ function renderAsChild(asChild, child, props) {
33
+ if (!isValidElement(child)) {
34
+ return child;
35
+ }
36
+ const element = child;
37
+ const mergedClassName = [props.className, element.props.className].filter(Boolean).join(" ");
38
+ const mergedStyle = {
39
+ ...props.style,
40
+ ...element.props.style
41
+ };
42
+ const propsOnClick = props.onClick;
43
+ const propsRef = props.ref;
44
+ return cloneElement(element, {
45
+ ...props,
46
+ ...element.props,
47
+ className: mergedClassName || void 0,
48
+ style: Object.keys(mergedStyle).length > 0 ? mergedStyle : void 0,
49
+ ref: mergeRefs(propsRef, element.props.ref),
50
+ onClick: (event) => {
51
+ propsOnClick?.(event);
52
+ element.props.onClick?.(event);
53
+ }
54
+ });
55
+ }
56
+ var init_as_child = __esm({
57
+ "src/features/file-viewer/primitives/as-child.ts"() {
58
+ }
59
+ });
60
+ function IconBase({ children, ...props }) {
61
+ return /* @__PURE__ */ jsx("svg", { ...ICON_DEFAULTS, ...props, children });
62
+ }
63
+ var ICON_DEFAULTS;
64
+ var init_icon = __esm({
65
+ "src/features/file-viewer/components/icons/icon.tsx"() {
66
+ ICON_DEFAULTS = {
67
+ xmlns: "http://www.w3.org/2000/svg",
68
+ width: 24,
69
+ height: 24,
70
+ viewBox: "0 0 24 24",
71
+ fill: "none",
72
+ stroke: "currentColor",
73
+ strokeWidth: 2,
74
+ strokeLinecap: "round",
75
+ strokeLinejoin: "round"
76
+ };
77
+ }
78
+ });
79
+ function X(props) {
80
+ return /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
81
+ /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
82
+ /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
83
+ ] });
84
+ }
85
+ var init_X = __esm({
86
+ "src/features/file-viewer/components/icons/X.tsx"() {
87
+ init_icon();
88
+ }
89
+ });
90
+ function Download(props) {
91
+ return /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
92
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
93
+ /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
94
+ /* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "15", y2: "3" })
95
+ ] });
96
+ }
97
+ var init_Download = __esm({
98
+ "src/features/file-viewer/components/icons/Download.tsx"() {
99
+ init_icon();
100
+ }
101
+ });
102
+ function Printer(props) {
103
+ return /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
104
+ /* @__PURE__ */ jsx("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }),
105
+ /* @__PURE__ */ jsx("path", { d: "M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6" }),
106
+ /* @__PURE__ */ jsx("rect", { x: "6", y: "14", width: "12", height: "8", rx: "1" })
107
+ ] });
108
+ }
109
+ var init_Printer = __esm({
110
+ "src/features/file-viewer/components/icons/Printer.tsx"() {
111
+ init_icon();
112
+ }
113
+ });
114
+ function Maximize2(props) {
115
+ return /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
116
+ /* @__PURE__ */ jsx("polyline", { points: "15 3 21 3 21 9" }),
117
+ /* @__PURE__ */ jsx("polyline", { points: "9 21 3 21 3 15" }),
118
+ /* @__PURE__ */ jsx("line", { x1: "21", x2: "14", y1: "3", y2: "10" }),
119
+ /* @__PURE__ */ jsx("line", { x1: "3", x2: "10", y1: "21", y2: "14" })
120
+ ] });
121
+ }
122
+ var init_Maximize2 = __esm({
123
+ "src/features/file-viewer/components/icons/Maximize2.tsx"() {
124
+ init_icon();
125
+ }
126
+ });
127
+ function LoaderCircle(props) {
128
+ return /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
129
+ }
130
+ var init_LoaderCircle = __esm({
131
+ "src/features/file-viewer/components/icons/LoaderCircle.tsx"() {
132
+ init_icon();
133
+ }
134
+ });
135
+ function ChevronLeft(props) {
136
+ return /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) });
137
+ }
138
+ var init_ChevronLeft = __esm({
139
+ "src/features/file-viewer/components/icons/ChevronLeft.tsx"() {
140
+ init_icon();
141
+ }
142
+ });
143
+ function ChevronRight(props) {
144
+ return /* @__PURE__ */ jsx(IconBase, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) });
145
+ }
146
+ var init_ChevronRight = __esm({
147
+ "src/features/file-viewer/components/icons/ChevronRight.tsx"() {
148
+ init_icon();
149
+ }
150
+ });
151
+ function ZoomIn(props) {
152
+ return /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
153
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
154
+ /* @__PURE__ */ jsx("line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65" }),
155
+ /* @__PURE__ */ jsx("line", { x1: "11", x2: "11", y1: "8", y2: "14" }),
156
+ /* @__PURE__ */ jsx("line", { x1: "8", x2: "14", y1: "11", y2: "11" })
157
+ ] });
158
+ }
159
+ var init_ZoomIn = __esm({
160
+ "src/features/file-viewer/components/icons/ZoomIn.tsx"() {
161
+ init_icon();
162
+ }
163
+ });
164
+ function ZoomOut(props) {
165
+ return /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
166
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
167
+ /* @__PURE__ */ jsx("line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65" }),
168
+ /* @__PURE__ */ jsx("line", { x1: "8", x2: "14", y1: "11", y2: "11" })
169
+ ] });
170
+ }
171
+ var init_ZoomOut = __esm({
172
+ "src/features/file-viewer/components/icons/ZoomOut.tsx"() {
173
+ init_icon();
174
+ }
175
+ });
176
+ function Scan(props) {
177
+ return /* @__PURE__ */ jsxs(IconBase, { ...props, children: [
178
+ /* @__PURE__ */ jsx("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }),
179
+ /* @__PURE__ */ jsx("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }),
180
+ /* @__PURE__ */ jsx("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }),
181
+ /* @__PURE__ */ jsx("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })
182
+ ] });
183
+ }
184
+ var init_Scan = __esm({
185
+ "src/features/file-viewer/components/icons/Scan.tsx"() {
186
+ init_icon();
187
+ }
188
+ });
189
+
190
+ // src/features/file-viewer/components/icons/index.ts
191
+ var init_icons = __esm({
192
+ "src/features/file-viewer/components/icons/index.ts"() {
193
+ init_X();
194
+ init_Download();
195
+ init_Printer();
196
+ init_Maximize2();
197
+ init_LoaderCircle();
198
+ init_ChevronLeft();
199
+ init_ChevronRight();
200
+ init_ZoomIn();
201
+ init_ZoomOut();
202
+ init_Scan();
203
+ }
204
+ });
205
+
206
+ // src/features/file-viewer/utils/deep-partial.ts
207
+ function deepMergePartial(base, patch) {
208
+ if (!patch) {
209
+ return { ...base };
210
+ }
211
+ const result = { ...base };
212
+ const patchRecord = patch;
213
+ const resultRecord = result;
214
+ const baseRecord = base;
215
+ for (const key of Object.keys(patchRecord)) {
216
+ const patchValue = patchRecord[key];
217
+ if (patchValue === void 0) {
218
+ continue;
219
+ }
220
+ const baseValue = baseRecord[key];
221
+ if (typeof patchValue === "object" && patchValue !== null && !Array.isArray(patchValue) && typeof baseValue === "object" && baseValue !== null && !Array.isArray(baseValue)) {
222
+ resultRecord[key] = deepMergePartial(
223
+ baseValue,
224
+ patchValue
225
+ );
226
+ continue;
227
+ }
228
+ resultRecord[key] = patchValue;
229
+ }
230
+ return result;
231
+ }
232
+ var init_deep_partial = __esm({
233
+ "src/features/file-viewer/utils/deep-partial.ts"() {
234
+ }
235
+ });
236
+
237
+ // src/features/file-viewer/utils/merge-slot-props.ts
238
+ function mergeClassNames(...classNames) {
239
+ return classNames.filter(Boolean).join(" ");
240
+ }
241
+ function mergeStyles(...styles) {
242
+ return Object.assign({}, ...styles.filter(Boolean));
243
+ }
244
+ function mergeSlotClassNames(...layers) {
245
+ const result = {};
246
+ for (const layer of layers) {
247
+ if (!layer) continue;
248
+ for (const key of Object.keys(layer)) {
249
+ const value = layer[key];
250
+ if (value !== void 0) {
251
+ result[key] = mergeClassNames(result[key], value);
252
+ }
253
+ }
254
+ }
255
+ return result;
256
+ }
257
+ function mergeSlotStyles(...layers) {
258
+ const result = {};
259
+ for (const layer of layers) {
260
+ if (!layer) continue;
261
+ for (const key of Object.keys(layer)) {
262
+ const value = layer[key];
263
+ if (value !== void 0) {
264
+ result[key] = mergeStyles(result[key], value);
265
+ }
266
+ }
267
+ }
268
+ return result;
269
+ }
270
+ var init_merge_slot_props = __esm({
271
+ "src/features/file-viewer/utils/merge-slot-props.ts"() {
272
+ }
273
+ });
274
+
275
+ // src/features/file-viewer/config.ts
276
+ function mergePdfViewerLayer(current, partial) {
277
+ if (!partial) {
278
+ return current;
279
+ }
280
+ return {
281
+ ...current,
282
+ ...partial,
283
+ classNames: mergeSlotClassNames(
284
+ current?.classNames,
285
+ partial.classNames
286
+ ),
287
+ styles: mergeSlotStyles(current?.styles, partial.styles)
288
+ };
289
+ }
290
+ function mergeFileViewerDefaults(current, partial) {
291
+ return {
292
+ ...current,
293
+ ...partial,
294
+ dialogClassNames: mergeSlotClassNames(
295
+ current?.dialogClassNames,
296
+ partial.dialogClassNames
297
+ ),
298
+ dialogStyles: mergeSlotStyles(current?.dialogStyles, partial.dialogStyles),
299
+ classNames: mergeSlotClassNames(current?.classNames, partial.classNames),
300
+ styles: mergeSlotStyles(current?.styles, partial.styles),
301
+ pdfViewerProps: mergePdfViewerLayer(current?.pdfViewerProps, partial.pdfViewerProps)
302
+ };
303
+ }
304
+ function mergeImageViewerDefaults(current, partial) {
305
+ return {
306
+ ...current,
307
+ ...partial,
308
+ classNames: mergeSlotClassNames(
309
+ current?.classNames,
310
+ partial.classNames
311
+ ),
312
+ styles: mergeSlotStyles(current?.styles, partial.styles)
313
+ };
314
+ }
315
+ function mergeToolbarDefaults(current, partial) {
316
+ return {
317
+ ...current,
318
+ ...partial,
319
+ classNames: mergeSlotClassNames(current?.classNames, partial.classNames),
320
+ styles: mergeSlotStyles(current?.styles, partial.styles)
321
+ };
322
+ }
323
+ function mergeTooltipDefaults(current, partial) {
324
+ return {
325
+ ...current,
326
+ ...partial,
327
+ classNames: mergeSlotClassNames(current?.classNames, partial.classNames),
328
+ styles: mergeSlotStyles(current?.styles, partial.styles)
329
+ };
330
+ }
331
+ function mergeTranslationDefaults(current, partial) {
332
+ const next = {
333
+ ...current
334
+ };
335
+ for (const language of Object.keys(partial)) {
336
+ const patch = partial[language];
337
+ if (!patch) {
338
+ continue;
339
+ }
340
+ next[language] = deepMergePartial(
341
+ next[language] ?? {},
342
+ patch
343
+ );
344
+ }
345
+ return next;
346
+ }
347
+ function setFileViewerDefaults(partial) {
348
+ const next = { ...globalDefaults };
349
+ if (partial.language !== void 0) {
350
+ next.language = partial.language;
351
+ }
352
+ if (partial.fileViewer) {
353
+ next.fileViewer = mergeFileViewerDefaults(
354
+ next.fileViewer,
355
+ partial.fileViewer
356
+ );
357
+ }
358
+ if (partial.pdfViewer) {
359
+ next.pdfViewer = mergePdfViewerLayer(
360
+ next.pdfViewer,
361
+ partial.pdfViewer
362
+ );
363
+ }
364
+ if (partial.imageViewer) {
365
+ next.imageViewer = mergeImageViewerDefaults(
366
+ next.imageViewer,
367
+ partial.imageViewer
368
+ );
369
+ }
370
+ if (partial.toolbar) {
371
+ next.toolbar = mergeToolbarDefaults(
372
+ next.toolbar,
373
+ partial.toolbar
374
+ );
375
+ }
376
+ if (partial.tooltip) {
377
+ next.tooltip = mergeTooltipDefaults(
378
+ next.tooltip,
379
+ partial.tooltip
380
+ );
381
+ }
382
+ if (partial.autoHide) {
383
+ next.autoHide = { ...next.autoHide, ...partial.autoHide };
384
+ }
385
+ if (partial.translations) {
386
+ next.translations = mergeTranslationDefaults(
387
+ next.translations,
388
+ partial.translations
389
+ );
390
+ }
391
+ globalDefaults = next;
392
+ }
393
+ function getFileViewerDefaults() {
394
+ return globalDefaults;
395
+ }
396
+ function resetFileViewerDefaults() {
397
+ globalDefaults = {};
398
+ }
399
+ function resolvePdfViewerProps(instance = {}) {
400
+ const defaults = getFileViewerDefaults();
401
+ const merged = mergePdfViewerLayer(
402
+ mergePdfViewerLayer(defaults.pdfViewer, defaults.fileViewer?.pdfViewerProps),
403
+ instance
404
+ ) ?? {};
405
+ const legacyClassNames = {
406
+ ...instance.className || merged.className ? { root: instance.className ?? merged.className } : {},
407
+ ...instance.pageClassName || merged.pageClassName ? { page: instance.pageClassName ?? merged.pageClassName } : {},
408
+ ...instance.paginationClassName || merged.paginationClassName ? {
409
+ pagination: instance.paginationClassName ?? merged.paginationClassName
410
+ } : {}
411
+ };
412
+ return {
413
+ ...merged,
414
+ classNames: mergeSlotClassNames(
415
+ merged.classNames,
416
+ legacyClassNames
417
+ )
418
+ };
419
+ }
420
+ function resolveImageViewerProps(instance = {}) {
421
+ const defaults = getFileViewerDefaults();
422
+ const merged = mergeImageViewerDefaults(defaults.imageViewer, instance) ?? {};
423
+ return merged;
424
+ }
425
+ var globalDefaults;
426
+ var init_config = __esm({
427
+ "src/features/file-viewer/config.ts"() {
428
+ init_deep_partial();
429
+ init_merge_slot_props();
430
+ globalDefaults = {};
431
+ }
432
+ });
433
+
434
+ // src/features/file-viewer/primitives/compute-tooltip-placement.ts
435
+ function resolveSide({
436
+ preferredSide,
437
+ triggerRect,
438
+ contentHeight,
439
+ sideOffset,
440
+ padding,
441
+ viewportHeight
442
+ }) {
443
+ const spaceAbove = triggerRect.top - padding;
444
+ const spaceBelow = viewportHeight - triggerRect.bottom - padding;
445
+ const needed = contentHeight + sideOffset;
446
+ if (preferredSide === "top") {
447
+ if (spaceAbove >= needed) return "top";
448
+ if (spaceBelow >= needed) return "bottom";
449
+ return spaceBelow > spaceAbove ? "bottom" : "top";
450
+ }
451
+ if (spaceBelow >= needed) return "bottom";
452
+ if (spaceAbove >= needed) return "top";
453
+ return spaceAbove > spaceBelow ? "top" : "bottom";
454
+ }
455
+ function computeTooltipPlacement(input) {
456
+ const padding = input.padding ?? 8;
457
+ const viewportWidth = input.viewportWidth ?? window.innerWidth;
458
+ const viewportHeight = input.viewportHeight ?? window.innerHeight;
459
+ const side = resolveSide({
460
+ ...input,
461
+ padding,
462
+ viewportHeight});
463
+ const top = side === "top" ? input.triggerRect.top - input.sideOffset - input.contentHeight : input.triggerRect.bottom + input.sideOffset;
464
+ let left = input.triggerRect.left + input.triggerRect.width / 2 - input.contentWidth / 2;
465
+ const maxLeft = viewportWidth - padding - input.contentWidth;
466
+ if (left < padding) {
467
+ left = padding;
468
+ } else if (left > maxLeft) {
469
+ left = Math.max(padding, maxLeft);
470
+ }
471
+ let clampedTop = top;
472
+ const maxTop = viewportHeight - padding - input.contentHeight;
473
+ if (clampedTop < padding) {
474
+ clampedTop = padding;
475
+ } else if (clampedTop > maxTop) {
476
+ clampedTop = Math.max(padding, maxTop);
477
+ }
478
+ return { top: clampedTop, left, side };
479
+ }
480
+ var init_compute_tooltip_placement = __esm({
481
+ "src/features/file-viewer/primitives/compute-tooltip-placement.ts"() {
482
+ }
483
+ });
484
+ function useTooltipGroup() {
485
+ const context = useContext(TooltipGroupContext);
486
+ if (!context) {
487
+ throw new Error("Tooltip components must be used within TooltipProvider");
488
+ }
489
+ return context;
490
+ }
491
+ function useCloseActiveTooltip() {
492
+ const context = useContext(TooltipGroupContext);
493
+ return context?.closeActiveTooltip ?? (() => void 0);
494
+ }
495
+ function TooltipProvider({
496
+ children,
497
+ delayDuration = 300,
498
+ skipDelayDuration = 500,
499
+ closeDelayDuration = DEFAULT_CLOSE_DELAY_MS
500
+ }) {
501
+ const isOpenDelayedRef = useRef(true);
502
+ const skipDelayTimerRef = useRef(null);
503
+ const activeCloseRef = useRef(null);
504
+ const onTooltipOpened = useCallback(() => {
505
+ if (skipDelayTimerRef.current !== null) {
506
+ window.clearTimeout(skipDelayTimerRef.current);
507
+ }
508
+ isOpenDelayedRef.current = false;
509
+ skipDelayTimerRef.current = window.setTimeout(() => {
510
+ isOpenDelayedRef.current = true;
511
+ skipDelayTimerRef.current = null;
512
+ }, skipDelayDuration);
513
+ }, [skipDelayDuration]);
514
+ const getOpenDelay = useCallback(
515
+ () => isOpenDelayedRef.current ? delayDuration : 0,
516
+ [delayDuration]
517
+ );
518
+ const requestExclusiveOpen = useCallback((closeCallback) => {
519
+ activeCloseRef.current?.();
520
+ activeCloseRef.current = closeCallback;
521
+ }, []);
522
+ const releaseExclusiveOpen = useCallback((closeCallback) => {
523
+ if (activeCloseRef.current === closeCallback) {
524
+ activeCloseRef.current = null;
525
+ }
526
+ }, []);
527
+ const closeActiveTooltip = useCallback(() => {
528
+ activeCloseRef.current?.();
529
+ activeCloseRef.current = null;
530
+ }, []);
531
+ useEffect(
532
+ () => () => {
533
+ if (skipDelayTimerRef.current !== null) {
534
+ window.clearTimeout(skipDelayTimerRef.current);
535
+ }
536
+ },
537
+ []
538
+ );
539
+ useEffect(() => {
540
+ const handleDismiss = () => closeActiveTooltip();
541
+ const handlePointerOut = (event) => {
542
+ if (event.pointerType !== "mouse") return;
543
+ const related = event.relatedTarget;
544
+ if (related instanceof Node && document.documentElement.contains(related)) {
545
+ return;
546
+ }
547
+ closeActiveTooltip();
548
+ };
549
+ window.addEventListener("blur", handleDismiss);
550
+ window.addEventListener("scroll", handleDismiss, true);
551
+ document.documentElement.addEventListener("pointerout", handlePointerOut);
552
+ return () => {
553
+ window.removeEventListener("blur", handleDismiss);
554
+ window.removeEventListener("scroll", handleDismiss, true);
555
+ document.documentElement.removeEventListener("pointerout", handlePointerOut);
556
+ };
557
+ }, [closeActiveTooltip]);
558
+ return /* @__PURE__ */ jsx(
559
+ TooltipGroupContext.Provider,
560
+ {
561
+ value: {
562
+ delayDuration,
563
+ skipDelayDuration,
564
+ closeDelayDuration,
565
+ getOpenDelay,
566
+ onTooltipOpened,
567
+ closeActiveTooltip,
568
+ requestExclusiveOpen,
569
+ releaseExclusiveOpen
570
+ },
571
+ children
572
+ }
573
+ );
574
+ }
575
+ function useTooltipRoot() {
576
+ const context = useContext(TooltipRootContext);
577
+ if (!context) {
578
+ throw new Error("Tooltip components must be used within TooltipRoot");
579
+ }
580
+ return context;
581
+ }
582
+ function TooltipRoot({ children }) {
583
+ const group = useTooltipGroup();
584
+ const [open, setOpen] = useState(false);
585
+ const triggerRef = useRef(null);
586
+ const contentId = useId();
587
+ const openTimerRef = useRef(null);
588
+ const closeTimerRef = useRef(null);
589
+ const closeTooltip = useCallback(() => {
590
+ setOpen(false);
591
+ group.releaseExclusiveOpen(closeTooltip);
592
+ }, [group]);
593
+ const clearTimers = useCallback(() => {
594
+ if (openTimerRef.current !== null) {
595
+ window.clearTimeout(openTimerRef.current);
596
+ openTimerRef.current = null;
597
+ }
598
+ if (closeTimerRef.current !== null) {
599
+ window.clearTimeout(closeTimerRef.current);
600
+ closeTimerRef.current = null;
601
+ }
602
+ }, []);
603
+ const openImmediately = useCallback(() => {
604
+ clearTimers();
605
+ group.requestExclusiveOpen(closeTooltip);
606
+ setOpen(true);
607
+ group.onTooltipOpened();
608
+ }, [clearTimers, closeTooltip, group]);
609
+ const scheduleOpen = useCallback(() => {
610
+ clearTimers();
611
+ const delay = group.getOpenDelay();
612
+ openTimerRef.current = window.setTimeout(() => {
613
+ openImmediately();
614
+ openTimerRef.current = null;
615
+ }, delay);
616
+ }, [clearTimers, group, openImmediately]);
617
+ const scheduleClose = useCallback(() => {
618
+ clearTimers();
619
+ closeTimerRef.current = window.setTimeout(() => {
620
+ closeTooltip();
621
+ closeTimerRef.current = null;
622
+ }, group.closeDelayDuration);
623
+ }, [clearTimers, closeTooltip, group.closeDelayDuration]);
624
+ useEffect(() => {
625
+ return () => {
626
+ clearTimers();
627
+ group.releaseExclusiveOpen(closeTooltip);
628
+ };
629
+ }, [clearTimers, closeTooltip, group]);
630
+ return /* @__PURE__ */ jsx(
631
+ TooltipRootContext.Provider,
632
+ {
633
+ value: {
634
+ open,
635
+ triggerRef,
636
+ contentId,
637
+ scheduleOpen,
638
+ scheduleClose,
639
+ openImmediately
640
+ },
641
+ children
642
+ }
643
+ );
644
+ }
645
+ function TooltipTrigger({
646
+ asChild,
647
+ children
648
+ }) {
649
+ const {
650
+ open,
651
+ triggerRef,
652
+ scheduleOpen,
653
+ scheduleClose,
654
+ openImmediately,
655
+ contentId
656
+ } = useTooltipRoot();
657
+ const triggerProps = {
658
+ ref: (node) => {
659
+ triggerRef.current = node;
660
+ },
661
+ "aria-describedby": open ? contentId : void 0,
662
+ onMouseEnter: () => scheduleOpen(),
663
+ onMouseLeave: () => scheduleClose(),
664
+ onFocus: () => openImmediately(),
665
+ onBlur: () => scheduleClose()
666
+ };
667
+ if (asChild) {
668
+ return renderAsChild(true, children, triggerProps);
669
+ }
670
+ return /* @__PURE__ */ jsx("span", { ...triggerProps, children });
671
+ }
672
+ function TooltipPortal({ children }) {
673
+ const { open } = useTooltipRoot();
674
+ if (!open || typeof document === "undefined") return null;
675
+ return createPortal(children, document.body);
676
+ }
677
+ function TooltipContent({
678
+ children,
679
+ side: preferredSide = "top",
680
+ sideOffset = 6,
681
+ className,
682
+ style
683
+ }) {
684
+ const { open, triggerRef, contentId } = useTooltipRoot();
685
+ const contentRef = useRef(null);
686
+ const [placement, setPlacement] = useState(null);
687
+ useLayoutEffect(() => {
688
+ if (!open) {
689
+ setPlacement(null);
690
+ return;
691
+ }
692
+ const updatePlacement = () => {
693
+ const trigger = triggerRef.current;
694
+ const content = contentRef.current;
695
+ if (!trigger || !content) return;
696
+ const triggerRect = trigger.getBoundingClientRect();
697
+ const { width, height } = content.getBoundingClientRect();
698
+ if (width === 0 || height === 0) {
699
+ requestAnimationFrame(updatePlacement);
700
+ return;
701
+ }
702
+ setPlacement(
703
+ computeTooltipPlacement({
704
+ triggerRect,
705
+ contentWidth: width,
706
+ contentHeight: height,
707
+ preferredSide,
708
+ sideOffset
709
+ })
710
+ );
711
+ };
712
+ updatePlacement();
713
+ window.addEventListener("scroll", updatePlacement, true);
714
+ window.addEventListener("resize", updatePlacement);
715
+ return () => {
716
+ window.removeEventListener("scroll", updatePlacement, true);
717
+ window.removeEventListener("resize", updatePlacement);
718
+ };
719
+ }, [open, preferredSide, sideOffset, triggerRef]);
720
+ if (!open) return null;
721
+ const isPositioned = placement !== null;
722
+ return /* @__PURE__ */ jsx(
723
+ "div",
724
+ {
725
+ ref: contentRef,
726
+ id: contentId,
727
+ role: "tooltip",
728
+ className: [TOOLTIP_CONTENT_BASE, className].filter(Boolean).join(" "),
729
+ style: {
730
+ top: placement?.top ?? -9999,
731
+ left: placement?.left ?? -9999,
732
+ visibility: isPositioned ? "visible" : "hidden",
733
+ opacity: isPositioned ? 1 : 0,
734
+ ...style
735
+ },
736
+ children
737
+ }
738
+ );
739
+ }
740
+ var TooltipGroupContext, DEFAULT_CLOSE_DELAY_MS, TooltipRootContext, TOOLTIP_CONTENT_BASE;
741
+ var init_tooltip = __esm({
742
+ "src/features/file-viewer/primitives/tooltip.tsx"() {
743
+ init_as_child();
744
+ init_compute_tooltip_placement();
745
+ TooltipGroupContext = createContext(null);
746
+ DEFAULT_CLOSE_DELAY_MS = 0;
747
+ TooltipRootContext = createContext(null);
748
+ TOOLTIP_CONTENT_BASE = "pointer-events-none fixed z-[200] w-max max-w-xs transition-opacity duration-200";
749
+ }
750
+ });
751
+ function FileViewerTooltipProvider({
752
+ children
753
+ }) {
754
+ const tooltipDefaults = getFileViewerDefaults().tooltip;
755
+ return /* @__PURE__ */ jsx(
756
+ TooltipProvider,
757
+ {
758
+ delayDuration: tooltipDefaults?.delayDuration ?? 300,
759
+ skipDelayDuration: tooltipDefaults?.skipDelayDuration ?? 500,
760
+ children
761
+ }
762
+ );
763
+ }
764
+ function TooltipDisabledTarget({ children }) {
765
+ const closeActiveTooltip = useCloseActiveTooltip();
766
+ return renderAsChild(true, children, {
767
+ className: "pointer-events-auto",
768
+ onMouseEnter: () => closeActiveTooltip(),
769
+ onFocus: () => closeActiveTooltip()
770
+ });
771
+ }
772
+ function FileViewerTooltip({
773
+ content,
774
+ children,
775
+ disabled = false,
776
+ classNames,
777
+ styles
778
+ }) {
779
+ if (disabled) {
780
+ return /* @__PURE__ */ jsx(TooltipDisabledTarget, { children });
781
+ }
782
+ const tooltipDefaults = getFileViewerDefaults().tooltip;
783
+ return /* @__PURE__ */ jsxs(TooltipRoot, { children: [
784
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children }),
785
+ /* @__PURE__ */ jsx(TooltipPortal, { children: /* @__PURE__ */ jsx(
786
+ TooltipContent,
787
+ {
788
+ side: "top",
789
+ sideOffset: 6,
790
+ className: mergeClassNames(
791
+ FILE_VIEWER_TOOLTIP_CONTENT_DEFAULT,
792
+ tooltipDefaults?.classNames?.content,
793
+ classNames?.content
794
+ ),
795
+ style: mergeStyles(
796
+ tooltipDefaults?.styles?.content,
797
+ styles?.content
798
+ ),
799
+ children: content
800
+ }
801
+ ) })
802
+ ] });
803
+ }
804
+ var FILE_VIEWER_TOOLTIP_CONTENT_DEFAULT;
805
+ var init_FileViewerTooltip = __esm({
806
+ "src/features/file-viewer/components/FileViewerTooltip.tsx"() {
807
+ init_config();
808
+ init_tooltip();
809
+ init_as_child();
810
+ init_merge_slot_props();
811
+ FILE_VIEWER_TOOLTIP_CONTENT_DEFAULT = "select-none rounded-md border border-neutral-700/50 bg-neutral-900/75 px-2 py-1.5 text-xs text-white/90 shadow-lg backdrop-blur-sm";
812
+ }
813
+ });
814
+
815
+ // src/features/file-viewer/translations.ts
816
+ function applyTranslationOverlay(builtIn, overlay) {
817
+ if (!overlay) return builtIn;
818
+ return {
819
+ fileViewer: { ...builtIn.fileViewer, ...overlay.fileViewer },
820
+ imageViewer: { ...builtIn.imageViewer, ...overlay.imageViewer },
821
+ pdfViewer: { ...builtIn.pdfViewer, ...overlay.pdfViewer }
822
+ };
823
+ }
824
+ function getFileViewerTranslations(language) {
825
+ const builtIn = fileViewerTranslationsByLanguage[language];
826
+ const overlay = getFileViewerDefaults().translations?.[language];
827
+ if (!overlay) {
828
+ return builtIn;
829
+ }
830
+ return applyTranslationOverlay(
831
+ builtIn,
832
+ overlay
833
+ );
834
+ }
835
+ function resolveFormattedMessage(message, params) {
836
+ return typeof message === "function" ? message(params) : message;
837
+ }
838
+ var fileViewerTranslationsByLanguage, defaultFileViewerTranslations;
839
+ var init_translations = __esm({
840
+ "src/features/file-viewer/translations.ts"() {
841
+ init_config();
842
+ fileViewerTranslationsByLanguage = {
843
+ english: {
844
+ fileViewer: {
845
+ closeAriaLabel: "Close",
846
+ printAriaLabel: "Print",
847
+ printTooltip: "Print",
848
+ downloadAriaLabel: "Download",
849
+ downloadTooltip: "Download",
850
+ downloadInProgressAriaLabel: "Downloading\u2026",
851
+ downloadInProgressTooltip: "Downloading\u2026",
852
+ openInModalAriaLabel: "Open in full screen",
853
+ openInModalTooltip: "Open in full screen",
854
+ unsupportedFileType: ({ extension }) => `No preview is available for file type "${extension}".`
855
+ },
856
+ imageViewer: {
857
+ zoomOutAriaLabel: "Zoom out",
858
+ fitScreenAriaLabel: "Fit image",
859
+ zoomInAriaLabel: "Zoom in",
860
+ zoomOutTooltip: "Zoom out",
861
+ fitScreenTooltip: "Fit image",
862
+ zoomInTooltip: "Zoom in"
863
+ },
864
+ pdfViewer: {
865
+ pageLabel: "Page",
866
+ pageInputAriaLabel: ({ value }) => `Page ${value}`,
867
+ previousPageAriaLabel: "Previous page",
868
+ nextPageAriaLabel: "Next page",
869
+ previousPageTooltip: "Previous page",
870
+ nextPageTooltip: "Next page",
871
+ zoomOutAriaLabel: "Zoom out",
872
+ fitWidthAriaLabel: "Fit width",
873
+ zoomInAriaLabel: "Zoom in",
874
+ zoomOutTooltip: "Zoom out",
875
+ fitWidthTooltip: "Fit width",
876
+ zoomInTooltip: "Zoom in"
877
+ }
878
+ },
879
+ portuguese: {
880
+ fileViewer: {
881
+ closeAriaLabel: "Fechar",
882
+ printAriaLabel: "Imprimir",
883
+ printTooltip: "Imprimir",
884
+ downloadAriaLabel: "Transferir",
885
+ downloadTooltip: "Transferir",
886
+ downloadInProgressAriaLabel: "A transferir\u2026",
887
+ downloadInProgressTooltip: "A transferir\u2026",
888
+ openInModalAriaLabel: "Visualizar em tela cheia",
889
+ openInModalTooltip: "Visualizar em tela cheia",
890
+ unsupportedFileType: ({ extension }) => `Ainda n\xE3o existem visualiza\xE7\xF5es para arquivos do tipo "${extension}".`
891
+ },
892
+ imageViewer: {
893
+ zoomOutAriaLabel: "Diminuir zoom",
894
+ fitScreenAriaLabel: "Ajustar imagem",
895
+ zoomInAriaLabel: "Aumentar zoom",
896
+ zoomOutTooltip: "Diminuir zoom",
897
+ fitScreenTooltip: "Ajustar imagem",
898
+ zoomInTooltip: "Aumentar zoom"
899
+ },
900
+ pdfViewer: {
901
+ pageLabel: "P\xE1gina",
902
+ pageInputAriaLabel: ({ value }) => `P\xE1gina ${value}`,
903
+ previousPageAriaLabel: "P\xE1gina anterior",
904
+ nextPageAriaLabel: "Pr\xF3xima p\xE1gina",
905
+ previousPageTooltip: "P\xE1gina anterior",
906
+ nextPageTooltip: "Pr\xF3xima p\xE1gina",
907
+ zoomOutAriaLabel: "Diminuir zoom",
908
+ fitWidthAriaLabel: "Ajustar \xE0 largura",
909
+ zoomInAriaLabel: "Aumentar zoom",
910
+ zoomOutTooltip: "Diminuir zoom",
911
+ fitWidthTooltip: "Ajustar \xE0 largura",
912
+ zoomInTooltip: "Aumentar zoom"
913
+ }
914
+ }
915
+ };
916
+ defaultFileViewerTranslations = fileViewerTranslationsByLanguage.english;
917
+ }
918
+ });
919
+
920
+ // src/features/file-viewer/utils/resolve-options.ts
921
+ function resolveOption(instance, global, builtIn) {
922
+ if (instance !== void 0) {
923
+ return instance;
924
+ }
925
+ if (global !== void 0) {
926
+ return global;
927
+ }
928
+ return builtIn;
929
+ }
930
+ function resolveHideCloseButton(instance, global, mode) {
931
+ if (instance !== void 0) {
932
+ return instance;
933
+ }
934
+ if (global !== void 0) {
935
+ return global;
936
+ }
937
+ return mode === "inline";
938
+ }
939
+ var init_resolve_options = __esm({
940
+ "src/features/file-viewer/utils/resolve-options.ts"() {
941
+ }
942
+ });
943
+
944
+ // src/features/file-viewer/primitives/scroll-area-math.ts
945
+ function getThumbRatio(viewportSize, contentSize) {
946
+ const ratio = viewportSize / contentSize;
947
+ return Number.isNaN(ratio) ? 0 : ratio;
948
+ }
949
+ function getThumbSize(sizes) {
950
+ const ratio = getThumbRatio(sizes.viewport, sizes.content);
951
+ const scrollbarPadding = sizes.scrollbar.paddingStart + sizes.scrollbar.paddingEnd;
952
+ const thumbSize = (sizes.scrollbar.size - scrollbarPadding) * ratio;
953
+ return Math.max(thumbSize, 18);
954
+ }
955
+ function hasScrollAreaThumb(sizes) {
956
+ if (sizes.viewport <= 0 || sizes.content <= 0 || sizes.scrollbar.size <= 0) {
957
+ return false;
958
+ }
959
+ return sizes.content > sizes.viewport + 1;
960
+ }
961
+ function clamp(value, [min, max]) {
962
+ return Math.min(Math.max(value, min), max);
963
+ }
964
+ function linearScale(input, output) {
965
+ return (value) => {
966
+ if (input[0] === input[1] || output[0] === output[1]) return output[0];
967
+ const ratio = (output[1] - output[0]) / (input[1] - input[0]);
968
+ return output[0] + ratio * (value - input[0]);
969
+ };
970
+ }
971
+ function getThumbOffsetFromScroll(scrollPos, sizes) {
972
+ const thumbSizePx = getThumbSize(sizes);
973
+ const scrollbarPadding = sizes.scrollbar.paddingStart + sizes.scrollbar.paddingEnd;
974
+ const scrollbarTrack = sizes.scrollbar.size - scrollbarPadding;
975
+ const maxScrollPos = sizes.content - sizes.viewport;
976
+ const maxThumbPos = scrollbarTrack - thumbSizePx;
977
+ const scrollWithoutMomentum = clamp(scrollPos, [0, maxScrollPos]);
978
+ const interpolate = linearScale([0, maxScrollPos], [0, maxThumbPos]);
979
+ return interpolate(scrollWithoutMomentum);
980
+ }
981
+ function getScrollPositionFromPointer(pointerPos, pointerOffset, sizes) {
982
+ const thumbSizePx = getThumbSize(sizes);
983
+ const thumbCenter = thumbSizePx / 2;
984
+ const offset = pointerOffset || thumbCenter;
985
+ const thumbOffsetFromEnd = thumbSizePx - offset;
986
+ const minPointerPos = sizes.scrollbar.paddingStart + offset;
987
+ const maxPointerPos = sizes.scrollbar.size - sizes.scrollbar.paddingEnd - thumbOffsetFromEnd;
988
+ const maxScrollPos = sizes.content - sizes.viewport;
989
+ const interpolate = linearScale([minPointerPos, maxPointerPos], [0, maxScrollPos]);
990
+ return interpolate(pointerPos);
991
+ }
992
+ var init_scroll_area_math = __esm({
993
+ "src/features/file-viewer/primitives/scroll-area-math.ts"() {
994
+ }
995
+ });
996
+ function useScrollAreaContext(component) {
997
+ const context = useContext(ScrollAreaContext);
998
+ if (!context) {
999
+ throw new Error(`${component} must be used within ScrollAreaRoot`);
1000
+ }
1001
+ return context;
1002
+ }
1003
+ function assignRef2(ref, value) {
1004
+ if (typeof ref === "function") {
1005
+ ref(value);
1006
+ return;
1007
+ }
1008
+ if (ref && typeof ref === "object") {
1009
+ ref.current = value;
1010
+ }
1011
+ }
1012
+ function mergeRefs2(...refs) {
1013
+ return (value) => {
1014
+ refs.forEach((ref) => assignRef2(ref, value));
1015
+ };
1016
+ }
1017
+ function readScrollbarPadding(track) {
1018
+ const computed = getComputedStyle(track);
1019
+ return {
1020
+ paddingStart: Number.parseInt(computed.paddingTop, 10) || 0,
1021
+ paddingEnd: Number.parseInt(computed.paddingBottom, 10) || 0,
1022
+ paddingLeft: Number.parseInt(computed.paddingLeft, 10) || 0,
1023
+ paddingRight: Number.parseInt(computed.paddingRight, 10) || 0
1024
+ };
1025
+ }
1026
+ function ScrollAreaRoot({
1027
+ className,
1028
+ style,
1029
+ children,
1030
+ ...rest
1031
+ }) {
1032
+ const viewportRef = useRef(null);
1033
+ const [cornerWidth, setCornerWidth] = useState(0);
1034
+ const [cornerHeight, setCornerHeight] = useState(0);
1035
+ return /* @__PURE__ */ jsx(
1036
+ ScrollAreaContext.Provider,
1037
+ {
1038
+ value: {
1039
+ viewportRef,
1040
+ cornerWidth,
1041
+ cornerHeight,
1042
+ setCornerWidth,
1043
+ setCornerHeight
1044
+ },
1045
+ children: /* @__PURE__ */ jsx(
1046
+ "div",
1047
+ {
1048
+ className,
1049
+ style: {
1050
+ position: "relative",
1051
+ overflow: "hidden",
1052
+ ["--scroll-area-corner-width"]: `${cornerWidth}px`,
1053
+ ["--scroll-area-corner-height"]: `${cornerHeight}px`,
1054
+ ...style
1055
+ },
1056
+ ...rest,
1057
+ children
1058
+ }
1059
+ )
1060
+ }
1061
+ );
1062
+ }
1063
+ function ScrollAreaScrollbar({
1064
+ orientation,
1065
+ className,
1066
+ style,
1067
+ children,
1068
+ ...rest
1069
+ }) {
1070
+ const context = useScrollAreaContext("ScrollAreaScrollbar");
1071
+ const trackRef = useRef(null);
1072
+ const thumbRef = useRef(null);
1073
+ const pointerOffsetRef = useRef(0);
1074
+ const [sizes, setSizes] = useState(EMPTY_SIZES);
1075
+ const isVertical = orientation === "vertical";
1076
+ const showThumb = hasScrollAreaThumb(sizes);
1077
+ const updateSizes = useCallback(() => {
1078
+ const viewport = context.viewportRef.current;
1079
+ const track = trackRef.current;
1080
+ if (!viewport || !track) return;
1081
+ const padding = readScrollbarPadding(track);
1082
+ if (isVertical) {
1083
+ setSizes({
1084
+ content: viewport.scrollHeight,
1085
+ viewport: viewport.offsetHeight,
1086
+ scrollbar: {
1087
+ size: track.clientHeight,
1088
+ paddingStart: padding.paddingStart,
1089
+ paddingEnd: padding.paddingEnd
1090
+ }
1091
+ });
1092
+ context.setCornerWidth(track.offsetWidth);
1093
+ return;
1094
+ }
1095
+ setSizes({
1096
+ content: viewport.scrollWidth,
1097
+ viewport: viewport.offsetWidth,
1098
+ scrollbar: {
1099
+ size: track.clientWidth,
1100
+ paddingStart: padding.paddingLeft,
1101
+ paddingEnd: padding.paddingRight
1102
+ }
1103
+ });
1104
+ context.setCornerHeight(track.offsetHeight);
1105
+ }, [context, isVertical]);
1106
+ const updateThumbPosition = useCallback(() => {
1107
+ const viewport = context.viewportRef.current;
1108
+ const thumb = thumbRef.current;
1109
+ if (!viewport || !thumb || !showThumb) return;
1110
+ const scrollPos = isVertical ? viewport.scrollTop : viewport.scrollLeft;
1111
+ const offset = getThumbOffsetFromScroll(scrollPos, sizes);
1112
+ thumb.style.transform = isVertical ? `translate3d(0, ${offset}px, 0)` : `translate3d(${offset}px, 0, 0)`;
1113
+ }, [context.viewportRef, isVertical, showThumb, sizes]);
1114
+ useEffect(() => {
1115
+ const viewport = context.viewportRef.current;
1116
+ const track = trackRef.current;
1117
+ if (!viewport || !track) return;
1118
+ const runUpdate = () => {
1119
+ requestAnimationFrame(updateSizes);
1120
+ };
1121
+ runUpdate();
1122
+ const resizeObserver = new ResizeObserver(runUpdate);
1123
+ resizeObserver.observe(viewport);
1124
+ resizeObserver.observe(track);
1125
+ const contentRoot = viewport.firstElementChild;
1126
+ if (contentRoot) {
1127
+ resizeObserver.observe(contentRoot);
1128
+ }
1129
+ const mutationObserver = new MutationObserver(runUpdate);
1130
+ mutationObserver.observe(viewport, {
1131
+ childList: true,
1132
+ subtree: true,
1133
+ attributes: true
1134
+ });
1135
+ return () => {
1136
+ resizeObserver.disconnect();
1137
+ mutationObserver.disconnect();
1138
+ };
1139
+ }, [context.viewportRef, updateSizes]);
1140
+ useEffect(() => {
1141
+ updateThumbPosition();
1142
+ }, [sizes, updateThumbPosition]);
1143
+ useEffect(() => {
1144
+ const viewport = context.viewportRef.current;
1145
+ if (!viewport) return;
1146
+ let frame = 0;
1147
+ const handleScroll = () => {
1148
+ if (frame) cancelAnimationFrame(frame);
1149
+ frame = requestAnimationFrame(() => {
1150
+ updateThumbPosition();
1151
+ frame = 0;
1152
+ });
1153
+ };
1154
+ viewport.addEventListener("scroll", handleScroll, { passive: true });
1155
+ return () => {
1156
+ viewport.removeEventListener("scroll", handleScroll);
1157
+ if (frame) cancelAnimationFrame(frame);
1158
+ };
1159
+ }, [context.viewportRef, updateThumbPosition]);
1160
+ const getPointerOnTrack = useCallback(
1161
+ (clientX, clientY) => {
1162
+ const track = trackRef.current;
1163
+ if (!track) return 0;
1164
+ const rect = track.getBoundingClientRect();
1165
+ return isVertical ? clientY - rect.top : clientX - rect.left;
1166
+ },
1167
+ [isVertical]
1168
+ );
1169
+ const setScrollFromPointer = useCallback(
1170
+ (pointerPos) => {
1171
+ const viewport = context.viewportRef.current;
1172
+ if (!viewport) return;
1173
+ const scrollPos = getScrollPositionFromPointer(
1174
+ pointerPos,
1175
+ pointerOffsetRef.current,
1176
+ sizes
1177
+ );
1178
+ if (isVertical) {
1179
+ viewport.scrollTop = scrollPos;
1180
+ } else {
1181
+ viewport.scrollLeft = scrollPos;
1182
+ }
1183
+ },
1184
+ [context.viewportRef, isVertical, sizes]
1185
+ );
1186
+ const handleTrackPointerDown = (event) => {
1187
+ if (event.button !== 0) return;
1188
+ const track = trackRef.current;
1189
+ if (!track) return;
1190
+ if (event.target === track) {
1191
+ pointerOffsetRef.current = 0;
1192
+ }
1193
+ event.currentTarget.setPointerCapture(event.pointerId);
1194
+ setScrollFromPointer(getPointerOnTrack(event.clientX, event.clientY));
1195
+ };
1196
+ const handleTrackPointerMove = (event) => {
1197
+ if (!event.currentTarget.hasPointerCapture(event.pointerId)) {
1198
+ return;
1199
+ }
1200
+ setScrollFromPointer(getPointerOnTrack(event.clientX, event.clientY));
1201
+ };
1202
+ const handleTrackPointerUp = (event) => {
1203
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
1204
+ event.currentTarget.releasePointerCapture(event.pointerId);
1205
+ }
1206
+ pointerOffsetRef.current = 0;
1207
+ };
1208
+ const thumbProps = children.props;
1209
+ const thumbClassName = thumbProps.className;
1210
+ const thumbStyleProp = thumbProps.style;
1211
+ const {
1212
+ className: _ignoredClassName,
1213
+ style: _ignoredStyle,
1214
+ ...thumbRest
1215
+ } = thumbProps;
1216
+ const thumbSizePx = getThumbSize(sizes);
1217
+ const thumbDimensionStyle = isVertical ? { height: thumbSizePx, width: "100%" } : { width: thumbSizePx, height: "100%" };
1218
+ const trackStyle = isVertical ? {
1219
+ position: "absolute",
1220
+ top: 0,
1221
+ right: 0,
1222
+ bottom: "var(--scroll-area-corner-height)"
1223
+ } : {
1224
+ position: "absolute",
1225
+ bottom: 0,
1226
+ left: 0,
1227
+ right: "var(--scroll-area-corner-width)"
1228
+ };
1229
+ return /* @__PURE__ */ jsx(
1230
+ "div",
1231
+ {
1232
+ ref: trackRef,
1233
+ "data-orientation": orientation,
1234
+ className,
1235
+ style: { ...trackStyle, ...style },
1236
+ onPointerDown: handleTrackPointerDown,
1237
+ onPointerMove: handleTrackPointerMove,
1238
+ onPointerUp: handleTrackPointerUp,
1239
+ onPointerCancel: handleTrackPointerUp,
1240
+ ...rest,
1241
+ children: showThumb ? /* @__PURE__ */ jsx(
1242
+ "div",
1243
+ {
1244
+ ref: thumbRef,
1245
+ "data-state": "visible",
1246
+ className: thumbClassName,
1247
+ style: {
1248
+ flex: "none",
1249
+ ...thumbDimensionStyle,
1250
+ ...thumbStyleProp
1251
+ },
1252
+ onPointerDownCapture: (event) => {
1253
+ thumbProps.onPointerDownCapture?.(event);
1254
+ const thumbRect = event.currentTarget.getBoundingClientRect();
1255
+ pointerOffsetRef.current = isVertical ? event.clientY - thumbRect.top : event.clientX - thumbRect.left;
1256
+ },
1257
+ ...thumbRest
1258
+ }
1259
+ ) : null
1260
+ }
1261
+ );
1262
+ }
1263
+ function ScrollAreaThumb({ className, style, ...rest }) {
1264
+ return /* @__PURE__ */ jsx(
1265
+ "div",
1266
+ {
1267
+ "data-state": "hidden",
1268
+ className,
1269
+ style,
1270
+ ...rest
1271
+ }
1272
+ );
1273
+ }
1274
+ function ScrollAreaCorner({ className, style, ...rest }) {
1275
+ const { cornerWidth, cornerHeight } = useScrollAreaContext("ScrollAreaCorner");
1276
+ if (!cornerWidth || !cornerHeight) {
1277
+ return null;
1278
+ }
1279
+ return /* @__PURE__ */ jsx(
1280
+ "div",
1281
+ {
1282
+ className,
1283
+ style: {
1284
+ position: "absolute",
1285
+ right: 0,
1286
+ bottom: 0,
1287
+ width: cornerWidth,
1288
+ height: cornerHeight,
1289
+ ...style
1290
+ },
1291
+ ...rest
1292
+ }
1293
+ );
1294
+ }
1295
+ var EMPTY_SIZES, ScrollAreaContext, ScrollAreaViewport;
1296
+ var init_scroll_area = __esm({
1297
+ "src/features/file-viewer/primitives/scroll-area.tsx"() {
1298
+ init_merge_slot_props();
1299
+ init_scroll_area_math();
1300
+ EMPTY_SIZES = {
1301
+ content: 0,
1302
+ viewport: 0,
1303
+ scrollbar: { size: 0, paddingStart: 0, paddingEnd: 0 }
1304
+ };
1305
+ ScrollAreaContext = createContext(null);
1306
+ ScrollAreaViewport = forwardRef(
1307
+ ({ className, children, onScroll, ...rest }, forwardedRef) => {
1308
+ const { viewportRef } = useScrollAreaContext("ScrollAreaViewport");
1309
+ return /* @__PURE__ */ jsx(
1310
+ "div",
1311
+ {
1312
+ ref: mergeRefs2(forwardedRef, viewportRef),
1313
+ onScroll,
1314
+ className: mergeClassNames(
1315
+ "size-full overflow-x-scroll overflow-y-scroll pdf-scroll-viewport",
1316
+ className
1317
+ ),
1318
+ ...rest,
1319
+ children: /* @__PURE__ */ jsx(
1320
+ "div",
1321
+ {
1322
+ className: "scroll-area-viewport-inner",
1323
+ style: { minWidth: "100%", display: "table" },
1324
+ children
1325
+ }
1326
+ )
1327
+ }
1328
+ );
1329
+ }
1330
+ );
1331
+ ScrollAreaViewport.displayName = "ScrollAreaViewport";
1332
+ }
1333
+ });
1334
+
1335
+ // src/features/file-viewer/pdf-viewer.css
1336
+ var init_pdf_viewer = __esm({
1337
+ "src/features/file-viewer/pdf-viewer.css"() {
1338
+ }
1339
+ });
1340
+ function ViewerToolbarDivider({
1341
+ className = "",
1342
+ style,
1343
+ classNames,
1344
+ styles
1345
+ }) {
1346
+ const globalToolbar = getFileViewerDefaults().toolbar;
1347
+ return /* @__PURE__ */ jsx(
1348
+ "div",
1349
+ {
1350
+ className: mergeClassNames(
1351
+ VIEWER_TOOLBAR_DIVIDER_DEFAULT,
1352
+ globalToolbar?.classNames?.divider,
1353
+ classNames?.divider,
1354
+ className
1355
+ ),
1356
+ style: mergeStyles(globalToolbar?.styles?.divider, styles?.divider, style),
1357
+ "aria-hidden": true
1358
+ }
1359
+ );
1360
+ }
1361
+ function ViewerToolbarIconButton({
1362
+ className = "",
1363
+ style,
1364
+ classNames,
1365
+ styles,
1366
+ ...rest
1367
+ }) {
1368
+ const globalToolbar = getFileViewerDefaults().toolbar;
1369
+ return /* @__PURE__ */ jsx(
1370
+ "button",
1371
+ {
1372
+ type: "button",
1373
+ className: mergeClassNames(
1374
+ VIEWER_TOOLBAR_ICON_BUTTON_DEFAULT,
1375
+ globalToolbar?.classNames?.iconButton,
1376
+ classNames?.iconButton,
1377
+ className
1378
+ ),
1379
+ style: mergeStyles(
1380
+ globalToolbar?.styles?.iconButton,
1381
+ styles?.iconButton,
1382
+ style
1383
+ ),
1384
+ ...rest
1385
+ }
1386
+ );
1387
+ }
1388
+ var VIEWER_TOOLBAR_DEFAULT, VIEWER_TOOLBAR_DIVIDER_DEFAULT, VIEWER_TOOLBAR_ICON_BUTTON_DEFAULT, ViewerFloatingToolbar;
1389
+ var init_ViewerToolbar = __esm({
1390
+ "src/features/file-viewer/components/ViewerToolbar.tsx"() {
1391
+ init_config();
1392
+ init_merge_slot_props();
1393
+ VIEWER_TOOLBAR_DEFAULT = "absolute bottom-4 left-1/2 z-10 flex h-[3.25rem] -translate-x-1/2 items-center gap-2 rounded-full border border-neutral-700/50 bg-neutral-900/60 px-4 text-white/90 shadow-lg backdrop-blur-sm transition-opacity duration-300";
1394
+ VIEWER_TOOLBAR_DIVIDER_DEFAULT = "mx-1 h-full w-px bg-white/20";
1395
+ VIEWER_TOOLBAR_ICON_BUTTON_DEFAULT = "cursor-pointer rounded-full p-2 text-white/70 transition-colors hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-white/40";
1396
+ ViewerFloatingToolbar = forwardRef(({ children, className = "", style, classNames, styles }, ref) => {
1397
+ const globalToolbar = getFileViewerDefaults().toolbar;
1398
+ return /* @__PURE__ */ jsx(
1399
+ "div",
1400
+ {
1401
+ ref,
1402
+ className: mergeClassNames(
1403
+ VIEWER_TOOLBAR_DEFAULT,
1404
+ globalToolbar?.classNames?.toolbar,
1405
+ classNames?.toolbar,
1406
+ className
1407
+ ),
1408
+ style: mergeStyles(globalToolbar?.styles?.toolbar, styles?.toolbar, style),
1409
+ children
1410
+ }
1411
+ );
1412
+ });
1413
+ ViewerFloatingToolbar.displayName = "ViewerFloatingToolbar";
1414
+ }
1415
+ });
1416
+ function getDistanceToElement(x, y, element) {
1417
+ const rect = element.getBoundingClientRect();
1418
+ const dx = Math.max(rect.left - x, 0, x - rect.right);
1419
+ const dy = Math.max(rect.top - y, 0, y - rect.bottom);
1420
+ return Math.sqrt(dx * dx + dy * dy);
1421
+ }
1422
+ function useAutoHide(targetRef, containerRef, options = {}) {
1423
+ const { proximityThreshold = 160, timeout = 3e3, activityDeps = [] } = options;
1424
+ const [isVisible, setIsVisible] = useState(true);
1425
+ const timerRef = useRef(null);
1426
+ const isNearRef = useRef(false);
1427
+ const startTimer = () => {
1428
+ if (timerRef.current !== null) {
1429
+ window.clearTimeout(timerRef.current);
1430
+ }
1431
+ timerRef.current = window.setTimeout(() => {
1432
+ setIsVisible(false);
1433
+ timerRef.current = null;
1434
+ }, timeout);
1435
+ };
1436
+ useEffect(() => {
1437
+ setIsVisible(true);
1438
+ if (!isNearRef.current) {
1439
+ startTimer();
1440
+ }
1441
+ return () => {
1442
+ if (timerRef.current !== null) {
1443
+ window.clearTimeout(timerRef.current);
1444
+ timerRef.current = null;
1445
+ }
1446
+ };
1447
+ }, activityDeps);
1448
+ useEffect(() => {
1449
+ const container = containerRef.current;
1450
+ if (!container) return;
1451
+ const handleMouseMove = (e) => {
1452
+ if (!targetRef.current) return;
1453
+ const distance = getDistanceToElement(e.clientX, e.clientY, targetRef.current);
1454
+ const isNear = distance <= proximityThreshold;
1455
+ if (isNear) {
1456
+ setIsVisible(true);
1457
+ if (timerRef.current !== null) {
1458
+ window.clearTimeout(timerRef.current);
1459
+ timerRef.current = null;
1460
+ }
1461
+ } else {
1462
+ if (isNearRef.current) {
1463
+ startTimer();
1464
+ }
1465
+ }
1466
+ isNearRef.current = isNear;
1467
+ };
1468
+ const handleMouseLeave = () => {
1469
+ isNearRef.current = false;
1470
+ startTimer();
1471
+ };
1472
+ container.addEventListener("mousemove", handleMouseMove);
1473
+ container.addEventListener("mouseleave", handleMouseLeave);
1474
+ return () => {
1475
+ container.removeEventListener("mousemove", handleMouseMove);
1476
+ container.removeEventListener("mouseleave", handleMouseLeave);
1477
+ };
1478
+ }, [proximityThreshold, timeout, targetRef, containerRef]);
1479
+ return isVisible;
1480
+ }
1481
+ var init_useAutoHide = __esm({
1482
+ "src/features/file-viewer/hooks/useAutoHide.ts"() {
1483
+ }
1484
+ });
1485
+
1486
+ // src/features/file-viewer/PdfViewer.tsx
1487
+ var PdfViewer_exports = {};
1488
+ __export(PdfViewer_exports, {
1489
+ default: () => PdfViewer
1490
+ });
1491
+ function PdfViewer(props) {
1492
+ const { url, language: languageProp, ...instanceProps } = props;
1493
+ const resolved = resolvePdfViewerProps(instanceProps);
1494
+ const viewMode = resolved.viewMode ?? "continuous";
1495
+ const debounceDelay = resolved.debounceDelay ?? 300;
1496
+ const zoomDebounceDelay = resolved.zoomDebounceDelay ?? 500;
1497
+ const renderTextLayer = resolved.renderTextLayer ?? true;
1498
+ const renderAnnotationLayer = resolved.renderAnnotationLayer ?? true;
1499
+ const renderLoading = resolved.renderLoading ?? null;
1500
+ const preloadAhead = resolved.preloadAhead ?? 1;
1501
+ const onLoadSuccess = resolved.onLoadSuccess;
1502
+ const onLoadError = resolved.onLoadError;
1503
+ const onPageChange = resolved.onPageChange;
1504
+ const defaultPage = resolved.defaultPage ?? 1;
1505
+ const renderPagination = resolved.renderPagination;
1506
+ const paginationClassName = resolved.paginationClassName;
1507
+ const className = resolved.className;
1508
+ const pageClassName = resolved.pageClassName;
1509
+ const classNames = resolved.classNames;
1510
+ const styles = resolved.styles;
1511
+ const language = resolveOption(
1512
+ languageProp,
1513
+ getFileViewerDefaults().language,
1514
+ "english"
1515
+ );
1516
+ const pdfT = getFileViewerTranslations(language).pdfViewer;
1517
+ const autoHideDefaults = getFileViewerDefaults().autoHide;
1518
+ const pdfClassName = (key, builtIn, extra) => mergeClassNames(
1519
+ builtIn,
1520
+ classNames?.[key],
1521
+ key === "root" ? className : void 0,
1522
+ key === "page" ? pageClassName : void 0,
1523
+ key === "pagination" ? paginationClassName : void 0,
1524
+ extra
1525
+ );
1526
+ const pdfStyle = (key) => styles?.[key];
1527
+ const [numPages, setNumPages] = useState(0);
1528
+ const [pageNumber, setPageNumber] = useState(defaultPage);
1529
+ const [visibleRange, setVisibleRange] = useState({ start: defaultPage, end: defaultPage });
1530
+ const [inputPage, setInputPage] = useState(String(defaultPage));
1531
+ const containerRef = useRef(null);
1532
+ const toolbarRef = useRef(null);
1533
+ const pageRefs = useRef(/* @__PURE__ */ new Map());
1534
+ const [instantSize, setInstantSize] = useState({ width: 0, height: 0 });
1535
+ const [renderedSize, setRenderedSize] = useState({ width: 0, height: 0 });
1536
+ const [pageOriginalSize, setPageOriginalSize] = useState({
1537
+ width: 0,
1538
+ height: 0
1539
+ });
1540
+ const renderedHeights = useRef(/* @__PURE__ */ new Map());
1541
+ const [instantZoom, setInstantZoom] = useState(1);
1542
+ const [renderedZoom, setRenderedZoom] = useState(1);
1543
+ const isToolbarVisible = useAutoHide(toolbarRef, containerRef, {
1544
+ proximityThreshold: autoHideDefaults?.proximityThreshold,
1545
+ timeout: autoHideDefaults?.timeout,
1546
+ activityDeps: [pageNumber, instantZoom]
1547
+ });
1548
+ const [hasZoomed, setHasZoomed] = useState(false);
1549
+ useEffect(() => {
1550
+ setInstantZoom(1);
1551
+ setRenderedZoom(1);
1552
+ setHasZoomed(false);
1553
+ renderedHeights.current.clear();
1554
+ }, [url, viewMode]);
1555
+ useEffect(() => {
1556
+ if (instantZoom === renderedZoom) return;
1557
+ const timerId = window.setTimeout(() => {
1558
+ setRenderedZoom(instantZoom);
1559
+ renderedHeights.current.clear();
1560
+ }, zoomDebounceDelay);
1561
+ return () => window.clearTimeout(timerId);
1562
+ }, [instantZoom, renderedZoom, zoomDebounceDelay]);
1563
+ const pendingInputPageRef = useRef(null);
1564
+ const pendingInputPageTimerRef = useRef(null);
1565
+ useEffect(() => {
1566
+ const pending = pendingInputPageRef.current;
1567
+ if (pending !== null) {
1568
+ if (pageNumber === pending) {
1569
+ pendingInputPageRef.current = null;
1570
+ if (pendingInputPageTimerRef.current !== null) {
1571
+ window.clearTimeout(pendingInputPageTimerRef.current);
1572
+ pendingInputPageTimerRef.current = null;
1573
+ }
1574
+ setInputPage(String(pageNumber));
1575
+ }
1576
+ return;
1577
+ }
1578
+ setInputPage(String(pageNumber));
1579
+ }, [pageNumber]);
1580
+ useEffect(() => {
1581
+ const updateSize = () => {
1582
+ if (containerRef.current) {
1583
+ const newSize = {
1584
+ width: Math.trunc(containerRef.current.clientWidth),
1585
+ height: Math.trunc(containerRef.current.clientHeight)
1586
+ };
1587
+ setInstantSize((prev) => {
1588
+ if (prev.width === newSize.width && prev.height === newSize.height) return prev;
1589
+ return newSize;
1590
+ });
1591
+ setRenderedSize(
1592
+ (prev) => prev.width === 0 && newSize.width > 0 ? newSize : prev
1593
+ );
1594
+ }
1595
+ };
1596
+ const observer = new ResizeObserver(() => updateSize());
1597
+ if (containerRef.current) observer.observe(containerRef.current);
1598
+ updateSize();
1599
+ return () => observer.disconnect();
1600
+ }, []);
1601
+ useEffect(() => {
1602
+ if (instantSize.width === 0) return;
1603
+ const timerId = window.setTimeout(() => {
1604
+ setRenderedSize(instantSize);
1605
+ }, debounceDelay);
1606
+ return () => window.clearTimeout(timerId);
1607
+ }, [instantSize, debounceDelay]);
1608
+ const calculateDimensions = useCallback(
1609
+ (container) => {
1610
+ if (!container.width || !container.height) {
1611
+ return {
1612
+ width: container.width ? Math.trunc(container.width) : void 0,
1613
+ height: container.height ? Math.trunc(container.height) : void 0
1614
+ };
1615
+ }
1616
+ let remToPx = 16;
1617
+ if (typeof document !== "undefined") {
1618
+ remToPx = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
1619
+ }
1620
+ const width = Math.trunc(Math.min(container.width, remToPx * 50));
1621
+ if (!pageOriginalSize.width || !pageOriginalSize.height) {
1622
+ return {
1623
+ width,
1624
+ height: Math.trunc(width * 1.414)
1625
+ // Approximate A4 ratio
1626
+ };
1627
+ }
1628
+ const pageRatio = pageOriginalSize.width / pageOriginalSize.height;
1629
+ return { width, height: Math.trunc(width / pageRatio) };
1630
+ },
1631
+ [pageOriginalSize]
1632
+ );
1633
+ const instantDimensions = useMemo(
1634
+ () => calculateDimensions(instantSize),
1635
+ [calculateDimensions, instantSize]
1636
+ );
1637
+ const renderedDimensions = useMemo(
1638
+ () => calculateDimensions(renderedSize),
1639
+ [calculateDimensions, renderedSize]
1640
+ );
1641
+ const zoomedRenderedDimensions = useMemo(() => {
1642
+ return {
1643
+ width: renderedDimensions.width ? Math.trunc(renderedDimensions.width * renderedZoom) : void 0,
1644
+ height: renderedDimensions.height ? Math.trunc(renderedDimensions.height * renderedZoom) : void 0
1645
+ };
1646
+ }, [renderedDimensions, renderedZoom]);
1647
+ const zoomedInstantDimensions = useMemo(() => {
1648
+ return {
1649
+ width: instantDimensions.width ? Math.trunc(instantDimensions.width * instantZoom) : void 0,
1650
+ height: instantDimensions.height ? Math.trunc(instantDimensions.height * instantZoom) : void 0
1651
+ };
1652
+ }, [instantDimensions, instantZoom]);
1653
+ const layoutScale = useMemo(() => {
1654
+ const renderedWidth = zoomedRenderedDimensions.width;
1655
+ const renderedHeight = zoomedRenderedDimensions.height;
1656
+ const instantWidth = zoomedInstantDimensions.width;
1657
+ const instantHeight = zoomedInstantDimensions.height;
1658
+ if (!renderedWidth || !instantWidth || !instantHeight) {
1659
+ return 1;
1660
+ }
1661
+ if (!renderedHeight) {
1662
+ return Number((instantWidth / renderedWidth).toFixed(4));
1663
+ }
1664
+ return Number(
1665
+ Math.min(
1666
+ instantWidth / renderedWidth,
1667
+ instantHeight / renderedHeight
1668
+ ).toFixed(4)
1669
+ );
1670
+ }, [zoomedInstantDimensions, zoomedRenderedDimensions]);
1671
+ const isLayoutSyncing = useMemo(() => {
1672
+ if (instantZoom !== renderedZoom) return true;
1673
+ if (instantSize.width !== renderedSize.width || instantSize.height !== renderedSize.height) {
1674
+ return true;
1675
+ }
1676
+ return Math.abs(layoutScale - 1) > 1e-3;
1677
+ }, [
1678
+ instantZoom,
1679
+ renderedZoom,
1680
+ instantSize,
1681
+ renderedSize,
1682
+ layoutScale
1683
+ ]);
1684
+ const handleDocumentLoadSuccess = ({
1685
+ numPages: loadedNumPages
1686
+ }) => {
1687
+ setNumPages(loadedNumPages);
1688
+ onLoadSuccess?.(loadedNumPages);
1689
+ };
1690
+ const handleDocumentLoadError = (error) => {
1691
+ console.error(error);
1692
+ onLoadError?.(error);
1693
+ };
1694
+ const handlePageLoadSuccess = (pageIndex, page) => {
1695
+ if (pageIndex === 1) {
1696
+ setPageOriginalSize({
1697
+ width: Math.trunc(page.width),
1698
+ height: Math.trunc(page.height)
1699
+ });
1700
+ }
1701
+ renderedHeights.current.set(pageIndex, Math.trunc(page.height));
1702
+ };
1703
+ const setPage = useCallback(
1704
+ (newPage) => {
1705
+ setPageNumber(newPage);
1706
+ onPageChange?.(newPage);
1707
+ },
1708
+ [onPageChange]
1709
+ );
1710
+ const previousPage = useCallback(() => {
1711
+ if (pendingInputPageRef.current !== null) return;
1712
+ const p = Math.max(pageNumber - 1, 1);
1713
+ if (viewMode === "continuous") {
1714
+ setTimeout(() => {
1715
+ pageRefs.current.get(p)?.scrollIntoView({ behavior: "smooth", block: "start" });
1716
+ }, 10);
1717
+ } else {
1718
+ setPage(p);
1719
+ }
1720
+ }, [pageNumber, viewMode, setPage]);
1721
+ const nextPage = useCallback(() => {
1722
+ if (pendingInputPageRef.current !== null) return;
1723
+ const p = Math.min(pageNumber + 1, numPages || 1);
1724
+ if (viewMode === "continuous") {
1725
+ setTimeout(() => {
1726
+ pageRefs.current.get(p)?.scrollIntoView({ behavior: "smooth", block: "start" });
1727
+ }, 10);
1728
+ } else {
1729
+ setPage(p);
1730
+ }
1731
+ }, [pageNumber, numPages, viewMode, setPage]);
1732
+ const goToPage = useCallback(
1733
+ (p) => {
1734
+ const validPage = Math.max(1, Math.min(p, numPages || 1));
1735
+ if (viewMode === "continuous") {
1736
+ pendingInputPageRef.current = validPage;
1737
+ setInputPage(String(validPage));
1738
+ if (pendingInputPageTimerRef.current !== null) {
1739
+ window.clearTimeout(pendingInputPageTimerRef.current);
1740
+ }
1741
+ pendingInputPageTimerRef.current = window.setTimeout(() => {
1742
+ pendingInputPageRef.current = null;
1743
+ pendingInputPageTimerRef.current = null;
1744
+ setInputPage(String(pageNumberRef.current));
1745
+ }, 750);
1746
+ setTimeout(() => {
1747
+ pageRefs.current.get(validPage)?.scrollIntoView({ behavior: "smooth", block: "start" });
1748
+ }, 10);
1749
+ } else {
1750
+ setPage(validPage);
1751
+ }
1752
+ },
1753
+ [numPages, viewMode, setPage]
1754
+ );
1755
+ const pageNumberRef = useRef(pageNumber);
1756
+ useEffect(() => {
1757
+ pageNumberRef.current = pageNumber;
1758
+ }, [pageNumber]);
1759
+ const CONTINUOUS_PAGE_GAP_PX = 16;
1760
+ const isPreservingScrollRef = useRef(false);
1761
+ const updateVisiblePagesFromScroll = useCallback(() => {
1762
+ const container = containerRef.current;
1763
+ if (!container || viewMode !== "continuous" || numPages <= 0 || isPreservingScrollRef.current) {
1764
+ return;
1765
+ }
1766
+ const scrollTop = container.scrollTop;
1767
+ const viewBottom = scrollTop + container.clientHeight;
1768
+ const defaultPageHeight = zoomedRenderedDimensions.height ?? 0;
1769
+ let offset = 0;
1770
+ let minVisible = Number.POSITIVE_INFINITY;
1771
+ let maxVisible = 0;
1772
+ for (let page = 1; page <= numPages; page++) {
1773
+ const renderedPageHeight = renderedHeights.current.get(page) ?? defaultPageHeight;
1774
+ const slotHeight = Math.trunc(renderedPageHeight * layoutScale);
1775
+ const pageTop = offset;
1776
+ const pageBottom = offset + slotHeight;
1777
+ if (pageBottom > scrollTop && pageTop < viewBottom) {
1778
+ minVisible = Math.min(minVisible, page);
1779
+ maxVisible = Math.max(maxVisible, page);
1780
+ }
1781
+ offset = pageBottom + CONTINUOUS_PAGE_GAP_PX;
1782
+ }
1783
+ if (minVisible === Number.POSITIVE_INFINITY) return;
1784
+ setVisibleRange(
1785
+ (prev) => prev.start !== minVisible || prev.end !== maxVisible ? { start: minVisible, end: maxVisible } : prev
1786
+ );
1787
+ }, [
1788
+ viewMode,
1789
+ numPages,
1790
+ layoutScale,
1791
+ zoomedRenderedDimensions.height
1792
+ ]);
1793
+ useEffect(() => {
1794
+ if (viewMode !== "continuous" || !numPages || !containerRef.current) return;
1795
+ const container = containerRef.current;
1796
+ const handleScroll = () => {
1797
+ requestAnimationFrame(updateVisiblePagesFromScroll);
1798
+ };
1799
+ container.addEventListener("scroll", handleScroll, { passive: true });
1800
+ updateVisiblePagesFromScroll();
1801
+ return () => container.removeEventListener("scroll", handleScroll);
1802
+ }, [numPages, viewMode, updateVisiblePagesFromScroll]);
1803
+ useEffect(() => {
1804
+ if (instantZoom === renderedZoom) {
1805
+ updateVisiblePagesFromScroll();
1806
+ }
1807
+ }, [instantZoom, renderedZoom, updateVisiblePagesFromScroll]);
1808
+ useEffect(() => {
1809
+ if (viewMode !== "continuous" || !numPages || !containerRef.current) return;
1810
+ const ratios = /* @__PURE__ */ new Map();
1811
+ const observer = new IntersectionObserver(
1812
+ (entries) => {
1813
+ for (const entry of entries) {
1814
+ const pStr = entry.target.getAttribute("data-page-number");
1815
+ if (!pStr) continue;
1816
+ const p = parseInt(pStr, 10);
1817
+ if (!Number.isFinite(p)) continue;
1818
+ ratios.set(p, entry.intersectionRatio);
1819
+ }
1820
+ let bestPage = 0;
1821
+ let bestRatio = 0;
1822
+ let minVisible = Infinity;
1823
+ let maxVisible = -Infinity;
1824
+ ratios.forEach((ratio, page) => {
1825
+ if (ratio > 0) {
1826
+ minVisible = Math.min(minVisible, page);
1827
+ maxVisible = Math.max(maxVisible, page);
1828
+ }
1829
+ if (ratio > bestRatio) {
1830
+ bestRatio = ratio;
1831
+ bestPage = page;
1832
+ }
1833
+ });
1834
+ if (bestPage > 0 && bestRatio > 0 && bestPage !== pageNumberRef.current) {
1835
+ setPage(bestPage);
1836
+ }
1837
+ if (minVisible !== Infinity && maxVisible !== -Infinity) {
1838
+ setVisibleRange((prev) => {
1839
+ if (prev.start !== minVisible || prev.end !== maxVisible) {
1840
+ return { start: minVisible, end: maxVisible };
1841
+ }
1842
+ return prev;
1843
+ });
1844
+ }
1845
+ },
1846
+ {
1847
+ root: containerRef.current,
1848
+ threshold: [0, 0.1, 0.25, 0.5, 0.75, 1]
1849
+ }
1850
+ );
1851
+ const currentRefs = pageRefs.current;
1852
+ currentRefs.forEach((ref) => {
1853
+ if (ref) observer.observe(ref);
1854
+ });
1855
+ return () => observer.disconnect();
1856
+ }, [numPages, viewMode, setPage]);
1857
+ const scrollRatioRef = useRef({ x: 0, y: 0 });
1858
+ const zoomAnimFrameRef = useRef(null);
1859
+ useEffect(() => {
1860
+ return () => {
1861
+ if (zoomAnimFrameRef.current !== null) {
1862
+ cancelAnimationFrame(zoomAnimFrameRef.current);
1863
+ }
1864
+ };
1865
+ }, []);
1866
+ const preserveScrollPosition = useCallback(() => {
1867
+ const container = containerRef.current;
1868
+ if (!container) return;
1869
+ isPreservingScrollRef.current = true;
1870
+ const centerY = container.scrollTop + container.clientHeight / 2;
1871
+ const ratioY = container.scrollHeight > 0 ? centerY / container.scrollHeight : 0;
1872
+ const centerX = container.scrollLeft + container.clientWidth / 2;
1873
+ const ratioX = container.scrollWidth > 0 ? centerX / container.scrollWidth : 0;
1874
+ scrollRatioRef.current = { x: ratioX, y: ratioY };
1875
+ if (zoomAnimFrameRef.current !== null) {
1876
+ cancelAnimationFrame(zoomAnimFrameRef.current);
1877
+ }
1878
+ const startTime = performance.now();
1879
+ const duration = 250;
1880
+ const step = (time) => {
1881
+ const currentContainer = containerRef.current;
1882
+ if (!currentContainer) return;
1883
+ const newCenterY = currentContainer.scrollHeight * scrollRatioRef.current.y;
1884
+ const newCenterX = currentContainer.scrollWidth * scrollRatioRef.current.x;
1885
+ currentContainer.scrollTop = newCenterY - currentContainer.clientHeight / 2;
1886
+ currentContainer.scrollLeft = newCenterX - currentContainer.clientWidth / 2;
1887
+ if (time - startTime < duration) {
1888
+ zoomAnimFrameRef.current = requestAnimationFrame(step);
1889
+ } else {
1890
+ zoomAnimFrameRef.current = null;
1891
+ isPreservingScrollRef.current = false;
1892
+ }
1893
+ };
1894
+ zoomAnimFrameRef.current = requestAnimationFrame(step);
1895
+ }, []);
1896
+ const handleZoomIn = useCallback(() => {
1897
+ setHasZoomed(true);
1898
+ preserveScrollPosition();
1899
+ setInstantZoom((prev) => Math.min(prev * 1.3, 4));
1900
+ }, [preserveScrollPosition]);
1901
+ const handleZoomOut = useCallback(() => {
1902
+ setHasZoomed(true);
1903
+ preserveScrollPosition();
1904
+ setInstantZoom((prev) => Math.max(prev / 1.3, 0.5));
1905
+ }, [preserveScrollPosition]);
1906
+ const handleZoomReset = useCallback(() => {
1907
+ setHasZoomed(true);
1908
+ preserveScrollPosition();
1909
+ setInstantZoom(1);
1910
+ }, [preserveScrollPosition]);
1911
+ const zoomOutDisabled = instantZoom <= 0.501;
1912
+ const zoomInDisabled = instantZoom >= 3.99;
1913
+ const fitDisabled = Math.abs(instantZoom - 1) < 0.01;
1914
+ const renderPaginationElement = () => {
1915
+ if (renderPagination === null || numPages <= 1) return null;
1916
+ const props2 = {
1917
+ pageNumber,
1918
+ numPages,
1919
+ previousPage,
1920
+ nextPage,
1921
+ goToPage,
1922
+ isFirstPage: pageNumber <= 1,
1923
+ isLastPage: pageNumber >= numPages,
1924
+ viewMode
1925
+ };
1926
+ if (renderPagination) {
1927
+ return renderPagination(props2);
1928
+ }
1929
+ const paginationBody = /* @__PURE__ */ jsxs(Fragment, { children: [
1930
+ /* @__PURE__ */ jsx("div", { className: "bg-black/20 rounded px-1 flex items-center justify-center", children: /* @__PURE__ */ jsx(
1931
+ "input",
1932
+ {
1933
+ type: "text",
1934
+ inputMode: "numeric",
1935
+ value: inputPage,
1936
+ onChange: (event) => setInputPage(event.target.value),
1937
+ onKeyDown: (event) => {
1938
+ if (event.key === "Enter") {
1939
+ const p = parseInt(inputPage, 10);
1940
+ if (!isNaN(p)) goToPage(p);
1941
+ } else if (event.key === "ArrowUp") {
1942
+ event.preventDefault();
1943
+ const p = parseInt(inputPage, 10);
1944
+ if (!isNaN(p)) {
1945
+ setInputPage(String(Math.min(p + 1, numPages)));
1946
+ }
1947
+ } else if (event.key === "ArrowDown") {
1948
+ event.preventDefault();
1949
+ const p = parseInt(inputPage, 10);
1950
+ if (!isNaN(p)) {
1951
+ setInputPage(String(Math.max(p - 1, 1)));
1952
+ }
1953
+ }
1954
+ },
1955
+ onBlur: () => {
1956
+ const p = parseInt(inputPage, 10);
1957
+ if (!isNaN(p)) goToPage(p);
1958
+ else setInputPage(String(pageNumber));
1959
+ },
1960
+ className: pdfClassName("pageInput", PDF_PAGE_INPUT_DEFAULT),
1961
+ style: pdfStyle("pageInput"),
1962
+ "aria-label": resolveFormattedMessage(pdfT.pageInputAriaLabel, {
1963
+ value: inputPage
1964
+ })
1965
+ }
1966
+ ) }),
1967
+ /* @__PURE__ */ jsx("span", { className: "opacity-60 mx-1", children: "/" }),
1968
+ /* @__PURE__ */ jsx("span", { children: numPages })
1969
+ ] });
1970
+ return /* @__PURE__ */ jsxs(
1971
+ ViewerFloatingToolbar,
1972
+ {
1973
+ ref: toolbarRef,
1974
+ className: mergeClassNames(
1975
+ pdfClassName("pagination", ""),
1976
+ isToolbarVisible ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"
1977
+ ),
1978
+ style: pdfStyle("pagination"),
1979
+ children: [
1980
+ /* @__PURE__ */ jsx(
1981
+ FileViewerTooltip,
1982
+ {
1983
+ content: pdfT.previousPageTooltip,
1984
+ disabled: props2.isFirstPage,
1985
+ children: /* @__PURE__ */ jsx(
1986
+ ViewerToolbarIconButton,
1987
+ {
1988
+ disabled: props2.isFirstPage,
1989
+ onClick: previousPage,
1990
+ "aria-label": pdfT.previousPageAriaLabel,
1991
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5", "aria-hidden": true })
1992
+ }
1993
+ )
1994
+ }
1995
+ ),
1996
+ paginationBody,
1997
+ /* @__PURE__ */ jsx(
1998
+ FileViewerTooltip,
1999
+ {
2000
+ content: pdfT.nextPageTooltip,
2001
+ disabled: props2.isLastPage,
2002
+ children: /* @__PURE__ */ jsx(
2003
+ ViewerToolbarIconButton,
2004
+ {
2005
+ disabled: props2.isLastPage,
2006
+ onClick: nextPage,
2007
+ "aria-label": pdfT.nextPageAriaLabel,
2008
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5", "aria-hidden": true })
2009
+ }
2010
+ )
2011
+ }
2012
+ ),
2013
+ /* @__PURE__ */ jsx(ViewerToolbarDivider, {}),
2014
+ /* @__PURE__ */ jsx(
2015
+ FileViewerTooltip,
2016
+ {
2017
+ content: pdfT.zoomOutTooltip,
2018
+ disabled: zoomOutDisabled,
2019
+ children: /* @__PURE__ */ jsx(
2020
+ ViewerToolbarIconButton,
2021
+ {
2022
+ disabled: zoomOutDisabled,
2023
+ onClick: handleZoomOut,
2024
+ "aria-label": pdfT.zoomOutAriaLabel,
2025
+ children: /* @__PURE__ */ jsx(ZoomOut, { className: "h-5 w-5" })
2026
+ }
2027
+ )
2028
+ }
2029
+ ),
2030
+ /* @__PURE__ */ jsx(
2031
+ FileViewerTooltip,
2032
+ {
2033
+ content: pdfT.fitWidthTooltip,
2034
+ disabled: fitDisabled,
2035
+ children: /* @__PURE__ */ jsx(
2036
+ ViewerToolbarIconButton,
2037
+ {
2038
+ disabled: fitDisabled,
2039
+ onClick: handleZoomReset,
2040
+ "aria-label": pdfT.fitWidthAriaLabel,
2041
+ children: /* @__PURE__ */ jsx(Scan, { className: "h-5 w-5" })
2042
+ }
2043
+ )
2044
+ }
2045
+ ),
2046
+ /* @__PURE__ */ jsx(
2047
+ FileViewerTooltip,
2048
+ {
2049
+ content: pdfT.zoomInTooltip,
2050
+ disabled: zoomInDisabled,
2051
+ children: /* @__PURE__ */ jsx(
2052
+ ViewerToolbarIconButton,
2053
+ {
2054
+ disabled: zoomInDisabled,
2055
+ onClick: handleZoomIn,
2056
+ "aria-label": pdfT.zoomInAriaLabel,
2057
+ children: /* @__PURE__ */ jsx(ZoomIn, { className: "h-5 w-5" })
2058
+ }
2059
+ )
2060
+ }
2061
+ )
2062
+ ]
2063
+ }
2064
+ );
2065
+ };
2066
+ const pageTransitionClass = hasZoomed && pageOriginalSize.width ? "transition-[width,height] duration-200 ease-out" : "";
2067
+ return /* @__PURE__ */ jsx(FileViewerTooltipProvider, { children: /* @__PURE__ */ jsxs(
2068
+ "div",
2069
+ {
2070
+ className: pdfClassName("root", PDF_VIEWER_ROOT_DEFAULT),
2071
+ style: pdfStyle("root"),
2072
+ children: [
2073
+ /* @__PURE__ */ jsxs(
2074
+ ScrollAreaRoot,
2075
+ {
2076
+ className: pdfClassName("scrollArea", PDF_SCROLL_AREA_DEFAULT),
2077
+ style: pdfStyle("scrollArea"),
2078
+ children: [
2079
+ /* @__PURE__ */ jsx(
2080
+ ScrollAreaViewport,
2081
+ {
2082
+ ref: containerRef,
2083
+ className: pdfClassName(
2084
+ "scrollViewport",
2085
+ `${PDF_SCROLL_VIEWPORT_DEFAULT} pdf-scroll-viewport`
2086
+ ),
2087
+ style: pdfStyle("scrollViewport"),
2088
+ children: /* @__PURE__ */ jsx("div", { className: "flex min-h-full min-w-full flex-col", children: /* @__PURE__ */ jsxs(
2089
+ Document,
2090
+ {
2091
+ file: url,
2092
+ onLoadSuccess: handleDocumentLoadSuccess,
2093
+ onLoadError: handleDocumentLoadError,
2094
+ loading: renderLoading,
2095
+ className: "flex w-full flex-col gap-4",
2096
+ children: [
2097
+ viewMode === "single" && zoomedRenderedDimensions.width && zoomedInstantDimensions.width && zoomedInstantDimensions.height ? /* @__PURE__ */ jsx(
2098
+ "div",
2099
+ {
2100
+ "data-layout-syncing": isLayoutSyncing ? "true" : void 0,
2101
+ className: pdfClassName(
2102
+ "page",
2103
+ PDF_PAGE_DEFAULT,
2104
+ pageTransitionClass
2105
+ ),
2106
+ style: {
2107
+ width: zoomedInstantDimensions.width,
2108
+ height: zoomedInstantDimensions.height,
2109
+ ...pdfStyle("page")
2110
+ },
2111
+ children: /* @__PURE__ */ jsx(
2112
+ "div",
2113
+ {
2114
+ className: pdfClassName("pageInner", PDF_PAGE_INNER_DEFAULT),
2115
+ style: pdfStyle("pageInner"),
2116
+ children: /* @__PURE__ */ jsx(
2117
+ Page,
2118
+ {
2119
+ pageNumber,
2120
+ onLoadSuccess: (page) => handlePageLoadSuccess(pageNumber, page),
2121
+ renderTextLayer,
2122
+ renderAnnotationLayer,
2123
+ className: "flex h-full w-full shrink flex-1 items-center justify-center !bg-transparent [&_canvas]:!h-full [&_canvas]:!w-full",
2124
+ renderMode: "canvas",
2125
+ width: zoomedRenderedDimensions.width,
2126
+ loading: null
2127
+ }
2128
+ )
2129
+ }
2130
+ )
2131
+ }
2132
+ ) : null,
2133
+ viewMode === "continuous" && numPages > 0 && zoomedRenderedDimensions.width && zoomedRenderedDimensions.height && zoomedInstantDimensions.width && Array.from(new Array(numPages), (_, index) => {
2134
+ const p = index + 1;
2135
+ const isInWindow = p >= visibleRange.start - preloadAhead && p <= visibleRange.end + preloadAhead;
2136
+ const renderedPageHeight = renderedHeights.current.get(p) ?? zoomedRenderedDimensions.height ?? 0;
2137
+ const slotWidth = zoomedInstantDimensions.width ? Math.trunc(zoomedInstantDimensions.width) : void 0;
2138
+ const slotHeight = Math.trunc(renderedPageHeight * layoutScale);
2139
+ const placeholderStyle = !isInWindow && slotHeight ? {
2140
+ width: slotWidth,
2141
+ height: slotHeight,
2142
+ backgroundColor: "#80808040"
2143
+ } : {
2144
+ width: slotWidth,
2145
+ height: slotHeight
2146
+ };
2147
+ return /* @__PURE__ */ jsx(
2148
+ "div",
2149
+ {
2150
+ "data-layout-syncing": isLayoutSyncing ? "true" : void 0,
2151
+ "data-page-number": p,
2152
+ ref: (el) => {
2153
+ if (el) pageRefs.current.set(p, el);
2154
+ else pageRefs.current.delete(p);
2155
+ },
2156
+ className: pdfClassName(
2157
+ "page",
2158
+ PDF_PAGE_DEFAULT,
2159
+ pageTransitionClass
2160
+ ),
2161
+ style: { ...placeholderStyle, ...pdfStyle("page") },
2162
+ children: isInWindow ? /* @__PURE__ */ jsx(
2163
+ "div",
2164
+ {
2165
+ className: pdfClassName(
2166
+ "pageInner",
2167
+ PDF_PAGE_INNER_DEFAULT
2168
+ ),
2169
+ style: pdfStyle("pageInner"),
2170
+ children: /* @__PURE__ */ jsx(
2171
+ Page,
2172
+ {
2173
+ pageNumber: p,
2174
+ onLoadSuccess: (page) => handlePageLoadSuccess(p, page),
2175
+ renderTextLayer,
2176
+ renderAnnotationLayer,
2177
+ className: "flex h-full w-full shrink flex-1 items-center justify-center !bg-transparent [&_canvas]:!h-full [&_canvas]:!w-full",
2178
+ renderMode: "canvas",
2179
+ width: zoomedRenderedDimensions.width,
2180
+ loading: null
2181
+ }
2182
+ )
2183
+ }
2184
+ ) : null
2185
+ },
2186
+ `page_${p}`
2187
+ );
2188
+ })
2189
+ ]
2190
+ }
2191
+ ) })
2192
+ }
2193
+ ),
2194
+ /* @__PURE__ */ jsx(
2195
+ ScrollAreaScrollbar,
2196
+ {
2197
+ orientation: "vertical",
2198
+ className: pdfClassName("scrollbar", PDF_SCROLLBAR_VERTICAL_DEFAULT),
2199
+ style: pdfStyle("scrollbar"),
2200
+ children: /* @__PURE__ */ jsx(
2201
+ ScrollAreaThumb,
2202
+ {
2203
+ className: pdfClassName("scrollbarThumb", PDF_SCROLLBAR_THUMB_DEFAULT),
2204
+ style: pdfStyle("scrollbarThumb")
2205
+ }
2206
+ )
2207
+ }
2208
+ ),
2209
+ /* @__PURE__ */ jsx(
2210
+ ScrollAreaScrollbar,
2211
+ {
2212
+ orientation: "horizontal",
2213
+ className: pdfClassName(
2214
+ "scrollbar",
2215
+ PDF_SCROLLBAR_HORIZONTAL_DEFAULT
2216
+ ),
2217
+ style: pdfStyle("scrollbar"),
2218
+ children: /* @__PURE__ */ jsx(
2219
+ ScrollAreaThumb,
2220
+ {
2221
+ className: pdfClassName("scrollbarThumb", PDF_SCROLLBAR_THUMB_DEFAULT),
2222
+ style: pdfStyle("scrollbarThumb")
2223
+ }
2224
+ )
2225
+ }
2226
+ ),
2227
+ /* @__PURE__ */ jsx(ScrollAreaCorner, { className: "bg-transparent" })
2228
+ ]
2229
+ }
2230
+ ),
2231
+ renderPaginationElement()
2232
+ ]
2233
+ }
2234
+ ) });
2235
+ }
2236
+ var PDF_VIEWER_ROOT_DEFAULT, PDF_SCROLL_AREA_DEFAULT, PDF_SCROLL_VIEWPORT_DEFAULT, PDF_SCROLLBAR_VERTICAL_DEFAULT, PDF_SCROLLBAR_HORIZONTAL_DEFAULT, PDF_SCROLLBAR_THUMB_DEFAULT, PDF_PAGE_DEFAULT, PDF_PAGE_INNER_DEFAULT, PDF_PAGE_INPUT_DEFAULT;
2237
+ var init_PdfViewer = __esm({
2238
+ "src/features/file-viewer/PdfViewer.tsx"() {
2239
+ "use client";
2240
+ init_icons();
2241
+ init_scroll_area();
2242
+ init_pdf_viewer();
2243
+ init_FileViewerTooltip();
2244
+ init_ViewerToolbar();
2245
+ init_useAutoHide();
2246
+ init_config();
2247
+ init_translations();
2248
+ init_resolve_options();
2249
+ init_merge_slot_props();
2250
+ PDF_VIEWER_ROOT_DEFAULT = "pdf-viewer flex flex-col items-center gap-4 w-full h-full max-h-full overflow-hidden relative";
2251
+ PDF_SCROLL_AREA_DEFAULT = "size-full min-h-0 min-w-0";
2252
+ PDF_SCROLL_VIEWPORT_DEFAULT = "w-full h-full rounded-[inherit] [&>div]:min-h-full";
2253
+ PDF_SCROLLBAR_VERTICAL_DEFAULT = "flex flex-col touch-none select-none transition-colors w-4 border-l border-l-transparent p-0.5 hover:bg-black/10 z-20";
2254
+ PDF_SCROLLBAR_HORIZONTAL_DEFAULT = "flex flex-col touch-none select-none transition-colors h-4 border-t border-t-transparent p-0.5 hover:bg-black/10 z-20";
2255
+ PDF_SCROLLBAR_THUMB_DEFAULT = "relative shrink-0 rounded-full bg-neutral-500/50 hover:bg-neutral-500/80";
2256
+ PDF_PAGE_DEFAULT = "relative flex shrink-0 items-center justify-center overflow-hidden m-auto";
2257
+ PDF_PAGE_INNER_DEFAULT = "relative flex h-full w-full items-center justify-center overflow-hidden bg-white shadow-lg";
2258
+ PDF_PAGE_INPUT_DEFAULT = "w-10 bg-transparent text-center focus:outline-none transition-colors";
2259
+ }
2260
+ });
2261
+
2262
+ // src/features/file-viewer/utils/zoom-utils.ts
2263
+ function calculateMultiplicativeZoom({
2264
+ scale,
2265
+ positionX,
2266
+ positionY,
2267
+ deltaY,
2268
+ clientX,
2269
+ clientY,
2270
+ wrapperComponent,
2271
+ minScale = 0.2,
2272
+ maxScale = 8,
2273
+ zoomFactor = 0.15
2274
+ }) {
2275
+ const isZoomIn = deltaY < 0;
2276
+ const factor = isZoomIn ? 1 + zoomFactor : 1 / (1 + zoomFactor);
2277
+ const newScale = Math.max(minScale, Math.min(scale * factor, maxScale));
2278
+ if (newScale === scale || !wrapperComponent) {
2279
+ return { newX: positionX, newY: positionY, newScale: scale };
2280
+ }
2281
+ const rect = wrapperComponent.getBoundingClientRect();
2282
+ const mx = clientX - rect.left;
2283
+ const my = clientY - rect.top;
2284
+ const ratio = newScale / scale;
2285
+ const newX = mx - (mx - positionX) * ratio;
2286
+ const newY = my - (my - positionY) * ratio;
2287
+ return { newX, newY, newScale };
2288
+ }
2289
+ var init_zoom_utils = __esm({
2290
+ "src/features/file-viewer/utils/zoom-utils.ts"() {
2291
+ }
2292
+ });
2293
+
2294
+ // src/features/file-viewer/utils/image-viewport-clamp.ts
2295
+ function roundNumber(num, decimal) {
2296
+ return Number(num.toFixed(decimal));
2297
+ }
2298
+ function getComponentsSizes(wrapperComponent, contentComponent, newScale) {
2299
+ const wrapperWidth = wrapperComponent.offsetWidth;
2300
+ const wrapperHeight = wrapperComponent.offsetHeight;
2301
+ const contentWidth = contentComponent.offsetWidth;
2302
+ const contentHeight = contentComponent.offsetHeight;
2303
+ const newContentWidth = contentWidth * newScale;
2304
+ const newContentHeight = contentHeight * newScale;
2305
+ const newDiffWidth = wrapperWidth - newContentWidth;
2306
+ const newDiffHeight = wrapperHeight - newContentHeight;
2307
+ return {
2308
+ wrapperWidth,
2309
+ wrapperHeight,
2310
+ newContentWidth,
2311
+ newDiffWidth,
2312
+ newContentHeight,
2313
+ newDiffHeight
2314
+ };
2315
+ }
2316
+ function getBoundsFromSizes(wrapperWidth, newContentWidth, diffWidth, wrapperHeight, newContentHeight, diffHeight, centerZoomedOut) {
2317
+ const scaleWidthFactor = wrapperWidth > newContentWidth ? diffWidth * (centerZoomedOut ? 0.5 : 1) : 0;
2318
+ const scaleHeightFactor = wrapperHeight > newContentHeight ? diffHeight * (centerZoomedOut ? 0.5 : 1) : 0;
2319
+ const minPositionX = wrapperWidth - newContentWidth - scaleWidthFactor;
2320
+ const maxPositionX = scaleWidthFactor;
2321
+ const minPositionY = wrapperHeight - newContentHeight - scaleHeightFactor;
2322
+ const maxPositionY = scaleHeightFactor;
2323
+ return {
2324
+ minPositionX,
2325
+ maxPositionX,
2326
+ minPositionY,
2327
+ maxPositionY
2328
+ };
2329
+ }
2330
+ function boundLimiter(value, minBound, maxBound, isActive) {
2331
+ if (!isActive) return roundNumber(value, 2);
2332
+ if (value < minBound) return roundNumber(minBound, 2);
2333
+ if (value > maxBound) return roundNumber(maxBound, 2);
2334
+ return roundNumber(value, 2);
2335
+ }
2336
+ function getMouseBoundedPosition(positionX, positionY, bounds, limitToBounds, paddingValueX, paddingValueY, wrapperComponent) {
2337
+ const { minPositionX, minPositionY, maxPositionX, maxPositionY } = bounds;
2338
+ const paddingX = wrapperComponent ? paddingValueX : 0;
2339
+ const paddingY = wrapperComponent ? paddingValueY : 0;
2340
+ const x = boundLimiter(
2341
+ positionX,
2342
+ minPositionX - paddingX,
2343
+ maxPositionX + paddingX,
2344
+ limitToBounds
2345
+ );
2346
+ const y = boundLimiter(
2347
+ positionY,
2348
+ minPositionY - paddingY,
2349
+ maxPositionY + paddingY,
2350
+ limitToBounds
2351
+ );
2352
+ return { x, y };
2353
+ }
2354
+ function clampPanToImageBounds(wrapperComponent, contentComponent, scale, positionX, positionY, setup) {
2355
+ const {
2356
+ wrapperWidth,
2357
+ wrapperHeight,
2358
+ newContentWidth,
2359
+ newContentHeight,
2360
+ newDiffWidth,
2361
+ newDiffHeight
2362
+ } = getComponentsSizes(wrapperComponent, contentComponent, scale);
2363
+ const bounds = getBoundsFromSizes(
2364
+ wrapperWidth,
2365
+ newContentWidth,
2366
+ newDiffWidth,
2367
+ wrapperHeight,
2368
+ newContentHeight,
2369
+ newDiffHeight,
2370
+ Boolean(setup.centerZoomedOut)
2371
+ );
2372
+ const contentFitsCompletely = wrapperWidth >= newContentWidth && wrapperHeight >= newContentHeight;
2373
+ if (setup.disablePadding && contentFitsCompletely && !setup.centerZoomedOut) {
2374
+ bounds.minPositionX = 0;
2375
+ bounds.maxPositionX = 0;
2376
+ bounds.minPositionY = 0;
2377
+ bounds.maxPositionY = 0;
2378
+ }
2379
+ const {
2380
+ minPositionX: propMinX,
2381
+ maxPositionX: propMaxX,
2382
+ minPositionY: propMinY,
2383
+ maxPositionY: propMaxY
2384
+ } = setup;
2385
+ if (propMinX != null) {
2386
+ bounds.minPositionX = wrapperWidth * (1 - scale) + propMinX * scale;
2387
+ }
2388
+ if (propMaxX != null) {
2389
+ bounds.maxPositionX = propMaxX * scale;
2390
+ }
2391
+ if (propMinY != null) {
2392
+ bounds.minPositionY = wrapperHeight * (1 - scale) + propMinY * scale;
2393
+ }
2394
+ if (propMaxY != null) {
2395
+ bounds.maxPositionY = propMaxY * scale;
2396
+ }
2397
+ const { x, y } = getMouseBoundedPosition(
2398
+ positionX,
2399
+ positionY,
2400
+ bounds,
2401
+ setup.limitToBounds,
2402
+ 0,
2403
+ 0,
2404
+ wrapperComponent
2405
+ );
2406
+ return { positionX: x, positionY: y };
2407
+ }
2408
+ var init_image_viewport_clamp = __esm({
2409
+ "src/features/file-viewer/utils/image-viewport-clamp.ts"() {
2410
+ }
2411
+ });
2412
+
2413
+ // src/features/file-viewer/ImageViewer.tsx
2414
+ var ImageViewer_exports = {};
2415
+ __export(ImageViewer_exports, {
2416
+ default: () => ImageViewer
2417
+ });
2418
+ function ImageViewer({
2419
+ url,
2420
+ name,
2421
+ language: languageProp,
2422
+ classNames: classNamesProp,
2423
+ styles: stylesProp
2424
+ }) {
2425
+ const resolved = resolveImageViewerProps({
2426
+ classNames: classNamesProp,
2427
+ styles: stylesProp
2428
+ });
2429
+ const classNames = resolved.classNames;
2430
+ const styles = resolved.styles;
2431
+ const language = resolveOption(
2432
+ languageProp,
2433
+ getFileViewerDefaults().language,
2434
+ "english"
2435
+ );
2436
+ const imageT = getFileViewerTranslations(language).imageViewer;
2437
+ const autoHideDefaults = getFileViewerDefaults().autoHide;
2438
+ const imageClassName = (key, builtIn, extra) => mergeClassNames(builtIn, classNames?.[key], extra);
2439
+ const imageStyle = (key) => styles?.[key];
2440
+ const [maxZoom, setMaxZoom] = useState(16);
2441
+ const [isPanning, setIsPanning] = useState(false);
2442
+ const [hasImageLoaded, setHasImageLoaded] = useState(false);
2443
+ const [viewport, setViewport] = useState({
2444
+ scale: 1,
2445
+ positionX: 0,
2446
+ positionY: 0
2447
+ });
2448
+ const minZoom = 0.5;
2449
+ const scaleRef = useRef(null);
2450
+ const transformRef = useRef(null);
2451
+ const containerRef = useRef(null);
2452
+ const toolbarRef = useRef(null);
2453
+ const wheelClampTimerRef = useRef(null);
2454
+ const libraryChangeUnsubscribeRef = useRef(null);
2455
+ const isToolbarVisible = useAutoHide(toolbarRef, containerRef, {
2456
+ proximityThreshold: autoHideDefaults?.proximityThreshold,
2457
+ timeout: autoHideDefaults?.timeout,
2458
+ activityDeps: [viewport.scale]
2459
+ });
2460
+ const updateScaleText = useCallback((scale) => {
2461
+ if (scaleRef.current) {
2462
+ scaleRef.current.innerText = `${Math.round(scale * 100)}%`;
2463
+ }
2464
+ }, []);
2465
+ const syncViewportFromLibrary = useCallback(
2466
+ (libraryRef) => {
2467
+ const { scale, positionX, positionY } = libraryRef.instance.state;
2468
+ setViewport({ scale, positionX, positionY });
2469
+ updateScaleText(scale);
2470
+ },
2471
+ [updateScaleText]
2472
+ );
2473
+ const handleLibraryInit = useCallback(
2474
+ (libraryRef) => {
2475
+ libraryChangeUnsubscribeRef.current?.();
2476
+ libraryChangeUnsubscribeRef.current = null;
2477
+ syncViewportFromLibrary(libraryRef);
2478
+ libraryChangeUnsubscribeRef.current = libraryRef.instance.onChange(
2479
+ (nextRef) => {
2480
+ syncViewportFromLibrary(nextRef);
2481
+ }
2482
+ );
2483
+ },
2484
+ [syncViewportFromLibrary]
2485
+ );
2486
+ const scheduleWheelViewportClamp = useCallback(() => {
2487
+ if (wheelClampTimerRef.current !== null) {
2488
+ clearTimeout(wheelClampTimerRef.current);
2489
+ }
2490
+ wheelClampTimerRef.current = window.setTimeout(() => {
2491
+ wheelClampTimerRef.current = null;
2492
+ const ref = transformRef.current;
2493
+ if (!ref) return;
2494
+ const { instance } = ref;
2495
+ const { scale, positionX, positionY } = instance.state;
2496
+ const wrapper = instance.wrapperComponent;
2497
+ const content = instance.contentComponent;
2498
+ if (!wrapper || !content) return;
2499
+ const setup = instance.setup;
2500
+ const clamped = clampPanToImageBounds(
2501
+ wrapper,
2502
+ content,
2503
+ scale,
2504
+ positionX,
2505
+ positionY,
2506
+ {
2507
+ centerZoomedOut: Boolean(setup.centerZoomedOut),
2508
+ disablePadding: Boolean(setup.disablePadding),
2509
+ minPositionX: setup.minPositionX ?? null,
2510
+ maxPositionX: setup.maxPositionX ?? null,
2511
+ minPositionY: setup.minPositionY ?? null,
2512
+ maxPositionY: setup.maxPositionY ?? null,
2513
+ limitToBounds: Boolean(setup.limitToBounds)
2514
+ }
2515
+ );
2516
+ if (Math.abs(clamped.positionX - positionX) > 0.01 || Math.abs(clamped.positionY - positionY) > 0.01) {
2517
+ ref.setTransform(
2518
+ clamped.positionX,
2519
+ clamped.positionY,
2520
+ scale,
2521
+ WHEEL_CLAMP_ANIMATION_MS,
2522
+ "easeOut"
2523
+ );
2524
+ }
2525
+ }, WHEEL_VIEWPORT_CLAMP_DEBOUNCE_MS);
2526
+ }, []);
2527
+ useEffect(() => {
2528
+ return () => {
2529
+ if (wheelClampTimerRef.current !== null) {
2530
+ clearTimeout(wheelClampTimerRef.current);
2531
+ }
2532
+ libraryChangeUnsubscribeRef.current?.();
2533
+ libraryChangeUnsubscribeRef.current = null;
2534
+ };
2535
+ }, []);
2536
+ useEffect(() => {
2537
+ setHasImageLoaded(false);
2538
+ }, [url]);
2539
+ const handleImageLoad = (event) => {
2540
+ const img = event.currentTarget;
2541
+ const naturalWidth = img.naturalWidth;
2542
+ const currentWidth = img.clientWidth;
2543
+ if (naturalWidth && currentWidth) {
2544
+ const nativeScale = naturalWidth / currentWidth;
2545
+ const calculatedMax = Math.max(4, Math.min(16, nativeScale * 2));
2546
+ setMaxZoom(calculatedMax);
2547
+ }
2548
+ setHasImageLoaded(true);
2549
+ };
2550
+ const handleImageError = () => {
2551
+ setHasImageLoaded(true);
2552
+ };
2553
+ const applyWheelZoom = useCallback(
2554
+ (event) => {
2555
+ if (!hasImageLoaded) return;
2556
+ const ref = transformRef.current;
2557
+ if (!ref) return;
2558
+ event.preventDefault();
2559
+ event.stopPropagation();
2560
+ const { scale, positionX, positionY } = ref.state;
2561
+ const { newX, newY, newScale } = calculateMultiplicativeZoom({
2562
+ scale,
2563
+ positionX,
2564
+ positionY,
2565
+ deltaY: event.deltaY,
2566
+ clientX: event.clientX,
2567
+ clientY: event.clientY,
2568
+ wrapperComponent: ref.instance.wrapperComponent,
2569
+ minScale: minZoom,
2570
+ maxScale: maxZoom
2571
+ });
2572
+ if (newScale !== scale) {
2573
+ ref.setTransform(newX, newY, newScale, 0);
2574
+ scheduleWheelViewportClamp();
2575
+ }
2576
+ },
2577
+ [hasImageLoaded, maxZoom, scheduleWheelViewportClamp]
2578
+ );
2579
+ useEffect(() => {
2580
+ const container = containerRef.current;
2581
+ if (!container) return;
2582
+ const handleWheel = (event) => {
2583
+ if (!container.contains(event.target)) return;
2584
+ applyWheelZoom(event);
2585
+ };
2586
+ container.addEventListener("wheel", handleWheel, { passive: false });
2587
+ return () => container.removeEventListener("wheel", handleWheel);
2588
+ }, [applyWheelZoom, hasImageLoaded]);
2589
+ const zoomOutDisabled = viewport.scale <= minZoom + SCALE_EPSILON;
2590
+ const zoomInDisabled = viewport.scale >= maxZoom - SCALE_EPSILON;
2591
+ const nearDefaultPan = Math.abs(viewport.positionX) < 12 && Math.abs(viewport.positionY) < 12;
2592
+ const fitDisabled = Math.abs(viewport.scale - 1) < FIT_AT_ONE_EPSILON && nearDefaultPan;
2593
+ const wrapperCursorClass = isPanning ? "cursor-move" : "";
2594
+ return /* @__PURE__ */ jsx(FileViewerTooltipProvider, { children: /* @__PURE__ */ jsxs(
2595
+ "div",
2596
+ {
2597
+ ref: containerRef,
2598
+ className: imageClassName("root", IMAGE_ROOT_DEFAULT),
2599
+ style: imageStyle("root"),
2600
+ children: [
2601
+ !hasImageLoaded ? /* @__PURE__ */ jsx(
2602
+ "div",
2603
+ {
2604
+ className: imageClassName("loader", IMAGE_LOADER_OVERLAY_DEFAULT),
2605
+ style: imageStyle("loader"),
2606
+ "aria-busy": true,
2607
+ "aria-live": "polite",
2608
+ children: /* @__PURE__ */ jsx(LoaderCircle, { className: IMAGE_LOADER_ICON_DEFAULT, "aria-hidden": true })
2609
+ }
2610
+ ) : null,
2611
+ /* @__PURE__ */ jsx(
2612
+ TransformWrapper,
2613
+ {
2614
+ ref: transformRef,
2615
+ initialScale: 1,
2616
+ minScale: minZoom,
2617
+ maxScale: maxZoom,
2618
+ centerOnInit: true,
2619
+ limitToBounds: true,
2620
+ wheel: { disabled: true },
2621
+ onInit: handleLibraryInit,
2622
+ onPanningStart: () => setIsPanning(true),
2623
+ onPanningStop: () => setIsPanning(false),
2624
+ children: ({ zoomIn, zoomOut, resetTransform }) => /* @__PURE__ */ jsxs(Fragment, { children: [
2625
+ /* @__PURE__ */ jsx(
2626
+ TransformComponent,
2627
+ {
2628
+ wrapperClass: `!size-full ${wrapperCursorClass}`,
2629
+ contentClass: "!size-full flex items-center justify-center",
2630
+ children: /* @__PURE__ */ jsx(
2631
+ "img",
2632
+ {
2633
+ src: url,
2634
+ alt: name,
2635
+ onLoad: handleImageLoad,
2636
+ onError: handleImageError,
2637
+ className: imageClassName("image", IMAGE_IMG_DEFAULT),
2638
+ style: imageStyle("image")
2639
+ }
2640
+ )
2641
+ }
2642
+ ),
2643
+ /* @__PURE__ */ jsxs(
2644
+ ViewerFloatingToolbar,
2645
+ {
2646
+ ref: toolbarRef,
2647
+ className: mergeClassNames(
2648
+ imageClassName("toolbar", ""),
2649
+ isToolbarVisible ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"
2650
+ ),
2651
+ style: imageStyle("toolbar"),
2652
+ children: [
2653
+ /* @__PURE__ */ jsx(
2654
+ "span",
2655
+ {
2656
+ ref: scaleRef,
2657
+ className: "w-12 text-center text-sm font-medium text-white/90",
2658
+ children: "100%"
2659
+ }
2660
+ ),
2661
+ /* @__PURE__ */ jsx(ViewerToolbarDivider, {}),
2662
+ /* @__PURE__ */ jsx(
2663
+ FileViewerTooltip,
2664
+ {
2665
+ content: imageT.zoomOutTooltip,
2666
+ disabled: zoomOutDisabled,
2667
+ children: /* @__PURE__ */ jsx(
2668
+ ViewerToolbarIconButton,
2669
+ {
2670
+ disabled: zoomOutDisabled,
2671
+ onClick: () => zoomOut(),
2672
+ "aria-label": imageT.zoomOutAriaLabel,
2673
+ children: /* @__PURE__ */ jsx(ZoomOut, { className: "size-5" })
2674
+ }
2675
+ )
2676
+ }
2677
+ ),
2678
+ /* @__PURE__ */ jsx(
2679
+ FileViewerTooltip,
2680
+ {
2681
+ content: imageT.fitScreenTooltip,
2682
+ disabled: fitDisabled,
2683
+ children: /* @__PURE__ */ jsx(
2684
+ ViewerToolbarIconButton,
2685
+ {
2686
+ disabled: fitDisabled,
2687
+ onClick: () => {
2688
+ resetTransform();
2689
+ updateScaleText(1);
2690
+ },
2691
+ "aria-label": imageT.fitScreenAriaLabel,
2692
+ children: /* @__PURE__ */ jsx(Scan, { className: "size-5" })
2693
+ }
2694
+ )
2695
+ }
2696
+ ),
2697
+ /* @__PURE__ */ jsx(
2698
+ FileViewerTooltip,
2699
+ {
2700
+ content: imageT.zoomInTooltip,
2701
+ disabled: zoomInDisabled,
2702
+ children: /* @__PURE__ */ jsx(
2703
+ ViewerToolbarIconButton,
2704
+ {
2705
+ disabled: zoomInDisabled,
2706
+ onClick: () => zoomIn(),
2707
+ "aria-label": imageT.zoomInAriaLabel,
2708
+ children: /* @__PURE__ */ jsx(ZoomIn, { className: "size-5" })
2709
+ }
2710
+ )
2711
+ }
2712
+ )
2713
+ ]
2714
+ }
2715
+ )
2716
+ ] })
2717
+ },
2718
+ url
2719
+ )
2720
+ ]
2721
+ }
2722
+ ) });
2723
+ }
2724
+ var IMAGE_ROOT_DEFAULT, IMAGE_LOADER_OVERLAY_DEFAULT, IMAGE_LOADER_ICON_DEFAULT, IMAGE_IMG_DEFAULT, SCALE_EPSILON, FIT_AT_ONE_EPSILON, WHEEL_VIEWPORT_CLAMP_DEBOUNCE_MS, WHEEL_CLAMP_ANIMATION_MS;
2725
+ var init_ImageViewer = __esm({
2726
+ "src/features/file-viewer/ImageViewer.tsx"() {
2727
+ "use client";
2728
+ init_icons();
2729
+ init_ViewerToolbar();
2730
+ init_config();
2731
+ init_useAutoHide();
2732
+ init_FileViewerTooltip();
2733
+ init_zoom_utils();
2734
+ init_image_viewport_clamp();
2735
+ init_translations();
2736
+ init_merge_slot_props();
2737
+ init_resolve_options();
2738
+ IMAGE_ROOT_DEFAULT = "relative flex size-full items-center justify-center overflow-hidden";
2739
+ IMAGE_LOADER_OVERLAY_DEFAULT = "absolute inset-0 z-30 flex items-center justify-center bg-neutral-950/40";
2740
+ IMAGE_LOADER_ICON_DEFAULT = "size-10 animate-spin text-white";
2741
+ IMAGE_IMG_DEFAULT = "max-h-[calc(100dvh-10rem)] max-w-full shadow";
2742
+ SCALE_EPSILON = 0.012;
2743
+ FIT_AT_ONE_EPSILON = 0.018;
2744
+ WHEEL_VIEWPORT_CLAMP_DEBOUNCE_MS = 500;
2745
+ WHEEL_CLAMP_ANIMATION_MS = 320;
2746
+ }
2747
+ });
2748
+ var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
2749
+ function useFocusTrap(containerRef, active) {
2750
+ useEffect(() => {
2751
+ if (!active) return;
2752
+ const container = containerRef.current;
2753
+ if (!container) return;
2754
+ const focusable = container.querySelectorAll(FOCUSABLE_SELECTOR);
2755
+ const first = focusable[0];
2756
+ const last = focusable[focusable.length - 1];
2757
+ first?.focus();
2758
+ const handleKeyDown = (event) => {
2759
+ if (event.key !== "Tab" || focusable.length === 0) return;
2760
+ if (event.shiftKey) {
2761
+ if (document.activeElement === first) {
2762
+ event.preventDefault();
2763
+ last?.focus();
2764
+ }
2765
+ return;
2766
+ }
2767
+ if (document.activeElement === last) {
2768
+ event.preventDefault();
2769
+ first?.focus();
2770
+ }
2771
+ };
2772
+ document.addEventListener("keydown", handleKeyDown);
2773
+ return () => document.removeEventListener("keydown", handleKeyDown);
2774
+ }, [active, containerRef]);
2775
+ }
2776
+
2777
+ // src/features/file-viewer/primitives/dialog.tsx
2778
+ init_as_child();
2779
+ var DialogContext = createContext(null);
2780
+ function useDialogContext(component) {
2781
+ const context = useContext(DialogContext);
2782
+ if (!context) {
2783
+ throw new Error(`${component} must be used within Dialog.Root`);
2784
+ }
2785
+ return context;
2786
+ }
2787
+ function Root({
2788
+ open,
2789
+ onOpenChange,
2790
+ children
2791
+ }) {
2792
+ const titleId = useId();
2793
+ return /* @__PURE__ */ jsx(DialogContext.Provider, { value: { open, onOpenChange, titleId }, children });
2794
+ }
2795
+ function Portal({ children }) {
2796
+ const { open } = useDialogContext("Dialog.Portal");
2797
+ if (!open || typeof document === "undefined") {
2798
+ return null;
2799
+ }
2800
+ return createPortal(children, document.body);
2801
+ }
2802
+ function Content({
2803
+ children,
2804
+ className,
2805
+ style,
2806
+ "aria-describedby": ariaDescribedBy,
2807
+ ...rest
2808
+ }) {
2809
+ const { open, onOpenChange, titleId } = useDialogContext("Dialog.Content");
2810
+ const contentRef = useRef(null);
2811
+ useFocusTrap(contentRef, open);
2812
+ useEffect(() => {
2813
+ if (!open) {
2814
+ return;
2815
+ }
2816
+ const previousOverflow = document.body.style.overflow;
2817
+ document.body.style.overflow = "hidden";
2818
+ return () => {
2819
+ document.body.style.overflow = previousOverflow;
2820
+ };
2821
+ }, [open]);
2822
+ useEffect(() => {
2823
+ if (!open) {
2824
+ return;
2825
+ }
2826
+ const handleKeyDown = (event) => {
2827
+ if (event.key === "Escape") {
2828
+ onOpenChange(false);
2829
+ }
2830
+ };
2831
+ document.addEventListener("keydown", handleKeyDown);
2832
+ return () => {
2833
+ document.removeEventListener("keydown", handleKeyDown);
2834
+ };
2835
+ }, [open, onOpenChange]);
2836
+ if (!open) {
2837
+ return null;
2838
+ }
2839
+ return /* @__PURE__ */ jsx(
2840
+ "div",
2841
+ {
2842
+ ref: contentRef,
2843
+ role: "dialog",
2844
+ "aria-modal": "true",
2845
+ "aria-labelledby": titleId,
2846
+ "aria-describedby": ariaDescribedBy,
2847
+ "data-visible": open ? "true" : "false",
2848
+ className,
2849
+ style,
2850
+ ...rest,
2851
+ children
2852
+ }
2853
+ );
2854
+ }
2855
+ function Close({
2856
+ asChild,
2857
+ children
2858
+ }) {
2859
+ const { onOpenChange } = useDialogContext("Dialog.Close");
2860
+ const handleClick = (event) => {
2861
+ children.props.onClick?.(event);
2862
+ onOpenChange(false);
2863
+ };
2864
+ if (asChild) {
2865
+ return renderAsChild(true, children, { onClick: handleClick });
2866
+ }
2867
+ return /* @__PURE__ */ jsx("button", { type: "button", onClick: handleClick, children });
2868
+ }
2869
+ function Title({
2870
+ asChild,
2871
+ children
2872
+ }) {
2873
+ const { titleId } = useDialogContext("Dialog.Title");
2874
+ if (asChild && isValidElement(children)) {
2875
+ return renderAsChild(true, children, { id: titleId });
2876
+ }
2877
+ return /* @__PURE__ */ jsx("p", { id: titleId, children });
2878
+ }
2879
+
2880
+ // src/features/file-viewer/FileViewer.tsx
2881
+ init_icons();
2882
+ init_FileViewerTooltip();
2883
+ init_config();
2884
+ init_translations();
2885
+
2886
+ // src/features/file-viewer/utils/file-actions.ts
2887
+ function printPdfFromUrl(url) {
2888
+ const iframe = document.createElement("iframe");
2889
+ iframe.setAttribute(
2890
+ "style",
2891
+ "position:fixed;right:0;bottom:0;width:0;height:0;border:0;visibility:hidden"
2892
+ );
2893
+ iframe.setAttribute("aria-hidden", "true");
2894
+ iframe.src = url;
2895
+ const removeIframe = () => {
2896
+ iframe.remove();
2897
+ };
2898
+ const onLoad = () => {
2899
+ try {
2900
+ iframe.contentWindow?.focus();
2901
+ iframe.contentWindow?.print();
2902
+ } finally {
2903
+ window.setTimeout(removeIframe, 500);
2904
+ }
2905
+ };
2906
+ iframe.addEventListener("load", onLoad, { once: true });
2907
+ iframe.addEventListener("error", removeIframe, { once: true });
2908
+ document.body.appendChild(iframe);
2909
+ }
2910
+ async function downloadFileFromUrl(url, filename) {
2911
+ try {
2912
+ const response = await fetch(url);
2913
+ const blob = await response.blob();
2914
+ const objectUrl = URL.createObjectURL(blob);
2915
+ const anchor = document.createElement("a");
2916
+ anchor.href = objectUrl;
2917
+ anchor.download = filename;
2918
+ anchor.rel = "noopener";
2919
+ document.body.appendChild(anchor);
2920
+ anchor.click();
2921
+ anchor.remove();
2922
+ window.setTimeout(() => URL.revokeObjectURL(objectUrl), 2e3);
2923
+ } catch {
2924
+ const anchor = document.createElement("a");
2925
+ anchor.href = url;
2926
+ anchor.download = filename;
2927
+ anchor.target = "_blank";
2928
+ anchor.rel = "noopener noreferrer";
2929
+ document.body.appendChild(anchor);
2930
+ anchor.click();
2931
+ anchor.remove();
2932
+ }
2933
+ }
2934
+
2935
+ // src/features/file-viewer/FileViewer.tsx
2936
+ init_resolve_options();
2937
+ init_merge_slot_props();
2938
+ var LazyPdfViewer = lazy(() => Promise.resolve().then(() => (init_PdfViewer(), PdfViewer_exports)));
2939
+ var LazyImageViewer = lazy(() => Promise.resolve().then(() => (init_ImageViewer(), ImageViewer_exports)));
2940
+ var FILE_VIEWER_DIALOG_BACKDROP_DEFAULT = "fixed inset-0 z-[100] cursor-default border-0 bg-black/80 p-0 data-[visible=true]:animate-in data-[visible=false]:animate-out data-[visible=false]:fade-out-0 data-[visible=true]:fade-in-0";
2941
+ var FILE_VIEWER_DIALOG_CONTENT_DEFAULT = "fixed inset-0 z-[101] flex h-full w-full min-h-0 cursor-default flex-col border-none bg-transparent p-0 shadow-none outline-none pointer-events-none duration-200 data-[visible=true]:animate-in data-[visible=false]:animate-out data-[visible=false]:fade-out-0 data-[visible=true]:fade-in-0 focus:outline-none";
2942
+ var FILE_VIEWER_DIALOG_PANEL_DEFAULT = "pointer-events-auto flex h-full w-full min-h-0 flex-col overflow-hidden rounded-none border-0 bg-neutral-800/95 shadow-none";
2943
+ var FILE_VIEWER_ROOT_DEFAULT = "flex min-h-0 flex-1 flex-col overflow-hidden";
2944
+ var FILE_VIEWER_HEADER_DEFAULT = "flex w-full shrink-0 justify-between gap-2 rounded-t-lg p-4 font-medium text-white";
2945
+ var FILE_VIEWER_HEADER_TITLE_WRAP_DEFAULT = "flex items-center gap-2";
2946
+ var FILE_VIEWER_CLOSE_BUTTON_DEFAULT = "cursor-pointer rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus-visible:opacity-100 focus-visible:ring-1 focus-visible:ring-white focus-visible:ring-offset-0 disabled:pointer-events-none";
2947
+ var FILE_VIEWER_TITLE_MODAL_DEFAULT = "truncate";
2948
+ var FILE_VIEWER_TITLE_INLINE_DEFAULT = "truncate font-semibold";
2949
+ var FILE_VIEWER_HEADER_ACTIONS_DEFAULT = "flex gap-3";
2950
+ var FILE_VIEWER_ACTION_BUTTON_DEFAULT = "cursor-pointer rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus-visible:opacity-100 focus-visible:ring-1 focus-visible:ring-white focus-visible:ring-offset-0";
2951
+ var FILE_VIEWER_ACTION_BUTTON_DOWNLOAD_DEFAULT = "cursor-pointer rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus-visible:opacity-100 focus-visible:ring-1 focus-visible:ring-white focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50";
2952
+ var FILE_VIEWER_VIEWER_DEFAULT = "flex min-h-0 w-full flex-1 items-center justify-center overflow-hidden text-white";
2953
+ var FILE_VIEWER_LOADER_DEFAULT = "size-10 animate-spin";
2954
+ function mergeFileViewerSlotClassName(builtIn, globalValue, instanceValue) {
2955
+ return mergeClassNames(builtIn, globalValue, instanceValue);
2956
+ }
2957
+ function FileViewer({
2958
+ mode: modeProp,
2959
+ open,
2960
+ onOpenChange,
2961
+ name,
2962
+ extension,
2963
+ url,
2964
+ isLoading,
2965
+ onDownload,
2966
+ pdfViewerProps,
2967
+ renderUnsupported,
2968
+ language: languageProp,
2969
+ hideCloseButton: hideCloseButtonProp,
2970
+ showOpenInModalButton: showOpenInModalButtonProp,
2971
+ onOpenInModal,
2972
+ classNames,
2973
+ styles,
2974
+ dialogClassNames,
2975
+ dialogStyles
2976
+ }) {
2977
+ const globalFileViewer = getFileViewerDefaults().fileViewer;
2978
+ const mode = resolveOption(modeProp, globalFileViewer?.mode, "inline");
2979
+ const language = resolveOption(
2980
+ languageProp,
2981
+ getFileViewerDefaults().language,
2982
+ "english"
2983
+ );
2984
+ const contentRef = useRef(null);
2985
+ const printImage = useReactToPrint({ contentRef });
2986
+ const t = getFileViewerTranslations(language);
2987
+ const ext = extension.toLowerCase();
2988
+ const isPdf = ext === "pdf";
2989
+ const isImage = ["jpg", "png", "jpeg"].includes(ext);
2990
+ const supportedForPreview = isPdf || isImage;
2991
+ const [isMounted, setIsMounted] = useState(false);
2992
+ const [isDownloading, setIsDownloading] = useState(false);
2993
+ const [isModalPreviewOpen, setIsModalPreviewOpen] = useState(false);
2994
+ const showOpenInModalButton = mode === "inline" && resolveOption(
2995
+ showOpenInModalButtonProp,
2996
+ globalFileViewer?.showOpenInModalButton,
2997
+ true
2998
+ );
2999
+ const mergedPdfViewerProps = resolvePdfViewerProps(pdfViewerProps);
3000
+ const mergedImageViewerProps = resolveImageViewerProps();
3001
+ useEffect(() => {
3002
+ setIsMounted(true);
3003
+ }, []);
3004
+ useEffect(() => {
3005
+ setIsDownloading(false);
3006
+ }, [open, url]);
3007
+ useEffect(() => {
3008
+ if (!open) {
3009
+ setIsModalPreviewOpen(false);
3010
+ }
3011
+ }, [open]);
3012
+ const slotClassName = (key, builtIn) => mergeFileViewerSlotClassName(
3013
+ builtIn,
3014
+ globalFileViewer?.classNames?.[key],
3015
+ classNames?.[key]
3016
+ );
3017
+ const slotStyle = (key) => mergeStyles(globalFileViewer?.styles?.[key], styles?.[key]);
3018
+ const backdropClasses = mergeFileViewerSlotClassName(
3019
+ FILE_VIEWER_DIALOG_BACKDROP_DEFAULT,
3020
+ globalFileViewer?.dialogClassNames?.backdrop,
3021
+ dialogClassNames?.backdrop
3022
+ );
3023
+ const contentClasses = mergeFileViewerSlotClassName(
3024
+ FILE_VIEWER_DIALOG_CONTENT_DEFAULT,
3025
+ globalFileViewer?.dialogClassNames?.content,
3026
+ dialogClassNames?.content
3027
+ );
3028
+ const panelClasses = mergeFileViewerSlotClassName(
3029
+ FILE_VIEWER_DIALOG_PANEL_DEFAULT,
3030
+ globalFileViewer?.dialogClassNames?.panel,
3031
+ dialogClassNames?.panel
3032
+ );
3033
+ const backdropStyle = mergeStyles(
3034
+ globalFileViewer?.dialogStyles?.backdrop,
3035
+ dialogStyles?.backdrop
3036
+ );
3037
+ const contentStyle = mergeStyles(
3038
+ globalFileViewer?.dialogStyles?.content,
3039
+ dialogStyles?.content
3040
+ );
3041
+ const panelStyle = mergeStyles(
3042
+ globalFileViewer?.dialogStyles?.panel,
3043
+ dialogStyles?.panel
3044
+ );
3045
+ const renderPdf = () => {
3046
+ if (!isMounted) return null;
3047
+ return /* @__PURE__ */ jsx(
3048
+ Suspense,
3049
+ {
3050
+ fallback: /* @__PURE__ */ jsx(
3051
+ LoaderCircle,
3052
+ {
3053
+ className: slotClassName("loader", "animate-spin size-10 text-white"),
3054
+ style: slotStyle("loader")
3055
+ }
3056
+ ),
3057
+ children: /* @__PURE__ */ jsx(LazyPdfViewer, { url, ...mergedPdfViewerProps, language })
3058
+ }
3059
+ );
3060
+ };
3061
+ const renderImage = () => {
3062
+ if (!isMounted) return null;
3063
+ return /* @__PURE__ */ jsx(
3064
+ Suspense,
3065
+ {
3066
+ fallback: /* @__PURE__ */ jsx(
3067
+ LoaderCircle,
3068
+ {
3069
+ className: slotClassName("loader", "animate-spin size-10 text-white"),
3070
+ style: slotStyle("loader")
3071
+ }
3072
+ ),
3073
+ children: /* @__PURE__ */ jsx(
3074
+ LazyImageViewer,
3075
+ {
3076
+ url,
3077
+ name,
3078
+ language,
3079
+ ...mergedImageViewerProps
3080
+ }
3081
+ )
3082
+ }
3083
+ );
3084
+ };
3085
+ const handlePrint = () => {
3086
+ if (!url) return;
3087
+ if (isPdf) {
3088
+ printPdfFromUrl(url);
3089
+ } else {
3090
+ printImage();
3091
+ }
3092
+ };
3093
+ const handleDownload = async () => {
3094
+ if (!url || isDownloading) return;
3095
+ setIsDownloading(true);
3096
+ try {
3097
+ if (onDownload) {
3098
+ await Promise.resolve(onDownload());
3099
+ } else {
3100
+ await downloadFileFromUrl(url, name);
3101
+ }
3102
+ } finally {
3103
+ setIsDownloading(false);
3104
+ }
3105
+ };
3106
+ const handleOpenInModal = () => {
3107
+ if (onOpenInModal) {
3108
+ onOpenInModal();
3109
+ return;
3110
+ }
3111
+ setIsModalPreviewOpen(true);
3112
+ };
3113
+ const renderCloseButton = (activeMode) => {
3114
+ const hideClose = resolveHideCloseButton(
3115
+ hideCloseButtonProp,
3116
+ globalFileViewer?.hideCloseButton,
3117
+ activeMode
3118
+ );
3119
+ if (hideClose) {
3120
+ return null;
3121
+ }
3122
+ if (activeMode === "modal") {
3123
+ return /* @__PURE__ */ jsx(Close, { asChild: true, children: /* @__PURE__ */ jsx(
3124
+ "button",
3125
+ {
3126
+ type: "button",
3127
+ "aria-label": t.fileViewer.closeAriaLabel,
3128
+ className: slotClassName("closeButton", FILE_VIEWER_CLOSE_BUTTON_DEFAULT),
3129
+ style: slotStyle("closeButton"),
3130
+ children: /* @__PURE__ */ jsx(X, { className: "size-7", "aria-hidden": true })
3131
+ }
3132
+ ) });
3133
+ }
3134
+ return /* @__PURE__ */ jsx(
3135
+ "button",
3136
+ {
3137
+ type: "button",
3138
+ onClick: () => onOpenChange(false),
3139
+ "aria-label": t.fileViewer.closeAriaLabel,
3140
+ className: slotClassName("closeButton", FILE_VIEWER_CLOSE_BUTTON_DEFAULT),
3141
+ style: slotStyle("closeButton"),
3142
+ children: /* @__PURE__ */ jsx(X, { className: "size-7", "aria-hidden": true })
3143
+ }
3144
+ );
3145
+ };
3146
+ const renderPanelContent = (activeMode) => {
3147
+ const closeButton = renderCloseButton(activeMode);
3148
+ return /* @__PURE__ */ jsxs(
3149
+ "div",
3150
+ {
3151
+ className: slotClassName("root", FILE_VIEWER_ROOT_DEFAULT),
3152
+ style: slotStyle("root"),
3153
+ children: [
3154
+ /* @__PURE__ */ jsxs(
3155
+ "div",
3156
+ {
3157
+ className: slotClassName("header", FILE_VIEWER_HEADER_DEFAULT),
3158
+ style: slotStyle("header"),
3159
+ children: [
3160
+ /* @__PURE__ */ jsxs("span", { className: FILE_VIEWER_HEADER_TITLE_WRAP_DEFAULT, children: [
3161
+ closeButton,
3162
+ activeMode === "modal" ? /* @__PURE__ */ jsx(Title, { asChild: true, children: /* @__PURE__ */ jsx(
3163
+ "p",
3164
+ {
3165
+ className: slotClassName(
3166
+ "headerTitle",
3167
+ FILE_VIEWER_TITLE_MODAL_DEFAULT
3168
+ ),
3169
+ style: slotStyle("headerTitle"),
3170
+ children: name
3171
+ }
3172
+ ) }) : /* @__PURE__ */ jsx(
3173
+ "p",
3174
+ {
3175
+ className: slotClassName(
3176
+ "headerTitle",
3177
+ FILE_VIEWER_TITLE_INLINE_DEFAULT
3178
+ ),
3179
+ style: slotStyle("headerTitle"),
3180
+ children: name
3181
+ }
3182
+ )
3183
+ ] }),
3184
+ url && !isLoading && supportedForPreview ? /* @__PURE__ */ jsxs(
3185
+ "span",
3186
+ {
3187
+ className: slotClassName(
3188
+ "headerActions",
3189
+ FILE_VIEWER_HEADER_ACTIONS_DEFAULT
3190
+ ),
3191
+ style: slotStyle("headerActions"),
3192
+ children: [
3193
+ showOpenInModalButton && activeMode === "inline" ? /* @__PURE__ */ jsx(FileViewerTooltip, { content: t.fileViewer.openInModalTooltip, children: /* @__PURE__ */ jsx(
3194
+ "button",
3195
+ {
3196
+ type: "button",
3197
+ onClick: handleOpenInModal,
3198
+ className: slotClassName(
3199
+ "openInModalButton",
3200
+ FILE_VIEWER_ACTION_BUTTON_DEFAULT
3201
+ ),
3202
+ style: slotStyle("openInModalButton"),
3203
+ "aria-label": t.fileViewer.openInModalAriaLabel,
3204
+ children: /* @__PURE__ */ jsx(Maximize2, { className: "size-6", "aria-hidden": true })
3205
+ }
3206
+ ) }) : null,
3207
+ /* @__PURE__ */ jsx(FileViewerTooltip, { content: t.fileViewer.printTooltip, children: /* @__PURE__ */ jsx(
3208
+ "button",
3209
+ {
3210
+ type: "button",
3211
+ onClick: handlePrint,
3212
+ className: slotClassName(
3213
+ "printButton",
3214
+ FILE_VIEWER_ACTION_BUTTON_DEFAULT
3215
+ ),
3216
+ style: slotStyle("printButton"),
3217
+ "aria-label": t.fileViewer.printAriaLabel,
3218
+ children: /* @__PURE__ */ jsx(Printer, { className: "size-6", "aria-hidden": true })
3219
+ }
3220
+ ) }),
3221
+ /* @__PURE__ */ jsx(
3222
+ FileViewerTooltip,
3223
+ {
3224
+ content: isDownloading ? t.fileViewer.downloadInProgressTooltip : t.fileViewer.downloadTooltip,
3225
+ children: /* @__PURE__ */ jsx(
3226
+ "button",
3227
+ {
3228
+ type: "button",
3229
+ disabled: isDownloading,
3230
+ onClick: () => {
3231
+ void handleDownload();
3232
+ },
3233
+ className: slotClassName(
3234
+ "downloadButton",
3235
+ FILE_VIEWER_ACTION_BUTTON_DOWNLOAD_DEFAULT
3236
+ ),
3237
+ style: slotStyle("downloadButton"),
3238
+ "aria-busy": isDownloading,
3239
+ "aria-label": isDownloading ? t.fileViewer.downloadInProgressAriaLabel : t.fileViewer.downloadAriaLabel,
3240
+ children: isDownloading ? /* @__PURE__ */ jsx(LoaderCircle, { className: "size-6 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(Download, { className: "size-6", "aria-hidden": true })
3241
+ }
3242
+ )
3243
+ }
3244
+ )
3245
+ ]
3246
+ }
3247
+ ) : null
3248
+ ]
3249
+ }
3250
+ ),
3251
+ /* @__PURE__ */ jsx(
3252
+ "div",
3253
+ {
3254
+ className: slotClassName("viewer", FILE_VIEWER_VIEWER_DEFAULT),
3255
+ style: slotStyle("viewer"),
3256
+ children: !url || isLoading ? /* @__PURE__ */ jsx(
3257
+ LoaderCircle,
3258
+ {
3259
+ className: slotClassName("loader", FILE_VIEWER_LOADER_DEFAULT),
3260
+ style: slotStyle("loader")
3261
+ }
3262
+ ) : isImage ? renderImage() : isPdf ? renderPdf() : renderUnsupported || /* @__PURE__ */ jsx(
3263
+ "p",
3264
+ {
3265
+ className: slotClassName("unsupported", ""),
3266
+ style: slotStyle("unsupported"),
3267
+ children: resolveFormattedMessage(t.fileViewer.unsupportedFileType, {
3268
+ extension
3269
+ })
3270
+ }
3271
+ )
3272
+ }
3273
+ )
3274
+ ]
3275
+ }
3276
+ );
3277
+ };
3278
+ const renderModalShell = (modalOpen, onModalOpenChange, content) => /* @__PURE__ */ jsx(Root, { open: modalOpen, onOpenChange: onModalOpenChange, children: /* @__PURE__ */ jsxs(Portal, { children: [
3279
+ /* @__PURE__ */ jsx(Close, { asChild: true, children: /* @__PURE__ */ jsx(
3280
+ "button",
3281
+ {
3282
+ type: "button",
3283
+ tabIndex: -1,
3284
+ "data-visible": modalOpen,
3285
+ className: backdropClasses,
3286
+ style: backdropStyle,
3287
+ "aria-hidden": true
3288
+ }
3289
+ ) }),
3290
+ /* @__PURE__ */ jsx(
3291
+ Content,
3292
+ {
3293
+ "aria-describedby": void 0,
3294
+ className: contentClasses,
3295
+ style: contentStyle,
3296
+ children: /* @__PURE__ */ jsx(
3297
+ "div",
3298
+ {
3299
+ className: panelClasses,
3300
+ style: panelStyle,
3301
+ onClick: (event) => event.stopPropagation(),
3302
+ children: content
3303
+ }
3304
+ )
3305
+ }
3306
+ )
3307
+ ] }) });
3308
+ const printOnlyNode = isImage && url ? /* @__PURE__ */ jsx("div", { className: "hidden", children: /* @__PURE__ */ jsx("div", { ref: contentRef, children: /* @__PURE__ */ jsx("img", { src: url, className: "max-h-[105dvh]", alt: name }) }) }) : null;
3309
+ if (mode === "inline") {
3310
+ if (!open) return null;
3311
+ return /* @__PURE__ */ jsxs(FileViewerTooltipProvider, { children: [
3312
+ /* @__PURE__ */ jsx("div", { className: panelClasses, style: panelStyle, children: renderPanelContent("inline") }),
3313
+ isModalPreviewOpen ? renderModalShell(
3314
+ isModalPreviewOpen,
3315
+ setIsModalPreviewOpen,
3316
+ renderPanelContent("modal")
3317
+ ) : null,
3318
+ printOnlyNode
3319
+ ] });
3320
+ }
3321
+ return /* @__PURE__ */ jsxs(FileViewerTooltipProvider, { children: [
3322
+ renderModalShell(open, onOpenChange, renderPanelContent("modal")),
3323
+ printOnlyNode
3324
+ ] });
3325
+ }
3326
+
3327
+ // src/features/file-viewer/index.ts
3328
+ init_PdfViewer();
3329
+ init_ImageViewer();
3330
+ init_FileViewerTooltip();
3331
+ var configuredWorkerSrc;
3332
+ function getFileViewerPdfWorkerSrc() {
3333
+ return `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
3334
+ }
3335
+ var getFileViewerPdfWorkerCdnUrl = getFileViewerPdfWorkerSrc;
3336
+ function isFileViewerPdfWorkerConfigured() {
3337
+ return configuredWorkerSrc !== void 0;
3338
+ }
3339
+ function configureFileViewerPdfWorker(options = {}) {
3340
+ const workerSrc = options.workerSrc ?? getFileViewerPdfWorkerSrc();
3341
+ pdfjs.GlobalWorkerOptions.workerSrc = workerSrc;
3342
+ configuredWorkerSrc = workerSrc;
3343
+ }
3344
+
3345
+ // src/features/file-viewer/index.ts
3346
+ init_config();
3347
+ init_translations();
3348
+
3349
+ export { FileViewer, FileViewerTooltip, FileViewerTooltipProvider, ImageViewer, PdfViewer, configureFileViewerPdfWorker, defaultFileViewerTranslations, fileViewerTranslationsByLanguage, getFileViewerDefaults, getFileViewerPdfWorkerCdnUrl, getFileViewerPdfWorkerSrc, getFileViewerTranslations, isFileViewerPdfWorkerConfigured, resetFileViewerDefaults, resolveFormattedMessage, resolveImageViewerProps, resolvePdfViewerProps, setFileViewerDefaults };
3350
+ //# sourceMappingURL=index.js.map
3351
+ //# sourceMappingURL=index.js.map