import { onBeforeUnmount, watch, type Ref } from 'vue' type ClickOutsideHandler = (event: MouseEvent) => void const handlers = new Set() function dispatch(event: MouseEvent) { for (const handler of [...handlers]) { handler(event) } } function addHandler(handler: ClickOutsideHandler) { const wasEmpty = handlers.size === 0 handlers.add(handler) if (wasEmpty) { window.addEventListener('mousedown', dispatch) } } function removeHandler(handler: ClickOutsideHandler) { if (!handlers.delete(handler)) { return } if (handlers.size === 0) { window.removeEventListener('mousedown', dispatch) } } /** * One window `mousedown` listener shared by all subscribers. * Subscribe only while `active` is true (e.g. dropdown open). */ export function useSharedClickOutside(handler: ClickOutsideHandler, active: Ref) { watch( active, (isActive) => { if (isActive) { addHandler(handler) } else { removeHandler(handler) } }, { immediate: true }, ) onBeforeUnmount(() => { removeHandler(handler) }) }