mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-19 00:46:47 +08:00
Add flow types to PanResponder (#21291)
Summary: This PR adds flow types to the `PanResponder` module. It is part of my effort to flowtype the `Swipable*` classes. A new `touchHistory` field had to be added to `SyntheticEvent` as well. Pull Request resolved: https://github.com/facebook/react-native/pull/21291 Reviewed By: TheSavior Differential Revision: D10013265 Pulled By: RSNara fbshipit-source-id: 3cd65a0eae41c756d1605e6771588d820f040e2a
This commit is contained in:
committed by
Facebook Github Bot
parent
57041ee44f
commit
3f79b2a4e9
@@ -25,6 +25,9 @@ const View = require('View');
|
||||
const createReactClass = require('create-react-class');
|
||||
const emptyFunction = require('fbjs/lib/emptyFunction');
|
||||
|
||||
import type {LayoutEvent, PressEvent} from 'CoreEventTypes';
|
||||
import type {GestureState} from 'PanResponder';
|
||||
|
||||
const IS_RTL = I18nManager.isRTL;
|
||||
|
||||
// NOTE: Eventually convert these consts to an input object of configurations
|
||||
@@ -204,7 +207,7 @@ const SwipeableRow = createReactClass({
|
||||
this._animateToClosedPosition();
|
||||
},
|
||||
|
||||
_onSwipeableViewLayout(event: Object): void {
|
||||
_onSwipeableViewLayout(event: LayoutEvent): void {
|
||||
this.setState({
|
||||
isSwipeableViewRendered: true,
|
||||
rowHeight: event.nativeEvent.layout.height,
|
||||
@@ -212,16 +215,19 @@ const SwipeableRow = createReactClass({
|
||||
},
|
||||
|
||||
_handleMoveShouldSetPanResponderCapture(
|
||||
event: Object,
|
||||
gestureState: Object,
|
||||
event: PressEvent,
|
||||
gestureState: GestureState,
|
||||
): boolean {
|
||||
// Decides whether a swipe is responded to by this component or its child
|
||||
return gestureState.dy < 10 && this._isValidSwipe(gestureState);
|
||||
},
|
||||
|
||||
_handlePanResponderGrant(event: Object, gestureState: Object): void {},
|
||||
_handlePanResponderGrant(
|
||||
event: PressEvent,
|
||||
gestureState: GestureState,
|
||||
): void {},
|
||||
|
||||
_handlePanResponderMove(event: Object, gestureState: Object): void {
|
||||
_handlePanResponderMove(event: PressEvent, gestureState: GestureState): void {
|
||||
if (this._isSwipingExcessivelyRightFromClosedPosition(gestureState)) {
|
||||
return;
|
||||
}
|
||||
@@ -235,22 +241,24 @@ const SwipeableRow = createReactClass({
|
||||
}
|
||||
},
|
||||
|
||||
_isSwipingRightFromClosed(gestureState: Object): boolean {
|
||||
_isSwipingRightFromClosed(gestureState: GestureState): boolean {
|
||||
const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
|
||||
return this._previousLeft === CLOSED_LEFT_POSITION && gestureStateDx > 0;
|
||||
},
|
||||
|
||||
_swipeFullSpeed(gestureState: Object): void {
|
||||
_swipeFullSpeed(gestureState: GestureState): void {
|
||||
this.state.currentLeft.setValue(this._previousLeft + gestureState.dx);
|
||||
},
|
||||
|
||||
_swipeSlowSpeed(gestureState: Object): void {
|
||||
_swipeSlowSpeed(gestureState: GestureState): void {
|
||||
this.state.currentLeft.setValue(
|
||||
this._previousLeft + gestureState.dx / SLOW_SPEED_SWIPE_FACTOR,
|
||||
);
|
||||
},
|
||||
|
||||
_isSwipingExcessivelyRightFromClosedPosition(gestureState: Object): boolean {
|
||||
_isSwipingExcessivelyRightFromClosedPosition(
|
||||
gestureState: GestureState,
|
||||
): boolean {
|
||||
/**
|
||||
* We want to allow a BIT of right swipe, to allow users to know that
|
||||
* swiping is available, but swiping right does not do anything
|
||||
@@ -264,8 +272,8 @@ const SwipeableRow = createReactClass({
|
||||
},
|
||||
|
||||
_onPanResponderTerminationRequest(
|
||||
event: Object,
|
||||
gestureState: Object,
|
||||
event: PressEvent,
|
||||
gestureState: GestureState,
|
||||
): boolean {
|
||||
return false;
|
||||
},
|
||||
@@ -338,7 +346,7 @@ const SwipeableRow = createReactClass({
|
||||
},
|
||||
|
||||
// Ignore swipes due to user's finger moving slightly when tapping
|
||||
_isValidSwipe(gestureState: Object): boolean {
|
||||
_isValidSwipe(gestureState: GestureState): boolean {
|
||||
if (
|
||||
this.props.preventSwipeRight &&
|
||||
this._previousLeft === CLOSED_LEFT_POSITION &&
|
||||
@@ -350,7 +358,7 @@ const SwipeableRow = createReactClass({
|
||||
return Math.abs(gestureState.dx) > HORIZONTAL_SWIPE_DISTANCE_THRESHOLD;
|
||||
},
|
||||
|
||||
_shouldAnimateRemainder(gestureState: Object): boolean {
|
||||
_shouldAnimateRemainder(gestureState: GestureState): boolean {
|
||||
/**
|
||||
* If user has swiped past a certain distance, animate the rest of the way
|
||||
* if they let go
|
||||
@@ -361,7 +369,7 @@ const SwipeableRow = createReactClass({
|
||||
);
|
||||
},
|
||||
|
||||
_handlePanResponderEnd(event: Object, gestureState: Object): void {
|
||||
_handlePanResponderEnd(event: PressEvent, gestureState: GestureState): void {
|
||||
const horizontalDistance = IS_RTL ? -gestureState.dx : gestureState.dx;
|
||||
if (this._isSwipingRightFromClosed(gestureState)) {
|
||||
this.props.onOpen();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
@@ -12,6 +13,8 @@
|
||||
const InteractionManager = require('./InteractionManager');
|
||||
const TouchHistoryMath = require('./TouchHistoryMath');
|
||||
|
||||
import type {PressEvent} from 'CoreEventTypes';
|
||||
|
||||
const currentCentroidXOfTouchesChangedAfter =
|
||||
TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
|
||||
const currentCentroidYOfTouchesChangedAfter =
|
||||
@@ -121,6 +124,93 @@ const currentCentroidY = TouchHistoryMath.currentCentroidY;
|
||||
* [PanResponder example in RNTester](https://github.com/facebook/react-native/blob/master/RNTester/js/PanResponderExample.js)
|
||||
*/
|
||||
|
||||
export type GestureState = {|
|
||||
/**
|
||||
* ID of the gestureState - persisted as long as there at least one touch on screen
|
||||
*/
|
||||
stateID: number,
|
||||
|
||||
/**
|
||||
* The latest screen coordinates of the recently-moved touch
|
||||
*/
|
||||
moveX: number,
|
||||
|
||||
/**
|
||||
* The latest screen coordinates of the recently-moved touch
|
||||
*/
|
||||
moveY: number,
|
||||
|
||||
/**
|
||||
* The screen coordinates of the responder grant
|
||||
*/
|
||||
x0: number,
|
||||
|
||||
/**
|
||||
* The screen coordinates of the responder grant
|
||||
*/
|
||||
y0: number,
|
||||
|
||||
/**
|
||||
* Accumulated distance of the gesture since the touch started
|
||||
*/
|
||||
dx: number,
|
||||
|
||||
/**
|
||||
* Accumulated distance of the gesture since the touch started
|
||||
*/
|
||||
dy: number,
|
||||
|
||||
/**
|
||||
* Current velocity of the gesture
|
||||
*/
|
||||
vx: number,
|
||||
|
||||
/**
|
||||
* Current velocity of the gesture
|
||||
*/
|
||||
vy: number,
|
||||
|
||||
/**
|
||||
* Number of touches currently on screen
|
||||
*/
|
||||
numberActiveTouches: number,
|
||||
|
||||
/**
|
||||
* All `gestureState` accounts for timeStamps up until this value
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_accountsForMovesUpTo: number,
|
||||
|};
|
||||
|
||||
type ActiveCallback = (
|
||||
event: PressEvent,
|
||||
gestureState: GestureState,
|
||||
) => boolean;
|
||||
|
||||
type PassiveCallback = (event: PressEvent, gestureState: GestureState) => mixed;
|
||||
|
||||
type PanResponderConfig = $ReadOnly<{|
|
||||
onMoveShouldSetPanResponder?: ?ActiveCallback,
|
||||
onMoveShouldSetPanResponderCapture?: ?ActiveCallback,
|
||||
onStartShouldSetPanResponder?: ?ActiveCallback,
|
||||
onStartShouldSetPanResponderCapture?: ?ActiveCallback,
|
||||
/**
|
||||
* The body of `onResponderGrant` returns a bool, but the vast majority of
|
||||
* callsites return void and this TODO notice is found in it:
|
||||
* TODO: t7467124 investigate if this can be removed
|
||||
*/
|
||||
onPanResponderGrant?: ?(PassiveCallback | ActiveCallback),
|
||||
onPanResponderReject?: ?PassiveCallback,
|
||||
onPanResponderStart?: ?PassiveCallback,
|
||||
onPanResponderEnd?: ?PassiveCallback,
|
||||
onPanResponderRelease?: ?PassiveCallback,
|
||||
onPanResponderMove?: ?PassiveCallback,
|
||||
onPanResponderTerminate?: ?PassiveCallback,
|
||||
onPanResponderTerminationRequest?: ?ActiveCallback,
|
||||
onShouldBlockNativeResponder?: ?ActiveCallback,
|
||||
|}>;
|
||||
|
||||
const PanResponder = {
|
||||
/**
|
||||
*
|
||||
@@ -185,7 +275,7 @@ const PanResponder = {
|
||||
* - vx/vy: Velocity.
|
||||
*/
|
||||
|
||||
_initializeGestureState: function(gestureState) {
|
||||
_initializeGestureState(gestureState: GestureState) {
|
||||
gestureState.moveX = 0;
|
||||
gestureState.moveY = 0;
|
||||
gestureState.x0 = 0;
|
||||
@@ -223,7 +313,10 @@ const PanResponder = {
|
||||
* typical responder callback pattern (without using `PanResponder`), but
|
||||
* avoids more dispatches than necessary.
|
||||
*/
|
||||
_updateGestureStateOnMove: function(gestureState, touchHistory) {
|
||||
_updateGestureStateOnMove(
|
||||
gestureState: GestureState,
|
||||
touchHistory: $PropertyType<PressEvent, 'touchHistory'>,
|
||||
) {
|
||||
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
|
||||
gestureState.moveX = currentCentroidXOfTouchesChangedAfter(
|
||||
touchHistory,
|
||||
@@ -290,40 +383,50 @@ const PanResponder = {
|
||||
* accordingly. (numberActiveTouches) may not be totally accurate unless you
|
||||
* are the responder.
|
||||
*/
|
||||
create: function(config) {
|
||||
create(config: PanResponderConfig) {
|
||||
const interactionState = {
|
||||
handle: (null: ?number),
|
||||
};
|
||||
const gestureState = {
|
||||
const gestureState: GestureState = {
|
||||
// Useful for debugging
|
||||
stateID: Math.random(),
|
||||
moveX: 0,
|
||||
moveY: 0,
|
||||
x0: 0,
|
||||
y0: 0,
|
||||
dx: 0,
|
||||
dy: 0,
|
||||
vx: 0,
|
||||
vy: 0,
|
||||
numberActiveTouches: 0,
|
||||
_accountsForMovesUpTo: 0,
|
||||
};
|
||||
PanResponder._initializeGestureState(gestureState);
|
||||
const panHandlers = {
|
||||
onStartShouldSetResponder: function(e) {
|
||||
return config.onStartShouldSetPanResponder === undefined
|
||||
onStartShouldSetResponder(event: PressEvent): boolean {
|
||||
return config.onStartShouldSetPanResponder == null
|
||||
? false
|
||||
: config.onStartShouldSetPanResponder(e, gestureState);
|
||||
: config.onStartShouldSetPanResponder(event, gestureState);
|
||||
},
|
||||
onMoveShouldSetResponder: function(e) {
|
||||
return config.onMoveShouldSetPanResponder === undefined
|
||||
onMoveShouldSetResponder(event: PressEvent): boolean {
|
||||
return config.onMoveShouldSetPanResponder == null
|
||||
? false
|
||||
: config.onMoveShouldSetPanResponder(e, gestureState);
|
||||
: config.onMoveShouldSetPanResponder(event, gestureState);
|
||||
},
|
||||
onStartShouldSetResponderCapture: function(e) {
|
||||
onStartShouldSetResponderCapture(event: PressEvent): boolean {
|
||||
// TODO: Actually, we should reinitialize the state any time
|
||||
// touches.length increases from 0 active to > 0 active.
|
||||
if (e.nativeEvent.touches.length === 1) {
|
||||
if (event.nativeEvent.touches.length === 1) {
|
||||
PanResponder._initializeGestureState(gestureState);
|
||||
}
|
||||
gestureState.numberActiveTouches = e.touchHistory.numberActiveTouches;
|
||||
return config.onStartShouldSetPanResponderCapture !== undefined
|
||||
? config.onStartShouldSetPanResponderCapture(e, gestureState)
|
||||
gestureState.numberActiveTouches =
|
||||
event.touchHistory.numberActiveTouches;
|
||||
return config.onStartShouldSetPanResponderCapture != null
|
||||
? config.onStartShouldSetPanResponderCapture(event, gestureState)
|
||||
: false;
|
||||
},
|
||||
|
||||
onMoveShouldSetResponderCapture: function(e) {
|
||||
const touchHistory = e.touchHistory;
|
||||
onMoveShouldSetResponderCapture(event: PressEvent): boolean {
|
||||
const touchHistory = event.touchHistory;
|
||||
// Responder system incorrectly dispatches should* to current responder
|
||||
// Filter out any touch moves past the first one - we would have
|
||||
// already processed multi-touch geometry during the first event.
|
||||
@@ -335,56 +438,56 @@ const PanResponder = {
|
||||
}
|
||||
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
||||
return config.onMoveShouldSetPanResponderCapture
|
||||
? config.onMoveShouldSetPanResponderCapture(e, gestureState)
|
||||
? config.onMoveShouldSetPanResponderCapture(event, gestureState)
|
||||
: false;
|
||||
},
|
||||
|
||||
onResponderGrant: function(e) {
|
||||
onResponderGrant(event: PressEvent): boolean {
|
||||
if (!interactionState.handle) {
|
||||
interactionState.handle = InteractionManager.createInteractionHandle();
|
||||
}
|
||||
gestureState.x0 = currentCentroidX(e.touchHistory);
|
||||
gestureState.y0 = currentCentroidY(e.touchHistory);
|
||||
gestureState.x0 = currentCentroidX(event.touchHistory);
|
||||
gestureState.y0 = currentCentroidY(event.touchHistory);
|
||||
gestureState.dx = 0;
|
||||
gestureState.dy = 0;
|
||||
if (config.onPanResponderGrant) {
|
||||
config.onPanResponderGrant(e, gestureState);
|
||||
config.onPanResponderGrant(event, gestureState);
|
||||
}
|
||||
// TODO: t7467124 investigate if this can be removed
|
||||
return config.onShouldBlockNativeResponder === undefined
|
||||
return config.onShouldBlockNativeResponder == null
|
||||
? true
|
||||
: config.onShouldBlockNativeResponder();
|
||||
: config.onShouldBlockNativeResponder(event, gestureState);
|
||||
},
|
||||
|
||||
onResponderReject: function(e) {
|
||||
onResponderReject(event: PressEvent): void {
|
||||
clearInteractionHandle(
|
||||
interactionState,
|
||||
config.onPanResponderReject,
|
||||
e,
|
||||
event,
|
||||
gestureState,
|
||||
);
|
||||
},
|
||||
|
||||
onResponderRelease: function(e) {
|
||||
onResponderRelease(event: PressEvent): void {
|
||||
clearInteractionHandle(
|
||||
interactionState,
|
||||
config.onPanResponderRelease,
|
||||
e,
|
||||
event,
|
||||
gestureState,
|
||||
);
|
||||
PanResponder._initializeGestureState(gestureState);
|
||||
},
|
||||
|
||||
onResponderStart: function(e) {
|
||||
const touchHistory = e.touchHistory;
|
||||
onResponderStart(event: PressEvent): void {
|
||||
const touchHistory = event.touchHistory;
|
||||
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
|
||||
if (config.onPanResponderStart) {
|
||||
config.onPanResponderStart(e, gestureState);
|
||||
config.onPanResponderStart(event, gestureState);
|
||||
}
|
||||
},
|
||||
|
||||
onResponderMove: function(e) {
|
||||
const touchHistory = e.touchHistory;
|
||||
onResponderMove(event: PressEvent): void {
|
||||
const touchHistory = event.touchHistory;
|
||||
// Guard against the dispatch of two touch moves when there are two
|
||||
// simultaneously changed touches.
|
||||
if (
|
||||
@@ -397,35 +500,35 @@ const PanResponder = {
|
||||
// already processed multi-touch geometry during the first event.
|
||||
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
||||
if (config.onPanResponderMove) {
|
||||
config.onPanResponderMove(e, gestureState);
|
||||
config.onPanResponderMove(event, gestureState);
|
||||
}
|
||||
},
|
||||
|
||||
onResponderEnd: function(e) {
|
||||
const touchHistory = e.touchHistory;
|
||||
onResponderEnd(event: PressEvent): void {
|
||||
const touchHistory = event.touchHistory;
|
||||
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
|
||||
clearInteractionHandle(
|
||||
interactionState,
|
||||
config.onPanResponderEnd,
|
||||
e,
|
||||
event,
|
||||
gestureState,
|
||||
);
|
||||
},
|
||||
|
||||
onResponderTerminate: function(e) {
|
||||
onResponderTerminate(event: PressEvent): void {
|
||||
clearInteractionHandle(
|
||||
interactionState,
|
||||
config.onPanResponderTerminate,
|
||||
e,
|
||||
event,
|
||||
gestureState,
|
||||
);
|
||||
PanResponder._initializeGestureState(gestureState);
|
||||
},
|
||||
|
||||
onResponderTerminationRequest: function(e) {
|
||||
return config.onPanResponderTerminationRequest === undefined
|
||||
onResponderTerminationRequest(event: PressEvent): boolean {
|
||||
return config.onPanResponderTerminationRequest == null
|
||||
? true
|
||||
: config.onPanResponderTerminationRequest(e, gestureState);
|
||||
: config.onPanResponderTerminationRequest(event, gestureState);
|
||||
},
|
||||
};
|
||||
return {
|
||||
@@ -439,9 +542,9 @@ const PanResponder = {
|
||||
|
||||
function clearInteractionHandle(
|
||||
interactionState: {handle: ?number},
|
||||
callback: Function,
|
||||
event: Object,
|
||||
gestureState: Object,
|
||||
callback: ?(ActiveCallback | PassiveCallback),
|
||||
event: PressEvent,
|
||||
gestureState: GestureState,
|
||||
) {
|
||||
if (interactionState.handle) {
|
||||
InteractionManager.clearInteractionHandle(interactionState.handle);
|
||||
|
||||
@@ -29,6 +29,29 @@ export type SyntheticEvent<T> = $ReadOnly<{|
|
||||
type: ?string,
|
||||
|}>;
|
||||
|
||||
export type ResponderSyntheticEvent<T> = $ReadOnly<{|
|
||||
...SyntheticEvent<T>,
|
||||
touchHistory: $ReadOnly<{|
|
||||
indexOfSingleActiveTouch: number,
|
||||
mostRecentTimeStamp: number,
|
||||
numberActiveTouches: number,
|
||||
touchBank: $ReadOnlyArray<
|
||||
$ReadOnly<{|
|
||||
touchActive: boolean,
|
||||
startPageX: number,
|
||||
startPageY: number,
|
||||
startTimeStamp: number,
|
||||
currentPageX: number,
|
||||
currentPageY: number,
|
||||
currentTimeStamp: number,
|
||||
previousPageX: number,
|
||||
previousPageY: number,
|
||||
previousTimeStamp: number,
|
||||
|}>,
|
||||
>,
|
||||
|}>,
|
||||
|}>;
|
||||
|
||||
export type Layout = $ReadOnly<{|
|
||||
x: number,
|
||||
y: number,
|
||||
@@ -57,7 +80,7 @@ export type TextLayoutEvent = SyntheticEvent<
|
||||
|}>,
|
||||
>;
|
||||
|
||||
export type PressEvent = SyntheticEvent<
|
||||
export type PressEvent = ResponderSyntheticEvent<
|
||||
$ReadOnly<{|
|
||||
changedTouches: $ReadOnlyArray<$PropertyType<PressEvent, 'nativeEvent'>>,
|
||||
force: number,
|
||||
|
||||
Reference in New Issue
Block a user