diff --git a/data/structures/example.yml b/data/structures/example.yml index c546eb56..2bb2aaf2 100644 --- a/data/structures/example.yml +++ b/data/structures/example.yml @@ -14,14 +14,12 @@ arguments: show_markup: type: bool optional: true - default: true comment: Indicates if the markup should be output in the HTML. deprecated: v1.0.0 alternative: show-markup show_preview: type: bool optional: true - default: true comment: Indicates if the preview should be output in the HTML. deprecated: v1.0.0 alternative: show-preview diff --git a/data/structures/live-pages.yml b/data/structures/live-pages.yml index 4a9712b0..96d8ae8b 100644 --- a/data/structures/live-pages.yml +++ b/data/structures/live-pages.yml @@ -73,4 +73,19 @@ arguments: optional: true default: true comment: >- - If set, returns the page collection in descending order. \ No newline at end of file + If set, returns the page collection in descending order. + group-by: + type: select + optional: true + comment: + If set, groups the results by the specified field. Currently only supports + grouping by section. + values: + - section + release: v2.0.0 + include-list: + type: bool + optional: true + comment: + If set, includes list pages in the results. + release: v2.0.0 \ No newline at end of file diff --git a/data/structures/sidebar.yml b/data/structures/sidebar.yml index 4f037b07..c77d76be 100644 --- a/data/structures/sidebar.yml +++ b/data/structures/sidebar.yml @@ -22,3 +22,22 @@ arguments: comment: >- Version of the sidebar navigation, used to define the base URL of generated links, together with the page's section. + auto-generate: + type: bool + optional: true + default: false + comment: >- + Flag to auto-generate the content of the sidebar. When set, the sidebar + lists all pages of the current section. Use `nested`, `reverse`, and + `sort` arguments to refine the behavior. + release: v2.0.0 + nested: + default: true + release: v2.0.0 + reverse: + default: false + release: v2.0.0 + sort: + optional: true + default: weight + release: v2.0.0 diff --git a/exampleSite/hugo_stats.json b/exampleSite/hugo_stats.json index 49f77ac2..b598d9bb 100644 --- a/exampleSite/hugo_stats.json +++ b/exampleSite/hugo_stats.json @@ -260,6 +260,9 @@ "fa-activity", "fa-address-card", "fa-angle-left", + "fa-angle-right", + "fa-angles-left", + "fa-angles-right", "fa-arrow-left", "fa-arrow-right", "fa-bars", @@ -379,8 +382,6 @@ "justify-content-between", "justify-content-center", "justify-content-end", - "justify-content-md-end", - "justify-content-md-start", "justify-content-start", "katex", "label", @@ -438,7 +439,6 @@ "multi-docs-collapse-2", "multi-file-collapse-1", "mx-auto", - "mx-md-0", "mx-md-2", "mx-md-n4", "my-2", @@ -583,6 +583,7 @@ "testimonial-img", "testimonial-logo", "testimonials", + "text-%!s()", "text-bg-body-tertiary", "text-bg-info", "text-bg-light", @@ -597,8 +598,6 @@ "text-decoration-none", "text-end", "text-info", - "text-md-center", - "text-md-end", "text-muted", "text-nowrap", "text-primary", @@ -735,11 +734,11 @@ "docs", "documentation", "dropdown-nav-0", - "dropdown-panel-0efae4d89ba1b34507425ff79e031ff6", - "dropdown-panel-47d1c4c5b870c52880f4d5431bfc8161", - "dropdown-panel-4ece72da2651b717239ac56d5426dd5a", - "dropdown-panel-82d63ff336d6d57ed4f0ffdd17f8267a", - "dropdown-panel-ecea5b8e7ce122a138e33bb6508e82ce", + "dropdown-panel-529c583cb632a4d4aa340c3144559397", + "dropdown-panel-ac14004c1d019760fbd77bae3d8cee5a", + "dropdown-panel-b791b28f1a6830b342ecf4e2cea555f1", + "dropdown-panel-ce74586edf42e3fd5170469a7a08bb5f", + "dropdown-panel-ead6ac83a313db58e6992fc381ae8e92", "eerste-artikel", "elements-type", "entity-relationship-diagram", @@ -762,11 +761,11 @@ "fab-whatsapp", "fab-x-twitter", "faq", - "faq-e61149242eef48ba8815560659885ee3", - "faq-e61149242eef48ba8815560659885ee3-heading-faq-e61149242eef48ba8815560659885ee3", - "faq-e61149242eef48ba8815560659885ee3-item-0", - "faq-e61149242eef48ba8815560659885ee3-item-1", - "faq-e61149242eef48ba8815560659885ee3-item-2", + "faq-97e0b2226faa254e6ce7099907045446", + "faq-97e0b2226faa254e6ce7099907045446-heading-faq-97e0b2226faa254e6ce7099907045446", + "faq-97e0b2226faa254e6ce7099907045446-item-0", + "faq-97e0b2226faa254e6ce7099907045446-item-1", + "faq-97e0b2226faa254e6ce7099907045446-item-2", "fas-1", "fas-2", "fas-3", @@ -775,6 +774,9 @@ "fas-6", "fas-address-card", "fas-angle-left", + "fas-angle-right", + "fas-angles-left", + "fas-angles-right", "fas-arrow-left", "fas-arrow-right", "fas-bars", @@ -881,11 +883,11 @@ "nav-0-btn-1", "nav-0-btn-2", "nav-nav-0", - "nav-panel-0efae4d89ba1b34507425ff79e031ff6", - "nav-panel-47d1c4c5b870c52880f4d5431bfc8161", - "nav-panel-4ece72da2651b717239ac56d5426dd5a", - "nav-panel-82d63ff336d6d57ed4f0ffdd17f8267a", - "nav-panel-ecea5b8e7ce122a138e33bb6508e82ce", + "nav-panel-529c583cb632a4d4aa340c3144559397", + "nav-panel-ac14004c1d019760fbd77bae3d8cee5a", + "nav-panel-b791b28f1a6830b342ecf4e2cea555f1", + "nav-panel-ce74586edf42e3fd5170469a7a08bb5f", + "nav-panel-ead6ac83a313db58e6992fc381ae8e92", "navbar", "navbar-0-collapse", "navbar-mode", @@ -895,36 +897,36 @@ "notification", "over-mij", "overview", - "panel-0efae4d89ba1b34507425ff79e031ff6-0", - "panel-0efae4d89ba1b34507425ff79e031ff6-1", - "panel-0efae4d89ba1b34507425ff79e031ff6-2", - "panel-0efae4d89ba1b34507425ff79e031ff6-btn-0", - "panel-0efae4d89ba1b34507425ff79e031ff6-btn-1", - "panel-0efae4d89ba1b34507425ff79e031ff6-btn-2", - "panel-47d1c4c5b870c52880f4d5431bfc8161-0", - "panel-47d1c4c5b870c52880f4d5431bfc8161-1", - "panel-47d1c4c5b870c52880f4d5431bfc8161-2", - "panel-47d1c4c5b870c52880f4d5431bfc8161-btn-0", - "panel-47d1c4c5b870c52880f4d5431bfc8161-btn-1", - "panel-47d1c4c5b870c52880f4d5431bfc8161-btn-2", - "panel-4ece72da2651b717239ac56d5426dd5a-0", - "panel-4ece72da2651b717239ac56d5426dd5a-1", - "panel-4ece72da2651b717239ac56d5426dd5a-2", - "panel-4ece72da2651b717239ac56d5426dd5a-btn-0", - "panel-4ece72da2651b717239ac56d5426dd5a-btn-1", - "panel-4ece72da2651b717239ac56d5426dd5a-btn-2", - "panel-82d63ff336d6d57ed4f0ffdd17f8267a-0", - "panel-82d63ff336d6d57ed4f0ffdd17f8267a-1", - "panel-82d63ff336d6d57ed4f0ffdd17f8267a-2", - "panel-82d63ff336d6d57ed4f0ffdd17f8267a-btn-0", - "panel-82d63ff336d6d57ed4f0ffdd17f8267a-btn-1", - "panel-82d63ff336d6d57ed4f0ffdd17f8267a-btn-2", - "panel-ecea5b8e7ce122a138e33bb6508e82ce-0", - "panel-ecea5b8e7ce122a138e33bb6508e82ce-1", - "panel-ecea5b8e7ce122a138e33bb6508e82ce-2", - "panel-ecea5b8e7ce122a138e33bb6508e82ce-btn-0", - "panel-ecea5b8e7ce122a138e33bb6508e82ce-btn-1", - "panel-ecea5b8e7ce122a138e33bb6508e82ce-btn-2", + "panel-529c583cb632a4d4aa340c3144559397-0", + "panel-529c583cb632a4d4aa340c3144559397-1", + "panel-529c583cb632a4d4aa340c3144559397-2", + "panel-529c583cb632a4d4aa340c3144559397-btn-0", + "panel-529c583cb632a4d4aa340c3144559397-btn-1", + "panel-529c583cb632a4d4aa340c3144559397-btn-2", + "panel-ac14004c1d019760fbd77bae3d8cee5a-0", + "panel-ac14004c1d019760fbd77bae3d8cee5a-1", + "panel-ac14004c1d019760fbd77bae3d8cee5a-2", + "panel-ac14004c1d019760fbd77bae3d8cee5a-btn-0", + "panel-ac14004c1d019760fbd77bae3d8cee5a-btn-1", + "panel-ac14004c1d019760fbd77bae3d8cee5a-btn-2", + "panel-b791b28f1a6830b342ecf4e2cea555f1-0", + "panel-b791b28f1a6830b342ecf4e2cea555f1-1", + "panel-b791b28f1a6830b342ecf4e2cea555f1-2", + "panel-b791b28f1a6830b342ecf4e2cea555f1-btn-0", + "panel-b791b28f1a6830b342ecf4e2cea555f1-btn-1", + "panel-b791b28f1a6830b342ecf4e2cea555f1-btn-2", + "panel-ce74586edf42e3fd5170469a7a08bb5f-0", + "panel-ce74586edf42e3fd5170469a7a08bb5f-1", + "panel-ce74586edf42e3fd5170469a7a08bb5f-2", + "panel-ce74586edf42e3fd5170469a7a08bb5f-btn-0", + "panel-ce74586edf42e3fd5170469a7a08bb5f-btn-1", + "panel-ce74586edf42e3fd5170469a7a08bb5f-btn-2", + "panel-ead6ac83a313db58e6992fc381ae8e92-0", + "panel-ead6ac83a313db58e6992fc381ae8e92-1", + "panel-ead6ac83a313db58e6992fc381ae8e92-2", + "panel-ead6ac83a313db58e6992fc381ae8e92-btn-0", + "panel-ead6ac83a313db58e6992fc381ae8e92-btn-1", + "panel-ead6ac83a313db58e6992fc381ae8e92-btn-2", "panels", "persona", "pie-chart", @@ -963,7 +965,7 @@ "tabs", "team", "testimonial", - "testimonial-carousel-93eb220a6fef19ea1a16ae43ac24bcd3", + "testimonial-carousel-1b351213617c94546d1f4b98565c71b7", "testimonials", "testimonials-type", "third-party-links--use-of-your-information", diff --git a/layouts/_markup/render-link.html b/layouts/_markup/render-link.html index cd5ef1a0..d757997f 100644 --- a/layouts/_markup/render-link.html +++ b/layouts/_markup/render-link.html @@ -9,7 +9,7 @@ {{ partial "assets/link.html" (dict "href" .Destination "page" .Page - "text" .Text - "title" .Title + "text" (.Text | htmlUnescape) + "title" (.Title | htmlUnescape) ) }} {{- end }} \ No newline at end of file diff --git a/layouts/_partials/assets/live-pages.html b/layouts/_partials/assets/live-pages.html index 7f71f447..abef7cb4 100644 --- a/layouts/_partials/assets/live-pages.html +++ b/layouts/_partials/assets/live-pages.html @@ -1,4 +1,4 @@ - @@ -17,6 +17,7 @@ {{/* Initialize local arguments */}} {{ $pages := slice }} {{ $total := 0 }} +{{ $menu := slice }} {{/* Main code */}} {{ if not $args.err }} @@ -76,13 +77,104 @@ {{ $pages = where $pages "Params.categories" "intersect" $categories }} {{ end }} {{ end }} - - {{/* Sort the pages as specified */}} + + {{/* Include the list pages if requested */}} + {{- $lists := slice -}} + {{ if or $args.includeList (eq $args.groupBy "section") }} + {{ range $pages }} + {{ $lists = $lists | append .Parent | uniq }} + {{ end }} + {{ $pages = $pages | append $lists }} + {{ end }} + + {{/* Sort the pages as specified */}} + {{- $sortField := printf "Params.%s" $args.sort -}} {{- $order := "asc" -}} {{- if $args.reverse }}{{ $order = "desc" }}{{ end -}} - {{- $pages = sort $pages (printf "Params.%s" $args.sort) $order -}} + + {{ if eq $args.groupBy "section" }} + {{- /* Build a hierarchical map: parent path -> slice of children */ -}} + {{- $grouped := dict -}} + {{- $rootPages := slice -}} + + {{- range $page := $pages -}} + {{- if $page.Parent -}} + {{- $parentPath := $page.Parent.RelPermalink -}} + {{- $childrenSlice := index $grouped $parentPath | default slice -}} + {{- $grouped = merge $grouped (dict $parentPath ($childrenSlice | append $page)) -}} + {{- else -}} + {{- $rootPages = $rootPages | append $page -}} + {{- end -}} + {{- end -}} + + {{- /* If a section was specified, use that section page as the root */ -}} + {{- if $args.section -}} + {{- $sectionPage := site.GetPage $args.section -}} + {{- if $sectionPage -}} + {{- $rootPages = slice | append $sectionPage -}} + {{- end -}} + {{- end -}} + + {{- /* Sort root pages first */ -}} + {{- $rootPages = sort $rootPages $sortField $order -}} + + {{- /* Process depth-first (pre-order): use a stack to traverse */ -}} + {{- $result := slice -}} + {{- $stack := slice -}} + + {{- /* Initialize stack with root pages: always push in reverse for LIFO to pop in correct order */ -}} + {{- if $rootPages -}} + {{- range $i := seq (len $rootPages) -}} + {{- $idx := sub (len $rootPages) $i -}} + {{- $page := index $rootPages $idx -}} + {{- $stack = $stack | append $page -}} + {{- end -}} + {{- end -}} + + {{- range $iteration := seq 1 1000 -}} + {{- /* Exit if stack is empty */ -}} + {{- if not $stack -}} + {{- break -}} + {{- end -}} + + {{- /* Pop from stack */ -}} + {{- $stackSize := len $stack -}} + {{- $lastIdx := sub $stackSize 1 -}} + {{- $current := index $stack $lastIdx -}} + {{- $stack = $stack | first $lastIdx -}} + + {{- /* Add current page to result */ -}} + {{- $result = $result | append $current -}} + + {{- /* Get and sort children */ -}} + {{- $pageKey := $current.RelPermalink -}} + {{- $children := index $grouped $pageKey | default slice -}} + {{- if $children -}} + {{- $children = sort $children $sortField $order -}} + {{- /* Push children to stack in reverse so LIFO pops in correct order */ -}} + {{- range $i := seq (len $children) -}} + {{- $idx := sub (len $children) $i -}} + {{- $child := index $children $idx -}} + {{- $stack = $stack | append $child -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- $pages = $result -}} + + {{/* Build menu structure from sorted pages and hierarchy */}} + {{- range $rootPages -}} + {{- $menuEntry := partial "assets/helpers/buildMenuEntry" (dict "page" . "grouped" $grouped) -}} + {{- $menu = $menu | append $menuEntry -}} + {{- end -}} + {{ else }} + {{- /* Use regular sort */ -}} + {{- $pages = sort $pages $sortField $order -}} + {{- /* For non-grouped sorts, use pages directly as menu */ -}} + {{- $menu = $pages | default slice -}} + {{ end }} {{ end }} {{ end }} {{ end }} -{{ return (dict "pages" $pages "total" $total) }} \ No newline at end of file +{{ return (dict "pages" $pages "menu" $menu "total" $total) }} \ No newline at end of file diff --git a/layouts/_partials/assets/sidebar.html b/layouts/_partials/assets/sidebar.html index 237b1830..23842776 100644 --- a/layouts/_partials/assets/sidebar.html +++ b/layouts/_partials/assets/sidebar.html @@ -1,4 +1,4 @@ -{{/* +{{/* Copyright © 2022 - 2025 The Hinode Team / Mark Dumay. All rights reserved. Use of this source code is governed by The MIT License (MIT) that can be found in the LICENSE file. Visit gethinode.com/license for more details. @@ -7,8 +7,8 @@ {{/* Initialize arguments */}} {{ $args := partial "utilities/InitArgs.html" (dict "structure" "sidebar" "args" . "group" "partial") }} {{ if or $args.err $args.warnmsg }} - {{ partial (cond $args.err "utilities/LogErr.html" "utilities/LogWarn.html") (dict - "partial" "assets/sidebar.html" + {{ partial (cond $args.err "utilities/LogErr.html" "utilities/LogWarn.html") (dict + "partial" "assets/sidebar.html" "warnid" "warn-invalid-arguments" "msg" "Invalid arguments" "details" ($args.errmsg | append $args.warnmsg) @@ -19,6 +19,26 @@ {{/* Initialize local arguments */}} {{- $section := $args.page.Section }} +{{/* Auto-generate hierarchical menu if enabled and no menu provided */}} +{{- if and $args.autoGenerate (not $args.menu) -}} + {{- $result := partial "assets/live-pages.html" (dict + "page" $args.page + "section" $section + "nested" $args.nested + "sort" "weight" + "reverse" "false" + "group-by" "section" + "include-list" true + ) -}} + {{- /* Skip root section page and only show its children in sidebar */ -}} + {{- $menu := $result.menu -}} + {{- if and (gt (len $menu) 0) (index $menu 0).pages -}} + {{- /* Root item has children - use only children, skipping the root */ -}} + {{- $menu = (index $menu 0).pages -}} + {{- end -}} + {{- $args = merge $args (dict "menu" $menu) -}} +{{- end -}} + {{- define "_partials/inline/sidebar/group.html" -}} {{- $page := .page -}} {{- $index := .index -}} @@ -29,6 +49,13 @@ {{- $doc_slug := partial "utilities/URLJoin.html" (dict "base" $baseURL "path" ($group.title | urlize)) -}} {{- $href := or $group.link $doc_slug -}} + {{- /* Handle absolute paths from hierarchical menu (starting with /) */ -}} + {{- if and $group.link (hasPrefix $group.link "/") -}} + {{- $href = $group.link -}} + {{- else if $group.link -}} + {{- /* Non-absolute link provided - use URLJoin */ -}} + {{- $href = partial "utilities/URLJoin.html" (dict "base" $baseURL "path" $group.link) -}} + {{- end -}} {{ $ref := partial "utilities/GetPage.html" (dict "url" $href "page" $page) }} {{ if eq $group.link "#" }}{{ $href = $doc_slug }}{{ end }} {{- $collapsed := strings.HasPrefix $page.RelPermalink $href -}} @@ -37,24 +64,24 @@