Skip to content
30 changes: 18 additions & 12 deletions app/entities/post/detail/PostBody.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';
import LoadingIndicator from '@/app/entities/common/Loading/LoadingIndicator';
import MDEditor from '@uiw/react-md-editor';
import PostTOC from '@/app/entities/post/detail/PostTOC';

interface Props {
content: string;
Expand All @@ -9,23 +10,28 @@ interface Props {

const PostBody = ({ content, loading }: Props) => {
return (
<div className={'w-full post-body px-4 py-16 min-h-[500px]'}>
<div
className={'max-w-3xl post-body px-4 py-16 min-h-[500px] relative '}
>
{loading ? (
<div className={'w-1/3 mx-auto'}>
<LoadingIndicator />
</div>
) : (
<MDEditor.Markdown
style={{
backgroundColor: 'var(--background)',
color: 'var(--text-primary)',
}}
className={''}
source={content}
wrapperElement={{
'data-color-mode': 'dark',
}}
/>
<>
<MDEditor.Markdown
style={{
backgroundColor: 'var(--background)',
color: 'var(--text-primary)',
}}
className={''}
source={content}
wrapperElement={{
'data-color-mode': 'dark',
}}
/>
<PostTOC postContent={content || ''} />
</>
)}
</div>
);
Expand Down
60 changes: 60 additions & 0 deletions app/entities/post/detail/PostTOC.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Link from 'next/link';

const PostTOC = ({ postContent }: { postContent: string }) => {
const parseHeadings = (content: string) => {
const headings = content.match(/#{1,6} .+/g);

return (headings ?? []).map((heading: string) => ({
id: heading.replace(/#/g, '').trim(),
type: heading.lastIndexOf('#') + 1,
title: heading.replace(/#/g, '').trim(),
}));
};

return (
<div className="fixed post-toc hidden lg:block w-[280px] top-1/2 -translate-y-1/2 left-[calc(50%+524px)] transition-all text-sm bg-gray-100/80 rounded-md p-4 text-black">
<h4 className={'text-xl font-bold mb-2'}>📌 Table of Contents</h4>
<ul className={'list-none'}>
{parseHeadings(postContent).map((heading) => {
const href =
`#${heading.id
.toLowerCase()
.replaceAll('.', '')
.replaceAll(/[^a-zA-Z0-9가-힣]/g, '-')
.replaceAll(/-+/g, '-')
.replaceAll(/^-|-$/g, '')}` || '';
return (
<li
key={heading.id}
style={{ marginLeft: `${(heading.type - 1) * 16}px` }}
className={`${heading.type === 1 ? 'font-bold' : ''} `}
>
<Link
scroll={true}
className={
'p-1 transition-all hover:bg-green-50 rounded-md text-nowrap overflow-x-hidden scroll-smooth '
}
onClick={(e) => {
e.preventDefault();
document.querySelector(href)?.scrollIntoView({
behavior: 'smooth',
});
}}
href={`#${heading.id
.toLowerCase()
.replaceAll('.', '')
.replaceAll(/[^a-zA-Z0-9가-힣]/g, '-')
.replaceAll(/-+/g, '-')
.replaceAll(/^-|-$/g, '')}`}
>
{'∟ ' + heading.title}
</Link>
</li>
);
})}
</ul>
</div>
);
};

export default PostTOC;
10 changes: 4 additions & 6 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ article.post li {
list-style: circle;
}

.post-toc ul > li {
list-style: none;
}

article.post h1,
article.post h2,
article.post h3,
Expand All @@ -73,12 +77,6 @@ article.post h3 {
margin-bottom: 1rem;
}

article.post h4 {
font-size: 1rem;
font-weight: bold;
margin-bottom: 1rem;
}

/* Footer Style */

footer {
Expand Down
1 change: 1 addition & 0 deletions app/posts/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Metadata } from 'next';
import dbConnect from '@/app/lib/dbConnect';
import Post from '@/app/models/Post';
import PostJSONLd from '@/app/entities/post/detail/PostJSONLd';
import PostTOC from '@/app/entities/post/detail/PostTOC';

async function getPostDetail(slug: string) {
await dbConnect();
Expand Down
1 change: 1 addition & 0 deletions app/public/googlee045d7c5db6fc750.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
google-site-verification: googlee045d7c5db6fc750.html
Loading