Reputation: 81
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:
Upvotes: 3
Views: 3872
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