70 lines
2.0 KiB
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>
|