docs(ui): scroll the sidebar so the active (bold) link is in view on load
Deep-linking into a low doc (e.g. ratings/get_ratings from the overview page) opened the right menus but sat the highlight below the sidebar's visible scroll area. scrollSidebarToActive() runs at docs Alpine init: finds the link for the current path, and if it's outside the sidebar's scrollport, moves ONLY the sidebar's own scrollTop to clamp it into the nearest edge — never the page scroll. Skips the off-canvas mobile drawer. (Same behavior as Docusaurus/MkDocs on deep links.) Live: x-init wired on docs pages, function in the shipped bundle, make test green, 146 vitests.
This commit is contained in:
1 parent
a1ac5b1541
commit
da0bae94bb
3 files changed
+30
-2
No files matched your search
@@ -23,7 +23,7 @@ templ DocsLayout(nav Navigation, doc Document, user User, currentPath string) {
|
|||||||
<link href="/static/style.css" rel="stylesheet"/>
|
<link href="/static/style.css" rel="stylesheet"/>
|
||||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg"/>
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg"/>
|
||||||
</head>
|
</head>
|
||||||
<body x-data="docs" x-init="initializeSearch(); initializeCodeCopyButtons()" class={ "theme-" + user.Theme + " page-docs font-sans antialiased" }>
|
<body x-data="docs" x-init="scrollSidebarToActive(); initializeSearch(); initializeCodeCopyButtons()" class={ "theme-" + user.Theme + " page-docs font-sans antialiased" }>
|
||||||
@DocsTopbar(user)
|
@DocsTopbar(user)
|
||||||
<!-- Mobile drawer backdrop -->
|
<!-- Mobile drawer backdrop -->
|
||||||
<div id="docs-overlay" class="hidden fixed inset-0 z-40 lg:hidden" style="background: var(--surface-overlay);" onclick="document.getElementById('docs-sidebar').classList.add('-translate-x-full'); this.classList.add('hidden')"></div>
|
<div id="docs-overlay" class="hidden fixed inset-0 z-40 lg:hidden" style="background: var(--surface-overlay);" onclick="document.getElementById('docs-sidebar').classList.add('-translate-x-full'); this.classList.add('hidden')"></div>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<body x-data=\"docs\" x-init=\"initializeSearch(); initializeCodeCopyButtons()\" class=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<body x-data=\"docs\" x-init=\"scrollSidebarToActive(); initializeSearch(); initializeCodeCopyButtons()\" class=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,33 @@ function toggleCollapsible(header: HTMLElement): void {
|
|||||||
applyCollapsed(items, !isCollapsed);
|
applyCollapsed(items, !isCollapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scrollSidebarToActive brings the current page's (bold) nav link into the
|
||||||
|
// sidebar's visible area on load — deep links from search, bookmarks, or
|
||||||
|
// the overview page otherwise open with the highlight scrolled out of
|
||||||
|
// sight. Only the sidebar's own scroll position changes, never the page.
|
||||||
|
function scrollSidebarToActive(): void {
|
||||||
|
const sidebar = document.getElementById("docs-sidebar");
|
||||||
|
if (!sidebar) return;
|
||||||
|
|
||||||
|
// Skip the off-canvas mobile drawer (translated off screen).
|
||||||
|
if (sidebar.classList.contains("-translate-x-full")) return;
|
||||||
|
|
||||||
|
const active = sidebar.querySelector<HTMLAnchorElement>(
|
||||||
|
`a[href="${location.pathname}"], a[href="${location.pathname.replace(/\.md$/, "")}"]`,
|
||||||
|
);
|
||||||
|
if (!active) return;
|
||||||
|
|
||||||
|
const sb = sidebar.getBoundingClientRect();
|
||||||
|
const ar = active.getBoundingClientRect();
|
||||||
|
if (ar.top >= sb.top && ar.bottom <= sb.bottom) return; // already visible
|
||||||
|
|
||||||
|
if (ar.top < sb.top) {
|
||||||
|
sidebar.scrollTop += ar.top - sb.top;
|
||||||
|
} else {
|
||||||
|
sidebar.scrollTop += ar.bottom - sb.bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let docsSearchTimeout: ReturnType<typeof setTimeout> | null = null;
|
let docsSearchTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
let docsSearchAbort: AbortController | null = null;
|
let docsSearchAbort: AbortController | null = null;
|
||||||
|
|
||||||
@@ -126,4 +153,5 @@ Alpine.data("docs", () => ({
|
|||||||
toggleCollapsible,
|
toggleCollapsible,
|
||||||
initializeSearch: initializeDocsSearch,
|
initializeSearch: initializeDocsSearch,
|
||||||
initializeCodeCopyButtons,
|
initializeCodeCopyButtons,
|
||||||
|
scrollSidebarToActive,
|
||||||
}));
|
}));
|
||||||
Reference in new issue
Block a user