Skip to content

Commit 60dee12

Browse files
committed
Experiment with navbar
1 parent c46e9ea commit 60dee12

File tree

2 files changed

+338
-1
lines changed

2 files changed

+338
-1
lines changed

config/_default/params.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ favicon:
88
logo: /images/logo.png
99

1010
navigation:
11-
logo: false
11+
logo: /images/logo.svg
1212
search: true
1313
searchModal: true
1414
size: xl

layouts/partials/assets/navbar.html

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
<!--
2+
Copyright © 2024 The Hinode Team / Mark Dumay. All rights reserved.
3+
Use of this source code is governed by The MIT License (MIT) that can be found in the LICENSE file.
4+
Visit gethinode.com/license for more details.
5+
-->
6+
7+
<!-- TODO: consider to drop style arg -->
8+
9+
{{ $error := false }}
10+
11+
<!-- Validate arguments -->
12+
{{ if partial "utilities/IsInvalidArgs.html" (dict "structure" "navbar" "args" . "group" "partial") }}
13+
{{ errorf "partial [assets/navbar.html] - Invalid arguments" -}}
14+
{{ $error = true }}
15+
{{ end }}
16+
17+
<!-- Inline partial to render the color mode switcher -->
18+
{{- define "partials/navbar-mode.html" -}}
19+
{{- $size := .size -}}
20+
{{- $collapsed := .collapsed -}}
21+
{{- $id := .id -}}
22+
23+
<li class="nav-item dropdown {{ if $collapsed }}d-{{ $size }}-none{{ else }}d-none d-{{ $size }}-block{{ end }}">
24+
<a class="nav-link dropdown-toggle" href="#!" role="button" data-bs-toggle="dropdown" aria-label="{{ T "colorMode" }}" aria-expanded="false" id="{{ $id }}-theme{{ if $collapsed }}-collapsed{{ end }}">
25+
<span class="theme-icon-active">{{- partial "assets/icon.html" (dict "icon" "fas sun fa-fw") }}</span>{{ if $collapsed }}&nbsp;{{ T "colorMode" }}{{ end }}
26+
<span class="d-md-none"></span>
27+
</a>
28+
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="{{ $id }}-theme{{ if $collapsed }}-collapsed{{ end }}">
29+
<li>
30+
<a class="dropdown-item{{ if $collapsed }} switch-mode-collapsed{{ end }}" data-bs-theme-value="light" href="#!">
31+
<span class="theme-icon">{{- partial "assets/icon.html" (dict "icon" "fas sun fa-fw" "spacing" false) }}</span>&nbsp;
32+
{{- T "colorLight" }}
33+
</a>
34+
</li>
35+
<li>
36+
<a class="dropdown-item{{ if $collapsed }} switch-mode-collapsed{{ end }}" data-bs-theme-value="dark" href="#!">
37+
<span class="theme-icon">{{- partial "assets/icon.html" (dict "icon" "fas moon fa-fw" "spacing" false) }}</span>&nbsp;
38+
{{- T "colorDark" }}
39+
</a>
40+
</li>
41+
<li>
42+
<a class="dropdown-item{{ if $collapsed }} switch-mode-collapsed{{ end }}" data-bs-theme-value="auto" href="#!">
43+
<span class="theme-icon">{{- partial "assets/icon.html" (dict "icon" "fas circle-half-stroke fa-fw" "spacing" false) }}</span>&nbsp;
44+
{{- T "colorAuto" }}
45+
</a>
46+
</li>
47+
</ul>
48+
</li>
49+
{{- end -}}
50+
51+
<!-- Inline partial to render the version switcher -->
52+
{{- define "partials/navbar-versions.html" -}}
53+
{{- $size := .size -}}
54+
{{- $collapsed := .collapsed -}}
55+
{{- $page := .page -}}
56+
{{- $baseURL := .baseURL -}}
57+
{{- $list := site.Params.docs.releases -}}
58+
{{- $id := .id -}}
59+
{{- $version := partial "utilities/GetVersion.html" (dict "page" $page) -}}
60+
61+
<li class="nav-item dropdown {{ if $collapsed }}d-{{ $size }}-none{{ else }}d-none d-{{ $size }}-block{{ end }}">
62+
<a class="nav-link dropdown-toggle" href="#!" role="button" data-bs-toggle="dropdown" aria-expanded="false" id="{{ $id }}-version-switch">
63+
{{ if $collapsed }}{{ site.Title }} {{ end }}{{ $version }}
64+
</a>
65+
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="{{ $id }}-version-switch">
66+
{{- range $index, $item := $list -}}
67+
{{- $active := eq $item.label $version -}}
68+
{{- $disabled := false -}}
69+
{{- if hugo.IsServer }}
70+
{{- $disabled = and $item.redirect (gt (len $item.redirect) 0) -}}
71+
{{- end -}}
72+
{{ if $item.url }}
73+
<li>
74+
{{- $url := (urls.JoinPath $baseURL $item.url) | relLangURL -}}
75+
<a class="pe-5 dropdown-item{{ if $collapsed }} switch-mode-collapsed{{ end }}{{ if $active }} active{{ end }}{{ if $disabled }} disabled{{ end }}" href="{{ $url }}">{{ $item.label }}
76+
{{ if $item.latest }}&nbsp;({{ T "latest" }}){{ end }}
77+
{{ if $active }}
78+
<span class="position-absolute end-0 me-3">{{- partial "assets/icon.html" (dict "icon" "fas check fa-fw") }}</span>
79+
{{ end }}
80+
</a>
81+
</li>
82+
{{ else }}
83+
{{ if gt $index 0}}<li><hr class="dropdown-divider"></li>{{ end }}
84+
<li><span class="dropdown-header fs-6">{{ $item.label }}</span></li>
85+
{{ end }}
86+
{{- end -}}
87+
{{- if site.Params.docs.overview -}}
88+
{{ if gt (len $list) 0 }}<li><hr class="dropdown-divider"></li>{{ end }}
89+
<li>
90+
{{- $url := (urls.JoinPath $baseURL site.Params.docs.overview) | relLangURL -}}
91+
<a class="dropdown-item{{ if $collapsed }} switch-mode-collapsed{{ end }}" href="{{ $url }}">{{ T "allVersions" }}</a>
92+
</li>
93+
{{- end -}}
94+
</ul>
95+
</li>
96+
{{- end -}}
97+
98+
<!-- Initialize arguments -->
99+
{{- $absoluteURL := site.Params.main.canonifyAssetsURLs | default false -}}
100+
{{- $id := .id | default (printf "navbar-%d" 0) -}}
101+
{{- $page := .page -}}
102+
{{- $baseURL := $page.Scratch.Get "baseURL" -}}
103+
104+
{{- $defaultMenu := "main" -}}
105+
{{- $menuName := .menus | default $defaultMenu }}
106+
{{- $menus := index site.Menus $menuName -}}
107+
{{- if or (ne (printf "%T" $menus) "navigation.Menu") (ne (index $menus 0).Menu $menuName) -}}
108+
{{- if ne $menuName $defaultMenu }}
109+
{{- errorf "partial [assets/navbar.html] - Invalid value for param 'menus': %s" $menuName -}}
110+
{{- end -}}
111+
{{- end -}}
112+
113+
{{- $size := .size | default "md" -}}
114+
{{- $fixed := .fixed | default false -}}
115+
{{- $overlay := .overlay | default false -}}
116+
{{- $overlayMode := .overlayMode | default "dark" -}}
117+
{{- if eq $overlayMode "none" }}{{ $overlayMode = "" }}{{ end }}
118+
{{- $color := .color | default "" -}}
119+
{{- $search := .search | default site.Params.navigation.search -}}
120+
{{- $searchModal := and $search site.Params.navigation.searchModal -}}
121+
{{- $enableDarkMode := .mode | default site.Params.main.enableDarkMode -}}
122+
123+
{{- $enableVersions := false -}}
124+
{{ $list := site.Params.docs.releases }}
125+
{{ if $list }}
126+
{{- $enableVersions = gt (len $list ) 1 -}}
127+
{{ end }}
128+
129+
{{- $enableLanguage := or $page.IsTranslated site.IsMultiLingual -}}
130+
{{- $horizontal := default false site.Params.navigation.horizontal -}}
131+
132+
{{- $logo := .logo | default site.Params.navigation.logo -}}
133+
{{- $logoLight := "" -}}
134+
{{- $logoDark := "" -}}
135+
{{- if $enableDarkMode -}}
136+
{{ $ext := path.Ext $logo -}}
137+
{{- $logoLight = printf "%s-light%s" (strings.TrimSuffix $ext $logo) $ext -}}
138+
{{- $logoDark = printf "%s-dark%s" (strings.TrimSuffix $ext $logo) $ext -}}
139+
{{- $light := fileExists (path.Join "/static" $logoLight) -}}
140+
{{- $dark := fileExists (path.Join "/static" $logoDark) -}}
141+
{{- if and $light (not $dark) -}}
142+
{{- warnf "partial [assets/navbar.html] - Missing file: %s" $logoDark -}}
143+
{{- $logoLight = "" -}}
144+
{{- $logoDark = "" -}}
145+
{{- end -}}
146+
{{- if and (not $light) $dark -}}
147+
{{- warnf "partial [assets/navbar.html] - Missing file: %s" $logoLight -}}
148+
{{- $logoLight = "" -}}
149+
{{- $logoDark = "" -}}
150+
{{- end -}}
151+
{{- if not (or $light $dark) -}}
152+
{{- $logoLight = "" -}}
153+
{{- $logoDark = "" -}}
154+
{{- end -}}
155+
{{- end -}}
156+
157+
{{ if $logo }}{{ $logo = urls.JoinPath $baseURL $logo }}{{ end }}
158+
{{ if $logoLight }}{{ $logoLight = urls.JoinPath $baseURL $logoLight }}{{ end }}
159+
{{ if $logoDark }}{{ $logoDark = urls.JoinPath $baseURL $logoDark }}{{ end }}
160+
161+
{{- $title := site.Title -}}
162+
{{- if .title -}}
163+
{{- $title = .title -}}
164+
{{- if not .logo }}{{ $logo = "" }}{{ end -}}
165+
{{- end -}}
166+
167+
{{- $pre := .Pre -}}
168+
{{- $post := .Post -}}
169+
170+
{{- $theme := "light" -}}
171+
{{- if in (slice "primary" "secondary" "success" "danger" "black") $color }}{{ $theme = "dark" }}{{ end -}}
172+
{{- if in (slice "body" "body-tertiary") $color }}{{ $theme = "" }}{{ end -}}
173+
{{- if not $color }}{{ $theme = "" }}{{ end -}}
174+
{{- $class := .class -}}
175+
176+
{{- $contrast := false -}}
177+
{{- if in (slice "primary" "secondary" "success" "danger") $color }}{{ $contrast = true }}{{ end -}}
178+
179+
<!-- Main code -->
180+
<div class="container-fluid {{ if $fixed }}fixed-top{{ end }} p-0{{ with $class }} {{ . }}{{ end }}">
181+
{{- partial "assets/page-alert.html" (dict "page" $page) -}}
182+
<nav class="navbar p-4
183+
{{- if not $overlay }}{{ with $color }} bg-{{ . }}{{ end }}{{ end -}}
184+
{{ if $fixed }} navbar-fixed-top{{ end }} navbar-expand-{{ $size -}}
185+
{{ if $contrast }} navbar-contrast{{ end }}"
186+
{{ if $overlay }}
187+
data-bs-theme="{{ $overlayMode }}"
188+
data-bs-overlay="{{ $overlayMode }}"
189+
{{ else }}{{ with $theme }}data-bs-theme="{{ . }}"{{ end }}{{ end }}
190+
{{ if $overlay }}data-navbar-color="{{ $color }}"{{ end }}
191+
>
192+
<div class="container-xxl p-0">
193+
<div class="d-flex navbar-container justify-content-center">
194+
<!-- Insert sidebar toggler when applicable -->
195+
<div class="d-flex align-items-center">
196+
{{- if $page.Scratch.Get "sidebar" -}}
197+
<button class="navbar-toggler collapsed p-0 mx-auto fw-30" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvass-sidebar" aria-controls="offcanvass-sidebar" aria-label="{{ T "toggleSidebar" }}">
198+
{{- partial "assets/icon.html" (dict "icon" "fas ellipsis fa-fw" "spacing" false) -}}
199+
</button>
200+
{{- else -}}
201+
<!-- Insert invisible sidebar toggler to center logo correctly on smaller screens -->
202+
<button class="navbar-toggler collapsed p-0 mx-auto invisible fw-30" type="button">
203+
{{- partial "assets/icon.html" (dict "icon" "fas ellipsis fa-fw" "spacing" false) -}}
204+
</button>
205+
{{- end -}}
206+
</div>
207+
208+
<!-- Insert the brand logo or name -->
209+
<div class="{{ if (or $logoLight $logo) }}mx-auto{{ else }}flex-grow-1 flex-{{ $size }}-grow-0{{ end }}">
210+
<a class="navbar-brand" href="{{ site.Home.RelPermalink }}" aria-label="{{ T "home" }}">
211+
{{- if (and $logoLight $logoDark) -}}
212+
{{ $width := partial "utilities/GetWidth.html" (dict "path" $logoLight "height" 30) }}
213+
<img src="{{if $absoluteURL }}{{ absURL $logoLight }}{{ else }}{{ $logoLight }}{{ end }}" class="d-none-inline-dark" alt="{{ $title }} logo" height="30"{{ with $width }} width="{{ . }}"{{ end }}>
214+
<img src="{{if $absoluteURL }}{{ absURL $logoDark }}{{ else }}{{ $logoDark }}{{ end }}" class="d-none-inline-light" alt="{{ $title }} logo" height="30"{{ with $width }} width="{{ . }}"{{ end }}>
215+
{{- else if $logo -}}
216+
{{ $width := partial "utilities/GetWidth.html" (dict "path" $logo "height" 30) }}
217+
<img src="{{if $absoluteURL }}{{ absURL $logo }}{{ else }}{{ $logo }}{{ end }}" alt="{{ $title }} logo" height="30"{{ with $width }} width="{{ . }}"{{ end }}><small class="ps-4 d-none d-{{ $size }}-inline">{{ $title }}</small>
218+
{{- else -}}
219+
<div class="navbar-title fw-bold h-100">{{ $title }}</div>
220+
{{- end -}}
221+
</a>
222+
</div>
223+
224+
<!-- Insert main navigation toggler -->
225+
<div class="d-flex align-items-center">
226+
<button class="navbar-toggler main-nav-toggler collapsed p-0" type="button" data-bs-toggle="collapse" data-bs-target="#{{ $id }}-collapse"
227+
aria-controls="{{ $id }}" aria-expanded="false" aria-label="{{ T "toggleMainNav" }}">
228+
<span class="toggler-icon top-bar emphasis{{ with $theme }}-{{ . }}{{ end }}"></span>
229+
<span class="toggler-icon middle-bar emphasis{{ with $theme }}-{{ . }}{{ end }}"></span>
230+
<span class="toggler-icon bottom-bar emphasis{{ with $theme }}-{{ . }}{{ end }}"></span>
231+
</button>
232+
</div>
233+
</div>
234+
235+
<div class="navbar-collapse collapse" id="{{ $id }}-collapse">
236+
<!-- Insert search input -->
237+
{{- if and $search (not $searchModal) }}{{ partial "assets/search-input.html" }}{{ end -}}
238+
239+
<!-- Render top-menu items (maximum depth of 2) -->
240+
<ul class="navbar-nav ms-auto">
241+
{{- range $menu := $menus -}}
242+
<li class="nav-item{{ if .HasChildren }} dropdown{{ if $horizontal }} dropdown-horizontal-{{ $size }}{{ end }}{{ end }}">
243+
{{- partial "assets/navbar-item.html" (dict "menu" $menu "page" $page) -}}
244+
{{- if .HasChildren -}}
245+
<ul class="dropdown-menu">
246+
{{- range .Children -}}
247+
<li>{{- partial "assets/navbar-item.html" (dict "menu" . "parent" $menu "page" $page) -}}</li>
248+
{{- end -}}
249+
</ul>
250+
{{- end -}}
251+
</li>
252+
{{- end -}}
253+
254+
<!-- Insert divider if applicable -->
255+
{{- if and $menus (or $enableLanguage $enableVersions) -}}
256+
<li class="nav-item py-2 py-md-1 col-12 col-md-auto d-none d-{{ $size }}-block">
257+
<div class="vr d-none d-md-flex h-100 mx-md-2"></div>
258+
</li>
259+
<li><hr class="dropdown-divider-bg"></li>
260+
{{- end -}}
261+
262+
<!-- Insert version switcher -->
263+
{{- if $enableVersions -}}
264+
{{- partial "partials/navbar-versions.html" (dict "page" $page "size" $size "collapsed" true "id" .id "baseURL" $baseURL) -}}
265+
{{- partial "partials/navbar-versions.html" (dict "page" $page "size" $size "collapsed" false "id" .id "baseURL" $baseURL) -}}
266+
{{- end -}}
267+
268+
<!-- Insert language switcher if applicable -->
269+
{{- if $enableLanguage -}}
270+
{{- $currentLang := $page.Language.Lang -}}
271+
<li class="nav-item dropdown">
272+
<a class="nav-link dropdown-toggle d-{{ $size }}-none" href="#!" role="button" data-bs-toggle="dropdown" aria-label="{{ T "languageSwitcherLabel" }}" aria-expanded="false">
273+
{{- partial "assets/icon.html" (dict "icon" "fas globe fa-fw" "spacing" false) }}&nbsp;{{ T "languageSwitcherLabel" }}
274+
</a>
275+
<a class="nav-link dropdown-toggle d-none d-{{ $size }}-block" href="#!" role="button" data-bs-toggle="dropdown" aria-label="{{ T "languageSwitcherLabel" }}" aria-expanded="false">
276+
{{- partial "assets/icon.html" (dict "icon" "fas globe fa-fw" "spacing" false) }}
277+
</a>
278+
<ul class="dropdown-menu dropdown-menu-end ">
279+
{{- if $page.IsTranslated -}}
280+
{{- range $page.AllTranslations -}}
281+
<li><a class="dropdown-item {{ if eq .Language.Lang $currentLang }}active{{ end }}" href="{{ .RelPermalink }}">{{ .Language.LanguageName }}</a></li>
282+
{{- end -}}
283+
{{- else -}}
284+
{{- range site.Languages -}}
285+
<li><a class="dropdown-item" href="{{ urls.JoinPath $baseURL .Lang }}">{{ default .Lang .LanguageName }}</a></li>
286+
{{- end -}}
287+
{{- end -}}
288+
</ul>
289+
</li>
290+
{{- end -}}
291+
292+
<!-- Insert color mode switcher -->
293+
{{- if $enableDarkMode -}}
294+
{{- partial "partials/navbar-mode.html" (dict "size" $size "collapsed" true "id" .id) -}}
295+
{{- partial "partials/navbar-mode.html" (dict "size" $size "collapsed" false "id" .id) -}}
296+
{{- end -}}
297+
298+
<!-- Insert modal search button -->
299+
{{- if $searchModal }}
300+
<li class="nav-item py-2 py-md-1 col-12 col-md-auto d-none d-{{ $size }}-block">
301+
<div class="vr d-none d-md-flex h-100 mx-md-2"></div>
302+
</li>
303+
<li><hr class="dropdown-divider-bg"></li>
304+
305+
<a class="nav-link d-{{ $size }}-none" href="#!" role="button" data-bs-toggle="modal" data-bs-target="#search-modal" aria-label="{{ T "ui_search" }}" aria-expanded="false">
306+
{{ partial "assets/icon.html" (dict "icon" "fas magnifying-glass fa-fw") }}&nbsp;{{ T "ui_search" }}
307+
</a>
308+
<a class="nav-link d-none d-{{ $size }}-block" href="#!" role="button" data-bs-toggle="modal" data-bs-target="#search-modal" aria-label="{{ T "ui_search" }}" aria-expanded="false">
309+
{{ partial "assets/icon.html" (dict "icon" "fas magnifying-glass fa-fw") }}
310+
</a>
311+
{{ end -}}
312+
</ul>
313+
</div>
314+
</div>
315+
</nav>
316+
</div>
317+
318+
{{/* Insert modal search element */}}
319+
{{- if $searchModal }}
320+
<div class="modal fade search-modal" tabindex="-1" id="search-modal" aria-labelledby="searchModalLabel" aria-hidden="true">
321+
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-lg">
322+
<div class="modal-content h-50">
323+
<div class="modal-header">
324+
<div class="w-100">
325+
<form class="search position-relative me-auto">
326+
<input id="search-input-modal" class="search-input form-control is-search" tabindex="1" type="search" placeholder="{{ T "ui_search" }}..." aria-label="{{ T "ui_search" }}" autocomplete="off">
327+
</form>
328+
</div>
329+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{ T "close" }}"></button>
330+
</div>
331+
<div class="modal-body p-2 search-background">
332+
<div class="search-suggestions" data-no-results="{{ T "ui_no_results" }}"></div>
333+
</div>
334+
</div>
335+
</div>
336+
</div>
337+
{{ end -}}

0 commit comments

Comments
 (0)