Rami
Rami

Reputation: 81

Angular Multiple Drag and Drop (ng2-dragula)

I'm looking for drag and drop library which supports multiple drag. But I couldn't find any for angular 2+. ng2-dragula supports what we want, except multiple drag and drop. Can any one suggest any library or how to overwrite ng2 dragula to achieve same. Here are the features what we are looking:

  1. Drag to particular position in target container
  2. Drag multiple items
  3. Drag inside same container
  4. Work for any kind of element
  5. Support both copy and move to target

Upvotes: 3

Views: 3872

Answers (1)

Mukyuu
Mukyuu

Reputation: 6759

Based on this comment on github bevacqua/dragula issue: Drag and drop multiple items #50.

I've made some improvement which allows multiple item drag and drops by selected item: https://jsfiddle.net/twfcd5hx/1/

And on ng2-dragula: https://stackblitz.com/edit/ng2-dragula-base-i41w6c?file=src/app/app.component.ts

Related code (note that I've tried using the pure angular way but not working as intended, will update once I found the pure angular one):

this.dragulaService.cloned("dnd").subscribe(({ clone, original, cloneType }) => {
      this.mirrorContainer = $('.gu-mirror').first();
      this.mirrorContainer.removeClass('ex-over');
      this.selectedItems = $('.ex-over');
      this.hasMultiple = this.selectedItems.length > 1 || (this.selectedItems.length == 1 && !$(original).hasClass('ex-over'));
      if (this.hasMultiple) {
        $('.gu-transit').addClass('ex-over');
        this.selectedItems = $('.ex-over');
        this.mirrorContainer.empty();
        var height = 0,
          width = 0;
        let temp = this.mirrorContainer;
        this.selectedItems.each(function (index) {
          var item = $(this);
          var mirror = item.clone(true);
          mirror.removeClass('ex-over gu-transit');
          temp.append(mirror);
          temp.css('background-color', 'transparent');
          item.addClass('gu-transit');
          var rect = item[0].getBoundingClientRect();
          height += rect.height;
          width = rect.width;
        });
        this.mirrorContainer = temp;
        this.mirrorContainer.css('height', height + 'px');
      }
    });

You would need to declare import $ from 'jquery'; on .ts file and install @types/jquery, jquery

Explanation:

this.mirrorContainer = $('.gu-mirror').first(); grab the mirror container dragula creates by default

this.mirrorContainer.removeClass('ex-over'); multi-selected items will have this class, but we don't want it on the ones in the mirror

this.selectedItems = $('.ex-over'); get the multi-selected items

this.hasMultiple = this.selectedItems.length > 1 || (this.selectedItems.length == 1 && !$(original).hasClass('ex-over')); check if multiple items are selected, and takes into account where they start dragging from an item that hasn't been selected

if (this.hasMultiple) { // we have multiple items selected
    $('.gu-transit').addClass('ex-over'); // edge case: if they started dragging from an unselected item, adds the selected item class
    this.selectedItems = $('.ex-over'); // update list of selected items in case of edge case above
    this.mirrorContainer.empty();  // clear the mirror container, we're going to fill it with clones of our items
    var height = 0,
      width = 0; // will track final dimensions of the mirror container
    let temp = this.mirrorContainer; // used to temporary store this.mirrorContainer since otherwise would return undefined
    this.selectedItems.each(function (index) { // clone the selected items into the mirror container
      var item = $(this); // the item
      var mirror = item.clone(true); // clone the item
      mirror.removeClass('ex-over gu-transit'); // remove the state classes if necessary
      temp.append(mirror); //add the clone to mirror container
      temp.css('background-color', 'transparent');
      item.addClass('gu-transit'); //add drag state class to item
      var rect = item[0].getBoundingClientRect(); // update the dimensions for the mirror container
      height += rect.height;
      width = rect.width;
    });
    this.mirrorContainer = temp; // restore this.mirrorContainer value after updated
    this.mirrorContainer.css('height', height + 'px'); //set final height of mirror container
  }

Note that in the following code I was using el.remove() to remove the dragged element being stored to target element because I've made the moveback() and moveto() function (related explanation).

this.dragulaService.drop("dnd")
  .subscribe(({ name, el, target, source, sibling }) => {
    if (source.attributes[2].value === "data" && (target.attributes[3].value === "ct" || target.attributes[2].value === "target")) {
      if (this.target.length > 0) {
        this.placeholdertarget = false;
      }
      if (this.data.length < 1) {
        this.placeholderdata = true;
      }
      if (!this.hasMultiple) {
        let target = document.getElementsByClassName('target');
        for (let i = target.length - 1; i >= 0; i--) {
          if (target[i].className.includes('ex-over') && i >= 0) {
            this.removeClass(target[i], 'ex-over');
          }
        }
      } else {
        el.remove();
        this.moveto();
      }
      for (let i = 0; i < this.target.length; i++) {
        this.target[i].state = false;
      }
      this.challtarget = false;
    } else if (source.attributes[2].value === "target" && (target.attributes[3].value === "cd" || target.attributes[2].value === "data")) {
      if (this.target.length < 1) {
        this.placeholdertarget = true;
      }
      if (this.data.length > 0) {
        this.placeholderdata = false;
      }
      if (!this.hasMultiple) {
        let target = document.getElementsByClassName('data');
        for (let i = target.length - 1; i >= 0; i--) {
          if (target[i].className.includes('ex-over') && i >= 0) {
            this.removeClass(target[i], 'ex-over');
          }
        }
      } else {
        el.remove();
        this.moveback();
      }
      for (let i = 0; i < this.data.length; i++) {
        this.data[i].state = false;
      }
      this.challdata = false;
    } else {
      // console.log('spilled');
      // this.dragulaService.find('dnd').drake.cancel(true);
    }
  });

Upvotes: 5

Related Questions