From 8535af0d3ed55eeca7709cdd592eae0d1f16bbfa Mon Sep 17 00:00:00 2001 From: Erwin Dondorp Date: Tue, 11 Aug 2020 22:16:51 +0200 Subject: [PATCH] use new menus --- saltgui/static/scripts/CommandBox.js | 17 +++--- saltgui/static/scripts/Documentation.js | 2 +- saltgui/static/scripts/DropDown.js | 15 ++--- saltgui/static/scripts/DropDownCheckbox.js | 18 ++++++ saltgui/static/scripts/DropDownCmd.js | 24 ++++++++ saltgui/static/scripts/DropDownRadio.js | 69 ++++++++++++++++++++++ saltgui/static/scripts/RunType.js | 48 +++------------ saltgui/static/scripts/TargetType.js | 41 +++++-------- saltgui/static/scripts/Utils.js | 10 ++-- 9 files changed, 154 insertions(+), 90 deletions(-) create mode 100644 saltgui/static/scripts/DropDownCheckbox.js create mode 100644 saltgui/static/scripts/DropDownCmd.js create mode 100644 saltgui/static/scripts/DropDownRadio.js diff --git a/saltgui/static/scripts/CommandBox.js b/saltgui/static/scripts/CommandBox.js index 105e3f15c..6bab911f8 100644 --- a/saltgui/static/scripts/CommandBox.js +++ b/saltgui/static/scripts/CommandBox.js @@ -2,7 +2,7 @@ import {Character} from "./Character.js"; import {Documentation} from "./Documentation.js"; -import {DropDownMenu} from "./DropDown.js"; +import {DropDownMenuCmd} from "./DropDownCmd.js"; import {Output} from "./output/Output.js"; import {ParseCommandLine} from "./ParseCommandLine.js"; import {Router} from "./Router.js"; @@ -17,14 +17,15 @@ export class CommandBox { this.api = pApi; const cmdbox = document.getElementById("cmd-box"); - this.cmdmenu = new DropDownMenu(cmdbox); + this.cmdmenu = new DropDownMenuCmd(cmdbox); - this.documentation = new Documentation(this.router, this); this._registerCommandBoxEventListeners(); RunType.createMenu(); TargetType.createMenu(); + this.documentation = new Documentation(pRouter, this); + const manualRun = document.getElementById("popup-run-command"); Utils.addTableHelp(manualRun, "Click for help", "bottom-center"); const helpButton = manualRun.querySelector("#help"); @@ -41,7 +42,7 @@ export class CommandBox { // since the storage-item is then not populated yet. return; } - const menu = new DropDownMenu(titleElement); + const menu = new DropDownMenuCmd(titleElement); const templatesText = Utils.getStorageItem("session", "templates", "{}"); const templates = JSON.parse(templatesText); const keys = Object.keys(templates).sort(); @@ -51,7 +52,7 @@ export class CommandBox { if (!description) { description = "(" + key + ")"; } - menu.addMenuItem( + menu.addMenuItemCmd( description, () => { CommandBox._applyTemplate(template); @@ -164,7 +165,7 @@ export class CommandBox { TargetType.setTargetType(targetType); } else { // not in the template, revert to default - TargetType.setTargetTypeDefault(); + TargetType.setTargetType(null); } if (template.target) { @@ -222,7 +223,7 @@ export class CommandBox { const commandField = document.getElementById("command"); const commandValue = commandField.value; - const targetType = TargetType.menuTargetType._value; + const targetType = TargetType.menuTargetType.getValue(); const patWhitespaceAll = /\s/g; const commandValueNoTabs = commandValue.replace(patWhitespaceAll, " "); @@ -380,7 +381,7 @@ export class CommandBox { // reset to default, so that its value is initially hidden RunType.setRunTypeDefault(); - TargetType.setTargetTypeDefault(); + TargetType.setTargetType(null); if (Router.currentPage) { Router.currentPage.refreshPage(); diff --git a/saltgui/static/scripts/Documentation.js b/saltgui/static/scripts/Documentation.js index 17b61a1d0..8722c943d 100644 --- a/saltgui/static/scripts/Documentation.js +++ b/saltgui/static/scripts/Documentation.js @@ -117,7 +117,7 @@ export class Documentation { dummyCommand = "sys.doc " + cmd; } - const targetType = TargetType.menuTargetType._value; + const targetType = TargetType.menuTargetType.getValue(); const func = this.commandbox.getRunParams(targetType, target, docCommand, true); if (func === null) { diff --git a/saltgui/static/scripts/DropDown.js b/saltgui/static/scripts/DropDown.js index 02d05ecfe..9dfa6acb4 100644 --- a/saltgui/static/scripts/DropDown.js +++ b/saltgui/static/scripts/DropDown.js @@ -18,6 +18,8 @@ import {Utils} from "./Utils.js"; // the menu. when at least one item is visible, the menu is visible // remember to call verifyApp() when that is potentially the case +// superclass for DropDownMenuRadio, DropDownMenuCheckbox and DropDownMenuCmd + export class DropDownMenu { // Creates an empty dropdown menu @@ -83,6 +85,7 @@ export class DropDownMenu { visibleCount += 1; } } + // hide the menu when it has no visible menu-items const displayVisible = this.menuDropdown.tagName === "TD" ? "table-cell" : "inline-block"; const displayInvisible = "none"; @@ -102,7 +105,7 @@ export class DropDownMenu { // function is called each time the menu opens // This allows dynamic menuitem titles (use menuitem.innerText) // or visibility (use menuitem.style.display = "none"/"inline-block") - addMenuItem (pTitle, pCallBack, pValue) { + addMenuItem (pTitle, pCallBack = null, pValue = null) { const button = Utils.createDiv("run-command-button", "..."); if (pValue) { button._value = pValue; @@ -124,9 +127,9 @@ export class DropDownMenu { return button; } - _callback (pClickEvent, pCallBack, pValue) { - this._value = pValue; + _callback (pClickEvent, pCallBack) { pCallBack(pClickEvent); + this.verifyAll(); pClickEvent.stopPropagation(); } @@ -148,10 +151,4 @@ export class DropDownMenu { __hideMenu () { this.menuDropdown.style.display = "none"; } - - clearMenu () { - while (this.menuDropdownContent.firstChild) { - this.menuDropdownContent.removeChild(this.menuDropdownContent.firstChild); - } - } } diff --git a/saltgui/static/scripts/DropDownCheckbox.js b/saltgui/static/scripts/DropDownCheckbox.js new file mode 100644 index 000000000..c0028a0c1 --- /dev/null +++ b/saltgui/static/scripts/DropDownCheckbox.js @@ -0,0 +1,18 @@ +import {DropDownMenu} from "./DropDown.js"; + +export class DropDownMenuCheckbox extends DropDownMenu { + // constructor (pParentElement) { + // super(pParentElement); + // } + +/* + addMenuItemCheckbox (pTitle, pCallback) { + const menuItem = super.addMenuItem(pTitle, () => { + this._selectCallback(pValue, pTitle, pMenuTitle); + }, (pMenuItem) => { + return this._verifyMenuItem(pMenuItem); + pCallback(ev); + }); + } +*/ +} diff --git a/saltgui/static/scripts/DropDownCmd.js b/saltgui/static/scripts/DropDownCmd.js new file mode 100644 index 000000000..fd71842d7 --- /dev/null +++ b/saltgui/static/scripts/DropDownCmd.js @@ -0,0 +1,24 @@ +import {DropDownMenu} from "./DropDown.js"; + +export class DropDownMenuCmd extends DropDownMenu { + // constructor (pParentElement) { + // super(pParentElement); + // } + + _selectCallback (pMenuItem, pTitle) { + // show the chosen value + if (typeof pTitle !== "string") { + pTitle = pTitle(pMenuItem); + } + this.setTitle(pTitle); + } + + addMenuItemCmd (pTitle, pCallback) { + const menuItem = super.addMenuItem(pTitle, (pMenuItem) => { + this._selectCallback(pMenuItem, pTitle); + }, (pMenuItem) => { + pCallback(pMenuItem); + }); + return menuItem; + } +} diff --git a/saltgui/static/scripts/DropDownRadio.js b/saltgui/static/scripts/DropDownRadio.js new file mode 100644 index 000000000..52b195dac --- /dev/null +++ b/saltgui/static/scripts/DropDownRadio.js @@ -0,0 +1,69 @@ +import {DropDownMenu} from "./DropDown.js"; + +export class DropDownMenuRadio extends DropDownMenu { + constructor (pParentElement) { + super(pParentElement); + this.value = null; + this.defaultValue = null; + } + + getValue () { + if (this.value === null) { + return this.defaultValue; + } + return this.value; + } + + setValue (pValue) { + this.value = pValue; + } + + setDefaultValue (pDefaultValue) { + this.defaultValue = pDefaultValue; + } + + _verifyMenuItem (pMenuItem) { + let title; + if (typeof pMenuItem._title === "string") { + title = pMenuItem._title; + } else { + title = pMenuItem._title(pMenuItem); + } + + if (title === null) { + // menu item will be hidden + } else if (pMenuItem._value === this.value) { + // 25CF = BLACK CIRCLE + title = "\u25CF " + title; + } else if (this.value === null && pMenuItem._value === this.defaultValue) { + // 25CB = WHITE CIRCLE + title = "\u25CB " + title; + } + return title; + } + + _selectCallback (pValue, pTitle, pMenuTitle) { + + this.value = pValue; + + // show the chosen value + if (pMenuTitle === null) { + // caller can specify a more specific menu title + // usually an abbreviation of the chosen menu item + if (typeof pTitle !== "string") { + pTitle = pTitle(); + } + this.setTitle(pTitle); + } else { + this.setTitle(pMenuTitle); + } + } + + addMenuItemRadio (pValue, pTitle, pMenuTitle = null) { + const menuItem = super.addMenuItem(pTitle, () => { + this._selectCallback(pValue, pTitle, pMenuTitle); + }, (pMenuItem) => this._verifyMenuItem(pMenuItem)); + + menuItem._value = pValue; + } +} diff --git a/saltgui/static/scripts/RunType.js b/saltgui/static/scripts/RunType.js index 75cc6adf7..cb4995ab8 100644 --- a/saltgui/static/scripts/RunType.js +++ b/saltgui/static/scripts/RunType.js @@ -1,60 +1,26 @@ /* global document */ -import {Character} from "./Character.js"; -import {DropDownMenu} from "./DropDown.js"; -import {Utils} from "./Utils.js"; +import {DropDownMenuRadio} from "./DropDownRadio.js"; export class RunType { static createMenu () { const runblock = document.getElementById("run-block"); - RunType.menuRunType = new DropDownMenu(runblock); - // do not show the menu title at first - RunType.menuRunType.setTitle(""); - RunType.menuRunType.addMenuItem("Normal", RunType._updateRunTypeText, "normal"); - RunType.menuRunType.addMenuItem("Async", RunType._updateRunTypeText, "async"); - RunType._updateRunTypeText(); - } - - static _updateRunTypeText () { - const runType = RunType.getRunType(); - - switch (runType) { - case "normal": - // now that the menu is used show the menu title - RunType.menuRunType.setTitle("Normal"); - break; - case "async": - RunType.menuRunType.setTitle("Async"); - break; - default: - Utils.error("runType", runType); - } - - const menuItems = RunType.menuRunType.menuDropdownContent.children; - for (let i = 0; i < menuItems.length; i++) { - let menuItemText = menuItems[i].innerText; - menuItemText = menuItemText.replace(/^. /, ""); - if (menuItems[i]._value === runType) { - menuItemText = Character.BLACK_CIRCLE + " " + menuItemText; - } - menuItems[i].innerText = menuItemText; - } + RunType.menuRunType = new DropDownMenuRadio(runblock); + RunType.menuRunType.setDefaultValue("normal"); + RunType.menuRunType.addMenuItemRadio("normal", "Normal"); + RunType.menuRunType.addMenuItemRadio("async", "Async"); } static setRunTypeDefault () { - RunType.menuRunType._value = "normal"; - RunType._updateRunTypeText(); + RunType.menuRunType.setValue(null); // reset the title to the absolute minimum // so that the menu does not stand out in trivial situations RunType.menuRunType.setTitle(""); } static getRunType () { - const runType = RunType.menuRunType._value; - if (runType === undefined || runType === "") { - return "normal"; - } + const runType = RunType.menuRunType.getValue(); return runType; } } diff --git a/saltgui/static/scripts/TargetType.js b/saltgui/static/scripts/TargetType.js index 97d512567..944d518fa 100644 --- a/saltgui/static/scripts/TargetType.js +++ b/saltgui/static/scripts/TargetType.js @@ -1,20 +1,21 @@ /* global document */ import {Character} from "./Character.js"; -import {DropDownMenu} from "./DropDown.js"; +import {DropDownMenuRadio} from "./DropDownRadio.js"; import {Utils} from "./Utils.js"; export class TargetType { static createMenu () { const targetbox = document.getElementById("target-box"); - TargetType.menuTargetType = new DropDownMenu(targetbox); + TargetType.menuTargetType = new DropDownMenuRadio(targetbox); // do not show the menu title at first - TargetType.menuTargetType.addMenuItem("Normal", TargetType._manualUpdateTargetTypeText, "glob"); - TargetType.menuTargetType.addMenuItem("List", TargetType._manualUpdateTargetTypeText, "list"); - TargetType.menuTargetType.addMenuItem(TargetType._targetTypeNodeGroupPrepare, TargetType._manualUpdateTargetTypeText, "nodegroup"); - TargetType.menuTargetType.addMenuItem("Compound", TargetType._manualUpdateTargetTypeText, "compound"); - TargetType.setTargetTypeDefault(); + TargetType.menuTargetType.setTitle(""); + TargetType.menuTargetType.addMenuItemRadio("glob", "Normal"); + TargetType.menuTargetType.addMenuItemRadio("list", "List"); + TargetType.menuTargetType.addMenuItemRadio("nodegroup", (pMenuItem) => TargetType._targetTypeNodeGroupPrepare(pMenuItem)); + TargetType.menuTargetType.addMenuItemRadio("compound", "Compound"); + TargetType.autoSelectTargetType(""); } // It takes a while before we known the list of nodegroups @@ -85,46 +86,34 @@ export class TargetType { } menuItems[i].innerText = menuItemText; } + return null; } static autoSelectTargetType (pTarget) { - if (!TargetType.menuTargetType._system) { - // user has selected the value, do not touch it - return; - } - if (pTarget.includes("@") || pTarget.includes(" ") || pTarget.includes("(") || pTarget.includes(")")) { // "@" is a strong indicator for compound target // but "space", "(" and ")" are also typical for compound target - TargetType.menuTargetType._value = "compound"; + TargetType.menuTargetType.setDefaultValue("compound"); } else if (pTarget.includes(",")) { // "," is a strong indicator for list target (when it is also not compound) - TargetType.menuTargetType._value = "list"; + TargetType.menuTargetType.setDefaultValue("list"); } else if (pTarget.startsWith("#")) { // "#" at the start of a line is a strong indicator for nodegroup target // this is not a SALTSTACK standard, but our own invention - TargetType.menuTargetType._value = "nodegroup"; + TargetType.menuTargetType.setDefaultValue("nodegroup"); } else { - TargetType.menuTargetType._value = "glob"; + TargetType.menuTargetType.setDefaultValue("glob"); } - - // show the new title - TargetType._updateTargetTypeText(); } static setTargetType (pTargetType) { - TargetType.menuTargetType._value = pTargetType; - TargetType.menuTargetType._system = true; - TargetType._updateTargetTypeText(); + TargetType.menuTargetType.setValue(pTargetType); } static _getTargetType () { - const targetType = TargetType.menuTargetType._value; - if (targetType === undefined || targetType === "") { - return "glob"; - } + const targetType = TargetType.menuTargetType.getValue(); return targetType; } diff --git a/saltgui/static/scripts/Utils.js b/saltgui/static/scripts/Utils.js index adf592e23..1c7b80064 100644 --- a/saltgui/static/scripts/Utils.js +++ b/saltgui/static/scripts/Utils.js @@ -1,7 +1,7 @@ /* global console document Hilitor window */ import {Character} from "./Character.js"; -import {DropDownMenu} from "./DropDown.js"; +import {DropDownMenuCheckbox} from "./DropDownCheckbox.js"; export class Utils { @@ -203,7 +203,7 @@ export class Utils { const menuAndFieldDiv = Utils.createDiv("search-menu-and-field", ""); - const searchOptionsMenu = new DropDownMenu(menuAndFieldDiv, true); + const searchOptionsMenu = new DropDownMenuCheckbox(menuAndFieldDiv, true); const input = document.createElement("input"); input.type = "text"; @@ -220,15 +220,15 @@ export class Utils { errorDiv.style.display = "none"; div.append(errorDiv); - searchOptionsMenu.addMenuItem( + searchOptionsMenu.addMenuItemCheckbox( "Case sensitive", (ev) => { Utils._updateSearchOption(ev, pTable, searchOptionsMenu, input); }); - searchOptionsMenu.addMenuItem( + searchOptionsMenu.addMenuItemCheckbox( "Regular expression", (ev) => { Utils._updateSearchOption(ev, pTable, searchOptionsMenu, input); }); - searchOptionsMenu.addMenuItem( + searchOptionsMenu.addMenuItemCheckbox( "Invert search", (ev) => { Utils._updateSearchOption(ev, pTable, searchOptionsMenu, input); });