feat: custom toc component
This commit is contained in:
parent
020ca20e01
commit
039e39b3ea
|
|
@ -1,6 +1,7 @@
|
|||
import clsx from "clsx"
|
||||
import { useCodeCopy } from "~/hooks/useCodeCopy"
|
||||
import { renderPageContent } from "~/markdown"
|
||||
import { PostToc } from "~/components/site/PostToc"
|
||||
|
||||
export const PageContent: React.FC<{
|
||||
content?: string
|
||||
|
|
@ -9,20 +10,17 @@ export const PageContent: React.FC<{
|
|||
}> = ({ className, content, toc }) => {
|
||||
useCodeCopy()
|
||||
|
||||
// TODO
|
||||
// const [element, setElement] = useState<ReactElement>()
|
||||
// const renderMarkdown = useCallback((doc: string) => {
|
||||
// setElement(renderPageContent(doc).element)
|
||||
// }, [])
|
||||
// useEffect(() => {
|
||||
// if (content) {
|
||||
// renderMarkdown(content)
|
||||
// }
|
||||
// }, [content, renderMarkdown])
|
||||
let parsedContent: ReturnType<typeof renderPageContent> | null = null
|
||||
if (content) {
|
||||
parsedContent = renderPageContent(content)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clsx("xlog-post-content", `prose`, className)}>
|
||||
{content && renderPageContent(content, false, toc).element}
|
||||
</div>
|
||||
<>
|
||||
<div className={clsx("xlog-post-content", `prose`, className)}>
|
||||
{parsedContent?.element}
|
||||
</div>
|
||||
{toc && parsedContent?.toc && <PostToc data={parsedContent?.toc} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Link } from "react-scroll"
|
||||
|
||||
export const PostLink: React.FC<any> = ({ children, ...props }) => {
|
||||
console.log(props.href)
|
||||
if (props.href?.startsWith("#")) {
|
||||
return (
|
||||
<Link
|
||||
|
|
|
|||
|
|
@ -1,8 +1,41 @@
|
|||
import { useRef, useEffect, useState } from "react"
|
||||
import React, { useEffect, useState, useRef } from "react"
|
||||
import type { Result as TocResult } from "mdast-util-toc"
|
||||
import { Link } from "react-scroll"
|
||||
|
||||
function renderItems(items: TocResult["map"], activeId: string, prefix = "") {
|
||||
return (
|
||||
<ol className={prefix ? "pl-5" : ""}>
|
||||
{items?.children?.map((item, index) => (
|
||||
<li key={index}>
|
||||
{item.children.map((child: any) => (
|
||||
<>
|
||||
{child.type === "paragraph" && child.children?.[0]?.url && (
|
||||
<Link
|
||||
to={decodeURI(child.children[0].url.slice(1))}
|
||||
spy={true}
|
||||
smooth={true}
|
||||
duration={500}
|
||||
offset={-20}
|
||||
href={child.children[0].url}
|
||||
>
|
||||
{`${prefix}${index + 1}. ${
|
||||
child.children[0].children?.[0]?.value
|
||||
}`}
|
||||
</Link>
|
||||
)}
|
||||
{child.type === "list" &&
|
||||
renderItems(child, activeId, `${index + 1}.`)}
|
||||
</>
|
||||
))}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
)
|
||||
}
|
||||
|
||||
export const PostToc: React.FC<{
|
||||
children: React.ReactNode
|
||||
}> = ({ children }) => {
|
||||
data: TocResult
|
||||
}> = ({ data }) => {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const [maxWidth, setMaxWidth] = useState(0)
|
||||
|
|
@ -19,14 +52,14 @@ export const PostToc: React.FC<{
|
|||
|
||||
return (
|
||||
<div
|
||||
className="xlog-post-toc absolute left-full pl-10 h-full top-0"
|
||||
className="xlog-post-toc absolute left-full pl-10 h-full top-[124px]"
|
||||
style={{
|
||||
maxWidth: maxWidth > 40 ? maxWidth : 0,
|
||||
}}
|
||||
ref={containerRef}
|
||||
>
|
||||
<div className="sticky top-14 text-sm leading-loose whitespace-nowrap truncate text-ellipsis">
|
||||
{children}
|
||||
{renderItems(data?.map, "activeId")}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export const SiteLayout: React.FC<SiteLayoutProps> = ({ children, title }) => {
|
|||
<SiteHeader site={site.data} subscriptions={subscriptions.data} />
|
||||
<style>{site.data?.css}</style>
|
||||
<div
|
||||
className={`xlog-post-id-${page.data?.id} max-w-screen-md mx-auto px-5 pt-12`}
|
||||
className={`xlog-post-id-${page.data?.id} max-w-screen-md mx-auto px-5 pt-12 relative`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ import { remarkYoutube } from "./remark-youtube"
|
|||
import sanitizeScheme from "./sanitize-schema"
|
||||
import rehypeSlug from "rehype-slug"
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings"
|
||||
import { toc } from "mdast-util-toc"
|
||||
import { PostToc } from "~/components/site/PostToc"
|
||||
import { PostLink } from "~/components/site/PostLink"
|
||||
import { toc, Result as TocResult } from "mdast-util-toc"
|
||||
import { Element } from "react-scroll"
|
||||
|
||||
export type MarkdownEnv = {
|
||||
|
|
@ -34,6 +32,7 @@ export type MarkdownEnv = {
|
|||
frontMatter: Record<string, any>
|
||||
__internal: Record<string, any>
|
||||
cover: string
|
||||
toc: TocResult | null
|
||||
}
|
||||
|
||||
export type Rendered = {
|
||||
|
|
@ -42,6 +41,7 @@ export type Rendered = {
|
|||
excerpt: string
|
||||
frontMatter: Record<string, any>
|
||||
cover: string
|
||||
toc: TocResult | null
|
||||
}
|
||||
|
||||
refractor.alias("html", ["svelte", "vue"])
|
||||
|
|
@ -58,6 +58,7 @@ export const renderPageContent = (
|
|||
__internal: {},
|
||||
frontMatter: {},
|
||||
cover: "",
|
||||
toc: null,
|
||||
}
|
||||
|
||||
const result = unified()
|
||||
|
|
@ -75,17 +76,7 @@ export const renderPageContent = (
|
|||
}
|
||||
})
|
||||
.use(() => (tree) => {
|
||||
if (enableToc) {
|
||||
const result = toc(tree, { tight: true, ordered: true })
|
||||
if (result.map) {
|
||||
tree.children = [
|
||||
...tree.children,
|
||||
{ type: "html", value: "<toc>" },
|
||||
result.map,
|
||||
{ type: "html", value: "</toc>" },
|
||||
]
|
||||
}
|
||||
}
|
||||
env.toc = toc(tree, { tight: true, ordered: true })
|
||||
})
|
||||
.use(remarkGfm, {})
|
||||
.use(remarkExcerpt, { env })
|
||||
|
|
@ -137,8 +128,6 @@ export const renderPageContent = (
|
|||
createElement: createElement,
|
||||
components: {
|
||||
img: Image,
|
||||
a: PostLink,
|
||||
toc: PostToc,
|
||||
anchor: Element,
|
||||
} as any,
|
||||
})
|
||||
|
|
@ -152,5 +141,6 @@ export const renderPageContent = (
|
|||
excerpt: env.excerpt,
|
||||
frontMatter: env.frontMatter,
|
||||
cover: env.cover,
|
||||
toc: env.toc,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue