import { useState, useEffect } from "react"; import { CalendarDays, ChevronLeft, ChevronRight } from "lucide-react"; import { useTranslation } from "react-i18next"; import { registerWidget } from "./WidgetRegistry"; import type { CalendarConfig, WidgetComponentProps, } from "@/types/homepage-types"; import { GRID_SIZE } from "@/types/homepage-types"; import { WidgetTitle } from "./WidgetTitle"; import { runVisibleInterval } from "../use-visible-interval"; function getMonthData(year: number, month: number, startOnMonday: boolean) { const firstDay = new Date(year, month, 1).getDay(); const daysInMonth = new Date(year, month + 1, 0).getDate(); const offset = startOnMonday ? (firstDay === 0 ? 6 : firstDay - 1) : firstDay; return { daysInMonth, offset }; } function CalendarWidget({ widget, config, }: WidgetComponentProps) { const { t } = useTranslation(); const { startOnMonday } = config; const [now, setNow] = useState(new Date()); const [viewYear, setViewYear] = useState(now.getFullYear()); const [viewMonth, setViewMonth] = useState(now.getMonth()); useEffect(() => { return runVisibleInterval(() => setNow(new Date()), 60_000); }, []); const { daysInMonth, offset } = getMonthData( viewYear, viewMonth, startOnMonday, ); const today = now; const isCurrentMonth = viewYear === today.getFullYear() && viewMonth === today.getMonth(); const dayNames = startOnMonday ? [ t("homepage.calMon"), t("homepage.calTue"), t("homepage.calWed"), t("homepage.calThu"), t("homepage.calFri"), t("homepage.calSat"), t("homepage.calSun"), ] : [ t("homepage.calSun"), t("homepage.calMon"), t("homepage.calTue"), t("homepage.calWed"), t("homepage.calThu"), t("homepage.calFri"), t("homepage.calSat"), ]; const monthLabel = new Intl.DateTimeFormat("en-US", { month: "long", year: "numeric", }).format(new Date(viewYear, viewMonth, 1)); const cells: (number | null)[] = [ ...Array(offset).fill(null), ...Array.from({ length: daysInMonth }, (_, i) => i + 1), ]; const prevMonth = () => { if (viewMonth === 0) { setViewMonth(11); setViewYear((y) => y - 1); } else setViewMonth((m) => m - 1); }; const nextMonth = () => { if (viewMonth === 11) { setViewMonth(0); setViewYear((y) => y + 1); } else setViewMonth((m) => m + 1); }; const accent = getComputedStyle(document.documentElement) .getPropertyValue("--accent-brand") .trim() || "#f59145"; return (
} />
{monthLabel}
{dayNames.map((d) => (
{d}
))}
{cells.map((day, i) => { if (day === null) return
; const isToday = isCurrentMonth && day === today.getDate(); return (
{day}
); })}
); } registerWidget({ id: "calendar", name: "Calendar", description: "A month calendar with today highlighted", category: "info", icon: , defaultConfig: { startOnMonday: true }, defaultSize: { w: GRID_SIZE * 10, h: GRID_SIZE * 9 }, minSize: { w: GRID_SIZE * 7, h: GRID_SIZE * 7 }, component: CalendarWidget, }); export { CalendarWidget };