TextBodySection.tsx44 lines · main
1'use client'
2
3import React from 'react'
4import type { Components } from 'react-markdown'
5import ReactMarkdown from 'react-markdown'
6
7import type { GoTextBodySection } from '../schemas'
8import { slugify } from './TableOfContents'
9
10function getTextContent(node: React.ReactNode): string {
11 if (typeof node === 'string') return node
12 if (typeof node === 'number') return String(node)
13 if (Array.isArray(node)) return node.map(getTextContent).join('')
14 if (React.isValidElement(node) && node.props) {
15 return getTextContent((node.props as { children?: React.ReactNode }).children)
16 }
17 return ''
18}
19
20const headingWithId =
21 (Tag: 'h1' | 'h2' | 'h3'): Components[typeof Tag] =>
22 ({ children }) => {
23 const text = getTextContent(children)
24 const id = slugify(text)
25 return (
26 <Tag id={id} className="scroll-mt-24">
27 {children}
28 </Tag>
29 )
30 }
31
32const components: Components = {
33 h1: headingWithId('h1'),
34 h2: headingWithId('h2'),
35 h3: headingWithId('h3'),
36}
37
38export default function TextBodySection({ section }: { section: GoTextBodySection }) {
39 return (
40 <article className="prose prose-headings:text-foreground prose-p:text-foreground-light prose-strong:text-foreground prose-li:text-foreground-light prose-a:text-brand-link prose-a:decoration-brand-link max-w-none">
41 <ReactMarkdown components={components}>{section.content}</ReactMarkdown>
42 </article>
43 )
44}