Skip to content

Commit 8503b04

Browse files
committed
refactor: Remove custom hit testing in usePress
1 parent ab9fd5c commit 8503b04

File tree

2 files changed

+27
-18
lines changed

2 files changed

+27
-18
lines changed

packages/@react-aria/interactions/src/usePress.ts

+16-17
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,13 @@ export function usePress(props: PressHookProps): PressResult {
433433

434434
shouldStopPropagation = triggerPressStart(e, state.pointerType);
435435

436-
addGlobalListener(getOwnerDocument(e.currentTarget), 'pointermove', onPointerMove, false);
436+
// Release pointer capture so that touch interactions can leave the original target.
437+
// This enables onPointerLeave and onPointerEnter to fire.
438+
let target = e.target as Element;
439+
if ('releasePointerCapture' in target) {
440+
target.releasePointerCapture(e.pointerId);
441+
}
442+
437443
addGlobalListener(getOwnerDocument(e.currentTarget), 'pointerup', onPointerUp, false);
438444
addGlobalListener(getOwnerDocument(e.currentTarget), 'pointercancel', onPointerCancel, false);
439445
}
@@ -467,27 +473,20 @@ export function usePress(props: PressHookProps): PressResult {
467473
}
468474

469475
// Only handle left clicks
470-
// Safari on iOS sometimes fires pointerup events, even
471-
// when the touch isn't over the target, so double check.
472-
if (e.button === 0 && isOverTarget(e, e.currentTarget)) {
476+
if (e.button === 0) {
473477
triggerPressUp(e, state.pointerType || e.pointerType);
474478
}
475479
};
476480

477-
// Safari on iOS < 13.2 does not implement pointerenter/pointerleave events correctly.
478-
// Use pointer move events instead to implement our own hit testing.
479-
// See https://bugs.webkit.org/show_bug.cgi?id=199803
480-
let onPointerMove = (e: PointerEvent) => {
481-
if (e.pointerId !== state.activePointerId) {
482-
return;
481+
pressProps.onPointerEnter = (e) => {
482+
if (e.pointerId === state.activePointerId && state.target && !state.isOverTarget && state.pointerType != null) {
483+
state.isOverTarget = true;
484+
triggerPressStart(createEvent(state.target, e), state.pointerType);
483485
}
486+
};
484487

485-
if (state.target && isOverTarget(e, state.target)) {
486-
if (!state.isOverTarget && state.pointerType != null) {
487-
state.isOverTarget = true;
488-
triggerPressStart(createEvent(state.target, e), state.pointerType);
489-
}
490-
} else if (state.target && state.isOverTarget && state.pointerType != null) {
488+
pressProps.onPointerLeave = (e) => {
489+
if (e.pointerId === state.activePointerId && state.target && state.isOverTarget && state.pointerType != null) {
491490
state.isOverTarget = false;
492491
triggerPressEnd(createEvent(state.target, e), state.pointerType, false);
493492
cancelOnPointerExit(e);
@@ -496,7 +495,7 @@ export function usePress(props: PressHookProps): PressResult {
496495

497496
let onPointerUp = (e: PointerEvent) => {
498497
if (e.pointerId === state.activePointerId && state.isPressed && e.button === 0 && state.target) {
499-
if (isOverTarget(e, state.target) && state.pointerType != null) {
498+
if (state.target.contains(e.target as Element) && state.pointerType != null) {
500499
triggerPressEnd(createEvent(state.target, e), state.pointerType);
501500
} else if (state.isOverTarget && state.pointerType != null) {
502501
triggerPressEnd(createEvent(state.target, e), state.pointerType, false);

packages/@react-aria/interactions/test/usePress.test.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,15 @@ describe('usePress', function () {
141141
);
142142

143143
let el = res.getByText('test');
144+
el.releasePointerCapture = jest.fn();
144145
fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
146+
expect(el.releasePointerCapture).toHaveBeenCalled();
145147
fireEvent(el, pointerEvent('pointermove', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
146-
fireEvent(el, pointerEvent('pointerup', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
148+
// react listens for pointerout and pointerover instead of pointerleave and pointerenter...
149+
fireEvent(el, pointerEvent('pointerout', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
150+
fireEvent(document, pointerEvent('pointerup', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
147151
fireEvent(el, pointerEvent('pointermove', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
152+
fireEvent(el, pointerEvent('pointerover', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
148153

149154
expect(events).toEqual([
150155
{
@@ -182,7 +187,10 @@ describe('usePress', function () {
182187
events = [];
183188
fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
184189
fireEvent(el, pointerEvent('pointermove', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
190+
// react listens for pointerout and pointerover instead of pointerleave and pointerenter...
191+
fireEvent(el, pointerEvent('pointerout', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
185192
fireEvent(el, pointerEvent('pointermove', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
193+
fireEvent(el, pointerEvent('pointerover', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
186194
fireEvent(el, pointerEvent('pointerup', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
187195

188196
expect(events).toEqual([
@@ -387,7 +395,9 @@ describe('usePress', function () {
387395
let el = res.getByText('test');
388396
fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
389397
fireEvent(el, pointerEvent('pointermove', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
398+
fireEvent(el, pointerEvent('pointerout', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
390399
fireEvent(el, pointerEvent('pointermove', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
400+
fireEvent(el, pointerEvent('pointerover', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
391401

392402
expect(events).toEqual([
393403
{

0 commit comments

Comments
 (0)