@@ -17,18 +17,18 @@ import type {ReactEventResponderListener} from 'shared/ReactTypes';
17
17
import React from 'react' ;
18
18
import {
19
19
buttonsEnum ,
20
- hasPointerEvents ,
21
- isMac ,
22
20
dispatchDiscreteEvent ,
23
21
dispatchUserBlockingEvent ,
24
22
getTouchById ,
25
23
hasModifierKey ,
24
+ hasPointerEvents ,
26
25
} from './shared' ;
27
26
28
27
type TapProps = $ReadOnly < { |
29
28
disabled ?: boolean ,
30
29
maximumDistance ?: number ,
31
30
preventDefault ?: boolean ,
31
+ onAuxiliaryTap ?: ( e : TapEvent ) => void ,
32
32
onTapCancel ?: ( e : TapEvent ) => void ,
33
33
onTapChange ?: boolean => void ,
34
34
onTapEnd ?: ( e : TapEvent ) => void ,
@@ -38,7 +38,6 @@ type TapProps = $ReadOnly<{|
38
38
39
39
type TapGestureState = { |
40
40
altKey : boolean ,
41
- buttons : 0 | 1 | 4 ,
42
41
ctrlKey : boolean ,
43
42
height : number ,
44
43
metaKey : boolean ,
@@ -67,13 +66,15 @@ type TapState = {|
67
66
ignoreEmulatedEvents : boolean ,
68
67
initialPosition : { | x : number , y : number | } ,
69
68
isActive : boolean ,
69
+ isAuxiliaryActive : boolean ,
70
70
pointerType : PointerType ,
71
71
responderTarget : null | Element ,
72
72
rootEvents : null | Array < string > ,
73
- shouldPreventClick : boolean ,
73
+ shouldPreventDefault : boolean ,
74
74
| } ;
75
75
76
76
type TapEventType =
77
+ | 'tap:auxiliary'
77
78
| 'tap:cancel'
78
79
| 'tap:change'
79
80
| 'tap:end'
@@ -125,14 +126,14 @@ function createInitialState(): TapState {
125
126
buttons : 0 ,
126
127
ignoreEmulatedEvents : false ,
127
128
isActive : false ,
129
+ isAuxiliaryActive : false ,
128
130
initialPosition : { x : 0 , y : 0 } ,
129
131
pointerType : '' ,
130
132
responderTarget : null ,
131
133
rootEvents : null ,
132
- shouldPreventClick : true ,
134
+ shouldPreventDefault : true ,
133
135
gestureState : {
134
136
altKey : false ,
135
- buttons : 0 ,
136
137
ctrlKey : false ,
137
138
height : 1 ,
138
139
metaKey : false ,
@@ -187,7 +188,6 @@ function createPointerEventGestureState(
187
188
188
189
return {
189
190
altKey,
190
- buttons : state . buttons ,
191
191
ctrlKey,
192
192
height,
193
193
metaKey,
@@ -249,7 +249,6 @@ function createFallbackGestureState(
249
249
250
250
return {
251
251
altKey,
252
- buttons : state . buttons != null ? state . buttons : 1 ,
253
252
ctrlKey,
254
253
height : ! isCancelType && radiusY != null ? radiusY * 2 : 1 ,
255
254
metaKey,
@@ -351,19 +350,23 @@ function isActivePointer(
351
350
}
352
351
}
353
352
353
+ function isAuxiliary ( buttons : number , nativeEvent : any ) : boolean {
354
+ return (
355
+ // middle-click
356
+ buttons === buttonsEnum . auxiliary ||
357
+ // open-in-new-tab
358
+ ( buttons === buttonsEnum . primary && nativeEvent . metaKey ) ||
359
+ // open-in-new-window
360
+ ( buttons === buttonsEnum . primary && nativeEvent . shiftKey )
361
+ ) ;
362
+ }
363
+
354
364
function shouldActivate ( event : ReactDOMResponderEvent ) : boolean {
355
365
const nativeEvent : any = event . nativeEvent ;
356
366
const pointerType = event . pointerType ;
357
367
const buttons = nativeEvent . buttons ;
358
- const isContextMenu = pointerType === 'mouse' && nativeEvent . ctrlKey && isMac ;
359
- const isValidButton =
360
- buttons === buttonsEnum . primary || buttons === buttonsEnum . middle ;
361
-
362
- if ( pointerType === 'touch' || ( isValidButton && ! isContextMenu ) ) {
363
- return true ;
364
- } else {
365
- return false ;
366
- }
368
+ const isValidButton = buttons === buttonsEnum . primary ;
369
+ return pointerType === 'touch' || ( isValidButton && ! hasModifierKey ( event ) ) ;
367
370
}
368
371
369
372
/**
@@ -418,7 +421,7 @@ function dispatchEnd(
418
421
const onTapEnd = props . onTapEnd ;
419
422
dispatchChange ( context , props , state ) ;
420
423
if ( onTapEnd != null ) {
421
- const defaultPrevented = state . shouldPreventClick === true ;
424
+ const defaultPrevented = state . shouldPreventDefault === true ;
422
425
const payload = context . objectAssign ( { } , state . gestureState , {
423
426
defaultPrevented,
424
427
type,
@@ -441,6 +444,22 @@ function dispatchCancel(
441
444
}
442
445
}
443
446
447
+ function dispatchAuxiliaryTap (
448
+ context : ReactDOMResponderContext ,
449
+ props : TapProps ,
450
+ state : TapState ,
451
+ ) : void {
452
+ const type = 'tap:auxiliary' ;
453
+ const onAuxiliaryTap = props . onAuxiliaryTap ;
454
+ if ( onAuxiliaryTap != null ) {
455
+ const payload = context . objectAssign ( { } , state . gestureState , {
456
+ defaultPrevented : false ,
457
+ type,
458
+ } ) ;
459
+ dispatchDiscreteEvent ( context , payload , onAuxiliaryTap ) ;
460
+ }
461
+ }
462
+
444
463
/**
445
464
* Responder implementation
446
465
*/
@@ -493,26 +512,41 @@ const responderImpl = {
493
512
}
494
513
}
495
514
496
- if ( ! state . isActive && shouldActivate ( event ) ) {
497
- state . isActive = true ;
498
- state . buttons = nativeEvent . buttons ;
499
- state . pointerType = event . pointerType ;
500
- state . responderTarget = context . getResponderNode ( ) ;
501
- state . shouldPreventClick = props . preventDefault !== false ;
502
-
503
- const gestureState = createGestureState ( context , props , state , event ) ;
504
- state . gestureState = gestureState ;
505
- state . initialPosition . x = gestureState . x ;
506
- state . initialPosition . y = gestureState . y ;
507
-
508
- dispatchStart ( context , props , state ) ;
509
- addRootEventTypes ( rootEventTypes , context , state ) ;
510
-
511
- if ( ! hasPointerEvents ) {
512
- if ( eventType === 'touchstart' ) {
513
- state . ignoreEmulatedEvents = true ;
515
+ if ( ! state . isActive ) {
516
+ const activate = shouldActivate ( event ) ;
517
+ const activateAuxiliary = isAuxiliary (
518
+ nativeEvent . buttons ,
519
+ nativeEvent ,
520
+ ) ;
521
+
522
+ if ( activate || activateAuxiliary ) {
523
+ state . buttons = nativeEvent . buttons ;
524
+ state . pointerType = event . pointerType ;
525
+ state . responderTarget = context . getResponderNode ( ) ;
526
+ addRootEventTypes ( rootEventTypes , context , state ) ;
527
+ if ( ! hasPointerEvents ) {
528
+ if ( eventType === 'touchstart' ) {
529
+ state . ignoreEmulatedEvents = true ;
530
+ }
514
531
}
515
532
}
533
+
534
+ if ( activate ) {
535
+ const gestureState = createGestureState (
536
+ context ,
537
+ props ,
538
+ state ,
539
+ event ,
540
+ ) ;
541
+ state . isActive = true ;
542
+ state . shouldPreventDefault = props . preventDefault !== false ;
543
+ state . gestureState = gestureState ;
544
+ state . initialPosition . x = gestureState . x ;
545
+ state . initialPosition . y = gestureState . y ;
546
+ dispatchStart ( context , props , state ) ;
547
+ } else if ( activateAuxiliary ) {
548
+ state . isAuxiliaryActive = true ;
549
+ }
516
550
}
517
551
break ;
518
552
}
@@ -575,24 +609,30 @@ const responderImpl = {
575
609
case 'mouseup' :
576
610
case 'touchend' : {
577
611
if ( state . isActive && isActivePointer ( event , state ) ) {
578
- if ( state . buttons === buttonsEnum . middle ) {
579
- // Remove the root events here as no 'click' event is dispatched
580
- // when this 'button' is pressed.
581
- removeRootEventTypes ( context , state ) ;
582
- }
583
-
584
612
state . gestureState = createGestureState ( context , props , state , event ) ;
585
-
586
613
state . isActive = false ;
587
- if ( context . isTargetWithinResponder ( hitTarget ) ) {
588
- // Determine whether to call preventDefault on subsequent native events.
589
- if ( hasModifierKey ( event ) ) {
590
- state . shouldPreventClick = false ;
591
- }
592
- dispatchEnd ( context , props , state ) ;
593
- } else {
614
+ if ( isAuxiliary ( state . buttons , nativeEvent ) ) {
615
+ dispatchCancel ( context , props , state ) ;
616
+ dispatchAuxiliaryTap ( context , props , state ) ;
617
+ // Remove the root events here as no 'click' event is dispatched
618
+ removeRootEventTypes ( context , state ) ;
619
+ } else if (
620
+ ! context . isTargetWithinResponder ( hitTarget ) ||
621
+ hasModifierKey ( event )
622
+ ) {
594
623
dispatchCancel ( context , props , state ) ;
624
+ } else {
625
+ dispatchEnd ( context , props , state ) ;
595
626
}
627
+ } else if (
628
+ state . isAuxiliaryActive &&
629
+ isAuxiliary ( state . buttons , nativeEvent )
630
+ ) {
631
+ state . isAuxiliaryActive = false ;
632
+ state . gestureState = createGestureState ( context , props , state , event ) ;
633
+ dispatchAuxiliaryTap ( context , props , state ) ;
634
+ // Remove the root events here as no 'click' event is dispatched
635
+ removeRootEventTypes ( context , state ) ;
596
636
}
597
637
598
638
if ( ! hasPointerEvents ) {
@@ -612,6 +652,7 @@ const responderImpl = {
612
652
state . gestureState = createGestureState ( context , props , state , event ) ;
613
653
state . isActive = false ;
614
654
dispatchCancel ( context , props , state ) ;
655
+ removeRootEventTypes ( context , state ) ;
615
656
}
616
657
break ;
617
658
}
@@ -630,12 +671,13 @@ const responderImpl = {
630
671
state . gestureState = createGestureState ( context , props , state , event ) ;
631
672
state . isActive = false ;
632
673
dispatchCancel ( context , props , state ) ;
674
+ removeRootEventTypes ( context , state ) ;
633
675
}
634
676
break ;
635
677
}
636
678
637
679
case 'click' : {
638
- if ( state . shouldPreventClick ) {
680
+ if ( state . shouldPreventDefault ) {
639
681
nativeEvent . preventDefault ( ) ;
640
682
}
641
683
removeRootEventTypes ( context , state ) ;
0 commit comments