diff --git a/src/components/Hoverable/index.js b/src/components/Hoverable/index.js index b3ab1850d874..a73b85795fb0 100644 --- a/src/components/Hoverable/index.js +++ b/src/components/Hoverable/index.js @@ -10,6 +10,9 @@ import {propTypes, defaultProps} from './hoverablePropTypes'; class Hoverable extends Component { constructor(props) { super(props); + + this.handleVisibilityChange = this.handleVisibilityChange.bind(this); + this.state = { isHovered: false, }; @@ -30,6 +33,8 @@ class Hoverable extends Component { // Remember Touchend fires before `mouse` events so we have to use alternative. document.addEventListener('touchmove', this.enableHover); + + document.addEventListener('visibilitychange', this.handleVisibilityChange); } componentDidUpdate(prevProps) { @@ -45,6 +50,7 @@ class Hoverable extends Component { componentWillUnmount() { document.removeEventListener('touchstart', this.disableHover); document.removeEventListener('touchmove', this.enableHover); + document.removeEventListener('visibilitychange', this.handleVisibilityChange); } /** @@ -67,6 +73,14 @@ class Hoverable extends Component { } } + handleVisibilityChange() { + if (document.visibilityState !== 'hidden') { + return; + } + + this.setIsHovered(false); + } + render() { let child = this.props.children; if (_.isArray(this.props.children) && this.props.children.length === 1) { diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js index 200fb885a2de..0ccd27757cd6 100644 --- a/src/components/MenuItem.js +++ b/src/components/MenuItem.js @@ -23,6 +23,7 @@ import * as DeviceCapabilities from '../libs/DeviceCapabilities'; import ControlSelection from '../libs/ControlSelection'; import variables from '../styles/variables'; import * as Session from '../libs/actions/Session'; +import Hoverable from './Hoverable'; const propTypes = { ...menuItemPropTypes, @@ -104,189 +105,202 @@ function MenuItem(props) { const fallbackAvatarSize = props.viewMode === CONST.OPTION_MODE.COMPACT ? CONST.AVATAR_SIZE.SMALL : CONST.AVATAR_SIZE.DEFAULT; return ( - { - if (props.disabled || !props.interactive) { - return; - } + + {(isHovered) => ( + { + if (props.disabled || !props.interactive) { + return; + } - if (e && e.type === 'click') { - e.currentTarget.blur(); - } + if (e && e.type === 'click') { + e.currentTarget.blur(); + } - props.onPress(e); - }, props.isAnonymousAction)} - onPressIn={() => props.shouldBlockSelection && props.isSmallScreenWidth && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} - onPressOut={ControlSelection.unblock} - onSecondaryInteraction={props.onSecondaryInteraction} - style={({hovered, pressed}) => [ - props.style, - !props.interactive && styles.cursorDefault, - StyleUtils.getButtonBackgroundColorStyle(getButtonState(props.focused || hovered, pressed, props.success, props.disabled, props.interactive), true), - (hovered || pressed) && props.hoverAndPressStyle, - ...(_.isArray(props.wrapperStyle) ? props.wrapperStyle : [props.wrapperStyle]), - props.shouldGreyOutWhenDisabled && props.disabled && styles.buttonOpacityDisabled, - ]} - disabled={props.disabled} - ref={props.forwardedRef} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.MENUITEM} - accessibilityLabel={props.title} - > - {({hovered, pressed}) => ( - <> - - {Boolean(props.label) && ( - - {props.label} - - )} - - {Boolean(props.icon) && _.isArray(props.icon) && ( - - )} - {Boolean(props.icon) && !_.isArray(props.icon) && ( - - {props.iconType === CONST.ICON_TYPE_ICON && ( - - )} - {props.iconType === CONST.ICON_TYPE_WORKSPACE && ( - props.shouldBlockSelection && props.isSmallScreenWidth && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} + onPressOut={ControlSelection.unblock} + onSecondaryInteraction={props.onSecondaryInteraction} + style={({pressed}) => [ + props.style, + !props.interactive && styles.cursorDefault, + StyleUtils.getButtonBackgroundColorStyle(getButtonState(props.focused || isHovered, pressed, props.success, props.disabled, props.interactive), true), + (isHovered || pressed) && props.hoverAndPressStyle, + ...(_.isArray(props.wrapperStyle) ? props.wrapperStyle : [props.wrapperStyle]), + props.shouldGreyOutWhenDisabled && props.disabled && styles.buttonOpacityDisabled, + ]} + disabled={props.disabled} + ref={props.forwardedRef} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.MENUITEM} + accessibilityLabel={props.title} + > + {({pressed}) => ( + <> + + {Boolean(props.label) && ( + + + {props.label} + + + )} + + {Boolean(props.icon) && _.isArray(props.icon) && ( + )} - {props.iconType === CONST.ICON_TYPE_AVATAR && ( - - )} - - )} - - {Boolean(props.description) && props.shouldShowDescriptionOnTop && ( - - {props.description} - - )} - - {Boolean(props.title) && ( - - {convertToLTR(props.title)} - - )} - {Boolean(props.shouldShowTitleIcon) && ( - - + {Boolean(props.icon) && !_.isArray(props.icon) && ( + + {props.iconType === CONST.ICON_TYPE_ICON && ( + + )} + {props.iconType === CONST.ICON_TYPE_WORKSPACE && ( + + )} + {props.iconType === CONST.ICON_TYPE_AVATAR && ( + + )} )} + + {Boolean(props.description) && props.shouldShowDescriptionOnTop && ( + + {props.description} + + )} + + {Boolean(props.title) && ( + + {convertToLTR(props.title)} + + )} + {Boolean(props.shouldShowTitleIcon) && ( + + + + )} + + {Boolean(props.description) && !props.shouldShowDescriptionOnTop && ( + + {props.description} + + )} + {Boolean(props.furtherDetails) && ( + + + + {props.furtherDetails} + + + )} + - {Boolean(props.description) && !props.shouldShowDescriptionOnTop && ( - - {props.description} - + + + {Boolean(props.badgeText) && ( + + )} + {/* Since subtitle can be of type number, we should allow 0 to be shown */} + {(props.subtitle || props.subtitle === 0) && ( + + {props.subtitle} + + )} + {!_.isEmpty(props.floatRightAvatars) && ( + + + )} - {Boolean(props.furtherDetails) && ( - + {Boolean(props.brickRoadIndicator) && ( + - - {props.furtherDetails} - )} + {Boolean(props.shouldShowRightIcon) && ( + + + + )} + {props.shouldShowSelectedState && } - - - - {Boolean(props.badgeText) && ( - - )} - {/* Since subtitle can be of type number, we should allow 0 to be shown */} - {(props.subtitle || props.subtitle === 0) && ( - - {props.subtitle} - - )} - {!_.isEmpty(props.floatRightAvatars) && ( - - - - )} - {Boolean(props.brickRoadIndicator) && ( - - - - )} - {Boolean(props.shouldShowRightIcon) && ( - - - - )} - {props.shouldShowSelectedState && } - - + + )} + )} - + ); }