From 88699ff909b026511da392bf2c0a96be02abc6fd Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 5 Sep 2023 14:40:25 -0700 Subject: fix: :lipstick: nicer autoscroll --- extension/react-app/src/pages/gui.tsx | 90 ++++++++++++----------------------- 1 file changed, 30 insertions(+), 60 deletions(-) (limited to 'extension/react-app/src/pages/gui.tsx') diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx index c4e13d2a..c82f4f2b 100644 --- a/extension/react-app/src/pages/gui.tsx +++ b/extension/react-app/src/pages/gui.tsx @@ -3,7 +3,13 @@ import { defaultBorderRadius } from "../components"; import Loader from "../components/Loader"; import ContinueButton from "../components/ContinueButton"; import { FullState } from "../../../schema/FullState"; -import { useCallback, useEffect, useRef, useState, useContext } from "react"; +import { + useEffect, + useRef, + useState, + useContext, + useLayoutEffect, +} from "react"; import { HistoryNode } from "../../../schema/HistoryNode"; import StepContainer from "../components/StepContainer"; import { GUIClientContext } from "../App"; @@ -99,70 +105,39 @@ function GUI(props: GUIProps) { ); }, [bottomMessage, aboveComboBoxDivRef.current]); - const [isScrolling, setIsScrolling] = useState(false); - - const [scrollTimeoutId, setScrollTimeoutId] = useState(); + const [userScrolledAwayFromBottom, setUserScrolledAwayFromBottom] = + useState(false); - const scrollToBottom = useCallback( - (force: boolean = false) => { - if (scrollTimeoutId) { - return; - } - - // Scroll only if user is within 100 pixels of the bottom of the window. - const edgeOffset = 50; - const scrollPosition = window.scrollY || 0; + useEffect(() => { + const handleScroll = () => { + // Scroll only if user is within 200 pixels of the bottom of the window. + const edgeOffset = -25; + const scrollPosition = topGuiDivRef.current?.scrollTop || 0; const scrollHeight = topGuiDivRef.current?.scrollHeight || 0; const clientHeight = window.innerHeight || 0; - const atBottomEdge = - scrollPosition + clientHeight + edgeOffset >= scrollHeight; - - if (isScrolling || (!atBottomEdge && !force)) { - return; + if (scrollPosition + clientHeight + edgeOffset >= scrollHeight) { + setUserScrolledAwayFromBottom(false); + } else { + setUserScrolledAwayFromBottom(true); } - - window.scrollTo({ - top: scrollHeight, - behavior: "smooth", - }); - - setScrollTimeoutId( - setTimeout(() => { - setScrollTimeoutId(undefined); - }, 800) - ); - }, - [ - window.scrollY, - topGuiDivRef.current?.scrollHeight, - topGuiDivRef.current?.clientHeight, - isScrolling, - scrollTimeoutId, - ] - ); - - const [timerId, setTimerId] = useState(); - - useEffect(() => { - const handleScrollEnd = () => { - setIsScrolling(false); }; - const handleScroll = () => { - window.clearTimeout(timerId); - - setIsScrolling(true); + topGuiDivRef.current?.addEventListener("wheel", handleScroll); - setTimerId(setTimeout(() => handleScrollEnd(), 800)); + return () => { + window.removeEventListener("wheel", handleScroll); }; + }, [topGuiDivRef.current]); - window.addEventListener("scroll", handleScroll); + useLayoutEffect(() => { + if (userScrolledAwayFromBottom) return; - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }, []); + topGuiDivRef.current?.scrollTo({ + top: topGuiDivRef.current?.scrollHeight, + behavior: "smooth" as any, + }); + }, [topGuiDivRef.current?.scrollHeight, history.timeline]); useEffect(() => { // Cmd + Backspace to delete current step @@ -217,15 +192,9 @@ function GUI(props: GUIProps) { } return nextStepsOpen; }); - - scrollToBottom(); }); }, [client]); - useEffect(() => { - scrollToBottom(true); - }, [waitingForSteps]); - // #endregion useEffect(() => { @@ -378,6 +347,7 @@ function GUI(props: GUIProps) { }, []); return (
{ if (e.key === "Enter" && e.ctrlKey) { -- cgit v1.2.3-70-g09d2