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
exceptions, see [Content not edited here](#content-not-edited-here)).
The structure of the sidebar navigation on the site is defined in
[`/data/toc.yaml`](./data/toc.yaml). To rename or change the location of a page
in the left-hand navigation, edit the `toc.yaml` file.
The structure of the sidebar navigation on the site is defined by the site's
section hierarchy in the `contents` directory. The titles of the pages are
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).

View File

@ -31,14 +31,6 @@
@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 {
background-color: theme(colors.white / 50%);
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:
main:
- name: Get started
url: /get-started/
pageRef: /get-started/
weight: 1
- name: Guides
url: /guides/
pageRef: /guides/
weight: 2
- name: Manuals
url: /manuals/
pageRef: /manuals/
weight: 3
- name: Reference
url: /reference/
pageRef: /reference/
weight: 4
- name: Learning paths
url: /learning-paths/
pageRef: /learning-paths/
weight: 5
footer:

View File

@ -307,12 +307,16 @@
"highlight",
"hover:bg-blue-light-400",
"hover:bg-gray-light-100",
"hover:bg-gray-light-300",
"hover:border-gray-light-200",
"hover:dark:bg-gray-dark-300",
"hover:dark:border-gray-dark",
"hover:dark:text-blue-dark",
"hover:drop-shadow-lg",
"hover:opacity-75",
"hover:opacity-90",
"hover:text-black",
"hover:text-blue-light",
"hover:underline",
"hub-api",
"icon-lg",
@ -451,9 +455,9 @@
"scale-75",
"scroll-mt-20",
"scroll-mt-36",
"select-none",
"self-center",
"shadow",
"sidebar-hover",
"sm:flex-row",
"sm:grid-cols-2",
"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">
{{ with ($scratch.GetSortedMapValues "sections") }}
{{ range $i, $e := . }}
{{- if $i -}}
<span>/</span>
{{- end -}}
<a href="{{ $e.path }}" class="link truncate">{{ markdownify $e.title }}</a>
{{- end -}}
{{- end -}}
{{- with $scratch.Get "lastsection" -}}
<span>/</span>
<span class="truncate">{{ markdownify .title }}</span>
{{- end -}}
{{ range .Ancestors.Reverse }}
<a href="{{ .Permalink }}" class="link truncate">{{ markdownify .LinkTitle }}</a>
<span>/</span>
{{- end }}
<span class="truncate">{{ markdownify .LinkTitle }}</span>
</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 */}}
<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>
{{ 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
{{- if eq $firstSection .Name }}
{{- if or (page.IsDescendant .Page) (eq page .Page) }}
underline
{{- end }}">
<a href="{{ .URL }}">{{ .Name }}</a>
</div>
{{ end }}
</nav>
{{ if $firstSection }}
{{ $allSections := slice }}
{{ range $i, $e := ($scratch.GetSortedMapValues "sections") }}
{{ $allSections = $allSections | append (index $e "title") }}
{{ end }}
<nav id="sectiontree" class="text-sm flex flex-col">
<nav class="text-sm flex flex-col">
<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 */}}
<ul>
{{/* Walk the toc.yaml nodes under the current main section */}}
{{ range (index site.Data.toc $firstSection) }}
{{ template "tocRender" (dict "ctx" $ctx "entry" . "sections" $allSections) }}
{{ end }}
{{ template "renderSingle" .FirstSection }}
{{ template "renderChildren" .FirstSection }}
</ul>
</nav>
{{ define "renderChildren" }}
{{- range .Pages }}
{{- if eq .Params.sitemap false }}
{{- continue }}
{{- end }}
{{- if .IsSection }}
{{- template "renderList" . }}
{{- else }}
{{- template "renderSingle" . }}
{{- end }}
{{- end }}
{{ end }}
{{/* Recursive template for sidebar items */}}
{{ define "tocRender" }}
{{ $ctx := .ctx }}
{{ $sections := .sections }}
{{ if .entry.sectiontitle }}
{{ $expanded := in $sections .entry.sectiontitle }}
{{/* .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">
{{ markdownify .entry.sectiontitle }}
</span>
<span class="icon-svg {{ if $expanded }}hidden{{ end }}">
{{ partialCached "icon" "expand_more" "expand_more" }}
</span>
<span class="icon-svg {{ if not $expanded }}hidden{{ end }}">
{{ partialCached "icon" "expand_less" "expand_less" }}
</span>
</button>
<ul class="{{if not $expanded}}hidden {{end}}ml-3">
{{ range .entry.section }}
{{ template "tocRender" (dict "entry" . "ctx" $ctx "sections" $sections ) }}
{{ end }}
</ul>
</li>
{{ else }}
{{/* .entry is a page */}}
{{ $isCurrent := eq (urls.Parse $ctx.Permalink).Path .entry.path }}
<li class="pl-4 sidebar-hover rounded
{{ 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"
href="{{ .entry.path }}" title="{{ markdownify .entry.title }}"
><span class="flex items-center gap-2">{{ markdownify .entry.title }}</span>
</a>
</li>
{{ define "renderList" }}
{{ $isCurrent := eq page . }}
{{ $expanded := or $isCurrent (page.IsDescendant .) }}
<li x-data="{ expanded: {{$expanded}} }">
<div class="rounded px-4 w-full flex items-center justify-between{{ if $isCurrent }} bg-gray-light-200 dark:bg-gray-dark-200{{ end }}">
<span class="py-2 truncate flex items-center gap-2">
{{- 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>
<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" }}
</span>
<span :class="{ 'hidden' : !expanded }" class="icon-svg {{ if not $expanded }}hidden{{ end }}">
{{ partialCached "icon" "expand_less" "expand_less" }}
</span>
</button>
</div>
<ul :class="{ 'hidden' : !expanded }" class="{{if not $expanded}}hidden {{end}}ml-3">
{{ template "renderChildren" . }}
</ul>
</li>
{{ end }}
{{ define "renderSingle" }}
{{ $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 }}">
<a {{ if $isCurrent }}aria-current="page" {{ end }} class="py-2 w-full truncate block"
href="{{ .Permalink }}" title="{{ markdownify .Title }}"
><span class="flex items-center gap-2">{{ markdownify .LinkTitle }}</span>
</a>
</li>
{{ end }}

View File

@ -1,10 +1,8 @@
{{ $scratch := partialCached "utils/tocparser.html" . . }}
{{ $firstSection := (index ($scratch.GetSortedMapValues "sections") 0).title }}
<div>
<nav>
<ul class="mt-1 box-content hidden gap-4 md:flex">
{{ 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>
</li>
{{ 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>
<tbody>
{{ range .Paginator.Pages }}
{{ $scratch := partialCached "utils/tocparser.html" . . }}
{{ $sections := $scratch.GetSortedMapValues "sections" }}
<tr>
<td>
<a href="{{ .Permalink }}" class="link">
@ -30,10 +28,9 @@
</td>
<td>
<span class="text-gray-light dark:text-gray-dark">
{{ range $i, $e := $sections }}
{{ if $i }} / {{ end }}
{{ $e.title }}
{{ end }}
{{- range .Ancestors.Reverse }}
{{ .Title }} /
{{- end }}
</span>
</td>
</tr>