Files
workspace/code/g3soft-libs/docs/.vitepress/theme/G3TocItem.vue
T
2026-10-09 17:32:14 +08:00

68 lines
2.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
/**
* TOC 条目(递归)—— 照抄 Docus / @nuxt/ui ContentToc 的 ul>li>a 结构。
*
* 实测(docus.dev,1440px / light):
* ul.list / ul.listWithChildren
* a.link display:flex; align-items:center; padding:4px 0;
* font-size:14px; line-height:20px; height:28px
* border-radius:4px(rounded-sm)
* color: text-muted
* a.link:active color: text-primary;font-weight 仍为 400
* h3 层级由父级 ul 的缩进体现(ms-3 = 12px)
*/
import { computed } from 'vue'
interface TocLink {
id: string
text: string
children?: TocLink[]
}
const props = defineProps<{
link: TocLink
level: number
activeIds: string[]
}>()
/** 点击时通知父级锁定该项为激活,避免滚动动画期间被 scrollspy 抢走 */
const emit = defineEmits<{ navigate: [id: string] }>()
const isActive = computed(() => props.activeIds.includes(props.link.id))
const linkClass = computed(() => ['g3-toc-link', isActive.value ? 'is-active' : ''])
function onClick(e: MouseEvent) {
const el = document.getElementById(props.link.id)
if (!el) return
e.preventDefault()
// 先锁定,再滚动:这样无论落点是多少,点击的项一定是高亮的
emit('navigate', props.link.id)
el.focus({ preventScroll: true })
// 滚到与 scrollspy 判据一致的位置:标题顶部对齐视口顶部下方一点,
// 使「绝对位置 <= scrollY + 余量」在滚动结束后必然成立。
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
history.replaceState(null, '', `#${props.link.id}`)
}
</script>
<template>
<li class="g3-toc-item">
<a :class="linkClass" :href="`#${link.id}`" @click="onClick">
<span class="g3-toc-link-text">{{ link.text }}</span>
</a>
<ul v-if="link.children?.length" class="g3-toc-list-with-children">
<G3TocItem
v-for="child in link.children"
:key="child.id"
:link="child"
:level="level + 1"
:active-ids="activeIds"
@navigate="emit('navigate', $event)"
/>
</ul>
</li>
</template>