@lodev09/react-native-true-sheet 3.11.0-beta.11 → 3.11.0-beta.13

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.
@@ -192,6 +192,11 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
192
192
  private val jsTouchDispatcher = JSTouchDispatcher(this)
193
193
  private val jsPointerDispatcher = JSPointerDispatcher(this)
194
194
 
195
+ // True when the active touch stream began inside the footer. Latched on
196
+ // ACTION_DOWN so subsequent MOVE events keep routing to the footer even if
197
+ // the finger drifts outside the footer's rect mid-gesture.
198
+ private var footerOwnsTouchStream = false
199
+
195
200
  private val eventDispatcher
196
201
  get() = delegate?.eventDispatcher
197
202
 
@@ -1291,26 +1296,36 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
1291
1296
 
1292
1297
  override fun dispatchTouchEvent(event: MotionEvent): Boolean {
1293
1298
  val footer = containerView?.footerView
1299
+ val action = event.actionMasked
1300
+
1294
1301
  if (footer != null && footer.isShown) {
1295
- val footerLocation = ScreenUtils.getScreenLocation(footer)
1296
- val touchX = event.rawX.toInt()
1297
- val touchY = event.rawY.toInt()
1298
-
1299
- if (touchX >= footerLocation[0] &&
1300
- touchX <= footerLocation[0] + footer.width &&
1301
- touchY >= footerLocation[1] &&
1302
- touchY <= footerLocation[1] + footer.height
1303
- ) {
1302
+ if (action == MotionEvent.ACTION_DOWN) {
1303
+ val loc = ScreenUtils.getScreenLocation(footer)
1304
+ val x = event.rawX.toInt()
1305
+ val y = event.rawY.toInt()
1306
+ footerOwnsTouchStream = x >= loc[0] &&
1307
+ x <= loc[0] + footer.width &&
1308
+ y >= loc[1] &&
1309
+ y <= loc[1] + footer.height
1310
+ }
1311
+
1312
+ if (footerOwnsTouchStream) {
1313
+ val loc = ScreenUtils.getScreenLocation(footer)
1304
1314
  val localEvent = MotionEvent.obtain(event)
1305
- localEvent.setLocation(
1306
- (touchX - footerLocation[0]).toFloat(),
1307
- (touchY - footerLocation[1]).toFloat()
1308
- )
1315
+ localEvent.setLocation(event.rawX - loc[0], event.rawY - loc[1])
1309
1316
  val handled = footer.dispatchTouchEvent(localEvent)
1310
1317
  localEvent.recycle()
1318
+
1319
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
1320
+ footerOwnsTouchStream = false
1321
+ }
1311
1322
  if (handled) return true
1312
1323
  }
1313
1324
  }
1325
+
1326
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
1327
+ footerOwnsTouchStream = false
1328
+ }
1314
1329
  return super.dispatchTouchEvent(event)
1315
1330
  }
1316
1331
 
@@ -36,6 +36,14 @@ class TrueSheetCoordinatorLayout(context: Context) :
36
36
  private var initialY = 0f
37
37
  private var activePointerId = 0
38
38
 
39
+ // Horizontal-dominance tracking. iOS lets horizontal child gestures win over
40
+ // the sheet's vertical pan; we mirror that by locking out BottomSheetBehavior
41
+ // interception once the first significant movement of a stream is horizontal.
42
+ private var streamInitialX = 0f
43
+ private var streamInitialY = 0f
44
+ private var streamHorizontalLocked = false
45
+ private var streamDirectionDecided = false
46
+
39
47
  init {
40
48
  layoutParams = LayoutParams(
41
49
  LayoutParams.MATCH_PARENT,
@@ -94,8 +102,35 @@ class TrueSheetCoordinatorLayout(context: Context) :
94
102
  * See: https://github.com/facebook/react-native/pull/44099
95
103
  */
96
104
  override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
97
- if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
105
+ val action = ev.actionMasked
106
+
107
+ if (action == MotionEvent.ACTION_DOWN) {
98
108
  clearStaleNestedScrollingChildRef()
109
+
110
+ streamInitialX = ev.rawX
111
+ streamInitialY = ev.rawY
112
+ streamHorizontalLocked = false
113
+ streamDirectionDecided = false
114
+ }
115
+
116
+ // Decide gesture direction on first significant movement of the stream.
117
+ // If horizontal wins, lock out BottomSheetBehavior for the rest of the stream
118
+ // so child handlers (carousels, swipe buttons, etc.) can claim the touch.
119
+ if (!streamDirectionDecided && action == MotionEvent.ACTION_MOVE) {
120
+ val dx = kotlin.math.abs(ev.rawX - streamInitialX)
121
+ val dy = kotlin.math.abs(ev.rawY - streamInitialY)
122
+ if (dx > touchSlop || dy > touchSlop) {
123
+ streamDirectionDecided = true
124
+ streamHorizontalLocked = dx > dy
125
+ }
126
+ }
127
+
128
+ if (streamHorizontalLocked) {
129
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
130
+ streamHorizontalLocked = false
131
+ streamDirectionDecided = false
132
+ }
133
+ return false
99
134
  }
100
135
 
101
136
  val scrollView = delegate?.findScrollView()
@@ -72,10 +72,27 @@ using namespace facebook::react;
72
72
  _headerView = nil;
73
73
  _footerView = nil;
74
74
  _scrollableSet = NO;
75
+ self.isAccessibilityElement = NO;
75
76
  }
76
77
  return self;
77
78
  }
78
79
 
80
+ #pragma mark - Accessibility
81
+
82
+ - (NSArray *)accessibilityElements {
83
+ NSMutableArray *elements = [NSMutableArray array];
84
+ if (_headerView) {
85
+ [elements addObject:_headerView];
86
+ }
87
+ if (_contentView) {
88
+ [elements addObject:_contentView];
89
+ }
90
+ if (_footerView) {
91
+ [elements addObject:_footerView];
92
+ }
93
+ return elements;
94
+ }
95
+
79
96
  #pragma mark - Layout
80
97
 
81
98
  - (void)layoutSubviews {
@@ -37,6 +37,7 @@ using namespace facebook::react;
37
37
  _props = defaultProps;
38
38
 
39
39
  self.backgroundColor = [UIColor clearColor];
40
+ self.isAccessibilityElement = NO;
40
41
 
41
42
  _lastHeight = 0;
42
43
  _pendingHeight = 0;
@@ -47,6 +48,24 @@ using namespace facebook::react;
47
48
  return self;
48
49
  }
49
50
 
51
+ #pragma mark - Accessibility
52
+
53
+ - (NSArray *)accessibilityElements {
54
+ NSMutableArray *elements = [NSMutableArray array];
55
+ [self collectAccessibilityElementsFromView:self into:elements];
56
+ return elements;
57
+ }
58
+
59
+ - (void)collectAccessibilityElementsFromView:(UIView *)view into:(NSMutableArray *)elements {
60
+ for (UIView *subview in view.subviews) {
61
+ if (subview.isAccessibilityElement || subview.accessibilityLabel || subview.accessibilityIdentifier) {
62
+ [elements addObject:subview];
63
+ } else {
64
+ [self collectAccessibilityElementsFromView:subview into:elements];
65
+ }
66
+ }
67
+ }
68
+
50
69
  #pragma mark - Layout
51
70
 
52
71
  - (void)setupConstraintsWithHeight:(CGFloat)height {
@@ -373,6 +373,9 @@ using namespace facebook::react;
373
373
  [_controller.view addSubview:_containerView];
374
374
  [LayoutUtil pinView:_containerView toParentView:_controller.view edges:UIRectEdgeAll];
375
375
  [_controller.view bringSubviewToFront:_containerView];
376
+ _containerView.accessibilityViewIsModal = YES;
377
+ _controller.accessibilityContentView = _containerView;
378
+ [_controller setupAccessibilityContainer];
376
379
 
377
380
  CGFloat contentHeight = [_containerView contentHeight];
378
381
  if (contentHeight > 0) {
@@ -75,9 +75,11 @@ NS_ASSUME_NONNULL_BEGIN
75
75
  @property (nonatomic, assign) BOOL isPresented;
76
76
  @property (nonatomic, assign) NSInteger activeDetentIndex;
77
77
  @property (nonatomic, readonly) BOOL isTopmostPresentedController;
78
+ @property (nonatomic, weak, nullable) UIView *accessibilityContentView;
78
79
 
79
80
  @property (nonatomic, readonly) CGFloat screenHeight;
80
81
 
82
+ - (void)setupAccessibilityContainer;
81
83
  - (void)applyActiveDetent;
82
84
  - (void)setupActiveDetentWithIndex:(NSInteger)index;
83
85
  - (void)resizeToDetentIndex:(NSInteger)index;
@@ -34,6 +34,8 @@ static BOOL TrueSheetPositionStateEquals(TrueSheetPositionState a, TrueSheetPosi
34
34
 
35
35
  @interface TrueSheetViewController ()
36
36
 
37
+ - (void)setAccessibilityContentElement:(UIView *)contentView;
38
+
37
39
  @end
38
40
 
39
41
  @implementation TrueSheetViewController {
@@ -207,6 +209,7 @@ static BOOL TrueSheetPositionStateEquals(TrueSheetPositionState a, TrueSheetPosi
207
209
  if ([presenter isKindOfClass:[TrueSheetViewController class]]) {
208
210
  _parentSheetController = (TrueSheetViewController *)presenter;
209
211
  [_parentSheetController.delegate viewControllerWillBlur];
212
+ [_parentSheetController setAccessibilityContentElement:self.accessibilityContentView ?: self.view];
210
213
  }
211
214
 
212
215
  dispatch_async(dispatch_get_main_queue(), ^{
@@ -241,10 +244,33 @@ static BOOL TrueSheetPositionStateEquals(TrueSheetPositionState a, TrueSheetPosi
241
244
  });
242
245
 
243
246
  [self setupGestureRecognizer];
247
+ [self setupAccessibilityContainer];
244
248
  _isPresented = YES;
245
249
  }
246
250
  }
247
251
 
252
+ - (void)setupAccessibilityContainer {
253
+ UIView *contentView = self.accessibilityContentView;
254
+ if (!contentView) {
255
+ return;
256
+ }
257
+
258
+ [self setAccessibilityContentElement:contentView];
259
+ }
260
+
261
+ - (void)setAccessibilityContentElement:(UIView *)contentView {
262
+ self.view.isAccessibilityElement = NO;
263
+ self.view.accessibilityViewIsModal = YES;
264
+ self.view.accessibilityElements = @[ contentView ];
265
+
266
+ UIView *presentedView = self.presentationController.presentedView;
267
+ if (presentedView) {
268
+ presentedView.isAccessibilityElement = NO;
269
+ presentedView.accessibilityViewIsModal = YES;
270
+ presentedView.accessibilityElements = @[ contentView ];
271
+ }
272
+ }
273
+
248
274
  - (void)emitWillDismissEvents {
249
275
  if (self.isBeingDismissed && !_isWillDismissEmitted) {
250
276
  _isWillDismissEmitted = YES;
@@ -264,6 +290,7 @@ static BOOL TrueSheetPositionStateEquals(TrueSheetPositionState a, TrueSheetPosi
264
290
  _anchorView = nil;
265
291
 
266
292
  [_parentSheetController.delegate viewControllerDidFocus];
293
+ [_parentSheetController setupAccessibilityContainer];
267
294
  _parentSheetController = nil;
268
295
 
269
296
  [self.delegate viewControllerDidBlur];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodev09/react-native-true-sheet",
3
- "version": "3.11.0-beta.11",
3
+ "version": "3.11.0-beta.13",
4
4
  "description": "The true native bottom sheet experience for your React Native Apps.",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/module/index.js",