/** * Copyright (c) 2023-present Plane Software, Inc. and contributors * SPDX-License-Identifier: AGPL-3.0-only * See the LICENSE file for details. */ import type React from "react"; import { useEffect, useCallback } from "react"; const usePeekOverviewOutsideClickDetector = ( ref: React.RefObject, callback: () => void, issueId: string, excludePreventionElementIds?: string[] ) => { const handleClick = useCallback( (event: MouseEvent) => { if (!(event.target instanceof HTMLElement)) return; if (ref.current && !ref.current.contains(event.target)) { // check for the closest element with attribute name data-prevent-outside-click const preventOutsideClickElement = event.target.closest("[data-prevent-outside-click]"); // if the closest element with attribute name data-prevent-outside-click is found if (preventOutsideClickElement) { // Check if this element's ID is in the exclusion list const elementId = preventOutsideClickElement.id; const shouldExcludePrevention = excludePreventionElementIds && elementId && excludePreventionElementIds.includes(elementId); if (!shouldExcludePrevention && !preventOutsideClickElement.contains(ref.current)) { // Only prevent the callback if the ref is NOT inside the same prevent-outside-click container. // This allows normal outside click detection for elements within the same container return; } } // check if the click target is the current issue element or its children let targetElement: HTMLElement | null = event.target; while (targetElement) { if (targetElement.id === `issue-${issueId}`) { // if the click target is the current issue element, return return; } targetElement = targetElement.parentElement; } const delayOutsideClickElement = event.target.closest("[data-delay-outside-click]"); if (delayOutsideClickElement) { // if the click target is the closest element with attribute name data-delay-outside-click, delay the callback setTimeout(() => { callback(); }, 0); return; } // else, call the callback immediately callback(); } }, [ref, callback, issueId, excludePreventionElementIds] ); useEffect(() => { document.addEventListener("mousedown", handleClick); return () => { document.removeEventListener("mousedown", handleClick); }; }, [handleClick]); }; export default usePeekOverviewOutsideClickDetector;