mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-24 14:58:43 +01:00
Speed up nextmatch context menus by creating them once & reuse them
This commit is contained in:
parent
2b5f15855b
commit
290dc59a09
@ -1,5 +1,5 @@
|
|||||||
import {css, html, LitElement, nothing, PropertyValues} from "lit";
|
import {css, html, LitElement, nothing} from "lit";
|
||||||
import {SlIcon, SlMenu, SlMenuItem} from "@shoelace-style/shoelace";
|
import {SlMenu, SlMenuItem} from "@shoelace-style/shoelace";
|
||||||
import {egwMenuItem} from "./egw_menu";
|
import {egwMenuItem} from "./egw_menu";
|
||||||
import {customElement} from "lit/decorators/custom-element.js";
|
import {customElement} from "lit/decorators/custom-element.js";
|
||||||
import {repeat} from "lit/directives/repeat.js";
|
import {repeat} from "lit/directives/repeat.js";
|
||||||
@ -26,6 +26,12 @@ export class EgwMenuShoelace extends LitElement
|
|||||||
box-shadow: var(--sl-shadow-x-large);
|
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) {
|
sl-menu-item::part(base) {
|
||||||
height: 1.7em;
|
height: 1.7em;
|
||||||
line-height: var(--sl-line-height-dense);
|
line-height: var(--sl-line-height-dense);
|
||||||
@ -60,7 +66,7 @@ export class EgwMenuShoelace extends LitElement
|
|||||||
|
|
||||||
private structure = [];
|
private structure = [];
|
||||||
private popup = null;
|
private popup = null;
|
||||||
private removeCallback = null;
|
private hideCallback = null;
|
||||||
|
|
||||||
private get menu() : SlMenu { return this.shadowRoot?.querySelector("sl-menu");}
|
private get menu() : SlMenu { return this.shadowRoot?.querySelector("sl-menu");}
|
||||||
|
|
||||||
@ -91,16 +97,16 @@ export class EgwMenuShoelace extends LitElement
|
|||||||
this.popup.remove();
|
this.popup.remove();
|
||||||
this.popup = null;
|
this.popup = null;
|
||||||
}
|
}
|
||||||
if(this.removeCallback)
|
if(this.hideCallback)
|
||||||
{
|
{
|
||||||
this.removeCallback.call();
|
this.hideCallback.call();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public showAt(_x, _y, _onHide)
|
public showAt(_x, _y, _onHide)
|
||||||
{
|
{
|
||||||
this.removeCallback = _onHide;
|
this.hideCallback = _onHide;
|
||||||
if(this.popup == null)
|
if(this.popup == null)
|
||||||
{
|
{
|
||||||
this.popup = Object.assign(document.createElement("sl-popup"), {
|
this.popup = Object.assign(document.createElement("sl-popup"), {
|
||||||
@ -111,7 +117,10 @@ export class EgwMenuShoelace extends LitElement
|
|||||||
});
|
});
|
||||||
this.popup.append(this);
|
this.popup.append(this);
|
||||||
this.popup.classList.add("egw_menu");
|
this.popup.classList.add("egw_menu");
|
||||||
|
document.body.append(this.popup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open where instructed
|
||||||
let menu = this;
|
let menu = this;
|
||||||
this.popup.anchor = {
|
this.popup.anchor = {
|
||||||
getBoundingClientRect()
|
getBoundingClientRect()
|
||||||
@ -129,7 +138,6 @@ export class EgwMenuShoelace extends LitElement
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.popup.active = true;
|
this.popup.active = true;
|
||||||
document.body.append(this.popup);
|
|
||||||
Promise.all([this.updateComplete, this.popup.updateComplete]).then(() =>
|
Promise.all([this.updateComplete, this.popup.updateComplete]).then(() =>
|
||||||
{
|
{
|
||||||
// Causes scroll issues if we don't position
|
// 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 = <SlMenuItem>this.shadowRoot.querySelector("[data-action-id='" + actionId + "']");
|
||||||
|
if(!menuItem)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
menuItem.disabled = !_links[actionId].enabled;
|
||||||
|
menuItem.hidden = !_links[actionId].visible;
|
||||||
|
});
|
||||||
|
}
|
||||||
public hide()
|
public hide()
|
||||||
{
|
{
|
||||||
if(this.popup)
|
if(this.popup)
|
||||||
{
|
{
|
||||||
this.popup.active = false;
|
this.popup.active = false;
|
||||||
}
|
}
|
||||||
|
if(this.hideCallback)
|
||||||
// egw_menu always creates a new menu
|
{
|
||||||
this.remove();
|
this.hideCallback.call();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelect(event)
|
handleSelect(event)
|
||||||
@ -230,14 +257,16 @@ export class EgwMenuShoelace extends LitElement
|
|||||||
{
|
{
|
||||||
item.iconUrl = item.checked ? "toggle-on" : "toggle-off";
|
item.iconUrl = item.checked ? "toggle-on" : "toggle-off";
|
||||||
}
|
}
|
||||||
|
const id = CSS.escape(item.id);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<sl-menu-item
|
<sl-menu-item
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
"default-item": item.default
|
"default-item": item.default
|
||||||
})}
|
})}
|
||||||
id=${item.id}
|
id=${id}
|
||||||
type="${item.checkbox ? "checkbox" : "normal"}"
|
type="${item.checkbox ? "checkbox" : "normal"}"
|
||||||
|
data-action-id="${item.id}"
|
||||||
?checked=${item.checkbox && item.checked}
|
?checked=${item.checkbox && item.checked}
|
||||||
?disabled=${!item.enabled}
|
?disabled=${!item.enabled}
|
||||||
.value=${item}
|
.value=${item}
|
||||||
|
@ -116,10 +116,24 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
|
|||||||
_context = {"posx": x, "posy": y};
|
_context = {"posx": x, "posy": y};
|
||||||
}
|
}
|
||||||
|
|
||||||
const menu = this._buildMenu(_links, _selected, _target);
|
let menu = null;
|
||||||
menu.showAt(_context.posx, _context.posy);
|
// Special handling for nextmatch context menu - reuse the same menu
|
||||||
|
if(!_target && !_context.menu && _selected[0].parent.manager.data.menu)
|
||||||
|
{
|
||||||
|
menu = _selected[0].parent.manager.data.menu
|
||||||
|
}
|
||||||
|
if(!menu)
|
||||||
|
{
|
||||||
|
menu = this._buildMenu(_links, _selected, _target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
menu.applyContext(_links, _selected, _target);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
menu.showAt(_context.posx, _context.posy);
|
||||||
|
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
const defaultAction = this._getDefaultLink(_links);
|
const defaultAction = this._getDefaultLink(_links);
|
||||||
if (defaultAction) {
|
if (defaultAction) {
|
||||||
@ -303,6 +317,13 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
|
|||||||
*/
|
*/
|
||||||
private _registerContext = (_node, _callback, _context) => {
|
private _registerContext = (_node, _callback, _context) => {
|
||||||
|
|
||||||
|
// 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 contextHandler = (e) => {
|
||||||
const x = _node
|
const x = _node
|
||||||
//use different node and context for callback if event happens on parent
|
//use different node and context for callback if event happens on parent
|
||||||
@ -526,7 +547,7 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
|
|||||||
const tree = {"root": []};
|
const tree = {"root": []};
|
||||||
|
|
||||||
// Automatically add in Drag & Drop actions
|
// 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);
|
this._addCopyPaste(_links, _selected);
|
||||||
}
|
}
|
||||||
@ -828,6 +849,7 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
private _context: any;
|
private _context: any;
|
||||||
|
private menu : EgwMenu;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,15 +9,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import {EgwMenuShoelace} from "./EgwMenuShoelace";
|
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
|
//Global variable which is used to store the currently active menu so that it
|
||||||
//may be closed when another menu opens
|
//may be closed when another menu opens
|
||||||
@ -160,7 +151,6 @@ export class egwMenu
|
|||||||
if (this.instance != null)
|
if (this.instance != null)
|
||||||
{
|
{
|
||||||
this.instance.hide();
|
this.instance.hide();
|
||||||
this.instance = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,18 +186,38 @@ export class egwMenu
|
|||||||
//Obtain a new egwMenuImpl object and pass this instance to it
|
//Obtain a new egwMenuImpl object and pass this instance to it
|
||||||
this.instance = new EgwMenuShoelace(this.children);
|
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;
|
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.
|
* Adds a new menu item to the list and returns a reference to that object.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user