From eb105dfad46df60957dc012385733151fedaffeb Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 18 Oct 2024 13:28:57 -0600 Subject: [PATCH] Fix Et2Tree dragging of tree items --- api/js/egw_action/EgwDragDropShoelaceTree.ts | 12 ++++++- .../egw_action/EgwDropActionImplementation.ts | 16 +++++++--- .../egw_action/egwDragActionImplementation.ts | 31 ++++++++++++++----- api/js/etemplate/Et2Tree/Et2Tree.ts | 16 +++++++++- 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/api/js/egw_action/EgwDragDropShoelaceTree.ts b/api/js/egw_action/EgwDragDropShoelaceTree.ts index bd6f998993..21e76e407b 100644 --- a/api/js/egw_action/EgwDragDropShoelaceTree.ts +++ b/api/js/egw_action/EgwDragDropShoelaceTree.ts @@ -9,7 +9,13 @@ */ import {egwActionObjectInterface} from "./egw_action"; 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, + 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 {FindActionTarget} from "../etemplate/FindActionTarget"; @@ -62,6 +68,10 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{ target.target.classList.remove("draggedOver", "drop-hover"); clearTimeout(this.timeouts[target.target.id]) } + else if(egw_event == EGW_AI_DRAG) + { + target.action.setSelected(true); + } return true } diff --git a/api/js/egw_action/EgwDropActionImplementation.ts b/api/js/egw_action/EgwDropActionImplementation.ts index 0f63c7f66e..8bc460b591 100644 --- a/api/js/egw_action/EgwDropActionImplementation.ts +++ b/api/js/egw_action/EgwDropActionImplementation.ts @@ -103,6 +103,7 @@ export class EgwDropActionImplementation implements EgwActionImplementation { if (!self.getTheDraggedDOM()) return; let dropActionObject = _context; + const helper = self.getHelperDOM(); // remove the hover class this.classList.remove('drop-hover'); @@ -111,16 +112,23 @@ export class EgwDropActionImplementation implements EgwActionImplementation { { dropActionObject = this.findActionTarget(event).action ?? _context; } + else if(self.isTheDraggedDOM(this)) + { + // clean up the helper dom + if(helper) + { + helper.remove(); + } + return; + } - const helper = self.getHelperDOM(); let ui = self.getTheDraggedData(); ui.position = {top: event.clientY, left: event.clientX}; ui.offset = {top: event.offsetY, left: event.offsetX}; + let data = JSON.parse(event.dataTransfer.getData('application/json')); - let data = JSON.parse(event.dataTransfer.getData('application/json')); - - if(!self.isAccepted(data, dropActionObject, _callback, undefined) || self.isTheDraggedDOM(this)) + if(!self.isAccepted(data, dropActionObject, _callback, undefined)) { // clean up the helper dom if (helper) helper.remove(); diff --git a/api/js/egw_action/egwDragActionImplementation.ts b/api/js/egw_action/egwDragActionImplementation.ts index 898395c099..bb6421afb6 100644 --- a/api/js/egw_action/egwDragActionImplementation.ts +++ b/api/js/egw_action/egwDragActionImplementation.ts @@ -46,10 +46,10 @@ export class EgwDragActionImplementation implements EgwActionImplementation { _selected[0]._context?._selectionMgr?._total : _selected.length; // Clone nodes but use copy webComponent properties - const carefulClone = (node) => + const carefulClone = (node, skip_text = false) => { // Don't clone text nodes, it causes duplication in et2-description - if(node.nodeType == node.TEXT_NODE) + if(skip_text && node.nodeType == node.TEXT_NODE) { return; } @@ -65,7 +65,8 @@ export class EgwDragActionImplementation implements EgwActionImplementation { // Children node.childNodes.forEach(c => { - const child = carefulClone(c) + // Don't clone text in et2-description, it causes duplication + const child = carefulClone(c, skip_text || ["ET2-DESCRIPTION"].indexOf(c.tagName) != -1) if(child) { clone.appendChild(child); @@ -80,7 +81,12 @@ export class EgwDragActionImplementation implements EgwActionImplementation { for(const egwActionObject of _selected) { - const row : Node = carefulClone(egwActionObject.iface.getDOMNode()); + let rowNode = egwActionObject.iface.getDOMNode(); + if(egwActionObject._context && egwActionObject._context instanceof HTMLElement) + { + rowNode = egwActionObject._context; + } + const row : Node = carefulClone(rowNode); if(row) { rows.push(row); @@ -191,14 +197,20 @@ export class EgwDragActionImplementation implements EgwActionImplementation { const ai = this const dragstart = function (event) { - // The helper function is called before the start function + let dragActionObject = _context; + if(this.findActionTarget) + { + dragActionObject = this.findActionTarget(event).action ?? _context; + } + + // The helper function is called before the start function // is evoked. Call the given callback function. The callback // function will gather the selected elements and action links // and call the doExecuteImplementation function. This // will call the onExecute function of the first action // in order to obtain the helper object (stored in ai.helper) // and the multiple dragDropTypes (ai.ddTypes) - _callback.call(_context, false, ai); + _callback.call(dragActionObject, false, ai); // Stop parent elements from also starting to drag if we're nested if(ai.selected.length) @@ -208,9 +220,12 @@ export class EgwDragActionImplementation implements EgwActionImplementation { if(action && egw.app_name() == 'filemanager') { - if (_context.isSelection(event)) return; + if(dragActionObject.isSelection(event)) + { + return; + } - // Get all selected + // Get all selected const selected = ai.selected; // Set file data diff --git a/api/js/etemplate/Et2Tree/Et2Tree.ts b/api/js/etemplate/Et2Tree/Et2Tree.ts index d01e5e18ed..0631908c7c 100644 --- a/api/js/etemplate/Et2Tree/Et2Tree.ts +++ b/api/js/etemplate/Et2Tree/Et2Tree.ts @@ -13,7 +13,12 @@ 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, EGW_AO_FLAG_IS_CONTAINER} from "../../egw_action/egw_action_constants"; +import { + EGW_AI_DRAG, + EGW_AI_DRAG_ENTER, + EGW_AI_DRAG_OUT, + EGW_AO_FLAG_IS_CONTAINER +} from "../../egw_action/egw_action_constants"; import styles from "./Et2Tree.styles"; export type TreeItemData = SelectOption & { @@ -789,6 +794,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin //console.log(event.type, id, event.target); const typeMap = { + dragstart: EGW_AI_DRAG, dragenter: EGW_AI_DRAG_ENTER, dragleave: EGW_AI_DRAG_OUT, } @@ -926,6 +932,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin } const value = selectOption.value ?? selectOption.id; const selected = typeof this.value == "string" && this.value == value || Array.isArray(this.value) && this.value.includes(value); + const draggable = this.widget_object?.actionLinks?.filter(al => al.actionObj.type == "drag").length > 0 return html` { // Don't react to expand or children @@ -1036,6 +1044,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin } } + @dragstart=${(event) => {this.handleDragEvent(event);}} @dragenter=${(event) => {this.handleDragEvent(event);}} @dragleave=${(event) => {this.handleDragEvent(event);}} @drop=${(event) => {this.handleDragEvent(event);}} @@ -1218,6 +1227,10 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin let target = e.composedPath().find(element => { return element.tagName == "SL-TREE-ITEM" }); + if(!target) + { + return {target: null, action: null}; + } let action : EgwActionObject = this.widget_object.getObjectById(target.id); // Create on the fly if not there? Action handlers might need the EgwActionObject @@ -1225,6 +1238,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin { // NOTE: FLAT object structure under the tree ActionObject to avoid nested selection action = this.widget_object.addObject(target.id, this.widget_object.iface) + action._context = target; action.setSelected = (set) => { target.action_selected = set;