mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-06-05 19:24:21 +00:00
[fix] Pan interactions should cancel 'click' events on the target
If a pan interaction has taken place, it is not expected that 'click' events occur on the target element when the pointer is released (as was occuring with mouse pointers). This patch cancels any 'click' that occurs within the pan target's subtree, within 250ms of the pan gesture ending. Fix #1788
This commit is contained in:
@@ -188,6 +188,8 @@ type ActiveCallback = (
|
|||||||
gestureState: GestureState,
|
gestureState: GestureState,
|
||||||
) => boolean;
|
) => boolean;
|
||||||
|
|
||||||
|
type InteractionState = {handle: ?number, shouldCancelClick: boolean, timeout: ?TimeoutID};
|
||||||
|
|
||||||
type PassiveCallback = (event: PressEvent, gestureState: GestureState) => mixed;
|
type PassiveCallback = (event: PressEvent, gestureState: GestureState) => mixed;
|
||||||
|
|
||||||
type PanResponderConfig = $ReadOnly<{|
|
type PanResponderConfig = $ReadOnly<{|
|
||||||
@@ -384,9 +386,12 @@ const PanResponder = {
|
|||||||
* are the responder.
|
* are the responder.
|
||||||
*/
|
*/
|
||||||
create(config: PanResponderConfig) {
|
create(config: PanResponderConfig) {
|
||||||
const interactionState = {
|
const interactionState: InteractionState = {
|
||||||
handle: (null: ?number),
|
handle: null,
|
||||||
|
shouldCancelClick: false,
|
||||||
|
timeout: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const gestureState: GestureState = {
|
const gestureState: GestureState = {
|
||||||
// Useful for debugging
|
// Useful for debugging
|
||||||
stateID: Math.random(),
|
stateID: Math.random(),
|
||||||
@@ -427,15 +432,6 @@ const PanResponder = {
|
|||||||
|
|
||||||
onMoveShouldSetResponderCapture(event: PressEvent): boolean {
|
onMoveShouldSetResponderCapture(event: PressEvent): boolean {
|
||||||
const touchHistory = event.touchHistory;
|
const touchHistory = event.touchHistory;
|
||||||
// Responder system incorrectly dispatches should* to current responder
|
|
||||||
// Filter out any touch moves past the first one - we would have
|
|
||||||
// already processed multi-touch geometry during the first event.
|
|
||||||
if (
|
|
||||||
gestureState._accountsForMovesUpTo ===
|
|
||||||
touchHistory.mostRecentTimeStamp
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
||||||
return config.onMoveShouldSetPanResponderCapture
|
return config.onMoveShouldSetPanResponderCapture
|
||||||
? config.onMoveShouldSetPanResponderCapture(event, gestureState)
|
? config.onMoveShouldSetPanResponderCapture(event, gestureState)
|
||||||
@@ -446,6 +442,10 @@ const PanResponder = {
|
|||||||
if (!interactionState.handle) {
|
if (!interactionState.handle) {
|
||||||
interactionState.handle = InteractionManager.createInteractionHandle();
|
interactionState.handle = InteractionManager.createInteractionHandle();
|
||||||
}
|
}
|
||||||
|
if (interactionState.timeout) {
|
||||||
|
clearInteractionTimeout(interactionState);
|
||||||
|
}
|
||||||
|
interactionState.shouldCancelClick = true;
|
||||||
gestureState.x0 = currentCentroidX(event.touchHistory);
|
gestureState.x0 = currentCentroidX(event.touchHistory);
|
||||||
gestureState.y0 = currentCentroidY(event.touchHistory);
|
gestureState.y0 = currentCentroidY(event.touchHistory);
|
||||||
gestureState.dx = 0;
|
gestureState.dx = 0;
|
||||||
@@ -475,6 +475,7 @@ const PanResponder = {
|
|||||||
event,
|
event,
|
||||||
gestureState,
|
gestureState,
|
||||||
);
|
);
|
||||||
|
setInteractionTimeout(interactionState);
|
||||||
PanResponder._initializeGestureState(gestureState);
|
PanResponder._initializeGestureState(gestureState);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -488,14 +489,6 @@ const PanResponder = {
|
|||||||
|
|
||||||
onResponderMove(event: PressEvent): void {
|
onResponderMove(event: PressEvent): void {
|
||||||
const touchHistory = event.touchHistory;
|
const touchHistory = event.touchHistory;
|
||||||
// Guard against the dispatch of two touch moves when there are two
|
|
||||||
// simultaneously changed touches.
|
|
||||||
if (
|
|
||||||
gestureState._accountsForMovesUpTo ===
|
|
||||||
touchHistory.mostRecentTimeStamp
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Filter out any touch moves past the first one - we would have
|
// Filter out any touch moves past the first one - we would have
|
||||||
// already processed multi-touch geometry during the first event.
|
// already processed multi-touch geometry during the first event.
|
||||||
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
||||||
@@ -522,6 +515,7 @@ const PanResponder = {
|
|||||||
event,
|
event,
|
||||||
gestureState,
|
gestureState,
|
||||||
);
|
);
|
||||||
|
setInteractionTimeout(interactionState);
|
||||||
PanResponder._initializeGestureState(gestureState);
|
PanResponder._initializeGestureState(gestureState);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -530,7 +524,19 @@ const PanResponder = {
|
|||||||
? true
|
? true
|
||||||
: config.onPanResponderTerminationRequest(event, gestureState);
|
: config.onPanResponderTerminationRequest(event, gestureState);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// We do not want to trigger 'click' activated gestures or native behaviors
|
||||||
|
// on any pan target that is under a mouse cursor when it is released.
|
||||||
|
// Browsers will natively cancel 'click' events on a target if a non-mouse
|
||||||
|
// active pointer moves.
|
||||||
|
onClickCapture: (event: any): void => {
|
||||||
|
if (interactionState.shouldCancelClick === true) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
panHandlers,
|
panHandlers,
|
||||||
getInteractionHandle(): ?number {
|
getInteractionHandle(): ?number {
|
||||||
@@ -541,7 +547,7 @@ const PanResponder = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function clearInteractionHandle(
|
function clearInteractionHandle(
|
||||||
interactionState: {handle: ?number},
|
interactionState: InteractionState,
|
||||||
callback: ?(ActiveCallback | PassiveCallback),
|
callback: ?(ActiveCallback | PassiveCallback),
|
||||||
event: PressEvent,
|
event: PressEvent,
|
||||||
gestureState: GestureState,
|
gestureState: GestureState,
|
||||||
@@ -555,6 +561,16 @@ function clearInteractionHandle(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearInteractionTimeout(interactionState: InteractionState) {
|
||||||
|
clearTimeout(interactionState.timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInteractionTimeout(interactionState: InteractionState) {
|
||||||
|
interactionState.timeout = setTimeout(() => {
|
||||||
|
interactionState.shouldCancelClick = false;
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
export type PanResponderInstance = $Call<
|
export type PanResponderInstance = $Call<
|
||||||
$PropertyType<typeof PanResponder, 'create'>,
|
$PropertyType<typeof PanResponder, 'create'>,
|
||||||
PanResponderConfig,
|
PanResponderConfig,
|
||||||
|
|||||||
Reference in New Issue
Block a user