From 02577c75021f5a8756db1cc9313e645064354c19 Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Sun, 29 Mar 2026 14:10:28 +0200
Subject: [PATCH] feat: add calendar hover preview settings and functionality
---
app/[locale]/calendar/page.tsx | 24 +++++++++++++++--------
components/settings/calendar-settings.tsx | 18 +++++++++++++++++
locales/en/common.json | 9 ++++++++-
stores/settings-store.ts | 4 ++++
4 files changed, 46 insertions(+), 9 deletions(-)
diff --git a/app/[locale]/calendar/page.tsx b/app/[locale]/calendar/page.tsx
index a526546f..712cc606 100644
--- a/app/[locale]/calendar/page.tsx
+++ b/app/[locale]/calendar/page.tsx
@@ -69,7 +69,7 @@ export default function CalendarPage() {
setSelectedDate, setViewMode, toggleCalendarVisibility, updateCalendar,
refreshAllSubscriptions,
} = useCalendarStore();
- const { firstDayOfWeek, timeFormat, showWeekNumbers, enableCalendarTasks, showTasksOnCalendar } = useSettingsStore();
+ const { firstDayOfWeek, timeFormat, showWeekNumbers, enableCalendarTasks, showTasksOnCalendar, calendarHoverPreview } = useSettingsStore();
const taskStore = useTaskStore();
const fetchTasksFn = useTaskStore(state => state.fetchTasks);
const { identities } = useIdentityStore();
@@ -338,18 +338,26 @@ export default function CalendarPage() {
const handleHoverEvent = useCallback((event: CalendarEvent, anchorRect: DOMRect) => {
if (isMobile) return;
+ if (calendarHoverPreview === 'off') return;
if (hoverTimerRef.current) { clearTimeout(hoverTimerRef.current); hoverTimerRef.current = null; }
// Don't show hover popover if the sidebar is already open for this event
if (showEventModal && editEvent?.id === event.id) return;
- setDetailEvent(event);
- setDetailAnchorRect(anchorRect);
- }, [isMobile, showEventModal, editEvent]);
+ if (calendarHoverPreview === 'delay-500ms' || calendarHoverPreview === 'delay-1s' || calendarHoverPreview === 'delay-2s') {
+ const ms = calendarHoverPreview === 'delay-500ms' ? 500 : calendarHoverPreview === 'delay-1s' ? 1000 : 2000;
+ hoverTimerRef.current = setTimeout(() => {
+ setDetailEvent(event);
+ setDetailAnchorRect(anchorRect);
+ }, ms);
+ } else {
+ setDetailEvent(event);
+ setDetailAnchorRect(anchorRect);
+ }
+ }, [isMobile, calendarHoverPreview, showEventModal, editEvent]);
const handleHoverLeave = useCallback(() => {
- hoverTimerRef.current = setTimeout(() => {
- setDetailEvent(null);
- setDetailAnchorRect(null);
- }, 200);
+ if (hoverTimerRef.current) { clearTimeout(hoverTimerRef.current); hoverTimerRef.current = null; }
+ setDetailEvent(null);
+ setDetailAnchorRect(null);
}, []);
const handleEditFromDetail = useCallback(() => {
diff --git a/components/settings/calendar-settings.tsx b/components/settings/calendar-settings.tsx
index 9834a614..479b99c1 100644
--- a/components/settings/calendar-settings.tsx
+++ b/components/settings/calendar-settings.tsx
@@ -19,6 +19,7 @@ export function CalendarSettings() {
showWeekNumbers,
enableCalendarTasks,
showTasksOnCalendar,
+ calendarHoverPreview,
updateSetting,
} = useSettingsStore();
const { isFeatureEnabled } = usePolicyStore();
@@ -80,6 +81,23 @@ export function CalendarSettings() {
/>
+
+
+
{isFeatureEnabled('calendarTasksEnabled') && (
<>
()(
expandedFilterView: state.expandedFilterView,
showTimeInMonthView: state.showTimeInMonthView,
showWeekNumbers: state.showWeekNumbers,
+ calendarHoverPreview: state.calendarHoverPreview,
toolbarPosition: state.toolbarPosition,
hideAccountSwitcher: state.hideAccountSwitcher,
showRailAccountList: state.showRailAccountList,