💚 Calendar & Date Picker를 만들어보자
시간이 흘러 벌써 2023년이 되었다.. 심지어 23년의 1분기가 지나가 버렸다.. 시간 너무 빠른 것 😂
날짜 개념을 잃지 않도록(?) 캘린더 & Date Picker를 구현해 보자!
✨ 결과물
👉🏻 input 창을 누르면 캘린더가 뜨고, 현재 해당 날짜인 2023-04-23을 표시하고 있다.
2023년 외에 이전 연도, 이후 년도 모두 날짜에 맞게 달력으로 표시하도록 구현하였다.
캘린더 외에 Date Picker도 구현해야 하기 때문에 캘린더를 만들고, 이후에 Date Picker를 구현하였다.
캘린더에 추가한 다른 기능들은 아래에서 소개할 계획이다 🙌🏻
📌 캘린더 레이아웃
👉🏻 calendar-nav는 flexbox로 구현 (1차원 선형 레이아웃에 적합)
👉🏻 calendar-grid는 grid로 구현 (2차원 매트릭스 레이아웃에 적합)
👉🏻 css 변수(css 커스텀 프로퍼티) 사용하여 HTML 요소의 width 값의 변화에 반응하는 뷰 구현
💚 calendar-nav는 flex 적용, calendar-grid는 grid로 각각 적용
// .html 'calendar-nav'
<div class="calendar-nav">
<i class='prev bx bxs-left-arrow'></i>
<h2 class="dateTitle"></h2>
<i class='next bx bxs-right-arrow' ></i>
</div>
// .css 'calendar-nav'
.calendar-nav{
border-radius: 15px 15px 0 0;
display: flex;
height: 20%;
justify-content: space-around;
align-items: center;
background-color: var(--calender-background);
}
// .html 'calendar-grid'
<div class="grid calendarBoard"></div>
// .css 'calendar-grid'
.grid{
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: calc(var(--calendar-width) / 20);
}
✅ flex는 원하는 크기의 구간을 여러 개 만들어 정렬해 놓을 때 사용하기 편한 css이기 때문에 calendar-nav에서 왼쪽, 오른쪽 버튼, 년도와 월을 나타내기에 적절한 css이다.
✅ grid는 일정한 크기를 원하는 구간만큼 만들며 이를 반복했을 때 사용하면 정말 편리하기 때문에 계속적으로 반복되는 화면인 날짜를 구현하기에 적합한 것 같다.
💚 css변수에 따른 width값 변화에 반응하는 뷰 구현
// 기본 크기 20rem 설정
:root {
--calendar-width: 20rem;
}
// width 값에 따른 변경 예시
.mouseover{
width: calc(var(--calendar-width) / 15);
height: calc(var(--calendar-width) / 17);
margin: -5px;
padding: 3px;
border: 1px solid var(--calender-background);
border-radius: 50%;
background-color: var(--calender-background);
color: var(--font-title-color);
transform: translate(calc(var(--calendar-width) / 65));
cursor: pointer;
}
✅ css를 전부 가져올 수 없어, width값에 따라 크기를 계산한 mouseover 예시를 가져왔다.
기본으로 지정한 width값에서 값이 변경될 때마다 동시에 크기를 계산하여 css가 적용되도록 작성하였다.
✨ 결과물
> 검사도구의 스타일에서 '--calendar-width: ' 값을 보자! (20 rem > 15 rem > 10 rem 순서)
📌 캘린더 기능
👉🏻 현재를 기준으로 .calendar 요소의 콘텐츠를 동적으로 생성
👉🏻 현재 표시 중인 달의 1일 앞과 말일 뒤에 이전 달과 다음 달의 날짜 표시
👉🏻 캘린더에 현재 날짜 표시, 일요일은 빨간 폰트 적용
👉🏻 날짜 클릭 시, 해당 날짜 'yyyy-mm-dd' 형식으로 콘솔 출력
💚 캘린더 기능을 위한 기본 설정
export const Calendarmaker = (date) => {
const currentYear = date.getFullYear();
const currentMonth = date.getMonth() + 1;
const currentDay = date.getDate();
const firstDay = new Date(date.setDate(1)).getDay();
const lastDay = new Date(currentYear, currentMonth, 0).getDate();
const lastNextDay = new Date(currentYear, currentMonth, 0).getDay();
}
✅ const date = new Date(); 으로 현재 날짜를 할당한 date을 인자값으로 넘겨, 캘린더 기능을 위한 기본적인 날짜를 설정해 주었다.
💚 calendar-nav 생성
export const get = (target) => document.querySelector(target);
let html = '';
let fullMonth = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
calendarBoard.innerHTML = html;
const month = fullMonth[date.getMonth()];
get('.dateTitle').innerText =
`${month}
${currentYear}`;
💚 달력 날짜 생성 (전월 마지막주 날짜 - 해당 월 - 익월 첫 주 날짜)
for (let i = firstDay; i > 0; i--) {
html += `<div class="firstDay-prev">${lastDay - i + 1}</div>`;
}
for (let i = 1; i <= lastDay; i++) {
html += `<div>${i}</div>`;
}
for (let i = 1; i < 7 - lastNextDay; i++) {
html += `<div class="lastDay-next">${i}</div>`;
}
calendarBoard.innerHTML = html;
💚 공휴일 표시 & 버튼 <> 클릭 시, 전월 & 익월 생성
// 공휴일 표시하기
for (let date in monthFull) {
if ((+date % 7 == 0 && +date != 0) || (+date % 7 == 0 && monthFull[date].innerHTML == 1)) {
monthFull[date].classList.add('dayoff');
}
}
// 전월 (<)
prevBtn.onclick = () => {
Calendarmaker(new Date(date.setMonth(date.getMonth() - 1)));
};
// 익월 (>)
nextBtn.onclick = () => {
Calendarmaker(new Date(date.setMonth(date.getMonth() + 1)));
};
💚 날짜 클릭 시, 'yyyy-mm-dd' 출력
let dateFormat = currentYear +
'-' + (currentMonth <= 9 ? "0" + currentMonth : currentMonth) +
'-' + (Number((e.target.textContent)) <= 9 ? '0' + (e.target.textContent) : (e.target.textContent));
✅ 1일 ~ 9일까지 숫자 한 개로 출력되는 경우, 앞에 '0'을 붙이기 위해 조건을 작성하였다.
✨ 결과물
✅ 현재 촬영 날짜 (2023-04-23)를 표시하고 있고, 공휴일은 빨간색, 전월 & 익월의 마지막주, 첫째 주는 회색으로 표시하였다.
✅ 해당 날짜를 클릭하면 콘솔창에 'yyyy-mm-dd' 형식으로 뜨도록 구현하였다.
✅ 추가로 mouseover, mouseout으로 css 효과를 주었다.
📌 DatePicker 기능
👉🏻 DatePicker를 클릭(포커스)하면 캘린더가 렌더링 되고, 캘린더의 날짜를 클릭하면 해당 날짜가 DatePicker의 값으로 출력
👉🏻 캘린더와 DatePicker 이외의 영역을 클릭하면 캘린더가 사라짐
👉🏻 DatePicker의 값이 존재할 때 DatePicker를 다시 클릭하면 DatePicker의 값을 기준으로 캘린더를 렌더링
💚 달력 표시 & 달력 삭제
// input창 click시 달력 표시
selectDate.addEventListener('click', (e) => {
e.stopPropagation();
if (e.target.className !== 'selectDate') return;
calendarContainer.classList.add('show');
});
// 달력 외 click시 달력 삭제
container.addEventListener('click', (e) => {
e.stopPropagation();
if (calendarContainer.classList.contains('show')
&& ((e.target.className === 'container')
|| (e.target.className === 'date-picker'))) {
calendarContainer.classList.remove('show');
}
});
💚 클릭한 날짜 input창에 표시하기
// input창에 click한 날짜 나타내기
calendarContainer.addEventListener('dateChange', (e) => {
e.stopPropagation();
const dateShow = e.detail;
selectDate.value = dateShow;
calendarContainer.classList.remove('show');
});
.
.
.
// CustomEvent
if (today) {
calendarContainer.dispatchEvent(
new CustomEvent('dateChange', {
detail: dateFormat,
bubbles: true,
}),
);
}
✅ star-rating 토이프로젝트에서 처음 적용했던 CustomEvent를 사용하여 클릭한 날짜를 캐치해 화면에 표시하였다.
✨ 결과물
✅ 캘린더 외에 다른 공간을 클릭할 경우, 캘린더가 삭제되도록 구현하였다. (다른 기능은 위에 결과물 동영상에서 확인)
🫠 레이아웃부터 스타일, 기능까지 직접 생각하면서 처음부터 만들어본 것은 거의 처음이었기 때문에 캘린더 & DatePicker를 구현하면서 너무 재밌었다. 오히려 조건대로 만들어야 한다는 부담감보다는 캘린더를 만들려면 '이렇게 조건대로 하면 되는구나?!' 하고 힌트를 얻은 느낌이었다.
아쉬운 점이 있다면 콘솔창과 input창에 'yyyy-mm-dd'로 출력하는 기능을 캘린더와 DatePicker 두 개의 파일에 반복해서 작성한 점이다. 처음에 캘린더를 생성하는 함수 내에 작성하지 말고, 따로 빼서 작성했으면 코드가 반복되지 않아도 됐겠다.. 하는 아쉬움이 남았다.
그래도 사용자가 한눈에 쉽게 볼 수 있도록 공휴일이나 전달 마지막 주 표시 등을 다양한 색깔로 나타내는 것도 구현해 봐서 좋았고, 토이프로젝트지만 처음부터 끝까지 구현해서 내가 아직 이 부분을 잘 못 다루는 구나하는 부족한 점도 알게 되어 재밌었다 😆
'프로젝트 > 토이 프로젝트' 카테고리의 다른 글
[React] 야구게임(BULLS AND COWS) (0) | 2023.04.28 |
---|---|
[javascript] News-Viewer (0) | 2023.04.24 |
[javascript] star-rating (0) | 2023.04.22 |
[javascript] AnalogClock (0) | 2023.04.06 |
[javascript] Toggle side navigation (0) | 2023.03.30 |