JSFiddle - React, Tailwind, and code Playground

by John Doe

HTML

<div class="container">
   
   <div>
      <h3>Sortable List 1</h3>
      <ul id="list-1" class="sortable">
         <li id="item-1">Item 01 &nbsp;-&nbsp; <a href="#1">Link 01</a></li>
         <li id="item-2">Item 02 &nbsp;-&nbsp; <a href="#2">Link 02</a></li>
         <li id="item-3">Item 03 &nbsp;-&nbsp; <a href="#3">Link 03</a></li>
         <li id="item-4">Item 04 &nbsp;-&nbsp; <a href="#4">Link 04</a></li>
         <li id="item-5">Item 05 &nbsp;-&nbsp; <a href="#5">Link 05</a></li>
         <li id="item-6">Item 06 &nbsp;-&nbsp; <a href="#6">Link 06</a></li>
      </ul>
   </div>
   
   <div>
      <h3>Sortable List 2</h3>
      <ul id="list-2" class="sortable">
         <li id="item-7">Item 07 &nbsp;-&nbsp; <a href="#7">Link 07</a></li>
         <li id="item-8">Item 08 &nbsp;-&nbsp; <a href="#8">Link 08</a></li>
         <li id="item-9">Item 09 &nbsp;-&nbsp; <a href="#9">Link 09</a></li>
         <li id="item-10">Item 10 &nbsp;-&nbsp; <a href="#10">Link 10</a></li>
         <li id="item-11">Item 11 &nbsp;-&nbsp; <a href="#11">Link 11</a></li>
         <li id="item-12">Item 12 &nbsp;-&nbsp; <a href="#12">Link 12</a></li>
      </ul>
   </div>
   
</div>

SCSS

*, *:before, *:after {
   box-sizing: border-box;
   transition: 
      background-color 400ms ease-out, 
      border-color 400ms ease-out, 
      color 400ms ease-out;
}

html, body {
   display: block; 
   height: 100%; 
}

body {
   overflow: hidden; 
   overflow-y: scroll;
   font-family: Arial, sans-serif;
   background: rgb(252,255,244);
   background: radial-gradient( ellipse at center, rgba(252,255,244,1) 0%,rgba(223,229,215,1) 50%,rgba(179,190,173,1) 100% );
}

.container {
   display: flex;
   flex-direction: row;
   align-items: center;
   justify-content: space-around;
   width: 100%;
   height: 100%; 
   max-width: 800px; 
   margin: 0 auto;
   
   div {
      flex-basis: 40%; 
      
      h3 {
         font-weight: bold; 
         font-size: 150%; 
         line-height: 1em; 
      }
   }
}

ul.sortable {
   display: block; 
   list-style: none; 
   background: #e0e0e0;
   border-radius: 4px;
   box-shadow: 0 0 40px rgba(0,0,0,0.4);
   margin: 20px 0;
   padding: 5px;
   
   & > li {
      display: block;
      margin: 1px; 
      padding: 10px;
      border-radius: 4px;
      cursor: move;
      background: #fff;
      
      &:hover {
         background: #ffffe0;
      }
      &.active {
         background: #ccc;
      }
      &.dragging {
         background: #e0ffff;
         box-shadow: 0 4px 20px rgba(0,0,0,0.5);
         opacity: 0.5;
      }
   }
}

JavaScript

(function( name, factory ) {
   
   if( typeof window === "object" ) {
      
      // add to window 
      window[ name ] = factory();
      
      // add jquery plugin, if available  
      if( typeof jQuery === "object" ) {
         jQuery.fn[ name ] = function( options ) {
            return this.each( function() {
               new window[ name ]( this, options );
            });
         };
      }
   }
    
})( "Sortable", function() {
   
   var _w = window,
       _b = document.body,
       _d = document.documentElement;
   
   // get position of mouse/touch in relation to viewport 
   var getPoint = function( e )
   {
      var scrollX = Math.max( 0, _w.pageXOffset || _d.scrollLeft || _b.scrollLeft || 0 ) - ( _d.clientLeft || 0 ), 
          scrollY = Math.max( 0, _w.pageYOffset || _d.scrollTop || _b.scrollTop || 0 ) - ( _d.clientTop || 0 ), 
          pointX  = e ? ( Math.max( 0, e.pageX || e.clientX || 0 ) - scrollX ) : 0,
          pointY  = e ? ( Math.max( 0, e.pageY || e.clientY || 0 ) - scrollY ) : 0; 
      
      return { x: pointX, y: pointY }; 
   }; 
   
   // class constructor
   var Factory = function( container, options )
   {
      if( container && container instanceof Element )
      {
         this._container = container; 
         this._options   = options || {}; /* nothing atm */
         this._clickItem = null;
         this._dragItem  = null;
         this._hovItem   = null;
         this._sortLists = [];
         this._click     = {};
         this._dragging  = false;
         
         this._container.setAttribute( "data-is-sortable", 1 );
         this._container.style["position"] = "static";
         
         window.addEventListener( "mousedown", this._onPress.bind( this ), true );
         window.addEventListener( "touchstart", this._onPress.bind( this ), true );
         window.addEventListener( "mouseup", this._onRelease.bind( this ), true ); 
         window.addEventListener( "touchend", this._onRelease.bind( this ), true...