From 12f61349c62f0df22c81f5b1ef9803807ecc7f76 Mon Sep 17 00:00:00 2001 From: Roy Mdr Date: Fri, 2 Sep 2022 14:09:10 -0500 Subject: [PATCH 1/7] Add Drag In and Drag Out events Leaved comments in this commit for code review before merge. known isues: - DnD HTML5 API: If drag too fast to another browser UI element (ej. dev tools) the HTML5 DnD dragLeave event is not triggered (not an issue from this code) ... a workaround could be to force a fallback to get the element at pointer position when dragging back to the browser window. --- src/Sortable.js | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/Sortable.js b/src/Sortable.js index 7a8c779b8..91fc49f41 100644 --- a/src/Sortable.js +++ b/src/Sortable.js @@ -229,6 +229,15 @@ let dragEl, let ret; sortables.some((sortable) => { const threshold = sortable[expando].options.emptyInsertThreshold; + + sortable[expando]._onDragLeave({ + type: 'pointermove', + clientX: x, + clientY: y, + target: document.elementFromPoint(x, y), + rootEl: sortable + }); + if (!threshold || lastChild(sortable)) return; const rect = getRect(sortable), @@ -403,6 +412,8 @@ function Sortable(el, options) { !(name in options) && (options[name] = defaults[name]); } + options._isHovered = false; + _prepareGroup(options); // Bind all private methods @@ -431,6 +442,7 @@ function Sortable(el, options) { if (this.nativeDraggable) { on(el, 'dragover', this); on(el, 'dragenter', this); + on(el, 'dragleave', this); } sortables.push(this.el); @@ -619,6 +631,7 @@ Sortable.prototype = /** @lends Sortable.prototype */ { }); on(ownerDocument, 'dragover', nearestEmptyInsertDetectEvent); + // on(ownerDocument, 'dragleave', nearestEmptyInsertDetectEvent); on(ownerDocument, 'mousemove', nearestEmptyInsertDetectEvent); on(ownerDocument, 'touchmove', nearestEmptyInsertDetectEvent); @@ -720,6 +733,7 @@ Sortable.prototype = /** @lends Sortable.prototype */ { if (this.nativeDraggable) { on(document, 'dragover', _checkOutsideTargetEl); + // on(document, 'dragleave', _checkOutsideTargetEl); } let options = this.options; @@ -731,6 +745,8 @@ Sortable.prototype = /** @lends Sortable.prototype */ { fallback && this._appendGhost(); + this.options._isHovered = true; + // Drag start event _dispatchEvent({ sortable: this, @@ -775,6 +791,18 @@ Sortable.prototype = /** @lends Sortable.prototype */ { if (inserted && !this.options.dragoverBubble) { break; } + } else { + // Is dragging over an element (var: target) that is not a Sortable container + // This element can be a child of the container or not + // This only works inside the window of the browser + // So moved outside of this if statement + /* parentEl[expando]._onDragLeave({ + type: 'pointermove', + clientX: touchEvt.clientX, + clientY: touchEvt.clientY, + target: target, + rootEl: parent + }); */ } target = parent; // store last element @@ -783,6 +811,18 @@ Sortable.prototype = /** @lends Sortable.prototype */ { while (parent = parent.parentNode); } + // I think the bug is because we tell the ParentEl to listen to the DragLeave + // but if we change too quickly of parent, then the ParentEl is another... + // so is never called and stays with the hover variable on true + // So moved to + /* parentEl[expando]._onDragLeave({ + type: 'pointermove', + clientX: touchEvt.clientX, + clientY: touchEvt.clientY, + target: target, + rootEl: parent + }); */ + _unhideGhostForTarget(); } }, @@ -988,6 +1028,32 @@ Sortable.prototype = /** @lends Sortable.prototype */ { }, + _onDragLeave: function (/**Event*/evt) { + var el = this.el, + target = evt.target, + options = this.options; + + if (!options._isHovered) { + // console.log(`there was no hover in ${options.group.name}`) + return; + } + // console.log(`there was hover in ${options.group.name}`, evt) + if ( + // ( evt.type == 'dragend' || evt.type == 'drop' ) || // from onDrop (makes no sense to trigger a drag-out on a drop event) + ( evt.type == 'dragleave' && ( !el.contains(evt.relatedTarget) || !evt.relatedTarget) ) || + ( evt.type == 'pointermove' && ( !el.contains(target) || !target ) ) + ) { + options._isHovered = false; + _dispatchEvent({ + rootEl: el, + name: 'dragOut', + originalEvent: evt + }); + } + + }, + + // Returns true - if no further action is needed (either inserted or another condition) _onDragOver: function (/**Event*/evt) { let el = this.el, @@ -1145,6 +1211,15 @@ Sortable.prototype = /** @lends Sortable.prototype */ { dragRect = getRect(dragEl); + if ( !options._isHovered ) { + options._isHovered = true; + _dispatchEvent({ + rootEl: el, + name: 'dragIn', + originalEvent: evt + }); + } + dragOverEvent('dragOverValid'); if (Sortable.eventCanceled) return completedFired; @@ -1321,6 +1396,7 @@ Sortable.prototype = /** @lends Sortable.prototype */ { off(document, 'touchmove', this._onTouchMove); off(document, 'pointermove', this._onTouchMove); off(document, 'dragover', nearestEmptyInsertDetectEvent); + off(document, 'dragleave', nearestEmptyInsertDetectEvent); off(document, 'mousemove', nearestEmptyInsertDetectEvent); off(document, 'touchmove', nearestEmptyInsertDetectEvent); }, @@ -1339,6 +1415,8 @@ Sortable.prototype = /** @lends Sortable.prototype */ { let el = this.el, options = this.options; + options._isHovered = false; + // Get the index of the dragged element within its parent newIndex = index(dragEl); newDraggableIndex = index(dragEl, options.draggable); @@ -1560,6 +1638,10 @@ Sortable.prototype = /** @lends Sortable.prototype */ { } break; + case 'dragleave': + this._onDragLeave(evt); + break; + case 'selectstart': evt.preventDefault(); break; From 1c776a01e02b1eccf2a4c16167a307f4e77eb12c Mon Sep 17 00:00:00 2001 From: Roy Mdr Date: Fri, 2 Sep 2022 15:01:31 -0500 Subject: [PATCH 2/7] Fix comment typo --- src/Sortable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sortable.js b/src/Sortable.js index 91fc49f41..996c2973f 100644 --- a/src/Sortable.js +++ b/src/Sortable.js @@ -814,7 +814,7 @@ Sortable.prototype = /** @lends Sortable.prototype */ { // I think the bug is because we tell the ParentEl to listen to the DragLeave // but if we change too quickly of parent, then the ParentEl is another... // so is never called and stays with the hover variable on true - // So moved to + // So moved to _detectNearestEmptySortable /* parentEl[expando]._onDragLeave({ type: 'pointermove', clientX: touchEvt.clientX, From dbdac6150ad795ac8c274e3a0326de1cb2e0d922 Mon Sep 17 00:00:00 2001 From: Roy Mdr Date: Wed, 7 Sep 2022 00:13:00 -0500 Subject: [PATCH 3/7] Fix: Bug on nested Sortables Before: dragIn event was only triggered when it was a valid list to drop in. However if the list was nested in another Sortable the event didn't fire again because it was already marked as valid. After: dragIn event is triggered even if is not a valid Sortable to drop in. Now is up to the Sortable component to handle and check if the list is allowed to drop in. --- src/Sortable.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Sortable.js b/src/Sortable.js index 996c2973f..a7b292ee6 100644 --- a/src/Sortable.js +++ b/src/Sortable.js @@ -1185,6 +1185,15 @@ Sortable.prototype = /** @lends Sortable.prototype */ { dragOverEvent('dragOver'); if (Sortable.eventCanceled) return completedFired; + if ( !options._isHovered ) { + options._isHovered = true; + _dispatchEvent({ + rootEl: el, + name: 'dragIn', + originalEvent: evt + }); + } + if ( dragEl.contains(evt.target) || target.animated && target.animatingX && target.animatingY || @@ -1211,15 +1220,6 @@ Sortable.prototype = /** @lends Sortable.prototype */ { dragRect = getRect(dragEl); - if ( !options._isHovered ) { - options._isHovered = true; - _dispatchEvent({ - rootEl: el, - name: 'dragIn', - originalEvent: evt - }); - } - dragOverEvent('dragOverValid'); if (Sortable.eventCanceled) return completedFired; From c6872122e15122ccbd99063691c3e9af50c067fb Mon Sep 17 00:00:00 2001 From: Roy Mdr Date: Wed, 7 Sep 2022 13:53:11 -0500 Subject: [PATCH 4/7] Fix: bug dragging into own ghost element triggered dragIn --- src/Sortable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sortable.js b/src/Sortable.js index a7b292ee6..f12eef9fe 100644 --- a/src/Sortable.js +++ b/src/Sortable.js @@ -1185,7 +1185,7 @@ Sortable.prototype = /** @lends Sortable.prototype */ { dragOverEvent('dragOver'); if (Sortable.eventCanceled) return completedFired; - if ( !options._isHovered ) { + if ( !options._isHovered && !dragEl.contains(el) ) { options._isHovered = true; _dispatchEvent({ rootEl: el, From ab354c2d923d7f1db84504e7b9821db47a18b715 Mon Sep 17 00:00:00 2001 From: Roy Mdr Date: Wed, 7 Sep 2022 13:57:53 -0500 Subject: [PATCH 5/7] Add onDrop event onDrop event is triggered when a sortable-dragging element is dropped inside the X and Y of the Sortable element, even if there was no onEnd event triggered --- src/Sortable.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Sortable.js b/src/Sortable.js index f12eef9fe..a7a1ea934 100644 --- a/src/Sortable.js +++ b/src/Sortable.js @@ -1567,6 +1567,16 @@ Sortable.prototype = /** @lends Sortable.prototype */ { newDraggableIndex = oldDraggableIndex; } + sortables.some((sortable) => { + if ( sortable.contains(document.elementFromPoint(evt.clientX, evt.clientY)) ) { + _dispatchEvent({ + sortable: sortable[expando], + name: 'drop', + originalEvent: evt + }); + } + }); + _dispatchEvent({ sortable: this, name: 'end', From ec6d080246406b3f761485ed49a94a9de40ba480 Mon Sep 17 00:00:00 2001 From: Roy Mdr Date: Wed, 7 Sep 2022 14:10:06 -0500 Subject: [PATCH 6/7] Fix test failing: add return value to some() method --- src/Sortable.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Sortable.js b/src/Sortable.js index a7a1ea934..a21a7498c 100644 --- a/src/Sortable.js +++ b/src/Sortable.js @@ -1574,7 +1574,9 @@ Sortable.prototype = /** @lends Sortable.prototype */ { name: 'drop', originalEvent: evt }); + return true; } + return false; }); _dispatchEvent({ From 9f20195a9350713ad4fc85e35876860843002cb5 Mon Sep 17 00:00:00 2001 From: Roy Mdr Date: Wed, 7 Sep 2022 18:22:54 -0500 Subject: [PATCH 7/7] Fix: Touch functionality --- src/Sortable.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Sortable.js b/src/Sortable.js index a21a7498c..76166db17 100644 --- a/src/Sortable.js +++ b/src/Sortable.js @@ -1185,7 +1185,9 @@ Sortable.prototype = /** @lends Sortable.prototype */ { dragOverEvent('dragOver'); if (Sortable.eventCanceled) return completedFired; - if ( !options._isHovered && !dragEl.contains(el) ) { + if ( !options._isHovered && !dragEl.contains(el) + && activeSortable && !options.disabled + ) { options._isHovered = true; _dispatchEvent({ rootEl: el, @@ -1568,7 +1570,12 @@ Sortable.prototype = /** @lends Sortable.prototype */ { } sortables.some((sortable) => { - if ( sortable.contains(document.elementFromPoint(evt.clientX, evt.clientY)) ) { + + let x = (evt.changedTouches ? evt.changedTouches[0] : evt).clientX, + y = (evt.changedTouches ? evt.changedTouches[0] : evt).clientY, + elem = document.elementFromPoint(x, y); + + if ( sortable.contains(elem) ) { _dispatchEvent({ sortable: sortable[expando], name: 'drop',