vueless
Version:
Vue Styleless UI Component Library, powered by Tailwind CSS.
101 lines (80 loc) • 2.86 kB
text/typescript
import { unref } from "vue";
import type { MaybeRef } from "vue";
import type {
ClickOutsideOptions,
ClickCallback,
RemoveEvents,
ClickOutsideTargetElement,
DirectiveBindingCallback,
DirectiveBindingOptions,
} from "./types.ts";
function isElement(element: EventTarget | null) {
return element instanceof HTMLElement || element instanceof SVGElement;
}
function clickOutside(
target: MaybeRef<HTMLElement | null>,
handler: ClickCallback,
options: ClickOutsideOptions,
): RemoveEvents {
const { capture = true, ignore = [] } = options;
const ignoreList = ignore.map((item) => unref(item));
const el = unref(target);
function onClick(event: MouseEvent) {
if (!isElement(event.target)) return;
const targetElements = event.composedPath().filter((element) => isElement(element));
if (
!el ||
el === event.target ||
targetElements.some((pathEl) => ignoreList.includes(pathEl)) ||
targetElements.includes(el) ||
ignoreList.includes(event.target)
) {
return;
}
handler(event);
}
window.addEventListener("click", onClick, { passive: true, capture });
window.addEventListener("pointerdown", onClick, { passive: true });
function removeEvents() {
window.removeEventListener("click", onClick, capture);
window.removeEventListener("pointerdown", onClick);
}
return removeEvents;
}
function onMounted(el: ClickOutsideTargetElement, binding: DirectiveBindingCallback): void;
function onMounted(el: ClickOutsideTargetElement, binding: DirectiveBindingOptions): void;
function onMounted(
el: ClickOutsideTargetElement,
binding: DirectiveBindingOptions | DirectiveBindingCallback,
): void {
const capture = !binding.modifiers.bubble;
if (typeof binding.value === "function") {
el._clickOutsideRemove = clickOutside(el, binding.value, { capture });
} else {
const [handler, options] = binding.value;
el._clickOutsideRemove = clickOutside(el, handler, Object.assign({ capture }, options));
}
}
function onUpdated(el: ClickOutsideTargetElement, binding: DirectiveBindingCallback): void;
function onUpdated(el: ClickOutsideTargetElement, binding: DirectiveBindingOptions): void;
function onUpdated(
el: ClickOutsideTargetElement,
binding: DirectiveBindingOptions | DirectiveBindingCallback,
): void {
el._clickOutsideRemove();
const capture = !binding.modifiers.bubble;
if (typeof binding.value === "function") {
el._clickOutsideRemove = clickOutside(el, binding.value, { capture });
} else {
const [handler, options] = binding.value;
el._clickOutsideRemove = clickOutside(el, handler, Object.assign({ capture }, options));
}
}
function onUnmounted(el: ClickOutsideTargetElement) {
el._clickOutsideRemove();
}
export default {
mounted: onMounted,
updated: onUpdated,
unmounted: onUnmounted,
};