Skip to content

Instantly share code, notes, and snippets.

@buroz
Last active May 26, 2021 13:03
Show Gist options
  • Save buroz/e6ec239762e316f53f4c57432148b25b to your computer and use it in GitHub Desktop.
Save buroz/e6ec239762e316f53f4c57432148b25b to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.startDate {
border-left: 3px solid #d1d5db;
}
.endDate {
border-right: 3px solid #d1d5db;
}
.day.is-valid:hover {
color: white !important;
}
</style>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" />
</head>
<body>
<div id="app" class="container mx-auto">
<div class="flex w-full flex-col justify-center items-center">
<button
class="bg-blue-500 hover:bg-blue-700 text-white p-2 rounded shadow"
@click="toggleUniversal"
>
{{ universal ? "Universal" : "Local" }}
</button>
<p>
{{ selected.startDate && selected.startDate.format('DD/MM/YYYY') || "-" }} to {{
selected.endDate && selected.endDate.format('DD/MM/YYYY') || "-" }}
</p>
</div>
<section class="w-full flex justify-between justify-items-center content-center items-center">
<button
class="bg-blue-500 hover:bg-blue-700 text-white p-2 rounded shadow text-xs"
@click="handlePrevious"
:disabled="isSameOrBefore(start)"
>
Previous
</button>
<button
class="bg-blue-500 hover:bg-blue-700 text-white p-2 rounded shadow text-xs"
@click="handleNext"
>
Next
</button>
</section>
<div
class="p-12 w-full flex-col flex md:flex-row justify-between justify-items-center content-center items-start"
>
<section class="w-full md:w-1/2 md:mr-4">
<h4 class="font-bold text-xl text-gray-400">
{{ start.format('MMM') }} {{ start.format('YYYY') }}
</h4>
<table class="w-full">
<thead>
<tr>
<th
class="font-light text-sm p-2 text-center text-gray-300"
scope="col"
class="font-light text-sm"
v-for="(dayStr, i) in days"
v-bind:key="i"
>
{{dayStr}}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(week, i) in range" v-bind:key="i">
<td
class="day"
:class="[
isBefore(day) ? 'text-gray-300' : 'is-valid cursor-pointer hover:bg-blue-700',
day && isSelectedDate(day) && 'bg-blue-700 text-white',
day && inRange(day) && 'bg-blue-200',
day && isStartOrEnd(day),
'p-2 text-center'
]"
v-for="(day, j) in week"
v-bind:key="j"
@click="!isBefore(day) && selectDate(day)"
@mouseover="!isBefore(day) && paintOnHover(day)"
@mouseleave="!isBefore(day) && paintOnHover(day)"
>
{{ day ? day.format("DD") : "" }}
</td>
</tr>
</tbody>
</table>
</section>
<section class="w-full md:w-1/2 md:mt-0 mt-8 md:ml-4">
<h4 class="font-bold text-xl text-gray-400">
{{ next.format('MMM') }} {{ next.format('YYYY') }}
</h4>
<table class="w-full">
<thead>
<tr>
<th
class="font-light text-sm p-2 text-center text-gray-300"
scope="col"
v-for="(dayStr, i) in days"
v-bind:key="i"
>
{{dayStr}}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(week, i) in nextRange" v-bind:key="i">
<td
class="day"
:class="[
isBefore(day) ? 'text-gray-300' : 'is-valid cursor-pointer hover:bg-blue-700',
day && isSelectedDate(day) && 'bg-blue-700 text-white',
day && inRange(day) && 'bg-blue-200',
day && isStartOrEnd(day),
'p-2 text-center'
]"
v-for="(day, j) in week"
v-bind:key="j"
@click="!isBefore(day) && selectDate(day)"
@mouseover="day && !isBefore(day) && paintOnHover(day)"
@mouseleave="day && !isBefore(day) && paintOnHover(day)"
>
{{ day ? day.format("DD") : "" }}
</td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
<script src="https://momentjs.com/downloads/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-range/4.0.2/moment-range.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
<script>
window['moment-range'].extendMoment(moment);
</script>
<script>
const today = moment().set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
const app = new Vue({
el: '#app',
data: {
start: today,
next: moment(today).add(1, 'month'),
range: [],
nextRange: [],
universal: false,
selected: {
startDate: null,
endDate: null,
},
},
watch: {
universal() {
this.calculateRanges();
},
},
computed: {
days() {
let days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
if (!this.universal) {
return days;
}
const lastDay = days[days.length - 1];
days.pop();
days.unshift(lastDay);
return days;
},
},
methods: {
sliceIntoDays(days) {
const daysOfWeek = 7;
return [].concat.apply(
[],
days.map((elem, i) => (i % daysOfWeek ? [] : [days.slice(i, i + daysOfWeek)])),
);
},
getToday() {
const date = moment();
date.set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
return date;
},
isBefore(day) {
if (day) {
return day.isBefore(this.getToday());
}
return false;
},
isSameOrBefore(day) {
return day.isSameOrBefore(this.getToday());
},
isSelectedDate(day) {
return day.isSame(this.selected.startDate) || day.isSame(this.selected.endDate);
},
isStartOrEnd(day) {
if (day.isSame(this.selected.startDate)) {
return 'startDate';
} else if (day.isSame(this.selected.endDate)) {
return 'endDate';
}
},
inRange(day) {
return day.isAfter(this.selected.startDate) && day.isBefore(this.selected.endDate);
},
paintOnHover(day) {
/*
if (this.selected.startDate && day.isAfter(this.selected.startDate)) {
day.painted = true;
}
*/
// console.log(day);
},
handleNext() {
this.start.add(2, 'month');
this.next.add(2, 'month');
this.calculateRanges();
},
handlePrevious() {
this.start.subtract(2, 'month');
this.next.subtract(2, 'month');
this.calculateRanges();
},
checkIfThisMoth(month) {
const thisYear = this.getToday().year();
const thisMonth = this.getToday().month();
return month.year() === thisYear && month.month() === thisMonth;
},
calculateRanges() {
this.range = this.calculateTable(this.start);
this.nextRange = this.calculateTable(this.next);
},
calculateTable(month) {
let range = this.getToday().range(
moment(month).startOf('month'),
moment(month).endOf('month'),
);
range = [...range.by('days')];
const firstDay = range[0].startOf('month').day();
const nthDay = this.universal ? firstDay + 1 : firstDay;
const emptyDays = nthDay >= 1 ? nthDay - 1 : 6;
range.unshift(...Array(emptyDays));
return this.sliceIntoDays(range);
},
toggleUniversal() {
this.universal = !this.universal;
},
selectDate(day) {
const startDate = this.selected.startDate;
const endDate = this.selected.endDate;
if (!startDate && !endDate) {
this.selected.startDate = day;
return;
}
if (startDate && !endDate) {
if (day.isBefore(startDate)) {
this.selected.startDate = day;
return;
}
this.selected.endDate = day;
return;
}
if (!startDate && endDate) {
if (day.isBefore(endDate)) {
this.selected.startDate = day;
return;
}
if (day.isAfter(endDate)) {
this.selected.startDate = day;
this.selected.endDate = null;
return;
}
this.selected.startDate = day;
this.selected.endDate = null;
return;
}
if (startDate && endDate) {
if (day.isBefore(startDate)) {
this.selected.startDate = day;
this.selected.endDate = null;
return;
}
if (day.isSame(startDate)) {
this.selected.startDate = null;
return;
}
if (!(day.isAfter(this.selected.startDate) && day.isBefore(this.selected.endDate))) {
this.selected.startDate = day;
this.selected.endDate = null;
return;
}
this.selected.endDate = day;
return;
}
},
},
mounted() {
this.calculateRanges();
},
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment