forked from mdx-editor/editor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.tsx
119 lines (110 loc) · 3.79 KB
/
index.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { realmPlugin } from '../../RealmWithPlugins'
import {
BOLD_ITALIC_STAR,
BOLD_ITALIC_UNDERSCORE,
BOLD_STAR,
BOLD_UNDERSCORE,
CHECK_LIST,
CODE,
ElementTransformer,
INLINE_CODE,
ITALIC_STAR,
ITALIC_UNDERSCORE,
LINK,
ORDERED_LIST,
QUOTE,
TextFormatTransformer,
TextMatchTransformer,
UNORDERED_LIST
} from '@lexical/markdown'
import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin.js'
import { $createHeadingNode, $isHeadingNode, HeadingNode, HeadingTagType } from '@lexical/rich-text'
import { ElementNode } from 'lexical'
import React from 'react'
import { $createCodeBlockNode, CodeBlockNode } from '../codeblock/CodeBlockNode'
import { activePlugins$, addComposerChild$, addNestedEditorChild$ } from '../core'
import { HEADING_LEVEL, allowedHeadingLevels$ } from '../headings'
/**
* A plugin that adds markdown shortcuts to the editor.
* @group Markdown Shortcuts
*/
export const markdownShortcutPlugin = realmPlugin({
init(realm) {
const pluginIds = realm.getValue(activePlugins$)
const allowedHeadingLevels: readonly HEADING_LEVEL[] = pluginIds.includes('headings') ? realm.getValue(allowedHeadingLevels$) : []
const transformers = pickTransformersForActivePlugins(pluginIds, allowedHeadingLevels)
realm.pubIn({
[addComposerChild$]: () => <MarkdownShortcutPlugin transformers={transformers} />,
[addNestedEditorChild$]: () => <MarkdownShortcutPlugin transformers={transformers} />
})
}
})
const createBlockNode = (createNode: (match: string[]) => ElementNode): ElementTransformer['replace'] => {
return (parentNode, children, match) => {
const node = createNode(match)
node.append(...children)
parentNode.replace(node)
node.select(0, 0)
}
}
function pickTransformersForActivePlugins(pluginIds: string[], allowedHeadingLevels: readonly HEADING_LEVEL[]) {
const transformers: (ElementTransformer | TextFormatTransformer | TextMatchTransformer)[] = [
BOLD_ITALIC_STAR,
BOLD_ITALIC_UNDERSCORE,
BOLD_STAR,
BOLD_UNDERSCORE,
INLINE_CODE,
ITALIC_STAR,
ITALIC_UNDERSCORE
// HIGHLIGHT,
// STRIKETHROUGH
]
if (pluginIds.includes('headings')) {
// Using a range is technically a bug, because the developer might have allowed h2 and h4, but not h3.
// However, it's a very unlikely edge case.
const minHeadingLevel = Math.min(...allowedHeadingLevels)
const maxHeadingLevel = Math.max(...allowedHeadingLevels)
const headingRegExp = new RegExp(`^(#{${minHeadingLevel},${maxHeadingLevel}})\\s`)
const HEADING: ElementTransformer = {
dependencies: [HeadingNode],
export: (node, exportChildren) => {
if (!$isHeadingNode(node)) {
return null
}
const level = Number(node.getTag().slice(1))
return '#'.repeat(level) + ' ' + exportChildren(node)
},
regExp: headingRegExp,
replace: createBlockNode((match) => {
const tag = `h${match[1].length}` as HeadingTagType
return $createHeadingNode(tag)
}),
type: 'element'
}
transformers.push(HEADING)
}
if (pluginIds.includes('quote')) {
transformers.push(QUOTE)
}
if (pluginIds.includes('link')) {
transformers.push(LINK)
}
if (pluginIds.includes('lists')) {
transformers.push(ORDERED_LIST, UNORDERED_LIST, CHECK_LIST)
}
if (pluginIds.includes('codeblock')) {
const codeTransformerCopy: ElementTransformer = {
...CODE,
dependencies: [CodeBlockNode],
replace: (parentNode, _children, match) => {
const codeBlockNode = $createCodeBlockNode({ code: '', language: match[1] ?? '', meta: '' })
parentNode.replace(codeBlockNode)
setTimeout(() => {
codeBlockNode.select()
}, 80)
}
}
transformers.push(codeTransformerCopy)
}
return transformers
}