Home Reference Source

cables_dev/cables_ui/src/ui/elements/tabpanel/tabpanel.js

import { Events, Logger } from "cables-shared-client";
import userSettings from "../../components/usersettings.js";
import { getHandleBarHtml } from "../../utils/handlebars.js";
import { notify, notifyError } from "../notification.js";


/**
 * a tab panel, that can contain tabs
 *
 * @export
 * @class TabPanel
 * @extends {Events}
 */
export default class TabPanel extends Events
{
    constructor(eleId)
    {
        super();
        this._log = new Logger("TabPanel " + eleId);

        this.id = CABLES.uuid();
        this._eleId = eleId;
        this._tabs = [];
        this._eleContentContainer = null;
        this._eleTabPanel = null;
        this.showTabListButton = false;
        this._dynCmds = [];

        if (!this._eleTabPanel)
        {
            this._eleTabPanel = document.createElement("div");
            this._eleTabPanel.classList.add("tabpanel");
            this._eleTabPanel.innerHTML = "";

            const el = ele.byId(this._eleId);
            if (!el)
            {
                console.error("could not find ele " + this._eleId);
                return;
            }
            el.appendChild(this._eleTabPanel);

            this._eleContentContainer = document.createElement("div");
            this._eleContentContainer.classList.add("contentcontainer");
            this._eleContentContainer.innerHTML = "";
            el.appendChild(this._eleContentContainer);
        }

        this.on("resize", () =>
        {
            for (let i = 0; i < this._tabs.length; i++) this._tabs[i].emitEvent("resize");
        });
    }


    getUniqueTitle(title)
    {
        const existingTab = this.getTabByTitle(title);
        let count = 0;
        while (existingTab)
        {
            count++;
            if (!this.getTabByTitle(title + " (" + count + ")")) break;
        }

        if (count > 0)
            title = title + " (" + count + ")";

        return title;
    }

    updateHtml()
    {
        let html = "";
        html += getHandleBarHtml("tabpanel_bar", { "id": this.id, "tabs": this._tabs });
        this._eleTabPanel.innerHTML = html;

        const editortabList = document.getElementById("editortabList" + this.id);
        if (!editortabList)
        {
            console.warn("no editortabList?!?");
            return;
        }
        if (!this.showTabListButton)
        {
            editortabList.style.display = "none";
            editortabList.parentElement.style["padding-left"] = "0";
        }
        else
        {
            editortabList.parentElement.style["padding-left"] = "34px";

            editortabList.style.display = "block";
            editortabList.addEventListener(
                "mousedown",
                (e) =>
                {
                    const items = [];
                    for (let i = 0; i < this._tabs.length; i++)
                    {
                        const tab = this._tabs[i];
                        items.push({
                            "title": tab.options.name,
                            "func": () => { this.activateTab(tab.id); }
                        });
                    }
                    CABLES.contextMenu.show(
                        {
                            "items": items
                        }, e.target);
                },
            );
        }


        for (let i = 0; i < this._dynCmds.length; i++) gui.cmdPallet.removeDynamic(this._dynCmds[i]);

        for (let i = 0; i < this._tabs.length; i++)
        {
            if (window.gui && this._eleId == "maintabs")
            {
                const t = this._tabs[i];

                const cmd = gui.cmdPallet.addDynamic("tab", "Tab " + t.title, () =>
                {
                    gui.maintabPanel.show(true);

                    this.activateTab(t.id, true);
                }, t.icon || "edit");
                this._dynCmds.push(cmd);
            }

            // ----------------


            document.getElementById("editortab" + this._tabs[i].id).addEventListener(
                "mousedown",
                function (e)
                {
                    if (e.target.dataset.id) this.activateTab(e.target.dataset.id, true);
                }.bind(this),
            );

            if (this._tabs[i].options.closable)
            {
                document.getElementById("editortab" + this._tabs[i].id).addEventListener(
                    "mousedown",
                    function (e)
                    {
                        if (e.button == 1) if (e.target.dataset.id) this.closeTab(e.target.dataset.id);
                    }.bind(this),
                );
            }

            if (document.getElementById("closetab" + this._tabs[i].id))
            {
                document.getElementById("closetab" + this._tabs[i].id).addEventListener(
                    "mousedown",
                    function (e)
                    {
                        this.closeTab(e.target.dataset.id);
                    }.bind(this),
                );
            }
        }

        this.scrollToActiveTab();
    }

    activateTabByName(name)
    {
        name = name || "";
        let found = false;
        let tab = null;
        for (let i = 0; i < this._tabs.length; i++)
        {
            if (this._tabs[i].title.toLowerCase() === name.toLowerCase() ||
                (this._tabs[i].options.name || "").toLowerCase() === name.toLowerCase())
            {
                tab = this._tabs[i];
                this.activateTab(tab.id);
                found = true;
            }
            else this._tabs[i].deactivate();
        }

        if (!found) console.log("[activateTabByName] could not find tab", name);

        this.updateHtml();
        return tab;
    }

    scrollToActiveTab()
    {
        const tab = this.getActiveTab();
        const w = this._eleTabPanel.clientWidth;
        if (!tab) return;
        let left = document.getElementById("editortab" + tab.id).offsetLeft;
        left += document.getElementById("editortab" + tab.id).clientWidth;
        left += 25;

        const tabContainer = document.querySelector("#maintabs .tabs");
        if (tabContainer && left > w) tabContainer.scrollLeft = left;
    }

    activateTab(id)
    {
        let found = false;
        for (let i = 0; i < this._tabs.length; i++)
        {
            if (this._tabs[i].id === id)
            {
                found = true;
                this.emitEvent("onTabActivated", this._tabs[i]);
                this._tabs[i].activate();
            }
        }

        if (found)
            for (let i = 0; i < this._tabs.length; i++)
                if (this._tabs[i].id != id)
                    this._tabs[i].deactivate();

        this.updateHtml();

        if (!found)
        {
            // console.log("could not find tab", id);
        }

        if (CABLES.editorSession && CABLES.editorSession.loaded() && CABLES.UI.loaded) this.saveCurrentTabUsersettings();
    }

    loadCurrentTabUsersettings()
    {
        let found = false;
        for (let i = 0; i < this._tabs.length; i++)
        {
            if (userSettings.get("tabsLastTitle_" + this._eleId) == this._tabs[i].title)
            {
                this.activateTab(this._tabs[i].id);
                found = true;
                break;
            }
        }
    }


    saveCurrentTabUsersettings()
    {
        const activeTab = this.getActiveTab();

        if (!activeTab) return;
        userSettings.set("tabsLastTitle_" + this._eleId, activeTab.title);
    }

    getTabByDataId(dataId)
    {
        for (let i = 0; i < this._tabs.length; i++) if (this._tabs[i].dataId == dataId) return this._tabs[i];
    }

    getTabByTitle(title)
    {
        for (let i = 0; i < this._tabs.length; i++) if (this._tabs[i].title == title) return this._tabs[i];
    }

    getTabById(id)
    {
        for (let i = 0; i < this._tabs.length; i++) if (this._tabs[i].id == id) return this._tabs[i];
    }

    closeAllTabs()
    {
        while (this._tabs.length) this.closeTab(this._tabs[0].id);
    }

    closeTab(id)
    {
        let tab = null;
        let idx = 0;
        for (let i = 0; i < this._tabs.length; i++)
        {
            if (this._tabs[i].id == id)
            {
                tab = this._tabs[i];
                // tab.emitEvent("close");
                this._tabs.splice(i, 1);
                idx = i;
                break;
            }
        }
        if (!tab) return;

        this.emitEvent("onTabRemoved", tab);
        tab.remove();

        if (idx > this._tabs.length - 1) idx = this._tabs.length - 1;
        if (this._tabs[idx]) this.activateTab(this._tabs[idx].id);

        this.updateHtml();
    }

    setChanged(id, changed)
    {
        if (this.getTabById(id)) this.getTabById(id).options.wasChanged = changed;
        this.updateHtml();
    }

    setTabNum(num)
    {
        const tab = this._tabs[Math.min(this._tabs.length, num)];
        this.activateTab(tab.id);
    }

    getNumTabs()
    {
        return this._tabs.length;
    }

    cycleActiveTab()
    {
        if (this._tabs.length <= 1) return;

        for (let i = 1; i < this._tabs.length; i++)
            if (this._tabs[i - 1].active)
                return this.activateTab(this._tabs[i].id);

        return this.activateTab(this._tabs[0].id);
    }

    getActiveTab()
    {
        for (let i = 0; i < this._tabs.length; i++) if (this._tabs[i].active) return this._tabs[i];
    }

    updateSize()
    {
        for (let i = 0; i < this._tabs.length; i++) this._tabs[i].updateSize();
    }

    getSaveButton()
    {
        const t = this.getActiveTab();
        if (!t) return;

        const b = t.getSaveButton();
        if (b) return b;
    }

    addTab(tab, activate)
    {
        if (tab.options.singleton)
        {
            const t = this.getTabByTitle(tab.title);
            if (t)
            {
                this.activateTab(t.id);
                this.emitEvent("onTabAdded", t, true);

                if (activate) this.activateTab(t.id);
                return t;
            }
        }

        tab.initHtml(this._eleContentContainer);
        this._tabs.push(tab);

        if (activate) this.activateTab(tab.id);

        // var tabEl=document.getElementById("editortab"+tab.id)

        this.updateHtml();
        this.emitEvent("onTabAdded", tab, false);

        // setTimeout(() => { this.updateSize(); console.log("update size of tab"); }, 200);
        return tab;
    }

    addIframeTab(title, url, options, userInteraction)
    {
        const iframeTab = this.addTab(new CABLES.UI.Tab(title, options));
        const id = CABLES.uuid();

        const html = "<div class=\"loading\" id=\"loading" + id + "\" style=\"position:absolute;left:45%;top:34%\"></div><iframe id=\"iframe" + id + "\" allow=\"clipboard-write\" style=\"border:none;width:100%;height:100%\" src=\"" + url + "\" onload=\"document.getElementById('loading" + id + "').style.display='none';\"></iframe";
        iframeTab.contentEle.innerHTML = html;
        iframeTab.contentEle.style.padding = "0px";
        if (options.gotoUrl)
        {
            iframeTab.toolbarEle.innerHTML = "<a class=\"button-small\" href=\"" + options.gotoUrl + "\" target=\"_blank\">Open in new tab</a>";
        }
        else
        {
            iframeTab.toolbarEle.innerHTML = "<a class=\"button-small\" href=\"" + url + "\" target=\"_blank\">Open in new tab</a>";
        }

        const frame = document.getElementById("iframe" + id);

        const talkerAPI = new CABLESUILOADER.TalkerAPI(frame.contentWindow);

        talkerAPI.addEventListener("manualScreenshot", (opts, next) =>
        {
            CABLES.platform.setManualScreenshot(opts.manualScreenshot);

            if (opts.manualScreenshot)
            {
                gui.patchView.store.saveScreenshot(true, () =>
                {
                    talkerAPI.send("screenshotSaved");
                });
            }
        });

        talkerAPI.addEventListener("notify", (opts, next) =>
        {
            notify(opts.msg, opts.text, opts.options);
        });

        talkerAPI.addEventListener("notifyError", (opts, next) =>
        {
            notifyError(opts.msg, opts.text, opts.options);
        });

        talkerAPI.addEventListener("updatePatchName", (opts, next) =>
        {
            gui.setProjectName(opts.name);
            CABLESUILOADER.talkerAPI.send("updatePatchName", opts, (err, r) => {});
        });

        talkerAPI.addEventListener("updatePatchSummary", (opts, next) =>
        {
            gui.project().summary = opts;
            gui.patchParamPanel.show(true);
        });

        talkerAPI.addEventListener("opsDeleted", (opts, next) =>
        {
            const opdocs = gui.opDocs.getAll();
            const deletedOps = opts.ops || [];
            for (let i = 0; i < deletedOps.length; i++)
            {
                const deletedOp = deletedOps[i];
                const opDocToDelete = opdocs.findIndex((opDoc) => { return opDoc.id === deletedOp.id; });
                if (opDocToDelete) opdocs.splice(opDocToDelete, 1);
                gui.opSelect().reload();
            }
            let plural = deletedOps.length > 1 ? "s" : "";
            if (deletedOps.length > 0) notify("deleted " + deletedOps.length + " op" + plural);
            this.closeTab(iframeTab.id);
        });

        this.activateTab(iframeTab.id);
        gui.maintabPanel.show(userInteraction);
        return iframeTab;
    }
}