@onekeyfe/react-native-pager-view 3.0.122 → 3.0.124

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.
@@ -6,12 +6,16 @@
6
6
  #import <react/renderer/components/pagerview/RCTComponentViewHelpers.h>
7
7
 
8
8
  #import "React/RCTConversions.h"
9
+ #import "React/RCTMountingTransactionObserving.h"
10
+ #import "React/RCTScrollViewComponentView.h"
9
11
  #import "React/RCTSurfaceTouchHandler.h"
10
12
  #import "React/RCTTouchHandler.h"
11
13
 
12
14
  using namespace facebook::react;
13
15
 
14
16
  static void *RNCCollapsiblePagerContentOffsetContext = &RNCCollapsiblePagerContentOffsetContext;
17
+ static NSString *const RNCCollapsiblePagerNativeScrollerIDPrefix =
18
+ @"rnc-collapsible-pager-native-scroller:";
15
19
 
16
20
  typedef void (^RNCCollapsiblePagerNativeTabPressHandler)(NSInteger index, NSString *key);
17
21
 
@@ -825,6 +829,22 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
825
829
 
826
830
  - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
827
831
  {
832
+ if (gestureRecognizer == self.panGestureRecognizer) {
833
+ UIPanGestureRecognizer *pan = self.panGestureRecognizer;
834
+ CGPoint translation = [pan translationInView:self];
835
+ CGPoint velocity = [pan velocityInView:self];
836
+ CGPoint intent = fabs(translation.x) + fabs(translation.y) >= 1
837
+ ? translation
838
+ : velocity;
839
+ BOOL hasMotion = MAX(fabs(intent.x), fabs(intent.y)) >= 1;
840
+ if (hasMotion && fabs(intent.y) > fabs(intent.x)) {
841
+ RNCCollapsiblePagerLog([NSString stringWithFormat:
842
+ @"pager-axis dx=%.2f dy=%.2f result=vertical-rejected",
843
+ intent.x,
844
+ intent.y]);
845
+ return NO;
846
+ }
847
+ }
828
848
  if (gestureRecognizer == self.panGestureRecognizer && self.excludedHeaderView != nil) {
829
849
  CGPoint point = [gestureRecognizer locationInView:self.excludedHeaderView];
830
850
  if ([self.excludedHeaderView hitTest:point withEvent:nil] != nil) {
@@ -895,7 +915,8 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
895
915
  UIPageViewControllerDataSource,
896
916
  UIPageViewControllerDelegate,
897
917
  UIScrollViewDelegate,
898
- UIGestureRecognizerDelegate
918
+ UIGestureRecognizerDelegate,
919
+ RCTMountingTransactionObserving
899
920
  >
900
921
  - (void)finishPagerScrollEmittingSelection:(BOOL)emitSelection;
901
922
  - (void)completeTransitionOnNextRunLoopForGeneration:(NSUInteger)generation
@@ -905,6 +926,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
905
926
  - (void)liftSharedHeadersForPagerTransition:(NSString *)reason;
906
927
  - (void)restoreSharedHeadersToContainer;
907
928
  - (void)layoutSharedHeaders;
929
+ - (UIScrollView *)currentVerticalScrollViewForParentPager;
930
+ - (UIScrollView *)verticalScrollViewForPageAtIndex:(NSInteger)pageIndex;
931
+ - (UIScrollView *)findExplicitNativeScrollerInView:(UIView *)view;
932
+ - (UIScrollView *)findFabricScrollViewInView:(UIView *)view;
933
+ - (UIScrollView *)findVerticalScrollViewInView:(UIView *)view;
934
+ - (void)scheduleScrollObserverRetryForCurrentPage;
908
935
  - (void)preparePageForHorizontalTransitionAtIndex:(NSInteger)pageIndex;
909
936
  - (void)updateSharedHeaderPressCancellationGesture;
910
937
  - (void)detachSharedHeaderPressCancellationGesture;
@@ -964,12 +991,18 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
964
991
  NSMapTable<UIScrollView *, NSNumber *> *_originalInsetAdjustmentBehavior;
965
992
  __weak UIScrollView *_observedScrollView;
966
993
  __weak UIScrollView *_sharedHeaderScrollView;
994
+ __weak UIScrollView *_pendingFallbackScrollView;
967
995
  __weak UIView *_sharedHeaderPressCancellationGestureHost;
968
996
  __weak UIGestureRecognizer *_reactTouchHandler;
969
997
  UIPanGestureRecognizer *_sharedHeaderPressCancellationGesture;
970
998
  RNCCollapsiblePagerOuterPagerPanGestureRecognizer *_sharedHeaderOuterPagerGesture;
971
999
  UIPanGestureRecognizer *_verticalPagerGesture;
972
1000
  BOOL _observingContentOffset;
1001
+ BOOL _scrollResolveRetryScheduled;
1002
+ CFTimeInterval _pendingFallbackScrollViewDetectedAt;
1003
+ NSInteger _scrollResolveRetryPageIndex;
1004
+ NSInteger _scrollResolveRetryAttempt;
1005
+ NSUInteger _scrollResolveRetryToken;
973
1006
  CGFloat _currentLogicalOffset;
974
1007
  CGFloat _observedListPanStartLogicalOffset;
975
1008
  BOOL _sharedHeadersLiftedForPagerTransition;
@@ -1010,6 +1043,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1010
1043
  _hasPendingDirectNativePagerChange = NO;
1011
1044
  _pendingGoToIndex = -1;
1012
1045
  _pendingGoToAnimated = YES;
1046
+ _scrollResolveRetryPageIndex = NSNotFound;
1013
1047
  _hasAppliedInitialPage = NO;
1014
1048
  _needsPropsReapply = YES;
1015
1049
  _layoutDirection = @"ltr";
@@ -1067,6 +1101,18 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1067
1101
  return self;
1068
1102
  }
1069
1103
 
1104
+ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
1105
+ {
1106
+ if (_nativeSmoothHeaderScrollEnabled && !_isBeingRecycled &&
1107
+ _sharedHeaderHostView.superview != _containerView) {
1108
+ CGPoint headerPoint = [_sharedHeaderHostView convertPoint:point fromView:self];
1109
+ UIView *headerHitView =
1110
+ [_sharedHeaderHostView hitTest:headerPoint withEvent:event];
1111
+ if (headerHitView != nil) return headerHitView;
1112
+ }
1113
+ return [super hitTest:point withEvent:event];
1114
+ }
1115
+
1070
1116
  - (void)willMoveToSuperview:(UIView *)newSuperview
1071
1117
  {
1072
1118
  [super willMoveToSuperview:newSuperview];
@@ -1224,6 +1270,14 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1224
1270
  {
1225
1271
  if (!_nativeSmoothHeaderScrollEnabled || _transitioning || _isPagerDragging ||
1226
1272
  scrollView == nil || _isBeingRecycled) return;
1273
+ if ((UIView *)scrollView == _sharedHeaderHostView ||
1274
+ [scrollView isDescendantOfView:_sharedHeaderHostView]) {
1275
+ [self restoreSharedHeadersToContainer];
1276
+ RNCCollapsiblePagerLog([NSString stringWithFormat:
1277
+ @"header-owner=pager page=%ld reason=cycle-guard",
1278
+ (long)_currentIndex]);
1279
+ return;
1280
+ }
1227
1281
  if (_sharedHeaderScrollView == scrollView &&
1228
1282
  _sharedHeaderHostView.superview == scrollView) {
1229
1283
  [self layoutSharedHeaders];
@@ -1240,8 +1294,12 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1240
1294
  [_nativeSubHeaderView updateAncestorGesturePrecedence];
1241
1295
  _currentLogicalOffset = scrollView.contentOffset.y + scrollView.contentInset.top;
1242
1296
  [self layoutSharedHeaders];
1297
+ NSString *scrollKind = [scrollView isKindOfClass:UICollectionView.class]
1298
+ ? @"list"
1299
+ : @"scroller";
1243
1300
  RNCCollapsiblePagerLog([NSString stringWithFormat:
1244
- @"header-owner=list page=%ld offset=%.2f",
1301
+ @"header-owner=%@ page=%ld offset=%.2f",
1302
+ scrollKind,
1245
1303
  (long)_currentIndex,
1246
1304
  _currentLogicalOffset]);
1247
1305
  }
@@ -1282,13 +1340,15 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1282
1340
  CGFloat stickyY = _headerHeight;
1283
1341
  _headerView.frame = CGRectMake(0, 0, width, _headerHeight);
1284
1342
  _stickyHeaderView.frame = CGRectMake(0, stickyY, width, _stickyHeaderHeight);
1343
+ CGFloat nativeTabBarVisibleHeight = _nativeTabBarView.hidden
1344
+ ? 0
1345
+ : MIN(_stickyHeaderHeight, _nativeTabBarHeight);
1285
1346
  _nativeTabBarView.frame = CGRectMake(
1286
1347
  0,
1287
1348
  stickyY,
1288
1349
  width,
1289
- MIN(_stickyHeaderHeight, _nativeTabBarHeight)
1350
+ nativeTabBarVisibleHeight
1290
1351
  );
1291
- CGFloat nativeTabBarVisibleHeight = CGRectGetHeight(_nativeTabBarView.frame);
1292
1352
  _nativeSubHeaderView.frame = CGRectMake(
1293
1353
  0,
1294
1354
  stickyY + nativeTabBarVisibleHeight,
@@ -1307,7 +1367,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1307
1367
  pageIndex < 0 || pageIndex >= _pageControllers.count) return;
1308
1368
 
1309
1369
  _pageOffsets[[self pageKeyForIndex:pageIndex]] = @(_headerOffset);
1310
- UIScrollView *scrollView = [self findVerticalScrollViewInView:_pageControllers[pageIndex].view];
1370
+ UIScrollView *scrollView = [self verticalScrollViewForPageAtIndex:pageIndex];
1311
1371
  if (scrollView != nil && scrollView != _observedScrollView) {
1312
1372
  [self applyInsetsToScrollView:scrollView pageIndex:pageIndex restore:YES];
1313
1373
  }
@@ -1315,6 +1375,25 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1315
1375
 
1316
1376
  #pragma mark - React mounting
1317
1377
 
1378
+ - (void)mountingTransactionDidMount:(const MountingTransaction &)transaction
1379
+ withSurfaceTelemetry:(const SurfaceTelemetry &)surfaceTelemetry
1380
+ {
1381
+ if (!_nativeSmoothHeaderScrollEnabled || _transitioning ||
1382
+ _isPagerDragging || _isBeingRecycled || self.window == nil ||
1383
+ _currentIndex < 0 || _currentIndex >= _pageControllers.count) return;
1384
+
1385
+ UIView *pageView = _pageControllers[_currentIndex].view;
1386
+ BOOL shouldResolve = _observedScrollView == nil ||
1387
+ ![_observedScrollView isDescendantOfView:pageView];
1388
+ if (!shouldResolve &&
1389
+ [_observedScrollView isKindOfClass:UICollectionView.class]) {
1390
+ UIScrollView *preferred = [self findExplicitNativeScrollerInView:pageView];
1391
+ if (preferred == nil) preferred = [self findFabricScrollViewInView:pageView];
1392
+ shouldResolve = preferred != nil && preferred != _observedScrollView;
1393
+ }
1394
+ if (shouldResolve) [self attachScrollObserverForCurrentPage];
1395
+ }
1396
+
1318
1397
  - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
1319
1398
  index:(NSInteger)index
1320
1399
  {
@@ -1738,17 +1817,89 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1738
1817
  return [NSString stringWithFormat:@"page-%ld", (long)index];
1739
1818
  }
1740
1819
 
1820
+ - (UIScrollView *)currentVerticalScrollViewForParentPager
1821
+ {
1822
+ return [self verticalScrollViewForPageAtIndex:_currentIndex];
1823
+ }
1824
+
1825
+ - (UIScrollView *)verticalScrollViewForPageAtIndex:(NSInteger)pageIndex
1826
+ {
1827
+ if (pageIndex < 0 || pageIndex >= _pageControllers.count) return nil;
1828
+ UIView *pageView = _pageControllers[pageIndex].view;
1829
+ UIScrollView *explicitScroller = [self findExplicitNativeScrollerInView:pageView];
1830
+ if (explicitScroller != nil) return explicitScroller;
1831
+ UIScrollView *fabricScrollView = [self findFabricScrollViewInView:pageView];
1832
+ return fabricScrollView ?: [self findVerticalScrollViewInView:pageView];
1833
+ }
1834
+
1835
+ - (UIScrollView *)findExplicitNativeScrollerInView:(UIView *)view
1836
+ {
1837
+ if (view == _sharedHeaderHostView) return nil;
1838
+ if (view != self &&
1839
+ [view isKindOfClass:RNCCollapsiblePagerViewComponentView.class]) {
1840
+ return [(RNCCollapsiblePagerViewComponentView *)view
1841
+ currentVerticalScrollViewForParentPager];
1842
+ }
1843
+ if ([view isKindOfClass:RCTScrollViewComponentView.class]) {
1844
+ RCTScrollViewComponentView *componentView =
1845
+ (RCTScrollViewComponentView *)view;
1846
+ if ([componentView.nativeId hasPrefix:RNCCollapsiblePagerNativeScrollerIDPrefix]) {
1847
+ return componentView.scrollView;
1848
+ }
1849
+ }
1850
+ for (UIView *subview in view.subviews) {
1851
+ UIScrollView *candidate = [self findExplicitNativeScrollerInView:subview];
1852
+ if (candidate != nil) return candidate;
1853
+ }
1854
+ return nil;
1855
+ }
1856
+
1857
+ - (UIScrollView *)findFabricScrollViewInView:(UIView *)view
1858
+ {
1859
+ if (view == _sharedHeaderHostView) return nil;
1860
+ if (view != self &&
1861
+ [view isKindOfClass:RNCCollapsiblePagerViewComponentView.class]) {
1862
+ return [(RNCCollapsiblePagerViewComponentView *)view
1863
+ currentVerticalScrollViewForParentPager];
1864
+ }
1865
+ if ([view isKindOfClass:RCTScrollViewComponentView.class]) {
1866
+ UIScrollView *scrollView =
1867
+ ((RCTScrollViewComponentView *)view).scrollView;
1868
+ if (!scrollView.alwaysBounceHorizontal) {
1869
+ return scrollView;
1870
+ }
1871
+ }
1872
+ for (UIView *subview in view.subviews) {
1873
+ UIScrollView *candidate = [self findFabricScrollViewInView:subview];
1874
+ if (candidate != nil) return candidate;
1875
+ }
1876
+ return nil;
1877
+ }
1878
+
1741
1879
  - (UIScrollView *)findVerticalScrollViewInView:(UIView *)view
1742
1880
  {
1881
+ if (view == _sharedHeaderHostView) return nil;
1882
+ // A parent pager must follow the nested pager's active page instead of the
1883
+ // first retained page found by recursive subview order.
1884
+ if (view != self &&
1885
+ [view isKindOfClass:RNCCollapsiblePagerViewComponentView.class]) {
1886
+ UIScrollView *nestedScrollView =
1887
+ [(RNCCollapsiblePagerViewComponentView *)view
1888
+ currentVerticalScrollViewForParentPager];
1889
+ if (nestedScrollView != nil) return nestedScrollView;
1890
+ }
1743
1891
  if ([view isKindOfClass:UICollectionView.class]) {
1744
1892
  UICollectionView *collectionView = (UICollectionView *)view;
1745
1893
  UICollectionViewLayout *layout = collectionView.collectionViewLayout;
1746
- if (![layout isKindOfClass:UICollectionViewFlowLayout.class] ||
1747
- ((UICollectionViewFlowLayout *)layout).scrollDirection == UICollectionViewScrollDirectionVertical) {
1894
+ BOOL hasUsableBounds = CGRectGetWidth(collectionView.bounds) > 0 &&
1895
+ CGRectGetHeight(collectionView.bounds) > 0;
1896
+ if (hasUsableBounds &&
1897
+ (![layout isKindOfClass:UICollectionViewFlowLayout.class] ||
1898
+ ((UICollectionViewFlowLayout *)layout).scrollDirection == UICollectionViewScrollDirectionVertical)) {
1748
1899
  return collectionView;
1749
1900
  }
1750
1901
  }
1751
- // Empty pages may use an RN ScrollView instead of a native row list.
1902
+ // Preserve compatibility with non-Fabric or custom vertical scroll views.
1752
1903
  if ([view isKindOfClass:UIScrollView.class] &&
1753
1904
  ![view isKindOfClass:UICollectionView.class]) {
1754
1905
  UIScrollView *scrollView = (UIScrollView *)view;
@@ -1764,12 +1915,66 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
1764
1915
  return nil;
1765
1916
  }
1766
1917
 
1918
+ - (void)scheduleScrollObserverRetryForCurrentPage
1919
+ {
1920
+ if (_scrollResolveRetryScheduled || _scrollResolveRetryAttempt >= 12 ||
1921
+ _isBeingRecycled) return;
1922
+ _scrollResolveRetryScheduled = YES;
1923
+ _scrollResolveRetryPageIndex = _currentIndex;
1924
+ _scrollResolveRetryAttempt += 1;
1925
+ NSUInteger token = _scrollResolveRetryToken;
1926
+ NSUInteger generation = _generation;
1927
+ NSInteger pageIndex = _currentIndex;
1928
+ dispatch_after(
1929
+ // Fabric can mount a recycled child's native view before attaching its
1930
+ // replacement ScrollView component. Let that transaction settle before
1931
+ // accepting an automatically discovered UICollectionView fallback.
1932
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(100 * NSEC_PER_MSEC)),
1933
+ dispatch_get_main_queue(),
1934
+ ^{
1935
+ if (self->_scrollResolveRetryToken != token) return;
1936
+ self->_scrollResolveRetryScheduled = NO;
1937
+ if (self->_isBeingRecycled || self->_generation != generation ||
1938
+ self->_currentIndex != pageIndex) return;
1939
+ [self attachScrollObserverForCurrentPage];
1940
+ }
1941
+ );
1942
+ }
1943
+
1767
1944
  - (void)attachScrollObserverForCurrentPage
1768
1945
  {
1769
1946
  [self restoreDetachedScrollInsets];
1770
- if (_currentIndex < 0 || _currentIndex >= _pageControllers.count) return;
1771
- UIScrollView *candidate = [self findVerticalScrollViewInView:_pageControllers[_currentIndex].view];
1772
- if (candidate == nil) return;
1947
+ if (_scrollResolveRetryPageIndex != _currentIndex) {
1948
+ _scrollResolveRetryToken += 1;
1949
+ _scrollResolveRetryScheduled = NO;
1950
+ _scrollResolveRetryAttempt = 0;
1951
+ _scrollResolveRetryPageIndex = _currentIndex;
1952
+ _pendingFallbackScrollView = nil;
1953
+ _pendingFallbackScrollViewDetectedAt = 0;
1954
+ }
1955
+ UIScrollView *candidate = [self verticalScrollViewForPageAtIndex:_currentIndex];
1956
+ if (candidate == nil) {
1957
+ [self scheduleScrollObserverRetryForCurrentPage];
1958
+ return;
1959
+ }
1960
+ if ([candidate isKindOfClass:UICollectionView.class] &&
1961
+ candidate != _observedScrollView) {
1962
+ if (_pendingFallbackScrollView != candidate) {
1963
+ _pendingFallbackScrollView = candidate;
1964
+ _pendingFallbackScrollViewDetectedAt = CACurrentMediaTime();
1965
+ _scrollResolveRetryAttempt = 0;
1966
+ }
1967
+ if (CACurrentMediaTime() - _pendingFallbackScrollViewDetectedAt < 0.5) {
1968
+ [self scheduleScrollObserverRetryForCurrentPage];
1969
+ return;
1970
+ }
1971
+ }
1972
+ _scrollResolveRetryAttempt = 0;
1973
+ _scrollResolveRetryPageIndex = _currentIndex;
1974
+ _scrollResolveRetryToken += 1;
1975
+ _scrollResolveRetryScheduled = NO;
1976
+ _pendingFallbackScrollView = nil;
1977
+ _pendingFallbackScrollViewDetectedAt = 0;
1773
1978
  // Legacy mode keeps the historical pager-first dependency. Smooth mode
1774
1979
  // arbitrates header touches by axis so the vertical list can begin directly.
1775
1980
  if (_pagerScrollView != nil && !_nativeSmoothHeaderScrollEnabled && !_needsPropsReapply) {
@@ -2153,7 +2358,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2153
2358
  NSInteger last = MIN((NSInteger)_pageControllers.count - 1, _currentIndex + 1);
2154
2359
  if (last < first) return;
2155
2360
  for (NSInteger index = first; index <= last; index++) {
2156
- UIScrollView *scrollView = [self findVerticalScrollViewInView:_pageControllers[index].view];
2361
+ UIScrollView *scrollView = [self verticalScrollViewForPageAtIndex:index];
2157
2362
  if (scrollView != nil && scrollView != _observedScrollView) {
2158
2363
  [self applyInsetsToScrollView:scrollView pageIndex:index restore:YES];
2159
2364
  }
@@ -2356,7 +2561,7 @@ static void RNCLogNativeTabScrollBoundary(NSString *owner,
2356
2561
  if (index == NSNotFound) return nil;
2357
2562
  NSInteger target = index + delta;
2358
2563
  if (target < 0 || target >= _pageControllers.count) return nil;
2359
- UIScrollView *scrollView = [self findVerticalScrollViewInView:_pageControllers[target].view];
2564
+ UIScrollView *scrollView = [self verticalScrollViewForPageAtIndex:target];
2360
2565
  if (scrollView != nil) {
2361
2566
  [self applyInsetsToScrollView:scrollView pageIndex:target restore:YES];
2362
2567
  }
@@ -30,6 +30,9 @@ export class CollapsiblePagerView extends React.PureComponent {
30
30
  lastMountSignature = null;
31
31
  nativeTabCommandId = 0;
32
32
  latestNativeTabTarget = null;
33
+ latestNativeSelectedPage = this.state.selectedPage;
34
+ nativePagerScrollState = "idle";
35
+ nativeTabCommandQueuedDuringScroll = false;
33
36
  componentDidMount() {
34
37
  this.notifyMountedPagesChanged();
35
38
  }
@@ -77,6 +80,7 @@ export class CollapsiblePagerView extends React.PureComponent {
77
80
  }
78
81
  dispatchNativeTabPageCommand(position, animated) {
79
82
  const commandId = ++this.nativeTabCommandId;
83
+ this.nativeTabCommandQueuedDuringScroll = this.nativePagerScrollState !== "idle";
80
84
  this.latestNativeTabTarget = position;
81
85
  this.setState(state => ({
82
86
  transientRetainedPages: state.transientRetainedPages.includes(position) ? state.transientRetainedPages : [...state.transientRetainedPages, position]
@@ -126,11 +130,13 @@ export class CollapsiblePagerView extends React.PureComponent {
126
130
  };
127
131
  onPageSelected = event => {
128
132
  const position = clampedPageIndex(event.nativeEvent.position, this.pages().length);
133
+ this.latestNativeSelectedPage = position;
129
134
  if (position !== this.state.selectedPage || this.state.transientRetainedPages.length > 0) {
130
135
  const pendingTarget = this.latestNativeTabTarget;
131
136
  const transitionComplete = pendingTarget === position;
132
137
  if (transitionComplete) {
133
138
  this.latestNativeTabTarget = null;
139
+ this.nativeTabCommandQueuedDuringScroll = false;
134
140
  }
135
141
  this.setState({
136
142
  selectedPage: position,
@@ -139,7 +145,29 @@ export class CollapsiblePagerView extends React.PureComponent {
139
145
  }
140
146
  this.props.onPageSelected?.(event);
141
147
  };
148
+ onPageScrollStateChanged = event => {
149
+ const scrollState = event.nativeEvent.pageScrollState;
150
+ const queuedDuringPreviousScroll = this.nativeTabCommandQueuedDuringScroll;
151
+ this.nativePagerScrollState = scrollState;
152
+ // Native drains tab commands queued during an active drag after reporting
153
+ // that drag as idle. Preserve the newer target through that first idle.
154
+ if (scrollState === "idle" && this.latestNativeTabTarget !== null && this.latestNativeTabTarget !== this.latestNativeSelectedPage && !queuedDuringPreviousScroll) {
155
+ ++this.nativeTabCommandId;
156
+ this.latestNativeTabTarget = null;
157
+ this.setState({
158
+ transientRetainedPages: []
159
+ }, this.notifyMountedPagesChanged);
160
+ }
161
+ if (scrollState === "idle") {
162
+ this.nativeTabCommandQueuedDuringScroll = false;
163
+ }
164
+ this.props.onPageScrollStateChanged?.(event);
165
+ };
142
166
  onNativeTabPress = event => {
167
+ const position = pageIndex(event.nativeEvent.position);
168
+ if (position !== null && position >= 0 && position < this.pages().length && (position !== this.state.selectedPage || this.latestNativeTabTarget !== null)) {
169
+ this.dispatchNativeTabPageCommand(position, true);
170
+ }
143
171
  this.props.onNativeTabPress?.(event);
144
172
  };
145
173
  onNativeSubHeaderPress = event => {
@@ -156,6 +184,7 @@ export class CollapsiblePagerView extends React.PureComponent {
156
184
  pageRetentionDistance: _pageRetentionDistance,
157
185
  onMountedPagesChanged: _onMountedPagesChanged,
158
186
  onPageSelected: _onPageSelected,
187
+ onPageScrollStateChanged: _onPageScrollStateChanged,
159
188
  nativeTabBar,
160
189
  onNativeTabPress: _onNativeTabPress,
161
190
  nativeSubHeader,
@@ -194,6 +223,7 @@ export class CollapsiblePagerView extends React.PureComponent {
194
223
  pageKeys: JSON.stringify(pageKeys),
195
224
  retainedPages: JSON.stringify([...retained]),
196
225
  onPageSelected: this.onPageSelected,
226
+ onPageScrollStateChanged: this.onPageScrollStateChanged,
197
227
  nativeTabBarItems: nativeTabBarEnabled ? JSON.stringify(nativeTabBar.items) : undefined,
198
228
  nativeTabBarHeight: nativeTabBarEnabled ? nativeTabBarStyle?.height ?? 44 : undefined,
199
229
  nativeTabBarContentPaddingHorizontal: nativeTabBarEnabled ? nativeTabBarStyle?.contentPaddingHorizontal ?? 20 : undefined,
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ import * as React from "react";
4
+ import { ScrollView } from "react-native";
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ export const NATIVE_SCROLLER_NATIVE_ID_PREFIX = "rnc-collapsible-pager-native-scroller";
7
+ /**
8
+ * Primary non-virtualized vertical scroller for a CollapsiblePagerView page.
9
+ */
10
+ export const NativeScroller = /*#__PURE__*/React.forwardRef(function NativeScroller({
11
+ nativeID,
12
+ nestedScrollEnabled = true,
13
+ pagerScrollKey,
14
+ ...props
15
+ }, ref) {
16
+ const generatedId = React.useId();
17
+ const scrollKey = pagerScrollKey ?? nativeID ?? generatedId;
18
+ return /*#__PURE__*/_jsx(ScrollView, {
19
+ ...props,
20
+ ref: ref,
21
+ nativeID: `${NATIVE_SCROLLER_NATIVE_ID_PREFIX}:${scrollKey}`,
22
+ nestedScrollEnabled: nestedScrollEnabled
23
+ });
24
+ });
25
+ NativeScroller.displayName = "NativeScroller";
26
+ //# sourceMappingURL=NativeScroller.js.map
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ import * as React from "react";
4
+ import { ScrollView } from "react-native";
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ export const NativeScroller = /*#__PURE__*/React.forwardRef(function NativeScroller(inputProps, ref) {
7
+ const {
8
+ dataSet,
9
+ pagerScrollKey: _pagerScrollKey,
10
+ ...props
11
+ } = inputProps;
12
+ const webDataProps = {
13
+ dataSet: {
14
+ ...dataSet,
15
+ collapsiblePagerScroll: "true"
16
+ }
17
+ };
18
+ return /*#__PURE__*/_jsx(ScrollView, {
19
+ ...props,
20
+ ref: ref,
21
+ ...webDataProps
22
+ });
23
+ });
24
+ NativeScroller.displayName = "NativeScroller";
25
+ //# sourceMappingURL=NativeScroller.web.js.map
@@ -4,4 +4,5 @@ import { PagerView } from './PagerView';
4
4
  export default PagerView;
5
5
  export * from "./usePagerView.js";
6
6
  export * from './CollapsiblePagerView';
7
+ export * from './NativeScroller';
7
8
  //# sourceMappingURL=index.js.map
@@ -3,4 +3,5 @@
3
3
  export { PagerView as default, PagerView } from "./PagerView.web.js";
4
4
  export * from "./usePagerView.js";
5
5
  export * from "./CollapsiblePagerView.web.js";
6
+ export * from "./NativeScroller.web.js";
6
7
  //# sourceMappingURL=index.web.js.map
@@ -100,6 +100,9 @@ export declare class CollapsiblePagerView extends React.PureComponent<Collapsibl
100
100
  private lastMountSignature;
101
101
  private nativeTabCommandId;
102
102
  private latestNativeTabTarget;
103
+ private latestNativeSelectedPage;
104
+ private nativePagerScrollState;
105
+ private nativeTabCommandQueuedDuringScroll;
103
106
  componentDidMount(): void;
104
107
  componentDidUpdate(): void;
105
108
  setPage: (selectedPage: number) => void;
@@ -112,6 +115,7 @@ export declare class CollapsiblePagerView extends React.PureComponent<Collapsibl
112
115
  private mountedPages;
113
116
  private notifyMountedPagesChanged;
114
117
  private onPageSelected;
118
+ private onPageScrollStateChanged;
115
119
  private onNativeTabPress;
116
120
  private onNativeSubHeaderPress;
117
121
  render(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ import { type ScrollViewProps } from "react-native";
3
+ export declare const NATIVE_SCROLLER_NATIVE_ID_PREFIX = "rnc-collapsible-pager-native-scroller";
4
+ export interface NativeScrollerProps extends ScrollViewProps {
5
+ /** Stable identity used when a page replaces or remounts its primary scroller. */
6
+ pagerScrollKey?: string;
7
+ }
8
+ /**
9
+ * Primary non-virtualized vertical scroller for a CollapsiblePagerView page.
10
+ */
11
+ export declare const NativeScroller: React.ForwardRefExoticComponent<NativeScrollerProps & React.RefAttributes<import("react-native/types_generated/Libraries/Components/ScrollView/ScrollView").PublicScrollViewInstance>>;
12
+ //# sourceMappingURL=NativeScroller.d.ts.map
@@ -0,0 +1,5 @@
1
+ import * as React from "react";
2
+ import type { NativeScrollerProps } from "./NativeScroller";
3
+ export declare const NativeScroller: React.ForwardRefExoticComponent<NativeScrollerProps & React.RefAttributes<import("react-native/types_generated/Libraries/Components/ScrollView/ScrollView").PublicScrollViewInstance>>;
4
+ export type { NativeScrollerProps } from "./NativeScroller";
5
+ //# sourceMappingURL=NativeScroller.web.d.ts.map
@@ -3,6 +3,7 @@ import { PagerView } from './PagerView';
3
3
  export default PagerView;
4
4
  export * from './usePagerView';
5
5
  export * from './CollapsiblePagerView';
6
+ export * from './NativeScroller';
6
7
  import type { OnPageScrollEventData as PagerViewOnPageScrollEventData, OnPageSelectedEventData as PagerViewOnPageSelectedEventData, OnPageScrollStateChangedEventData as PageScrollStateChangedNativeEventData, NativeProps } from './PagerViewNativeComponent';
7
8
  export type { PagerViewOnPageScrollEventData, PagerViewOnPageSelectedEventData, PageScrollStateChangedNativeEventData, NativeProps as PagerViewProps, };
8
9
  export type PagerViewOnPageScrollEvent = ReactNative.NativeSyntheticEvent<PagerViewOnPageScrollEventData>;
@@ -2,4 +2,5 @@ export { PagerView as default, PagerView } from "./PagerView.web";
2
2
  export type { PagerViewProps, PagerViewOnPageScrollEvent, PagerViewOnPageScrollEventData, PagerViewOnPageSelectedEvent, PagerViewOnPageSelectedEventData, PageScrollStateChangedNativeEvent, PageScrollStateChangedNativeEventData, } from "./PagerView.web";
3
3
  export * from "./usePagerView";
4
4
  export * from "./CollapsiblePagerView.web";
5
+ export * from "./NativeScroller.web";
5
6
  //# sourceMappingURL=index.web.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-pager-view",
3
- "version": "3.0.122",
3
+ "version": "3.0.124",
4
4
  "description": "React Native wrapper for Android and iOS ViewPager",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",
@@ -67,7 +67,7 @@
67
67
  "typescript": "^5.9.2"
68
68
  },
69
69
  "peerDependencies": {
70
- "@onekeyfe/react-native-native-logger": "3.0.122",
70
+ "@onekeyfe/react-native-native-logger": "3.0.124",
71
71
  "react": "*",
72
72
  "react-native": "*"
73
73
  },