blog / src /components /layout /NavMenuPanel.astro
cacode's picture
Upload 434 files
96dd062 verified
---
import { Icon } from "astro-icon/components";
import { LinkPresets } from "@/constants/link-presets";
import { LinkPreset, type NavBarLink } from "@/types/config";
import { url } from "@/utils/url-utils";
interface Props {
links: NavBarLink[];
}
// 处理links中的LinkPreset转换
const processedLinks = Astro.props.links.map((link: NavBarLink) => ({
...link,
children: link.children?.map((child: NavBarLink | LinkPreset): NavBarLink => {
if (typeof child === "number") {
return LinkPresets[child];
}
return child;
}),
}));
---
<div id="nav-menu-panel" class:list={["float-panel float-panel-closed absolute transition-all fixed right-4 px-2 py-2 max-h-[80vh] overflow-y-auto"]}>
{processedLinks.map((link) => (
<div class="mobile-menu-item">
{link.children && link.children.length > 0 ? (
<!-- 有子菜单的项目 -->
<div class="mobile-dropdown" data-mobile-dropdown>
<button
class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8 w-full text-left
hover:bg-(--btn-plain-bg-hover) active:bg-(--btn-plain-bg-active) transition"
data-mobile-dropdown-trigger
aria-expanded="false"
>
<div class="flex items-center transition text-black/75 dark:text-white/75 font-bold group-hover:text-(--primary) group-active:text-(--primary)">
{link.icon && <Icon name={link.icon} class="text-[1.1rem] mr-2" />}
{link.name}
</div>
<Icon name="material-symbols:keyboard-arrow-down-rounded"
class="transition text-[1.25rem] text-(--primary) mobile-dropdown-arrow duration-200"
/>
</button>
<div class="mobile-submenu" data-mobile-submenu>
{link.children.map((child) => (
<a href={child.external ? child.url : url(child.url)}
class="group flex justify-between items-center py-2 pl-6 pr-1 rounded-lg gap-8
hover:bg-(--btn-plain-bg-hover) active:bg-(--btn-plain-bg-active) transition"
target={child.external ? "_blank" : null}
>
<div class="flex items-center transition text-black/60 dark:text-white/60 font-medium group-hover:text-(--primary) group-active:text-(--primary)">
{child.icon && <Icon name={child.icon} class="text-[1.1rem] mr-2" />}
{child.name}
</div>
{child.external && <Icon name="fa7-solid:arrow-up-right-from-square"
class="transition text-[0.75rem] text-black/25 dark:text-white/25 -translate-x-1"
/>}
</a>
))}
</div>
</div>
) : (
<!-- 普通链接项目 -->
<a href={link.external ? link.url : url(link.url)} class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-(--btn-plain-bg-hover) active:bg-(--btn-plain-bg-active) transition
"
target={link.external ? "_blank" : null}
>
<div class="flex items-center transition text-black/75 dark:text-white/75 font-bold group-hover:text-(--primary) group-active:text-(--primary)">
{link.icon && <Icon name={link.icon} class="text-[1.1rem] mr-2" />}
{link.name}
</div>
{!link.external && <Icon name="material-symbols:chevron-right-rounded"
class="transition text-[1.25rem] text-(--primary)"
/>}
{link.external && <Icon name="fa7-solid:arrow-up-right-from-square"
class="transition text-[0.75rem] text-black/25 dark:text-white/25 -translate-x-1"
/>}
</a>
)}
</div>
))}
</div>
<style>
@reference "../../styles/main.css";
.mobile-submenu {
@apply max-h-0 overflow-hidden transition-all duration-300 ease-in-out;
}
.mobile-dropdown[data-expanded="true"] .mobile-submenu {
@apply max-h-96;
}
.mobile-dropdown[data-expanded="true"] .mobile-dropdown-arrow {
@apply rotate-180;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const mobileDropdowns = document.querySelectorAll('[data-mobile-dropdown]');
mobileDropdowns.forEach(dropdown => {
const trigger = dropdown.querySelector('[data-mobile-dropdown-trigger]');
const submenu = dropdown.querySelector('[data-mobile-submenu]');
if (!trigger || !submenu) return;
trigger.addEventListener('click', function(e) {
e.preventDefault();
const isExpanded = dropdown.getAttribute('data-expanded') === 'true';
// 关闭其他打开的下拉菜单
mobileDropdowns.forEach(otherDropdown => {
if (otherDropdown !== dropdown) {
otherDropdown.setAttribute('data-expanded', 'false');
const otherTrigger = otherDropdown.querySelector('[data-mobile-dropdown-trigger]');
if (otherTrigger) {
otherTrigger.setAttribute('aria-expanded', 'false');
}
}
});
// 切换当前下拉菜单
const newState = !isExpanded;
dropdown.setAttribute('data-expanded', newState.toString());
trigger.setAttribute('aria-expanded', newState.toString());
});
});
});
</script>