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
         ...