From 42cddbff455a4fff8558f4731c87468ec46f8d3b Mon Sep 17 00:00:00 2001 From: Vanessa Date: Wed, 26 Apr 2023 19:24:18 +0800 Subject: [PATCH] :art: https://github.com/siyuan-note/siyuan/issues/8108 --- app/src/protyle/util/insertHTML.ts | 3 +- app/src/protyle/wysiwyg/input.ts | 37 +++----- app/src/protyle/wysiwyg/keydown.ts | 15 ++-- app/src/protyle/wysiwyg/turnIntoList.ts | 109 ++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 33 deletions(-) create mode 100644 app/src/protyle/wysiwyg/turnIntoList.ts diff --git a/app/src/protyle/util/insertHTML.ts b/app/src/protyle/util/insertHTML.ts index bd1174cb0..8fe5a2495 100644 --- a/app/src/protyle/util/insertHTML.ts +++ b/app/src/protyle/util/insertHTML.ts @@ -84,7 +84,8 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false, } const tempElement = document.createElement("template"); // 需要再 spin 一次 https://github.com/siyuan-note/siyuan/issues/7118 - tempElement.innerHTML = protyle.lute.SpinBlockDOM(html); + tempElement.innerHTML = protyle.lute.SpinBlockDOM(html) || + html; // 空格会被 Spin 不再,需要使用原文 const editableElement = getContenteditableElement(blockElement); // 使用 lute 方法会添加 p 元素,只有一个 p 元素或者只有一个字符串或者为 b 时的时候只拷贝内部 if (!isBlock) { diff --git a/app/src/protyle/wysiwyg/input.ts b/app/src/protyle/wysiwyg/input.ts index f18b2c68f..20919824a 100644 --- a/app/src/protyle/wysiwyg/input.ts +++ b/app/src/protyle/wysiwyg/input.ts @@ -11,6 +11,7 @@ import {blockRender} from "../markdown/blockRender"; import {hideElements} from "../ui/hideElements"; import {hasClosestByAttribute} from "../util/hasClosest"; import {fetchPost, fetchSyncPost} from "../../util/fetch"; +import {headingTurnIntoList, turnIntoTaskList} from "./turnIntoList"; export const input = async (protyle: IProtyle, blockElement: HTMLElement, range: Range, needRender = true) => { if (!blockElement.parentElement) { @@ -41,13 +42,19 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range: } const wbrElement = document.createElement("wbr"); range.insertNode(wbrElement); - let id = blockElement.getAttribute("data-node-id"); + const id = blockElement.getAttribute("data-node-id"); if (type !== "NodeCodeBlock" && (editElement.innerHTML.endsWith("\n") || editElement.innerHTML.endsWith("\n\n"))) { // 软换行 updateTransaction(protyle, id, blockElement.outerHTML, blockElement.outerHTML.replace("\n", "")); wbrElement.remove(); return; } + if (turnIntoTaskList(protyle, type, blockElement, editElement)) { + return; + } + if (headingTurnIntoList(protyle, type, blockElement, editElement)) { + return; + } // table、粗体 中也会有 br,仅用于类似#a#,删除后会产生的 br const brElement = blockElement.querySelector("br"); if (brElement && brElement.parentElement.tagName !== "TD" && brElement.parentElement.tagName !== "TH" && ( @@ -80,9 +87,8 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range: } } let html = blockElement.outerHTML; - let todoOldHTML = ""; let focusHR = false; - if (editElement.textContent === "---" && !blockElement.classList.contains("code-block")) { + if (editElement.textContent === "---" && type !== "NodeCodeBlock") { html = `
`; const nextBlockElement = getNextBlock(editElement); if (nextBlockElement) { @@ -94,23 +100,6 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range: } else { html += genEmptyBlock(false, true); } - } else if (!blockElement.classList.contains("code-block") && (["[]", "[ ]", "[x]", "[X]", "【】", "【 】", "【x】", "【X】"].includes(editElement.textContent))) { - const isDone = editElement.textContent.indexOf("x") > -1 || editElement.textContent.indexOf("X") > -1; - if (blockElement.parentElement.classList.contains("li") && - blockElement.parentElement.childElementCount === 3 // https://ld246.com/article/1659315815506 - ) { - // 仅有一项的列表才可转换 - if (!blockElement.parentElement.parentElement.classList.contains("protyle-wysiwyg") && // https://ld246.com/article/1659315815506 - blockElement.parentElement.parentElement.childElementCount === 2) { - html = `
`; - id = blockElement.parentElement.parentElement.getAttribute("data-node-id"); - blockElement = blockElement.parentElement.parentElement; - todoOldHTML = blockElement.outerHTML; - } - } else { - html = `
`; - todoOldHTML = blockElement.outerHTML; - } } else { if (trimStartText.startsWith("```") || trimStartText.startsWith("~~~") || trimStartText.startsWith("···") || trimStartText.indexOf("\n```") > -1 || trimStartText.indexOf("\n~~~") > -1 || trimStartText.indexOf("\n···") > -1) { @@ -137,7 +126,7 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range: // 内容删空后使用上下键,光标无法到达 https://github.com/siyuan-note/siyuan/issues/4167 https://ld246.com/article/1636256333803 tempElement.content.childElementCount === 1 && getContenteditableElement(tempElement.content.firstElementChild)?.innerHTML === "" ) && - !(tempElement.content.childElementCount === 1 && tempElement.content.firstElementChild.classList.contains("code-block") && blockElement.classList.contains("code-block")) + !(tempElement.content.childElementCount === 1 && tempElement.content.firstElementChild.classList.contains("code-block") && type === "NodeCodeBlock") ) { log("SpinBlockDOM", blockElement.outerHTML, "argument", protyle.options.debugger); log("SpinBlockDOM", html, "result", protyle.options.debugger); @@ -216,10 +205,10 @@ export const input = async (protyle: IProtyle, blockElement: HTMLElement, range: protyle.hint.render(protyle); } hideElements(["gutter"], protyle); - updateInput(html, protyle, id, type, todoOldHTML); + updateInput(html, protyle, id, type); }; -const updateInput = (html: string, protyle: IProtyle, id: string, type: string, oldHTML?: string) => { +const updateInput = (html: string, protyle: IProtyle, id: string, type: string) => { const tempElement = document.createElement("template"); tempElement.innerHTML = html; const doOperations: IOperation[] = []; @@ -237,7 +226,7 @@ const updateInput = (html: string, protyle: IProtyle, id: string, type: string, }); undoOperations.push({ id, - data: protyle.wysiwyg.lastHTMLs[id] || oldHTML, + data: protyle.wysiwyg.lastHTMLs[id], action: "update" }); } else { diff --git a/app/src/protyle/wysiwyg/keydown.ts b/app/src/protyle/wysiwyg/keydown.ts index df88e2b25..056383a3e 100644 --- a/app/src/protyle/wysiwyg/keydown.ts +++ b/app/src/protyle/wysiwyg/keydown.ts @@ -577,7 +577,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { event.preventDefault(); return; } - + const selectText = range.toString(); // 上下左右光标移动 if (!event.altKey && !event.shiftKey && !isCtrl(event) && !event.isComposing && (event.key.indexOf("Arrow") > -1)) { // 需使用 editabled,否则代码块会把语言字数算入 @@ -637,7 +637,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { } } } - } else if (range.toString() === "" && (event.key === "ArrowDown" || event.key === "ArrowRight") && nodeElement.isSameNode(getLastBlock(protyle.wysiwyg.element.lastElementChild)) && + } else if (selectText === "" && (event.key === "ArrowDown" || event.key === "ArrowRight") && nodeElement.isSameNode(getLastBlock(protyle.wysiwyg.element.lastElementChild)) && // 表格无法右移动 https://ld246.com/article/1631434502215 !hasClosestByMatchTag(range.startContainer, "TD") && !hasClosestByMatchTag(range.startContainer, "TH")) { // 页面按向下/右箭头丢失焦点 https://ld246.com/article/1629954026096 @@ -648,7 +648,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { event.preventDefault(); focusByRange(range); } - } else if (range.toString() === "" && event.key === "ArrowLeft" && nodeElement.isSameNode(getFirstBlock(protyle.wysiwyg.element.firstElementChild))) { + } else if (selectText === "" && event.key === "ArrowLeft" && nodeElement.isSameNode(getFirstBlock(protyle.wysiwyg.element.firstElementChild))) { // 页面向左箭头丢失焦点 https://github.com/siyuan-note/siyuan/issues/2768 const firstEditElement = getContenteditableElement(nodeElement); if (firstEditElement && getSelectionOffset(firstEditElement, undefined, range).start === 0) { @@ -688,7 +688,6 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { return; } - const selectText = range.toString(); // 删除,不可使用 !isCtrl(event),否则软删除回导致 https://github.com/siyuan-note/siyuan/issues/5607 // 不可使用 !event.shiftKey,否则 https://ld246.com/article/1666434796806 if (!event.altKey && (event.key === "Backspace" || event.key === "Delete")) { @@ -699,8 +698,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { return; } // https://github.com/siyuan-note/siyuan/issues/6796 - const previousSibling = hasPreviousSibling(range.startContainer) as HTMLElement; - if (range.toString() === "" && event.key === "Backspace" && + if (selectText === "" && event.key === "Backspace" && range.startOffset === range.startContainer.textContent.length && range.startContainer.textContent.endsWith("\n" + Constants.ZWSP)) { range.setStart(range.startContainer, range.startOffset - 1); @@ -709,6 +707,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { event.preventDefault(); return; } + const previousSibling = hasPreviousSibling(range.startContainer) as HTMLElement; // https://github.com/siyuan-note/siyuan/issues/5547 if (range.startOffset === 1 && range.startContainer.textContent === Constants.ZWSP && previousSibling && previousSibling.nodeType !== 3 && @@ -843,7 +842,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { } // 软换行 - if (matchHotKey("⇧↩", event) && range.toString() === "") { + if (matchHotKey("⇧↩", event) && selectText === "") { let startElement = range.startContainer as HTMLElement; const nextSibling = hasNextSibling(startElement) as Element; // 图片之前软换行 @@ -1443,7 +1442,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { } if (matchHotKey(window.siyuan.config.keymap.editor.general.copyPlainText.custom, event)) { - if (range.toString() === "") { + if (selectText === "") { const selectsElement: HTMLElement[] = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); let html = ""; if (selectsElement.length === 0) { diff --git a/app/src/protyle/wysiwyg/turnIntoList.ts b/app/src/protyle/wysiwyg/turnIntoList.ts new file mode 100644 index 000000000..48510c366 --- /dev/null +++ b/app/src/protyle/wysiwyg/turnIntoList.ts @@ -0,0 +1,109 @@ +import {transaction} from "./transaction"; +import {focusByWbr} from "../util/selection"; + +export const turnIntoTaskList = (protyle: IProtyle, type: string, blockElement: HTMLElement, editElement: HTMLElement) => { + if (type !== "NodeCodeBlock" && + ( + ["[ ]", "[x]", "[X]", "【 】", "【x】", "【X】"].includes(editElement.innerHTML.substring(0, 3)) || + ["[]", "【】"].includes(editElement.innerHTML.substring(0, 2)) + ) + ) { + const doOperation: IOperation[] = [] + const undoOperation: IOperation[] = [] + const contextStartIndex = (editElement.innerHTML.indexOf("]") || editElement.innerHTML.indexOf("]")) + 1; + const isDone = editElement.innerHTML.substring(1, 2).toLowerCase() === "x"; + if (blockElement.parentElement.classList.contains("li") && + blockElement.parentElement.childElementCount === 3 // https://ld246.com/article/1659315815506 + ) { + // 仅有一项的列表才可转换 + if (!blockElement.parentElement.parentElement.classList.contains("protyle-wysiwyg") && // https://ld246.com/article/1659315815506 + blockElement.parentElement.parentElement.childElementCount === 2) { + `
`; + } + return true; + } else { + const id = blockElement.getAttribute("data-node-id") + const newId = Lute.NewNodeID(); + const emptyId = Lute.NewNodeID(); + const liItemId = Lute.NewNodeID(); + const oldHTML = blockElement.outerHTML; + editElement.innerHTML = editElement.innerHTML.substring(contextStartIndex); + transaction(protyle, [{ + action: "update", + id, + data: blockElement.outerHTML, + }, { + action: "insert", + id: newId, + data: `
`, + previousID: id, + }, { + action: "move", + id, + previousID: emptyId, + }, { + action: "delete", + id: emptyId + }], [{ + action: "update", + id, + data: oldHTML, + }, { + action: "move", + id, + previousID: newId, + }, { + action: "delete", + id: newId + }]); + blockElement.outerHTML = `
${blockElement.outerHTML}
` + focusByWbr(protyle.wysiwyg.element, getSelection().getRangeAt(0)) + return true; + } + } + return false +} + + +export const headingTurnIntoList = (protyle: IProtyle, type: string, blockElement: HTMLElement, editElement: HTMLElement) => { + if (type !== "NodeHeading" && ["* ", "- "].includes(editElement.innerHTML.substring(0, 2))) { + const id = blockElement.getAttribute("data-node-id") + const newId = Lute.NewNodeID(); + const emptyId = Lute.NewNodeID(); + const liItemId = Lute.NewNodeID(); + const oldHTML = blockElement.outerHTML; + editElement.innerHTML = editElement.innerHTML.substring(2); + transaction(protyle, [{ + action: "update", + id, + data: blockElement.outerHTML, + }, { + action: "insert", + id: newId, + data: `
`, + previousID: id, + }, { + action: "move", + id, + previousID: emptyId, + }, { + action: "delete", + id: emptyId + }], [{ + action: "update", + id, + data: oldHTML, + }, { + action: "move", + id, + previousID: newId, + }, { + action: "delete", + id: newId + }]); + blockElement.outerHTML = `
${blockElement.outerHTML}
` + focusByWbr(protyle.wysiwyg.element, getSelection().getRangeAt(0)) + return true; + } + return false +}