react-native/Libraries/Components/Touchable/TouchableOpacity.js

199 lines
5.6 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 TouchableOpacity
* @noflow
*/
'use strict';
// Note (avik): add @flow when Flow supports spread properties in propTypes
var Animated = require('Animated');
var Easing = require('Easing');
var NativeMethodsMixin = require('NativeMethodsMixin');
var React = require('React');
var PropTypes = require('prop-types');
var TimerMixin = require('react-timer-mixin');
var Touchable = require('Touchable');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
var createReactClass = require('create-react-class');
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
var flattenStyle = require('flattenStyle');
type Event = Object;
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, dimming it.
*
* Opacity is controlled by wrapping the children in an Animated.View, which is
* added to the view hiearchy. Be aware that this can affect layout.
*
* Example:
*
* ```
* renderButton: function() {
* return (
* <TouchableOpacity onPress={this._onPressButton}>
* <Image
* style={styles.button}
* source={require('./myButton.png')}
* />
* </TouchableOpacity>
* );
* },
* ```
*/
var TouchableOpacity = createReactClass({
displayName: 'TouchableOpacity',
mixins: [TimerMixin, Touchable.Mixin, NativeMethodsMixin],
propTypes: {
...TouchableWithoutFeedback.propTypes,
/**
* Determines what the opacity of the wrapped view should be when touch is
* active. Defaults to 0.2.
*/
activeOpacity: PropTypes.number,
/**
* Apple TV parallax effects
*/
tvParallaxProperties: PropTypes.object,
},
getDefaultProps: function() {
return {
activeOpacity: 0.2,
};
},
getInitialState: function() {
return {
...this.touchableGetInitialState(),
anim: new Animated.Value(this._getChildStyleOpacityWithDefault()),
};
},
componentDidMount: function() {
ensurePositiveDelayProps(this.props);
},
componentWillReceiveProps: function(nextProps) {
ensurePositiveDelayProps(nextProps);
},
/**
* Animate the touchable to a new opacity.
*/
setOpacityTo: function(value: number, duration: number) {
Animated.timing(
this.state.anim,
{
toValue: value,
duration: duration,
easing: Easing.inOut(Easing.quad),
useNativeDriver: true,
}
).start();
},
/**
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
* defined on your component.
*/
touchableHandleActivePressIn: function(e: Event) {
if (e.dispatchConfig.registrationName === 'onResponderGrant') {
this._opacityActive(0);
} else {
this._opacityActive(150);
}
this.props.onPressIn && this.props.onPressIn(e);
},
touchableHandleActivePressOut: function(e: Event) {
this._opacityInactive(250);
this.props.onPressOut && this.props.onPressOut(e);
},
touchableHandlePress: function(e: Event) {
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 || 0;
},
touchableGetLongPressDelayMS: function() {
return this.props.delayLongPress === 0 ? 0 :
this.props.delayLongPress || 500;
},
touchableGetPressOutDelayMS: function() {
return this.props.delayPressOut;
},
_opacityActive: function(duration: number) {
this.setOpacityTo(this.props.activeOpacity, duration);
},
_opacityInactive: function(duration: number) {
this.setOpacityTo(
this._getChildStyleOpacityWithDefault(),
duration
);
},
_getChildStyleOpacityWithDefault: function() {
var childStyle = flattenStyle(this.props.style) || {};
return childStyle.opacity == undefined ? 1 : childStyle.opacity;
},
render: function() {
return (
<Animated.View
accessible={this.props.accessible !== false}
accessibilityLabel={this.props.accessibilityLabel}
accessibilityComponentType={this.props.accessibilityComponentType}
accessibilityTraits={this.props.accessibilityTraits}
style={[this.props.style, {opacity: this.state.anim}]}
nativeID={this.props.nativeID}
testID={this.props.testID}
onLayout={this.props.onLayout}
isTVSelectable={true}
tvParallaxProperties={this.props.tvParallaxProperties}
hitSlop={this.props.hitSlop}
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
onResponderGrant={this.touchableHandleResponderGrant}
onResponderMove={this.touchableHandleResponderMove}
onResponderRelease={this.touchableHandleResponderRelease}
onResponderTerminate={this.touchableHandleResponderTerminate}>
{this.props.children}
{Touchable.renderDebugView({color: 'cyan', hitSlop: this.props.hitSlop})}
</Animated.View>
);
},
});
module.exports = TouchableOpacity;