Add additional event API responder surfaces (#15248)
* Add rest of event modules + small fixes
This commit is contained in:
parent
700f17be67
commit
a41b217708
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
import SyntheticEvent from 'events/SyntheticEvent';
|
import SyntheticEvent from 'events/SyntheticEvent';
|
||||||
import type {AnyNativeEvent} from 'events/PluginModuleType';
|
import type {AnyNativeEvent} from 'events/PluginModuleType';
|
||||||
|
import type {ReactEventResponderEventType} from 'shared/ReactTypes';
|
||||||
|
|
||||||
export type EventResponderContext = {
|
export type EventResponderContext = {
|
||||||
event: AnyNativeEvent,
|
event: AnyNativeEvent,
|
||||||
|
@ -30,8 +31,12 @@ export type EventResponderContext = {
|
||||||
isTargetOwned: EventTarget => boolean,
|
isTargetOwned: EventTarget => boolean,
|
||||||
isTargetWithinEventComponent: EventTarget => boolean,
|
isTargetWithinEventComponent: EventTarget => boolean,
|
||||||
isPositionWithinTouchHitTarget: (x: number, y: number) => boolean,
|
isPositionWithinTouchHitTarget: (x: number, y: number) => boolean,
|
||||||
addRootEventTypes: (rootEventTypes: Array<string>) => void,
|
addRootEventTypes: (
|
||||||
removeRootEventTypes: (rootEventTypes: Array<string>) => void,
|
rootEventTypes: Array<ReactEventResponderEventType>,
|
||||||
|
) => void,
|
||||||
|
removeRootEventTypes: (
|
||||||
|
rootEventTypes: Array<ReactEventResponderEventType>,
|
||||||
|
) => void,
|
||||||
requestOwnership: (target: EventTarget | null) => boolean,
|
requestOwnership: (target: EventTarget | null) => boolean,
|
||||||
releaseOwnership: (target: EventTarget | null) => boolean,
|
releaseOwnership: (target: EventTarget | null) => boolean,
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,6 +44,7 @@ const targetEventTypeCached: Map<
|
||||||
Array<ReactEventResponderEventType>,
|
Array<ReactEventResponderEventType>,
|
||||||
Set<DOMTopLevelEventType>,
|
Set<DOMTopLevelEventType>,
|
||||||
> = new Map();
|
> = new Map();
|
||||||
|
const targetOwnership: Map<EventTarget, Fiber> = new Map();
|
||||||
|
|
||||||
type EventListener = (event: SyntheticEvent) => void;
|
type EventListener = (event: SyntheticEvent) => void;
|
||||||
|
|
||||||
|
@ -204,16 +205,37 @@ DOMEventResponderContext.prototype.isPositionWithinTouchHitTarget = function() {
|
||||||
// TODO
|
// TODO
|
||||||
};
|
};
|
||||||
|
|
||||||
DOMEventResponderContext.prototype.isTargetOwned = function() {
|
DOMEventResponderContext.prototype.isTargetOwned = function(
|
||||||
// TODO
|
targetElement: Element | Node,
|
||||||
|
): boolean {
|
||||||
|
const targetDoc = targetElement.ownerDocument;
|
||||||
|
return targetOwnership.has(targetDoc);
|
||||||
};
|
};
|
||||||
|
|
||||||
DOMEventResponderContext.prototype.requestOwnership = function() {
|
DOMEventResponderContext.prototype.requestOwnership = function(
|
||||||
// TODO
|
targetElement: Element | Node,
|
||||||
|
): boolean {
|
||||||
|
const targetDoc = targetElement.ownerDocument;
|
||||||
|
if (targetOwnership.has(targetDoc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
targetOwnership.set(targetDoc, this._fiber);
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
DOMEventResponderContext.prototype.releaseOwnership = function() {
|
DOMEventResponderContext.prototype.releaseOwnership = function(
|
||||||
// TODO
|
targetElement: Element | Node,
|
||||||
|
): boolean {
|
||||||
|
const targetDoc = targetElement.ownerDocument;
|
||||||
|
if (!targetOwnership.has(targetDoc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const owner = targetOwnership.get(targetDoc);
|
||||||
|
if (owner === this._fiber || owner === this._fiber.alternate) {
|
||||||
|
targetOwnership.delete(targetDoc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
function getTargetEventTypes(
|
function getTargetEventTypes(
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Drag = require('./src/Drag');
|
||||||
|
|
||||||
|
module.exports = Drag.default || Drag;
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Focus = require('./src/Focus');
|
||||||
|
|
||||||
|
module.exports = Focus.default || Focus;
|
|
@ -0,0 +1,7 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
module.exports = require('./cjs/react-events-drag.production.min.js');
|
||||||
|
} else {
|
||||||
|
module.exports = require('./cjs/react-events-drag.development.js');
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
module.exports = require('./cjs/react-events-focus.production.min.js');
|
||||||
|
} else {
|
||||||
|
module.exports = require('./cjs/react-events-focus.development.js');
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
module.exports = require('./cjs/react-events-swipe.production.min.js');
|
||||||
|
} else {
|
||||||
|
module.exports = require('./cjs/react-events-swipe.development.js');
|
||||||
|
}
|
|
@ -12,6 +12,11 @@
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"README.md",
|
"README.md",
|
||||||
"press.js",
|
"press.js",
|
||||||
|
"hover.js",
|
||||||
|
"focus.js",
|
||||||
|
"swipe.js",
|
||||||
|
"drag.js",
|
||||||
|
"index.js",
|
||||||
"build-info.json",
|
"build-info.json",
|
||||||
"cjs/",
|
"cjs/",
|
||||||
"umd/"
|
"umd/"
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
/**
|
||||||
|
* 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 {EventResponderContext} from 'events/EventTypes';
|
||||||
|
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
|
||||||
|
|
||||||
|
const targetEventTypes = ['pointerdown', 'pointercancel'];
|
||||||
|
const rootEventTypes = ['pointerup', {name: 'pointermove', passive: false}];
|
||||||
|
|
||||||
|
type DragState = {
|
||||||
|
dragTarget: null | EventTarget,
|
||||||
|
isPointerDown: boolean,
|
||||||
|
isDragging: boolean,
|
||||||
|
startX: number,
|
||||||
|
startY: number,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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', 'touchend', 'mousedown', 'touchcancel');
|
||||||
|
rootEventTypes.push('mouseup', 'mousemove', {
|
||||||
|
name: 'touchmove',
|
||||||
|
passive: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchDragEvent(
|
||||||
|
context: EventResponderContext,
|
||||||
|
name: string,
|
||||||
|
listener: (e: Object) => void,
|
||||||
|
state: DragState,
|
||||||
|
discrete: boolean,
|
||||||
|
eventData?: {
|
||||||
|
diffX: number,
|
||||||
|
diffY: number,
|
||||||
|
},
|
||||||
|
): void {
|
||||||
|
context.dispatchEvent(name, listener, state.dragTarget, discrete, eventData);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragResponder = {
|
||||||
|
targetEventTypes,
|
||||||
|
createInitialState(): DragState {
|
||||||
|
return {
|
||||||
|
dragTarget: null,
|
||||||
|
isPointerDown: false,
|
||||||
|
isDragging: false,
|
||||||
|
startX: 0,
|
||||||
|
startY: 0,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
handleEvent(
|
||||||
|
context: EventResponderContext,
|
||||||
|
props: Object,
|
||||||
|
state: DragState,
|
||||||
|
): void {
|
||||||
|
const {eventTarget, eventType, event} = context;
|
||||||
|
|
||||||
|
switch (eventType) {
|
||||||
|
case 'touchstart':
|
||||||
|
case 'mousedown':
|
||||||
|
case 'pointerdown': {
|
||||||
|
if (!state.isDragging) {
|
||||||
|
const obj =
|
||||||
|
eventType === 'touchstart' ? (event: any).changedTouches[0] : event;
|
||||||
|
const x = (state.startX = (obj: any).screenX);
|
||||||
|
const y = (state.startY = (obj: any).screenY);
|
||||||
|
state.x = x;
|
||||||
|
state.y = y;
|
||||||
|
state.dragTarget = eventTarget;
|
||||||
|
state.isPointerDown = true;
|
||||||
|
context.addRootEventTypes(rootEventTypes);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'touchmove':
|
||||||
|
case 'mousemove':
|
||||||
|
case 'pointermove': {
|
||||||
|
if (context.isPassive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.isPointerDown) {
|
||||||
|
const obj =
|
||||||
|
eventType === 'touchmove' ? (event: any).changedTouches[0] : event;
|
||||||
|
const x = (obj: any).screenX;
|
||||||
|
const y = (obj: any).screenY;
|
||||||
|
state.x = x;
|
||||||
|
state.y = y;
|
||||||
|
if (!state.isDragging && x !== state.startX && y !== state.startY) {
|
||||||
|
let shouldEnableDragging = true;
|
||||||
|
|
||||||
|
if (
|
||||||
|
props.onShouldClaimOwnership &&
|
||||||
|
props.onShouldClaimOwnership()
|
||||||
|
) {
|
||||||
|
shouldEnableDragging = context.requestOwnership(state.dragTarget);
|
||||||
|
}
|
||||||
|
if (shouldEnableDragging) {
|
||||||
|
state.isDragging = true;
|
||||||
|
if (props.onDragChange) {
|
||||||
|
const dragChangeEventListener = () => {
|
||||||
|
props.onDragChange(true);
|
||||||
|
};
|
||||||
|
context.dispatchEvent(
|
||||||
|
'dragchange',
|
||||||
|
dragChangeEventListener,
|
||||||
|
state.dragTarget,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.dragTarget = null;
|
||||||
|
state.isPointerDown = false;
|
||||||
|
context.removeRootEventTypes(rootEventTypes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (props.onDragMove) {
|
||||||
|
const eventData = {
|
||||||
|
diffX: x - state.startX,
|
||||||
|
diffY: y - state.startY,
|
||||||
|
};
|
||||||
|
dispatchDragEvent(
|
||||||
|
context,
|
||||||
|
'dragmove',
|
||||||
|
props.onDragMove,
|
||||||
|
state,
|
||||||
|
false,
|
||||||
|
eventData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(event: any).preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'pointercancel':
|
||||||
|
case 'touchcancel':
|
||||||
|
case 'touchend':
|
||||||
|
case 'mouseup':
|
||||||
|
case 'pointerup': {
|
||||||
|
if (state.isDragging) {
|
||||||
|
if (props.onShouldClaimOwnership) {
|
||||||
|
context.releaseOwnership(state.dragTarget);
|
||||||
|
}
|
||||||
|
if (props.onDragEnd) {
|
||||||
|
dispatchDragEvent(context, 'dragend', props.onDragEnd, state, true);
|
||||||
|
}
|
||||||
|
if (props.onDragChange) {
|
||||||
|
const dragChangeEventListener = () => {
|
||||||
|
props.onDragChange(false);
|
||||||
|
};
|
||||||
|
context.dispatchEvent(
|
||||||
|
'dragchange',
|
||||||
|
dragChangeEventListener,
|
||||||
|
state.dragTarget,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
state.isDragging = false;
|
||||||
|
}
|
||||||
|
if (state.isPointerDown) {
|
||||||
|
state.dragTarget = null;
|
||||||
|
state.isPointerDown = false;
|
||||||
|
context.removeRootEventTypes(rootEventTypes);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
$$typeof: REACT_EVENT_COMPONENT_TYPE,
|
||||||
|
props: null,
|
||||||
|
responder: DragResponder,
|
||||||
|
};
|
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
* 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 {EventResponderContext} from 'events/EventTypes';
|
||||||
|
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
|
||||||
|
|
||||||
|
const targetEventTypes = [
|
||||||
|
{name: 'focus', passive: true, capture: true},
|
||||||
|
{name: 'blur', passive: true, capture: true},
|
||||||
|
];
|
||||||
|
|
||||||
|
type FocusState = {
|
||||||
|
isFocused: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
function dispatchFocusInEvents(context: EventResponderContext, props: Object) {
|
||||||
|
const {event, eventTarget} = context;
|
||||||
|
if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (props.onFocus) {
|
||||||
|
context.dispatchEvent('focus', props.onFocus, eventTarget, true);
|
||||||
|
}
|
||||||
|
if (props.onFocusChange) {
|
||||||
|
const focusChangeEventListener = () => {
|
||||||
|
props.onFocusChange(true);
|
||||||
|
};
|
||||||
|
context.dispatchEvent(
|
||||||
|
'focuschange',
|
||||||
|
focusChangeEventListener,
|
||||||
|
eventTarget,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchFocusOutEvents(context: EventResponderContext, props: Object) {
|
||||||
|
const {event, eventTarget} = context;
|
||||||
|
if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (props.onBlur) {
|
||||||
|
context.dispatchEvent('blur', props.onBlur, eventTarget, true);
|
||||||
|
}
|
||||||
|
if (props.onFocusChange) {
|
||||||
|
const focusChangeEventListener = () => {
|
||||||
|
props.onFocusChange(false);
|
||||||
|
};
|
||||||
|
context.dispatchEvent(
|
||||||
|
'focuschange',
|
||||||
|
focusChangeEventListener,
|
||||||
|
eventTarget,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FocusResponder = {
|
||||||
|
targetEventTypes,
|
||||||
|
createInitialState(): FocusState {
|
||||||
|
return {
|
||||||
|
isFocused: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
handleEvent(
|
||||||
|
context: EventResponderContext,
|
||||||
|
props: Object,
|
||||||
|
state: FocusState,
|
||||||
|
): void {
|
||||||
|
const {eventTarget, eventType} = context;
|
||||||
|
|
||||||
|
switch (eventType) {
|
||||||
|
case 'focus': {
|
||||||
|
if (!state.isFocused && !context.isTargetOwned(eventTarget)) {
|
||||||
|
dispatchFocusInEvents(context, props);
|
||||||
|
state.isFocused = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'blur': {
|
||||||
|
if (state.isFocused) {
|
||||||
|
dispatchFocusOutEvents(context, props);
|
||||||
|
state.isFocused = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
$$typeof: REACT_EVENT_COMPONENT_TYPE,
|
||||||
|
props: null,
|
||||||
|
responder: FocusResponder,
|
||||||
|
};
|
|
@ -17,13 +17,13 @@ const targetEventTypes = [
|
||||||
'pointercancel',
|
'pointercancel',
|
||||||
'contextmenu',
|
'contextmenu',
|
||||||
];
|
];
|
||||||
const rootEventTypes = ['pointerup', 'scroll'];
|
const rootEventTypes = [{name: 'pointerup', passive: false}, 'scroll'];
|
||||||
|
|
||||||
// In the case we don't have PointerEvents (Safari), we listen to touch events
|
// In the case we don't have PointerEvents (Safari), we listen to touch events
|
||||||
// too
|
// too
|
||||||
if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
|
if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
|
||||||
targetEventTypes.push('touchstart', 'touchend', 'mousedown', 'touchcancel');
|
targetEventTypes.push('touchstart', 'touchend', 'mousedown', 'touchcancel');
|
||||||
rootEventTypes.push('mouseup');
|
rootEventTypes.push({name: 'mouseup', passive: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
type PressState = {
|
type PressState = {
|
||||||
|
@ -169,11 +169,11 @@ const PressResponder = {
|
||||||
// Android can show previous of anchor tags that requires working
|
// Android can show previous of anchor tags that requires working
|
||||||
// with click rather than touch events (and mouse down/up).
|
// with click rather than touch events (and mouse down/up).
|
||||||
if (!isAnchorTagElement(eventTarget)) {
|
if (!isAnchorTagElement(eventTarget)) {
|
||||||
keyPressEventListener = (e, key) => {
|
keyPressEventListener = e => {
|
||||||
if (!e.isDefaultPrevented() && !e.nativeEvent.defaultPrevented) {
|
if (!e.isDefaultPrevented() && !e.nativeEvent.defaultPrevented) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
state.defaultPrevented = true;
|
state.defaultPrevented = true;
|
||||||
props.onPress(e, key);
|
props.onPress(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -279,8 +279,8 @@ const PressResponder = {
|
||||||
props.onPress &&
|
props.onPress &&
|
||||||
!(state.isLongPressed && props.longPressCancelsPress)
|
!(state.isLongPressed && props.longPressCancelsPress)
|
||||||
) {
|
) {
|
||||||
const pressEventListener = (e, key) => {
|
const pressEventListener = e => {
|
||||||
props.onPress(e, key);
|
props.onPress(e);
|
||||||
if (e.nativeEvent.defaultPrevented) {
|
if (e.nativeEvent.defaultPrevented) {
|
||||||
state.defaultPrevented = true;
|
state.defaultPrevented = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
/**
|
||||||
|
* 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 {EventResponderContext} from 'events/EventTypes';
|
||||||
|
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
|
||||||
|
|
||||||
|
const targetEventTypes = ['pointerdown', 'pointercancel'];
|
||||||
|
const rootEventTypes = ['pointerup', {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', 'touchend', 'mousedown', 'touchcancel');
|
||||||
|
rootEventTypes.push('mouseup', 'mousemove', {
|
||||||
|
name: 'touchmove',
|
||||||
|
passive: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchSwipeEvent(
|
||||||
|
context: EventResponderContext,
|
||||||
|
name: string,
|
||||||
|
listener: (e: Object) => void,
|
||||||
|
state: SwipeState,
|
||||||
|
discrete: boolean,
|
||||||
|
eventData?: {
|
||||||
|
diffX: number,
|
||||||
|
diffY: number,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
context.dispatchEvent(name, listener, state.swipeTarget, discrete, eventData);
|
||||||
|
}
|
||||||
|
|
||||||
|
type SwipeState = {
|
||||||
|
direction: number,
|
||||||
|
isSwiping: boolean,
|
||||||
|
lastDirection: number,
|
||||||
|
startX: number,
|
||||||
|
startY: number,
|
||||||
|
touchId: null | number,
|
||||||
|
swipeTarget: null | EventTarget,
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
handleEvent(
|
||||||
|
context: EventResponderContext,
|
||||||
|
props: Object,
|
||||||
|
state: SwipeState,
|
||||||
|
): void {
|
||||||
|
const {eventTarget, eventType, event} = context;
|
||||||
|
|
||||||
|
switch (eventType) {
|
||||||
|
case 'touchstart':
|
||||||
|
case 'mousedown':
|
||||||
|
case 'pointerdown': {
|
||||||
|
if (!state.isSwiping && !context.isTargetOwned(eventTarget)) {
|
||||||
|
let obj = event;
|
||||||
|
if (eventType === 'touchstart') {
|
||||||
|
obj = (event: 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.requestOwnership(eventTarget);
|
||||||
|
}
|
||||||
|
if (shouldEnableSwiping) {
|
||||||
|
state.isSwiping = true;
|
||||||
|
state.startX = x;
|
||||||
|
state.startY = y;
|
||||||
|
state.x = x;
|
||||||
|
state.y = y;
|
||||||
|
state.swipeTarget = eventTarget;
|
||||||
|
context.addRootEventTypes(rootEventTypes);
|
||||||
|
} else {
|
||||||
|
state.touchId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'touchmove':
|
||||||
|
case 'mousemove':
|
||||||
|
case 'pointermove': {
|
||||||
|
if (context.isPassive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.isSwiping) {
|
||||||
|
let obj = null;
|
||||||
|
if (eventType === 'touchmove') {
|
||||||
|
const targetTouches = (event: any).targetTouches;
|
||||||
|
for (let i = 0; i < targetTouches.length; i++) {
|
||||||
|
if (state.touchId === targetTouches[i].identifier) {
|
||||||
|
obj = targetTouches[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
obj = event;
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
(event: 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(state.swipeTarget);
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
$$typeof: REACT_EVENT_COMPONENT_TYPE,
|
||||||
|
props: null,
|
||||||
|
responder: SwipeResponder,
|
||||||
|
};
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Swipe = require('./src/Swipe');
|
||||||
|
|
||||||
|
module.exports = Swipe.default || Swipe;
|
|
@ -507,6 +507,51 @@ const bundles = [
|
||||||
global: 'ReactEventsHover',
|
global: 'ReactEventsHover',
|
||||||
externals: [],
|
externals: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
bundleTypes: [
|
||||||
|
UMD_DEV,
|
||||||
|
UMD_PROD,
|
||||||
|
NODE_DEV,
|
||||||
|
NODE_PROD,
|
||||||
|
FB_WWW_DEV,
|
||||||
|
FB_WWW_PROD,
|
||||||
|
],
|
||||||
|
moduleType: NON_FIBER_RENDERER,
|
||||||
|
entry: 'react-events/focus',
|
||||||
|
global: 'ReactEventsFocus',
|
||||||
|
externals: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
bundleTypes: [
|
||||||
|
UMD_DEV,
|
||||||
|
UMD_PROD,
|
||||||
|
NODE_DEV,
|
||||||
|
NODE_PROD,
|
||||||
|
FB_WWW_DEV,
|
||||||
|
FB_WWW_PROD,
|
||||||
|
],
|
||||||
|
moduleType: NON_FIBER_RENDERER,
|
||||||
|
entry: 'react-events/swipe',
|
||||||
|
global: 'ReactEventsSwipe',
|
||||||
|
externals: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
bundleTypes: [
|
||||||
|
UMD_DEV,
|
||||||
|
UMD_PROD,
|
||||||
|
NODE_DEV,
|
||||||
|
NODE_PROD,
|
||||||
|
FB_WWW_DEV,
|
||||||
|
FB_WWW_PROD,
|
||||||
|
],
|
||||||
|
moduleType: NON_FIBER_RENDERER,
|
||||||
|
entry: 'react-events/drag',
|
||||||
|
global: 'ReactEventsDrag',
|
||||||
|
externals: [],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Based on deep-freeze by substack (public domain)
|
// Based on deep-freeze by substack (public domain)
|
||||||
|
|
Loading…
Reference in New Issue