docs(ui): drop sidebar saved-state — server defaults already ARE the standard docs behavior
Simplification per spec: the menu tracks state through the URL alone. Land on /docs: fully closed, headers only (an explicit home rule now overrides the contains-URL rule, since the root overview doc lives in Getting Started and would otherwise auto-open it). Land on any doc page: its containing section + group render open at first paint, current link bold — no JS, no jump, arrows consistent. Navigate elsewhere: old closes, new opens. Deleted: docs_nav cookie const/parse, ApplyNavOverrides, cookie write + collect/persist helpers in web/src/docs.ts, data-key attributes in the sidebar templ, and the first-section-open fallback (docs home used to force-open the first section — spec'd as closed). toggleCollapsible remains for in-page peeking; purely ephemeral. The [data-collapsed=true] CSS from the previous round stays — it is what makes the server-rendered state actually hide sections at paint (the original bug was that this CSS never existed). Tests: TestBuildNavigationNothingCurrent now asserts zero sections open on unmatched paths; TestBuildNavigation asserts Getting Started closed on a device page (no site-wide pin). make test green; 146 vitests; live verified: home 0 expanded, doc pages exactly 2 (section+group).
This commit is contained in:
1 parent
a8f8c4def9
commit
73e8cdef4b
6 files changed
+155
-272
No files matched your search
+5
-33
@@ -15,39 +15,13 @@ function toggleSidebar(): void {
|
||||
overlay.classList.toggle("hidden");
|
||||
}
|
||||
|
||||
// Docs sidebar collapse persistence. Rendering ownership: the server
|
||||
// renders what is expanded (navigation defaults + docs_nav cookie
|
||||
// overrides merged in internal/docs), so first paint is already correct —
|
||||
// no post-paint correction, no jumping. This module only records intent:
|
||||
// every toggle rewrites the cookie with the full current state.
|
||||
|
||||
const DOCS_NAV_COOKIE = "docs_nav";
|
||||
|
||||
type DocsNavState = Record<string, boolean>;
|
||||
|
||||
// collectDocsNavState reads the current collapsed flag of every
|
||||
// collapsible in the sidebar straight from the DOM.
|
||||
function collectDocsNavState(): DocsNavState {
|
||||
const state: DocsNavState = {};
|
||||
document.querySelectorAll<HTMLElement>("[data-key][data-collapsed]").forEach((el) => {
|
||||
const key = el.getAttribute("data-key");
|
||||
if (key) state[key] = el.getAttribute("data-collapsed") === "true";
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
function persistDocsNavCookie(state: DocsNavState): void {
|
||||
try {
|
||||
// Strictly-necessary UI state: first-party, no identifiers, one year.
|
||||
document.cookie = `${DOCS_NAV_COOKIE}=${encodeURIComponent(JSON.stringify(state))}; path=/; max-age=31536000; SameSite=Lax`;
|
||||
} catch {
|
||||
// Storage blocked: the sidebar still works, state just doesn't persist.
|
||||
}
|
||||
}
|
||||
// Docs sidebar sections toggle open/closed in-page (purely client-local,
|
||||
// ephemeral — the SERVER decides what is expanded on each page load from
|
||||
// navigation defaults, so there is no saved state and nothing to jump).
|
||||
// The data-collapsed attribute is the single source of truth: the
|
||||
// stylesheet hides on it, and the arrow rotates relative to it.
|
||||
|
||||
function applyCollapsed(items: HTMLElement, collapsed: boolean): void {
|
||||
// The data-collapsed attribute is the single source of truth — the
|
||||
// stylesheet hides on it, and the arrow is rotated relative to it.
|
||||
items.setAttribute("data-collapsed", collapsed ? "true" : "false");
|
||||
const arrow = items.previousElementSibling?.querySelector<HTMLElement>(".section-arrow");
|
||||
if (arrow) {
|
||||
@@ -60,9 +34,7 @@ function toggleCollapsible(header: HTMLElement): void {
|
||||
if (!items) return;
|
||||
|
||||
const isCollapsed = items.getAttribute("data-collapsed") === "true";
|
||||
|
||||
applyCollapsed(items, !isCollapsed);
|
||||
persistDocsNavCookie(collectDocsNavState());
|
||||
}
|
||||
|
||||
let docsSearchTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
Reference in new issue
Block a user