mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-23 14:57:13 +00:00
[fix] Responder negotiation between siblings
There should be responder negotiation between siblings if there is no common ancestor connected to the responder system. Instead the current responder should continue to receive events. This was only occuring for mouse events during mousemove, as the target can change during the course of the movement.
This commit is contained in:
+21
-14
@@ -329,21 +329,28 @@ function eventListener(domEvent: any) {
|
||||
|
||||
if (currentResponderIdPath != null && eventIdPath != null) {
|
||||
const lowestCommonAncestor = getLowestCommonAncestor(currentResponderIdPath, eventIdPath);
|
||||
const indexOfLowestCommonAncestor = eventIdPath.indexOf(lowestCommonAncestor);
|
||||
// Skip the current responder so it doesn't receive unexpected "shouldSet" events.
|
||||
const index =
|
||||
indexOfLowestCommonAncestor + (lowestCommonAncestor === currentResponder.id ? 1 : 0);
|
||||
eventPaths = {
|
||||
idPath: eventIdPath.slice(index),
|
||||
nodePath: eventPaths.nodePath.slice(index)
|
||||
};
|
||||
if (lowestCommonAncestor != null) {
|
||||
const indexOfLowestCommonAncestor = eventIdPath.indexOf(lowestCommonAncestor);
|
||||
// Skip the current responder so it doesn't receive unexpected "shouldSet" events.
|
||||
const index =
|
||||
indexOfLowestCommonAncestor + (lowestCommonAncestor === currentResponder.id ? 1 : 0);
|
||||
eventPaths = {
|
||||
idPath: eventIdPath.slice(index),
|
||||
nodePath: eventPaths.nodePath.slice(index)
|
||||
};
|
||||
} else {
|
||||
eventPaths = null;
|
||||
}
|
||||
}
|
||||
// If a node wants to become the responder, attempt to transfer.
|
||||
wantsResponder = findWantsResponder(eventPaths, domEvent, responderEvent);
|
||||
if (wantsResponder != null) {
|
||||
// Sets responder if none exists, or negotates with existing responder.
|
||||
attemptTransfer(responderEvent, wantsResponder);
|
||||
wasNegotiated = true;
|
||||
|
||||
if (eventPaths != null) {
|
||||
// If a node wants to become the responder, attempt to transfer.
|
||||
wantsResponder = findWantsResponder(eventPaths, domEvent, responderEvent);
|
||||
if (wantsResponder != null) {
|
||||
// Sets responder if none exists, or negotates with existing responder.
|
||||
attemptTransfer(responderEvent, wantsResponder);
|
||||
wasNegotiated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+100
@@ -2154,6 +2154,106 @@ describe('useResponderEvents', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
/**
|
||||
* If siblings are connected to the responder system but have no ancestors
|
||||
* connected, there should be no negotiation between siblings after one
|
||||
* becomes the active responder.
|
||||
*/
|
||||
test('no negotation between siblings with no responder ancestors', () => {
|
||||
const pointerType = 'mouse';
|
||||
const eventLog = [];
|
||||
|
||||
const targetConfig = {
|
||||
onStartShouldSetResponderCapture(e) {
|
||||
eventLog.push('target: onStartShouldSetResponderCapture');
|
||||
return false;
|
||||
},
|
||||
onStartShouldSetResponder(e) {
|
||||
eventLog.push('target: onStartShouldSetResponder');
|
||||
return true;
|
||||
},
|
||||
onMoveShouldSetResponderCapture(e) {
|
||||
eventLog.push('target: onMoveShouldSetResponderCapture');
|
||||
return false;
|
||||
},
|
||||
onMoveShouldSetResponder(e) {
|
||||
eventLog.push('target: onMoveShouldSetResponder');
|
||||
return false;
|
||||
},
|
||||
onResponderGrant(e) {
|
||||
eventLog.push('target: onResponderGrant');
|
||||
},
|
||||
onResponderStart(e) {
|
||||
eventLog.push('target: onResponderStart');
|
||||
},
|
||||
onResponderMove(e) {
|
||||
eventLog.push('target: onResponderMove');
|
||||
}
|
||||
};
|
||||
const siblingConfig = {
|
||||
onStartShouldSetResponderCapture(e) {
|
||||
eventLog.push('sibling: onStartShouldSetResponderCapture');
|
||||
return true;
|
||||
},
|
||||
onStartShouldSetResponder(e) {
|
||||
eventLog.push('sibling: onStartShouldSetResponder');
|
||||
return true;
|
||||
},
|
||||
onMoveShouldSetResponderCapture(e) {
|
||||
eventLog.push('sibling: onMoveShouldSetResponderCapture');
|
||||
return true;
|
||||
},
|
||||
onMoveShouldSetResponder(e) {
|
||||
eventLog.push('sibling: onMoveShouldSetResponder');
|
||||
return true;
|
||||
},
|
||||
onResponderGrant(e) {
|
||||
eventLog.push('sibling: onResponderGrant');
|
||||
},
|
||||
onResponderStart(e) {
|
||||
eventLog.push('sibling: onResponderStart');
|
||||
},
|
||||
onResponderMove(e) {
|
||||
eventLog.push('sibling: onResponderMove');
|
||||
}
|
||||
};
|
||||
|
||||
const Component = () => {
|
||||
useResponderEvents(targetRef, targetConfig);
|
||||
useResponderEvents(siblingRef, siblingConfig);
|
||||
return (
|
||||
<div id="grandParent">
|
||||
<div id="parent">
|
||||
<div id="target" ref={targetRef} />
|
||||
<div id="sibling" ref={siblingRef} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// render
|
||||
act(() => {
|
||||
render(<Component />);
|
||||
});
|
||||
const target = createEventTarget(targetRef.current);
|
||||
const sibling = createEventTarget(siblingRef.current);
|
||||
// gesture start and move on target
|
||||
act(() => {
|
||||
target.pointerdown({ pointerType });
|
||||
target.pointermove({ pointerType });
|
||||
sibling.pointermove({ pointerType });
|
||||
});
|
||||
// target remains responder, no negotation occurs
|
||||
expect(eventLog).toEqual([
|
||||
'target: onStartShouldSetResponderCapture',
|
||||
'target: onStartShouldSetResponder',
|
||||
'target: onResponderGrant',
|
||||
'target: onResponderStart',
|
||||
'target: onResponderMove',
|
||||
'target: onResponderMove'
|
||||
]);
|
||||
});
|
||||
|
||||
/**
|
||||
* If a node is responder and it rejects a termination request, it
|
||||
* should continue to receive responder events.
|
||||
|
||||
Reference in New Issue
Block a user