본문 바로가기

React

[React] 캘린더 react-datepicker 라이브러리 사용법

날짜를 선택하는 캘린더를 적용하고 싶어서 react-datepicker 라이브러리를 사용하였다.

다양한 커스텀이 가능하며, 예제코드는 아래 공식사이트에서 확인할 수 있다. 

 

 

react-datepicker

A simple and reusable datepicker component for React. Latest version: 4.16.0, last published: 6 days ago. Start using react-datepicker in your project by running `npm i react-datepicker`. There are 2673 other projects in the npm registry using react-datepi

www.npmjs.com

 

 

React Datepicker crafted by HackerOne

 

reactdatepicker.com

 

설치

install

npm install react-datepicker --save

 

TypeScript

npm install --save @types/react-datepicker

 

적용

기본 Default 적용시

import { useState } from 'react';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';

//scss 모듈적용
import styles from './calendarCommon.module.scss';
import classNames from 'classnames/bind';

const cx = classNames.bind(styles); //scss 모듈적용

export default function CalendarCommon() {
  const [startDate, setStartDate] = useState<Date | null>(new Date());

  return (
    <div>
      {/* <p className={styles['date-picker']}></p> */}
      <p className={cx('date-picker')}></p>
      <DatePicker
        selected={startDate}
        onChange={(date) => setStartDate(date)}
        //커스텀
      />
    </div>
  );
}

 

 

커스텀

*달력:현재 보여지는 월말고 다른 월은 회색글자로 처리

css 

.react-datepicker__day--outside-month {
  color: #a8a8a8 !important;
  pointer-events: none;
} //보여지는달력 해당월아닌 다른월 날짜는 회색컬러로 글자처리

 


+ 또 다른 커스텀 예제

 

<커스텀2>

import { forwardRef, useEffect, useState } from 'react';

import DatePicker, { registerLocale } from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import ko from 'date-fns/locale/ko';
import { getMonth, getYear, getDate } from 'date-fns';

import dayjs from 'dayjs';

import './calendarCustomThree.scss';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import CalendarMonthIcon from '@mui/icons-material/CalendarMonth';

registerLocale('ko', ko); //날짜 한국어로 표시

const CustomInput = forwardRef((props: any, ref) => {
  return (
    <div className='calendar-input-wrap'>
      <input {...props} ref={ref} type='text' />
      <CalendarMonthIcon />
    </div>
  );
});

export default function CalendarCustomThree() {
  const [startDate, setStartDate] = useState<Date | null>(new Date());

  const _ = require('lodash');

  registerLocale('ko', ko); //날짜 한국어로 표시

  // 연도 선택 select box에 보여질 데이터 : range(시작 연도, 끝 연도, 연도 간격)
  // const years = _.range(1990, getYear(new Date()) + 1, 1);
  const years = _.range(getYear(new Date()) - 10, getYear(new Date()) + 1, 1);

  // 월 선택 select box에 보여질 데이터
  const months = [
    '1',
    '2',
    '3',
    '4',
    '5',
    '6',
    '7',
    '8',
    '9',
    '10',
    '11',
    '12',
  ];

  // const currentMonth = getMonth(new Date());

  //선택날짜
  const date = dayjs(startDate).format('YYYY-MM-DD');
  // console.log(date);

  return (
    <div className='custom-wrap3'>
      <DatePicker
        //Input 커스텀
        customInput={<CustomInput />}
        //header 커스텀
        renderCustomHeader={({
          date,
          changeYear,
          changeMonth,
          decreaseMonth,
          increaseMonth,
          prevMonthButtonDisabled,
          nextMonthButtonDisabled,
        }) => (
          <div
            style={{
              margin: 10,
              display: 'flex',
              justifyContent: 'center',
            }}
          >
            <button onClick={decreaseMonth} disabled={prevMonthButtonDisabled}>
              <ArrowBackIosIcon />
            </button>
            <select
              value={getYear(date)}
              onChange={({ target: { value } }) => changeYear(Number(value))}
              className='selectbox'
            >
              {years.map((option: string) => (
                <option key={option} value={option}>
                  {option}년
                </option>
              ))}
            </select>

            <select
              value={months[getMonth(date)]}
              onChange={({ target: { value } }) =>
                changeMonth(months.indexOf(value))
              }
              className='selectbox'
            >
              {months.map((option) => (
                <option key={option} value={option}>
                  {option}월
                </option>
              ))}

              {/* 이렇게 하면 범위에 따라 나타나지만, 2023년에서 3월선택후 2013년 3월가면 select에서 7월부터 시작되도록 범위정했지만 3월로 인식되어짐 /사용 X
                {getYear(date) === years[0]
                ? months.slice(currentMonth, 12).map((option) => (
                    <option key={option} value={option}>
                      {option}
                    </option>
                  ))
                : months.map((option) => (
                    <option key={option} value={option}>
                      {option}
                    </option>
                  ))} */}
            </select>

            <button onClick={increaseMonth} disabled={nextMonthButtonDisabled}>
              <ArrowForwardIosIcon />
            </button>
          </div>
        )}
        selected={startDate}
        onChange={(date) => setStartDate(date)}
        dayClassName={(d) => 'custom-day'}
        dateFormat='yyyy.MM.dd'
        disabledKeyboardNavigation //다른달에도 해당일자에 색표시되는거 제거
        locale='ko' //한국어로 설정
        //최소(minDate),최대날짜(maxDate) 범위 지정(바꿔도 됨)
        //minDate : '최소 시작 날짜'를 적용해야 <이전버튼눌러도 안넘어가짐(select안에있는 년도랑 다를수있으니 범위 한정 짓기)
        // minDate={new Date(`01-01-${getYear(new Date()) - 10}`)} //01-01-2013(월/일/현재년도에서-10) 기준으로 해놓음
        minDate={
          new Date(
            `${getMonth(new Date()) + 1}-${getDate(new Date())}-${
              getYear(new Date()) - 10
            }`
          )
        } //현재 월, 현재 일, 지정 년도(현재에서 -10년전)/ex: 현재날짜가 07.19.2023이면 07.19.2013년으로 해놓음
        maxDate={new Date()} //일단 현재날짜까지로 최대날짜 적용시킴
      />
    </div>
  );
}

 

CSS

.custom-wrap3 {
  .react-datepicker__day--outside-month {
    color: #a8a8a8 !important;
    pointer-events: none;
  } //보여지는달력 해당월아닌 다른월 날짜는 회색컬러로 글자처리
  .react-datepicker__day-name {
    width: 28px; //달력 요일
    color: #fff;
  }
  .react-datepicker {
    font-size: 1.2rem; //달력 글씨
  }

  .custom-day {
    width: 28px;
    height: 28px;
    line-height: 2.8;
  } //달력일

  .react-datepicker__header {
    background-color: rgb(64 123 128);
    color: #fff;
  }

  /* .react-datepicker__day--selected {
    background-color: rgb(77, 182, 191);
  }  */ //달력 선택 일
  .react-datepicker__triangle {
    display: none;
  } //세모 말꼬리
  .react-datepicker-popper {
    padding-top: 0;
  }
  .selectbox {
    padding: 4px 6px;
    border: none;
    margin: 0 4px;
  }

  .calendar-input-wrap {
    border: 1px solid #999;
    height: 24px;
    text-indent: 4px;
    font-size: 1.8rem;
    width: 128px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 4px;
  }
  .react-datepicker__input-container input {
    font-size: 1.8rem;
    border: none;
    padding: 0;
    width: 100px;
  }
}

 


<커스텀3>

import { forwardRef, useEffect, useState } from 'react';

import DatePicker, { registerLocale } from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import ko from 'date-fns/locale/ko';
import { getMonth, getYear, getDate } from 'date-fns';

import dayjs from 'dayjs';

import './calendarCustomThree.scss';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import CalendarMonthIcon from '@mui/icons-material/CalendarMonth';

registerLocale('ko', ko); //날짜 한국어로 표시

const CustomInput = forwardRef((props: any, ref) => {
  return (
    <div className='calendar-input-wrap'>
      <input {...props} ref={ref} type='text' />
      <CalendarMonthIcon />
    </div>
  );
});

export default function CalendarCustomFour() {
  //시작날짜
  const [startDate, setStartDate] = useState<Date | null>(
    new Date(
      `${getMonth(new Date()) + 1}-${getDate(new Date()) - 1}-${getYear(
        new Date()
      )}`
    )
  );
  //종료날짜
  const [endDate, setEndDate] = useState<Date | null>(new Date());

  const _ = require('lodash');

  registerLocale('ko', ko); //날짜 한국어로 표시

  // 연도 선택 select box에 보여질 데이터 : range(시작 연도, 끝 연도, 연도 간격)
  // const years = _.range(1990, getYear(new Date()) + 1, 1);
  const years = _.range(getYear(new Date()) - 10, getYear(new Date()) + 1, 1);

  // 월 선택 select box에 보여질 데이터
  const months = [
    '1',
    '2',
    '3',
    '4',
    '5',
    '6',
    '7',
    '8',
    '9',
    '10',
    '11',
    '12',
  ];

  //최종 선택 날짜
  const selectStartDate = dayjs(startDate).format('YYYY-MM-DD');
  const selectEndDate = dayjs(endDate).format('YYYY-MM-DD');

  // console.log('시작:', selectStartDate);
  // console.log('종료', selectEndDate);

  //+ 추가 적용: 만약 버튼 클릭시 해당 년도로 startDate를 바꾸고 싶을 경우
  const handleClickYear = (year: number) => {
    const startYears = new Date(
      `${getMonth(new Date()) + 1}-${getDate(new Date())}-${
        getYear(new Date()) - year
      }`
    );
    setStartDate(startYears);
  };

  const handleClickMonth = (month: number) => {
    const startYears = new Date(
      `${getMonth(new Date()) + 1 - month}-${getDate(new Date())}-${getYear(
        new Date()
      )}`
    );
    setStartDate(startYears);
  };

  return (
    <div className='custom-wrap3 custom-wrap4'>
      <div>
        <p className='input-start-date' onClick={() => handleClickYear(10)}>
          최대 10년
        </p>
        <p className='input-start-date' onClick={() => handleClickYear(5)}>
          5년
        </p>
        <p className='input-start-date' onClick={() => handleClickMonth(3)}>
          3개월
        </p>
        <p className='input-start-date' onClick={() => handleClickMonth(1)}>
          1개월
        </p>
      </div>

      <div className='date-picker-wrap'>
        <DatePicker
          //Input 커스텀
          customInput={<CustomInput />}
          //header 커스텀
          renderCustomHeader={({
            date,
            changeYear,
            changeMonth,
            decreaseMonth,
            increaseMonth,
            prevMonthButtonDisabled,
            nextMonthButtonDisabled,
          }) => (
            <div
              style={{
                margin: 10,
                display: 'flex',
                justifyContent: 'center',
              }}
            >
              <button
                onClick={decreaseMonth}
                disabled={prevMonthButtonDisabled}
              >
                <ArrowBackIosIcon />
              </button>
              <select
                value={getYear(date)}
                onChange={({ target: { value } }) => changeYear(Number(value))}
                className='selectbox'
              >
                {years.map((option: string) => (
                  <option key={option} value={option}>
                    {option}년
                  </option>
                ))}
              </select>

              <select
                value={months[getMonth(date)]}
                onChange={({ target: { value } }) =>
                  changeMonth(months.indexOf(value))
                }
                className='selectbox'
              >
                {months.map((option) => (
                  <option key={option} value={option}>
                    {option}월
                  </option>
                ))}

                {/* 이렇게 하면 범위에 따라 나타나지만, 2023년에서 3월선택후 2013년 3월가면 select에서 7월부터 시작되도록 범위정했지만 3월로 인식되어짐 /사용 X
                {getYear(date) === years[0]
                ? months.slice(currentMonth, 12).map((option) => (
                    <option key={option} value={option}>
                      {option}
                    </option>
                  ))
                : months.map((option) => (
                    <option key={option} value={option}>
                      {option}
                    </option>
                  ))} */}
              </select>

              <button
                onClick={increaseMonth}
                disabled={nextMonthButtonDisabled}
              >
                <ArrowForwardIosIcon />
              </button>
            </div>
          )}
          dayClassName={(d) => 'custom-day'}
          dateFormat='yyyy.MM.dd'
          disabledKeyboardNavigation //다른달에도 해당일자에 색표시되는거 제거
          locale='ko' //한국어로 설정
          //최소(minDate),최대날짜(maxDate) 범위 지정(바꿔도 됨)
          //minDate : '최소 시작 날짜'를 적용해야 <이전버튼눌러도 안넘어가짐(select안에있는 년도랑 다를수있으니 범위 한정 짓기)
          // minDate={new Date(`01-01-${getYear(new Date()) - 10}`)} //01-01-2013(월/일/현재년도에서-10) 기준으로 해놓음
          minDate={
            new Date(
              `${getMonth(new Date()) + 1}-${getDate(new Date())}-${
                getYear(new Date()) - 10
              }`
            )
          } //현재 월, 현재 일, 지정 년도(현재에서 -10년전)/ex: 현재날짜가 07.19.2023이면 07.19.2013년으로 해놓음
          maxDate={new Date()} //일단 현재날짜까지로 최대날짜 적용시킴
          /////
          selected={startDate}
          onChange={(date) => setStartDate(date)}
          selectsStart
          startDate={startDate}
          endDate={endDate}
        />
        <p className='wave'>&nbsp; &#126; &nbsp;</p>
        <DatePicker
          customInput={<CustomInput />}
          renderCustomHeader={({
            date,
            changeYear,
            changeMonth,
            decreaseMonth,
            increaseMonth,
            prevMonthButtonDisabled,
            nextMonthButtonDisabled,
          }) => (
            <div
              style={{
                margin: 10,
                display: 'flex',
                justifyContent: 'center',
              }}
            >
              <button
                onClick={decreaseMonth}
                disabled={prevMonthButtonDisabled}
              >
                <ArrowBackIosIcon />
              </button>
              <select
                value={getYear(date)}
                onChange={({ target: { value } }) => changeYear(Number(value))}
                className='selectbox'
              >
                {years.map((option: string) => (
                  <option key={option} value={option}>
                    {option}년
                  </option>
                ))}
              </select>

              <select
                value={months[getMonth(date)]}
                onChange={({ target: { value } }) =>
                  changeMonth(months.indexOf(value))
                }
                className='selectbox'
              >
                {months.map((option) => (
                  <option key={option} value={option}>
                    {option}월
                  </option>
                ))}
              </select>

              <button
                onClick={increaseMonth}
                disabled={nextMonthButtonDisabled}
              >
                <ArrowForwardIosIcon />
              </button>
            </div>
          )}
          dayClassName={(d) => 'custom-day'}
          dateFormat='yyyy.MM.dd'
          disabledKeyboardNavigation
          locale='ko'
          maxDate={new Date()}
          /////
          selected={endDate}
          onChange={(date) => setEndDate(date)}
          selectsEnd
          startDate={startDate}
          endDate={endDate}
          minDate={startDate}
        />
      </div>
    </div>
  );
}

 

CSS

.custom-wrap4 {
  .date-picker-wrap {
    display: flex;
    margin-top: 2rem;
  }

  //달력 header
  .react-datepicker__header {
    background-color: rgb(124 196 183);
  }
  //✨ 시작~종료 달력일자 구간 선택 hover안되었을경우(원래 회색이었는데 안나타게함)
  .react-datepicker__day {
    &--in-range:not(&--in-selecting-range) {
      background-color: transparent;
      color: #000;
    } //:not() => :not() 선택자는 괄호 안의 선택자를 포함하지 않는 요소를 선택합니다.
    //&--in-range => .react-datepicker__day--in-range
    //:not(&--in-selecting-range) => react-datepicker__day--in-selecting-range 클래스를 갖지 않은 것
  }

  //시작~종료 달력일자 구간 선택 컬러
  .react-datepicker__day--in-selecting-range,
  .react-datepicker__day--in-range {
    background-color: rgb(151 212 207);
  }
  //시작, 종료 달력일자 선택 컬러
  .react-datepicker__day--selecting-range-start,
  .react-datepicker__day--selecting-range-end {
    background-color: rgb(77, 182, 191);
  }

  //my 추가
  .wave {
    line-height: 1.5;
  }
  .input-start-date {
    background-color: rgb(77, 182, 191);
    padding: 1rem 1.4rem;
    color: #fff;
    margin-right: 1rem;
    cursor: pointer;
  }
  
 //위 커스텀 CSS와 동일
  .react-datepicker__day--outside-month {
    color: #a8a8a8 !important;
    pointer-events: none;
  } //보여지는달력 해당월아닌 다른월 날짜는 회색컬러로 글자처리
  .react-datepicker__day-name {
    width: 28px; //달력 요일
    color: #fff;
  }
  .react-datepicker {
    font-size: 1.2rem; //달력 글씨
  }

  .custom-day {
    width: 28px;
    height: 28px;
    line-height: 2.8;
  } //달력일

  .react-datepicker__header {
    background-color: rgb(64 123 128);
    color: #fff;
  }

  /* .react-datepicker__day--selected {
    background-color: rgb(77, 182, 191);
  }  */ //달력 선택 일
  .react-datepicker__triangle {
    display: none;
  } //세모 말꼬리
  .react-datepicker-popper {
    padding-top: 0;
  }
  .selectbox {
    padding: 4px 6px;
    border: none;
    margin: 0 4px;
  }

  .calendar-input-wrap {
    border: 1px solid #999;
    height: 24px;
    text-indent: 4px;
    font-size: 1.8rem;
    width: 128px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 4px;
  }
  .react-datepicker__input-container input {
    font-size: 1.8rem;
    border: none;
    padding: 0;
    width: 100px;
  }
}

 


참고사이트)

 

react-datepicker 적용하기(+ 커스텀)

달력 구현에 어떤 라이브러리를 쓸까 고민하다가 가장 무난하고 많이 쓰이는 react-datepicker를 적용해보았다. React Datepicker 설치, 적용하기 라이브러리 설치 yarn add react-datepicker date-fns yarn add -D @type

doooodle932.tistory.com