@clayui/shared
Version:
ClayShared component
112 lines (108 loc) • 3.89 kB
JavaScript
/**
* SPDX-FileCopyrightText: © 2022 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: BSD-3-Clause
*/
import { useEffect, useRef } from 'react';
/**
* Code ported with some minor modifications
* https://github.com/adobe/react-spectrum/blob/810579b671791f1593108f62cdc1893de3a220e3/packages/%40react-aria/interactions/src/useInteractOutside.ts
*/
export function useInteractOutside(_ref) {
let {
isDisabled = false,
onInteract,
onInteractStart,
ref,
triggerRef
} = _ref;
const stateRef = useRef({
ignoreEmulatedMouseEvents: false,
isPointerDown: false,
onInteract
});
const state = stateRef.current;
state.onInteract = onInteract;
useEffect(() => {
if (isDisabled) {
return;
}
const onPointerDown = event => {
if (isValidEvent(event, ref, triggerRef) && state.onInteract) {
if (onInteractStart) {
onInteractStart(event);
}
state.isPointerDown = true;
}
};
const onBlur = event => {
if (state.onInteract && event.view === undefined) {
state.onInteract(event);
}
};
// Use pointer events if available. Otherwise, fall back to mouse and touch events.
if (typeof PointerEvent !== 'undefined') {
const onPointerUp = event => {
if (state.isPointerDown && state.onInteract && isValidEvent(event, ref, triggerRef)) {
state.isPointerDown = false;
state.onInteract(event);
}
};
document.addEventListener('pointerdown', onPointerDown, true);
document.addEventListener('pointerup', onPointerUp, true);
window.addEventListener('blur', onBlur, true);
return () => {
document.removeEventListener('pointerdown', onPointerDown, true);
document.removeEventListener('pointerup', onPointerUp, true);
window.removeEventListener('blur', onBlur, true);
};
} else {
const onMouseUp = event => {
if (state.ignoreEmulatedMouseEvents) {
state.ignoreEmulatedMouseEvents = false;
} else if (state.isPointerDown && state.onInteract && isValidEvent(event, ref, triggerRef)) {
state.isPointerDown = false;
state.onInteract(event);
}
};
const onTouchEnd = event => {
state.ignoreEmulatedMouseEvents = true;
if (state.onInteract && state.isPointerDown && isValidEvent(event, ref, triggerRef)) {
state.isPointerDown = false;
state.onInteract(event);
}
};
document.addEventListener('mousedown', onPointerDown, true);
document.addEventListener('mouseup', onMouseUp, true);
document.addEventListener('touchstart', onPointerDown, true);
document.addEventListener('touchend', onTouchEnd, true);
window.addEventListener('blur', onBlur, true);
return () => {
document.removeEventListener('mousedown', onPointerDown, true);
document.removeEventListener('mouseup', onMouseUp, true);
document.removeEventListener('touchstart', onPointerDown, true);
document.removeEventListener('touchend', onTouchEnd, true);
window.removeEventListener('blur', onBlur, true);
};
}
}, [ref, state, isDisabled]);
}
function isValidEvent(event, ref, trigger) {
// @ts-ignore
if (event.button > 0) {
return false;
}
// if the event target is no longer in the document
if (event.target) {
// @ts-ignore
const ownerDocument = event.target.ownerDocument;
if (!ownerDocument || !ownerDocument.documentElement.contains(event.target)) {
return false;
}
}
// We disregard the trigger click because we already have an event that
// toggles the state of the overlay on the trigger.
if (trigger.current && trigger.current.contains(event.target)) {
return false;
}
return ref.current && !ref.current.contains(event.target);
}