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

289 lines
7.6 KiB
JavaScript
Raw Normal View History

/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const Animated = require('Animated');
const Easing = require('Easing');
const NativeMethodsMixin = require('NativeMethodsMixin');
const React = require('React');
const PropTypes = require('prop-types');
const TimerMixin = require('react-timer-mixin');
const Touchable = require('Touchable');
const TouchableWithoutFeedback = require('TouchableWithoutFeedback');
const createReactClass = require('create-react-class');
const ensurePositiveDelayProps = require('ensurePositiveDelayProps');
const flattenStyle = require('flattenStyle');
import type {Props as TouchableWithoutFeedbackProps} from 'TouchableWithoutFeedback';
import type {ViewStyleProp} from 'StyleSheet';
type Event = Object;
const PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
type TVProps = $ReadOnly<{|
hasTVPreferredFocus?: ?boolean,
tvParallaxProperties?: ?Object,
|}>;
type Props = $ReadOnly<{|
...TouchableWithoutFeedbackProps,
...TVProps,
activeOpacity?: ?number,
style?: ?ViewStyleProp,
|}>;
/**
* 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>
* );
* },
* ```
* ### Example
*
* ```ReactNativeWebPlayer
* import React, { Component } from 'react'
* import {
* AppRegistry,
* StyleSheet,
* TouchableOpacity,
* Text,
* View,
* } from 'react-native'
*
* class App extends Component {
* constructor(props) {
* super(props)
* this.state = { count: 0 }
* }
*
* onPress = () => {
* this.setState({
* count: this.state.count+1
* })
* }
*
* render() {
* return (
* <View style={styles.container}>
* <TouchableOpacity
* style={styles.button}
* onPress={this.onPress}
* >
* <Text> Touch Here </Text>
* </TouchableOpacity>
* <View style={[styles.countContainer]}>
* <Text style={[styles.countText]}>
* { this.state.count !== 0 ? this.state.count: null}
* </Text>
* </View>
* </View>
* )
* }
* }
*
* const styles = StyleSheet.create({
* container: {
* flex: 1,
* justifyContent: 'center',
* paddingHorizontal: 10
* },
* button: {
* alignItems: 'center',
* backgroundColor: '#DDDDDD',
* padding: 10
* },
* countContainer: {
* alignItems: 'center',
* padding: 10
* },
* countText: {
* color: '#FF00FF'
* }
* })
*
* AppRegistry.registerComponent('App', () => App)
* ```
*
*/
const TouchableOpacity = ((createReactClass({
displayName: 'TouchableOpacity',
[Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated Summary: Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great. The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages: - We no longer use cloneElement so we're no longer messing up with elements that are not our own. - Refs are now working correctly for children elements - No longer need to enforce that there's only one child and that this child is a native element The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process. I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well. **Upgrade Path:** If the child is a View, move the style of the View to TouchableOpacity and remove the View itself. ``` <TouchableOpacity onPress={...}> <View style={...}> ... </View> </TouchableOpacity> --> <TouchableOpacity onPress={...} style={...}> ... </TouchableOpacity> ``` If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 23:29:40 +00:00
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,
/**
Add support for Android TV devices Summary: <!-- Thank you for sending the PR! We appreciate you spending the time to work on these changes. Help us understand your motivation by explaining why you decided to make this change. You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html Happy contributing! --> * To be on par with Apple TV support, this makes it possible to run React Native apps on Android TV devices (See also: https://react-native.canny.io/feature-requests/p/android-tv-support) * These changes also make it possible to navigate through the app using D-PAD buttons that are present on some mobile devices * Since these changes affect, among others, `ReactRootView.java` and `Touchable.js` code and are closely related to Apple TV implementation, it makes sense for them to be included in the core - React native apps can be launched on Android TV devices and properly render their content - Navigation is possible using left, right, top, bottom arrows from the remote (or D-PAD) - Touchable components can handle D-PAD center button press events and correctly fire their `onPress` handlers - Touchable components will receive `onPressIn` and `onPressOut` events and can react to focus/blur changes appropriately (just like on Apple TV) - `Platform` constants allow to check if the react-native app is running on TV (`Platform.isTV`) - `ScrollView`s behave correctly (same as native implementation) when switching to view outside bounds – that is, the container would scroll such that the newly focused element is fully visible - Native "clicking" sounds are played when moving between focusable elements - Play/Pause click event is send to `TVEventHandler` - Rewind and FastForward events are send to `TVEventHandler` - Back button behaves as a normal Android back button - Diagonal buttons work correctly on Android TV, e.g. if there is no button directly to the right from the focused one, but there is one to the right but a bit higher/lower it will grab focus - Dev menu can be accessed by long pressing fast forward button A demo showing RNTester app running on Android TV device (Amazon Fire TV Stick) can be found here: [![RNAndroidTVDemo](http://img.youtube.com/vi/EzIQErHhY20/0.jpg)](http://www.youtube.com/watch?v=EzIQErHhY20) - `TextInput` will not work on Android TV devices. There's an issue with native `ReactEditText` implementation that prevents it from receiving focus. This makes it impossible to navigate to `TextInput`. This will be fixed next, but will be included in a separate Pull Request - ~Overlay permissions cannot be granted on Android TV devices running Android version >= 6.0 This is because the overlay permission can only be granted by firing an Intent to open settings page (`ACTION_MANAGE_OVERLAY_PERMISSION`). Since this page does not exist on TV devices the permission cannot be requested. This will make the app crash when trying to open dev menu (⌘+M) or displaying a redbox error. Note: This does not affect devices running Android version < 6.0 (for example Amazon Fire TV Stick)~ This is now fixed by: https://github.com/facebook/react-native/pull/16596 * Launch the RNTester app on Android TV device. * Ensure it launches without a crash * Ensure basic navigation is possible * Ensure Touchable components can receive select events * Ensure the changes do not break current Android and iOS mobile devices functionality. * Ensure the changes do not break current Apple TV functionality. [RNAndroidTVDemo video](http://img.youtube.com/vi/EzIQErHhY20/0.jpg) * Added `ReactAndroidTVViewManager` that handles TV `KeyEvent`s and dispatches events to JS - This is the core that enables basic navigation functionality on Android TV devices * Following the above change we copy `TVEventHandler.ios.js` into `TVEventHandler.android.js` to enable JS to pick up those native navigation events and dispatch them further to subscribed views. (Note: We do not have a native `TVNavigationEventEmitter` implementation on Android, thus this file is slightly modified, e.g. it does pass `null` to `NativeEventEmitter` constructor) * Added `uiMode` to `AndroidInfoModule`. (**Note**: This required changing `extends BaseJavaModule` to `extends ReactContextBaseJavaModule` to be able to use `getSystemService` which requires `Context` instance! * Added `isTV` constants to both `Platform.ios.js` (keeping the deprecated `isTVOS` as well) and `Platform.android.js` * Changed condition check on `Touchable.js` to use the newly added `isTV` flag to properly handle TV navigation events on Android as well * Added `LEANBACK_LAUNCHER` to `RNTester` `intent-filter` so that it is possible to launch it on Android TV devices. * See also a PR to `react-native-website` repo with updated docs for Android TV: https://github.com/facebook/react-native-website/pull/59 - [ ] Fix `TextInput` components handling by allowing them to be focused and making a proper navigation between them (and/or other components) possible. One thing to note here that the default behavior to immediately open software keyboard when focused on `TextInput` field will need to be adjusted on Android TV as well) - [x] Fix overlay permissions issue by changing the way redbox/dev menu are displayed (see: https://github.com/facebook/react-native/pull/16596) - [ ] Adjust placement of TV-related files (e.g. the `TVEventHandler.js` file is placed inside `AppleTV` directory which is not accurate, since it does handle Android TV events as well) Previous discussion: https://github.com/SoftwareMansion/react-native/pull/1 <!-- Help reviewers and the release process by writing your own release notes **INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.** CATEGORY [----------] TYPE [ CLI ] [-------------] LOCATION [ DOCS ] [ BREAKING ] [-------------] [ GENERAl ] [ BUGFIX ] [-{Component}-] [ INTERNAL ] [ ENHANCEMENT ] [ {File} ] [ IOS ] [ FEATURE ] [ {Directory} ] |-----------| [ ANDROID ] [ MINOR ] [ {Framework} ] - | {Message} | [----------] [-------------] [-------------] |-----------| [CATEGORY] [TYPE] [LOCATION] - MESSAGE EXAMPLES: [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see --> [ANDROID] [FEATURE] [TV] - Added support for Android TV devices Closes https://github.com/facebook/react-native/pull/16500 Differential Revision: D6536847 Pulled By: hramos fbshipit-source-id: 17bbb11e8583b97f195ced5fd9762f8902fb8a3d
2018-03-06 18:41:27 +00:00
* TV preferred focus (see documentation for the View component).
*/
hasTVPreferredFocus: PropTypes.bool,
/**
* Apple TV parallax effects
*/
tvParallaxProperties: PropTypes.object,
},
getDefaultProps: function() {
return {
activeOpacity: 0.2,
};
},
getInitialState: function() {
[Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated Summary: Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great. The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages: - We no longer use cloneElement so we're no longer messing up with elements that are not our own. - Refs are now working correctly for children elements - No longer need to enforce that there's only one child and that this child is a native element The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process. I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well. **Upgrade Path:** If the child is a View, move the style of the View to TouchableOpacity and remove the View itself. ``` <TouchableOpacity onPress={...}> <View style={...}> ... </View> </TouchableOpacity> --> <TouchableOpacity onPress={...} style={...}> ... </TouchableOpacity> ``` If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 23:29:40 +00:00
return {
...this.touchableGetInitialState(),
anim: new Animated.Value(this._getChildStyleOpacityWithDefault()),
[Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated Summary: Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great. The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages: - We no longer use cloneElement so we're no longer messing up with elements that are not our own. - Refs are now working correctly for children elements - No longer need to enforce that there's only one child and that this child is a native element The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process. I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well. **Upgrade Path:** If the child is a View, move the style of the View to TouchableOpacity and remove the View itself. ``` <TouchableOpacity onPress={...}> <View style={...}> ... </View> </TouchableOpacity> --> <TouchableOpacity onPress={...} style={...}> ... </TouchableOpacity> ``` If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 23:29:40 +00:00
};
},
componentDidMount: function() {
ensurePositiveDelayProps(this.props);
},
UNSAFE_componentWillReceiveProps: function(nextProps) {
ensurePositiveDelayProps(nextProps);
},
Update opacity when `disabled` prop is changed Summary: fixes #17105 If you render ``` <TouchableOpacity disabled={true} style={{opacity: 0.5}} > ... </TouchableOpacity> ``` and then ``` <TouchableOpacity disabled={false} style={{opacity: 1}} > ... </TouchableOpacity> ``` The content of `TouchableOpacity` will still have opacity = 0.5 because real opacity is controlled by animated property which should be properly updated when `disabled` prop changes. <!-- Thank you for sending the PR! We appreciate you spending the time to work on these changes. Help us understand your motivation by explaining why you decided to make this change. You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html Happy contributing! --> Usually when a button or other UI component is built with `TouchableOpacity` you may want to change it's opacity if state of component is changed (enabled/disabled). Opacity provided in props is overridden with internally-managed animated value. Add extra check when component is updated to trigger opacity animation upon change of `disabled` flag. You can use code from https://github.com/facebook/react-native/issues/17105. (If this PR adds or changes functionality, please take some time to update the docs at https://github.com/facebook/react-native-website, and link to your PR here.) [GENERAL][BUGFIX][TouchableOpacity] - trigger animation on `opacity` upon change in `disabled` prop. <!-- Help reviewers and the release process by writing your own release notes **INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.** CATEGORY [----------] TYPE [ CLI ] [-------------] LOCATION [ DOCS ] [ BREAKING ] [-------------] [ GENERAL ] [ BUGFIX ] [-{Component}-] [ INTERNAL ] [ ENHANCEMENT ] [ {File} ] [ IOS ] [ FEATURE ] [ {Directory} ] |-----------| [ ANDROID ] [ MINOR ] [ {Framework} ] - | {Message} | [----------] [-------------] [-------------] |-----------| [CATEGORY] [TYPE] [LOCATION] - MESSAGE EXAMPLES: [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see --> Closes https://github.com/facebook/react-native/pull/17106 Differential Revision: D7158549 Pulled By: hramos fbshipit-source-id: 209cc433b829b129810e8a884964c8853ca3fe8f
2018-03-05 23:46:32 +00:00
componentDidUpdate: function(prevProps, prevState) {
if (this.props.disabled !== prevProps.disabled) {
this._opacityInactive(250);
}
},
/**
* 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() {
const childStyle = flattenStyle(this.props.style) || {};
return childStyle.opacity == undefined ? 1 : childStyle.opacity;
},
render: function() {
[Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated Summary: Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great. The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages: - We no longer use cloneElement so we're no longer messing up with elements that are not our own. - Refs are now working correctly for children elements - No longer need to enforce that there's only one child and that this child is a native element The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process. I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well. **Upgrade Path:** If the child is a View, move the style of the View to TouchableOpacity and remove the View itself. ``` <TouchableOpacity onPress={...}> <View style={...}> ... </View> </TouchableOpacity> --> <TouchableOpacity onPress={...} style={...}> ... </TouchableOpacity> ``` If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 23:29:40 +00:00
return (
<Animated.View
accessible={this.props.accessible !== false}
accessibilityLabel={this.props.accessibilityLabel}
accessibilityHint={this.props.accessibilityHint}
accessibilityTraits + accessibilityComponentType >> accessibilityRole + accessibilityStates 2/3 Summary: Previously, I created two props, `accessibilityRole` and `accessibilityStates` for view. These props were intended to be a cross-platform solution to replace `accessibilityComponentType` on Android and `accessibilityTraits` on iOS. In this stack, I ran a code mod to replace instances of the two old properties used in our codebase with the new ones. For this diff, I did a search for all the remnant uses of `accessibilityComponentType` that was not caught by my script, and I manually changed them to `accessibilityRole` and `accessibilityStates`. If the same prop also set `accessibilityTraits` I also removed that here because the two new props works on both platforms. It was difficult to write a script for this, because most of them were contextual changes. Out of the contextual changes, most of them followed one of these two patterns: Before: ``` const accessibilityComponentType = 'button'; const accessibilityTraits = ['button']; if (this.props.checked) { accessibilityTraits.push('selected'); } if (this.props.disabled) { accessibilityTraits.push('disabled'); } contentView = ( <AdsManagerTouchableHighlight accessibilityComponentType={accessibilityComponentType} accessibilityTraits={accessibilityTraits} ``` After: const accessibilityRole = 'button'; const accessibilityStates = []; if (this.props.checked) { accessibilityStates.push('selected'); } if (this.props.disabled) { accessibilityStates.push('disabled'); } contentView = ( <AdsManagerTouchableHighlight accessibilityRole={accessibilityRole} accessibilityStates={accessibilityStates} Before: ``` <PressableBackground accessible={this.props.accessible} accessibilityLabel={this.props.accessibilityLabel} accessibilityTraits={this.props.accessibilityTraits} ``` After: ``` <PressableBackground accessible={this.props.accessible} accessibilityLabel={this.props.accessibilityLabel} accessibilityRole={this.props.accessibilityRole} accessibilityRole={this.props.accessibilityStates} ``` In addition to changing the props on the components, Another fix I had to do was to add props accessibilityRole and accessibilityStates to components that don't directly inherit properties from view including text input and touchables. Reviewed By: PeteTheHeat Differential Revision: D8943499 fbshipit-source-id: fbb40a5e5f5d630b0fe56a009ff24635d4c8cc93
2018-07-26 06:37:16 +00:00
accessibilityRole={this.props.accessibilityRole}
accessibilityStates={this.props.accessibilityStates}
[Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated Summary: Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great. The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages: - We no longer use cloneElement so we're no longer messing up with elements that are not our own. - Refs are now working correctly for children elements - No longer need to enforce that there's only one child and that this child is a native element The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process. I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well. **Upgrade Path:** If the child is a View, move the style of the View to TouchableOpacity and remove the View itself. ``` <TouchableOpacity onPress={...}> <View style={...}> ... </View> </TouchableOpacity> --> <TouchableOpacity onPress={...} style={...}> ... </TouchableOpacity> ``` If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 23:29:40 +00:00
style={[this.props.style, {opacity: this.state.anim}]}
nativeID={this.props.nativeID}
[Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated Summary: Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great. The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages: - We no longer use cloneElement so we're no longer messing up with elements that are not our own. - Refs are now working correctly for children elements - No longer need to enforce that there's only one child and that this child is a native element The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process. I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well. **Upgrade Path:** If the child is a View, move the style of the View to TouchableOpacity and remove the View itself. ``` <TouchableOpacity onPress={...}> <View style={...}> ... </View> </TouchableOpacity> --> <TouchableOpacity onPress={...} style={...}> ... </TouchableOpacity> ``` If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 23:29:40 +00:00
testID={this.props.testID}
onLayout={this.props.onLayout}
isTVSelectable={true}
hasTVPreferredFocus={this.props.hasTVPreferredFocus}
tvParallaxProperties={this.props.tvParallaxProperties}
hitSlop={this.props.hitSlop}
[Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated Summary: Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great. The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages: - We no longer use cloneElement so we're no longer messing up with elements that are not our own. - Refs are now working correctly for children elements - No longer need to enforce that there's only one child and that this child is a native element The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process. I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well. **Upgrade Path:** If the child is a View, move the style of the View to TouchableOpacity and remove the View itself. ``` <TouchableOpacity onPress={...}> <View style={...}> ... </View> </TouchableOpacity> --> <TouchableOpacity onPress={...} style={...}> ... </TouchableOpacity> ``` If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 23:29:40 +00:00
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
onResponderTerminationRequest={
this.touchableHandleResponderTerminationRequest
}
[Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated Summary: Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great. The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages: - We no longer use cloneElement so we're no longer messing up with elements that are not our own. - Refs are now working correctly for children elements - No longer need to enforce that there's only one child and that this child is a native element The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process. I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well. **Upgrade Path:** If the child is a View, move the style of the View to TouchableOpacity and remove the View itself. ``` <TouchableOpacity onPress={...}> <View style={...}> ... </View> </TouchableOpacity> --> <TouchableOpacity onPress={...} style={...}> ... </TouchableOpacity> ``` If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 23:29:40 +00:00
onResponderGrant={this.touchableHandleResponderGrant}
onResponderMove={this.touchableHandleResponderMove}
onResponderRelease={this.touchableHandleResponderRelease}
onResponderTerminate={this.touchableHandleResponderTerminate}>
{this.props.children}
{Touchable.renderDebugView({
color: 'cyan',
hitSlop: this.props.hitSlop,
})}
[Animated][BREAKING_CHANGE] Convert <TouchableOpacity> to Animated Summary: Because we don't want to integrate Animated inside of the core of React, we can only pass Animated.Value to styles of <Animated.View>. TouchableOpacity unfortunately used cloneElement. This means that we should have asked every single call site to replace their children to Animated.View. This isn't great. The other solution is to stop using cloneElement and instead wrap the children inside of an <Animated.View>. This has many advantages: - We no longer use cloneElement so we're no longer messing up with elements that are not our own. - Refs are now working correctly for children elements - No longer need to enforce that there's only one child and that this child is a native element The downside is that we're introducing a <View> into the hierarchy. Sadly with CSS there is no way to have a View that doesn't affect layout. What we need to do is to remove the inner <View> and transfer all the styles to the TouchableOpacity. It is annoying but fortunately a pretty mechanical process. I think that having a wrapper is the best solution. I will investigate to see if we can make wrappers on TouchableHighliht and TouchableWithoutFeedback as well. **Upgrade Path:** If the child is a View, move the style of the View to TouchableOpacity and remove the View itself. ``` <TouchableOpacity onPress={...}> <View style={...}> ... </View> </TouchableOpacity> --> <TouchableOpacity onPress={...} style={...}> ... </TouchableOpacity> ``` If the child is an Image or Text, on all the examples at Facebook it worked without any change. But it is a great idea to double check them anyway.
2015-07-20 23:29:40 +00:00
</Animated.View>
);
},
}): any): React.ComponentType<Props>);
module.exports = TouchableOpacity;