JSFiddle - React, Tailwind, and code Playground

by tonyleeper

HTML

<script src="//cdnjs.cloudflare.com/ajax/libs/less.js/1.4.1/less.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>

<h1>Date Selector POC</h1>

<br />
<section class="day-selector" data-bind="dateSelector: null">
    <div class="row">
        <div class="cell">
            <button class="left" data-bind="click: selectPreviousDate"> <i class="triangle-left"></i>&nbsp;</button>
        </div>
        <div class="cell">
            <ul tabindex="0" data-bind="foreach: dates,
              onKeyLeftArrow: selectPreviousDate,
              onKeyRightArrow: selectNextDate
            ">
                <li>
                    <div class="date" data-bind="
                      css: { 
                        'month-start': date === 1,
                        'selected': $root.isSelectedDate($data)
                      },
                      click: $root.selectDate.bind($root)
                    ">
                        <div data-bind="text: month, visible: date === 1 || $root.isSelectedDate($data)"></div>
                        <div data-bind="text: date"></div>
                    </div>
                </li>
            </ul>
        </div>
        <div class="cell">
            <button class="right" data-bind="click: selectNextDate"> <i class="triangle-right"></i>&nbsp;</button>
        </div>
    </div>
</section>
<br />
<p data-bind="text: 'The selected date is ' + selectedDate().date + ' ' + selectedDate().month"></p>

<div class="dragTest" data-bind="dragTest: null"></div>

CSS

@height: 27px;
 html, body {
    height: 100%;
    padding: 5px;
    cursor: default;
}
* {
    box-sizing: border-box;
    -webkit-user-select: none;
    padding: 0;
    margin: 0;
}
.day-selector {
    width: 100%;
    min-width: 800px;
    display: table;
    table-layout: fixed;
    font-family: arial;
    font-size: 11px;
    color: white;
    font-weight: bold;
    -webkit-transform: translateZ(0); /* speed hack */
}
.row {
    display: table-row;
    height: 100%;
}
.cell {
    display: table-cell;
    vertical-align: middle;
}
.cell:first-child, .cell:last-child {
    width: 30px;
}
button {
    width: 100%;
    height: @height;
    border: 1px solid #aaa;
    background: -webkit-linear-gradient(top, #f4f4f4 4%, #e2e2e2 4%, #e2e2e2 96%, #d6d6d6 96%);
    position: relative;
}
button.left {
    border-radius: 0;
    border-top-left-radius: 2px;
    border-bottom-left-radius: 2px;
}
button.right {
    border-radius: 0;
    border-top-right-radius: 2px;
    border-bottom-right-radius: 2px;
}
.date {
    border: 0;
    border-top: 1px solid #aaa;
    border-bottom: 1px solid #aaa;
    height: @height;
    text-align: center;
    background-color: #878787;
    box-shadow: inset 0 1px 0 0 #454545, inset 0 -1px 0 0 #454545;
}
.date.month-start {
    border-left: 1px solid #aaa;
    box-shadow: inset 1px 0 0 0 #454545, inset 0 1px 0 0 #454545, inset 0 -1px 0 0 #454545;
}
li:first-child > .date {
    box-shadow: inset 1px 0 0 0 #454545, inset 0 1px 0 0 #454545, inset 0 -1px 0 0 #454545;
    border-left: 0;
}
li:last-child > .date {
    box-shadow: inset -1px 0 0 0 #454545, inset 0 1px 0 0 #454545, inset 0 -1px 0 0 #454545;
}
li > .date.selected {
    background-color: #e2e2e2;
    color: black;
    text-shadow: 0 1px #fff;
    box-shadow: 0 7px 10px -10px #262c39, inset 0 -1px 0 0 #d6d6d6, inset 0 1px 0 0 #f4f4f4;
    cursor: ew-resize;
    border-left: 1px solid #878787;
    border-right: 1px solid #878787;
    &:hover {
        background-color: #f4f4f4;
       ...

JavaScript

/**
    Date Selector POC
*/

// LESS SHIM
(function () {
    $('head style[type="text/css"]').attr('type', 'text/less');
    less.refreshStyles();
}());

ko.keyBindings = [{
    keyCode: 37,
    binding: "onKeyLeftArrow"
}, {
    keyCode: 38,
    binding: "onKeyUpArrow"
}, {
    keyCode: 39,
    binding: "onKeyRightArrow"
}, {
    keyCode: 40,
    binding: "onKeyDownArrow"
}, {
    keyCode: 13,
    binding: "onKeyEnter"
}, {
    keyCode: 32,
    binding: "onKeySpace"
}, {
    keyCode: 8,
    binding: "onKeyBackspace"
}];

for (var i = 0; i < ko.keyBindings.length; i++) {
    ko.bindingHandlers[ko.keyBindings[i].binding] = (function () {
        var keyCode = ko.keyBindings[i].keyCode;
        var binding = ko.keyBindings[i].binding;

        return {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
                element.addEventListener("keydown", function (event) {
                    if (event.keyCode === keyCode) {
                        allBindingsAccessor()[binding].call(viewModel);
                        event.preventDefault();
                    }
                });
            }
        };
    }());
}

(function($){
    $.fn.draggable = function(onDrag) {
        return this.each(function () {
            var $this = $(this);
            var element = this;
            var $document = $(document);
            
            $this.on("mousedown touchstart", function(event){
                element.dragging = true;
                // prevent scrolling on touch devices
                if (event.type === 'touchstart') {
                    event.preventDefault();
                }
            });
                    
            $document.on("mouseup touchend", function () {
                element.dragging = false;
            });
            
            $document.on("mousemove touchmove", function(event){
                if (element.dragging){
                    // throttle drag events to 60 per second
           ...