Skip to content

Commit 3189e15

Browse files
committed
chore: renderCalendarを分離
1 parent 08ca804 commit 3189e15

File tree

2 files changed

+180
-141
lines changed

2 files changed

+180
-141
lines changed

task_yell/src/app/home/page.tsx

+13-141
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,7 @@ import {
1313
} from "@/lib/want-todo";
1414
import {
1515
addMonths,
16-
eachDayOfInterval,
17-
endOfMonth,
18-
endOfWeek,
1916
format,
20-
isSameDay,
21-
isSameMonth,
22-
startOfMonth,
23-
startOfWeek,
2417
subMonths,
2518
} from "date-fns";
2619
import { ja } from "date-fns/locale";
@@ -35,11 +28,11 @@ import { useRouter } from "next/navigation";
3528
import { useEffect, useMemo, useRef, useState } from "react";
3629
import { generateStickyNoteServer } from "./actions";
3730
import { Event, Todo, StickyNote } from "@/components/types";
38-
import { priorityColors } from "@/components/priority-colors";
3931
import { Navigation } from "@/components/navigation";
4032
import { EditWantodoDialog } from "@/components/edit-wantodo-dialog";
4133
import { CreateEventDialog } from "@/components/create-event-dialog";
4234
import { StickyNoteItem } from "@/components/sticky-note-item";
35+
import { CalendarRenderer } from "@/components/calendar-renderer";
4336

4437
export default function Home() {
4538
const [todos] = useState<Todo[]>([]);
@@ -199,138 +192,6 @@ export default function Home() {
199192
}
200193
};
201194

202-
const getDaysInMonth = (date: Date) => {
203-
const start = startOfWeek(startOfMonth(date), { weekStartsOn: 0 });
204-
const end = endOfWeek(endOfMonth(date), { weekStartsOn: 0 });
205-
return eachDayOfInterval({ start, end });
206-
};
207-
208-
const getTodoCountForDay = (day: Date) => {
209-
return todos.filter((todo) => isSameDay(todo.date, day)).length;
210-
};
211-
212-
const getEventCountForDay = (day: Date) => {
213-
return events.filter((event) => event.start && isSameDay(event.start, day))
214-
.length;
215-
};
216-
217-
const getTaskIndicatorStyle = (todoCount: number, eventCount: number) => {
218-
const count = todoCount + eventCount;
219-
if (count === 0) return "";
220-
const baseColor = isDarkMode ? "bg-red-" : "bg-red-";
221-
const intensity = Math.min(count * 100, 900);
222-
const colorClass = `${baseColor}${intensity}`;
223-
return `${colorClass} ${count >= 3 ? "animate-pulse" : ""}`;
224-
};
225-
226-
const renderCalendar = () => {
227-
const days = getDaysInMonth(currentMonth);
228-
const weeks = Math.ceil(days.length / 7);
229-
230-
return (
231-
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4">
232-
<div className="grid grid-cols-7 gap-1">
233-
{["日", "月", "火", "水", "木", "金", "土"].map((day) => (
234-
<div
235-
key={day}
236-
className="text-center font-semibold text-gray-600 dark:text-gray-300 p-2"
237-
>
238-
{day}
239-
</div>
240-
))}
241-
</div>
242-
{Array.from({ length: weeks }).map((_, weekIndex) => {
243-
const weekDays = days.slice(weekIndex * 7, (weekIndex + 1) * 7);
244-
const maxEventsInWeek = Math.max(
245-
...weekDays.map(
246-
(day) => getTodoCountForDay(day) + getEventCountForDay(day),
247-
),
248-
);
249-
const weekHeight =
250-
maxEventsInWeek > 2 ? Math.min(maxEventsInWeek * 20, 100) : "auto";
251-
252-
return (
253-
<div
254-
key={weekIndex}
255-
className="grid grid-cols-7 gap-1"
256-
style={{ minHeight: "100px", height: weekHeight }}
257-
>
258-
{weekDays.map((day) => {
259-
const todoCount = getTodoCountForDay(day);
260-
const eventCount = getEventCountForDay(day);
261-
const isSelected = isSameDay(day, selectedDate);
262-
const isCurrentMonth = isSameMonth(day, currentMonth);
263-
const dayItems = [
264-
...todos.filter((todo) => isSameDay(todo.date, day)),
265-
...events.filter(
266-
(event) => event.start && isSameDay(event.start, day),
267-
),
268-
];
269-
270-
return (
271-
<motion.div
272-
key={day.toISOString()}
273-
className={`p-1 border rounded-md cursor-pointer transition-all duration-300 overflow-hidden ${isSelected ? "border-blue-300 dark:border-blue-600" : ""} ${!isCurrentMonth
274-
? "text-gray-400 dark:text-gray-600 bg-gray-100 dark:bg-gray-700"
275-
: ""
276-
} ${getTaskIndicatorStyle(todoCount, eventCount)} hover:bg-gray-100 dark:hover:bg-gray-700`}
277-
onClick={() => handleDateSelect(day)}
278-
whileHover={{ scale: 1.05 }}
279-
whileTap={{ scale: 0.95 }}
280-
onDragOver={(e) => {
281-
e.preventDefault();
282-
e.currentTarget.classList.add(
283-
"bg-blue-100",
284-
"dark:bg-blue-800",
285-
);
286-
}}
287-
onDragLeave={(e) => {
288-
e.currentTarget.classList.remove(
289-
"bg-blue-100",
290-
"dark:bg-blue-800",
291-
);
292-
}}
293-
onDrop={(e) => {
294-
e.preventDefault();
295-
e.currentTarget.classList.remove(
296-
"bg-blue-100",
297-
"dark:bg-blue-800",
298-
);
299-
if (draggedStickyNote) {
300-
handleDateSelect(day);
301-
setIsEventModalOpen(true);
302-
deleteStickyNote(draggedStickyNote.id);
303-
}
304-
}}
305-
>
306-
<div className="text-right text-sm">{format(day, "d")}</div>
307-
{(todoCount > 0 || eventCount > 0) && (
308-
<div className="mt-1 space-y-1">
309-
{dayItems.slice(0, 2).map((item, index) => (
310-
<div
311-
key={index}
312-
className={`text-xs p-1 rounded ${"text" in item ? priorityColors[item.priority] : priorityColors[item.priority]}`}
313-
>
314-
{"text" in item ? item.text : item.title}
315-
</div>
316-
))}
317-
{dayItems.length > 2 && (
318-
<div className="text-xs text-center font-bold">
319-
+{dayItems.length - 2} more
320-
</div>
321-
)}
322-
</div>
323-
)}
324-
</motion.div>
325-
);
326-
})}
327-
</div>
328-
);
329-
})}
330-
</div>
331-
);
332-
};
333-
334195
const handleDateSelect = (date: Date) => {
335196
setSelectedDate(date);
336197
setIsEventModalOpen(true);
@@ -377,7 +238,18 @@ export default function Home() {
377238
<ChevronRight className="h-4 w-4" />
378239
</Button>
379240
</div>
380-
{renderCalendar()}
241+
<CalendarRenderer
242+
todos={todos}
243+
events={events}
244+
stickyNotes={stickyNotes}
245+
currentMonth={currentMonth}
246+
selectedDate={selectedDate}
247+
handleDateSelect={handleDateSelect}
248+
isDarkMode={isDarkMode}
249+
draggedStickyNote={draggedStickyNote}
250+
deleteStickyNote={deleteStickyNote}
251+
setIsEventModalOpen={setIsEventModalOpen}
252+
/>
381253
</div>
382254

383255
<div className="w-full lg:w-1/2 pl-2 bg-white dark:bg-gray-800 overflow-auto lg:block hidden">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
"use client";
2+
3+
import {
4+
eachDayOfInterval,
5+
endOfMonth,
6+
endOfWeek,
7+
format,
8+
isSameDay,
9+
isSameMonth,
10+
startOfMonth,
11+
startOfWeek,
12+
} from "date-fns";
13+
import { motion } from "framer-motion";
14+
import { Event, Todo, StickyNote } from "@/components/types";
15+
import { priorityColors } from "@/components/priority-colors";
16+
17+
type Props = {
18+
todos: Todo[];
19+
events: Event[];
20+
stickyNotes: StickyNote[];
21+
currentMonth: Date;
22+
selectedDate: Date;
23+
handleDateSelect: (date: Date) => void;
24+
isDarkMode: boolean;
25+
draggedStickyNote: StickyNote | null;
26+
deleteStickyNote: (id: string) => void;
27+
setIsEventModalOpen: (isOpen: boolean) => void;
28+
}
29+
30+
function getDaysInMonth(date: Date) {
31+
const start = startOfWeek(startOfMonth(date), { weekStartsOn: 0 });
32+
const end = endOfWeek(endOfMonth(date), { weekStartsOn: 0 });
33+
return eachDayOfInterval({ start, end });
34+
}
35+
36+
function getTodoCountForDay(todos: Todo[], day: Date) {
37+
return todos.filter((todo) => isSameDay(todo.date, day)).length;
38+
}
39+
40+
function getEventCountForDay(events: Event[], day: Date) {
41+
return events.filter((event) => event.start && isSameDay(event.start, day))
42+
.length;
43+
}
44+
45+
function getTaskIndicatorStyle(isDarkMode: boolean, todoCount: number, eventCount: number) {
46+
const count = todoCount + eventCount;
47+
if (count === 0) return "";
48+
const baseColor = isDarkMode ? "bg-red-" : "bg-red-";
49+
const intensity = Math.min(count * 100, 900);
50+
const colorClass = `${baseColor}${intensity}`;
51+
return `${colorClass} ${count >= 3 ? "animate-pulse" : ""}`;
52+
}
53+
54+
export function CalendarRenderer({
55+
todos, events,
56+
currentMonth, selectedDate, handleDateSelect,
57+
isDarkMode,
58+
draggedStickyNote, deleteStickyNote,
59+
setIsEventModalOpen
60+
}: Props
61+
) {
62+
const days = getDaysInMonth(currentMonth);
63+
const weeks = Math.ceil(days.length / 7);
64+
65+
return (
66+
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4">
67+
<div className="grid grid-cols-7 gap-1">
68+
{["日", "月", "火", "水", "木", "金", "土"].map((day) => (
69+
<div
70+
key={day}
71+
className="text-center font-semibold text-gray-600 dark:text-gray-300 p-2"
72+
>
73+
{day}
74+
</div>
75+
))}
76+
</div>
77+
{Array.from({ length: weeks }).map((_, weekIndex) => {
78+
const weekDays = days.slice(weekIndex * 7, (weekIndex + 1) * 7);
79+
const maxEventsInWeek = Math.max(
80+
...weekDays.map(
81+
(day) => getTodoCountForDay(todos, day) + getEventCountForDay(events, day),
82+
),
83+
);
84+
const weekHeight =
85+
maxEventsInWeek > 2 ? Math.min(maxEventsInWeek * 20, 100) : "auto";
86+
87+
return (
88+
<div
89+
key={weekIndex}
90+
className="grid grid-cols-7 gap-1"
91+
style={{ minHeight: "100px", height: weekHeight }}
92+
>
93+
{weekDays.map((day) => {
94+
const todoCount = getTodoCountForDay(todos, day);
95+
const eventCount = getEventCountForDay(events, day);
96+
const isSelected = isSameDay(day, selectedDate);
97+
const isCurrentMonth = isSameMonth(day, currentMonth);
98+
const dayItems = [
99+
...todos.filter((todo) => isSameDay(todo.date, day)),
100+
...events.filter(
101+
(event) => event.start && isSameDay(event.start, day),
102+
),
103+
];
104+
105+
return (
106+
<motion.div
107+
key={day.toISOString()}
108+
className={`p-1 border rounded-md cursor-pointer transition-all duration-300 overflow-hidden ${isSelected ? "border-blue-300 dark:border-blue-600" : ""} ${!isCurrentMonth
109+
? "text-gray-400 dark:text-gray-600 bg-gray-100 dark:bg-gray-700"
110+
: ""
111+
} ${getTaskIndicatorStyle(isDarkMode, todoCount, eventCount)} hover:bg-gray-100 dark:hover:bg-gray-700`}
112+
onClick={() => handleDateSelect(day)}
113+
whileHover={{ scale: 1.05 }}
114+
whileTap={{ scale: 0.95 }}
115+
onDragOver={(e) => {
116+
e.preventDefault();
117+
e.currentTarget.classList.add(
118+
"bg-blue-100",
119+
"dark:bg-blue-800",
120+
);
121+
}}
122+
onDragLeave={(e) => {
123+
e.currentTarget.classList.remove(
124+
"bg-blue-100",
125+
"dark:bg-blue-800",
126+
);
127+
}}
128+
onDrop={(e) => {
129+
e.preventDefault();
130+
e.currentTarget.classList.remove(
131+
"bg-blue-100",
132+
"dark:bg-blue-800",
133+
);
134+
if (draggedStickyNote) {
135+
handleDateSelect(day);
136+
setIsEventModalOpen(true);
137+
deleteStickyNote(draggedStickyNote.id);
138+
}
139+
}}
140+
>
141+
<div className="text-right text-sm">{format(day, "d")}</div>
142+
{(todoCount > 0 || eventCount > 0) && (
143+
<div className="mt-1 space-y-1">
144+
{dayItems.slice(0, 2).map((item, index) => (
145+
<div
146+
key={index}
147+
className={`text-xs p-1 rounded ${"text" in item ? priorityColors[item.priority] : priorityColors[item.priority]}`}
148+
>
149+
{"text" in item ? item.text : item.title}
150+
</div>
151+
))}
152+
{dayItems.length > 2 && (
153+
<div className="text-xs text-center font-bold">
154+
+{dayItems.length - 2} more
155+
</div>
156+
)}
157+
</div>
158+
)}
159+
</motion.div>
160+
);
161+
})}
162+
</div>
163+
);
164+
})}
165+
</div>
166+
);
167+
};

0 commit comments

Comments
 (0)