diff --git a/docs/tools/helper/client.md b/docs/tools/helper/client.md index b08be8439e..0a01e9400d 100644 --- a/docs/tools/helper/client.md +++ b/docs/tools/helper/client.md @@ -71,7 +71,7 @@ locale.value // '标题' Get headers from current page. ```ts -export const getHeaders: (options: GetHeadersOptions) => MenuItem[] +export const getHeaders: (options: GetHeadersOptions) => HeaderItem[] ``` **Params:** @@ -112,7 +112,7 @@ export interface GetHeadersOptions { **Result:** ```ts -export interface Header { +interface PageHeader { /** * The level of the header * @@ -138,14 +138,14 @@ export interface Header { /** * The children of the header */ - children: Header[] + children: MarkdownItHeader[] } export type HeaderLevels = number | 'deep' | false | [number, number] -export type MenuItem = Omit & { - element: HTMLHeadElement - children?: MenuItem[] +export type HeaderItem = Omit & { + element: HTMLHeadingElement + children?: HeaderItem[] } ``` diff --git a/docs/zh/tools/helper/client.md b/docs/zh/tools/helper/client.md index 0e8069978b..57c3c2e813 100644 --- a/docs/zh/tools/helper/client.md +++ b/docs/zh/tools/helper/client.md @@ -71,7 +71,7 @@ locale.value // '标题' 获取当前页面指定的 标题列表。 ```ts -export const getHeaders: (options: GetHeadersOptions) => MenuItem[] +export const getHeaders: (options: GetHeadersOptions) => HeaderItem[] ``` **参数:** @@ -112,7 +112,7 @@ export interface GetHeadersOptions { **返回结果:** ```ts -export interface Header { +interface PageHeader { /** * 当前标题的层级 * @@ -143,9 +143,9 @@ export interface Header { export type HeaderLevels = number | 'deep' | false | [number, number] -export type MenuItem = Omit & { - element: HTMLHeadElement - children?: MenuItem[] +export type HeaderItem = Omit & { + element: HTMLHeadingElement + children?: HeaderItem[] } ``` diff --git a/themes/theme-default/src/client/composables/useHeaders.ts b/themes/theme-default/src/client/composables/useHeaders.ts index 597132cfcc..d8c58821d8 100644 --- a/themes/theme-default/src/client/composables/useHeaders.ts +++ b/themes/theme-default/src/client/composables/useHeaders.ts @@ -1,5 +1,5 @@ import { useThemeLocaleData } from '@theme/useThemeData' -import type { MenuItem } from '@vuepress/helper/client' +import type { HeaderItem } from '@vuepress/helper/client' import { getHeaders } from '@vuepress/helper/client' import { injectLocal, provideLocal, watchImmediate } from '@vueuse/core' import type { InjectionKey, Ref } from 'vue' @@ -7,7 +7,7 @@ import { computed, onMounted, ref } from 'vue' import { usePageFrontmatter, useRoutePath } from 'vuepress/client' import type { DefaultThemeNormalPageFrontmatter } from '../../shared/index.js' -export type HeadersRef = Ref +export type HeadersRef = Ref export const headersSymbol: InjectionKey = Symbol('headers') diff --git a/themes/theme-default/src/client/composables/useSidebarItems.ts b/themes/theme-default/src/client/composables/useSidebarItems.ts index 1fb23c5876..09b5cab501 100644 --- a/themes/theme-default/src/client/composables/useSidebarItems.ts +++ b/themes/theme-default/src/client/composables/useSidebarItems.ts @@ -2,7 +2,7 @@ import { resolveAutoLink } from '@theme/resolveAutoLink' import { resolvePrefix } from '@theme/resolvePrefix' import { useHeaders } from '@theme/useHeaders' import { useThemeLocaleData } from '@theme/useThemeData' -import type { MenuItem } from '@vuepress/helper/client' +import type { HeaderItem } from '@vuepress/helper/client' import { isLinkRelative, keys, startsWith } from '@vuepress/helper/client' import type { ComputedRef, InjectionKey } from 'vue' import { computed, inject, provide } from 'vue' @@ -28,7 +28,7 @@ import type { SidebarHeaderItem, SidebarItem } from '../typings.js' * Util to transform page header to sidebar item */ export const resolveSidebarHeaderItem = ( - header: MenuItem, + header: HeaderItem, ): SidebarHeaderItem => ({ text: header.title, link: header.link, @@ -37,7 +37,7 @@ export const resolveSidebarHeaderItem = ( }) export const resolveSidebarHeaderItems = ( - headers?: MenuItem[], + headers?: HeaderItem[], ): SidebarHeaderItem[] => headers ? headers.map((header) => resolveSidebarHeaderItem(header)) : [] @@ -46,7 +46,7 @@ export const resolveSidebarHeaderItems = ( */ export const resolveSidebarHeadingItem = ( page: PageData, - headers: MenuItem[], + headers: HeaderItem[], ): SidebarItem[] => [ { text: page.title, @@ -59,7 +59,7 @@ export const resolveSidebarHeadingItem = ( */ export const resolveArraySidebarItems = ( sidebarConfig: SidebarArrayOptions, - headers: MenuItem[], + headers: HeaderItem[], path: string, prefix = '', ): SidebarItem[] => { @@ -112,7 +112,7 @@ export const resolveArraySidebarItems = ( export const resolveMultiSidebarItems = ( sidebarConfig: SidebarObjectOptions, page: PageData, - headers: MenuItem[], + headers: HeaderItem[], path: string, ): SidebarItem[] => { const sidebarRoutes = keys(sidebarConfig).sort((x, y) => y.length - x.length) @@ -161,7 +161,7 @@ export const resolveSidebarItems = ( page: PageData, path: string, routeLocale: string, - headers: MenuItem[], + headers: HeaderItem[], ): SidebarItem[] => { // resolve sidebar items according to the config if (sidebarConfig === false) { diff --git a/tools/helper/src/client/utils/getHeaders.ts b/tools/helper/src/client/utils/getHeaders.ts index 439e26b6d0..9b115aedaf 100644 --- a/tools/helper/src/client/utils/getHeaders.ts +++ b/tools/helper/src/client/utils/getHeaders.ts @@ -1,43 +1,16 @@ -export interface Header { - /** - * The level of the header - * - * `1` to `6` for `

` to `

` - */ - level: number - /** - * The title of the header - */ - title: string - /** - * The slug of the header - * - * Typically the `id` attr of the header anchor - */ - slug: string - /** - * Link of the header - * - * Typically using `#${slug}` as the anchor hash - */ - link: string - /** - * The children of the header - */ - children: Header[] -} +import type { PageHeader } from 'vuepress/shared' export type HeaderLevels = number | 'deep' | false | [number, number] -export type MenuItem = Omit & { - element: HTMLHeadElement - children?: MenuItem[] +export type HeaderItem = Omit & { + element: HTMLHeadingElement + children?: HeaderItem[] } export const resolveHeaders = ( - headers: MenuItem[], + headers: HeaderItem[], levels: HeaderLevels = 2, -): MenuItem[] => { +): HeaderItem[] => { if (levels === false) { return [] } @@ -49,29 +22,30 @@ export const resolveHeaders = ( ? [2, 6] : levels const allowedHeaders = headers.filter( - (h) => h.level >= high && h.level <= low, + (header) => header.level >= high && header.level <= low, ) - const res: MenuItem[] = [] + const result: HeaderItem[] = [] // eslint-disable-next-line no-restricted-syntax outer: for (let i = 0; i < allowedHeaders.length; i++) { - const cur = allowedHeaders[i] + const current = allowedHeaders[i] + if (i === 0) { - res.push(cur) + result.push(current) } else { for (let j = i - 1; j >= 0; j--) { const prev = allowedHeaders[j] - if (prev.level < cur.level) { - ;(prev.children ??= []).push(cur) + if (prev.level < current.level) { + ;(prev.children ??= []).push(current) continue outer } } - res.push(cur) + result.push(current) } } - return res + return result } const serializeHeader = (h: Element, ignore: string[] = []): string => { @@ -92,6 +66,20 @@ const serializeHeader = (h: Element, ignore: string[] = []): string => { return text.trim() } +export const getHeadersFromDom = ( + selector: string, + ignore: string[], +): HeaderItem[] => + Array.from(document.querySelectorAll(selector)) + .filter((el) => el.id && el.hasChildNodes()) + .map((el) => ({ + element: el as HTMLHeadingElement, + title: serializeHeader(el, ignore), + link: `#${el.id}`, + slug: el.id, + level: Number(el.tagName[1]), + })) + export interface GetHeadersOptions { /** * The selector of the headers. @@ -134,18 +122,5 @@ export const getHeaders = ({ .join(','), levels = 2, ignore = [], -}: GetHeadersOptions = {}): MenuItem[] => { - const headers = Array.from(document.querySelectorAll(selector)) - .filter((el) => el.id && el.hasChildNodes()) - .map((el) => { - const level = Number(el.tagName[1]) - return { - element: el as HTMLHeadElement, - title: serializeHeader(el, ignore), - link: `#${el.id}`, - slug: el.id, - level, - } - }) - return resolveHeaders(headers, levels) -} +}: GetHeadersOptions = {}): HeaderItem[] => + resolveHeaders(getHeadersFromDom(selector, ignore), levels)