[react-datepicker] 달력 만들기_v1
본문 바로가기
더 알아보기/기능

[react-datepicker] 달력 만들기_v1

by 은돌1113 2022. 3. 29.

- 참고 블로그

 

- 문서


달력 라이브러리를 찾던 중 react-datePicker 라이브러리를 사용하게 되었다.  (예제가 많아서? 선택했다.)

 

첫번째 과정

react-datePicker 라이브러리를 터미널에 설치한다.

npm install react-datepicker --save
(or)
yarn add react-datepicker

 

두번째 과정

react-dattePicker 문서에 있는 예제 중 하나를 가져와서 달력 컴포넌트를 하나 생성한다.
(나는 아래 두가지 예제를 가져와서 사용했다.)

 

세번째 과정

이때 addMonths 또는 getDay가 없다는 오류가 발생하였는 데 이는 상단에 date-fns를 import 받고, {} 안에 함수 이름을 적어주면 된다.

import { getDay, addMonths } from "date-fns"; 
// DatePicker에 부가적인 기능 가져오고 싶을 때 해당 함수들 date-fns에서 import 해오기

 

네번째 과정

위 과정이  끝나면 달력이 기본적인 달력이 만들어 진다.

더보기
import { useState } from "react";

import DatePicker, { registerLocale } from "react-datepicker"; // DatePicker 컴포넌트 가져오기
import { getDay, addMonths } from "date-fns"; // DatePicker에 부가적인 기능 가져오고 싶을 때 해당 함수들 date-fns에서 import 해오기
// 출처: https://reactdatepicker.com/#example-filter-dates
// 출처: https://date-fns.org/

import ko from "date-fns/locale/ko"; // 한국어로
registerLocale("ko", ko); // 한국어로
const _ = require("lodash"); // _.range를 표현하기 위하여 사용
// 출처: https://blog.naver.com/PostList.naver?blogId=marsdo

const test2 = () => {
  const [selectDate, setSelectDate] = useState(null); // 선택한 날짜

  // 주말 filter 함수
  const isWeekday = (date) => {
    const day = getDay(date);
    return day !== 0 && day !== 6;
  };

  return (
    <DatePicker
      className="datePicker"
      selected={selectDate}
      onChange={(date) => setSelectDate(date)}
      minDate={new Date()}
      maxDate={addMonths(new Date(), 100)}
      showDisabledMonthNavigation
      // minDate, maxDate, showDisabledMonthNavigation => Date Range with disabled navigation shown
      filterDate={isWeekday}
      placeholderText="Select a weekday"
      // filterDate, placeholderText => Filter dates
    />
  );
};

export default test2;

 

5. 달력 라이브러리가 문제 없이 작동하는 걸 확인하여 커스텀을 시작했다.

상단에 커스텀 블로그를 참고했고, styled-components 라이브러리를 사용하여 커스텀을 진행했다.

전체 코드에 주석을 달아 놓았기 때문에 따로 설명하지는 않겠다.

+  부가설명

날짜 형식이 안예쁘게 나와서 date format 함수를 생성하여 날짜 형식을 변경 했더니 오류가 발생했다.

RangeError: invalid date

찾아보니 날짜 형식이 안맞을 때 나타나는 오류라는 데 datePicker에서 뭔가가 꼬인게 아닐까 싶다.

 

RangeError: invalid date - JavaScript | MDN

RangeError

developer.mozilla.org

안된다고 해서 안할 건 아니라서.. ㅇㅅㅇ date format 함수는 보류 시키고 원상태로 돌렸다.

 

그 다음 input 박스를 달력 이미지로 변경하고, 달력 이미지 클릭 시 달력이 나타나도록 커스텀 해서 바꾸고,

 

[CSS] 체크박스 꾸미기

체크박스 꾸미는 방법 체크박스는 체크박스와 연결된 label을 만들고, 라베을 꾸미며 됩니다. 그렇게 할 경우 라벨을 클릭하면 체크박스가 클릭되고, 다시 클릭하면 체크가 해제되는 성질을 가지

eundol1113.tistory.com

 날짜는 JSX에서 date format 함수를 불러오는 방식으로 사용했다.

(사용자가 선택한 날짜가 있으면 날짜 format 해서 return 하고, 사용자가 선택한 날짜가 없으면 문구 출력)


전체 코드

더보기
import { useState } from "react";
import styled from "styled-components";

import DatePicker, { registerLocale } from "react-datepicker"; // DatePicker 컴포넌트 가져오기
import { getDay, addMonths } from "date-fns"; // DatePicker에 부가적인 기능 가져오고 싶을 때 해당 함수들 date-fns에서 import 해오기
// 출처: https://reactdatepicker.com/#example-filter-dates
// 출처: https://date-fns.org/

import ko from "date-fns/locale/ko"; // 한국어로
registerLocale("ko", ko); // 한국어로
const _ = require("lodash"); // _.range를 표현하기 위하여 사용
// 출처: https://blog.naver.com/PostList.naver?blogId=marsdo

import ArrowLeftSvg from "public/img/common/arrow-left.svg";
import ArrowRightSvg from "public/img/common/arrow-right.svg";

const test2 = () => {
  const [selectDate, setSelectDate] = useState(null); // 선택한 날짜

  const months = [
    // 월 표시
    "01",
    "02",
    "03",
    "04",
    "05",
    "06",
    "07",
    "08",
    "09",
    "10",
    "11",
    "12",
  ];

  // 주말 filter 함수
  const isWeekday = (date) => {
    const day = getDay(date);
    return day !== 0 && day !== 6;
  };

  // 날짜 형식 format 함수
  const dateChangeHandler = () => {
    if (selectDate) {
      // 선택한 날짜가 있을 경우 (selectDate: true)
      const date = selectDate;
      const year = date.getFullYear(); // 년
      let month = date.getMonth() + 1; // 월
      month = month >= 10 ? month : "0" + month; // 월 두자리로 저장
      let day = date.getDate(); // 일
      day = day >= 10 ? day : "0" + day; // 일 두자리로 저장

      return `${year}-${month}-${day}`;
    } else {
      // 선택한 날짜가 없을 경우 (selectDate: false)
      return "날짜를 선택 해주세요.";
    }
  };

  return (
    <DateStyle>
      <DatePicker
        className="datePicker"
        id="datePicker"
        selected={selectDate}
        onChange={(date) => setSelectDate(date)}
        minDate={new Date()}
        maxDate={addMonths(new Date(), 100)}
        showDisabledMonthNavigation
        // minDate, maxDate, showDisabledMonthNavigation => Date Range with disabled navigation shown
        filterDate={isWeekday}
        placeholderText="Select a weekday"
        // filterDate, placeholderText => Filter dates
        locale="ko"
        showPoperArrow={false}
        fixedHeight
        renderCustomHeader={({
          date,
          decreaseMonth,
          increaseMonth,
          prevMonthButtonDisabled,
          nexMonthButtonDisabled,
        }) => (
          <div className="date-customheader">
            <ArrowLeftSvg
              onClick={decreaseMonth}
              width={20}
              height={20}
              style={
                prevMonthButtonDisabled
                  ? { visibility: "hidden" }
                  : { display: "block" }
              }
            />
            <div className="custom-month">
              <span className="month">{months[date.getMonth()]}월,</span>
              <span className="year"> {date.getFullYear()}</span>
            </div>
            <ArrowRightSvg
              onClick={increaseMonth}
              width={20}
              height={20}
              style={
                nexMonthButtonDisabled
                  ? { display: "none" }
                  : { display: "block" }
              }
            />
          </div>
        )}
      />
      <label
        htmlFor="datePicker"
        style={{
          width: "50px",
          height: "50px",
          backgroundImage: "url(/img/nav/sharedOffice/lease/calendar@4x.png)",
          backgroundSize: "cover",
        }}
      />

      {/* 날짜를 출력하는 부분 */}
      <div>날짜 : {dateChangeHandler()}</div>
    </DateStyle>
  );
};

const DateStyle = styled.section`
  position: relative;

  // 드래그 방지
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;

  // 달력 위치 조절
  .react-datepicker-popper {
    top: -100px !important;
    left: 60px !important;
  }

  // 달력 이미지 사용을 위해 input 박스 커스텀하는 부분
  & #datePicker:checked + label {
    background-color: #038dff;
    border: 2px solid #038dff;
  }

  & #datePicker {
    display: none;
  }

  .datePicker::before,
  .datePicker::after {
    content: "\e99c";
    display: inline-block;
    position: absolute;
    top: 7px;
    left: 7px;
    font-family: icomoon !important;
    font-size: 1rem;
    color: #333;

    border: 1px solid #ffffff;
  }

  // 달력 Header 조절
  .date-customheader {
    display: flex;
    justify-content: space-between;
    margin: 0 10px;

    svg {
      cursor: pointer;
      stroke: #666666;
      stroke-width: 2px;
      margin-top: 4px;
    }
  }

  .react-datepicker__day-names {
    font-size: 12px;
    margin-top: 10px;

    .react-datepicker__day-name {
      color: #0072de;
    }
  }

  .react-datepicker__header {
    background: #fff;
    border-bottom: 1px solid #eeeeee;
    padding: 10px;
  }

  // 달력 헤더 > 월, 년 부분
  .custom-month {
    .month {
      color: #000000;
      font-size: 17px;
    }

    .year {
      color: #666666;
      font-size: 15px;
    }
  }

  // 달력 Content 부분
  .react-datepicker-popper[data-placement^="bottom"]
    .react-datepicker__triangle::before,
  .react-datepicker-popper[data-placement^="bottom"]
    .react-datepicker__triangle::after {
    top: -100px;
    left: -100px;
    border-bottom-color: white;
  }

  .react-datepicker__day--keyboard-selected,
  .react-datepicker__month-text--keyboard-selected,
  .react-datepicker__quarter-text--keyboard-selected,
  .react-datepicker__year-text--keyboard-selected {
    background: #0072de;
  }
`;

export default test2;

 

20220415 - 변경된 CSS 코드

const DateStyle = styled.section`
  position: relative;
  margin-top: 10px;

  .calendar_wrap_1 {
    display: flex;
    margin: 10px 25px;
    position: relative;

    .startDate_label {
      width: 25px;
      height: 25px;
      background-image: url(/img/nav/sharedOffice/lease/calendar@4x.png);
      background-size: cover;
      cursor: pointer;
      position: absolute;
      right: 350px;

      span {
        margin-top: 2px;
        margin-left: 30px;
        font: normal normal normal 16px/19px Pretendard;
        letter-spacing: 0px;
        color: #333333;
      }
    }
  }

  .calendar_wrap_2 {
    display: flex;
    margin: 10px 25px;
    position: relative;

    .endDate_label {
      width: 25px;
      height: 25px;
      background-image: url(/img/nav/sharedOffice/lease/calendar@4x.png);
      background-size: cover;
      cursor: pointer;
      position: absolute;
      right: 350px;

      span {
        margin-top: 2px;
        margin-left: 30px;
        font: normal normal normal 16px/19px Pretendard;
        letter-spacing: 0px;
        color: #333333;
      }
    }
  }

  .select_hr {
    margin-top: -5px;
  }

  // 드래그 방지
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;

  // 이미지 위치 조절
  .react-datepicker-wrapper {
    display: none;
  }

  // 달력 위치 조절
  .react-datepicker-popper {
    top: -130px !important;
    left: 54% !important;
    transform: translate(0, 0) !important;
    z-index: 100;
    transition: all 0.5s;

    @media screen and (max-width: 2300px) {
      left: 45% !important;
    }

    @media screen and (max-width: 2100px) {
      left: 35% !important;
    }
  }

  // 달력 이미지 사용을 위해 input 박스 커스텀하는 부분
  & #startDate:checked + label,
  & #endDate:checked + label {
    background-color: #038dff;
    border: 2px solid #038dff;
  }

  & #startDate,
  #endDate {
    display: none;
  }

  #startDate::before,
  #startDate::after,
  #endDate::before,
  #endDate::after {
    content: "\e99c";
    display: inline-block;
    position: absolute;
    top: 7px;
    left: 7px;
    font-family: icomoon !important;
    font-size: 1rem;
    color: #333;

    border: 1px solid #ffffff;
  }

  // 달력 Header 조절
  .date-customheader {
    display: flex;
    justify-content: space-between;
    margin: 0 10px;

    svg {
      cursor: pointer;
      stroke: #666666;
      stroke-width: 2px;
      margin-top: 4px;
    }
  }

  .react-datepicker__day-names {
    font-size: 12px;
    margin-top: 10px;

    .react-datepicker__day-name {
      color: #0072de;
    }
  }

  .react-datepicker__header {
    background: #fff;
    border-bottom: 1px solid #eeeeee;
    padding: 10px;
  }

  // 달력 헤더 > 월, 년 부분
  .custom-month {
    margin-top: 5px;

    .month {
      color: #000000;
      font-size: 17px;
    }

    .year {
      color: #666666;
      font-size: 15px;
    }
  }

  // 달력 Content 부분
  .react-datepicker-popper[data-placement^="bottom"]
    .react-datepicker__triangle::before,
  .react-datepicker-popper[data-placement^="bottom"]
    .react-datepicker__triangle::after {
    border-top: none;
    border-bottom: none;
  }

  .react-datepicker-popper[data-placement^="top"]
    .react-datepicker__triangle::before,
  .react-datepicker-popper[data-placement^="top"]
    .react-datepicker__triangle::after {
    border-top: none;
    border-bottom: none;
  }

  .react-datepicker__day--selected,
  .react-datepicker__day--in-selecting-range,
  .react-datepicker__day--in-range,
  .react-datepicker__month-text--selected,
  .react-datepicker__month-text--in-selecting-range,
  .react-datepicker__month-text--in-range,
  .react-datepicker__quarter-text--selected,
  .react-datepicker__quarter-text--in-selecting-range,
  .react-datepicker__quarter-text--in-range,
  .react-datepicker__year-text--selected,
  .react-datepicker__year-text--in-selecting-range,
  .react-datepicker__year-text--in-range {
    background: #0072de;
  }
`;

'더 알아보기 > 기능' 카테고리의 다른 글

[Next.js] Chart.js  (0) 2022.04.06
차트(Chart) 라이브러리  (0) 2022.04.06
[React] 다음 주소 API 적용하기  (0) 2022.03.28
[Next.js] 이미지 미리보기  (0) 2022.03.24
[Editor] String 형태의 HTML 렌더링 하기  (2) 2022.03.23

댓글