JSFiddle - React, Tailwind, and code Playground

by tonyleeper

HTML

<div class="calendar">
    
    <button class="today" data-bind="text: resources['Today'],
                                     click: gotoToday"></button>
    
    <div class="month">
        <button class="previous" data-bind="click: gotoPreviousMonth">&lt;</button>
        <span data-bind="text: resources['Month.' + month()]"></span>
        <button class="next" data-bind="click: gotoNextMonth">&gt;</button>
    </div>
    
    <div class="year">
        <button class="previous" data-bind="click: gotoPreviousYear">&lt;</button>
        <span data-bind="text: year()"></span>
        <button class="next" data-bind="click: gotoNextYear">&gt;</button>
    </div>
    
    <div class="weekdays">
        <div class="row">
            <div class="cell">MON</div>
            <div class="cell">TUE</div>
            <div class="cell">WED</div>
            <div class="cell">THU</div>
            <div class="cell">FRI</div>
            <div class="cell">SAT</div>
            <div class="cell">SUN</div>
        </div>
    </div>
    
    <div class="dates" data-bind="foreach: weeks">
        <div class="row" data-bind="foreach: dates">
            <div class="cell" data-bind="text: $data.date === 1 ? resources['Month.' + $data.month] + ' ' + $data.date : $data.date, css: { 'current-month': $data.month === $root.month(), 'today': $data.date === $root.today().getDate() && $data.month === $root.today().getMonth() && $root.today().getFullYear() === $root.year() }"></div>
        </div>
    </div>
</div>

CSS

html, body {
    width: 95%;
    height: 95%;
    margin: 0;
    padding: 0;
}

.calendar {
    text-align: center;
    cursor: default;
    -webkit-user-select: none;
    height: 100%;
    width: 100%;
    min-height: 100px;
    min-width: 100px;
}

.year, .month {
    display: inline-block;
}

.year span, .month span {
    display: inline-block;
    width: 100px;
}

.weekdays {
    display: table;
    table-layout: fixed;
    width: 100%;
    border-top: 1px solid lightgray;
    border-left: 1px solid lightgray;
}

.dates {
    display: table;
    table-layout: fixed;
    width: 100%;
    height: 100%;
    border-left: 1px solid lightgray;
}

.row {
    display: table-row;
}

.cell {
    white-space: nowrap;
    height: 16%;
    display: table-cell;
    overflow: hidden;
    border-right: 1px solid lightgray;
    border-bottom: 1px solid lightgray;
}

.dates .cell {
    outline: 0;
    text-align: right;
    color: lightgray;
}

.dates .cell.current-month {
    color: black;
}

.dates .cell.today {
    background-color: lightblue;
}

.weekdays .cell {
    height: 20px;
    text-align: center;
}

/*.cell:active, .cell.focus {
    background-color: darkblue;
    color: white;
}*/

JavaScript

/**
    calendar view concept
*/

var CalendarViewModel = function () {
    
    var self = this;
       
    self.today = ko.observable(new Date());
    self.date = ko.observable(self.today().getDate());
    self.month = ko.observable(self.today().getMonth());
    self.year = ko.observable(self.today().getFullYear());
       
    self.getFirstDayOfMonth = function () {
        var firstOfMonth = new Date(self.year(), self.month(), 1);
        var jsDay = firstOfMonth.getDay();
        
        return jsDay === 0 ? 6 : jsDay - 1; // convert from sun (0) - sat (6) to mon (0) - sun (6)
    };
    
    self.getNumberOfDaysInMonth = function (month) {
        var lastOfMonth = new Date(2013, month + 1, 0);
        return lastOfMonth.getDate();
    };
    
    self.weekCount = function () {
        return Math.ceil((self.getFirstDayOfMonth() + self.getNumberOfDaysInMonth(self.month())) / 7);
    };
    
    self.weeks = ko.computed(function () {
        var weeks = [];
        
        var firstWeek = {
            dates: []
        };
        
        // fill in the correct dates for this month
        var date = 1;
        var month = self.month();
        var firstDayOfMonth = self.getFirstDayOfMonth();
        var previousMonth = self.month() === 0 ? 11 : self.month() - 1;
        var daysInPreviousMonth = self.getNumberOfDaysInMonth(previousMonth);
        firstWeek.dates[firstDayOfMonth] = {
            date: date++,
            month: month
        };
        for (var i = firstDayOfMonth - 1; i >= 0; i--) {
            firstWeek.dates[i] = {
                date: daysInPreviousMonth--,
                month: previousMonth
            };
        }
        for (var i = firstDayOfMonth + 1; i < 7; i++) {
            firstWeek.dates[i] = {
                date: date++,
                month: month
            };
        }
        
        // add the first week
        weeks.push(firstWeek);
        
        // add the remaining weeks
        var daysInMonth =...