-
Notifications
You must be signed in to change notification settings - Fork 0
/
mdx-components.tsx
100 lines (88 loc) · 2.28 KB
/
mdx-components.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { ReactNode, createElement } from 'react'
import Link from 'next/link'
import type { MDXComponents } from 'mdx/types'
import { highlighter } from './lib/shiki'
function CustomLink(props: { href: string; children: ReactNode }) {
const { href, ...props2 } = props
if (href.startsWith('/')) {
return (
<Link href={href} {...props2}>
{props.children}
</Link>
)
}
if (href.startsWith('#')) {
return <a {...props} />
}
return <a target='_blank' rel='noopener noreferrer' {...props} />
}
async function Code({ children, ...props }: any) {
const isMultiline = /[\n\r]/.test(children)
const html = await (
await highlighter()
).codeToHtml(children, {
lang: 'typescript',
theme: 'one-dark-pro',
transformers: [
{
pre(node) {
this.addClassToHast(
node,
`rounded text-sm ${
isMultiline ? 'p-4 overflow-auto' : 'px-1 py-0.5 inline-block'
}`
)
},
code(node) {
this.addClassToHast(node, 'not-prose')
},
},
],
})
return <span dangerouslySetInnerHTML={{ __html: html }} {...props} />
}
function slugify(str: string) {
return str
.toString()
.toLowerCase()
.trim() // Remove whitespace from both ends of a string
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/&/g, '-and-') // Replace & with 'and'
.replace(/[^\w\-]+/g, '') // Remove all non-word characters except for -
.replace(/\-\-+/g, '-') // Replace multiple - with single -
}
function createHeading(level: number) {
const createHeadingChild = ({ children }: { children: string }) => {
const slug = slugify(children)
return createElement(
`h${level}`,
{ id: slug },
[
createElement('a', {
href: `#${slug}`,
key: `link-${slug}`,
className: 'anchor',
}),
],
children
)
}
return createHeadingChild
}
const COMPONENTS = {
h1: createHeading(1),
h2: createHeading(2),
h3: createHeading(3),
h4: createHeading(4),
h5: createHeading(5),
h6: createHeading(6),
a: CustomLink,
code: Code,
}
export function useMDXComponents(components: MDXComponents): MDXComponents {
// @ts-ignore
return {
...COMPONENTS,
...components,
}
}