Files
Dwinzo_Demo/app/src/utils/useOuterClick.ts

67 lines
2.2 KiB
TypeScript

import { useEffect, useRef } from "react";
export function useOuterClick(
callback: () => void,
contextClassNames: string[],
enabled: boolean = true,
ignoreIfMoved: boolean = true,
clickType: "left" | "right" = "left",
dependencies: any[] = []
) {
const callbackRef = useRef(callback);
const classNamesRef = useRef(contextClassNames);
const mouseDownTargetRef = useRef<EventTarget | null>(null);
const movedRef = useRef(false);
// Keep refs updated
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
useEffect(() => {
classNamesRef.current = contextClassNames;
}, [contextClassNames]);
useEffect(() => {
if (!enabled) return;
const handleMouseDown = (event: MouseEvent) => {
mouseDownTargetRef.current = event.target;
movedRef.current = false;
};
const handleMouseMove = () => {
movedRef.current = true;
};
const handleMouseUp = (event: MouseEvent) => {
const downTarget = mouseDownTargetRef.current as HTMLElement | null;
const upTarget = event.target as HTMLElement;
if (!downTarget || !upTarget) return;
// Check click type
const isCorrectClick = (clickType === "left" && event.button === 0) || (clickType === "right" && event.button === 2);
if (!isCorrectClick) return;
if (ignoreIfMoved && movedRef.current) return;
// Check if click is inside any ignored context
const isInside = classNamesRef.current.some((className) => downTarget.closest(`.${className}`) || upTarget.closest(`.${className}`));
if (!isInside) {
callbackRef.current();
}
};
document.addEventListener("mousedown", handleMouseDown);
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
return () => {
document.removeEventListener("mousedown", handleMouseDown);
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};
}, [enabled, ignoreIfMoved, clickType, ...dependencies]);
}