Isotope Sortable

by selahssea

HTML

<script src="http://isotope.metafizzy.co/jquery.isotope.min.js"></script>
<ul>
  <li class="isotopey large">1</li>
  <li class="isotopey">2</li>
  <li class="isotopey large">3</li>
  <li class="isotopey">4</li>
  <li class="isotopey">5</li>
  <li class="isotopey">6</li>
  <li class="isotopey">7</li>
  <li class="isotopey">8</li>
  <li class="isotopey">9</li>
  <li class="isotopey">10</li>
  <li class="isotopey">11</li>
  <li class="isotopey">12</li>
  <li class="isotopey">13</li>
  <li class="isotopey">14</li>
  <li class="isotopey">15</li>
  <li class="isotopey">16</li>
  <li class="isotopey">17</li>
  <li class="isotopey">18</li>
  <li class="isotopey">19</li>
  <li class="isotopey">20</li>
  <li class="isotopey">21</li>
  <li class="isotopey">22</li>
  <li class="isotopey">23</li>
  <li class="isotopey">24</li>  
</ul>

CSS

@import "compass";
$charcoal: #6c6c6c;
ul {
  width: 1000px;
  border: 1px solid black;
  margin: 10px;
}
li {
  width: 100px;
  height: 100px;
  background: #ccc;
  margin: 5px; 
  padding-top: 40px;
  color: white;
  font-size: 25px;
  text-shadow: 0 0 5px $charcoal;
  box-sizing: border-box;
  text-align: center;
  transition: all 500ms ease;
  transition-property: transform, top, left;
  &.ui-sortable-placeholder {
    visibility: visible!important;
    background: #aaa;
    box-shadow: inset 0 0 100px $charcoal;
    &.active {
      box-shadow: inset 0 0 200px $charcoal;                
    }
    &.starting {
      transition-property: none;
    }
  }
  &.grabbing {
    transform: rotate(3deg);            
  }
  &.moving {
    box-shadow: $charcoal 0 0 5px 2px!important;
    transition: transform 200ms ease;    
  }
}

/**** Isotope Filtering ****/

.isotope-item {
  z-index: 2;
}
.large {
    height:200px;
}
.isotope-hidden.isotope-item {
  pointer-events: none;
  z-index: 1;
}

JavaScript

var list = $('ul');
list.isotope({  
  transformsEnabled: false
  , itemSelector: '.isotopey'
  , onLayout: function() {
    list.css('overflow', 'visible');
  }  
});
list.sortable({
  cursor: 'move'
  //, tolerance: 'intersection'  //'pointer' is too janky
  , start: function(event, ui) {                        
    //add grabbing and moving classes as user has begun
    //REMOVE isotopey so that isotope does not try to sort our item,
    //resulting in the item moving around and flickering on 'change'
    ui.item.addClass('grabbing moving').removeClass('isotopey');
    
    ui.placeholder
      .addClass('starting') //adding the 'starting' class removes the transitions from the placeholder.
      //remove 'moving' class because if the user clicks on a tile they just moved,
      //the placeholder will have 'moving' class and it will mess with the transitions
      .removeClass('moving')
      //put placeholder directly below tile. 'starting' class ensures the
      //placeholder simply appears and does not 'fly' into place
      .css({
        top: ui.originalPosition.top
        , left: ui.originalPosition.left
      })
      ;
    //reload the items in their current state to override any previous
    //sorting and to include placeholder, but do NOT call a re-layout
    list.isotope('reloadItems');                    
  }                
  , change: function(event, ui) {
    //change only fires when the DOM is changed. the DOM changes when 
    //the placeholder moves up or down in the document order 
    //within the sortable container
    
    //remove 'starting' class so that placeholder can now move smoothly
    //with the interface
    ui.placeholder.removeClass('starting');
    //reload items to include the placeholder's new position in the DOM. 
    //then when you sort, everything around the placeholder moves as 
    //though the item were moving it.
    list
      .isotope('reloadItems')
      .isotope({ sortBy: 'original-order'})
    ;
  }
  ,...