hugo: render site navigation using sections

Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
This commit is contained in:
David Karlsson 2024-09-03 10:59:47 +02:00
parent 135870fc13
commit c36309ed8a
11 changed files with 82 additions and 2599 deletions

View File

@ -24,9 +24,12 @@ The website is built using [Hugo](https://gohugo.io/). The content is primarily
Markdown files in the `/content` directory of this repository (with a few Markdown files in the `/content` directory of this repository (with a few
exceptions, see [Content not edited here](#content-not-edited-here)). exceptions, see [Content not edited here](#content-not-edited-here)).
The structure of the sidebar navigation on the site is defined in The structure of the sidebar navigation on the site is defined by the site's
[`/data/toc.yaml`](./data/toc.yaml). To rename or change the location of a page section hierarchy in the `contents` directory. The titles of the pages are
in the left-hand navigation, edit the `toc.yaml` file. defined in the front matter of the Markdown files. You can use `title` and
`linkTitle` to define the title of the page. `title` is used for the page
title, and `linkTitle` is used for the sidebar title. If `linkTitle` is not
defined, the `title` is used for both.
You must fork this repository to create a pull request to propose changes. For more details, see [Local setup](#local-setup). You must fork this repository to create a pull request to propose changes. For more details, see [Local setup](#local-setup).

View File

@ -31,14 +31,6 @@
@apply dark:hue-rotate-180 dark:invert dark:filter; @apply dark:hue-rotate-180 dark:invert dark:filter;
} }
.sidebar-hover {
@apply hover:bg-gray-light-200 hover:dark:bg-gray-dark-200;
}
.sidebar-underline {
@apply underline decoration-blue-light decoration-4 underline-offset-4 dark:decoration-blue-dark;
}
.bg-pattern-blue { .bg-pattern-blue {
background-color: theme(colors.white / 50%); background-color: theme(colors.white / 50%);
background-image: url('/assets/images/bg-pattern-blue.webp'); background-image: url('/assets/images/bg-pattern-blue.webp');

View File

@ -1,14 +0,0 @@
function toggleMenuItem(event) {
const section = event.currentTarget.parentElement;
const icons = event.currentTarget.querySelectorAll(".icon-svg");
const subsection = section.querySelector("ul");
subsection.classList.toggle("hidden");
icons.forEach(i => i.classList.toggle('hidden'))
}
const sectiontree = document.querySelector("#sectiontree");
if (sectiontree) {
for (const button of sectiontree.querySelectorAll("button")) {
button.addEventListener("click", toggleMenuItem);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -120,19 +120,19 @@ params:
menus: menus:
main: main:
- name: Get started - name: Get started
url: /get-started/ pageRef: /get-started/
weight: 1 weight: 1
- name: Guides - name: Guides
url: /guides/ pageRef: /guides/
weight: 2 weight: 2
- name: Manuals - name: Manuals
url: /manuals/ pageRef: /manuals/
weight: 3 weight: 3
- name: Reference - name: Reference
url: /reference/ pageRef: /reference/
weight: 4 weight: 4
- name: Learning paths - name: Learning paths
url: /learning-paths/ pageRef: /learning-paths/
weight: 5 weight: 5
footer: footer:

View File

@ -307,12 +307,16 @@
"highlight", "highlight",
"hover:bg-blue-light-400", "hover:bg-blue-light-400",
"hover:bg-gray-light-100", "hover:bg-gray-light-100",
"hover:bg-gray-light-300",
"hover:border-gray-light-200", "hover:border-gray-light-200",
"hover:dark:bg-gray-dark-300",
"hover:dark:border-gray-dark", "hover:dark:border-gray-dark",
"hover:dark:text-blue-dark",
"hover:drop-shadow-lg", "hover:drop-shadow-lg",
"hover:opacity-75", "hover:opacity-75",
"hover:opacity-90", "hover:opacity-90",
"hover:text-black", "hover:text-black",
"hover:text-blue-light",
"hover:underline", "hover:underline",
"hub-api", "hub-api",
"icon-lg", "icon-lg",
@ -451,9 +455,9 @@
"scale-75", "scale-75",
"scroll-mt-20", "scroll-mt-20",
"scroll-mt-36", "scroll-mt-36",
"select-none",
"self-center", "self-center",
"shadow", "shadow",
"sidebar-hover",
"sm:flex-row", "sm:flex-row",
"sm:grid-cols-2", "sm:grid-cols-2",
"sm:items-center", "sm:items-center",

View File

@ -1,18 +1,7 @@
{{ $scratch := partialCached "utils/tocparser.html" . . }}
{{ $ctx := . }}
<nav id="breadcrumbs" data-pagefind-ignore class="py-4 gap-4 flex items-center text-gray-light dark:text-gray-dark max-w-full min-w-0"> <nav id="breadcrumbs" data-pagefind-ignore class="py-4 gap-4 flex items-center text-gray-light dark:text-gray-dark max-w-full min-w-0">
{{ with ($scratch.GetSortedMapValues "sections") }} {{ range .Ancestors.Reverse }}
{{ range $i, $e := . }} <a href="{{ .Permalink }}" class="link truncate">{{ markdownify .LinkTitle }}</a>
{{- if $i -}}
<span>/</span> <span>/</span>
{{- end -}} {{- end }}
<a href="{{ $e.path }}" class="link truncate">{{ markdownify $e.title }}</a> <span class="truncate">{{ markdownify .LinkTitle }}</span>
{{- end -}}
{{- end -}}
{{- with $scratch.Get "lastsection" -}}
<span>/</span>
<span class="truncate">{{ markdownify .title }}</span>
{{- end -}}
</nav> </nav>

View File

@ -1,74 +1,78 @@
{{/* Parse toc.yaml and store the resulting map to $scratch */}}
{{ $scratch := partialCached "utils/tocparser.html" . . }}
{{ $ctx := . }}
{{/* Get the name of the first section */}}
{{ $firstSection := (index ($scratch.GetSortedMapValues "sections") 0).title }}
{{/* Render the top-nav in sidebar for small screens */}} {{/* Render the top-nav in sidebar for small screens */}}
<nav class="text-sm pb-4 gap-4 flex md:hidden flex-col justify-evenly"> <nav class="text-sm pb-4 gap-4 flex md:hidden flex-col justify-evenly">
<div class="text-gray-light dark:text-gray-dark">Main sections</div> <div class="text-gray-light dark:text-gray-dark">Main sections</div>
{{ range site.Menus.main }} {{ range site.Menus.main }}
<div class="pl-2 underline-offset-8 decoration-2 hover:underline decoration-blue-light dark:decoration-blue-dark hover:opacity-75 <div class="pl-2 underline-offset-8 decoration-2 hover:underline decoration-blue-light dark:decoration-blue-dark hover:opacity-75
{{- if eq $firstSection .Name }} {{- if or (page.IsDescendant .Page) (eq page .Page) }}
underline underline
{{- end }}"> {{- end }}">
<a href="{{ .URL }}">{{ .Name }}</a> <a href="{{ .URL }}">{{ .Name }}</a>
</div> </div>
{{ end }} {{ end }}
</nav> </nav>
{{ if $firstSection }} <nav class="text-sm flex flex-col">
{{ $allSections := slice }}
{{ range $i, $e := ($scratch.GetSortedMapValues "sections") }}
{{ $allSections = $allSections | append (index $e "title") }}
{{ end }}
<nav id="sectiontree" class="text-sm flex flex-col">
<div class="block py-4 md:hidden text-gray-light dark:text-gray-dark">This section</div> <div class="block py-4 md:hidden text-gray-light dark:text-gray-dark">This section</div>
{{/* The current page is in the table of contents */}} {{/* The current page is in the table of contents */}}
<ul> <ul>
{{/* Walk the toc.yaml nodes under the current main section */}} {{ template "renderSingle" .FirstSection }}
{{ range (index site.Data.toc $firstSection) }} {{ template "renderChildren" .FirstSection }}
{{ template "tocRender" (dict "ctx" $ctx "entry" . "sections" $allSections) }}
{{ end }}
</ul> </ul>
</nav> </nav>
{{ define "renderChildren" }}
{{- range .Pages }}
{{- if eq .Params.sitemap false }}
{{- continue }}
{{- end }}
{{- if .IsSection }}
{{- template "renderList" . }}
{{- else }}
{{- template "renderSingle" . }}
{{- end }}
{{- end }}
{{ end }} {{ end }}
{{/* Recursive template for sidebar items */}} {{/* Recursive template for sidebar items */}}
{{ define "tocRender" }} {{ define "renderList" }}
{{ $ctx := .ctx }} {{ $isCurrent := eq page . }}
{{ $sections := .sections }} {{ $expanded := or $isCurrent (page.IsDescendant .) }}
{{ if .entry.sectiontitle }} <li x-data="{ expanded: {{$expanded}} }">
{{ $expanded := in $sections .entry.sectiontitle }} <div class="rounded px-4 w-full flex items-center justify-between{{ if $isCurrent }} bg-gray-light-200 dark:bg-gray-dark-200{{ end }}">
{{/* .entry is a section */}}
<li>
{{/* See event handler in assets/js/src/sidebar.js */}}
<button class="rounded px-4 sidebar-hover w-full flex items-center justify-between">
<span class="py-2 truncate flex items-center gap-2"> <span class="py-2 truncate flex items-center gap-2">
{{ markdownify .entry.sectiontitle }} {{- if .Permalink }}
{{/* If the link is not empty, use it */}}
<a class="select-none hover:text-blue-light hover:dark:text-blue-dark"
href="{{ .Permalink }}">{{ markdownify .LinkTitle }}</a>
{{- else }}
{{/* Otherwise, just expand the section */}}
<button @click="expanded = !expanded"
class="select-none hover:text-blue-light hover:dark:text-blue-dark">
{{ markdownify .LinkTitle }}
</button>
{{- end }}
</span> </span>
<span class="icon-svg {{ if $expanded }}hidden{{ end }}"> <button @click="expanded = !expanded" class="hover:bg-gray-light-300 hover:dark:bg-gray-dark-300 rounded">
<span :class="{ 'hidden' : expanded }" class="icon-svg {{ if $expanded }}hidden{{ end }}">
{{ partialCached "icon" "expand_more" "expand_more" }} {{ partialCached "icon" "expand_more" "expand_more" }}
</span> </span>
<span class="icon-svg {{ if not $expanded }}hidden{{ end }}"> <span :class="{ 'hidden' : !expanded }" class="icon-svg {{ if not $expanded }}hidden{{ end }}">
{{ partialCached "icon" "expand_less" "expand_less" }} {{ partialCached "icon" "expand_less" "expand_less" }}
</span> </span>
</button> </button>
<ul class="{{if not $expanded}}hidden {{end}}ml-3"> </div>
{{ range .entry.section }} <ul :class="{ 'hidden' : !expanded }" class="{{if not $expanded}}hidden {{end}}ml-3">
{{ template "tocRender" (dict "entry" . "ctx" $ctx "sections" $sections ) }} {{ template "renderChildren" . }}
{{ end }}
</ul> </ul>
</li> </li>
{{ else }} {{ end }}
{{/* .entry is a page */}}
{{ $isCurrent := eq (urls.Parse $ctx.Permalink).Path .entry.path }} {{ define "renderSingle" }}
<li class="pl-4 sidebar-hover rounded {{ $isCurrent := eq page . }}
<li class="pl-4 hover:text-blue-light hover:dark:text-blue-dark
{{ if $isCurrent }} bg-gray-light-200 dark:bg-gray-dark-200{{ end }}"> {{ if $isCurrent }} bg-gray-light-200 dark:bg-gray-dark-200{{ end }}">
<a {{ if $isCurrent }}aria-current="page" {{ end }} class="py-2 w-full truncate block" <a {{ if $isCurrent }}aria-current="page" {{ end }} class="py-2 w-full truncate block"
href="{{ .entry.path }}" title="{{ markdownify .entry.title }}" href="{{ .Permalink }}" title="{{ markdownify .Title }}"
><span class="flex items-center gap-2">{{ markdownify .entry.title }}</span> ><span class="flex items-center gap-2">{{ markdownify .LinkTitle }}</span>
</a> </a>
</li> </li>
{{ end }} {{ end }}
{{ end }}

View File

@ -1,10 +1,8 @@
{{ $scratch := partialCached "utils/tocparser.html" . . }}
{{ $firstSection := (index ($scratch.GetSortedMapValues "sections") 0).title }}
<div> <div>
<nav> <nav>
<ul class="mt-1 box-content hidden gap-4 md:flex"> <ul class="mt-1 box-content hidden gap-4 md:flex">
{{ range site.Menus.main }} {{ range site.Menus.main }}
<li {{- if or (eq $firstSection .Name) }} class="border-b-4" {{- end }}> <li {{- if or (eq page .Page) (page.IsDescendant .Page) }} class="border-b-4" {{- end }}>
<a class="block px-2 py-1" href="{{ .URL }}">{{ .Name }}</a> <a class="block px-2 py-1" href="{{ .URL }}">{{ .Name }}</a>
</li> </li>
{{ end }} {{ end }}

View File

@ -1,49 +0,0 @@
{{ $ctx := . }}
{{ $scratch := newScratch }}
{{ $toc := site.Data.toc }}
{{ range $root, $section := $toc }}
{{ if ne ($scratch.Get "match") true }}
{{ $scratch.Set "depth" 1 }}
{{ $rootSectionLink := "" }}
{{ with (index $section 0) }}
{{ if .path }}
{{ $rootSectionLink = .path }}
{{ else }}
{{ $rootSectionLink = (index .section 0).path }}
{{ end }}
{{ end }}
{{ $scratch.SetInMap "sections" "1" (dict "title" $root "path" $rootSectionLink) }}
{{ template "tocWalk" (dict "scratch" $scratch "section" $section "ctx" $ctx) }}
{{ end }}
{{ end }}
{{ define "tocWalk" }}
{{ $ctx := .ctx }}
{{ $scratch := .scratch }}
{{ $scratch.Set "depth" (add ($scratch.Get "depth") 1) }}
{{ range .section }}
{{ if ne ($scratch.Get "match") true }}
{{ if .path }}
{{ $match := eq (urls.Parse .path).Path (urls.Parse $ctx.Permalink).Path }}
{{ if $match }}
{{ $scratch.Set "match" true }}
{{ $scratch.Set "maxdepth" ($scratch.Get "depth") }}
{{ $scratch.Set "lastsection" (dict "title" .title "path" .path) }}
{{ end }}
{{ else }}
{{ $sectionLink := (index .section 0).path }}
{{ $scratch.SetInMap "sections" (string ($scratch.Get "depth")) (dict "title" .sectiontitle "path" $sectionLink) }}
{{ template "tocWalk" (dict "scratch" $scratch "section" .section "ctx" $ctx) }}
{{ end }}
{{ end }}
{{ end }}
{{ $scratch.Set "depth" (sub ($scratch.Get "depth") 1) }}
{{ end }}
{{ range $depth, $e := ($scratch.Get "sections") }}
{{ if ge $depth ($scratch.Get "maxdepth") }}
{{ $scratch.DeleteInMap "sections" $depth }}
{{ end }}
{{ end }}
{{ return $scratch }}

View File

@ -20,8 +20,6 @@
</thead> </thead>
<tbody> <tbody>
{{ range .Paginator.Pages }} {{ range .Paginator.Pages }}
{{ $scratch := partialCached "utils/tocparser.html" . . }}
{{ $sections := $scratch.GetSortedMapValues "sections" }}
<tr> <tr>
<td> <td>
<a href="{{ .Permalink }}" class="link"> <a href="{{ .Permalink }}" class="link">
@ -30,10 +28,9 @@
</td> </td>
<td> <td>
<span class="text-gray-light dark:text-gray-dark"> <span class="text-gray-light dark:text-gray-dark">
{{ range $i, $e := $sections }} {{- range .Ancestors.Reverse }}
{{ if $i }} / {{ end }} {{ .Title }} /
{{ $e.title }} {{- end }}
{{ end }}
</span> </span>
</td> </td>
</tr> </tr>