@arbidocs/react 0.3.185 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-YJAYST7S.js → chunk-3CLGHHME.js} +880 -240
- package/dist/chunk-3CLGHHME.js.map +1 -0
- package/dist/chunk-6ALMBER7.js +176 -0
- package/dist/chunk-6ALMBER7.js.map +1 -0
- package/dist/chunk-BXP6S34V.cjs +284 -0
- package/dist/chunk-BXP6S34V.cjs.map +1 -0
- package/dist/chunk-EJ2BMHYK.js +272 -0
- package/dist/chunk-EJ2BMHYK.js.map +1 -0
- package/dist/{chunk-IZPXHVPJ.cjs → chunk-OU2QLIKS.cjs} +969 -344
- package/dist/chunk-OU2QLIKS.cjs.map +1 -0
- package/dist/chunk-RZQACYNQ.cjs +218 -0
- package/dist/chunk-RZQACYNQ.cjs.map +1 -0
- package/dist/{index-4WwLZsQO.d.cts → index-CcbVSNSP.d.cts} +40 -2
- package/dist/{index-4WwLZsQO.d.ts → index-CcbVSNSP.d.ts} +40 -2
- package/dist/index.cjs +589 -1070
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -155
- package/dist/index.d.ts +11 -155
- package/dist/index.js +223 -725
- package/dist/index.js.map +1 -1
- package/dist/shell/index.cjs +13 -4
- package/dist/shell/index.d.cts +1 -1
- package/dist/shell/index.d.ts +1 -1
- package/dist/shell/index.js +2 -1
- package/dist/ui/index.cjs +211 -190
- package/dist/ui/index.d.cts +204 -3
- package/dist/ui/index.d.ts +204 -3
- package/dist/ui/index.js +2 -1
- package/dist/viewers/index.cjs +327 -76
- package/dist/viewers/index.cjs.map +1 -1
- package/dist/viewers/index.d.cts +90 -4
- package/dist/viewers/index.d.ts +90 -4
- package/dist/viewers/index.js +316 -78
- package/dist/viewers/index.js.map +1 -1
- package/package.json +4 -3
- package/dist/chunk-3MLQUKT4.cjs +0 -97
- package/dist/chunk-3MLQUKT4.cjs.map +0 -1
- package/dist/chunk-IZPXHVPJ.cjs.map +0 -1
- package/dist/chunk-J3XEKOV5.js +0 -87
- package/dist/chunk-J3XEKOV5.js.map +0 -1
- package/dist/chunk-YJAYST7S.js.map +0 -1
package/dist/viewers/index.cjs
CHANGED
|
@@ -7,6 +7,59 @@ require('react-pdf/dist/Page/AnnotationLayer.css');
|
|
|
7
7
|
require('react-pdf/dist/Page/TextLayer.css');
|
|
8
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
9
9
|
|
|
10
|
+
var PAGE_SELECTOR = '[role="article"][data-page-number]';
|
|
11
|
+
function anchorDelta(previousHeight, nextHeight, pageTop, containerTop) {
|
|
12
|
+
const delta = nextHeight - previousHeight;
|
|
13
|
+
if (delta === 0) return 0;
|
|
14
|
+
return pageTop + previousHeight <= containerTop + 1 ? delta : 0;
|
|
15
|
+
}
|
|
16
|
+
function measureVisiblePages(pages, viewTop, viewBottom) {
|
|
17
|
+
const visibility = /* @__PURE__ */ new Map();
|
|
18
|
+
let page = null;
|
|
19
|
+
let bestPx = 0;
|
|
20
|
+
let score = 0;
|
|
21
|
+
const viewHeight = viewBottom - viewTop;
|
|
22
|
+
for (const p of pages) {
|
|
23
|
+
const height = p.bottom - p.top;
|
|
24
|
+
const px = Math.min(p.bottom, viewBottom) - Math.max(p.top, viewTop);
|
|
25
|
+
if (px <= 0 || height <= 0) continue;
|
|
26
|
+
visibility.set(p.n, px / height);
|
|
27
|
+
if (px > bestPx) {
|
|
28
|
+
bestPx = px;
|
|
29
|
+
page = p.n;
|
|
30
|
+
score = px / Math.min(height, viewHeight);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { page, score, visibility };
|
|
34
|
+
}
|
|
35
|
+
function useScrollAnchor(containerRef, key) {
|
|
36
|
+
react.useEffect(() => {
|
|
37
|
+
const container = containerRef.current;
|
|
38
|
+
if (!container || typeof ResizeObserver === "undefined") return;
|
|
39
|
+
container.style.overflowAnchor = "none";
|
|
40
|
+
const heights = /* @__PURE__ */ new Map();
|
|
41
|
+
const observer = new ResizeObserver((entries) => {
|
|
42
|
+
let shift = 0;
|
|
43
|
+
const containerTop = container.getBoundingClientRect().top;
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const el = entry.target;
|
|
46
|
+
const height = el.getBoundingClientRect().height;
|
|
47
|
+
const previous = heights.get(el);
|
|
48
|
+
heights.set(el, height);
|
|
49
|
+
if (previous === void 0) continue;
|
|
50
|
+
shift += anchorDelta(previous, height, el.getBoundingClientRect().top, containerTop);
|
|
51
|
+
}
|
|
52
|
+
if (shift !== 0 && !container.dataset.zooming) container.scrollTop += shift;
|
|
53
|
+
});
|
|
54
|
+
container.querySelectorAll(PAGE_SELECTOR).forEach((el) => {
|
|
55
|
+
heights.set(el, el.getBoundingClientRect().height);
|
|
56
|
+
observer.observe(el);
|
|
57
|
+
});
|
|
58
|
+
return () => observer.disconnect();
|
|
59
|
+
}, [containerRef, key]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/viewers/usePageIntersectionObserver.ts
|
|
10
63
|
var PAGE_UPDATE_DEBOUNCE_MS = 10;
|
|
11
64
|
var MIN_VISIBILITY_THRESHOLD = 0.15;
|
|
12
65
|
var INTERSECTION_THRESHOLDS = [0, 0.25, 0.5, 0.75, 1];
|
|
@@ -17,66 +70,194 @@ function usePageIntersectionObserver({
|
|
|
17
70
|
getPageElements,
|
|
18
71
|
getPageNumber,
|
|
19
72
|
onPageChange,
|
|
20
|
-
onVisibilityUpdate
|
|
73
|
+
onVisibilityUpdate,
|
|
74
|
+
observeKey
|
|
21
75
|
}) {
|
|
76
|
+
const latest = react.useRef({ getPageElements, getPageNumber, onPageChange, onVisibilityUpdate });
|
|
77
|
+
latest.current = { getPageElements, getPageNumber, onPageChange, onVisibilityUpdate };
|
|
22
78
|
react.useEffect(() => {
|
|
23
79
|
if (!isEnabled || !containerRef.current) return;
|
|
24
80
|
const container = containerRef.current;
|
|
25
|
-
const visibilityMap = /* @__PURE__ */ new Map();
|
|
26
81
|
let lastReportedPage = null;
|
|
27
82
|
let updateTimeout = null;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (scrollCompleteRef.current && mostVisiblePage !== lastReportedPage && maxVisibility > MIN_VISIBILITY_THRESHOLD) {
|
|
38
|
-
lastReportedPage = mostVisiblePage;
|
|
39
|
-
onPageChange(mostVisiblePage);
|
|
83
|
+
let frame = 0;
|
|
84
|
+
const evaluate = () => {
|
|
85
|
+
const root = container.getBoundingClientRect();
|
|
86
|
+
const pages = [];
|
|
87
|
+
for (const el of latest.current.getPageElements()) {
|
|
88
|
+
const n = latest.current.getPageNumber(el);
|
|
89
|
+
if (n <= 0) continue;
|
|
90
|
+
const r = el.getBoundingClientRect();
|
|
91
|
+
pages.push({ n, top: r.top, bottom: r.bottom });
|
|
40
92
|
}
|
|
41
|
-
|
|
42
|
-
|
|
93
|
+
const { page, score, visibility } = measureVisiblePages(pages, root.top, root.bottom);
|
|
94
|
+
if (scrollCompleteRef.current && page !== null && page !== lastReportedPage && score > MIN_VISIBILITY_THRESHOLD) {
|
|
95
|
+
lastReportedPage = page;
|
|
96
|
+
latest.current.onPageChange(page);
|
|
43
97
|
}
|
|
98
|
+
latest.current.onVisibilityUpdate?.(visibility);
|
|
44
99
|
};
|
|
45
100
|
const observer = new IntersectionObserver(
|
|
46
|
-
(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (pageNum > 0) {
|
|
50
|
-
visibilityMap.set(pageNum, entry.intersectionRatio);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
if (updateTimeout) {
|
|
54
|
-
clearTimeout(updateTimeout);
|
|
55
|
-
}
|
|
56
|
-
updateTimeout = setTimeout(updatePageNumber, PAGE_UPDATE_DEBOUNCE_MS);
|
|
101
|
+
() => {
|
|
102
|
+
if (updateTimeout) clearTimeout(updateTimeout);
|
|
103
|
+
updateTimeout = setTimeout(evaluate, PAGE_UPDATE_DEBOUNCE_MS);
|
|
57
104
|
},
|
|
58
|
-
{
|
|
59
|
-
root: container,
|
|
60
|
-
threshold: INTERSECTION_THRESHOLDS
|
|
61
|
-
}
|
|
105
|
+
{ root: container, threshold: INTERSECTION_THRESHOLDS }
|
|
62
106
|
);
|
|
63
|
-
const
|
|
64
|
-
|
|
107
|
+
const onScroll = () => {
|
|
108
|
+
if (!frame) {
|
|
109
|
+
frame = requestAnimationFrame(() => {
|
|
110
|
+
frame = 0;
|
|
111
|
+
evaluate();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
container.addEventListener("scroll", onScroll, { passive: true });
|
|
116
|
+
latest.current.getPageElements().forEach((element) => observer.observe(element));
|
|
65
117
|
return () => {
|
|
66
118
|
observer.disconnect();
|
|
67
|
-
|
|
68
|
-
|
|
119
|
+
container.removeEventListener("scroll", onScroll);
|
|
120
|
+
if (updateTimeout) clearTimeout(updateTimeout);
|
|
121
|
+
if (frame) cancelAnimationFrame(frame);
|
|
122
|
+
};
|
|
123
|
+
}, [containerRef, scrollCompleteRef, isEnabled, observeKey]);
|
|
124
|
+
}
|
|
125
|
+
var MIN_ZOOM = 0.5;
|
|
126
|
+
var MAX_ZOOM = 4;
|
|
127
|
+
var DOUBLE_TAP_ZOOM = 2;
|
|
128
|
+
var MAX_CANVAS_PIXELS = 8e6;
|
|
129
|
+
var MAX_RENDER_DPR = 2;
|
|
130
|
+
function clampZoom(zoom) {
|
|
131
|
+
if (!Number.isFinite(zoom)) return 1;
|
|
132
|
+
return Math.round(Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoom)) * 100) / 100;
|
|
133
|
+
}
|
|
134
|
+
var ZOOM_PRESETS = [1.5, 2, 3];
|
|
135
|
+
function cycleZoom(zoom) {
|
|
136
|
+
return ZOOM_PRESETS.find((preset) => preset > zoom + 0.05) ?? 1;
|
|
137
|
+
}
|
|
138
|
+
function doubleTapZoom(zoom) {
|
|
139
|
+
return zoom > 1.05 ? 1 : DOUBLE_TAP_ZOOM;
|
|
140
|
+
}
|
|
141
|
+
function zoomLabel(zoom) {
|
|
142
|
+
return `${Math.round(zoom * 100)}%`;
|
|
143
|
+
}
|
|
144
|
+
function pinchZoom(startZoom, startDistance, distance2) {
|
|
145
|
+
if (startDistance <= 0) return startZoom;
|
|
146
|
+
return clampZoom(startZoom * distance2 / startDistance);
|
|
147
|
+
}
|
|
148
|
+
function anchoredScroll(scroll, focal, oldZoom, newZoom) {
|
|
149
|
+
if (oldZoom <= 0) return scroll;
|
|
150
|
+
return (scroll + focal) * newZoom / oldZoom - focal;
|
|
151
|
+
}
|
|
152
|
+
function renderDpr(cssWidth, cssHeight, devicePixelRatio, maxPixels = MAX_CANVAS_PIXELS) {
|
|
153
|
+
const wanted = Math.min(Math.max(devicePixelRatio || 1, 1), MAX_RENDER_DPR);
|
|
154
|
+
const area = cssWidth * cssHeight;
|
|
155
|
+
if (area <= 0) return wanted;
|
|
156
|
+
const fits = Math.sqrt(maxPixels / area);
|
|
157
|
+
return Math.max(1, Math.min(wanted, fits));
|
|
158
|
+
}
|
|
159
|
+
var distance = (a, b) => Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY);
|
|
160
|
+
var INTERACTIVE = 'button, a, input, textarea, select, [role="button"]';
|
|
161
|
+
var DOUBLE_TAP_MS = 300;
|
|
162
|
+
var DOUBLE_TAP_SLOP_PX = 30;
|
|
163
|
+
function usePinchZoom({ containerRef, zoom, onZoomChange }) {
|
|
164
|
+
const zoomRef = react.useRef(zoom);
|
|
165
|
+
const onChangeRef = react.useRef(onZoomChange);
|
|
166
|
+
onChangeRef.current = onZoomChange;
|
|
167
|
+
const focalRef = react.useRef(null);
|
|
168
|
+
const appliedZoomRef = react.useRef(zoom);
|
|
169
|
+
const request = react.useCallback((next, focal) => {
|
|
170
|
+
const clamped = clampZoom(next);
|
|
171
|
+
if (clamped === zoomRef.current) return;
|
|
172
|
+
focalRef.current = focal;
|
|
173
|
+
zoomRef.current = clamped;
|
|
174
|
+
onChangeRef.current?.(clamped);
|
|
175
|
+
}, []);
|
|
176
|
+
react.useLayoutEffect(() => {
|
|
177
|
+
zoomRef.current = zoom;
|
|
178
|
+
const container = containerRef.current;
|
|
179
|
+
const previous = appliedZoomRef.current;
|
|
180
|
+
appliedZoomRef.current = zoom;
|
|
181
|
+
if (!container || previous === zoom) return;
|
|
182
|
+
const rect = container.getBoundingClientRect();
|
|
183
|
+
const focal = focalRef.current ?? { x: rect.width / 2, y: rect.height / 2 };
|
|
184
|
+
focalRef.current = null;
|
|
185
|
+
container.dataset.zooming = "true";
|
|
186
|
+
container.scrollLeft = anchoredScroll(container.scrollLeft, focal.x, previous, zoom);
|
|
187
|
+
container.scrollTop = anchoredScroll(container.scrollTop, focal.y, previous, zoom);
|
|
188
|
+
requestAnimationFrame(
|
|
189
|
+
() => requestAnimationFrame(() => {
|
|
190
|
+
delete container.dataset.zooming;
|
|
191
|
+
})
|
|
192
|
+
);
|
|
193
|
+
}, [zoom, containerRef]);
|
|
194
|
+
react.useEffect(() => {
|
|
195
|
+
const el = containerRef.current;
|
|
196
|
+
if (!el) return;
|
|
197
|
+
let pinch = null;
|
|
198
|
+
let lastTap = null;
|
|
199
|
+
let frame = 0;
|
|
200
|
+
let pending = null;
|
|
201
|
+
const focalOf = (a, b) => {
|
|
202
|
+
const rect = el.getBoundingClientRect();
|
|
203
|
+
return {
|
|
204
|
+
x: (a.clientX + b.clientX) / 2 - rect.left,
|
|
205
|
+
y: (a.clientY + b.clientY) / 2 - rect.top
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
const flush = () => {
|
|
209
|
+
frame = 0;
|
|
210
|
+
if (pending) request(pending.zoom, pending.focal);
|
|
211
|
+
pending = null;
|
|
212
|
+
};
|
|
213
|
+
const onStart = (e) => {
|
|
214
|
+
if (e.touches.length === 2) {
|
|
215
|
+
pinch = { startDistance: distance(e.touches[0], e.touches[1]), startZoom: zoomRef.current };
|
|
216
|
+
lastTap = null;
|
|
69
217
|
}
|
|
70
218
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
219
|
+
const onMove = (e) => {
|
|
220
|
+
if (!pinch || e.touches.length !== 2) return;
|
|
221
|
+
e.preventDefault();
|
|
222
|
+
pending = {
|
|
223
|
+
zoom: pinchZoom(pinch.startZoom, pinch.startDistance, distance(e.touches[0], e.touches[1])),
|
|
224
|
+
focal: focalOf(e.touches[0], e.touches[1])
|
|
225
|
+
};
|
|
226
|
+
if (!frame) frame = requestAnimationFrame(flush);
|
|
227
|
+
};
|
|
228
|
+
const onEnd = (e) => {
|
|
229
|
+
if (pinch && e.touches.length < 2) {
|
|
230
|
+
pinch = null;
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (e.touches.length !== 0 || e.changedTouches.length !== 1) return;
|
|
234
|
+
const t = e.changedTouches[0];
|
|
235
|
+
if (e.target?.closest?.(INTERACTIVE)) return;
|
|
236
|
+
const now = e.timeStamp;
|
|
237
|
+
if (lastTap && now - lastTap.time < DOUBLE_TAP_MS && Math.hypot(t.clientX - lastTap.x, t.clientY - lastTap.y) < DOUBLE_TAP_SLOP_PX) {
|
|
238
|
+
e.preventDefault();
|
|
239
|
+
const rect = el.getBoundingClientRect();
|
|
240
|
+
request(doubleTapZoom(zoomRef.current), {
|
|
241
|
+
x: t.clientX - rect.left,
|
|
242
|
+
y: t.clientY - rect.top
|
|
243
|
+
});
|
|
244
|
+
lastTap = null;
|
|
245
|
+
} else {
|
|
246
|
+
lastTap = { time: now, x: t.clientX, y: t.clientY };
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
el.addEventListener("touchstart", onStart, { passive: true });
|
|
250
|
+
el.addEventListener("touchmove", onMove, { passive: false });
|
|
251
|
+
el.addEventListener("touchend", onEnd, { passive: false });
|
|
252
|
+
el.addEventListener("touchcancel", onEnd, { passive: false });
|
|
253
|
+
return () => {
|
|
254
|
+
el.removeEventListener("touchstart", onStart);
|
|
255
|
+
el.removeEventListener("touchmove", onMove);
|
|
256
|
+
el.removeEventListener("touchend", onEnd);
|
|
257
|
+
el.removeEventListener("touchcancel", onEnd);
|
|
258
|
+
if (frame) cancelAnimationFrame(frame);
|
|
259
|
+
};
|
|
260
|
+
}, [containerRef, request]);
|
|
80
261
|
}
|
|
81
262
|
|
|
82
263
|
// src/viewers/pdf-viewer-constants.ts
|
|
@@ -94,14 +275,28 @@ function ensurePdfWorker(workerSrc) {
|
|
|
94
275
|
reactPdf.pdfjs.GlobalWorkerOptions.workerSrc = workerSrc;
|
|
95
276
|
}
|
|
96
277
|
}
|
|
97
|
-
var CONTAINER_PADDING_PX =
|
|
98
|
-
var
|
|
278
|
+
var CONTAINER_PADDING_PX = 34;
|
|
279
|
+
var DEFAULT_PAGE_ASPECT_RATIO = 1.414;
|
|
280
|
+
var PAGE_BORDER_PX = 2;
|
|
281
|
+
var RERENDER_DEBOUNCE_MS = 250;
|
|
282
|
+
var ZOOMED_BUFFER_THRESHOLD = 1.5;
|
|
283
|
+
function sameSet(a, b) {
|
|
284
|
+
if (a.size !== b.size) return false;
|
|
285
|
+
for (const v of a) if (!b.has(v)) return false;
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
99
288
|
var TEXT_LAYER_BUFFER = 1;
|
|
289
|
+
var ratioOf = (page) => {
|
|
290
|
+
const { width, height } = page.getViewport({ scale: 1 });
|
|
291
|
+
return width > 0 && height > 0 ? height / width : DEFAULT_PAGE_ASPECT_RATIO;
|
|
292
|
+
};
|
|
100
293
|
function PdfViewer({
|
|
101
294
|
file,
|
|
102
295
|
currentPage,
|
|
103
296
|
onLoadSuccess,
|
|
104
297
|
scrollTrigger,
|
|
298
|
+
zoom = 1,
|
|
299
|
+
onZoomChange,
|
|
105
300
|
onPageChange,
|
|
106
301
|
disableFontFace = false,
|
|
107
302
|
assets
|
|
@@ -113,6 +308,8 @@ function PdfViewer({
|
|
|
113
308
|
ensurePdfWorker(workerSrc);
|
|
114
309
|
const [numPages, setNumPages] = react.useState(0);
|
|
115
310
|
const [containerWidth, setContainerWidth] = react.useState(0);
|
|
311
|
+
const [defaultRatio, setDefaultRatio] = react.useState(DEFAULT_PAGE_ASPECT_RATIO);
|
|
312
|
+
const [pageRatios, setPageRatios] = react.useState(/* @__PURE__ */ new Map());
|
|
116
313
|
const [loadError, setLoadError] = react.useState(null);
|
|
117
314
|
const [documentKey, setDocumentKey] = react.useState(() => `pdf-${Date.now()}`);
|
|
118
315
|
const initialPage = currentPage || 1;
|
|
@@ -132,7 +329,10 @@ function PdfViewer({
|
|
|
132
329
|
const containerRef = react.useRef(null);
|
|
133
330
|
const pageRefs = react.useRef(/* @__PURE__ */ new Map());
|
|
134
331
|
const onDocumentLoadSuccess = react.useCallback(
|
|
135
|
-
(
|
|
332
|
+
(pdf) => {
|
|
333
|
+
const n = pdf.numPages;
|
|
334
|
+
void pdf.getPage?.(1).then((page) => setDefaultRatio(ratioOf(page))).catch(() => {
|
|
335
|
+
});
|
|
136
336
|
setNumPages(n);
|
|
137
337
|
setLoadError(null);
|
|
138
338
|
const expand = () => {
|
|
@@ -211,45 +411,51 @@ function PdfViewer({
|
|
|
211
411
|
});
|
|
212
412
|
}
|
|
213
413
|
}, [numPages]);
|
|
414
|
+
const currentPageRef = react.useRef(currentPage);
|
|
415
|
+
currentPageRef.current = currentPage;
|
|
214
416
|
react.useEffect(() => {
|
|
215
|
-
|
|
417
|
+
const currentPage2 = currentPageRef.current;
|
|
418
|
+
if (!scrollTrigger || numPages === 0 || !currentPage2) return;
|
|
216
419
|
scrollCompleteRef.current = false;
|
|
217
420
|
setRenderedPages((prev) => {
|
|
218
421
|
const newSet = new Set(prev);
|
|
219
|
-
for (let i = Math.max(1,
|
|
422
|
+
for (let i = Math.max(1, currentPage2 - LAZY_LOAD_BUFFER_BEFORE); i <= Math.min(numPages, currentPage2 + LAZY_LOAD_BUFFER_AFTER); i++) {
|
|
220
423
|
newSet.add(i);
|
|
221
424
|
}
|
|
222
425
|
return newSet;
|
|
223
426
|
});
|
|
224
427
|
requestAnimationFrame(() => {
|
|
225
428
|
requestAnimationFrame(() => {
|
|
226
|
-
const pageElement = pageRefs.current.get(
|
|
429
|
+
const pageElement = pageRefs.current.get(currentPage2);
|
|
227
430
|
if (pageElement) {
|
|
228
431
|
pageElement.scrollIntoView({ behavior: "instant", block: "start" });
|
|
229
432
|
}
|
|
230
433
|
scrollCompleteRef.current = true;
|
|
231
434
|
});
|
|
232
435
|
});
|
|
233
|
-
}, [scrollTrigger,
|
|
436
|
+
}, [scrollTrigger, numPages]);
|
|
234
437
|
const handleVisibilityUpdate = react.useCallback(
|
|
235
438
|
(visibilityMap) => {
|
|
236
439
|
const visiblePageNums = Array.from(visibilityMap.entries()).filter(([, ratio]) => ratio > 0).map(([pageNum]) => pageNum);
|
|
237
440
|
if (visiblePageNums.length > 0) {
|
|
238
441
|
const minVisible = Math.min(...visiblePageNums);
|
|
239
442
|
const maxVisible = Math.max(...visiblePageNums);
|
|
443
|
+
const zoomed = zoom > ZOOMED_BUFFER_THRESHOLD;
|
|
444
|
+
const before = zoomed ? 1 : LAZY_LOAD_BUFFER_BEFORE;
|
|
445
|
+
const after = zoomed ? 2 : LAZY_LOAD_BUFFER_AFTER;
|
|
240
446
|
const pagesToRender = /* @__PURE__ */ new Set();
|
|
241
|
-
for (let i = Math.max(1, minVisible -
|
|
447
|
+
for (let i = Math.max(1, minVisible - before); i <= Math.min(numPages, maxVisible + after); i++) {
|
|
242
448
|
pagesToRender.add(i);
|
|
243
449
|
}
|
|
244
|
-
setRenderedPages(pagesToRender);
|
|
450
|
+
setRenderedPages((prev) => sameSet(prev, pagesToRender) ? prev : pagesToRender);
|
|
245
451
|
const nearViewport = /* @__PURE__ */ new Set();
|
|
246
452
|
for (let i = Math.max(1, minVisible - TEXT_LAYER_BUFFER); i <= Math.min(numPages, maxVisible + TEXT_LAYER_BUFFER); i++) {
|
|
247
453
|
nearViewport.add(i);
|
|
248
454
|
}
|
|
249
|
-
setVisiblePages(nearViewport);
|
|
455
|
+
setVisiblePages((prev) => sameSet(prev, nearViewport) ? prev : nearViewport);
|
|
250
456
|
}
|
|
251
457
|
},
|
|
252
|
-
[numPages]
|
|
458
|
+
[numPages, zoom]
|
|
253
459
|
);
|
|
254
460
|
const documentOptions = react.useMemo(
|
|
255
461
|
() => ({
|
|
@@ -272,19 +478,30 @@ function PdfViewer({
|
|
|
272
478
|
getPageElements: () => Array.from(pageRefs.current.values()),
|
|
273
479
|
getPageNumber: (element) => parseInt(element.getAttribute("data-page-number") ?? "0"),
|
|
274
480
|
onPageChange: handlePageChange,
|
|
275
|
-
onVisibilityUpdate: handleVisibilityUpdate
|
|
481
|
+
onVisibilityUpdate: handleVisibilityUpdate,
|
|
482
|
+
observeKey: `${numPages}:${containerWidth > 0}`
|
|
276
483
|
});
|
|
484
|
+
useScrollAnchor(containerRef, `${numPages}:${containerWidth > 0}`);
|
|
485
|
+
usePinchZoom({ containerRef, zoom, onZoomChange });
|
|
486
|
+
const [renderZoom, setRenderZoom] = react.useState(zoom);
|
|
487
|
+
react.useEffect(() => {
|
|
488
|
+
if (zoom === renderZoom) return;
|
|
489
|
+
const timer = setTimeout(() => setRenderZoom(zoom), RERENDER_DEBOUNCE_MS);
|
|
490
|
+
return () => clearTimeout(timer);
|
|
491
|
+
}, [zoom, renderZoom]);
|
|
277
492
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
278
493
|
"div",
|
|
279
494
|
{
|
|
280
495
|
ref: containerRef,
|
|
281
|
-
className: "w-full h-full overflow-
|
|
496
|
+
className: "w-full h-full overflow-auto bg-muted flex flex-col items-start p-4 pb-20",
|
|
497
|
+
style: { touchAction: "pan-x pan-y" },
|
|
282
498
|
role: "region",
|
|
283
499
|
"aria-label": "PDF document viewer",
|
|
284
500
|
"data-testid": "pdf-viewer",
|
|
285
501
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
286
502
|
reactPdf.Document,
|
|
287
503
|
{
|
|
504
|
+
className: "flex w-max min-w-full flex-col",
|
|
288
505
|
file,
|
|
289
506
|
onLoadSuccess: onDocumentLoadSuccess,
|
|
290
507
|
onLoadError: onDocumentLoadError,
|
|
@@ -301,7 +518,11 @@ function PdfViewer({
|
|
|
301
518
|
children: containerWidth > 0 && Array.from(new Array(numPages), (_, index) => {
|
|
302
519
|
const pageNumber = index + 1;
|
|
303
520
|
const shouldRender = renderedPages.has(pageNumber);
|
|
304
|
-
const
|
|
521
|
+
const ratio = pageRatios.get(pageNumber) ?? defaultRatio;
|
|
522
|
+
const pageWidth = containerWidth * zoom;
|
|
523
|
+
const pageHeight = pageWidth * ratio;
|
|
524
|
+
const renderWidth = containerWidth * renderZoom;
|
|
525
|
+
const scale = zoom / renderZoom;
|
|
305
526
|
const shouldRenderTextLayer = visiblePages.has(pageNumber);
|
|
306
527
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
307
528
|
"div",
|
|
@@ -314,28 +535,45 @@ function PdfViewer({
|
|
|
314
535
|
}
|
|
315
536
|
},
|
|
316
537
|
"data-page-number": pageNumber,
|
|
317
|
-
className: "mb-6 shadow-xl bg-white border border-border",
|
|
318
|
-
style:
|
|
538
|
+
className: "mx-auto mb-6 shrink-0 shadow-xl bg-white border border-border",
|
|
539
|
+
style: { width: pageWidth + PAGE_BORDER_PX, height: pageHeight + PAGE_BORDER_PX },
|
|
319
540
|
role: "article",
|
|
320
541
|
"aria-label": `Page ${pageNumber} of ${numPages}`,
|
|
321
542
|
children: shouldRender ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
322
|
-
|
|
543
|
+
"div",
|
|
323
544
|
{
|
|
324
|
-
pageNumber
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
devicePixelRatio: Math.min(window.devicePixelRatio || 1, 2),
|
|
329
|
-
loading: /* @__PURE__ */ jsxRuntime.jsx(
|
|
330
|
-
"div",
|
|
545
|
+
"data-testid": `pdf-page-scaler-${pageNumber}`,
|
|
546
|
+
style: scale === 1 ? void 0 : { transform: `scale(${scale})`, transformOrigin: "0 0" },
|
|
547
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
548
|
+
reactPdf.Page,
|
|
331
549
|
{
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
550
|
+
pageNumber,
|
|
551
|
+
width: renderWidth,
|
|
552
|
+
renderTextLayer: shouldRenderTextLayer,
|
|
553
|
+
renderAnnotationLayer: false,
|
|
554
|
+
onLoadSuccess: (page) => {
|
|
555
|
+
const ratio2 = page.originalHeight / page.originalWidth;
|
|
556
|
+
setPageRatios(
|
|
557
|
+
(prev) => Math.abs((prev.get(pageNumber) ?? defaultRatio) - ratio2) < 1e-3 ? prev : new Map(prev).set(pageNumber, ratio2)
|
|
558
|
+
);
|
|
559
|
+
},
|
|
560
|
+
devicePixelRatio: renderDpr(
|
|
561
|
+
renderWidth,
|
|
562
|
+
renderWidth * ratio,
|
|
563
|
+
window.devicePixelRatio || 1
|
|
564
|
+
),
|
|
565
|
+
loading: /* @__PURE__ */ jsxRuntime.jsx(
|
|
566
|
+
"div",
|
|
567
|
+
{
|
|
568
|
+
className: "flex items-center justify-center bg-muted",
|
|
569
|
+
style: { width: renderWidth, height: renderWidth * ratio },
|
|
570
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-muted-foreground", children: [
|
|
571
|
+
"Loading page ",
|
|
572
|
+
pageNumber,
|
|
573
|
+
"..."
|
|
574
|
+
] })
|
|
575
|
+
}
|
|
576
|
+
)
|
|
339
577
|
}
|
|
340
578
|
)
|
|
341
579
|
}
|
|
@@ -343,7 +581,7 @@ function PdfViewer({
|
|
|
343
581
|
"div",
|
|
344
582
|
{
|
|
345
583
|
className: "flex items-center justify-center bg-muted",
|
|
346
|
-
style: { width:
|
|
584
|
+
style: { width: pageWidth, height: pageHeight },
|
|
347
585
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-muted-foreground", children: [
|
|
348
586
|
"Page ",
|
|
349
587
|
pageNumber
|
|
@@ -665,9 +903,22 @@ exports.DocxViewer = DocxViewer;
|
|
|
665
903
|
exports.INITIAL_LAZY_LOAD_BUFFER = INITIAL_LAZY_LOAD_BUFFER;
|
|
666
904
|
exports.LAZY_LOAD_BUFFER_AFTER = LAZY_LOAD_BUFFER_AFTER;
|
|
667
905
|
exports.LAZY_LOAD_BUFFER_BEFORE = LAZY_LOAD_BUFFER_BEFORE;
|
|
906
|
+
exports.MAX_ZOOM = MAX_ZOOM;
|
|
907
|
+
exports.MIN_ZOOM = MIN_ZOOM;
|
|
908
|
+
exports.PAGE_SELECTOR = PAGE_SELECTOR;
|
|
668
909
|
exports.PdfViewer = PdfViewer;
|
|
669
910
|
exports.XlsxViewer = XlsxViewer;
|
|
911
|
+
exports.anchorDelta = anchorDelta;
|
|
912
|
+
exports.anchoredScroll = anchoredScroll;
|
|
913
|
+
exports.clampZoom = clampZoom;
|
|
914
|
+
exports.cycleZoom = cycleZoom;
|
|
915
|
+
exports.doubleTapZoom = doubleTapZoom;
|
|
670
916
|
exports.nativeViewerKind = nativeViewerKind;
|
|
917
|
+
exports.pinchZoom = pinchZoom;
|
|
918
|
+
exports.renderDpr = renderDpr;
|
|
671
919
|
exports.usePageIntersectionObserver = usePageIntersectionObserver;
|
|
920
|
+
exports.usePinchZoom = usePinchZoom;
|
|
921
|
+
exports.useScrollAnchor = useScrollAnchor;
|
|
922
|
+
exports.zoomLabel = zoomLabel;
|
|
672
923
|
//# sourceMappingURL=index.cjs.map
|
|
673
924
|
//# sourceMappingURL=index.cjs.map
|