@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.
@@ -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 DEFAULT_MAX_PAGE_IMAGE_ZOOM = 6;
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
- maxPageImageZoom = DEFAULT_MAX_PAGE_IMAGE_ZOOM,
20
- zoom
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, maxPageImageZoom);
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
- return /*#__PURE__*/React.createElement("div", {
99
- "data-testid": "document-viewer",
100
- style: style,
101
- onTouchStart: onTouchStart,
102
- onTouchMove: onTouchMove,
103
- onTouchEnd: onTouchEnd,
104
- className: ax(["_zulputpp _1reo1wug _18m91wug _19pkxncg _1ul91osq _1e0c1txw _2lx21bp4 _1bsb1ris"])
105
- }, [...Array((_documentMetadata$pag = documentMetadata.pageCount) !== null && _documentMetadata$pag !== void 0 ? _documentMetadata$pag : 4)].map((_, i) => {
106
- const {
107
- page,
108
- fonts
109
- } = getPageContent(i);
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: () => 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
  };
@@ -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", !fg('media-document-viewer-clear-render') && "_1kemd8h4"])
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 = getImageZoom(zoom, maxPageImageZoom);
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
- import { fg } from '@atlaskit/platform-feature-flags';
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 imageZoom = getImageZoom(zoom, maxPageImageZoom);
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, imageZoom);
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 DEFAULT_MAX_PAGE_IMAGE_ZOOM = 6;
15
- export var DocumentViewer = function DocumentViewer(_ref) {
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 onSuccess = _ref.onSuccess,
18
- getContent = _ref.getContent,
19
- getPageImageUrl = _ref.getPageImageUrl,
20
- _ref$paginationSize = _ref.paginationSize,
21
- paginationSize = _ref$paginationSize === void 0 ? DEFAULT_PAGINATION_SIZE : _ref$paginationSize,
22
- _ref$maxPageImageZoom = _ref.maxPageImageZoom,
23
- maxPageImageZoom = _ref$maxPageImageZoom === void 0 ? DEFAULT_MAX_PAGE_IMAGE_ZOOM : _ref$maxPageImageZoom,
24
- zoom = _ref.zoom;
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, maxPageImageZoom);
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
- return /*#__PURE__*/React.createElement("div", {
100
- "data-testid": "document-viewer",
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
- className: ax(["_zulputpp _1reo1wug _18m91wug _19pkxncg _1ul91osq _1e0c1txw _2lx21bp4 _1bsb1ris"])
106
- }, _toConsumableArray(Array((_documentMetadata$pag = documentMetadata.pageCount) !== null && _documentMetadata$pag !== void 0 ? _documentMetadata$pag : 4)).map(function (_, i) {
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
  };