mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(settings): keep the section nav fixed while only the content pane scrolls
This commit is contained in:
1 parent
611de0f274
commit
e5ee8f3b3e
2 files changed
+87
-69
No files matched your search
@@ -309,51 +309,60 @@ export function AdminShell({ active, children }: AdminShellProps) {
|
||||
{headingBlock}
|
||||
</div>
|
||||
|
||||
{/* Desktop sticky sidebar — starts at the cards row (row 2). */}
|
||||
{/* Desktop sticky sidebar — starts at the cards row (row 2).
|
||||
|
||||
The sticky lives on the `<aside>` grid item itself, with
|
||||
`self-start` so it shrinks to its content instead of stretching
|
||||
to the full row height. A sticky CHILD inside a stretched grid
|
||||
item resolves its containing block to the stretched item, which
|
||||
in some engines lets the nav scroll away with the content rather
|
||||
than pin. Sticking the grid item directly — sized to content and
|
||||
offset `top-6` to clear the scroll viewport's padding — keeps the
|
||||
nav fixed while only the content column scrolls. `max-h` +
|
||||
`overflow-y-auto` let a long section list scroll within the
|
||||
pinned panel on short viewports. */}
|
||||
<aside
|
||||
aria-label={t("admin.shell.sectionsNav")}
|
||||
className="hidden md:col-start-1 md:row-start-2 md:block"
|
||||
className="no-scrollbar hidden max-h-[calc(100dvh-5.5rem)] overflow-y-auto md:sticky md:top-6 md:col-start-1 md:row-start-2 md:block md:self-start"
|
||||
>
|
||||
<div className="no-scrollbar sticky top-6 max-h-[calc(100dvh-5.5rem)] overflow-y-auto">
|
||||
<ul className="space-y-1">
|
||||
<li>
|
||||
<Link
|
||||
href="/admin"
|
||||
aria-current={activeSlug === null ? "page" : undefined}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
||||
activeSlug === null
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-foreground hover:bg-accent",
|
||||
)}
|
||||
>
|
||||
<ScrollText className="h-4 w-4" aria-hidden="true" />
|
||||
{t("admin.shell.overview")}
|
||||
</Link>
|
||||
</li>
|
||||
{ADMIN_SECTIONS.map((section) => {
|
||||
const isActive = section.slug === activeSlug;
|
||||
const Icon = section.icon;
|
||||
return (
|
||||
<li key={section.slug}>
|
||||
<Link
|
||||
href={`/admin/${section.slug}`}
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
||||
isActive
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-foreground hover:bg-accent",
|
||||
)}
|
||||
>
|
||||
<Icon className="h-4 w-4" aria-hidden="true" />
|
||||
{t(section.titleKey)}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<ul className="space-y-1">
|
||||
<li>
|
||||
<Link
|
||||
href="/admin"
|
||||
aria-current={activeSlug === null ? "page" : undefined}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
||||
activeSlug === null
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-foreground hover:bg-accent",
|
||||
)}
|
||||
>
|
||||
<ScrollText className="h-4 w-4" aria-hidden="true" />
|
||||
{t("admin.shell.overview")}
|
||||
</Link>
|
||||
</li>
|
||||
{ADMIN_SECTIONS.map((section) => {
|
||||
const isActive = section.slug === activeSlug;
|
||||
const Icon = section.icon;
|
||||
return (
|
||||
<li key={section.slug}>
|
||||
<Link
|
||||
href={`/admin/${section.slug}`}
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
||||
isActive
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-foreground hover:bg-accent",
|
||||
)}
|
||||
>
|
||||
<Icon className="h-4 w-4" aria-hidden="true" />
|
||||
{t(section.titleKey)}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
{/* Main column (cards) — row 2 / col 2. Same
|
||||
|
||||
@@ -496,36 +496,45 @@ export function SettingsShell({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Desktop sticky sidebar — starts at the cards row (row 2). */}
|
||||
{/* Desktop sticky sidebar — starts at the cards row (row 2).
|
||||
|
||||
The sticky lives on the `<aside>` grid item itself, with
|
||||
`self-start` so it shrinks to its content instead of stretching
|
||||
to the full row height. A sticky CHILD inside a stretched grid
|
||||
item resolves its containing block to the stretched item, which
|
||||
in some engines lets the nav scroll away with the content rather
|
||||
than pin. Sticking the grid item directly — sized to content and
|
||||
offset `top-6` to clear the scroll viewport's padding — keeps the
|
||||
nav fixed while only the content column scrolls. `max-h` +
|
||||
`overflow-y-auto` let a long section list scroll within the
|
||||
pinned panel on short viewports. */}
|
||||
<aside
|
||||
aria-label={t("settings.shell.sectionsNav")}
|
||||
className="hidden md:col-start-1 md:row-start-2 md:block"
|
||||
className="no-scrollbar hidden max-h-[calc(100dvh-5.5rem)] overflow-y-auto md:sticky md:top-6 md:col-start-1 md:row-start-2 md:block md:self-start"
|
||||
>
|
||||
<div className="no-scrollbar sticky top-6 max-h-[calc(100dvh-5.5rem)] overflow-y-auto">
|
||||
<ul className="space-y-1">
|
||||
{visibleSections.map((section) => {
|
||||
const isActive = section.slug === activeSlug;
|
||||
const Icon = section.icon;
|
||||
return (
|
||||
<li key={section.slug}>
|
||||
<Link
|
||||
href={`/settings/${section.slug}`}
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
||||
isActive
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-foreground hover:bg-accent",
|
||||
)}
|
||||
>
|
||||
<Icon className="h-4 w-4" aria-hidden="true" />
|
||||
{t(section.titleKey)}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<ul className="space-y-1">
|
||||
{visibleSections.map((section) => {
|
||||
const isActive = section.slug === activeSlug;
|
||||
const Icon = section.icon;
|
||||
return (
|
||||
<li key={section.slug}>
|
||||
<Link
|
||||
href={`/settings/${section.slug}`}
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
||||
isActive
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-foreground hover:bg-accent",
|
||||
)}
|
||||
>
|
||||
<Icon className="h-4 w-4" aria-hidden="true" />
|
||||
{t(section.titleKey)}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
{/* Main column — page renders its own h1 + subtitle. The
|
||||
|
||||
Reference in new issue
Block a user