Fix Et2Tree dragging of tree items

This commit is contained in:
nathan 2024-10-18 13:28:57 -06:00
parent 853d6ddd89
commit eb105dfad4
4 changed files with 61 additions and 14 deletions

View File

@ -9,7 +9,13 @@
*/ */
import {egwActionObjectInterface} from "./egw_action"; import {egwActionObjectInterface} from "./egw_action";
import {Et2Tree} from "../etemplate/Et2Tree/Et2Tree"; 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 {egwBitIsSet} from "./egw_action_common";
import {SlTreeItem} from "@shoelace-style/shoelace"; import {SlTreeItem} from "@shoelace-style/shoelace";
import {FindActionTarget} from "../etemplate/FindActionTarget"; import {FindActionTarget} from "../etemplate/FindActionTarget";
@ -62,6 +68,10 @@ export class EgwDragDropShoelaceTree extends egwActionObjectInterface{
target.target.classList.remove("draggedOver", "drop-hover"); target.target.classList.remove("draggedOver", "drop-hover");
clearTimeout(this.timeouts[target.target.id]) clearTimeout(this.timeouts[target.target.id])
} }
else if(egw_event == EGW_AI_DRAG)
{
target.action.setSelected(true);
}
return true return true
} }

View File

@ -103,6 +103,7 @@ export class EgwDropActionImplementation implements EgwActionImplementation {
if (!self.getTheDraggedDOM()) return; if (!self.getTheDraggedDOM()) return;
let dropActionObject = _context; let dropActionObject = _context;
const helper = self.getHelperDOM();
// remove the hover class // remove the hover class
this.classList.remove('drop-hover'); this.classList.remove('drop-hover');
@ -111,16 +112,23 @@ export class EgwDropActionImplementation implements EgwActionImplementation {
{ {
dropActionObject = this.findActionTarget(event).action ?? _context; 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(); let ui = self.getTheDraggedData();
ui.position = {top: event.clientY, left: event.clientX}; ui.position = {top: event.clientY, left: event.clientX};
ui.offset = {top: event.offsetY, left: event.offsetX}; 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))
if(!self.isAccepted(data, dropActionObject, _callback, undefined) || self.isTheDraggedDOM(this))
{ {
// clean up the helper dom // clean up the helper dom
if (helper) helper.remove(); if (helper) helper.remove();

View File

@ -46,10 +46,10 @@ export class EgwDragActionImplementation implements EgwActionImplementation {
_selected[0]._context?._selectionMgr?._total : _selected.length; _selected[0]._context?._selectionMgr?._total : _selected.length;
// Clone nodes but use copy webComponent properties // 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 // 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; return;
} }
@ -65,7 +65,8 @@ export class EgwDragActionImplementation implements EgwActionImplementation {
// Children // Children
node.childNodes.forEach(c => 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) if(child)
{ {
clone.appendChild(child); clone.appendChild(child);
@ -80,7 +81,12 @@ export class EgwDragActionImplementation implements EgwActionImplementation {
for(const egwActionObject of _selected) 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) if(row)
{ {
rows.push(row); rows.push(row);
@ -191,14 +197,20 @@ export class EgwDragActionImplementation implements EgwActionImplementation {
const ai = this const ai = this
const dragstart = function (event) { 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 // is evoked. Call the given callback function. The callback
// function will gather the selected elements and action links // function will gather the selected elements and action links
// and call the doExecuteImplementation function. This // and call the doExecuteImplementation function. This
// will call the onExecute function of the first action // will call the onExecute function of the first action
// in order to obtain the helper object (stored in ai.helper) // in order to obtain the helper object (stored in ai.helper)
// and the multiple dragDropTypes (ai.ddTypes) // 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 // Stop parent elements from also starting to drag if we're nested
if(ai.selected.length) if(ai.selected.length)
@ -208,9 +220,12 @@ export class EgwDragActionImplementation implements EgwActionImplementation {
if(action && egw.app_name() == 'filemanager') 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; const selected = ai.selected;
// Set file data // Set file data

View File

@ -13,7 +13,12 @@ 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, 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"; import styles from "./Et2Tree.styles";
export type TreeItemData = SelectOption & { export type TreeItemData = SelectOption & {
@ -789,6 +794,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
//console.log(event.type, id, event.target); //console.log(event.type, id, event.target);
const typeMap = { const typeMap = {
dragstart: EGW_AI_DRAG,
dragenter: EGW_AI_DRAG_ENTER, dragenter: EGW_AI_DRAG_ENTER,
dragleave: EGW_AI_DRAG_OUT, dragleave: EGW_AI_DRAG_OUT,
} }
@ -926,6 +932,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
} }
const value = selectOption.value ?? selectOption.id; const value = selectOption.value ?? selectOption.id;
const selected = typeof this.value == "string" && this.value == value || Array.isArray(this.value) && this.value.includes(value); 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` return html`
<sl-tree-item <sl-tree-item
@ -940,6 +947,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement) implements Fin
?disabled=${selectOption.disabled} ?disabled=${selectOption.disabled}
?lazy=${lazy} ?lazy=${lazy}
?focused=${selectOption.focused || nothing} ?focused=${selectOption.focused || nothing}
draggable=${draggable}
@click=${async(event) => @click=${async(event) =>
{ {
// Don't react to expand or children // 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);}} @dragenter=${(event) => {this.handleDragEvent(event);}}
@dragleave=${(event) => {this.handleDragEvent(event);}} @dragleave=${(event) => {this.handleDragEvent(event);}}
@drop=${(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 => { let target = e.composedPath().find(element => {
return element.tagName == "SL-TREE-ITEM" return element.tagName == "SL-TREE-ITEM"
}); });
if(!target)
{
return {target: null, action: null};
}
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? Action handlers might need the EgwActionObject // 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 // NOTE: FLAT object structure under the tree ActionObject to avoid nested selection
action = this.widget_object.addObject(target.id, this.widget_object.iface) action = this.widget_object.addObject(target.id, this.widget_object.iface)
action._context = target;
action.setSelected = (set) => action.setSelected = (set) =>
{ {
target.action_selected = set; target.action_selected = set;