mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-15 08:41:37 +00:00
Update table of contents
This commit is contained in:
@ -2,13 +2,16 @@ import { AnchorHTMLAttributes } from 'react';
|
||||
import cn from 'classnames';
|
||||
import { FiExternalLink } from 'react-icons/fi';
|
||||
|
||||
interface Props extends AnchorHTMLAttributes<HTMLAnchorElement> {}
|
||||
interface Props extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
external?: boolean;
|
||||
}
|
||||
|
||||
const Anchor = ({ children, ...rest }: Props) => {
|
||||
const Anchor = ({ children, external = true, ...rest }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<a
|
||||
{...rest}
|
||||
target={external ? '_blank' : undefined}
|
||||
className={cn(
|
||||
'mx-[2px] text-teal-500 relative',
|
||||
'before:left-0 before:top-[1px] before:block before:absolute',
|
||||
@ -18,6 +21,7 @@ const Anchor = ({ children, ...rest }: Props) => {
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{external && <FiExternalLink className="inline ml-1 mb-[0.2rem]" />}
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
|
29
components/post/PostTOC.tsx
Normal file
29
components/post/PostTOC.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { FC } from 'react';
|
||||
import { getHeadings } from 'lib/utils';
|
||||
import Anchor from 'components/mdx/Anchor';
|
||||
|
||||
interface Props {
|
||||
headings: ReturnType<typeof getHeadings>;
|
||||
}
|
||||
|
||||
const PostTOC: FC<Props> = ({ headings }) => {
|
||||
return (
|
||||
<>
|
||||
<h2>What's inside?</h2>
|
||||
|
||||
<ul className="pl-4 border-l-4 border-gray-300">
|
||||
{headings?.map((h) => (
|
||||
<>
|
||||
<li>
|
||||
<Anchor href={h.link} external={false}>
|
||||
{h.text}
|
||||
</Anchor>
|
||||
</li>
|
||||
</>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostTOC;
|
@ -1,7 +1,9 @@
|
||||
import { FC } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { MyMatters } from 'types';
|
||||
// import { renderToString } from 'react-dom/server';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { getHeadings } from 'lib/utils';
|
||||
import PostTOC from 'components/post/PostTOC';
|
||||
|
||||
const Footer = dynamic(() => import('components/Footer'));
|
||||
const HeadBar = dynamic(() => import('components/NavBar'));
|
||||
@ -9,29 +11,8 @@ const HeadBar = dynamic(() => import('components/NavBar'));
|
||||
interface Props extends MyMatters {}
|
||||
|
||||
const MainLayout: FC<Props> = ({ title, date, children }) => {
|
||||
// const contentString = renderToString(children as any);
|
||||
|
||||
// const getHeadings = (source: string) => {
|
||||
// const regex = /<h2>(.*?)<\/h2>/g;
|
||||
|
||||
// if (source.match(regex)) {
|
||||
// return source.match(regex)?.map((heading) => {
|
||||
// const headingText = heading.replace('<h2>', '').replace('</h2>', '');
|
||||
|
||||
// const link = '#' + headingText.replace(/ /g, '_').toLowerCase();
|
||||
|
||||
// return {
|
||||
// text: headingText,
|
||||
// link,
|
||||
// };
|
||||
// });
|
||||
// }
|
||||
|
||||
// return [];
|
||||
// };
|
||||
|
||||
// const headings = getHeadings(contentString);
|
||||
// console.log(headings);
|
||||
const contentString = renderToString(children as any);
|
||||
const headings = getHeadings(contentString);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -43,6 +24,8 @@ const MainLayout: FC<Props> = ({ title, date, children }) => {
|
||||
|
||||
<time>{date}</time>
|
||||
|
||||
<PostTOC headings={headings} />
|
||||
|
||||
{children}
|
||||
</article>
|
||||
</main>
|
||||
|
@ -10,3 +10,30 @@ export const sortByDate = (
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Read post head to mark TOC
|
||||
* @param source
|
||||
* @returns
|
||||
*/
|
||||
export const getHeadings = (source: string) => {
|
||||
const regex = /<h2 id="(.*?)">(.*?)<\/h2>/g;
|
||||
const linkRegx = /id="(.*?)"/;
|
||||
|
||||
if (source.match(regex)) {
|
||||
return source.match(regex)?.map((heading) => {
|
||||
const headingText = heading
|
||||
.replace(/<h2 id="(.*?)">/, '')
|
||||
.replace(/<\/h2>/, '');
|
||||
|
||||
const link = '#' + heading.match(linkRegx)?.[1];
|
||||
|
||||
return {
|
||||
text: headingText,
|
||||
link,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import remarkFrontmatter from 'remark-frontmatter';
|
||||
import mdx from '@next/mdx';
|
||||
import rehypePrism from '@mapbox/rehype-prism';
|
||||
import remarkToc from 'remark-toc';
|
||||
// import remarkToc from 'remark-toc';
|
||||
import composePlugins from 'next-compose-plugins';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
@ -12,7 +12,7 @@ const composedConfig = composePlugins([
|
||||
options: {
|
||||
remarkPlugins: [
|
||||
remarkFrontmatter,
|
||||
() => remarkToc({ maxDepth: 2 }),
|
||||
// [remarkToc, { maxDepth: 2 }],
|
||||
remarkGfm,
|
||||
],
|
||||
rehypePlugins: [rehypePrism, rehypeSlug],
|
||||
|
@ -14,8 +14,6 @@ export const meta = {
|
||||
|
||||
export default ({ children }) => <Layout {...meta}>{children}</Layout>;
|
||||
|
||||
## Table of contents
|
||||
|
||||
## Hello
|
||||
|
||||
This is my second post.
|
||||
|
@ -2,15 +2,6 @@
|
||||
@apply text-lg leading-10;
|
||||
}
|
||||
|
||||
/* For the table of content */
|
||||
#article h2#table-of-contents + ul {
|
||||
@apply border-l-4 border-gray-300;
|
||||
@apply pl-4;
|
||||
}
|
||||
#article h2#table-of-contents:hover::before {
|
||||
content: unset;
|
||||
}
|
||||
|
||||
#article h1 {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
|
Reference in New Issue
Block a user