@qazuor/react-hooks
Version:
A comprehensive collection of production-ready React hooks for modern web applications. Features type-safe implementations, extensive testing, and zero dependencies. Includes hooks for state management, browser APIs, user interactions, and development uti
92 lines (79 loc) • 2.81 kB
text/typescript
import { useCallback, useEffect, useState } from 'react';
export interface UsePageLeaveOptions {
/** Threshold in pixels from the top of the page. Default is 0. */
threshold?: number;
/** Whether to track mouse position. Default is true. */
enabled?: boolean;
/** Callback when user leaves the page */
onLeave?: () => void;
/** Callback when user returns to the page */
onReturn?: () => void;
}
export interface UsePageLeaveReturn {
/** Whether the mouse has left the page */
hasLeft: boolean;
/** Enable tracking */
enable: () => void;
/** Disable tracking */
disable: () => void;
}
/**
* usePageLeave
*
* @description This hook detects when the user moves their mouse out of the top edge of the browser window,
* typically indicating an intent to leave the page. It returns a boolean: `true` if the mouse has left,
* `false` otherwise.
* @param {UsePageLeaveOptions} [options] - Optional configuration.
*
* @returns {boolean} - `true` when the mouse pointer moves out of the top boundary of the window.
*
* @example
* ```ts
* const { hasLeft } = usePageLeave();
* if (hasLeft) {
* // Show a modal or an exit-intent popup.
* }
*
* // With options:
* // const { hasLeft, enable, disable } = usePageLeave({ threshold: 20, enabled: false, onLeave: () => console.log('Left!'), onReturn: () => console.log('Returned!') });
* ```
*/
export function usePageLeave(options: UsePageLeaveOptions = {}): UsePageLeaveReturn {
const { threshold = 0, enabled = true, onLeave, onReturn } = options;
const [hasLeft, setHasLeft] = useState(false);
const [isEnabled, setIsEnabled] = useState(enabled);
const handleMouseOut = useCallback(
(e: MouseEvent) => {
if (!isEnabled) return;
if (e.clientY <= threshold) {
setHasLeft(true);
onLeave?.();
}
},
[threshold, isEnabled, onLeave]
);
const handleMouseOver = useCallback(() => {
if (!isEnabled) return;
if (hasLeft) {
setHasLeft(false);
onReturn?.();
}
}, [isEnabled, hasLeft, onReturn]);
const enable = useCallback(() => {
setIsEnabled(true);
}, []);
const disable = useCallback(() => {
setIsEnabled(false);
}, []);
useEffect(() => {
if (isEnabled) {
document.addEventListener('mouseout', handleMouseOut);
document.addEventListener('mouseover', handleMouseOver);
}
return () => {
document.removeEventListener('mouseout', handleMouseOut);
document.removeEventListener('mouseover', handleMouseOver);
};
}, [isEnabled, handleMouseOut, handleMouseOver]);
return { hasLeft, enable, disable };
}