Browse Source

:art: #355 感觉比其他两个模式复杂多了,继续重写

Liyuan Li 5 years ago
parent
commit
25a1b0d9b0
4 changed files with 87 additions and 60 deletions
  1. 1 0
      src/assets/scss/_panel.scss
  2. 2 2
      src/index.ts
  3. 31 13
      src/ts/sv/inputEvent.ts
  4. 53 45
      src/ts/sv/processKeydown.ts

+ 1 - 0
src/assets/scss/_panel.scss

@@ -40,6 +40,7 @@
       min-width: auto;
       max-width: none;
       white-space: nowrap;
+      opacity: .86;
     }
 
     &--arrow:before {

+ 2 - 2
src/index.ts

@@ -99,8 +99,8 @@ class Vditor extends VditorMethod {
             this.vditor.upload = new Upload();
         }
 
-        // const lutePath = `http://192.168.0.107:9090/lute.min.js?${new Date().getTime()}`;
-        const lutePath = "src/js/lute/lute.min.js";
+        const lutePath = `http://192.168.0.107:9090/lute.min.js?${new Date().getTime()}`;
+        // const lutePath = "src/js/lute/lute.min.js";
         // const lutePath = `${mergedOptions.cdn}/dist/js/lute/lute.min.js`;
         addScript(lutePath, "vditorLuteScript").then(() => {
             this.vditor.lute = setLute({

+ 31 - 13
src/ts/sv/inputEvent.ts

@@ -7,17 +7,14 @@ import {processAfterRender} from "./process";
 export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
     const range = getSelection().getRangeAt(0).cloneRange();
     let blockElement = hasClosestByAttribute(range.startContainer, "data-block", "0");
-    // 前可以输入空格
-    if (blockElement && event) {
-        // 前空格处理
-        const startOffset = getSelectPosition(blockElement, range).start;
-
+    // 不调用 lute 解析
+    if (blockElement && event && (event.inputType === "deleteContentBackward" || event.data === " ")) {
         // 开始可以输入空格
+        const startOffset = getSelectPosition(blockElement, range).start;
         let startSpace = true;
         for (let i = startOffset - 1;
             // 软换行后有空格
-             i > blockElement.textContent.substr(0, startOffset).lastIndexOf("\n");
-             i--) {
+             i > blockElement.textContent.substr(0, startOffset).lastIndexOf("\n"); i--) {
             if (blockElement.textContent.charAt(i) !== " " &&
                 // 多个 tab 前删除不形成代码块 https://github.com/Vanessa219/vditor/issues/162 1
                 blockElement.textContent.charAt(i) !== "\t") {
@@ -25,26 +22,47 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
                 break;
             }
         }
-
         if (startOffset === 0) {
             startSpace = false;
         }
-
         if (startSpace) {
             return;
         }
+        //  list item marker 删除或空格
+        const listElement = hasClosestByAttribute(range.startContainer, "data-type", "li");
+        if (listElement) {
+            const liMarkerElement = listElement.querySelector('[data-type="li-marker"]');
+            if (getSelectPosition(listElement, range).start <= listElement.getAttribute("data-space").length +
+                (liMarkerElement ? liMarkerElement.textContent.length : 0) &&
+                event.inputType === "deleteContentBackward") {
+                return;
+            }
+            if (event.data === " " &&
+                hasClosestByAttribute(range.startContainer, "data-type", "li-marker")) {
+                return;
+            }
+        }
+        // heading marker 删除或空格
+        const headingElement = hasClosestByAttribute(range.startContainer, "data-type", "heading-marker");
+        if (headingElement &&  (event.data === " " ||  event.inputType === "deleteContentBackward")) {
+            return;
+        }
+        // blockquote markder删除或空格
+        const blockquoteElement = hasClosestByAttribute(range.startContainer, "data-type", "blockquote-marker");
+        if (blockquoteElement &&  (event.data === " " ||  event.inputType === "deleteContentBackward")) {
+            return;
+        }
     }
-    // TODO: 代码块、table 等元素不需要渲染
     if (!blockElement) {
         blockElement = vditor.sv.element;
     }
     // 添加光标位置
     range.insertNode(document.createTextNode(Lute.Caret));
     // 清除浏览器自带的样式
-    blockElement.querySelectorAll("[style]").forEach((item) => {
+    blockElement.querySelectorAll("[style]").forEach((item) => { // 不可前置,否则会影响 newline 的样式
         item.removeAttribute("style");
     });
-    blockElement.querySelectorAll("font").forEach((item) => {
+    blockElement.querySelectorAll("font").forEach((item) => { // 不可前置,否则会影响光标的位置
         item.outerHTML = item.innerHTML;
     });
     let html = blockElement.textContent;
@@ -54,11 +72,11 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
         html = blockElement.previousElementSibling.outerHTML + html;
         blockElement.previousElementSibling.remove();
     }
-    // TODO: 链接引用,脚注?
     const isSVElement = blockElement.isEqualNode(vditor.sv.element);
     if (isSVElement) {
         html = blockElement.textContent;
     } else {
+        // TODO: 链接引用,脚注?
         if (blockElement.previousElementSibling) {
             html = blockElement.previousElementSibling.textContent + html;
             blockElement.previousElementSibling.remove();

+ 53 - 45
src/ts/sv/processKeydown.ts

@@ -16,21 +16,29 @@ export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
     }
     // 仅处理以下快捷键操作
     if (event.key !== "Enter" && event.key !== "Tab" && event.key !== "Backspace" && event.key.indexOf("Arrow") === -1
-        && !isCtrl(event) && event.key !== "Escape" && event.key !== " ") {
+        && !isCtrl(event) && event.key !== "Escape") {
         return false;
     }
     const range = getEditorRange(vditor.sv.element);
     const startContainer = range.startContainer;
-    // 回车
-    if (event.key === "Enter" && !isCtrl(event) && !event.altKey) {
-        const blockElement = hasClosestByAttribute(startContainer, "data-block", "0");
-        if (blockElement && !blockElement.textContent.endsWith("\n")) {
-            // 结尾需 \n
-            blockElement.insertAdjacentText("beforeend", "\n");
-        }
-        range.insertNode(document.createTextNode("\n"));
-        range.collapse(false);
-        if (!blockElement || (blockElement && blockElement.innerHTML.trim() !== "")) {
+
+    // list item
+    const listElement = hasClosestByAttribute(startContainer, "data-type", "li");
+    if (listElement && event.key === "Enter" && !isCtrl(event) && !event.altKey) {
+        const markerElement = listElement.querySelector('[data-type="li-marker"]');
+        if (markerElement && getSelectPosition(listElement, range).start ===
+            markerElement.textContent.length + listElement.getAttribute("data-space").length) {
+            // 清空列表标记符
+            if (listElement.getAttribute("data-space") === "") {
+                markerElement.remove();
+            } else {
+                markerElement.previousElementSibling.remove();
+            }
+        } else {
+            // 添加标记符号
+            range.insertNode(document.createTextNode("\n" +
+                (markerElement ? listElement.getAttribute("data-space") + markerElement.textContent : "")));
+            range.collapse(false);
             inputEvent(vditor);
         }
         event.preventDefault();
@@ -66,44 +74,44 @@ export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
         return true;
     }
 
-    // 删除或空格不调用 lute 解析
-    if (event.key === "Backspace" || event.key === " ") {
-        range.insertNode(document.createElement("wbr"));
-        const wbrElement = document.querySelector("wbr");
-        let markerElement;
-        // blockquote, heading, list marker 删除或空格
-        if (wbrElement.parentElement?.className.indexOf("vditor-sv__marker") > -1) {
-            markerElement = wbrElement.parentElement;
-        } else if (wbrElement.previousSibling && wbrElement.previousSibling.nodeType !== 3 &&
-            (wbrElement.previousSibling as HTMLElement).className.indexOf("vditor-sv__marker") > -1) {
-            markerElement = wbrElement.previousSibling;
-        }
-        if (markerElement) {
-            const lastIndex = wbrElement.previousSibling.textContent.lastIndexOf(" ");
-            if (event.key === "Backspace" && lastIndex > -1) {
-                markerElement.textContent = markerElement.textContent.substr(0, lastIndex);
-                range.selectNode(markerElement.firstChild);
-                range.collapse(false);
-                event.preventDefault();
-                wbrElement.remove();
-                return true;
-            }
-            if (event.key === " ") {
-                markerElement.textContent = markerElement.textContent + " ";
-                range.selectNode(markerElement.firstChild);
-                range.collapse(false);
-                event.preventDefault();
-                wbrElement.remove();
-                return true;
-            }
-        }
-        wbrElement.remove();
-    }
-
     // tab
     if (fixTab(vditor, range, event)) {
         return true;
     }
 
+    // 回车,除 list item 外
+    if (event.key === "Enter" && !isCtrl(event) && !event.altKey) {
+        // 添加 \n
+        range.insertNode(document.createTextNode("\n"));
+        range.collapse(false);
+        event.preventDefault();
+        return true;
+    }
+
+    // 删除后光标前有 newline 的处理
+    const blockElement = hasClosestByAttribute(startContainer, "data-block", "0");
+    if (blockElement && event.key === "Backspace" && !isCtrl(event) && !event.altKey && !event.shiftKey) {
+        const startIndex = getSelectPosition(blockElement, range).start;
+        // 光标在每一行的开始位置
+        if (startIndex === 0 && blockElement.previousElementSibling &&
+            blockElement.previousElementSibling.lastElementChild.getAttribute("data-type") === "newline") {
+            blockElement.previousElementSibling.lastElementChild.remove();
+            if (blockElement.textContent.trim() !== "") {
+                inputEvent(vditor);
+            }
+            event.preventDefault();
+            return true;
+        }
+        // 光标在每一行的第一个字符后
+        const textElement = hasClosestByAttribute(startContainer, "data-type", "text") ||
+            hasClosestByAttribute(startContainer, "data-type", "li-marker");
+        if (textElement && range.startOffset === 1 && textElement.previousElementSibling &&
+            textElement.previousElementSibling.getAttribute("data-type") === "newline") {
+            range.setStart(startContainer, 0);
+            range.extractContents();
+            event.preventDefault();
+            return true;
+        }
+    }
     return false;
 };