Further fixes on actions

- fixed some duplicate binding causing long delay on tree context menu & general weirdness
- remove some unneeded stuff
This commit is contained in:
nathan 2024-10-02 15:31:52 -06:00
parent ad3a7c16a9
commit 557e5bd236
5 changed files with 80 additions and 111 deletions

View File

@ -12,7 +12,7 @@ import {Et2Tree} from "../etemplate/Et2Tree/Et2Tree";
import {EGW_AI_DRAG_ENTER, EGW_AI_DRAG_OUT, EGW_AO_STATE_FOCUSED, EGW_AO_STATE_SELECTED} from "./egw_action_constants";
import {egwBitIsSet} from "./egw_action_common";
import {SlTreeItem} from "@shoelace-style/shoelace";
import {EgwActionObject} from "./EgwActionObject";
import {FindActionTarget} from "../etemplate/FindActionTarget";
export const EXPAND_FOLDER_ON_DRAG_DROP_TIMEOUT = 1000
@ -23,7 +23,7 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
tree: Et2Tree;
// Reference to the widget that's handling actions for us
public findActionTargetHandler : EgwActionObject;
public findActionTargetHandler : FindActionTarget;
private timeout : ReturnType<typeof setTimeout>;
@ -32,12 +32,13 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
super();
this.tree = _tree
this.findActionTargetHandler = _tree.widget_object;
this.findActionTargetHandler = _tree;
}
public doTriggerEvent(egw_event : number, dom_event : Event)
public doTriggerEvent(egw_event : number, data : any)
{
const target = this.tree.findActionTarget(dom_event);
let dom_event = data.event ?? data;
const target = this.findActionTargetHandler.findActionTarget(dom_event);
if(egw_event == EGW_AI_DRAG_ENTER)
{
target.target.classList.add("draggedOver", "drop-hover");
@ -54,10 +55,6 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
target.target.classList.remove("draggedOver", "drop-hover");
clearTimeout(this.timeout)
}
else
{
debugger;
}
return true
}
@ -71,16 +68,6 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
{
const target = this.tree.shadowRoot.querySelector("[id='" + this.stateChangeContext.id + "']");
// Just set the attribute, we're not changing the tree value
// The selected attribute will be reset by the tree next render()
if(target && egwBitIsSet(_state, EGW_AO_STATE_SELECTED))
{
target.setAttribute("selected", "");
}
else if(target)
{
target.removeAttribute("selected");
}
if(target && egwBitIsSet(_state, EGW_AO_STATE_FOCUSED))
{
target.focus();
@ -98,6 +85,11 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
}
}
getWidget()
{
return this.tree;
}
doGetDOMNode()
{
return this.tree;

View File

@ -46,7 +46,10 @@ export class EgwDropActionImplementation implements EgwActionImplementation {
{
_aoi.handlers = {};
}
_aoi.handlers[this.type] = [];
if(typeof _aoi.handlers[this.type] == "undefined")
{
_aoi.handlers[this.type] = [];
}
node.classList.add('et2dropzone');
const dragover = (event)=> {
if (event.preventDefault) {
@ -229,24 +232,7 @@ export class EgwDropActionImplementation implements EgwActionImplementation {
event.preventDefault();
return false;
};
// Bind events on parent, if provided, instead of individual node
if(_aoi.findActionTargetHandler)
{
// But only bind once
if(parentAO && !parentAO.iface.handlers[this.type])
{
parentAO.iface.handlers[this.type] = parentAO.iface.handlers[this.type] ?? [];
// Swap objects, bind down below
_aoi = parentAO.iface;
node = parentAO.iface.getDOMNode();
}
else
{
return true;
}
}
if(_aoi.handlers[this.type].length == 0)
{
// DND Event listeners

View File

@ -41,20 +41,32 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
_aoi.findActionTargetHandler = parentNode;
isNew = true;
}
if(isNew)
{
//if a parent is available the context menu Event-listener will only be bound once on the parent
this._registerDefault(parentNode, _callback, parentAO);
this._registerContext(parentNode, _callback, parentAO);
return true;
}
else if(node && !parentNode)
if(typeof _aoi.handlers == "undefined")
{
this._registerDefault(node, _callback, _context);
this._registerContext(node, _callback, _context);
return true;
_aoi.handlers = {};
}
if(typeof _aoi.handlers[this.type] == "undefined")
{
_aoi.handlers[this.type] = [];
}
if(_aoi.handlers[this.type].length == 0)
{
_aoi.handlers[this.type].push({type: 'contextmenu', listener: _callback});
if(isNew)
{
//if a parent is available the context menu Event-listener will only be bound once on the parent
this._registerDefault(parentNode, _callback, parentAO);
this._registerContext(parentNode, _callback, parentAO);
return true;
}
else if(node && !parentNode)
{
this._registerDefault(node, _callback, _context);
this._registerContext(node, _callback, _context);
return true;
}
}
return false;
@ -64,6 +76,13 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
const node = _aoi.getDOMNode();
//TODO jQuery replacement
jQuery(node).off();
// Unregister handlers
if(_aoi.handlers)
{
_aoi.handlers[this.type]?.forEach(h => node.removeEventListener(h.type, h.listener));
delete _aoi.handlers[this.type];
}
return true
};

View File

@ -133,7 +133,11 @@ export class EgwDragActionImplementation implements EgwActionImplementation {
{
_aoi.handlers = {};
}
_aoi.handlers[this.type] = [];
if(typeof _aoi.handlers[this.type] == "undefined")
{
_aoi.handlers[this.type] = [];
}
// Prevent selection
node.onselectstart = function () {
return false;
@ -150,7 +154,13 @@ export class EgwDragActionImplementation implements EgwActionImplementation {
return;
}
// Bind mouse handlers
if(_aoi.handlers[this.type].length !== 0)
{
// Already bound
return;
}
// Bind mouse handlers
//et2_dataview_view_aoi binds mousedown event in et2_dataview_rowAOI to "egwPreventSelect" function from egw_action_common via jQuery.mousedown
//jQuery(node).off("mousedown",egwPreventSelect)
//et2_dataview_view_aoi binds mousedown event in et2_dataview_rowAOI to "egwPreventSelect" function from egw_action_common via addEventListener
@ -291,12 +301,11 @@ export class EgwDragActionImplementation implements EgwActionImplementation {
};
// Drag Event listeners
node.addEventListener('dragstart', dragstart, false);
node.addEventListener('dragstart', dragstart, false);
_aoi.handlers[this.type].push({type: 'dragstart', listener: dragstart});
node.addEventListener('dragend', dragend, false);
node.addEventListener('dragend', dragend, false);
_aoi.handlers[this.type].push({type: 'dragend', listener: dragend});
return true;
}
return false;

View File

@ -13,7 +13,7 @@ import {EgwActionObject} from "../../egw_action/EgwActionObject";
import {EgwAction} from "../../egw_action/EgwAction";
import {EgwDragDropShoelaceTree} from "../../egw_action/EgwDragDropShoelaceTree";
import {FindActionTarget} from "../FindActionTarget";
import {EGW_AI_DRAG_ENTER, EGW_AI_DRAG_OUT} from "../../egw_action/egw_action_constants";
import {EGW_AI_DRAG_ENTER, EGW_AI_DRAG_OUT, EGW_AO_FLAG_IS_CONTAINER} from "../../egw_action/egw_action_constants";
export type TreeItemData = SelectOption & {
focused?: boolean;
@ -849,9 +849,8 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
const typeMap = {
dragenter: EGW_AI_DRAG_ENTER,
dragleave: EGW_AI_DRAG_OUT,
drop: EGW_AI_DRAG_OUT,
}
this.widget_object.getObjectById(id).iface.triggerEvent(typeMap[event.type], event);
this.widget_object.iface.triggerEvent(typeMap[event.type] ?? event.type, event);
}
protected async finishedLazyLoading()
@ -946,7 +945,6 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
this.getDomNode(parentNode.id).loading = false
}
this.requestUpdate("_selectOptions")
this._link_actions(this.actions)
})
}
@ -1102,16 +1100,6 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
itemAO.remove();
}
// Need the DOM nodes to actually link the actions
this.updateComplete.then(() =>
{
this.linkLeafActions(
parentAO ?? this.widget_object,
_item,
this._get_action_links(this.actions)
);
});
return results;
});
}
@ -1130,19 +1118,22 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
// Get the top level element for the tree
let objectManager = egw_getAppObjectManager(true);
this.widget_object = objectManager.getObjectById(this.id);
const ao_impl = new et2_action_object_impl(this, this);
ao_impl.aoi = new EgwDragDropShoelaceTree(this);
if (this.widget_object == null)
{
// Add a new container to the object manager which will hold the widget
// objects
this.widget_object = objectManager.insertObject(false, new EgwActionObject(
//@ts-ignore
this.id, objectManager, (new et2_action_object_impl(this, this)).getAOI(),
this._actionManager || objectManager.manager.getActionById(this.id) || objectManager.manager
this.id, objectManager, ao_impl.getAOI(),
this._actionManager || objectManager.manager.getActionById(this.id) || objectManager.manager,
EGW_AO_FLAG_IS_CONTAINER
));
} else
{
// @ts-ignore
this.widget_object.setAOI((new et2_action_object_impl(this, this)).getAOI());
this.widget_object.setAOI(ao_impl.getAOI());
}
// Delete all old objects
@ -1153,41 +1144,6 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
// 'allowed' for this widget at this time
var action_links = this._get_action_links(actions);
this.widget_object.updateActionLinks(action_links);
//Drop target enabeling
if (typeof this._selectOptions != 'undefined')
{
let self: Et2Tree = this
// Iterate over the options (leaves) and add action to each one
for (const selectOption of this._selectOptions)
{
this.linkLeafActions(this.widget_object, selectOption, action_links)
}
}
}
/**
* Add actions on a leaf
*
* @param {EgwActionObject} parentActionObject
* @param {TreeItemData} option
* @param {string[]} action_links
* @protected
*/
protected linkLeafActions(parentActionObject : EgwActionObject, option : TreeItemData, action_links : string[])
{
// Add a new action object to the object manager
let id = option.value ?? (typeof option.id == 'number' ? String(option.id) : option.id);
// @ts-ignore
let obj : EgwActionObject = parentActionObject.addObject(id, new EgwDragDropShoelaceTree(this, id));
obj.findActionTargetHandler = this;
obj.updateActionLinks(action_links);
const children = <TreeItemData[]><unknown>(option.children ?? option.item) ?? [];
for(let i = 0; i < children.length; i++)
{
this.linkLeafActions(obj, children[i], action_links);
}
}
/**
@ -1280,7 +1236,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
}
/**
* returns the closest SlItem to the click position, and the corresponding EgwActionObject
* returns the closest SlTreeItem to the click position, and the corresponding EgwActionObject
* @param _event the click event
* @returns { target:SlTreeItem, action:EgwActionObject }
*/
@ -1292,11 +1248,18 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
});
let action : EgwActionObject = this.widget_object.getObjectById(target.id);
// Create on the fly if not there?
// Create on the fly if not there? Action handlers might need the EgwActionObject
if(!action)
{
debugger;
// NOTE: FLAT object structure under the tree ActionObject to avoid nested selection
action = this.widget_object.addObject(target.id, this.widget_object.iface);
// Required to get dropped accepted, but also re-binds
action.updateActionLinks(this._get_action_links(this.actions));
}
// This is just the action system, which we override
this.widget_object.setAllSelected(false);
action.setSelected(true);
return {target: target, action: action};
}
}