From ac171a1076fc5f90a12716765802188fa822110c Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Tue, 21 Jun 2022 16:21:23 +0200 Subject: [PATCH] Implements DND for et2-select-email tags --- api/js/etemplate/Et2Select/Et2SelectEmail.ts | 52 ++++++ api/js/etemplate/Et2Select/SearchMixin.ts | 28 ++- api/js/etemplate/et2_widget_toolbar.ts | 12 +- api/templates/default/etemplate2.css | 2 +- mail/js/app.js | 179 ------------------- mail/templates/default/compose.xet | 6 +- 6 files changed, 89 insertions(+), 190 deletions(-) diff --git a/api/js/etemplate/Et2Select/Et2SelectEmail.ts b/api/js/etemplate/Et2Select/Et2SelectEmail.ts index 8f70819a6c..44d033ff0b 100644 --- a/api/js/etemplate/Et2Select/Et2SelectEmail.ts +++ b/api/js/etemplate/Et2Select/Et2SelectEmail.ts @@ -10,6 +10,7 @@ import {Et2Select} from "./Et2Select"; import {css} from "@lion/core"; import {IsEmail} from "../Validators/IsEmail"; +import interact from "@interactjs/interact"; export class Et2SelectEmail extends Et2Select { @@ -39,6 +40,18 @@ export class Et2SelectEmail extends Et2Select ]; } + static get properties() + { + return { + ...super.properties, + + /** + * Allow drag and drop tags + */ + allowDragAndDrop: {type: Boolean} + } + } + constructor(...args : any[]) { super(...args); @@ -46,6 +59,7 @@ export class Et2SelectEmail extends Et2Select this.searchUrl = "EGroupware\\Api\\Etemplate\\Widget\\Taglist::ajax_email"; this.allowFreeEntries = true; this.editModeEnabled = true; + this.allowDragAndDrop = false; this.multiple = true; this.defaultValidators.push(new IsEmail()); } @@ -94,6 +108,44 @@ export class Et2SelectEmail extends Et2Select results.forEach(r => r.value = r.id); super.processRemoteResults(results); } + + /** + * override tag creation in order to add DND functionality + * @param item + * @protected + */ + protected _createTagNode(item) + { + let tag = super._createTagNode(item); + if (!this.readonly && this.allowFreeEntries && this.allowDragAndDrop) + { + let dragTranslate = {x:0,y:0}; + tag.class = item.classList.value + " et2-select-draggable"; + let draggable = interact(tag).draggable({ + startAxis: 'xy', + listeners: { + start: function(e) + { + let dragPosition = {x:e.page.x, y:e.page.y}; + e.target.setAttribute('style', `width:${e.target.clientWidth}px !important`); + e.target.style.position = 'fixed'; + e.target.style.transform = + `translate(${dragPosition.x}px, ${dragPosition.y}px)`; + }, + move : function(e) + { + dragTranslate.x += e.delta.x; + dragTranslate.y += e.delta.y; + e.target.style.transform = + `translate(${dragTranslate.x}px, ${dragTranslate.y}px)`; + } + } + }); + // set parent_node with widget context in order to make it accessible after drop + draggable.parent_node = this; + } + return tag; + } } // @ts-ignore TypeScript is not recognizing that this widget is a LitElement diff --git a/api/js/etemplate/Et2Select/SearchMixin.ts b/api/js/etemplate/Et2Select/SearchMixin.ts index b88cef16a6..c02bd3e618 100644 --- a/api/js/etemplate/Et2Select/SearchMixin.ts +++ b/api/js/etemplate/Et2Select/SearchMixin.ts @@ -12,6 +12,7 @@ import {css, html, LitElement, render, repeat, SlotMixin} from "@lion/core"; import {cleanSelectOptions, SelectOption} from "./FindSelectOptions"; import {Validator} from "@lion/form-core"; import {Et2Tag} from "./Tag/Et2Tag"; +import interact from "@interactjs/interact"; // Otherwise import gets stripped let keep_import : Et2Tag; @@ -441,8 +442,33 @@ export const Et2WithSearchMixin = >(superclass this.addEventListener("sl-blur", this._handleSearchAbort); this.addEventListener("sl-select", this._handleSelect); this.addEventListener("sl-clear", this._handleClear) - this._searchButtonNode.addEventListener("click", this._handleSearchButtonClick); + if (this.nodeName === 'ET2-SELECT-EMAIL') + { + interact(this).dropzone({ + accept: `.et2-select-draggable`, + ondrop: function(e) + { + e.target.createFreeEntry(e.draggable.target.value); + e.target.classList.remove('et2_toolbarDropArea'); + + // remove the dragged value from its origin source + e.draggable.parent_node.value = e.draggable.parent_node.value.filter(_item=>{return e.draggable.target.value !== _item;}) + + // set value for newly dropped target + e.target.value.push(e.draggable.target.value); + }, + ondragenter: function(e) + { + e.target.classList.add('et2_dropZone'); + }, + ondragleave: function(e) + { + e.target.classList.remove('et2_dropZone'); + } + }); + } + } protected _unbindListeners() diff --git a/api/js/etemplate/et2_widget_toolbar.ts b/api/js/etemplate/et2_widget_toolbar.ts index 69706dac6d..f5b98c3dd1 100644 --- a/api/js/etemplate/et2_widget_toolbar.ts +++ b/api/js/etemplate/et2_widget_toolbar.ts @@ -248,10 +248,10 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput // Clear existing this.div.empty(); this.actionbox - .removeClass('et2_toolbarDropArea') + .removeClass('et2_dropZone') .empty(); this.actionlist - .removeClass('et2_toolbarDropArea') + .removeClass('et2_dropZone') .empty(); let admin_setting = this.options.is_admin ? '': ''; const list_header = this.options.list_header == 'more'?true:false; @@ -509,11 +509,11 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput }, ondragenter: function(e) { - e.target.classList.add('et2_toolbarDropArea'); + e.target.classList.add('et2_dropZone'); }, ondragleave: function(e) { - e.target.classList.remove('et2_toolbarDropArea'); + e.target.classList.remove('et2_dropZone'); } }); const menulist = [`.et2_toolbar_dropzone_more${this.id}`, `#${this.id}-menulist`]; @@ -546,11 +546,11 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput }, ondragenter: function(e) { - e.target.classList.add('et2_toolbarDropArea'); + e.target.classList.add('et2_dropZone'); }, ondragleave: function(e) { - e.target.classList.remove('et2_toolbarDropArea'); + e.target.classList.remove('et2_dropZone'); } }); }); diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css index 92b99e9ae9..aeb7698846 100644 --- a/api/templates/default/etemplate2.css +++ b/api/templates/default/etemplate2.css @@ -3132,7 +3132,7 @@ div.et2_toolbar_more h.ui-accordion-header { min-width: 50px; } -.et2_toolbarDropArea { +.et2_dropZone { border: 2px dashed #d3d3d3 !important; padding: 0px !important; background: #effaff !important; diff --git a/mail/js/app.js b/mail/js/app.js index 563a8c608c..5728355af5 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -344,9 +344,6 @@ app.classes.mail = AppJS.extend( // Init key handler this.init_keyHandler(); - //Call drag_n_drop initialization for emails on compose - this.init_dndCompose(); - // Set focus on To/body field // depending on To field value var to = this.et2.getWidgetById('to'); @@ -5070,182 +5067,6 @@ app.classes.mail = AppJS.extend( }); }, - /** - * Initialize dropping targets for draggable emails - * - - */ - init_dndCompose: function () - { - - var self = this; - var emailTags = jQuery('#mail-compose_to,#mail-compose_cc,#mail-compose_bcc'); - //Call to make new items draggable - emailTags.hover(function(){ - self.set_dragging_dndCompose(); - }); - //Make used email-tag list widgets in mail compose droppable - emailTags.droppable({ - accept:'.ms-sel-item', - - /** - * Run after a draggable email item dropped over one of the email-taglists - * -Set the dropped item to the dropped current target widget - * - * @param {type} event - * @param {type} ui - */ - drop:function (event, ui) - { - var widget = self.et2.getWidgetById(this.getAttribute('name')); - var emails, distLists = []; - var fromWidget = {}; - - var parentWidgetDOM = ui.draggable.siblings().filter('input'); - if (parentWidgetDOM != 'undefined' && parentWidgetDOM.length > 0) - { - fromWidget = self.et2.getWidgetById(parentWidgetDOM.attr('name')); - } - - var draggedValue = ui.draggable.text(); - - // index of draggable item in selection list - var dValueKey = draggedValue; - - var distItem = ui.draggable.find('.mailinglist'); - if (distItem.length>0) - { - var distItemId = parseInt(distItem.attr('data')); - if (distItemId) - { - var fromDistLists = resolveDistList(fromWidget); - for (var i=0;i0) widget.taglist.addToSelection(distLists); - - if (!jQuery.isEmptyObject(fromWidget) - && !(ui.draggable.attr('class').search('mailCompose_copyEmail') > -1)) - { - if (widget.node != fromWidget.node && !_removeDragged(fromWidget, dValueKey)) - { - //Not successful remove, returns the item to its origin - jQuery(ui.draggable).draggable('option','revert',true); - } - } - else - { - ui.draggable - .removeClass('mailCompose_copyEmail') - .css('cursor','move'); - } - - var dragItems = jQuery('div.ms-sel-item'); - dragItems.each(function(i,item){ - var $isErr = jQuery(item).find('.ui-state-error'); - if ($isErr.length > 0) - { - delete dragItems.splice(i,1); - } - }); - } - } - }); - - /** - * Remove dragged item from the widget which the item was dragged - * - * @param {type} _widget - * @param {type} _value - * @return {boolean} true if successul | false unsuccessul - */ - var _removeDragged = function (_widget, _value) - { - if (_widget && _value) - { - var emails = _widget.get_value(); - var itemIndex = emails.indexOf(_value); - var dist = []; - if (itemIndex > -1) - { - emails.splice(itemIndex,1); - // Resolve the dist list and normal emails - var dist = resolveDistList(_widget, emails); - - // Add normal emails - _widget.set_value(emails); - - //check if there's any dist list to be added - if (dist) - { - for(var i=0;i - + @@ -67,12 +67,12 @@ - + - +