click-away-listener
Version:
A React component and hook library for detecting clicks outside an element, compatible with React 18 and Next.js
87 lines • 4.05 kB
JavaScript
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
import { useEffect, useRef, useCallback } from 'react';
import { isMouseEvent, isTouchEvent, isFocusEvent, isKeyboardEvent, isInExcluded, getEventGroup } from './utils';
export var useClickAway = function (ref, onClickAway, options) {
if (options === void 0) { options = {}; }
var _a = options.disabled, disabled = _a === void 0 ? false : _a, _b = options.eventConfig, eventConfig = _b === void 0 ? { mouseEvents: ['mousedown'], touchEvents: ['touchstart'] } : _b, _c = options.excludeRefs, excludeRefs = _c === void 0 ? [] : _c, _d = options.debounceTimeout, debounceTimeout = _d === void 0 ? 0 : _d, _e = options.clickTolerance, clickTolerance = _e === void 0 ? 5 : _e, _f = options.testMode, testMode = _f === void 0 ? false : _f;
var mountedRef = useRef(false);
var timerRef = useRef();
var coordsRef = useRef(null);
var handleEvent = useCallback(function (event) {
var _a;
if (!mountedRef.current || disabled)
return;
var target = event.target;
var eventGroup = getEventGroup(event);
if (!target || !document.contains(target))
return;
if (((_a = ref.current) === null || _a === void 0 ? void 0 : _a.contains(target)) || isInExcluded(target, excludeRefs)) {
return;
}
if (isMouseEvent(event) && clickTolerance > 0) {
var clientX = event.clientX, clientY = event.clientY;
if (coordsRef.current) {
var distance = Math.sqrt(Math.pow(clientX - coordsRef.current.x, 2) +
Math.pow(clientY - coordsRef.current.y, 2));
if (distance > clickTolerance)
return;
}
coordsRef.current = { x: clientX, y: clientY };
}
var handler = function () {
if (eventGroup === 'mouse' && isMouseEvent(event)) {
onClickAway(event);
}
else if (eventGroup === 'touch' && isTouchEvent(event)) {
onClickAway(event);
}
else if (eventGroup === 'focus' && isFocusEvent(event)) {
onClickAway(event);
}
else if (eventGroup === 'keyboard' && isKeyboardEvent(event)) {
onClickAway(event);
}
};
if (debounceTimeout > 0) {
if (timerRef.current) {
window.clearTimeout(timerRef.current);
}
timerRef.current = window.setTimeout(handler, debounceTimeout);
}
else {
handler();
}
}, [ref, disabled, excludeRefs, onClickAway, clickTolerance, debounceTimeout]);
useEffect(function () {
mountedRef.current = true;
return function () {
mountedRef.current = false;
if (timerRef.current) {
window.clearTimeout(timerRef.current);
}
};
}, []);
useEffect(function () {
if (testMode || disabled)
return;
var events = __spreadArray(__spreadArray(__spreadArray(__spreadArray([], (eventConfig.mouseEvents || []), true), (eventConfig.touchEvents || []), true), (eventConfig.focusEvents || []), true), (eventConfig.keyboardEvents || []), true);
var options = { capture: true };
events.forEach(function (event) {
document.addEventListener(event, handleEvent, options);
});
return function () {
events.forEach(function (event) {
document.removeEventListener(event, handleEvent, options);
});
};
}, [eventConfig, handleEvent, disabled, testMode]);
};
//# sourceMappingURL=useClickAway.js.map