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
+20
-51
@@ -178,8 +178,8 @@ func docURL(docPath string) string {
|
||||
|
||||
// BuildNavigation creates the category → subcategory navigation from the
|
||||
// docs filesystem. The section and group containing currentPath render
|
||||
// expanded; everything else starts collapsed (with Getting Started as the
|
||||
// fallback when nothing matches).
|
||||
// expanded; everything else starts collapsed. When nothing matches (docs
|
||||
// home, 404), the sidebar renders fully closed — headers only.
|
||||
func (h *DocsHandler) BuildNavigation(currentPath string) *templates.Navigation {
|
||||
docs, err := h.ListDocuments()
|
||||
if err != nil {
|
||||
@@ -198,16 +198,17 @@ func (h *DocsHandler) BuildNavigation(currentPath string) *templates.Navigation
|
||||
}
|
||||
|
||||
type groupBuild struct {
|
||||
items []templates.NavItem
|
||||
urls []string
|
||||
overview bool // has a pinned index page
|
||||
items []templates.NavItem
|
||||
urls []string
|
||||
// overview: the group leads with its "#"Overview landing doc (pinned
|
||||
// subdirectory index), so it sorts first.
|
||||
overview bool
|
||||
}
|
||||
type sectionBuild struct {
|
||||
items []templates.NavItem
|
||||
urls []string
|
||||
pinned map[string]bool // landing pages lead their section
|
||||
groups map[string]*groupBuild
|
||||
overview bool
|
||||
items []templates.NavItem
|
||||
urls []string
|
||||
pinned map[string]bool // landing pages lead their section
|
||||
groups map[string]*groupBuild
|
||||
}
|
||||
|
||||
builds := make(map[string]*sectionBuild, len(sectionOrder))
|
||||
@@ -231,10 +232,6 @@ func (h *DocsHandler) BuildNavigation(currentPath string) *templates.Navigation
|
||||
if place.pin < 0 {
|
||||
build.pinned[item.URL] = true
|
||||
}
|
||||
if place.pin == -100 {
|
||||
// Only the root Overview forces its section open.
|
||||
build.overview = true
|
||||
}
|
||||
} else {
|
||||
group, ok := build.groups[place.group]
|
||||
if !ok {
|
||||
@@ -301,52 +298,24 @@ func (h *DocsHandler) BuildNavigation(currentPath string) *templates.Navigation
|
||||
}
|
||||
}
|
||||
}
|
||||
if build.overview {
|
||||
section.Collapsed = false
|
||||
}
|
||||
nav.Sections = append(nav.Sections, section)
|
||||
}
|
||||
|
||||
// Nothing matches the current page (404, search landing): open the
|
||||
// first section so the sidebar doesn't render fully closed.
|
||||
anyOpen := false
|
||||
for _, s := range nav.Sections {
|
||||
if !s.Collapsed {
|
||||
anyOpen = true
|
||||
break
|
||||
// No default expansion when nothing matches the current page (404,
|
||||
// search landing): the sidebar renders fully closed. Same for the docs
|
||||
// HOME: although the root overview doc lives there (its contains-URL
|
||||
// rule would open "Getting Started"), the spec is "home = all closed,
|
||||
// headers only" — the standard docs-site behavior.
|
||||
if strings.TrimSuffix(currentPath, "/") == "/docs" {
|
||||
for si := range nav.Sections {
|
||||
nav.Sections[si].Collapsed = true
|
||||
}
|
||||
}
|
||||
if !anyOpen && len(nav.Sections) > 0 {
|
||||
nav.Sections[0].Collapsed = false
|
||||
return nav
|
||||
}
|
||||
|
||||
return nav
|
||||
}
|
||||
|
||||
// ApplyNavOverrides merges the browser-saved collapse state (docs_nav
|
||||
// cookie) into the nav so the sidebar first-paints in the visitor's chosen
|
||||
// state — no post-paint JS correction, no jumping. Keys mirror the
|
||||
// data-key attributes DocsSidebar renders: section titles, and
|
||||
// "Section / Group" for groups. Entries for unknown keys (renamed or
|
||||
// removed sections) are ignored; keys absent from the map keep the
|
||||
// path-derived default.
|
||||
func ApplyNavOverrides(nav *templates.Navigation, overrides map[string]bool) {
|
||||
if len(overrides) == 0 {
|
||||
return
|
||||
}
|
||||
for si := range nav.Sections {
|
||||
s := &nav.Sections[si]
|
||||
if v, ok := overrides[s.Title]; ok {
|
||||
s.Collapsed = v
|
||||
}
|
||||
for gi := range s.Groups {
|
||||
if v, ok := overrides[s.Title+" / "+s.Groups[gi].Title]; ok {
|
||||
s.Groups[gi].Collapsed = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// containsURL reports whether currentPath is one of the given doc URLs.
|
||||
func containsURL(currentPath string, urls []string) bool {
|
||||
for _, u := range urls {
|
||||
|
||||
Reference in new issue
Block a user