Implements DND for et2-select-email tags

This commit is contained in:
Hadi Nategh 2022-06-21 16:21:23 +02:00
parent 1894a19a86
commit ac171a1076
6 changed files with 89 additions and 190 deletions

View File

@ -10,6 +10,7 @@
import {Et2Select} from "./Et2Select"; import {Et2Select} from "./Et2Select";
import {css} from "@lion/core"; import {css} from "@lion/core";
import {IsEmail} from "../Validators/IsEmail"; import {IsEmail} from "../Validators/IsEmail";
import interact from "@interactjs/interact";
export class Et2SelectEmail extends Et2Select 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[]) constructor(...args : any[])
{ {
super(...args); super(...args);
@ -46,6 +59,7 @@ export class Et2SelectEmail extends Et2Select
this.searchUrl = "EGroupware\\Api\\Etemplate\\Widget\\Taglist::ajax_email"; this.searchUrl = "EGroupware\\Api\\Etemplate\\Widget\\Taglist::ajax_email";
this.allowFreeEntries = true; this.allowFreeEntries = true;
this.editModeEnabled = true; this.editModeEnabled = true;
this.allowDragAndDrop = false;
this.multiple = true; this.multiple = true;
this.defaultValidators.push(new IsEmail()); this.defaultValidators.push(new IsEmail());
} }
@ -94,6 +108,44 @@ export class Et2SelectEmail extends Et2Select
results.forEach(r => r.value = r.id); results.forEach(r => r.value = r.id);
super.processRemoteResults(results); 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 // @ts-ignore TypeScript is not recognizing that this widget is a LitElement

View File

@ -12,6 +12,7 @@ import {css, html, LitElement, render, repeat, SlotMixin} from "@lion/core";
import {cleanSelectOptions, SelectOption} from "./FindSelectOptions"; import {cleanSelectOptions, SelectOption} from "./FindSelectOptions";
import {Validator} from "@lion/form-core"; import {Validator} from "@lion/form-core";
import {Et2Tag} from "./Tag/Et2Tag"; import {Et2Tag} from "./Tag/Et2Tag";
import interact from "@interactjs/interact";
// Otherwise import gets stripped // Otherwise import gets stripped
let keep_import : Et2Tag; let keep_import : Et2Tag;
@ -441,8 +442,33 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
this.addEventListener("sl-blur", this._handleSearchAbort); this.addEventListener("sl-blur", this._handleSearchAbort);
this.addEventListener("sl-select", this._handleSelect); this.addEventListener("sl-select", this._handleSelect);
this.addEventListener("sl-clear", this._handleClear) this.addEventListener("sl-clear", this._handleClear)
this._searchButtonNode.addEventListener("click", this._handleSearchButtonClick); 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() protected _unbindListeners()

View File

@ -248,10 +248,10 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
// Clear existing // Clear existing
this.div.empty(); this.div.empty();
this.actionbox this.actionbox
.removeClass('et2_toolbarDropArea') .removeClass('et2_dropZone')
.empty(); .empty();
this.actionlist this.actionlist
.removeClass('et2_toolbarDropArea') .removeClass('et2_dropZone')
.empty(); .empty();
let admin_setting = this.options.is_admin ? '<span class="toolbar-admin-pref" title="'+egw.lang('Admin settings')+' ..."></span>': ''; let admin_setting = this.options.is_admin ? '<span class="toolbar-admin-pref" title="'+egw.lang('Admin settings')+' ..."></span>': '';
const list_header = this.options.list_header == 'more'?true:false; 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) ondragenter: function(e)
{ {
e.target.classList.add('et2_toolbarDropArea'); e.target.classList.add('et2_dropZone');
}, },
ondragleave: function(e) 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`]; 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) ondragenter: function(e)
{ {
e.target.classList.add('et2_toolbarDropArea'); e.target.classList.add('et2_dropZone');
}, },
ondragleave: function(e) ondragleave: function(e)
{ {
e.target.classList.remove('et2_toolbarDropArea'); e.target.classList.remove('et2_dropZone');
} }
}); });
}); });

View File

@ -3132,7 +3132,7 @@ div.et2_toolbar_more h.ui-accordion-header {
min-width: 50px; min-width: 50px;
} }
.et2_toolbarDropArea { .et2_dropZone {
border: 2px dashed #d3d3d3 !important; border: 2px dashed #d3d3d3 !important;
padding: 0px !important; padding: 0px !important;
background: #effaff !important; background: #effaff !important;

View File

@ -344,9 +344,6 @@ app.classes.mail = AppJS.extend(
// Init key handler // Init key handler
this.init_keyHandler(); this.init_keyHandler();
//Call drag_n_drop initialization for emails on compose
this.init_dndCompose();
// Set focus on To/body field // Set focus on To/body field
// depending on To field value // depending on To field value
var to = this.et2.getWidgetById('to'); 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;i<fromDistLists.length;i++)
{
if (distItemId == fromDistLists[i]['id'])
{
draggedValue = fromDistLists[i];
// dist list item index
dValueKey = fromDistLists[i]['id'];
}
}
}
}
if (typeof widget != 'undefined')
{
emails = widget.get_value();
if (emails) emails = emails.concat([draggedValue]);
// Resolve the dist list and normal emails
distLists = resolveDistList(widget, emails);
// Add normal emails
if (emails) widget.set_value(emails);
// check if there's any dist list to be added
if (distLists.length>0) 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<dist.length;i++)
{
if (dist[i]['id'] == _value) dist.splice(i,1);
}
_widget.taglist.addToSelection(dist);
}
}
else
{
return false;
}
}
return true;
};
/**
* Resolve taglist widget which has distribution list
*
* @param {type} _widget
* @param {type} _emails
* @returns {Array} returns an array of distribution lists in selected widget
*/
var resolveDistList = function (_widget, _emails)
{
var list = [];
var selectedList = _widget.taglist.getSelection();
// Make a list of distribution list from the selection
for (var i=0;i<selectedList.length;i++)
{
if (!isNaN(selectedList[i]['id']) && selectedList[i]['class'] === 'mailinglist')
{
list.push(selectedList[i]);
}
}
// Remove dist list from emails list
for(var key in _emails)
{
if (!isNaN(_emails[key]))
{
_emails.splice(key,1);
}
}
// returns distlist
return list;
};
},
/** /**
* Check sharing mode and disable not available options * Check sharing mode and disable not available options
* *

View File

@ -59,7 +59,7 @@
</row> </row>
<row class="mailComposeHeaders" > <row class="mailComposeHeaders" >
<description value="To"/> <description value="To"/>
<taglist-email id="to" width="100%" onclick="app.mail.address_click" multiple="true" include_lists="true" onchange="app.mail.recipients_onchange" empty_label="select or insert email address"/> <taglist-email id="to" allowDragAndDrop="true" width="100%" onclick="app.mail.address_click" multiple="true" include_lists="true" onchange="app.mail.recipients_onchange" empty_label="select or insert email address"/>
<hbox> <hbox>
<description id="cc_expander" value="Cc" class="et2_button_text" onclick="app.mail.compose_fieldExpander"/> <description id="cc_expander" value="Cc" class="et2_button_text" onclick="app.mail.compose_fieldExpander"/>
<description id="bcc_expander" value="Bcc" class="et2_button_text" onclick="app.mail.compose_fieldExpander"/> <description id="bcc_expander" value="Bcc" class="et2_button_text" onclick="app.mail.compose_fieldExpander"/>
@ -67,12 +67,12 @@
</row> </row>
<row class="mailComposeHeaders mailComposeJQueryCc"> <row class="mailComposeHeaders mailComposeJQueryCc">
<description value="Cc"/> <description value="Cc"/>
<taglist-email id="cc" width="100%" onclick="app.mail.address_click" multiple="true" include_lists="true" onchange="app.mail.recipients_onchange" empty_label="select or insert email address"/> <taglist-email id="cc" width="100%" allowDragAndDrop="true" onclick="app.mail.address_click" multiple="true" include_lists="true" onchange="app.mail.recipients_onchange" empty_label="select or insert email address"/>
<description/> <description/>
</row> </row>
<row class="mailComposeHeaders mailComposeJQueryBcc"> <row class="mailComposeHeaders mailComposeJQueryBcc">
<description value="Bcc"/> <description value="Bcc"/>
<taglist-email id="bcc" width="100%" onclick="app.mail.address_click" multiple="true" include_lists="true" onchange="app.mail.recipients_onchange" empty_label="select or insert email address"/> <taglist-email id="bcc" width="100%" allowDragAndDrop="true" onclick="app.mail.address_click" multiple="true" include_lists="true" onchange="app.mail.recipients_onchange" empty_label="select or insert email address"/>
<description/> <description/>
</row> </row>
<row class="mailComposeHeaders"> <row class="mailComposeHeaders">