Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config/_default/params.toml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@
breadcrumb = true
toc = true
sidebar = true
sidebarIcons = false
sidebarIconLevel = 1
size = "md"
startLevel = 2
endLevel = 3
Expand Down
32 changes: 30 additions & 2 deletions layouts/_partials/assets/helpers/sidebar-menu-entry.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,48 @@
- sortField: Field to sort children by (e.g., "title", "date", "weight")
- reverse: Whether to reverse sort order (default: false)
- seen: Slice of ancestor permalinks in the current walk, used as cycle guard (internal)
- iconLevel: Deepest 1-based level that may show an icon; 0 disables them
(resolved once by the caller via utilities/GetSidebarIcons.html)

Returns: Menu entry dict with title and pages (optional array of nested entries)
Returns: Menu entry dict with title, link, an optional pre (leading icon,
taken from the page's `icon` param), and optional pages (array of nested
entries)
*/}}

{{- $page := .page -}}
{{- $grouped := .grouped -}}
{{- $sortField := .sortField | default "title" -}}
{{- $reverse := .reverse | default false -}}
{{- $seen := .seen | default slice -}}
{{- $iconLevel := .iconLevel | default 0 -}}

{{- /* Create menu entry with title and absolute link */ -}}
{{- /* Include the full RelPermalink; sidebar will recognize leading "/" as absolute */ -}}
{{- $entry := dict "title" $page.LinkTitle "link" $page.RelPermalink -}}

{{- /* Carry the page's icon as "pre", the field the sidebar renderer already
reads for a leading icon on menu-defined entries. Without this a page-derived
sidebar can never show one, however the page is authored.

Gated on depth, and off unless the site opts in: `icon` is authored for a
page's card, so rendering it here unconditionally would put icons into the
sidebars of existing sites that never asked for them - and only on the pages
that happen to set one, which reads as a bug rather than a feature. Icons on
the top level alone is the common case, hence a level rather than a bool.

Levels are counted as the sidebar renders them, not as this partial recurses.
assets/sidebar.html drops the walk's root entry and promotes its children to
the top of the list, so the root is never a visible row; `len $seen` is
therefore already the rendered 1-based level, and level 1 is the first row a
reader sees. Omitted when unset or out of range so the renderer's `with`
guards stay false rather than seeing an empty string. */ -}}
{{- $level := len $seen -}}
{{- if le $level $iconLevel -}}
{{- with $page.Params.icon -}}
{{- $entry = merge $entry (dict "pre" .) -}}
{{- end -}}
{{- end -}}

{{- /* Get children from grouped hierarchy */ -}}
{{- $children := index $grouped $page.RelPermalink | default slice -}}
{{- /* Guard against cycles: the grouped map keys pages by permalink, so any permalink collision
Expand Down Expand Up @@ -57,7 +85,7 @@
{{- /* Recursively build nested menu entries */ -}}
{{- $pages := slice -}}
{{- range $children -}}
{{- $childEntry := partial "assets/helpers/sidebar-menu-entry" (dict "page" . "grouped" $grouped "sortField" $sortField "reverse" $reverse "seen" $seen) -}}
{{- $childEntry := partial "assets/helpers/sidebar-menu-entry" (dict "page" . "grouped" $grouped "sortField" $sortField "reverse" $reverse "seen" $seen "iconLevel" $iconLevel) -}}
{{- $pages = $pages | append $childEntry -}}
{{- end -}}
{{- $entry = merge $entry (dict "pages" $pages) -}}
Expand Down
5 changes: 5 additions & 0 deletions layouts/_partials/assets/live-pages.html
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,17 @@
{{- $pages = $result -}}

{{/* Build menu structure from sorted pages and hierarchy */}}
{{- /* Resolve the icon depth once against the page being rendered,
not against each entry: the cascade is keyed on the current
page's type, while the entries are the pages being listed. */ -}}
{{- $iconLevel := partial "utilities/GetSidebarIcons.html" (or $args.page page) -}}
{{- range $rootPages -}}
{{- $menuEntry := partial "assets/helpers/sidebar-menu-entry" (dict
"page" .
"grouped" $grouped
"sortField" $sortField
"reverse" $reverse
"iconLevel" $iconLevel
) -}}
{{- $menu = $menu | append $menuEntry -}}
{{- end -}}
Expand Down
55 changes: 55 additions & 0 deletions layouts/_partials/utilities/GetSidebarIcons.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{{- /*
Resolve how deep the page-hierarchy sidebar should render entry icons.

Returns the deepest 1-based level that may show an icon, or 0 for none.
Callers compare an entry's level against it, so "off" needs no separate
boolean at the call site.

Resolution mirrors utilities/GetIncludeTOC.html: a site-wide switch, then a
per-page override, then a per-type default. Both the switch and the level
cascade, so a section can enable icons without restating the depth, or
change the depth without restating the switch.

navigation.sidebarIcons bool, site-wide (default false)
navigation.sidebarIconLevel int, deepest level (default 1)
<page>.Params.sidebarIcons bool, per page
<page>.Params.sidebarIconLevel int, per page
pages.<Type>.sidebaricons bool, per content type
pages.<Type>.sidebariconlevel int, per content type

Defaults to off. A page's `icon` param is authored for its card, so
enabling this by default would put icons in the sidebars of existing sites
that never asked for them - and only on the pages that happen to set one,
which looks like a bug rather than a feature.

Hugo lowercases the keys of the `pages.<Type>` maps, hence `sidebaricons`
and `sidebariconlevel` there but camelCase everywhere else.
*/ -}}

{{- $enabled := site.Params.navigation.sidebarIcons | default false -}}
{{- $level := site.Params.navigation.sidebarIconLevel | default 1 -}}

{{- $typeDefaults := index site.Params.pages .Type -}}
{{- with $typeDefaults -}}
{{- if isset . "sidebaricons" -}}
{{- $enabled = index . "sidebaricons" -}}
{{- if ne (printf "%T" $enabled) "bool" -}}
{{- errorf "Expected bool value in site parameters: pages.%s.sidebarIcons" $.Type -}}
{{- end -}}
{{- end -}}
{{- if isset . "sidebariconlevel" -}}
{{- $level = index . "sidebariconlevel" -}}
{{- end -}}
{{- end -}}

{{- if isset .Params "sidebaricons" -}}
{{- $enabled = .Params.sidebaricons -}}
{{- if ne (printf "%T" $enabled) "bool" -}}
{{- errorf "Expected bool value in page parameters: sidebarIcons (%s)" .RelPermalink -}}
{{- end -}}
{{- end -}}
{{- if isset .Params "sidebariconlevel" -}}
{{- $level = .Params.sidebariconlevel -}}
{{- end -}}

{{- return cond $enabled (int $level) 0 -}}