mirror of
https://github.com/HoshinoSuzumi/rayine-ui.git
synced 2025-04-07 11:38:50 +08:00
93 lines
2.1 KiB
Vue
93 lines
2.1 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed } from 'vue'
|
|
|
|
interface TocItem {
|
|
id: string
|
|
depth: number
|
|
text: string
|
|
children?: TocItem[]
|
|
}
|
|
|
|
const props = defineProps<{
|
|
toc: TocItem[]
|
|
maxDepth?: number
|
|
}>()
|
|
|
|
const maxDepth = props.maxDepth ?? 3
|
|
|
|
const filteredToc = computed(() => {
|
|
const filterByDepth = (items: TocItem[]): TocItem[] => {
|
|
return items
|
|
.filter(item => item.depth <= maxDepth)
|
|
.map(item => ({
|
|
...item,
|
|
children: item.children ? filterByDepth(item.children) : undefined,
|
|
}))
|
|
}
|
|
|
|
return filterByDepth(props.toc)
|
|
})
|
|
|
|
const activeLinks = ref<string[]>([])
|
|
|
|
onMounted(() => {
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
activeLinks.value.push(entry.target.id)
|
|
}
|
|
else {
|
|
activeLinks.value = activeLinks.value.filter(link => link !== entry.target.id)
|
|
}
|
|
})
|
|
},
|
|
// { rootMargin: '0px 0px -80% 0px' }
|
|
)
|
|
|
|
filteredToc.value.forEach((item) => {
|
|
const element = document.getElementById(item.id)
|
|
if (element) {
|
|
observer.observe(element)
|
|
}
|
|
item.children?.forEach((child) => {
|
|
const childElement = document.getElementById(child.id)
|
|
if (childElement) {
|
|
observer.observe(childElement)
|
|
}
|
|
})
|
|
})
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<ul>
|
|
<template v-for="item in filteredToc" :key="item.id">
|
|
<li>
|
|
<NuxtLink :href="'#' + item.id" class="link" :class="{ active: activeLinks.includes(item.id) }">
|
|
{{ item.text }}
|
|
</NuxtLink>
|
|
<ul v-if="item.children && item.children.length" class="ml-4">
|
|
<template v-for="child in item.children" :key="child.id">
|
|
<li>
|
|
<NuxtLink :href="'#' + child.id" class="link" :class="{ active: activeLinks.includes(child.id) }">
|
|
{{ child.text }}
|
|
</NuxtLink>
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.link {
|
|
@apply text-xs text-neutral-400 dark:text-neutral-500 font-medium;
|
|
}
|
|
|
|
.link.active {
|
|
@apply text-primary;
|
|
}
|
|
</style>
|