djmaze 2020-09-23 10:08:34 +02:00
parent add9cb14aa
commit 302f2847b8
3 changed files with 89 additions and 75 deletions

View file

@ -1,7 +1,28 @@
const ko = window.ko, const ko = window.ko,
rlContentType = 'application/x-rainloop-action',
validTransfer = data => ['move','copy'].includes(data.dropEffect) // effectAllowed rlContentType = 'rainloop/action',
&& data.getData('application/x-rainloop-messages');
// In Chrome we have no access to getData unless it's the 'drop' event
getDragAction = e => dragData && e.dataTransfer.types.includes(rlContentType) ? dragData.action : false,
setDragAction = (e, action, effect, data, img) => {
dragData = {
action: action,
data: data
};
e.dataTransfer.setData(rlContentType, action);
e.dataTransfer.setData('Text', rlContentType+'/'+action);
e.dataTransfer.setDragImage(img, 0, 0);
e.dataTransfer.effectAllowed = effect;
},
dragTimer = {
id: 0,
stop: () => clearTimeout(dragTimer.id),
start: fn => dragTimer.id = setTimeout(fn, 500)
};
let dragImage,
dragData;
ko.bindingHandlers.editor = { ko.bindingHandlers.editor = {
init: (element, fValueAccessor) => { init: (element, fValueAccessor) => {
@ -85,26 +106,49 @@ ko.bindingHandlers.emailsTags = {
ko.bindingHandlers.dragmessages = { ko.bindingHandlers.dragmessages = {
init: (element, fValueAccessor) => { init: (element, fValueAccessor) => {
if (!rl.settings.app('mobile')) { if (!rl.settings.app('mobile')) {
element.addEventListener("dragstart", e => fValueAccessor()(e)); element.addEventListener("dragstart", e => {
let data = fValueAccessor()(e);
dragImage || (dragImage = document.getElementById('messagesDragImage'));
if (data && dragImage) {
dragImage.querySelector('.text').textContent = data.uids.length;
let img = dragImage.querySelector('.icon-white');
img.classList.toggle('icon-copy', e.ctrlKey);
img.classList.toggle('icon-mail', !e.ctrlKey);
// Else Chrome doesn't show it
dragImage.style.left = e.clientX + 'px';
dragImage.style.top = e.clientY + 'px';
dragImage.style.right = 'auto';
setDragAction(e, 'messages', e.ctrlKey ? 'copy' : 'move', data, dragImage);
// Remove the Chrome visibility
dragImage.style.cssText = '';
} else {
e.preventDefault();
}
}, false);
element.addEventListener("dragend", () => dragData = null);
element.setAttribute('draggable', true);
} }
} }
}; };
// Drop selected messages on folder // Drop selected messages on folder
const dragTimer = {
id: 0,
stop: () => clearTimeout(this.id),
start: fn => this.id = setTimeout(fn, 500)
};
ko.bindingHandlers.dropmessages = { ko.bindingHandlers.dropmessages = {
init: (element, fValueAccessor) => { init: (element, fValueAccessor) => {
if (!rl.settings.app('mobile')) { if (!rl.settings.app('mobile')) {
const folder = fValueAccessor(), const folder = fValueAccessor(),
// folder = ko.dataFor(element), // folder = ko.dataFor(element),
fnStop = e => {
e.preventDefault();
element.classList.remove('droppableHover');
dragTimer.stop();
},
fnHover = e => { fnHover = e => {
if (validTransfer(e.dataTransfer)) { if ('messages' === getDragAction(e)) {
dragTimer.stop(); fnStop(e);
e.preventDefault();
element.classList.add('droppableHover'); element.classList.add('droppableHover');
if (folder && folder.collapsed()) { if (folder && folder.collapsed()) {
dragTimer.start(() => { dragTimer.start(() => {
@ -116,18 +160,13 @@ ko.bindingHandlers.dropmessages = {
}; };
element.addEventListener("dragenter", fnHover); element.addEventListener("dragenter", fnHover);
element.addEventListener("dragover", fnHover); element.addEventListener("dragover", fnHover);
element.addEventListener("dragleave", e => { element.addEventListener("dragleave", fnStop);
e.preventDefault();
element.classList.remove('droppableHover');
dragTimer.stop();
});
element.addEventListener("drop", e => { element.addEventListener("drop", e => {
const data = JSON.parse(validTransfer(e.dataTransfer)); fnStop(e);
if (data) { if ('messages' === getDragAction(e) && ['move','copy'].includes(e.dataTransfer.effectAllowed)) {
data.copy = data.copy && event.ctrlKey; let data = dragData.data;
e.preventDefault();
if (folder && data && data.folder && Array.isArray(data.uids)) { if (folder && data && data.folder && Array.isArray(data.uids)) {
rl.app.moveMessagesToFolder(data.folder, data.uids, folder.fullNameRaw, data.copy); rl.app.moveMessagesToFolder(data.folder, data.uids, folder.fullNameRaw, data.copy && e.ctrlKey);
} }
} }
}); });
@ -135,41 +174,40 @@ ko.bindingHandlers.dropmessages = {
} }
}; };
let sortableElement = null,
isSortableData = data => 'move' === data.dropEffect && 'sortable' === data.getData(rlContentType);
ko.bindingHandlers.sortableItem = { ko.bindingHandlers.sortableItem = {
init: (element, fValueAccessor) => { init: (element, fValueAccessor) => {
let options = ko.utils.unwrapObservable(fValueAccessor()) || {}, let options = ko.utils.unwrapObservable(fValueAccessor()) || {},
parent = element.parentNode, parent = element.parentNode,
fnHover = e => { fnHover = e => {
if (isSortableData(e.dataTransfer)) { if ('sortable' === getDragAction(e)) {
e.preventDefault(); e.preventDefault();
let node = (e.target.closest ? e.target : e.target.parentNode).closest('[draggable]'); let node = (e.target.closest ? e.target : e.target.parentNode).closest('[draggable]');
if (node && node !== sortableElement && parent.contains(node)) { if (node && node !== dragData.data && parent.contains(node)) {
let rect = node.getBoundingClientRect(); let rect = node.getBoundingClientRect();
if (rect.top + (rect.height / 2) <= e.clientY) { if (rect.top + (rect.height / 2) <= e.clientY) {
if (node.nextElementSibling !== sortableElement) { if (node.nextElementSibling !== dragData.data) {
node.after(sortableElement); node.after(dragData.data);
} }
} else if (node.previousElementSibling !== sortableElement) { } else if (node.previousElementSibling !== dragData.data) {
node.before(sortableElement); node.before(dragData.data);
} }
} }
} }
}; };
element.addEventListener("dragstart", e => { element.addEventListener("dragstart", e => {
sortableElement = element; dragData = {
e.dataTransfer.setData(rlContentType, 'sortable'); action: 'sortable',
e.dataTransfer.setDragImage(element, 0, 0); element: element
e.dataTransfer.effectAllowed = 'move'; };
setDragAction(e, 'sortable', 'move', element, element);
element.style.opacity = 0.25; element.style.opacity = 0.25;
}); });
element.addEventListener("dragend", e => { element.addEventListener("dragend", e => {
element.style.opacity = null; element.style.opacity = null;
if (isSortableData(e.dataTransfer)) { if ('sortable' === getDragAction(e)) {
let row = parent.rows[options.list.indexOf(ko.dataFor(element))]; let row = parent.rows[options.list.indexOf(ko.dataFor(element))];
if (row != sortableElement) { if (row != dragData.data) {
row.before(sortableElement); row.before(dragData.data);
} }
} }
}); });
@ -178,17 +216,18 @@ ko.bindingHandlers.sortableItem = {
parent.addEventListener("dragenter", fnHover); parent.addEventListener("dragenter", fnHover);
parent.addEventListener("dragover", fnHover); parent.addEventListener("dragover", fnHover);
parent.addEventListener("drop", e => { parent.addEventListener("drop", e => {
if (isSortableData(e.dataTransfer)) { if ('sortable' === getDragAction(e)) {
e.dataTransfer.clearData(); dragData.data.style.opacity = null;
let data = ko.dataFor(sortableElement), e.preventDefault();
let data = ko.dataFor(dragData.data),
from = options.list.indexOf(data), from = options.list.indexOf(data),
to = [...parent.children].indexOf(sortableElement); to = [...parent.children].indexOf(dragData.data);
if (from != to) { if (from != to) {
let arr = options.list(); let arr = options.list();
arr.splice(to, 0, ...arr.splice(from, 1)); arr.splice(to, 0, ...arr.splice(from, 1));
options.list(arr); options.list(arr);
} }
e.preventDefault(); dragData = null;
options.afterMove && options.afterMove(); options.afterMove && options.afterMove();
} }
}); });

View file

@ -45,8 +45,6 @@ const
canBeMovedHelper = (self) => self.canBeMoved(), canBeMovedHelper = (self) => self.canBeMoved(),
ifvisible = window.ifvisible; ifvisible = window.ifvisible;
let dragImage;
@view({ @view({
name: 'View/User/MailBox/MessageList', name: 'View/User/MailBox/MessageList',
type: ViewType.Right, type: ViewType.Right,
@ -479,39 +477,16 @@ class MessageListMailBoxUserView extends AbstractViewNext {
return false; return false;
} }
dragStart(event) { getDragData(event) {
const item = ko.dataFor(document.elementFromPoint(event.clientX, event.clientY)); const item = ko.dataFor(document.elementFromPoint(event.clientX, event.clientY));
item && item.checked(true); item && item.checked && item.checked(true);
dragImage || (dragImage = document.getElementById('messagesDragImage'));
const uids = MessageStore.messageListCheckedOrSelectedUidsWithSubMails(); const uids = MessageStore.messageListCheckedOrSelectedUidsWithSubMails();
item && !uids.includes(item.uid) && uids.push(item.uid); item && !uids.includes(item.uid) && uids.push(item.uid);
if (dragImage && uids.length) { return uids.length ? {
dragImage.querySelector('.text').textContent = uids.length; copy: event.ctrlKey,
let img = dragImage.querySelector('.icon-white'); folder: FolderStore.currentFolderFullNameRaw(),
img.classList.toggle('icon-copy', event.ctrlKey); uids: uids
img.classList.toggle('icon-mail', !event.ctrlKey); } : null;
// Else Chrome doesn't show it
dragImage.style.left = event.clientX + 'px';
dragImage.style.top = event.clientY + 'px';
dragImage.style.right = 'auto';
let action = event.ctrlKey ? 'copy' : 'move';
event.dataTransfer.setData('application/x-rainloop-messages', JSON.stringify({
copy: event.ctrlKey,
folder: FolderStore.currentFolderFullNameRaw(),
uids: uids
}));
event.dataTransfer.setDragImage(dragImage, 0, 0);
event.dataTransfer.effectAllowed = action;
// Remove the Chrome visibility
dragImage.style.cssText = '';
} else {
event.preventDefault();
}
} }
/** /**

View file

@ -198,7 +198,7 @@
<div class="listEmptySearchList" data-bind="visible: !dragOver() && 0 === messageList().length && !messageListCompleteLoadingThrottle() && '' === messageListError() && '' !== messageListSearch()"> <div class="listEmptySearchList" data-bind="visible: !dragOver() && 0 === messageList().length && !messageListCompleteLoadingThrottle() && '' === messageListError() && '' !== messageListSearch()">
<span class="i18n" data-i18n="MESSAGE_LIST/EMPTY_SEARCH_LIST"></span> <span class="i18n" data-i18n="MESSAGE_LIST/EMPTY_SEARCH_LIST"></span>
</div> </div>
<div draggable="true" data-bind="dragmessages: dragStart"> <div data-bind="dragmessages: getDragData">
<div class="messageListPlace" data-bind="template: { name: messageListItemTemplate(), foreach: messageList }"></div> <div class="messageListPlace" data-bind="template: { name: messageListItemTemplate(), foreach: messageList }"></div>
</div> </div>
<div id="messagesDragImage"><span class="text"></span>&nbsp;<i class="icon-mail icon-white"></i></div> <div id="messagesDragImage"><span class="text"></span>&nbsp;<i class="icon-mail icon-white"></i></div>