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 {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 {egwBitIsSet} from "./egw_action_common";
import {SlTreeItem} from "@shoelace-style/shoelace"; import {SlTreeItem} from "@shoelace-style/shoelace";
import {EgwActionObject} from "./EgwActionObject"; import {FindActionTarget} from "../etemplate/FindActionTarget";
export const EXPAND_FOLDER_ON_DRAG_DROP_TIMEOUT = 1000 export const EXPAND_FOLDER_ON_DRAG_DROP_TIMEOUT = 1000
@ -23,7 +23,7 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
tree: Et2Tree; tree: Et2Tree;
// Reference to the widget that's handling actions for us // Reference to the widget that's handling actions for us
public findActionTargetHandler : EgwActionObject; public findActionTargetHandler : FindActionTarget;
private timeout : ReturnType<typeof setTimeout>; private timeout : ReturnType<typeof setTimeout>;
@ -32,12 +32,13 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
super(); super();
this.tree = _tree 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) if(egw_event == EGW_AI_DRAG_ENTER)
{ {
target.target.classList.add("draggedOver", "drop-hover"); target.target.classList.add("draggedOver", "drop-hover");
@ -54,10 +55,6 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
target.target.classList.remove("draggedOver", "drop-hover"); target.target.classList.remove("draggedOver", "drop-hover");
clearTimeout(this.timeout) clearTimeout(this.timeout)
} }
else
{
debugger;
}
return true return true
} }
@ -71,16 +68,6 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
{ {
const target = this.tree.shadowRoot.querySelector("[id='" + this.stateChangeContext.id + "']"); 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)) if(target && egwBitIsSet(_state, EGW_AO_STATE_FOCUSED))
{ {
target.focus(); target.focus();
@ -98,6 +85,11 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
} }
} }
getWidget()
{
return this.tree;
}
doGetDOMNode() doGetDOMNode()
{ {
return this.tree; return this.tree;

View File

@ -46,7 +46,10 @@ export class EgwDropActionImplementation implements EgwActionImplementation {
{ {
_aoi.handlers = {}; _aoi.handlers = {};
} }
if(typeof _aoi.handlers[this.type] == "undefined")
{
_aoi.handlers[this.type] = []; _aoi.handlers[this.type] = [];
}
node.classList.add('et2dropzone'); node.classList.add('et2dropzone');
const dragover = (event)=> { const dragover = (event)=> {
if (event.preventDefault) { if (event.preventDefault) {
@ -230,23 +233,6 @@ export class EgwDropActionImplementation implements EgwActionImplementation {
return false; 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) if(_aoi.handlers[this.type].length == 0)
{ {
// DND Event listeners // DND Event listeners

View File

@ -41,7 +41,18 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
_aoi.findActionTargetHandler = parentNode; _aoi.findActionTargetHandler = parentNode;
isNew = true; isNew = true;
} }
if(typeof _aoi.handlers == "undefined")
{
_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(isNew)
{ {
//if a parent is available the context menu Event-listener will only be bound once on the parent //if a parent is available the context menu Event-listener will only be bound once on the parent
@ -56,6 +67,7 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
this._registerContext(node, _callback, _context); this._registerContext(node, _callback, _context);
return true; return true;
} }
}
return false; return false;
}; };
@ -64,6 +76,13 @@ export class EgwPopupActionImplementation implements EgwActionImplementation {
const node = _aoi.getDOMNode(); const node = _aoi.getDOMNode();
//TODO jQuery replacement //TODO jQuery replacement
jQuery(node).off(); 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 return true
}; };

View File

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

View File

@ -13,7 +13,7 @@ import {EgwActionObject} from "../../egw_action/EgwActionObject";
import {EgwAction} from "../../egw_action/EgwAction"; import {EgwAction} from "../../egw_action/EgwAction";
import {EgwDragDropShoelaceTree} from "../../egw_action/EgwDragDropShoelaceTree"; import {EgwDragDropShoelaceTree} from "../../egw_action/EgwDragDropShoelaceTree";
import {FindActionTarget} from "../FindActionTarget"; 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 & { export type TreeItemData = SelectOption & {
focused?: boolean; focused?: boolean;
@ -849,9 +849,8 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
const typeMap = { const typeMap = {
dragenter: EGW_AI_DRAG_ENTER, dragenter: EGW_AI_DRAG_ENTER,
dragleave: EGW_AI_DRAG_OUT, 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() protected async finishedLazyLoading()
@ -946,7 +945,6 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
this.getDomNode(parentNode.id).loading = false this.getDomNode(parentNode.id).loading = false
} }
this.requestUpdate("_selectOptions") this.requestUpdate("_selectOptions")
this._link_actions(this.actions)
}) })
} }
@ -1102,16 +1100,6 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
itemAO.remove(); 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; return results;
}); });
} }
@ -1130,19 +1118,22 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
// Get the top level element for the tree // Get the top level element for the tree
let objectManager = egw_getAppObjectManager(true); let objectManager = egw_getAppObjectManager(true);
this.widget_object = objectManager.getObjectById(this.id); 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) if (this.widget_object == null)
{ {
// Add a new container to the object manager which will hold the widget // Add a new container to the object manager which will hold the widget
// objects // objects
this.widget_object = objectManager.insertObject(false, new EgwActionObject( this.widget_object = objectManager.insertObject(false, new EgwActionObject(
//@ts-ignore //@ts-ignore
this.id, objectManager, (new et2_action_object_impl(this, this)).getAOI(), this.id, objectManager, ao_impl.getAOI(),
this._actionManager || objectManager.manager.getActionById(this.id) || objectManager.manager this._actionManager || objectManager.manager.getActionById(this.id) || objectManager.manager,
EGW_AO_FLAG_IS_CONTAINER
)); ));
} else } else
{ {
// @ts-ignore // @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 // Delete all old objects
@ -1153,41 +1144,6 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
// 'allowed' for this widget at this time // 'allowed' for this widget at this time
var action_links = this._get_action_links(actions); var action_links = this._get_action_links(actions);
this.widget_object.updateActionLinks(action_links); 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 * @param _event the click event
* @returns { target:SlTreeItem, action:EgwActionObject } * @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); 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) 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}; return {target: target, action: action};
} }
} }