JosefAssad
JosefAssad

Reputation: 4128

sortable connected to fancytree is forgetting its position upon drag and drop

I am trying to connect a fancytree and a JQueryUI sortable in a specific manner, such that the following is possible:

  1. fancytree nodes can be rearranged inside the fancytree
  2. sortable items can be rearranged inside the sortable
  3. A fancytree node can be dropped on the sortable as a clone, and
  4. A sortable item can be dropped on the fancytree as a clone to create a new fancytree node

The code I have does all of this right now, with one problem which I am having problems ironing out; when I drag a sortable node to the fancytree, it does not retain its position in the sortable; it moves to the end.

What I am looking for is this: exactly the behavior displayed in this sample code, but when dragging from the sortable to the fancytree, the sortable item retains its place.

Here is a fiddle with my code:

And here is the code itself (identical to the fiddle, maybe save someone a click...):

<html>
    <head>
    <meta charset="UTF-8">
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script
        src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
         integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
         crossorigin="anonymous"></script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/skin-win8/ui.fancytree.min.css" rel="stylesheet">
    <!-- <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.fancytree-all-deps.min.js"></script> -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.fancytree-all.min.js"></script>
    </head>
    <body>
    <div id="tree"></div>

    <ul id="mylist">
        <li class="ui-state-default tonga">Item 1</li>
        <li class="ui-state-default tonga">Item 2</li>
        <li class="ui-state-default tonga">Item 3</li>
        <li class="ui-state-default tonga">Item 4</li>
        <li class="ui-state-default tonga">Item 5</li>
        <li class="ui-state-default tonga">Item 6</li>
        <li class="ui-state-default tonga">Item 7</li>
    </ul>

    <script type="text/javascript">
     $(function(){  // on page load
         $("#tree").fancytree({
         debugLevel: 0,
         selectMode: 1,
         extensions: ["dnd"],
         source: [
             {title: "Node 1", key: "1", "baloney": 44},
             {title: "Folder 2", key: "2", folder: true, children: [
             {title: "Node 2.1", key: "3", myOwnAttr: "abc"},
             {title: "Node 2.2", key: "4"}
             ]}
         ],
         dnd: {
             dragStart: function(node, data) {
             return true;
             },
             dragEnter: function(node, data) {
             return true;
             },
             dragDrop: function(node, data) {
             if ( !data.otherNode ) {
                 // it's a draggable from outside the fancytree
                 node.addNode({title: "Hello butt"}, data.hitMode);
                 return;
             } else {
                 // SOLUTION: this line enables reorder inside tree
                 data.otherNode.moveTo(node, data.hitMode);
             }
             },
             initHelper: function(sourceNode, data) {
             var helper = data.ui.helper;
             var foo = $(helper).find(".fancytree-title")[0].innerHTML;
             $(helper).find(".fancytree-drag-helper-img").remove();
             $(helper).find(".fancytree-title").replaceWith('<li class="ui-state-default tonga">'+foo+"</li>");
             },
             updateHelper: function(sourceNode, data) {
             },
             draggable: {
             appendTo: "body",
             connectToSortable: "#mylist",
             revert: "invalid",
             containment: "document"
             }
         },
         });

         $("#mylist").sortable(
         {
             connectWith: "#mylist",
             //containment: "parent"
         }
         ).disableSelection();

         $(".tonga").draggable({
         revert: true,
         helper: "clone",
         connectToFancytree: true
         });

         $(".tonga").draggable({
         revert: false,
         helper: "original",
         connectToSortable: "#mylist"
         });
     });
    </script>

    </body>
</html>

Upvotes: -1

Views: 501

Answers (1)

mechenbier
mechenbier

Reputation: 3067

I think I've figured it out. A few things need to be reworked.

First, the out event of the sortable object needs to be handled to store whether the item has been drug out of the bounds of the sortable, which is what for some reason reorders the list:

$("#mylist").sortable({
  connectWith: "#mylist",
  out: function(event, ui) {
    // store the original index of the sortable item only if we are moving the mouse (extra events fire which mess this up)
    if (event.originalEvent.type === "mousemove") {
      $(ui.item).data('drugout', true);
    }
  }
}).disableSelection();

Then a few event listeners need to be added to the draggable. The start handler stores the original index of the draggable item. The stop handler determines if the item has been drug outside the bounds of the sortable. If the item has been drug out of the sortable, then it reinserts the item at it's previous index, effectively preserving the order of the list:

$(".tonga").draggable({
    revert: true,
    helper: "clone",
    connectToFancytree: true,
    start: function(event, ui) {
      $(event.target).data('previndex', $(event.target).index());
    },
    stop: function(event, ui) {
       if ($(event.target).data('drugout')) {
           var originalIndex = $(event.target).data('previndex');
          $("#mylist li:last").insertBefore($("#mylist li:eq(" + originalIndex + ")"));
       }
    }
});

This JSFiddle has a working example.

$(function() { // on page load
  $("#tree").fancytree({
    debugLevel: 0,
    selectMode: 1,
    extensions: ["dnd"],
    source: [{
        title: "Node 1",
        key: "1",
        "baloney": 44
      },
      {
        title: "Folder 2",
        key: "2",
        folder: true,
        children: [{
            title: "Node 2.1",
            key: "3",
            myOwnAttr: "abc"
          },
          {
            title: "Node 2.2",
            key: "4"
          }
        ]
      }
    ],
    dnd: {
      dragStart: function(node, data) {
        return true;
      },
      dragEnter: function(node, data) {
        return true;
      },
      dragDrop: function(node, data) {
        if (!data.otherNode) {
          // it's a draggable from outside the fancytree
          node.addNode({
            title: "Hello butt"
          }, data.hitMode);
          return;
        } else {
          // SOLUTION: this line enables reorder inside tree
          data.otherNode.moveTo(node, data.hitMode);
        }
      },
      initHelper: function(sourceNode, data) {
        var helper = data.ui.helper;
        var foo = $(helper).find(".fancytree-title")[0].innerHTML;
        $(helper).find(".fancytree-drag-helper-img").remove();
        $(helper).find(".fancytree-title").replaceWith('<li class="ui-state-default tonga">' + foo + "</li>");
      },
      updateHelper: function(sourceNode, data) {},
      draggable: {
        appendTo: "body",
        connectToSortable: "#mylist",
        revert: "invalid",
        containment: "document"
      }
    },
  });

  $("#mylist").sortable({
    connectWith: "#mylist",
    out: function(event, ui) {
      if (event.originalEvent.type === "mousemove") {
        $(ui.item).data('drugout', true);
      }
    }
  }).disableSelection();

  $(".tonga").draggable({
    revert: true,
    helper: "clone",
    connectToFancytree: true,
    start: function(event, ui) {
      $(event.target).data('previndex', $(event.target).index());
    },
    stop: function(event, ui) {
      if ($(event.target).data('drugout')) {
        var originalIndex = $(event.target).data('previndex');
        $("#mylist li:last").insertBefore($("#mylist li:eq(" + originalIndex + ")"));
      }
    }
  });

  $(".tonga").draggable({
    revert: false,
    helper: "original",
    connectToSortable: "#mylist"
  });
});
<head>
  <meta charset="UTF-8">
  <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/skin-win8/ui.fancytree.min.css" rel="stylesheet">
  <!-- <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.fancytree-all-deps.min.js"></script> -->
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.fancytree-all.min.js"></script>
</head>

<body>
  <div id="tree"></div>
  <ul id="mylist">
    <li class="ui-state-default tonga">Item 1</li>
    <li class="ui-state-default tonga">Item 2</li>
    <li class="ui-state-default tonga">Item 3</li>
    <li class="ui-state-default tonga">Item 4</li>
    <li class="ui-state-default tonga">Item 5</li>
    <li class="ui-state-default tonga">Item 6</li>
    <li class="ui-state-default tonga">Item 7</li>
  </ul>
</body>

Upvotes: 1

Related Questions