Final Project 신세계아이앤씨

캘린더 (react, npm, 커스텀)

pjh8838 2024. 7. 23. 15:37
반응형

리액트 캘린더 라이브러리 설치 후 커스텀 할려고 했으나 실패 ( 이유 : 타입스크립트 형식 파일, css 재정의 )

라이브러리 설치하면 node-modules에 설치되고 

커스텀할려면 react-calendar 밑에 dist에 css 파일을 오버라이딩(재정의) 해야 된다.

 

css 재정의는 

같은 이름의 css파일을 내 src 아래 생성 후

같은 클래스명에 대해 css를 적어준다

=> 이 방법으로는 내가 만들려는 캘린더와 똑같이 만들기 힘들었고 다른 방법을 찾음

 

---------------------------------------------------------------------------------------------------------------------

새로운 방법

달력의 기본적인 틀은 내가 만들고 날짜값만 라이브러리로 설치해서 받아온다

 

npm install date-fns  // 날짜 라이브러리 설치

 

import React, { useState } from 'react';
import { format, addMonths, subMonths, startOfMonth, endOfMonth, startOfWeek, addDays, getDay, getWeek, isSameMonth } from 'date-fns';
import '../components/Calendar.css';
import ExpiringProducts from './ExpiringProducts';

const Calendar = ({ onDateSelect }) => {
  const [currentDate, setCurrentDate] = useState(new Date()); // 현재 날짜를 저장하는 상태
  const [selectedDate, setSelectedDate] = useState(null); // 선택된 날짜를 저장하는 상태

  // 한국어로 된 월 이름을 반환하는 함수
  const getKoreanMonth = (date) => {
    const months = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
    return months[date.getMonth()];
  };

  // 해당 월의 날짜들을 반환하는 함수
  const getDaysInMonth = (date) => {
    const start = startOfWeek(startOfMonth(date), { weekStartsOn: 1 });
    let days = [];
    for (let i = 0; i < 35; i++) { // 5주(7일*5=35일)를 표현하기 위해 35일을 계산
      days.push(addDays(start, i));
    }
    return days;
  };

  // 특정 날짜가 공휴일인지 확인하는 함수
  const isHoliday = (date) => {
    const dayOfWeek = getDay(date); // 요일을 가져옴 (0: 일요일, 1: 월요일, ...)
    const weekOfMonth = getWeek(date) - getWeek(startOfMonth(date)); // 해당 월의 몇 번째 주인지 계산
    return dayOfWeek === 0 && (weekOfMonth === 2 || weekOfMonth === 4); // 둘째 주, 넷째 주 일요일을 공휴일로 간주
  };

  const days = getDaysInMonth(currentDate); // 현재 월의 날짜들 가져오기

  // 이전 달로 이동하는 함수
  const onPrevMonth = () => setCurrentDate(subMonths(currentDate, 1));
  // 다음 달로 이동하는 함수
  const onNextMonth = () => setCurrentDate(addMonths(currentDate, 1));

  // 날짜를 클릭했을 때 실행되는 함수
  const handleDateClick = (date) => {
    setSelectedDate(date); // 선택된 날짜 상태 업데이트
    onDateSelect(date); // 부모 컴포넌트로 선택된 날짜 전달
  };

  return (
    <div className="calendar">
      <div className="calendar-header">
        <button onClick={onPrevMonth} className='cal_btnP'>&lt;</button> {/* 이전 달 버튼 */}
        <h2>{getKoreanMonth(currentDate)} {format(currentDate, 'yyyy')}</h2> {/* 현재 월과 연도 표시 */}
        <button onClick={onNextMonth} className='cal_btnN'>&gt;</button> {/* 다음 달 버튼 */}
      </div>
      <div className="calendar-body">
        <div className="calendar-week-days">
          {['월', '화', '수', '목', '금', '토', '일'].map(day => (
            <div key={day} className="week-day">{day}</div> // 요일 표시
          ))}
        </div>
        <div className="calendar-days">
          {days.map(day => (
            <div
              key={day.toString()}
              className={`calendar-day
                ${!isSameMonth(day, currentDate) ? 'other-month' : ''} // 다른 달의 날짜 표시
                ${isHoliday(day) ? 'holiday' : ''} // 공휴일 표시
                ${selectedDate && day.toDateString() === selectedDate.toDateString() ? 'selected' : ''}` // 선택된 날짜 표시
              }
              onClick={() => handleDateClick(day)}
            >
              {format(day, 'd')} // 날짜 숫자 표시
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

export default Calendar;

 

 


  .calendar {
    font-family: 'Noto Sans KR', Arial, sans-serif;
    width: 400px;
    border: 1px solid #ddd;
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    margin-bottom: 10px;
  }
 
  .calendar-header {
    background-color: #f0f0f0;
    padding: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #aada2a;
  }
 
  .calendar-header button {
    background: none;
    border: none;
    font-size: 18px;
    cursor: pointer;
  }

  .cal_btnP {
    margin-right: 10px;
  }
  .cal_btnN {
    margin-left: 10px;
  }
 
  .calendar-header h2 {
    margin: 0;
    font-size: 18px;
  }
 
  .calendar-body {
    padding: 10px;
  }
 
  .calendar-week-days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 5px;
    margin-bottom: 10px;
  }
 
  .week-day {
    text-align: center;
    font-weight: bold;
    color: #777;
  }
 
  .calendar-days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    grid-template-rows: repeat(5, 1fr);
    gap: 5px;
    height: 200px; /* 달력 높이 고정 */
  }
 
  .calendar-day {
    text-align: center;
    padding: 5px;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
  }
 
  .calendar-day:hover {
    background-color: #f0f0f0;
  }
 
  .calendar-day.other-month {
    color: #ccc;
  }
 
  .calendar-week-days div:nth-child(7),
  .calendar-days div:nth-child(7n) {
    color: #ff5555;
  }
 
  .calendar-day.holiday {
    color: #ff0000;
    font-weight: bold;
    background-color: #ffeeee;
  }


  .calendar-day.selected {
    background-color: #aada2a;
    color: white;
  }
 
  .expiring-products {
    margin-top: 20px;
    padding: 15px;
    border: 1px solid #ddd;
    border-radius: 8px;
    background-color: #f9f9f9;
  }
 
  .expiring-products h3 {
    margin-top: 0;
    color: #333;
  }
 
  .expiring-products ul {
    list-style-type: none;
    padding: 0;
  }
 
  .expiring-products li {
    margin-bottom: 10px;
    padding: 5px;
    background-color: #fff;
    border: 1px solid #eee;
    border-radius: 4px;
  }

 

728x90
반응형