feat: enhance BlogPost component with Table of Contents functionality and content formatting improvements

This commit is contained in:
2025-05-23 10:42:36 +02:00
parent 4cc133fffa
commit 574e87bf99
2 changed files with 93 additions and 3 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ export const blogPostsData: BlogPostProps[] = [
The realm of IoT demands reliable, efficient communication protocols that can withstand the chaotic nature of real-world environments. Through careful implementation and optimization of the STM32's communication peripherals, we can craft resilient connections that bridge the gap between digital and physical domains. The realm of IoT demands reliable, efficient communication protocols that can withstand the chaotic nature of real-world environments. Through careful implementation and optimization of the STM32's communication peripherals, we can craft resilient connections that bridge the gap between digital and physical domains.
## The Three Elemental Protocols: I2C, SPI, and UART # The Three Elemental Protocols: I2C, SPI, and UART
Before we forge ahead into advanced territory, let us briefly recall the elemental forces at our disposal. Each protocol serves as a channel through which our digital entities communicate, each with its unique strengths and limitations: Before we forge ahead into advanced territory, let us briefly recall the elemental forces at our disposal. Each protocol serves as a channel through which our digital entities communicate, each with its unique strengths and limitations:
* **I2C (Inter-Integrated Circuit)** - A two-wire interface requiring minimal physical connections, ideal for communication with multiple peripheral devices over short distances. * **I2C (Inter-Integrated Circuit)** - A two-wire interface requiring minimal physical connections, ideal for communication with multiple peripheral devices over short distances.
+91 -1
View File
@@ -5,6 +5,13 @@ import { NotFound } from './NotFound';
import style from '@styles/BlogPost.module.scss'; import style from '@styles/BlogPost.module.scss';
import { get } from 'http';
interface TableOfContentsItem {
title: string;
id: number;
children?: TableOfContentsItem[];
}
function dedent(str: string): string { function dedent(str: string): string {
const lines = str.split('\n'); const lines = str.split('\n');
@@ -27,9 +34,12 @@ export const BlogPost = () => {
const { slug } = useParams<{ slug: string }>(); const { slug } = useParams<{ slug: string }>();
const [post, setPost] = useState(blogPostsData.find(post => post.slug === slug)); const [post, setPost] = useState(blogPostsData.find(post => post.slug === slug));
const [relatedPosts, setRelatedPosts] = useState(blogPostsData.slice(0, 3)); const [relatedPosts, setRelatedPosts] = useState(blogPostsData.slice(0, 3));
const [toc, setToc] = useState<TableOfContentsItem[]>([]);
useEffect(() => { useEffect(() => {
const currentPost = blogPostsData.find(post => post.slug === slug); const currentPost = blogPostsData.find(post => post.slug === slug);
let id = 0;
const toc: TableOfContentsItem[] = [];
if (currentPost) { if (currentPost) {
setPost(currentPost); setPost(currentPost);
@@ -45,6 +55,85 @@ export const BlogPost = () => {
setRelatedPosts(related); setRelatedPosts(related);
let newContent = '';
if (currentPost.content) {
const content = dedent(currentPost.content);
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
if (/^#\s/.test(lines[i])) {
newContent += `<h2 id="${id}">${lines[i].replace(/^#+\s*/, '')}</h2> \n`;
toc.push({ title: lines[i].replace(/^#+\s*/, ''), id });
id++;
}
else if (/^##\s/.test(lines[i])) {
newContent += `<h3 id="${id}">${lines[i].replace(/^#+\s*/, '')}</h3> \n`;
if (toc.length > 0) {
const parent = toc[toc.length - 1];
if (!parent.children) {
parent.children = [];
}
parent.children.push({ title: lines[i].replace(/^#+\s*/, ''), id });
}
id++;
}
else if (/^###\s/.test(lines[i])) {
newContent += `<h4 id="${id}">${lines[i].replace(/^#+\s*/, '')}</h4> \n`;
if (toc.length > 0) {
const h2Parent = toc[toc.length - 1];
if (h2Parent.children && h2Parent.children.length > 0) {
const h3Parent = h2Parent.children[h2Parent.children.length - 1];
if (!h3Parent.children) {
h3Parent.children = [];
}
h3Parent.children.push({ title: lines[i].replace(/^#+\s*/, ''), id });
}
}
id++;
}
else if (/^\*\s/.test(lines[i])) {
newContent += '<ul>\n';
for (; i < lines.length; i++) {
if (/^\*\s/.test(lines[i])) {
newContent += `<li>${lines[i].replace(/^\*\s/, '')}</li>\n`;
}
else {
break;
}
}
newContent += '</ul>\n';
}
else if (/^\-\s/.test(lines[i])) {
newContent += '<ol>\n';
for (; i < lines.length; i++) {
if (/^\-\s/.test(lines[i])) {
newContent += `<li>${lines[i].replace(/^\-\s/, '')}</li>\n`;
}
else {
break;
}
}
newContent += '</ol>\n';
}
else {
newContent += `<p>${lines[i]}</p>\n`;
}
}
}
newContent = newContent.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
newContent = newContent.replace(/__(.*?)__/g, '<strong>$1</strong>');
newContent = newContent.replace(/\*(.*?)\*/g, '<em>$1</em>');
newContent = newContent.replace(/_(.*?)_/g, '<em>$1</em>');
newContent = newContent.replace(/`(.*?)`/g, '<code>$1</code>');
newContent = newContent.replace(/!\[(.*?)\]\((.*?)\)/g, '<img alt="$1" src="$2" />');
newContent = newContent.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2">$1</a>');
const contentElement = document.getElementById('postContent');
contentElement!.innerHTML = newContent;
setToc(toc);
window.scrollTo(0, 0); window.scrollTo(0, 0);
} }
}, [slug]); }, [slug]);
@@ -86,7 +175,8 @@ export const BlogPost = () => {
</div> </div>
</div> </div>
<div className={style.postContent} dangerouslySetInnerHTML={{ __html: post.content }}></div> <div className={style.postContent} id="postContent">
</div>
<div className={style.postContent}> <div className={style.postContent}>
{/* Post Tags */} {/* Post Tags */}
<div className={style.postTags}> <div className={style.postTags}>