From aa6e99ef9ec115ea1cfe404d3881faa03be174b9 Mon Sep 17 00:00:00 2001 From: Ken Date: Sat, 27 Jan 2018 18:05:28 -0600 Subject: [PATCH] Swap Feature This is a rough implementation of a swapping feature. Setting the new "swap" option to true will cause a dragged item to be swapped with the item you drop it on (instead of inserted next to it). The item being hovered over (and potentially swapped with) will be highlighted by adding the class specified in the new "swapHighlightClass" option (defaults to "sortable-swap-highlight"). When swapping is enabled, the "end" event's "to" property will be set to the item being dropped on and swapped with. It's a bit hackish, but it works as a solution to issues #1211, #1082, #1072, #891, #869, and #469. Maybe it can at least serve as a starting off point to adding this feature to master eventually? Thanks! --- Sortable.js | 118 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 88 insertions(+), 30 deletions(-) diff --git a/Sortable.js b/Sortable.js index bec7954..8427043 100644 --- a/Sortable.js +++ b/Sortable.js @@ -261,6 +261,7 @@ var defaults = { group: null, sort: true, + swap: false, disabled: false, store: null, handle: null, @@ -271,6 +272,7 @@ ghostClass: 'sortable-ghost', chosenClass: 'sortable-chosen', dragClass: 'sortable-drag', + swapHighlightClass: 'sortable-swap-highlight', ignore: 'a, img', filter: null, preventOnFilter: true, @@ -860,10 +862,21 @@ _cloneHide(activeSortable, isOwner); if (!dragEl.contains(el)) { - if (after && !nextSibling) { - el.appendChild(dragEl); - } else { - target.parentNode.insertBefore(dragEl, after ? nextSibling : target); + if (this.options.swap) + { + var elements = document.getElementsByClassName(this.options.swapHighlightClass); + while(elements.length > 0){ + _toggleClass(elements[0], this.options.swapHighlightClass, false); + } + _toggleClass(target, this.options.swapHighlightClass, true); + } + else + { + if (after && !nextSibling) { + el.appendChild(dragEl); + } else { + target.parentNode.insertBefore(dragEl, after ? nextSibling : target); + } } } @@ -969,44 +982,63 @@ // Drag stop event _dispatchEvent(this, rootEl, 'unchoose', dragEl, parentEl, rootEl, oldIndex, null, evt); - if (rootEl !== parentEl) { - newIndex = _index(dragEl, options.draggable); - - if (newIndex >= 0) { - // Add event - _dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); - - // Remove event - _dispatchEvent(this, rootEl, 'remove', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); + if (this.options.swap) + { + var elements = document.getElementsByClassName(this.options.swapHighlightClass); + var dropEl = null; + while(elements.length > 0){ + dropEl = elements[0]; + _toggleClass(elements[0], this.options.swapHighlightClass, false); + } - // drag from one list and drop into another - _dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); - _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); + if (dropEl) + { + _swapNodes(dragEl, dropEl); + newIndex = _index(dragEl, options.draggable); + _dispatchEvent(this, rootEl, 'end', dragEl, dropEl, rootEl, oldIndex, newIndex); } } - else { - if (dragEl.nextSibling !== nextEl) { - // Get the index of the dragged element within its parent + else + { + if (rootEl !== parentEl) { newIndex = _index(dragEl, options.draggable); if (newIndex >= 0) { - // drag & drop within the same list - _dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); + // Add event + _dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); + + // Remove event + _dispatchEvent(this, rootEl, 'remove', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); + + // drag from one list and drop into another + _dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); } } - } - - if (Sortable.active) { - /* jshint eqnull:true */ - if (newIndex == null || newIndex === -1) { - newIndex = oldIndex; + else { + if (dragEl.nextSibling !== nextEl) { + // Get the index of the dragged element within its parent + newIndex = _index(dragEl, options.draggable); + + if (newIndex >= 0) { + // drag & drop within the same list + _dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); + _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); + } + } } - _dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); + if (Sortable.active) { + /* jshint eqnull:true */ + if (newIndex == null || newIndex === -1) { + newIndex = oldIndex; + } + + _dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); - // Save sorting - this.save(); + // Save sorting + this.save(); + } } } @@ -1511,6 +1543,32 @@ function _cancelNextTick(id) { return clearTimeout(id); } + + function _swapNodes(n1, n2) { + + var p1 = n1.parentNode; + var p2 = n2.parentNode; + var i1, i2; + + if ( !p1 || !p2 || p1.isEqualNode(n2) || p2.isEqualNode(n1) ) return; + + for (var i = 0; i < p1.children.length; i++) { + if (p1.children[i].isEqualNode(n1)) { + i1 = i; + } + } + for (var i = 0; i < p2.children.length; i++) { + if (p2.children[i].isEqualNode(n2)) { + i2 = i; + } + } + + if ( p1.isEqualNode(p2) && i1 < i2 ) { + i2++; + } + p1.insertBefore(n2, p1.children[i1]); + p2.insertBefore(n1, p2.children[i2]); + } // Fixed #973: _on(document, 'touchmove', function (evt) {