mirror of
https://github.com/TautvydasDerzinskas/Thingport.git
synced 2026-10-21 22:22:24 -05:00
feat: back to top button
This commit is contained in:
1 parent
f737c0480b
commit
b60fbd8092
4 files changed
+62
No files matched your search
@@ -8,6 +8,7 @@ import Alert from "@mui/material/Alert";
|
||||
import Sidebar from "./Sidebar";
|
||||
import TopBar from "./TopBar";
|
||||
import ImportProgressBar from "./ImportProgressBar";
|
||||
import BackToTopButton from "./BackToTopButton";
|
||||
import { ConfirmProvider } from "../ConfirmProvider";
|
||||
import { ToastProvider } from "../ToastProvider";
|
||||
import { PageHeaderContext, type PageHeader } from "./PageHeaderContext";
|
||||
@@ -176,6 +177,7 @@ function AppLayoutShell({
|
||||
</Box>
|
||||
</Box>
|
||||
<ImportProgressBar />
|
||||
<BackToTopButton />
|
||||
</ImportJobProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Box from "@mui/material/Box";
|
||||
import Fab from "@mui/material/Fab";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Zoom from "@mui/material/Zoom";
|
||||
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
||||
import { useImportJob } from "./ImportJobContext";
|
||||
|
||||
// Appears once enough of the page has scrolled past that "a bit down" reads as true, not on the
|
||||
// first pixel of scroll -- matches the threshold feel of most scroll-to-top buttons.
|
||||
const SCROLL_SHOW_THRESHOLD = 300;
|
||||
|
||||
function scrollToTop() {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}
|
||||
|
||||
/** Fixed bottom-right "back to top" FAB, shown app-wide (mounted once in AppLayout) -- the app
|
||||
* scrolls at the window level (see AppLayout's own comment on why `main` has no overflow of its
|
||||
* own), so a single `window.scroll` listener here covers every route. Zoom in/out on the
|
||||
* visibility flip for the "pop" MUI's own back-to-top example uses, rather than an instant
|
||||
* show/hide. Shifts up while ImportProgressBar is showing (also fixed to the bottom, full-width)
|
||||
* so the two don't overlap. */
|
||||
export default function BackToTopButton() {
|
||||
const { t } = useTranslation("app");
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { activeJob } = useImportJob();
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setVisible(window.scrollY > SCROLL_SHOW_THRESHOLD);
|
||||
onScroll();
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
|
||||
const label = t("shell.backToTop");
|
||||
|
||||
return (
|
||||
<Zoom in={visible}>
|
||||
<Box
|
||||
onClick={scrollToTop}
|
||||
sx={{
|
||||
position: "fixed",
|
||||
right: 24,
|
||||
bottom: activeJob ? 88 : 24,
|
||||
zIndex: (theme) => theme.zIndex.fab,
|
||||
transition: (theme) => theme.transitions.create("bottom"),
|
||||
}}
|
||||
>
|
||||
<Tooltip title={label}>
|
||||
<Fab color="primary" aria-label={label ?? undefined} size="medium">
|
||||
<KeyboardArrowUpIcon />
|
||||
</Fab>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Zoom>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
"shell": {
|
||||
"apiUnreachable": "API unreachable. Ensure the API container is running and reachable.",
|
||||
"backToLibrary": "Back to library",
|
||||
"backToTop": "Back to top",
|
||||
"sessionExpired": "Your session has expired. Please sign in again."
|
||||
},
|
||||
"auth": {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"shell": {
|
||||
"apiUnreachable": "API nepasiekiamas. Įsitikinkite, kad API konteineris veikia ir pasiekiamas.",
|
||||
"backToLibrary": "Grįžti į biblioteką",
|
||||
"backToTop": "Grįžti į viršų",
|
||||
"sessionExpired": "Jūsų sesija baigėsi. Prašome prisijungti iš naujo."
|
||||
},
|
||||
"auth": {
|
||||
|
||||
Reference in new issue
Block a user