Files
atridotdad/src/components/NavigationBar.vue
2026-01-25 00:26:45 -07:00

124 lines
3.7 KiB
Vue

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from "vue";
import { config } from "../config";
import type { Component } from "vue";
const props = defineProps<{
currentPath: string;
}>();
const isVisible = ref(true);
const isScrolling = ref(false);
const currentClientPath = ref(props.currentPath);
const enabledNavigationItems = config.navigationItems.filter(
(item) => item.enabled !== false,
);
const activePath = computed(() => currentClientPath.value);
const normalizedPath = computed(() => {
const path = activePath.value;
return path.endsWith("/") && path.length > 1 ? path.slice(0, -1) : path;
});
const updatePath = () => {
if (typeof window !== "undefined") {
currentClientPath.value = window.location.pathname;
}
};
let lastScrollY = 0;
let ticking = false;
let scrollTimer: ReturnType<typeof setTimeout> | undefined;
const updateScroll = () => {
const currentScrollY = window.scrollY;
if (currentScrollY < 50) {
isVisible.value = true;
} else {
if (Math.abs(currentScrollY - lastScrollY) > 0) {
isVisible.value = currentScrollY < lastScrollY;
}
}
lastScrollY = currentScrollY;
ticking = false;
};
const onScroll = () => {
isScrolling.value = true;
if (scrollTimer) clearTimeout(scrollTimer);
scrollTimer = setTimeout(() => {
isScrolling.value = false;
}, 200);
if (!ticking) {
window.requestAnimationFrame(updateScroll);
ticking = true;
}
};
onMounted(() => {
updatePath();
lastScrollY = window.scrollY;
document.addEventListener("astro:page-load", updatePath);
document.addEventListener("astro:after-swap", updatePath);
window.addEventListener("popstate", updatePath);
window.addEventListener("scroll", onScroll, { passive: true });
});
onUnmounted(() => {
document.removeEventListener("astro:page-load", updatePath);
document.removeEventListener("astro:after-swap", updatePath);
window.removeEventListener("popstate", updatePath);
window.removeEventListener("scroll", onScroll);
if (scrollTimer) clearTimeout(scrollTimer);
});
</script>
<template>
<div
class="fixed bottom-3 sm:bottom-4 left-1/2 transform -translate-x-1/2 z-20 transition-all duration-300"
:class="[
isScrolling ? 'opacity-30' : 'opacity-100',
isVisible ? 'translate-y-0' : 'translate-y-20',
]"
>
<div class="overflow-visible">
<ul
class="menu menu-horizontal bg-base-200 rounded-box border border-solid border-primary p-1.5 sm:p-2 flex flex-nowrap whitespace-nowrap"
>
<li
v-for="item in enabledNavigationItems"
:key="item.id"
class="mx-0.5 sm:mx-1"
>
<a
:href="item.path"
class="tooltip tooltip-top min-h-11 min-w-11 inline-flex items-center justify-center"
:class="{
'menu-active': item.isActive
? item.isActive(normalizedPath)
: normalizedPath === item.path,
}"
:aria-label="item.tooltip"
:data-tip="item.tooltip"
data-astro-prefetch="hover"
>
<component
:is="item.icon as Component"
:size="18"
class="sm:w-5 sm:h-5"
/>
<span class="sr-only">{{ item.name }}</span>
</a>
</li>
</ul>
</div>
</div>
</template>