mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-11 11:29:03 +08:00
Summary: Update to docs to indicate that TouchableHighlight must have exactly one child (i.e. not zero or more than one). Previously it was only indicated that it cannot have more than one. Closes https://github.com/facebook/react-native/pull/10244 Differential Revision: D3970841 fbshipit-source-id: f1c4c223cfaf150fec9bbae1041567d0c81eb63b
267 lines
7.8 KiB
JavaScript
267 lines
7.8 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule TouchableHighlight
|
|
* @noflow
|
|
*/
|
|
'use strict';
|
|
|
|
// Note (avik): add @flow when Flow supports spread properties in propTypes
|
|
|
|
var ColorPropType = require('ColorPropType');
|
|
var NativeMethodsMixin = require('react/lib/NativeMethodsMixin');
|
|
var React = require('React');
|
|
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
|
var StyleSheet = require('StyleSheet');
|
|
var TimerMixin = require('react-timer-mixin');
|
|
var Touchable = require('Touchable');
|
|
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
|
|
var View = require('View');
|
|
|
|
var ensureComponentIsNative = require('ensureComponentIsNative');
|
|
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
|
|
var keyOf = require('fbjs/lib/keyOf');
|
|
var merge = require('merge');
|
|
var onlyChild = require('react/lib/onlyChild');
|
|
|
|
type Event = Object;
|
|
|
|
var DEFAULT_PROPS = {
|
|
activeOpacity: 0.85,
|
|
underlayColor: 'black',
|
|
};
|
|
|
|
var PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
|
|
|
/**
|
|
* A wrapper for making views respond properly to touches.
|
|
* On press down, the opacity of the wrapped view is decreased, which allows
|
|
* the underlay color to show through, darkening or tinting the view. The
|
|
* underlay comes from adding a view to the view hierarchy, which can sometimes
|
|
* cause unwanted visual artifacts if not used correctly, for example if the
|
|
* backgroundColor of the wrapped view isn't explicitly set to an opaque color.
|
|
*
|
|
* Example:
|
|
*
|
|
* ```
|
|
* renderButton: function() {
|
|
* return (
|
|
* <TouchableHighlight onPress={this._onPressButton}>
|
|
* <Image
|
|
* style={styles.button}
|
|
* source={require('./myButton.png')}
|
|
* />
|
|
* </TouchableHighlight>
|
|
* );
|
|
* },
|
|
* ```
|
|
* > **NOTE**: TouchableHighlight must have one child (not zero or more than one)
|
|
* >
|
|
* > If you wish to have several child components, wrap them in a View.
|
|
*/
|
|
|
|
var TouchableHighlight = React.createClass({
|
|
propTypes: {
|
|
...TouchableWithoutFeedback.propTypes,
|
|
/**
|
|
* Determines what the opacity of the wrapped view should be when touch is
|
|
* active.
|
|
*/
|
|
activeOpacity: React.PropTypes.number,
|
|
/**
|
|
* The color of the underlay that will show through when the touch is
|
|
* active.
|
|
*/
|
|
underlayColor: ColorPropType,
|
|
style: View.propTypes.style,
|
|
/**
|
|
* Called immediately after the underlay is shown
|
|
*/
|
|
onShowUnderlay: React.PropTypes.func,
|
|
/**
|
|
* Called immediately after the underlay is hidden
|
|
*/
|
|
onHideUnderlay: React.PropTypes.func,
|
|
},
|
|
|
|
mixins: [NativeMethodsMixin, TimerMixin, Touchable.Mixin],
|
|
|
|
getDefaultProps: () => DEFAULT_PROPS,
|
|
|
|
// Performance optimization to avoid constantly re-generating these objects.
|
|
_computeSyntheticState: function(props) {
|
|
return {
|
|
activeProps: {
|
|
style: {
|
|
opacity: props.activeOpacity,
|
|
}
|
|
},
|
|
activeUnderlayProps: {
|
|
style: {
|
|
backgroundColor: props.underlayColor,
|
|
}
|
|
},
|
|
underlayStyle: [
|
|
INACTIVE_UNDERLAY_PROPS.style,
|
|
props.style,
|
|
]
|
|
};
|
|
},
|
|
|
|
getInitialState: function() {
|
|
return merge(
|
|
this.touchableGetInitialState(), this._computeSyntheticState(this.props)
|
|
);
|
|
},
|
|
|
|
componentDidMount: function() {
|
|
ensurePositiveDelayProps(this.props);
|
|
ensureComponentIsNative(this.refs[CHILD_REF]);
|
|
},
|
|
|
|
componentDidUpdate: function() {
|
|
ensureComponentIsNative(this.refs[CHILD_REF]);
|
|
},
|
|
|
|
componentWillReceiveProps: function(nextProps) {
|
|
ensurePositiveDelayProps(nextProps);
|
|
if (nextProps.activeOpacity !== this.props.activeOpacity ||
|
|
nextProps.underlayColor !== this.props.underlayColor ||
|
|
nextProps.style !== this.props.style) {
|
|
this.setState(this._computeSyntheticState(nextProps));
|
|
}
|
|
},
|
|
|
|
viewConfig: {
|
|
uiViewClassName: 'RCTView',
|
|
validAttributes: ReactNativeViewAttributes.RCTView
|
|
},
|
|
|
|
/**
|
|
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
|
|
* defined on your component.
|
|
*/
|
|
touchableHandleActivePressIn: function(e: Event) {
|
|
this.clearTimeout(this._hideTimeout);
|
|
this._hideTimeout = null;
|
|
this._showUnderlay();
|
|
this.props.onPressIn && this.props.onPressIn(e);
|
|
},
|
|
|
|
touchableHandleActivePressOut: function(e: Event) {
|
|
if (!this._hideTimeout) {
|
|
this._hideUnderlay();
|
|
}
|
|
this.props.onPressOut && this.props.onPressOut(e);
|
|
},
|
|
|
|
touchableHandlePress: function(e: Event) {
|
|
this.clearTimeout(this._hideTimeout);
|
|
this._showUnderlay();
|
|
this._hideTimeout = this.setTimeout(this._hideUnderlay,
|
|
this.props.delayPressOut || 100);
|
|
this.props.onPress && this.props.onPress(e);
|
|
},
|
|
|
|
touchableHandleLongPress: function(e: Event) {
|
|
this.props.onLongPress && this.props.onLongPress(e);
|
|
},
|
|
|
|
touchableGetPressRectOffset: function() {
|
|
return this.props.pressRetentionOffset || PRESS_RETENTION_OFFSET;
|
|
},
|
|
|
|
touchableGetHitSlop: function() {
|
|
return this.props.hitSlop;
|
|
},
|
|
|
|
touchableGetHighlightDelayMS: function() {
|
|
return this.props.delayPressIn;
|
|
},
|
|
|
|
touchableGetLongPressDelayMS: function() {
|
|
return this.props.delayLongPress;
|
|
},
|
|
|
|
touchableGetPressOutDelayMS: function() {
|
|
return this.props.delayPressOut;
|
|
},
|
|
|
|
_showUnderlay: function() {
|
|
if (!this.isMounted() || !this._hasPressHandler()) {
|
|
return;
|
|
}
|
|
|
|
this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
|
|
this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
|
|
this.props.onShowUnderlay && this.props.onShowUnderlay();
|
|
},
|
|
|
|
_hideUnderlay: function() {
|
|
this.clearTimeout(this._hideTimeout);
|
|
this._hideTimeout = null;
|
|
if (this._hasPressHandler() && this.refs[UNDERLAY_REF]) {
|
|
this.refs[CHILD_REF].setNativeProps(INACTIVE_CHILD_PROPS);
|
|
this.refs[UNDERLAY_REF].setNativeProps({
|
|
...INACTIVE_UNDERLAY_PROPS,
|
|
style: this.state.underlayStyle,
|
|
});
|
|
this.props.onHideUnderlay && this.props.onHideUnderlay();
|
|
}
|
|
},
|
|
|
|
_hasPressHandler: function() {
|
|
return !!(
|
|
this.props.onPress ||
|
|
this.props.onPressIn ||
|
|
this.props.onPressOut ||
|
|
this.props.onLongPress
|
|
);
|
|
},
|
|
|
|
render: function() {
|
|
return (
|
|
<View
|
|
accessible={this.props.accessible !== false}
|
|
accessibilityLabel={this.props.accessibilityLabel}
|
|
accessibilityComponentType={this.props.accessibilityComponentType}
|
|
accessibilityTraits={this.props.accessibilityTraits}
|
|
ref={UNDERLAY_REF}
|
|
style={this.state.underlayStyle}
|
|
onLayout={this.props.onLayout}
|
|
hitSlop={this.props.hitSlop}
|
|
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
|
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
|
onResponderGrant={this.touchableHandleResponderGrant}
|
|
onResponderMove={this.touchableHandleResponderMove}
|
|
onResponderRelease={this.touchableHandleResponderRelease}
|
|
onResponderTerminate={this.touchableHandleResponderTerminate}
|
|
testID={this.props.testID}>
|
|
{React.cloneElement(
|
|
onlyChild(this.props.children),
|
|
{
|
|
ref: CHILD_REF,
|
|
}
|
|
)}
|
|
{Touchable.renderDebugView({color: 'green', hitSlop: this.props.hitSlop})}
|
|
</View>
|
|
);
|
|
}
|
|
});
|
|
|
|
var CHILD_REF = keyOf({childRef: null});
|
|
var UNDERLAY_REF = keyOf({underlayRef: null});
|
|
var INACTIVE_CHILD_PROPS = {
|
|
style: StyleSheet.create({x: {opacity: 1.0}}).x,
|
|
};
|
|
var INACTIVE_UNDERLAY_PROPS = {
|
|
style: StyleSheet.create({x: {backgroundColor: 'transparent'}}).x,
|
|
};
|
|
|
|
module.exports = TouchableHighlight;
|