JSFiddle - React, Tailwind, and code Playground

by muter

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.js"></script>
<div class="drag-container">
  <ul class="drag-list">
    <li class="drag-column drag-column-to-do">
      <span class="drag-column-header">
        <h2>待辦 / To Do</h2>
        <svg class="drag-header-more" data-target="options1" fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24">
          <path d="M0 0h24v24H0z" fill="none" />
          <path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" /</svg>
      </span>

      <div class="drag-options" id="options1"></div>

      <ul class="drag-inner-list" id="1">
        <li class="drag-item"><p>[頻道] 開放投稿功能</p></li>
        <li class="drag-item"><p>[機械人] 增加投稿功能</p></li>
        <li class="drag-item"><p>[模板] 提升特殊樣式兼容性</p></li>
      </ul>
    </li>
    <li class="drag-column drag-column-in-progress">
      <span class="drag-column-header">
        <h2>進行中 / In Progress</h2>
        <svg class="drag-header-more" data-target="options2" fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24">
          <path d="M0 0h24v24H0z" fill="none" />
          <path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" /</svg>
      </span>
      <div class="drag-options" id="options2"></div>
      <ul class="drag-inner-list" id="2">
        <li class="drag-item"><p>[模板] 適配關聯文章</p></li>
        <li class="drag-item"><p>[機械人] 支援Inline模式</p></li>
        <li class="drag-item"><p>[機械人] 支援解析通過Matters PWA直接分享的文章連結</p></li>
      </ul>
    </li>
    <!--
    <li class="drag-column drag-column-needs-review">
      <span class="drag-column-header">
        <h2>Needs Review</h2>
        <svg data-target="options3" class="drag-header-more" fill="#FFFFFF"...

SCSS

$ease-out: all .3s cubic-bezier(0.23, 1, 0.32, 1);
$to-do: #4A9FF9;
$in-progress: #f9944a;
$needs-review: #F4CE46;
$approved: #2ac06d;

* {
  box-sizing: border-box;
}

body {
  background: #FFFFFF;
  color: white;
  font-family: 'Noto San TC, Muli';
  font-weight: 300;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
}

.drag-container {
  max-width: 1000px;
  margin: 20px auto;
}

.drag-list {
  display: flex;
  align-items: flex-start;

  @media (max-width: 690px) {
    display: block;
  }
}

.drag-column {
  flex: 1;
  margin: 0 10px;
  position: relative;
  background: rgba(black, 0.2);
  overflow: hidden;

  @media (max-width: 690px) {
    margin-bottom: 30px;
  }

  h2 {
    font-size: 0.8rem;
    margin: 0;
    color: white;
    text-transform: uppercase;
    font-weight: 600;
  }

  &-to-do {

    .drag-column-header,
    .is-moved,
    .drag-options {
      background: $to-do;
    }
  }

  &-in-progress {

    .drag-column-header,
    .is-moved,
    .drag-options {
      background: $in-progress;
    }
  }

  &-needs-review {

    .drag-column-header,
    .is-moved,
    .drag-options {
      background: $needs-review;
    }
  }

  &-approved {

    .drag-column-header,
    .is-moved,
    .drag-options {
      background: $approved;
    }
  }
}

.drag-column-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px;
}

.drag-inner-list {
  position: relative;
  border: 5px solid transparent;
  min-height: 95px;
}

.drag-item {
  margin: 5px;
  position: relative;
  padding: 10px;
  background: #fff;
  border-radius: 4px;
  cursor: pointer;
  -webkit-box-shadow: 0px 1px 3px 0 rgba(0, 0, 0, 0.2);
  box-shadow: 0px 1px 3px 0 rgba(0, 0, 0, 0.2);
  transition: $ease-out;

  &.is-moving {
    transform: scale(1.1);
    background: rgba(black, 0.2);
  }

  p {
    display: block;
    color: black;
    margin-block-start: 0px;
    margin-block-end:...

JavaScript

dragula([
    document.getElementById('1'),
    document.getElementById('2'),
    document.getElementById('3'),
    document.getElementById('4'),
    document.getElementById('5')
  ])

  .on('drag', function(el) {

    // add 'is-moving' class to element being dragged
    el.classList.add('is-moving');
  })
  .on('dragend', function(el) {

    // remove 'is-moving' class from element after dragging has stopped
    el.classList.remove('is-moving');

    // add the 'is-moved' class for 600ms then remove it
    window.setTimeout(function() {
      el.classList.add('is-moved');
      window.setTimeout(function() {
        el.classList.remove('is-moved');
      }, 600);
    }, 100);
  });


var createOptions = (function() {
  var dragOptions = document.querySelectorAll('.drag-options');

  // these strings are used for the checkbox labels
  var options = ['模板', '頻道', '機械人'];

  // create the checkbox and labels here, just to keep the html clean. append the <label> to '.drag-options'
  function create() {
    for (var i = 0; i < dragOptions.length; i++) {

      options.forEach(function(item) {
        var checkbox = document.createElement('input');
        var label = document.createElement('label');
        var span = document.createElement('span');
        checkbox.setAttribute('type', 'checkbox');
        span.innerHTML = item;
        label.appendChild(span);
        label.insertBefore(checkbox, label.firstChild);
        label.classList.add('drag-options-label');
        dragOptions[i].appendChild(label);
      });

    }
  }

  return {
    create: create
  }


}());

var showOptions = (function() {

  // the 3 dot icon
  var more = document.querySelectorAll('.drag-header-more');

  function show() {
    // show 'drag-options' div when the more icon is clicked
    var target = this.getAttribute('data-target');
    var options = document.getElementById(target);
    options.classList.toggle('active');
  }


  function init() {
    for (i = 0; i < more.length; i++)...