Pages

Thursday, May 19, 2016

Learning Meteor.js [Part 23]: Enable drag and drop with interact.js

The next step in the application is to let users drag and drop recipes and/or ingredients into their order. To enable this I chose interact.js and in this post I'll go over how to integrate it.

The most important file is draggable.js which defines the function that enables a set of elements to become 'draggable' with the mouse. The recipe and ingredient lists will register their items to be draggable:
import { setupDraggable } from '../interactions/draggable.js';

Template.recipesListPart.onRendered(function() {
    setupDraggable('.recipesListPart .recipe-item');
});

Now, a newly created orderDetailsPart will include the dropzone where the items can be dropped, this is where in the future we will update the order object.
Template.orderDetailsPart.onRendered(function() {
    interact('.orderDetailsPart .dropzone').dropzone({
      ondrop: function (event) {
        $('<div></div>').text(event.relatedTarget.textContent).appendTo(event.target);
      }
    });
});

The most difficult part is that I don't want the actual list item itself to be dragged, but instead a copy of the list item. Interact.js supports this by registering to the 'move' event and cloning the item that is being dragged and then initiating a 'drag' operation on the clone. Notice that care must be taken to position the clone so that it looks like its coming from the original list item
export const setupDraggable = function(selector) {
    var interaction = interact(selector).draggable({...});

    interaction.on('move', function (event) {
        var interaction = event.interaction;

      // if the pointer was moved while being held down and an interaction hasn't started yet
      if (interaction.pointerIsDown && !interaction.interacting()) {
        var original = event.currentTarget;

        // create a clone of the currentTarget element
        var clone = event.currentTarget.cloneNode(true);

        // insert the clone to the page and position it
        original.parentNode.appendChild(clone);
        clone.style.width = original.offsetWidth + 'px';
        clone.style.height = original.offsetHeight + 'px';
        clone.style.position = 'absolute';
        clone.style.top = original.offsetTop + 'px';
        clone.style.left = 0;

        // start a drag interaction targeting the clone
        interaction.start({ name: 'drag' }, event.interactable, clone);

      } else {
        interaction.start({ name: 'drag' }, event.interactable, event.currentTarget);
      }
    })
}

It took a while to get this to work, now I wonder if something like dragula would have been better suited for this since it has native support for dragging copies. Anyhow, you can checkout the source up to this point by using this commit or view the live demo.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.