/** * DatePicker 컴포넌트 * 연/월/일 선택이 가능한 드롭다운 형태의 날짜 선택기 */ import { useState, useEffect, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Calendar, ChevronLeft, ChevronRight, ChevronDown } from 'lucide-react'; function DatePicker({ value, onChange, placeholder = '날짜 선택', showDayOfWeek = false }) { const [isOpen, setIsOpen] = useState(false); const [viewMode, setViewMode] = useState('days'); const [viewDate, setViewDate] = useState(() => { if (value) return new Date(value); return new Date(); }); const ref = useRef(null); useEffect(() => { const handleClickOutside = (e) => { if (ref.current && !ref.current.contains(e.target)) { setIsOpen(false); setViewMode('days'); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const year = viewDate.getFullYear(); const month = viewDate.getMonth(); const firstDay = new Date(year, month, 1).getDay(); const daysInMonth = new Date(year, month + 1, 0).getDate(); const days = []; for (let i = 0; i < firstDay; i++) { days.push(null); } for (let i = 1; i <= daysInMonth; i++) { days.push(i); } const MIN_YEAR = 2025; const startYear = Math.max(MIN_YEAR, Math.floor(year / 12) * 12 - 1); const years = Array.from({ length: 12 }, (_, i) => startYear + i); const canGoPrevYearRange = startYear > MIN_YEAR; const prevMonth = () => setViewDate(new Date(year, month - 1, 1)); const nextMonth = () => setViewDate(new Date(year, month + 1, 1)); const prevYearRange = () => canGoPrevYearRange && setViewDate(new Date(Math.max(MIN_YEAR, year - 12), month, 1)); const nextYearRange = () => setViewDate(new Date(year + 12, month, 1)); const selectDate = (day) => { const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; onChange(dateStr); setIsOpen(false); setViewMode('days'); }; const selectYear = (y) => { setViewDate(new Date(y, month, 1)); }; const selectMonth = (m) => { setViewDate(new Date(year, m, 1)); setViewMode('days'); }; // 날짜 표시 포맷 (요일 포함 옵션) const formatDisplayDate = (dateStr) => { if (!dateStr) return ''; const [y, m, d] = dateStr.split('-'); if (showDayOfWeek) { const dayNames = ['일', '월', '화', '수', '목', '금', '토']; const date = new Date(parseInt(y), parseInt(m) - 1, parseInt(d)); const dayOfWeek = dayNames[date.getDay()]; return `${y}년 ${parseInt(m)}월 ${parseInt(d)}일 (${dayOfWeek})`; } return `${y}년 ${parseInt(m)}월 ${parseInt(d)}일`; }; const isSelected = (day) => { if (!value || !day) return false; const [y, m, d] = value.split('-'); return parseInt(y) === year && parseInt(m) === month + 1 && parseInt(d) === day; }; const isToday = (day) => { if (!day) return false; const today = new Date(); return today.getFullYear() === year && today.getMonth() === month && today.getDate() === day; }; const isCurrentYear = (y) => new Date().getFullYear() === y; const isCurrentMonth = (m) => { const today = new Date(); return today.getFullYear() === year && today.getMonth() === m; }; const monthNames = [ '1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월', ]; return (
{isOpen && (
{viewMode === 'years' && (
년도
{years.map((y) => ( ))}
{monthNames.map((m, i) => ( ))}
)} {viewMode === 'months' && (
월 선택
{monthNames.map((m, i) => ( ))}
)} {viewMode === 'days' && (
{['일', '월', '화', '수', '목', '금', '토'].map((d, i) => (
{d}
))}
{days.map((day, i) => { const dayOfWeek = i % 7; return ( ); })}
)}
)}
); } export default DatePicker;