import { children, createSignal, mergeProps, splitProps } from "solid-js";
import { isServer } from "solid-js/web";
import contains from "dom-helpers/contains";
import { createControlledProp } from "solid-bootstrap-core";
import Overlay from "./Overlay";
function normalizeDelay(delay) {
    return delay && typeof delay === "object"
        ? delay
        : {
            show: delay,
            hide: delay,
        };
}
// Simple implementation of mouseEnter and mouseLeave.
// React's built version is broken: https://github.com/facebook/react/issues/4251
// for cases when the trigger is disabled and mouseOut/Over can cause flicker
// moving from one child element to another.
function handleMouseOverOut(
// eslint-disable-next-line @typescript-eslint/no-shadow
handler, args, relatedNative) {
    const [e] = args;
    const target = e.currentTarget;
    const related = e.relatedTarget;
    if ((!related || related !== target) && !contains(target, related)) {
        handler(...args);
    }
}
const defaultProps = {
    defaultShow: false,
    trigger: ["hover", "focus"],
    popperConfig: {},
};
function OverlayTrigger(p) {
    const [local, props] = splitProps(mergeProps(defaultProps, {
        flip: p.placement && p.placement.indexOf("auto") !== -1,
    }, p), [
        "trigger",
        "overlay",
        "children",
        "popperConfig",
        "show",
        "defaultShow",
        "onToggle",
        "delay",
        "placement",
        "flip",
    ]);
    let [triggerNodeRef, setTriggerNodeRef] = createSignal();
    const mergedRef = (r) => {
        setTriggerNodeRef(r);
        local.children.ref?.(r);
    };
    let timeout;
    let hoverStateRef = "";
    const [show, setShow] = createControlledProp(() => local.show, () => local.defaultShow, local.onToggle);
    const delay = normalizeDelay(local.delay);
    const handleShow = () => {
        window.clearTimeout(timeout);
        hoverStateRef = "show";
        if (!delay.show) {
            setShow(true);
            return;
        }
        timeout = window.setTimeout(() => {
            if (hoverStateRef === "show")
                setShow(true);
        }, delay.show);
    };
    const handleHide = () => {
        window.clearTimeout(timeout);
        hoverStateRef = "hide";
        if (!delay.hide) {
            setShow(false);
            return;
        }
        timeout = window.setTimeout(() => {
            if (hoverStateRef === "hide")
                setShow(false);
        }, delay.hide);
    };
    const handleFocus = (...args) => {
        handleShow();
    };
    const handleBlur = (...args) => {
        handleHide();
    };
    const handleClick = (...args) => {
        setShow(!show());
    };
    const handleMouseOver = (...args) => {
        handleMouseOverOut(handleShow, args, "fromElement");
    };
    const handleMouseOut = (...args) => {
        handleMouseOverOut(handleHide, args, "toElement");
    };
    const addListeners = (el) => {
        const triggers = local.trigger == null ? [] : [].concat(local.trigger);
        if (triggers.indexOf("click") !== -1) {
            el.addEventListener("click", handleClick);
        }
        if (triggers.indexOf("focus") !== -1) {
            el.addEventListener("focus", handleFocus);
            el.addEventListener("blur", handleBlur);
        }
        if (triggers.indexOf("hover") !== -1) {
            el.addEventListener("mouseover", handleMouseOver);
            el.addEventListener("mouseout", handleMouseOut);
        }
        // no need to cleanup as element will be removed anyway
    };
    let resolvedChildren = children(() => local.children);
    let Target = () => {
        let el = resolvedChildren();
        while (typeof el === "function")
            el = el();
        mergedRef(el);
        if (!isServer) {
            addListeners(el);
        }
        return el;
    };
    return (<>
      <Target />
      <Overlay {...props} show={show()} onHide={handleHide} flip={local.flip} placement={local.placement} popperConfig={local.popperConfig} target={triggerNodeRef}>
        {local.overlay}
      </Overlay>
    </>);
}
export default OverlayTrigger;
