Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 74 additions & 87 deletions packages/react-events/src/dom/Press.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type PressState = {
ignoreEmulatedMouseEvents: boolean,
activePointerId: null | number,
shouldPreventClick: boolean,
touchEvent: null | Touch,
};

type PressEventType =
Expand Down Expand Up @@ -160,6 +161,7 @@ function createPressEvent(
target: Element | Document,
pointerType: PointerType,
event: ?ReactDOMResponderEvent,
touchEvent: null | Touch,
defaultPrevented: boolean,
): PressEvent {
const timeStamp = context.getTimeStamp();
Expand All @@ -180,11 +182,7 @@ function createPressEvent(
// Only check for one property, checking for all of them is costly. We can assume
// if clientX exists, so do the rest.
let eventObject;
if (nativeEvent.clientX !== undefined) {
eventObject = (nativeEvent: any);
} else if (isNativeTouchEvent(nativeEvent)) {
eventObject = getTouchFromPressEvent(nativeEvent);
}
eventObject = (touchEvent: any) || (nativeEvent: any);
if (eventObject) {
({clientX, clientY, pageX, pageY, screenX, screenY} = eventObject);
}
Expand Down Expand Up @@ -223,12 +221,14 @@ function dispatchEvent(
const defaultPrevented =
(event != null && event.nativeEvent.defaultPrevented === true) ||
(name === 'press' && state.shouldPreventClick);
const touchEvent = state.touchEvent;
const syntheticEvent = createPressEvent(
context,
name,
target,
pointerType,
event,
touchEvent,
defaultPrevented,
);
context.dispatchEvent(syntheticEvent, listener, eventPriority);
Expand Down Expand Up @@ -269,10 +269,10 @@ function dispatchLongPressChangeEvent(

function activate(event: ReactDOMResponderEvent, context, props, state) {
const nativeEvent: any = event.nativeEvent;
const {x, y} = getEventViewportCoords(nativeEvent);
const {clientX: x, clientY: y} = state.touchEvent || nativeEvent;
const wasActivePressed = state.isActivePressed;
state.isActivePressed = true;
if (x !== null && y !== null) {
if (x !== undefined && y !== undefined) {
state.activationPosition = {x, y};
}

Expand Down Expand Up @@ -433,6 +433,7 @@ function dispatchCancel(
props: PressProps,
state: PressState,
): void {
state.touchEvent = null;
if (state.isPressed) {
state.ignoreEmulatedMouseEvents = false;
dispatchPressEndEvents(event, context, props, state);
Expand Down Expand Up @@ -495,33 +496,12 @@ function calculateResponderRegion(
};
}

function isNativeTouchEvent(nativeEvent: Event): boolean {
const changedTouches = ((nativeEvent: any): TouchEvent).changedTouches;
return changedTouches && typeof changedTouches.length === 'number';
}

function getTouchFromPressEvent(nativeEvent: TouchEvent): Touch {
const {changedTouches, touches} = nativeEvent;
return changedTouches.length > 0
? changedTouches[0]
: touches.length > 0
? touches[0]
: (nativeEvent: any);
}

function getEventViewportCoords(
nativeEvent: Event,
): {x: null | number, y: null | number} {
let eventObject = (nativeEvent: any);
if (isNativeTouchEvent(eventObject)) {
eventObject = getTouchFromPressEvent(eventObject);
function getTouchFromPressEvent(nativeEvent: TouchEvent): null | Touch {
const targetTouches = nativeEvent.targetTouches;
if (targetTouches.length > 0) {
return targetTouches[0];
}
const x = eventObject.clientX;
const y = eventObject.clientY;
return {
x: x != null ? x : null,
y: y != null ? y : null,
};
return null;
}

function unmountResponder(
Expand Down Expand Up @@ -575,54 +555,46 @@ function getTouchTarget(context: ReactDOMResponderContext, touchEvent: Touch) {
}

function updateIsPressWithinResponderRegion(
target: Element | Document,
nativeEventOrTouchEvent: Event | Touch,
context: ReactDOMResponderContext,
props: PressProps,
state: PressState,
): void {
let isPressWithinResponderRegion = true;
if (
state.pressTarget != null &&
!context.isTargetWithinElement(target, state.pressTarget)
) {
// Calculate the responder region we use for deactivation if not
// already done during move event.
if (state.responderRegionOnDeactivation == null) {
state.responderRegionOnDeactivation = calculateResponderRegion(
context,
state.pressTarget,
props,
);
}
const {responderRegionOnActivation, responderRegionOnDeactivation} = state;
let left, top, right, bottom;

if (responderRegionOnActivation != null) {
left = responderRegionOnActivation.left;
top = responderRegionOnActivation.top;
right = responderRegionOnActivation.right;
bottom = responderRegionOnActivation.bottom;

if (responderRegionOnDeactivation != null) {
left = Math.min(left, responderRegionOnDeactivation.left);
top = Math.min(top, responderRegionOnDeactivation.top);
right = Math.max(right, responderRegionOnDeactivation.right);
bottom = Math.max(bottom, responderRegionOnDeactivation.bottom);
}
// Calculate the responder region we use for deactivation if not
// already done during move event.
if (state.responderRegionOnDeactivation == null) {
state.responderRegionOnDeactivation = calculateResponderRegion(
context,
((state.pressTarget: any): Element),
props,
);
}
const {responderRegionOnActivation, responderRegionOnDeactivation} = state;
let left, top, right, bottom;

if (responderRegionOnActivation != null) {
left = responderRegionOnActivation.left;
top = responderRegionOnActivation.top;
right = responderRegionOnActivation.right;
bottom = responderRegionOnActivation.bottom;

if (responderRegionOnDeactivation != null) {
left = Math.min(left, responderRegionOnDeactivation.left);
top = Math.min(top, responderRegionOnDeactivation.top);
right = Math.max(right, responderRegionOnDeactivation.right);
bottom = Math.max(bottom, responderRegionOnDeactivation.bottom);
}
const {clientX: x, clientY: y} = (nativeEventOrTouchEvent: any);

isPressWithinResponderRegion =
left != null &&
right != null &&
top != null &&
bottom != null &&
x !== null &&
y !== null &&
(x >= left && x <= right && y >= top && y <= bottom);
}
state.isPressWithinResponderRegion = isPressWithinResponderRegion;
const {clientX: x, clientY: y} = (nativeEventOrTouchEvent: any);

state.isPressWithinResponderRegion =
left != null &&
right != null &&
top != null &&
bottom != null &&
x !== null &&
y !== null &&
(x >= left && x <= right && y >= top && y <= bottom);
}

function handleStopPropagation(
Expand Down Expand Up @@ -650,7 +622,6 @@ const PressResponder: ReactDOMEventResponder = {
return {
activationPosition: null,
addedRootEvents: false,
didDispatchEvent: false,
isActivePressed: false,
isActivePressStart: false,
isLongPressed: false,
Expand All @@ -666,6 +637,7 @@ const PressResponder: ReactDOMEventResponder = {
ignoreEmulatedMouseEvents: false,
activePointerId: null,
shouldPreventClick: false,
touchEvent: null,
};
},
allowMultipleHostChildren: false,
Expand Down Expand Up @@ -731,6 +703,10 @@ const PressResponder: ReactDOMEventResponder = {
state.activePointerId = pointerId;
} else if (isTouchEvent) {
const touchEvent = getTouchFromPressEvent(nativeEvent);
if (touchEvent === null) {
return;
}
state.touchEvent = touchEvent;
state.activePointerId = touchEvent.identifier;
}

Expand Down Expand Up @@ -852,18 +828,23 @@ const PressResponder: ReactDOMEventResponder = {
if (touchEvent === null) {
return;
}
target = getTouchTarget(context, touchEvent);
state.touchEvent = touchEvent;
}

// Calculate the responder region we use for deactivation, as the
// element dimensions may have changed since activation.
updateIsPressWithinResponderRegion(
target,
touchEvent || nativeEvent,
context,
props,
state,
);
if (
state.pressTarget !== null &&
(pointerType !== 'mouse' ||
!context.isTargetWithinElement(target, state.pressTarget))
) {
// Calculate the responder region we use for deactivation, as the
// element dimensions may have changed since activation.
updateIsPressWithinResponderRegion(
touchEvent || nativeEvent,
context,
props,
state,
);
}

if (state.isPressWithinResponderRegion) {
if (isPressed) {
Expand Down Expand Up @@ -914,6 +895,7 @@ const PressResponder: ReactDOMEventResponder = {
if (touchEvent === null) {
return;
}
state.touchEvent = touchEvent;
target = getTouchTarget(context, touchEvent);
} else if (type === 'keyup') {
// Ignore unrelated keyboard events
Expand Down Expand Up @@ -961,12 +943,16 @@ const PressResponder: ReactDOMEventResponder = {
dispatchPressEndEvents(event, context, props, state);

if (state.pressTarget !== null && props.onPress) {
if (!isKeyboardEvent) {
if (
!isKeyboardEvent &&
state.pressTarget !== null &&
(pointerType !== 'mouse' ||
!context.isTargetWithinElement(target, state.pressTarget))
) {
// If the event target isn't within the press target, check if we're still
// within the responder region. The region may have changed if the
// element's layout was modified after activation.
updateIsPressWithinResponderRegion(
target,
touchEvent || nativeEvent,
context,
props,
Expand All @@ -992,6 +978,7 @@ const PressResponder: ReactDOMEventResponder = {
}
}
}
state.touchEvent = null;
} else if (type === 'mouseup') {
state.ignoreEmulatedMouseEvents = false;
}
Expand Down
Loading