From c2b4c66e79fde7479f5f43841e1921a5c220c9a5 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:48:14 +0530 Subject: [PATCH 1/4] fix(client): add computed dir and lang to html root instead of accessing raw siteData.lang/dir fixes https://github.com/vuejs/vitepress/pull/3353#issuecomment-1874753809 --- src/client/app/data.ts | 2 +- src/client/app/index.ts | 6 +++--- src/node/build/render.ts | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/client/app/data.ts b/src/client/app/data.ts index ccca8123..7e802956 100644 --- a/src/client/app/data.ts +++ b/src/client/app/data.ts @@ -44,8 +44,8 @@ export interface VitePressData { title: Ref description: Ref lang: Ref - isDark: Ref dir: Ref + isDark: Ref localeIndex: Ref } diff --git a/src/client/app/index.ts b/src/client/app/index.ts index a7db0c23..57cfe15a 100644 --- a/src/client/app/index.ts +++ b/src/client/app/index.ts @@ -38,13 +38,13 @@ const Theme = resolveThemeExtends(RawTheme) const VitePressApp = defineComponent({ name: 'VitePressApp', setup() { - const { site } = useData() + const { site, lang, dir } = useData() // change the language on the HTML element based on the current lang onMounted(() => { watchEffect(() => { - document.documentElement.lang = site.value.lang - document.documentElement.dir = site.value.dir + document.documentElement.lang = lang.value + document.documentElement.dir = dir.value }) }) diff --git a/src/node/build/render.ts b/src/node/build/render.ts index 92768185..7aacff68 100644 --- a/src/node/build/render.ts +++ b/src/node/build/render.ts @@ -152,8 +152,10 @@ export async function renderPage( } } + const dir = pageData.frontmatter.dir || siteData.dir || 'ltr' + const html = ` - + ${ From 017395fea820ad2d172d53fa6a05880c759ed87c Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:55:02 +0530 Subject: [PATCH 2/4] chore: minor tweaks --- src/client/app/data.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/client/app/data.ts b/src/client/app/data.ts index 7e802956..c75f79e7 100644 --- a/src/client/app/data.ts +++ b/src/client/app/data.ts @@ -45,8 +45,8 @@ export interface VitePressData { description: Ref lang: Ref dir: Ref - isDark: Ref localeIndex: Ref + isDark: Ref } // site data is a singleton @@ -89,14 +89,12 @@ export function initData(route: Route): VitePressData { frontmatter: computed(() => route.data.frontmatter), params: computed(() => route.data.params), lang: computed(() => site.value.lang), - dir: computed(() => route.data.frontmatter.dir || site.value.dir || 'ltr'), + dir: computed(() => route.data.frontmatter.dir || site.value.dir), localeIndex: computed(() => site.value.localeIndex || 'root'), - title: computed(() => { - return createTitle(site.value, route.data) - }), - description: computed(() => { - return route.data.description || site.value.description - }), + title: computed(() => createTitle(site.value, route.data)), + description: computed( + () => route.data.description || site.value.description + ), isDark } } From 36544707e09b40d691f2b268891608a7f6d633fa Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Wed, 3 Jan 2024 04:42:12 -0500 Subject: [PATCH 3/4] refactor(theme): improve robustness and readability of outline component (#3368) --- .../theme-default/composables/outline.ts | 105 ++++++++++-------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/src/client/theme-default/composables/outline.ts b/src/client/theme-default/composables/outline.ts index 59ee7447..8ec4f3e5 100644 --- a/src/client/theme-default/composables/outline.ts +++ b/src/client/theme-default/composables/outline.ts @@ -4,10 +4,11 @@ import type { Header } from '../../shared' import { useAside } from './aside' import { throttleAndDebounce } from '../support/utils' -// magic number to avoid repeated retrieval -const PAGE_OFFSET = 71 +// cached list of anchor elements from resolveHeaders +const resolvedHeaders: { element: HTMLHeadElement; link: string }[] = [] export type MenuItem = Omit & { + element: HTMLHeadElement children?: MenuItem[] } @@ -29,6 +30,7 @@ export function getHeaders(range: DefaultTheme.Config['outline']) { .map((el) => { const level = Number(el.tagName[1]) return { + element: el as HTMLHeadElement, title: serializeHeader(el), link: '#' + el.id, level @@ -78,6 +80,12 @@ export function resolveHeaders( : levelsRange headers = headers.filter((h) => h.level >= high && h.level <= low) + // clear previous caches + resolvedHeaders.length = 0 + // update global header list for active link rendering + for (const { element, link } of headers) { + resolvedHeaders.push({ element, link }) + } const ret: MenuItem[] = [] outer: for (let i = 0; i < headers.length; i++) { @@ -128,40 +136,55 @@ export function useActiveAnchor( return } - const links = [].slice.call( - container.value.querySelectorAll('.outline-link') - ) as HTMLAnchorElement[] - - const anchors = [].slice - .call(document.querySelectorAll('.content .header-anchor')) - .filter((anchor: HTMLAnchorElement) => { - return links.some((link) => { - return link.hash === anchor.hash && anchor.offsetParent !== null - }) - }) as HTMLAnchorElement[] + // pixel offset, start of main content + const offsetDocTop = (() => { + const container = + document.querySelector('#VPContent .VPDoc')?.firstElementChild + if (container) return getAbsoluteTop(container as HTMLElement) + else return 78 + })() const scrollY = window.scrollY const innerHeight = window.innerHeight const offsetHeight = document.body.offsetHeight const isBottom = Math.abs(scrollY + innerHeight - offsetHeight) < 1 - // page bottom - highlight last one - if (anchors.length && isBottom) { - activateLink(anchors[anchors.length - 1].hash) + // resolvedHeaders may be repositioned, hidden or fix positioned + const headers = resolvedHeaders + .map(({ element, link }) => ({ + link, + top: getAbsoluteTop(element) + })) + .filter(({ top }) => !Number.isNaN(top)) + .sort((a, b) => a.top - b.top) + + // no headers available for active link + if (!headers.length) { + activateLink(null) return } - for (let i = 0; i < anchors.length; i++) { - const anchor = anchors[i] - const nextAnchor = anchors[i + 1] + // page top + if (scrollY < 1) { + activateLink(null) + return + } - const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor) + // page bottom - highlight last link + if (isBottom) { + activateLink(headers[headers.length - 1].link) + return + } - if (isActive) { - activateLink(hash) - return + // find the last header above the top of viewport + let activeLink: string | null = null + for (const { link, top } of headers) { + if (top > scrollY + offsetDocTop) { + break } + activeLink = link } + activateLink(activeLink) } function activateLink(hash: string | null) { @@ -190,28 +213,18 @@ export function useActiveAnchor( } } -function getAnchorTop(anchor: HTMLAnchorElement): number { - return anchor.parentElement!.offsetTop - PAGE_OFFSET -} - -function isAnchorActive( - index: number, - anchor: HTMLAnchorElement, - nextAnchor: HTMLAnchorElement | undefined -): [boolean, string | null] { - const scrollTop = window.scrollY - - if (index === 0 && scrollTop === 0) { - return [true, null] - } - - if (scrollTop < getAnchorTop(anchor)) { - return [false, null] - } - - if (!nextAnchor || scrollTop < getAnchorTop(nextAnchor)) { - return [true, anchor.hash] +function getAbsoluteTop(element: HTMLElement): number { + let offsetTop = 0 + while (element !== document.body) { + if (element === null) { + // child element is: + // - not attached to the DOM (display: none) + // - set to fixed position (not scrollable) + // - body or html element (null offsetParent) + return NaN + } + offsetTop += element.offsetTop + element = element.offsetParent as HTMLElement } - - return [false, null] + return offsetTop } From 737251e2b453bef5635c4c1116eecd66c0e90c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=83=BD=E5=AE=81?= Date: Wed, 3 Jan 2024 18:03:38 +0800 Subject: [PATCH 4/4] docs(zh): minor improvement (#3404) --- docs/zh/guide/markdown.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/zh/guide/markdown.md b/docs/zh/guide/markdown.md index 93db97e0..674b7c2e 100644 --- a/docs/zh/guide/markdown.md +++ b/docs/zh/guide/markdown.md @@ -625,11 +625,11 @@ const line4 = 'This is line 4' ```md <<< @/snippets/snippet.cs{c#} - + <<< @/snippets/snippet.cs{1,2,4-6 c#} - + <<< @/snippets/snippet.cs{1,2,4-6 c#:line-numbers} ```