UNPKG

@ariakit/react-core

Version:

Ariakit React core

134 lines (131 loc) 4.49 kB
"use client"; import { usePreviousMouseDownRef } from "./HLTQOHKZ.js"; import { isElementMarked } from "./3NDVDEB4.js"; import { isSafariFocusAncestor } from "./OE2EFRVA.js"; import { useStoreState } from "./RTNCFSKZ.js"; import { useEvent, useSafeLayoutEffect } from "./5GGHRIN3.js"; import { __spreadProps, __spreadValues } from "./3YLGPPWQ.js"; // src/dialog/utils/use-hide-on-interact-outside.ts import { contains, getDocument, isVisible } from "@ariakit/core/utils/dom"; import { addGlobalEventListener } from "@ariakit/core/utils/events"; import { useEffect, useRef } from "react"; function isInDocument(target) { if (target.tagName === "HTML") return true; return contains(getDocument(target).body, target); } function isDisclosure(disclosure, target) { if (!disclosure) return false; if (contains(disclosure, target)) return true; const activeId = target.getAttribute("aria-activedescendant"); if (activeId) { const activeElement = getDocument(disclosure).getElementById(activeId); if (activeElement) { return contains(disclosure, activeElement); } } return false; } function isMouseEventOnDialog(event, dialog) { if (!("clientY" in event)) return false; const rect = dialog.getBoundingClientRect(); if (rect.width === 0 || rect.height === 0) return false; return rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width; } function useEventOutside({ store, type, listener, capture, domReady }) { const callListener = useEvent(listener); const open = useStoreState(store, "open"); const focusedRef = useRef(false); useSafeLayoutEffect(() => { if (!open) return; if (!domReady) return; const { contentElement } = store.getState(); if (!contentElement) return; const onFocus = () => { focusedRef.current = true; }; contentElement.addEventListener("focusin", onFocus, true); return () => contentElement.removeEventListener("focusin", onFocus, true); }, [store, open, domReady]); useEffect(() => { if (!open) return; const onEvent = (event) => { const { contentElement, disclosureElement } = store.getState(); const target = event.target; if (!contentElement) return; if (!target) return; if (!isInDocument(target)) return; if (contains(contentElement, target)) return; if (isDisclosure(disclosureElement, target)) return; if (target.hasAttribute("data-focus-trap")) return; if (isMouseEventOnDialog(event, contentElement)) return; const focused = focusedRef.current; if (focused && !isElementMarked(target, contentElement.id)) return; if (isSafariFocusAncestor(target)) return; callListener(event); }; return addGlobalEventListener(type, onEvent, capture); }, [open, capture]); } function shouldHideOnInteractOutside(hideOnInteractOutside, event) { if (typeof hideOnInteractOutside === "function") { return hideOnInteractOutside(event); } return !!hideOnInteractOutside; } function useHideOnInteractOutside(store, hideOnInteractOutside, domReady) { const open = useStoreState(store, "open"); const previousMouseDownRef = usePreviousMouseDownRef(open); const props = { store, domReady, capture: true }; useEventOutside(__spreadProps(__spreadValues({}, props), { type: "click", listener: (event) => { const { contentElement } = store.getState(); const previousMouseDown = previousMouseDownRef.current; if (!previousMouseDown) return; if (!isVisible(previousMouseDown)) return; if (!isElementMarked(previousMouseDown, contentElement == null ? void 0 : contentElement.id)) return; if (!shouldHideOnInteractOutside(hideOnInteractOutside, event)) return; store.hide(); } })); useEventOutside(__spreadProps(__spreadValues({}, props), { type: "focusin", listener: (event) => { const { contentElement } = store.getState(); if (!contentElement) return; if (event.target === getDocument(contentElement)) return; if (!shouldHideOnInteractOutside(hideOnInteractOutside, event)) return; store.hide(); } })); useEventOutside(__spreadProps(__spreadValues({}, props), { type: "contextmenu", listener: (event) => { if (!shouldHideOnInteractOutside(hideOnInteractOutside, event)) return; store.hide(); } })); } export { useHideOnInteractOutside };