import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment'; | |
/** | |
* Touch devices emulate mouse events. This functions makes it possible to know | |
* if the current modality supports hover (including for multi-modality | |
* devices). | |
*/ | |
const createHoverMonitor = () => { | |
let isHoverEnabled = false; | |
let lastTouchTime = 0; | |
function enableHover() { | |
if (isHoverEnabled || Date.now() - lastTouchTime < 500) { | |
return; | |
} | |
isHoverEnabled = true; | |
} | |
function disableHover() { | |
lastTouchTime = new Date(); | |
if (isHoverEnabled) { | |
isHoverEnabled = false; | |
} | |
} | |
if (canUseDOM) { | |
document.addEventListener('touchstart', disableHover, true); | |
document.addEventListener('mousemove', enableHover, true); | |
} | |
return { | |
get isEnabled() { | |
return isHoverEnabled; | |
} | |
}; | |
}; | |
export default createHoverMonitor; |
import createHoverMonitor from './createHoverMonitor'; | |
import { element, func, oneOfType } from 'prop-types'; | |
import React, { Component } from 'react'; | |
const hover = createHoverMonitor(); | |
/** | |
* Use: | |
* <Hoverable> | |
* {(hover) => <View style={hover && styles.hovered} />} | |
* </Hoverable> | |
* | |
* Example: https://imaginary-lycra.glitch.me/ | |
* Example source: https://glitch.com/edit/#!/imaginary-lycra | |
*/ | |
class Hoverable extends Component { | |
static displayName = 'Hoverable'; | |
static propTypes = { | |
children: oneOfType([func, element]).isRequired, | |
onHoverIn: func, | |
onHoverOut: func | |
}; | |
state = { isHovered: false }; | |
_handleMouseEnter = e => { | |
if (hover.isEnabled && !this.state.isHovered) { | |
const { onHoverIn } = this.props; | |
if (onHoverIn) { | |
onHoverIn(); | |
} | |
this.setState(() => ({ isHovered: true })); | |
} | |
}; | |
_handleMouseLeave = e => { | |
if (this.state.isHovered) { | |
const { onHoverOut } = this.props; | |
if (onHoverOut) { | |
onHoverOut(); | |
} | |
this.setState(() => ({ isHovered: false })); | |
} | |
}; | |
render() { | |
const { | |
children, | |
/* eslint-disable */ | |
onHoverIn, | |
onHoverOut | |
/* eslint-enable */ | |
} = this.props; | |
const child = typeof children === 'function' ? children(this.state.isHovered) : children; | |
return React.cloneElement(React.Children.only(child), { | |
onMouseEnter: this._handleMouseEnter, | |
onMouseLeave: this._handleMouseLeave | |
}); | |
} | |
} | |
export default Hoverable; |
version with Hooks: https://codesandbox.io/s/react-native-hover-hooks-version-4oxpp
version with Hooks: https://codesandbox.io/s/react-native-hover-hooks-version-4oxpp
Very neat solution! Did anyone notice that after a click, the hover effect isn't performing anymore? Cool tricks!
gd
version with Hooks: https://codesandbox.io/s/react-native-hover-hooks-version-4oxpp
Very neat solution! Did anyone notice that after a click, the hover effect isn't performing anymore? Cool tricks!
I did! But I'm running almost the exact same code on a project and it's working great even after clicks.
I did! But I'm running almost the exact same code on a project and it's working great even after clicks.
Could you share your improved version (or atleast the changes) please @ianmartorell?
Could you share your improved version (or atleast the changes) please @ianmartorell?
Definitely! I don't really remember but I think the only thing I added were Typescript types really.
https://gist.github.com/ianmartorell/32bb7df95e5eff0a5ee2b2f55095e6a6
I've been using a version of this for a while, and it's been great! But it doesn't play nice with modals:
- I'm hovering over a component
- Click a button to open a modal
- Click outside the modal to close it
- The
Hoverable
is stuck in the wrong state becauseonHoverOut
is never called, but my mouse is no longer hovering it.
Has anyone run into this? Any clever ideas for a solution?
Here's my implementation btw: https://github.com/Expensify/Expensify.cash/tree/3a59fc2d212c9dc6af9eab1292e48d1cacaafbc3/src/components/Hoverable
EDIT: Found a solution that works for me:
https://gist.github.com/roryabraham/65cd1d2d5e8a48da78fec6a6e3105398
I actually don't have that problem at all, using React Native Paper's Dialog
.
Hoverable.mov
Here's my version, which uses TypeScript, hooks, and Reanimated shared values instead of re-renders: https://gist.github.com/nandorojo/066ff2f40419b7e06054cc7282e24f8d
Updated version on CodeSandbox: https://codesandbox.io/s/o9q8vy70l5