@navikt/ds-react
Version:
React components from the Norwegian Labour and Welfare Administration.
77 lines • 3.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePointerUpOutside = usePointerUpOutside;
const react_1 = require("react");
const hooks_1 = require("../../../utils/hooks");
const dispatchCustomEvent_1 = require("./dispatchCustomEvent");
/**
* Listens for `pointerup` outside a react subtree.
* Returns props to pass to the node we want to check for outside events.
* By checking `isPointerInsideReactTreeRef` we can determine if the event happened outside the subtree of the node, saving some element-comparisons.
*/
function usePointerUpOutside(callback, ownerDocument = globalThis === null || globalThis === void 0 ? void 0 : globalThis.document, enabled = true) {
// Keep callback ref stable
const handlePointerUpOutside = (0, hooks_1.useEventCallback)(callback);
// Tracks if the pointer interaction started inside the React subtree.
const isPointerInsideReactTreeRef = (0, react_1.useRef)(false);
(0, react_1.useEffect)(() => {
if (!enabled) {
return;
}
const handlePointerUp = (event) => {
/**
* The `DismisableLayer`-API is based on the ability to stop events from propagating and in the end calling `onDismiss`
* if `usePointerUpOutside`-callback does not run `event.preventDefault()`.
*
* Although `pointerup` is already a cancelable event, we still dispatch a custom event
* to keep parity with focus outside handling and ensure ordering.
*/
if (event.target && !isPointerInsideReactTreeRef.current) {
(0, dispatchCustomEvent_1.dispatchCustomEvent)(dispatchCustomEvent_1.CUSTOM_EVENTS.POINTER_UP_OUTSIDE, handlePointerUpOutside, { originalEvent: event });
}
/* Reset for next interaction. */
isPointerInsideReactTreeRef.current = false;
};
/* Mostly relevant if user moved touch after touch-start */
const handlePointerCancel = () => {
/* Reset state if interaction is cancelled */
isPointerInsideReactTreeRef.current = false;
};
/**
* If this hook executes in a component that mounts via a `pointerup` event, the event
* would bubble up to the document and trigger a `pointerUpOutside` event. We avoid
* this by delaying the event listener registration on the document.
* This is not React specific, but rather how the DOM works, ie:
* ```
* button.addEventListener('pointerup', () => {
* console.log('I will log');
* document.addEventListener('pointerup', () => {
* console.log('I will also log');
* })
* });
*/
const timerId = window.setTimeout(() => {
ownerDocument.addEventListener("pointerup", handlePointerUp);
ownerDocument.addEventListener("pointercancel", handlePointerCancel);
}, 0);
return () => {
window.clearTimeout(timerId);
ownerDocument.removeEventListener("pointerup", handlePointerUp);
ownerDocument.removeEventListener("pointercancel", handlePointerCancel);
};
}, [ownerDocument, handlePointerUpOutside, enabled]);
/**
* Ensures we check React component tree (not just DOM tree).
* This makes sure that if you start or end a pointer interaction inside the
* React tree (e.g. Modal), we don't trigger the outside event on pointer up.
*/
return {
onPointerDownCapture: () => {
isPointerInsideReactTreeRef.current = true;
},
onPointerUpCapture: () => {
isPointerInsideReactTreeRef.current = true;
},
};
}
//# sourceMappingURL=usePointerUpOutside.js.map