From 90b1db0432ff920ffb6a7ac54352d32be52ca6b2 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:34:35 +0200 Subject: [PATCH] feat(calendar): fold calendar lists and show options on hover in the sidebar The calendar list in the sidebar now works like Google Calendar's: - "My calendars" and a new "Other calendars" section (shared calendars, still grouped by owner) fold away, and stay folded - each calendar has a checkbox filled in its colour with a check mark - hovering a calendar shows an options button that opens the same menu as a right-click In the mini month, today is the filled circle and the selected day is tinted, so the two no longer look alike. --- .../calendar/calendar-sidebar-panel.tsx | 132 +++++++++++++----- components/calendar/mini-calendar.tsx | 11 +- locales/ar/common.json | 4 +- locales/ca/common.json | 4 +- locales/cs/common.json | 4 +- locales/da/common.json | 4 +- locales/de/common.json | 4 +- locales/en/common.json | 4 +- locales/es/common.json | 4 +- locales/fa/common.json | 4 +- locales/fr/common.json | 4 +- locales/he/common.json | 4 +- locales/hu/common.json | 4 +- locales/it/common.json | 4 +- locales/ja/common.json | 4 +- locales/ko/common.json | 4 +- locales/lv/common.json | 4 +- locales/mn/common.json | 4 +- locales/nb/common.json | 4 +- locales/nl/common.json | 4 +- locales/pl/common.json | 4 +- locales/pt/common.json | 4 +- locales/ro/common.json | 4 +- locales/ru/common.json | 4 +- locales/sk/common.json | 4 +- locales/tr/common.json | 4 +- locales/uk/common.json | 4 +- locales/zh-TW/common.json | 4 +- locales/zh/common.json | 4 +- 29 files changed, 184 insertions(+), 67 deletions(-) diff --git a/components/calendar/calendar-sidebar-panel.tsx b/components/calendar/calendar-sidebar-panel.tsx index 3ad161ce0..37b6f6abc 100644 --- a/components/calendar/calendar-sidebar-panel.tsx +++ b/components/calendar/calendar-sidebar-panel.tsx @@ -2,7 +2,7 @@ import { useMemo, useState } from "react"; import { useTranslations } from "next-intl"; -import { ChevronDown, ChevronRight, Globe, ListTodo, Pencil, RefreshCw, Share2, Star, Trash2, Cake, User, Users, Plus, Eraser, Palette, Shuffle } from "@/components/icons"; +import { Check, ChevronDown, ChevronRight, Globe, ListTodo, MoreVertical, Pencil, RefreshCw, Share2, Star, Trash2, Cake, User, Users, Plus, Eraser, Palette, Shuffle } from "@/components/icons"; import { cn, formatDateTime } from "@/lib/utils"; import type { Calendar } from "@/lib/jmap/types"; import { CalendarColorPicker } from "@/components/settings/calendar-management-settings"; @@ -17,6 +17,7 @@ import { ContextMenu, ContextMenuItem, ContextMenuSeparator, ContextMenuSubMenu import { useContextMenu } from "@/hooks/use-context-menu"; import type { IJMAPClient } from '@/lib/jmap/client-interface'; import { displayNow } from "@/lib/timezone"; +import { readableTextOn } from "@/lib/color-transform"; /** * Split a per-account calendar list into "owned" (the user's own) and @@ -243,26 +244,33 @@ export function CalendarSidebarPanel({ const hasMenu = isSubscriptionCalendar(cal.id) ? !!client : true; return ( -
+
+ {hasMenu && ( + + )}
); }; @@ -396,6 +419,51 @@ export function CalendarSidebarPanel({ ); }; + // "My calendars" / "Other calendars" fold away like the account groups, + // and share their remembered state. + const renderSectionHeader = (key: string, label: string, onAdd?: () => void) => { + const expanded = !collapsedAccountGroups.has(key); + return ( +
+ + {onAdd && ( + + )} +
+ ); + }; + + const renderSection = (key: string, label: string, list: Calendar[], onAdd?: () => void) => ( +
+ {renderSectionHeader(key, label, onAdd)} + {!collapsedAccountGroups.has(key) && ( +
+ {list.map(renderCalendarItem)} +
+ )} +
+ ); + return (
{enableCalendarTasks && ( @@ -484,37 +552,29 @@ export function CalendarSidebarPanel({ ) : ( <> -
- {onCreateCalendar ? ( - - ) : ( -

- {t('my_calendars')} -

- )} -
-
- {personalCalendars.map(renderCalendarItem)} -
+ {renderSection( + '__mine__', + t('my_calendars'), + personalCalendars, + onCreateCalendar, + )} - {sharedAccountGroups.map((group) => ( -
-

- - {group.accountName} -

-
- {group.calendars.map(renderCalendarItem)} -
+ {sharedAccountGroups.length > 0 && ( +
+ {renderSectionHeader('__others__', t('other_calendars'))} + {!collapsedAccountGroups.has('__others__') && sharedAccountGroups.map((group) => ( +
+
+ + {group.accountName} +
+
+ {group.calendars.map(renderCalendarItem)} +
+
+ ))}
- ))} + )} )} diff --git a/components/calendar/mini-calendar.tsx b/components/calendar/mini-calendar.tsx index 10404986b..03bc55dc8 100644 --- a/components/calendar/mini-calendar.tsx +++ b/components/calendar/mini-calendar.tsx @@ -187,13 +187,16 @@ export function MiniCalendar({ 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" + // Today is the filled circle; the selected day is tinted. + today && "bg-primary text-primary-foreground font-semibold", + selected && !today && "bg-primary/15 text-primary font-semibold", + !today && !selected && "hover:bg-muted" )} + aria-current={today ? "date" : undefined} + aria-pressed={selected} > {formatDayNumber(day)} - {hasEvent && !selected && ( + {hasEvent && !selected && !today && ( )} diff --git a/locales/ar/common.json b/locales/ar/common.json index 3c3171e17..34fba01c3 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "إجراءات أخرى", "select_view": "تغيير العرض، الحالي: {view}" - } + }, + "other_calendars": "تقويمات أخرى", + "calendar_options": "خيارات {name}" }, "calendar_event_notifications": { "someone": "شخص ما", diff --git a/locales/ca/common.json b/locales/ca/common.json index 0259420cd..46eb9d5ca 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Més accions", "select_view": "Canvia la vista, actualment {view}" - } + }, + "other_calendars": "Altres calendaris", + "calendar_options": "Opcions de {name}" }, "calendar_event_notifications": { "someone": "Algú", diff --git a/locales/cs/common.json b/locales/cs/common.json index 9a66630eb..aedc165aa 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Další akce", "select_view": "Změnit zobrazení, nyní {view}" - } + }, + "other_calendars": "Další kalendáře", + "calendar_options": "Možnosti pro {name}" }, "advanced_search": { "title": "Pokročilé hledání", diff --git a/locales/da/common.json b/locales/da/common.json index 176e1d915..6a849c45b 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Flere handlinger", "select_view": "Skift visning, aktuelt {view}" - } + }, + "other_calendars": "Andre kalendere", + "calendar_options": "Indstillinger for {name}" }, "calendar_event_notifications": { "someone": "Nogen", diff --git a/locales/de/common.json b/locales/de/common.json index 8b3a2f963..a120bd650 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Weitere Aktionen", "select_view": "Ansicht wechseln, aktuell {view}" - } + }, + "other_calendars": "Weitere Kalender", + "calendar_options": "Optionen für {name}" }, "advanced_search": { "title": "Erweiterte Suche", diff --git a/locales/en/common.json b/locales/en/common.json index 219a4dc1e..a1df65771 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "More actions", "select_view": "Change view, currently {view}" - } + }, + "other_calendars": "Other calendars", + "calendar_options": "Options for {name}" }, "calendar_event_notifications": { "someone": "Someone", diff --git a/locales/es/common.json b/locales/es/common.json index dd856353a..72553e8b6 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Más acciones", "select_view": "Cambiar vista, actualmente {view}" - } + }, + "other_calendars": "Otros calendarios", + "calendar_options": "Opciones de {name}" }, "advanced_search": { "title": "Búsqueda avanzada", diff --git a/locales/fa/common.json b/locales/fa/common.json index b14f4692d..ddf0a589c 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "اقدامات بیشتر", "select_view": "تغییر نما، نمای فعلی: {view}" - } + }, + "other_calendars": "تقویم‌های دیگر", + "calendar_options": "گزینه‌های {name}" }, "calendar_event_notifications": { "someone": "کسی", diff --git a/locales/fr/common.json b/locales/fr/common.json index b99533d08..02b2d946d 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Plus d'actions", "select_view": "Changer de vue, actuellement {view}" - } + }, + "other_calendars": "Autres agendas", + "calendar_options": "Options de {name}" }, "advanced_search": { "title": "Recherche avancée", diff --git a/locales/he/common.json b/locales/he/common.json index 83a031ed7..0714241fe 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -3364,7 +3364,9 @@ "toolbar": { "more": "פעולות נוספות", "select_view": "החלפת תצוגה, כעת {view}" - } + }, + "other_calendars": "יומנים אחרים", + "calendar_options": "אפשרויות עבור {name}" }, "advanced_search": { "title": "חיפוש מתקדם", diff --git a/locales/hu/common.json b/locales/hu/common.json index 84c241e40..381144f0b 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "További műveletek", "select_view": "Nézet váltása, jelenleg {view}" - } + }, + "other_calendars": "Egyéb naptárak", + "calendar_options": "{name} beállításai" }, "calendar_event_notifications": { "someone": "Valaki", diff --git a/locales/it/common.json b/locales/it/common.json index 450c6ea6d..08b1c25e9 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Altre azioni", "select_view": "Cambia vista, attualmente {view}" - } + }, + "other_calendars": "Altri calendari", + "calendar_options": "Opzioni per {name}" }, "advanced_search": { "title": "Ricerca avanzata", diff --git a/locales/ja/common.json b/locales/ja/common.json index 050872feb..d24a27366 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "その他の操作", "select_view": "表示を切り替え(現在: {view})" - } + }, + "other_calendars": "他のカレンダー", + "calendar_options": "{name} のオプション" }, "advanced_search": { "title": "詳細検索", diff --git a/locales/ko/common.json b/locales/ko/common.json index c4748e741..25aba3272 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "추가 작업", "select_view": "보기 변경, 현재 {view}" - } + }, + "other_calendars": "다른 캘린더", + "calendar_options": "{name} 옵션" }, "advanced_search": { "title": "상세 검색", diff --git a/locales/lv/common.json b/locales/lv/common.json index 938a11ebd..b70805b68 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Citas darbības", "select_view": "Mainīt skatu, pašlaik {view}" - } + }, + "other_calendars": "Citi kalendāri", + "calendar_options": "{name} opcijas" }, "advanced_search": { "title": "Izvērstā meklēšana", diff --git a/locales/mn/common.json b/locales/mn/common.json index d2f8c619f..699a2439e 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Бусад үйлдэл", "select_view": "Харагдац солих, одоо {view}" - } + }, + "other_calendars": "Бусад хуанли", + "calendar_options": "{name}-ийн тохиргоо" }, "calendar_event_notifications": { "someone": "Хэн нэгэн", diff --git a/locales/nb/common.json b/locales/nb/common.json index 979a8fb39..7dae98835 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Flere handlinger", "select_view": "Bytt visning, nå {view}" - } + }, + "other_calendars": "Andre kalendere", + "calendar_options": "Alternativer for {name}" }, "calendar_event_notifications": { "someone": "Noen", diff --git a/locales/nl/common.json b/locales/nl/common.json index a43e6426b..f3d731e36 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Meer acties", "select_view": "Weergave wijzigen, nu {view}" - } + }, + "other_calendars": "Andere agenda's", + "calendar_options": "Opties voor {name}" }, "advanced_search": { "title": "Geavanceerd zoeken", diff --git a/locales/pl/common.json b/locales/pl/common.json index bfba324c1..7a6635aa7 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Więcej działań", "select_view": "Zmień widok, obecnie {view}" - } + }, + "other_calendars": "Inne kalendarze", + "calendar_options": "Opcje dla {name}" }, "advanced_search": { "title": "Wyszukiwanie zaawansowane", diff --git a/locales/pt/common.json b/locales/pt/common.json index e259e23e8..8b43bbc6e 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Mais ações", "select_view": "Mudar vista, atualmente {view}" - } + }, + "other_calendars": "Outros calendários", + "calendar_options": "Opções de {name}" }, "advanced_search": { "title": "Pesquisa avançada", diff --git a/locales/ro/common.json b/locales/ro/common.json index 302a01d24..039fbc0e9 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Mai multe acțiuni", "select_view": "Schimbă vizualizarea, acum {view}" - } + }, + "other_calendars": "Alte calendare", + "calendar_options": "Opțiuni pentru {name}" }, "calendar_event_notifications": { "someone": "Cineva", diff --git a/locales/ru/common.json b/locales/ru/common.json index 89f14ab91..96a589c67 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Другие действия", "select_view": "Сменить вид, сейчас {view}" - } + }, + "other_calendars": "Другие календари", + "calendar_options": "Параметры: {name}" }, "advanced_search": { "title": "Расширенный поиск", diff --git a/locales/sk/common.json b/locales/sk/common.json index 58a7f763e..312e62dd5 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Ďalšie akcie", "select_view": "Zmeniť zobrazenie, teraz {view}" - } + }, + "other_calendars": "Ďalšie kalendáre", + "calendar_options": "Možnosti pre {name}" }, "calendar_event_notifications": { "someone": "Niekto", diff --git a/locales/tr/common.json b/locales/tr/common.json index b21279371..1c6b8ba9e 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Diğer işlemler", "select_view": "Görünümü değiştir, şu an {view}" - } + }, + "other_calendars": "Diğer takvimler", + "calendar_options": "{name} seçenekleri" }, "calendar_event_notifications": { "someone": "Birisi", diff --git a/locales/uk/common.json b/locales/uk/common.json index 9d0d3fb98..c414e9d06 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "Інші дії", "select_view": "Змінити вигляд, зараз {view}" - } + }, + "other_calendars": "Інші календарі", + "calendar_options": "Параметри: {name}" }, "advanced_search": { "title": "Розширений пошук", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index 428fa8d7d..0165a7eee 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "更多動作", "select_view": "切換檢視,目前為{view}" - } + }, + "other_calendars": "其他日曆", + "calendar_options": "{name} 的選項" }, "calendar_event_notifications": { "someone": "有人", diff --git a/locales/zh/common.json b/locales/zh/common.json index d3c045459..350d13dcc 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -3440,7 +3440,9 @@ "toolbar": { "more": "更多操作", "select_view": "切换视图,当前为{view}" - } + }, + "other_calendars": "其他日历", + "calendar_options": "{name} 的选项" }, "advanced_search": { "title": "高级搜索",