WEEK PLANNER CALENDAR
by whelkaholism
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="events">
<div class="event new">
<input type="text" data-bind="value: NewText" /> -
<input type="date" data-bind="value: NewFrom" /> -
<input type="date" data-bind="value: NewTo" />
<label><input type="checkbox" data-bind="checked: NewIsConfirmed" />
Confirmed?
</label>
<button data-bind="click: addEvent">+ Add</button>
</div>
</div>
<div class="weeks">
<div class="week legend">
<div class="day">Mon</div>
<div class="day">Tue</div>
<div class="day">Wed</div>
<div class="day">Thu</div>
<div class="day">Fri</div>
<div class="day">Sat</div>
<div class="day">Sun</div>
</div>
<!-- ko foreach: Weeks -->
<div class="week">
<!-- ko foreach: Days -->
<div class="day" data-bind="css: Day + (IsFirstOfMonth ? ' first-of-month' : '') + (IsEvenMonth ? ' even-month' : '')">
<div class="label">
<span class="date" data-bind="text: Date"></span>
<span class="month" data-bind="text: Month, visible: IsFirstOfMonth"></span>
<div class="day-events">
<!-- ko foreach: $parents[1].getDayEvents($data) -->
<span class="day-event" data-bind="text: Text, style: { 'background-color': IsConfirmed ? Color : null, border: IsConfirmed ? null : '1px dotted ' + Color, color: IsConfirmed ? '#ffffff' : Color }, css: { confirmed: IsConfirmed }"></span>
<!-- /ko -->
</div>
</div>
</div>
<!-- /ko -->
</div><!-- /ko -->
</div>
CSS
body {
padding: 10px;
font-family: Arial, Helvetica, sans-serif;
}
div {
box-sizing: border-box;
}
.weeks {
display: flex;
flex-direction: column;
width: 100%;
}
.week {
display: flex;
justify-content: stretch;
height: calc(10vh);
}
.week.legend {
border-bottom: 3px solid #808080;
}
.day {
display: flex;
width: calc(100% / 7);
flex-grow: 0;
flex-shrink: 0;
border-left: 1px solid #808080;
border-bottom: 1px solid #808080;
overflow: hidden;
}
.day.first-of-month {
border-left: 6px solid #808080 !important;
font-weight: bold;
}
.day.Saturday, .day.Sunday {
background: #dddddd;
}
.day.even-month {
background: #eeeeee;
}
.day.even-month.Saturday, .day.even-month.Sunday {
background: #cccccc;
}
.day.Saturday {
border-left: 3px solid #808080;
}
.day:last-child {
border-right: 1px solid #808080;
}
.week:first-child .day {
border-top: 1px solid #808080;
}
.day .label, .week.legend .day {
padding: 10px;
}
.day-events {
display: flex;
flex-direction: column;
width: 100%;
}
.day-event {
width: 100%;
font-size: 11px;
padding: 2px;
margin-bottom: 2px;
}
.day-event:last-child {
margin-bottom: 0;
}
JavaScript
moment.updateLocale('en', {
week : {
dow : 1
}
});
var Model = function()
{
var _this = this;
var _colors = [
'#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075'
];
var _coloridx = 0;
this.Start = ko.observable({ dt: ko.observable('2020-07-20') });
this.End = ko.observable({ dt: ko.observable('2020-09-02') });
this.NewText = ko.observable();
this.NewFrom = ko.observable();
this.NewTo = ko.observable();
this.NewIsConfirmed = ko.observable();
this.Weeks = ko.observableArray();
this.Events = ko.observableArray();
var _eventcolors = {};
this.ActualStart = ko.computed(
function()
{
var mom = moment(_this.Start().dt());
mom.startOf('week');
return { dt: ko.observable(mom.format('YYYY-MM-DD')) };
}
);
this.ActualEnd = ko.computed(
function()
{
var mom = moment(_this.End().dt());
mom.endOf('week');
return { dt: ko.observable(mom.format('YYYY-MM-DD')) };
}
);
this.addEvent = function()
{
var m = /^([^\(]+)/.exec(_this.NewText());
var label = m[1];
label = label.replace(/ +$/, '');
var color = _eventcolors[label];
if(!color)
{
color = _colors[(_coloridx++) % _colors.length];
_eventcolors[label] = color;
}
_this.Events.push({
Text: _this.NewText(),
Label: label,
Color: color,
From: { dt: ko.observable(_this.NewFrom()) },
To: { dt: ko.observable(_this.NewTo()) },
IsConfirmed: _this.NewIsConfirmed()
});
_this.NewText('');
_this.NewFrom('');
_this.NewTo('');
_this.NewIsConfirmed(false);
};
this.getDayEvents = function(day)
{
var res = [];
_this.Events().forEach(function(evt)
{
...