Files
convex-monorepo/packages/ui/src/hooks/use-on-click-outside.tsx
T

60 lines
1.5 KiB
TypeScript

import * as React from 'react';
type EventType =
| 'mousedown'
| 'mouseup'
| 'touchstart'
| 'touchend'
| 'focusin'
| 'focusout';
export const useOnClickOutside = <T extends Element>(
ref: React.RefObject<T | null> | React.RefObject<T | null>[],
handler: (event: MouseEvent | TouchEvent | FocusEvent) => void,
eventType: EventType = 'mousedown',
eventListenerOptions: AddEventListenerOptions = {},
): void => {
const savedHandler = React.useRef(handler);
React.useLayoutEffect(() => {
savedHandler.current = handler;
}, [handler]);
React.useEffect(() => {
const listener = (event: MouseEvent | TouchEvent | FocusEvent) => {
const target = event.target as Node;
// Do nothing if the target is not connected element with document
if (!target.isConnected) {
return;
}
const isOutside = Array.isArray(ref)
? ref
.filter((r) => Boolean(r.current))
.every((r) => r.current && !r.current.contains(target))
: ref.current && !ref.current.contains(target);
if (isOutside) {
savedHandler.current(event);
}
};
document.addEventListener(
eventType,
listener as EventListener,
eventListenerOptions,
);
return () => {
document.removeEventListener(
eventType,
listener as EventListener,
eventListenerOptions,
);
};
}, [ref, eventType, eventListenerOptions]);
};
export type { EventType };