diff --git a/src/client/app/router.ts b/src/client/app/router.ts index a1397018..00c98531 100644 --- a/src/client/app/router.ts +++ b/src/client/app/router.ts @@ -1,9 +1,9 @@ -import { reactive, inject, markRaw, nextTick, readonly } from 'vue' import type { Component, InjectionKey } from 'vue' +import { inject, markRaw, nextTick, reactive, readonly } from 'vue' +import type { Awaitable, PageData, PageDataPayload } from '../shared' import { notFoundPageData, treatAsHtml } from '../shared' -import type { PageData, PageDataPayload, Awaitable } from '../shared' -import { inBrowser, withBase } from './utils' import { siteDataRef } from './data' +import { getScrollOffset, inBrowser, withBase } from './utils' export interface Route { path: string @@ -261,26 +261,6 @@ export function scrollTo(el: Element, hash: string, smooth = false) { } if (target) { - let scrollOffset = siteDataRef.value.scrollOffset - let offset = 0 - let padding = 24 - if (typeof scrollOffset === 'object' && 'padding' in scrollOffset) { - padding = scrollOffset.padding - scrollOffset = scrollOffset.selector - } - if (typeof scrollOffset === 'number') { - offset = scrollOffset - } else if (typeof scrollOffset === 'string') { - offset = tryOffsetSelector(scrollOffset, padding) - } else if (Array.isArray(scrollOffset)) { - for (const selector of scrollOffset) { - const res = tryOffsetSelector(selector, padding) - if (res) { - offset = res - break - } - } - } const targetPadding = parseInt( window.getComputedStyle(target).paddingTop, 10 @@ -288,7 +268,7 @@ export function scrollTo(el: Element, hash: string, smooth = false) { const targetTop = window.scrollY + target.getBoundingClientRect().top - - offset + + getScrollOffset() + targetPadding function scrollToTarget() { // only smooth scroll if distance is smaller than screen height. @@ -300,14 +280,6 @@ export function scrollTo(el: Element, hash: string, smooth = false) { } } -function tryOffsetSelector(selector: string, padding: number): number { - const el = document.querySelector(selector) - if (!el) return 0 - const bot = el.getBoundingClientRect().bottom - if (bot < 0) return 0 - return bot + padding -} - function handleHMR(route: Route): void { // update route.data on HMR updates of active page if (import.meta.hot) { diff --git a/src/client/app/utils.ts b/src/client/app/utils.ts index 5028483c..dfa5cffc 100644 --- a/src/client/app/utils.ts +++ b/src/client/app/utils.ts @@ -107,3 +107,36 @@ export function defineClientComponent( } } } + +export function getScrollOffset() { + let scrollOffset = siteDataRef.value.scrollOffset + let offset = 0 + let padding = 24 + if (typeof scrollOffset === 'object' && 'padding' in scrollOffset) { + padding = scrollOffset.padding + scrollOffset = scrollOffset.selector + } + if (typeof scrollOffset === 'number') { + offset = scrollOffset + } else if (typeof scrollOffset === 'string') { + offset = tryOffsetSelector(scrollOffset, padding) + } else if (Array.isArray(scrollOffset)) { + for (const selector of scrollOffset) { + const res = tryOffsetSelector(selector, padding) + if (res) { + offset = res + break + } + } + } + + return offset +} + +function tryOffsetSelector(selector: string, padding: number): number { + const el = document.querySelector(selector) + if (!el) return 0 + const bot = el.getBoundingClientRect().bottom + if (bot < 0) return 0 + return bot + padding +} diff --git a/src/client/theme-default/composables/outline.ts b/src/client/theme-default/composables/outline.ts index 8ec4f3e5..1b9137fb 100644 --- a/src/client/theme-default/composables/outline.ts +++ b/src/client/theme-default/composables/outline.ts @@ -1,8 +1,9 @@ import type { DefaultTheme } from 'vitepress/theme' import { onMounted, onUnmounted, onUpdated, type Ref } from 'vue' +import { getScrollOffset } from '../../app/utils' import type { Header } from '../../shared' -import { useAside } from './aside' import { throttleAndDebounce } from '../support/utils' +import { useAside } from './aside' // cached list of anchor elements from resolveHeaders const resolvedHeaders: { element: HTMLHeadElement; link: string }[] = [] @@ -179,7 +180,7 @@ export function useActiveAnchor( // find the last header above the top of viewport let activeLink: string | null = null for (const { link, top } of headers) { - if (top > scrollY + offsetDocTop) { + if (top > scrollY + offsetDocTop + getScrollOffset()) { break } activeLink = link