Kick.js Draggable, droppable, and sortable list

by Ritesh Pahwa

HTML

<script src="https://cdn.jsdelivr.net/gh/radkick/kickjs/dist/kick.min.js"></script>
<div kick-app>
  <div>
    <div id="main">
    <h2>High Priority Tasks</h2>
    <div id="items1" class="items" :sort-list="highPriorityTasks">
      <div class="item" *item="highPriorityTasks" :sort="setSort(item,highPriorityTasks)">
        <a href="#" :="item.name" ^="selectTask(item)" -="show(item)"></a>
        <input @="item.name" ^blur="blur(item)" +="show(item)" />
      </div>
    </div>

    <!--<div class="container high" data-bind="template: { name: 'taskTmpl', foreach: highPriorityTasks, templateOptions: { parentList: highPriorityTasks} }, sortableList: highPriorityTasks"></div>-->

    <h2>Tasks</h2>
    <div id="items2" class="items" :sort-list="normalPriorityTasks">
      <div class="item" *item="normalPriorityTasks" :sort="setSort(item,normalPriorityTasks)">
        <a href="#" :="item.name" ^="selectTask(item)" -="item.show"></a>
        <input @="item.name" ^blur="blur(item)" +="item.show" />
      </div>
    </div>
    <a href="#" ^="addTask">Add Task</a>

    <h2>Trash Bin</h2>
    <div class="items container" :sort-list="trash">
    
    </div>
      </div>

    <div id="results">
      <h2>High Priority Tasks</h2>
      <ul>
        <li *item="highPriorityTasks" :="item.name" />
      </ul>
      <hr>
      <h2>Tasks</h2>
      <ul>
        <li *item="normalPriorityTasks" :="item.name" />
      </ul>
      <h2>Bin</h2>
      <ul>
        <li *item="trash" :="item.name" />
      </ul>
    </div>


  </div>

</div>

  <!-- Not used from ko example -->
  <script id="taskTmpl" type="text/html">
    <div class="item" data-bind="sortableItem: { item: $data, parentList: $item.parentList }">
      <a href="#" data-bind="text: name, click: function() { viewModel.selectTask($data); }, visible: $data !== viewModel.selectedTask()"></a>
      <input data-bind="value: name, visibleAndSelect: $data === viewModel.selectedTask(), event: { blur: function() {...

CSS

body {
  font-family: arial;
}

h2 {
  font-weight: bold;
}

div {
  padding: 5px;
  margin: 5px;
  border: #eee 1px solid;
}

p,
a {
  font-size: .8em;
}

ul {
  padding-bottom: 10px;
}

.container {
  width: 125px;
  min-height: 50px;
  background-color: #AAA;
}

.high {
  background-color: red;
}

.trash {
  background-color: #000;
}

.item {
  background-color: #DDD;
  cursor: move;
}

.item input {
  width: 100px;
}

#main {
  float: left;
}

#results {
  margin-left: 175px;
  width: 150px;
}

JavaScript

var vm = {
  highPriorityTasks: [{
      name: "Get dog food"
    },
    {
      name: "Mow lawn"
    },
    {
      name: "Fix car"
    }
  ],
  normalPriorityTasks: [{
      name: "Fix fence"
    },
    {
      name: "Walk dog"
    },
    {
      name: "Read book"
    }
  ],
  setSort: function(item, list) {
    return {
      item: item,
      list: list
    };
  },
  selectedTask: null,
  blur: function(item) {
    vm.selectTask = null;
    item.show = false;
  },
  selectTask: function(task, sel) {
    vm.selectedTask = task;
    task && (task.show = true);
  },
  show: function(item) {
    item !== vm.selectedTask;

  },
  addTask: function() {
    var task = {
      name: "new",
      show: true
    };
    vm.selectedTask = task;
    vm.normalPriorityTasks.push(task);
  },
  trash: []
};

kick.binders[':sort-list'] = function(el, list) {
  $(el).data("list", list)
    .sortable({
      revert: true,
      helper: function(e, u) {
        return $('<div class="item"></div>')
      },
      stop: function(event, ui) {
        var item = ui.item.data("item");
        if (item) {
          //identify parents
          var originalParent = ui.item.data("list");
          var newParent = ui.item.parent().data("list");
          //figure out its new position
          var position = ui.item.parent().children().index(ui.item);
          if (position >= 0) {
            // Cancel is important it helps in keeping the item attached to binder element
            // https://stackoverflow.com/questions/14199681/jquery-ui-sortable-move-clone-but-keep-original
            $(this).sortable('cancel');
            originalParent.splice(originalParent.indexOf(item), 1);
            if(newParent){newParent.splice(position, 0, item);}
            ui.item.data("list", newParent);
          }

          ui.item.remove();
        }
      },
      connectWith: '.items'
    });
}
kick.binders[':sort'] = function(el, v) {
  $(el).data("item", v.item);
  $(el).data("list", v.list);
 ...