React
by alanhchoi
HTML
<div id="app"></div>
SCSS
body {
background: #20262e;
font-family: sans-serif;
}
.Calendar {
width: 320px;
background: #fff;
border-radius: 4px;
padding: 20px;
}
.Calendar__Header {
display: grid;
grid-template-columns: 1fr repeat(3, auto);
grid-gap: 3px;
align-items: baseline;
margin-bottom: 20px;
}
.Calendar__Header__MonthName {
font-size: 18px;
font-weight: 600;
}
.Calendar__Header__Button {
min-width: 32px;
height: 32px;
padding: 0 8px;
font-size: 14px;
border: 0;
border-radius: 4px;
cursor: pointer;
outline: none;
&:hover {
background: rgba(0, 0, 0, 0.6);
color: white;
}
&:focus {
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);
}
}
.Calendar__Grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-auto-flow: row;
}
.Calender__Grid__GridItem {
padding: 8px;
user-select: none;
text-align: center;
}
.Calender__Grid__GridItem--Header {
font-weight: 500;
color: rgba(0, 0, 0, 0.5);
}
.Calender__Grid__GridItem--Day {
border-radius: 4px;
&:hover {
background-color: rgba(0, 0, 0, 0.6);
color: white;
}
}
.Calender__Grid__GridItem--Today {
text-decoration: underline;
}
@for $i from 0 through 6 {
.Calender__Grid__GridItem--FirstDateWithDay#{$i} {
grid-column-start: #{$i + 1};
}
}
React
// Day initials from Monday to Sunday
const dayInitials = (() => {
const dateObject = new Date();
dateObject.setDate(dateObject.getDate() - dateObject.getDay()); // dateObject becomes Sunday.
return [...Array(7)].map(() => {
dateObject.setDate(dateObject.getDate() + 1);
return dateObject.toLocaleString(undefined, { weekday: 'narrow' });
});
})();
const Calendar = () => {
const currentDate = new Date();
const [{ year: selectedYear, month: selectedMonth }, setSelectedYearMonth] = React.useState({
year: currentDate.getFullYear(),
month: currentDate.getMonth(),
});
const selectedMonthDay = new Date(selectedYear, selectedMonth, 1);
const isYearDisplayed = currentDate.getFullYear() !== selectedYear;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
const monthName = selectedMonthDay.toLocaleString(undefined, {
month: 'long',
year: isYearDisplayed ? 'numeric' : undefined,
});
const onNextMonthButtonClick = () =>
setSelectedYearMonth(({ year, month }) => {
return month === 11 ? { year: year + 1, month: 0 } : { year, month: month + 1 };
});
const onPreviousMonthButtonClick = () =>
setSelectedYearMonth(({ year, month }) => {
return month === 0 ? { year: year - 1, month: 11 } : { year, month: month - 1 };
});
const onCurrentMonthButtonClick = () => {
setSelectedYearMonth({ year: currentDate.getFullYear(), month: currentDate.getMonth() });
};
const firstDayColumnIndex = (selectedMonthDay.getDay() + 6) % 7;
const dates = (() => {
const dateElements = [];
while (selectedMonthDay.getFullYear() === selectedYear && selectedMonthDay.getMonth() === selectedMonth) {
const date = selectedMonthDay.getDate();
const isFirstDay = dateElements.length === 0;
const isToday = currentDate.toLocaleDateString() === selectedMonthDay.toLocaleDateString();
dateElements.push(
<div
...