feat: add option to show week numbers in mini-calendar
This commit is contained in:
@@ -63,7 +63,7 @@ export default function CalendarPage() {
|
||||
setSelectedDate, setViewMode, toggleCalendarVisibility, updateCalendar,
|
||||
refreshAllSubscriptions,
|
||||
} = useCalendarStore();
|
||||
const { firstDayOfWeek, timeFormat } = useSettingsStore();
|
||||
const { firstDayOfWeek, timeFormat, showWeekNumbers } = useSettingsStore();
|
||||
const { identities } = useIdentityStore();
|
||||
const normalizedViewMode = isCalendarViewMode(viewMode) ? viewMode : "month";
|
||||
|
||||
@@ -751,6 +751,7 @@ export default function CalendarPage() {
|
||||
onChangeMonth={handleMiniMonthChange}
|
||||
events={events}
|
||||
firstDayOfWeek={firstDayOfWeek}
|
||||
showWeekNumbers={showWeekNumbers}
|
||||
/>
|
||||
<CalendarSidebarPanel
|
||||
calendars={calendars}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import { useState, useMemo, Fragment } from "react";
|
||||
import { useTranslations, useFormatter } from "next-intl";
|
||||
import { ChevronLeft, ChevronRight, ChevronDown } from "lucide-react";
|
||||
import {
|
||||
startOfMonth, endOfMonth, startOfWeek, endOfWeek,
|
||||
addMonths, subMonths, addYears, subYears, setMonth, setYear,
|
||||
eachDayOfInterval, getMonth, getYear,
|
||||
eachDayOfInterval, getMonth, getYear, getISOWeek, getWeek,
|
||||
isSameDay, isSameMonth, isToday, format,
|
||||
} from "date-fns";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -26,6 +26,7 @@ interface MiniCalendarProps {
|
||||
onChangeMonth: (date: Date) => void;
|
||||
events?: CalendarEvent[];
|
||||
firstDayOfWeek?: number;
|
||||
showWeekNumbers?: boolean;
|
||||
}
|
||||
|
||||
export function MiniCalendar({
|
||||
@@ -35,6 +36,7 @@ export function MiniCalendar({
|
||||
onChangeMonth,
|
||||
events = [],
|
||||
firstDayOfWeek = 1,
|
||||
showWeekNumbers = false,
|
||||
}: MiniCalendarProps) {
|
||||
const t = useTranslations("calendar");
|
||||
const intlFormatter = useFormatter();
|
||||
@@ -61,6 +63,17 @@ export function MiniCalendar({
|
||||
? ["sun", "mon", "tue", "wed", "thu", "fri", "sat"] as const
|
||||
: ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] as const;
|
||||
|
||||
// Compute week numbers for each row (one per 7-day chunk)
|
||||
const weekNumbers = useMemo(() => {
|
||||
if (!showWeekNumbers) return [];
|
||||
const nums: number[] = [];
|
||||
for (let i = 0; i < days.length; i += 7) {
|
||||
// Use the first day of each row to determine the week number
|
||||
nums.push(weekStart === 1 ? getISOWeek(days[i]) : getWeek(days[i], { weekStartsOn: 0 }));
|
||||
}
|
||||
return nums;
|
||||
}, [days, showWeekNumbers, weekStart]);
|
||||
|
||||
const currentYear = getYear(displayMonth);
|
||||
const currentMonth = getMonth(displayMonth);
|
||||
const decadeStart = Math.floor(currentYear / 10) * 10;
|
||||
@@ -135,35 +148,49 @@ export function MiniCalendar({
|
||||
</div>
|
||||
|
||||
{pickerView === "days" && (
|
||||
<div className="grid grid-cols-7 gap-0">
|
||||
<div className={cn("grid gap-0", showWeekNumbers ? "grid-cols-[auto_repeat(7,1fr)]" : "grid-cols-7")}>
|
||||
{showWeekNumbers && (
|
||||
<div className="text-center text-[10px] font-medium text-muted-foreground py-1 w-5" />
|
||||
)}
|
||||
{dayHeaders.map((d) => (
|
||||
<div key={d} className="text-center text-[10px] font-medium text-muted-foreground py-1">
|
||||
{t(`days.${d}`)}
|
||||
</div>
|
||||
))}
|
||||
{days.map((day) => {
|
||||
{days.map((day, index) => {
|
||||
const inMonth = isSameMonth(day, displayMonth);
|
||||
const selected = isSameDay(day, selectedDate);
|
||||
const today = isToday(day);
|
||||
const hasEvent = eventDates.has(format(day, "yyyy-MM-dd"));
|
||||
const isFirstDayOfRow = index % 7 === 0;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={day.toISOString()}
|
||||
onClick={() => onSelectDate(day)}
|
||||
className={cn(
|
||||
"relative flex items-center justify-center w-7 h-7 text-xs rounded-full transition-colors",
|
||||
!inMonth && "text-muted-foreground/40",
|
||||
inMonth && !selected && "hover:bg-muted",
|
||||
today && !selected && "font-bold text-primary",
|
||||
selected && "bg-primary text-primary-foreground"
|
||||
<Fragment key={day.toISOString()}>
|
||||
{showWeekNumbers && isFirstDayOfRow && (
|
||||
<div
|
||||
key={`wk-${index}`}
|
||||
className="flex items-center justify-center w-5 text-[9px] text-muted-foreground/60 font-medium"
|
||||
>
|
||||
{weekNumbers[index / 7]}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{format(day, "d")}
|
||||
{hasEvent && !selected && (
|
||||
<span className="absolute bottom-0.5 left-1/2 -translate-x-1/2 w-1 h-1 rounded-full bg-primary" />
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
key={`day-${day.toISOString()}`}
|
||||
onClick={() => onSelectDate(day)}
|
||||
className={cn(
|
||||
"relative flex items-center justify-center w-7 h-7 text-xs rounded-full transition-colors",
|
||||
!inMonth && "text-muted-foreground/40",
|
||||
inMonth && !selected && "hover:bg-muted",
|
||||
today && !selected && "font-bold text-primary",
|
||||
selected && "bg-primary text-primary-foreground"
|
||||
)}
|
||||
>
|
||||
{format(day, "d")}
|
||||
{hasEvent && !selected && (
|
||||
<span className="absolute bottom-0.5 left-1/2 -translate-x-1/2 w-1 h-1 rounded-full bg-primary" />
|
||||
)}
|
||||
</button>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,7 @@ export function CalendarSettings() {
|
||||
timeFormat,
|
||||
firstDayOfWeek,
|
||||
showTimeInMonthView,
|
||||
showWeekNumbers,
|
||||
calendarNotificationsEnabled,
|
||||
calendarNotificationSound,
|
||||
calendarInvitationParsingEnabled,
|
||||
@@ -68,6 +69,16 @@ export function CalendarSettings() {
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
label={t('show_week_numbers')}
|
||||
description={t('show_week_numbers_desc')}
|
||||
>
|
||||
<ToggleSwitch
|
||||
checked={showWeekNumbers}
|
||||
onChange={(checked) => updateSetting('showWeekNumbers', checked)}
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
label={t('notifications_enabled')}
|
||||
description={t('notifications_enabled_desc')}
|
||||
|
||||
@@ -1858,7 +1858,9 @@
|
||||
"invitation_parsing": "E-Mail-Einladungen verarbeiten",
|
||||
"invitation_parsing_desc": "Kalendereinladungen in E-Mail-Anhängen erkennen und Kalenderaktionen anzeigen",
|
||||
"show_time_in_month_view": "Zeit in Monatsansicht anzeigen",
|
||||
"show_time_in_month_view_desc": "Ereigniszeiten in der Monatskalenderansicht anzeigen"
|
||||
"show_time_in_month_view_desc": "Ereigniszeiten in der Monatskalenderansicht anzeigen",
|
||||
"show_week_numbers": "Kalenderwochen anzeigen",
|
||||
"show_week_numbers_desc": "Kalenderwochen im Minikalender anzeigen"
|
||||
},
|
||||
"days": {
|
||||
"monday": "Montag",
|
||||
|
||||
@@ -1858,7 +1858,9 @@
|
||||
"invitation_parsing": "Parse email invitations",
|
||||
"invitation_parsing_desc": "Detect calendar invitations in email attachments and show calendar actions",
|
||||
"show_time_in_month_view": "Show time in month view",
|
||||
"show_time_in_month_view_desc": "Display event times in the month calendar view"
|
||||
"show_time_in_month_view_desc": "Display event times in the month calendar view",
|
||||
"show_week_numbers": "Show week numbers",
|
||||
"show_week_numbers_desc": "Display week numbers in the mini-calendar"
|
||||
},
|
||||
"days": {
|
||||
"monday": "Monday",
|
||||
|
||||
@@ -1858,7 +1858,9 @@
|
||||
"invitation_parsing": "Analizar invitaciones por correo",
|
||||
"invitation_parsing_desc": "Detectar invitaciones de calendario en archivos adjuntos del correo y mostrar acciones del calendario",
|
||||
"show_time_in_month_view": "Mostrar hora en vista mensual",
|
||||
"show_time_in_month_view_desc": "Mostrar las horas de los eventos en la vista mensual del calendario"
|
||||
"show_time_in_month_view_desc": "Mostrar las horas de los eventos en la vista mensual del calendario",
|
||||
"show_week_numbers": "Mostrar números de semana",
|
||||
"show_week_numbers_desc": "Mostrar los números de semana en el minicalendario"
|
||||
},
|
||||
"days": {
|
||||
"monday": "Lunes",
|
||||
|
||||
@@ -1858,7 +1858,9 @@
|
||||
"invitation_parsing": "Analyser les invitations par e-mail",
|
||||
"invitation_parsing_desc": "Détecter les invitations de calendrier dans les pièces jointes des e-mails et afficher les actions du calendrier",
|
||||
"show_time_in_month_view": "Afficher l'heure dans la vue mensuelle",
|
||||
"show_time_in_month_view_desc": "Afficher les heures des événements dans la vue mensuelle du calendrier"
|
||||
"show_time_in_month_view_desc": "Afficher les heures des événements dans la vue mensuelle du calendrier",
|
||||
"show_week_numbers": "Afficher les numéros de semaine",
|
||||
"show_week_numbers_desc": "Afficher les numéros de semaine dans le mini-calendrier"
|
||||
},
|
||||
"days": {
|
||||
"monday": "Lundi",
|
||||
|
||||
@@ -1858,7 +1858,9 @@
|
||||
"invitation_parsing": "Analizza gli inviti email",
|
||||
"invitation_parsing_desc": "Rileva gli inviti del calendario negli allegati email e mostra le azioni del calendario",
|
||||
"show_time_in_month_view": "Mostra orario nella vista mensile",
|
||||
"show_time_in_month_view_desc": "Visualizza gli orari degli eventi nella vista mensile del calendario"
|
||||
"show_time_in_month_view_desc": "Visualizza gli orari degli eventi nella vista mensile del calendario",
|
||||
"show_week_numbers": "Mostra numeri di settimana",
|
||||
"show_week_numbers_desc": "Mostra i numeri di settimana nel mini-calendario"
|
||||
},
|
||||
"days": {
|
||||
"monday": "Lunedì",
|
||||
|
||||
@@ -1858,7 +1858,9 @@
|
||||
"invitation_parsing": "メール招待を解析する",
|
||||
"invitation_parsing_desc": "メール添付のカレンダー招待を検出してカレンダー操作を表示する",
|
||||
"show_time_in_month_view": "月表示で時刻を表示",
|
||||
"show_time_in_month_view_desc": "月カレンダー表示でイベントの時刻を表示する"
|
||||
"show_time_in_month_view_desc": "月カレンダー表示でイベントの時刻を表示する",
|
||||
"show_week_numbers": "週番号を表示",
|
||||
"show_week_numbers_desc": "ミニカレンダーに週番号を表示する"
|
||||
},
|
||||
"days": {
|
||||
"monday": "月曜日",
|
||||
|
||||
@@ -1858,7 +1858,9 @@
|
||||
"invitation_parsing": "E-mailuitnodigingen verwerken",
|
||||
"invitation_parsing_desc": "Kalenderuitnodigingen in e-mailbijlagen detecteren en kalenderacties tonen",
|
||||
"show_time_in_month_view": "Tijd weergeven in maandweergave",
|
||||
"show_time_in_month_view_desc": "Evenementtijden weergeven in de maandkalenderweergave"
|
||||
"show_time_in_month_view_desc": "Evenementtijden weergeven in de maandkalenderweergave",
|
||||
"show_week_numbers": "Weeknummers weergeven",
|
||||
"show_week_numbers_desc": "Weeknummers weergeven in de minikalender"
|
||||
},
|
||||
"days": {
|
||||
"monday": "Maandag",
|
||||
|
||||
@@ -1858,7 +1858,9 @@
|
||||
"invitation_parsing": "Analisar convites por e-mail",
|
||||
"invitation_parsing_desc": "Detectar convites de calendário em anexos de e-mail e mostrar ações do calendário",
|
||||
"show_time_in_month_view": "Mostrar horário na visualização mensal",
|
||||
"show_time_in_month_view_desc": "Exibir horários dos eventos na visualização mensal do calendário"
|
||||
"show_time_in_month_view_desc": "Exibir horários dos eventos na visualização mensal do calendário",
|
||||
"show_week_numbers": "Mostrar números da semana",
|
||||
"show_week_numbers_desc": "Exibir números da semana no minicalendário"
|
||||
},
|
||||
"days": {
|
||||
"monday": "Segunda-feira",
|
||||
|
||||
@@ -121,6 +121,7 @@ interface SettingsState {
|
||||
|
||||
// Calendar
|
||||
showTimeInMonthView: boolean;
|
||||
showWeekNumbers: boolean;
|
||||
|
||||
// Calendar Notifications
|
||||
calendarNotificationsEnabled: boolean;
|
||||
@@ -221,6 +222,7 @@ const DEFAULT_SETTINGS = {
|
||||
|
||||
// Calendar
|
||||
showTimeInMonthView: false,
|
||||
showWeekNumbers: false,
|
||||
|
||||
// Calendar Notifications
|
||||
calendarNotificationsEnabled: true,
|
||||
@@ -307,6 +309,7 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
calendarNotificationSound: state.calendarNotificationSound,
|
||||
calendarInvitationParsingEnabled: state.calendarInvitationParsingEnabled,
|
||||
showTimeInMonthView: state.showTimeInMonthView,
|
||||
showWeekNumbers: state.showWeekNumbers,
|
||||
toolbarPosition: state.toolbarPosition,
|
||||
senderFavicons: state.senderFavicons,
|
||||
folderIcons: state.folderIcons,
|
||||
|
||||
Reference in New Issue
Block a user