From 290dc59a097fee6cf98968c2e6424828850ae0e2 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 6 Jan 2025 13:30:17 -0700 Subject: [PATCH] Speed up nextmatch context menus by creating them once & reuse them --- api/js/egw_action/EgwMenuShoelace.ts | 51 +++++++++++++++---- .../EgwPopupActionImplementation.ts | 30 +++++++++-- api/js/egw_action/egw_menu.ts | 44 +++++++++------- 3 files changed, 93 insertions(+), 32 deletions(-) diff --git a/api/js/egw_action/EgwMenuShoelace.ts b/api/js/egw_action/EgwMenuShoelace.ts index f9cc7d1933..ff12415d61 100644 --- a/api/js/egw_action/EgwMenuShoelace.ts +++ b/api/js/egw_action/EgwMenuShoelace.ts @@ -1,5 +1,5 @@ -import {css, html, LitElement, nothing, PropertyValues} from "lit"; -import {SlIcon, SlMenu, SlMenuItem} from "@shoelace-style/shoelace"; +import {css, html, LitElement, nothing} from "lit"; +import {SlMenu, SlMenuItem} from "@shoelace-style/shoelace"; import {egwMenuItem} from "./egw_menu"; import {customElement} from "lit/decorators/custom-element.js"; import {repeat} from "lit/directives/repeat.js"; @@ -26,6 +26,12 @@ export class EgwMenuShoelace extends LitElement box-shadow: var(--sl-shadow-x-large); } + /* sl-menu-item:host overrides display */ + + sl-menu-item[hidden] { + display: none !important; + } + sl-menu-item::part(base) { height: 1.7em; line-height: var(--sl-line-height-dense); @@ -60,7 +66,7 @@ export class EgwMenuShoelace extends LitElement private structure = []; private popup = null; - private removeCallback = null; + private hideCallback = null; private get menu() : SlMenu { return this.shadowRoot?.querySelector("sl-menu");} @@ -91,16 +97,16 @@ export class EgwMenuShoelace extends LitElement this.popup.remove(); this.popup = null; } - if(this.removeCallback) + if(this.hideCallback) { - this.removeCallback.call(); + this.hideCallback.call(); } } public showAt(_x, _y, _onHide) { - this.removeCallback = _onHide; + this.hideCallback = _onHide; if(this.popup == null) { this.popup = Object.assign(document.createElement("sl-popup"), { @@ -111,7 +117,10 @@ export class EgwMenuShoelace extends LitElement }); this.popup.append(this); this.popup.classList.add("egw_menu"); + document.body.append(this.popup); } + + // Open where instructed let menu = this; this.popup.anchor = { getBoundingClientRect() @@ -129,7 +138,6 @@ export class EgwMenuShoelace extends LitElement } }; this.popup.active = true; - document.body.append(this.popup); Promise.all([this.updateComplete, this.popup.updateComplete]).then(() => { // Causes scroll issues if we don't position @@ -138,15 +146,34 @@ export class EgwMenuShoelace extends LitElement }); } + /** + * Update the menu items with current disabled / visible settings + * + * @param _links + */ + public applyContext(_links) + { + Object.keys(_links).forEach((actionId) => + { + const menuItem = this.shadowRoot.querySelector("[data-action-id='" + actionId + "']"); + if(!menuItem) + { + return; + } + menuItem.disabled = !_links[actionId].enabled; + menuItem.hidden = !_links[actionId].visible; + }); + } public hide() { if(this.popup) { this.popup.active = false; } - - // egw_menu always creates a new menu - this.remove(); + if(this.hideCallback) + { + this.hideCallback.call(); + } } handleSelect(event) @@ -230,14 +257,16 @@ export class EgwMenuShoelace extends LitElement { item.iconUrl = item.checked ? "toggle-on" : "toggle-off"; } + const id = CSS.escape(item.id); return html` { + // Special handling for nextmatch: only build the menu once and just re-use it. + if(!_context.menu && _context.actionLinks && _context.parent?.manager?.data?.nextmatch && !_context.parent.manager.data.menu) + { + _context.parent.manager.data.menu = this._buildMenu(_context.actionLinks.filter(l => l.actionObj.type == "popup"), [_context], null); + _context.parent.manager.data.menu.showAt(0, 0); + _context.parent.manager.data.menu.hide(); + } const contextHandler = (e) => { const x = _node //use different node and context for callback if event happens on parent @@ -526,7 +547,7 @@ export class EgwPopupActionImplementation implements EgwActionImplementation { const tree = {"root": []}; // Automatically add in Drag & Drop actions - if(this.auto_paste && !window.egwIsMobile() && !this._context.event?.type.match(/touch/)) + if(this.auto_paste && !window.egwIsMobile() && this._context?.event && !this._context.event?.type.match(/touch/)) { this._addCopyPaste(_links, _selected); } @@ -828,6 +849,7 @@ export class EgwPopupActionImplementation implements EgwActionImplementation { } }; private _context: any; + private menu : EgwMenu; } diff --git a/api/js/egw_action/egw_menu.ts b/api/js/egw_action/egw_menu.ts index 910c58a2a1..c38471e974 100755 --- a/api/js/egw_action/egw_menu.ts +++ b/api/js/egw_action/egw_menu.ts @@ -9,15 +9,6 @@ * */ import {EgwMenuShoelace} from "./EgwMenuShoelace"; -import {egw_registeredShortcuts, egw_shortcutIdx} from './egw_keymanager'; -import { - EGW_KEY_ARROW_DOWN, - EGW_KEY_ARROW_LEFT, - EGW_KEY_ARROW_RIGHT, - EGW_KEY_ARROW_UP, - EGW_KEY_ENTER, - EGW_KEY_ESCAPE -} from "./egw_action_constants"; //Global variable which is used to store the currently active menu so that it //may be closed when another menu opens @@ -160,7 +151,6 @@ export class egwMenu if (this.instance != null) { this.instance.hide(); - this.instance = null; } } @@ -196,18 +186,38 @@ export class egwMenu //Obtain a new egwMenuImpl object and pass this instance to it this.instance = new EgwMenuShoelace(this.children); - _egw_active_menu = this; - - this.instance.showAt(_x, _y, () => { - this.instance = null; - _egw_active_menu = null; - }); - return true; } + _egw_active_menu = this; + + this.instance.showAt(_x, _y, () => + { + _egw_active_menu = null; + }); + return true; + return false; } + + /** + * Enable / disable menu items for the given selection & target + * + * @param _context + * @param _links + * @param _selected + * @param _target + * @private + */ + public applyContext(_links, _selected, _target) + { + if(!this.instance) + { + this.instance = new EgwMenuShoelace(this.children); + } + this.instance.applyContext(_links); + } + /** * Adds a new menu item to the list and returns a reference to that object. *