Search code examples
javascripthtmlcssdragula

How can I limit the number of elements going to drop-area using dragula?


I have such a drag-and-drop problem. I've used the library of dragula. I want to limit the number of elements going to the drop-area to two. Like this:

  • if the number of elements is less than two in drop-area allow to add a new element from draggable;
  • if there are two elements in drop-area, the user can add another element if only he/she returns one of the elements from drop-area to draggable.

Here is my code:

  function $(id) {
  return document.getElementById(id);
}

dragula([$('draggable'), $('drop-area')], {
  revertOnSpill: true
});
#chart, #chart tr {
    border: solid 3px;
    width: 1000px;
    border-collapse: collapse;
    font-family: "Almarai";
    font-size: 20px;
}
#chart {
    position: relative;
    left: 200px;
}
#chart #drop-area {
    height: 100px;
}
.gu-mirror {
  position: fixed !important;
  margin: 0 !important;
  z-index: 9999 !important;
  padding: 1em;
}
.gu-hide {
  display: none !important;
}
.gu-unselectable {
  user-select: none !important;
}
.gu-transit {
  opacity: 0;
}
.gu-mirror {
  opacity: 1;
}
<script src="https://rawgit.com/bevacqua/dragula/master/dist/dragula.js"></script>
<table id="chart">
<tr>
<td id="drop-area">
</td>
</tr>
</table><br>
<div id="draggable">
<div id="a1">option 1</div>
<div id="a2">option 2</div>
<div id="a3">option 3</div>
<div id="a4">option 4</div>
<div id="text"></div>

Please help! Thank you in advance.


Solution

  • There are a variety of ways you can approach this based on the various events available in the dragula api.

    You can use the on('drop', callback) event and check the length of children and use cancel().

    I've also used the on('over') event to change class on drop zone and on('cancel') to remove it.

    There are lots of available events you can play with to make the UI more intuitive as needed

    function $(id) {
      return document.getElementById(id);
    }
    
    const dropContainer = $('drop-area');
    
    const drake = dragula([$('draggable'), dropContainer], {
      revertOnSpill: true
    });
    
    drake.on('drop', function(el, target, source, sibling) {
      if (target.matches('#drop-area') && target.children.length > 2) {
        drake.cancel()
      }
    });
    
    drake.on('cancel', function(el, container, source) {
      console.log('Cancelled')
      dropContainer.classList.remove('invalid')
    
    })
    
    drake.on('over', function(el, container, source) {
      if (container !== source && container.matches('#drop-area')) {
        // moved from source to dropzone
        if (container.children.length === 2) {
          container.classList.add('invalid')
        }
      }
    })
    #chart,
    #chart tr {
      border: solid 3px;
      width: 1000px;
      border-collapse: collapse;
      font-family: "Almarai";
      font-size: 20px;
    }
    
    #drop-area.invalid {
      background: yellow;
      color: red
    }
    
    #chart {
      position: relative;
      left: 200px;
    }
    
    #chart #drop-area {
      height: 100px;
    }
    
    .gu-mirror {
      position: fixed !important;
      margin: 0 !important;
      z-index: 9999 !important;
      padding: 1em;
    }
    
    .gu-hide {
      display: none !important;
    }
    
    .gu-unselectable {
      user-select: none !important;
    }
    
    .gu-transit {
      opacity: 0;
    }
    
    .gu-mirror {
      opacity: 1;
    }
    <script src="https://rawgit.com/bevacqua/dragula/master/dist/dragula.js"></script>
    <table id="chart">
      <tr>
        <td id="drop-area">
        </td>
      </tr>
    </table><br>
    <div id="draggable">
      <div id="a1">option 1</div>
      <div id="a2">option 2</div>
      <div id="a3">option 3</div>
      <div id="a4">option 4</div>
      <div id="text"></div>