JSFiddle - React, Tailwind, and code Playground

by rniemeyer

HTML

<script src="https://github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.debug.js"></script>
<script src="https://github.com/downloads/rniemeyer/knockout-sortable/knockout-sortable.js"></script>
    <div class="grid_6">
        <h2>Unscheduled</h2>
    <hr/>
        <ul id="routes" data-bind="sortable: { data: tasksByRoute, allowDrop: false }">
            <li>
                <h2 data-bind="text: name"></h2>
                <ul class="routeTasks" data-bind="sortable: { data: tasks, allowDrop: false }">
                    <li><div data-bind="text: customerName"></div></li>
                </ul>
            </li>
        </ul>
    </div>
    <hr/>
        <h2>Scheduled</h2>
    <hr/>

    <div class="grid_6">
        <ul id="scheduledtasks" data-bind="sortable:{data: scheduledTasks, beforeMove: myDropCallback, afterMove: afterMoveCallback}">
            <li>
                <span data-bind="text: customerName"></span> (<span class="order" data-bind="text: order"></span>)<span class="koLink" data-bind="click: $root.removeScheduledTask">x</span>
            </li>
        </ul>
    </div>

CSS

h2{
    font-weight: bold;
    font-size: 20px;
}

li{
    cursor: move;
}

li ul li{
    margin-left: 20px;
}


div ul >li:hover{    
   background-color: #EFEFFF
}

li ul li:hover{
   background-color: #EFFFEF
}

.koLink {
    margin-left:20px;
    color: blue;
    cursor: default;
}

.order{
    color: green;
}

JavaScript

//track an index on items in an observableArray
ko.observableArray.fn.indexed = function(prop) {
    prop = prop || 'index';
   //whenever the array changes, make one loop to update the index on each
   this.subscribe(function(newValue) {
       if (newValue) {
           var item;
           for (var i = 0, j = newValue.length; i < j; i++) {
               item = newValue[i];
               if (!ko.isObservable(item[prop])) {
                  item[prop] = ko.observable();
               }
               item[prop](i);      
           }
       }   
   }); 

   //initialize the index
   this.valueHasMutated(); 
   return this;
};

var Task = function(id, routeId, routeName, customerName, order) {
    this.id = ko.observable(id);
    this.routeId = ko.observable(routeId);
    this.routeName = ko.observable(routeName);
    this.customerName = ko.observable(customerName);
    this.order = ko.observable(order || undefined);
    this.isLocked = ko.observable(false);
};

var ViewModel = function() {
    var self = this;
    self.tasks = ko.observableArray();
    self.tasksByRoute = ko.observableArray();
    //self.tasksByRoute.id = "hey";
    self.tasks.subscribe(function(tasks) {
        var routes = [],
            routeIndex = {};
        ko.utils.arrayForEach(tasks || [], function(task) {
            var routeId = task.routeId(),
                routeTasks = routeIndex[routeId];
            //first time that we have seen this routeID
            if (!routeTasks) {
                //add it to the index, so we can find it without looping next time
                routeIndex[routeId] = routeTasks = {
                    id: routeId,
                    name: task.routeName,
                    tasks: ko.observableArray()
                };
                //add it to the array that we will eventually return
                routes.push(routeTasks);
            }
            routeTasks.tasks.push(task);
        });
        //return an array of routes that each contain...