import { handleAndDispatchCustomEvent } from "../shared/handleAndDispatchCustomEvent.js";
import { nextTick, ref, toValue, watchEffect } from "vue";
import { isClient } from "@vueuse/shared";

//#region src/DismissableLayer/utils.ts
const POINTER_DOWN_OUTSIDE = "dismissableLayer.pointerDownOutside";
const FOCUS_OUTSIDE = "dismissableLayer.focusOutside";
function isLayerExist(layerElement, targetElement) {
	const targetLayer = targetElement.closest("[data-dismissable-layer]");
	const mainLayer = layerElement.dataset.dismissableLayer === "" ? layerElement : layerElement.querySelector("[data-dismissable-layer]");
	const nodeList = Array.from(layerElement.ownerDocument.querySelectorAll("[data-dismissable-layer]"));
	if (targetLayer && (mainLayer === targetLayer || nodeList.indexOf(mainLayer) < nodeList.indexOf(targetLayer))) return true;
	else return false;
}
/**
* Listens for `pointerdown` outside a DOM subtree. We use `pointerdown` rather than `pointerup`
* to mimic layer dismissing behaviour present in OS.
* Returns props to pass to the node we want to check for outside events.
*/
function usePointerDownOutside(onPointerDownOutside, element, enabled = true) {
	const ownerDocument = element?.value?.ownerDocument ?? globalThis?.document;
	const isPointerInsideDOMTree = ref(false);
	const handleClickRef = ref(() => {});
	watchEffect((cleanupFn) => {
		if (!isClient || !toValue(enabled)) return;
		const handlePointerDown = async (event) => {
			const target = event.target;
			if (!element?.value || !target) return;
			if (isLayerExist(element.value, target)) {
				isPointerInsideDOMTree.value = false;
				return;
			}
			if (event.target && !isPointerInsideDOMTree.value) {
				const eventDetail = { originalEvent: event };
				function handleAndDispatchPointerDownOutsideEvent() {
					handleAndDispatchCustomEvent(POINTER_DOWN_OUTSIDE, onPointerDownOutside, eventDetail);
				}
				/**
				* On touch devices, we need to wait for a click event because browsers implement
				* a ~350ms delay between the time the user stops touching the display and when the
				* browser executes events. We need to ensure we don't reactivate pointer-events within
				* this timeframe otherwise the browser may execute events that should have been prevented.
				*
				* Additionally, this also lets us deal automatically with cancellations when a click event
				* isn't raised because the page was considered scrolled/drag-scrolled, long-pressed, etc.
				*
				* This is why we also continuously remove the previous listener, because we cannot be
				* certain that it was raised, and therefore cleaned-up.
				*/
				if (event.pointerType === "touch") {
					ownerDocument.removeEventListener("click", handleClickRef.value);
					handleClickRef.value = handleAndDispatchPointerDownOutsideEvent;
					ownerDocument.addEventListener("click", handleClickRef.value, { once: true });
				} else handleAndDispatchPointerDownOutsideEvent();
			} else ownerDocument.removeEventListener("click", handleClickRef.value);
			isPointerInsideDOMTree.value = false;
		};
		/**
		* if this hook executes in a component that mounts via a `pointerdown` event, the event
		* would bubble up to the document and trigger a `pointerDownOutside` event. We avoid
		* this by delaying the event listener registration on the document.
		* This is how the DOM works, ie:
		* ```
		* button.addEventListener('pointerdown', () => {
		*   console.log('I will log');
		*   document.addEventListener('pointerdown', () => {
		*     console.log('I will also log');
		*   })
		* });
		*/
		const timerId = window.setTimeout(() => {
			ownerDocument.addEventListener("pointerdown", handlePointerDown);
		}, 0);
		cleanupFn(() => {
			window.clearTimeout(timerId);
			ownerDocument.removeEventListener("pointerdown", handlePointerDown);
			ownerDocument.removeEventListener("click", handleClickRef.value);
		});
	});
	return { onPointerDownCapture: () => {
		if (!toValue(enabled)) return;
		isPointerInsideDOMTree.value = true;
	} };
}
/**
* Listens for when focus happens outside a DOM subtree.
* Returns props to pass to the root (node) of the subtree we want to check.
*/
function useFocusOutside(onFocusOutside, element, enabled = true) {
	const ownerDocument = element?.value?.ownerDocument ?? globalThis?.document;
	const isFocusInsideDOMTree = ref(false);
	watchEffect((cleanupFn) => {
		if (!isClient || !toValue(enabled)) return;
		const handleFocus = async (event) => {
			if (!element?.value) return;
			await nextTick();
			await nextTick();
			const target = event.target;
			if (!element.value || !target || isLayerExist(element.value, target)) return;
			if (event.target && !isFocusInsideDOMTree.value) {
				const eventDetail = { originalEvent: event };
				handleAndDispatchCustomEvent(FOCUS_OUTSIDE, onFocusOutside, eventDetail);
			}
		};
		ownerDocument.addEventListener("focusin", handleFocus);
		cleanupFn(() => ownerDocument.removeEventListener("focusin", handleFocus));
	});
	return {
		onFocusCapture: () => {
			if (!toValue(enabled)) return;
			isFocusInsideDOMTree.value = true;
		},
		onBlurCapture: () => {
			if (!toValue(enabled)) return;
			isFocusInsideDOMTree.value = false;
		}
	};
}

//#endregion
export { useFocusOutside, usePointerDownOutside };
//# sourceMappingURL=utils.js.map