1
1
import { Metadata } from "next" ;
2
2
import Link from "next/link" ;
3
3
import { notFound } from "next/navigation" ;
4
+ import toc from "markdown-toc-unlazy" ;
4
5
import { getPostBySlug , getPostsByCategory } from "@/lib/posts" ;
6
+ import { Itoc } from "@/interfaces/Post" ;
5
7
import styles from "./page.module.css" ;
6
8
import ReactMarkdown from "@/components/ReactMarkdown" ;
9
+ import TOC from "@/components/TOC" ;
7
10
import Comments from "@/components/Comments" ;
8
11
import { SITE_CONFIG } from "@/app/site.config" ;
9
12
@@ -14,52 +17,72 @@ export default function Page({ params }: { params: { category: string; slug: str
14
17
const { title, description, tags } = frontMatter ;
15
18
const createdDate = new Date ( frontMatter . date ) ;
16
19
const updatedDate = frontMatter . updated ? new Date ( frontMatter . updated ) : null ;
20
+ const tocContent = handleTocHeader (
21
+ toc ( content ) . json . filter ( ( header : Itoc ) => header . lvl <= SITE_CONFIG . tocMaxHeader ) ,
22
+ ) ;
17
23
return (
18
- < >
19
- < div className = { styles . postHeader } >
20
- < div className = { styles . postTitle } > { title } </ div >
21
- < div className = { styles . postDesc } > { description } </ div >
22
- { tags && (
23
- < div >
24
- { tags . map ( ( tag , index ) => (
25
- < div key = { index . toString ( ) } className = "flexItem" >
26
- < Link href = { `/tag/${ tag } ` } className = { styles . postTag } >
27
- # { tag }
28
- </ Link >
29
- </ div >
30
- ) ) }
24
+ < div className = { styles . pageWrapper } >
25
+ < TOC tocContent = { tocContent } style = { { visibility : "hidden" } } className = { styles . toc } />
26
+ < div className = { `${ styles . postWrapper } container` } >
27
+ < div className = { styles . postHeader } >
28
+ < div className = { styles . postTitle } id = "toc-title" >
29
+ { title }
31
30
</ div >
32
- ) }
33
- < div className = { styles . postTime } >
34
- < span >
35
- 发布于 { createdDate . getFullYear ( ) } 年 { createdDate . getMonth ( ) + 1 } 月 { createdDate . getDate ( ) } 日
36
- </ span >
37
- { updatedDate && (
38
- < >
39
- < span > |</ span >
40
- < span >
41
- 更新于 { updatedDate . getFullYear ( ) } 年 { updatedDate . getMonth ( ) + 1 } 月 { updatedDate . getDate ( ) } 日
42
- </ span >
43
- </ >
31
+ < div className = { styles . postDesc } > { description } </ div >
32
+ { tags && (
33
+ < div >
34
+ { tags . map ( ( tag , index ) => (
35
+ < div key = { index . toString ( ) } className = "flexItem" >
36
+ < Link href = { `/tag/${ tag } ` } className = { styles . postTag } >
37
+ # { tag }
38
+ </ Link >
39
+ </ div >
40
+ ) ) }
41
+ </ div >
44
42
) }
45
- < span > |</ span >
46
- < span >
47
- 遵循{ " " }
48
- < Link href = "https://creativecommons.org/licenses/by-nc-sa/4.0/" target = "_blank" >
49
- CC BY-NC-SA 4.0
50
- </ Link > { " " }
51
- 许可
52
- </ span >
43
+ < div className = { styles . postTime } >
44
+ < span >
45
+ 发布于 { createdDate . getFullYear ( ) } 年 { createdDate . getMonth ( ) + 1 } 月 { createdDate . getDate ( ) } 日
46
+ </ span >
47
+ { updatedDate && (
48
+ < >
49
+ < span > |</ span >
50
+ < span >
51
+ 更新于 { updatedDate . getFullYear ( ) } 年 { updatedDate . getMonth ( ) + 1 } 月 { updatedDate . getDate ( ) } 日
52
+ </ span >
53
+ </ >
54
+ ) }
55
+ < span > |</ span >
56
+ < span >
57
+ 遵循{ " " }
58
+ < Link href = "https://creativecommons.org/licenses/by-nc-sa/4.0/" target = "_blank" >
59
+ CC BY-NC-SA 4.0
60
+ </ Link > { " " }
61
+ 许可
62
+ </ span >
63
+ </ div >
53
64
</ div >
65
+ < div >
66
+ < ReactMarkdown abbrlink = { frontMatter . abbrlink ! } > { content } </ ReactMarkdown >
67
+ </ div >
68
+ < Comments id = "toc-comments" />
54
69
</ div >
55
- < div >
56
- < ReactMarkdown abbrlink = { frontMatter . abbrlink ! } > { content } </ ReactMarkdown >
57
- </ div >
58
- < Comments />
59
- </ >
70
+ < TOC tocContent = { tocContent } className = { styles . toc } />
71
+ </ div >
60
72
) ;
61
73
}
62
74
75
+ const handleTocHeader = ( tocContent : Array < Itoc > ) => {
76
+ const minLvl = Math . min ( ...tocContent . map ( ( header ) => header . lvl ) ) ;
77
+ return tocContent . map ( ( header ) => {
78
+ const { lvl, ...rest } = header ;
79
+ return {
80
+ lvl : lvl - minLvl ,
81
+ ...rest ,
82
+ } ;
83
+ } ) ;
84
+ } ;
85
+
63
86
export function generateMetadata ( { params } : { params : { slug : string } } ) : Metadata {
64
87
const post = getPostBySlug ( params . slug ) ;
65
88
if ( ! post ) return notFound ( ) ;
0 commit comments