2025-10-14 10:48:17 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { CalendarConfig } from "../types";
|
|
|
|
|
import { CalendarDay, getWeekDayNames } from "./calendarUtils";
|
|
|
|
|
|
|
|
|
|
interface MonthViewProps {
|
|
|
|
|
days: CalendarDay[];
|
|
|
|
|
config: CalendarConfig;
|
|
|
|
|
isCompact?: boolean; // 작은 크기 (2x2, 3x3)
|
2025-10-17 09:49:02 +09:00
|
|
|
selectedDate?: Date | null; // 선택된 날짜
|
|
|
|
|
onDateClick?: (date: Date) => void; // 날짜 클릭 핸들러
|
2025-10-14 10:48:17 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 월간 달력 뷰 컴포넌트
|
|
|
|
|
*/
|
2025-10-17 09:49:02 +09:00
|
|
|
export function MonthView({ days, config, isCompact = false, selectedDate, onDateClick }: MonthViewProps) {
|
2025-10-14 10:48:17 +09:00
|
|
|
const weekDayNames = getWeekDayNames(config.startWeekOn);
|
|
|
|
|
|
|
|
|
|
// 테마별 스타일
|
|
|
|
|
const getThemeStyles = () => {
|
|
|
|
|
if (config.theme === "custom" && config.customColor) {
|
|
|
|
|
return {
|
|
|
|
|
todayBg: config.customColor,
|
|
|
|
|
holidayText: config.customColor,
|
|
|
|
|
weekendText: "#dc2626",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config.theme === "dark") {
|
|
|
|
|
return {
|
|
|
|
|
todayBg: "#3b82f6",
|
|
|
|
|
holidayText: "#f87171",
|
|
|
|
|
weekendText: "#f87171",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// light 테마
|
|
|
|
|
return {
|
|
|
|
|
todayBg: "#3b82f6",
|
|
|
|
|
holidayText: "#dc2626",
|
|
|
|
|
weekendText: "#dc2626",
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const themeStyles = getThemeStyles();
|
|
|
|
|
|
2025-10-17 09:49:02 +09:00
|
|
|
// 날짜가 선택된 날짜인지 확인
|
|
|
|
|
const isSelected = (day: CalendarDay) => {
|
|
|
|
|
if (!selectedDate || !day.isCurrentMonth) return false;
|
|
|
|
|
return (
|
|
|
|
|
selectedDate.getFullYear() === day.date.getFullYear() &&
|
|
|
|
|
selectedDate.getMonth() === day.date.getMonth() &&
|
|
|
|
|
selectedDate.getDate() === day.date.getDate()
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 날짜 클릭 핸들러
|
|
|
|
|
const handleDayClick = (day: CalendarDay) => {
|
|
|
|
|
if (!day.isCurrentMonth || !onDateClick) return;
|
|
|
|
|
onDateClick(day.date);
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-14 10:48:17 +09:00
|
|
|
// 날짜 셀 스타일 클래스
|
|
|
|
|
const getDayCellClass = (day: CalendarDay) => {
|
|
|
|
|
const baseClass = "flex aspect-square items-center justify-center rounded-lg transition-colors";
|
|
|
|
|
const sizeClass = isCompact ? "text-xs" : "text-sm";
|
2025-10-17 09:49:02 +09:00
|
|
|
const cursorClass = day.isCurrentMonth ? "cursor-pointer" : "cursor-default";
|
2025-10-14 10:48:17 +09:00
|
|
|
|
|
|
|
|
let colorClass = "text-gray-700";
|
|
|
|
|
|
|
|
|
|
// 현재 월이 아닌 날짜
|
|
|
|
|
if (!day.isCurrentMonth) {
|
|
|
|
|
colorClass = "text-gray-300";
|
|
|
|
|
}
|
2025-10-17 09:49:02 +09:00
|
|
|
// 선택된 날짜
|
|
|
|
|
else if (isSelected(day)) {
|
|
|
|
|
colorClass = "text-white font-bold";
|
|
|
|
|
}
|
2025-10-14 10:48:17 +09:00
|
|
|
// 오늘
|
|
|
|
|
else if (config.highlightToday && day.isToday) {
|
|
|
|
|
colorClass = "text-white font-bold";
|
|
|
|
|
}
|
|
|
|
|
// 공휴일
|
|
|
|
|
else if (config.showHolidays && day.isHoliday) {
|
|
|
|
|
colorClass = "font-semibold";
|
|
|
|
|
}
|
|
|
|
|
// 주말
|
|
|
|
|
else if (config.highlightWeekends && day.isWeekend) {
|
|
|
|
|
colorClass = "text-red-600";
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-17 09:49:02 +09:00
|
|
|
let bgClass = "";
|
|
|
|
|
if (isSelected(day)) {
|
|
|
|
|
bgClass = ""; // 선택된 날짜는 배경색이 style로 적용됨
|
|
|
|
|
} else if (config.highlightToday && day.isToday) {
|
|
|
|
|
bgClass = "";
|
|
|
|
|
} else {
|
|
|
|
|
bgClass = "hover:bg-gray-100";
|
|
|
|
|
}
|
2025-10-14 10:48:17 +09:00
|
|
|
|
2025-10-17 09:49:02 +09:00
|
|
|
return `${baseClass} ${sizeClass} ${colorClass} ${bgClass} ${cursorClass}`;
|
2025-10-14 10:48:17 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex h-full flex-col p-2">
|
|
|
|
|
{/* 요일 헤더 */}
|
|
|
|
|
{!isCompact && (
|
|
|
|
|
<div className="mb-2 grid grid-cols-7 gap-1">
|
|
|
|
|
{weekDayNames.map((name, index) => {
|
|
|
|
|
const isWeekend = config.startWeekOn === "sunday" ? index === 0 || index === 6 : index === 5 || index === 6;
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={name}
|
|
|
|
|
className={`text-center text-xs font-semibold ${isWeekend && config.highlightWeekends ? "text-red-600" : "text-gray-600"}`}
|
|
|
|
|
>
|
|
|
|
|
{name}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* 날짜 그리드 */}
|
|
|
|
|
<div className="grid flex-1 grid-cols-7 gap-1">
|
|
|
|
|
{days.map((day, index) => (
|
|
|
|
|
<div
|
|
|
|
|
key={index}
|
|
|
|
|
className={getDayCellClass(day)}
|
2025-10-17 09:49:02 +09:00
|
|
|
onClick={() => handleDayClick(day)}
|
2025-10-14 10:48:17 +09:00
|
|
|
style={{
|
2025-10-17 09:49:02 +09:00
|
|
|
backgroundColor: isSelected(day)
|
|
|
|
|
? "#10b981" // 선택된 날짜는 초록색
|
|
|
|
|
: config.highlightToday && day.isToday
|
|
|
|
|
? themeStyles.todayBg
|
|
|
|
|
: undefined,
|
2025-10-14 10:48:17 +09:00
|
|
|
color:
|
|
|
|
|
config.showHolidays && day.isHoliday && day.isCurrentMonth
|
|
|
|
|
? themeStyles.holidayText
|
|
|
|
|
: undefined,
|
|
|
|
|
}}
|
|
|
|
|
title={day.isHoliday ? day.holidayName : undefined}
|
|
|
|
|
>
|
|
|
|
|
{day.day}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|