mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
263 lines
6.6 KiB
JavaScript
263 lines
6.6 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import type {
|
|
ReactResponderEvent,
|
|
ReactResponderContext,
|
|
} from 'shared/ReactTypes';
|
|
|
|
import React from 'react';
|
|
|
|
const targetEventTypes = ['pointerdown'];
|
|
const rootEventTypes = [
|
|
'pointerup',
|
|
'pointercancel',
|
|
{name: 'pointermove', passive: false},
|
|
];
|
|
|
|
// In the case we don't have PointerEvents (Safari), we listen to touch events
|
|
// too
|
|
if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
|
|
targetEventTypes.push('touchstart', 'mousedown');
|
|
rootEventTypes.push('mouseup', 'mousemove', 'touchend', 'touchcancel', {
|
|
name: 'touchmove',
|
|
passive: false,
|
|
});
|
|
}
|
|
|
|
type EventData = {
|
|
diffX: number,
|
|
diffY: number,
|
|
};
|
|
type SwipeEventType = 'swipeleft' | 'swiperight' | 'swipeend' | 'swipemove';
|
|
|
|
type SwipeEvent = {|
|
|
target: Element | Document,
|
|
type: SwipeEventType,
|
|
timeStamp: number,
|
|
diffX?: number,
|
|
diffY?: number,
|
|
|};
|
|
|
|
function createSwipeEvent(
|
|
context: ReactResponderContext,
|
|
type: SwipeEventType,
|
|
target: Element | Document,
|
|
eventData?: EventData,
|
|
): SwipeEvent {
|
|
return {
|
|
target,
|
|
type,
|
|
timeStamp: context.getTimeStamp(),
|
|
...eventData,
|
|
};
|
|
}
|
|
|
|
function dispatchSwipeEvent(
|
|
context: ReactResponderContext,
|
|
name: SwipeEventType,
|
|
listener: SwipeEvent => void,
|
|
state: SwipeState,
|
|
discrete: boolean,
|
|
eventData?: EventData,
|
|
) {
|
|
const target = ((state.swipeTarget: any): Element | Document);
|
|
const syntheticEvent = createSwipeEvent(context, name, target, eventData);
|
|
context.dispatchEvent(syntheticEvent, listener, discrete);
|
|
}
|
|
|
|
type SwipeState = {
|
|
direction: number,
|
|
isSwiping: boolean,
|
|
lastDirection: number,
|
|
startX: number,
|
|
startY: number,
|
|
touchId: null | number,
|
|
swipeTarget: null | Element | Document,
|
|
x: number,
|
|
y: number,
|
|
};
|
|
|
|
const SwipeResponder = {
|
|
targetEventTypes,
|
|
createInitialState(): SwipeState {
|
|
return {
|
|
direction: 0,
|
|
isSwiping: false,
|
|
lastDirection: 0,
|
|
startX: 0,
|
|
startY: 0,
|
|
touchId: null,
|
|
swipeTarget: null,
|
|
x: 0,
|
|
y: 0,
|
|
};
|
|
},
|
|
allowMultipleHostChildren: false,
|
|
stopLocalPropagation: true,
|
|
onEvent(
|
|
event: ReactResponderEvent,
|
|
context: ReactResponderContext,
|
|
props: Object,
|
|
state: SwipeState,
|
|
): void {
|
|
const {target, type, nativeEvent} = event;
|
|
|
|
switch (type) {
|
|
case 'touchstart':
|
|
case 'mousedown':
|
|
case 'pointerdown': {
|
|
if (!state.isSwiping) {
|
|
let obj = nativeEvent;
|
|
if (type === 'touchstart') {
|
|
obj = (nativeEvent: any).targetTouches[0];
|
|
state.touchId = obj.identifier;
|
|
}
|
|
const x = (obj: any).screenX;
|
|
const y = (obj: any).screenY;
|
|
|
|
let shouldEnableSwiping = true;
|
|
|
|
if (props.onShouldClaimOwnership && props.onShouldClaimOwnership()) {
|
|
shouldEnableSwiping = context.requestGlobalOwnership();
|
|
}
|
|
if (shouldEnableSwiping) {
|
|
state.isSwiping = true;
|
|
state.startX = x;
|
|
state.startY = y;
|
|
state.x = x;
|
|
state.y = y;
|
|
state.swipeTarget = target;
|
|
context.addRootEventTypes(rootEventTypes);
|
|
} else {
|
|
state.touchId = null;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
onRootEvent(
|
|
event: ReactResponderEvent,
|
|
context: ReactResponderContext,
|
|
props: Object,
|
|
state: SwipeState,
|
|
): void {
|
|
const {type, nativeEvent} = event;
|
|
|
|
switch (type) {
|
|
case 'touchmove':
|
|
case 'mousemove':
|
|
case 'pointermove': {
|
|
if (event.passive) {
|
|
return;
|
|
}
|
|
if (state.isSwiping) {
|
|
let obj = null;
|
|
if (type === 'touchmove') {
|
|
const targetTouches = (nativeEvent: any).targetTouches;
|
|
for (let i = 0; i < targetTouches.length; i++) {
|
|
if (state.touchId === targetTouches[i].identifier) {
|
|
obj = targetTouches[i];
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
obj = nativeEvent;
|
|
}
|
|
if (obj === null) {
|
|
state.isSwiping = false;
|
|
state.swipeTarget = null;
|
|
state.touchId = null;
|
|
context.removeRootEventTypes(rootEventTypes);
|
|
return;
|
|
}
|
|
const x = (obj: any).screenX;
|
|
const y = (obj: any).screenY;
|
|
if (x < state.x && props.onSwipeLeft) {
|
|
state.direction = 3;
|
|
} else if (x > state.x && props.onSwipeRight) {
|
|
state.direction = 1;
|
|
}
|
|
state.x = x;
|
|
state.y = y;
|
|
if (props.onSwipeMove) {
|
|
const eventData = {
|
|
diffX: x - state.startX,
|
|
diffY: y - state.startY,
|
|
};
|
|
dispatchSwipeEvent(
|
|
context,
|
|
'swipemove',
|
|
props.onSwipeMove,
|
|
state,
|
|
false,
|
|
eventData,
|
|
);
|
|
(nativeEvent: any).preventDefault();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'pointercancel':
|
|
case 'touchcancel':
|
|
case 'touchend':
|
|
case 'mouseup':
|
|
case 'pointerup': {
|
|
if (state.isSwiping) {
|
|
if (state.x === state.startX && state.y === state.startY) {
|
|
return;
|
|
}
|
|
if (props.onShouldClaimOwnership) {
|
|
context.releaseOwnership();
|
|
}
|
|
const direction = state.direction;
|
|
const lastDirection = state.lastDirection;
|
|
if (direction !== lastDirection) {
|
|
if (props.onSwipeLeft && direction === 3) {
|
|
dispatchSwipeEvent(
|
|
context,
|
|
'swipeleft',
|
|
props.onSwipeLeft,
|
|
state,
|
|
true,
|
|
);
|
|
} else if (props.onSwipeRight && direction === 1) {
|
|
dispatchSwipeEvent(
|
|
context,
|
|
'swiperight',
|
|
props.onSwipeRight,
|
|
state,
|
|
true,
|
|
);
|
|
}
|
|
}
|
|
if (props.onSwipeEnd) {
|
|
dispatchSwipeEvent(
|
|
context,
|
|
'swipeend',
|
|
props.onSwipeEnd,
|
|
state,
|
|
true,
|
|
);
|
|
}
|
|
state.lastDirection = direction;
|
|
state.isSwiping = false;
|
|
state.swipeTarget = null;
|
|
state.touchId = null;
|
|
context.removeRootEventTypes(rootEventTypes);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
};
|
|
|
|
export default React.unstable_createEventComponent(SwipeResponder, 'Swipe');
|