Browse Source

输入[`{"']自动包裹对应字符 (#1418)

* fixbug hint

button按钮type默认下是submit类型https://www.w3.org/TR/2011/WD-html5-20110525/the-button-element.html#attr-button-type

* feat:支持自动包裹鼠标选中的字符串

* feat:补充ir、wysiwyg封装到selection中

* chore:修复Delete和ctrl+v也要重置选中内容

* chore:选中后,输入第一次包裹重置

* chore:通过键盘选中内容,获取oldSelectContent
790891601 2 years ago
parent
commit
9f84027def

+ 1 - 0
src/index.ts

@@ -444,6 +444,7 @@ class Vditor extends VditorMethod {
         this.vditor = {
             currentMode: mergedOptions.mode,
             element: id,
+            oldSelectContent: '',
             hint: new Hint(mergedOptions.hint.extend),
             lute: undefined,
             options: mergedOptions,

+ 3 - 1
src/ts/ir/input.ts

@@ -8,7 +8,7 @@ import {
 import {hasClosestByTag} from "../util/hasClosestByHeadings";
 import {log} from "../util/log";
 import {processCodeRender} from "../util/processCode";
-import {getSelectPosition, setRangeByWbr} from "../util/selection";
+import {getSelectPosition, setRangeByWbr, setSelectionParcel} from "../util/selection";
 import {renderToc} from "../util/toc";
 import {processAfterRender} from "./process";
 import {getMarkdown} from "../markdown/getMarkdown";
@@ -79,6 +79,8 @@ export const input = (vditor: IVditor, range: Range, ignoreSpace = false, event?
         item.classList.remove("vditor-ir__node--expand");
     });
 
+    setSelectionParcel(vditor, range, event);
+
     if (!blockElement) {
         // 使用顶级块元素,应使用 innerHTML
         blockElement = vditor.ir.element;

+ 1 - 1
src/ts/sv/index.ts

@@ -16,7 +16,7 @@ import {processAfterRender} from "./process";
 class Editor {
     public range: Range;
     public element: HTMLPreElement;
-    public composingLock: boolean = false;
+    public composingLock = false;
     public processTimeoutId: number;
     public hlToolbarTimeoutId: number;
     public preventInput: boolean;

+ 2 - 1
src/ts/sv/inputEvent.ts

@@ -1,6 +1,6 @@
 import {scrollCenter} from "../util/editorCommonEvent";
 import {hasClosestByAttribute} from "../util/hasClosest";
-import {getSelectPosition, setRangeByWbr} from "../util/selection";
+import {getSelectPosition, setRangeByWbr, setSelectionParcel} from "../util/selection";
 import {getSideByType, processAfterRender, processSpinVditorSVDOM} from "./process";
 
 export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
@@ -10,6 +10,7 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
         startContainer = range.startContainer.childNodes[range.startOffset - 1];
     }
     let blockElement = hasClosestByAttribute(startContainer, "data-block", "0");
+    setSelectionParcel(vditor, range, event);
     // 不调用 lute 解析
     if (blockElement && event && (event.inputType === "deleteContentBackward" || event.data === " ")) {
         // 开始可以输入空格

+ 17 - 4
src/ts/util/editorCommonEvent.ts

@@ -124,10 +124,19 @@ export const hotkeyEvent = (vditor: IVditor, editorElement: HTMLElement) => {
         }
 
         // 重置 comment
-        if (vditor.options.comment.enable && vditor.currentMode === "wysiwyg" &&
-            (event.key === "Backspace" || matchHotKey("⌘X", event))) {
+        if((event.key === "Backspace" || matchHotKey("⌘X", event)) &&
+            vditor.options.comment.enable && vditor.currentMode === "wysiwyg") {
             vditor.wysiwyg.getComments(vditor);
         }
+         //重置选中内容
+        if(event.key === 'Backspace' || event.key === "Delete" || matchHotKey("⌘X", event) || matchHotKey("⌘v", event)) {
+            vditor.oldSelectContent = '';
+        }
+        //如果通过键盘选中内容,获取oldSelectContent
+        const selectText = getSelectText(vditor[vditor.currentMode].element);
+        if(selectText.trim()) {
+            vditor.oldSelectContent = selectText
+        }
 
         if (vditor.currentMode === "sv") {
             if (mdProcessKeydown(vditor, event)) {
@@ -235,7 +244,8 @@ export const hotkeyEvent = (vditor: IVditor, editorElement: HTMLElement) => {
 
 export const selectEvent = (vditor: IVditor, editorElement: HTMLElement) => {
     editorElement.addEventListener("selectstart", (event: Event & { target: HTMLElement }) => {
-        editorElement.onmouseup = () => {
+        const mouseup = (e: Event)=> {
+            e.stopPropagation(); //阻止冒泡
             setTimeout(() => { // 鼠标放开后 range 没有即时更新
                 const selectText = getSelectText(vditor[vditor.currentMode].element);
                 if (selectText.trim()) {
@@ -255,7 +265,10 @@ export const selectEvent = (vditor: IVditor, editorElement: HTMLElement) => {
                         vditor.wysiwyg.hideComment();
                     }
                 }
+                vditor.oldSelectContent = selectText.trim();
             });
-        };
+        }
+        editorElement.onmouseup = mouseup
+        document.onmouseup = mouseup
     });
 };

+ 19 - 0
src/ts/util/selection.ts

@@ -235,6 +235,25 @@ export const setRangeByWbr = (element: HTMLElement, range: Range) => {
     setSelectionFocus(range);
 };
 
+// 设置包裹范围字符
+export const setSelectionParcel = (vditor: IVditor, range: Range, event: InputEvent) => {
+    const char = event ? event.data : '';
+    const oldSelectContent = vditor.oldSelectContent;
+    const charMaps = ["'", '"', '`', '{'];
+    let charIndex = -1;
+    if((charIndex = charMaps.indexOf(char)) > -1) {
+        let token = charMaps[charIndex];
+        if(token === '`') token = `\``;
+        else if(token === '{') token = `}`;
+        const textNode = document.createTextNode(oldSelectContent ? `${oldSelectContent}${token}` : `${token}`)
+        if(oldSelectContent) {
+            range.deleteContents();
+            range.insertNode(textNode);
+            vditor.oldSelectContent = '';
+        }
+    }
+}
+
 export const insertHTML = (html: string, vditor: IVditor) => {
     // 使用 lute 方法会添加 p 元素,只有一个 p 元素的时候进行删除
     const tempElement = document.createElement("div");

+ 3 - 1
src/ts/wysiwyg/input.ts

@@ -5,7 +5,7 @@ import {
 import { hasClosestByTag} from "../util/hasClosestByHeadings";
 import {log} from "../util/log";
 import {processCodeRender} from "../util/processCode";
-import {setRangeByWbr} from "../util/selection";
+import {setRangeByWbr, setSelectionParcel} from "../util/selection";
 import {renderToc} from "../util/toc";
 import {afterRenderEvent} from "./afterRenderEvent";
 import {previoueIsEmptyA} from "./inlineTag";
@@ -18,6 +18,8 @@ export const input = (vditor: IVditor, range: Range, event?: InputEvent) => {
         blockElement = vditor.wysiwyg.element;
     }
 
+    setSelectionParcel(vditor, range, event);
+
     if (event && event.inputType !== "formatItalic"
         && event.inputType !== "deleteByDrag"
         && event.inputType !== "insertFromDrop"

+ 1 - 0
types/index.d.ts

@@ -754,6 +754,7 @@ interface IVditor {
     element: HTMLElement;
     options: IOptions;
     originalInnerHTML: string;
+    oldSelectContent: string;
     lute: Lute;
     currentMode: "sv" | "wysiwyg" | "ir";
     devtools?: {