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
66 changes: 50 additions & 16 deletions packages/react-events/src/Press.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ type PressProps = {
type PointerType = '' | 'mouse' | 'keyboard' | 'pen' | 'touch';

type PressState = {
activationPosition: null | $ReadOnly<{|
pageX: number,
pageY: number,
|}>,
addedRootEvents: boolean,
isActivePressed: boolean,
isActivePressStart: boolean,
Expand Down Expand Up @@ -174,9 +178,18 @@ function dispatchLongPressChangeEvent(
dispatchEvent(context, state, 'longpresschange', listener);
}

function activate(context, props, state) {
function activate(event, context, props, state) {
const nativeEvent: any = event.nativeEvent;
const pageX = nativeEvent.pageX;
const pageY = nativeEvent.pageY;
const wasActivePressed = state.isActivePressed;
state.isActivePressed = true;
if (pageX != null && pageY != null) {
state.activationPosition = {
pageX: nativeEvent.pageX,
pageY: nativeEvent.pageY,
};
}

if (props.onPressStart) {
dispatchEvent(context, state, 'pressstart', props.onPressStart);
Expand All @@ -203,6 +216,7 @@ function deactivate(context, props, state) {
}

function dispatchPressStartEvents(
event: ReactResponderEvent,
context: ReactResponderContext,
props: PressProps,
state: PressState,
Expand All @@ -216,7 +230,7 @@ function dispatchPressStartEvents(

const dispatch = () => {
state.isActivePressStart = true;
activate(context, props, state);
activate(event, context, props, state);

if (
(props.onLongPress || props.onLongPressChange) &&
Expand Down Expand Up @@ -258,6 +272,7 @@ function dispatchPressStartEvents(
}

function dispatchPressEndEvents(
event: ?ReactResponderEvent,
context: ReactResponderContext,
props: PressProps,
state: PressState,
Expand All @@ -277,9 +292,9 @@ function dispatchPressEndEvents(
context.clearTimeout(state.pressStartTimeout);
state.pressStartTimeout = null;
// don't activate if a press has moved beyond the responder region
if (state.isPressWithinResponderRegion) {
if (state.isPressWithinResponderRegion && event != null) {
// if we haven't yet activated (due to delays), activate now
activate(context, props, state);
activate(event, context, props, state);
activationWasForced = true;
}
}
Expand Down Expand Up @@ -396,24 +411,26 @@ function unmountResponder(
): void {
if (state.isPressed) {
removeRootEventTypes(context, state);
dispatchPressEndEvents(context, props, state);
dispatchPressEndEvents(null, context, props, state);
}
}

function dispatchCancel(
type: string,
nativeEvent: $PropertyType<ReactResponderEvent, 'nativeEvent'>,
event: ReactResponderEvent,
context: ReactResponderContext,
props: PressProps,
state: PressState,
): void {
const nativeEvent: any = event.nativeEvent;
const type = event.type;

if (state.isPressed) {
if (type === 'contextmenu' && props.preventDefault !== false) {
(nativeEvent: any).preventDefault();
nativeEvent.preventDefault();
} else {
state.ignoreEmulatedMouseEvents = false;
removeRootEventTypes(context, state);
dispatchPressEndEvents(context, props, state);
dispatchPressEndEvents(event, context, props, state);
}
} else if (state.allowPressReentry) {
removeRootEventTypes(context, state);
Expand Down Expand Up @@ -445,6 +462,7 @@ const PressResponder = {
targetEventTypes,
createInitialState(): PressState {
return {
activationPosition: null,
addedRootEvents: false,
didDispatchEvent: false,
isActivePressed: false,
Expand Down Expand Up @@ -474,7 +492,7 @@ const PressResponder = {

if (props.disabled) {
removeRootEventTypes(context, state);
dispatchPressEndEvents(context, props, state);
dispatchPressEndEvents(event, context, props, state);
state.ignoreEmulatedMouseEvents = false;
return;
}
Expand Down Expand Up @@ -527,7 +545,7 @@ const PressResponder = {
props,
);
state.isPressWithinResponderRegion = true;
dispatchPressStartEvents(context, props, state);
dispatchPressStartEvents(event, context, props, state);
addRootEventTypes(context, state);
} else {
// Prevent spacebar press from scrolling the window
Expand All @@ -540,7 +558,7 @@ const PressResponder = {

// CANCEL
case 'contextmenu': {
dispatchCancel(type, nativeEvent, context, props, state);
dispatchCancel(event, context, props, state);
break;
}

Expand Down Expand Up @@ -603,18 +621,34 @@ const PressResponder = {
discrete: false,
});
}
if (
state.activationPosition != null &&
state.longPressTimeout != null
) {
const deltaX =
state.activationPosition.pageX - nativeEvent.pageX;
const deltaY =
state.activationPosition.pageY - nativeEvent.pageY;
if (
Math.hypot(deltaX, deltaY) > 10 &&
state.longPressTimeout != null
) {
context.clearTimeout(state.longPressTimeout);
}
}
} else {
dispatchPressStartEvents(context, props, state);
dispatchPressStartEvents(event, context, props, state);
}
} else {
if (!state.allowPressReentry) {
removeRootEventTypes(context, state);
}
dispatchPressEndEvents(context, props, state);
dispatchPressEndEvents(event, context, props, state);
}
}
break;
}

// END
case 'pointerup':
case 'keyup':
Expand Down Expand Up @@ -650,7 +684,7 @@ const PressResponder = {

const wasLongPressed = state.isLongPressed;
removeRootEventTypes(context, state);
dispatchPressEndEvents(context, props, state);
dispatchPressEndEvents(event, context, props, state);

if (state.pressTarget !== null && props.onPress) {
if (state.isPressWithinResponderRegion) {
Expand All @@ -677,7 +711,7 @@ const PressResponder = {
case 'pointercancel':
case 'scroll':
case 'touchcancel': {
dispatchCancel(type, nativeEvent, context, props, state);
dispatchCancel(event, context, props, state);
}
}
},
Expand Down
17 changes: 17 additions & 0 deletions packages/react-events/src/__tests__/Press-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,23 @@ describe('Event responder: Press', () => {
expect(onLongPress).not.toBeCalled();
});

it('is not called when a large enough move occurs before delay', () => {
ref.current.getBoundingClientRect = () => ({
top: 0,
left: 0,
bottom: 100,
right: 100,
});
ref.current.dispatchEvent(
createPointerEvent('pointerdown', {pageX: 10, pageY: 10}),
);
ref.current.dispatchEvent(
createPointerEvent('pointermove', {pageX: 50, pageY: 50}),
);
jest.runAllTimers();
expect(onLongPress).not.toBeCalled();
});

describe('delayLongPress', () => {
it('can be configured', () => {
const element = (
Expand Down