import { dedupe, delayHide, delayShow } from './middlewares/basic.js';
import { safeBridge, safeTriangle } from './middlewares/safe-area.js';
/**
 * Call the `whenHoverChange` callback when the element is hovered.
 *
 * After the mouse leaves the element, there is a 300ms delay by default.
 *
 * Note: The callback may be called multiple times when the mouse is hovering or hovering out.
 *
 * See also https://floating-ui.com/docs/useHover
 *
 * @example
 * ```ts
 * private _setReference: RefOrCallback;
 *
 * connectedCallback() {
 *   let hoverTip: HTMLElement | null = null;
 *   const { setReference, setFloating } = whenHover(isHover => {
 *     if (!isHover) {
 *       hoverTips?.remove();
 *       return;
 *     }
 *     hoverTip = document.createElement('div');
 *     document.body.append(hoverTip);
 *     setFloating(hoverTip);
 *   }, { hoverDelay: 500 });
 *   this._setReference = setReference;
 * }
 *
 * render() {
 *   return html`
 *     <div ref=${this._setReference}></div>
 *   `;
 * }
 * ```
 */
export const whenHover = (whenHoverChange, { enterDelay = 0, leaveDelay = 300, alwayRunWhenNoFloating = true, safeTriangle: triangleOptions = false, safeBridge: bridgeOptions = true, } = {}) => {
    /**
     * The event listener will be removed when the signal is aborted.
     */
    const abortController = new AbortController();
    let referenceElement;
    let floatingElement;
    const middlewares = [
        dedupe(alwayRunWhenNoFloating),
        triangleOptions &&
            safeTriangle(typeof triangleOptions === 'boolean' ? undefined : triangleOptions),
        bridgeOptions &&
            safeBridge(typeof bridgeOptions === 'boolean' ? undefined : bridgeOptions),
        delayShow(enterDelay),
        delayHide(leaveDelay),
    ].filter(v => typeof v !== 'boolean');
    let id = 0;
    let resId = 0;
    const onHoverChange = (async (e) => {
        const curId = id++;
        for (const middleware of middlewares) {
            const go = await middleware({
                event: e,
                floatingElement,
                referenceElement,
            });
            if (!go)
                return;
        }
        // ignore expired event
        if (curId < resId)
            return;
        resId = curId;
        if (e.type === 'mouseover') {
            whenHoverChange(true, e);
            return;
        }
        if (e.type === 'mouseleave') {
            whenHoverChange(false, e);
            return;
        }
        console.error('Unknown event type in whenHover', e);
    });
    const addHoverListener = (element) => {
        if (!element)
            return;
        // see https://stackoverflow.com/questions/14795099/pure-javascript-to-check-if-something-has-hover-without-setting-on-mouseover-ou
        const alreadyHover = element.matches(':hover');
        if (alreadyHover && !abortController.signal.aborted) {
            // When the element is already hovered, we need to trigger the callback manually
            onHoverChange(new MouseEvent('mouseover'));
        }
        element.addEventListener('mouseover', onHoverChange, {
            signal: abortController.signal,
        });
        element.addEventListener('mouseleave', onHoverChange, {
            signal: abortController.signal,
        });
    };
    const removeHoverListener = (element) => {
        if (!element)
            return;
        element.removeEventListener('mouseover', onHoverChange);
        element.removeEventListener('mouseleave', onHoverChange);
    };
    const setReference = (element) => {
        // Clean previous listeners
        removeHoverListener(referenceElement);
        addHoverListener(element);
        referenceElement = element;
    };
    const setFloating = (element) => {
        // Clean previous listeners
        removeHoverListener(floatingElement);
        addHoverListener(element);
        floatingElement = element;
    };
    return {
        setReference,
        setFloating,
        dispose: () => {
            abortController.abort();
        },
    };
};
//# sourceMappingURL=when-hover.js.map