diff --git a/config/_default/params.toml b/config/_default/params.toml index 749e0bda..2f7e5404 100644 --- a/config/_default/params.toml +++ b/config/_default/params.toml @@ -151,6 +151,8 @@ breadcrumb = true toc = true sidebar = true + sidebarIcons = false + sidebarIconLevel = 1 size = "md" startLevel = 2 endLevel = 3 diff --git a/layouts/_partials/assets/helpers/sidebar-menu-entry.html b/layouts/_partials/assets/helpers/sidebar-menu-entry.html index ff97e5ff..703db971 100644 --- a/layouts/_partials/assets/helpers/sidebar-menu-entry.html +++ b/layouts/_partials/assets/helpers/sidebar-menu-entry.html @@ -7,8 +7,12 @@ - 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 -}} @@ -16,11 +20,35 @@ {{- $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 @@ -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) -}} diff --git a/layouts/_partials/assets/live-pages.html b/layouts/_partials/assets/live-pages.html index a65b1972..434defba 100644 --- a/layouts/_partials/assets/live-pages.html +++ b/layouts/_partials/assets/live-pages.html @@ -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 -}} diff --git a/layouts/_partials/utilities/GetSidebarIcons.html b/layouts/_partials/utilities/GetSidebarIcons.html new file mode 100644 index 00000000..3c816f74 --- /dev/null +++ b/layouts/_partials/utilities/GetSidebarIcons.html @@ -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) + .Params.sidebarIcons bool, per page + .Params.sidebarIconLevel int, per page + pages..sidebaricons bool, per content type + pages..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.` 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 -}}