-
-
Save ianmartorell/32bb7df95e5eff0a5ee2b2f55095e6a6 to your computer and use it in GitHub Desktop.
Hover styles in React Native for Web
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useCallback, ReactNode } from 'react'; | |
import { isHoverEnabled } from './HoverState'; | |
export interface HoverableProps { | |
onHoverIn?: () => void; | |
onHoverOut?: () => void; | |
children: ((isHovered: boolean) => ReactNode) | ReactNode; | |
} | |
export default function Hoverable({ onHoverIn, onHoverOut, children }: HoverableProps) { | |
const [isHovered, setHovered] = React.useState(false); | |
const [showHover, setShowHover] = React.useState(true); | |
const handleMouseEnter = useCallback(() => { | |
if (isHoverEnabled() && !isHovered) { | |
if (onHoverIn) onHoverIn(); | |
setHovered(true); | |
} | |
}, [isHovered, onHoverIn]); | |
const handleMouseLeave = useCallback(() => { | |
if (isHovered) { | |
if (onHoverOut) onHoverOut(); | |
setHovered(false); | |
} | |
}, [isHovered, onHoverOut]); | |
const handleGrant = useCallback(() => { | |
setShowHover(false); | |
}, []); | |
const handleRelease = useCallback(() => { | |
setShowHover(true); | |
}, []); | |
const child = | |
typeof children === 'function' ? children(showHover && isHovered) : children; | |
return React.cloneElement(React.Children.only(child), { | |
onMouseEnter: handleMouseEnter, | |
onMouseLeave: handleMouseLeave, | |
// prevent hover showing while responder | |
onResponderGrant: handleGrant, | |
onResponderRelease: handleRelease, | |
// if child is Touchable | |
onPressIn: handleGrant, | |
onPressOut: handleRelease, | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable no-inner-declarations */ | |
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment'; | |
let isEnabled = false; | |
if (canUseDOM) { | |
/** | |
* Web browsers emulate mouse events (and hover states) after touch events. | |
* This code infers when the currently-in-use modality supports hover | |
* (including for multi-modality devices) and considers "hover" to be enabled | |
* if a mouse movement occurs more than 1 second after the last touch event. | |
* This threshold is long enough to account for longer delays between the | |
* browser firing touch and mouse events on low-powered devices. | |
*/ | |
const HOVER_THRESHOLD_MS = 1000; | |
let lastTouchTimestamp = 0; | |
function enableHover() { | |
if (isEnabled || Date.now() - lastTouchTimestamp < HOVER_THRESHOLD_MS) { | |
return; | |
} | |
isEnabled = true; | |
} | |
function disableHover() { | |
lastTouchTimestamp = Date.now(); | |
if (isEnabled) { | |
isEnabled = false; | |
} | |
} | |
document.addEventListener('touchstart', disableHover, true); | |
document.addEventListener('touchmove', disableHover, true); | |
document.addEventListener('mousemove', enableHover, true); | |
} | |
export function isHoverEnabled(): boolean { | |
return isEnabled; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const Usage = () => ( | |
<Hoverable> | |
{isHovered => ( | |
<Touchable accessibilityRole="link" onPress={() => {}}> | |
<Text style={[styles.link, isHovered && styles.linkActive]}> | |
Click me | |
</Text> | |
</Touchable> | |
)} | |
</Hoverable> | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment