원래는 npm 관련 모듈을 사용하려고 했으나...
css적용하기도 까다롭기도 해서 그냥 내가 만들려고 한다
도전!
일단 과정은 다음과 같다
1. 오늘이 속하는 달의 첫날은 1년 52주 중 몇번째 주?
2. 오늘이 속하는 달의 마지막 날은 1년 52주 중 몇번째 주?
3. 첫 주부터 마지막 주를 +1 씩 돌아가며 날짜를 7일씩 달력에 뿌린다
moment 라이브러리를 사용해줄것이다.
const [date, setDate] = useState(moment());
const today = date.clone().format("YYYY-MM-DD") // 오늘 날짜 미리 저장
builddCalendar함수로 달력이 출력될 때 필요한 array들을 관리해준다.
const buildCalendar = () => {
const dateStartWeek = date.clone().startOf('month').week();
// date가 속하는 달의 첫번쨰 주가 이번년도의 몇번째 주인가
const dateEndWeek = date.clone().endOf('month').week() === 1 ? 53 : date.clone().endOf('month').week();
// date가 속하는 달의 마지막 주가 이번년도의 몇번째 주인가
// 마지막 주가 1이 나오는 경우(12월의 마지막주에서 1월이 과반수일때)엔 53으로 수정해주어야 함
let calendar = [];
for (let week = dateStartWeek; week <= dateEndWeek; week++) {
calendar.push(
<div key={week} className="grid grid-cols-7 items-center justify-items-center">
{[Array(7).fill(0).map( (n,i) => {
let current = date.clone().week(week).startOf('week').add(i, 'day');
// 현재 상태의 날짜의 달의 첫 주부터 날짜를 세어준다
const isMonth = current.format('MM') !== date.format('MM'); // current가 이번달이 아닌가?
const isToday = current.clone().format(`YYYY-MM-DD`) === today; // cureent가 오늘인가?
return <span key={i} className={cls("text-center", isMonth ? "text-gray-200": "", !isMonth&& isToday ? "w-5 h-5 ring-2 ring-[#C5E5E9] rounded-full" :"")} >{current.format("D")}</span>
})]}
</div>
)
}
return calendar
}
...
<div className="space-y-3" >{buildCalendar()}</div>
이건 어떤 버튼이냐에 따라 setDate를 다시 설정해주면 되는거라 쉽다
...
const handleMonth = (num) => (num ? setDate(date.clone().add(1, 'month')) : setDate(date.clone().subtract(1, 'month')));
const handleYear = (num) =>(num ? setDate(date.clone().add(1, 'year')) : setDate(date.clone().subtract(1, 'year')));
...
<div className="flex justify-center items-center space-x-5 pt-3 pb-5">
<span onClick={() => handleYear(0)}>
//"<<"
</svg>
</span>
<span onClick={()=>handleMonth(0)}>
//"<"
</span>
<span className="w-20 text-center font-semibold" >{date.clone().format("MMMM")}</span>
<span onClick={()=>handleMonth(1)}>
//">"
</span>
<span onClick={() => handleYear(1)}>
//">>"
</span>
이걸 구현하느라 꽤 애먹었는데 ....ㅜㅜ
정리하다보니까 왜 그렇게 오래 걸렸나 싶다.
일단 올해 연도를 별도로 변수로 저장해준다.
그리고 연도가 클릭되었는지 아닌지에 대한 변수도 지정해준다.
const [selectedYear, setSelectedYear] = useState(Number(date.clone().format("YYYY")));
const [yearClicked, setYearClicked] = useState(false);
이후 현재 보여지는 연도가 클릭되면 다른 연도들의 리스트를 보여준다.
const buildYear = () => {
let years = [];
const onClickSelectedYear = (i) => {
setDate(date.clone().year(i))
setYearClicked(false);
}// 클릭되면 date를 해당연도로 다시 설정하고 yearClicked를 false로 설정
for (let i = selectedYear; i < selectedYear + 12 ; i++) {
years.push(<span className={cls(i==date.clone().format('YYYY') ? "text-textPoint": "")} onClick={() =>onClickSelectedYear(i)}>{i}</span>)
// i 가 현재 눌러지는 연도와 같다면 text 색상 변경
}
return years
}
...
<div className="grid grid-cols-4 gap-4">
{buildYear()}
</div>
연도들은 12개씩 보여줄 것인데 위아래 버튼을 통해 보여지는 숫자들을 조절한다.
const handleSelectedYear = (num) => num ? setSelectedYear(selectedYear + 12): setSelectedYear(selectedYear - 12);
...
<button onClick={()=>handleSelectedYear(0)}>
// ↑
</button>
<button onClick={()=>handleSelectedYear(1)}>
// ↓
</button>
year이 클릭되면 이 모든것을 보여줄 것이므로 삼항연산자를 이용해준다.
{yearClicked ? 보여줄 것들 : null}
추가적으로 해당 부분이 아닌 바깥쪽을 누르면 무조건 사라지게 하고 싶어서 div하나를 더 만들어줬다.
const onClickYear = () => {
setYearClicked(prev => !prev)
}
...
{yearClicked ?<div className="fixed top-0 bottom-0 z-10 w-screen h-srceen opacity-0" onClick={onClickYear}></div> : null}
[Next] 대충프로젝트(3) - useEffect 의존성 배열 제대로 사용하기 (0) | 2023.05.12 |
---|---|
[Next] 대충프로젝트(1) - 기능 및 UI/UX (0) | 2023.02.04 |