amplesdk icon indicating copy to clipboard operation
amplesdk copied to clipboard

Drag and drop manager fails to find proper drop zone

Open tedbeer opened this issue 9 years ago • 0 comments

The bug show case - http://codepen.io/tedbeer/pen/MwXEZg It happens when a drop zone inside another drop zone is bigger than the parent. DnD manager looks for a drop target and selectы the smallest. It does not take into account that a child node can be bigger than a parent node.

I propose changing the algorithm a bit:

  • find all targets intersecting with the drag source
  • from related targets (parent-child-descendant) leave the deepest (descendants) only
  • among remaining targets choose the target with the biggest intersection

The pen using patched amplesdk - http://codepen.io/tedbeer/pen/KpeBBz

Here the patch implementing the algorithm and solving the problem:

--- /amplesdk3/ample/runtime/managers/DragAndDropManager.js
+++ /ample-patched/ample/runtime/managers/DragAndDropManager.js
@@ -242,10 +242,19 @@

        // fill in array with drag targets
        var aElements   = fElement_getElementsByTagName(oBrowser_modalNode || this.documentElement, '*');
-       for (var nIndex = 0, nLength = aElements.length; nIndex < nLength; nIndex++)
-           if (aElements[nIndex].$droppable)
-               aDragAndDropManager_dropTargets.push(aElements[nIndex]);
-
+       for (var oRect2, nArea, nIndex = 0, nLength = aElements.length; nIndex < nLength; nIndex++)
+           if (aElements[nIndex].$droppable) {
+               oRect2 = fElement_getBoundingClientRect(aElements[nIndex]);
+               nArea = (oRect2.right - oRect2.left) * (oRect2.bottom - oRect2.top);
+               //skip invisible
+               if (nArea > 0)
+                   aDragAndDropManager_dropTargets.push({
+                       elm: aElements[nIndex],
+                       rect: oRect2,
+                       area: nArea,
+                       intersection: 0
+                   });
+           }
        //
        nDragAndDropManager_dragState   = nDragAndDropManager_STATE_DRAGGED;

@@ -274,41 +283,32 @@
        nDragAndDropManager_scrollTop   = oScroll.top;
    }

-   var oDropTarget = null,
-       oRect2,
-       nAreaSource =(oRect.right - oRect.left) * (oRect.bottom - oRect.top),
-       nAreaTarget,
-       nAreaTargetMin  = nInfinity,
-       nIntersection,
-       nPartialMax = 0;
-
-   for (var nIndex = 0, nLength = aDragAndDropManager_dropTargets.length; nIndex < nLength; nIndex++) {
-       // if source is the target, continue
-       if (aDragAndDropManager_dropTargets[nIndex] == oDragAndDropManager_dragSource)
-           continue;
-
-       // if target contains source, continue
-       if (fNode_compareDocumentPosition(aDragAndDropManager_dropTargets[nIndex], oDragAndDropManager_dragSource) & 8 /* cNode.DOCUMENT_POSITION_CONTAINS */)
-           continue;
-
-       oRect2  = fElement_getBoundingClientRect(aDragAndDropManager_dropTargets[nIndex]);
-       nAreaTarget =(oRect2.right - oRect2.left) * (oRect2.bottom - oRect2.top);
-       nIntersection   = fDragAndDropManager_intersectRectangle(oRect, oRect2);
-       if (nIntersection < nAreaSource) {
-           // partial intersection
-           if (nIntersection > nPartialMax) {
-               nPartialMax = nIntersection;
-               oDropTarget = aDragAndDropManager_dropTargets[nIndex];
-           }
-       }
-       else {
-           // complete intersection
-           if (nAreaTarget < nAreaTargetMin) {
-               nAreaTargetMin  = nAreaTarget;
-               oDropTarget = aDragAndDropManager_dropTargets[nIndex];
+   var aCandidates = aDragAndDropManager_dropTargets.filter(function(one) {
+       if (one.area === 0 || one.elm == oDragAndDropManager_dragSource ||
+           fNode_compareDocumentPosition(one.elm, oDragAndDropManager_dragSource) & 8)
+           return false;
+       one.intersection = fDragAndDropManager_intersectRectangle(oRect, one.rect);
+       return one.intersection > 0;
+   });
+   var oElm, rel, oldLen = 0;
+   //filter out related candidates (leave the deepest)
+   while(aCandidates.length > 1 && oldLen !== aCandidates.length) {
+       oElm = aCandidates[0].elm;
+       oldLen = aCandidates.length; //to break the loop if no changes in the candidates array
+       for (var ind = aCandidates.length - 1; ind > 0; ind--) {
+           rel = fNode_compareDocumentPosition(oElm, aCandidates[ind].elm);
+           if (rel & 8) {
+               aCandidates.splice(ind, 1);
+           } else if (rel & 16) {
+               aCandidates.splice(0, 1);
+               break;
            }
        }
    }
+   //find maximum intersection among candidates
+   var oDropTarget = aCandidates.reduce(function(prev, cand) {
+       return cand.intersection > prev.intersection ? cand : prev;
+   }, {intersection: 0}).elm;

    // if there was a drop target and it is different from a new one
    if (oDragAndDropManager_dropTarget) {

tedbeer avatar Jul 15 '15 08:07 tedbeer