@atlaskit/media-document-viewer 0.6.10 → 0.7.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/CHANGELOG.md +20 -0
- package/dist/cjs/documentLinks.js +9 -3
- package/dist/cjs/documentViewer.js +179 -34
- package/dist/cjs/page.js +11 -10
- package/dist/cjs/utils/useCachedGetImage.js +14 -34
- package/dist/es2019/documentLinks.js +7 -3
- package/dist/es2019/documentViewer.js +169 -31
- package/dist/es2019/page.js +11 -10
- package/dist/es2019/utils/useCachedGetImage.js +4 -23
- package/dist/esm/documentLinks.js +9 -3
- package/dist/esm/documentViewer.js +180 -35
- package/dist/esm/page.js +11 -10
- package/dist/esm/utils/useCachedGetImage.js +14 -33
- package/dist/types/documentLinks.d.ts +2 -1
- package/dist/types/documentViewer.d.ts +7 -1
- package/dist/types/page.d.ts +9 -3
- package/dist/types/utils/useCachedGetImage.d.ts +1 -11
- package/dist/types-ts4.5/documentLinks.d.ts +2 -1
- package/dist/types-ts4.5/documentViewer.d.ts +7 -1
- package/dist/types-ts4.5/page.d.ts +9 -3
- package/dist/types-ts4.5/utils/useCachedGetImage.d.ts +1 -11
- package/package.json +4 -10
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import "./documentViewer.compiled.css";
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { ax, ix } from "@compiled/react/runtime";
|
|
5
|
-
import { useCallback, useLayoutEffect, useRef } from 'react';
|
|
5
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
6
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
6
7
|
import { useStaticCallback } from '@atlaskit/media-common';
|
|
7
8
|
import { Page } from './page';
|
|
8
9
|
import { usePageContent } from './usePageContent';
|
|
@@ -10,22 +11,171 @@ import { getScrollElement } from './utils/getDocumentRoot';
|
|
|
10
11
|
import { useCachedGetImage } from './utils/useCachedGetImage';
|
|
11
12
|
const documentViewerStyles = null;
|
|
12
13
|
const DEFAULT_PAGINATION_SIZE = 50;
|
|
13
|
-
const
|
|
14
|
+
const INITIAL_PAGE_COUNT = 50;
|
|
15
|
+
const APPEND_STEP = 50;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* TAIL-APPEND PATTERN FOR LAZY LOADING:
|
|
19
|
+
*
|
|
20
|
+
* The <Page> component has an `onVisible: () => void` prop that is called when the page
|
|
21
|
+
* enters the viewport (via useIntersectionObserver with a 300% rootMargin). This callback
|
|
22
|
+
* is currently used to load page content via `loadPageContent(i)`.
|
|
23
|
+
*
|
|
24
|
+
* For the LazyAppendPages feature (T2.3), the parent component can leverage this same
|
|
25
|
+
* mechanism by:
|
|
26
|
+
* 1. Rendering only a subset of pages (lazyPageCount instead of all documentMetadata.pageCount)
|
|
27
|
+
* 2. Passing onVisible={handleAppend} ONLY to the last rendered page (i === lazyPageCount - 1)
|
|
28
|
+
* 3. For all other pages, passing onVisible={() => loadPageContent(i)} as usual
|
|
29
|
+
*
|
|
30
|
+
* When the last page enters the viewport, handleAppend will be triggered, which can:
|
|
31
|
+
* - Fetch the next batch of pages
|
|
32
|
+
* - Append them to the rendered page list
|
|
33
|
+
* - This effectively creates a tail-append/infinite-scroll behavior without any changes to <Page>
|
|
34
|
+
*
|
|
35
|
+
* No changes to the <Page> signature are required. The onVisible callback is already
|
|
36
|
+
* properly wired and can be overridden by parent components for different behaviors.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
const LegacyPages = ({
|
|
40
|
+
documentMetadata,
|
|
41
|
+
getPageContent,
|
|
42
|
+
loadPageContent,
|
|
43
|
+
getImageUrl,
|
|
44
|
+
zoom,
|
|
45
|
+
onSuccess,
|
|
46
|
+
style,
|
|
47
|
+
onTouchStart,
|
|
48
|
+
onTouchMove,
|
|
49
|
+
onTouchEnd
|
|
50
|
+
}) => {
|
|
51
|
+
var _documentMetadata$pag;
|
|
52
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
53
|
+
"data-testid": "document-viewer",
|
|
54
|
+
style: style,
|
|
55
|
+
onTouchStart: onTouchStart,
|
|
56
|
+
onTouchMove: onTouchMove,
|
|
57
|
+
onTouchEnd: onTouchEnd,
|
|
58
|
+
className: ax(["_zulputpp _1reo1wug _18m91wug _19pkxncg _1ul91osq _1e0c1txw _2lx21bp4 _1bsb1ris"])
|
|
59
|
+
}, [...Array((_documentMetadata$pag = documentMetadata.pageCount) !== null && _documentMetadata$pag !== void 0 ? _documentMetadata$pag : 4)].map((_, i) => {
|
|
60
|
+
const {
|
|
61
|
+
page,
|
|
62
|
+
fonts
|
|
63
|
+
} = getPageContent(i);
|
|
64
|
+
return /*#__PURE__*/React.createElement(Page, {
|
|
65
|
+
key: i,
|
|
66
|
+
getPageSrc: getImageUrl,
|
|
67
|
+
pageIndex: i,
|
|
68
|
+
zoom: zoom,
|
|
69
|
+
defaultDimensions: documentMetadata.defaultDimensions,
|
|
70
|
+
onVisible: loadPageContent,
|
|
71
|
+
onLoad: i === 0 ? onSuccess : undefined,
|
|
72
|
+
fonts: fonts,
|
|
73
|
+
content: page
|
|
74
|
+
});
|
|
75
|
+
}));
|
|
76
|
+
};
|
|
77
|
+
const LazyAppendPages = ({
|
|
78
|
+
documentMetadata,
|
|
79
|
+
getPageContent,
|
|
80
|
+
loadPageContent,
|
|
81
|
+
getImageUrl,
|
|
82
|
+
zoom,
|
|
83
|
+
onSuccess,
|
|
84
|
+
style,
|
|
85
|
+
onTouchStart,
|
|
86
|
+
onTouchMove,
|
|
87
|
+
onTouchEnd
|
|
88
|
+
}) => {
|
|
89
|
+
var _documentMetadata$pag2;
|
|
90
|
+
const totalPages = (_documentMetadata$pag2 = documentMetadata.pageCount) !== null && _documentMetadata$pag2 !== void 0 ? _documentMetadata$pag2 : 4;
|
|
91
|
+
const [lazyPageCount, setLazyPageCount] = useState(() => Math.min(INITIAL_PAGE_COUNT, totalPages));
|
|
92
|
+
|
|
93
|
+
// lastAppendTriggerIndex deduplicates: only append when a strictly-larger
|
|
94
|
+
// tail page fires onVisible. This prevents double-append on scroll-up-then-down.
|
|
95
|
+
const lastAppendTriggerIndex = useRef(-1);
|
|
96
|
+
|
|
97
|
+
// Handle #page-N URL hash on first load: fast-forward lazyPageCount to include the target page.
|
|
98
|
+
// Capture totalPages in a ref so the one-shot effect can read it without being re-run when
|
|
99
|
+
// totalPages changes (the effect must only fire once, on mount).
|
|
100
|
+
const totalPagesRef = useRef(totalPages);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
const total = totalPagesRef.current;
|
|
103
|
+
const hash = window.location.hash;
|
|
104
|
+
const match = hash.match(/^#page-(\d+)$/);
|
|
105
|
+
if (match) {
|
|
106
|
+
const targetPage = parseInt(match[1], 10);
|
|
107
|
+
if (targetPage >= 1 && targetPage <= total) {
|
|
108
|
+
// Update dedup ref BEFORE setLazyPageCount so handleAppend does not
|
|
109
|
+
// cascade when the new tail page enters the viewport via IntersectionObserver.
|
|
110
|
+
lastAppendTriggerIndex.current = targetPage - 1;
|
|
111
|
+
// Fast-forward lazyPageCount to include the target page
|
|
112
|
+
setLazyPageCount(prev => Math.max(prev, targetPage));
|
|
113
|
+
// After the pages are mounted (next animation frame), scroll to the target
|
|
114
|
+
requestAnimationFrame(() => {
|
|
115
|
+
var _getDocument;
|
|
116
|
+
const el = (_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.getElementById(`page-${targetPage}`);
|
|
117
|
+
if (el) {
|
|
118
|
+
el.scrollIntoView();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps -- intentionally one-shot; totalPages read via ref
|
|
124
|
+
|
|
125
|
+
const handleAppendAndLoadPageContent = useCallback(pageIndex => {
|
|
126
|
+
if (pageIndex > lastAppendTriggerIndex.current) {
|
|
127
|
+
lastAppendTriggerIndex.current = pageIndex;
|
|
128
|
+
setLazyPageCount(c => Math.min(c + APPEND_STEP, totalPages));
|
|
129
|
+
}
|
|
130
|
+
loadPageContent(pageIndex);
|
|
131
|
+
}, [totalPages, loadPageContent]);
|
|
132
|
+
const appendUpTo = useCallback(targetPage => {
|
|
133
|
+
const clamped = Math.min(Math.max(targetPage, 0), totalPages);
|
|
134
|
+
if (clamped > lastAppendTriggerIndex.current) {
|
|
135
|
+
lastAppendTriggerIndex.current = clamped - 1;
|
|
136
|
+
setLazyPageCount(prev => Math.max(prev, clamped));
|
|
137
|
+
}
|
|
138
|
+
}, [totalPages]);
|
|
139
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
140
|
+
"data-testid": "document-viewer",
|
|
141
|
+
style: style,
|
|
142
|
+
onTouchStart: onTouchStart,
|
|
143
|
+
onTouchMove: onTouchMove,
|
|
144
|
+
onTouchEnd: onTouchEnd,
|
|
145
|
+
className: ax(["_zulputpp _1reo1wug _18m91wug _19pkxncg _1ul91osq _1e0c1txw _2lx21bp4 _1bsb1ris"])
|
|
146
|
+
}, [...Array(lazyPageCount)].map((_, i) => {
|
|
147
|
+
const {
|
|
148
|
+
page,
|
|
149
|
+
fonts
|
|
150
|
+
} = getPageContent(i);
|
|
151
|
+
return /*#__PURE__*/React.createElement(Page, {
|
|
152
|
+
key: i,
|
|
153
|
+
getPageSrc: getImageUrl,
|
|
154
|
+
pageIndex: i,
|
|
155
|
+
zoom: zoom,
|
|
156
|
+
defaultDimensions: documentMetadata.defaultDimensions,
|
|
157
|
+
onVisible: i === lazyPageCount - 1 ? handleAppendAndLoadPageContent : loadPageContent,
|
|
158
|
+
onLoad: i === 0 ? onSuccess : undefined,
|
|
159
|
+
fonts: fonts,
|
|
160
|
+
content: page,
|
|
161
|
+
appendUpTo: appendUpTo
|
|
162
|
+
});
|
|
163
|
+
}));
|
|
164
|
+
};
|
|
14
165
|
export const DocumentViewer = ({
|
|
15
166
|
onSuccess,
|
|
16
167
|
getContent,
|
|
17
168
|
getPageImageUrl,
|
|
18
169
|
paginationSize = DEFAULT_PAGINATION_SIZE,
|
|
19
|
-
|
|
20
|
-
|
|
170
|
+
zoom,
|
|
171
|
+
enableLazyPageRendering = false
|
|
21
172
|
}) => {
|
|
22
|
-
var _documentMetadata$pag;
|
|
23
173
|
const {
|
|
24
174
|
getPageContent,
|
|
25
175
|
loadPageContent,
|
|
26
176
|
documentMetadata
|
|
27
177
|
} = usePageContent(getContent, paginationSize);
|
|
28
|
-
const getImageUrl = useCachedGetImage(getPageImageUrl
|
|
178
|
+
const getImageUrl = useCachedGetImage(getPageImageUrl);
|
|
29
179
|
const style = {
|
|
30
180
|
'--document-viewer-zoom': zoom
|
|
31
181
|
};
|
|
@@ -95,29 +245,17 @@ export const DocumentViewer = ({
|
|
|
95
245
|
const onTouchEnd = useCallback(() => {
|
|
96
246
|
touchStartRef.current = null;
|
|
97
247
|
}, []);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
key: i,
|
|
112
|
-
getPageSrc: getImageUrl,
|
|
113
|
-
maxPageImageZoom: maxPageImageZoom,
|
|
114
|
-
pageIndex: i,
|
|
115
|
-
zoom: zoom,
|
|
116
|
-
defaultDimensions: documentMetadata.defaultDimensions,
|
|
117
|
-
onVisible: () => loadPageContent(i),
|
|
118
|
-
onLoad: i === 0 ? onSuccess : undefined,
|
|
119
|
-
fonts: fonts,
|
|
120
|
-
content: page
|
|
121
|
-
});
|
|
122
|
-
}));
|
|
248
|
+
const sharedProps = {
|
|
249
|
+
documentMetadata,
|
|
250
|
+
getPageContent,
|
|
251
|
+
loadPageContent,
|
|
252
|
+
getImageUrl,
|
|
253
|
+
zoom,
|
|
254
|
+
onSuccess,
|
|
255
|
+
style,
|
|
256
|
+
onTouchStart,
|
|
257
|
+
onTouchMove,
|
|
258
|
+
onTouchEnd
|
|
259
|
+
};
|
|
260
|
+
return enableLazyPageRendering ? /*#__PURE__*/React.createElement(LazyAppendPages, sharedProps) : /*#__PURE__*/React.createElement(LegacyPages, sharedProps);
|
|
123
261
|
};
|
package/dist/es2019/page.js
CHANGED
|
@@ -4,12 +4,10 @@ import * as React from 'react';
|
|
|
4
4
|
import { ax, ix } from "@compiled/react/runtime";
|
|
5
5
|
import { forwardRef, useEffect, useState } from 'react';
|
|
6
6
|
import { useStaticCallback } from '@atlaskit/media-common';
|
|
7
|
-
import { fg } from '@atlaskit/platform-feature-flags';
|
|
8
7
|
import Spinner from '@atlaskit/spinner';
|
|
9
8
|
import { Annotations } from './annotations';
|
|
10
9
|
import { DocumentLinks } from './documentLinks';
|
|
11
10
|
import { getDocumentRoot } from './utils/getDocumentRoot';
|
|
12
|
-
import { getImageZoom } from './utils/useCachedGetImage';
|
|
13
11
|
import { useIntersectionObserver } from './utils/useIntersectionObserver';
|
|
14
12
|
const Span = ({
|
|
15
13
|
span,
|
|
@@ -72,7 +70,8 @@ const PageView = /*#__PURE__*/forwardRef(({
|
|
|
72
70
|
fonts,
|
|
73
71
|
pageIndex,
|
|
74
72
|
zoom,
|
|
75
|
-
onImageLoad
|
|
73
|
+
onImageLoad,
|
|
74
|
+
appendUpTo
|
|
76
75
|
}, ref) => {
|
|
77
76
|
const style = {};
|
|
78
77
|
if (dimensions) {
|
|
@@ -105,7 +104,7 @@ const PageView = /*#__PURE__*/forwardRef(({
|
|
|
105
104
|
src: imageSrc,
|
|
106
105
|
alt: "",
|
|
107
106
|
onLoad: onImageLoad,
|
|
108
|
-
className: ax(["_kqswstnw _154iidpf _1ltvidpf _uiztglyw",
|
|
107
|
+
className: ax(["_kqswstnw _154iidpf _1ltvidpf _uiztglyw", "_1kemd8h4"])
|
|
109
108
|
}), content && /*#__PURE__*/React.createElement("svg", {
|
|
110
109
|
"data-testid": `page-${pageIndex}-text-layer`,
|
|
111
110
|
style: style,
|
|
@@ -120,19 +119,20 @@ const PageView = /*#__PURE__*/forwardRef(({
|
|
|
120
119
|
})), content.annotations && /*#__PURE__*/React.createElement(Annotations, {
|
|
121
120
|
annotations: content.annotations
|
|
122
121
|
}), content.links && /*#__PURE__*/React.createElement(DocumentLinks, {
|
|
123
|
-
links: content.links
|
|
122
|
+
links: content.links,
|
|
123
|
+
appendUpTo: appendUpTo
|
|
124
124
|
})));
|
|
125
125
|
});
|
|
126
126
|
export const Page = ({
|
|
127
127
|
getPageSrc,
|
|
128
|
-
maxPageImageZoom,
|
|
129
128
|
content,
|
|
130
129
|
fonts,
|
|
131
130
|
pageIndex,
|
|
132
131
|
zoom,
|
|
133
132
|
defaultDimensions,
|
|
134
133
|
onVisible,
|
|
135
|
-
onLoad
|
|
134
|
+
onLoad,
|
|
135
|
+
appendUpTo
|
|
136
136
|
}) => {
|
|
137
137
|
var _ref;
|
|
138
138
|
const [imageSrc, setImageSrc] = useState();
|
|
@@ -146,7 +146,7 @@ export const Page = ({
|
|
|
146
146
|
rootMargin: '300%',
|
|
147
147
|
threshold: 0.1
|
|
148
148
|
}, () => {
|
|
149
|
-
onVisible();
|
|
149
|
+
onVisible(pageIndex);
|
|
150
150
|
getPageSrc(pageIndex, zoom).then(setImageSrc);
|
|
151
151
|
});
|
|
152
152
|
const [dimensions, setDimensions] = useState();
|
|
@@ -155,7 +155,7 @@ export const Page = ({
|
|
|
155
155
|
onLoad === null || onLoad === void 0 ? void 0 : onLoad();
|
|
156
156
|
if (!content) {
|
|
157
157
|
const zoom = image.dataset.zoom ? Number(image.dataset.zoom) : 1;
|
|
158
|
-
const imageZoom =
|
|
158
|
+
const imageZoom = zoom;
|
|
159
159
|
const contentWidth = image.naturalWidth / imageZoom;
|
|
160
160
|
const contentHeight = image.naturalHeight / imageZoom;
|
|
161
161
|
setDimensions({
|
|
@@ -177,6 +177,7 @@ export const Page = ({
|
|
|
177
177
|
fonts: fonts,
|
|
178
178
|
pageIndex: pageIndex,
|
|
179
179
|
zoom: zoom,
|
|
180
|
-
onImageLoad: onImageLoad
|
|
180
|
+
onImageLoad: onImageLoad,
|
|
181
|
+
appendUpTo: appendUpTo
|
|
181
182
|
});
|
|
182
183
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
2
|
import { useStaticCallback } from '@atlaskit/media-common';
|
|
3
|
-
|
|
4
|
-
export const useCachedGetImage = (getPageImageUrl, maxPageImageZoom) => {
|
|
3
|
+
export const useCachedGetImage = getPageImageUrl => {
|
|
5
4
|
const imageUrlRefs = useRef({});
|
|
6
5
|
useEffect(() => {
|
|
7
6
|
return () => {
|
|
@@ -12,12 +11,11 @@ export const useCachedGetImage = (getPageImageUrl, maxPageImageZoom) => {
|
|
|
12
11
|
};
|
|
13
12
|
}, []);
|
|
14
13
|
const getImageUrl = useStaticCallback(async (pageNumber, zoom) => {
|
|
15
|
-
const
|
|
16
|
-
const key = `${pageNumber}-${imageZoom}`;
|
|
14
|
+
const key = `${pageNumber}-${zoom}`;
|
|
17
15
|
if (imageUrlRefs.current[key]) {
|
|
18
16
|
return imageUrlRefs.current[key];
|
|
19
17
|
}
|
|
20
|
-
let url = await getPageImageUrl(pageNumber,
|
|
18
|
+
let url = await getPageImageUrl(pageNumber, zoom);
|
|
21
19
|
const isBlobUrl = url.startsWith('blob:');
|
|
22
20
|
if (!isBlobUrl) {
|
|
23
21
|
const blob = await fetch(url).then(res => res.blob());
|
|
@@ -27,21 +25,4 @@ export const useCachedGetImage = (getPageImageUrl, maxPageImageZoom) => {
|
|
|
27
25
|
return url;
|
|
28
26
|
});
|
|
29
27
|
return getImageUrl;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The actual zoom that will be requested from the image service.
|
|
34
|
-
*
|
|
35
|
-
* This is different from the 'zoom' prop because it is adjusted for the device pixel ratio.
|
|
36
|
-
* We're achieving the same result as a srcset, crisp images for high DPI screens.
|
|
37
|
-
*
|
|
38
|
-
* We also reduce impact of excessively slow rendering by setting a max zoom level.
|
|
39
|
-
* If the user is zoomed past this level, the image will just be scaled-up client side.
|
|
40
|
-
*/
|
|
41
|
-
export function getImageZoom(zoom, maxPageImageZoom) {
|
|
42
|
-
if (!fg('media-document-viewer-clear-render')) {
|
|
43
|
-
return zoom;
|
|
44
|
-
}
|
|
45
|
-
const dpiAdjustedZoom = zoom * (window.devicePixelRatio || 1);
|
|
46
|
-
return Math.min(dpiAdjustedZoom, maxPageImageZoom);
|
|
47
|
-
}
|
|
28
|
+
};
|
|
@@ -12,7 +12,8 @@ var anchorStyles = {
|
|
|
12
12
|
};
|
|
13
13
|
var DocumentLink = function DocumentLink(_ref) {
|
|
14
14
|
var link = _ref.link,
|
|
15
|
-
dataTestId = _ref.dataTestId
|
|
15
|
+
dataTestId = _ref.dataTestId,
|
|
16
|
+
appendUpTo = _ref.appendUpTo;
|
|
16
17
|
return /*#__PURE__*/React.createElement("foreignObject", {
|
|
17
18
|
x: link.x,
|
|
18
19
|
y: -link.y,
|
|
@@ -26,15 +27,20 @@ var DocumentLink = function DocumentLink(_ref) {
|
|
|
26
27
|
style: anchorStyles
|
|
27
28
|
})) : /*#__PURE__*/React.createElement(Anchor, _extends({}, foreignObjectProps, {
|
|
28
29
|
href: "#page-".concat(link.p_num + 1),
|
|
29
|
-
style: anchorStyles
|
|
30
|
+
style: anchorStyles,
|
|
31
|
+
onClick: appendUpTo ? function () {
|
|
32
|
+
return appendUpTo(link.p_num + 1);
|
|
33
|
+
} : undefined
|
|
30
34
|
})));
|
|
31
35
|
};
|
|
32
36
|
export var DocumentLinks = function DocumentLinks(_ref2) {
|
|
33
|
-
var links = _ref2.links
|
|
37
|
+
var links = _ref2.links,
|
|
38
|
+
appendUpTo = _ref2.appendUpTo;
|
|
34
39
|
return /*#__PURE__*/React.createElement(React.Fragment, null, links.map(function (link, i) {
|
|
35
40
|
return /*#__PURE__*/React.createElement(DocumentLink, {
|
|
36
41
|
link: link,
|
|
37
42
|
dataTestId: "document-link-".concat(i),
|
|
43
|
+
appendUpTo: appendUpTo,
|
|
38
44
|
key: i
|
|
39
45
|
});
|
|
40
46
|
}));
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/* documentViewer.tsx generated by @compiled/babel-plugin v0.39.1 */
|
|
2
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
3
|
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
3
4
|
import "./documentViewer.compiled.css";
|
|
4
5
|
import * as React from 'react';
|
|
5
6
|
import { ax, ix } from "@compiled/react/runtime";
|
|
6
|
-
import { useCallback, useLayoutEffect, useRef } from 'react';
|
|
7
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
8
|
+
import { getDocument } from '@atlaskit/browser-apis';
|
|
7
9
|
import { useStaticCallback } from '@atlaskit/media-common';
|
|
8
10
|
import { Page } from './page';
|
|
9
11
|
import { usePageContent } from './usePageContent';
|
|
@@ -11,22 +13,178 @@ import { getScrollElement } from './utils/getDocumentRoot';
|
|
|
11
13
|
import { useCachedGetImage } from './utils/useCachedGetImage';
|
|
12
14
|
var documentViewerStyles = null;
|
|
13
15
|
var DEFAULT_PAGINATION_SIZE = 50;
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
+
var INITIAL_PAGE_COUNT = 50;
|
|
17
|
+
var APPEND_STEP = 50;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* TAIL-APPEND PATTERN FOR LAZY LOADING:
|
|
21
|
+
*
|
|
22
|
+
* The <Page> component has an `onVisible: () => void` prop that is called when the page
|
|
23
|
+
* enters the viewport (via useIntersectionObserver with a 300% rootMargin). This callback
|
|
24
|
+
* is currently used to load page content via `loadPageContent(i)`.
|
|
25
|
+
*
|
|
26
|
+
* For the LazyAppendPages feature (T2.3), the parent component can leverage this same
|
|
27
|
+
* mechanism by:
|
|
28
|
+
* 1. Rendering only a subset of pages (lazyPageCount instead of all documentMetadata.pageCount)
|
|
29
|
+
* 2. Passing onVisible={handleAppend} ONLY to the last rendered page (i === lazyPageCount - 1)
|
|
30
|
+
* 3. For all other pages, passing onVisible={() => loadPageContent(i)} as usual
|
|
31
|
+
*
|
|
32
|
+
* When the last page enters the viewport, handleAppend will be triggered, which can:
|
|
33
|
+
* - Fetch the next batch of pages
|
|
34
|
+
* - Append them to the rendered page list
|
|
35
|
+
* - This effectively creates a tail-append/infinite-scroll behavior without any changes to <Page>
|
|
36
|
+
*
|
|
37
|
+
* No changes to the <Page> signature are required. The onVisible callback is already
|
|
38
|
+
* properly wired and can be overridden by parent components for different behaviors.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
var LegacyPages = function LegacyPages(_ref) {
|
|
16
42
|
var _documentMetadata$pag;
|
|
17
|
-
var
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
var documentMetadata = _ref.documentMetadata,
|
|
44
|
+
getPageContent = _ref.getPageContent,
|
|
45
|
+
loadPageContent = _ref.loadPageContent,
|
|
46
|
+
getImageUrl = _ref.getImageUrl,
|
|
47
|
+
zoom = _ref.zoom,
|
|
48
|
+
onSuccess = _ref.onSuccess,
|
|
49
|
+
style = _ref.style,
|
|
50
|
+
onTouchStart = _ref.onTouchStart,
|
|
51
|
+
onTouchMove = _ref.onTouchMove,
|
|
52
|
+
onTouchEnd = _ref.onTouchEnd;
|
|
53
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
54
|
+
"data-testid": "document-viewer",
|
|
55
|
+
style: style,
|
|
56
|
+
onTouchStart: onTouchStart,
|
|
57
|
+
onTouchMove: onTouchMove,
|
|
58
|
+
onTouchEnd: onTouchEnd,
|
|
59
|
+
className: ax(["_zulputpp _1reo1wug _18m91wug _19pkxncg _1ul91osq _1e0c1txw _2lx21bp4 _1bsb1ris"])
|
|
60
|
+
}, _toConsumableArray(Array((_documentMetadata$pag = documentMetadata.pageCount) !== null && _documentMetadata$pag !== void 0 ? _documentMetadata$pag : 4)).map(function (_, i) {
|
|
61
|
+
var _getPageContent = getPageContent(i),
|
|
62
|
+
page = _getPageContent.page,
|
|
63
|
+
fonts = _getPageContent.fonts;
|
|
64
|
+
return /*#__PURE__*/React.createElement(Page, {
|
|
65
|
+
key: i,
|
|
66
|
+
getPageSrc: getImageUrl,
|
|
67
|
+
pageIndex: i,
|
|
68
|
+
zoom: zoom,
|
|
69
|
+
defaultDimensions: documentMetadata.defaultDimensions,
|
|
70
|
+
onVisible: loadPageContent,
|
|
71
|
+
onLoad: i === 0 ? onSuccess : undefined,
|
|
72
|
+
fonts: fonts,
|
|
73
|
+
content: page
|
|
74
|
+
});
|
|
75
|
+
}));
|
|
76
|
+
};
|
|
77
|
+
var LazyAppendPages = function LazyAppendPages(_ref2) {
|
|
78
|
+
var _documentMetadata$pag2;
|
|
79
|
+
var documentMetadata = _ref2.documentMetadata,
|
|
80
|
+
getPageContent = _ref2.getPageContent,
|
|
81
|
+
loadPageContent = _ref2.loadPageContent,
|
|
82
|
+
getImageUrl = _ref2.getImageUrl,
|
|
83
|
+
zoom = _ref2.zoom,
|
|
84
|
+
onSuccess = _ref2.onSuccess,
|
|
85
|
+
style = _ref2.style,
|
|
86
|
+
onTouchStart = _ref2.onTouchStart,
|
|
87
|
+
onTouchMove = _ref2.onTouchMove,
|
|
88
|
+
onTouchEnd = _ref2.onTouchEnd;
|
|
89
|
+
var totalPages = (_documentMetadata$pag2 = documentMetadata.pageCount) !== null && _documentMetadata$pag2 !== void 0 ? _documentMetadata$pag2 : 4;
|
|
90
|
+
var _useState = useState(function () {
|
|
91
|
+
return Math.min(INITIAL_PAGE_COUNT, totalPages);
|
|
92
|
+
}),
|
|
93
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
94
|
+
lazyPageCount = _useState2[0],
|
|
95
|
+
setLazyPageCount = _useState2[1];
|
|
96
|
+
|
|
97
|
+
// lastAppendTriggerIndex deduplicates: only append when a strictly-larger
|
|
98
|
+
// tail page fires onVisible. This prevents double-append on scroll-up-then-down.
|
|
99
|
+
var lastAppendTriggerIndex = useRef(-1);
|
|
100
|
+
|
|
101
|
+
// Handle #page-N URL hash on first load: fast-forward lazyPageCount to include the target page.
|
|
102
|
+
// Capture totalPages in a ref so the one-shot effect can read it without being re-run when
|
|
103
|
+
// totalPages changes (the effect must only fire once, on mount).
|
|
104
|
+
var totalPagesRef = useRef(totalPages);
|
|
105
|
+
useEffect(function () {
|
|
106
|
+
var total = totalPagesRef.current;
|
|
107
|
+
var hash = window.location.hash;
|
|
108
|
+
var match = hash.match(/^#page-(\d+)$/);
|
|
109
|
+
if (match) {
|
|
110
|
+
var targetPage = parseInt(match[1], 10);
|
|
111
|
+
if (targetPage >= 1 && targetPage <= total) {
|
|
112
|
+
// Update dedup ref BEFORE setLazyPageCount so handleAppend does not
|
|
113
|
+
// cascade when the new tail page enters the viewport via IntersectionObserver.
|
|
114
|
+
lastAppendTriggerIndex.current = targetPage - 1;
|
|
115
|
+
// Fast-forward lazyPageCount to include the target page
|
|
116
|
+
setLazyPageCount(function (prev) {
|
|
117
|
+
return Math.max(prev, targetPage);
|
|
118
|
+
});
|
|
119
|
+
// After the pages are mounted (next animation frame), scroll to the target
|
|
120
|
+
requestAnimationFrame(function () {
|
|
121
|
+
var _getDocument;
|
|
122
|
+
var el = (_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.getElementById("page-".concat(targetPage));
|
|
123
|
+
if (el) {
|
|
124
|
+
el.scrollIntoView();
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps -- intentionally one-shot; totalPages read via ref
|
|
130
|
+
|
|
131
|
+
var handleAppendAndLoadPageContent = useCallback(function (pageIndex) {
|
|
132
|
+
if (pageIndex > lastAppendTriggerIndex.current) {
|
|
133
|
+
lastAppendTriggerIndex.current = pageIndex;
|
|
134
|
+
setLazyPageCount(function (c) {
|
|
135
|
+
return Math.min(c + APPEND_STEP, totalPages);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
loadPageContent(pageIndex);
|
|
139
|
+
}, [totalPages, loadPageContent]);
|
|
140
|
+
var appendUpTo = useCallback(function (targetPage) {
|
|
141
|
+
var clamped = Math.min(Math.max(targetPage, 0), totalPages);
|
|
142
|
+
if (clamped > lastAppendTriggerIndex.current) {
|
|
143
|
+
lastAppendTriggerIndex.current = clamped - 1;
|
|
144
|
+
setLazyPageCount(function (prev) {
|
|
145
|
+
return Math.max(prev, clamped);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}, [totalPages]);
|
|
149
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
150
|
+
"data-testid": "document-viewer",
|
|
151
|
+
style: style,
|
|
152
|
+
onTouchStart: onTouchStart,
|
|
153
|
+
onTouchMove: onTouchMove,
|
|
154
|
+
onTouchEnd: onTouchEnd,
|
|
155
|
+
className: ax(["_zulputpp _1reo1wug _18m91wug _19pkxncg _1ul91osq _1e0c1txw _2lx21bp4 _1bsb1ris"])
|
|
156
|
+
}, _toConsumableArray(Array(lazyPageCount)).map(function (_, i) {
|
|
157
|
+
var _getPageContent2 = getPageContent(i),
|
|
158
|
+
page = _getPageContent2.page,
|
|
159
|
+
fonts = _getPageContent2.fonts;
|
|
160
|
+
return /*#__PURE__*/React.createElement(Page, {
|
|
161
|
+
key: i,
|
|
162
|
+
getPageSrc: getImageUrl,
|
|
163
|
+
pageIndex: i,
|
|
164
|
+
zoom: zoom,
|
|
165
|
+
defaultDimensions: documentMetadata.defaultDimensions,
|
|
166
|
+
onVisible: i === lazyPageCount - 1 ? handleAppendAndLoadPageContent : loadPageContent,
|
|
167
|
+
onLoad: i === 0 ? onSuccess : undefined,
|
|
168
|
+
fonts: fonts,
|
|
169
|
+
content: page,
|
|
170
|
+
appendUpTo: appendUpTo
|
|
171
|
+
});
|
|
172
|
+
}));
|
|
173
|
+
};
|
|
174
|
+
export var DocumentViewer = function DocumentViewer(_ref3) {
|
|
175
|
+
var onSuccess = _ref3.onSuccess,
|
|
176
|
+
getContent = _ref3.getContent,
|
|
177
|
+
getPageImageUrl = _ref3.getPageImageUrl,
|
|
178
|
+
_ref3$paginationSize = _ref3.paginationSize,
|
|
179
|
+
paginationSize = _ref3$paginationSize === void 0 ? DEFAULT_PAGINATION_SIZE : _ref3$paginationSize,
|
|
180
|
+
zoom = _ref3.zoom,
|
|
181
|
+
_ref3$enableLazyPageR = _ref3.enableLazyPageRendering,
|
|
182
|
+
enableLazyPageRendering = _ref3$enableLazyPageR === void 0 ? false : _ref3$enableLazyPageR;
|
|
25
183
|
var _usePageContent = usePageContent(getContent, paginationSize),
|
|
26
184
|
getPageContent = _usePageContent.getPageContent,
|
|
27
185
|
loadPageContent = _usePageContent.loadPageContent,
|
|
28
186
|
documentMetadata = _usePageContent.documentMetadata;
|
|
29
|
-
var getImageUrl = useCachedGetImage(getPageImageUrl
|
|
187
|
+
var getImageUrl = useCachedGetImage(getPageImageUrl);
|
|
30
188
|
var style = {
|
|
31
189
|
'--document-viewer-zoom': zoom
|
|
32
190
|
};
|
|
@@ -96,30 +254,17 @@ export var DocumentViewer = function DocumentViewer(_ref) {
|
|
|
96
254
|
var onTouchEnd = useCallback(function () {
|
|
97
255
|
touchStartRef.current = null;
|
|
98
256
|
}, []);
|
|
99
|
-
|
|
100
|
-
|
|
257
|
+
var sharedProps = {
|
|
258
|
+
documentMetadata: documentMetadata,
|
|
259
|
+
getPageContent: getPageContent,
|
|
260
|
+
loadPageContent: loadPageContent,
|
|
261
|
+
getImageUrl: getImageUrl,
|
|
262
|
+
zoom: zoom,
|
|
263
|
+
onSuccess: onSuccess,
|
|
101
264
|
style: style,
|
|
102
265
|
onTouchStart: onTouchStart,
|
|
103
266
|
onTouchMove: onTouchMove,
|
|
104
|
-
onTouchEnd: onTouchEnd
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
var _getPageContent = getPageContent(i),
|
|
108
|
-
page = _getPageContent.page,
|
|
109
|
-
fonts = _getPageContent.fonts;
|
|
110
|
-
return /*#__PURE__*/React.createElement(Page, {
|
|
111
|
-
key: i,
|
|
112
|
-
getPageSrc: getImageUrl,
|
|
113
|
-
maxPageImageZoom: maxPageImageZoom,
|
|
114
|
-
pageIndex: i,
|
|
115
|
-
zoom: zoom,
|
|
116
|
-
defaultDimensions: documentMetadata.defaultDimensions,
|
|
117
|
-
onVisible: function onVisible() {
|
|
118
|
-
return loadPageContent(i);
|
|
119
|
-
},
|
|
120
|
-
onLoad: i === 0 ? onSuccess : undefined,
|
|
121
|
-
fonts: fonts,
|
|
122
|
-
content: page
|
|
123
|
-
});
|
|
124
|
-
}));
|
|
267
|
+
onTouchEnd: onTouchEnd
|
|
268
|
+
};
|
|
269
|
+
return enableLazyPageRendering ? /*#__PURE__*/React.createElement(LazyAppendPages, sharedProps) : /*#__PURE__*/React.createElement(LegacyPages, sharedProps);
|
|
125
270
|
};
|