From bfa921fac878efa25d02ee822449f2ee68c3601d Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:14:04 +0530 Subject: [PATCH] fix(theme): misaligned outline indicator --- docs/.vitepress/config/index.ts | 60 +------------------ docs/.vitepress/config/shared.ts | 60 +++++++++++++++++++ src/client/app/router.ts | 36 ++--------- src/client/app/utils.ts | 33 ++++++++++ src/client/index.ts | 3 +- .../theme-default/composables/outline.ts | 5 +- 6 files changed, 105 insertions(+), 92 deletions(-) create mode 100644 docs/.vitepress/config/shared.ts diff --git a/docs/.vitepress/config/index.ts b/docs/.vitepress/config/index.ts index 50f024d1..de218788 100644 --- a/docs/.vitepress/config/index.ts +++ b/docs/.vitepress/config/index.ts @@ -1,64 +1,10 @@ import { defineConfig } from 'vitepress' +import { shared } from './shared' import { en } from './en' -import { zh, search as zhSearch } from './zh' +import { zh } from './zh' export default defineConfig({ - title: 'VitePress', - - lastUpdated: true, - cleanUrls: true, - - markdown: { - math: true, - codeTransformers: [ - // We use `[!!code` in demo to prevent transformation, here we revert it back. - { - postprocess(code) { - return code.replace(/\[\!\!code/g, '[!code') - } - } - ] - }, - - sitemap: { - hostname: 'https://vitepress.dev', - transformItems(items) { - return items.filter((item) => !item.url.includes('migration')) - } - }, - - /* prettier-ignore */ - head: [ - ['link', { rel: 'icon', type: 'image/svg+xml', href: '/vitepress-logo-mini.svg' }], - ['link', { rel: 'icon', type: 'image/png', href: '/vitepress-logo-mini.png' }], - ['meta', { name: 'theme-color', content: '#5f67ee' }], - ['meta', { name: 'og:type', content: 'website' }], - ['meta', { name: 'og:locale', content: 'en' }], - ['meta', { name: 'og:site_name', content: 'VitePress' }], - ['meta', { name: 'og:image', content: 'https://vitepress.dev/vitepress-og.jpg' }], - ['script', { src: 'https://cdn.usefathom.com/script.js', 'data-site': 'AZBRSFGG', 'data-spa': 'auto', defer: '' }] - ], - - themeConfig: { - logo: { src: '/vitepress-logo-mini.svg', width: 24, height: 24 }, - - socialLinks: [ - { icon: 'github', link: 'https://github.com/vuejs/vitepress' } - ], - - search: { - provider: 'algolia', - options: { - appId: '8J64VVRP8K', - apiKey: 'a18e2f4cc5665f6602c5631fd868adfd', - indexName: 'vitepress', - locales: { ...zhSearch } - } - }, - - carbonAds: { code: 'CEBDT27Y', placement: 'vuejsorg' } - }, - + ...shared, locales: { root: { label: 'English', ...en }, zh: { label: '简体中文', ...zh } diff --git a/docs/.vitepress/config/shared.ts b/docs/.vitepress/config/shared.ts new file mode 100644 index 00000000..a99b7702 --- /dev/null +++ b/docs/.vitepress/config/shared.ts @@ -0,0 +1,60 @@ +import { defineConfig } from 'vitepress' +import { search as zhSearch } from './zh' + +export const shared = defineConfig({ + title: 'VitePress', + + lastUpdated: true, + cleanUrls: true, + + markdown: { + math: true, + codeTransformers: [ + // We use `[!!code` in demo to prevent transformation, here we revert it back. + { + postprocess(code) { + return code.replace(/\[\!\!code/g, '[!code') + } + } + ] + }, + + sitemap: { + hostname: 'https://vitepress.dev', + transformItems(items) { + return items.filter((item) => !item.url.includes('migration')) + } + }, + + /* prettier-ignore */ + head: [ + ['link', { rel: 'icon', type: 'image/svg+xml', href: '/vitepress-logo-mini.svg' }], + ['link', { rel: 'icon', type: 'image/png', href: '/vitepress-logo-mini.png' }], + ['meta', { name: 'theme-color', content: '#5f67ee' }], + ['meta', { name: 'og:type', content: 'website' }], + ['meta', { name: 'og:locale', content: 'en' }], + ['meta', { name: 'og:site_name', content: 'VitePress' }], + ['meta', { name: 'og:image', content: 'https://vitepress.dev/vitepress-og.jpg' }], + ['script', { src: 'https://cdn.usefathom.com/script.js', 'data-site': 'AZBRSFGG', 'data-spa': 'auto', defer: '' }] + ], + + themeConfig: { + logo: { src: '/vitepress-logo-mini.svg', width: 24, height: 24 }, + + socialLinks: [ + { icon: 'github', link: 'https://github.com/vuejs/vitepress' } + ], + + search: { + provider: 'algolia', + options: { + appId: '8J64VVRP8K', + apiKey: 'a18e2f4cc5665f6602c5631fd868adfd', + indexName: 'vitepress', + locales: { ...zhSearch } + } + }, + + carbonAds: { code: 'CEBDT27Y', placement: 'vuejsorg' } + } +}) 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/index.ts b/src/client/index.ts index 072adba3..5fde6c5d 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -20,7 +20,8 @@ export { inBrowser, onContentUpdated, defineClientComponent, - withBase + withBase, + getScrollOffset } from './app/utils' // components diff --git a/src/client/theme-default/composables/outline.ts b/src/client/theme-default/composables/outline.ts index 8ec4f3e5..e097a2b7 100644 --- a/src/client/theme-default/composables/outline.ts +++ b/src/client/theme-default/composables/outline.ts @@ -1,8 +1,9 @@ +import { getScrollOffset } from 'vitepress' import type { DefaultTheme } from 'vitepress/theme' import { onMounted, onUnmounted, onUpdated, type Ref } from 'vue' 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