@nypl/web-reader 5.0.0-alpha.0 → 5.0.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/index.mjs CHANGED
@@ -1373,7 +1373,7 @@ import * as React29 from "react";
1373
1373
  // src/constants.ts
1374
1374
  var ReadiumWebpubContext = "http://readium.org/webpub/default.jsonld";
1375
1375
  var IS_DEV = process.env.NODE_ENV === "development";
1376
- var HEADER_HEIGHT = 48;
1376
+ var HEADER_HEIGHT = 49;
1377
1377
  var CHROME_HEIGHT = HEADER_HEIGHT;
1378
1378
  var DEFAULT_HEIGHT = `calc(100vh - ${CHROME_HEIGHT}px)`;
1379
1379
  var DEFAULT_SHOULD_GROW_WHEN_SCROLLING = true;
@@ -1413,6 +1413,7 @@ var FONT_DETAILS = {
1413
1413
  fontWeight: "regular"
1414
1414
  }
1415
1415
  };
1416
+ var IN_VIEW_DELAY_MS = 150;
1416
1417
  var LOCAL_STORAGE_SETTINGS_KEY = "web-reader-settings";
1417
1418
  var LOCAL_STORAGE_LOCATIONS_KEY = "web-reader-locations";
1418
1419
  var MAIN_CONTENT_ID = "mainContent";
@@ -3506,10 +3507,7 @@ function createMultiStyleConfigHelpers(parts) {
3506
3507
  }
3507
3508
 
3508
3509
  // src/ui/theme/components/tabs.ts
3509
- var {
3510
- defineMultiStyleConfig,
3511
- definePartsStyle
3512
- } = createMultiStyleConfigHelpers(["root", "tab", "tablist"]);
3510
+ var { defineMultiStyleConfig, definePartsStyle } = createMultiStyleConfigHelpers(["root", "tab", "tablist"]);
3513
3511
  var getTabsStyle = (getColor2) => defineMultiStyleConfig({
3514
3512
  variants: {
3515
3513
  custom: definePartsStyle({
@@ -6141,14 +6139,7 @@ function Header(props) {
6141
6139
  var _a, _b, _c, _d;
6142
6140
  const [, toggleFullscreenHook] = useFullscreen();
6143
6141
  const [isFullscreen, setIsFullScreen] = useState10(false);
6144
- const {
6145
- navigator,
6146
- manifest,
6147
- type,
6148
- containerRef,
6149
- currentPage,
6150
- totalPages
6151
- } = props;
6142
+ const { navigator, manifest, type, containerRef, currentPage, totalPages } = props;
6152
6143
  const isAtStart = (_a = props.state) == null ? void 0 : _a.atStart;
6153
6144
  const isAtEnd = (_b = props.state) == null ? void 0 : _b.atEnd;
6154
6145
  const iconFill = useColorModeValue_default(
@@ -6774,12 +6765,8 @@ function setReflowableCss(iframeHtml, settings) {
6774
6765
  }
6775
6766
  function setFixedCss(iframeDocument, iframeContainer) {
6776
6767
  if (!iframeContainer) return;
6777
- let { contentWidth, contentHeight } = extractContentViewportSize(
6778
- iframeDocument
6779
- );
6780
- const { containerWidth, containerHeight } = extractContentContainerSize(
6781
- iframeContainer
6782
- );
6768
+ let { contentWidth, contentHeight } = extractContentViewportSize(iframeDocument);
6769
+ const { containerWidth, containerHeight } = extractContentContainerSize(iframeContainer);
6783
6770
  contentWidth = contentWidth != null ? contentWidth : containerWidth;
6784
6771
  contentHeight = contentHeight != null ? contentHeight : containerHeight;
6785
6772
  const scale = Math.min(
@@ -7346,9 +7333,7 @@ function useResource(manifest, state2, getContent, injectables, dispatch) {
7346
7333
  if (element) document2 == null ? void 0 : document2.head.appendChild(element);
7347
7334
  }
7348
7335
  injectJS(document2.body);
7349
- const iframeContainer = window.document.querySelector(
7350
- 'main [role="progressbar"]'
7351
- );
7336
+ const iframeContainer = window.document.querySelector('main [role="progressbar"]');
7352
7337
  const readerSettings = {
7353
7338
  colorMode: state2.settings.colorMode,
7354
7339
  fontSize: state2.settings.fontSize,
@@ -7713,129 +7698,6 @@ var ChakraPage = chakra3(
7713
7698
  );
7714
7699
  var ChakraPage_default = ChakraPage;
7715
7700
 
7716
- // src/PdfReader/ScrollPage.tsx
7717
- import React48 from "react";
7718
- import { useInView } from "react-intersection-observer";
7719
- var Placeholder = ({ width, height, pageNumber }) => {
7720
- return /* @__PURE__ */ React48.createElement(
7721
- "div",
7722
- {
7723
- "data-page-number": pageNumber,
7724
- style: { width, height }
7725
- }
7726
- );
7727
- };
7728
- var ScrollPage = ({
7729
- scale,
7730
- pageNumber,
7731
- width,
7732
- height,
7733
- onLoadSuccess,
7734
- placeholderHeight,
7735
- placeholderWidth,
7736
- allowInView,
7737
- onInView,
7738
- fitMode,
7739
- rotate
7740
- }) => {
7741
- const { ref: loadRef, inView: loadInView } = useInView({
7742
- threshold: 0,
7743
- triggerOnce: true
7744
- });
7745
- const { ref: visibilityRef, entry } = useInView({
7746
- threshold: Array.from({ length: 11 }, (_, i) => i * 0.1),
7747
- triggerOnce: false
7748
- });
7749
- const setRefs = React48.useCallback(
7750
- (el) => {
7751
- if (typeof loadRef === "function") loadRef(el);
7752
- if (typeof visibilityRef === "function") visibilityRef(el);
7753
- },
7754
- [loadRef, visibilityRef]
7755
- );
7756
- const handleLoadSuccess = React48.useCallback(
7757
- (page) => {
7758
- onLoadSuccess(page);
7759
- },
7760
- [onLoadSuccess]
7761
- );
7762
- React48.useEffect(() => {
7763
- if (onInView && entry) {
7764
- onInView(pageNumber, entry.intersectionRatio || 0);
7765
- }
7766
- }, [entry, onInView, pageNumber]);
7767
- return /* @__PURE__ */ React48.createElement("div", { ref: setRefs }, loadInView ? /* @__PURE__ */ React48.createElement(
7768
- ChakraPage_default,
7769
- {
7770
- "data-page-number": pageNumber,
7771
- pageNumber,
7772
- scale,
7773
- width,
7774
- height,
7775
- onLoadSuccess: handleLoadSuccess,
7776
- fitMode,
7777
- rotate
7778
- }
7779
- ) : /* @__PURE__ */ React48.createElement(
7780
- Placeholder,
7781
- {
7782
- width: placeholderWidth,
7783
- height: placeholderHeight,
7784
- pageNumber
7785
- }
7786
- ));
7787
- };
7788
- var ScrollPage_default = ScrollPage;
7789
-
7790
- // src/PdfReader/useMeasure.tsx
7791
- import * as React49 from "react";
7792
- var DEFAULT_DIMENSION = {
7793
- x: 0,
7794
- y: 0,
7795
- width: 0,
7796
- height: 0,
7797
- top: 0,
7798
- left: 0,
7799
- bottom: 0,
7800
- right: 0
7801
- };
7802
- function useMeasure() {
7803
- const [element, ref] = React49.useState(null);
7804
- const [rect, setRect] = React49.useState(DEFAULT_DIMENSION);
7805
- const observer = React49.useMemo(
7806
- () => new window.ResizeObserver(
7807
- (entries) => {
7808
- if (entries[0]) {
7809
- const {
7810
- x,
7811
- y,
7812
- width,
7813
- height,
7814
- top,
7815
- left,
7816
- bottom,
7817
- right
7818
- } = entries[0].contentRect;
7819
- setRect({ x, y, width, height, top, left, bottom, right });
7820
- }
7821
- }
7822
- ),
7823
- []
7824
- );
7825
- React49.useLayoutEffect(() => {
7826
- if (!element) return;
7827
- observer.observe(element);
7828
- return () => {
7829
- observer.disconnect();
7830
- };
7831
- }, [element, observer]);
7832
- return [ref, rect];
7833
- }
7834
-
7835
- // src/PdfReader/index.tsx
7836
- import "react-pdf/dist/Page/AnnotationLayer.css";
7837
- import "react-pdf/dist/Page/TextLayer.css";
7838
-
7839
7701
  // src/PdfReader/lib.ts
7840
7702
  var SCALE_STEP = 0.1;
7841
7703
  var START_QUERY = "start";
@@ -8067,6 +7929,118 @@ function isStartOfResource(pageNumber, resourceHref) {
8067
7929
  return pageNumber === (startPage != null ? startPage : 1);
8068
7930
  }
8069
7931
 
7932
+ // src/PdfReader/ScrollPage.tsx
7933
+ import React48 from "react";
7934
+ import { useInView } from "react-intersection-observer";
7935
+ var Placeholder = ({ width, height, pageNumber }) => {
7936
+ return /* @__PURE__ */ React48.createElement(
7937
+ "div",
7938
+ {
7939
+ "data-page-number": pageNumber,
7940
+ style: { width, height }
7941
+ }
7942
+ );
7943
+ };
7944
+ var ScrollPage = ({
7945
+ scale,
7946
+ pageNumber,
7947
+ width,
7948
+ height,
7949
+ onLoadSuccess,
7950
+ onPageRef,
7951
+ placeholderHeight,
7952
+ placeholderWidth,
7953
+ allowInView,
7954
+ onInView,
7955
+ fitMode,
7956
+ rotate
7957
+ }) => {
7958
+ const { ref: loadRef, inView: loadInView } = useInView({
7959
+ threshold: 0,
7960
+ triggerOnce: true
7961
+ });
7962
+ const { ref: visibilityRef, entry } = useInView({
7963
+ threshold: Array.from({ length: 11 }, (_, i) => i * 0.1),
7964
+ triggerOnce: false
7965
+ });
7966
+ const setRefs = React48.useCallback(
7967
+ (el) => {
7968
+ if (typeof loadRef === "function") loadRef(el);
7969
+ if (typeof visibilityRef === "function") visibilityRef(el);
7970
+ if (onPageRef) onPageRef(pageNumber, el);
7971
+ },
7972
+ [loadRef, onPageRef, pageNumber, visibilityRef]
7973
+ );
7974
+ const handleLoadSuccess = React48.useCallback(
7975
+ (page) => {
7976
+ onLoadSuccess(page);
7977
+ },
7978
+ [onLoadSuccess]
7979
+ );
7980
+ React48.useEffect(() => {
7981
+ if (allowInView && onInView && entry) {
7982
+ onInView(pageNumber, entry.intersectionRatio || 0);
7983
+ }
7984
+ }, [allowInView, entry, onInView, pageNumber]);
7985
+ return /* @__PURE__ */ React48.createElement("div", { ref: setRefs }, loadInView ? /* @__PURE__ */ React48.createElement(
7986
+ ChakraPage_default,
7987
+ {
7988
+ "data-page-number": pageNumber,
7989
+ pageNumber,
7990
+ scale,
7991
+ width,
7992
+ height,
7993
+ onLoadSuccess: handleLoadSuccess,
7994
+ fitMode,
7995
+ rotate
7996
+ }
7997
+ ) : /* @__PURE__ */ React48.createElement(
7998
+ Placeholder,
7999
+ {
8000
+ width: placeholderWidth,
8001
+ height: placeholderHeight,
8002
+ pageNumber
8003
+ }
8004
+ ));
8005
+ };
8006
+ var ScrollPage_default = ScrollPage;
8007
+
8008
+ // src/PdfReader/useMeasure.tsx
8009
+ import * as React49 from "react";
8010
+ var DEFAULT_DIMENSION = {
8011
+ x: 0,
8012
+ y: 0,
8013
+ width: 0,
8014
+ height: 0,
8015
+ top: 0,
8016
+ left: 0,
8017
+ bottom: 0,
8018
+ right: 0
8019
+ };
8020
+ function useMeasure() {
8021
+ const [element, ref] = React49.useState(null);
8022
+ const [rect, setRect] = React49.useState(DEFAULT_DIMENSION);
8023
+ const observer = React49.useMemo(
8024
+ () => new window.ResizeObserver(
8025
+ (entries) => {
8026
+ if (entries[0]) {
8027
+ const { x, y, width, height, top, left, bottom, right } = entries[0].contentRect;
8028
+ setRect({ x, y, width, height, top, left, bottom, right });
8029
+ }
8030
+ }
8031
+ ),
8032
+ []
8033
+ );
8034
+ React49.useLayoutEffect(() => {
8035
+ if (!element) return;
8036
+ observer.observe(element);
8037
+ return () => {
8038
+ observer.disconnect();
8039
+ };
8040
+ }, [element, observer]);
8041
+ return [ref, rect];
8042
+ }
8043
+
8070
8044
  // src/PdfReader/index.tsx
8071
8045
  function usePdfReader(args) {
8072
8046
  var _a, _b, _c, _d, _e, _f;
@@ -8104,7 +8078,24 @@ function usePdfReader(args) {
8104
8078
  const isFetching = !state2.resource;
8105
8079
  const isParsed = typeof state2.numPages === "number";
8106
8080
  const [containerRef, containerSize] = useMeasure();
8107
- const [pageHeight, setPageHeight] = React50.useState(0);
8081
+ const documentContainerRef = React50.useRef(null);
8082
+ const pageRefs = React50.useRef(/* @__PURE__ */ new Map());
8083
+ const setPageRef = React50.useCallback(
8084
+ (pageNumber, element) => {
8085
+ if (element) {
8086
+ pageRefs.current.set(pageNumber, element);
8087
+ return;
8088
+ }
8089
+ pageRefs.current.delete(pageNumber);
8090
+ },
8091
+ []
8092
+ );
8093
+ const scrollState = React50.useRef({
8094
+ ratios: /* @__PURE__ */ new Map(),
8095
+ lastVisiblePage: state2.pageNumber,
8096
+ isInViewUpdate: false,
8097
+ lastProgrammaticNavAt: 0
8098
+ });
8108
8099
  React50.useEffect(() => {
8109
8100
  if (!webpubManifestUrl || !manifest) {
8110
8101
  return dispatch({ type: "ARGS_CHANGED", args: void 0 });
@@ -8177,13 +8168,6 @@ function usePdfReader(args) {
8177
8168
  var _a2;
8178
8169
  resizePage(containerSize, state2.fitMode, (_a2 = state2.rotation) != null ? _a2 : 0, state2.scale);
8179
8170
  }, [containerSize, resizePage, state2.fitMode, state2.rotation, state2.scale]);
8180
- React50.useEffect(() => {
8181
- if (pageHeight === 0 && state2.pdfWidth && state2.pdfHeight) {
8182
- const aspectRatio = state2.pdfHeight / state2.pdfWidth;
8183
- const initialPageHeight = (containerSize.width - READER_MARGIN) * aspectRatio;
8184
- setPageHeight(Math.round(initialPageHeight));
8185
- }
8186
- }, [state2.pdfWidth, state2.pdfHeight, containerSize.width, pageHeight]);
8187
8171
  React50.useEffect(() => {
8188
8172
  var _a2, _b2;
8189
8173
  const isFirstResource = state2.resourceIndex === 0;
@@ -8206,21 +8190,40 @@ function usePdfReader(args) {
8206
8190
  ]);
8207
8191
  React50.useEffect(() => {
8208
8192
  var _a2;
8209
- if (!((_a2 = state2.settings) == null ? void 0 : _a2.isScrolling)) return;
8210
- if (!state2.rendered) return;
8211
- process.nextTick(() => {
8212
- const page = document.querySelector(
8213
- `[data-page-number="${state2.pageNumber}"]`
8214
- );
8215
- page == null ? void 0 : page.scrollIntoView();
8216
- });
8193
+ if (!((_a2 = state2.settings) == null ? void 0 : _a2.isScrolling) || !state2.rendered) return;
8194
+ if (scrollState.current.isInViewUpdate) {
8195
+ scrollState.current.isInViewUpdate = false;
8196
+ return;
8197
+ }
8198
+ const documentContainer = documentContainerRef.current;
8199
+ const pageRef = pageRefs.current.get(state2.pageNumber);
8200
+ if (documentContainer && pageRef) {
8201
+ const containerRect = documentContainer.getBoundingClientRect();
8202
+ const pageRect = pageRef.getBoundingClientRect();
8203
+ documentContainer.scrollTo({
8204
+ top: documentContainer.scrollTop + (pageRect.top - containerRect.top)
8205
+ });
8206
+ }
8217
8207
  }, [state2.pageNumber, (_c = state2.settings) == null ? void 0 : _c.isScrolling, state2.rendered]);
8208
+ const beginProgrammaticNavigation = React50.useCallback(
8209
+ (pendingPage) => {
8210
+ const currentScrollState = scrollState.current;
8211
+ currentScrollState.lastVisiblePage = pendingPage;
8212
+ currentScrollState.lastProgrammaticNavAt = Date.now();
8213
+ currentScrollState.ratios.clear();
8214
+ },
8215
+ []
8216
+ );
8218
8217
  const goForward = React50.useCallback(() => __async(null, null, function* () {
8218
+ beginProgrammaticNavigation(
8219
+ state2.numPages ? Math.min(state2.pageNumber + 1, state2.numPages) : state2.pageNumber + 1
8220
+ );
8219
8221
  dispatch({ type: "GO_FORWARD" });
8220
- }), []);
8222
+ }), [beginProgrammaticNavigation, state2.numPages, state2.pageNumber]);
8221
8223
  const goBackward = React50.useCallback(() => __async(null, null, function* () {
8224
+ beginProgrammaticNavigation(Math.max(1, state2.pageNumber - 1));
8222
8225
  dispatch({ type: "GO_BACKWARD" });
8223
- }), []);
8226
+ }), [beginProgrammaticNavigation, state2.pageNumber]);
8224
8227
  const setScroll = React50.useCallback(
8225
8228
  (val) => __async(null, null, function* () {
8226
8229
  const isScrolling = val === "scrolling";
@@ -8246,35 +8249,40 @@ function usePdfReader(args) {
8246
8249
  const goToPage = React50.useCallback((href) => __async(null, null, function* () {
8247
8250
  dispatch({ type: "GO_TO_HREF", href });
8248
8251
  }), []);
8249
- const goToPageNumber = React50.useCallback((page) => {
8250
- dispatch({ type: "GO_TO_PAGE", page });
8251
- }, []);
8252
+ const goToPageNumber = React50.useCallback(
8253
+ (page) => {
8254
+ beginProgrammaticNavigation(page);
8255
+ dispatch({ type: "GO_TO_PAGE", page });
8256
+ },
8257
+ [beginProgrammaticNavigation]
8258
+ );
8252
8259
  const setFitMode = React50.useCallback((mode) => {
8253
8260
  dispatch({ type: "SET_FIT_MODE", fitMode: mode });
8254
8261
  }, []);
8255
- const intersectionRatios = React50.useRef({});
8256
- const lastMostVisiblePage = React50.useRef(state2.pageNumber);
8257
8262
  const onInView = React50.useCallback(
8258
8263
  (pageNum, ratio) => {
8259
8264
  var _a2;
8260
- if (!((_a2 = state2.settings) == null ? void 0 : _a2.isScrolling)) return;
8261
- intersectionRatios.current[pageNum] = ratio;
8262
- Object.keys(intersectionRatios.current).forEach((key) => {
8263
- if (intersectionRatios.current[Number(key)] === 0) {
8264
- delete intersectionRatios.current[Number(key)];
8265
- }
8266
- });
8265
+ const currentScrollState = scrollState.current;
8266
+ if (!((_a2 = state2.settings) == null ? void 0 : _a2.isScrolling) || Date.now() - currentScrollState.lastProgrammaticNavAt < IN_VIEW_DELAY_MS)
8267
+ return;
8268
+ if (ratio <= 0) {
8269
+ currentScrollState.ratios.delete(pageNum);
8270
+ return;
8271
+ }
8272
+ currentScrollState.ratios.set(pageNum, ratio);
8273
+ let mostVisiblePage = currentScrollState.lastVisiblePage;
8267
8274
  let maxRatio = -1;
8268
- let mostVisiblePage = state2.pageNumber;
8269
- for (const [page, r] of Object.entries(intersectionRatios.current)) {
8275
+ for (const [p, r] of currentScrollState.ratios) {
8270
8276
  if (r > maxRatio) {
8271
8277
  maxRatio = r;
8272
- mostVisiblePage = Number(page);
8278
+ mostVisiblePage = p;
8273
8279
  }
8280
+ if (r > 0.8) break;
8274
8281
  }
8275
- if (mostVisiblePage !== lastMostVisiblePage.current) {
8276
- lastMostVisiblePage.current = mostVisiblePage;
8282
+ if (mostVisiblePage !== currentScrollState.lastVisiblePage) {
8283
+ currentScrollState.lastVisiblePage = mostVisiblePage;
8277
8284
  if (state2.pageNumber !== mostVisiblePage) {
8285
+ currentScrollState.isInViewUpdate = true;
8278
8286
  dispatch({ type: "PAGE_IN_VIEW", page: mostVisiblePage });
8279
8287
  }
8280
8288
  }
@@ -8339,7 +8347,7 @@ function usePdfReader(args) {
8339
8347
  tabIndex: -1,
8340
8348
  id: MAIN_CONTENT_ID,
8341
8349
  ref: containerRef,
8342
- height: pageHeight,
8350
+ height,
8343
8351
  sx: {
8344
8352
  ".react-pdf__Document": {
8345
8353
  width: "100%",
@@ -8358,7 +8366,8 @@ function usePdfReader(args) {
8358
8366
  {
8359
8367
  file: state2.resource,
8360
8368
  onLoadSuccess: onDocumentLoadSuccess,
8361
- onLoadError: onDocumentLoadError
8369
+ onLoadError: onDocumentLoadError,
8370
+ inputRef: documentContainerRef
8362
8371
  },
8363
8372
  isParsed && state2.numPages && /* @__PURE__ */ React50.createElement(React50.Fragment, null, state2.settings.isScrolling && Array.from(new Array(state2.numPages), (_, index) => {
8364
8373
  var _a2;
@@ -8373,10 +8382,11 @@ function usePdfReader(args) {
8373
8382
  scale: state2.scale,
8374
8383
  pageNumber: index + 1,
8375
8384
  onLoadSuccess: onRenderSuccess,
8376
- allowInView: !isFetching,
8385
+ allowInView: state2.rendered,
8377
8386
  onInView,
8378
8387
  fitMode: state2.fitMode,
8379
- rotate: (_a2 = state2.rotation) != null ? _a2 : 0
8388
+ rotate: (_a2 = state2.rotation) != null ? _a2 : 0,
8389
+ onPageRef: setPageRef
8380
8390
  }
8381
8391
  );
8382
8392
  }), !state2.settings.isScrolling && /* @__PURE__ */ React50.createElement(
@@ -8597,6 +8607,7 @@ export {
8597
8607
  DEFAULT_SHOULD_GROW_WHEN_SCROLLING,
8598
8608
  FONT_DETAILS,
8599
8609
  HEADER_HEIGHT,
8610
+ IN_VIEW_DELAY_MS,
8600
8611
  IS_DEV,
8601
8612
  LOCAL_STORAGE_LOCATIONS_KEY,
8602
8613
  LOCAL_STORAGE_SETTINGS_KEY,