-
-
Notifications
You must be signed in to change notification settings - Fork 377
/
node.ts
100 lines (95 loc) 路 3.4 KB
/
node.ts
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
/* Copyright 2021, Milkdown by Mirone. */
import { RemarkPlugin } from '@milkdown/core';
import { InputRule } from '@milkdown/prose/inputrules';
import { createNode } from '@milkdown/utils';
import nodeEmoji from 'node-emoji';
import remarkEmoji from 'remark-emoji';
import { input } from './constant';
import { filter } from './filter';
import { parse } from './parse';
import { twemojiPlugin } from './remark-twemoji';
export type EmojiOptions = {
maxListSize: number;
};
export const emojiNode = createNode<string, EmojiOptions>((utils, options) => {
const getStyle = () =>
utils.getStyle(
({ css }) => css`
.emoji {
height: 1em;
width: 1em;
margin: 0 0.05em 0 0.1em;
vertical-align: -0.1em;
}
`,
);
return {
id: 'emoji',
schema: () => ({
group: 'inline',
inline: true,
atom: true,
attrs: {
html: {
default: '',
},
},
parseDOM: [
{
tag: 'span[data-type="emoji"]',
getAttrs: (dom) => {
if (!(dom instanceof HTMLElement)) {
throw new Error();
}
return { html: dom.innerHTML };
},
},
],
toDOM: (node) => {
const span = document.createElement('span');
span.classList.add('emoji-wrapper');
span.dataset['type'] = 'emoji';
utils.themeManager.onFlush(() => {
const style = getStyle();
if (style) {
span.classList.add(style);
}
});
span.innerHTML = node.attrs['html'];
return { dom: span };
},
parseMarkdown: {
match: ({ type }) => type === 'emoji',
runner: (state, node, type) => {
state.addNode(type, { html: node['value'] as string });
},
},
toMarkdown: {
match: (node) => node.type.name === 'emoji',
runner: (state, node) => {
const span = document.createElement('span');
span.innerHTML = node.attrs['html'];
const img = span.querySelector('img');
const title = img?.title;
span.remove();
state.addNode('text', undefined, title);
},
},
}),
inputRules: (nodeType) => [
new InputRule(input, (state, match, start, end) => {
const content = match[0];
if (!content) return null;
const got = nodeEmoji.get(content);
if (!got || content.includes(got)) return null;
const html = parse(got);
return state.tr
.setMeta('emoji', true)
.replaceRangeWith(start, end, nodeType.create({ html }))
.scrollIntoView();
}),
],
remarkPlugins: () => [remarkEmoji as RemarkPlugin, twemojiPlugin],
prosePlugins: () => [filter(utils, options?.maxListSize ?? 6)],
};
});