UNPKG

react-vite-themes

Version:

A test/experimental React theme system created for learning purposes. Features atomic design components, SCSS variables, and dark/light theme support. Not intended for production use.

119 lines (118 loc) 6.22 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import React, { useState, useRef, useEffect } from 'react'; import { Icon } from '../Icon'; import { Button } from '../Button'; import { cn } from '../../../utils'; import './Calendar.scss'; export const Calendar = ({ value, onChange, minDate, maxDate, className, size = 'md', variant = 'default', isOpen = false, onOpenChange, placeholder = 'Select date', disabled = false }) => { const [currentDate, setCurrentDate] = useState(value || new Date()); const [selectedDate, setSelectedDate] = useState(value || null); const [isCalendarOpen, setIsCalendarOpen] = useState(isOpen); const calendarRef = useRef(null); useEffect(() => { setCurrentDate(value || new Date()); setSelectedDate(value || null); }, [value]); useEffect(() => { setIsCalendarOpen(isOpen); }, [isOpen]); useEffect(() => { const handleClickOutside = (event) => { if (calendarRef.current && !calendarRef.current.contains(event.target)) { setIsCalendarOpen(false); onOpenChange?.(false); } }; if (isCalendarOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isCalendarOpen, onOpenChange]); const toggleCalendar = () => { if (!disabled) { const newState = !isCalendarOpen; setIsCalendarOpen(newState); onOpenChange?.(newState); } }; const formatDate = (date) => { return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); }; const getDaysInMonth = (date) => { const year = date.getFullYear(); const month = date.getMonth(); const firstDay = new Date(year, month, 1); const lastDay = new Date(year, month + 1, 0); const daysInMonth = lastDay.getDate(); const startingDayOfWeek = firstDay.getDay(); const days = []; // Add previous month's days for (let i = startingDayOfWeek - 1; i >= 0; i--) { const prevDate = new Date(year, month, -i); days.push(prevDate); } // Add current month's days for (let i = 1; i <= daysInMonth; i++) { days.push(new Date(year, month, i)); } // Add next month's days to fill the grid const remainingDays = 42 - days.length; // 6 rows * 7 days for (let i = 1; i <= remainingDays; i++) { days.push(new Date(year, month + 1, i)); } return days; }; const isToday = (date) => { const today = new Date(); return date.toDateString() === today.toDateString(); }; const isSelected = (date) => { return selectedDate ? date.toDateString() === selectedDate.toDateString() : false; }; const isCurrentMonth = (date) => { return date.getMonth() === currentDate.getMonth(); }; const isDisabled = (date) => { if (minDate && date < minDate) return true; if (maxDate && date > maxDate) return true; return false; }; const handleDateClick = (date) => { if (!isDisabled(date)) { setSelectedDate(date); onChange?.(date); setIsCalendarOpen(false); onOpenChange?.(false); } }; const goToPreviousMonth = () => { setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)); }; const goToNextMonth = () => { setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)); }; const goToToday = () => { const today = new Date(); setCurrentDate(today); if (!isDisabled(today)) { setSelectedDate(today); onChange?.(today); } }; const days = getDaysInMonth(currentDate); const monthYear = currentDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long' }); // Remove unused variable const calendarClasses = cn('calendar', `calendar--${size}`, `calendar--${variant}`, isCalendarOpen && 'calendar--open', disabled && 'calendar--disabled', className); return (_jsxs("div", { className: calendarClasses, ref: calendarRef, children: [_jsxs("div", { className: "calendar-trigger", onClick: toggleCalendar, children: [_jsxs("div", { className: "calendar-input", children: [_jsx(Icon, { name: "calendar", size: "sm" }), _jsx("span", { className: "calendar-value", children: selectedDate ? formatDate(selectedDate) : placeholder })] }), _jsx(Icon, { name: isCalendarOpen ? 'chevron-up' : 'chevron-down', size: "sm" })] }), isCalendarOpen && (_jsxs("div", { className: "calendar-dropdown", children: [_jsxs("div", { className: "calendar-header", children: [_jsx(Button, { variant: "secondary", size: "sm", onClick: goToPreviousMonth, className: "calendar-nav-button", children: _jsx(Icon, { name: "chevron-left", size: "sm" }) }), _jsx("div", { className: "calendar-month-year", children: monthYear }), _jsx(Button, { variant: "secondary", size: "sm", onClick: goToNextMonth, className: "calendar-nav-button", children: _jsx(Icon, { name: "chevron-right", size: "sm" }) })] }), _jsx("div", { className: "calendar-weekdays", children: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => (_jsx("div", { className: "calendar-weekday", children: day }, day))) }), _jsx("div", { className: "calendar-grid", children: days.map((date, index) => (_jsx("button", { className: cn('calendar-day', isToday(date) && 'calendar-day--today', isSelected(date) && 'calendar-day--selected', !isCurrentMonth(date) && 'calendar-day--other-month', isDisabled(date) && 'calendar-day--disabled'), onClick: () => handleDateClick(date), disabled: isDisabled(date), children: date.getDate() }, index))) }), _jsx("div", { className: "calendar-footer", children: _jsx(Button, { variant: "primary", size: "sm", onClick: goToToday, className: "calendar-today-button", children: "Today" }) })] }))] })); };