forked from
standard.site/standard.site
Standard.site landing page built in Next.js
1import type { MDXComponents } from 'mdx/types'
2import { isValidElement } from 'react'
3import Link from 'next/link'
4import { ClickableHeading } from '@/app/components/docs/ClickableHeading'
5import { DocsPageMenu } from '@/app/components/docs/DocsPageMenu'
6import { Updated } from '@/app/components/docs/Updated'
7
8function extractHeadingText(node: React.ReactNode): string {
9 if (typeof node === 'string') return node
10 if (typeof node === 'number') return String(node)
11 if (Array.isArray(node)) return node.map(extractHeadingText).join('')
12 if (isValidElement(node)) {
13 if (node.type === Updated) return ''
14 const props = node.props as { children?: React.ReactNode }
15 return extractHeadingText(props.children)
16 }
17 return ''
18}
19
20export function useMDXComponents(components: MDXComponents): MDXComponents {
21 return {
22 h1: ({ children }) => (
23 <div className="flex items-start justify-between gap-2">
24 <ClickableHeading level={1} text={extractHeadingText(children)}>{children}</ClickableHeading>
25 <DocsPageMenu />
26 </div>
27 ),
28 h2: ({ children }) => (
29 <ClickableHeading level={2} text={extractHeadingText(children)}>{children}</ClickableHeading>
30 ),
31 h3: ({ children }) => (
32 <ClickableHeading level={3} text={extractHeadingText(children)}>{children}</ClickableHeading>
33 ),
34 h4: ({ children }) => (
35 <ClickableHeading level={4} text={extractHeadingText(children)}>{children}</ClickableHeading>
36 ),
37 p: ({ children }) => (
38 <p className="text-base sm:text-lg leading-relaxed tracking-tight text-muted mb-4">
39 {children}
40 </p>
41 ),
42 a: ({ href, children }) => {
43 const isInternal = href && (href.startsWith('/') || href.startsWith('#'))
44 const className = "text-base-content underline underline-offset-2 hover:text-muted transition-colors"
45
46 if (isInternal) {
47 return (
48 <Link href={href} className={className}>
49 {children}
50 </Link>
51 )
52 }
53
54 return (
55 <a href={href} className={className} target="_blank" rel="noopener noreferrer">
56 {children}
57 </a>
58 )
59 },
60 strong: ({ children }) => (
61 <strong className="font-medium text-base-content">{children}</strong>
62 ),
63 code: ({ children }) => (
64 <code className="font-mono text-sm bg-base-200 px-1.5 py-0.5 rounded text-base-content">
65 {children}
66 </code>
67 ),
68 pre: ({ children }) => (
69 <pre className="font-mono text-sm bg-base-200 border border-border rounded-xl p-4 overflow-x-auto mb-6">
70 {children}
71 </pre>
72 ),
73 ul: ({ children }) => (
74 <ul className="list-disc list-outside ml-5 text-muted mb-4 space-y-2">
75 {children}
76 </ul>
77 ),
78 ol: ({ children }) => (
79 <ol className="list-decimal list-outside ml-5 text-muted mb-4 space-y-2">
80 {children}
81 </ol>
82 ),
83 li: ({ children }) => (
84 <li className="text-base sm:text-lg leading-relaxed tracking-tight">
85 {children}
86 </li>
87 ),
88 blockquote: ({ children }) => (
89 <blockquote className="border-l-4 border-border pl-4 italic text-muted my-6">
90 {children}
91 </blockquote>
92 ),
93 hr: () => <hr className="h-px w-full bg-border my-8 border-0" />,
94 table: ({ children }) => (
95 <div className="overflow-x-auto mb-6">
96 <table className="w-full text-base border-collapse">
97 {children}
98 </table>
99 </div>
100 ),
101 thead: ({ children }) => (
102 <thead>{children}</thead>
103 ),
104 tbody: ({ children }) => (
105 <tbody>{children}</tbody>
106 ),
107 tr: ({ children }) => (
108 <tr>{children}</tr>
109 ),
110 th: ({ children }) => (
111 <th className="text-left font-medium text-base-content border-b border-border px-3 py-2">
112 {children}
113 </th>
114 ),
115 td: ({ children }) => (
116 <td className="text-muted border-b border-border px-3 py-2">
117 {children}
118 </td>
119 ),
120 ...components,
121 }
122}