Files
Portfolio/src/components/SiteNavigation.vue

70 lines
2.0 KiB
Vue

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue";
import type { SectionId } from "@/content/types";
import type { JourneySnapshot } from "@/journey/types";
const props = defineProps<{
items: ReadonlyArray<{ id: SectionId; label: string }>;
journey: JourneySnapshot;
onSectionLinkClick: (event: MouseEvent, requestSign?: boolean) => void;
}>();
const navigationLabels: Readonly<Record<SectionId, string>> = {
start: "Start",
profile: "Profile",
experience: "Career",
infrastructure: "Infra",
"selected-builds": "Builds",
"additional-work": "More work",
toolkit: "Toolkit",
contact: "Contact",
};
const disclosure = ref<HTMLDetailsElement | null>(null);
let compactQuery: MediaQueryList | null = null;
const syncDisclosureToViewport = ({
matches,
}: Pick<MediaQueryList, "matches">): void => {
if (matches) disclosure.value?.removeAttribute("open");
else disclosure.value?.setAttribute("open", "");
};
onMounted(() => {
compactQuery = window.matchMedia("(max-width: 767px)");
syncDisclosureToViewport(compactQuery);
compactQuery.addEventListener("change", syncDisclosureToViewport);
});
onBeforeUnmount(() => {
compactQuery?.removeEventListener("change", syncDisclosureToViewport);
compactQuery = null;
});
const navigate = (event: MouseEvent): void => {
props.onSectionLinkClick(event, true);
if (window.matchMedia("(max-width: 767px)").matches)
disclosure.value?.removeAttribute("open");
};
</script>
<template>
<nav class="site-navigation" aria-label="Portfolio sections">
<details ref="disclosure" class="site-navigation__disclosure" open>
<summary>Navigate</summary>
<ul>
<li v-for="item in items" :key="item.id">
<a
:href="`#${item.id}`"
:aria-current="
props.journey.activeSectionId === item.id ? 'page' : undefined
"
@click="navigate"
>{{ navigationLabels[item.id] }}</a
>
</li>
</ul>
</details>
</nav>
</template>