{"version":3,"file":"utils.cjs","sources":["../../src/DismissableLayer/utils.ts"],"sourcesContent":["import type { MaybeRefOrGetter, Ref } from 'vue'\nimport { isClient } from '@vueuse/shared'\nimport { nextTick, ref, toValue, watchEffect } from 'vue'\nimport { handleAndDispatchCustomEvent } from '@/shared'\n\nexport type PointerDownOutsideEvent = CustomEvent<{\n originalEvent: PointerEvent\n}>\nexport type FocusOutsideEvent = CustomEvent<{ originalEvent: FocusEvent }>\n\nexport const DISMISSABLE_LAYER_NAME = 'DismissableLayer'\nexport const CONTEXT_UPDATE = 'dismissableLayer.update'\nexport const POINTER_DOWN_OUTSIDE = 'dismissableLayer.pointerDownOutside'\nexport const FOCUS_OUTSIDE = 'dismissableLayer.focusOutside'\n\nfunction isLayerExist(layerElement: HTMLElement, targetElement: HTMLElement) {\n const targetLayer = targetElement.closest(\n '[data-dismissable-layer]',\n )\n\n const mainLayer = layerElement.dataset.dismissableLayer === ''\n ? layerElement\n : layerElement.querySelector(\n '[data-dismissable-layer]',\n ) as HTMLElement\n\n const nodeList = Array.from(\n layerElement.ownerDocument.querySelectorAll('[data-dismissable-layer]'),\n )\n\n if (targetLayer && (mainLayer === targetLayer || nodeList.indexOf(mainLayer) < nodeList.indexOf(targetLayer))\n ) {\n return true\n }\n else {\n return false\n }\n}\n\n/**\n * Listens for `pointerdown` outside a DOM subtree. We use `pointerdown` rather than `pointerup`\n * to mimic layer dismissing behaviour present in OS.\n * Returns props to pass to the node we want to check for outside events.\n */\nexport function usePointerDownOutside(\n onPointerDownOutside?: (event: PointerDownOutsideEvent) => void,\n element?: Ref,\n enabled: MaybeRefOrGetter = true,\n) {\n const ownerDocument: Document\n = element?.value?.ownerDocument ?? globalThis?.document\n\n const isPointerInsideDOMTree = ref(false)\n const handleClickRef = ref(() => {})\n\n watchEffect((cleanupFn) => {\n if (!isClient || !toValue(enabled))\n return\n const handlePointerDown = async (event: PointerEvent) => {\n const target = event.target as HTMLElement | undefined\n\n if (!element?.value || !target)\n return\n\n if (isLayerExist(element.value, target)) {\n isPointerInsideDOMTree.value = false\n return\n }\n\n if (event.target && !isPointerInsideDOMTree.value) {\n const eventDetail = { originalEvent: event }\n\n function handleAndDispatchPointerDownOutsideEvent() {\n handleAndDispatchCustomEvent(\n POINTER_DOWN_OUTSIDE,\n onPointerDownOutside,\n eventDetail,\n )\n }\n\n /**\n * On touch devices, we need to wait for a click event because browsers implement\n * a ~350ms delay between the time the user stops touching the display and when the\n * browser executes events. We need to ensure we don't reactivate pointer-events within\n * this timeframe otherwise the browser may execute events that should have been prevented.\n *\n * Additionally, this also lets us deal automatically with cancellations when a click event\n * isn't raised because the page was considered scrolled/drag-scrolled, long-pressed, etc.\n *\n * This is why we also continuously remove the previous listener, because we cannot be\n * certain that it was raised, and therefore cleaned-up.\n */\n if (event.pointerType === 'touch') {\n ownerDocument.removeEventListener('click', handleClickRef.value)\n handleClickRef.value = handleAndDispatchPointerDownOutsideEvent\n ownerDocument.addEventListener('click', handleClickRef.value, {\n once: true,\n })\n }\n else {\n handleAndDispatchPointerDownOutsideEvent()\n }\n }\n else {\n // We need to remove the event listener in case the outside click has been canceled.\n // See: https://github.com/radix-ui/primitives/issues/2171\n ownerDocument.removeEventListener('click', handleClickRef.value)\n }\n isPointerInsideDOMTree.value = false\n }\n /**\n * if this hook executes in a component that mounts via a `pointerdown` event, the event\n * would bubble up to the document and trigger a `pointerDownOutside` event. We avoid\n * this by delaying the event listener registration on the document.\n * This is how the DOM works, ie:\n * ```\n * button.addEventListener('pointerdown', () => {\n * console.log('I will log');\n * document.addEventListener('pointerdown', () => {\n * console.log('I will also log');\n * })\n * });\n */\n const timerId = window.setTimeout(() => {\n ownerDocument.addEventListener('pointerdown', handlePointerDown)\n }, 0)\n\n cleanupFn(() => {\n window.clearTimeout(timerId)\n ownerDocument.removeEventListener('pointerdown', handlePointerDown)\n ownerDocument.removeEventListener('click', handleClickRef.value)\n })\n })\n\n return {\n onPointerDownCapture: () => {\n if (!toValue(enabled))\n return\n isPointerInsideDOMTree.value = true\n },\n }\n}\n\n/**\n * Listens for when focus happens outside a DOM subtree.\n * Returns props to pass to the root (node) of the subtree we want to check.\n */\nexport function useFocusOutside(\n onFocusOutside?: (event: FocusOutsideEvent) => void,\n element?: Ref,\n) {\n const ownerDocument: Document\n = element?.value?.ownerDocument ?? globalThis?.document\n\n const isFocusInsideDOMTree = ref(false)\n watchEffect((cleanupFn) => {\n if (!isClient)\n return\n const handleFocus = async (event: FocusEvent) => {\n if (!element?.value)\n return\n\n await nextTick()\n await nextTick()\n const target = event.target as HTMLElement | undefined\n if (!element.value || !target || isLayerExist(element.value, target))\n return\n\n if (event.target && !isFocusInsideDOMTree.value) {\n const eventDetail = { originalEvent: event }\n handleAndDispatchCustomEvent(\n FOCUS_OUTSIDE,\n onFocusOutside,\n eventDetail,\n )\n }\n }\n\n ownerDocument.addEventListener('focusin', handleFocus)\n\n cleanupFn(() => ownerDocument.removeEventListener('focusin', handleFocus))\n })\n\n return {\n onFocusCapture: () => (isFocusInsideDOMTree.value = true),\n onBlurCapture: () => (isFocusInsideDOMTree.value = false),\n }\n}\n\nexport function dispatchUpdate() {\n const event = new CustomEvent(CONTEXT_UPDATE)\n document.dispatchEvent(event)\n}\n"],"names":["ref","watchEffect","isClient","toValue","handleAndDispatchCustomEvent","nextTick"],"mappings":";;;;;;AAYO,MAAM,oBAAuB,GAAA,qCAAA;AAC7B,MAAM,aAAgB,GAAA,+BAAA;AAE7B,SAAS,YAAA,CAAa,cAA2B,aAA4B,EAAA;AAC3E,EAAA,MAAM,cAAc,aAAc,CAAA,OAAA;AAAA,IAChC;AAAA,GACF;AAEA,EAAA,MAAM,YAAY,YAAa,CAAA,OAAA,CAAQ,gBAAqB,KAAA,EAAA,GACxD,eACA,YAAa,CAAA,aAAA;AAAA,IACb;AAAA,GACF;AAEF,EAAA,MAAM,WAAW,KAAM,CAAA,IAAA;AAAA,IACrB,YAAA,CAAa,aAAc,CAAA,gBAAA,CAAiB,0BAA0B;AAAA,GACxE;AAEA,EAAI,IAAA,WAAA,KAAgB,SAAc,KAAA,WAAA,IAAe,QAAS,CAAA,OAAA,CAAQ,SAAS,CAAI,GAAA,QAAA,CAAS,OAAQ,CAAA,WAAW,CACzG,CAAA,EAAA;AACA,IAAO,OAAA,IAAA;AAAA,GAEJ,MAAA;AACH,IAAO,OAAA,KAAA;AAAA;AAEX;AAOO,SAAS,qBACd,CAAA,oBAAA,EACA,OACA,EAAA,OAAA,GAAqC,IACrC,EAAA;AACA,EAAA,MAAM,aACF,GAAA,OAAA,EAAS,KAAO,EAAA,aAAA,IAAiB,UAAY,EAAA,QAAA;AAEjD,EAAM,MAAA,sBAAA,GAAyBA,QAAI,KAAK,CAAA;AACxC,EAAM,MAAA,cAAA,GAAiBA,QAAI,MAAM;AAAA,GAAE,CAAA;AAEnC,EAAAC,eAAA,CAAY,CAAC,SAAc,KAAA;AACzB,IAAA,IAAI,CAACC,eAAA,IAAY,CAACC,WAAA,CAAQ,OAAO,CAAA;AAC/B,MAAA;AACF,IAAM,MAAA,iBAAA,GAAoB,OAAO,KAAwB,KAAA;AACvD,MAAA,MAAM,SAAS,KAAM,CAAA,MAAA;AAErB,MAAI,IAAA,CAAC,OAAS,EAAA,KAAA,IAAS,CAAC,MAAA;AACtB,QAAA;AAEF,MAAA,IAAI,YAAa,CAAA,OAAA,CAAQ,KAAO,EAAA,MAAM,CAAG,EAAA;AACvC,QAAA,sBAAA,CAAuB,KAAQ,GAAA,KAAA;AAC/B,QAAA;AAAA;AAGF,MAAA,IAAI,KAAM,CAAA,MAAA,IAAU,CAAC,sBAAA,CAAuB,KAAO,EAAA;AAGjD,QAAA,IAAS,2CAAT,WAAoD;AAClD,UAAAC,gEAAA;AAAA,YACE,oBAAA;AAAA,YACA,oBAAA;AAAA,YACA;AAAA,WACF;AAAA,SACF;AARA,QAAM,MAAA,WAAA,GAAc,EAAE,aAAA,EAAe,KAAM,EAAA;AAsB3C,QAAI,IAAA,KAAA,CAAM,gBAAgB,OAAS,EAAA;AACjC,UAAc,aAAA,CAAA,mBAAA,CAAoB,OAAS,EAAA,cAAA,CAAe,KAAK,CAAA;AAC/D,UAAA,cAAA,CAAe,KAAQ,GAAA,wCAAA;AACvB,UAAc,aAAA,CAAA,gBAAA,CAAiB,OAAS,EAAA,cAAA,CAAe,KAAO,EAAA;AAAA,YAC5D,IAAM,EAAA;AAAA,WACP,CAAA;AAAA,SAEE,MAAA;AACH,UAAyC,wCAAA,EAAA;AAAA;AAC3C,OAEG,MAAA;AAGH,QAAc,aAAA,CAAA,mBAAA,CAAoB,OAAS,EAAA,cAAA,CAAe,KAAK,CAAA;AAAA;AAEjE,MAAA,sBAAA,CAAuB,KAAQ,GAAA,KAAA;AAAA,KACjC;AAcA,IAAM,MAAA,OAAA,GAAU,MAAO,CAAA,UAAA,CAAW,MAAM;AACtC,MAAc,aAAA,CAAA,gBAAA,CAAiB,eAAe,iBAAiB,CAAA;AAAA,OAC9D,CAAC,CAAA;AAEJ,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,MAAA,CAAO,aAAa,OAAO,CAAA;AAC3B,MAAc,aAAA,CAAA,mBAAA,CAAoB,eAAe,iBAAiB,CAAA;AAClE,MAAc,aAAA,CAAA,mBAAA,CAAoB,OAAS,EAAA,cAAA,CAAe,KAAK,CAAA;AAAA,KAChE,CAAA;AAAA,GACF,CAAA;AAED,EAAO,OAAA;AAAA,IACL,sBAAsB,MAAM;AAC1B,MAAI,IAAA,CAACD,YAAQ,OAAO,CAAA;AAClB,QAAA;AACF,MAAA,sBAAA,CAAuB,KAAQ,GAAA,IAAA;AAAA;AACjC,GACF;AACF;AAMgB,SAAA,eAAA,CACd,gBACA,OACA,EAAA;AACA,EAAA,MAAM,aACF,GAAA,OAAA,EAAS,KAAO,EAAA,aAAA,IAAiB,UAAY,EAAA,QAAA;AAEjD,EAAM,MAAA,oBAAA,GAAuBH,QAAI,KAAK,CAAA;AACtC,EAAAC,eAAA,CAAY,CAAC,SAAc,KAAA;AACzB,IAAA,IAAI,CAACC,eAAA;AACH,MAAA;AACF,IAAM,MAAA,WAAA,GAAc,OAAO,KAAsB,KAAA;AAC/C,MAAA,IAAI,CAAC,OAAS,EAAA,KAAA;AACZ,QAAA;AAEF,MAAA,MAAMG,YAAS,EAAA;AACf,MAAA,MAAMA,YAAS,EAAA;AACf,MAAA,MAAM,SAAS,KAAM,CAAA,MAAA;AACrB,MAAI,IAAA,CAAC,QAAQ,KAAS,IAAA,CAAC,UAAU,YAAa,CAAA,OAAA,CAAQ,OAAO,MAAM,CAAA;AACjE,QAAA;AAEF,MAAA,IAAI,KAAM,CAAA,MAAA,IAAU,CAAC,oBAAA,CAAqB,KAAO,EAAA;AAC/C,QAAM,MAAA,WAAA,GAAc,EAAE,aAAA,EAAe,KAAM,EAAA;AAC3C,QAAAD,gEAAA;AAAA,UACE,aAAA;AAAA,UACA,cAAA;AAAA,UACA;AAAA,SACF;AAAA;AACF,KACF;AAEA,IAAc,aAAA,CAAA,gBAAA,CAAiB,WAAW,WAAW,CAAA;AAErD,IAAA,SAAA,CAAU,MAAM,aAAA,CAAc,mBAAoB,CAAA,SAAA,EAAW,WAAW,CAAC,CAAA;AAAA,GAC1E,CAAA;AAED,EAAO,OAAA;AAAA,IACL,cAAA,EAAgB,MAAO,oBAAA,CAAqB,KAAQ,GAAA,IAAA;AAAA,IACpD,aAAA,EAAe,MAAO,oBAAA,CAAqB,KAAQ,GAAA;AAAA,GACrD;AACF;;;;;"}