diff --git a/__tests__/client/theme-default/composables/outline.spec.ts b/__tests__/client/theme-default/composables/outline.spec.ts new file mode 100644 index 00000000..93568926 --- /dev/null +++ b/__tests__/client/theme-default/composables/outline.spec.ts @@ -0,0 +1,166 @@ +import { describe, test, expect } from 'vitest' +import * as outline from 'client/theme-default/composables/outline' + +describe('client/theme-default/composables/outline', () => { + describe('resolveHeader', () => { + test('levels range', () => { + expect( + outline.resolveHeaders( + [ + { + level: 2, + title: 'h2 - 1', + link: '#h2-1' + }, + { + level: 3, + title: 'h3 - 1', + link: '#h3-1' + } + ], + [2, 3] + ) + ).toEqual([ + { + level: 2, + title: 'h2 - 1', + link: '#h2-1', + children: [ + { + level: 3, + title: 'h3 - 1', + link: '#h3-1' + } + ] + } + ]) + }) + + test('specific level', () => { + expect( + outline.resolveHeaders( + [ + { + level: 2, + title: 'h2 - 1', + link: '#h2-1' + }, + { + level: 3, + title: 'h3 - 1', + link: '#h3-1' + } + ], + 2 + ) + ).toEqual([ + { + level: 2, + title: 'h2 - 1', + link: '#h2-1' + } + ]) + }) + + test('complex deep', () => { + expect( + outline.resolveHeaders( + [ + { + level: 2, + title: 'h2 - 1', + link: '#h2-1' + }, + { + level: 3, + title: 'h3 - 1', + link: '#h3-1' + }, + { + level: 4, + title: 'h4 - 1', + link: '#h4-1' + }, + { + level: 3, + title: 'h3 - 2', + link: '#h3-2' + }, + { + level: 4, + title: 'h4 - 2', + link: '#h4-2' + }, + { + level: 2, + title: 'h2 - 2', + link: '#h2-2' + }, + { + level: 3, + title: 'h3 - 3', + link: '#h3-3' + }, + { + level: 4, + title: 'h4 - 3', + link: '#h4-3' + } + ], + 'deep' + ) + ).toEqual([ + { + level: 2, + title: 'h2 - 1', + link: '#h2-1', + children: [ + { + level: 3, + title: 'h3 - 1', + link: '#h3-1', + children: [ + { + level: 4, + title: 'h4 - 1', + link: '#h4-1' + } + ] + }, + { + level: 3, + title: 'h3 - 2', + link: '#h3-2', + children: [ + { + level: 4, + title: 'h4 - 2', + link: '#h4-2' + } + ] + } + ] + }, + { + level: 2, + title: 'h2 - 2', + link: '#h2-2', + children: [ + { + level: 3, + title: 'h3 - 3', + link: '#h3-3', + children: [ + { + level: 4, + title: 'h4 - 3', + link: '#h4-3' + } + ] + } + ] + } + ]) + }) + }) +}) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 037c3064..a24c1be1 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -9,6 +9,12 @@ export default defineConfig({ lastUpdated: true, cleanUrls: 'without-subfolders', + markdown: { + headers: { + level: [0, 0] + } + }, + themeConfig: { nav: nav(), diff --git a/docs/config/frontmatter-configs.md b/docs/config/frontmatter-configs.md index ef7b54e3..6a0c4e0d 100644 --- a/docs/config/frontmatter-configs.md +++ b/docs/config/frontmatter-configs.md @@ -212,3 +212,10 @@ If you want the right aside component in `doc` layout not to be shown, set this aside: false --- ``` + +## outline + +- Type: `number | [number, number] | 'deep' | false` +- Default: `2` + +The levels of header in the outline to display for the page. It's same as [config.themeConfig.outline](../config/theme-configs#outline), and it overrides the theme config. diff --git a/docs/config/theme-configs.md b/docs/config/theme-configs.md index c43edcd1..70b24df3 100644 --- a/docs/config/theme-configs.md +++ b/docs/config/theme-configs.md @@ -135,6 +135,13 @@ interface SidebarItem { } ``` +## outline + +- Type: `number | [number, number] | 'deep' | false` +- Default: `2` + +The levels of header to display in the outline. You can specify a particular level by passing a number, or you can provide a level range by passing a tuple containing the bottom and upper limits. When passing `'deep'` which equals `[2, 6]`, all header levels are shown in the outline except `h1`. Set `false` to hide outline. + ## outlineTitle - Type: `string` diff --git a/examples/configured/__test__/outline.spec.ts b/examples/configured/__test__/outline.spec.ts index 3d003be3..542e8ae4 100644 --- a/examples/configured/__test__/outline.spec.ts +++ b/examples/configured/__test__/outline.spec.ts @@ -18,15 +18,27 @@ describe('outline', () => { expect(outlineLinksContent).toEqual([ 'h2 - 1', 'h3 - 1', + 'h4 - 1', 'h3 - 2', + 'h4 - 2', 'h2 - 2', - 'h3 - 3' + 'h3 - 3', + 'h4 - 3' ]) const linkHrefs = await outlineLinksLocator.evaluateAll((element) => element.map((element) => element.getAttribute('href')) ) - expect(linkHrefs).toEqual(['#h2-1', '#h3-1', '#h3-2', '#h2-2', '#h3-3']) + expect(linkHrefs).toEqual([ + '#h2-1', + '#h3-1', + '#h4-1', + '#h3-2', + '#h4-2', + '#h2-2', + '#h3-3', + '#h4-3' + ]) }) }) diff --git a/src/client/app/components/Content.ts b/src/client/app/components/Content.ts index 2a3c3762..148d9e11 100644 --- a/src/client/app/components/Content.ts +++ b/src/client/app/components/Content.ts @@ -1,10 +1,16 @@ -import { defineComponent, h } from 'vue' +import { defineComponent, h, onUpdated } from 'vue' import { useRoute } from '../router.js' export const Content = defineComponent({ name: 'VitePressContent', - setup() { + props: { + onContentUpdated: Function + }, + setup(props) { const route = useRoute() + onUpdated(() => { + props.onContentUpdated?.() + }) return () => h('div', { style: { position: 'relative' } }, [ route.component ? h(route.component) : null diff --git a/src/client/theme-default/components/VPDoc.vue b/src/client/theme-default/components/VPDoc.vue index 7b29fd37..53322ba6 100644 --- a/src/client/theme-default/components/VPDoc.vue +++ b/src/client/theme-default/components/VPDoc.vue @@ -1,6 +1,6 @@