From 2383bcbdc035d475b63dc2565648ba1ea28f3369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Fri, 22 May 2026 08:48:23 +0200 Subject: [PATCH] =?UTF-8?q?fix(timeline):=20trigger=20scroll-to-today=20on?= =?UTF-8?q?=20isInitialLoading=E2=86=92false=20not=20totalCanvasWidth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit totalCanvasWidth is computed from viewStart/viewDays before data loads, so the previous trigger fired during the loading spinner. scrollLeft was clipped to 0 (no canvas in DOM yet) and the guard was set, blocking the real scroll after data arrived. Using isInitialLoading as the dep fires the effect exactly when the canvas enters the DOM. Co-Authored-By: Claude Sonnet 4.6 --- apps/web/src/components/timeline/TimelineView.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/timeline/TimelineView.tsx b/apps/web/src/components/timeline/TimelineView.tsx index 3e5a399..8767b5b 100644 --- a/apps/web/src/components/timeline/TimelineView.tsx +++ b/apps/web/src/components/timeline/TimelineView.tsx @@ -698,16 +698,16 @@ function TimelineViewContent({ }; }, []); - // Scroll to today the first time the canvas has its full width (after initial data load). - // Depends on totalCanvasWidth so it fires after isInitialLoading → false renders the canvas. + // Scroll to today the first time the canvas is in the DOM (isInitialLoading → false). + // totalCanvasWidth is non-zero before data loads, so it can't be used as the trigger. useLayoutEffect(() => { - if (totalCanvasWidth === 0) return; + if (isInitialLoading) return; if (hasScrolledToTodayOnLoad.current) return; const el = scrollContainerRef.current; if (!el) return; el.scrollLeft = toLeft(today); hasScrolledToTodayOnLoad.current = true; - }, [totalCanvasWidth, toLeft, today]); + }, [isInitialLoading, toLeft, today]); // Apply scroll compensation synchronously after the canvas grows (left-extend or Today button). useLayoutEffect(() => {