123 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import Link from "next/link";
import React, { useEffect, useState } from "react";
import { usePathname } from "next/navigation";
// !text-[var(--current-color)]
export default function Nav({ color = "#fab758" }) {
const [menu, setMenu] = useState([]);
const pathname = usePathname();
useEffect(() => {
// Dynamically import Bootstrap
import("bootstrap").then((Bootstrap) => {
const CLASS_NAME = "has-child-dropdown-show";
// Save the original toggle function
const originalToggle = Bootstrap.Dropdown.prototype.toggle;
// Override the toggle function
Bootstrap.Dropdown.prototype.toggle = function () {
// Remove the CLASS_NAME from all dropdowns
document.querySelectorAll("." + CLASS_NAME).forEach(function (e) {
e.classList.remove(CLASS_NAME);
});
// Traverse up through the closest dropdown parents
let dd = this._element
.closest(".dropdown")
.parentNode.closest(".dropdown");
for (; dd && dd !== document; dd = dd.parentNode.closest(".dropdown")) {
dd.classList.add(CLASS_NAME);
}
// Call the original toggle function
return originalToggle.call(this);
};
// Add event listeners for hide.bs.dropdown to remove the CLASS_NAME
document.querySelectorAll(".dropdown").forEach(function (dd) {
dd.addEventListener("hide.bs.dropdown", function (e) {
if (this.classList.contains(CLASS_NAME)) {
this.classList.remove(CLASS_NAME);
e.preventDefault();
}
e.stopPropagation();
});
});
});
// 获取菜单数据
fetch("/api/get-menu")
.then((res) => res.json())
.then((data) => setMenu(data.menu || []));
// Optional cleanup function for any potential side effects
return () => {
// Cleanup code here (if needed)
};
}, []);
// 只在顶级菜单过滤 position
const filterHeaderMenu = (items) => {
return items.filter(item => item.position === 'Header');
};
// 递归渲染菜单
const renderMenu = (items, level = 0) => {
// 顶级菜单过滤 position
if (level === 0) items = filterHeaderMenu(items);
return items.map((item) => {
const hasChildren = item.children && item.children.length > 0;
const hasHref = !!item.href && item.href !== "#";
if (hasChildren) {
return (
<li key={item.id} className={`nav-item dropdown${level > 0 ? " dropdown-submenu dropend" : ""}`}>
<div style={{ display: 'flex', alignItems: 'center' }}>
{/* 如果有href左侧为可点击跳转始终用当前item的href */}
{hasHref && (
<Link
className={`${level > 0 ? "dropdown-item" : "nav-link"} !text-[.7rem] !tracking-[normal] hover:!text-[var(--current-color)] after:!text-[var(--current-color)] ${pathname === item.href ? "!text-[var(--current-color)]" : ""}`}
href={item.href}
style={{ paddingRight: 0 }}
>
{item.label}
</Link>
)}
{/* 右侧小三角,负责展开下拉 */}
<a
className={`${hasHref ? "dropdown-toggle" : (level > 0 ? "dropdown-item" : "nav-link") + " dropdown-toggle"} !text-[.7rem] !tracking-[normal] hover:!text-[var(--current-color)] after:!text-[var(--current-color)] ${!hasHref && pathname === item.href ? "!text-[var(--current-color)]" : ""}`}
href="#"
data-bs-toggle="dropdown"
aria-expanded="false"
style={hasHref ? { paddingLeft: 8, minWidth: 24 } : {}}
>
{hasHref ? <span className="sr-only">展开</span> : item.label}
</a>
</div>
<ul className={`dropdown-menu${level > 0 ? " submenu" : ""}`}>
{renderMenu(item.children, level + 1)}
</ul>
</li>
);
} else {
return (
<li key={item.id} className="nav-item">
<Link
className={`${level > 0 ? "dropdown-item" : "nav-link"} !text-[.7rem] !tracking-[normal] hover:!text-[var(--current-color)] after:!text-[var(--current-color)] ${pathname === item.href ? "!text-[var(--current-color)]" : ""}`}
href={item.href || "#"}
>
{item.label}
</Link>
</li>
);
}
});
};
return (
<ul className="navbar-nav" style={{ "--current-color": color }}>
{renderMenu(menu)}
</ul>
);
}