Skip to content

Commit

Permalink
fix(helper): fix getHeaders function (#342)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mister-Hope authored Jan 16, 2025
1 parent bcf4a89 commit df653e7
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 76 deletions.
12 changes: 6 additions & 6 deletions docs/tools/helper/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**
Expand Down Expand Up @@ -112,7 +112,7 @@ export interface GetHeadersOptions {
**Result:**

```ts
export interface Header {
interface PageHeader {
/**
* The level of the header
*
Expand All @@ -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<Header, 'children' | 'slug'> & {
element: HTMLHeadElement
children?: MenuItem[]
export type HeaderItem = Omit<PageHeader, 'children'> & {
element: HTMLHeadingElement
children?: HeaderItem[]
}
```
Expand Down
10 changes: 5 additions & 5 deletions docs/zh/tools/helper/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ locale.value // '标题'
获取当前页面指定的 标题列表。

```ts
export const getHeaders: (options: GetHeadersOptions) => MenuItem[]
export const getHeaders: (options: GetHeadersOptions) => HeaderItem[]
```
**参数:**
Expand Down Expand Up @@ -112,7 +112,7 @@ export interface GetHeadersOptions {
**返回结果:**

```ts
export interface Header {
interface PageHeader {
/**
* 当前标题的层级
*
Expand Down Expand Up @@ -143,9 +143,9 @@ export interface Header {

export type HeaderLevels = number | 'deep' | false | [number, number]

export type MenuItem = Omit<Header, 'children' | 'slug'> & {
element: HTMLHeadElement
children?: MenuItem[]
export type HeaderItem = Omit<PageHeader, 'children'> & {
element: HTMLHeadingElement
children?: HeaderItem[]
}
```
Expand Down
4 changes: 2 additions & 2 deletions themes/theme-default/src/client/composables/useHeaders.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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'
import { computed, onMounted, ref } from 'vue'
import { usePageFrontmatter, useRoutePath } from 'vuepress/client'
import type { DefaultThemeNormalPageFrontmatter } from '../../shared/index.js'

export type HeadersRef = Ref<MenuItem[]>
export type HeadersRef = Ref<HeaderItem[]>

export const headersSymbol: InjectionKey<HeadersRef> = Symbol('headers')

Expand Down
14 changes: 7 additions & 7 deletions themes/theme-default/src/client/composables/useSidebarItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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,
Expand All @@ -37,7 +37,7 @@ export const resolveSidebarHeaderItem = (
})

export const resolveSidebarHeaderItems = (
headers?: MenuItem[],
headers?: HeaderItem[],
): SidebarHeaderItem[] =>
headers ? headers.map((header) => resolveSidebarHeaderItem(header)) : []

Expand All @@ -46,7 +46,7 @@ export const resolveSidebarHeaderItems = (
*/
export const resolveSidebarHeadingItem = (
page: PageData,
headers: MenuItem[],
headers: HeaderItem[],
): SidebarItem[] => [
{
text: page.title,
Expand All @@ -59,7 +59,7 @@ export const resolveSidebarHeadingItem = (
*/
export const resolveArraySidebarItems = (
sidebarConfig: SidebarArrayOptions,
headers: MenuItem[],
headers: HeaderItem[],
path: string,
prefix = '',
): SidebarItem[] => {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down
87 changes: 31 additions & 56 deletions tools/helper/src/client/utils/getHeaders.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,16 @@
export interface Header {
/**
* The level of the header
*
* `1` to `6` for `<h1>` to `<h6>`
*/
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<Header, 'children' | 'slug'> & {
element: HTMLHeadElement
children?: MenuItem[]
export type HeaderItem = Omit<PageHeader, 'children'> & {
element: HTMLHeadingElement
children?: HeaderItem[]
}

export const resolveHeaders = (
headers: MenuItem[],
headers: HeaderItem[],
levels: HeaderLevels = 2,
): MenuItem[] => {
): HeaderItem[] => {
if (levels === false) {
return []
}
Expand All @@ -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 => {
Expand All @@ -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.
Expand Down Expand Up @@ -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)

0 comments on commit df653e7

Please sign in to comment.