From 840b16f04142e7503fe16ca2eca746f5dd88161b Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 8 Jan 2025 22:59:08 +0800 Subject: [PATCH] chore: tweak back to top button --- .../PagedMemoList/PagedMemoList.tsx | 105 ++++++++++++------ web/src/components/ScrollToTop.tsx | 64 ----------- web/src/locales/en.json | 4 +- 3 files changed, 70 insertions(+), 103 deletions(-) delete mode 100644 web/src/components/ScrollToTop.tsx diff --git a/web/src/components/PagedMemoList/PagedMemoList.tsx b/web/src/components/PagedMemoList/PagedMemoList.tsx index eafe4f03..e12c8a95 100644 --- a/web/src/components/PagedMemoList/PagedMemoList.tsx +++ b/web/src/components/PagedMemoList/PagedMemoList.tsx @@ -1,9 +1,7 @@ import { Button } from "@usememos/mui"; -import { ArrowDownIcon, LoaderIcon } from "lucide-react"; -import { useEffect, useRef, useState, useMemo } from "react"; -import { useLocation } from "react-router-dom"; +import { ArrowDownIcon, ArrowUpIcon, LoaderIcon, SlashIcon } from "lucide-react"; +import { useEffect, useMemo, useRef, useState } from "react"; import PullToRefresh from "react-simple-pull-to-refresh"; -import ScrollToTop from "@/components/ScrollToTop"; import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts"; import useResponsiveWidth from "@/hooks/useResponsiveWidth"; import { Routes } from "@/router"; @@ -30,32 +28,15 @@ const PagedMemoList = (props: Props) => { const memoStore = useMemoStore(); const memoList = useMemoList(); const containerRef = useRef(null); - const [containerRightOffset, setContainerRightOffset] = useState(0); const [state, setState] = useState({ isRequesting: true, // Initial request nextPageToken: "", }); - const sortedMemoList = props.listSort ? props.listSort(memoList.value) : memoList.value; - const location = useLocation(); - - const shouldShowScrollToTop = useMemo( + const shouldShowBackToTop = useMemo( () => [Routes.ROOT, Routes.EXPLORE, Routes.ARCHIVED].includes(location.pathname as Routes) || location.pathname.startsWith("/u/"), [location.pathname], ); - - useEffect(() => { - const updateOffset = () => { - if (containerRef.current) { - const rect = containerRef.current.getBoundingClientRect(); - const offset = window.innerWidth - rect.right; - setContainerRightOffset(offset); - } - }; - - updateOffset(); - window.addEventListener("resize", updateOffset); - return () => window.removeEventListener("resize", updateOffset); - }, []); + const sortedMemoList = props.listSort ? props.listSort(memoList.value) : memoList.value; const fetchMoreMemos = async (nextPageToken: string) => { setState((state) => ({ ...state, isRequesting: true })); @@ -88,21 +69,29 @@ const PagedMemoList = (props: Props) => { )} - {!state.isRequesting && state.nextPageToken && ( -
- -
+ {!state.isRequesting && ( + <> + {!state.nextPageToken && sortedMemoList.length === 0 ? ( +
+ +

{t("message.no-data")}

+
+ ) : ( +
+ {state.nextPageToken && ( + <> + + {shouldShowBackToTop && } + + )} + {shouldShowBackToTop && } +
+ )} + )} - {!state.isRequesting && !state.nextPageToken && sortedMemoList.length === 0 && ( -
- -

{t("message.no-data")}

-
- )} - ); @@ -130,4 +119,46 @@ const PagedMemoList = (props: Props) => { ); }; +const BackToTop = () => { + const t = useTranslate(); + const [isVisible, setIsVisible] = useState(false); + const [shouldRender, setShouldRender] = useState(false); + + useEffect(() => { + const handleScroll = () => { + const shouldBeVisible = window.scrollY > 400; + if (shouldBeVisible !== isVisible) { + if (shouldBeVisible) { + setShouldRender(true); + setIsVisible(true); + } else { + setShouldRender(false); + setIsVisible(false); + } + } + }; + + window.addEventListener("scroll", handleScroll); + return () => window.removeEventListener("scroll", handleScroll); + }, [isVisible]); + + const scrollToTop = () => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }; + + if (!shouldRender) { + return null; + } + + return ( + + ); +}; + export default PagedMemoList; diff --git a/web/src/components/ScrollToTop.tsx b/web/src/components/ScrollToTop.tsx deleted file mode 100644 index 81244f30..00000000 --- a/web/src/components/ScrollToTop.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import clsx from "clsx"; -import { ArrowUpIcon } from "lucide-react"; -import { useEffect, useState } from "react"; - -interface ScrollToTopProps { - className?: string; - style?: React.CSSProperties; - enabled?: boolean; -} - -const ScrollToTop = ({ className, style, enabled = true }: ScrollToTopProps) => { - const [isVisible, setIsVisible] = useState(false); - const [shouldRender, setShouldRender] = useState(false); - - useEffect(() => { - const handleScroll = () => { - const shouldBeVisible = window.scrollY > 400; - if (shouldBeVisible !== isVisible) { - if (shouldBeVisible) { - setShouldRender(true); - setTimeout(() => setIsVisible(true), 50); - } else { - setIsVisible(false); - setTimeout(() => setShouldRender(false), 200); - } - } - }; - - if (enabled) { - window.addEventListener("scroll", handleScroll); - return () => window.removeEventListener("scroll", handleScroll); - } - }, [enabled, isVisible]); - - const scrollToTop = () => { - window.scrollTo({ - top: 0, - behavior: "smooth", - }); - }; - - if (!enabled || !shouldRender) { - return null; - } - - return ( - - ); -}; - -export default ScrollToTop; diff --git a/web/src/locales/en.json b/web/src/locales/en.json index 3b54c7a4..e55d125f 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -329,7 +329,7 @@ "create-dialog": { "create-access-token": "Create Access Token", "description": "Description", - "some-description": "Some description...", + "some-description": "Some description...", "expiration": "Expiration", "created-at": "Created At", "expires-at": "Expires At", @@ -359,7 +359,7 @@ "week-start-day": "Week start day", "saturday": "Saturday", "sunday": "Sunday", - "monday": "Monday" + "monday": "Monday" }, "memo-related-settings": { "title": "Memo related settings",