JSFiddle - React, Tailwind, and code Playground

by neonDog

HTML

<div class="btn" data-action="reset">Reset</div>
<div class="btn" data-filter-attr="data-color" data-color="pink">Pink</div>
<div class="btn" data-filter-attr="data-color" data-color="purple">Purple</div>
<div class="btn" data-filter-attr="data-color" data-color="red">Red</div>
<div class="btn" data-filter-attr="data-color" data-color="orange">Orange</div>

<div id="testparent" class="row">
  
  <div data-id="1" data-color="pink" class="col card w-50">Test 1</div>
  
  <div data-id="2" data-color="orange" class="col card h-30">Test 2</div>
  
  <div data-id="3" data-color="red" class="col card">Test 3</div>
  
  <div data-id="4" data-color="orange" class="col card">Test 4</div>
  
  <div data-id="5" data-color="pink" class="col card">Test 5</div>
  
  <div data-id="6" data-color="pink" class="col card">Test 6</div>
  
  <div data-id="7" data-color="purple" class="col card">Test 7</div>
  
  <div data-id="8" data-color="orange" class="col card">Test 8</div>
  
  <div data-id="9" data-color="purple" class="col card">Test 9</div>
  
  <div data-id="10" data-color="red" class="col card">Test 10</div>
  
</div>

CSS

* {
  box-sizing: border-box;
}

.col {
  width: 200px;
  height: 150px;
  background: pink;
  margin: 5px;
  padding: 10px;
  display: inline-block;
  font-size: 30px;
  transition: 1s all ease;
}
.w-500 {
  width: 500px;
}
.h-300 {
  height: 300px;
}

[data-color="green"] {
  background: green;
}
[data-color="red"] {
  background: red;
}
[data-color="orange"] {
  background: orange;
}
[data-color="purple"] {
  background: purple;
}

.btn {
  padding: 10px;
  background: #eee;
  border: 1px #555 solid;
  display: inline-block;
  margin: 4px;
}

.d-none {
  display: none;
}

JavaScript

const arrayMoveIndex = function(arr, from, to) {
	return arr.splice(to, 0, arr.splice(from, 1)[0]);
};

const forceArray = function (value) {
    if (Array.isArray(value)) return value;
    return [value];
};

const swapOrders = function(els, newOrder=[], actions=[], elBounds=[], parentEl=null, settings={}) {
	
  const {removeEasing, addEasing, swapEasing, duration, afterRemove} = settings;
  
  const currentOrder = els.map(el => el.getAttribute('data-id'));
  
  if (!parentEl) parentEl = els[0].parentElement;
  
  if (!elBounds || elBounds.length !== els.length) {
  	elBounds = Array(els.length).fill({});
    
    //Get current element bounds...
  	for (let i=0, l=newOrder.length; i < l; i++) {
    	
    	let id = newOrder[i];
      const index = currentOrder.indexOf(id);
      if (index === -1) continue;
      
      elBounds[index] = els[index].getBoundingClientRect();
      
    }
    
  }
  
  
  for (let i=0, l=newOrder.length; i < l; i++) {
  	
    const id = newOrder[i];
    let index = currentOrder.indexOf(id);
    let newIndex = i;
    if (!elBounds[index]) continue; //Skip if already in the correct index or ID not found
    const action = actions?.[i];
    
    let el= els[index];
    
    if (action === 'add') {
    	el = parentEl.insertBefore(el, parentEl.children[i]);
    	animateAdd(el, el.getBoundingClientRect(), elBounds[index], duration, addEasing);
      
    } else if (action === 'remove') {
    
      //Move to the end...
      parentEl.append(el);
			animateRemove(el, elBounds[index], duration, removeEasing, afterRemove);
      newIndex = newOrder.length;
      
    } else {
    	
    	if (index !== i) {
   			//Move element to index
        el = parentEl.insertBefore(el, parentEl.children[i]);
      }
      //Animate the movement
    	animateSwap(el, el.getBoundingClientRect(), elBounds[index], duration, swapEasing);
    
    }
    
    
    if (index !== newIndex) {
      //Update currentOrder
      arrayMoveIndex(currentOrder, index,...