fix: topbar + model info box stickiness

This commit is contained in:
Tautvydas Derzinskas committed 2026-09-11 22:21:59 +02:00
1 parent f7806d6153
commit f737c0480b
2 files changed
+51 -3

No files matched your search

+38 -2
View File
@@ -1,4 +1,4 @@
import React from "react";
import React, { useLayoutEffect, useRef } from "react";
import { useTranslation } from "react-i18next";
import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
@@ -47,8 +47,44 @@ export default function TopBar({
onLogout,
}: Props) {
const { t } = useTranslation("app");
const rootRef = useRef<HTMLDivElement | null>(null);
// Publishes this bar's real rendered height (it can wrap to two lines on narrow widths, so a
// guessed constant would drift) as a CSS var on the document root -- any sticky element further
// down the page (currently just ModelSidePanel) reads it to stick just below the bar instead of
// guessing a fixed offset and ending up stuck underneath it once both are pinned at once.
useLayoutEffect(() => {
const el = rootRef.current;
if (!el) return;
const update = () => document.documentElement.style.setProperty("--topbar-height", `${el.offsetHeight}px`);
update();
const observer = new ResizeObserver(update);
observer.observe(el);
return () => observer.disconnect();
}, []);
return (
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1.5, flexWrap: "wrap", mb: 2 }}>
<Box
ref={rootRef}
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
gap: 1.5,
flexWrap: "wrap",
mb: 2,
// Pinned to the top of the window (the app scrolls at that level, not inside `main` --
// see AppLayout's own comment on why overflow was removed from main) so the title, back
// button, and the Add/Notifications/User cluster stay reachable no matter how far a long
// page (e.g. the model detail page's sticky side panel) gets scrolled. Needs its own
// opaque background, exactly matching the shell's, or scrolled content would show through
// behind it instead of being covered.
position: "sticky",
top: 0,
zIndex: (muiTheme) => muiTheme.zIndex.appBar,
bgcolor: (muiTheme) => muiTheme.thingport.pageBackground,
}}
>
<Stack direction="row" alignItems="center" spacing={1} minWidth={0}>
{onBack && (
<Tooltip title={t("shell.backToLibrary")}>
@@ -59,7 +59,19 @@ export default function ModelSidePanel({ print, onSelectCategory, onUnauthorized
const authorName = print.author?.name || print.author?.handle || print.creator || null;
return (
<Paper variant="outlined" sx={{ p: 2.5, borderRadius: "12px", position: { xs: "static", md: "sticky" }, top: 16 }}>
<Paper
variant="outlined"
sx={{
p: 2.5,
borderRadius: "12px",
position: { xs: "static", md: "sticky" },
// TopBar is sticky too (see its own doc comment) and sits above this in stacking order --
// sticking at a fixed 16px from the viewport top would land this panel right underneath
// it once both are pinned simultaneously. --topbar-height (set by TopBar itself, since it
// can wrap taller on narrow widths) keeps this just below it instead.
top: "calc(var(--topbar-height, 64px) + 16px)",
}}
>
<Stack spacing={2}>
<Box>
<Typography variant="caption" color="text.secondary" sx={{ display: "block", mb: 0.5 }}>