UNPKG

cirsim

Version:

Cirsim Circuit Simulator

256 lines (217 loc) 6.92 kB
import {Component} from './Component'; import {Tools} from './DOM/Tools'; import {FileMenu} from './Menus/FileMenu'; import {EditMenu} from './Menus/EditMenu'; import {TabsMenu} from './Menus/TabsMenu'; import {OptionsMenu} from './Menus/OptionsMenu'; import {HelpMenu} from './Menus/HelpMenu'; /** * The program menu bar * @param main Main object * @constructor */ export const Menu = function(main) { // // Create the menu components // const fileMenu = new FileMenu(this, main); const editMenu = new EditMenu(this, main); const tabsMenu = new TabsMenu(this, main); const optionsMenu = new OptionsMenu(this, main); const helpMenu = new HelpMenu(this, main); this.helpMenu = helpMenu; /// The nav and ul elements for the menu this.nav = null; this.ul = null; const initialize = () => { // // <nav class="menubar"><ul></ul></nav> // this.nav = document.createElement('nav'); Tools.addClass(this.nav, 'menubar'); main.div.appendChild(this.nav); const ul = document.createElement('ul'); this.nav.appendChild(ul); this.ul = ul; // // Add the menu component's HTML // let html = ''; html += fileMenu.html(); html += editMenu.html(); if(main.options.tabsMenu) { html += tabsMenu.html(); } html += optionsMenu.html(); html += helpMenu.html(); // // Add any tests as top-level menu options // let i=0; for(const test of main.test.tests) { html += '<li><a class="cirsim-test-' + i + '">' + test.name + '</a></li>'; i++; } ul.innerHTML = html; // // Menu option for testing the Toast error reporting mechanism // // html += `<li><a class="toast-test">Toast!</a></li>`; // ul.innerHTML = html; // // this.toasts = 0; // this.click('.toast-test', (event)=> { // this.toasts++; // main.toast.message('Toast message ' + this.toasts); // }); // // Install click handlers for all top-level menus // for(const node of ul.childNodes) { if(node.tagName === 'LI') { node.addEventListener('click', (event) => { event.preventDefault(); // Find the <ul> in this menu let ulSub = Tools.child(node, 'UL'); if(ulSub !== null) { if(getComputedStyle(ulSub).getPropertyValue('visibility') === 'hidden') { open(node); } else { // If already open, close all this.closeAll(); } } }); } } // // Install test function handlers // i = 0; for(const test of main.test.tests) { let cls = ".cirsim-test-" + i; this.click(cls, (event) => { event.preventDefault(); main.test.runTestDlg(test); }); i++; } // Activate all of the menus fileMenu.activate(); editMenu.activate(); tabsMenu.activate(); optionsMenu.activate(); helpMenu.activate(); } /* * Listen to key down events so we can close the menu * if we click outside of the menu while it is open. */ const closeListener = (event) => { // See if we clicked on some child of nav... let node = event.target.parentNode; for( ; node !== null; node = node.parentNode) { if(node === this.nav) { return; } } this.closeAll(); } /* * Open a menu */ const open = (li) => { // Hide any other menus for(const node of this.ul.childNodes) { if(node.tagName === 'LI') { const ul = Tools.child(node, 'UL'); if(ul !== null) { Tools.removeClass(ul, 'menu-open'); } } } selectionDependent(".edit-delete"); componentSelectionDependent(".edit-properties"); fileMenu.opening(); tabsMenu.opening(); optionsMenu.opening(); helpMenu.opening(); // And open this menu const ul = Tools.child(li, 'UL'); if(ul !== null) { Tools.addClass(ul, 'menu-open'); } document.addEventListener('click', closeListener); document.addEventListener('mousedown', closeListener); } /** Close all menus */ this.closeAll = () => { for(const node of this.ul.childNodes) { if(node.tagName === 'LI') { const ul = Tools.child(node, 'UL'); if(ul !== null) { Tools.removeClass(ul, 'menu-open'); } } } document.removeEventListener('click', closeListener); document.removeEventListener('mousedown', closeListener); } /** * Enable or disable a menu option by selector * @param sel Selector for the menu option (like '.tabs-add') * @param enable True to enable */ this.enable = (sel, enable) => { const element = this.ul.querySelector(sel); if(element === null) { return; } if(enable) { Tools.removeClass(element.parentNode, "menu-disabled"); } else { Tools.addClass(element.parentNode, "menu-disabled"); } } /* * Enable a menu option only if there is a current selection */ const selectionDependent = (sel) => { this.enable(sel, main.currentView().selection.selection.length > 0); } /* * Enable a menu option only if one component is selected */ const componentSelectionDependent = (sel) => { if(main.currentView().selection.selection.length !== 1 || !(main.currentView().selection.selection[0] instanceof Component)) { this.enable(sel, false); } else { this.enable(sel, true); } } /** * Find a menu option by selector * @param sel * @returns {Element} */ this.find = (sel) => { return this.ul.querySelector(sel); } /** * Install a menu option click hander * @param sel Selector for the menu option * @param closure The closure to call (passes 'event') */ this.click = (sel, closure) => { const element = this.find(sel); if(element !== null) { element.addEventListener('click', (event) => { event.preventDefault(); this.closeAll(); closure(event); }); } } initialize(); };