mirror of
https://github.com/imfing/hextra.git
synced 2025-06-19 09:23:37 -04:00
feat: child menu support in navbar (#695)
* feat: implement child menu support in main navbar - Added a new JavaScript file for handling dropdown functionality in the navbar. - Implemented event listeners for toggling dropdowns, closing them on outside clicks, and dismissing with the Escape key. - Updated navbar HTML to support dropdown items with children, enhancing the navigation experience. - Adjusted core script imports to include the new dropdown functionality. * chore: update menu identifiers and add missing translations for development versions * chore: update hugo stats * chore: update script name * chore: update menu item names to include arrows for external links
This commit is contained in:
61
assets/js/nav-menu.js
Normal file
61
assets/js/nav-menu.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
(function () {
|
||||||
|
const dropdownToggles = document.querySelectorAll(".hextra-nav-menu-toggle");
|
||||||
|
|
||||||
|
dropdownToggles.forEach((toggle) => {
|
||||||
|
toggle.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
// Close all other dropdowns first
|
||||||
|
dropdownToggles.forEach((otherToggle) => {
|
||||||
|
if (otherToggle !== toggle) {
|
||||||
|
otherToggle.dataset.state = "closed";
|
||||||
|
const otherMenuItems = otherToggle.nextElementSibling;
|
||||||
|
otherMenuItems.classList.add("hx:hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle current dropdown
|
||||||
|
const isOpen = toggle.dataset.state === "open";
|
||||||
|
toggle.dataset.state = isOpen ? "closed" : "open";
|
||||||
|
const menuItemsElement = toggle.nextElementSibling;
|
||||||
|
|
||||||
|
if (!isOpen) {
|
||||||
|
// Position dropdown centered with toggle
|
||||||
|
menuItemsElement.style.position = "absolute";
|
||||||
|
menuItemsElement.style.top = "100%";
|
||||||
|
menuItemsElement.style.left = "50%";
|
||||||
|
menuItemsElement.style.transform = "translateX(-50%)";
|
||||||
|
menuItemsElement.style.zIndex = "1000";
|
||||||
|
|
||||||
|
// Show dropdown
|
||||||
|
menuItemsElement.classList.remove("hx:hidden");
|
||||||
|
} else {
|
||||||
|
// Hide dropdown
|
||||||
|
menuItemsElement.classList.add("hx:hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dismiss dropdown when clicking outside
|
||||||
|
document.addEventListener("click", (e) => {
|
||||||
|
if (e.target.closest(".hextra-nav-menu-toggle") === null) {
|
||||||
|
dropdownToggles.forEach((toggle) => {
|
||||||
|
toggle.dataset.state = "closed";
|
||||||
|
const menuItemsElement = toggle.nextElementSibling;
|
||||||
|
menuItemsElement.classList.add("hx:hidden");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close dropdowns on escape key
|
||||||
|
document.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
dropdownToggles.forEach((toggle) => {
|
||||||
|
toggle.dataset.state = "closed";
|
||||||
|
const menuItemsElement = toggle.nextElementSibling;
|
||||||
|
menuItemsElement.classList.add("hx:hidden");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
@ -58,7 +58,7 @@ markup:
|
|||||||
extensions:
|
extensions:
|
||||||
passthrough:
|
passthrough:
|
||||||
delimiters:
|
delimiters:
|
||||||
block: [['\[', '\]'], ['$$', '$$']]
|
block: [['\[', '\]'], ["$$", "$$"]]
|
||||||
inline: [['\(', '\)']]
|
inline: [['\(', '\)']]
|
||||||
enable: true
|
enable: true
|
||||||
|
|
||||||
@ -70,9 +70,8 @@ menu:
|
|||||||
name: Documentation
|
name: Documentation
|
||||||
pageRef: /docs
|
pageRef: /docs
|
||||||
weight: 1
|
weight: 1
|
||||||
- identifier: showcase
|
- identifier: versions
|
||||||
name: Showcase
|
name: Versions
|
||||||
pageRef: /showcase
|
|
||||||
weight: 2
|
weight: 2
|
||||||
- identifier: blog
|
- identifier: blog
|
||||||
name: Blog
|
name: Blog
|
||||||
@ -82,15 +81,27 @@ menu:
|
|||||||
name: About
|
name: About
|
||||||
pageRef: /about
|
pageRef: /about
|
||||||
weight: 4
|
weight: 4
|
||||||
- name: Search
|
- identifier: showcase
|
||||||
|
name: Showcase
|
||||||
|
pageRef: /showcase
|
||||||
weight: 5
|
weight: 5
|
||||||
|
- name: Search
|
||||||
|
weight: 6
|
||||||
params:
|
params:
|
||||||
type: search
|
type: search
|
||||||
- name: GitHub
|
- name: GitHub
|
||||||
weight: 6
|
weight: 7
|
||||||
url: "https://github.com/imfing/hextra"
|
url: "https://github.com/imfing/hextra"
|
||||||
params:
|
params:
|
||||||
icon: github
|
icon: github
|
||||||
|
- identifier: development
|
||||||
|
name: Development ↗
|
||||||
|
url: https://imfing.github.io/hextra/versions/latest/
|
||||||
|
parent: versions
|
||||||
|
- identifier: v0.9
|
||||||
|
name: v0.9 ↗
|
||||||
|
url: https://imfing.github.io/hextra/versions/v0.9/
|
||||||
|
parent: versions
|
||||||
|
|
||||||
sidebar:
|
sidebar:
|
||||||
- identifier: more
|
- identifier: more
|
||||||
|
@ -121,6 +121,9 @@
|
|||||||
"hextra-max-footer-width",
|
"hextra-max-footer-width",
|
||||||
"hextra-max-navbar-width",
|
"hextra-max-navbar-width",
|
||||||
"hextra-max-page-width",
|
"hextra-max-page-width",
|
||||||
|
"hextra-nav-menu-item",
|
||||||
|
"hextra-nav-menu-items",
|
||||||
|
"hextra-nav-menu-toggle",
|
||||||
"hextra-pdf",
|
"hextra-pdf",
|
||||||
"hextra-scrollbar",
|
"hextra-scrollbar",
|
||||||
"hextra-sidebar-collapsible-button",
|
"hextra-sidebar-collapsible-button",
|
||||||
@ -311,6 +314,7 @@
|
|||||||
"hx:duration-200",
|
"hx:duration-200",
|
||||||
"hx:duration-75",
|
"hx:duration-75",
|
||||||
"hx:ease-in",
|
"hx:ease-in",
|
||||||
|
"hx:ease-in-out",
|
||||||
"hx:first:mt-0",
|
"hx:first:mt-0",
|
||||||
"hx:flex",
|
"hx:flex",
|
||||||
"hx:flex-col",
|
"hx:flex-col",
|
||||||
|
@ -4,3 +4,5 @@ blog: "وبلاگ"
|
|||||||
about: "درباره ما"
|
about: "درباره ما"
|
||||||
more: "بیشتر"
|
more: "بیشتر"
|
||||||
hugoDocs: "مستندات هیوگو ↖"
|
hugoDocs: "مستندات هیوگو ↖"
|
||||||
|
versions: "نسخهها"
|
||||||
|
development: "آخرین نسخه توسعهای"
|
||||||
|
@ -4,3 +4,5 @@ blog: "ブログ"
|
|||||||
about: "概要"
|
about: "概要"
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
hugoDocs: "Hugo ドキュメント ↗"
|
hugoDocs: "Hugo ドキュメント ↗"
|
||||||
|
versions: "バージョン"
|
||||||
|
development: "最新の開発版"
|
||||||
|
@ -4,3 +4,5 @@ blog: "博客"
|
|||||||
about: "关于"
|
about: "关于"
|
||||||
more: "更多"
|
more: "更多"
|
||||||
hugoDocs: "Hugo 文档 ↗"
|
hugoDocs: "Hugo 文档 ↗"
|
||||||
|
versions: "版本"
|
||||||
|
development: "最新开发版本"
|
||||||
|
@ -13,8 +13,11 @@
|
|||||||
{{ end -}}
|
{{ end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
|
|
||||||
<div class="nav-container hx:sticky hx:top-0 hx:z-20 hx:w-full hx:bg-transparent hx:print:hidden">
|
<div class="nav-container hx:sticky hx:top-0 hx:z-20 hx:w-full hx:bg-transparent hx:print:hidden">
|
||||||
<div class="nav-container-blur hx:pointer-events-none hx:absolute hx:z-[-1] hx:h-full hx:w-full hx:bg-white hx:dark:bg-dark hx:shadow-[0_2px_4px_rgba(0,0,0,.02),0_1px_0_rgba(0,0,0,.06)] hx:contrast-more:shadow-[0_0_0_1px_#000] hx:dark:shadow-[0_-1px_0_rgba(255,255,255,.1)_inset] hx:contrast-more:dark:shadow-[0_0_0_1px_#fff]"></div>
|
<div
|
||||||
|
class="nav-container-blur hx:pointer-events-none hx:absolute hx:z-[-1] hx:h-full hx:w-full hx:bg-white hx:dark:bg-dark hx:shadow-[0_2px_4px_rgba(0,0,0,.02),0_1px_0_rgba(0,0,0,.06)] hx:contrast-more:shadow-[0_0_0_1px_#000] hx:dark:shadow-[0_-1px_0_rgba(255,255,255,.1)_inset] hx:contrast-more:dark:shadow-[0_0_0_1px_#fff]"
|
||||||
|
></div>
|
||||||
|
|
||||||
<nav class="hextra-max-navbar-width hx:mx-auto hx:flex hx:items-center hx:justify-end hx:gap-2 hx:h-16 hx:px-6">
|
<nav class="hextra-max-navbar-width hx:mx-auto hx:flex hx:items-center hx:justify-end hx:gap-2 hx:h-16 hx:px-6">
|
||||||
<a class="hx:flex hx:items-center hx:hover:opacity-75 hx:ltr:mr-auto hx:rtl:ml-auto" href="{{ $logoLink }}">
|
<a class="hx:flex hx:items-center hx:hover:opacity-75 hx:ltr:mr-auto hx:rtl:ml-auto" href="{{ $logoLink }}">
|
||||||
@ -51,6 +54,46 @@
|
|||||||
{{- else -}}
|
{{- else -}}
|
||||||
{{- $active := or ($currentPage.HasMenuCurrent "main" .) ($currentPage.IsMenuCurrent "main" .) -}}
|
{{- $active := or ($currentPage.HasMenuCurrent "main" .) ($currentPage.IsMenuCurrent "main" .) -}}
|
||||||
{{- $activeClass := cond $active "hx:font-medium" "hx:text-gray-600 hx:hover:text-gray-800 hx:dark:text-gray-400 hx:dark:hover:text-gray-200" -}}
|
{{- $activeClass := cond $active "hx:font-medium" "hx:text-gray-600 hx:hover:text-gray-800 hx:dark:text-gray-400 hx:dark:hover:text-gray-200" -}}
|
||||||
|
|
||||||
|
{{- if .HasChildren -}}
|
||||||
|
{{/* Dropdown menu for items with children */}}
|
||||||
|
<div class="hx:relative hx:hidden hx:md:inline-block">
|
||||||
|
<button
|
||||||
|
title="{{ or (T .Identifier) .Name | safeHTML }}"
|
||||||
|
data-state="closed"
|
||||||
|
class="hextra-nav-menu-toggle hx:cursor-pointer hx:text-sm hx:contrast-more:text-gray-700 hx:contrast-more:dark:text-gray-100 hx:relative hx:-ml-2 hx:whitespace-nowrap hx:p-2 hx:flex hx:items-center hx:gap-1 {{ $activeClass }}"
|
||||||
|
type="button"
|
||||||
|
aria-label="{{ or (T .Identifier) .Name | safeHTML }}"
|
||||||
|
>
|
||||||
|
<span class="hx:text-center">{{ or (T .Identifier) .Name | safeHTML }}</span>
|
||||||
|
{{- partial "utils/icon.html" (dict "name" "chevron-down" "attributes" "height=12 class=\"hx:transition-transform hx:duration-200 hx:ease-in-out\"") -}}
|
||||||
|
</button>
|
||||||
|
<ul
|
||||||
|
class="hextra-nav-menu-items hx:hidden hx:z-20 hx:max-h-64 hx:overflow-auto hx:rounded-md hx:ring-1 hx:ring-black/5 hx:bg-white hx:py-1 hx:text-sm hx:shadow-lg hx:dark:ring-white/20 hx:dark:bg-neutral-800"
|
||||||
|
style="min-width: 100px;"
|
||||||
|
>
|
||||||
|
{{ range .Children }}
|
||||||
|
{{- $link := .URL -}}
|
||||||
|
{{- $external := strings.HasPrefix $link "http" -}}
|
||||||
|
{{- with .PageRef -}}
|
||||||
|
{{- if hasPrefix . "/" -}}
|
||||||
|
{{- $link = relLangURL (strings.TrimPrefix "/" .) -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
<li class="hextra-nav-menu-item hx:flex hx:flex-col">
|
||||||
|
<a
|
||||||
|
href="{{ $link }}"
|
||||||
|
{{ if $external }}target="_blank" rel="noreferrer"{{ end }}
|
||||||
|
class="hx:text-gray-600 hx:hover:text-gray-800 hx:dark:text-gray-400 hx:dark:hover:text-gray-200 hx:relative hx:cursor-pointer hx:whitespace-nowrap hx:py-1.5 hx:transition-colors hx:ltr:pl-3 hx:ltr:pr-9 hx:rtl:pr-3 hx:rtl:pl-9"
|
||||||
|
>
|
||||||
|
{{- or (T .Identifier) .Name | safeHTML -}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{- end -}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{- else -}}
|
||||||
|
{{/* Regular menu item without children */}}
|
||||||
<a
|
<a
|
||||||
title="{{ or (T .Identifier) .Name | safeHTML }}"
|
title="{{ or (T .Identifier) .Name | safeHTML }}"
|
||||||
href="{{ $link }}"
|
href="{{ $link }}"
|
||||||
@ -62,6 +105,7 @@
|
|||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
|
||||||
<button type="button" aria-label="Menu" class="hamburger-menu hx:cursor-pointer hx:-mr-2 hx:rounded-sm hx:p-2 hx:active:bg-gray-400/20 hx:md:hidden">
|
<button type="button" aria-label="Menu" class="hamburger-menu hx:cursor-pointer hx:-mr-2 hx:rounded-sm hx:p-2 hx:active:bg-gray-400/20 hx:md:hidden">
|
||||||
|
@ -2,12 +2,13 @@
|
|||||||
{{- $jsMenu := resources.Get "js/menu.js" -}}
|
{{- $jsMenu := resources.Get "js/menu.js" -}}
|
||||||
{{- $jsTabs := resources.Get "js/tabs.js" -}}
|
{{- $jsTabs := resources.Get "js/tabs.js" -}}
|
||||||
{{- $jsLang := resources.Get "js/lang.js" -}}
|
{{- $jsLang := resources.Get "js/lang.js" -}}
|
||||||
|
{{- $jsNavMenu := resources.Get "js/nav-menu.js" -}}
|
||||||
{{- $jsCodeCopy := resources.Get "js/code-copy.js" -}}
|
{{- $jsCodeCopy := resources.Get "js/code-copy.js" -}}
|
||||||
{{- $jsFileTree := resources.Get "js/filetree.js" -}}
|
{{- $jsFileTree := resources.Get "js/filetree.js" -}}
|
||||||
{{- $jsSidebar := resources.Get "js/sidebar.js" -}}
|
{{- $jsSidebar := resources.Get "js/sidebar.js" -}}
|
||||||
{{- $jsBackToTop := resources.Get "js/back-to-top.js" -}}
|
{{- $jsBackToTop := resources.Get "js/back-to-top.js" -}}
|
||||||
|
|
||||||
{{- $scripts := slice $jsTheme $jsMenu $jsCodeCopy $jsTabs $jsLang $jsFileTree $jsSidebar $jsBackToTop | resources.Concat "js/main.js" -}}
|
{{- $scripts := slice $jsTheme $jsMenu $jsCodeCopy $jsTabs $jsLang $jsNavMenu $jsFileTree $jsSidebar $jsBackToTop | resources.Concat "js/main.js" -}}
|
||||||
{{- if hugo.IsProduction -}}
|
{{- if hugo.IsProduction -}}
|
||||||
{{- $scripts = $scripts | minify | fingerprint -}}
|
{{- $scripts = $scripts | minify | fingerprint -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
Reference in New Issue
Block a user