List

by levchenko_d

HTML

<script src="https://beat.today/app/js/app.min.js"></script>
<div class="list-wrapper">
  <div class="list-actions">
    <a class="check-all">Select all</a>
    &nbsp;
    <a class="uncheck-all">Deselect all</a>
    
    &nbsp;
    <a class="delete">Delete selected</a>
    &nbsp;
    <span class="check-counter">0</span>
    &nbsp;
    <a class="append">Append</a>
    &nbsp;
    <a class="prepend">Prepend</a>
    
  </div>
  
  
  <div class="list-empty __hide">
    <p class="text">There are no list items</p>
  </div>
  
  <div class="list-waiting-data __hide">
    <p class="text">Loading...</p>
  </div>
  
  <div class="load-more-indicator __hide">
    <div class="text text-center">Loading more...</div>
  </div>
  
  <ul class="list bordered">
    

  </ul>

</div>
<!-- <li class="list-item">
      <div class="item-checker">
        <input type="checkbox">
        <i class="indicator"></i>
      </div>
      <div class="item-details">
        <div class="prefix">a</div>
        <div class="postfix" data-list-item-delete="true">X</div>
        <div class="content">content</div>
      </div>
    </li>
    
    
    <li class="list-item">
      <div class="item-checker">
        <input type="checkbox">
        <i class="indicator"></i>
      </div>
      <div class="item-details">
        <div class="prefix">a</div>
        <div class="content">content</div>
      </div>
    </li>
    
    
    <li class="list-item">
      <div class="item-checker">
        <input type="checkbox">
        <i class="indicator"></i>
      </div>
      <div class="item-details">
        <div class="prefix">a</div>
        <div class="postfix" data-list-item-delete="true">X</div>
        <div class="content">content</div>
      </div>
    </li>
    
    
    <li class="list-item">
      <div class="item-checker">
        <input type="checkbox">
        <i class="indicator"></i>
      </div>
      <div class="item-details">
        <div class="postfix"...

SCSS

.__hide{
  display: none !important;
}

*{
  box-sizing: border-box;
}

.text-center{
  text-align: center;
}

.list{
height: 300px;
  overflow-y: scroll;
  border: solid 1px silver;
}

.hidden{
  visibility: hidden !important;
}

/* ------ IGNORE ABOVE ------ */


$blue: #2C9EE4;

$listBorder: #f2f2f2;
$listHoverBG: #f2f3f4;
$listActiveBG: #e6e8e9;

$fix_width: 40px;
$height: 40px;
$radius: 7px;
$bottom_spacing: 5px;
$content_padding_x: 10px;
$content_padding_y: 0;
$checker_width: 20px;
$indicator_size: 5px;
$indicator_active_size: 10px;

.list-actions{
  border: solid 1px $listBorder;
  margin-bottom: $bottom_spacing * 3;
  padding: $content_padding_x;
}

.list-wrapper.selecting-list-items *{
  -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
     -khtml-user-select: none; /* Konqueror HTML */
       -moz-user-select: none; /* Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
            user-select: none; /* Non-prefixed version, currently
                                  supported by Chrome and Opera */
}

.list-empty{

}

.list-wrapper .load-more-indicator{
  padding: 10px;
}

.list {
  
  margin: 0;
  padding: 0;
  
  .list-item {
     list-style-type: none;
     padding-bottom: $bottom_spacing;
     
     .item-checker{
       position: relative;
       float: left;
       width: $checker_width;
       height: $height;
       line-height: $height;
       cursor: pointer;
       
       input[type="checkbox"],
       .indicator{
         position: absolute;
         top: 0;
         right: 0;
         bottom: 0;
         left: 0;
         margin: auto;
       }
       
       input[type="checkbox"]{
         display: none;
         z-index: 9;
         opacity: 0;
         cursor: pointer;
         width: 100%;
         height: 100%;
       }
       
       .indicator{
         background: $listBorder;
         width: $indicator_size;
         height: $indicator_size;
         border-radius:...

JavaScript

console.clear();

/*

Cluster thing.

invisibleA
 ----
|    |
|    | Visible
|    |
 ----
invisibleB

onScroll down:
1. Find invisible doms until visible
2. Store them in clusterAStore with height and width value
3. Apply common height to insivleA
4. Remove invisible doms
5. Find new cluster in invisibleB by looping clusterBStore from the beggining until new elements will be invisible

onScroll up:
1. Find invisible doms until visible
2. Store them in clusterBStore with height and width value
3. Apply common height to insivleB
4. Remove invisible doms
5. Find new cluster in invisibleA by looping clusterAStore from the end until new elements will be invisible

In this case, first listLoad might be heavy, but no extra loops required.
Plus list items might be different height
*/


(function(){

App.supportsPassive = false;

try {
  var opts = Object.defineProperty({}, 'passive', {
    get: function() {
      App.supportsPassive = true;
    }
  });
  window.addEventListener("testPassive", null, opts);
  window.removeEventListener("testPassive", null, opts);
} catch (e) {};

App.passiveEvent = App.supportsPassive ? {'passive': true} : false;


function setScrollTop(obj, position){
  if(obj === document.body){
    return document.body.scrollTop = position;
  } else if(obj === window){
    return window.scrollTo(0, position);
  } else {
    return obj.scrollTop = position;
  }
  
}
App.setScrollTop = setScrollTop;


function getScrollHeight(obj){
  var body = document.body,
      html = document.documentElement;
      
  if(obj === window || body || html){
    return Math.max(
                      body.scrollHeight, 
                      body.offsetHeight, 
                      html.clientHeight, 
                      html.scrollHeight, 
                      html.offsetHeight
                    );
  } else {
    return obj.scrollHeight;
  }
}
App.getScrollHeight = getScrollHeight;

App.async = {};
App.async.map = function(list, iteratee, callback, reverse){
	var i...