vitepress icon indicating copy to clipboard operation
vitepress copied to clipboard

feat(theme): support nested headings in outline

Open fi3ework opened this issue 3 years ago • 13 comments

resolve #954

  • [x] no breaking change
  • [x] test added
  • [x] new frontmatter config added

fi3ework avatar Jul 11 '22 17:07 fi3ework

@brc-dd Friendly ping. Could you take a review or is there something else I could provide to this PR to ensure the functionality?

fi3ework avatar Jul 13 '22 06:07 fi3ework

Hi! Kia will have a look on this one.

brc-dd avatar Jul 13 '22 06:07 brc-dd

Rest of the stuff seems fine. I still haven't checked the UI though.

brc-dd avatar Jul 13 '22 07:07 brc-dd

Made an update with a new commit and some refactoring. And I think I made another breaking change 😅.

before:

There would be an outline title without items but only a title when the content does not have h2/h3 headings, such as ##### xx.

now:

Outline will only be displayed when there are really outline items to display.

fi3ework avatar Jul 13 '22 17:07 fi3ework

Rest of the stuff seems fine. I still haven't checked the UI though.

The UI is like this, I'm also wondering is there a better way to release a preview build for custom config and docs that can reveal the different UI.

image

fi3ework avatar Jul 13 '22 17:07 fi3ework

Made an update with a new commit and some refactoring. And I think I made another breaking change 😅.

before:

There would be an outline title without items but only a title when the content does not have h2/h3 headings, such as ##### xx.

This was probably a bug 😅. Thanks for fixing that! 🙌

brc-dd avatar Jul 13 '22 17:07 brc-dd

I created a stackblitz project to preview the affection on UI.

  • project https://github-b4nsvg--3000.local.webcontainer.io/
  • live show https://github-b4nsvg--3000.local.webcontainer.io/guide/what-is-vitepress.html

I think we can do more things to make preview more easy in two ways:

  1. Provide a stackblitz like which will be created from the branch of the PR via a comment by GitHub bot for each PR automatically. So the reviewer can know the UI affection by changing some file or the PR author could provide the demo as above.
  2. Add example docs that contain different use cases of Vitepress which could also be used by E2E test, and deploy a preview link by Netlify for each PR.

fi3ework avatar Jul 16 '22 16:07 fi3ework

@fi3ework Is there a way not to add this on every file: outline: "deep", otherwise it would be too much trouble

sishen654 avatar Jul 29 '22 09:07 sishen654

@fi3ework Is there a way not to add this on every file: outline: "deep", otherwise it would be too much trouble

Yes, it should support. Seems like I lost some code with the app configuration. 😅 I'll bring it back later.

fi3ework avatar Jul 29 '22 10:07 fi3ework

@fi3ework Is there a way not to add this on every file: outline: "deep", otherwise it would be too much trouble

Yes, it should support. Seems like I lost some code with the app configuration. 😅 I'll bring it back later. Haha, trouble you,😁😁😁

sishen654 avatar Jul 29 '22 14:07 sishen654

Ready to be reviewed again.


I kind of want to add preview deploy for examples before this PR. But multiple sites deploy Netlify (doc) require setting permission to add a new site for the example site that I don't have. If there is any vuejs member who has permission also thinks of a preview deployment of examples to facilitate observing UI changes of code, let me know and I would like to come up with the PR with your help of setting Netlify.

fi3ework avatar Jul 30 '22 10:07 fi3ework

would it make sense to add a max depth in this pr?

trwnh avatar Aug 01 '22 14:08 trwnh

would it make sense to add a max depth in this pr?

max depth is supported in this PR. Using like this - [2, 4]

fi3ework avatar Aug 01 '22 15:08 fi3ework

Can we fix #785 here itself? I can fix that separately too, but then this would need rebase and might cause other issues. Here is the diff on vite-3 branch. You will need to modify then manually apply it. That query selector h2 thing probably need to be made h2, h3, h4, h5, h6. Also probably we need something like .innerText.split('\n')[0] instead of textContent.

diff --git a/src/client/theme-default/components/VPDocAside.vue b/src/client/theme-default/components/VPDocAside.vue
index 4232d15..2f590ab 100644
--- a/src/client/theme-default/components/VPDocAside.vue
+++ b/src/client/theme-default/components/VPDocAside.vue
@@ -3,7 +3,7 @@ import { useData } from 'vitepress'
 import VPDocAsideOutline from './VPDocAsideOutline.vue'
 import VPDocAsideCarbonAds from './VPDocAsideCarbonAds.vue'
 
-const { page, theme } = useData()
+const { theme } = useData()
 </script>
 
 <template>
@@ -11,7 +11,7 @@ const { page, theme } = useData()
     <slot name="aside-top" />
 
     <slot name="aside-outline-before" />
-    <VPDocAsideOutline v-if="page.headers.length" />
+    <ClientOnly><VPDocAsideOutline /></ClientOnly>
     <slot name="aside-outline-after" />
 
     <div class="spacer" />
diff --git a/src/client/theme-default/components/VPDocAsideOutline.vue b/src/client/theme-default/components/VPDocAsideOutline.vue
index dad4378..42acb27 100644
--- a/src/client/theme-default/components/VPDocAsideOutline.vue
+++ b/src/client/theme-default/components/VPDocAsideOutline.vue
@@ -1,25 +1,35 @@
 <script setup lang="ts">
-import { ref, computed } from 'vue'
-import { useData } from 'vitepress'
-import {
-  resolveHeaders,
-  useOutline,
-  useActiveAnchor
-} from '../composables/outline.js'
-
-const { page, frontmatter, theme } = useData()
-
-const { hasOutline } = useOutline()
+import { useData, useRoute, type Header } from 'vitepress'
+import { computed, ref, watch } from 'vue'
+import { resolveHeaders, useActiveAnchor } from '../composables/outline.js'
+
+const { frontmatter, theme } = useData()
+const route = useRoute()
+
+const headers = ref<Header[]>([])
+
+watch(
+  () => route.path,
+  () => {
+    document.querySelectorAll('h2').forEach((el) => {
+      if (el.textContent && el.id)
+        headers.value.push({
+          level: Number(el.tagName[1]),
+          title: el.textContent,
+          slug: el.id
+        })
+    })
+  },
+  { immediate: true }
+)
+
+const hasOutline = computed(() => !!headers.value.length)
+const resolvedHeaders = computed(() => resolveHeaders(headers.value))
 
 const container = ref()
 const marker = ref()
-
 useActiveAnchor(container, marker)
 
-const resolvedHeaders = computed(() => {
-  return resolveHeaders(page.value.headers)
-})
-
 function handleClick({ target: el }: Event) {
   const id = '#' + (el as HTMLAnchorElement).href!.split('#')[1]
   const heading = document.querySelector<HTMLAnchorElement>(

brc-dd avatar Aug 14 '22 10:08 brc-dd

Looking forward this feature.

Charles7c avatar Aug 21 '22 11:08 Charles7c