diff --git a/Libraries/Components/Touchable/Touchable.js b/Libraries/Components/Touchable/Touchable.js index e7925e63c..ea4319a32 100644 --- a/Libraries/Components/Touchable/Touchable.js +++ b/Libraries/Components/Touchable/Touchable.js @@ -583,7 +583,8 @@ const TouchableMixin = { * visually distinguish the `VisualRect` so that the user knows that it * currently has the focus. Most platforms only support a single element being * focused at a time, in which case there may have been a previously focused - * element that was blurred just prior to this. + * element that was blurred just prior to this. This can be overridden when + * using `Touchable.Mixin.withoutDefaultFocusAndBlur`. */ touchableHandleFocus: function(e: Event) { this.props.onFocus && this.props.onFocus(e); @@ -594,6 +595,8 @@ const TouchableMixin = { * visually distinguish the `VisualRect` so that the user knows that it * no longer has focus. Most platforms only support a single element being * focused at a time, in which case the focus may have moved to another. + * This can be overridden when using + * `Touchable.Mixin.withoutDefaultFocusAndBlur`. */ touchableHandleBlur: function(e: Event) { this.props.onBlur && this.props.onBlur(e); @@ -900,8 +903,23 @@ const TouchableMixin = { } } }, + + withoutDefaultFocusAndBlur: {}, }; +/** + * Provide an optional version of the mixin where `touchableHandleFocus` and + * `touchableHandleBlur` can be overridden. This allows appropriate defaults to + * be set on TV platforms, without breaking existing implementations of + * `Touchable`. + */ +const { + touchableHandleFocus, + touchableHandleBlur, + ...TouchableMixinWithoutDefaultFocusAndBlur +} = TouchableMixin; +TouchableMixin.withoutDefaultFocusAndBlur = TouchableMixinWithoutDefaultFocusAndBlur; + const Touchable = { Mixin: TouchableMixin, TOUCH_TARGET_DEBUG: false, // Highlights all touchable targets. Toggle with Inspector. diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index d25cc8326..16415a765 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -13,6 +13,7 @@ const Animated = require('Animated'); const DeprecatedViewPropTypes = require('DeprecatedViewPropTypes'); const DeprecatedEdgeInsetsPropType = require('DeprecatedEdgeInsetsPropType'); const NativeMethodsMixin = require('NativeMethodsMixin'); +const Platform = require('Platform'); const PropTypes = require('prop-types'); const React = require('React'); const Touchable = require('Touchable'); @@ -52,7 +53,7 @@ type Props = $ReadOnly<{| */ const TouchableBounce = ((createReactClass({ displayName: 'TouchableBounce', - mixins: [Touchable.Mixin, NativeMethodsMixin], + mixins: [Touchable.Mixin.withoutDefaultFocusAndBlur, NativeMethodsMixin], propTypes: { ...TouchableWithoutFeedback.propTypes, @@ -118,6 +119,20 @@ const TouchableBounce = ((createReactClass({ this.props.onPressOut && this.props.onPressOut(e); }, + touchableHandleFocus: function(e: Event) { + if (Platform.isTV) { + this.bounceTo(0.93, 0.1, 0); + } + this.props.onFocus && this.props.onFocus(e); + }, + + touchableHandleBlur: function(e: Event) { + if (Platform.isTV) { + this.bounceTo(1, 0.4, 0); + } + this.props.onBlur && this.props.onBlur(e); + }, + touchableHandlePress: function(e: PressEvent) { const onPressWithCompletion = this.props.onPressWithCompletion; if (onPressWithCompletion) { diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 03e7cd8b8..23d6c7567 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -195,7 +195,7 @@ const TouchableHighlight = ((createReactClass({ testOnly_pressed: PropTypes.bool, }, - mixins: [NativeMethodsMixin, Touchable.Mixin], + mixins: [NativeMethodsMixin, Touchable.Mixin.withoutDefaultFocusAndBlur], getDefaultProps: () => DEFAULT_PROPS, @@ -257,6 +257,20 @@ const TouchableHighlight = ((createReactClass({ this.props.onPressOut && this.props.onPressOut(e); }, + touchableHandleFocus: function(e: Event) { + if (Platform.isTV) { + this._showUnderlay(); + } + this.props.onFocus && this.props.onFocus(e); + }, + + touchableHandleBlur: function(e: Event) { + if (Platform.isTV) { + this._hideUnderlay(); + } + this.props.onBlur && this.props.onBlur(e); + }, + touchableHandlePress: function(e: PressEvent) { clearTimeout(this._hideTimeout); if (!Platform.isTV) { diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 52135ee26..10a4e4a16 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -13,6 +13,7 @@ const Animated = require('Animated'); const Easing = require('Easing'); const NativeMethodsMixin = require('NativeMethodsMixin'); +const Platform = require('Platform'); const React = require('React'); const PropTypes = require('prop-types'); const Touchable = require('Touchable'); @@ -131,7 +132,7 @@ type Props = $ReadOnly<{| */ const TouchableOpacity = ((createReactClass({ displayName: 'TouchableOpacity', - mixins: [Touchable.Mixin, NativeMethodsMixin], + mixins: [Touchable.Mixin.withoutDefaultFocusAndBlur, NativeMethodsMixin], propTypes: { ...TouchableWithoutFeedback.propTypes, @@ -207,6 +208,20 @@ const TouchableOpacity = ((createReactClass({ this.props.onPressOut && this.props.onPressOut(e); }, + touchableHandleFocus: function(e: Event) { + if (Platform.isTV) { + this._opacityActive(150); + } + this.props.onFocus && this.props.onFocus(e); + }, + + touchableHandleBlur: function(e: Event) { + if (Platform.isTV) { + this._opacityInactive(250); + } + this.props.onBlur && this.props.onBlur(e); + }, + touchableHandlePress: function(e: PressEvent) { this.props.onPress && this.props.onPress(e); },