tharikida-ui
Version:
A modern, lightweight React UI component library with built-in theming, accessibility, and full TypeScript support. Create beautiful, responsive, and customizable web apps faster with ready-to-use components for any project.
212 lines (211 loc) • 10.8 kB
JavaScript
"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState } from "react";
import Dropdown from "./DropDown";
import { useTheme } from "../../theme/ThemeProvider";
const Calendar = ({ size, handleDateClick, initialDate, cornerRadius, primaryColor, textColor, backgroundColor, fontSize, fontFamily, borderColor, borderWidth, borderStyle, fontWeight, transitionDuration, hoverColor, }) => {
const theme = useTheme();
const [showCalendar, setShowCalendar] = useState(false);
const [selectedDate, setSelectedDate] = useState(initialDate || null);
// Use prop > theme > fallback
const t = {
primaryColor: primaryColor || theme.primaryColor,
textColor: textColor || theme.textColor,
backgroundColor: backgroundColor || theme.backgroundColor,
fontSize: fontSize || theme.fontSize,
fontFamily: fontFamily || theme.fontFamily,
cornerRadius: typeof cornerRadius === "number"
? cornerRadius
: theme.cornerRadius ?? theme.spacingfactor * 2,
borderColor: borderColor || theme.borderColor,
borderWidth: borderWidth ?? theme.borderWidth,
borderStyle: borderStyle ?? theme.borderStyle,
fontWeight: fontWeight ?? theme.fontWeight,
transitionDuration: transitionDuration ?? theme.transitionDuration,
hoverColor: hoverColor || theme.hoverColor,
// fallback for removed props
padding: theme.padding,
margin: theme.margin,
shadowOffsetX: theme.shadowOffsetX,
shadowOffsetY: theme.shadowOffsetY,
shadowBlur: theme.shadowBlur,
shadowSpread: theme.shadowSpread,
shadowColor: theme.shadowColor,
shadowInset: theme.shadowInset,
};
const generateCalendar = (month, year) => {
const daysInMonth = new Date(year, month + 1, 0).getDate();
const firstDay = new Date(year, month, 1).getDay();
const dates = Array(firstDay).fill(null);
for (let i = 1; i <= daysInMonth; i++)
dates.push(i);
return dates;
};
const today = new Date();
const [currentMonth, setCurrentMonth] = useState(today.getMonth());
const [currentYear, setCurrentYear] = useState(today.getFullYear());
const dates = generateCalendar(currentMonth, currentYear);
const handleDateClickInternal = (day) => {
if (day !== null) {
const date = new Date(currentYear, currentMonth, day);
setSelectedDate(date);
setShowCalendar(false);
handleDateClick && handleDateClick(day);
}
};
const changeMonth = (offset) => {
let newMonth = currentMonth + offset;
let newYear = currentYear;
if (newMonth > 11) {
newMonth = 0;
newYear++;
}
else if (newMonth < 0) {
newMonth = 11;
newYear--;
}
setCurrentMonth(newMonth);
setCurrentYear(newYear);
};
const handleMonthChange = (value) => {
setCurrentMonth(months.indexOf(value));
};
const handleYearChange = (event) => {
setCurrentYear(parseInt(event.target.value, 10));
};
const years = Array.from({ length: 101 }, (_, i) => 1950 + i);
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const formatDate = (date) => {
if (!date)
return "Pick a Date";
const options = {
year: "numeric",
month: "short",
day: "numeric",
};
return date.toLocaleDateString(undefined, options);
};
return (_jsx("div", { style: { width: size || "300px" }, children: _jsxs("div", { style: {
boxSizing: "border-box",
display: "flex",
flexDirection: "column",
alignItems: "center",
position: "relative",
width: "100%",
padding: t.padding,
fontFamily: t.fontFamily,
fontSize: t.fontSize,
fontWeight: t.fontWeight,
lineHeight: theme.lineHeight,
letterSpacing: theme.letterSpacing,
margin: t.margin,
}, children: [_jsx("div", { style: {
boxSizing: "border-box",
padding: t.padding,
border: `${t.borderWidth} ${t.borderStyle} ${t.borderColor}`,
borderRadius: t.cornerRadius,
textAlign: "center",
cursor: "pointer",
backgroundColor: t.backgroundColor,
width: "100%",
boxShadow: `${t.shadowOffsetX} ${t.shadowOffsetY} ${t.shadowBlur} ${t.shadowSpread} ${t.shadowColor}`,
fontSize: t.fontSize,
color: t.textColor,
transition: t.transitionDuration,
}, onClick: () => setShowCalendar(!showCalendar), children: formatDate(selectedDate) }), showCalendar && (_jsxs("div", { style: {
position: "absolute",
top: "60px",
backgroundColor: t.backgroundColor,
zIndex: 10,
width: "100%",
border: `${t.borderWidth} ${t.borderStyle} ${t.borderColor}`,
boxShadow: `${t.shadowInset ? "inset " : ""}${t.shadowOffsetX} ${t.shadowOffsetY} ${t.shadowBlur} ${t.shadowSpread} ${t.shadowColor}`,
borderRadius: t.cornerRadius,
gap: theme.spacingfactor,
padding: t.padding,
transition: t.transitionDuration,
}, children: [_jsxs("div", { style: {
display: "flex",
justifyContent: "space-between",
gap: theme.spacingfactor,
}, children: [_jsx(Dropdown, { options: months, defaultOption: months[currentMonth], onChange: handleMonthChange }), _jsx(Dropdown, { options: years.map(String), defaultOption: String(currentYear), onChange: (value) => handleYearChange({
target: { value },
}) })] }), _jsx("div", { style: {
boxSizing: "border-box",
textAlign: "center",
padding: `${theme.spacingfactor * 2}px ${theme.spacingfactor * 1.5}px`,
display: "grid",
gridTemplateColumns: "repeat(7, 1fr)",
width: "100%",
color: t.textColor,
fontFamily: t.fontFamily,
fontWeight: t.fontWeight,
fontSize: t.fontSize,
}, children: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((day) => (_jsx("div", { style: { fontFamily: t.fontFamily }, children: day }, day))) }), _jsx("div", { style: {
boxSizing: "border-box",
padding: t.padding,
gap: theme.spacingfactor,
display: "grid",
width: "100%",
gridTemplateColumns: "repeat(7, 1fr)",
textAlign: "center",
}, children: dates.map((day, index) => {
const isSelected = day !== null &&
selectedDate &&
selectedDate.toISOString().split("T")[0] ===
new Date(currentYear, currentMonth, day)
.toISOString()
.split("T")[0];
const isToday = day !== null &&
today.getDate() === day &&
today.getMonth() === currentMonth &&
today.getFullYear() === currentYear;
return (_jsx("div", { style: {
boxSizing: "border-box",
width: "100%",
aspectRatio: "1/1",
display: "flex",
justifyContent: "center",
alignItems: "center",
borderRadius: t.cornerRadius,
transition: t.transitionDuration,
color: t.textColor,
fontFamily: t.fontFamily,
fontWeight: t.fontWeight,
fontSize: t.fontSize,
...(day && { cursor: "pointer" }),
...(isSelected && {
backgroundColor: t.primaryColor,
color: "#fff",
borderRadius: "50%",
border: `2px solid ${t.primaryColor}`,
}),
...(!isSelected &&
isToday && {
border: `2px solid ${t.primaryColor}`,
}),
}, onClick: () => handleDateClickInternal(day), onMouseOver: (e) => {
if (day && !isSelected) {
e.currentTarget.style.backgroundColor = t.hoverColor;
}
}, onMouseOut: (e) => {
if (day && !isSelected) {
e.currentTarget.style.backgroundColor = "";
}
}, children: day || "" }, index));
}) })] }))] }) }));
};
export default Calendar;