Dragula Multiselect

Let's you select multiple items in a container and drag it to another container.

by gbos

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.css">
<html>
  <head>
  </head>
  <body>
    <div class="parent">
      <strong>NOTE: Click this panel to give it focus to capture key press events.</strong> 
      <br/>
      <label>Move stuff between these two containers. You can multiselect items on the left by holding down the shift key and clicking items.</label>
      <div class="wrapper">
        <div id="left" class="container">
          <div>Something 1</div>
          <div>Something 2</div>
          <div>Something 3</div>
          <div>Something 4</div>
          <div>Something 5</div>
          <div>Something 6</div>
          <div>Something 7</div>
          <div>Something 8</div>
          <div>Something 9</div>
        </div>
        <div id="right" class="container">
          <div>Something A</div>
          <div>Something B</div>
          <div>Something C</div>
          <div>Something D</div>
          <div>Something E</div>
          <div>Something F</div>
          <div>Something G</div>
          <div>Something H</div>
          <div>Something I</div>
        </div>
      </div>
    </div>
  </body>
</html>

CSS

body {
  background-color: #942A57;
  margin: 0 auto;
  font-family: Georgia, Helvetica;
  font-size: 17px;
  color: #ecf0f1;
  max-width: 760px;
}

html, body {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

*, *:before, *:after {
  -webkit-box-sizing: inherit;
  -moz-box-sizing: inherit;
  box-sizing: inherit;
}

label {
  display: block;
  font-size: 12px;
  font-style: italic;
  margin: 5px 0;
}

.parent {
  background-color: rgba(255, 255, 255, 0.2);
  margin: 50px 0;
  padding: 20px;
}

/* dragula-specific example page styles */
.wrapper {
  display: table;
  width: 100%;
}
.container {
  display: table-cell;
  background-color: rgba(255, 255, 255, 0.2);
  width: 50%;
}
.container:nth-child(odd) {
  background-color: rgba(0, 0, 0, 0.2);
}

#right.container .selectedItem {
  display: block !important;
}
/*
 * note that styling gu-mirror directly is a bad practice because it's too generic.
 * you're better off giving the draggable elements a unique class and styling that directly!
 */
.container > div,
.gu-mirror, /* single selection */
.gu-mirror > div /* multiple selection */{
  margin: 10px;
  padding: 10px;
  background-color: rgba(0, 0, 0, 0.2);
  transition: opacity 0.4s ease-in-out;
}
.container > div {
  cursor: move;
  cursor: grab;
  cursor: -moz-grab;
  cursor: -webkit-grab;
}
.gu-mirror {
  cursor: grabbing;
  cursor: -moz-grabbing;
  cursor: -webkit-grabbing;
  transform: rotate(-2deg);
  opacity: 0.7;
}
.container > div.selectedItem {
  background-color: lightseagreen;
}
.container  div.selectedItem.gu-transit {
  background-color: rgba(0, 0, 0, 0.5);
}

JavaScript

$(document).ready(function () {
  'use strict';

  // global references
  var left = $('#left'),      // left container
      right = $('#right'),    // right container
      hasMultiple = false,    // flags if there are multiple selection
      selectedItems,          // the multiple selections
      mirrorContainer,        // the floating preview 
      shiftIsPressed = false;  // shift key on keyboard

  // setup draggable containers
  var drake = dragula([left[0], right[0]], {
    revertOnSpill: true
  });

  // handle events
  drake.on('drag', (el) => { 
      // nothing happening here
    })
    .on('cloned', (clone, original, type) => {
      
      // are we dragging from left to right?
      var isFromLeft = $(original).parent().attr('id') === 'left';

      // we're dragging from left to right
      if (isFromLeft) {

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

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

        // get the multi selected items
        selectedItems = $('.selectedItem');

        // do we have multiple items selected?
        // (takes into account edge case where they start dragging from an item that hasn't been selected)
        hasMultiple = selectedItems.length > 1 || (selectedItems.length == 1 && !$(original).hasClass('selectedItem')); 

        // we have multiple items selected
        if(hasMultiple) {

          // edge case: if they started dragging from an unselected item, adds the selected item class
          $('.gu-transit').addClass('selectedItem');
          
          // update list of selected items in case of edge case above
          selectedItems = $('.selectedItem');
          
          // clear the mirror container, we're going to fill it with clones of our items
          mirrorContainer.empty();

          // will track final...