From 6d5e64b121097fa455e3a25a0211fc56173fefa5 Mon Sep 17 00:00:00 2001 From: JeonYumin <40783675+JeonYumin94@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:02:00 +0900 Subject: [PATCH] =?UTF-8?q?FE-36=20:sparkles:=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=ED=95=A8=EC=88=98=20(#58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FE-36 :lipstick: 감정달력 UI * FE-36 :sparkles: 캘린더 함수 생성 --- package-lock.json | 10 +++ package.json | 1 + public/icon/arrow-bottom-icon.svg | 5 ++ public/icon/arrow-right-icon.svg | 5 ++ src/hooks/useCalendar.ts | 74 +++++++++++++++++++ src/pageLayout/MypageLayout/MyPageLayout.tsx | 3 +- src/user/ui-profile/Calendar.tsx | 76 ++++++++++++++++++++ src/user/utill/constants.ts | 14 +++- 8 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 public/icon/arrow-bottom-icon.svg create mode 100644 public/icon/arrow-right-icon.svg create mode 100644 src/hooks/useCalendar.ts create mode 100644 src/user/ui-profile/Calendar.tsx diff --git a/package-lock.json b/package-lock.json index d3bddea1..983823d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "axios": "^1.7.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "date-fns": "^3.6.0", "formik": "^2.4.6", "lucide-react": "^0.402.0", "next": "14.2.4", @@ -2910,6 +2911,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", diff --git a/package.json b/package.json index e1fa6298..348c8937 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "axios": "^1.7.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "date-fns": "^3.6.0", "formik": "^2.4.6", "lucide-react": "^0.402.0", "next": "14.2.4", diff --git a/public/icon/arrow-bottom-icon.svg b/public/icon/arrow-bottom-icon.svg new file mode 100644 index 00000000..8e0e4d20 --- /dev/null +++ b/public/icon/arrow-bottom-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icon/arrow-right-icon.svg b/public/icon/arrow-right-icon.svg new file mode 100644 index 00000000..dc5959d5 --- /dev/null +++ b/public/icon/arrow-right-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/hooks/useCalendar.ts b/src/hooks/useCalendar.ts new file mode 100644 index 00000000..1687f326 --- /dev/null +++ b/src/hooks/useCalendar.ts @@ -0,0 +1,74 @@ +import { getDaysInMonth } from 'date-fns'; +import React, { useState } from 'react'; +import { CALENDAR_LENGTH, DAY_OF_WEEK } from '../user/utill/constants'; + +interface CalendarData { + weekCalendarList: number[][]; // 주별 날짜 리스트 + currentDate: Date; // 현재 날짜 + setCurrentDate: React.Dispatch>; // 현재 날짜를 설정하는 함수 +} + +// 이전 달의 날짜를 계산하는 함수 +const getPreviousDays = (firstDayOfCurrentMonth: Date, totalPrevMonthDays: number): number[] => + // 현재 월의 첫 번째 날의 요일을 기준으로 이전 달의 날짜를 배열로 반환 + Array.from({ length: firstDayOfCurrentMonth.getDay() }, (_, index) => totalPrevMonthDays - firstDayOfCurrentMonth.getDay() + index + 1); +// 현재 월의 날짜를 배열로 반환하는 함수 +const getCurrentDays = (totalMonthDays: number): number[] => Array.from({ length: totalMonthDays }, (_, i) => i + 1); // 1부터 totalMonthDays까지의 배열 생성 +// 다음 달의 날짜를 계산하는 함수 +const getNextDays = (currentDayList: number[], prevDayList: number[]): number[] => { + // 다음 달의 날짜 수를 계산하여 배열로 반환 + const nextDayCount = CALENDAR_LENGTH - currentDayList.length - prevDayList.length; + return Array.from({ length: Math.max(nextDayCount, 0) }, (_, index) => index + 1); +}; + +const useCalendar = (): CalendarData => { + const [currentDate, setCurrentDate] = useState(new Date()); // 현재 날짜를 상태로 관리 + + // 현재 월의 총 날짜 수를 가져옴 + const totalMonthDays = getDaysInMonth(currentDate); + + // 이전 달의 마지막 날짜를 계산 + const prevMonthLastDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 0); + // 이전 달의 총 날짜 수를 가져옴 + const totalPrevMonthDays = getDaysInMonth(prevMonthLastDate); + + // 현재 월의 첫 번째 날짜를 계산 + const firstDayOfCurrentMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); + // 이전 달의 날짜 리스트 + const prevDayList = getPreviousDays(firstDayOfCurrentMonth, totalPrevMonthDays); + // 현재 월의 날짜 리스트 + const currentDayList = getCurrentDays(totalMonthDays); + // 다음 달의 날짜 리스트 + const nextDayList = getNextDays(currentDayList, prevDayList); + + // 전체 날짜 리스트 (이전 / 현재 / 다음 달 날짜 포함) + const currentCalendarList = [...prevDayList, ...currentDayList, ...nextDayList]; + + // 주별로 날짜 리스트를 분할 + const weekCalendarList: number[][] = []; + currentCalendarList.forEach((currDate, index) => { + const chunkIndex = Math.floor(index / DAY_OF_WEEK); + if (!weekCalendarList[chunkIndex]) { + weekCalendarList[chunkIndex] = []; // 주 배열이 없으면 초기화 + } + weekCalendarList[chunkIndex].push(currDate); // 누적값 반환 + }); + + // NOTE: 한 달이 5주 일 수도, 6주 일 수도 있을 때 5주인 경우 해당 달에 필요없는 다음 달의 날짜가 출력되기 때문에 (CALENDAR_LENGTH를 최대치인 42로 잡아서) 마지막 주의 첫 번째 숫자가 10이하의 날짜로 시작한다면 해당 배열을 삭제하도록 추가. + // TODO: 추후 다른 방법이 있다면 변경 할 예정 + if (weekCalendarList.length > 0) { + const lastWeek = weekCalendarList[weekCalendarList.length - 1]; + if (lastWeek[0] <= 10) { + weekCalendarList.pop(); + } + } + + // 캘린더 정보를 반환 + return { + weekCalendarList, // 주별 날짜 리스트 + currentDate, + setCurrentDate, + }; +}; + +export default useCalendar; diff --git a/src/pageLayout/MypageLayout/MyPageLayout.tsx b/src/pageLayout/MypageLayout/MyPageLayout.tsx index 0046edaa..7eebb5a9 100644 --- a/src/pageLayout/MypageLayout/MyPageLayout.tsx +++ b/src/pageLayout/MypageLayout/MyPageLayout.tsx @@ -1,6 +1,7 @@ import Header from '@/components/Header/Header'; import { useMeQuery } from '@/hooks/userQueryHooks'; import UserInfo from '@/types/user'; +import Calendar from '@/user/ui-profile/Calendar'; import Profile from '@/user/ui-profile/Profile'; import { useRouter } from 'next/navigation'; @@ -30,7 +31,7 @@ export default function MyPageLayout() {
오늘의 감정
-
캘린더
+
감정차트
diff --git a/src/user/ui-profile/Calendar.tsx b/src/user/ui-profile/Calendar.tsx new file mode 100644 index 00000000..69ffbe35 --- /dev/null +++ b/src/user/ui-profile/Calendar.tsx @@ -0,0 +1,76 @@ +import React, { useState } from 'react'; +import Image from 'next/image'; +import { subMonths } from 'date-fns'; +import { Button } from '@/components/ui/button'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; +import ARROW_BOTTOM_ICON from '../../../public/icon/arrow-bottom-icon.svg'; +import ARROW_RIGHT_ICON from '../../../public/icon/arrow-right-icon.svg'; +import ARROW_LEFT_ICON from '../../../public/icon/arrow-left-icon.svg'; +import useCalendar from '../../hooks/useCalendar'; +import { DAY_LIST, DATE_MONTH_FIXER, DEFAULT_TRASH_VALUE } from '../utill/constants'; + +export default function Calendar() { + const [position, setPosition] = useState('bottom'); + const { weekCalendarList, currentDate, setCurrentDate } = useCalendar(); + + // 이전 달 클릭 + const handlePrevMonth = () => setCurrentDate((prevDate) => subMonths(prevDate, DATE_MONTH_FIXER)); + // 다음 달 클릭 + const handleNextMonth = () => setCurrentDate((prevDate) => subMonths(prevDate, -DATE_MONTH_FIXER)); + + return ( +
+ {/* 캘린더 헤더 */} +
+
+
{`${currentDate.getFullYear()}년 ${currentDate.getMonth() + 1}월`}
+ + + + + + + + EmotionSelector 추가 예정 + + + +
+
+ + +
+
+ {/* 캘린더 */} +
+
+ {DAY_LIST.map((day) => ( +
+ {day} +
+ ))} +
+ {weekCalendarList.map((week, weekIndex) => ( + // eslint-disable-next-line react/no-array-index-key +
+ {week.map((day, dayIndex) => ( + // eslint-disable-next-line react/no-array-index-key +
+ {day === DEFAULT_TRASH_VALUE ? '' : day} +
+ ))} +
+ ))} +
+
+ ); +} diff --git a/src/user/utill/constants.ts b/src/user/utill/constants.ts index e8d8b154..ca63a32d 100644 --- a/src/user/utill/constants.ts +++ b/src/user/utill/constants.ts @@ -1,3 +1,11 @@ -export const MAX_FILE_SIZE = 1024 * 1024 * 5; // 5MB -export const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/jpg', 'image/png']; -export const sampleImage = '/ProfileTestImage.jpg'; +// 파일 업로드 관련 +export const MAX_FILE_SIZE = 1024 * 1024 * 5; // 파일 업로드 최대 용량 5MB +export const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/jpg', 'image/png']; // 허용 가능 확장자 +export const sampleImage = '/ProfileTestImage.jpg'; // 초기프로필 이미지 + +// 캘린더 관련 상수 +export const DAY_LIST = ['일', '월', '화', '수', '목', '금', '토']; // 요일 +export const DATE_MONTH_FIXER = 1; // 날짜 조정 상수 (현재 사용되지 않음, 필요에 따라 활용 가능) +export const CALENDAR_LENGTH = 42; // 6주에 맞추어 캘린더의 총 길이를 42로 설정 +export const DAY_OF_WEEK = 7; // 한 주의 날 수 (일~토) +export const DEFAULT_TRASH_VALUE = -1; // 기본값 설정 (필요에 따라 사용 가능)