JSFiddle - React, Tailwind, and code Playground
by ninty9notout
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/smooth-scroll/16.0.3/smooth-scroll.min.js"></script>
<div class="calendar calendar--hidden">
<div class="calendar__header">
<div class="month">
<button class="month__previous" disabled>
◀ <span class="visuallyhidden">Previous Month</span>
</button>
<h2 class="month__title">
</h2>
<button class="month__next" disabled>
▶ <span class="visuallyhidden">Next Month</span>
</button>
</div>
<div class="headings">
<div class="headings__label">Mon</div>
<div class="headings__label">Tue</div>
<div class="headings__label">Wed</div>
<div class="headings__label">Thu</div>
<div class="headings__label">Fri</div>
<div class="headings__label">Sat</div>
<div class="headings__label">Sun</div>
</div>
</div>
<div class="calendar__body days">
<!-- intentionally empty -->
</div>
<!--
<div class="calendar__overlay overlay">
<div class="overlay__inner">
<h2 class="overlay__title">Tuesday, 4th June, 2019</h2>
<div class="overlay__events">
<div class="overlay__event event">
<span class="event__time">7.30pm</span>
<span class="event__title">Hamlet</span>
<span class="event__action">
<a href="#" class="event__button">Buy Tickets</a>
</span>
</div>
<div class="overlay__event event">
<span class="event__time">7.30pm</span>
<span...
SCSS
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
body {
margin: 0;
font-family: sans-serif;
font-size: 16px;
}
.visuallyhidden {
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
border: 0;
}
.calendar--hidden {
display: none;
}
.calendar {
position: relative;
}
.calendar__header {
position: sticky;
z-index: 5;
top: 0;
background: #fff;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.1);
}
.month {
display: flex;
justify-content: center;
align-items: center;
padding: 0.5rem 0;
font-size: 0;
cursor: default;
}
.month__previous,
.month__next {
border: none;
outline: none;
background-color: transparent;
padding: 0.25rem;
margin: 0;
font-size: 0.5rem;
&[disabled] {
visibility: hidden;
}
}
.month__title {
margin: 0;
width: 50%;
max-width: 300px;
text-align: center;
font-size: 1rem;
font-weight: bold;
text-transform: uppercase;
cursor: default;
}
.headings {
display: grid;
grid-template-columns: repeat(7, 1fr);
border-bottom: 1px solid #fcfcfc;
font-size: 0;
padding: 0.5rem 0;
}
.headings__label {
text-align: center;
font-weight: bold;
text-transform: uppercase;
font-size: 0.7rem;
cursor: default;
}
.calendar__body {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: 1px;
background-color: #eee;
}
.day {
min-width: 0;
min-height: 90px;
padding: 10px 10px 5px;
}
.day--odd {
background-color: #ffffff;
}
.day--even {
background-color: #f9f9f9;
}
.day--past {
background-color: #f2f2f2;
cursor: not-allowed;
}
.day--today {
background: repeating-linear-gradient(-45deg, #ffffff, #ffffff 10px, #f9f9f9 10px, #f9f9f9 20px);
}
.day__title {
font-size: 0.8rem;
margin: 0 0 0.25rem;
pointer-events:...
JavaScript
// days start on monday
moment.updateLocale('en', {
week: {
dow: 1,
},
});
const { flatten, orderBy, first, last, groupBy } = _;
const scroll = new SmoothScroll();
const today = moment();
const calendar = document.querySelector('.calendar');
const calendarHeader = document.querySelector('.calendar__header');
const previousMonthButton = document.querySelector('.month__previous');
const nextMonthButton = document.querySelector('.month__next');
const monthTitle = document.querySelector('.month__title');
const calendarBody = document.querySelector('.calendar__body');
const arrayWrap = (maybeArray) => {
if (Array.isArray(maybeArray)) {
return maybeArray;
}
return [maybeArray];
}
const parseEvents = async () => {
return fetch('https://api.myjson.com/bins/lbky7').then(response => {
return response.json();
}).then(response => {
let events = response.reduce((carry, event) => {
let instances = arrayWrap(event.Times.EventTime);
instances = instances.map((instance) => {
const timeMoment = moment(instance.Time);
return {
event_id: parseInt(event.Id, 10),
instance_id: parseInt(instance.EventInstanceId, 10),
title: event.Name,
description: event.Description,
body: event.Html,
image_url: event.ImageUrl,
is_on_sale: instance.OnSaleOnWeb,
total_seats: parseInt(instance.Capacity, 10),
seats_available: parseInt(instance.SeatsAvailable, 10),
time: instance.Time,
time_moment: timeMoment,
duration: parseInt(event.Duration, 10),
day_group: timeMoment.format('YYYYMMDD'),
}
});
carry.push(instances);
return carry;
}, []);
events = flatten(events);
...