@onekeyfe/react-native-pager-view 3.0.172 → 3.0.174
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/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +506 -56
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +4 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerView.kt +14 -0
- package/android/src/main/res/values/native_scroll_coordinator_ids.xml +2 -0
- package/docs/SPEC.md +54 -48
- package/ios/RNCCollapsiblePagerViewComponentView.mm +245 -32
- package/ios/RNCNativeScrollerComponentView.mm +6 -0
- package/ios/tests/RNCCollapsiblePagerFirstFrameTests.mm +85 -49
- package/ios/tests/RNCCollapsiblePagerRefreshControlTests.mm +14 -5
- package/lib/module/CollapsiblePagerView.js +6 -0
- package/lib/module/CollapsiblePagerView.web.js +81 -0
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +6 -0
- package/lib/typescript/src/CollapsiblePagerView.d.ts +6 -0
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +11 -0
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +1 -0
- package/package.json +2 -2
- package/src/CollapsiblePagerView.tsx +18 -0
- package/src/CollapsiblePagerView.web.tsx +170 -36
- package/src/CollapsiblePagerViewNativeComponent.ts +6 -0
|
@@ -345,6 +345,8 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
345
345
|
{
|
|
346
346
|
if (!_momentumActive) return;
|
|
347
347
|
_momentumActive = NO;
|
|
348
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:@"OneKeyNativeListScrollCommandAccepted"
|
|
349
|
+
object:_scrollView userInfo:@{@"phase": @2}];
|
|
348
350
|
[self emitScrollEvent:4 velocity:CGPointZero target:_scrollView.contentOffset];
|
|
349
351
|
}
|
|
350
352
|
|
|
@@ -443,11 +445,15 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
443
445
|
MAX(-inset.top, MIN(y,
|
|
444
446
|
MAX(-inset.top, _scrollView.contentSize.height - _scrollView.bounds.size.height + inset.bottom))));
|
|
445
447
|
if (CGPointEqualToPoint(target, _scrollView.contentOffset)) return;
|
|
448
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:@"OneKeyNativeListScrollCommandAccepted"
|
|
449
|
+
object:_scrollView userInfo:@{@"phase": @1}];
|
|
446
450
|
if (animated) {
|
|
447
451
|
_momentumActive = YES;
|
|
448
452
|
[self emitScrollEvent:3 velocity:CGPointZero target:target];
|
|
449
453
|
}
|
|
450
454
|
[_scrollView setContentOffset:target animated:animated];
|
|
455
|
+
if (!animated) [[NSNotificationCenter defaultCenter] postNotificationName:@"OneKeyNativeListScrollCommandAccepted"
|
|
456
|
+
object:_scrollView userInfo:@{@"phase": @2}];
|
|
451
457
|
}
|
|
452
458
|
|
|
453
459
|
- (void)scrollToEnd:(BOOL)animated
|
|
@@ -18,6 +18,7 @@ using namespace facebook::react;
|
|
|
18
18
|
- (void)beginPageStateTransition;
|
|
19
19
|
- (void)updatePageStateTransitionProgress:(CGFloat)progress;
|
|
20
20
|
- (void)finishPageStateTransition;
|
|
21
|
+
- (void)scrollToTop:(NSInteger)target animated:(BOOL)animated;
|
|
21
22
|
- (void)preparePageForHorizontalTransitionAtIndex:(NSInteger)index;
|
|
22
23
|
- (void)applyInsetsToScrollView:(UIScrollView *)scrollView pageIndex:(NSInteger)index restore:(BOOL)restore;
|
|
23
24
|
@end
|
|
@@ -39,6 +40,7 @@ using namespace facebook::react;
|
|
|
39
40
|
RNCNativeScrollerComponentView *scroller = [self addScrollerToPager:pager atIndex:0];
|
|
40
41
|
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
41
42
|
offsets[@"page-0"] = @660;
|
|
43
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
42
44
|
scroller.scrollView.contentSize = CGSizeMake(0, 0);
|
|
43
45
|
[pager attachScrollObserverForCurrentPage];
|
|
44
46
|
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
@@ -58,14 +60,14 @@ using namespace facebook::react;
|
|
|
58
60
|
else [scroller scrollTo:0 y:-scroller.scrollView.adjustedContentInset.top animated:NO];
|
|
59
61
|
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top, command.integerValue == 2 ? 120 : 0, 0.01);
|
|
60
62
|
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
61
|
-
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue],
|
|
63
|
+
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue], 0, 0.01);
|
|
62
64
|
XCTAssertEqual([pager valueForKey:@"observedScrollView"], scroller.scrollView);
|
|
63
65
|
XCTAssertEqualObjects([pager valueForKey:@"observedPageKey"], @"page-0");
|
|
64
66
|
}
|
|
65
67
|
// The existing observed content-size path retries the pending restoration.
|
|
66
68
|
scroller.scrollView.contentSize = CGSizeMake(320, 1500);
|
|
67
69
|
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
68
|
-
CGFloat expected = command.integerValue == 2 ? 120 : (command.boolValue ? 0 :
|
|
70
|
+
CGFloat expected = command.integerValue == 2 ? 120 : (command.boolValue ? 0 : 780);
|
|
69
71
|
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top,
|
|
70
72
|
expected, 0.01, @"command=%@ must not replay a cancelled reading position", command);
|
|
71
73
|
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], command.integerValue == 1 ? 0 : 120, 0.01);
|
|
@@ -80,6 +82,7 @@ using namespace facebook::react;
|
|
|
80
82
|
RNCNativeScrollerComponentView *other = [self addScrollerToPager:pager atIndex:1];
|
|
81
83
|
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
82
84
|
offsets[@"page-0"] = @660;
|
|
85
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
83
86
|
offsets[@"page-1"] = @360;
|
|
84
87
|
current.scrollView.contentSize = CGSizeMake(0, 0);
|
|
85
88
|
[pager attachScrollObserverForCurrentPage];
|
|
@@ -103,21 +106,51 @@ using namespace facebook::react;
|
|
|
103
106
|
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
104
107
|
offsets[@"page-0"] = @0;
|
|
105
108
|
offsets[@"page-1"] = @360;
|
|
109
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
106
110
|
[pager attachScrollObserverForCurrentPage];
|
|
107
111
|
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
108
112
|
[current scrollTo:0 y:200 - current.scrollView.contentInset.top animated:NO];
|
|
109
113
|
XCTAssertEqualWithAccuracy(current.scrollView.contentOffset.y + current.scrollView.contentInset.top, 200, 0.01);
|
|
110
|
-
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue],
|
|
114
|
+
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue], 80, 0.01);
|
|
111
115
|
XCTAssertEqualObjects(offsets[@"page-1"], @360);
|
|
112
116
|
}
|
|
113
117
|
|
|
118
|
+
- (void)testPagerTopCommandsClearOnlyCurrentReadingMemoryInBothModes
|
|
119
|
+
{
|
|
120
|
+
for (NSNumber *smooth in @[@NO, @YES]) {
|
|
121
|
+
for (NSNumber *target in @[@0, @1]) {
|
|
122
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
123
|
+
[pager setValue:smooth forKey:@"nativeSmoothHeaderScrollEnabled"];
|
|
124
|
+
RNCNativeScrollerComponentView *current = [self addScrollerToPager:pager atIndex:0];
|
|
125
|
+
[self addScrollerToPager:pager atIndex:1];
|
|
126
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
127
|
+
offsets[@"page-0"] = smooth.boolValue ? @660 : @780;
|
|
128
|
+
offsets[@"page-1"] = @360;
|
|
129
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
130
|
+
[pager attachScrollObserverForCurrentPage];
|
|
131
|
+
[pager scrollToTop:target.integerValue animated:NO];
|
|
132
|
+
CGFloat expectedQ = target.boolValue ? 120 : 0;
|
|
133
|
+
XCTAssertEqualWithAccuracy(current.scrollView.contentOffset.y + current.scrollView.contentInset.top, expectedQ, 0.01);
|
|
134
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], expectedQ, 0.01);
|
|
135
|
+
XCTAssertEqualWithAccuracy([offsets[@"page-0"] doubleValue], smooth.boolValue ? 0 : expectedQ, 0.01);
|
|
136
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @360);
|
|
137
|
+
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
138
|
+
XCTAssertNil([pager valueForKey:@"pagerTopScrollView"]);
|
|
139
|
+
// A same-target call cannot resurrect the old reading position.
|
|
140
|
+
[pager scrollToTop:target.integerValue animated:NO];
|
|
141
|
+
XCTAssertEqualWithAccuracy(current.scrollView.contentOffset.y + current.scrollView.contentInset.top, expectedQ, 0.01);
|
|
142
|
+
XCTAssertEqualObjects(offsets[@"page-1"], @360);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
114
147
|
- (void)testTransitionLayoutCannotSaveSourceOffsetIntoDestinationCache
|
|
115
148
|
{
|
|
116
149
|
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
117
150
|
RNCNativeScrollerComponentView *source = [self addScrollerToPager:pager atIndex:0];
|
|
118
151
|
RNCNativeScrollerComponentView *target = [self addScrollerToPager:pager atIndex:1];
|
|
119
152
|
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
120
|
-
offsets[@"page-0"] = @
|
|
153
|
+
offsets[@"page-0"] = @0;
|
|
121
154
|
offsets[@"page-1"] = @660;
|
|
122
155
|
[pager setValue:@120 forKey:@"headerOffset"];
|
|
123
156
|
[pager attachScrollObserverForCurrentPage];
|
|
@@ -131,49 +164,50 @@ using namespace facebook::react;
|
|
|
131
164
|
[pager setValue:@1 forKey:@"currentIndex"];
|
|
132
165
|
[pager attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
133
166
|
XCTAssertEqualWithAccuracy([offsets[@"page-1"] doubleValue], 660, 0.01);
|
|
134
|
-
XCTAssertEqualWithAccuracy(target.scrollView.contentOffset.y + target.scrollView.contentInset.top,
|
|
167
|
+
XCTAssertEqualWithAccuracy(target.scrollView.contentOffset.y + target.scrollView.contentInset.top, 780, 0.01);
|
|
135
168
|
XCTAssertNotEqual([pager valueForKey:@"observedScrollView"], source.scrollView);
|
|
136
169
|
}
|
|
137
170
|
|
|
138
|
-
- (void)
|
|
171
|
+
- (void)testSmoothTransitionKeepsGlobalHeaderAndIndependentBodyMemory
|
|
139
172
|
{
|
|
140
|
-
for (NSNumber *
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
173
|
+
for (NSNumber *collapse in @[@0, @60, @120]) {
|
|
174
|
+
for (NSNumber *body in @[@0, @60, @660]) {
|
|
175
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
176
|
+
RNCNativeScrollerComponentView *source = [self addScrollerToPager:pager atIndex:0];
|
|
177
|
+
RNCNativeScrollerComponentView *target = [self addScrollerToPager:pager atIndex:1];
|
|
178
|
+
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
179
|
+
offsets[@"page-0"] = @0;
|
|
180
|
+
offsets[@"page-1"] = body;
|
|
181
|
+
[pager setValue:collapse forKey:@"headerOffset"];
|
|
182
|
+
[pager attachScrollObserverForCurrentPage];
|
|
183
|
+
[pager beginPageStateTransition];
|
|
184
|
+
[pager setValue:@YES forKey:@"isPagerDragging"];
|
|
185
|
+
[pager setValue:@YES forKey:@"transitioning"];
|
|
186
|
+
[pager updatePageStateTransitionProgress:0.5];
|
|
187
|
+
CGFloat targetQ = collapse.doubleValue + (collapse.doubleValue == 120 ? body.doubleValue : 0);
|
|
188
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], collapse.doubleValue, 0.01);
|
|
189
|
+
XCTAssertEqualWithAccuracy(source.scrollView.contentOffset.y + source.scrollView.contentInset.top, collapse.doubleValue, 0.01);
|
|
190
|
+
XCTAssertEqualWithAccuracy(target.scrollView.contentOffset.y + target.scrollView.contentInset.top, targetQ, 0.01);
|
|
191
|
+
XCTAssertEqualObjects(offsets[@"page-0"], @0);
|
|
192
|
+
XCTAssertEqualObjects(offsets[@"page-1"], body);
|
|
193
|
+
// Cancellation restores the source display without consuming either reading memory.
|
|
194
|
+
[pager finishPageStateTransition];
|
|
195
|
+
XCTAssertEqualWithAccuracy(source.scrollView.contentOffset.y + source.scrollView.contentInset.top, collapse.doubleValue, 0.01);
|
|
196
|
+
XCTAssertEqualObjects(offsets[@"page-1"], body);
|
|
197
|
+
[pager setValue:@NO forKey:@"isPagerDragging"];
|
|
198
|
+
[pager setValue:@NO forKey:@"transitioning"];
|
|
199
|
+
[pager attachScrollObserverForCurrentPage];
|
|
200
|
+
[pager beginPageStateTransition];
|
|
201
|
+
[pager setValue:@1 forKey:@"destinationIndex"];
|
|
202
|
+
[pager preparePageForHorizontalTransitionAtIndex:1];
|
|
203
|
+
[pager updatePageStateTransitionProgress:1];
|
|
204
|
+
XCTAssertEqualWithAccuracy(source.scrollView.contentOffset.y + source.scrollView.contentInset.top, collapse.doubleValue, 0.01);
|
|
205
|
+
[pager setValue:@1 forKey:@"currentIndex"];
|
|
206
|
+
[pager attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
207
|
+
XCTAssertEqualWithAccuracy(target.scrollView.contentOffset.y + target.scrollView.contentInset.top, targetQ, 0.01);
|
|
208
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], collapse.doubleValue, 0.01);
|
|
209
|
+
XCTAssertEqualObjects(offsets[@"page-1"], body);
|
|
210
|
+
}
|
|
177
211
|
}
|
|
178
212
|
}
|
|
179
213
|
|
|
@@ -186,12 +220,13 @@ using namespace facebook::react;
|
|
|
186
220
|
offsets[@"page-1"] = @60;
|
|
187
221
|
offsets[@"page-2"] = @360;
|
|
188
222
|
offsets[@"page-3"] = @660;
|
|
223
|
+
[pager setValue:@60 forKey:@"headerOffset"];
|
|
189
224
|
[pager attachScrollObserverForCurrentPage];
|
|
190
225
|
[pager beginPageStateTransition];
|
|
191
226
|
[pager setValue:@3 forKey:@"destinationIndex"];
|
|
192
227
|
[pager preparePageForHorizontalTransitionAtIndex:3];
|
|
193
228
|
[pager updatePageStateTransitionProgress:1];
|
|
194
|
-
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue],
|
|
229
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], 60, 0.01);
|
|
195
230
|
[pager setValue:@3 forKey:@"currentIndex"];
|
|
196
231
|
[pager attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
197
232
|
XCTAssertEqualObjects(offsets[@"page-1"], @60);
|
|
@@ -200,23 +235,23 @@ using namespace facebook::react;
|
|
|
200
235
|
for (NSInteger index = 1; index < 4; index++) {
|
|
201
236
|
UIViewController *page = [pager valueForKey:@"pageControllers"][index];
|
|
202
237
|
RNCNativeScrollerComponentView *scroller = page.view.subviews.firstObject;
|
|
203
|
-
NSString *key = [NSString stringWithFormat:@"page-%ld", (long)index];
|
|
204
238
|
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top,
|
|
205
|
-
|
|
239
|
+
60, 0.01);
|
|
206
240
|
}
|
|
207
241
|
}
|
|
208
242
|
|
|
209
|
-
- (void)
|
|
243
|
+
- (void)testZeroLayoutPreservesPendingBodyRestore
|
|
210
244
|
{
|
|
211
245
|
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
212
246
|
RNCNativeScrollerComponentView *scroller = [self addScrollerToPager:pager atIndex:0];
|
|
213
247
|
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
214
248
|
offsets[@"page-0"] = @660;
|
|
249
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
215
250
|
scroller.scrollView.contentSize = CGSizeMake(0, 0);
|
|
216
251
|
[pager attachScrollObserverForCurrentPage];
|
|
217
252
|
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
218
253
|
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
219
|
-
// Releasing an observed but unmeasured identity must retain its pending
|
|
254
|
+
// Releasing an observed but unmeasured identity must retain its pending B.
|
|
220
255
|
scroller.tag = 91001;
|
|
221
256
|
ShadowView removedView;
|
|
222
257
|
removedView.tag = (Tag)scroller.tag;
|
|
@@ -229,7 +264,7 @@ using namespace facebook::react;
|
|
|
229
264
|
scroller.scrollView.contentSize = CGSizeMake(320, 1500);
|
|
230
265
|
XCTAssertEqualObjects(offsets[@"page-0"], @660);
|
|
231
266
|
XCTAssertFalse([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-0"]);
|
|
232
|
-
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top,
|
|
267
|
+
XCTAssertEqualWithAccuracy(scroller.scrollView.contentOffset.y + scroller.scrollView.contentInset.top, 780, 0.01);
|
|
233
268
|
}
|
|
234
269
|
|
|
235
270
|
- (void)testUnresolvedReplacementDoesNotCommitTemporaryClamp
|
|
@@ -239,6 +274,7 @@ using namespace facebook::react;
|
|
|
239
274
|
NSMutableDictionary *offsets = [pager valueForKey:@"pageOffsets"];
|
|
240
275
|
offsets[@"page-0"] = @0;
|
|
241
276
|
offsets[@"page-1"] = @660;
|
|
277
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
242
278
|
[pager attachScrollObserverForCurrentPage];
|
|
243
279
|
[pager beginPageStateTransition];
|
|
244
280
|
[pager setValue:@1 forKey:@"destinationIndex"];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#import <XCTest/XCTest.h>
|
|
2
2
|
#import <React/RCTViewComponentView.h>
|
|
3
3
|
#import "RNCCollapsiblePagerViewComponentView.h"
|
|
4
|
+
#import "RNCNativeScrollerComponentView.h"
|
|
4
5
|
|
|
5
6
|
@interface RNCCollapsiblePagerViewComponentView (RefreshControlTesting)
|
|
6
7
|
- (void)updateRefreshControlForScrollView:(UIScrollView *)scrollView;
|
|
@@ -8,6 +9,10 @@
|
|
|
8
9
|
- (void)attachScrollObserverForCurrentPage;
|
|
9
10
|
@end
|
|
10
11
|
|
|
12
|
+
@interface RNCNativeScrollerComponentView (RefreshControlScrollCommandTesting)
|
|
13
|
+
- (void)scrollTo:(double)x y:(double)y animated:(BOOL)animated;
|
|
14
|
+
@end
|
|
15
|
+
|
|
11
16
|
@interface RNCCollapsiblePagerRefreshControlTests : XCTestCase
|
|
12
17
|
@end
|
|
13
18
|
|
|
@@ -184,14 +189,18 @@
|
|
|
184
189
|
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithHeader:64];
|
|
185
190
|
[pager mountChildComponentView:[RCTViewComponentView new] index:0];
|
|
186
191
|
[pager mountChildComponentView:[RCTViewComponentView new] index:1];
|
|
187
|
-
|
|
192
|
+
RNCNativeScrollerComponentView *scroller = [RNCNativeScrollerComponentView new];
|
|
193
|
+
scroller.frame = CGRectMake(0, 0, 320, 600);
|
|
194
|
+
scroller.scrollView.frame = scroller.bounds;
|
|
195
|
+
UIScrollView *scroll = scroller.scrollView;
|
|
188
196
|
scroll.refreshControl = [UIRefreshControl new];
|
|
189
197
|
scroll.contentSize = CGSizeMake(320, 2000);
|
|
190
198
|
scroll.contentInset = UIEdgeInsetsMake(232, 0, 0, 0);
|
|
191
199
|
UIView *content = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 2000)];
|
|
192
200
|
[scroll addSubview:content];
|
|
193
201
|
UIViewController *page = [UIViewController new];
|
|
194
|
-
[page.view addSubview:
|
|
202
|
+
[page.view addSubview:scroller];
|
|
203
|
+
[pager addSubview:page.view];
|
|
195
204
|
NSMutableArray<UIViewController *> *pages = [pager valueForKey:@"pageControllers"];
|
|
196
205
|
[pages addObject:page];
|
|
197
206
|
[pager attachScrollObserverForCurrentPage];
|
|
@@ -204,7 +213,7 @@
|
|
|
204
213
|
XCTAssertEqualWithAccuracy(scroll.contentInset.top, 340, 0.001);
|
|
205
214
|
XCTAssertEqualWithAccuracy(headerHost.transform.ty, 0, 0.001);
|
|
206
215
|
|
|
207
|
-
|
|
216
|
+
[scroller scrollTo:0 y:500 animated:NO];
|
|
208
217
|
CGFloat baselineStickyTranslation = stickyHeader.transform.ty;
|
|
209
218
|
CGFloat baselineTabBarTranslation = tabBar.transform.ty;
|
|
210
219
|
XCTAssertEqualWithAccuracy(baselineStickyTranslation, 776, 0.001);
|
|
@@ -217,11 +226,11 @@
|
|
|
217
226
|
XCTAssertEqualWithAccuracy(tabBar.transform.ty, baselineTabBarTranslation, 0.001);
|
|
218
227
|
XCTAssertEqualWithAccuracy(CGRectGetMaxY(headerHost.frame), CGRectGetMinY(content.frame), 0.001);
|
|
219
228
|
|
|
220
|
-
// Exercise
|
|
229
|
+
// Exercise accepted native commands while the temporary refresh inset is
|
|
221
230
|
// present, including direction reversals and the sticky/unpinned boundary.
|
|
222
231
|
// The pre-fix formula adds the extra 60 points on these scroll updates.
|
|
223
232
|
for (NSNumber *nextOffset in @[@620, @450, @-300, @-340, @500]) {
|
|
224
|
-
|
|
233
|
+
[scroller scrollTo:0 y:nextOffset.doubleValue animated:NO];
|
|
225
234
|
CGFloat expectedTranslation = MAX(0, nextOffset.doubleValue + 340 - 64);
|
|
226
235
|
XCTAssertEqualWithAccuracy(headerHost.transform.ty, 0, 0.001);
|
|
227
236
|
XCTAssertEqualWithAccuracy(stickyHeader.transform.ty, expectedTranslation, 0.001);
|
|
@@ -71,6 +71,12 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
|
+
scrollToPageTop = options => {
|
|
75
|
+
if (this.nativeRef) CollapsiblePagerViewNativeCommands.scrollToTop(this.nativeRef, 0, options?.animated ?? true);
|
|
76
|
+
};
|
|
77
|
+
scrollToStickyTop = options => {
|
|
78
|
+
if (this.nativeRef) CollapsiblePagerViewNativeCommands.scrollToTop(this.nativeRef, 1, options?.animated ?? true);
|
|
79
|
+
};
|
|
74
80
|
setScrollEnabled = scrollEnabled => {
|
|
75
81
|
if (this.nativeRef) {
|
|
76
82
|
CollapsiblePagerViewNativeCommands.setScrollEnabledImperatively(this.nativeRef, scrollEnabled);
|
|
@@ -47,6 +47,7 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
47
47
|
scrollEnabled = this.props.scrollEnabled ?? true;
|
|
48
48
|
lastMountSignature = null;
|
|
49
49
|
configureFrame = null;
|
|
50
|
+
pendingTopFrame = null;
|
|
50
51
|
transitionTimer = null;
|
|
51
52
|
pageScrollState = "idle";
|
|
52
53
|
configuredScrollElement = null;
|
|
@@ -70,6 +71,7 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
70
71
|
const pageCount = this.pages().length;
|
|
71
72
|
const selectedPage = clampedPageIndex(this.state.selectedPage, pageCount);
|
|
72
73
|
if (selectedPage !== this.state.selectedPage) {
|
|
74
|
+
this.cancelPendingTopFrame();
|
|
73
75
|
this.finishPageTransition();
|
|
74
76
|
this.setState({
|
|
75
77
|
selectedPage,
|
|
@@ -81,6 +83,7 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
81
83
|
this.notifyMountedPagesChanged();
|
|
82
84
|
}
|
|
83
85
|
componentWillUnmount() {
|
|
86
|
+
this.cancelPendingTopFrame();
|
|
84
87
|
this.savePageOffset(this.state.selectedPage);
|
|
85
88
|
if (this.configureFrame !== null) {
|
|
86
89
|
cancelAnimationFrame(this.configureFrame);
|
|
@@ -99,6 +102,55 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
99
102
|
setPageWithoutAnimation = selectedPage => {
|
|
100
103
|
this.selectPage(selectedPage, false);
|
|
101
104
|
};
|
|
105
|
+
scrollToPageTop = options => {
|
|
106
|
+
this.scrollCurrentPageToTop(0, options?.animated ?? true);
|
|
107
|
+
};
|
|
108
|
+
scrollToStickyTop = options => {
|
|
109
|
+
this.scrollCurrentPageToTop(Math.max(0, this.props.headerHeight), options?.animated ?? true);
|
|
110
|
+
};
|
|
111
|
+
cancelPendingTopFrame = () => {
|
|
112
|
+
if (!this.pendingTopFrame) return;
|
|
113
|
+
const pending = this.pendingTopFrame;
|
|
114
|
+
cancelAnimationFrame(pending.frame);
|
|
115
|
+
this.pendingTopFrame = null;
|
|
116
|
+
if (this.pageScrollElement(this.state.selectedPage) === pending.element && this.pages().map(pageKey)[this.state.selectedPage] === pending.key) this.savePageOffset(this.state.selectedPage);
|
|
117
|
+
};
|
|
118
|
+
onQueuedTopWheel = event => {
|
|
119
|
+
if (event.deltaY !== 0) this.cancelPendingTopFrame();
|
|
120
|
+
};
|
|
121
|
+
onQueuedTopKey = event => {
|
|
122
|
+
if (["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "].includes(event.key)) this.cancelPendingTopFrame();
|
|
123
|
+
};
|
|
124
|
+
scrollCurrentPageToTop(offset, animated) {
|
|
125
|
+
if (this.horizontalTouch || this.transitionTimer !== null) return;
|
|
126
|
+
const element = this.pageScrollElement(this.state.selectedPage);
|
|
127
|
+
const key = this.pages().map(pageKey)[this.state.selectedPage];
|
|
128
|
+
if (!element?.isConnected || !key) return;
|
|
129
|
+
this.cancelPendingTopFrame();
|
|
130
|
+
const EventConstructor = element.ownerDocument.defaultView?.Event;
|
|
131
|
+
if (EventConstructor) element.dispatchEvent(new EventConstructor("ok-collapsible-pager-cancel-scroll-command"));
|
|
132
|
+
element.scrollTo({
|
|
133
|
+
left: element.scrollLeft,
|
|
134
|
+
top: element.scrollTop,
|
|
135
|
+
behavior: "instant"
|
|
136
|
+
});
|
|
137
|
+
const frame = requestAnimationFrame(() => {
|
|
138
|
+
if (this.pendingTopFrame?.frame !== frame) return;
|
|
139
|
+
this.pendingTopFrame = null;
|
|
140
|
+
if (!element.isConnected || this.horizontalTouch || this.transitionTimer !== null || this.pageScrollElement(this.state.selectedPage) !== element || this.pages().map(pageKey)[this.state.selectedPage] !== key) return;
|
|
141
|
+
this.pageOffsets.set(key, offset);
|
|
142
|
+
this.sharedHeaderOffset = offset;
|
|
143
|
+
element.scrollTo({
|
|
144
|
+
top: offset,
|
|
145
|
+
behavior: animated ? "smooth" : "instant"
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
this.pendingTopFrame = {
|
|
149
|
+
frame,
|
|
150
|
+
element,
|
|
151
|
+
key
|
|
152
|
+
};
|
|
153
|
+
}
|
|
102
154
|
setScrollEnabled = scrollEnabled => {
|
|
103
155
|
this.scrollEnabled = scrollEnabled;
|
|
104
156
|
};
|
|
@@ -165,8 +217,14 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
165
217
|
restoreConfiguredScrollElement() {
|
|
166
218
|
const configured = this.configuredScrollElement;
|
|
167
219
|
if (!configured) return;
|
|
220
|
+
this.cancelPendingTopFrame();
|
|
221
|
+
configured.element.removeEventListener("ok-native-list-scroll-command-accepted", this.cancelPendingTopFrame);
|
|
222
|
+
configured.element.removeEventListener("wheel", this.onQueuedTopWheel);
|
|
223
|
+
configured.element.removeEventListener("keydown", this.onQueuedTopKey);
|
|
168
224
|
configured.element.removeEventListener("scrollend", this.onVerticalScrollSettled);
|
|
169
225
|
const style = timelineStyle(configured.element);
|
|
226
|
+
configured.rangeSpacer.remove();
|
|
227
|
+
style.position = configured.position;
|
|
170
228
|
style.paddingTop = configured.paddingTop;
|
|
171
229
|
style.boxSizing = configured.boxSizing;
|
|
172
230
|
style.scrollTimelineName = configured.scrollTimelineName;
|
|
@@ -185,8 +243,23 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
185
243
|
this.restoreConfiguredScrollElement();
|
|
186
244
|
const style = timelineStyle(element);
|
|
187
245
|
const computedPaddingTop = element.ownerDocument.defaultView?.getComputedStyle(element).paddingTop || style.paddingTop || "0px";
|
|
246
|
+
const rangeSpacer = element.ownerDocument.createElement("div");
|
|
247
|
+
Object.assign(rangeSpacer.style, {
|
|
248
|
+
position: "absolute",
|
|
249
|
+
top: "0",
|
|
250
|
+
left: "0",
|
|
251
|
+
width: "1px",
|
|
252
|
+
pointerEvents: "none",
|
|
253
|
+
opacity: "0"
|
|
254
|
+
});
|
|
255
|
+
rangeSpacer.setAttribute("aria-hidden", "true");
|
|
256
|
+
const position = style.position;
|
|
257
|
+
if (element.ownerDocument.defaultView?.getComputedStyle(element).position === "static") style.position = "relative";
|
|
258
|
+
element.appendChild(rangeSpacer);
|
|
188
259
|
this.configuredScrollElement = {
|
|
189
260
|
element,
|
|
261
|
+
rangeSpacer,
|
|
262
|
+
position,
|
|
190
263
|
paddingTop: style.paddingTop,
|
|
191
264
|
basePaddingTop: computedPaddingTop,
|
|
192
265
|
boxSizing: style.boxSizing,
|
|
@@ -196,10 +269,14 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
196
269
|
pagerStickyInset: style.getPropertyValue(PAGER_STICKY_INSET)
|
|
197
270
|
};
|
|
198
271
|
element.addEventListener("scrollend", this.onVerticalScrollSettled);
|
|
272
|
+
element.addEventListener("ok-native-list-scroll-command-accepted", this.cancelPendingTopFrame);
|
|
273
|
+
element.addEventListener("wheel", this.onQueuedTopWheel);
|
|
274
|
+
element.addEventListener("keydown", this.onQueuedTopKey);
|
|
199
275
|
}
|
|
200
276
|
const style = timelineStyle(element);
|
|
201
277
|
const headerInset = Math.max(0, this.props.headerHeight);
|
|
202
278
|
const stickyInset = Math.max(0, this.props.stickyHeaderHeight);
|
|
279
|
+
if (this.configuredScrollElement) this.configuredScrollElement.rangeSpacer.style.height = `calc(100% + ${headerInset}px)`;
|
|
203
280
|
const basePaddingTop = this.configuredScrollElement?.basePaddingTop ?? "0px";
|
|
204
281
|
const paddingTop = `calc(${basePaddingTop} + ${headerInset + stickyInset}px)`;
|
|
205
282
|
const headerInsetValue = `${headerInset}px`;
|
|
@@ -223,6 +300,7 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
223
300
|
}
|
|
224
301
|
}
|
|
225
302
|
onVerticalScrollSettled = () => {
|
|
303
|
+
if (this.pendingTopFrame) return;
|
|
226
304
|
this.savePageOffset(this.state.selectedPage);
|
|
227
305
|
this.emitDiagnostics("scroll-settled");
|
|
228
306
|
};
|
|
@@ -246,6 +324,7 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
246
324
|
if (position === null || position < 0 || position >= pages.length || position === this.state.selectedPage) {
|
|
247
325
|
return;
|
|
248
326
|
}
|
|
327
|
+
this.cancelPendingTopFrame();
|
|
249
328
|
this.savePageOffset(this.state.selectedPage);
|
|
250
329
|
if (animated) this.beginPageTransition();else this.finishPageTransition();
|
|
251
330
|
this.setState({
|
|
@@ -313,7 +392,9 @@ export class CollapsiblePagerView extends React.PureComponent {
|
|
|
313
392
|
if (!touch) return;
|
|
314
393
|
const deltaX = touch.pageX - this.touchStartX;
|
|
315
394
|
const deltaY = touch.pageY - (this.touchStartY ?? touch.pageY);
|
|
395
|
+
if (Math.abs(deltaY) >= 8 && Math.abs(deltaY) > Math.abs(deltaX)) this.cancelPendingTopFrame();
|
|
316
396
|
if (Math.abs(deltaX) >= 8 && Math.abs(deltaX) > Math.abs(deltaY)) {
|
|
397
|
+
this.cancelPendingTopFrame();
|
|
317
398
|
this.horizontalTouch = true;
|
|
318
399
|
this.emitPageScrollState("dragging");
|
|
319
400
|
}
|
|
@@ -83,6 +83,11 @@ export interface NativeProps extends ViewProps {
|
|
|
83
83
|
type CollapsiblePagerViewNativeType = HostComponent<NativeProps>;
|
|
84
84
|
|
|
85
85
|
export interface NativeCommands {
|
|
86
|
+
scrollToTop: (
|
|
87
|
+
viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
|
|
88
|
+
target: Int32,
|
|
89
|
+
animated: boolean
|
|
90
|
+
) => void;
|
|
86
91
|
setPage: (
|
|
87
92
|
viewRef: React.ElementRef<CollapsiblePagerViewNativeType>,
|
|
88
93
|
selectedPage: Int32
|
|
@@ -99,6 +104,7 @@ export interface NativeCommands {
|
|
|
99
104
|
|
|
100
105
|
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
|
|
101
106
|
supportedCommands: [
|
|
107
|
+
"scrollToTop",
|
|
102
108
|
"setPage",
|
|
103
109
|
"setPageWithoutAnimation",
|
|
104
110
|
"setScrollEnabledImperatively",
|
|
@@ -113,6 +113,12 @@ export declare class CollapsiblePagerView extends React.PureComponent<Collapsibl
|
|
|
113
113
|
componentDidUpdate(): void;
|
|
114
114
|
setPage: (selectedPage: number) => void;
|
|
115
115
|
setPageWithoutAnimation: (selectedPage: number) => void;
|
|
116
|
+
scrollToPageTop: (options?: Readonly<{
|
|
117
|
+
animated?: boolean;
|
|
118
|
+
}>) => void;
|
|
119
|
+
scrollToStickyTop: (options?: Readonly<{
|
|
120
|
+
animated?: boolean;
|
|
121
|
+
}>) => void;
|
|
116
122
|
setScrollEnabled: (scrollEnabled: boolean) => void;
|
|
117
123
|
private pages;
|
|
118
124
|
private nativeTabBarEnabled;
|
|
@@ -62,6 +62,7 @@ export declare class CollapsiblePagerView extends React.PureComponent<Collapsibl
|
|
|
62
62
|
private scrollEnabled;
|
|
63
63
|
private lastMountSignature;
|
|
64
64
|
private configureFrame;
|
|
65
|
+
private pendingTopFrame;
|
|
65
66
|
private transitionTimer;
|
|
66
67
|
private pageScrollState;
|
|
67
68
|
private configuredScrollElement;
|
|
@@ -71,6 +72,16 @@ export declare class CollapsiblePagerView extends React.PureComponent<Collapsibl
|
|
|
71
72
|
componentWillUnmount(): void;
|
|
72
73
|
setPage: (selectedPage: number) => void;
|
|
73
74
|
setPageWithoutAnimation: (selectedPage: number) => void;
|
|
75
|
+
scrollToPageTop: (options?: Readonly<{
|
|
76
|
+
animated?: boolean;
|
|
77
|
+
}>) => void;
|
|
78
|
+
scrollToStickyTop: (options?: Readonly<{
|
|
79
|
+
animated?: boolean;
|
|
80
|
+
}>) => void;
|
|
81
|
+
private cancelPendingTopFrame;
|
|
82
|
+
private onQueuedTopWheel;
|
|
83
|
+
private onQueuedTopKey;
|
|
84
|
+
private scrollCurrentPageToTop;
|
|
74
85
|
setScrollEnabled: (scrollEnabled: boolean) => void;
|
|
75
86
|
private pages;
|
|
76
87
|
private retentionDistance;
|
|
@@ -62,6 +62,7 @@ export interface NativeProps extends ViewProps {
|
|
|
62
62
|
}
|
|
63
63
|
type CollapsiblePagerViewNativeType = HostComponent<NativeProps>;
|
|
64
64
|
export interface NativeCommands {
|
|
65
|
+
scrollToTop: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, target: Int32, animated: boolean) => void;
|
|
65
66
|
setPage: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, selectedPage: Int32) => void;
|
|
66
67
|
setPageWithoutAnimation: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, selectedPage: Int32) => void;
|
|
67
68
|
setScrollEnabledImperatively: (viewRef: React.ElementRef<CollapsiblePagerViewNativeType>, scrollEnabled: boolean) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-pager-view",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.174",
|
|
4
4
|
"description": "React Native wrapper for Android and iOS ViewPager",
|
|
5
5
|
"source": "./src/index.tsx",
|
|
6
6
|
"main": "./lib/module/index.js",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"typescript": "^5.9.2"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
|
-
"@onekeyfe/react-native-native-logger": "3.0.
|
|
75
|
+
"@onekeyfe/react-native-native-logger": "3.0.174",
|
|
76
76
|
"react": "*",
|
|
77
77
|
"react-native": "*"
|
|
78
78
|
},
|
|
@@ -250,6 +250,24 @@ export class CollapsiblePagerView extends React.PureComponent<
|
|
|
250
250
|
}
|
|
251
251
|
};
|
|
252
252
|
|
|
253
|
+
public scrollToPageTop = (options?: Readonly<{ animated?: boolean }>) => {
|
|
254
|
+
if (this.nativeRef)
|
|
255
|
+
CollapsiblePagerViewNativeCommands.scrollToTop(
|
|
256
|
+
this.nativeRef,
|
|
257
|
+
0,
|
|
258
|
+
options?.animated ?? true
|
|
259
|
+
);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
public scrollToStickyTop = (options?: Readonly<{ animated?: boolean }>) => {
|
|
263
|
+
if (this.nativeRef)
|
|
264
|
+
CollapsiblePagerViewNativeCommands.scrollToTop(
|
|
265
|
+
this.nativeRef,
|
|
266
|
+
1,
|
|
267
|
+
options?.animated ?? true
|
|
268
|
+
);
|
|
269
|
+
};
|
|
270
|
+
|
|
253
271
|
public setScrollEnabled = (scrollEnabled: boolean) => {
|
|
254
272
|
if (this.nativeRef) {
|
|
255
273
|
CollapsiblePagerViewNativeCommands.setScrollEnabledImperatively(
|