UNPKG

@navikt/ds-react

Version:

React components from the Norwegian Labour and Welfare Administration.

77 lines 3.8 kB
"use strict"; 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