mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-07 01:08:49 +01:00
This commit is contained in:
parent
a46324df77
commit
42cddbff45
4 changed files with 131 additions and 33 deletions
|
|
@ -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 元素或者只有一个字符串或者为 <u>b</u> 时的时候只拷贝内部
|
||||
if (!isBlock) {
|
||||
|
|
|
|||
|
|
@ -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<wbr>") || editElement.innerHTML.endsWith("\n<wbr>\n"))) {
|
||||
// 软换行
|
||||
updateTransaction(protyle, id, blockElement.outerHTML, blockElement.outerHTML.replace("\n<wbr>", "<wbr>"));
|
||||
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 = `<div data-node-id="${id}" data-type="NodeThematicBreak" class="hr"><div></div></div>`;
|
||||
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 = `<div data-subtype="t" data-node-id="${blockElement.parentElement.parentElement.getAttribute("data-node-id")}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${blockElement.parentElement.getAttribute("data-node-id")}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${id}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"><wbr></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`;
|
||||
id = blockElement.parentElement.parentElement.getAttribute("data-node-id");
|
||||
blockElement = blockElement.parentElement.parentElement;
|
||||
todoOldHTML = blockElement.outerHTML;
|
||||
}
|
||||
} else {
|
||||
html = `<div data-subtype="t" data-node-id="${id}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${Lute.NewNodeID()}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${Lute.NewNodeID()}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"><wbr></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`;
|
||||
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 === "<wbr>"
|
||||
) &&
|
||||
!(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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
109
app/src/protyle/wysiwyg/turnIntoList.ts
Normal file
109
app/src/protyle/wysiwyg/turnIntoList.ts
Normal file
|
|
@ -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) {
|
||||
`<div data-subtype="t" data-node-id="${blockElement.parentElement.parentElement.getAttribute("data-node-id")}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${blockElement.parentElement.getAttribute("data-node-id")}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${id}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"><wbr></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`;
|
||||
}
|
||||
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: `<div data-subtype="t" data-node-id="${newId}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${liItemId}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${emptyId}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`,
|
||||
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 = `<div data-subtype="t" data-node-id="${newId}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${liItemId}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div>${blockElement.outerHTML}<div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`
|
||||
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: `<div data-subtype="t" data-node-id="${newId}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${liItemId}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div><div data-node-id="${emptyId}" data-type="NodeParagraph" class="p"><div contenteditable="true" spellcheck="${window.siyuan.config.editor.spellcheck}"></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`,
|
||||
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 = `<div data-subtype="t" data-node-id="${newId}" data-type="NodeList" class="list"><div data-marker="*" data-subtype="t" data-node-id="${liItemId}" data-type="NodeListItem" class="li${isDone ? " protyle-task--done" : ""}"><div class="protyle-action protyle-action--task" draggable="true"><svg><use xlink:href="#icon${isDone ? "C" : "Unc"}heck"></use></svg></div>${blockElement.outerHTML}<div class="protyle-attr" contenteditable="false"></div></div><div class="protyle-attr" contenteditable="false"></div></div>`
|
||||
focusByWbr(protyle.wysiwyg.element, getSelection().getRangeAt(0))
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue