Skip to content

Commit

Permalink
feat : 달력컴포넌트 개발 1차 (#61)
Browse files Browse the repository at this point in the history
* feat continue calendar

* feat : 달력 컴포넌트 1차 개발 완료

* feat : 달력 컴포넌트 1차 개발 완료

* feat : 달력 컴포넌트 1차 개발 완료
  • Loading branch information
minjoon97 authored Oct 29, 2024
1 parent 41538fd commit fd80613
Show file tree
Hide file tree
Showing 13 changed files with 419 additions and 13 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"husky": "^9.1.6",
"init": "^0.1.2",
"jest": "^29.7.0",
"moment": "^2.30.1",
"react": "^18.3.1",
"react-calendar": "^5.1.0",
"react-dom": "^18.3.1",
"storybook": "^8.3.0",
"styled-components": "^6.1.13",
Expand Down
3 changes: 3 additions & 0 deletions public/calendar_plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions public/emoji/emoji_blank.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions public/emoji/emoji_grateful.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const App: React.FC = () => {
return (
<QueryClientProvider client={queryClient}>
<GlobalStyles />
hello world!!!
</QueryClientProvider>
);
};
Expand Down
17 changes: 17 additions & 0 deletions src/features/calendar/calendar-logic/calendarDataFetch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const calendarDataFetch = async (activeMonth: string) => {
const userEmail = '[email protected]';
const split = activeMonth.split('-');
try {
const response = await fetch(
`https://td3axvf8x7.execute-api.ap-northeast-2.amazonaws.com/moodi/diary?limit=40&user_email=${userEmail}&year=${split[0]}&month=${split[1]}`
);
if (!response.ok) {
throw new Error('데이터를 가져오는데 실패했습니다.');
}
const data = await response.json();
return data;
} catch (error) {
console.error(error);
return null;
}
};
26 changes: 26 additions & 0 deletions src/features/calendar/calendar-logic/calendarLogic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import moment from 'moment';

export const handleTileClick = (
date: Date,
event: React.MouseEvent<HTMLButtonElement>
) => {
const target = event.currentTarget.querySelector(
'.custom-tile-content'
) as HTMLElement;
const id = target?.getAttribute('data-id');
const dateString = moment(date).format('YYYY-MM-DD');

if (id) {
alert(`${id}로 이동`);
} else {
alert(`${dateString}의 작성 페이지로 이동.`);
}
};

export const getActiveMonth = (
activeStartDate: moment.MomentInput,
setActiveMonth: React.Dispatch<React.SetStateAction<string>>
) => {
const newActiveMonth = moment(activeStartDate).format('YYYY-MM');
setActiveMonth(newActiveMonth);
};
90 changes: 90 additions & 0 deletions src/features/calendar/calendar-ui/CalendarUi.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { StyledCalendar, DateInnerContent } from './calendarUiCss';
import { useState, useEffect } from 'react';
import moment from 'moment';
import { calendarDataFetch } from '../calendar-logic/calendarDataFetch';
import {
getActiveMonth,
handleTileClick
} from '../calendar-logic/calendarLogic';
import { getEmoticonPath } from '../get-emotion-path/getEmotionPath';

const CalendarUi: React.FC = () => {
const curDate = new Date();
const [vlu, onChange] = useState(curDate);
const monthOfActiveDate = moment(vlu).format('YYYY-MM');
const [activeMonth, setActiveMonth] = useState(monthOfActiveDate);
const [fetchedData, setFetchedData] = useState<
{ id: number; created_date: string; emotion: string | null }[]
>([]);
const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

// fetchedData에 데이터를 저장
const loadDates = async () => {
const data = await calendarDataFetch(activeMonth);
if (data) {
const dates = data.map(
(entry: {
id: number;
created_date: string;
emotion: string | null;
}) => ({
id: entry.id,
created_date: moment(entry.created_date).format(
'YYYY-MM-DD'
),
emotion: entry.emotion
})
);
setFetchedData(dates);
}
};

// 일기 여부에 따라 class주어주기
const getTileClassName = (dateparam: Date): string => {
const dateString = moment(dateparam).format('YYYY-MM-DD');
const isCheckedDate = fetchedData.some(
(entry) => entry.created_date === dateString
);

return isCheckedDate ? 'diary-date' : 'nodiary-date';
};

// fetchedData에서 아이템마다 id와 emotion 데이터 넣기
const getTileContent = ({ date }: { date: Date }) => {
const dateString = moment(date).format('YYYY-MM-DD');
const matchedEntry = fetchedData.find(
(entry) => entry.created_date === dateString
);

if (matchedEntry && matchedEntry.emotion) {
return (
<DateInnerContent
data-id={matchedEntry.id}
data-date={matchedEntry.created_date}
emotion={getEmoticonPath(matchedEntry.emotion)}
className="custom-tile-content"
/>
);
}
return null; // 해당 날짜와 일치하는 데이터가 없으면 null 반환
};

useEffect(() => {
loadDates();
}, [activeMonth]);

return (
<StyledCalendar
onActiveStartDateChange={({ activeStartDate }) =>
getActiveMonth(activeStartDate, setActiveMonth)
}
formatDay={(locale, d) => d.getDate().toString()}
formatShortWeekday={(locale, d) => weekDays[d.getDay()]}
tileClassName={({ date }) => getTileClassName(date)}
onClickDay={(date, event) => handleTileClick(date, event)}
tileContent={getTileContent}
/>
);
};

export default CalendarUi;
155 changes: 155 additions & 0 deletions src/features/calendar/calendar-ui/calendarUiCss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import Calendar, { CalendarType } from 'react-calendar';
import styled from 'styled-components';

export const StyledCalendar = styled(Calendar).attrs({
calendarType: 'gregory' as CalendarType
})`
width: 100%;
max-width: 960px;
background-color: rgba(255, 255, 255, 1);
border: none;
font-family: 'Pretendard', sans-serif;
//년월컨테이너
.react-calendar__navigation {
display: flex;
padding: 2rem 0;
//년월선택ui
button {
border: none;
border-radius: 10px;
background-color: white;
outline: none;
font-size: 1.5rem;
font-family: 'Pretendard', sans-serif;
&:hover {
background-color: rgba(0, 0, 0, 0.05);
cursor: pointer;
}
}
//년월텍스트
.react-calendar__navigation__label {
font-size: 1rem;
border: 1px solid rgba(0, 0, 0, 0.1);
margin: 0 1rem;
}
}
//날짜/년월/10년 아이템
.react-calendar__tile {
max-width: initial !important;
padding: 15px;
height: 100px;
background-color: white;
border-radius: 28px;
border: 1px solid rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
justify-content: space-between;
&:hover {
background-color: rgba(0, 0, 0, 0.05);
cursor: pointer;
}
}
//10년박스
.react-calendar__century-view__decades {
display: grid !important;
grid-template-columns: repeat(4, 1fr);
column-gap: 0.5rem;
row-gap: 1rem;
}
//년박스
.react-calendar__decade-view__years {
display: grid !important;
grid-template-columns: repeat(4, 1fr);
column-gap: 0.5rem;
row-gap: 1rem;
}
//월박스
.react-calendar__year-view__months {
display: grid !important;
grid-template-columns: repeat(4, 1fr);
column-gap: 0.5rem;
row-gap: 1rem;
}
//날짜박스
.react-calendar__month-view__days {
display: grid !important;
grid-template-columns: repeat(7, 1fr);
column-gap: 0.5rem;
row-gap: 1rem;
// 체크된 날짜 스타일
.diary-date {
border: 1px solid rgba(0, 0, 0, 0.25);
}
// 체크되지 않은 날짜 스타일
.nodiary-date {
border: 1px solid rgba(0, 0, 0, 0.08);
position: relative;
&:hover {
background-color: rgba(0, 0, 0, 0.1);
cursor: pointer;
}
&:hover::after {
content: '';
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-40%, -40%);
width: 20px;
height: 20px;
background-size: contain;
background-repeat: no-repeat;
background-image: url('calendar_plus.svg');
}
}
// 현재날짜
.react-calendar__tile--now {
background-color: #ffeee9;
border: 1px solid #ff480e;
color: black;
}
// 앞뒤월의 날
.react-calendar__month-view__days__day--neighboringMonth {
visibility: hidden !important;
pointer-events: none !important;
}
}
//요일박스
.react-calendar__month-view__weekdays {
padding: 1rem 0;
//요일아이템
.react-calendar__month-view__weekdays__weekday {
text-align: center;
abbr {
font-size: 14px;
text-decoration: none;
}
}
}
`;

interface DateInnerContentProps {
emotion: string | null;
}

export const DateInnerContent = styled.div<DateInnerContentProps>`
background-image: url(${(props) => props.emotion});
width: 40px;
height: 40px;
background-size: contain;
background-repeat: no-repeat;
align-self: flex-end;
`;
32 changes: 32 additions & 0 deletions src/features/calendar/get-emotion-path/getEmotionPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export const getEmoticonPath = (emotion: string) => {
const emotionMap: Record<string, string> = {
기뻐요: 'emoji_happy',
'자신 있어요': 'emoji_confident',
감사해요: 'emoji_grateful',
편안해요: 'emoji_comfortable',
신이나요: 'emoji_excited',
즐거워요: 'emoji_fun',
만족스러워요: 'emoji_satisfied',
사랑스러워요: 'emoji_lovely',
모르겠어요: 'emoji_not_sure',
부끄러워요: 'emoji_embarrassed',
놀라워요: 'emoji_surprised',
'아무생각이 없어요': 'emoji_blank',
슬퍼요: 'emoji_sad',
우울해요: 'emoji_depressed',
실망스러워요: 'emoji_disappointed',
후회돼요: 'emoji_regret',
짜증나요: 'emoji_annoyed',
화나요: 'emoji_angry',
외로워요: 'emoji_lonley',
충격받았어요: 'emoji_shocked',
곤란해요: 'emoji_awkward'
};

const svgName = emotionMap[emotion];
if (!svgName) {
return null; // 매칭되는 감정이 없을 경우
}

return `emoji/${svgName}.svg`;
};
12 changes: 12 additions & 0 deletions src/features/calendar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import CalendarUi from './calendar-ui/CalendarUi';
import { CalendarWrapper } from './indexCss';

const Calendar = () => {
return (
<CalendarWrapper>
<CalendarUi />
</CalendarWrapper>
);
};

export default Calendar;
6 changes: 6 additions & 0 deletions src/features/calendar/indexCss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import styled from 'styled-components';

export const CalendarWrapper = styled.div`
display: flex;
justify-content: center;
`;
Loading

0 comments on commit fd80613

Please sign in to comment.