"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "useOnClickOutside", { enumerable: true, get: function() { return useOnClickOutside; } }); const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard"); const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react")); const _useEventCallback = require("./useEventCallback"); const _reactsharedcontexts = require("@fluentui/react-shared-contexts"); const DEFAULT_CONTAINS = (parent, child)=>!!(parent === null || parent === void 0 ? void 0 : parent.contains(child)); const useOnClickOutside = (options)=>{ const { targetDocument } = (0, _reactsharedcontexts.useFluent_unstable)(); const win = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.defaultView; const { refs, callback, element, disabled, disabledFocusOnIframe, contains = DEFAULT_CONTAINS } = options; const timeoutId = _react.useRef(undefined); useIFrameFocus({ element, disabled: disabledFocusOnIframe || disabled, callback, refs, contains }); const isMouseDownInsideRef = _react.useRef(false); const listener = (0, _useEventCallback.useEventCallback)((ev)=>{ if (isMouseDownInsideRef.current) { isMouseDownInsideRef.current = false; return; } const target = ev.composedPath()[0]; const isOutside = refs.every((ref)=>!contains(ref.current || null, target)); if (isOutside && !disabled) { callback(ev); } }); const handleMouseDown = (0, _useEventCallback.useEventCallback)((ev)=>{ // Selecting text from inside to outside will rigger click event. // In this case click event target is outside but mouse down event target is inside. // And this click event should be considered as inside click. isMouseDownInsideRef.current = refs.some((ref)=>contains(ref.current || null, ev.target)); }); _react.useEffect(()=>{ if (disabled) { return; } // Store the current event to avoid triggering handlers immediately // Note this depends on a deprecated but extremely well supported quirk of the web platform // https://github.com/facebook/react/issues/20074 let currentEvent = getWindowEvent(win); const conditionalHandler = (event)=>{ // Skip if this event is the same as the one running when we added the handlers if (event === currentEvent) { currentEvent = undefined; return; } listener(event); }; // use capture phase because React can update DOM before the event bubbles to the document element === null || element === void 0 ? void 0 : element.addEventListener('click', conditionalHandler, true); element === null || element === void 0 ? void 0 : element.addEventListener('touchstart', conditionalHandler, true); element === null || element === void 0 ? void 0 : element.addEventListener('contextmenu', conditionalHandler, true); element === null || element === void 0 ? void 0 : element.addEventListener('mousedown', handleMouseDown, true); // Garbage collect this event after it's no longer useful to avoid memory leaks timeoutId.current = win === null || win === void 0 ? void 0 : win.setTimeout(()=>{ currentEvent = undefined; }, 1); return ()=>{ element === null || element === void 0 ? void 0 : element.removeEventListener('click', conditionalHandler, true); element === null || element === void 0 ? void 0 : element.removeEventListener('touchstart', conditionalHandler, true); element === null || element === void 0 ? void 0 : element.removeEventListener('contextmenu', conditionalHandler, true); element === null || element === void 0 ? void 0 : element.removeEventListener('mousedown', handleMouseDown, true); win === null || win === void 0 ? void 0 : win.clearTimeout(timeoutId.current); currentEvent = undefined; }; }, [ listener, element, disabled, handleMouseDown, win ]); }; const getWindowEvent = (target)=>{ if (target) { var _target_ownerDocument_defaultView, _target_ownerDocument; if (typeof target.window === 'object' && target.window === target) { // eslint-disable-next-line deprecation/deprecation return target.event; } var _target_ownerDocument_defaultView_event; // eslint-disable-next-line deprecation/deprecation return (_target_ownerDocument_defaultView_event = (_target_ownerDocument = target.ownerDocument) === null || _target_ownerDocument === void 0 ? void 0 : (_target_ownerDocument_defaultView = _target_ownerDocument.defaultView) === null || _target_ownerDocument_defaultView === void 0 ? void 0 : _target_ownerDocument_defaultView.event) !== null && _target_ownerDocument_defaultView_event !== void 0 ? _target_ownerDocument_defaultView_event : undefined; } return undefined; }; const FUI_FRAME_EVENT = 'fuiframefocus'; /** * Since click events do not propagate past iframes, we use focus to detect if a * click has happened inside an iframe, since the only ways of focusing inside an * iframe are: * - clicking inside * - tabbing inside * * Polls the value of `document.activeElement`. If it is an iframe, then dispatch * a custom DOM event. When the custom event is received call the provided callback */ const useIFrameFocus = (options)=>{ const { disabled, element: targetDocument, callback, contains = DEFAULT_CONTAINS, pollDuration = 1000, refs } = options; const timeoutRef = _react.useRef(); const listener = (0, _useEventCallback.useEventCallback)((e)=>{ const isOutside = refs.every((ref)=>!contains(ref.current || null, e.target)); if (isOutside && !disabled) { callback(e); } }); // Adds listener to the custom iframe focus event _react.useEffect(()=>{ if (disabled) { return; } targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.addEventListener(FUI_FRAME_EVENT, listener, true); return ()=>{ targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.removeEventListener(FUI_FRAME_EVENT, listener, true); }; }, [ targetDocument, disabled, listener ]); // Starts polling for the active element _react.useEffect(()=>{ var _targetDocument_defaultView; if (disabled) { return; } timeoutRef.current = targetDocument === null || targetDocument === void 0 ? void 0 : (_targetDocument_defaultView = targetDocument.defaultView) === null || _targetDocument_defaultView === void 0 ? void 0 : _targetDocument_defaultView.setInterval(()=>{ const activeElement = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.activeElement; if ((activeElement === null || activeElement === void 0 ? void 0 : activeElement.tagName) === 'IFRAME' || (activeElement === null || activeElement === void 0 ? void 0 : activeElement.tagName) === 'WEBVIEW') { const event = new CustomEvent(FUI_FRAME_EVENT, { bubbles: true }); activeElement.dispatchEvent(event); } }, pollDuration); return ()=>{ var _targetDocument_defaultView; targetDocument === null || targetDocument === void 0 ? void 0 : (_targetDocument_defaultView = targetDocument.defaultView) === null || _targetDocument_defaultView === void 0 ? void 0 : _targetDocument_defaultView.clearTimeout(timeoutRef.current); }; }, [ targetDocument, disabled, pollDuration ]); };