-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
Copy pathwithHtml.js
117 lines (97 loc) · 3.3 KB
/
withHtml.js
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
// source: https://github.com/ianstormtaylor/slate/blob/main/site/examples/ts/paste-html.tsx
import { jsx } from 'slate-hyperscript';
import { Transforms } from 'slate';
const ELEMENT_TAGS = {
A: el => ({ type: 'link', url: el.getAttribute('href') }),
BLOCKQUOTE: () => ({ type: 'quote' }),
H1: () => ({ type: 'heading-one' }),
H2: () => ({ type: 'heading-two' }),
H3: () => ({ type: 'heading-three' }),
H4: () => ({ type: 'heading-four' }),
H5: () => ({ type: 'heading-five' }),
H6: () => ({ type: 'heading-six' }),
IMG: el => ({ type: 'image', url: el.getAttribute('src') }),
LI: () => ({ type: 'list-item' }),
OL: () => ({ type: 'numbered-list' }),
P: () => ({ type: 'paragraph' }),
PRE: () => ({ type: 'code' }),
UL: () => ({ type: 'bulleted-list' }),
};
// COMPAT: `B` is omitted here because Google Docs uses `<b>` in weird ways.
const TEXT_TAGS = {
CODE: () => ({ code: true }),
DEL: () => ({ strikethrough: true }),
EM: () => ({ italic: true }),
I: () => ({ italic: true }),
S: () => ({ strikethrough: true }),
STRONG: () => ({ bold: true }),
U: () => ({ underline: true }),
};
const INLINE_STYLES = {
'font-style': value => (value === 'italic' ? { italic: true } : {}),
'font-weight': value => (value === 'bold' || parseInt(value, 10) >= 600 ? { bold: true } : {}),
};
function deserialize(el) {
if (el.nodeType === 3) {
return el.textContent.replace(/(\r)?\n/g, '');
} else if (el.nodeType !== 1) {
return null;
} else if (el.nodeName === 'BR') {
return '\n';
}
const { nodeName } = el;
let parent = el;
if (nodeName === 'PRE' && el.childNodes[0] && el.childNodes[0].nodeName === 'CODE') {
parent = el.childNodes[0];
}
let children = Array.from(parent.childNodes).map(deserialize).flat();
if (children.length === 0) {
children = [{ text: '' }];
}
if (el.nodeName === 'BODY') {
return jsx('fragment', {}, children);
}
if (ELEMENT_TAGS[nodeName]) {
const attrs = ELEMENT_TAGS[nodeName](el);
return jsx('element', attrs, children);
}
if (TEXT_TAGS[nodeName]) {
const attrs = TEXT_TAGS[nodeName](el);
return children.map(child => jsx('text', attrs, child));
}
// Convert inline CSS on span elements generated by Google Docs
if (nodeName === 'SPAN') {
const attrs = {};
for (let i = 0; i < el.style.length; i++) {
const propertyName = el.style[i];
if (INLINE_STYLES[propertyName]) {
const propertyValue = el.style.getPropertyValue(propertyName);
const propertyStyle = INLINE_STYLES[propertyName](propertyValue);
Object.assign(attrs, propertyStyle);
}
}
return children.map(child => jsx('text', attrs, child));
}
return children;
}
function withHtml(editor) {
const { insertData, isInline, isVoid } = editor;
editor.isInline = element => {
return element.type === 'link' ? true : isInline(element);
};
editor.isVoid = element => {
return element.type === 'image' ? true : isVoid(element);
};
editor.insertData = data => {
const html = data.getData('text/html');
if (html) {
const parsed = new DOMParser().parseFromString(html, 'text/html');
const fragment = deserialize(parsed.body);
Transforms.insertFragment(editor, fragment);
return;
}
insertData(data);
};
return editor;
}
export default withHtml;