@onekeyfe/react-native-pager-view 6.7.0-alpha.378 → 6.7.0-alpha.383

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.
@@ -1,6 +1,7 @@
1
1
  #import <XCTest/XCTest.h>
2
2
  #import "RNCCollapsiblePagerViewComponentView.h"
3
3
  #import "RNCNativeScrollerComponentView.h"
4
+ #import <React/RCTScrollViewComponentView.h>
4
5
  #import <react/renderer/components/pagerview/Props.h>
5
6
  #import <react/renderer/mounting/MountingTransaction.h>
6
7
 
@@ -13,6 +14,7 @@ using namespace facebook::react;
13
14
  withSurfaceTelemetry:(const SurfaceTelemetry &)telemetry;
14
15
  - (UIScrollView *)findExplicitNativeScrollerInView:(UIView *)view;
15
16
  - (UIScrollView *)findVerticalScrollViewInView:(UIView *)view;
17
+ - (UIScrollView *)verticalScrollViewForPageAtIndex:(NSInteger)pageIndex;
16
18
  - (void)attachScrollObserverForCurrentPage;
17
19
  - (void)attachScrollObserverForCurrentPageAfterTransition:(BOOL)selectionSettled;
18
20
  - (void)detachScrollObserver;
@@ -789,4 +791,85 @@ using namespace facebook::react;
789
791
  XCTAssertNil([pager valueForKey:@"releasedPageScrollStates"][@"page-0"]);
790
792
  }
791
793
 
794
+ - (RCTViewComponentView *)addNativeListToPager:(RNCCollapsiblePagerViewComponentView *)pager
795
+ {
796
+ Class nativeListClass = NSClassFromString(@"HybridNativeListComponent");
797
+ XCTAssertNotNil(nativeListClass, @"The real NativeList test dependency must be linked");
798
+ RCTViewComponentView *component = [[nativeListClass alloc] init];
799
+ component.tag = 92001;
800
+ component.frame = CGRectMake(0, 0, 320, 600);
801
+ component.contentView.frame = component.bounds;
802
+ UIViewController *page = [pager valueForKey:@"pageControllers"][0];
803
+ [page.view addSubview:component];
804
+ [component.contentView layoutIfNeeded];
805
+ return component;
806
+ }
807
+
808
+ - (void)testNativeListFooterScrollViewCannotReplacePageOwner
809
+ {
810
+ RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
811
+ RCTViewComponentView *component = [self addNativeListToPager:pager];
812
+ UICollectionView *list = nil;
813
+ for (UIView *child in component.contentView.subviews) {
814
+ if ([child isKindOfClass:UICollectionView.class]) {
815
+ list = (UICollectionView *)child;
816
+ break;
817
+ }
818
+ }
819
+ XCTAssertTrue([list isKindOfClass:UICollectionView.class]);
820
+ XCTAssertGreaterThan(CGRectGetWidth(list.bounds), 0);
821
+ XCTAssertGreaterThan(CGRectGetHeight(list.bounds), 0);
822
+ XCTAssertEqual(list.delegate, (id)component.contentView);
823
+ XCTAssertTrue([list isDescendantOfView:component.contentView]);
824
+ [pager attachScrollObserverForCurrentPage];
825
+ UIView *header = [pager valueForKey:@"sharedHeaderHostView"];
826
+ XCTAssertNotNil(header);
827
+ XCTAssertEqual([pager valueForKey:@"observedScrollView"], list);
828
+ XCTAssertEqual(header.superview, list);
829
+ CGFloat inset = list.contentInset.top;
830
+ CGFloat offset = list.contentOffset.y;
831
+
832
+ RCTScrollViewComponentView *footerList = [RCTScrollViewComponentView new];
833
+ footerList.tag = 92002;
834
+ footerList.frame = CGRectMake(0, 0, 320, 400);
835
+ footerList.scrollView.frame = footerList.bounds;
836
+ footerList.scrollView.contentSize = CGSizeMake(320, 1000);
837
+ footerList.scrollView.alwaysBounceHorizontal = NO;
838
+ for (NSInteger index = 0; index < 6; index++) {
839
+ UIView *slot = index == 2 ? footerList : [UIView new];
840
+ [component mountChildComponentView:(id)slot index:index];
841
+ }
842
+ XCTAssertTrue([footerList isDescendantOfView:list]);
843
+ XCTAssertEqual([pager verticalScrollViewForPageAtIndex:0], list);
844
+ ShadowView inserted;
845
+ inserted.tag = (Tag)footerList.tag;
846
+ MountingTransaction transaction(1, 2,
847
+ {ShadowViewMutation::InsertMutation((Tag)component.tag, inserted, 2)}, {});
848
+ SurfaceTelemetry telemetry;
849
+ [pager mountingTransactionDidMount:transaction withSurfaceTelemetry:telemetry];
850
+ XCTAssertEqual([pager valueForKey:@"observedScrollView"], list);
851
+ XCTAssertEqual(header.superview, list);
852
+ XCTAssertEqualWithAccuracy(list.contentInset.top, inset, 0.01);
853
+ XCTAssertEqualWithAccuracy(list.contentOffset.y, offset, 0.01);
854
+ XCTAssertEqualWithAccuracy(footerList.scrollView.contentInset.top, 0, 0.01);
855
+ }
856
+
857
+ - (void)testExplicitScrollerSiblingStillPrecedesNativeList
858
+ {
859
+ RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
860
+ [self addNativeListToPager:pager];
861
+ RNCNativeScrollerComponentView *scroller = [self addScrollerToPager:pager atIndex:0];
862
+ XCTAssertEqual([pager verticalScrollViewForPageAtIndex:0], scroller.scrollView);
863
+ }
864
+
865
+ - (void)testPageWithoutNativeListStillDelegatesToNestedPager
866
+ {
867
+ RNCCollapsiblePagerViewComponentView *outer = [self pagerWithPageCount:1];
868
+ RNCCollapsiblePagerViewComponentView *inner = [self pagerWithPageCount:1];
869
+ RNCNativeScrollerComponentView *scroller = [self addScrollerToPager:inner atIndex:0];
870
+ UIViewController *page = [outer valueForKey:@"pageControllers"][0];
871
+ [page.view addSubview:inner];
872
+ XCTAssertEqual([outer verticalScrollViewForPageAtIndex:0], scroller.scrollView);
873
+ }
874
+
792
875
  @end
@@ -2,13 +2,35 @@
2
2
 
3
3
  #import "RNCCollapsiblePagerViewComponentView.h"
4
4
  #import "RNCPagerViewComponentView.h"
5
+ #import <React/RCTViewComponentView.h>
5
6
  #import <react/renderer/components/pagerview/Props.h>
7
+ #import <react/renderer/components/view/ViewProps.h>
6
8
 
7
9
  using namespace facebook::react;
8
10
 
9
11
  @interface RNCCollapsiblePagerViewComponentView (ScrollEdgeTesting)
10
12
  - (void)updateTopScrollEdgeForScrollView:(UIScrollView *)scrollView;
11
13
  - (void)updateScrollEdgeContentMasks;
14
+ - (void)layoutSharedHeaders;
15
+ @end
16
+
17
+ @interface UIView (NativeTabReservationTesting)
18
+ - (void)updateItemsJSON:(NSString *)itemsJSON;
19
+ - (void)updateStyleWithHeight:(CGFloat)height
20
+ variant:(NSString *)variant
21
+ contentPaddingHorizontal:(CGFloat)contentPaddingHorizontal
22
+ contentPaddingVertical:(CGFloat)contentPaddingVertical
23
+ itemSpacing:(CGFloat)itemSpacing
24
+ fontSize:(CGFloat)fontSize
25
+ fontFamily:(NSString *)fontFamily
26
+ backgroundColor:(UIColor *)backgroundColor
27
+ activeTextColor:(UIColor *)activeTextColor
28
+ inactiveTextColor:(UIColor *)inactiveTextColor
29
+ indicatorColor:(UIColor *)indicatorColor
30
+ indicatorHeight:(CGFloat)indicatorHeight
31
+ indicatorBottom:(CGFloat)indicatorBottom;
32
+ - (void)setProgress:(CGFloat)progress;
33
+ - (void)setLeftToRight:(BOOL)leftToRight;
12
34
  @end
13
35
 
14
36
  @interface RNCCollapsiblePagerHitTestTests : XCTestCase
@@ -56,6 +78,162 @@ using namespace facebook::react;
56
78
  XCTAssertEqual([self.root hitTest:CGPointMake(20, 80) withEvent:nil], self.toolbar);
57
79
  }
58
80
 
81
+ - (void)testNativeTabReservationKeepsReactToolbarAboveTabsAndResetsInRTL
82
+ {
83
+ UIView *sharedHost = [self.pager valueForKey:@"sharedHeaderHostView"];
84
+ UIView *tabBar = [self.pager valueForKey:@"nativeTabBarView"];
85
+ [tabBar updateItemsJSON:@"[{\"key\":\"a\",\"title\":\"Overview\"},"
86
+ @"{\"key\":\"b\",\"title\":\"Ethereum\"},"
87
+ @"{\"key\":\"c\",\"title\":\"BNB Chain\"},"
88
+ @"{\"key\":\"d\",\"title\":\"Polygon\"}]"];
89
+ RCTViewComponentView *sticky = [RCTViewComponentView new];
90
+ auto stickyProps = std::make_shared<ViewProps>();
91
+ stickyProps->pointerEvents = PointerEventsMode::BoxNone;
92
+ [sticky updateProps:stickyProps oldProps:nullptr];
93
+ UIButton *trailingControl = [[UIButton alloc] initWithFrame:CGRectMake(250, 0, 70, 44)];
94
+ [sticky addSubview:trailingControl];
95
+ [sharedHost addSubview:sticky];
96
+ [sharedHost addSubview:tabBar];
97
+ [self.pager setValue:sticky forKey:@"stickyHeaderView"];
98
+ [self.pager setValue:@0 forKey:@"headerHeight"];
99
+ [self.pager setValue:@44 forKey:@"stickyHeaderHeight"];
100
+ tabBar.hidden = NO;
101
+
102
+ [self.pager setValue:@64 forKey:@"nativeTabBarTrailingReservedWidth"];
103
+ [self.pager layoutSharedHeaders];
104
+ XCTAssertEqualWithAccuracy(CGRectGetWidth(sticky.frame), 320, 0.001);
105
+ XCTAssertEqualWithAccuracy(CGRectGetWidth(tabBar.frame), 256, 0.001);
106
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(tabBar.frame), 0, 0.001);
107
+ XCTAssertEqual(sharedHost.subviews.lastObject, sticky);
108
+ XCTAssertEqual([sharedHost hitTest:CGPointMake(300, 22) withEvent:nil], trailingControl);
109
+ XCTAssertTrue([tabBar pointInside:CGPointMake(250, 22) withEvent:nil]);
110
+ [tabBar layoutIfNeeded];
111
+ [tabBar setProgress:3];
112
+ UIScrollView *tabScroll = (UIScrollView *)tabBar.subviews.firstObject;
113
+ NSArray<UIButton *> *buttons = [tabScroll.subviews filteredArrayUsingPredicate:
114
+ [NSPredicate predicateWithBlock:^BOOL(UIView *view, NSDictionary *bindings) {
115
+ return [view isKindOfClass:UIButton.class];
116
+ }]];
117
+ UIButton *lastTab = buttons.lastObject;
118
+ XCTAssertNotNil(lastTab);
119
+ XCTAssertGreaterThanOrEqual(CGRectGetMinX(lastTab.frame), tabScroll.contentOffset.x - 1);
120
+ XCTAssertLessThanOrEqual(CGRectGetMaxX(lastTab.frame),
121
+ tabScroll.contentOffset.x + CGRectGetWidth(tabScroll.bounds) + 1);
122
+
123
+ [self.pager setValue:@"rtl" forKey:@"layoutDirection"];
124
+ [tabBar setLeftToRight:NO];
125
+ trailingControl.frame = CGRectMake(0, 0, 70, 44);
126
+ [self.pager layoutSharedHeaders];
127
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(tabBar.frame), 64, 0.001);
128
+ XCTAssertEqualWithAccuracy(CGRectGetMaxX(tabBar.frame), 320, 0.001);
129
+ XCTAssertEqual([sharedHost hitTest:CGPointMake(20, 22) withEvent:nil], trailingControl);
130
+
131
+ [self.pager setValue:@500 forKey:@"nativeTabBarTrailingReservedWidth"];
132
+ [self.pager layoutSharedHeaders];
133
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(tabBar.frame), 320, 0.001);
134
+ XCTAssertEqualWithAccuracy(CGRectGetWidth(tabBar.frame), 0, 0.001);
135
+
136
+ [self.pager setValue:@0 forKey:@"nativeTabBarTrailingReservedWidth"];
137
+ [self.pager layoutSharedHeaders];
138
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(tabBar.frame), 0, 0.001);
139
+ XCTAssertEqualWithAccuracy(CGRectGetWidth(tabBar.frame), 320, 0.001);
140
+ XCTAssertEqual(sharedHost.subviews.lastObject, tabBar);
141
+ }
142
+
143
+ - (void)testNativeTabReservationPropsRestoreDefaultOnRemoval
144
+ {
145
+ auto reservedProps = std::make_shared<RNCCollapsiblePagerViewProps>();
146
+ reservedProps->nativeSmoothHeaderScrollEnabled = true;
147
+ reservedProps->nativeTabBarTrailingReservedWidth = 64;
148
+ [self.pager updateProps:reservedProps oldProps:nullptr];
149
+ XCTAssertEqualWithAccuracy(
150
+ [[self.pager valueForKey:@"nativeTabBarTrailingReservedWidth"] doubleValue], 64, 0.001);
151
+
152
+ auto defaultProps = std::make_shared<RNCCollapsiblePagerViewProps>();
153
+ defaultProps->nativeSmoothHeaderScrollEnabled = true;
154
+ [self.pager updateProps:defaultProps oldProps:reservedProps];
155
+ XCTAssertEqualWithAccuracy(
156
+ [[self.pager valueForKey:@"nativeTabBarTrailingReservedWidth"] doubleValue], 0, 0.001);
157
+ }
158
+
159
+ - (void)testSegmentedNativeTabsFollowFractionalProgressAndResetToUnderline
160
+ {
161
+ UIView *tabBar = [self.pager valueForKey:@"nativeTabBarView"];
162
+ [tabBar updateItemsJSON:@"[{\"key\":\"recent\",\"title\":\"Recents\"},"
163
+ @"{\"key\":\"account\",\"title\":\"Accounts\"},"
164
+ @"{\"key\":\"book\",\"title\":\"Address Book\"}]"];
165
+ [tabBar updateStyleWithHeight:56 variant:@"segmented"
166
+ contentPaddingHorizontal:20 contentPaddingVertical:12 itemSpacing:0
167
+ fontSize:14 fontFamily:nil backgroundColor:UIColor.lightGrayColor
168
+ activeTextColor:UIColor.blackColor inactiveTextColor:UIColor.grayColor
169
+ indicatorColor:UIColor.whiteColor indicatorHeight:2 indicatorBottom:0];
170
+ tabBar.frame = CGRectMake(0, 0, 320, 56);
171
+ [tabBar layoutIfNeeded];
172
+ UIScrollView *scroll = (UIScrollView *)tabBar.subviews.firstObject;
173
+ UIView *track = scroll.subviews.firstObject;
174
+ UIView *thumb = [tabBar valueForKey:@"indicatorView"];
175
+ NSArray<UIButton *> *buttons = [scroll.subviews filteredArrayUsingPredicate:
176
+ [NSPredicate predicateWithBlock:^BOOL(UIView *view, NSDictionary *bindings) {
177
+ return [view isKindOfClass:UIButton.class];
178
+ }]];
179
+ XCTAssertEqual(buttons.count, 3);
180
+ XCTAssertFalse(scroll.scrollEnabled);
181
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(track.frame), 20, 0.01);
182
+ XCTAssertEqualWithAccuracy(CGRectGetHeight(track.frame), 32, 0.01);
183
+ XCTAssertEqualWithAccuracy(CGRectGetWidth(buttons[0].frame),
184
+ CGRectGetWidth(buttons[1].frame), 0.01);
185
+ [tabBar setProgress:0];
186
+ CGFloat firstX = CGRectGetMinX(thumb.frame);
187
+ [tabBar setProgress:1];
188
+ CGFloat secondX = CGRectGetMinX(thumb.frame);
189
+ [tabBar setProgress:0.25];
190
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(thumb.frame), firstX + (secondX - firstX) * 0.25, 0.01);
191
+ [tabBar setProgress:0.5];
192
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(thumb.frame), (firstX + secondX) / 2, 0.01);
193
+ [tabBar setProgress:0];
194
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(thumb.frame), firstX, 0.01);
195
+
196
+ [tabBar setLeftToRight:NO];
197
+ [tabBar layoutIfNeeded];
198
+ XCTAssertGreaterThan(CGRectGetMinX(buttons[0].frame), CGRectGetMinX(buttons[1].frame));
199
+ [tabBar updateItemsJSON:@"[{\"key\":\"recent\",\"title\":\"Recents (8)\"},"
200
+ @"{\"key\":\"account\",\"title\":\"Accounts\"}]"];
201
+ [tabBar layoutIfNeeded];
202
+ NSArray<UIButton *> *twoButtons = [scroll.subviews filteredArrayUsingPredicate:
203
+ [NSPredicate predicateWithBlock:^BOOL(UIView *view, NSDictionary *bindings) {
204
+ return [view isKindOfClass:UIButton.class];
205
+ }]];
206
+ XCTAssertEqual(twoButtons.count, 2);
207
+ XCTAssertEqualWithAccuracy(CGRectGetWidth(twoButtons[0].frame),
208
+ CGRectGetWidth(track.frame) / 2, 0.01);
209
+ [tabBar updateStyleWithHeight:56 variant:@"segmented"
210
+ contentPaddingHorizontal:20 contentPaddingVertical:40 itemSpacing:0
211
+ fontSize:14 fontFamily:nil backgroundColor:UIColor.lightGrayColor
212
+ activeTextColor:UIColor.blackColor inactiveTextColor:UIColor.grayColor
213
+ indicatorColor:UIColor.whiteColor indicatorHeight:2 indicatorBottom:0];
214
+ tabBar.frame = CGRectMake(0, 0, 30, 56);
215
+ [tabBar layoutIfNeeded];
216
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(track.frame), 15, 0.01);
217
+ XCTAssertEqualWithAccuracy(CGRectGetWidth(track.frame), 0, 0.01);
218
+ XCTAssertEqualWithAccuracy(CGRectGetMinY(track.frame), 28, 0.01);
219
+ XCTAssertEqualWithAccuracy(CGRectGetHeight(track.frame), 0, 0.01);
220
+ XCTAssertTrue(thumb.hidden);
221
+ [tabBar updateItemsJSON:@"[]"];
222
+ XCTAssertTrue(tabBar.hidden);
223
+
224
+ [tabBar updateItemsJSON:@"[{\"key\":\"recent\",\"title\":\"Recents\"}]"];
225
+ [tabBar updateStyleWithHeight:44 variant:@"underline"
226
+ contentPaddingHorizontal:20 contentPaddingVertical:0 itemSpacing:8
227
+ fontSize:16 fontFamily:nil backgroundColor:UIColor.clearColor
228
+ activeTextColor:UIColor.blackColor inactiveTextColor:UIColor.grayColor
229
+ indicatorColor:UIColor.blackColor indicatorHeight:2 indicatorBottom:0];
230
+ tabBar.frame = CGRectMake(0, 0, 320, 44);
231
+ [tabBar layoutIfNeeded];
232
+ XCTAssertTrue(scroll.scrollEnabled);
233
+ XCTAssertTrue(track.hidden);
234
+ XCTAssertEqualWithAccuracy(CGRectGetHeight(thumb.frame), 2, 0.01);
235
+ }
236
+
59
237
  - (void)testPinnedChildOutsideHeaderHostStillReceivesViewportTouches
60
238
  {
61
239
  XCTAssertEqual([self.pager hitTest:CGPointMake(20, 90) withEvent:nil], self.sticky);
@@ -764,6 +764,26 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
764
764
  XCTAssertTrue([pager isKindOfClass:NSClassFromString(@"RNCCollapsiblePagerDirectScrollView")]);
765
765
  }
766
766
 
767
+ - (void)configureSegmentedTabsForTwoPages
768
+ {
769
+ auto baseline = std::make_shared<RNCCollapsiblePagerViewProps>();
770
+ baseline->nativeSmoothHeaderScrollEnabled = true;
771
+ baseline->headerHeight = 120;
772
+ baseline->stickyHeaderHeight = 44;
773
+ auto segmented = std::make_shared<RNCCollapsiblePagerViewProps>();
774
+ segmented->nativeSmoothHeaderScrollEnabled = true;
775
+ segmented->headerHeight = 120;
776
+ segmented->stickyHeaderHeight = 56;
777
+ segmented->nativeTabBarItems =
778
+ R"([{"key":"recent","title":"Recents"},{"key":"account","title":"Accounts"}])";
779
+ segmented->nativeTabBarVariant = "segmented";
780
+ segmented->nativeTabBarHeight = 56;
781
+ segmented->nativeTabBarContentPaddingHorizontal = 20;
782
+ segmented->nativeTabBarContentPaddingVertical = 12;
783
+ [self.pager updateProps:segmented oldProps:baseline];
784
+ [self.window layoutIfNeeded];
785
+ }
786
+
767
787
  - (void)recordPagerPan:(UIPanGestureRecognizer *)pan
768
788
  {
769
789
  if (pan.state == UIGestureRecognizerStateBegan) self.pagerPanBegins += 1;
@@ -905,6 +925,51 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
905
925
  XCTAssertEqual(self.handler.events.count, eventsBeforeLift);
906
926
  }
907
927
 
928
+ - (void)testSegmentedThumbTracksDeliveredPagerDragAndReverseBeforeLift
929
+ {
930
+ [self assertFixtureIsReadyForDelivery];
931
+ [self assertDirectPagerForRegression];
932
+ [self configureSegmentedTabsForTwoPages];
933
+
934
+ UIScrollView *pagerScrollView = [self.pager valueForKey:@"pagerScrollView"];
935
+ UIView *tabBar = [self.pager valueForKey:@"nativeTabBarView"];
936
+ UIView *thumb = [tabBar valueForKey:@"indicatorView"];
937
+ NSArray<UIButton *> *buttons = [tabBar valueForKey:@"buttons"];
938
+ XCTAssertEqual(buttons.count, 2);
939
+ XCTAssertFalse(tabBar.hidden);
940
+ XCTAssertEqualWithAccuracy(CGRectGetHeight(tabBar.bounds), 56, 0.01);
941
+ CGFloat firstThumbX = CGRectGetMinX(buttons[0].frame) + 2;
942
+ CGFloat secondThumbX = CGRectGetMinX(buttons[1].frame) + 2;
943
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(thumb.frame), firstThumbX, 1);
944
+
945
+ CGPoint point = CGPointMake(350, 600);
946
+ UITouch *touch = [self.driver beginAt:point];
947
+ CGFloat largestOffset = 0;
948
+ NSUInteger intermediateFrames = 0;
949
+ for (NSInteger step = 1; step <= 18; ++step) {
950
+ [self.driver move:touch to:CGPointMake(point.x - 14 * step, point.y)];
951
+ CGFloat offset = pagerScrollView.contentOffset.x;
952
+ largestOffset = MAX(largestOffset, offset);
953
+ if (offset > 1 && offset < CGRectGetWidth(pagerScrollView.bounds) - 1) {
954
+ CGFloat progress = offset / CGRectGetWidth(pagerScrollView.bounds);
955
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(thumb.frame),
956
+ firstThumbX + (secondThumbX - firstThumbX) * progress, 1);
957
+ intermediateFrames += 1;
958
+ }
959
+ }
960
+ XCTAssertTrue(pagerScrollView.isDragging);
961
+ XCTAssertGreaterThan(largestOffset, 80);
962
+ XCTAssertGreaterThan(intermediateFrames, 2);
963
+
964
+ for (NSInteger step = 17; step >= 0; --step) {
965
+ [self.driver move:touch to:CGPointMake(point.x - 14 * step, point.y)];
966
+ }
967
+ // UIKit can briefly bounce past the leading edge before the finger lifts.
968
+ XCTAssertLessThan(fabs(pagerScrollView.contentOffset.x), 10);
969
+ XCTAssertEqualWithAccuracy(CGRectGetMinX(thumb.frame), firstThumbX, 1);
970
+ [self.driver end:touch];
971
+ }
972
+
908
973
  - (void)testStationaryTapKeepsReactPress
909
974
  {
910
975
  [self assertFixtureIsReadyForDelivery];
@@ -82,6 +82,12 @@ export class CollapsiblePagerView extends React.PureComponent {
82
82
  scrollToPageTop = options => {
83
83
  if (this.nativeRef) CollapsiblePagerViewNativeCommands.scrollToTop(this.nativeRef, 0, options?.animated ?? true);
84
84
  };
85
+ scrollHeaderBy = delta => {
86
+ if (this.nativeRef && Number.isFinite(delta)) CollapsiblePagerViewNativeCommands.scrollHeaderBy(this.nativeRef, delta);
87
+ };
88
+ scrollHeaderToOffset = offset => {
89
+ if (this.nativeRef && Number.isFinite(offset)) CollapsiblePagerViewNativeCommands.scrollHeaderToOffset(this.nativeRef, offset);
90
+ };
85
91
  scrollToStickyTop = options => {
86
92
  if (this.nativeRef) CollapsiblePagerViewNativeCommands.scrollToTop(this.nativeRef, 1, options?.animated ?? true);
87
93
  };
@@ -96,7 +102,7 @@ export class CollapsiblePagerView extends React.PureComponent {
96
102
  nativeTabBarEnabled() {
97
103
  return (Platform.OS === "ios" || Platform.OS === "android") && !!this.props.nativeTabBar?.items.length;
98
104
  }
99
- dispatchNativeTabPageCommand(position, animated) {
105
+ dispatchNativeTabPageCommand(position, animated, expectedKey) {
100
106
  const commandId = ++this.nativeTabCommandId;
101
107
  this.nativeTabCommandQueuedDuringScroll = this.nativePagerScrollState !== "idle";
102
108
  this.latestNativeTabTarget = position;
@@ -104,7 +110,7 @@ export class CollapsiblePagerView extends React.PureComponent {
104
110
  transientRetainedPages: state.transientRetainedPages.includes(position) ? state.transientRetainedPages : [...state.transientRetainedPages, position]
105
111
  }), () => {
106
112
  requestAnimationFrame(() => {
107
- if (commandId !== this.nativeTabCommandId || !this.nativeRef) {
113
+ if (commandId !== this.nativeTabCommandId || !this.nativeRef || expectedKey !== undefined && (!this.nativeTabBarEnabled() || this.props.nativeTabBar?.items[position]?.key !== expectedKey)) {
108
114
  return;
109
115
  }
110
116
  this.dispatchedNativeTabTarget = position;
@@ -186,10 +192,13 @@ export class CollapsiblePagerView extends React.PureComponent {
186
192
  };
187
193
  onNativeTabPress = event => {
188
194
  const position = pageIndex(event.nativeEvent.position);
189
- if (position !== null && position >= 0 && position < this.pages().length && (position !== this.state.selectedPage || this.latestNativeTabTarget !== null)) {
195
+ if (!this.nativeTabBarEnabled() || position === null || this.props.nativeTabBar?.items[position]?.key !== event.nativeEvent.key) {
196
+ return;
197
+ }
198
+ if (position >= 0 && position < this.pages().length && (position !== this.state.selectedPage || this.latestNativeTabTarget !== null)) {
190
199
  // Selection state lags an in-flight transition until native reports it.
191
200
  const currentPage = this.dispatchedNativeTabTarget ?? this.state.selectedPage;
192
- this.dispatchNativeTabPageCommand(position, this.props.nativeTabPressAnimationEnabled ?? Math.abs(position - currentPage) <= 1);
201
+ this.dispatchNativeTabPageCommand(position, this.props.nativeTabPressAnimationEnabled ?? Math.abs(position - currentPage) <= 1, event.nativeEvent.key);
193
202
  }
194
203
  this.props.onNativeTabPress?.(event);
195
204
  };
@@ -233,6 +242,10 @@ export class CollapsiblePagerView extends React.PureComponent {
233
242
  const deducedLayoutDirection = !layoutDirection || layoutDirection === "locale" ? I18nManager.isRTL ? "rtl" : "ltr" : layoutDirection;
234
243
  const nativeTabBarEnabled = (Platform.OS === "ios" || Platform.OS === "android") && !!nativeTabBar?.items.length;
235
244
  const nativeTabBarStyle = nativeTabBar?.style;
245
+ const requestedContentPaddingVertical = nativeTabBarStyle?.contentPaddingVertical;
246
+ const nativeTabBarContentPaddingVertical = typeof requestedContentPaddingVertical === "number" && Number.isFinite(requestedContentPaddingVertical) && requestedContentPaddingVertical >= 0 ? requestedContentPaddingVertical : 0;
247
+ const requestedTrailingReservedWidth = nativeTabBarStyle?.trailingReservedWidth;
248
+ const nativeTabBarTrailingReservedWidth = typeof requestedTrailingReservedWidth === "number" && Number.isFinite(requestedTrailingReservedWidth) && requestedTrailingReservedWidth >= 0 ? requestedTrailingReservedWidth : 0;
236
249
  const nativeSubHeaderEnabled = (Platform.OS === "ios" || Platform.OS === "android") && !!nativeSubHeader?.items.length;
237
250
  const nativeSubHeaderStyle = nativeSubHeader?.style;
238
251
  const nativeSubHeaderConfig = nativeSubHeaderEnabled ? JSON.stringify({
@@ -263,7 +276,10 @@ export class CollapsiblePagerView extends React.PureComponent {
263
276
  onPageScrollStateChanged: this.onPageScrollStateChanged,
264
277
  nativeTabBarItems: nativeTabBarEnabled ? JSON.stringify(nativeTabBar.items) : undefined,
265
278
  nativeTabBarHeight: nativeTabBarEnabled ? nativeTabBarStyle?.height ?? 44 : undefined,
279
+ nativeTabBarVariant: nativeTabBarEnabled ? nativeTabBarStyle?.variant ?? "underline" : undefined,
280
+ nativeTabBarTrailingReservedWidth: nativeTabBarEnabled ? nativeTabBarTrailingReservedWidth : 0,
266
281
  nativeTabBarContentPaddingHorizontal: nativeTabBarEnabled ? nativeTabBarStyle?.contentPaddingHorizontal ?? 20 : undefined,
282
+ nativeTabBarContentPaddingVertical: nativeTabBarEnabled ? nativeTabBarContentPaddingVertical : undefined,
267
283
  nativeTabBarItemSpacing: nativeTabBarEnabled ? nativeTabBarStyle?.itemSpacing ?? 8 : undefined,
268
284
  nativeTabBarFontSize: nativeTabBarEnabled ? nativeTabBarStyle?.fontSize ?? 16 : undefined,
269
285
  nativeTabBarFontFamily: nativeTabBarEnabled ? nativeTabBarStyle?.fontFamily : undefined,
@@ -335,6 +335,15 @@ export class CollapsiblePagerView extends React.PureComponent {
335
335
  scrollToPageTop = options => {
336
336
  this.scrollCurrentPageToTop(0, options?.animated ?? true);
337
337
  };
338
+ scrollHeaderBy = delta => {
339
+ if (!Number.isFinite(delta)) return;
340
+ const offset = Math.min(this.headerHeight, Math.max(0, this.sharedHeaderOffset + delta));
341
+ this.scrollCurrentPageToTop(offset, false);
342
+ };
343
+ scrollHeaderToOffset = offset => {
344
+ if (!Number.isFinite(offset)) return;
345
+ this.scrollCurrentPageToTop(Math.min(this.headerHeight, Math.max(0, offset)), false);
346
+ };
338
347
  scrollToStickyTop = options => {
339
348
  this.scrollCurrentPageToTop(this.headerHeight, options?.animated ?? true);
340
349
  };
@@ -386,13 +395,13 @@ export class CollapsiblePagerView extends React.PureComponent {
386
395
  this.pendingTopFrame = null;
387
396
  if (!element.isConnected || this.horizontalTouch || this.transitionTimer !== null || this.pageScrollElement(this.state.selectedPage) !== element || this.pages().map(pageKey)[this.state.selectedPage] !== key) return;
388
397
  this.pageOffsets.set(key, 0);
389
- this.sharedHeaderOffset = offset;
398
+ this.sharedHeaderOffset = Math.min(this.headerHeight, Math.max(0, offset));
390
399
  this.bodyAlignment = {
391
400
  element,
392
401
  key
393
402
  };
394
403
  scrollElementTo(element, {
395
- top: offset,
404
+ top: this.sharedHeaderOffset,
396
405
  behavior: animated ? "smooth" : "instant"
397
406
  });
398
407
  this.syncFallbackHeader();
@@ -67,7 +67,10 @@ export interface NativeProps extends ViewProps {
67
67
  retainedPages?: string;
68
68
  nativeTabBarItems?: string;
69
69
  nativeTabBarHeight?: Double;
70
+ nativeTabBarVariant?: string;
71
+ nativeTabBarTrailingReservedWidth?: WithDefault<Double, 0>;
70
72
  nativeTabBarContentPaddingHorizontal?: Double;
73
+ nativeTabBarContentPaddingVertical?: Double;
71
74
  nativeTabBarItemSpacing?: Double;
72
75
  nativeTabBarFontSize?: Double;
73
76
  nativeTabBarFontFamily?: string;
@@ -91,6 +94,14 @@ export interface NativeProps extends ViewProps {
91
94
  type CollapsiblePagerViewNativeType = HostComponent<NativeProps>;
92
95
 
93
96
  export interface NativeCommands {
97
+ scrollHeaderToOffset: (
98
+ viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
99
+ offset: Double
100
+ ) => void;
101
+ scrollHeaderBy: (
102
+ viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
103
+ delta: Double
104
+ ) => void;
94
105
  scrollToTop: (
95
106
  viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
96
107
  target: Int32,
@@ -112,6 +123,8 @@ export interface NativeCommands {
112
123
 
113
124
  export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
114
125
  supportedCommands: [
126
+ "scrollHeaderToOffset",
127
+ "scrollHeaderBy",
115
128
  "scrollToTop",
116
129
  "setPage",
117
130
  "setPageWithoutAnimation",
@@ -16,8 +16,11 @@ export type CollapsiblePagerNativeTabBarItem = Readonly<{
16
16
  testID?: string;
17
17
  }>;
18
18
  export type CollapsiblePagerNativeTabBarStyle = Readonly<{
19
+ variant?: "underline" | "segmented";
19
20
  height?: number;
21
+ trailingReservedWidth?: number;
20
22
  contentPaddingHorizontal?: number;
23
+ contentPaddingVertical?: number;
21
24
  itemSpacing?: number;
22
25
  fontSize?: number;
23
26
  fontFamily?: string;
@@ -56,7 +59,7 @@ export type CollapsiblePagerNativeSubHeaderConfig = Readonly<{
56
59
  }>;
57
60
  export type CollapsiblePagerViewOnNativeTabPressEvent = ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>;
58
61
  export type CollapsiblePagerViewOnNativeSubHeaderPressEvent = ReactNative.NativeSyntheticEvent<OnNativeTabPressEventData>;
59
- export interface CollapsiblePagerViewProps extends Omit<NativeProps, "children" | "headerHeight" | "stickyHeaderHeight" | "topScrollEdgeEffect" | "topOverlayHeight" | "externalTopOverlayTag" | "pageKeys" | "retainedPages" | "nativeTabBarItems" | "nativeTabBarHeight" | "nativeTabBarContentPaddingHorizontal" | "nativeTabBarItemSpacing" | "nativeTabBarFontSize" | "nativeTabBarFontFamily" | "nativeTabBarBackgroundColor" | "nativeTabBarActiveTextColor" | "nativeTabBarInactiveTextColor" | "nativeTabBarIndicatorColor" | "nativeTabBarIndicatorHeight" | "nativeTabBarIndicatorBottom" | "nativeSubHeaderConfig" | "nativeSubHeaderSelectedBackgroundColor" | "onNativeTabPress" | "onNativeSubHeaderPress" | "layoutDirection"> {
62
+ export interface CollapsiblePagerViewProps extends Omit<NativeProps, "children" | "headerHeight" | "stickyHeaderHeight" | "topScrollEdgeEffect" | "topOverlayHeight" | "externalTopOverlayTag" | "pageKeys" | "retainedPages" | "nativeTabBarItems" | "nativeTabBarHeight" | "nativeTabBarVariant" | "nativeTabBarTrailingReservedWidth" | "nativeTabBarContentPaddingHorizontal" | "nativeTabBarContentPaddingVertical" | "nativeTabBarItemSpacing" | "nativeTabBarFontSize" | "nativeTabBarFontFamily" | "nativeTabBarBackgroundColor" | "nativeTabBarActiveTextColor" | "nativeTabBarInactiveTextColor" | "nativeTabBarIndicatorColor" | "nativeTabBarIndicatorHeight" | "nativeTabBarIndicatorBottom" | "nativeSubHeaderConfig" | "nativeSubHeaderSelectedBackgroundColor" | "onNativeTabPress" | "onNativeSubHeaderPress" | "layoutDirection"> {
60
63
  /** Collapsible content rendered above the sticky bar. */
61
64
  header: React.ReactNode;
62
65
  /** Bar which remains pinned after the header has collapsed. */
@@ -128,6 +131,8 @@ export declare class CollapsiblePagerView extends React.PureComponent<Collapsibl
128
131
  scrollToPageTop: (options?: Readonly<{
129
132
  animated?: boolean;
130
133
  }>) => void;
134
+ scrollHeaderBy: (delta: number) => void;
135
+ scrollHeaderToOffset: (offset: number) => void;
131
136
  scrollToStickyTop: (options?: Readonly<{
132
137
  animated?: boolean;
133
138
  }>) => void;
@@ -132,6 +132,8 @@ export declare class CollapsiblePagerView extends React.PureComponent<Collapsibl
132
132
  scrollToPageTop: (options?: Readonly<{
133
133
  animated?: boolean;
134
134
  }>) => void;
135
+ scrollHeaderBy: (delta: number) => void;
136
+ scrollHeaderToOffset: (offset: number) => void;
135
137
  scrollToStickyTop: (options?: Readonly<{
136
138
  animated?: boolean;
137
139
  }>) => void;
@@ -50,7 +50,10 @@ export interface NativeProps extends ViewProps {
50
50
  retainedPages?: string;
51
51
  nativeTabBarItems?: string;
52
52
  nativeTabBarHeight?: Double;
53
+ nativeTabBarVariant?: string;
54
+ nativeTabBarTrailingReservedWidth?: WithDefault<Double, 0>;
53
55
  nativeTabBarContentPaddingHorizontal?: Double;
56
+ nativeTabBarContentPaddingVertical?: Double;
54
57
  nativeTabBarItemSpacing?: Double;
55
58
  nativeTabBarFontSize?: Double;
56
59
  nativeTabBarFontFamily?: string;
@@ -72,6 +75,8 @@ export interface NativeProps extends ViewProps {
72
75
  }
73
76
  type CollapsiblePagerViewNativeType = HostComponent<NativeProps>;
74
77
  export interface NativeCommands {
78
+ scrollHeaderToOffset: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, offset: Double) => void;
79
+ scrollHeaderBy: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, delta: Double) => void;
75
80
  scrollToTop: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, target: Int32, animated: boolean) => void;
76
81
  setPage: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, selectedPage: Int32) => void;
77
82
  setPageWithoutAnimation: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, selectedPage: Int32) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-pager-view",
3
- "version": "6.7.0-alpha.378",
3
+ "version": "6.7.0-alpha.383",
4
4
  "description": "React Native wrapper for Android and iOS ViewPager",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",
@@ -73,7 +73,7 @@
73
73
  "typescript": "^5.9.2"
74
74
  },
75
75
  "peerDependencies": {
76
- "@onekeyfe/react-native-native-logger": "6.7.0-alpha.378",
76
+ "@onekeyfe/react-native-native-logger": "6.7.0-alpha.383",
77
77
  "react": "*",
78
78
  "react-native": "*"
79
79
  },
@@ -19,6 +19,7 @@ Pod::Spec.new do |s|
19
19
 
20
20
  s.test_spec "Tests" do |test_spec|
21
21
  test_spec.source_files = "ios/tests/**/*.{m,mm}"
22
+ test_spec.dependency "react-native-native-list"
22
23
  end
23
24
 
24
25
  install_modules_dependencies(s)