<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - Date Picker with Click & Drag! | @keyframers 2.30.0</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<a href="https://youtu.be/jrD7eiRvyvc" target="_blank" data-keyframers-credit style="color: #000"></a>
<script src="https://codepen.io/shshaw/pen/QmZYMG.js"></script>
<div class="calendar">
<!--
.day*31[data-day="$"]>span.number{$}
-->
<div class="day" data-day="1"><span class="number">1</span></div>
<div class="day" data-day="2"><span class="number">2</span></div>
<div class="day" data-day="3"><span class="number">3</span></div>
<div class="day" data-day="4"><span class="number">4</span></div>
<div class="day" data-day="5"><span class="number">5</span></div>
<div class="day" data-day="6"><span class="number">6</span></div>
<div class="day" data-day="7"><span class="number">7</span></div>
<div class="day" data-day="8"><span class="number">8</span></div>
<div class="day" data-day="9"><span class="number">9</span></div>
<div class="day" data-day="10"><span class="number">10</span></div>
<div class="day" data-day="11"><span class="number">11</span></div>
<div class="day" data-day="12"><span class="number">12</span></div>
<div class="day" data-day="13" data-selected="start"><span class="number" >13</span></div>
<div class="day" data-day="14"><span class="number">14</span></div>
<div class="day" data-day="15"><span class="number">15</span></div>
<div class="day" data-day="16"><span class="number">16</span></div>
<div class="day" data-day="17"><span class="number">17</span></div>
<div class="day" data-day="18"><span class="number">18</span></div>
<div class="day" data-day="19"><span class="number">19</span></div>
<div class="day" data-day="20"><span class="number">20</span></div>
<div class="day" data-day="21"><span class="number">21</span></div>
<div class="day" data-day="22"><span class="number">22</span></div>
<div class="day" data-day="23" data-selected="end"><span class="number" >23</span></div>
<div class="day" data-day="24"><span class="number">24</span></div>
<div class="day" data-day="25"><span class="number">25</span></div>
<div class="day" data-day="26"><span class="number">26</span></div>
<div class="day" data-day="27"><span class="number">27</span></div>
<div class="day" data-day="28"><span class="number">28</span></div>
<div class="day" data-day="29"><span class="number">29</span></div>
<div class="day" data-day="30"><span class="number">30</span></div>
<div class="day" data-day="31"><span class="number">31</span></div>
</div>
<!-- partial -->
<script src="./script.js"></script>
</body>
</html>
*,
*::before,
*::after {
position: relative;
box-sizing: border-box;
}
html {
height: 100%;
display: -webkit-box;
display: flex;
background: #eee;
}
body {
margin: auto;
}
:root {
--color-primary: #00AEF2;
}
.calendar {
font-size: 3vw;
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: 0.25em 0;
margin: auto;
padding: 1em;
background: #FFF;
box-shadow: 0 1em 2em rgba(0, 0, 0, 0.25);
border-radius: .5em;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.day:first-child {
grid-column-start: 4;
}
.day {
padding: 0.5em;
text-align: center;
}
.day::before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
.day[data-selected]::before {
border-radius: .5em;
}
.day[data-selected="start"]::before {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.day[data-selected], .day[data-selected] ~ .day {
color: white;
}
.day[data-selected]::before, .day[data-selected] ~ .day::before {
background-color: var(--color-primary);
}
.day[data-selected="start"] ~ .day:not([data-selected="end"])::before {
opacity: 0.5;
}
.day[data-selected="end"][data-selected="end"]::before {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.day[data-selected="end"] ~ .day {
color: inherit;
}
.day[data-selected="end"] ~ .day::before {
background-color: transparent;
}
console.clear();
// idle
// -> onPointerDown
// dragging
// -> onPointerMove / onPointerOver
// <- onPointerUp
/* NOTE: We retooled the data to be based on first selection and second selection. We'll calculate which is the start & end date later in the `updateDOM` function. */
const data = {
firstDate: null,
secondDate: null
};
const machine = {
initial: 'idle',
states: {
idle: {
on: {
pointerdown: (data, event) => {
data.firstDate = +event.currentTarget.dataset.day;
data.secondDate = null;
return 'dragging';
}
}
},
dragging: {
on: {
pointerover: (data, event) => {
data.secondDate = +event.currentTarget.dataset.day;
return 'dragging';
},
pointerup: 'idle',
pointercancel: 'idle'
}
}
}
};
// idle
let currentState = machine.initial;
function send(event) {
const transition = machine
.states[currentState]
.on[event.type];
if (typeof transition === 'function') {
currentState = transition(data, event);
updateDOM();
} else if (transition) {
currentState = transition;
updateDOM();
}
}
/* ---------------------------------- */
const allDayEls = document.querySelectorAll('[data-day]');
allDayEls.forEach(dayEl => {
dayEl.addEventListener('pointerdown', send);
dayEl.addEventListener('pointerover', send);
});
document.body.addEventListener('pointerup', send);
/* ---------------------------------- */
function updateDOM(){
document.querySelectorAll('[data-selected]')
.forEach(el => {
delete el.dataset.selected
});
const startDate = Math.min(data.firstDate, data.secondDate);
const endDate = Math.max(data.firstDate, data.secondDate);
if ( startDate ) {
const startDateEl = document.querySelector(`[data-day="${startDate}"]`);
startDateEl.dataset.selected = "start";
}
if ( endDate ) {
const endDateEl = document.querySelector(`[data-day="${endDate}"]`);
endDateEl.dataset.selected = "end";
}
}