diff options
| author | schererleander <leander@schererleander.de> | 2025-06-25 01:00:49 +0200 |
|---|---|---|
| committer | schererleander <leander@schererleander.de> | 2025-06-25 01:00:59 +0200 |
| commit | 64564a6fee02708d375a349d75ce49d515e66f8d (patch) | |
| tree | fa7685e6f51e90af9f8bd45fae1f2133b1237772 /src/pages/Post.tsx | |
| parent | 12aa0baac380149cdbb36803605be42368e736ad (diff) | |
add markdown-based blog
Diffstat (limited to 'src/pages/Post.tsx')
| -rw-r--r-- | src/pages/Post.tsx | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/src/pages/Post.tsx b/src/pages/Post.tsx new file mode 100644 index 0000000..17022d2 --- /dev/null +++ b/src/pages/Post.tsx @@ -0,0 +1,75 @@ +import { useEffect, useState } from "react"; +import { useParams, Link } from "react-router-dom"; +import matter from "gray-matter"; +import ReactMarkdown from "react-markdown"; +import CodeSnippet from "../components/CodeSnippet"; +import LinkWithIcon from "../components/LinkWithIcon"; +import NotFoundPage from "./404Page"; + +interface PostMeta { + title: string; + date: string; + cover?: string; +} + +export default function Post() { + const { slug } = useParams<{ slug: string }>(); + const [meta, setMeta] = useState<PostMeta | null>(null); + const [content, setContent] = useState(""); + const [notFound, setNotFound] = useState(false); + + useEffect(() => { + import(`../blog/${slug}.md?raw`) + .then((m) => { + const { data, content } = matter(m.default); + setMeta(data as PostMeta); + setContent(content); + }) + .catch(() => setNotFound(true)); + }, [slug]); + + if (!meta) return null; + if (notFound) return <NotFoundPage />; + + return ( + <article className="prose prose-zinc dark:prose-invert mx-auto px-4 py-10"> + <Link to="/blog" className="no-underline"> + ← Zurück zum Blog + </Link> + + {meta.cover && ( + <img + src={meta.cover} + alt={meta.title} + className="w-full h-60 object-cover rounded-lg my-6" + /> + )} + + <h1>{meta.title}</h1> + <p className="text-sm text-zinc-500 mb-8"> + {new Date(meta.date).toLocaleDateString("de-DE")} + </p> + + <article className="markdown-body"> + <ReactMarkdown + components={{ + code({ children }) { + const text = String(children).replace(/\n$/, ""); + return <CodeSnippet code={text} initialLines={5} />; + }, + + a({ href = "", children, ...props }) { + return ( + <LinkWithIcon href={href} {...props}> + {children} + </LinkWithIcon> + ); + }, + }} + > + {content} + </ReactMarkdown> + </article> + </article> + ); +}
\ No newline at end of file |
