From cad59143a3b092912fd446138ebb0417ffabc5b8 Mon Sep 17 00:00:00 2001 From: yiekheng Date: Sun, 12 Apr 2026 13:04:35 +0800 Subject: [PATCH] Center current chapter in drawer on open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chapter drawer now opens with the active row pre-scrolled to the center of the list instead of always starting at chapter #1. useLayoutEffect measures via getBoundingClientRect so the scroll lands before paint — no visible jump. Co-Authored-By: Claude Opus 4.6 (1M context) --- components/PageReader.tsx | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/components/PageReader.tsx b/components/PageReader.tsx index aa4c5fd..b868e78 100644 --- a/components/PageReader.tsx +++ b/components/PageReader.tsx @@ -86,6 +86,8 @@ export function PageReader({ const pageElRef = useRef>(new Map()); const observerRef = useRef(null); const hiddenByScrollRef = useRef(false); + const drawerScrollRef = useRef(null); + const drawerActiveRef = useRef(null); // Pages currently inside the observer's viewport margin. The scroll tick // walks this small set instead of every loaded page. const intersectingPagesRef = useRef>(new Map()); @@ -322,6 +324,21 @@ export function PageReader({ [] ); + useLayoutEffect(() => { + if (!showDrawer) return; + const scroll = drawerScrollRef.current; + const active = drawerActiveRef.current; + if (!scroll || !active) return; + const scrollRect = scroll.getBoundingClientRect(); + const activeRect = active.getBoundingClientRect(); + const delta = + activeRect.top - + scrollRect.top - + scroll.clientHeight / 2 + + active.clientHeight / 2; + scroll.scrollTop = Math.max(0, scroll.scrollTop + delta); + }, [showDrawer]); + const currentChapter = chapters.find((c) => c.number === currentChapterNum) ?? chapters.find((c) => c.number === startChapterNumber); @@ -474,12 +491,16 @@ export function PageReader({ -
+
{chapters.map((ch) => { const isActive = ch.number === currentChapterNum; return (