@papyrus-sdk/ui-react-native 0.2.29 → 0.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -25939,12 +25939,17 @@ var shouldEnableViewerScrollForPageScrub = ({
25939
25939
  isScrubbing
25940
25940
  }) => viewerScrollEnabled && !isScrubbing;
25941
25941
  var resolvePageScrubberTouchPolicy = () => ({
25942
- responderTarget: "pill",
25942
+ responderTarget: "track",
25943
25943
  claimOnStart: true,
25944
25944
  captureOnStart: true,
25945
25945
  captureOnMove: true,
25946
25946
  childConsumesTouch: false
25947
25947
  });
25948
+ var resolvePageScrubberOverlayStyle = () => ({
25949
+ zIndex: 30,
25950
+ elevation: 30
25951
+ });
25952
+ var resolvePageScrubberPointerEvents = () => "box-only";
25948
25953
  var resolvePageScrubberReleaseAction = ({
25949
25954
  hasMoved,
25950
25955
  pendingPage
@@ -26008,6 +26013,23 @@ var resolvePageScrubberPage = ({
26008
26013
  Math.min(pageCount, Math.round(ratio * (pageCount - 1)) + 1)
26009
26014
  );
26010
26015
  };
26016
+ var isPageScrubberTrackYTrusted = (trackY, windowHeight) => Number.isFinite(trackY) && Number.isFinite(windowHeight) && trackY >= 0 && trackY <= windowHeight;
26017
+ var resolvePageScrubberGesturePosition = ({
26018
+ moveY,
26019
+ dy,
26020
+ trackY,
26021
+ windowHeight,
26022
+ trackHeight,
26023
+ thumbHeight,
26024
+ startThumbTop
26025
+ }) => {
26026
+ if (isPageScrubberTrackYTrusted(trackY, windowHeight)) {
26027
+ return moveY - trackY;
26028
+ }
26029
+ const travel = Math.max(0, trackHeight - thumbHeight);
26030
+ const nextThumbTop = Math.max(0, Math.min(travel, startThumbTop + dy));
26031
+ return nextThumbTop + thumbHeight / 2;
26032
+ };
26011
26033
 
26012
26034
  // components/viewerNavigation.ts
26013
26035
  var resolveViewerScrollTarget = (pageIndex, isDouble) => ({
@@ -26062,6 +26084,15 @@ var resolvePositiveInt = (value, fallback, min, max) => {
26062
26084
  const rounded = Math.round(value);
26063
26085
  return Math.max(min, Math.min(max, rounded));
26064
26086
  };
26087
+ var scrollViewerListToOffset = (list, offset, animated) => {
26088
+ if (list === null) return;
26089
+ const nativeScrollRef = list.getNativeScrollRef?.();
26090
+ if (nativeScrollRef?.scrollTo) {
26091
+ nativeScrollRef.scrollTo({ y: offset, animated });
26092
+ return;
26093
+ }
26094
+ list.scrollToOffset?.({ offset, animated });
26095
+ };
26065
26096
  var Viewer = ({
26066
26097
  engine,
26067
26098
  virtualWindowSize,
@@ -26134,6 +26165,7 @@ var Viewer = ({
26134
26165
  const pendingScrollTimeoutRef = (0, import_react5.useRef)(
26135
26166
  null
26136
26167
  );
26168
+ const pendingScrollFrameRef = (0, import_react5.useRef)(null);
26137
26169
  const pendingChromeShowTimeoutRef = (0, import_react5.useRef)(null);
26138
26170
  const chromeVisibleRef = (0, import_react5.useRef)(mobileChromeVisible);
26139
26171
  const lastScrollOffsetYRef = (0, import_react5.useRef)(0);
@@ -26141,6 +26173,7 @@ var Viewer = ({
26141
26173
  const scrollUpAccumRef = (0, import_react5.useRef)(0);
26142
26174
  const [layoutRevision, setLayoutRevision] = (0, import_react5.useState)(0);
26143
26175
  const [selectionDragActive, setSelectionDragActive] = (0, import_react5.useState)(false);
26176
+ const [programmaticScrollActive, setProgrammaticScrollActive] = (0, import_react5.useState)(false);
26144
26177
  const selectionDragActiveRef = (0, import_react5.useRef)(false);
26145
26178
  const [gestureScrollLockActive, setGestureScrollLockActive] = (0, import_react5.useState)(false);
26146
26179
  const gestureScrollLockActiveRef = (0, import_react5.useRef)(false);
@@ -26235,6 +26268,7 @@ var Viewer = ({
26235
26268
  pendingScrollTargetRef.current = null;
26236
26269
  pendingScrollAttemptsRef.current = 0;
26237
26270
  clearPendingScrollRetry();
26271
+ setProgrammaticScrollActive(false);
26238
26272
  }, [clearPendingScrollRetry]);
26239
26273
  const clearPendingChromeShow = (0, import_react5.useCallback)(() => {
26240
26274
  if (pendingChromeShowTimeoutRef.current) {
@@ -26289,6 +26323,10 @@ var Viewer = ({
26289
26323
  cancelAnimationFrame(pinchAnchorRestoreFrameRef.current);
26290
26324
  }
26291
26325
  clearPendingScrollRetry();
26326
+ if (pendingScrollFrameRef.current !== null) {
26327
+ cancelAnimationFrame(pendingScrollFrameRef.current);
26328
+ pendingScrollFrameRef.current = null;
26329
+ }
26292
26330
  clearPendingChromeShow();
26293
26331
  },
26294
26332
  [clearPendingChromeShow, clearPendingScrollRetry]
@@ -27142,12 +27180,20 @@ var Viewer = ({
27142
27180
  (animated) => {
27143
27181
  const target = pendingScrollTargetRef.current;
27144
27182
  if (target === null || listRef.current === null) return;
27145
- listRef.current.scrollToOffset({
27146
- offset: Math.max(0, getFallbackOffsetForIndex(target.listIndex)),
27147
- animated
27183
+ setViewerScrollEnabledNative(true);
27184
+ if (pendingScrollFrameRef.current !== null) {
27185
+ cancelAnimationFrame(pendingScrollFrameRef.current);
27186
+ }
27187
+ pendingScrollFrameRef.current = requestAnimationFrame(() => {
27188
+ pendingScrollFrameRef.current = null;
27189
+ const nextTarget = pendingScrollTargetRef.current;
27190
+ const list = listRef.current;
27191
+ if (nextTarget === null || list === null) return;
27192
+ const offset = getFallbackOffsetForIndex(nextTarget.listIndex);
27193
+ scrollViewerListToOffset(list, offset, animated);
27148
27194
  });
27149
27195
  },
27150
- [getFallbackOffsetForIndex]
27196
+ [getFallbackOffsetForIndex, setViewerScrollEnabledNative]
27151
27197
  );
27152
27198
  const restoreOrientationScrollOffset = (0, import_react5.useCallback)(() => {
27153
27199
  if (pendingOrientationRestoreWidthRef.current !== windowWidth || pageCount <= 0 || isSingle || isWebView) {
@@ -27246,6 +27292,7 @@ var Viewer = ({
27246
27292
  const target = resolveViewerScrollTarget(scrollToPageSignal, isDouble);
27247
27293
  pendingScrollTargetRef.current = target;
27248
27294
  pendingScrollAttemptsRef.current = 0;
27295
+ setProgrammaticScrollActive(true);
27249
27296
  clearPendingScrollRetry();
27250
27297
  setDocumentStateTracked(
27251
27298
  {
@@ -27501,6 +27548,89 @@ var Viewer = ({
27501
27548
  windowWidth
27502
27549
  ]
27503
27550
  );
27551
+ const continuousList = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
27552
+ import_react_native4.FlatList,
27553
+ {
27554
+ ref: listRef,
27555
+ nestedScrollEnabled: true,
27556
+ data: isDouble ? rows : pages,
27557
+ style: { width: documentSurfaceWidth },
27558
+ initialNumToRender: FLATLIST_INITIAL_NUM_TO_RENDER,
27559
+ windowSize: resolvedWindowSize,
27560
+ maxToRenderPerBatch: resolvedMaxToRenderPerBatch,
27561
+ updateCellsBatchingPeriod: FLATLIST_UPDATE_CELLS_BATCHING_PERIOD,
27562
+ removeClippedSubviews: resolvedRemoveClippedSubviews,
27563
+ getItemLayout,
27564
+ keyExtractor,
27565
+ contentContainerStyle: styles4.listContent,
27566
+ renderItem,
27567
+ onViewableItemsChanged,
27568
+ viewabilityConfig: { itemVisiblePercentThreshold: 60 },
27569
+ scrollEnabled: pageScrubResolvedScrollEnabled || programmaticScrollActive,
27570
+ onLayout: () => {
27571
+ captureViewerFrame(listRef.current);
27572
+ restoreOrientationScrollOffset();
27573
+ scrollToPendingTarget(false);
27574
+ },
27575
+ onContentSizeChange: (_, height) => {
27576
+ viewerContentHeightRef.current = height;
27577
+ scrollToPendingTarget(false);
27578
+ },
27579
+ onScrollToIndexFailed: ({ index, averageItemLength }) => {
27580
+ const dataLength = isDouble ? rows.length : pages.length;
27581
+ if (index < 0 || index >= dataLength) return;
27582
+ const existingTarget = pendingScrollTargetRef.current;
27583
+ pendingScrollTargetRef.current = isDouble ? existingTarget?.listIndex === index ? existingTarget : resolveViewerScrollTarget(rows[index]?.left ?? index * 2, true) : resolveViewerScrollTarget(index, false);
27584
+ const offset = Math.max(0, getFallbackOffsetForIndex(index));
27585
+ scrollViewerListToOffset(listRef.current, offset, false);
27586
+ if (!isDouble) {
27587
+ ensurePageDimensions(index);
27588
+ } else {
27589
+ const row = rows[index];
27590
+ if (row) {
27591
+ ensurePageDimensions(row.left);
27592
+ if (row.right !== null) {
27593
+ ensurePageDimensions(row.right);
27594
+ }
27595
+ }
27596
+ }
27597
+ scheduleScrollRetry("onScrollToIndexFailed");
27598
+ if (perfEnabled) {
27599
+ logPerfEvent("Viewer", "scrollToIndexFailed", {
27600
+ index,
27601
+ averageItemLength,
27602
+ fallbackOffset: offset,
27603
+ fallbackSource: "cached-item-layout",
27604
+ itemCount: dataLength,
27605
+ retryAttempt: pendingScrollAttemptsRef.current
27606
+ });
27607
+ }
27608
+ },
27609
+ onScroll: (event) => handleViewerScroll(event, "continuous"),
27610
+ onScrollBeginDrag: perfEnabled ? () => {
27611
+ scrollMonitorRef.current.begin("continuous.beginDrag");
27612
+ } : void 0,
27613
+ onMomentumScrollBegin: perfEnabled ? () => {
27614
+ scrollMonitorRef.current.begin("continuous.momentumBegin");
27615
+ } : void 0,
27616
+ onScrollEndDrag: perfEnabled ? () => {
27617
+ scrollMonitorRef.current.end("continuous.endDrag");
27618
+ endViewerScroll("continuous.endDrag");
27619
+ sampleMemory("Viewer", "continuous.endDrag", {
27620
+ pageCount
27621
+ });
27622
+ } : void 0,
27623
+ onMomentumScrollEnd: perfEnabled ? () => {
27624
+ scrollMonitorRef.current.end("continuous.momentumEnd");
27625
+ endViewerScroll("continuous.momentumEnd");
27626
+ sampleMemory("Viewer", "continuous.momentumEnd", {
27627
+ pageCount
27628
+ });
27629
+ } : void 0,
27630
+ scrollEventThrottle: 16,
27631
+ showsVerticalScrollIndicator: false
27632
+ }
27633
+ );
27504
27634
  if (isWebView) {
27505
27635
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native4.View, { style: [styles4.container, isDark && styles4.containerDark], children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
27506
27636
  WebViewViewer_default,
@@ -27535,6 +27665,7 @@ var Viewer = ({
27535
27665
  {
27536
27666
  ref: horizontalScrollRef,
27537
27667
  horizontal: true,
27668
+ nestedScrollEnabled: true,
27538
27669
  scrollEnabled: !gestureScrollLockActive && !pageScrubActive && documentSurfaceWidth > windowWidth,
27539
27670
  showsHorizontalScrollIndicator: false,
27540
27671
  onScroll: (event) => {
@@ -27619,101 +27750,21 @@ var Viewer = ({
27619
27750
  ]
27620
27751
  }
27621
27752
  ],
27622
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
27753
+ children: documentSurfaceWidth > windowWidth + 1 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
27623
27754
  import_react_native4.ScrollView,
27624
27755
  {
27625
27756
  ref: horizontalScrollRef,
27626
27757
  horizontal: true,
27627
- scrollEnabled: !gestureScrollLockActive && !pageScrubActive && documentSurfaceWidth > windowWidth,
27758
+ nestedScrollEnabled: true,
27759
+ scrollEnabled: !gestureScrollLockActive && !pageScrubActive,
27628
27760
  showsHorizontalScrollIndicator: false,
27629
27761
  onScroll: (event) => {
27630
27762
  horizontalScrollOffsetRef.current = event.nativeEvent.contentOffset?.x ?? 0;
27631
27763
  },
27632
27764
  scrollEventThrottle: 16,
27633
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
27634
- import_react_native4.FlatList,
27635
- {
27636
- ref: listRef,
27637
- data: isDouble ? rows : pages,
27638
- style: { width: documentSurfaceWidth },
27639
- initialNumToRender: FLATLIST_INITIAL_NUM_TO_RENDER,
27640
- windowSize: resolvedWindowSize,
27641
- maxToRenderPerBatch: resolvedMaxToRenderPerBatch,
27642
- updateCellsBatchingPeriod: FLATLIST_UPDATE_CELLS_BATCHING_PERIOD,
27643
- removeClippedSubviews: resolvedRemoveClippedSubviews,
27644
- getItemLayout,
27645
- keyExtractor,
27646
- contentContainerStyle: styles4.listContent,
27647
- renderItem,
27648
- onViewableItemsChanged,
27649
- viewabilityConfig: { itemVisiblePercentThreshold: 60 },
27650
- scrollEnabled: pageScrubResolvedScrollEnabled,
27651
- onLayout: () => {
27652
- captureViewerFrame(listRef.current);
27653
- restoreOrientationScrollOffset();
27654
- scrollToPendingTarget(false);
27655
- },
27656
- onContentSizeChange: (_, height) => {
27657
- viewerContentHeightRef.current = height;
27658
- scrollToPendingTarget(false);
27659
- },
27660
- onScrollToIndexFailed: ({ index, averageItemLength }) => {
27661
- const dataLength = isDouble ? rows.length : pages.length;
27662
- if (index < 0 || index >= dataLength) return;
27663
- const existingTarget = pendingScrollTargetRef.current;
27664
- pendingScrollTargetRef.current = isDouble ? existingTarget?.listIndex === index ? existingTarget : resolveViewerScrollTarget(rows[index]?.left ?? index * 2, true) : resolveViewerScrollTarget(index, false);
27665
- const offset = Math.max(0, getFallbackOffsetForIndex(index));
27666
- listRef.current?.scrollToOffset({ offset, animated: false });
27667
- if (!isDouble) {
27668
- ensurePageDimensions(index);
27669
- } else {
27670
- const row = rows[index];
27671
- if (row) {
27672
- ensurePageDimensions(row.left);
27673
- if (row.right !== null) {
27674
- ensurePageDimensions(row.right);
27675
- }
27676
- }
27677
- }
27678
- scheduleScrollRetry("onScrollToIndexFailed");
27679
- if (perfEnabled) {
27680
- logPerfEvent("Viewer", "scrollToIndexFailed", {
27681
- index,
27682
- averageItemLength,
27683
- fallbackOffset: offset,
27684
- fallbackSource: "cached-item-layout",
27685
- itemCount: dataLength,
27686
- retryAttempt: pendingScrollAttemptsRef.current
27687
- });
27688
- }
27689
- },
27690
- onScroll: (event) => handleViewerScroll(event, "continuous"),
27691
- onScrollBeginDrag: perfEnabled ? () => {
27692
- scrollMonitorRef.current.begin("continuous.beginDrag");
27693
- } : void 0,
27694
- onMomentumScrollBegin: perfEnabled ? () => {
27695
- scrollMonitorRef.current.begin("continuous.momentumBegin");
27696
- } : void 0,
27697
- onScrollEndDrag: perfEnabled ? () => {
27698
- scrollMonitorRef.current.end("continuous.endDrag");
27699
- endViewerScroll("continuous.endDrag");
27700
- sampleMemory("Viewer", "continuous.endDrag", {
27701
- pageCount
27702
- });
27703
- } : void 0,
27704
- onMomentumScrollEnd: perfEnabled ? () => {
27705
- scrollMonitorRef.current.end("continuous.momentumEnd");
27706
- endViewerScroll("continuous.momentumEnd");
27707
- sampleMemory("Viewer", "continuous.momentumEnd", {
27708
- pageCount
27709
- });
27710
- } : void 0,
27711
- scrollEventThrottle: 16,
27712
- showsVerticalScrollIndicator: false
27713
- }
27714
- )
27765
+ children: continuousList
27715
27766
  }
27716
- )
27767
+ ) : continuousList
27717
27768
  }
27718
27769
  ) }) });
27719
27770
  };
@@ -32084,6 +32135,8 @@ function ProgressPill({
32084
32135
  const isDark = uiTheme === "dark";
32085
32136
  const offsets = resolveMobileChromeOffsets(usePapyrusSafeAreaInsets());
32086
32137
  const { height: windowHeight } = (0, import_react_native17.useWindowDimensions)();
32138
+ const windowHeightRef = (0, import_react14.useRef)(windowHeight);
32139
+ windowHeightRef.current = windowHeight;
32087
32140
  const trackHeight = Math.max(
32088
32141
  180,
32089
32142
  Math.min(520, windowHeight - offsets.progress - offsets.bottom - 132)
@@ -32134,6 +32187,7 @@ function ProgressPill({
32134
32187
  const thumbTopRef = (0, import_react14.useRef)(new import_react_native17.Animated.Value(thumbTop));
32135
32188
  const thumbTopSnapshotRef = (0, import_react14.useRef)(thumbTop);
32136
32189
  thumbTopSnapshotRef.current = thumbTop;
32190
+ const scrubStartThumbTopRef = (0, import_react14.useRef)(0);
32137
32191
  const scrubberMetricsRef = (0, import_react14.useRef)({ trackHeight, thumbHeight, pageCount });
32138
32192
  scrubberMetricsRef.current = { trackHeight, thumbHeight, pageCount };
32139
32193
  (0, import_react14.useEffect)(() => {
@@ -32201,6 +32255,7 @@ function ProgressPill({
32201
32255
  onScrubbingChangeRef.current?.(true);
32202
32256
  hasMovedRef.current = false;
32203
32257
  longPressTriggeredRef.current = false;
32258
+ scrubStartThumbTopRef.current = thumbTopSnapshotRef.current;
32204
32259
  scrubNavigationControllerRef.current.begin();
32205
32260
  pendingReleasedScrubPageRef.current = null;
32206
32261
  setScrubPreviewPage(currentPageRef.current);
@@ -32222,7 +32277,18 @@ function ProgressPill({
32222
32277
  clearTimeout(longPressTimerRef.current);
32223
32278
  longPressTimerRef.current = null;
32224
32279
  }
32225
- scrubToPositionRef.current(gestureState.moveY - trackYRef.current);
32280
+ const { trackHeight: trackHeight2, thumbHeight: thumbHeight2 } = scrubberMetricsRef.current;
32281
+ scrubToPositionRef.current(
32282
+ resolvePageScrubberGesturePosition({
32283
+ moveY: gestureState.moveY,
32284
+ dy: gestureState.dy,
32285
+ trackY: trackYRef.current,
32286
+ windowHeight: windowHeightRef.current,
32287
+ trackHeight: trackHeight2,
32288
+ thumbHeight: thumbHeight2,
32289
+ startThumbTop: scrubStartThumbTopRef.current
32290
+ })
32291
+ );
32226
32292
  },
32227
32293
  onPanResponderRelease: () => {
32228
32294
  if (longPressTimerRef.current !== null) {
@@ -32276,6 +32342,7 @@ function ProgressPill({
32276
32342
  pointerEvents: "box-none",
32277
32343
  style: [
32278
32344
  styles17.frame,
32345
+ resolvePageScrubberOverlayStyle(),
32279
32346
  {
32280
32347
  top: offsets.progress,
32281
32348
  right: offsets.right
@@ -32285,6 +32352,7 @@ function ProgressPill({
32285
32352
  import_react_native17.View,
32286
32353
  {
32287
32354
  ref: scrubberRef,
32355
+ pointerEvents: resolvePageScrubberPointerEvents(),
32288
32356
  style: [styles17.scrubber, { height: trackHeight }],
32289
32357
  ...scrubberPanHandlers,
32290
32358
  onLayout: () => {