@@ -52,10 +52,16 @@ type PressState = {
52
52
isPressWithinResponderRegion : boolean ,
53
53
longPressTimeout : null | Symbol ,
54
54
pointerType : PointerType ,
55
- pressTarget : null | Element | Document ,
55
+ pressTarget : null | Element ,
56
56
pressEndTimeout : null | Symbol ,
57
57
pressStartTimeout : null | Symbol ,
58
- responderRegion : null | $ReadOnly < { |
58
+ responderRegionOnActivation : null | $ReadOnly < { |
59
+ bottom : number ,
60
+ left : number ,
61
+ right : number ,
62
+ top : number ,
63
+ | } > ,
64
+ responderRegionOnDeactivation : null | $ReadOnly < { |
59
65
bottom : number ,
60
66
left : number ,
61
67
right : number ,
@@ -312,7 +318,7 @@ function calculateDelayMS(delay: ?number, min = 0, fallback = 0) {
312
318
}
313
319
314
320
// TODO: account for touch hit slop
315
- function calculateResponderRegion ( target , props ) {
321
+ function calculateResponderRegion ( target : Element , props : PressProps ) {
316
322
const pressRetentionOffset = {
317
323
...DEFAULT_PRESS_RETENTION_OFFSET ,
318
324
...props . pressRetentionOffset ,
@@ -352,15 +358,33 @@ function isPressWithinResponderRegion(
352
358
nativeEvent : $PropertyType < ReactResponderEvent , 'nativeEvent' > ,
353
359
state : PressState ,
354
360
) : boolean {
355
- const { responderRegion } = state ;
361
+ const { responderRegionOnActivation , responderRegionOnDeactivation } = state ;
356
362
const event = ( nativeEvent : any ) ;
363
+ let left , top , right , bottom ;
364
+
365
+ if ( responderRegionOnActivation != null ) {
366
+ left = responderRegionOnActivation . left ;
367
+ top = responderRegionOnActivation . top ;
368
+ right = responderRegionOnActivation . right ;
369
+ bottom = responderRegionOnActivation . bottom ;
370
+
371
+ if ( responderRegionOnDeactivation != null ) {
372
+ left = Math . min ( left , responderRegionOnDeactivation . left ) ;
373
+ top = Math . min ( top , responderRegionOnDeactivation . top ) ;
374
+ right = Math . max ( right , responderRegionOnDeactivation . right ) ;
375
+ bottom = Math . max ( bottom , responderRegionOnDeactivation . bottom ) ;
376
+ }
377
+ }
357
378
358
379
return (
359
- responderRegion != null &&
360
- ( event . pageX >= responderRegion . left &&
361
- event . pageX <= responderRegion . right &&
362
- event . pageY >= responderRegion . top &&
363
- event . pageY <= responderRegion . bottom )
380
+ left != null &&
381
+ right != null &&
382
+ top != null &&
383
+ bottom != null &&
384
+ ( event . pageX >= left &&
385
+ event . pageX <= right &&
386
+ event . pageY >= top &&
387
+ event . pageY <= bottom )
364
388
) ;
365
389
}
366
390
@@ -408,7 +432,8 @@ const PressResponder = {
408
432
pressEndTimeout : null ,
409
433
pressStartTimeout : null ,
410
434
pressTarget : null ,
411
- responderRegion : null ,
435
+ responderRegionOnActivation : null ,
436
+ responderRegionOnDeactivation : null ,
412
437
ignoreEmulatedMouseEvents : false ,
413
438
} ;
414
439
} ,
@@ -469,7 +494,11 @@ const PressResponder = {
469
494
}
470
495
471
496
state . pointerType = pointerType ;
472
- state . pressTarget = target ;
497
+ state . pressTarget = getEventCurrentTarget ( event , context ) ;
498
+ state . responderRegionOnActivation = calculateResponderRegion (
499
+ state . pressTarget ,
500
+ props ,
501
+ ) ;
473
502
state . isPressWithinResponderRegion = true ;
474
503
dispatchPressStartEvents ( context , props , state ) ;
475
504
context . addRootEventTypes ( rootEventTypes ) ;
@@ -519,26 +548,34 @@ const PressResponder = {
519
548
case 'touchmove ': {
520
549
if ( state . isPressed ) {
521
550
// Ignore emulated events (pointermove will dispatch touch and mouse events)
522
- // Ignore pointermove events during a keyboard press
551
+ // Ignore pointermove events during a keyboard press.
523
552
if ( state . pointerType !== pointerType ) {
524
553
return ;
525
554
}
526
555
527
- if ( state . responderRegion == null ) {
528
- state . responderRegion = calculateResponderRegion (
529
- getEventCurrentTarget ( event , context ) ,
556
+ // Calculate the responder region we use for deactivation, as the
557
+ // element dimensions may have changed since activation.
558
+ if (
559
+ state . pressTarget !== null &&
560
+ state . responderRegionOnDeactivation == null
561
+ ) {
562
+ state . responderRegionOnDeactivation = calculateResponderRegion (
563
+ state . pressTarget ,
530
564
props ,
531
565
) ;
532
566
}
533
- if ( isPressWithinResponderRegion ( nativeEvent , state ) ) {
534
- state . isPressWithinResponderRegion = true ;
567
+ state . isPressWithinResponderRegion = isPressWithinResponderRegion (
568
+ nativeEvent ,
569
+ state ,
570
+ ) ;
571
+
572
+ if ( state . isPressWithinResponderRegion ) {
535
573
if ( props . onPressMove ) {
536
574
dispatchEvent ( context , state , 'pressmove' , props . onPressMove , {
537
575
discrete : false ,
538
576
} ) ;
539
577
}
540
578
} else {
541
- state . isPressWithinResponderRegion = false ;
542
579
dispatchPressEndEvents ( context , props , state ) ;
543
580
}
544
581
}
@@ -551,18 +588,38 @@ const PressResponder = {
551
588
case 'mouseup ':
552
589
case 'touchend ': {
553
590
if ( state . isPressed ) {
554
- // Ignore unrelated keyboard events
591
+ // Ignore unrelated keyboard events and verify press is within
592
+ // responder region for non-keyboard events.
555
593
if ( pointerType === 'keyboard' ) {
556
594
if ( ! isValidKeyPress ( nativeEvent . key ) ) {
557
595
return ;
558
596
}
597
+ // If the event target isn't within the press target, check if we're still
598
+ // within the responder region. The region may have changed if the
599
+ // element's layout was modified after activation.
600
+ } else if (
601
+ state . pressTarget != null &&
602
+ ! context . isTargetWithinElement ( target , state . pressTarget )
603
+ ) {
604
+ // Calculate the responder region we use for deactivation if not
605
+ // already done during move event.
606
+ if ( state . responderRegionOnDeactivation == null ) {
607
+ state . responderRegionOnDeactivation = calculateResponderRegion (
608
+ state . pressTarget ,
609
+ props ,
610
+ ) ;
611
+ }
612
+ state . isPressWithinResponderRegion = isPressWithinResponderRegion (
613
+ nativeEvent ,
614
+ state ,
615
+ ) ;
559
616
}
560
617
561
618
const wasLongPressed = state . isLongPressed ;
562
619
dispatchPressEndEvents ( context , props , state ) ;
563
620
564
621
if ( state . pressTarget !== null && props . onPress ) {
565
- if ( context . isTargetWithinElement ( target , state . pressTarget ) ) {
622
+ if ( state . isPressWithinResponderRegion ) {
566
623
if (
567
624
! (
568
625
wasLongPressed &&
0 commit comments