Learning Log/React

[react] react datepicker 커스텀하기

자척개 2023. 6. 19. 00:24
반응형

Blocker :

날짜를 입력하는 대신 달력에서 기간을 선택할 수 있도록 datepicker 라이브러리를 사용하게 됐습니다

다만 처음 사용해보는 거라 특히 style 커스텀할 때 어려움이 많았습니다,,,

우선 공식문서를 참고하여 저는 date range 형태로 달력을 만들었습니다

https://reactdatepicker.com/#example-date-range

 

React Datepicker crafted by HackerOne

 

reactdatepicker.com

달력을 입력받는 창과 달력 모양의 스타일을 커스텀해야 했습니다

아래는 datepicker의 기본 스타일입니다

커스텀 전
커스텀 전

 

아래는 커스텀 후 제가 적용해야 할 디자인 시안입니다

커스텀 후
커스텀 후


해결방법 :

똑같은 디자인으로 커스텀하는 예시를 찾기는 어렵기 때문에 다른 분들이 커스텀한 코드를 뜯어보며 진행했습니다

우선 날짜를 입력받는 창에는 이미지를 추가했고 formattedStart와 formattedEnd로 날짜 형식을 year-month-date 형태로 바꿨습니다

아마 날짜 형식을 바꾸는 방식은 코드를 중복 작성한 형태라 더 좋은 방법이 있을 것으로 생각되지만 저는 이 둘을 비교해서 마감날짜가 시작날짜보다 빠르면 입력할 수 없도록 했기 때문에 둘을 따로 관리해야 해서 우선 이런 형태로 구현해보았습니다

// jsx
const formattedStart =
    startDate &&
    startDate.getFullYear() +
      '-' +
      (startDate.getMonth() + 1 <= 9
        ? '0' + (startDate.getMonth() + 1)
        : startDate.getMonth() + 1) +
      '-' +
      (startDate.getDate() <= 9
        ? '0' + startDate.getDate()
        : startDate.getDate());

  const formattedEnd =
    endDate &&
    endDate.getFullYear() +
      '-' +
      (endDate.getMonth() + 1 <= 9
        ? '0' + (endDate.getMonth() + 1)
        : endDate.getMonth() + 1) +
      '-' +
      (endDate.getDate() <= 9 ? '0' + endDate.getDate() : endDate.getDate());

<S.StartDateBox
  value={formattedStart}
  placeholder="시작일자"
  onClick={onClickDate}
  readOnly
  autoFocus="off"/>
<p> ~ </p>
<S.EndDateBox
  value={formattedEnd}
  placeholder="마감일자"
  onClick={onClickDate}
  readOnly
  autoFocus="off"
  invalidEndDate={invalidEndDate}
/>


// style
const DateBox = css`
  position: relative;
  width: 155px;
  height: 50px;
  padding: 10px 40px;
  background: url('/images/upload/calender.png') left 7% center no-repeat;
  border: 1px solid #dbdbdb;
  border-radius: 8px;
  cursor: pointer;

  ::placeholder {
    color: #dbdbdb;
  }
`;

 

그리고 젤 어려웠던 달력을 커스텀하는 코드입니다

여기서 아쉬웠던 점은 현재 제 달력은 선택한 날짜 전체가 주황색 동그라미로 표시되는데 처음에는 시작, 마감일자만 주황색이고 그 사이의 날짜들은 다른 색으로 구현하려 했으나 저의 서치력의 한계로 우선 이렇게 구현했습니다

이 부분은 좀 아쉬움이 남는 부분입니다,,,

 

자세히 설명해보면, 우선 저는 datepicker 밖을 div로 묶어 custombox라는 tag를 만들고 style을 줄 때 그 안에서 class로 각 스타일을 줬습니다

그리고 제 달력을 보시면 상단의 header 부분의 디자인도 변경한 걸 보실 수 있는데, 이건 custom header라는 tag를 통해 수정했습니다

아쉬움이 남았던 부분을 해결하기 위해 class를 통해 style을 다르게 붜 봤는데, range-start와 range-end에 주황색을 하고 day--in-range에 다른 색을 설정해도 결국은 day--in-range 색상에 따라 색깔이 결정되더라구요

그래서 결국 선택된 모든 날짜를 주황색으로 통일했습니다

 

// jsx
<S.CustomBox>
  <DatePicker
    selected={startDate}
    onChange={onChange}
    startDate={startDate}
    endDate={endDate}
    selectsRange
    inline
    locale={ko}
    disabledKeyboardNavigation
    renderCustomHeader={({
      date,
      prevMonthButtonDisabled,
      nextMonthButtonDisabled,
      decreaseMonth,
      increaseMonth,
    }) => (
      <S.CustomHeader>
        <div
          className="btn_month btn_month-prev"
          onClick={decreaseMonth}
          disabled={prevMonthButtonDisabled}
        >
          <img alt="prev" src="images/upload/prev.png" />
        </div>
        <div className="month-day">
          {date.getFullYear()}.{date.getMonth() + 1}
        </div>
        <div
          className="btn_month btn_month-next"
          onClick={increaseMonth}
          disabled={nextMonthButtonDisabled}
        >
          <img alt="next" src="images/upload/next.png" />
        </div>
      </S.CustomHeader>
     
// style
export const CustomBox = styled.div`
  ${flex('center', 'center', null)}

  .react-datepicker__month-container {
    width: 360px;
  }

  .react-datepicker {
    border-style: none;
  }

  .react-datepicker__header {
    background-color: #ffffff;
    border-bottom: 1px solid #ececec;
  }

  .react-datepicker__day-name,
  .react-datepicker__day {
    width: 2.7rem;
    height: 2.7rem;
    line-height: 2.7rem;
  }

  .react-datepicker__day--selected,
  .react-datepicker__day--range-start,
  .react-datepicker__day--range-end,
  .react-datepicker__day--in-range,
  .react-datepicker__day--in-selecting-range {
    background: #ff6a21;
    border-width: 10%;
    border-radius: 50px;
    color: #ffffff;
  }
`;

export const CustomHeader = styled.div`
  ${flex('center', 'center', null)}
  gap: 30px;
  margin: 10px;

  .month-day {
    font-weight: 700;
    font-size: 18px;
    color: #252525;
  }
`;
반응형