UNPKG

@navikt/ds-react

Version:

React components from the Norwegian Labour and Welfare Administration.

42 lines 2.11 kB
import { useEffect, useRef } from "react"; import { useCallbackRef } from "../../../util/hooks/index.js"; import { CUSTOM_EVENTS, dispatchCustomEvent, } from "./dispatchCustomEvent.js"; /** * Tracks focus outside a React subtree. Returns props for the subtree root. */ export function useFocusOutside(callback, ownerDocument = globalThis === null || globalThis === void 0 ? void 0 : globalThis.document) { const handleFocusOutside = useCallbackRef(callback); const isFocusInsideReactTreeRef = useRef(false); useEffect(() => { const handleFocus = (event) => { if (event.target && !isFocusInsideReactTreeRef.current) { const eventDetail = { originalEvent: event }; /** * The `DismisableLayer`-API is based on the ability to stop events from propagating and in the end calling `onDismiss` * if `useFocusOutside`-callback runs `event.preventDefault()`. * * Because of the `focusin`-event not being cancelable, * we need to use a custom event to ensure that the event is cancelable. * https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event * > The focusin event is not cancelable. */ dispatchCustomEvent(CUSTOM_EVENTS.FOCUS_OUTSIDE, handleFocusOutside, eventDetail); } }; ownerDocument.addEventListener("focusin", handleFocus); return () => ownerDocument.removeEventListener("focusin", handleFocus); }, [ownerDocument, handleFocusOutside]); /** * By directly setting isFocusInsideReactTreeRef on focus-events at the root of the "dismissable" element, * we can eliminate the need for DOM traversal to verify if the focused element is within the react tree. */ return { onFocusCapture: () => { isFocusInsideReactTreeRef.current = true; }, onBlurCapture: () => { isFocusInsideReactTreeRef.current = false; }, }; } //# sourceMappingURL=useFocusOutside.js.map