diff --git a/dev/External/User/ko.js b/dev/External/User/ko.js index 9728c8348..a8e4fd0a1 100644 --- a/dev/External/User/ko.js +++ b/dev/External/User/ko.js @@ -1,5 +1,7 @@ const ko = window.ko, - $ = jQuery; + $ = jQuery, + validTransfer = data => ('move' === data.dropEffect || 'copy' === data.dropEffect) // effectAllowed + && data.getData('text/x-rainloop-json'); ko.bindingHandlers.editor = { init: (element, fValueAccessor) => { @@ -89,106 +91,40 @@ ko.bindingHandlers.emailsTags = { }; ko.bindingHandlers.draggable = { - init: (element, fValueAccessor, fAllBindingsAccessor) => { + init: (element, fValueAccessor) => { if (!rl.settings.app('mobile')) { - const triggerZone = 50, - scrollSpeed = 3, - fAllValueFunc = fAllBindingsAccessor(), - selector = fAllValueFunc ? fAllValueFunc.droppableSelector : '', - droppable = selector ? document.querySelector(selector) : null, - conf = { - distance: 20, - handle: '.dragHandle', - cursorAt: { top: 22, left: 3 }, - refreshPositions: true, - scroll: true, - drag: null, - stop: null, - helper: null - }; - let bcr; - - if (droppable) { - conf.drag = event => { - if (droppable.scrollTopMax) { - clearInterval(droppable.timerScroll); - if (droppable.scrollTop - && bcr.top < event.clientY - && bcr.top + triggerZone > event.clientY) - { - droppable.timerScroll = setInterval(() => droppable.scrollTop -= scrollSpeed, 10); - } - else if (droppable.scrollTop < droppable.scrollTopMax - && bcr.bottom > event.clientY - && bcr.bottom - triggerZone < event.clientY) - { - droppable.timerScroll = setInterval(() => droppable.scrollTop += scrollSpeed, 10); - } - else { - clearInterval(droppable.timerScroll); - } - } - }; - - conf.stop = () => clearInterval(droppable.timerScroll); - } - - conf.helper = event => fValueAccessor()(event && event.target ? ko.dataFor(event.target) : null); - - $(element) - .draggable(conf) - .on('mousedown.koDraggable', () => { - require('Common/Utils').removeInFocus(); - bcr = droppable ? droppable.getBoundingClientRect() : null; - }); - - ko.utils.domNodeDisposal.addDisposeCallback(element, () => - $(element) - .off('mousedown.koDraggable') - .draggable('destroy') - ); + element.addEventListener("dragstart", e => fValueAccessor()(e)); } } }; ko.bindingHandlers.droppable = { - init: (element, fValueAccessor, fAllBindingsAccessor) => { + init: (element, fValueAccessor) => { if (!rl.settings.app('mobile')) { - const fValueFunc = fValueAccessor(), - fAllValueFunc = fAllBindingsAccessor(), - fOverCallback = fAllValueFunc && fAllValueFunc.droppableOver ? fAllValueFunc.droppableOver : null, - fOutCallback = fAllValueFunc && fAllValueFunc.droppableOut ? fAllValueFunc.droppableOut : null, - conf = { - tolerance: 'pointer', - hoverClass: 'droppableHover', - drop: null, - over: null, - out: null + const FolderList = fValueAccessor(), + folder = ko.dataFor(element), + fnHover = e => { + if (validTransfer(e.dataTransfer)) { + e.preventDefault(); + element.classList.add('droppableHover'); + FolderList.messagesDropOver(folder); + } }; - - if (fValueFunc) { - conf.drop = (event, ui) => { - fValueFunc(event, ui); - }; - - if (fOverCallback) { - conf.over = (event, ui) => { - fOverCallback(event, ui); - }; + element.addEventListener("dragenter", fnHover); + element.addEventListener("dragover", fnHover); + element.addEventListener("dragleave", e => { + e.preventDefault(); + element.classList.remove('droppableHover'); + FolderList.messagesDropOut(); + }); + element.addEventListener("drop", e => { + const data = JSON.parse(validTransfer(e.dataTransfer)); + if (data) { + data.copy = data.copy && event.ctrlKey; + e.preventDefault(); + FolderList.messagesDrop(folder, data); } - - if (fOutCallback) { - conf.out = (event, ui) => { - fOutCallback(event, ui); - }; - } - - $(element).droppable(conf); - - ko.utils.domNodeDisposal.addDisposeCallback(element, () => { - $(element).droppable('destroy'); - }); - } + }); } } }; diff --git a/dev/Styles/FolderList.less b/dev/Styles/FolderList.less index f1faff943..16b7c465c 100644 --- a/dev/Styles/FolderList.less +++ b/dev/Styles/FolderList.less @@ -138,6 +138,10 @@ color: #fff; } + &.droppableHover * { + pointer-events: none; + } + &.focused { color: #fff; } diff --git a/dev/Styles/MessageList.less b/dev/Styles/MessageList.less index cd66a516b..a8438c505 100644 --- a/dev/Styles/MessageList.less +++ b/dev/Styles/MessageList.less @@ -621,16 +621,15 @@ html .messageList .line-loading { height: 5px !important; } -.draggablePlace { - z-index: 10003; +#messagesDragImage { color: #fff; - background-color: #333; - background-color: rgba(0,0,0,0.5); - padding: 4px 10px; - min-width: 30px; + background-color: #000; height: 20px; - cursor: pointer; - cursor: move; + min-width: 30px; + padding: 4px 10px; + position: fixed; + right: -100px; + top: 0; } html.rl-mobile { diff --git a/dev/View/User/MailBox/FolderList.js b/dev/View/User/MailBox/FolderList.js index 4d70ef0f5..6e2d111bb 100644 --- a/dev/View/User/MailBox/FolderList.js +++ b/dev/View/User/MailBox/FolderList.js @@ -1,7 +1,7 @@ import ko from 'ko'; import { Capa, Focused, Layout, KeyState } from 'Common/Enums'; -import { $htmlCL, leftPanelDisabled, moveAction } from 'Common/Globals'; +import { leftPanelDisabled, moveAction } from 'Common/Globals'; import { mailBox, settings } from 'Common/Links'; import { setFolderHash } from 'Common/Cache'; @@ -218,18 +218,12 @@ class FolderListMailBoxUserView extends AbstractViewNext { /** * @param {FolderModel} toFolder - * @param {{helper:jQuery}} ui + * @param {Array} data * @returns {void} */ - messagesDrop(toFolder, ui) { - if (toFolder && ui && ui.helper) { - const fromFolderFullNameRaw = ui.helper.rlFolder, - copy = $htmlCL.contains('rl-ctrl-key-pressed'), - uids = ui.helper.rlUids; - - if (fromFolderFullNameRaw && Array.isArray(uids)) { - rl.app.moveMessagesToFolder(fromFolderFullNameRaw, uids, toFolder.fullNameRaw, copy); - } + messagesDrop(toFolder, data) { + if (toFolder && data && data.folder && Array.isArray(data.uids)) { + rl.app.moveMessagesToFolder(data.folder, data.uids, toFolder.fullNameRaw, data.copy); } } diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index c0487d21d..41a1670e3 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -45,6 +45,8 @@ const canBeMovedHelper = (self) => self.canBeMoved(), ifvisible = window.ifvisible; +let dragImage; + @view({ name: 'View/User/MailBox/MessageList', type: ViewType.Right, @@ -479,30 +481,38 @@ class MessageListMailBoxUserView extends AbstractViewNext { return false; } - dragAndDronHelper(oMessageListItem) { - if (oMessageListItem) { - oMessageListItem.checked(true); + dragStart(event) { + const item = ko.dataFor(document.elementFromPoint(event.clientX, event.clientY)); + item && item.checked(true); + + dragImage || (dragImage = document.getElementById('messagesDragImage')); + + const uids = MessageStore.messageListCheckedOrSelectedUidsWithSubMails(); + if (dragImage && uids.length) { + dragImage.querySelector('.text').textContent = uids.length; + let img = dragImage.querySelector('.icon-white'); + img.classList.toggle('icon-copy', event.ctrlKey); + img.classList.toggle('icon-mail', !event.ctrlKey); + + // 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('text/x-rainloop-json', 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(); } - - const el = Element.fromHTML('