File size: 2,987 Bytes
bc20498
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Modified from Grail UI v0.9.6 (2023-06-10)
// Source: https://github.com/grail-ui/grail-ui
// https://github.com/grail-ui/grail-ui/tree/master/packages/grail-ui/src/clickOutside/clickOutside.ts
import { readable } from 'svelte/store';
import { addEventListener } from '../../helpers/event.js';
import { get } from 'svelte/store';
import { isFunction } from '../../helpers/is.js';
/**
 * Creates a readable store that tracks the latest PointerEvent that occurred on the document.
 *
 * @returns A function to unsubscribe from the event listener and stop tracking pointer events.
 */
const documentClickStore = readable(undefined, (set) => {
    /**
     * Event handler for pointerdown events on the document.
     * Updates the store's value with the latest PointerEvent and then resets it to undefined.
     */
    function clicked(event) {
        set(event);
        // New subscriptions will not trigger immediately
        set(undefined);
    }
    // Adds a pointerdown event listener to the document, calling the clicked function when triggered.
    const unsubscribe = addEventListener(document, 'pointerup', clicked, {
        passive: false,
        capture: true,
    });
    // Returns a function to unsubscribe from the event listener and stop tracking pointer events.
    return unsubscribe;
});
export const useClickOutside = (node, config = {}) => {
    let options = { enabled: true, ...config };
    // Returns true if the click outside handler is enabled
    function isEnabled() {
        return typeof options.enabled === 'boolean' ? options.enabled : get(options.enabled);
    }
    // Handle document clicks
    const unsubscribe = documentClickStore.subscribe((e) => {
        // If the click outside handler is disabled, or if the event is null or the node itself, return early
        if (!isEnabled() || !e || e.target === node) {
            return;
        }
        const composedPath = e.composedPath();
        // If the target is in the node, return early
        if (composedPath.includes(node))
            return;
        // If an ignore function is passed, check if it returns true
        if (options.ignore) {
            if (isFunction(options.ignore)) {
                if (options.ignore(e))
                    return;
            }
            // If an ignore array is passed, check if any elements in the array match the target
            else if (Array.isArray(options.ignore)) {
                if (options.ignore.length > 0 &&
                    options.ignore.some((ignoreEl) => {
                        return ignoreEl && (e.target === ignoreEl || composedPath.includes(ignoreEl));
                    }))
                    return;
            }
        }
        // If none of the above conditions are met, call the handler
        options.handler?.(e);
    });
    return {
        update(params) {
            options = { ...options, ...params };
        },
        destroy() {
            unsubscribe();
        },
    };
};