Browse Source

Add Table of Contents (TOC) to article pages

master
Riyyi 3 weeks ago
parent
commit
8110667234
  1. 5
      content/articles/first.md
  2. 20
      src/assets/css/style.css
  3. 40
      src/components/articles/TableOfContents.vue
  4. 18
      src/components/articles/TableOfContentsLink.vue
  5. 9
      src/components/shared/Footer.vue
  6. 0
      src/components/shared/Header.vue
  7. 0
      src/components/shared/NavMenu.vue
  8. 64
      src/layouts/default.vue
  9. 8
      src/pages/articles/[slug].vue

5
content/articles/first.md

@ -1,11 +1,12 @@
--- ---
title: "My First Blog Post" title: "My First Blog Post"
description: "This is a test article." description: "This is a test article."
navigation: true
--- ---
# Hello World ## Hello World
This is a test article. Foobarbazbuz.
## Component Rendering ## Component Rendering

20
src/assets/css/style.css

@ -18,26 +18,6 @@ body {
/* Main content */ /* Main content */
/*----------------------------------------*/ /*----------------------------------------*/
.container {
margin: 56px auto 0 auto;
}
.content {
position: relative;
min-height: calc(100vh - 104px - .5rem);
padding: 20px 20px 50px 20px;
background-color: #fff;
}
a:hover { a:hover {
color: #818080 !important; color: #818080 !important;
} }
/* Footer */
/*----------------------------------------*/
footer {
text-align: center;
color: #909090;
}

40
src/components/articles/TableOfContents.vue

@ -0,0 +1,40 @@
<template>
<aside v-if="toc" class="position-fixed toc shadow">
<h5 class="text-center">{{ toc.title }}</h5>
<ul class="font-smaller mb-0">
<li>
<a href="#">Overview</a>
</li>
</ul>
<ArticlesTableOfContentsLink :links="toc.links" :first="true" class="font-smaller mb-0" />
</aside>
</template>
<style scoped>
.toc {
display: none;
}
@media (min-width: 1450px) {
.toc {
display: block;
width: calc((100vw - 960px) / 2 - 35px);
max-width: 300px;
top: 56px;
left: calc(100vw - (100vw - 960px) / 2 + 12px);
padding: 20px 5px 20px 0;
border-radius: 6px;
background-color: #fff;
}
}
.font-smaller {
font-size: .8125rem;
}
</style>
<script setup lang="ts">
import type { Toc } from "@nuxtjs/mdc";
defineProps<{ toc?: Toc }>();
</script>

18
src/components/articles/TableOfContentsLink.vue

@ -0,0 +1,18 @@
<template>
<ul>
<template v-for="link in links" :key="link.id">
<li>
<a :href="'#' + link.id">{{ link.text }}</a>
</li>
<template v-if="link.children && link.children?.length > 0">
<ArticlesTableOfContentsLink :links="link.children" :first="false" />
</template>
</template>
</ul>
</template>
<script setup lang="ts">
import type { TocLink } from "@nuxtjs/mdc";
defineProps<{ links: TocLink[] }>();
</script>

9
src/components/Shared/Footer.vue → src/components/shared/Footer.vue

@ -6,6 +6,13 @@
</footer> </footer>
</template> </template>
<style scoped>
footer {
text-align: center;
color: #909090;
}
</style>
<script setup lang="ts"> <script setup lang="ts">
const year = new Date().getFullYear(); const year: number = new Date().getFullYear();
</script> </script>

0
src/components/Shared/Header.vue → src/components/shared/Header.vue

0
src/components/Shared/NavMenu.vue → src/components/shared/NavMenu.vue

64
src/layouts/default.vue

@ -12,3 +12,67 @@
</div> </div>
<ToggleColorMode /> <ToggleColorMode />
</template> </template>
<style scoped>
.container {
margin: 56px auto 0 auto;
}
.content {
position: relative;
min-height: calc(100vh - 104px - .5rem);
padding: 20px 20px 50px 20px;
background-color: #fff;
}
@media (min-width: 576px) {
.container,
.container-sm {
max-width: 540px;
}
}
@media (min-width: 768px) {
.container,
.container-md,
.container-sm {
max-width: 630px;
}
}
@media (min-width: 992px) {
.container,
.container-lg,
.container-md,
.container-sm {
max-width: 720px;
}
}
@media (min-width: 1200px) {
.container,
.container-lg,
.container-md,
.container-sm,
.container-xl {
max-width: 960px;
}
}
@media (min-width: 1400px) {
.container,
.container-lg,
.container-md,
.container-sm,
.container-xl,
.container-xxl {
max-width: 960px;
}
}
</style>

8
src/pages/articles/[slug].vue

@ -1,6 +1,8 @@
<template> <template>
<div> <div>
<template v-if="article"> <template v-if="article">
<ArticlesTableOfContents v-if="article.navigation" :toc="article.body.toc" />
<h1>{{ article.title }}</h1> <h1>{{ article.title }}</h1>
<p>{{ article.description }}</p> <p>{{ article.description }}</p>
<ContentRenderer :value="article" /> <ContentRenderer :value="article" />
@ -59,9 +61,11 @@
</style> </style>
<script setup lang="ts"> <script setup lang="ts">
import type { ContentCollectionItem } from "@nuxt/content";
const { params } = useRoute(); const { params } = useRoute();
const { data: article } = await useAsyncData( const { data: article } = await useAsyncData<ContentCollectionItem | null>(
`article-${params.slug}`, `article-${params.slug}`,
() => queryCollection("content").path("/articles/" + params.slug).first() () => queryCollection("content").path("/articles/" + params.slug).first()
); );
@ -70,6 +74,4 @@ useSeoMeta({
title: article.value?.title, title: article.value?.title,
description: article.value?.description description: article.value?.description
}) })
console.log(article);
</script> </script>

Loading…
Cancel
Save