@8btc/mditor 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +16 -8
- package/dist/index.js +405 -98
- package/dist/index.min.js +1 -1
- package/dist/method.d.ts +10 -10
- package/dist/method.js +3 -3
- package/dist/method.min.js +1 -1
- package/dist/ts/util/attachLineNumbers.d.ts +26 -4
- package/dist/types/index.d.ts +13 -1
- package/package.json +1 -1
- package/src/assets/less/_line-number.less +4 -4
- package/src/index.ts +17 -0
- package/src/ts/ir/input.ts +180 -66
- package/src/ts/ir/process.ts +4 -2
- package/src/ts/sv/inputEvent.ts +169 -64
- package/src/ts/sv/process.ts +4 -2
- package/src/ts/util/attachLineNumbers.ts +32 -6
- package/src/ts/wysiwyg/afterRenderEvent.ts +7 -2
- package/src/ts/wysiwyg/index.ts +494 -262
- package/src/ts/wysiwyg/input.ts +148 -64
- package/src/ts/wysiwyg/renderDomByMd.ts +26 -11
package/src/ts/sv/inputEvent.ts
CHANGED
|
@@ -1,27 +1,48 @@
|
|
|
1
|
-
import {scrollCenter} from "../util/editorCommonEvent";
|
|
2
|
-
import {hasClosestByAttribute} from "../util/hasClosest";
|
|
3
|
-
import {getSelectPosition, setRangeByWbr} from "../util/selection";
|
|
4
|
-
import {
|
|
5
|
-
|
|
1
|
+
import { scrollCenter } from "../util/editorCommonEvent";
|
|
2
|
+
import { hasClosestByAttribute } from "../util/hasClosest";
|
|
3
|
+
import { getSelectPosition, setRangeByWbr } from "../util/selection";
|
|
4
|
+
import {
|
|
5
|
+
getSideByType,
|
|
6
|
+
processAfterRender,
|
|
7
|
+
processSpinVditorSVDOM,
|
|
8
|
+
} from "./process";
|
|
9
|
+
import { combineFootnote } from "./combineFootnote";
|
|
6
10
|
|
|
7
11
|
export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
|
|
8
12
|
const range = getSelection().getRangeAt(0).cloneRange();
|
|
9
13
|
let startContainer = range.startContainer;
|
|
10
|
-
if (
|
|
14
|
+
if (
|
|
15
|
+
range.startContainer.nodeType !== 3 &&
|
|
16
|
+
(range.startContainer as HTMLElement).tagName === "DIV"
|
|
17
|
+
) {
|
|
11
18
|
startContainer = range.startContainer.childNodes[range.startOffset - 1];
|
|
12
19
|
}
|
|
13
20
|
let blockElement = hasClosestByAttribute(startContainer, "data-block", "0");
|
|
14
21
|
// 不调用 lute 解析
|
|
15
|
-
if (
|
|
22
|
+
if (
|
|
23
|
+
blockElement &&
|
|
24
|
+
event &&
|
|
25
|
+
(event.inputType === "deleteContentBackward" || event.data === " ")
|
|
26
|
+
) {
|
|
16
27
|
// 开始可以输入空格
|
|
17
|
-
const startOffset = getSelectPosition(
|
|
28
|
+
const startOffset = getSelectPosition(
|
|
29
|
+
blockElement,
|
|
30
|
+
vditor.sv.element,
|
|
31
|
+
range
|
|
32
|
+
).start;
|
|
18
33
|
let startSpace = true;
|
|
19
|
-
for (
|
|
34
|
+
for (
|
|
35
|
+
let i = startOffset - 1;
|
|
20
36
|
// 软换行后有空格
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
i >
|
|
38
|
+
blockElement.textContent.substr(0, startOffset).lastIndexOf("\n");
|
|
39
|
+
i--
|
|
40
|
+
) {
|
|
41
|
+
if (
|
|
42
|
+
blockElement.textContent.charAt(i) !== " " &&
|
|
23
43
|
// 多个 tab 前删除不形成代码块 https://github.com/Vanessa219/vditor/issues/162 1
|
|
24
|
-
blockElement.textContent.charAt(i) !== "\t"
|
|
44
|
+
blockElement.textContent.charAt(i) !== "\t"
|
|
45
|
+
) {
|
|
25
46
|
startSpace = false;
|
|
26
47
|
break;
|
|
27
48
|
}
|
|
@@ -37,63 +58,120 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
|
|
|
37
58
|
if (event.inputType === "deleteContentBackward") {
|
|
38
59
|
// https://github.com/Vanessa219/vditor/issues/584 代码块 marker 删除
|
|
39
60
|
const codeBlockMarkerElement =
|
|
40
|
-
hasClosestByAttribute(
|
|
41
|
-
|
|
61
|
+
hasClosestByAttribute(
|
|
62
|
+
startContainer,
|
|
63
|
+
"data-type",
|
|
64
|
+
"code-block-open-marker"
|
|
65
|
+
) ||
|
|
66
|
+
hasClosestByAttribute(
|
|
67
|
+
startContainer,
|
|
68
|
+
"data-type",
|
|
69
|
+
"code-block-close-marker"
|
|
70
|
+
);
|
|
42
71
|
if (codeBlockMarkerElement) {
|
|
43
|
-
if (
|
|
44
|
-
|
|
72
|
+
if (
|
|
73
|
+
codeBlockMarkerElement.getAttribute("data-type") ===
|
|
74
|
+
"code-block-close-marker"
|
|
75
|
+
) {
|
|
76
|
+
const openMarkerElement = getSideByType(
|
|
77
|
+
startContainer,
|
|
78
|
+
"code-block-open-marker"
|
|
79
|
+
);
|
|
45
80
|
if (openMarkerElement) {
|
|
46
|
-
openMarkerElement.textContent =
|
|
81
|
+
openMarkerElement.textContent =
|
|
82
|
+
codeBlockMarkerElement.textContent;
|
|
47
83
|
processAfterRender(vditor);
|
|
48
84
|
return;
|
|
49
85
|
}
|
|
50
86
|
}
|
|
51
|
-
if (
|
|
52
|
-
|
|
87
|
+
if (
|
|
88
|
+
codeBlockMarkerElement.getAttribute("data-type") ===
|
|
89
|
+
"code-block-open-marker"
|
|
90
|
+
) {
|
|
91
|
+
const openMarkerElement = getSideByType(
|
|
92
|
+
startContainer,
|
|
93
|
+
"code-block-close-marker",
|
|
94
|
+
false
|
|
95
|
+
);
|
|
53
96
|
if (openMarkerElement) {
|
|
54
|
-
openMarkerElement.textContent =
|
|
97
|
+
openMarkerElement.textContent =
|
|
98
|
+
codeBlockMarkerElement.textContent;
|
|
55
99
|
processAfterRender(vditor);
|
|
56
100
|
return;
|
|
57
101
|
}
|
|
58
102
|
}
|
|
59
103
|
}
|
|
60
104
|
// https://github.com/Vanessa219/vditor/issues/877 数学公式输入删除生成节点
|
|
61
|
-
const mathBlockMarkerElement =
|
|
62
|
-
|
|
105
|
+
const mathBlockMarkerElement = hasClosestByAttribute(
|
|
106
|
+
startContainer,
|
|
107
|
+
"data-type",
|
|
108
|
+
"math-block-open-marker"
|
|
109
|
+
);
|
|
63
110
|
if (mathBlockMarkerElement) {
|
|
64
|
-
const mathBlockCloseElement =
|
|
65
|
-
|
|
111
|
+
const mathBlockCloseElement =
|
|
112
|
+
mathBlockMarkerElement.nextElementSibling
|
|
113
|
+
.nextElementSibling;
|
|
114
|
+
if (
|
|
115
|
+
mathBlockCloseElement &&
|
|
116
|
+
mathBlockCloseElement.getAttribute("data-type") ===
|
|
117
|
+
"math-block-close-marker"
|
|
118
|
+
) {
|
|
66
119
|
mathBlockCloseElement.remove();
|
|
67
120
|
processAfterRender(vditor);
|
|
68
121
|
}
|
|
69
122
|
return;
|
|
70
123
|
}
|
|
71
124
|
|
|
72
|
-
blockElement
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
125
|
+
blockElement
|
|
126
|
+
.querySelectorAll('[data-type="code-block-open-marker"]')
|
|
127
|
+
.forEach((item: HTMLElement) => {
|
|
128
|
+
if (item.textContent.length === 1) {
|
|
129
|
+
item.remove();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
blockElement
|
|
133
|
+
.querySelectorAll('[data-type="code-block-close-marker"]')
|
|
134
|
+
.forEach((item: HTMLElement) => {
|
|
135
|
+
if (item.textContent.length === 1) {
|
|
136
|
+
item.remove();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
82
139
|
|
|
83
140
|
// 标题删除
|
|
84
|
-
const headingElement = hasClosestByAttribute(
|
|
85
|
-
|
|
141
|
+
const headingElement = hasClosestByAttribute(
|
|
142
|
+
startContainer,
|
|
143
|
+
"data-type",
|
|
144
|
+
"heading-marker"
|
|
145
|
+
);
|
|
146
|
+
if (
|
|
147
|
+
headingElement &&
|
|
148
|
+
headingElement.textContent.indexOf("#") === -1
|
|
149
|
+
) {
|
|
86
150
|
processAfterRender(vditor);
|
|
87
151
|
return;
|
|
88
152
|
}
|
|
89
153
|
}
|
|
90
154
|
// 删除或空格不解析,否则会 format 回去
|
|
91
|
-
if (
|
|
92
|
-
(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
155
|
+
if (
|
|
156
|
+
(event.data === " " ||
|
|
157
|
+
event.inputType === "deleteContentBackward") &&
|
|
158
|
+
(hasClosestByAttribute(startContainer, "data-type", "padding") || // 场景:b 前进行删除 [> 1. a\n> b]
|
|
159
|
+
hasClosestByAttribute(
|
|
160
|
+
startContainer,
|
|
161
|
+
"data-type",
|
|
162
|
+
"li-marker"
|
|
163
|
+
) || // 场景:删除最后一个字符 [* 1\n* ]
|
|
164
|
+
hasClosestByAttribute(
|
|
165
|
+
startContainer,
|
|
166
|
+
"data-type",
|
|
167
|
+
"task-marker"
|
|
168
|
+
) || // 场景:删除最后一个字符 [* [ ] ]
|
|
169
|
+
hasClosestByAttribute(
|
|
170
|
+
startContainer,
|
|
171
|
+
"data-type",
|
|
172
|
+
"blockquote-marker"
|
|
173
|
+
)) // 场景:删除最后一个字符 [> ]
|
|
174
|
+
) {
|
|
97
175
|
processAfterRender(vditor);
|
|
98
176
|
return;
|
|
99
177
|
}
|
|
@@ -106,7 +184,10 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
|
|
|
106
184
|
if (!blockElement) {
|
|
107
185
|
blockElement = vditor.sv.element;
|
|
108
186
|
}
|
|
109
|
-
if (
|
|
187
|
+
if (
|
|
188
|
+
blockElement.firstElementChild?.getAttribute("data-type") ===
|
|
189
|
+
"link-ref-defs-block"
|
|
190
|
+
) {
|
|
110
191
|
// 修改链接引用
|
|
111
192
|
blockElement = vditor.sv.element;
|
|
112
193
|
}
|
|
@@ -120,10 +201,12 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
|
|
|
120
201
|
range.insertNode(document.createTextNode(Lute.Caret));
|
|
121
202
|
}
|
|
122
203
|
// 清除浏览器自带的样式
|
|
123
|
-
blockElement.querySelectorAll("[style]").forEach((item) => {
|
|
204
|
+
blockElement.querySelectorAll("[style]").forEach((item) => {
|
|
205
|
+
// 不可前置,否则会影响 newline 的样式
|
|
124
206
|
item.removeAttribute("style");
|
|
125
207
|
});
|
|
126
|
-
blockElement.querySelectorAll("font").forEach((item) => {
|
|
208
|
+
blockElement.querySelectorAll("font").forEach((item) => {
|
|
209
|
+
// 不可前置,否则会影响光标的位置
|
|
127
210
|
item.outerHTML = item.innerHTML;
|
|
128
211
|
});
|
|
129
212
|
let html = blockElement.textContent;
|
|
@@ -136,28 +219,45 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
|
|
|
136
219
|
html = blockElement.previousElementSibling.textContent + html;
|
|
137
220
|
blockElement.previousElementSibling.remove();
|
|
138
221
|
}
|
|
139
|
-
if (
|
|
222
|
+
if (
|
|
223
|
+
blockElement.previousElementSibling &&
|
|
224
|
+
html.indexOf("---\n") === 0
|
|
225
|
+
) {
|
|
140
226
|
// 确认 yaml-front 是否为首行
|
|
141
227
|
html = blockElement.previousElementSibling.textContent + html;
|
|
142
228
|
blockElement.previousElementSibling.remove();
|
|
143
229
|
}
|
|
144
230
|
// 添加链接引用
|
|
145
|
-
let footnotes = ""
|
|
231
|
+
let footnotes = "";
|
|
146
232
|
|
|
147
|
-
vditor.sv.element
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
233
|
+
vditor.sv.element
|
|
234
|
+
.querySelectorAll("[data-type='link-ref-defs-block']")
|
|
235
|
+
.forEach((item, index) => {
|
|
236
|
+
if (
|
|
237
|
+
item &&
|
|
238
|
+
!(blockElement as HTMLElement).isEqualNode(
|
|
239
|
+
item.parentElement
|
|
240
|
+
)
|
|
241
|
+
) {
|
|
242
|
+
footnotes += item.parentElement.textContent + "\n";
|
|
243
|
+
item.parentElement.remove();
|
|
244
|
+
}
|
|
245
|
+
});
|
|
153
246
|
|
|
154
247
|
// 添加脚注到文章头,便于lute处理
|
|
155
|
-
vditor.sv.element
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
248
|
+
vditor.sv.element
|
|
249
|
+
.querySelectorAll("[data-type='footnotes-link']")
|
|
250
|
+
.forEach((item, index) => {
|
|
251
|
+
if (
|
|
252
|
+
item &&
|
|
253
|
+
!(blockElement as HTMLElement).isEqualNode(
|
|
254
|
+
item.parentElement
|
|
255
|
+
)
|
|
256
|
+
) {
|
|
257
|
+
footnotes += item.parentElement.textContent + "\n";
|
|
258
|
+
item.parentElement.remove();
|
|
259
|
+
}
|
|
260
|
+
});
|
|
161
261
|
html = footnotes + html;
|
|
162
262
|
}
|
|
163
263
|
html = processSpinVditorSVDOM(html, vditor);
|
|
@@ -167,14 +267,19 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
|
|
|
167
267
|
blockElement.outerHTML = html;
|
|
168
268
|
}
|
|
169
269
|
|
|
170
|
-
vditor.sv.element
|
|
171
|
-
|
|
172
|
-
|
|
270
|
+
vditor.sv.element
|
|
271
|
+
.querySelectorAll("[data-type='link-ref-defs-block']")
|
|
272
|
+
.forEach((item) => {
|
|
273
|
+
vditor.sv.element.insertAdjacentElement(
|
|
274
|
+
"beforeend",
|
|
275
|
+
item.parentElement
|
|
276
|
+
);
|
|
277
|
+
});
|
|
173
278
|
|
|
174
279
|
// 合并脚注
|
|
175
280
|
combineFootnote(vditor.sv.element, (root: HTMLElement) => {
|
|
176
|
-
vditor.sv.element.insertAdjacentElement("beforeend", root)
|
|
177
|
-
})
|
|
281
|
+
vditor.sv.element.insertAdjacentElement("beforeend", root);
|
|
282
|
+
});
|
|
178
283
|
|
|
179
284
|
setRangeByWbr(vditor.sv.element, range);
|
|
180
285
|
|
package/src/ts/sv/process.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { log } from "../util/log";
|
|
|
7
7
|
import { getEditorRange, setRangeByWbr } from "../util/selection";
|
|
8
8
|
import { inputEvent } from "./inputEvent";
|
|
9
9
|
import { combineFootnote } from "./combineFootnote";
|
|
10
|
-
import {
|
|
10
|
+
import { attachLineNumbersToBlocksThrottled } from "../util/attachLineNumbers";
|
|
11
11
|
|
|
12
12
|
export const processPaste = (vditor: IVditor, text: string) => {
|
|
13
13
|
const range = getEditorRange(vditor);
|
|
@@ -178,7 +178,9 @@ export const processAfterRender = (
|
|
|
178
178
|
vditor.undo.addToUndoStack(vditor);
|
|
179
179
|
}
|
|
180
180
|
try {
|
|
181
|
-
|
|
181
|
+
if (vditor.options.lineNumber) {
|
|
182
|
+
attachLineNumbersToBlocksThrottled(vditor.sv.element, text);
|
|
183
|
+
}
|
|
182
184
|
} catch {
|
|
183
185
|
void 0;
|
|
184
186
|
}
|
|
@@ -152,9 +152,10 @@ export const attachLineNumbersToBlocks = (
|
|
|
152
152
|
const usedOrderedLines = new Set<number>();
|
|
153
153
|
const ORDERED_RE =
|
|
154
154
|
/\s*\d+(?:[.)、.。]|[\uFF0E\uFF09\u3001])\s+(?:\[(?: |x|X)\]\s+)?(.*)/;
|
|
155
|
+
const ORDERED_FULL = new RegExp(`^${ORDERED_RE.source}$`);
|
|
155
156
|
for (let i = 0; i < srcLines.length; i++) {
|
|
156
157
|
const raw = srcLines[i];
|
|
157
|
-
const m = raw.match(
|
|
158
|
+
const m = raw.match(ORDERED_FULL);
|
|
158
159
|
if (m && typeof m[1] === "string") {
|
|
159
160
|
const contentStripped = stripInlineForList(m[1]);
|
|
160
161
|
orderedListEntries.push({ ln: i + 1, content: contentStripped });
|
|
@@ -172,12 +173,9 @@ export const attachLineNumbersToBlocks = (
|
|
|
172
173
|
const orderedGroups: Array<number[]> = [];
|
|
173
174
|
for (let i = 0; i < srcLines.length; ) {
|
|
174
175
|
const raw = srcLines[i];
|
|
175
|
-
if (
|
|
176
|
+
if (ORDERED_FULL.test(raw)) {
|
|
176
177
|
const group: number[] = [];
|
|
177
|
-
while (
|
|
178
|
-
i < srcLines.length &&
|
|
179
|
-
new RegExp(`^${ORDERED_RE.source}$`).test(srcLines[i])
|
|
180
|
-
) {
|
|
178
|
+
while (i < srcLines.length && ORDERED_FULL.test(srcLines[i])) {
|
|
181
179
|
group.push(i + 1);
|
|
182
180
|
i++;
|
|
183
181
|
}
|
|
@@ -435,3 +433,31 @@ export const attachLineNumbersToBlocks = (
|
|
|
435
433
|
}
|
|
436
434
|
});
|
|
437
435
|
};
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* 节流后的行号更新函数,避免频繁更新 data-linenumber
|
|
439
|
+
*/
|
|
440
|
+
export const attachLineNumbersToBlocksThrottled = (() => {
|
|
441
|
+
let last = 0;
|
|
442
|
+
let timer = 0;
|
|
443
|
+
let lastArgs: [HTMLElement, string] | null = null;
|
|
444
|
+
const wait = 500;
|
|
445
|
+
const invoke = () => {
|
|
446
|
+
timer = 0;
|
|
447
|
+
last = Date.now();
|
|
448
|
+
const args = lastArgs;
|
|
449
|
+
lastArgs = null;
|
|
450
|
+
if (args) {
|
|
451
|
+
attachLineNumbersToBlocks(args[0], args[1]);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
return (root: HTMLElement, source: string) => {
|
|
455
|
+
const now = Date.now();
|
|
456
|
+
lastArgs = [root, source];
|
|
457
|
+
if (now - last >= wait) {
|
|
458
|
+
invoke();
|
|
459
|
+
} else if (!timer) {
|
|
460
|
+
timer = window.setTimeout(invoke, wait - (now - last));
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
})();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getMarkdown } from "../markdown/getMarkdown";
|
|
2
2
|
import { accessLocalStorage } from "../util/compatibility";
|
|
3
|
-
import {
|
|
3
|
+
import { attachLineNumbersToBlocksThrottled } from "../util/attachLineNumbers";
|
|
4
4
|
|
|
5
5
|
export const afterRenderEvent = (
|
|
6
6
|
vditor: IVditor,
|
|
@@ -43,7 +43,12 @@ export const afterRenderEvent = (
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
try {
|
|
46
|
-
|
|
46
|
+
if (vditor.options.lineNumber) {
|
|
47
|
+
attachLineNumbersToBlocksThrottled(
|
|
48
|
+
vditor.wysiwyg.element,
|
|
49
|
+
text
|
|
50
|
+
);
|
|
51
|
+
}
|
|
47
52
|
} catch {
|
|
48
53
|
void 0;
|
|
49
54
|
}
|