45 lines
1.1 KiB
Vue
45 lines
1.1 KiB
Vue
<script setup lang="ts">
|
|
import type { SectionId } from '@/content/types';
|
|
|
|
defineProps<{ items: ReadonlyArray<{ id: SectionId; label: string }> }>();
|
|
|
|
const focusActivatedSection = (event: MouseEvent): void => {
|
|
if (
|
|
event.defaultPrevented ||
|
|
event.button !== 0 ||
|
|
event.altKey ||
|
|
event.ctrlKey ||
|
|
event.metaKey ||
|
|
event.shiftKey ||
|
|
!(event.currentTarget instanceof HTMLAnchorElement)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
let sectionId: string;
|
|
try {
|
|
sectionId = decodeURIComponent(event.currentTarget.hash.slice(1));
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
const section = document.getElementById(sectionId);
|
|
const heading = section?.querySelector<HTMLElement>(
|
|
':scope > h2[tabindex="-1"]',
|
|
);
|
|
if (heading === undefined || heading === null) return;
|
|
|
|
requestAnimationFrame(() => heading.focus({ preventScroll: true }));
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<nav class="site-navigation" aria-label="Portfolio sections">
|
|
<ul>
|
|
<li v-for="item in items" :key="item.id">
|
|
<a :href="`#${item.id}`" @click="focusActivatedSection">{{ item.label }}</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</template>
|