mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-06-03 02:42:05 +00:00
[fix] improve event normalization coverage
This commit is contained in:
@@ -0,0 +1,117 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { storiesOf, action } from '@kadira/storybook';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var ReactNative = require('react-native');
|
||||||
|
var {
|
||||||
|
PanResponder,
|
||||||
|
StyleSheet,
|
||||||
|
View
|
||||||
|
} = ReactNative;
|
||||||
|
|
||||||
|
var CIRCLE_SIZE = 80;
|
||||||
|
|
||||||
|
var PanResponderExample = React.createClass({
|
||||||
|
_panResponder: {},
|
||||||
|
_previousLeft: 0,
|
||||||
|
_previousTop: 0,
|
||||||
|
_circleStyles: {},
|
||||||
|
circle: (null : ?{ setNativeProps(props: Object): void }),
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
this._panResponder = PanResponder.create({
|
||||||
|
onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder,
|
||||||
|
onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
|
||||||
|
onPanResponderGrant: this._handlePanResponderGrant,
|
||||||
|
onPanResponderMove: this._handlePanResponderMove,
|
||||||
|
onPanResponderRelease: this._handlePanResponderEnd,
|
||||||
|
onPanResponderTerminate: this._handlePanResponderEnd,
|
||||||
|
});
|
||||||
|
this._previousLeft = 20;
|
||||||
|
this._previousTop = 84;
|
||||||
|
this._circleStyles = {
|
||||||
|
style: {
|
||||||
|
left: this._previousLeft,
|
||||||
|
top: this._previousTop,
|
||||||
|
backgroundColor: 'green',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this._updateNativeStyles();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={styles.container}>
|
||||||
|
<View
|
||||||
|
ref={(circle) => {
|
||||||
|
this.circle = circle;
|
||||||
|
}}
|
||||||
|
style={styles.circle}
|
||||||
|
{...this._panResponder.panHandlers}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_highlight: function() {
|
||||||
|
this._circleStyles.style.backgroundColor = 'blue';
|
||||||
|
this._updateNativeStyles();
|
||||||
|
},
|
||||||
|
|
||||||
|
_unHighlight: function() {
|
||||||
|
this._circleStyles.style.backgroundColor = 'green';
|
||||||
|
this._updateNativeStyles();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateNativeStyles: function() {
|
||||||
|
this.circle && this.circle.setNativeProps(this._circleStyles);
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleStartShouldSetPanResponder: function(e: Object, gestureState: Object): boolean {
|
||||||
|
// Should we become active when the user presses down on the circle?
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleMoveShouldSetPanResponder: function(e: Object, gestureState: Object): boolean {
|
||||||
|
// Should we become active when the user moves a touch over the circle?
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_handlePanResponderGrant: function(e: Object, gestureState: Object) {
|
||||||
|
this._highlight();
|
||||||
|
},
|
||||||
|
_handlePanResponderMove: function(e: Object, gestureState: Object) {
|
||||||
|
this._circleStyles.style.left = this._previousLeft + gestureState.dx;
|
||||||
|
this._circleStyles.style.top = this._previousTop + gestureState.dy;
|
||||||
|
this._updateNativeStyles();
|
||||||
|
},
|
||||||
|
_handlePanResponderEnd: function(e: Object, gestureState: Object) {
|
||||||
|
this._unHighlight();
|
||||||
|
this._previousLeft += gestureState.dx;
|
||||||
|
this._previousTop += gestureState.dy;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
circle: {
|
||||||
|
width: CIRCLE_SIZE,
|
||||||
|
height: CIRCLE_SIZE,
|
||||||
|
borderRadius: CIRCLE_SIZE / 2,
|
||||||
|
position: 'absolute',
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
paddingTop: 64,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
storiesOf('PanResponder', module)
|
||||||
|
.add('example', () => <PanResponderExample />)
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import normalizeNativeEvent from '../../modules/normalizeNativeEvent';
|
|
||||||
var TouchHistoryMath = require('react/lib/TouchHistoryMath');
|
var TouchHistoryMath = require('react/lib/TouchHistoryMath');
|
||||||
|
|
||||||
var currentCentroidXOfTouchesChangedAfter =
|
var currentCentroidXOfTouchesChangedAfter =
|
||||||
@@ -288,11 +287,11 @@ var PanResponder = {
|
|||||||
var panHandlers = {
|
var panHandlers = {
|
||||||
onStartShouldSetResponder: function(e) {
|
onStartShouldSetResponder: function(e) {
|
||||||
return config.onStartShouldSetPanResponder === undefined ? false :
|
return config.onStartShouldSetPanResponder === undefined ? false :
|
||||||
config.onStartShouldSetPanResponder(normalizeEvent(e), gestureState);
|
config.onStartShouldSetPanResponder(e, gestureState);
|
||||||
},
|
},
|
||||||
onMoveShouldSetResponder: function(e) {
|
onMoveShouldSetResponder: function(e) {
|
||||||
return config.onMoveShouldSetPanResponder === undefined ? false :
|
return config.onMoveShouldSetPanResponder === undefined ? false :
|
||||||
config.onMoveShouldSetPanResponder(normalizeEvent(e), gestureState);
|
config.onMoveShouldSetPanResponder(e, gestureState);
|
||||||
},
|
},
|
||||||
onStartShouldSetResponderCapture: function(e) {
|
onStartShouldSetResponderCapture: function(e) {
|
||||||
// TODO: Actually, we should reinitialize the state any time
|
// TODO: Actually, we should reinitialize the state any time
|
||||||
@@ -302,12 +301,12 @@ var PanResponder = {
|
|||||||
PanResponder._initializeGestureState(gestureState);
|
PanResponder._initializeGestureState(gestureState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (e.nativeEvent.type === 'mousedown') {
|
else if (e.nativeEvent.originalEvent && e.nativeEvent.originalEvent.type === 'mousedown') {
|
||||||
PanResponder._initializeGestureState(gestureState);
|
PanResponder._initializeGestureState(gestureState);
|
||||||
}
|
}
|
||||||
gestureState.numberActiveTouches = e.touchHistory.numberActiveTouches;
|
gestureState.numberActiveTouches = e.touchHistory.numberActiveTouches;
|
||||||
return config.onStartShouldSetPanResponderCapture !== undefined ?
|
return config.onStartShouldSetPanResponderCapture !== undefined ?
|
||||||
config.onStartShouldSetPanResponderCapture(normalizeEvent(e), gestureState) : false;
|
config.onStartShouldSetPanResponderCapture(e, gestureState) : false;
|
||||||
},
|
},
|
||||||
|
|
||||||
onMoveShouldSetResponderCapture: function(e) {
|
onMoveShouldSetResponderCapture: function(e) {
|
||||||
@@ -320,7 +319,7 @@ var PanResponder = {
|
|||||||
}
|
}
|
||||||
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
||||||
return config.onMoveShouldSetPanResponderCapture ?
|
return config.onMoveShouldSetPanResponderCapture ?
|
||||||
config.onMoveShouldSetPanResponderCapture(normalizeEvent(e), gestureState) : false;
|
config.onMoveShouldSetPanResponderCapture(e, gestureState) : false;
|
||||||
},
|
},
|
||||||
|
|
||||||
onResponderGrant: function(e) {
|
onResponderGrant: function(e) {
|
||||||
@@ -328,25 +327,25 @@ var PanResponder = {
|
|||||||
gestureState.y0 = currentCentroidY(e.touchHistory);
|
gestureState.y0 = currentCentroidY(e.touchHistory);
|
||||||
gestureState.dx = 0;
|
gestureState.dx = 0;
|
||||||
gestureState.dy = 0;
|
gestureState.dy = 0;
|
||||||
config.onPanResponderGrant && config.onPanResponderGrant(normalizeEvent(e), gestureState);
|
config.onPanResponderGrant && config.onPanResponderGrant(e, gestureState);
|
||||||
// TODO: t7467124 investigate if this can be removed
|
// TODO: t7467124 investigate if this can be removed
|
||||||
return config.onShouldBlockNativeResponder === undefined ? true :
|
return config.onShouldBlockNativeResponder === undefined ? true :
|
||||||
config.onShouldBlockNativeResponder();
|
config.onShouldBlockNativeResponder();
|
||||||
},
|
},
|
||||||
|
|
||||||
onResponderReject: function(e) {
|
onResponderReject: function(e) {
|
||||||
config.onPanResponderReject && config.onPanResponderReject(normalizeEvent(e), gestureState);
|
config.onPanResponderReject && config.onPanResponderReject(e, gestureState);
|
||||||
},
|
},
|
||||||
|
|
||||||
onResponderRelease: function(e) {
|
onResponderRelease: function(e) {
|
||||||
config.onPanResponderRelease && config.onPanResponderRelease(normalizeEvent(e), gestureState);
|
config.onPanResponderRelease && config.onPanResponderRelease(e, gestureState);
|
||||||
PanResponder._initializeGestureState(gestureState);
|
PanResponder._initializeGestureState(gestureState);
|
||||||
},
|
},
|
||||||
|
|
||||||
onResponderStart: function(e) {
|
onResponderStart: function(e) {
|
||||||
var touchHistory = e.touchHistory;
|
var touchHistory = e.touchHistory;
|
||||||
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
|
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
|
||||||
config.onPanResponderStart && config.onPanResponderStart(normalizeEvent(e), gestureState);
|
config.onPanResponderStart && config.onPanResponderStart(e, gestureState);
|
||||||
},
|
},
|
||||||
|
|
||||||
onResponderMove: function(e) {
|
onResponderMove: function(e) {
|
||||||
@@ -359,13 +358,13 @@ var PanResponder = {
|
|||||||
// Filter out any touch moves past the first one - we would have
|
// Filter out any touch moves past the first one - we would have
|
||||||
// already processed multi-touch geometry during the first event.
|
// already processed multi-touch geometry during the first event.
|
||||||
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
PanResponder._updateGestureStateOnMove(gestureState, touchHistory);
|
||||||
config.onPanResponderMove && config.onPanResponderMove(normalizeEvent(e), gestureState);
|
config.onPanResponderMove && config.onPanResponderMove(e, gestureState);
|
||||||
},
|
},
|
||||||
|
|
||||||
onResponderEnd: function(e) {
|
onResponderEnd: function(e) {
|
||||||
var touchHistory = e.touchHistory;
|
var touchHistory = e.touchHistory;
|
||||||
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
|
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
|
||||||
config.onPanResponderEnd && config.onPanResponderEnd(normalizeEvent(e), gestureState);
|
config.onPanResponderEnd && config.onPanResponderEnd(e, gestureState);
|
||||||
},
|
},
|
||||||
|
|
||||||
onResponderTerminate: function(e) {
|
onResponderTerminate: function(e) {
|
||||||
@@ -376,17 +375,11 @@ var PanResponder = {
|
|||||||
|
|
||||||
onResponderTerminationRequest: function(e) {
|
onResponderTerminationRequest: function(e) {
|
||||||
return config.onPanResponderTerminationRequest === undefined ? true :
|
return config.onPanResponderTerminationRequest === undefined ? true :
|
||||||
config.onPanResponderTerminationRequest(normalizeEvent(e), gestureState);
|
config.onPanResponderTerminationRequest(e, gestureState);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return {panHandlers: panHandlers};
|
return {panHandlers: panHandlers};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function normalizeEvent(e) {
|
|
||||||
const normalizedEvent = Object.create(e);
|
|
||||||
normalizedEvent.nativeEvent = normalizeNativeEvent(e.nativeEvent, e.type);
|
|
||||||
return normalizedEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = PanResponder;
|
module.exports = PanResponder;
|
||||||
|
|||||||
@@ -8,6 +8,29 @@ import StyleSheet from '../../apis/StyleSheet'
|
|||||||
import StyleSheetPropType from '../../propTypes/StyleSheetPropType'
|
import StyleSheetPropType from '../../propTypes/StyleSheetPropType'
|
||||||
import ViewStylePropTypes from './ViewStylePropTypes'
|
import ViewStylePropTypes from './ViewStylePropTypes'
|
||||||
|
|
||||||
|
const eventHandlerNames = [
|
||||||
|
'onClick',
|
||||||
|
'onClickCapture',
|
||||||
|
'onMoveShouldSetResponder',
|
||||||
|
'onMoveShouldSetResponderCapture',
|
||||||
|
'onResponderGrant',
|
||||||
|
'onResponderMove',
|
||||||
|
'onResponderReject',
|
||||||
|
'onResponderRelease',
|
||||||
|
'onResponderTerminate',
|
||||||
|
'onResponderTerminationRequest',
|
||||||
|
'onStartShouldSetResponder',
|
||||||
|
'onStartShouldSetResponderCapture',
|
||||||
|
'onTouchCancel',
|
||||||
|
'onTouchCancelCapture',
|
||||||
|
'onTouchEnd',
|
||||||
|
'onTouchEndCapture',
|
||||||
|
'onTouchMove',
|
||||||
|
'onTouchMoveCapture',
|
||||||
|
'onTouchStart',
|
||||||
|
'onTouchStartCapture'
|
||||||
|
]
|
||||||
|
|
||||||
class View extends Component {
|
class View extends Component {
|
||||||
static displayName = 'View'
|
static displayName = 'View'
|
||||||
|
|
||||||
@@ -79,19 +102,18 @@ class View extends Component {
|
|||||||
// 'View' needs to set 'flexShrink:0' only when there is no 'flex' or 'flexShrink' style provided
|
// 'View' needs to set 'flexShrink:0' only when there is no 'flex' or 'flexShrink' style provided
|
||||||
const needsFlexReset = flattenedStyle.flex == null && flattenedStyle.flexShrink == null
|
const needsFlexReset = flattenedStyle.flex == null && flattenedStyle.flexShrink == null
|
||||||
|
|
||||||
|
const normalizedEventHandlers = eventHandlerNames.reduce((handlerProps, handlerName) => {
|
||||||
|
const handler = this.props[handlerName]
|
||||||
|
if (typeof handler === 'function') {
|
||||||
|
handlerProps[handlerName] = this._normalizeEventForHandler(handler)
|
||||||
|
}
|
||||||
|
return handlerProps
|
||||||
|
}, {})
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
...other,
|
...other,
|
||||||
|
...normalizedEventHandlers,
|
||||||
component: this.context.isInAButtonView ? 'span' : 'div',
|
component: this.context.isInAButtonView ? 'span' : 'div',
|
||||||
onClick: this._normalizeEventForHandler(this.props.onClick),
|
|
||||||
onClickCapture: this._normalizeEventForHandler(this.props.onClickCapture),
|
|
||||||
onTouchCancel: this._normalizeEventForHandler(this.props.onTouchCancel),
|
|
||||||
onTouchCancelCapture: this._normalizeEventForHandler(this.props.onTouchCancelCapture),
|
|
||||||
onTouchEnd: this._normalizeEventForHandler(this.props.onTouchEnd),
|
|
||||||
onTouchEndCapture: this._normalizeEventForHandler(this.props.onTouchEndCapture),
|
|
||||||
onTouchMove: this._normalizeEventForHandler(this.props.onTouchMove),
|
|
||||||
onTouchMoveCapture: this._normalizeEventForHandler(this.props.onTouchMoveCapture),
|
|
||||||
onTouchStart: this._normalizeEventForHandler(this.props.onTouchStart),
|
|
||||||
onTouchStartCapture: this._normalizeEventForHandler(this.props.onTouchStartCapture),
|
|
||||||
style: [
|
style: [
|
||||||
styles.initial,
|
styles.initial,
|
||||||
style,
|
style,
|
||||||
@@ -103,23 +125,15 @@ class View extends Component {
|
|||||||
return createReactDOMComponent(props)
|
return createReactDOMComponent(props)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* React Native expects `pageX` and `pageY` to be on the `nativeEvent`, but
|
|
||||||
* React doesn't include them for touch events.
|
|
||||||
*/
|
|
||||||
_normalizeEventForHandler(handler) {
|
_normalizeEventForHandler(handler) {
|
||||||
return (e) => {
|
const callback = (e) => {
|
||||||
const { pageX } = e.nativeEvent
|
e.nativeEvent = normalizeNativeEvent(e.nativeEvent)
|
||||||
if (pageX === undefined) {
|
return handler(e)
|
||||||
e.nativeEvent = normalizeNativeEvent(e.nativeEvent)
|
|
||||||
}
|
|
||||||
handler && handler(e)
|
|
||||||
}
|
}
|
||||||
|
return callback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyLayout(applyNativeMethods(View))
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
// https://github.com/facebook/css-layout#default-values
|
// https://github.com/facebook/css-layout#default-values
|
||||||
initial: {
|
initial: {
|
||||||
@@ -150,4 +164,4 @@ const styles = StyleSheet.create({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = View
|
module.exports = applyLayout(applyNativeMethods(View))
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ function normalizeMouseEvent(nativeEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeNativeEvent(nativeEvent) {
|
function normalizeNativeEvent(nativeEvent) {
|
||||||
|
if (nativeEvent.originalEvent) { return nativeEvent; }
|
||||||
const eventType = nativeEvent.type || (nativeEvent.originalEvent && nativeEvent.originalEvent.type) || ''
|
const eventType = nativeEvent.type || (nativeEvent.originalEvent && nativeEvent.originalEvent.type) || ''
|
||||||
const mouse = eventType.indexOf('mouse') >= 0
|
const mouse = eventType.indexOf('mouse') >= 0
|
||||||
return mouse ? normalizeMouseEvent(nativeEvent) : normalizeTouchEvent(nativeEvent)
|
return mouse ? normalizeMouseEvent(nativeEvent) : normalizeTouchEvent(nativeEvent)
|
||||||
|
|||||||
Reference in New Issue
Block a user