@darius_rosendahl/react-use-hoverintent
Version:
React hook for hoverIntent
89 lines (81 loc) • 2.29 kB
text/typescript
import { useState, useEffect, useRef, useImperativeHandle } from 'react';
interface optionType {
ref?: React.Ref<HTMLElement | null>;
sensitivity?: number;
interval?: number;
timeout?: number;
}
export function useHoverIntent(options: optionType) {
const { ref, sensitivity = 6, interval = 100, timeout = 0 } = options;
const intentRef = useRef<HTMLElement>(null);
const [isHovering, setIsHovering] = useState(false);
let x = 0,
y = 0,
pX = 0,
pY = 0,
timer = 0;
const delay = (e: MouseEvent) => {
if (timer) {
clearTimeout(timer);
}
return setIsHovering(false);
};
const tracker = (e: MouseEvent) => {
x = e.clientX;
y = e.clientY;
};
const compare = (e: MouseEvent) => {
if (timer) {
clearTimeout(timer);
}
if (Math.abs(pX - x) + Math.abs(pY - y) < sensitivity) {
return setIsHovering(true);
} else {
pX = x;
pY = y;
timer = window.setTimeout(() => compare(e), interval);
}
};
const dispatchOver = (e: MouseEvent) => {
if (timer) {
clearTimeout(timer);
}
if (intentRef.current) {
intentRef.current.removeEventListener('mousemove', tracker, false);
}
if (!isHovering) {
pX = e.clientX;
pY = e.clientY;
if (intentRef.current) {
intentRef.current.addEventListener('mousemove', tracker, false);
}
timer = window.setTimeout(() => compare(e), interval);
}
};
const dispatchOut = (e: MouseEvent) => {
if (timer) {
clearTimeout(timer);
}
if (intentRef.current) {
intentRef.current.removeEventListener('mousemove', tracker, false);
}
if (isHovering) {
timer = window.setTimeout(() => delay(e), timeout);
}
};
useEffect(() => {
const currentRef = intentRef.current;
if (currentRef) {
currentRef.addEventListener('mouseover', dispatchOver, false);
currentRef.addEventListener('mouseout', dispatchOut, false);
}
return () => {
if (currentRef) {
currentRef.removeEventListener('mouseover', dispatchOver, false);
currentRef.removeEventListener('mouseout', dispatchOut, false);
}
};
});
useImperativeHandle(ref, () => intentRef.current, [intentRef]);
return [isHovering, intentRef];
}