Skip to content

Commit

Permalink
fix(theme): misaligned outline indicator (#3458)
Browse files Browse the repository at this point in the history
  • Loading branch information
brc-dd committed Jan 16, 2024
1 parent 2b13bc9 commit 0ce5ece
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 92 deletions.
60 changes: 3 additions & 57 deletions 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 }
Expand Down
60 changes: 60 additions & 0 deletions 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' }
}
})
36 changes: 4 additions & 32 deletions 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
Expand Down Expand Up @@ -261,34 +261,14 @@ 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
)
const targetTop =
window.scrollY +
target.getBoundingClientRect().top -
offset +
getScrollOffset() +
targetPadding
function scrollToTarget() {
// only smooth scroll if distance is smaller than screen height.
Expand All @@ -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) {
Expand Down
33 changes: 33 additions & 0 deletions src/client/app/utils.ts
Expand Up @@ -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
}
3 changes: 2 additions & 1 deletion src/client/index.ts
Expand Up @@ -20,7 +20,8 @@ export {
inBrowser,
onContentUpdated,
defineClientComponent,
withBase
withBase,
getScrollOffset
} from './app/utils'

// components
Expand Down
5 changes: 3 additions & 2 deletions 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 }[] = []
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 0ce5ece

Please sign in to comment.