@onekeyfe/react-native-pager-view 6.7.0-15 → 6.7.0-17
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/README.md +5 -0
- package/android/src/androidTest/AndroidManifest.xml +7 -0
- package/android/src/androidTest/java/com/reactnativepagerview/CollapsiblePagerTabReservationTest.kt +247 -0
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +76 -21
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerNativeHeaders.kt +72 -7
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +27 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerView.kt +50 -6
- package/docs/SPEC.md +146 -1
- package/ios/RNCCollapsiblePagerViewComponentView.mm +355 -40
- package/ios/RNCNativeScrollerComponentView.h +2 -0
- package/ios/RNCNativeScrollerComponentView.mm +18 -0
- package/ios/tests/RNCCollapsiblePagerFirstFrameTests.mm +186 -0
- package/ios/tests/RNCCollapsiblePagerHitTestTests.mm +178 -0
- package/ios/tests/RNCCollapsiblePagerPressCancellationTests.mm +74 -9
- package/lib/module/CollapsiblePagerView.js +20 -4
- package/lib/module/CollapsiblePagerView.web.js +11 -2
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +13 -0
- package/lib/typescript/src/CollapsiblePagerView.d.ts +6 -1
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +2 -0
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +5 -0
- package/package.json +2 -2
- package/src/CollapsiblePagerView.tsx +68 -4
- package/src/CollapsiblePagerView.web.tsx +19 -2
- package/src/CollapsiblePagerViewNativeComponent.ts +13 -0
|
@@ -13,6 +13,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
13
13
|
- (void)nativeScrollerWillReceiveScrollCommand:(RNCNativeScrollerComponentView *)scroller;
|
|
14
14
|
- (void)nativeScroller:(RNCNativeScrollerComponentView *)scroller
|
|
15
15
|
callerInsetChangedFrom:(UIEdgeInsets)previous to:(UIEdgeInsets)next;
|
|
16
|
+
@optional
|
|
17
|
+
- (void)nativeScrollViewDidBecomeReady:(UIScrollView *)scrollView;
|
|
16
18
|
@end
|
|
17
19
|
|
|
18
20
|
NS_ASSUME_NONNULL_END
|
|
@@ -298,10 +298,23 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
- (void)notifyCoordinatorScrollViewReadyIfNeeded
|
|
302
|
+
{
|
|
303
|
+
if (_recycled || self.window == nil ||
|
|
304
|
+
CGRectGetWidth(_scrollView.bounds) <= 0 || CGRectGetHeight(_scrollView.bounds) <= 0) return;
|
|
305
|
+
for (UIView *owner = self.superview; owner != nil; owner = owner.superview) {
|
|
306
|
+
if ([owner conformsToProtocol:@protocol(RNCNativeScrollerInsetCoordinator)] &&
|
|
307
|
+
[owner respondsToSelector:@selector(nativeScrollViewDidBecomeReady:)]) {
|
|
308
|
+
[(id<RNCNativeScrollerInsetCoordinator>)owner nativeScrollViewDidBecomeReady:_scrollView];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
301
313
|
- (void)didMoveToSuperview
|
|
302
314
|
{
|
|
303
315
|
[super didMoveToSuperview];
|
|
304
316
|
[self updateTopScrollEdge];
|
|
317
|
+
[self notifyCoordinatorScrollViewReadyIfNeeded];
|
|
305
318
|
}
|
|
306
319
|
|
|
307
320
|
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
|
|
@@ -314,6 +327,10 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
314
327
|
if (!CGSizeEqualToSize(previous, _scrollView.bounds.size)) {
|
|
315
328
|
_needsContentClamp = YES;
|
|
316
329
|
[self scheduleContentClamp];
|
|
330
|
+
if ((previous.width <= 0 || previous.height <= 0) &&
|
|
331
|
+
_scrollView.bounds.size.width > 0 && _scrollView.bounds.size.height > 0) {
|
|
332
|
+
[self notifyCoordinatorScrollViewReadyIfNeeded];
|
|
333
|
+
}
|
|
317
334
|
}
|
|
318
335
|
}
|
|
319
336
|
|
|
@@ -574,6 +591,7 @@ static Event RNCScrollerMetrics(UIScrollView *scroll, CGPoint velocity, CGPoint
|
|
|
574
591
|
const auto &props = *std::static_pointer_cast<const RNCNativeScrollerProps>(_props);
|
|
575
592
|
if (props.refreshing) [_refreshControl beginRefreshing];
|
|
576
593
|
[self scheduleContentClamp];
|
|
594
|
+
[self notifyCoordinatorScrollViewReadyIfNeeded];
|
|
577
595
|
}
|
|
578
596
|
}
|
|
579
597
|
|
|
@@ -459,6 +459,138 @@ using namespace facebook::react;
|
|
|
459
459
|
XCTAssertTrue([[pager valueForKey:@"pendingPageRestoreKeys"] containsObject:@"page-1"]);
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
+
- (void)testReleasedOwnerRecoversReplacementWithoutAnotherFabricInsertion
|
|
463
|
+
{
|
|
464
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
465
|
+
// The fixture mounts page 0 directly; UIKit must not select it again.
|
|
466
|
+
[pager setValue:@YES forKey:@"hasAppliedInitialPage"];
|
|
467
|
+
RNCNativeScrollerComponentView *source = [self addScrollerToPager:pager atIndex:0];
|
|
468
|
+
source.tag = 91001;
|
|
469
|
+
[pager.window layoutIfNeeded];
|
|
470
|
+
[pager layoutIfNeeded];
|
|
471
|
+
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.15]];
|
|
472
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
473
|
+
[pager attachScrollObserverForCurrentPage];
|
|
474
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], source.scrollView);
|
|
475
|
+
|
|
476
|
+
// Finish the old owner's removal before the replacement becomes available.
|
|
477
|
+
ShadowView removedView;
|
|
478
|
+
removedView.tag = (Tag)source.tag;
|
|
479
|
+
MountingTransaction removal(1, 1, {ShadowViewMutation::RemoveMutation(1, removedView, 0)}, {});
|
|
480
|
+
SurfaceTelemetry telemetry;
|
|
481
|
+
[pager mountingTransactionWillMount:removal withSurfaceTelemetry:telemetry];
|
|
482
|
+
[source removeFromSuperview];
|
|
483
|
+
[self finishMount:pager];
|
|
484
|
+
XCTAssertNil([pager valueForKey:@"observedScrollView"]);
|
|
485
|
+
|
|
486
|
+
RNCNativeScrollerComponentView *replacement = [self addScrollerToPager:pager atIndex:0];
|
|
487
|
+
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.3]];
|
|
488
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], replacement.scrollView);
|
|
489
|
+
XCTAssertEqual([pager valueForKey:@"sharedHeaderScrollView"], replacement.scrollView);
|
|
490
|
+
XCTAssertEqualWithAccuracy(replacement.scrollView.contentOffset.y + replacement.scrollView.contentInset.top,
|
|
491
|
+
120, 0.01);
|
|
492
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], 120, 0.01);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
- (void)testCurrentPageInsertionRestartsExhaustedReplacementRecovery
|
|
496
|
+
{
|
|
497
|
+
[self assertReplacementRecoveryWithCancelledDrag:NO finalRetryPending:NO];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
- (void)testCancelledDragRetainsReplacementRecoveryFromInsertion
|
|
501
|
+
{
|
|
502
|
+
[self assertReplacementRecoveryWithCancelledDrag:YES finalRetryPending:NO];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
- (void)testInsertionInvalidatesPendingFinalReplacementRetry
|
|
506
|
+
{
|
|
507
|
+
[self assertReplacementRecoveryWithCancelledDrag:NO finalRetryPending:YES];
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
- (void)assertReplacementRecoveryWithCancelledDrag:(BOOL)cancelDrag
|
|
511
|
+
finalRetryPending:(BOOL)finalRetryPending
|
|
512
|
+
{
|
|
513
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:2];
|
|
514
|
+
[pager setValue:@YES forKey:@"hasAppliedInitialPage"];
|
|
515
|
+
RNCNativeScrollerComponentView *source = [self addScrollerToPager:pager atIndex:0];
|
|
516
|
+
source.tag = 91001;
|
|
517
|
+
UIView *page = source.superview;
|
|
518
|
+
page.tag = 92001;
|
|
519
|
+
UIViewController *otherPage = [pager valueForKey:@"pageControllers"][1];
|
|
520
|
+
otherPage.view.tag = 92002;
|
|
521
|
+
[pager.window layoutIfNeeded];
|
|
522
|
+
[pager layoutIfNeeded];
|
|
523
|
+
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.15]];
|
|
524
|
+
[pager setValue:@120 forKey:@"headerOffset"];
|
|
525
|
+
[pager attachScrollObserverForCurrentPage];
|
|
526
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], source.scrollView);
|
|
527
|
+
|
|
528
|
+
ShadowView removedView;
|
|
529
|
+
removedView.tag = (Tag)source.tag;
|
|
530
|
+
MountingTransaction removal(1, 1, {ShadowViewMutation::RemoveMutation((Tag)page.tag, removedView, 0)}, {});
|
|
531
|
+
SurfaceTelemetry telemetry;
|
|
532
|
+
[pager mountingTransactionWillMount:removal withSurfaceTelemetry:telemetry];
|
|
533
|
+
[source removeFromSuperview];
|
|
534
|
+
[self finishMount:pager];
|
|
535
|
+
NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:3];
|
|
536
|
+
while (([[pager valueForKey:@"scrollResolveRetryAttempt"] integerValue] < 12 ||
|
|
537
|
+
(!finalRetryPending && [[pager valueForKey:@"scrollResolveRetryScheduled"] boolValue])) &&
|
|
538
|
+
deadline.timeIntervalSinceNow > 0) {
|
|
539
|
+
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
|
|
540
|
+
}
|
|
541
|
+
XCTAssertEqualObjects([pager valueForKey:@"scrollResolveRetryAttempt"], @12);
|
|
542
|
+
XCTAssertEqualObjects([pager valueForKey:@"scrollResolveRetryScheduled"], @(finalRetryPending));
|
|
543
|
+
XCTAssertNotNil([pager valueForKey:@"releasedPageScrollStates"][@"page-0"]);
|
|
544
|
+
|
|
545
|
+
// Only a new insertion in the current page can reopen the exhausted budget.
|
|
546
|
+
[self finishMount:pager];
|
|
547
|
+
[pager mountingTransactionDidMount:removal withSurfaceTelemetry:telemetry];
|
|
548
|
+
UIView *replacementHost = [UIView new];
|
|
549
|
+
replacementHost.tag = 93001;
|
|
550
|
+
[otherPage.view addSubview:replacementHost];
|
|
551
|
+
ShadowView insertedView;
|
|
552
|
+
insertedView.tag = (Tag)replacementHost.tag;
|
|
553
|
+
MountingTransaction unrelated(1, 2, {ShadowViewMutation::InsertMutation((Tag)otherPage.view.tag, insertedView, 0)}, {});
|
|
554
|
+
[pager mountingTransactionDidMount:unrelated withSurfaceTelemetry:telemetry];
|
|
555
|
+
XCTAssertEqualObjects([pager valueForKey:@"scrollResolveRetryAttempt"], @12);
|
|
556
|
+
XCTAssertEqualObjects([pager valueForKey:@"scrollResolveRetryScheduled"], @(finalRetryPending));
|
|
557
|
+
|
|
558
|
+
if (cancelDrag) {
|
|
559
|
+
[pager beginPageStateTransition];
|
|
560
|
+
[pager setValue:@YES forKey:@"transitioning"];
|
|
561
|
+
[pager setValue:@YES forKey:@"isPagerDragging"];
|
|
562
|
+
}
|
|
563
|
+
[page addSubview:replacementHost];
|
|
564
|
+
MountingTransaction insertion(1, 3, {ShadowViewMutation::InsertMutation((Tag)page.tag, insertedView, 0)}, {});
|
|
565
|
+
[pager mountingTransactionDidMount:insertion withSurfaceTelemetry:telemetry];
|
|
566
|
+
XCTAssertNil([pager valueForKey:@"observedScrollView"]);
|
|
567
|
+
NSNumber *retryToken = [pager valueForKey:@"scrollResolveRetryToken"];
|
|
568
|
+
[pager mountingTransactionDidMount:insertion withSurfaceTelemetry:telemetry];
|
|
569
|
+
XCTAssertEqualObjects([pager valueForKey:@"scrollResolveRetryToken"], retryToken);
|
|
570
|
+
if (cancelDrag) {
|
|
571
|
+
XCTAssertNil([pager valueForKey:@"sharedHeaderScrollView"]);
|
|
572
|
+
XCTAssertFalse([[pager valueForKey:@"scrollResolveRetryScheduled"] boolValue]);
|
|
573
|
+
// Settle on the source page while the replacement is still unavailable.
|
|
574
|
+
[pager attachScrollObserverForCurrentPageAfterTransition:YES];
|
|
575
|
+
[pager setValue:@NO forKey:@"isPagerDragging"];
|
|
576
|
+
[pager setValue:@NO forKey:@"transitioning"];
|
|
577
|
+
[pager attachScrollObserverForCurrentPage];
|
|
578
|
+
XCTAssertEqualObjects([pager valueForKey:@"currentIndex"], @0);
|
|
579
|
+
}
|
|
580
|
+
__block RNCNativeScrollerComponentView *replacement;
|
|
581
|
+
int64_t replacementDelay = finalRetryPending ? 150 : 50;
|
|
582
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, replacementDelay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
|
|
583
|
+
replacement = [self addScrollerToPager:pager atIndex:0];
|
|
584
|
+
[replacementHost addSubview:replacement];
|
|
585
|
+
});
|
|
586
|
+
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.3]];
|
|
587
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], replacement.scrollView);
|
|
588
|
+
XCTAssertEqual([pager valueForKey:@"sharedHeaderScrollView"], replacement.scrollView);
|
|
589
|
+
XCTAssertEqualWithAccuracy(replacement.scrollView.contentOffset.y + replacement.scrollView.contentInset.top,
|
|
590
|
+
120, 0.01);
|
|
591
|
+
XCTAssertEqualWithAccuracy([[pager valueForKey:@"headerOffset"] doubleValue], 120, 0.01);
|
|
592
|
+
}
|
|
593
|
+
|
|
462
594
|
- (void)testRenamedPageDoesNotResurrectPrunedObserverCache
|
|
463
595
|
{
|
|
464
596
|
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
@@ -603,4 +735,58 @@ using namespace facebook::react;
|
|
|
603
735
|
XCTAssertEqual(removed.count, 0u);
|
|
604
736
|
}
|
|
605
737
|
|
|
738
|
+
- (void)testEmptyPageWithoutReleasedOwnerOrScrollHostDoesNotScheduleRetry
|
|
739
|
+
{
|
|
740
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
741
|
+
[pager attachScrollObserverForCurrentPage];
|
|
742
|
+
XCTAssertNil([pager valueForKey:@"observedScrollView"]);
|
|
743
|
+
XCTAssertFalse([[pager valueForKey:@"scrollResolveRetryScheduled"] boolValue]);
|
|
744
|
+
XCTAssertEqual([[pager valueForKey:@"scrollResolveRetryAttempt"] integerValue], 0);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
- (void)testLateReplacementRecoversImmediatelyOnReadinessEvenAfterRetryBudgetExpires
|
|
748
|
+
{
|
|
749
|
+
RNCCollapsiblePagerViewComponentView *pager = [self pagerWithPageCount:1];
|
|
750
|
+
RNCNativeScrollerComponentView *previous = [self addScrollerToPager:pager atIndex:0];
|
|
751
|
+
previous.tag = 91003;
|
|
752
|
+
[pager attachScrollObserverForCurrentPage];
|
|
753
|
+
UIView *sharedHeader = [pager valueForKey:@"sharedHeaderHostView"];
|
|
754
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], previous.scrollView);
|
|
755
|
+
|
|
756
|
+
ShadowView removedView;
|
|
757
|
+
removedView.tag = (Tag)previous.tag;
|
|
758
|
+
MountingTransaction removal(1, 1, {ShadowViewMutation::RemoveMutation(1, removedView, 0)}, {});
|
|
759
|
+
SurfaceTelemetry telemetry;
|
|
760
|
+
[pager mountingTransactionWillMount:removal withSurfaceTelemetry:telemetry];
|
|
761
|
+
[previous removeFromSuperview];
|
|
762
|
+
[self finishMount:pager];
|
|
763
|
+
XCTAssertNil([pager valueForKey:@"observedScrollView"]);
|
|
764
|
+
|
|
765
|
+
// Simulate retry budget exhaustion before the replacement becomes ready.
|
|
766
|
+
[pager setValue:@12 forKey:@"scrollResolveRetryAttempt"];
|
|
767
|
+
[pager setValue:@NO forKey:@"scrollResolveRetryScheduled"];
|
|
768
|
+
NSUInteger oldToken = [[pager valueForKey:@"scrollResolveRetryToken"] unsignedIntegerValue];
|
|
769
|
+
[pager setValue:@(oldToken + 1) forKey:@"scrollResolveRetryToken"];
|
|
770
|
+
|
|
771
|
+
UIViewController *page = [pager valueForKey:@"pageControllers"][0];
|
|
772
|
+
UICollectionView *lateList = [[UICollectionView alloc]
|
|
773
|
+
initWithFrame:CGRectMake(0, 0, 320, 600)
|
|
774
|
+
collectionViewLayout:[UICollectionViewFlowLayout new]];
|
|
775
|
+
[page.view addSubview:lateList];
|
|
776
|
+
|
|
777
|
+
// Readiness notification triggers immediate reconcile even after retry budget expired.
|
|
778
|
+
[[NSNotificationCenter defaultCenter]
|
|
779
|
+
postNotificationName:@"OneKeyNativeScrollViewDidBecomeReady"
|
|
780
|
+
object:lateList];
|
|
781
|
+
XCTAssertEqualObjects([pager valueForKey:@"pendingFallbackScrollView"], lateList);
|
|
782
|
+
|
|
783
|
+
// Explicit scroller readiness attaches synchronously with zero debounce delay.
|
|
784
|
+
[lateList removeFromSuperview];
|
|
785
|
+
RNCNativeScrollerComponentView *lateScroller = [self addScrollerToPager:pager atIndex:0];
|
|
786
|
+
XCTAssertEqual([pager valueForKey:@"observedScrollView"], lateScroller.scrollView);
|
|
787
|
+
XCTAssertEqual(sharedHeader.superview, lateScroller.scrollView);
|
|
788
|
+
XCTAssertEqualWithAccuracy(lateScroller.scrollView.contentInset.top, 164, 0.01);
|
|
789
|
+
XCTAssertNil([pager valueForKey:@"releasedPageScrollStates"][@"page-0"]);
|
|
790
|
+
}
|
|
791
|
+
|
|
606
792
|
@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);
|
|
@@ -668,6 +668,7 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
|
|
|
668
668
|
@property (nonatomic, strong) RNCSynthesizedTouchDriver *driver;
|
|
669
669
|
@property (nonatomic, strong) UIView *firstPage;
|
|
670
670
|
@property (nonatomic) NSUInteger longPressBegins;
|
|
671
|
+
@property (nonatomic, strong) XCTestExpectation *longPressExpectation;
|
|
671
672
|
@property (nonatomic) NSUInteger pagerPanBegins;
|
|
672
673
|
@end
|
|
673
674
|
|
|
@@ -763,6 +764,26 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
|
|
|
763
764
|
XCTAssertTrue([pager isKindOfClass:NSClassFromString(@"RNCCollapsiblePagerDirectScrollView")]);
|
|
764
765
|
}
|
|
765
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
|
+
|
|
766
787
|
- (void)recordPagerPan:(UIPanGestureRecognizer *)pan
|
|
767
788
|
{
|
|
768
789
|
if (pan.state == UIGestureRecognizerStateBegan) self.pagerPanBegins += 1;
|
|
@@ -904,6 +925,51 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
|
|
|
904
925
|
XCTAssertEqual(self.handler.events.count, eventsBeforeLift);
|
|
905
926
|
}
|
|
906
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
|
+
|
|
907
973
|
- (void)testStationaryTapKeepsReactPress
|
|
908
974
|
{
|
|
909
975
|
[self assertFixtureIsReadyForDelivery];
|
|
@@ -1001,7 +1067,10 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
|
|
|
1001
1067
|
|
|
1002
1068
|
- (void)nestedLongPress:(UILongPressGestureRecognizer *)recognizer
|
|
1003
1069
|
{
|
|
1004
|
-
if (recognizer.state == UIGestureRecognizerStateBegan)
|
|
1070
|
+
if (recognizer.state == UIGestureRecognizerStateBegan) {
|
|
1071
|
+
self.longPressBegins += 1;
|
|
1072
|
+
[self.longPressExpectation fulfill];
|
|
1073
|
+
}
|
|
1005
1074
|
}
|
|
1006
1075
|
|
|
1007
1076
|
- (CGPoint)pressPointInNestedScrollView:(UIScrollView *)scroll
|
|
@@ -1022,14 +1091,12 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
|
|
|
1022
1091
|
[pager addGestureRecognizer:longPress];
|
|
1023
1092
|
// Exercise production wiring after a root recognizer is attached.
|
|
1024
1093
|
((void (*)(id, SEL))objc_msgSend)(self.pager, NSSelectorFromString(@"connectVerticalPagerGuard"));
|
|
1094
|
+
self.longPressExpectation = [self expectationWithDescription:@"root long press recognized before finger-up"];
|
|
1025
1095
|
UITouch *touch = [self.driver beginAt:[self pressPoint]];
|
|
1026
1096
|
UIGestureRecognizer *guard = [self.pager valueForKey:@"verticalPagerGesture"];
|
|
1027
1097
|
XCTAssertEqual(guard.state, UIGestureRecognizerStatePossible);
|
|
1028
1098
|
XCTAssertEqual(pager.panGestureRecognizer.minimumNumberOfTouches, 2);
|
|
1029
|
-
|
|
1030
|
-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)),
|
|
1031
|
-
dispatch_get_main_queue(), ^{ [hold fulfill]; });
|
|
1032
|
-
[self waitForExpectations:@[hold] timeout:2];
|
|
1099
|
+
[self waitForExpectations:@[self.longPressExpectation] timeout:2];
|
|
1033
1100
|
NSLog(@"[PagerPressDelivery] rootLongPress begins=%lu state=%ld touchPhase=%ld guardState=%ld pagerPanMinTouches=%lu",
|
|
1034
1101
|
(unsigned long)self.longPressBegins, (long)longPress.state, (long)touch.phase,
|
|
1035
1102
|
(long)guard.state, (unsigned long)pager.panGestureRecognizer.minimumNumberOfTouches);
|
|
@@ -1045,14 +1112,12 @@ typedef struct __IOHIDEvent *RNCTestHIDEventRef;
|
|
|
1045
1112
|
- (void)testNestedStationaryNativeLongPressRecognizesBeforeFingerUp
|
|
1046
1113
|
{
|
|
1047
1114
|
UIScrollView *scroll = [self mountNestedHorizontalScrollView];
|
|
1115
|
+
self.longPressExpectation = [self expectationWithDescription:@"nested long press recognized before finger-up"];
|
|
1048
1116
|
UITouch *touch = [self.driver beginAt:[self pressPointInNestedScrollView:scroll]];
|
|
1049
1117
|
UIGestureRecognizer *guard = [self.pager valueForKey:@"verticalPagerGesture"];
|
|
1050
1118
|
XCTAssertEqual(guard.state, UIGestureRecognizerStatePossible);
|
|
1051
1119
|
XCTAssertEqual(scroll.panGestureRecognizer.minimumNumberOfTouches, 2);
|
|
1052
|
-
|
|
1053
|
-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)),
|
|
1054
|
-
dispatch_get_main_queue(), ^{ [hold fulfill]; });
|
|
1055
|
-
[self waitForExpectations:@[hold] timeout:2];
|
|
1120
|
+
[self waitForExpectations:@[self.longPressExpectation] timeout:2];
|
|
1056
1121
|
// The real UIKit target action must arrive while the finger is still down.
|
|
1057
1122
|
XCTAssertEqual(self.longPressBegins, 1);
|
|
1058
1123
|
XCTAssertEqual(touch.phase, UITouchPhaseBegan);
|
|
@@ -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 (
|
|
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,
|