You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

61 lines
1.6 KiB

import { useRef, useEffect, MutableRefObject } from 'react';
type EventType = MouseEvent | TouchEvent;
type TargetElement = HTMLElement | Element | Document | Window;
type BasicTarget<T = HTMLElement> =
| (() => T | null)
| T
| null
| MutableRefObject<T | null | undefined>;
const defaultEvent = 'click';
const getTargetElement = (
target: BasicTarget<TargetElement>,
defaultElement?: TargetElement,
): TargetElement | undefined | null => {
if (!target) {
return defaultElement;
}
let targetElement: TargetElement | undefined | null;
if (typeof target === 'function') {
targetElement = target();
} else if ('current' in target) {
targetElement = target.current;
} else {
targetElement = target;
}
return targetElement;
};
const useClickAway = (
onClickAway: (event: EventType) => void,
target: BasicTarget | BasicTarget[],
eventName: string = defaultEvent,
): void => {
const onClickAwayRef = useRef(onClickAway);
onClickAwayRef.current = onClickAway;
useEffect(() => {
const handler = (event: any) => {
const targets = Array.isArray(target) ? target : [target];
if (
targets.some((targetItem) => {
const targetElement = getTargetElement(targetItem) as HTMLElement;
return !targetElement || targetElement?.contains(event.target);
})
) {
return;
}
onClickAwayRef.current(event);
};
document.addEventListener(eventName, handler);
return () => {
document.removeEventListener(eventName, handler);
};
}, [target, eventName]);
};
export default useClickAway;