import { div, empty, InlineStyleSheet, ClassList } from "../dom";
import { reversed } from "./array";
import { isString } from "./types";
import { enumerate } from "./iterator";
import menus_css, * as menus from "../../styles/menus.css";
import icons_css from "../../styles/icons.css";
import base_css from "../../styles/base.css";
//import {DOMComponentView} from "../dom_view"
export class ContextMenu {
    items;
    static __name__ = "ContextMenu";
    el = div();
    shadow_el;
    _open = false;
    get is_open() {
        return this._open;
    }
    get can_open() {
        return this.items.length != 0;
    }
    target;
    orientation;
    reversed;
    prevent_hide;
    extra_styles;
    entry_handler;
    class_list;
    constructor(items, options) {
        this.items = items;
        this.target = options.target;
        this.orientation = options.orientation ?? "vertical";
        this.reversed = options.reversed ?? false;
        this.prevent_hide = options.prevent_hide;
        this.extra_styles = options.extra_styles ?? [];
        this.entry_handler = options.entry_handler;
        this.shadow_el = this.el.attachShadow({ mode: "open" });
        this.class_list = new ClassList(this.el.classList);
    }
    _item_click = (entry, i) => {
        this.entry_handler?.(entry, i);
        entry.handler?.();
        this.hide();
    };
    _on_mousedown = (event) => {
        if (event.composedPath().includes(this.el)) {
            return;
        }
        if (this.prevent_hide?.(event) ?? false) {
            return;
        }
        this.hide();
    };
    _on_keydown = (event) => {
        if (event.key == "Escape") {
            this.hide();
        }
    };
    _on_blur = () => {
        this.hide();
    };
    remove() {
        this._unlisten();
        this.el.remove();
    }
    _listen() {
        document.addEventListener("mousedown", this._on_mousedown);
        document.addEventListener("keydown", this._on_keydown);
        window.addEventListener("blur", this._on_blur);
    }
    _unlisten() {
        document.removeEventListener("mousedown", this._on_mousedown);
        document.removeEventListener("keydown", this._on_keydown);
        window.removeEventListener("blur", this._on_blur);
    }
    _position(at) {
        const pos = (() => {
            if ("left_of" in at) {
                const { left, top } = at.left_of.getBoundingClientRect();
                return { right: left, top };
            }
            if ("right_of" in at) {
                const { top, right } = at.right_of.getBoundingClientRect();
                return { left: right, top };
            }
            if ("below" in at) {
                const { left, bottom } = at.below.getBoundingClientRect();
                return { left, top: bottom };
            }
            if ("above" in at) {
                const { left, top } = at.above.getBoundingClientRect();
                return { left, bottom: top };
            }
            return at;
        })();
        const parent_el = this.el.offsetParent ?? document.body;
        const origin = (() => {
            const rect = parent_el.getBoundingClientRect();
            const style = getComputedStyle(parent_el);
            return {
                left: rect.left - parseFloat(style.marginLeft),
                right: rect.right + parseFloat(style.marginRight),
                top: rect.top - parseFloat(style.marginTop),
                bottom: rect.bottom + parseFloat(style.marginBottom),
            };
        })();
        const { style } = this.el;
        style.left = pos.left != null ? `${pos.left - origin.left}px` : "auto";
        style.top = pos.top != null ? `${pos.top - origin.top}px` : "auto";
        style.right = pos.right != null ? `${origin.right - pos.right}px` : "auto";
        style.bottom = pos.bottom != null ? `${origin.bottom - pos.bottom}px` : "auto";
    }
    stylesheets() {
        return [base_css, /*...super.stylesheets(), */ menus_css, icons_css, ...this.extra_styles];
    }
    empty() {
        empty(this.shadow_el);
        this.class_list.clear();
    }
    render() {
        this.empty();
        for (const style of this.stylesheets()) {
            const stylesheet = isString(style) ? new InlineStyleSheet(style) : style;
            stylesheet.install(this.shadow_el);
        }
        this.class_list.add(menus[this.orientation]);
        const items = this.reversed ? reversed(this.items) : this.items;
        for (const [item, i] of enumerate(items)) {
            let el;
            if (item == null) {
                el = div({ class: menus.divider });
            }
            else if (item.if != null && !item.if()) {
                continue;
            }
            else if (item.custom != null) {
                el = item.custom;
            }
            else {
                const icon = item.icon != null ? div({ class: [menus.menu_icon, item.icon] }) : null;
                const classes = [item.active?.() ?? false ? menus.active : null, item.class];
                el = div({ class: classes, title: item.tooltip, tabIndex: 0 }, icon, item.label, item.content);
                el.addEventListener("click", () => {
                    this._item_click(item, i);
                });
                el.addEventListener("keydown", (event) => {
                    if (event.key == "Enter") {
                        this._item_click(item, i);
                    }
                });
            }
            this.shadow_el.appendChild(el);
        }
    }
    show(at) {
        if (this.items.length == 0) {
            return;
        }
        this.render();
        if (this.shadow_el.children.length == 0) {
            return;
        }
        (this.target.shadowRoot ?? this.target).appendChild(this.el);
        this._position(at ?? { left: 0, top: 0 });
        this._listen();
        this._open = true;
    }
    hide() {
        if (this._open) {
            this._open = false;
            this._unlisten();
            this.el.remove();
        }
    }
    toggle(at) {
        this._open ? this.hide() : this.show(at);
    }
}
//# sourceMappingURL=menus.js.map