Context menu fixes

- Add remove() to remove menu, not needed normally
- Remove reference to widget.options
- Fix filemanager modifying menu after setting the actions was not working (now re-generating menu after directly messing with actions)
- Hide dividers if section is empty
This commit is contained in:
nathan 2025-01-13 14:34:51 -07:00
parent cb6d657f6c
commit a4a1e2837a
4 changed files with 46 additions and 15 deletions

View File

@ -1,5 +1,5 @@
import {css, html, LitElement, nothing} from "lit";
import {SlMenu, SlMenuItem} from "@shoelace-style/shoelace";
import {SlDivider, 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";
@ -28,7 +28,7 @@ export class EgwMenuShoelace extends LitElement
/* sl-menu-item:host overrides display */
sl-menu-item[hidden] {
sl-menu-item[hidden], sl-divider[hidden] {
display: none !important;
}
@ -142,7 +142,7 @@ export class EgwMenuShoelace extends LitElement
{
// Causes scroll issues if we don't position
this.popup.popup.style = "top: 0px";
(<SlMenuItem>this.menu.querySelector('sl-menu-item')).focus();
(<SlMenuItem>this.menu.querySelector('sl-menu-item'))?.focus();
});
}
@ -153,9 +153,15 @@ export class EgwMenuShoelace extends LitElement
*/
public applyContext(_links, _selected, _target)
{
// Reset & hide all, in case some actions were not included in links
this.menu.querySelectorAll("sl-menu-item").forEach(i => i.disabled = i.hidden = true);
this.menu.querySelectorAll("sl-divider").forEach(i => i.hidden = false);
Object.keys(_links).forEach((actionId) =>
{
const menuItem = <SlMenuItem>this.shadowRoot.querySelector("[data-action-id='" + actionId + "']");
// Take the last one if there's more than one with the same ID as a work-around to automatic drag actions getting added twice
// in different places in some cases (nextmatch_controller vs EgwPopupActionImplementation)
const menuItem = <SlMenuItem>Array.from(this.shadowRoot.querySelectorAll("[data-action-id='" + actionId + "']")).pop();
if(!menuItem)
{
return;
@ -168,6 +174,9 @@ export class EgwMenuShoelace extends LitElement
}
});
// Hide dividers before empty sections
this.menu.querySelectorAll("sl-divider:not(:has( + sl-menu-item:not([hidden])))").forEach((i : SlDivider) => i.hidden = true);
// Copy caption changes
let osClipboard;
if(_links.egw_os_clipboard && (osClipboard = <SlMenuItem>this.shadowRoot.querySelector("[data-action-id='egw_os_clipboard']")))

View File

@ -122,7 +122,10 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
{
menu = _selected[0].parent.manager.data.menu
}
this._addCopyPaste(_links, _selected);
if(this.auto_paste && !window.egwIsMobile() && (!this._context?.event || this._context?.event && !this._context.event?.type.match(/touch/)))
{
this._addCopyPaste(_links, _selected);
}
if(!menu)
{
menu = this._buildMenu(_links, _selected, _target);
@ -559,10 +562,7 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
}
for (const k in _links) {
if(_links[k].actionObj.type == "popup")
{
_links[k].actionObj.appendToTree(tree);
}
_links[k].actionObj.appendToTree(tree);
}
// We need the dummy object container in order to pass the array by
@ -800,9 +800,13 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
// Add titles of entries
for (let i = 0; i < clipboard.selected.length; i++) {
let id = clipboard.selected[i].id.split('::');
window.egw.link_title(id[0], id[1], function (title) {
if (title) this.hint += title + "\n";
}, paste_action);
window.egw.link_title(id[0], id[1], (title) =>
{
if(title)
{
hint += title + "\n";
}
}, paste_action);
}
// Add into links, so it's included in menu
@ -827,7 +831,11 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
// Add in actual actions as children
for (let k in drop) {
// Add some choices - need to be a copy, or they interfere with
if(k == "egw_cancel_drop")
{
continue;
}
// Add some choices - need to be a copy, or they interfere with
// the original
//replace jQuery with spread operator
// set the Prototype of the copy set_onExecute is not available otherwise
@ -841,7 +849,7 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
drop_clone.set_onExecute(paste_exec);
parent.children.push(drop_clone);
parent.allowOnMultiple = paste_action.allowOnMultiple && drop_clone.allowOnMultiple;
_links[k] = jQuery.extend({}, drop[k]);
_links[k] = Object.assign({}, drop[k]);
_links[k].actionObj = drop_clone;
// Drop is allowed if clipboard types intersect drop types

View File

@ -128,6 +128,11 @@ export class egwMenu
{
}
remove()
{
this.instance?.remove();
}
/**
* The private _checkImpl function checks whether a menu implementation is available.
*

View File

@ -1072,7 +1072,7 @@ export class filemanagerAPP extends EgwApp
actions.push({
id:_action.id+'_current', caption: current_dir, path: current_dir,
enabled: dir && dir.data && dir.data.class && dir.data.class.indexOf('noEdit') === -1 ||
!dir && path_widget && !path_widget.options?.readonly
!dir && path_widget && !path_widget.readonly
});
// Target, if directory
@ -1137,6 +1137,15 @@ export class filemanagerAPP extends EgwApp
_action.getActionById(actions[i].id).set_onExecute(paste_exec);
}
// Changing the actions like this is incompatible with re-using the menu
// Remove the menu it so it will be re-generated
const nm_action = _action.getManager();
if(actions.length > 0 && nm_action && nm_action.data?.menu)
{
nm_action.data.menu.remove();
delete nm_action.data.menu;
}
return actions.length > 0;
}