Files
DefectingCat.github.io/pages/posts/[slug].tsx
2021-11-16 21:00:46 +08:00

180 lines
4.6 KiB
TypeScript

import { getAllPostSlugs, getPostData } from '../../lib/posts';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import Head from 'next/head';
import Date from '../../components/DateFormater';
import remarkToc from 'remark-toc';
import rehypeSlug from 'rehype-slug';
import rehypeReact from 'rehype-react';
import remarkRehype from 'remark-rehype';
import remarkParse from 'remark-parse';
import rehypeHighlight from 'rehype-highlight';
import { unified } from 'unified';
import { createElement, Fragment } from 'react';
import {
Box,
Image,
Heading,
Flex,
Icon,
Link,
Button,
} from '@chakra-ui/react';
import 'highlight.js/styles/github.css';
import xml from 'highlight.js/lib/languages/xml';
import bash from 'highlight.js/lib/languages/bash';
import rehypeRaw from 'rehype-raw';
import Zoom from 'react-medium-image-zoom';
import 'react-medium-image-zoom/dist/styles.css';
import { FiCalendar } from 'react-icons/fi';
import { useRouter } from 'next/router';
export async function getStaticPaths() {
const paths = getAllPostSlugs();
return {
paths,
fallback: false,
};
}
export const getStaticProps: GetStaticProps = async ({ params }) => {
const postData = await getPostData(params?.slug?.toString() ?? '');
return {
props: {
postData,
},
};
};
const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
const router = useRouter();
const processedContent = unified()
.use(remarkParse)
.use(remarkToc, {
maxDepth: 3,
})
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeRaw)
.use(rehypeHighlight, {
languages: { vue: xml, bash },
aliases: { bash: ['npm'] },
ignoreMissing: true,
})
.use(rehypeSlug)
.use(rehypeReact, {
createElement,
components: {
img: (props: any) => {
return (
<Zoom wrapElement="a">
<Image borderRadius="10px" src={props.src} alt="" />
</Zoom>
);
},
a: (props: any) => {
return (
<Box>
<Link display="inline-flex" alignItems="center" href={props.href}>
{props.children}
</Link>
</Box>
);
},
},
Fragment,
});
const postContent = processedContent.processSync(
`\n## Table of contents\n${postData.content}`
).result;
// Process the table of content
const tocHead = (postContent.props as any).children.shift();
(postContent.props as any).children.shift(); // '\n'
let toc;
// Table of content is ul element
if ((postContent.props as any).children[0].type === 'ul') {
toc = (postContent.props as any).children.shift();
}
return (
<>
<Head>
<title>RUA - {postData.title}</title>
</Head>
{/* Child will not be 100% height, so need alignItems="flex-start" */}
<Flex
h={['unset', 'unset', '100vh']}
justifyContent="center"
overflow="auto"
alignItems="flex-start"
flexFlow={['column', 'column', 'row']}
px="0.5rem"
>
<Button
size="lg"
mt={['1rem', '1rem', '3rem']}
position={['unset', 'unset', 'sticky']}
top="3rem"
onClick={() => router.back()}
>
BACK
</Button>
<Box
w={['full', 'full', '55rem']}
borderRadius="10px"
mt={['1rem', '1rem', '3rem']}
shadow="lg"
bg="white"
mx={['unset', 'unset', '1.5rem']}
overflow="hidden"
flex="1"
maxW="55rem"
>
{postData.index_img && (
<Image
src={postData.index_img}
w="100%"
fallback={<></>}
fit="cover"
alt="Head image"
/>
)}
<Box as="article" p={['1rem', '1rem', '2rem']}>
<Box as="header">
<Heading as="h1" mb="1rem">
{postData.title}
</Heading>
<Flex alignItems="center" color="gray.600">
<Icon as={FiCalendar} mr="0.5rem" />
<Date dateString={postData.date} />
</Flex>
</Box>
<Box as="section" id="write" mt="2rem">
{postContent}
</Box>
</Box>
</Box>
{toc && (
<Box
display={['none', 'none', 'none', 'block']}
position="sticky"
top="3rem"
mt="3rem"
>
{tocHead}
{toc}
</Box>
)}
</Flex>
</>
);
};
export default Post;