Merge branch 'main' into chore/replace-fast-glob-with-tinyglobby

pull/4132/head
Cristopher 1 year ago committed by GitHub
commit 87c4448807
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,5 +1,11 @@
import { resolveHeaders } from 'client/theme-default/composables/outline'
const element = {
classList: {
contains: () => false
}
} as unknown as HTMLHeadElement
describe('client/theme-default/composables/outline', () => {
describe('resolveHeader', () => {
test('levels range', () => {
@ -9,12 +15,14 @@ describe('client/theme-default/composables/outline', () => {
{
level: 2,
title: 'h2 - 1',
link: '#h2-1'
link: '#h2-1',
element
},
{
level: 3,
title: 'h3 - 1',
link: '#h3-1'
link: '#h3-1',
element
}
],
[2, 3]
@ -28,9 +36,12 @@ describe('client/theme-default/composables/outline', () => {
{
level: 3,
title: 'h3 - 1',
link: '#h3-1'
link: '#h3-1',
children: [],
element
}
]
],
element
}
])
})
@ -42,12 +53,14 @@ describe('client/theme-default/composables/outline', () => {
{
level: 2,
title: 'h2 - 1',
link: '#h2-1'
link: '#h2-1',
element
},
{
level: 3,
title: 'h3 - 1',
link: '#h3-1'
link: '#h3-1',
element
}
],
2
@ -56,7 +69,9 @@ describe('client/theme-default/composables/outline', () => {
{
level: 2,
title: 'h2 - 1',
link: '#h2-1'
link: '#h2-1',
children: [],
element
}
])
})
@ -68,42 +83,50 @@ describe('client/theme-default/composables/outline', () => {
{
level: 2,
title: 'h2 - 1',
link: '#h2-1'
link: '#h2-1',
element
},
{
level: 3,
title: 'h3 - 1',
link: '#h3-1'
link: '#h3-1',
element
},
{
level: 4,
title: 'h4 - 1',
link: '#h4-1'
link: '#h4-1',
element
},
{
level: 3,
title: 'h3 - 2',
link: '#h3-2'
link: '#h3-2',
element
},
{
level: 4,
title: 'h4 - 2',
link: '#h4-2'
link: '#h4-2',
element
},
{
level: 2,
title: 'h2 - 2',
link: '#h2-2'
link: '#h2-2',
element
},
{
level: 3,
title: 'h3 - 3',
link: '#h3-3'
link: '#h3-3',
element
},
{
level: 4,
title: 'h4 - 3',
link: '#h4-3'
link: '#h4-3',
element
}
],
'deep'
@ -122,9 +145,12 @@ describe('client/theme-default/composables/outline', () => {
{
level: 4,
title: 'h4 - 1',
link: '#h4-1'
link: '#h4-1',
children: [],
element
}
]
],
element
},
{
level: 3,
@ -134,11 +160,15 @@ describe('client/theme-default/composables/outline', () => {
{
level: 4,
title: 'h4 - 2',
link: '#h4-2'
link: '#h4-2',
children: [],
element
}
]
],
element
}
]
],
element
},
{
level: 2,
@ -153,11 +183,15 @@ describe('client/theme-default/composables/outline', () => {
{
level: 4,
title: 'h4 - 3',
link: '#h4-3'
link: '#h4-3',
children: [],
element
}
]
],
element
}
]
],
element
}
])
})

@ -161,6 +161,12 @@ aside: false
The levels of header in the outline to display for the page. It's same as [config.themeConfig.outline.level](./default-theme-config#outline), and it overrides the value set in site-level config.
```yaml
---
outline: [2, 4]
---
```
### lastUpdated
- Type: `boolean | Date`

@ -107,6 +107,10 @@ interface Router {
* updated). Return `false` to cancel the navigation.
*/
onBeforePageLoad?: (to: string) => Awaitable<void | boolean>
/**
* Called after the page component is loaded (before the page component is updated).
*/
onAfterPageLoad?: (to: string) => Awaitable<void>
/**
* Called after the route changes.
*/

@ -102,6 +102,10 @@ interface Router {
* 在页面组件加载前history 状态更新后)调用。返回 `false` 表示取消导航
*/
onBeforePageLoad?: (to: string) => Awaitable<void | boolean>
/**
* 在页面组件加载后(页面组件实际更新前)调用
*/
onAfterPageLoad?: (to: string) => Awaitable<void>
/**
* 在路由更改后调用
*/

@ -29,6 +29,10 @@ export interface Router {
* updated). Return `false` to cancel the navigation.
*/
onBeforePageLoad?: (to: string) => Awaitable<void | boolean>
/**
* Called after the page component is loaded (before the page component is updated).
*/
onAfterPageLoad?: (to: string) => Awaitable<void>
/**
* Called after the route changes.
*/
@ -94,6 +98,8 @@ export function createRouter(
throw new Error(`Invalid route component: ${comp}`)
}
await router.onAfterPageLoad?.(href)
route.path = inBrowser ? pendingPath : withBase(pendingPath)
route.component = markRaw(comp)
route.data = import.meta.env.PROD

@ -13,7 +13,7 @@ export type MenuItem = Omit<Header, 'slug' | 'children'> & {
children?: MenuItem[]
}
export function resolveTitle(theme: DefaultTheme.Config) {
export function resolveTitle(theme: DefaultTheme.Config): string {
return (
(typeof theme.outline === 'object' &&
!Array.isArray(theme.outline) &&
@ -23,7 +23,7 @@ export function resolveTitle(theme: DefaultTheme.Config) {
)
}
export function getHeaders(range: DefaultTheme.Config['outline']) {
export function getHeaders(range: DefaultTheme.Config['outline']): MenuItem[] {
const headers = [
...document.querySelectorAll('.VPDoc :where(h1,h2,h3,h4,h5,h6)')
]
@ -80,38 +80,13 @@ export function resolveHeaders(
? [2, 6]
: 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++) {
const cur = headers[i]
if (i === 0) {
ret.push(cur)
} else {
for (let j = i - 1; j >= 0; j--) {
const prev = headers[j]
if (prev.level < cur.level) {
;(prev.children || (prev.children = [])).push(cur)
continue outer
}
}
ret.push(cur)
}
}
return ret
return buildTree(headers, high, low)
}
export function useActiveAnchor(
container: Ref<HTMLElement>,
marker: Ref<HTMLElement>
) {
): void {
const { isAsideEnabled } = useAside()
const onScroll = throttleAndDebounce(setActiveLink, 100)
@ -221,3 +196,38 @@ function getAbsoluteTop(element: HTMLElement): number {
}
return offsetTop
}
function buildTree(data: MenuItem[], min: number, max: number): MenuItem[] {
resolvedHeaders.length = 0
const result: MenuItem[] = []
const stack: (MenuItem | { level: number; shouldIgnore: true })[] = []
data.forEach((item) => {
const node = { ...item, children: [] }
let parent = stack[stack.length - 1]
while (parent && parent.level >= node.level) {
stack.pop()
parent = stack[stack.length - 1]
}
if (
node.element.classList.contains('ignore-header') ||
(parent && 'shouldIgnore' in parent)
) {
stack.push({ level: node.level, shouldIgnore: true })
return
}
if (node.level > max || node.level < min) return
resolvedHeaders.push({ element: node.element, link: node.link })
if (parent) parent.children!.push(node)
else result.push(node)
stack.push(node)
})
return result
}

@ -16,7 +16,7 @@ import type { MarkdownEnv } from '../../shared'
* captures: ['/path/to/file.extension', 'extension', '#region', '{meta}', '[title]']
*/
export const rawPathRegexp =
/^(.+?(?:(?:\.([a-z0-9]+))?))(?:(#[\w-]+))?(?: ?(?:{(\d+(?:[,-]\d+)*)? ?(\S+)?}))? ?(?:\[(.+)\])?$/
/^(.+?(?:(?:\.([a-z0-9]+))?))(?:(#[\w-]+))?(?: ?(?:{(\d+(?:[,-]\d+)*)? ?(\S+)? ?(\S+)?}))? ?(?:\[(.+)\])?$/
export function rawPathToToken(rawPath: string) {
const [
@ -25,12 +25,13 @@ export function rawPathToToken(rawPath: string) {
region = '',
lines = '',
lang = '',
attrs = '',
rawTitle = ''
] = (rawPathRegexp.exec(rawPath) || []).slice(1)
const title = rawTitle || filepath.split('/').pop() || ''
return { filepath, extension, region, lines, lang, title }
return { filepath, extension, region, lines, lang, attrs, title }
}
export function dedent(text: string): string {
@ -126,7 +127,7 @@ export const snippetPlugin = (md: MarkdownIt, srcDir: string) => {
.replace(/^@/, srcDir)
.trim()
const { filepath, extension, region, lines, lang, title } =
const { filepath, extension, region, lines, lang, attrs, title } =
rawPathToToken(rawPath)
state.line = startLine + 1
@ -134,7 +135,7 @@ export const snippetPlugin = (md: MarkdownIt, srcDir: string) => {
const token = state.push('fence', 'code', 0)
token.info = `${lang || extension}${lines ? `{${lines}}` : ''}${
title ? `[${title}]` : ''
}`
} ${attrs ?? ''}`
const { realPath, path: _path } = state.env as MarkdownEnv
const resolvedPath = path.resolve(path.dirname(realPath ?? _path), filepath)

Loading…
Cancel
Save