feat: add Book layout

Similar to the Docs layout but much easier to use.

Add `type: book` to a page's front matter and place Markdown files within a folder hierarchy to automatically create the book sidebar navigation.

Introduces {{< list_children >}} shortcode for listing children of a book chapter.
This commit is contained in:
George Cushen 2020-07-25 01:47:37 +01:00
commit 0b9cbc4150
8 changed files with 187 additions and 4 deletions

3
layouts/book/list.html Normal file
View file

@ -0,0 +1,3 @@
{{- define "main" -}}
{{ partial "book_layout.html" . }}
{{- end -}}

3
layouts/book/single.html Normal file
View file

@ -0,0 +1,3 @@
{{- define "main" -}}
{{ partial "book_layout.html" . }}
{{- end -}}

View file

@ -0,0 +1,59 @@
{{ $current_page := . }}
<div class="container-fluid docs">
<div class="row flex-xl-nowrap">
<div class="col-12 col-md-3 col-xl-2 docs-sidebar">
{{ partial "book_sidebar" . }}
</div>
{{/* Show ToC by default. */}}
{{ if ne .Params.toc false }}
<div class="d-none d-xl-block col-xl-2 docs-toc">
<ul class="nav toc-top">
<li><a href="#" id="back_to_top" class="docs-toc-title">{{ i18n "on_this_page" }}</a></li>
</ul>
{{ .TableOfContents }}
{{ partial "docs_toc_foot" . }}
</div>
{{ end }}
<main class="col-12 col-md-9 col-xl-8 py-md-3 pl-md-5 docs-content" role="main">
<article class="article">
<div class="docs-article-container">
<h1>{{ .Title }}</h1>
<div class="article-style">
{{ .Content }}
</div>
{{ partial "tags.html" . }}
{{/* Show next/previous pages by default. */}}
{{ if ne site.Params.book_section_pager false }}
<div class="article-widget">
{{ partial "section_pager" . }}
</div>
{{ end }}
</div>
<div class="body-footer">
<p>{{ i18n "last_updated" }} {{ $.Lastmod.Format site.Params.date_format }}</p>
{{ partial "page_edit" . }}
{{ partial "comments" . }}
{{ partial "page_related" . }}
</div>
</article>
{{ partial "site_footer" . }}
</main>
</div>
</div>

View file

@ -0,0 +1,83 @@
{{ define "book-menu" }}
{{- $first := false -}}
{{- $current_node := .current_node -}}
{{- $order_by := .order_by -}}
{{ $icon := "" }}
{{ with .sect }}
{{ if .IsSection}}
{{- $first = eq $current_node.FirstSection . -}}
{{- safeHTML $current_node.FirstSection.Params.pre_nav -}}
{{/* Get section icon. */}}
{{ $pack := or .Params.icon_pack "fas" }}
{{ $pack_prefix := $pack }}
{{ if in (slice "fab" "fas" "far" "fal") $pack }}
{{ $pack_prefix = "fa" }}
{{ end }}
{{ with .Params.icon }}
{{- if eq $pack "emoji" -}}
{{- . | emojify -}}
{{- else if eq $pack "custom" -}}
{{- $svg_icon := resources.Get (printf "images/icon-pack/%s.svg" .) -}}
{{- if $svg_icon -}}
{{ $icon = printf "<img src=\"%s\" alt=\"%s\" class=\"svg-icon svg-baseline pr-1\">" $svg_icon.RelPermalink . }}
{{- end -}}
{{- else -}}
{{ $icon = printf "<i class=\"%s %s-%s pr-1\"></i>" $pack $pack_prefix . }}
{{- end -}}
{{ end }}
{{ if $first }}
<ul class="nav docs-sidenav">
<li class="{{ if and .File $current_node.File | and (eq .File.UniqueID $current_node.File.UniqueID) }}active{{ end }}"><a href="{{ .RelPermalink }}">{{ safeHTML $icon }}{{ .LinkTitle | default .Title }}</a></li>
{{else}}
<div class="docs-toc-item">
<a class="docs-toc-link {{ if and .File $current_node.File | and (eq .File.UniqueID $current_node.File.UniqueID) }} active{{ end }}" href="{{ .RelPermalink }}">{{ safeHTML $icon }}{{ .LinkTitle | default .Title }}</a>
{{end}}
{{- $page_count := (add (len .Pages) (len .Sections)) -}}
{{ if ne $page_count 0 }}
{{ if not $first }}
<ul class="nav docs-sidenav">
{{end}}
{{- .Scratch.Set "pages" .Pages -}}
{{- if .Sections -}}
{{- .Scratch.Set "pages" (.Pages | union .Sections) -}}
{{- end -}}
{{- $pages := (.Scratch.Get "pages") -}}
{{- if eq $order_by "title" -}}
{{- range $pages.ByTitle -}}
{{ template "book-menu" dict "sect" . "current_node" $current_node "order_by" $order_by }}
{{- end -}}
{{- else if eq $order_by "title_desc" -}}
{{- range $pages.ByTitle.Reverse -}}
{{ template "book-menu" dict "sect" . "current_node" $current_node "order_by" $order_by }}
{{- end -}}
{{- else -}}
{{- range $pages.ByWeight -}}
{{ template "book-menu" dict "sect" . "current_node" $current_node "order_by" $order_by }}
{{- end -}}
{{- end}}
{{ if not $first }}
</ul>
{{end}}
{{end}}
{{ if not $first }}
</div>
{{else}}
</ul>
{{end}}
{{- else -}}
{{- if not .Params.Hidden -}}
<li class="{{ if and .File $current_node.File | and (eq .File.UniqueID $current_node.File.UniqueID) }}active{{ end }}"><a href="{{ .RelPermalink }}">{{ safeHTML $icon }}{{ .LinkTitle | default .Title }}</a></li>
{{- end -}}
{{ end -}}
{{- end -}}
{{/* End define. */}}
{{ end -}}

View file

@ -0,0 +1,28 @@
<form class="docs-search d-flex align-items-center">
<button class="btn docs-toggle d-md-none p-0 mr-3" type="button" data-toggle="collapse" data-target="#docs-nav" aria-controls="docs-nav" aria-expanded="false" aria-label="Toggle section navigation">
<span><i class="fas fa-bars"></i></span>
</button>
{{ if eq site.Params.search.engine 1 }}
<input name="q" type="search" class="form-control" placeholder="{{ i18n "search_placeholder" }}" autocomplete="off">
{{ end }}
</form>
<nav class="collapse docs-links" id="docs-nav">
{{ $current_node := . }}
{{ $menu_name := (path.Base (path.Split .FirstSection).Dir) }}
{{ $order_by := cond (eq $menu_name "updates") "title_desc" site.Params.books.order_by }}
{{ $query := "" }}
{{- if eq $order_by "title" -}}
{{- $query = where .Site.Home.Sections.ByTitle "Section" $menu_name -}}
{{- else if eq $order_by "title_desc" -}}
{{- $query = where .Site.Home.Sections.ByTitle.Reverse "Section" $menu_name -}}
{{- else -}}
{{- $query = where .Site.Home.Sections.ByWeight "Section" $menu_name -}}
{{- end}}
{{- range $query -}}
{{ template "book-menu" dict "sect" . "current_node" $current_node "order_by" $order_by }}
{{- end -}}
</nav>

View file

@ -1,15 +1,15 @@
{{ $str := "" }}
<div class="post-nav">
{{if .NextInSection}}
{{/* For the docs layout, prev/next labels are reversed. */}}
{{ if eq .Type "docs" }}{{ $str = "previous" }}{{else}}{{ $str = "next" }}{{end}}
{{/* For the Book layout, prev/next labels are reversed. */}}
{{ if in .Type (dict "docs" "book") }}{{ $str = "previous" }}{{else}}{{ $str = "next" }}{{end}}
<div class="post-nav-item">
<div class="meta-nav">{{ i18n $str }}</div>
<a href="{{.NextInSection.RelPermalink}}" rel="next">{{.NextInSection.Title}}</a>
</div>
{{end}}
{{if .PrevInSection}}
{{ if eq .Type "docs" }}{{ $str = "next" }}{{else}}{{ $str = "previous" }}{{end}}
{{ if in .Type (dict "docs" "book") }}{{ $str = "next" }}{{else}}{{ $str = "previous" }}{{end}}
<div class="post-nav-item">
<div class="meta-nav">{{ i18n $str }}</div>
<a href="{{.PrevInSection.RelPermalink}}" rel="prev">{{.PrevInSection.Title}}</a>

View file

@ -23,7 +23,7 @@
<a href="https://sourcethemes.com/academic/" target="_blank" rel="noopener">Academic theme</a> for
<a href="https://gohugo.io" target="_blank" rel="noopener">Hugo</a>.
{{ if not (in (slice "docs" "updates") .Type) }}
{{ if not (in (slice "book" "docs" "updates") .Type) }}
<span class="float-right" aria-hidden="true">
<a href="#" class="back-to-top">
<span class="button_icon">

View file

@ -0,0 +1,7 @@
{{ if gt (len $.Page.Pages) 0}}
<ul class="list-unstyled">
{{ range $.Page.Pages }}
<li><h5><a href="{{.RelPermalink}}">{{.LinkTitle}}</a></h5> {{with .Params.summary}}<p>{{. | plainify | emojify}}</p>{{end}}</li>
{{end}}
</ul>
{{end}}