본문으로 바로가기

왜 안 패키지? => 디자인이 구려서

React-Calendar 이거 좋은데 weekly download가 150,708라 react-day-picker의 1/3이다. 

다만 프로젝트 관리가 react-day-picker보다 훨씬 잘되고 있다.

www.npmjs.com/package/react-calendar

 

toast-ui 이건 그냥 일정 관리할 때나 편하지. 지금 프로젝트에서는 안 맞는다. 기각. 디자인은 인정 ㅇㅇ

ui.toast.com/tui-calendar

 

react-day-picker 사용하기 npm weekly download가 399,438이다. 쩐다. 그런데 최근 프로젝트 관리가 잘되고 있지는 않다. 

react-day-picker.js.org/

 

 

어케 만듦?

딱히 막 어려운 건 없지만, moment.js의 공식 문서가 찾아보기 편하게 색인이 되어있는 편은 아니라 고생했다.

momentjs.com/docs/

 

Moment.js | Docs

moment.relativeTimeThreshold(unit); // getter moment.relativeTimeThreshold(unit, limit); // setter duration.humanize has thresholds which define when a unit is considered a minute, an hour and so on. For example, by default more than 45 seconds is consider

momentjs.com

 

달력을 생성하기 위한 로직에 대해서는 주석 처리했다.

 

간단히 설명하자면 다음과 같다.

1. 오늘이 속하는 달의 첫 날은 1년 52주 중 몇번 째 주에 해당하는가? (윤년은 알아서 moment에서 처리해준다)

2. 오늘이 속하는 달의 마지막 날은 1년 52주 중 몇번 째 주에 해당하는가?

3. 시작 주부터 마지막 주를 +1 씩 돌아가며 날짜를 7일씩 달력에 뿌린다.

4. profit!

 

* moment() 값을 처음 state로 넣어줬는데 이는 계산해야 하는 판정값이므로 함수 형태로 넣어주었다. React 에서의 작은 팁이다 ㅋ

* 달을 jump 하는 경우 day를 30일 씩 빼고 더하는 식으로 처리하였다. 0, 1로 넣어서 truthy, falsy함으로 함수를 재사용할 수 있도록 만들었다.

* 현재 날짜를 state로 관리하자.

 

import React, { useState } from 'react';
import styled from 'styled-components';
import moment, { Moment as MomentTypes } from 'moment';

function Calendar() {
  const [date, setdate] = useState<moment.Moment>(() => moment());

  // func
  const handleDayClick = (current: moment.Moment) => setdate(current);
  const returnToday = () => setdate(moment());
  const jumpToMonth = (num: number) => (num ? setdate(date.clone().add(30, 'day')) : setdate(date.clone().subtract(30, 'day')));

  // chalandar generate logic
  function generate() {
    // 님 날짜 뭐 눌렀어요? (초기값은 오늘)
    const today = date;

    // startOf('month') : 이번 달의 첫번 째 날로 설정 set to the first of this month, 12:00 am
    // week() : Week of Year. 이번 년도의 몇번째 주인가? => 3월 8일이면 10이겠죠?
    const startWeek = today.clone().startOf('month').week();

    // endOf('month').week() : 이번 달의 마지막 날로 설정 한 후 그것이 이번 년도의 몇번째 주인지 체크
    // 만약 이번 해의 첫번째 주(1월 1일이 속한 주)라면 53으로 세팅, 아니라면 그대로 유지
    // 이런 작업의 이유는 마지막 주가 첫 주가 될 수 없기 때문에 당연한 것임
    const endWeek = today.clone().endOf('month').week() === 1 ? 53 : today.clone().endOf('month').week();

    let calendar = [];

    // 시작 주부터 마지막 주까지 +1 씩 증가시킴
    // 이제 주마다 일을 표기해야 하므로 len이 7인 arr를 생성 후 index를 기반으로 day를 표기하자
    for (let week = startWeek; week <= endWeek; week++) {
      calendar.push(
        <div className="row" key={week}>
          {Array(7)
            .fill(0)
            .map((n, i) => {
              // 오늘 => 주어진 주의 시작 => n + i일 만큼 더해서 각 주의 '일'을 표기한다.
              let current = today
                .clone()
                .week(week)
                .startOf('week')
                .add(n + i, 'day');

              // 오늘이 current와 같다면 우선 '선택'으로 두자
              let isSelected = today.format('YYYYMMDD') === current.format('YYYYMMDD') ? 'selected' : '';

              // 만약, 이번 달이 아닌 다른 달의 날짜라면 회색으로 표시하자
              let isGrayed = current.format('MM') !== today.format('MM') ? 'grayed' : '';

              return (
                <div className={`box ${isSelected} ${isGrayed}`} key={i} onClick={() => handleDayClick(current)}>
                  <span className="text">{current.format('D')}</span>
                </div>
              );
            })}
        </div>,
      );
    }
    return calendar;
  }

  return (
    <S.Wrapper>
      <S.CalendarHead>
        <div className="head">
          <span className="title">{date.format('MMMM YYYY')}</span>
          <div className="util-button">
            <button onClick={() => jumpToMonth(0)}>
              <i className="fas fa-angle-left"></i>
            </button>
            <button onClick={returnToday}>Today</button>
            <button onClick={() => jumpToMonth(1)}>
              <i className="fas fa-angle-right"></i>
            </button>
          </div>
        </div>
      </S.CalendarHead>
      <S.CalendarBody>
        <div className="row">
          {['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'].map((el) => (
            <div className="box" key={el}>
              <span className="text">{el}</span>
            </div>
          ))}
        </div>
        {generate()}
      </S.CalendarBody>
    </S.Wrapper>
  );
}
export default Calendar;

 

결과물을 다음과 같이 생겼다.

달력이 커서 도중에 짤린거지, 정상적으로 잘 보인다 ㅇㅇ

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체