Skip to content

Commit 8ad5dbc

Browse files
committed
feat: 캘린더 YYYY.MM 상단 날짜 표기 추가
1 parent 24eeb56 commit 8ad5dbc

File tree

6 files changed

+157
-79
lines changed

6 files changed

+157
-79
lines changed

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function App() {
2727
</label>
2828
</div>
2929
<div className="flex-1">
30-
<h1 className="base-200 min-w-40 p-2 text-center">개인 일정 캘린더</h1>
30+
<h1 className="text-sm min-w-40 p-2 text-center bg-base-200 rounded flex-end">개인 일정 캘린더</h1>
3131
</div>
3232
</div>
3333
</div>

src/components/common/Calendar.tsx

Lines changed: 115 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,97 +4,134 @@ import interactionPlugin from '@fullcalendar/interaction';
44
import { useRef, useState, useEffect } from 'react';
55

66
const events = [
7-
{ title: 'Meeting', start: new Date() },
8-
{ title: 'Meeting', start: '2024-05-08' },
9-
{ title: 'Meeting', start: '2024-05-08' },
10-
{ title: 'Meeting', start: '2024-05-08' },
7+
{ title: 'Meeting', start: new Date() },
8+
{ title: 'Meeting', start: '2024-05-08' },
9+
{ title: 'Meeting', start: '2024-05-08' },
10+
{ title: 'Meeting', start: '2024-05-08' },
1111
];
1212

1313
export function Calendar() {
14-
const [calendarHeight, setCalendarHeight] = useState<string | number>('auto');
15-
const calendarRef = useRef<FullCalendar | null>(null);
16-
const handlePrev = () => {
17-
const calendarApi = calendarRef?.current?.getApi();
18-
if (calendarApi) {
19-
calendarApi.prev();
20-
} else {
21-
console.error('Calendar API is not available.');
22-
}
23-
};
14+
const [calendarHeight, setCalendarHeight] = useState<string | number>('auto');
15+
const calendarRef = useRef<FullCalendar | null>(null);
16+
const handlePrev = () => {
17+
const calendarApi = calendarRef?.current?.getApi();
18+
if (calendarApi) {
19+
calendarApi.prev();
20+
} else {
21+
console.error('Calendar API is not available.');
22+
}
23+
};
2424

25-
const handleNext = () => {
26-
const calendarApi = calendarRef?.current?.getApi();
27-
if (calendarApi) {
28-
calendarApi.next();
29-
} else {
30-
console.error('Calendar API is not available.');
31-
}
32-
};
25+
const handleNext = () => {
26+
const calendarApi = calendarRef?.current?.getApi();
27+
if (calendarApi) {
28+
calendarApi.next();
29+
} else {
30+
console.error('Calendar API is not available.');
31+
}
32+
};
3333

34-
useEffect(() => {
34+
// eslint-disable-next-line react-hooks/exhaustive-deps
3535
const updateSize = () => {
36-
const isMobile = window.innerWidth < 768;
37-
setCalendarHeight(isMobile ? 500 : 'auto');
36+
const isMobile = window.innerWidth < 768;
37+
setCalendarHeight(isMobile ? 500 : 'auto');
38+
updateTitle();
3839
};
3940

40-
window.addEventListener('resize', updateSize);
41-
updateSize();
42-
43-
return () => window.removeEventListener('resize', updateSize);
44-
}, []);
41+
const updateTitle = () => {
42+
const calendarApi = calendarRef?.current?.getApi();
43+
if (calendarApi) {
44+
const calendarView = calendarApi.view;
45+
46+
const date = new Date(calendarView.currentStart);
47+
const year = date.getFullYear();
48+
const month = (date.getMonth() + 1).toString().padStart(2, '0');
49+
const newTitle = `${year}.${month}`;
50+
51+
const titleElement = document.querySelector(".fc-toolbar-title");
52+
if (titleElement) {
53+
titleElement.textContent = newTitle;
54+
}
55+
}
56+
};
57+
58+
useEffect(() => {
59+
const calendarApi = calendarRef?.current?.getApi();
60+
61+
if (calendarApi) {
62+
calendarApi.on('datesSet', updateTitle);
63+
}
64+
65+
updateTitle(); // 컴포넌트 마운트 시 제목 업데이트
66+
67+
/* 캘린더 - 반응형 사이즈 */
68+
window.addEventListener('resize', updateSize);
69+
updateSize(); // 컴포넌트 마운트 시 화면 크기에 따른 업데이트
70+
71+
return () => {
72+
window.removeEventListener('resize', updateSize);
73+
if (calendarApi) {
74+
calendarApi.off('datesSet', updateTitle);
75+
}
76+
}
77+
}, [updateSize]);
4578

46-
return (
47-
<div>
48-
<FullCalendar
49-
ref={calendarRef}
50-
plugins={[dayGridPlugin, interactionPlugin]}
51-
initialView="dayGridMonth"
52-
events={events}
53-
dayMaxEvents={2} //Max개수까지보이고 나머지는 more
54-
navLinks={true} // 날짜/주 이름을 클릭하여 뷰를 변경할 수 있습니다.
55-
editable={true} // 이벤트를 수정할 수 있습니다.
56-
eventContent={renderEventContent}
57-
contentHeight={calendarHeight}
58-
titleFormat={{
59-
year: 'numeric',
60-
month: '2-digit',
61-
meridiem: false,
62-
}}
63-
dayHeaderFormat={{
64-
weekday: 'short',
65-
}}
66-
headerToolbar={{
67-
left: 'prevButton',
68-
center: 'title',
69-
right: 'nextButton',
70-
}}
71-
customButtons={{
72-
prevButton: {
73-
icon: 'chevron-left',
74-
click: handlePrev,
75-
},
76-
nextButton: {
77-
icon: 'chevron-right',
78-
click: handleNext,
79-
},
80-
}}
81-
/>
82-
</div>
83-
);
79+
return (
80+
<div>
81+
<FullCalendar
82+
ref={calendarRef}
83+
plugins={[dayGridPlugin, interactionPlugin]}
84+
initialView="dayGridMonth"
85+
events={events}
86+
dayMaxEvents={2} //Max개수까지보이고 나머지는 more
87+
navLinks={true} // 날짜/주 이름을 클릭하여 뷰를 변경할 수 있습니다.
88+
editable={true} // 이벤트를 수정할 수 있습니다.
89+
eventContent={renderEventContent}
90+
contentHeight={calendarHeight}
91+
titleFormat={{
92+
year: 'numeric',
93+
month: '2-digit',
94+
}}
95+
eventTimeFormat={{
96+
hour: '2-digit',
97+
minute: '2-digit',
98+
meridiem: false,
99+
}}
100+
dayHeaderFormat={{
101+
weekday: 'short',
102+
}}
103+
headerToolbar={{
104+
left: 'prevButton',
105+
center: 'title',
106+
right: 'nextButton',
107+
}}
108+
customButtons={{
109+
prevButton: {
110+
click: handlePrev,
111+
},
112+
nextButton: {
113+
click: handleNext,
114+
},
115+
}}
116+
/>
117+
</div>
118+
);
84119
}
85120

86121
interface EventInfo {
87-
timeText: string;
88-
event: {
89-
title: string;
90-
};
122+
timeText: string;
123+
event: {
124+
title: string;
125+
};
91126
}
92127

93128
function renderEventContent(eventInfo: EventInfo) {
94-
return (
95-
<>
96-
<b>{eventInfo.timeText}</b>
97-
<i>{eventInfo.event.title}</i>
98-
</>
99-
);
129+
return (
130+
<>
131+
<div className="bg-secondary border-0 p-0.5 w-full text-white">
132+
<b className='border-0'>{eventInfo.timeText}</b>
133+
<i> {eventInfo.event.title}</i>
134+
</div>
135+
</>
136+
);
100137
}

src/img/icon_arrow_left.png

563 Bytes
Loading

src/img/icon_arrow_right.png

553 Bytes
Loading

src/styles/index.css

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,39 @@
11
@tailwind base;
22
@tailwind components;
33
@tailwind utilities;
4+
5+
@layer base {
6+
@font-face {
7+
font-family: 'Pretendard-Regular';
8+
src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Regular.woff') format('woff');
9+
font-weight: 400;
10+
font-style: normal;
11+
}
12+
}
13+
14+
/* 캘린더 - 화살표 아이콘 */
15+
.fc .fc-button-primary:hover {
16+
background-color: var(--fc-event-text-color);
17+
}
18+
.fc .fc-prevButton-button {
19+
background-color: transparent;
20+
border: 0;
21+
background-image: url('../img/icon_arrow_left.png');
22+
width: 20px;
23+
height: 20px;
24+
display: block;
25+
background-size: contain;
26+
}
27+
.fc .fc-nextButton-button {
28+
background-color: transparent;
29+
border: 0;
30+
background-image: url('../img/icon_arrow_right.png');
31+
width: 20px;
32+
height: 20px;
33+
display: block;
34+
background-size: contain;
35+
}
36+
/* 캘린더 - 일정컬러 */
37+
.fc .fc-h-event {
38+
border: var(--fc-event-text-color);
39+
}

tailwind.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export default {
66
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
77
theme: {
88
extend: {},
9+
fontFamily: {
10+
'sans': ['Pretendard-Regular', 'sans-serif'],
11+
'serif': ['Pretendard-Regular', 'sans-serif'],
12+
'mono': ['Pretendard-Regular', 'sans-serif']
13+
}
914
},
1015

1116
daisyui: {

0 commit comments

Comments
 (0)