mirror of
https://github.com/imfing/hextra.git
synced 2025-06-19 02:04:17 -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:
|
||||
passthrough:
|
||||
delimiters:
|
||||
block: [['\[', '\]'], ['$$', '$$']]
|
||||
block: [['\[', '\]'], ["$$", "$$"]]
|
||||
inline: [['\(', '\)']]
|
||||
enable: true
|
||||
|
||||
@ -70,9 +70,8 @@ menu:
|
||||
name: Documentation
|
||||
pageRef: /docs
|
||||
weight: 1
|
||||
- identifier: showcase
|
||||
name: Showcase
|
||||
pageRef: /showcase
|
||||
- identifier: versions
|
||||
name: Versions
|
||||
weight: 2
|
||||
- identifier: blog
|
||||
name: Blog
|
||||
@ -82,15 +81,27 @@ menu:
|
||||
name: About
|
||||
pageRef: /about
|
||||
weight: 4
|
||||
- name: Search
|
||||
- identifier: showcase
|
||||
name: Showcase
|
||||
pageRef: /showcase
|
||||
weight: 5
|
||||
- name: Search
|
||||
weight: 6
|
||||
params:
|
||||
type: search
|
||||
- name: GitHub
|
||||
weight: 6
|
||||
weight: 7
|
||||
url: "https://github.com/imfing/hextra"
|
||||
params:
|
||||
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:
|
||||
- identifier: more
|
||||
@ -163,7 +174,7 @@ params:
|
||||
# date | lastmod | publishDate | title | weight
|
||||
sortBy: date
|
||||
sortOrder: desc # or "asc"
|
||||
|
||||
|
||||
article:
|
||||
displayPagination: true
|
||||
|
||||
|
@ -121,6 +121,9 @@
|
||||
"hextra-max-footer-width",
|
||||
"hextra-max-navbar-width",
|
||||
"hextra-max-page-width",
|
||||
"hextra-nav-menu-item",
|
||||
"hextra-nav-menu-items",
|
||||
"hextra-nav-menu-toggle",
|
||||
"hextra-pdf",
|
||||
"hextra-scrollbar",
|
||||
"hextra-sidebar-collapsible-button",
|
||||
@ -311,6 +314,7 @@
|
||||
"hx:duration-200",
|
||||
"hx:duration-75",
|
||||
"hx:ease-in",
|
||||
"hx:ease-in-out",
|
||||
"hx:first:mt-0",
|
||||
"hx:flex",
|
||||
"hx:flex-col",
|
||||
|
@ -4,3 +4,5 @@ blog: "وبلاگ"
|
||||
about: "درباره ما"
|
||||
more: "بیشتر"
|
||||
hugoDocs: "مستندات هیوگو ↖"
|
||||
versions: "نسخهها"
|
||||
development: "آخرین نسخه توسعهای"
|
||||
|
@ -3,4 +3,6 @@ showcase: "展示"
|
||||
blog: "ブログ"
|
||||
about: "概要"
|
||||
more: "もっと見る"
|
||||
hugoDocs: "Hugo ドキュメント ↗"
|
||||
hugoDocs: "Hugo ドキュメント ↗"
|
||||
versions: "バージョン"
|
||||
development: "最新の開発版"
|
||||
|
@ -4,3 +4,5 @@ blog: "博客"
|
||||
about: "关于"
|
||||
more: "更多"
|
||||
hugoDocs: "Hugo 文档 ↗"
|
||||
versions: "版本"
|
||||
development: "最新开发版本"
|
||||
|
@ -13,8 +13,11 @@
|
||||
{{ 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-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">
|
||||
<a class="hx:flex hx:items-center hx:hover:opacity-75 hx:ltr:mr-auto hx:rtl:ml-auto" href="{{ $logoLink }}">
|
||||
@ -51,14 +54,55 @@
|
||||
{{- else -}}
|
||||
{{- $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" -}}
|
||||
<a
|
||||
title="{{ or (T .Identifier) .Name | safeHTML }}"
|
||||
href="{{ $link }}"
|
||||
{{ if $external }}target="_blank" rel="noreferrer"{{ end }}
|
||||
class="hx:text-sm hx:contrast-more:text-gray-700 hx:contrast-more:dark:text-gray-100 hx:relative hx:-ml-2 hx:hidden hx:whitespace-nowrap hx:p-2 hx:md:inline-block {{ $activeClass }}"
|
||||
>
|
||||
<span class="hx:text-center">{{ or (T .Identifier) .Name | safeHTML }}</span>
|
||||
</a>
|
||||
|
||||
{{- 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
|
||||
title="{{ or (T .Identifier) .Name | safeHTML }}"
|
||||
href="{{ $link }}"
|
||||
{{ if $external }}target="_blank" rel="noreferrer"{{ end }}
|
||||
class="hx:text-sm hx:contrast-more:text-gray-700 hx:contrast-more:dark:text-gray-100 hx:relative hx:-ml-2 hx:hidden hx:whitespace-nowrap hx:p-2 hx:md:inline-block {{ $activeClass }}"
|
||||
>
|
||||
<span class="hx:text-center">{{ or (T .Identifier) .Name | safeHTML }}</span>
|
||||
</a>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
@ -2,12 +2,13 @@
|
||||
{{- $jsMenu := resources.Get "js/menu.js" -}}
|
||||
{{- $jsTabs := resources.Get "js/tabs.js" -}}
|
||||
{{- $jsLang := resources.Get "js/lang.js" -}}
|
||||
{{- $jsNavMenu := resources.Get "js/nav-menu.js" -}}
|
||||
{{- $jsCodeCopy := resources.Get "js/code-copy.js" -}}
|
||||
{{- $jsFileTree := resources.Get "js/filetree.js" -}}
|
||||
{{- $jsSidebar := resources.Get "js/sidebar.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 -}}
|
||||
{{- $scripts = $scripts | minify | fingerprint -}}
|
||||
{{- end -}}
|
||||
|
Reference in New Issue
Block a user