Skip to content
Snippets Groups Projects
neighbors.js 7.33 KiB
Newer Older
  • Learn to ignore specific revisions
  • import * as Helpers from "../helpers";
    
    import { createHTMLElement } from "../helpers";
    
    
    export { NodeNeighborOverlay };
    
    
    /**
     * Displays an overlay showing the neighbors of a node divided by the link type
     * that connects them.
     */
    
    class NodeNeighborOverlay {
    
        constructor(graph, parentNode, infoOverlay, type = "link") {
    
            this.graph = graph;
            this.parentNode = parentNode;
            this.infoOverlay = infoOverlay;
    
            this.contentTab = null;
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
            this.tabContentPages = {}; // Page content - links to other nodes
            this.tabNavHandles = {}; // Top-level handles of the content pages
    
            this.tabPageVisibility = {}; // Visibility of each content page
    
        /**
         * Creates the visible elements of the overlay.
         * Must be called after the graph has been initialized.
         */
    
        create() {
            const bottomContainerDiv = Helpers.createDiv(
    
                "neighbor-container",
    
                this.parentNode
            );
    
            bottomContainerDiv.classList.add("fancy-scrollbar");
    
    Harm Kube's avatar
    Harm Kube committed
            // Create the collapsible of the entire menu
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
            const coll = Helpers.createDiv("button", bottomContainerDiv);
    
            coll.className = "neighbor-collapsible-title";
    
            coll.innerText = "Verwandte Inhalte";
    
            coll.style.textAlign = "center";
    
    Harm Kube's avatar
    Harm Kube committed
    
            // Div that displays the information about all the chapters
    
            const contentTab = Helpers.createDiv(
    
                "neighbor-content-tabs",
    
                bottomContainerDiv
            );
    
            //contentTab.classList.add("neighbor-collapsible-wrapper");
            this.contentTab = contentTab;
    
            coll.addEventListener("click", () => {
                if (contentTab.style.height === "0px") {
                    contentTab.style.height = "auto";
    
                } else {
    
                    contentTab.style.height = "0px";
    
            const colors =
                this.type === "link"
                    ? this.graph.edgeColors
                    : this.graph.nodeColors;
            for (const [cls, color] of Object.entries(colors)) {
    
                this.createCollapsibleTab(contentTab, cls, color);
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
        /**
    
         * Creates a new collapsible tab and content area for a specific node.
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
         * @param {HTMLElement} parent Parent of the new tab.
         * @param {string} name Name of the node type class
         * @param {string} color Color of the node type class
         */
        createCollapsibleTab(parent, name, color) {
            // Creating the collapsible tabs for the different chapters
            const collTab = Helpers.createDiv("button", parent);
            collTab.className = "neighbor-collapsible-section";
            collTab.innerText = name;
            collTab.type = name;
            this.tabNavHandles[name] = collTab;
    
            this.tabPageVisibility[name] = false;
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
    
            const collTabMarker = Helpers.createDiv(
                "neighbor-collapsible-marker-div",
                collTab
            );
            collTabMarker.style.borderColor = color;
            collTabMarker.style.backgroundColor = color;
    
            const openMarkerTabs = Helpers.createDiv(
                "neighbor-tab-open-status-marker",
                collTab
            );
            openMarkerTabs.innerText = "+";
            collTab.marker = openMarkerTabs;
    
            // Content of the different tabs
            const collTabContent = Helpers.createDiv(
                "neighbor-content-linksection",
                parent
            );
    
            collTabContent.classList.add("neighbor-collapsible-wrapper");
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
            collTabContent.type = name;
    
            const list = createHTMLElement("ul", collTabContent);
            list.style.margin = 0;
    
    
            collTabContent.list = list;
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
            collTabContent.marker = openMarkerTabs;
    
            this.tabContentPages[name] = collTabContent;
    
            collTab.addEventListener("click", () => {
                this.toggleSectionVisibility(name);
    
        /**
         * Toggles the visibility of a specific section
         * @param {string} name Id of the section
         */
        toggleSectionVisibility(name) {
            if (this.tabPageVisibility[name]) {
                this.collapseSection(name);
            } else {
                this.expandSection(name);
            }
        }
    
        /**
         * Collapses the content area of a specific section
         * @param {string} name Id of the section
         */
        collapseSection(name) {
            this.tabPageVisibility[name] = false;
            const section = this.tabContentPages[name];
            section.style.height = "0px";
            section.marker.innerText = "+";
        }
    
        /**
         * Expands the content area of a specific section
         * @param {string} name Id of the section
         */
        expandSection(name) {
            this.tabPageVisibility[name] = true;
            const section = this.tabContentPages[name];
            section.style.height = `${section.scrollHeight}px`;
            section.marker.innerText = "-";
        }
    
    
    Harm Kube's avatar
    Harm Kube committed
         * Clears the images from all tab content pages and makes the object
         * invisible.
    
        clearTabContentPages() {
    
    Harm Kube's avatar
    Harm Kube committed
            for (const page of Object.values(this.tabContentPages)) {
    
                page.list.replaceChildren();
    
    Harm Kube's avatar
    Harm Kube committed
            }
    
         * Creates a new list element for the given target node.
    
         * @param target
         * @returns {HTMLDivElement}
         */
    
        createReference(target) {
    
            const linkDiv = document.createElement("li");
            var linkText = document.createTextNode(target.name);
            linkDiv.className = "neighbor-content-link";
    
    Harm Kube's avatar
    Harm Kube committed
            linkDiv.appendChild(linkText);
    
            linkDiv.addEventListener("click", () => {
    
                this.graph.focusOnNode(target);
                this.infoOverlay.updateInfoOverlay(target);
            });
            return linkDiv;
        }
    
    
        /**
         * Updates all tabs to have content matching the given node.
         * @param node
         */
    
        updateTabs(node) {
    
    Harm Kube's avatar
    Harm Kube committed
            this.clearTabContentPages();
    
            for (const link of node.links) {
    
                const target = link.source === node ? link.target : link.source;
    
                const reference = this.createReference(target);
    
                    this.tabContentPages[link.type].list.appendChild(reference);
    
                    this.tabContentPages[target.type].list.appendChild(reference);
    
    Harm Kube's avatar
    Harm Kube committed
            }
    
            this.updatePageVisibility();
    
         * Updates the content page visibility on node change.
         * Hides all empty content pages.
    
        updatePageVisibility() {
    
            for (const page of Object.values(this.tabContentPages)) {
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
                if (!page.list.hasChildNodes()) {
    
                    this.tabNavHandles[page.type].style.display = "none";
    
                    page.style.display = "none";
    
                } else {
                    this.tabNavHandles[page.type].style.display = "flex";
    
                    page.style.display = "flex";
                    if (this.tabPageVisibility[page.type]) {
                        page.style.height = `${page.list.scrollHeight}px`;
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
        /**
    
         * Toggle the visibility for a category
         * @param {string} type The name of the category that should be toggled
    
    Matthias Konitzny's avatar
    Matthias Konitzny committed
         */
        toggleCategory(type) {
    
            const page = this.tabContentPages[type];
    
            const handle = this.tabNavHandles[type];
    
            if (handle.style.display === "flex") {
                page.style.display = "none";
                handle.style.display = "none";
            } else if (page.list.hasChildNodes()) {
                page.style.display = "flex";
                handle.style.display = "flex";