Source: ui/sidebar/Sidebar.js

import { UiElement } from "../UiElement.js";
import "./Sidebar.css";

export { Sidebar };

/**
 * Sidebar with content group navigation
 * 
 * @author rhess <robin.hess@awi.de>
 * @memberof vef.ui.sidebar
 */
class Sidebar extends UiElement {

    classes = ["sidebar", "collapsed"];
    html = `
        <div class="sidebar-menu"></div>
        <div class="sidebar-content">
            <div class="sidebar-header">
                <div class="sidebar-title"></div>
                <i class="fas fa-times"></i>
            </div>
        </div>
    `;

    /**
     * Create sidebar and optionally append it to a target element.
     * {@code target} can be an "#id" with a hash in the beginning
     * or an instance of HTMLElement.
     * 
     * @param {string/HTMLElement} target id or HTMLElement (optional)
     */
    constructor(target, options) {
        super(target, {
            "show": [],
            "close": []
        });

        this.groups_ = {};
        this.activeGroup_ = null;

        this.setHtml(this.html);
        this.setClass(this.classes);
        this.query(".fas.fa-times").addEventListener("click", () => this.close());

        // set options with defaults
        this.setOptions(Object.assign({
            position: "left",
            menu: true,
            closeable: true,
            responsive: true,
            underlineHeader: false
        }, options));
    }

    setOptions(options) {
        // positioning of the sidebar
        if (options.position == "left") {
            this.removeClass(["right", "bottom"]);
            this.setClass("left");
        } else if (options.position == "right") {
            this.removeClass(["left", "bottom"]);
            this.setClass("right");
        } else if (options.position == "bottom") {
            this.removeClass(["left", "right"]);
            this.setClass("bottom");
        }

        // responsive or fixed size
        if (typeof options.menu == "boolean") {
            if (options.menu) {
                this.setClass("menu");
            } else {
                this.removeClass("menu");
            }
        }

        // underline headline
        if (typeof options.underlineHeader == "boolean") {
            if (options.underlineHeader) {
                this.setClass("underline-header");
            } else {
                this.removeClass("underline-header");
            }
        }

        // show close button ?
        if (typeof options.closeable == "boolean") {
            if (options.closeable) {
                this.setClass("closeable");
            } else {
                this.removeClass("closeable");
            }
        }

        // show close button ?
        if (typeof options.responsive == "boolean") {
            if (options.responsive) {
                this.setClass("responsive");
            } else {
                this.removeClass("responsive");
            }
        }
    }

    /** 
     * Adds a content group in the sidebar navigation
     * 
     * @param {string} name identifier of the group
     * @param {string} title headline of the group
     * @param {string} classNames css classes separated by space
     * @param {string/HTMLElement} content content as HTML-String or HTMLElement
     * @param {string} position "begin" or "end" (default is end)
     */
    addGroup(name, title, classNames, content, position = "end") {
        if (!this.groups_[name]) {
            const menuElement = document.createElement("div");
            menuElement.classList.add("menu-element");
            menuElement.setAttribute("tabindex", "0");  

            if (classNames) {
                const classes = classNames.trim().split(" ");
                for (let i in classes) {
                    menuElement.classList.add(classes[i]);
                }
            }

            menuElement.addEventListener("click", e => {
                if (this.activeGroup_ == this.groups_[name]) {
                    this.close();
                } else {
                    this.show(name);
                }
            }, false);

            menuElement.addEventListener("keydown", e => {
                if(e.key != "Enter") return;
                if (this.activeGroup_ == this.groups_[name]) {
                    this.close();
                } else {
                    this.show(name);
                }
            }, false);

            const contentContainer = document.createElement("div");
            contentContainer.classList.add("sidebar-group");
            contentContainer.dataset.title = title;

            if (content && typeof content == "string") {
                contentContainer.insertAdjacentHTML('afterbegin', content);
            } else if (content instanceof HTMLElement) {
                contentContainer.appendChild(content);
            }

            this.query(".sidebar-menu").insertAdjacentElement((position == "begin") ? "afterbegin" : "beforeend", menuElement);
            this.query(".sidebar-content").appendChild(contentContainer);

            this.groups_[name] = {
                menuElement: menuElement,
                content: contentContainer,
                title: title
            };

        }
    }

    /**
     * Update the title of a group wth the given name
     * 
     * @param {string} name 
     * @param {string} title 
     */
    setTitle(name, title) {
        if (this.groups_[name] && (typeof title == "string")) {
            this.groups_[name].title = title;
            this.groups_[name].content.dataset.title = title;
            if (this.activeGroup_ == this.groups_[name]) {
                const titleContainer = this.getElement().querySelector(".sidebar-header .sidebar-title");
                titleContainer.innerHTML = title;
                titleContainer.title = titleContainer.innerText;
            }
        }
    }

    /**
     * Update the content of a group with the given name
     * 
     * @param {string} name
     * @param {string | HTMLElement} content
     * @param {boolean} keepScrollposition
     */
    setContent(name, content, keepScrollposition) {
        const group = this.groups_[name];
        if (group) {
            if (!keepScrollposition) group.content.scrollTo(0, 0);
            if (typeof content == "string") {
                group.content.innerHTML = content;
            } else if (content instanceof HTMLElement) {
                group.content.innerHTML = "";
                group.content.appendChild(content);
            } else {
                group.content.innerHTML = "";
            }
        }
    }

    /** 
     * Remove content group from the sidebar navigation
     * 
     * @param {string} name identifier of the group
     */
    removeGroup(name) {
        if (this.groups_[name]) {
            if (this.groups_[name] == this.activeGroup_) this.close();
            this.groups_[name].menuElement.remove();
            this.groups_[name].content.remove();
            delete this.groups_[name];
        }
    }

    /**
     * Returns all defined groups.
     */
    getGroups() {
        return this.groups_;
    }

    /** 
     * Show the content of a group
     * 
     * @param {string} name identifier of the group
     */
    show(name) {
        if (this.groups_[name]) {
            if (this.activeGroup_) {
                this.activeGroup_.menuElement.classList.remove("active");
                this.activeGroup_.content.classList.remove("show");
            }

            this.removeClass("collapsed");
            const titleContainer = this.getElement().querySelector(".sidebar-header .sidebar-title");
            titleContainer.innerHTML = (this.groups_[name].title) ? this.groups_[name].title : "";
            titleContainer.title = titleContainer.innerText;

            this.groups_[name].menuElement.classList.add("active");
            this.groups_[name].content.classList.add("show");
            this.groups_[name].content.scrollTo(0, 0);

            this.activeGroup_ = this.groups_[name];

            this.fire("show", name);
        } else {
            this.close();
        }
    }

    /** 
     * Close the sidebar
     */
    close() {
        this.setClass("collapsed");
        if (this.activeGroup_) {
            this.activeGroup_.menuElement.classList.remove("active");
            this.activeGroup_.content.classList.remove("show");
            this.activeGroup_ = null;
        }
        this.fire("close");
    }

    /** 
     * get container element of a content group
     * 
     * @param {string} name identifier of the group
     * @returns {HTMLElement} content container
     */
    getGroupContainer(name) {
        if (this.groups_[name]) {
            return this.groups_[name].content;
        }
    }

    isOpen() {
        return !this.getElement().classList.contains("collapsed");
    }

}