vditor 3.11.3 → 4.0.0

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.
Files changed (39) hide show
  1. package/README.md +1 -0
  2. package/dist/index.css +32 -79
  3. package/dist/index.js +3762 -3993
  4. package/dist/index.min.js +1 -1
  5. package/dist/js/lute/lute.min.js +2 -2
  6. package/dist/method.js +15 -6
  7. package/dist/method.min.js +1 -1
  8. package/dist/ts/sv/index.d.ts +1 -5
  9. package/dist/ts/sv/process.d.ts +1 -3
  10. package/dist/ts/upload/getElement.d.ts +1 -1
  11. package/dist/ts/upload/index.d.ts +2 -0
  12. package/dist/ts/util/editorCommonEvent.d.ts +1 -1
  13. package/dist/types/index.d.ts +7 -5
  14. package/package.json +1 -1
  15. package/src/assets/less/_sv.less +2 -98
  16. package/src/assets/less/_wysiwyg.less +30 -0
  17. package/src/index.ts +45 -14
  18. package/src/ts/hint/index.ts +9 -4
  19. package/src/ts/ir/highlightToolbarIR.ts +4 -4
  20. package/src/ts/markdown/getMarkdown.ts +3 -3
  21. package/src/ts/sv/index.ts +37 -76
  22. package/src/ts/sv/process.ts +48 -164
  23. package/src/ts/sv/processKeydown.ts +19 -186
  24. package/src/ts/toolbar/EditMode.ts +3 -10
  25. package/src/ts/toolbar/Emoji.ts +8 -1
  26. package/src/ts/toolbar/Record.ts +10 -2
  27. package/src/ts/toolbar/index.ts +3 -0
  28. package/src/ts/ui/initUI.ts +7 -3
  29. package/src/ts/undo/index.ts +32 -17
  30. package/src/ts/upload/index.ts +42 -10
  31. package/src/ts/util/editorCommonEvent.ts +18 -9
  32. package/src/ts/util/fixBrowserBehavior.ts +106 -36
  33. package/src/ts/util/selection.ts +11 -3
  34. package/src/ts/wysiwyg/highlightToolbarWYSIWYG.ts +53 -1
  35. package/src/ts/wysiwyg/processKeydown.ts +47 -0
  36. package/dist/ts/sv/combineFootnote.d.ts +0 -6
  37. package/dist/ts/sv/inputEvent.d.ts +0 -1
  38. package/src/ts/sv/combineFootnote.ts +0 -27
  39. package/src/ts/sv/inputEvent.ts +0 -188
package/src/index.ts CHANGED
@@ -13,7 +13,6 @@ import {Outline} from "./ts/outline/index";
13
13
  import {Preview} from "./ts/preview/index";
14
14
  import {Resize} from "./ts/resize/index";
15
15
  import {Editor} from "./ts/sv/index";
16
- import {inputEvent} from "./ts/sv/inputEvent";
17
16
  import {processAfterRender as processSVAfterRender, processPaste} from "./ts/sv/process";
18
17
  import {Tip} from "./ts/tip/index";
19
18
  import {Toolbar} from "./ts/toolbar/index";
@@ -167,10 +166,11 @@ class Vditor extends VditorMethod {
167
166
  this.vditor.toolbar.elements,
168
167
  Constants.EDIT_TOOLBARS.concat(["undo", "redo", "fullscreen", "edit-mode"]),
169
168
  );
170
- this.vditor[this.vditor.currentMode].element.setAttribute(
171
- "contenteditable",
172
- "false",
173
- );
169
+ if (this.vditor.currentMode === "sv") {
170
+ this.vditor.sv.element.disabled = true;
171
+ } else {
172
+ this.vditor[this.vditor.currentMode].element.setAttribute("contenteditable", "false");
173
+ }
174
174
  }
175
175
 
176
176
  /** 解除编辑器禁用 */
@@ -180,7 +180,11 @@ class Vditor extends VditorMethod {
180
180
  Constants.EDIT_TOOLBARS.concat(["undo", "redo", "fullscreen", "edit-mode"]),
181
181
  );
182
182
  this.vditor.undo.resetIcon(this.vditor);
183
- this.vditor[this.vditor.currentMode].element.setAttribute("contenteditable", "true");
183
+ if (this.vditor.currentMode === "sv") {
184
+ this.vditor.sv.element.disabled = false;
185
+ } else {
186
+ this.vditor[this.vditor.currentMode].element.setAttribute("contenteditable", "true");
187
+ }
184
188
  }
185
189
 
186
190
  /** 返回选中的字符串 */
@@ -188,7 +192,10 @@ class Vditor extends VditorMethod {
188
192
  if (this.vditor.currentMode === "wysiwyg") {
189
193
  return getSelectText(this.vditor.wysiwyg.element);
190
194
  } else if (this.vditor.currentMode === "sv") {
191
- return getSelectText(this.vditor.sv.element);
195
+ return this.vditor.sv.element.value.substring(
196
+ this.vditor.sv.element.selectionStart,
197
+ this.vditor.sv.element.selectionEnd,
198
+ );
192
199
  } else if (this.vditor.currentMode === "ir") {
193
200
  return getSelectText(this.vditor.ir.element);
194
201
  }
@@ -201,6 +208,15 @@ class Vditor extends VditorMethod {
201
208
 
202
209
  /** 获取焦点位置 */
203
210
  public getCursorPosition() {
211
+ if (this.vditor.currentMode === "sv") {
212
+ const element = this.vditor.sv.element;
213
+ const lineHeight = parseInt(getComputedStyle(element).lineHeight, 10) || 22;
214
+ const line = element.value.substring(0, element.selectionStart).split("\n").length - 1;
215
+ return {
216
+ left: 0,
217
+ top: line * lineHeight - element.scrollTop,
218
+ };
219
+ }
204
220
  return getCursorPosition(this.vditor[this.vditor.currentMode].element);
205
221
  }
206
222
 
@@ -258,6 +274,14 @@ class Vditor extends VditorMethod {
258
274
 
259
275
  /** 删除选中内容 */
260
276
  public deleteValue() {
277
+ if (this.vditor.currentMode === "sv") {
278
+ if (this.vditor.sv.element.selectionStart === this.vditor.sv.element.selectionEnd) {
279
+ return;
280
+ }
281
+ processPaste(this.vditor, "");
282
+ processSVAfterRender(this.vditor);
283
+ return;
284
+ }
261
285
  if (window.getSelection().isCollapsed) {
262
286
  return;
263
287
  }
@@ -266,11 +290,23 @@ class Vditor extends VditorMethod {
266
290
 
267
291
  /** 更新选中内容 */
268
292
  public updateValue(value: string) {
293
+ if (this.vditor.currentMode === "sv") {
294
+ processPaste(this.vditor, value);
295
+ processSVAfterRender(this.vditor);
296
+ return;
297
+ }
269
298
  document.execCommand("insertHTML", false, value);
270
299
  }
271
300
 
272
301
  /** 在焦点处插入内容,并默认进行 Markdown 渲染 */
273
302
  public insertValue(value: string, render = true) {
303
+ if (this.vditor.currentMode === "sv") {
304
+ processPaste(this.vditor, value);
305
+ if (render) {
306
+ processSVAfterRender(this.vditor);
307
+ }
308
+ return;
309
+ }
274
310
  const range = getEditorRange(this.vditor);
275
311
  range.collapse(true);
276
312
  // https://github.com/Vanessa219/vditor/issues/716
@@ -279,12 +315,7 @@ class Vditor extends VditorMethod {
279
315
  tmpElement.innerHTML = value;
280
316
  range.insertNode(tmpElement.content.cloneNode(true));
281
317
  range.collapse(false);
282
- if (this.vditor.currentMode === "sv") {
283
- this.vditor.sv.preventInput = true;
284
- if (render) {
285
- inputEvent(this.vditor);
286
- }
287
- } else if (this.vditor.currentMode === "wysiwyg") {
318
+ if (this.vditor.currentMode === "wysiwyg") {
288
319
  // 由于 https://github.com/Vanessa219/vditor/issues/1566 不能使用 this.vditor.wysiwyg.preventInput = true;
289
320
  if (render) {
290
321
  input(this.vditor, getSelection().getRangeAt(0));
@@ -314,7 +345,7 @@ class Vditor extends VditorMethod {
314
345
  /** 设置编辑器内容 */
315
346
  public setValue(markdown: string, clearStack = false) {
316
347
  if (this.vditor.currentMode === "sv") {
317
- this.vditor.sv.element.innerHTML = `<div data-block='0'>${this.vditor.lute.SpinVditorSVDOM(markdown)}</div>`;
348
+ this.vditor.sv.element.value = markdown;
318
349
  processSVAfterRender(this.vditor, {
319
350
  enableAddUndoStack: true,
320
351
  enableHint: false,
@@ -23,6 +23,10 @@ export class Hint {
23
23
  }
24
24
 
25
25
  public render(vditor: IVditor) {
26
+ if (vditor.currentMode === "sv") {
27
+ this.element.style.display = "none";
28
+ return;
29
+ }
26
30
  if (!window.getSelection().focusNode) {
27
31
  return;
28
32
  }
@@ -128,6 +132,9 @@ ${i === 0 ? "class='vditor-hint--current'" : ""}> ${html}</button>`;
128
132
 
129
133
  public fillEmoji = (element: HTMLElement, vditor: IVditor) => {
130
134
  this.element.style.display = "none";
135
+ if (vditor.currentMode === "sv") {
136
+ return;
137
+ }
131
138
 
132
139
  const value = decodeURIComponent(element.getAttribute("data-value"));
133
140
  const range: Range = window.getSelection().getRangeAt(0);
@@ -172,9 +179,7 @@ ${i === 0 ? "class='vditor-hint--current'" : ""}> ${html}</button>`;
172
179
  range.deleteContents();
173
180
 
174
181
  if (vditor.options.hint.parse) {
175
- if (vditor.currentMode === "sv") {
176
- insertHTML(vditor.lute.SpinVditorSVDOM(value), vditor);
177
- } else if (vditor.currentMode === "wysiwyg") {
182
+ if (vditor.currentMode === "wysiwyg") {
178
183
  insertHTML(vditor.lute.SpinVditorDOM(value), vditor);
179
184
  } else {
180
185
  insertHTML(vditor.lute.SpinVditorIRDOM(value), vditor);
@@ -182,7 +187,7 @@ ${i === 0 ? "class='vditor-hint--current'" : ""}> ${html}</button>`;
182
187
  } else {
183
188
  insertHTML(value, vditor);
184
189
  }
185
- if (this.splitChar === ":" && value.indexOf(":") > -1 && vditor.currentMode !== "sv") {
190
+ if (this.splitChar === ":" && value.indexOf(":") > -1) {
186
191
  range.insertNode(document.createTextNode(" "));
187
192
  }
188
193
  range.collapse(false);
@@ -5,12 +5,12 @@ import {hasClosestByHeadings} from "../util/hasClosestByHeadings";
5
5
  import {getEditorRange, selectIsEditor} from "../util/selection";
6
6
 
7
7
  export const highlightToolbarIR = (vditor: IVditor) => {
8
- clearTimeout(vditor[vditor.currentMode].hlToolbarTimeoutId);
9
- vditor[vditor.currentMode].hlToolbarTimeoutId = window.setTimeout(() => {
10
- if (vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
8
+ clearTimeout(vditor.ir.hlToolbarTimeoutId);
9
+ vditor.ir.hlToolbarTimeoutId = window.setTimeout(() => {
10
+ if (vditor.ir.element.getAttribute("contenteditable") === "false") {
11
11
  return;
12
12
  }
13
- if (!selectIsEditor(vditor[vditor.currentMode].element)) {
13
+ if (!selectIsEditor(vditor.ir.element)) {
14
14
  return;
15
15
  }
16
16
 
@@ -2,11 +2,11 @@ import {code160to32} from "../util/code160to32";
2
2
 
3
3
  export const getMarkdown = (vditor: IVditor) => {
4
4
  if (vditor.currentMode === "sv") {
5
- return code160to32(`${vditor.sv.element.textContent}\n`.replace(/\n\n$/, "\n"));
5
+ return code160to32(`${vditor.sv.element.value}\n`.replace(/\n\n$/, "\n"));
6
6
  } else if (vditor.currentMode === "wysiwyg") {
7
- return vditor.lute.VditorDOM2Md(vditor.wysiwyg.element.innerHTML);
7
+ return code160to32(vditor.lute.VditorDOM2Md(vditor.wysiwyg.element.innerHTML));
8
8
  } else if (vditor.currentMode === "ir") {
9
- return vditor.lute.VditorIRDOM2Md(vditor.ir.element.innerHTML);
9
+ return code160to32(vditor.lute.VditorIRDOM2Md(vditor.ir.element.innerHTML));
10
10
  }
11
11
  return "";
12
12
  };
@@ -1,55 +1,39 @@
1
- import {isCtrl, isFirefox} from "../util/compatibility";
2
- import {
3
- blurEvent,
4
- copyEvent, cutEvent,
5
- dropEvent,
6
- focusEvent,
7
- hotkeyEvent,
8
- scrollCenter,
9
- selectEvent,
10
- } from "../util/editorCommonEvent";
1
+ import {getMarkdown} from "../markdown/getMarkdown";
2
+ import {isFirefox} from "../util/compatibility";
3
+ import {dropEvent, focusEvent, hotkeyEvent} from "../util/editorCommonEvent";
11
4
  import {paste} from "../util/fixBrowserBehavior";
12
- import {getSelectText} from "../util/getSelectText";
13
- import {inputEvent} from "./inputEvent";
14
- import {processAfterRender} from "./process";
5
+ import {processAfterRender, processPaste} from "./process";
15
6
 
16
7
  class Editor {
17
- public range: Range;
18
- public element: HTMLPreElement;
8
+ public element: HTMLTextAreaElement;
19
9
  public composingLock: boolean = false;
20
10
  public processTimeoutId: number;
21
- public hlToolbarTimeoutId: number;
22
- public preventInput: boolean;
23
11
 
24
12
  constructor(vditor: IVditor) {
25
- this.element = document.createElement("pre");
13
+ this.element = document.createElement("textarea");
26
14
  this.element.className = "vditor-sv vditor-reset";
27
- this.element.setAttribute("placeholder", vditor.options.placeholder);
28
- this.element.setAttribute("contenteditable", "true");
29
- this.element.setAttribute("spellcheck", "false");
15
+ this.element.placeholder = vditor.options.placeholder;
16
+ this.element.spellcheck = false;
30
17
 
31
18
  this.bindEvent(vditor);
32
-
33
19
  focusEvent(vditor, this.element);
34
- blurEvent(vditor, this.element);
35
20
  hotkeyEvent(vditor, this.element);
36
- selectEvent(vditor, this.element);
37
- dropEvent(vditor, this.element);
38
- copyEvent(vditor, this.element, this.copy);
39
- cutEvent(vditor, this.element, this.copy);
40
- }
41
-
42
- private copy(event: ClipboardEvent, vditor: IVditor) {
43
- event.stopPropagation();
44
- event.preventDefault();
45
- event.clipboardData.setData("text/plain", getSelectText(vditor[vditor.currentMode].element));
21
+ dropEvent(vditor, this.element, (code: string) => {
22
+ processPaste(vditor, code.replace(/&lt;/g, "<").replace(/&amp;/g, "&"));
23
+ });
46
24
  }
47
25
 
48
26
  private bindEvent(vditor: IVditor) {
49
- this.element.addEventListener("paste", (event: ClipboardEvent & { target: HTMLElement }) => {
27
+ this.element.addEventListener("blur", () => {
28
+ if (vditor.options.blur) {
29
+ vditor.options.blur(getMarkdown(vditor));
30
+ }
31
+ });
32
+
33
+ this.element.addEventListener("paste", (event: ClipboardEvent & {target: HTMLElement}) => {
50
34
  paste(vditor, event, {
51
35
  pasteCode: (code: string) => {
52
- document.execCommand("insertHTML", false, code);
36
+ processPaste(vditor, code.replace(/&lt;/g, "<").replace(/&amp;/g, "&"));
53
37
  },
54
38
  });
55
39
  });
@@ -62,61 +46,38 @@ class Editor {
62
46
  const textHeight = this.element.clientHeight;
63
47
  const textScrollHeight = this.element.scrollHeight - parseFloat(this.element.style.paddingBottom || "0");
64
48
  const preview = vditor.preview.element;
65
- if ((textScrollTop / textHeight > 0.5)) {
66
- preview.scrollTop = (textScrollTop + textHeight) *
67
- preview.scrollHeight / textScrollHeight - textHeight;
49
+ if (textScrollTop / textHeight > 0.5) {
50
+ preview.scrollTop = (textScrollTop + textHeight) * preview.scrollHeight / textScrollHeight - textHeight;
68
51
  } else {
69
- preview.scrollTop = textScrollTop *
70
- preview.scrollHeight / textScrollHeight;
52
+ preview.scrollTop = textScrollTop * preview.scrollHeight / textScrollHeight;
71
53
  }
72
54
  });
73
55
 
74
- this.element.addEventListener("compositionstart", (event: InputEvent) => {
56
+ this.element.addEventListener("compositionstart", () => {
75
57
  this.composingLock = true;
76
58
  });
77
59
 
78
- this.element.addEventListener("compositionend", (event: InputEvent) => {
60
+ this.element.addEventListener("compositionend", () => {
61
+ this.composingLock = false;
79
62
  if (!isFirefox()) {
80
- inputEvent(vditor, event);
63
+ processAfterRender(vditor);
81
64
  }
82
- this.composingLock = false;
83
65
  });
84
66
 
85
- this.element.addEventListener("input", (event: InputEvent) => {
86
- if (event.inputType === "deleteByDrag" || event.inputType === "insertFromDrop") {
87
- // https://github.com/Vanessa219/vditor/issues/801 编辑器内容拖拽问题
88
- return;
89
- }
90
- if (this.composingLock || event.data === "‘" || event.data === "“" || event.data === "《") {
91
- return;
92
- }
93
- if (this.preventInput) {
94
- this.preventInput = false;
95
- processAfterRender(vditor, {
96
- enableAddUndoStack: true,
97
- enableHint: true,
98
- enableInput: true,
99
- });
100
- return;
67
+ this.element.addEventListener("input", () => {
68
+ if (!this.composingLock) {
69
+ processAfterRender(vditor);
101
70
  }
102
- inputEvent(vditor, event);
103
71
  });
104
72
 
105
- this.element.addEventListener("keyup", (event) => {
106
- if (event.isComposing || isCtrl(event)) {
107
- return;
108
- }
109
- if ((event.key === "Backspace" || event.key === "Delete") &&
110
- vditor.sv.element.innerHTML !== "" && vditor.sv.element.childNodes.length === 1 &&
111
- vditor.sv.element.firstElementChild && vditor.sv.element.firstElementChild.tagName === "DIV"
112
- && vditor.sv.element.firstElementChild.childElementCount === 2
113
- && (vditor.sv.element.firstElementChild.textContent === "" || vditor.sv.element.textContent === "\n")) {
114
- // 为空时显示 placeholder
115
- vditor.sv.element.innerHTML = "";
116
- return;
117
- }
118
- if (event.key === "Enter") {
119
- scrollCenter(vditor);
73
+ this.element.addEventListener("select", () => {
74
+ const selectedText = this.element.value.substring(this.element.selectionStart, this.element.selectionEnd);
75
+ if (selectedText.trim()) {
76
+ if (vditor.options.select) {
77
+ vditor.options.select(selectedText);
78
+ }
79
+ } else if (vditor.options.unSelect) {
80
+ vditor.options.unSelect();
120
81
  }
121
82
  });
122
83
  }
@@ -1,104 +1,14 @@
1
1
  import {getMarkdown} from "../markdown/getMarkdown";
2
2
  import {accessLocalStorage} from "../util/compatibility";
3
- import {scrollCenter} from "../util/editorCommonEvent";
4
- import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest";
5
- import {hasClosestByTag} from "../util/hasClosestByHeadings";
6
- import {log} from "../util/log";
7
- import {getEditorRange, setRangeByWbr} from "../util/selection";
8
- import {inputEvent} from "./inputEvent";
9
- import {combineFootnote} from "./combineFootnote";
10
3
 
11
-
12
- export const processPaste = (vditor: IVditor, text: string) => {
13
- const range = getEditorRange(vditor);
14
- range.extractContents();
15
- range.insertNode(document.createTextNode(Lute.Caret));
16
- range.insertNode(document.createTextNode(text));
17
- let blockElement = hasClosestByAttribute(range.startContainer, "data-block", "0");
18
- if (!blockElement) {
19
- blockElement = vditor.sv.element;
20
- }
21
- let spinHTML = vditor.lute.SpinVditorSVDOM(blockElement.textContent)
22
- spinHTML = "<div data-block='0'>" +
23
- spinHTML.replace(/<span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span><span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span></g, '<span data-type="newline"><br /><span style="display: none">\n</span></span><span data-type="newline"><br /><span style="display: none">\n</span></span></div><div data-block="0"><') +
24
- "</div>";
25
- if (blockElement.isEqualNode(vditor.sv.element)) {
26
- blockElement.innerHTML = spinHTML;
27
- } else {
28
- blockElement.outerHTML = spinHTML;
29
- }
30
- combineFootnote(vditor.sv.element)
31
- setRangeByWbr(vditor.sv.element, range);
32
-
33
- scrollCenter(vditor);
34
- };
35
-
36
- export const getSideByType = (spanNode: Node, type: string, isPrevious = true) => {
37
- let sideElement = spanNode as Element;
38
- if (sideElement.nodeType === 3) {
39
- sideElement = sideElement.parentElement;
40
- }
41
- while (sideElement) {
42
- if (sideElement.getAttribute("data-type") === type) {
43
- return sideElement;
44
- }
45
- if (isPrevious) {
46
- sideElement = sideElement.previousElementSibling;
47
- } else {
48
- sideElement = sideElement.nextElementSibling;
49
- }
50
- }
51
- return false;
4
+ export const replaceSelection = (vditor: IVditor, text: string, selectionMode: SelectionMode = "end") => {
5
+ const element = vditor.sv.element;
6
+ element.setRangeText(text, element.selectionStart, element.selectionEnd, selectionMode);
7
+ element.focus();
52
8
  };
53
9
 
54
- export const processSpinVditorSVDOM = (html: string, vditor: IVditor) => {
55
- log("SpinVditorSVDOM", html, "argument", vditor.options.debugger);
56
- const spinHTML = vditor.lute.SpinVditorSVDOM(html)
57
- html = "<div data-block='0'>" +
58
- spinHTML.replace(/<span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span><span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span></g, '<span data-type="newline"><br /><span style="display: none">\n</span></span><span data-type="newline"><br /><span style="display: none">\n</span></span></div><div data-block="0"><') +
59
- "</div>";
60
- log("SpinVditorSVDOM", html, "result", vditor.options.debugger);
61
- return html;
62
- };
63
-
64
- export const processPreviousMarkers = (spanElement: HTMLElement) => {
65
- const spanType = spanElement.getAttribute("data-type");
66
- let previousElement = spanElement.previousElementSibling;
67
- // 有内容的子列表/标题,在其 marker 后换行
68
- let markerText = (spanType && spanType !== "text" && spanType !== "table" && spanType !== "heading-marker" &&
69
- spanType !== "newline" && spanType !== "yaml-front-matter-open-marker" && spanType !== "yaml-front-matter-close-marker"
70
- && spanType !== "code-block-info" && spanType !== "code-block-close-marker" && spanType !== "code-block-open-marker") ?
71
- spanElement.textContent : "";
72
- let hasNL = false;
73
- if (spanType === "newline") {
74
- hasNL = true;
75
- }
76
- while (previousElement && !hasNL) {
77
- const previousType = previousElement.getAttribute("data-type");
78
- if (previousType === "li-marker" || previousType === "blockquote-marker" || previousType === "task-marker" ||
79
- previousType === "padding") {
80
- const previousText = previousElement.textContent;
81
- if (previousType === "li-marker" &&
82
- (spanType === "code-block-open-marker" || spanType === "code-block-info")) {
83
- // https://github.com/Vanessa219/vditor/issues/586
84
- markerText = previousText.replace(/\S/g, " ") + markerText;
85
- } else if (spanType === "code-block-close-marker" &&
86
- previousElement.nextElementSibling.isSameNode(spanElement)) {
87
- // https://github.com/Vanessa219/vditor/issues/594
88
- const openMarker = getSideByType(spanElement, "code-block-open-marker");
89
- if (openMarker && openMarker.previousElementSibling) {
90
- previousElement = openMarker.previousElementSibling;
91
- markerText = previousText + markerText;
92
- }
93
- } else {
94
- markerText = previousText + markerText;
95
- }
96
- } else if (previousType === "newline") {
97
- hasNL = true;
98
- }
99
- previousElement = previousElement.previousElementSibling;
100
- }
101
- return markerText;
10
+ export const processPaste = (vditor: IVditor, text: string) => {
11
+ replaceSelection(vditor, text);
102
12
  };
103
13
 
104
14
  export const processAfterRender = (vditor: IVditor, options = {
@@ -106,10 +16,6 @@ export const processAfterRender = (vditor: IVditor, options = {
106
16
  enableHint: false,
107
17
  enableInput: true,
108
18
  }) => {
109
- if (options.enableHint) {
110
- vditor.hint.render(vditor);
111
- }
112
-
113
19
  vditor.preview.render(vditor);
114
20
 
115
21
  const text = getMarkdown(vditor);
@@ -141,75 +47,53 @@ export const processAfterRender = (vditor: IVditor, options = {
141
47
  };
142
48
 
143
49
  export const processHeading = (vditor: IVditor, value: string) => {
144
- const range = getEditorRange(vditor);
145
- const headingElement = hasClosestByTag(range.startContainer, "SPAN");
146
- if (headingElement && headingElement.textContent.trim() !== "") {
147
- value = "\n" + value;
148
- }
149
- range.collapse(true);
150
- document.execCommand("insertHTML", false, value);
50
+ const element = vditor.sv.element;
51
+ const lineStart = element.value.lastIndexOf("\n", element.selectionStart - 1) + 1;
52
+ const lineEndIndex = element.value.indexOf("\n", element.selectionEnd);
53
+ const lineEnd = lineEndIndex === -1 ? element.value.length : lineEndIndex;
54
+ const selectedLines = element.value.substring(lineStart, lineEnd);
55
+ const heading = value ? value + selectedLines.replace(/^#{1,6}\s+/, "") : selectedLines.replace(/^#{1,6}\s+/, "");
56
+ element.setRangeText(heading, lineStart, lineEnd, "select");
57
+ element.focus();
58
+ processAfterRender(vditor);
151
59
  };
152
60
 
153
61
  export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: string, suffix: string) => {
154
- const range = getEditorRange(vditor);
62
+ const element = vditor.sv.element;
155
63
  const commandName = actionBtn.getAttribute("data-type");
156
- // 添加
157
- if (vditor.sv.element.childNodes.length === 0) {
158
- vditor.sv.element.innerHTML = `<span data-type="p" data-block="0"><span data-type="text"><wbr></span></span><span data-type="newline"><br><span style="display: none">
159
- </span></span>`;
160
- setRangeByWbr(vditor.sv.element, range);
161
- }
162
- const blockElement = hasClosestBlock(range.startContainer);
163
- const spanElement = hasClosestByTag(range.startContainer, "SPAN");
164
- if (!blockElement) {
165
- return;
166
- }
167
- if (commandName === "link") {
168
- let html;
169
- if (range.toString() === "") {
170
- html = `${prefix}${Lute.Caret}${suffix}`;
171
- } else {
172
- html = `${prefix}${range.toString()}${suffix.replace(")", Lute.Caret + ")")}`;
173
- }
174
- document.execCommand("insertHTML", false, html);
175
- return;
176
- } else if (commandName === "italic" || commandName === "bold" || commandName === "strike" ||
177
- commandName === "inline-code" || commandName === "code" || commandName === "table" || commandName === "line") {
178
- let html;
179
- // https://github.com/Vanessa219/vditor/issues/563 代码块不需要后面的 ```
180
- if (range.toString() === "") {
181
- html = `${prefix}${Lute.Caret}${commandName === "code" ? "" : suffix}`;
182
- } else {
183
- html = `${prefix}${range.toString()}${Lute.Caret}${commandName === "code" ? "" : suffix}`;
184
- }
185
- if (commandName === "table" || (commandName === "code" && spanElement && spanElement.textContent !== "")) {
186
- html = "\n\n" + html;
187
- } else if (commandName === "line") {
188
- html = `\n\n${prefix}\n${Lute.Caret}`;
189
- }
190
- document.execCommand("insertHTML", false, html);
191
- return;
192
- } else if (commandName === "check" || commandName === "list" || commandName === "ordered-list" ||
64
+ const start = element.selectionStart;
65
+ const end = element.selectionEnd;
66
+ const selectedText = element.value.substring(start, end);
67
+ let text = "";
68
+ let selectionStart = start + prefix.length;
69
+ let selectionEnd = selectionStart + selectedText.length;
70
+
71
+ if (commandName === "check" || commandName === "list" || commandName === "ordered-list" ||
193
72
  commandName === "quote") {
194
- if (spanElement) {
195
- let marker = "* ";
196
- if (commandName === "check") {
197
- marker = "* [ ] ";
198
- } else if (commandName === "ordered-list") {
199
- marker = "1. ";
200
- } else if (commandName === "quote") {
201
- marker = "> ";
202
- }
203
- const newLine = getSideByType(spanElement, "newline");
204
- if (newLine) {
205
- newLine.insertAdjacentText("afterend", marker);
206
- } else {
207
- blockElement.insertAdjacentText("afterbegin", marker);
208
- }
209
- inputEvent(vditor);
210
- return;
211
- }
73
+ const lineStart = element.value.lastIndexOf("\n", start - 1) + 1;
74
+ const lineEndIndex = element.value.indexOf("\n", end);
75
+ const lineEnd = lineEndIndex === -1 ? element.value.length : lineEndIndex;
76
+ const lines = element.value.substring(lineStart, lineEnd);
77
+ text = lines.split("\n").map((line) => prefix + line).join("\n");
78
+ element.setRangeText(text, lineStart, lineEnd, "select");
79
+ } else if (commandName === "line") {
80
+ text = `${start === 0 ? "" : "\n\n"}${prefix}\n`;
81
+ element.setRangeText(text, start, end, "end");
82
+ } else if (commandName === "code") {
83
+ text = `${prefix}\n${selectedText}${suffix}`;
84
+ element.setRangeText(text, start, end, "end");
85
+ selectionStart = start + prefix.length + 1;
86
+ selectionEnd = selectionStart + selectedText.length;
87
+ element.setSelectionRange(selectionStart, selectionEnd);
88
+ } else if (commandName === "table") {
89
+ text = `${start === 0 ? "" : "\n\n"}${prefix}${suffix}`;
90
+ element.setRangeText(text, start, end, "end");
91
+ } else {
92
+ text = `${prefix}${selectedText}${suffix}`;
93
+ element.setRangeText(text, start, end, "end");
94
+ element.setSelectionRange(selectionStart, selectionEnd);
212
95
  }
213
- setRangeByWbr(vditor.sv.element, range);
96
+
97
+ element.focus();
214
98
  processAfterRender(vditor);
215
99
  };