jQuery Sortable for nested lists

by Rolf Friedrich

HTML

<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<label>Cursor At:</label>
<label id="cursor"></label>
<h3>
Targetlist
</h3>
<ul id="targetlist" class='targetlist'>
    <li class="unsortable">Unsortable Item </li>
    <li>
        <ol class="items" id="item_1">
            <li class="unsortable">Item 1</li>            
        </ol>
    </li>
    <li>
        <ol class="items" id="item_2">
            <li class="unsortable">Item 2</li>           
        </ol>
    </li>  
   
</ul>

<h3>
Sourcelist
</h3>
<ul id="sourcelist" class="secondlist">
    <li class="item sourceitem">Source item</li>
    <li class="item sourceitem">Source item</li>
    <li class="item sourceitem">Source item</li>
    <li class="item sourceitem">Source item</li>  
</ul>

CSS

ul,
.items,
.secondlist {
    list-style: none outside none;
    /* margin-bottom: 20px; */
    padding: 0;
    width: 100%;
    position: relative;
}

ol {
  list-style: none outside none;
  margin: 0;
  padding: 0;
}


ol.disabled li {
  background-color: #ffb7b7;
}

li.disabled {
  background:#f00 !important;
  display:none !important ;
}

ol.disabled .placeholder {
  display:none !important;
}

ol.enabled li{
  background: #d8ffdf;
}


ul .item,
.unsortable{
    background: #fff;
    border: 1px solid #ddd;
    cursor: move;
    /* height: 50px; */
    line-height: 50px;
    padding: 0 10px;
    width: 50%;
}

ul li:has(ol) {
  padding:0;
}

li.unsortable,
.item.unsortable {
  cursor: default !important;
  background: #fff;
  pointer-events: none;
  
}

li:hover,
.item:hover{
  /*background: #d8ffdf;*/
  cursor:move;
}


li:has(ol):hover,
.item:has(ol):hover{
  background: #fff;
}

.item:hover ol:empty{
  background: #ff0;
  min-height:2rem;
}

.item.unsortable:hover{
   cursor: not-allowed;
  background: #ffb7b7;
}


ol.open{
  min-height:4rem;
}


.nested {
    margin-left: 20px;
    width: 50%;
}
.dragHover {
    border-color: blue;
    color: blue;
}
.placeholder {
    background: #fff;
    border: 1px dashed #ccc;
    height: 50px;
	width: 50%;
}

.placeholder-nested {
    background: #fff;
    border: 1px dashed #ccc;
    height: 50px;
	width: 480px;
    margin-left: 20px;
}

.secondlist{
  min-height: 4rem;
}

JavaScript

var sortable = Sortable.create( document.getElementById('sourcelist'), {
	filter: '.unsortable',
  draggable: '.item',
  ghostClass :'placeholder-nested',
  group: {
    name: 'items',
  },
  sort: false,
  animation: 100,
  onMove: function( evt, originalEvent) {
  	
    evt.willInsertAfter= true;
    
    let condition = evt.to.children.length >= 2;
          
      if (condition) {
       	
        if( evt.dragged.classList.contains('enabled') ){
            evt.dragged.classList.remove('enabled');
        }
        
      	evt.dragged.classList.add('disabled');
      
       	if( evt.related.classList.contains('items') ) {
        	evt.related.classList.add('disabled');
        }
        else{
        	evt.related.closest('.items').classList.add('disabled');
        }
      }
      else{
      	
        if( evt.dragged.classList.contains('disabled') ){
            evt.dragged.classList.remove('disabled');
        }
        
        evt.dragged.classList.add('enabled');
        
        if( evt.related.classList.contains('items') ) {
        	 evt.related.classList.add('enabled');
        }
        else{
        	evt.related.closest('.items').classList.add('enabled');
        }
        
        //remove all 
      document.querySelectorAll('.disabled').forEach( disabled => {
        disabled.classList.remove('disabled');
      });
      
      document.querySelectorAll('ol.enabled').forEach( enabled => {
        enabled.classList.remove('enabled');
      });
      
      }
	},
  onEnd: function ( evt) { 

    //remove all 
    document.querySelectorAll('.disabled').forEach( disabled => {
      disabled.classList.remove('disabled');
    });
            
      document.querySelectorAll('.enabled').forEach( enabled => {
        enabled.classList.remove('enabled');
      });
    
    	let condition = evt.to.children.length <= 2;
          
      if (!condition) {
      	
        //remove "disabled" class on cancel
        if(...