forked from extern/egroupware
Implements DND for et2-select-email tags
This commit is contained in:
parent
1894a19a86
commit
ac171a1076
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
|
179
mail/js/app.js
179
mail/js/app.js
@ -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
|
||||||
*
|
*
|
||||||
|
@ -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">
|
||||||
|
Loading…
Reference in New Issue
Block a user