@sv-use/core
Version:
A collection of Svelte 5 utilities.
62 lines (61 loc) • 2.28 kB
JavaScript
import { handleEventListener } from '../handle-event-listener/index.svelte.js';
import { toArray } from '../__internal__/utils.svelte.js';
import { defaultWindow } from '../__internal__/configurable.js';
import { onDestroy } from 'svelte';
/**
* Runs a callback when a click occurs outside the element or its ignore list.
* @param element The main element on which not to trigger a click.
* @param callback The callback to run when an outside click is valid.
* @param options Additional options to customize the behavior.
* @see https://svelte-librarian.github.io/sv-use/docs/core/on-click-outside
*/
export function onClickOutside(element, callback, options = {}) {
const { autoCleanup = false, capture = true, ignore = [], window = defaultWindow } = options;
let shouldListen = true;
let isProcessingClick = false;
const cleanups = [];
if (window) {
cleanups.push(handleEventListener(window, 'click', (event) => {
if (!isProcessingClick) {
isProcessingClick = true;
setTimeout(() => {
isProcessingClick = false;
}, 0);
handleClick(event);
}
}, { passive: true, capture }), handleEventListener(window, 'pointerdown', (e) => {
const el = element();
shouldListen = !shouldIgnoreClick(e) && !!(el && !e.composedPath().includes(el));
}, { passive: true }));
}
if (autoCleanup) {
onDestroy(() => {
cleanup();
});
}
function handleClick(event) {
const el = element();
if (!event.target)
return;
if (!el || el === event.target || event.composedPath().includes(el))
return;
if (event.detail === 0) {
shouldListen = !shouldIgnoreClick(event);
}
if (!shouldListen) {
shouldListen = true;
return;
}
callback(event);
}
function shouldIgnoreClick(event) {
return toArray(ignore).some((target) => {
const el = target();
return el && (event.target === el || event.composedPath().includes(el));
});
}
function cleanup() {
cleanups.forEach((fn) => fn());
}
return cleanup;
}