import { computePosition } from "./position";
import { computeVisibleClientRect, getScrollableParents } from "./utils";
export class PopupController {
    options;
    root;
    scrollableParents;
    visibleClientRect;
    context;
    constructor(options) {
        this.options = options;
        this.root = options.root || document.documentElement;
        this.scrollableParents = getScrollableParents(this.anchor, this.root);
        this.visibleClientRect = computeVisibleClientRect(this.scrollableParents);
    }
    get anchor() {
        return this.options.anchor;
    }
    registerListeners(element) {
        const parents = this.scrollableParents;
        const updateHandler = () => {
            this.update();
        };
        // add a window resize listener
        window.addEventListener('resize', updateHandler);
        //TODO do we add scroll listeners to the window?
        // add scroll listeners to all scrollable parents
        for (const parent of parents) {
            parent.addEventListener('scroll', updateHandler);
        }
        let closeOnClick;
        if (this.options.closeOnClick) {
            closeOnClick = (ev) => {
                if (!element.contains(ev.target)) {
                    this.tryClose();
                }
            };
            // register in the next event loop cycle since the current one
            // is may be triggered by a click event
            window.setTimeout(function () {
                closeOnClick && document.addEventListener('click', closeOnClick);
            }, 0);
        }
        let closeOnEsc;
        if (this.options.closeOnEsc) {
            closeOnEsc = (ev) => {
                if (ev.key === 'Escape') {
                    this.tryClose();
                }
            };
            window.setTimeout(function () {
                closeOnEsc && document.addEventListener('keydown', closeOnEsc);
            }, 0);
        }
        const blockPageScroll = this.options.blockPageScroll;
        if (blockPageScroll) {
            document.body.style.overflow = "hidden";
            document.body.style.height = "100%";
        }
        return () => {
            window.removeEventListener('resize', updateHandler);
            for (const parent of parents) {
                parent.removeEventListener('scroll', updateHandler);
            }
            closeOnClick && document.removeEventListener('click', closeOnClick);
            closeOnEsc && document.removeEventListener('keydown', closeOnEsc);
            if (blockPageScroll) {
                document.body.style.overflow = "";
                document.body.style.height = "";
            }
        };
    }
    open(element, constraints) {
        if (this.context) {
            throw new Error("The popup controller is already bound to an element");
        }
        this.tryOpen(element, constraints);
    }
    tryOpen(element, constraints) {
        if (this.context) {
            return; // do nothing if the popup is already open
        }
        this.context = {
            element,
            constraints,
            cleanup: this.registerListeners(element)
        };
        element.style.display = "";
        element.style.visibility = "hidden";
        // update the popup position
        this.update();
        this.options.onOpen && this.options.onOpen(this);
    }
    close() {
        if (!this.context) {
            throw new Error("The popup controller is not bound to an element");
        }
        this.tryClose();
    }
    tryClose() {
        if (!this.context) {
            return; // do nothing if the popup is not open
        }
        this.options.onClose && this.options.onClose(this);
        this.context.cleanup();
        //TODO
        this.context.element.style.display = "none";
        this.context = undefined;
    }
    get isOpen() {
        return !!this.context;
    }
    update() {
        if (!this.context)
            return; // do nothing if the popup is not open
        // update the position of the popup
        const element = this.context.element;
        const constraints = this.context.constraints;
        const elemRect = element.getBoundingClientRect();
        const anchorRect = this.anchor.getBoundingClientRect();
        const clientRect = this.visibleClientRect;
        if (!clientRect) {
            // the popup is not visible
            return;
        }
        const position = computePosition(constraints, elemRect, anchorRect, clientRect);
        this.context.position = position || undefined;
        if (position) {
            if (position.constrainHeight) {
                element.style.height = position.rect.height + 'px';
            }
            if (position.constrainWidth) {
                element.style.width = position.rect.width + 'px';
            }
            element.style.left = position.rect.left + 'px';
            element.style.top = position.rect.top + 'px';
            element.style.visibility = "visible";
        }
    }
    createPopupElement() {
        const popup = document.createElement('div');
        popup.style.margin = "0";
        popup.style.padding = "0";
        popup.style.border = "none";
        popup.style.background = "transparent";
        popup.className = "composable-Popup";
        return popup;
    }
}
//# sourceMappingURL=PopupController.js.map