Skip to content

Commit 824e9be

Browse files
authored
[Flare] Fix issues with touch + pointer interactions (facebook#15997)
1 parent dd93357 commit 824e9be

File tree

2 files changed

+125
-94
lines changed

2 files changed

+125
-94
lines changed

packages/react-events/src/dom/Press.js

Lines changed: 74 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ type PressState = {
7575
ignoreEmulatedMouseEvents: boolean,
7676
activePointerId: null | number,
7777
shouldPreventClick: boolean,
78+
touchEvent: null | Touch,
7879
};
7980

8081
type PressEventType =
@@ -160,6 +161,7 @@ function createPressEvent(
160161
target: Element | Document,
161162
pointerType: PointerType,
162163
event: ?ReactDOMResponderEvent,
164+
touchEvent: null | Touch,
163165
defaultPrevented: boolean,
164166
): PressEvent {
165167
const timeStamp = context.getTimeStamp();
@@ -180,11 +182,7 @@ function createPressEvent(
180182
// Only check for one property, checking for all of them is costly. We can assume
181183
// if clientX exists, so do the rest.
182184
let eventObject;
183-
if (nativeEvent.clientX !== undefined) {
184-
eventObject = (nativeEvent: any);
185-
} else if (isNativeTouchEvent(nativeEvent)) {
186-
eventObject = getTouchFromPressEvent(nativeEvent);
187-
}
185+
eventObject = (touchEvent: any) || (nativeEvent: any);
188186
if (eventObject) {
189187
({clientX, clientY, pageX, pageY, screenX, screenY} = eventObject);
190188
}
@@ -223,12 +221,14 @@ function dispatchEvent(
223221
const defaultPrevented =
224222
(event != null && event.nativeEvent.defaultPrevented === true) ||
225223
(name === 'press' && state.shouldPreventClick);
224+
const touchEvent = state.touchEvent;
226225
const syntheticEvent = createPressEvent(
227226
context,
228227
name,
229228
target,
230229
pointerType,
231230
event,
231+
touchEvent,
232232
defaultPrevented,
233233
);
234234
context.dispatchEvent(syntheticEvent, listener, eventPriority);
@@ -269,10 +269,10 @@ function dispatchLongPressChangeEvent(
269269

270270
function activate(event: ReactDOMResponderEvent, context, props, state) {
271271
const nativeEvent: any = event.nativeEvent;
272-
const {x, y} = getEventViewportCoords(nativeEvent);
272+
const {clientX: x, clientY: y} = state.touchEvent || nativeEvent;
273273
const wasActivePressed = state.isActivePressed;
274274
state.isActivePressed = true;
275-
if (x !== null && y !== null) {
275+
if (x !== undefined && y !== undefined) {
276276
state.activationPosition = {x, y};
277277
}
278278

@@ -433,6 +433,7 @@ function dispatchCancel(
433433
props: PressProps,
434434
state: PressState,
435435
): void {
436+
state.touchEvent = null;
436437
if (state.isPressed) {
437438
state.ignoreEmulatedMouseEvents = false;
438439
dispatchPressEndEvents(event, context, props, state);
@@ -495,33 +496,12 @@ function calculateResponderRegion(
495496
};
496497
}
497498

498-
function isNativeTouchEvent(nativeEvent: Event): boolean {
499-
const changedTouches = ((nativeEvent: any): TouchEvent).changedTouches;
500-
return changedTouches && typeof changedTouches.length === 'number';
501-
}
502-
503-
function getTouchFromPressEvent(nativeEvent: TouchEvent): Touch {
504-
const {changedTouches, touches} = nativeEvent;
505-
return changedTouches.length > 0
506-
? changedTouches[0]
507-
: touches.length > 0
508-
? touches[0]
509-
: (nativeEvent: any);
510-
}
511-
512-
function getEventViewportCoords(
513-
nativeEvent: Event,
514-
): {x: null | number, y: null | number} {
515-
let eventObject = (nativeEvent: any);
516-
if (isNativeTouchEvent(eventObject)) {
517-
eventObject = getTouchFromPressEvent(eventObject);
499+
function getTouchFromPressEvent(nativeEvent: TouchEvent): null | Touch {
500+
const targetTouches = nativeEvent.targetTouches;
501+
if (targetTouches.length > 0) {
502+
return targetTouches[0];
518503
}
519-
const x = eventObject.clientX;
520-
const y = eventObject.clientY;
521-
return {
522-
x: x != null ? x : null,
523-
y: y != null ? y : null,
524-
};
504+
return null;
525505
}
526506

527507
function unmountResponder(
@@ -575,54 +555,46 @@ function getTouchTarget(context: ReactDOMResponderContext, touchEvent: Touch) {
575555
}
576556

577557
function updateIsPressWithinResponderRegion(
578-
target: Element | Document,
579558
nativeEventOrTouchEvent: Event | Touch,
580559
context: ReactDOMResponderContext,
581560
props: PressProps,
582561
state: PressState,
583562
): void {
584-
let isPressWithinResponderRegion = true;
585-
if (
586-
state.pressTarget != null &&
587-
!context.isTargetWithinElement(target, state.pressTarget)
588-
) {
589-
// Calculate the responder region we use for deactivation if not
590-
// already done during move event.
591-
if (state.responderRegionOnDeactivation == null) {
592-
state.responderRegionOnDeactivation = calculateResponderRegion(
593-
context,
594-
state.pressTarget,
595-
props,
596-
);
597-
}
598-
const {responderRegionOnActivation, responderRegionOnDeactivation} = state;
599-
let left, top, right, bottom;
600-
601-
if (responderRegionOnActivation != null) {
602-
left = responderRegionOnActivation.left;
603-
top = responderRegionOnActivation.top;
604-
right = responderRegionOnActivation.right;
605-
bottom = responderRegionOnActivation.bottom;
606-
607-
if (responderRegionOnDeactivation != null) {
608-
left = Math.min(left, responderRegionOnDeactivation.left);
609-
top = Math.min(top, responderRegionOnDeactivation.top);
610-
right = Math.max(right, responderRegionOnDeactivation.right);
611-
bottom = Math.max(bottom, responderRegionOnDeactivation.bottom);
612-
}
563+
// Calculate the responder region we use for deactivation if not
564+
// already done during move event.
565+
if (state.responderRegionOnDeactivation == null) {
566+
state.responderRegionOnDeactivation = calculateResponderRegion(
567+
context,
568+
((state.pressTarget: any): Element),
569+
props,
570+
);
571+
}
572+
const {responderRegionOnActivation, responderRegionOnDeactivation} = state;
573+
let left, top, right, bottom;
574+
575+
if (responderRegionOnActivation != null) {
576+
left = responderRegionOnActivation.left;
577+
top = responderRegionOnActivation.top;
578+
right = responderRegionOnActivation.right;
579+
bottom = responderRegionOnActivation.bottom;
580+
581+
if (responderRegionOnDeactivation != null) {
582+
left = Math.min(left, responderRegionOnDeactivation.left);
583+
top = Math.min(top, responderRegionOnDeactivation.top);
584+
right = Math.max(right, responderRegionOnDeactivation.right);
585+
bottom = Math.max(bottom, responderRegionOnDeactivation.bottom);
613586
}
614-
const {clientX: x, clientY: y} = (nativeEventOrTouchEvent: any);
615-
616-
isPressWithinResponderRegion =
617-
left != null &&
618-
right != null &&
619-
top != null &&
620-
bottom != null &&
621-
x !== null &&
622-
y !== null &&
623-
(x >= left && x <= right && y >= top && y <= bottom);
624587
}
625-
state.isPressWithinResponderRegion = isPressWithinResponderRegion;
588+
const {clientX: x, clientY: y} = (nativeEventOrTouchEvent: any);
589+
590+
state.isPressWithinResponderRegion =
591+
left != null &&
592+
right != null &&
593+
top != null &&
594+
bottom != null &&
595+
x !== null &&
596+
y !== null &&
597+
(x >= left && x <= right && y >= top && y <= bottom);
626598
}
627599

628600
function handleStopPropagation(
@@ -650,7 +622,6 @@ const PressResponder: ReactDOMEventResponder = {
650622
return {
651623
activationPosition: null,
652624
addedRootEvents: false,
653-
didDispatchEvent: false,
654625
isActivePressed: false,
655626
isActivePressStart: false,
656627
isLongPressed: false,
@@ -666,6 +637,7 @@ const PressResponder: ReactDOMEventResponder = {
666637
ignoreEmulatedMouseEvents: false,
667638
activePointerId: null,
668639
shouldPreventClick: false,
640+
touchEvent: null,
669641
};
670642
},
671643
allowMultipleHostChildren: false,
@@ -731,6 +703,10 @@ const PressResponder: ReactDOMEventResponder = {
731703
state.activePointerId = pointerId;
732704
} else if (isTouchEvent) {
733705
const touchEvent = getTouchFromPressEvent(nativeEvent);
706+
if (touchEvent === null) {
707+
return;
708+
}
709+
state.touchEvent = touchEvent;
734710
state.activePointerId = touchEvent.identifier;
735711
}
736712

@@ -852,18 +828,23 @@ const PressResponder: ReactDOMEventResponder = {
852828
if (touchEvent === null) {
853829
return;
854830
}
855-
target = getTouchTarget(context, touchEvent);
831+
state.touchEvent = touchEvent;
856832
}
857833

858-
// Calculate the responder region we use for deactivation, as the
859-
// element dimensions may have changed since activation.
860-
updateIsPressWithinResponderRegion(
861-
target,
862-
touchEvent || nativeEvent,
863-
context,
864-
props,
865-
state,
866-
);
834+
if (
835+
state.pressTarget !== null &&
836+
(pointerType !== 'mouse' ||
837+
!context.isTargetWithinElement(target, state.pressTarget))
838+
) {
839+
// Calculate the responder region we use for deactivation, as the
840+
// element dimensions may have changed since activation.
841+
updateIsPressWithinResponderRegion(
842+
touchEvent || nativeEvent,
843+
context,
844+
props,
845+
state,
846+
);
847+
}
867848

868849
if (state.isPressWithinResponderRegion) {
869850
if (isPressed) {
@@ -914,6 +895,7 @@ const PressResponder: ReactDOMEventResponder = {
914895
if (touchEvent === null) {
915896
return;
916897
}
898+
state.touchEvent = touchEvent;
917899
target = getTouchTarget(context, touchEvent);
918900
} else if (type === 'keyup') {
919901
// Ignore unrelated keyboard events
@@ -961,12 +943,16 @@ const PressResponder: ReactDOMEventResponder = {
961943
dispatchPressEndEvents(event, context, props, state);
962944

963945
if (state.pressTarget !== null && props.onPress) {
964-
if (!isKeyboardEvent) {
946+
if (
947+
!isKeyboardEvent &&
948+
state.pressTarget !== null &&
949+
(pointerType !== 'mouse' ||
950+
!context.isTargetWithinElement(target, state.pressTarget))
951+
) {
965952
// If the event target isn't within the press target, check if we're still
966953
// within the responder region. The region may have changed if the
967954
// element's layout was modified after activation.
968955
updateIsPressWithinResponderRegion(
969-
target,
970956
touchEvent || nativeEvent,
971957
context,
972958
props,
@@ -992,6 +978,7 @@ const PressResponder: ReactDOMEventResponder = {
992978
}
993979
}
994980
}
981+
state.touchEvent = null;
995982
} else if (type === 'mouseup') {
996983
state.ignoreEmulatedMouseEvents = false;
997984
}

0 commit comments

Comments
 (0)