---
# System prepended metadata

title: Custom calendar
tags: [Projects]

---

---
title: Custom calendar
tags: Projects
---

Back in the day, when dealing with date picker, I always go for ready made plugins on the internet, But the size of calendar is usually kind of small, it just lowers UX, so this time, I gonna show you guys how I build a customer calendar from stratch, let's started!!

## [example code](https://codepen.io/nienyingchou/pen/bGRqQpJ)

## Agenda
* Get browser language
* Put moment.js in that language
* Get weekday names
* Generate calendar arrays
* Build the layout
* Add functions (last month, next month, select date)

## Get browser language
* because the property of browser in mobile is not the same, so we have to detect if the user is using mobile


```javascript=
const isUsingMobile =
  navigator.userAgent.match(/Android/i) ||
  navigator.userAgent.match(/webOS/i) ||
  navigator.userAgent.match(/iPhone/i) ||
  navigator.userAgent.match(/iPad/i) ||
  navigator.userAgent.match(/iPod/i) ||
  navigator.userAgent.match(/BlackBerry/i) ||
  navigator.userAgent.match(/Windows Phone/i);

const getBrowserLanguage = () => {
  // default the locale to English
  let lang = "en";

  if (isUsingMobile) {
    lang = navigator.languages
      .find((x) => x.substr(0, 2) === navigator.language.substr(0, 2))
      .substr(0, 2);
  } else {
    lang = navigator.language;
  }

  console.log("getBrowserLanguage", lang);

  return lang;
};
```
 
## Put moment.js in that language
```javascript=
moment.locale(getBrowserLanguage());
```

## Get weekday names
```javascript=
moment.weekdaysMin(true);
```

## Generate calendar arrays
```javascript=
  // generate calendar array according to selectDate
  // return date array
  const getCalendarArray = (selectDate) => {
    // default to the beginning of the month
    let startDate = moment(selectDate).format("01-MM-YYYY");

    // default to the end of the month
    const daysInMonth = moment(selectDate).daysInMonth();
    let endDate = moment(selectDate).format(`${daysInMonth}-MM-YYYY`);

    // modify startDate to the exact date 
    const firstDate = selectDate
      .clone()
      .startOf("month")
      .startOf("week")
      .date();

    if (firstDate > 20) {
      // means the date is from last month
      const MMYYYY = moment(selectDate).subtract(1, "month").format("MM-YYYY");
      startDate = moment(selectDate).format(`${firstDate}-${MMYYYY}`);
    }
    
    // modify endDate to the exact date 
    const lastDate = selectDate.clone().endOf("month").endOf("week").date();

    if (lastDate < 10) {
      // means the date is from next month
      const MMYYYY = moment(selectDate).add(1, "month").format("MM-YYYY");
      endDate = moment(selectDate).format(`0${lastDate}-${MMYYYY}`);
    }

    const date = [];

    // format startDate and endDate
    const formatStartDate = moment(startDate, dateFormat);
    const formatEndDate = moment(endDate, dateFormat);

    // push the dates between startDate and endDate into date array     
    for (
      let m = moment(formatStartDate);
      m.isSame(formatEndDate) || m.isBefore(formatEndDate);
      m.add(1, "days")
    ) {
      date.push(m.format(dateFormat));
    }

    const daysInWeek = 7;

    // split the array by 7 (7, because 7 days in a week)
    const dateArrayOfWeeks = [
      ...Array(Math.ceil(date.length / daysInWeek))
      // eslint-disable-next-line
    ].map((_) => date.splice(0, daysInWeek));

    console.log(dateArrayOfWeeks);

    setDates(dateArrayOfWeeks);
  };
```

## Build the layout
### calender body
```javascript=
<table>
{/* Calendar Header */}
<tr>
  {weekdaysShort.map((weekday) => (
    <th key={weekday}>{weekday}</th>
  ))}
</tr>

{/* Calendar Body */}
{dates.map((everyWeek) => (
  <tr key={everyWeek[0]}>
    {everyWeek.map((everyDay) => (
      <td
        onClick={() => handleSelectDate(everyDay)}
        style={{
          background:
            moment(everyDay, dateFormat).format(dateFormat) ===
            selectDate.format(dateFormat)
              ? "#CCC"
              : "inherit",
          color:
            moment(everyDay, dateFormat).format("MM") ===
            selectDate.format("MM")
              ? "inherit"
              : "#CCC"
        }}
      >
        {moment(everyDay, dateFormat).format("DD")}
      </td>
    ))}
  </tr>
))}
</table>
```

### calender control
```javascript=
  <div className="flex">
    <button onClick={lastMonth}>
      <b>{"<"}</b>
    </button>
    <div>{selectDate.format("LL")}</div>
    <button onClick={nextMonth}>
      <b>{">"}</b>
    </button>
  </div>
```

## Add functions
```javascript=
  const lastMonth = () => {
    const newDate = moment(selectDate).subtract(1, "month");
    updateData(newDate);
  };

  const nextMonth = () => {
    const newDate = moment(selectDate).add(1, "month");
    updateData(newDate);
  };

  // update calender when user click date
  const handleSelectDate = (selectDate) => {
    const newDate = moment(selectDate, dateFormat);
    updateData(newDate);
  };

  const updateData = (newDate) => {
    setSelectDate(newDate);
    getCalendarArray(newDate);
  };
```
