✨ https://github.com/siyuan-note/siyuan/issues/2911 inline math
This commit is contained in:
parent
a3548499be
commit
0285730c59
7 changed files with 123 additions and 45 deletions
|
@ -213,7 +213,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
span[data-type="inline-math"] {
|
||||
span[data-type~="inline-math"] {
|
||||
user-select: text;
|
||||
display: inline;
|
||||
// https://github.com/siyuan-note/siyuan/issues/3081 test $\begin{bmatrix} a & b \\c & d \\ e & f\end{bmatrix}$ test
|
||||
|
|
75
app/src/protyle/toolbar/InlineMath.ts
Normal file
75
app/src/protyle/toolbar/InlineMath.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import {ToolbarItem} from "./ToolbarItem";
|
||||
import * as dayjs from "dayjs";
|
||||
import {updateTransaction} from "../wysiwyg/transaction";
|
||||
import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest";
|
||||
import {hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock";
|
||||
import {focusByRange, focusByWbr} from "../util/selection";
|
||||
import {mathRender} from "../markdown/mathRender";
|
||||
|
||||
export class InlineMath extends ToolbarItem {
|
||||
public element: HTMLElement;
|
||||
|
||||
constructor(protyle: IProtyle, menuItem: IMenuItem) {
|
||||
super(protyle, menuItem);
|
||||
this.element.addEventListener("click", async (event: MouseEvent & { changedTouches: MouseEvent[] }) => {
|
||||
protyle.toolbar.element.classList.add("fn__none");
|
||||
event.stopPropagation();
|
||||
|
||||
const range = protyle.toolbar.range;
|
||||
const nodeElement = hasClosestBlock(range.startContainer);
|
||||
if (!nodeElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inlineMathElement = hasClosestByAttribute(range.startContainer, "data-type", "inline-math");
|
||||
if (inlineMathElement) {
|
||||
mathRender(inlineMathElement);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!["DIV", "TD", "TH"].includes(range.startContainer.parentElement.tagName) && range.startOffset === 0 && !hasPreviousSibling(range.startContainer)) {
|
||||
range.setStartBefore(range.startContainer.parentElement);
|
||||
}
|
||||
if (!["DIV", "TD", "TH"].includes(range.endContainer.parentElement.tagName) && range.endOffset === range.endContainer.textContent.length && !hasNextSibling(range.endContainer)) {
|
||||
range.setEndAfter(range.endContainer.parentElement);
|
||||
}
|
||||
const wbrElement = document.createElement("wbr");
|
||||
range.insertNode(wbrElement);
|
||||
const html = nodeElement.outerHTML;
|
||||
|
||||
const newElement = document.createElement("span");
|
||||
newElement.className = "render-node";
|
||||
newElement.setAttribute("contenteditable", "false");
|
||||
newElement.setAttribute("data-type", "inline-math");
|
||||
newElement.setAttribute("data-subtype", "math");
|
||||
newElement.setAttribute("data-content", range.toString());
|
||||
range.extractContents();
|
||||
range.insertNode(newElement);
|
||||
mathRender(newElement);
|
||||
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
|
||||
updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, html);
|
||||
wbrElement.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const removeLink = (linkElement: HTMLElement, range: Range) => {
|
||||
const types = linkElement.getAttribute("data-type").split(" ");
|
||||
if (types.length === 1) {
|
||||
const linkParentElement = linkElement.parentElement;
|
||||
linkElement.outerHTML = linkElement.innerHTML + "<wbr>";
|
||||
focusByWbr(linkParentElement, range);
|
||||
} else {
|
||||
types.find((itemType, index) => {
|
||||
if ("a" === itemType) {
|
||||
types.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
linkElement.setAttribute("data-type", types.join(" "));
|
||||
linkElement.removeAttribute("data-href");
|
||||
range.selectNodeContents(linkElement);
|
||||
range.collapse(false);
|
||||
focusByRange(range);
|
||||
}
|
||||
};
|
|
@ -12,7 +12,7 @@ export class ToolbarItem {
|
|||
this.element.setAttribute("data-type", menuItem.name);
|
||||
this.element.setAttribute("aria-label", tip + hotkey);
|
||||
this.element.innerHTML = `<svg><use xlink:href="#${menuItem.icon}"></use></svg>`;
|
||||
if (["text", "a", "block-ref"].includes(menuItem.name)) {
|
||||
if (["text", "a", "block-ref", "inline-math"].includes(menuItem.name)) {
|
||||
return;
|
||||
}
|
||||
this.element.addEventListener(getEventName(), (event) => {
|
||||
|
|
|
@ -41,6 +41,7 @@ import {renderAssetsPreview} from "../../asset/renderAssets";
|
|||
import {electronUndo} from "../undo";
|
||||
import {previewTemplate} from "./util";
|
||||
import {showMessage} from "../../dialog/message";
|
||||
import {InlineMath} from "./InlineMath";
|
||||
|
||||
export class Toolbar {
|
||||
public element: HTMLElement;
|
||||
|
@ -125,7 +126,7 @@ export class Toolbar {
|
|||
});
|
||||
const types = this.getCurrentType();
|
||||
types.forEach(item => {
|
||||
if (["a", "block-ref", "text", "file-annotation-ref"].includes(item)) {
|
||||
if (["a", "block-ref", "text", "file-annotation-ref", "inline-math"].includes(item)) {
|
||||
return;
|
||||
}
|
||||
this.element.querySelector(`[data-type="${item}"]`).classList.add("protyle-toolbar__item--current");
|
||||
|
@ -163,15 +164,15 @@ export class Toolbar {
|
|||
types = types.concat((endElement.getAttribute("data-type") || "").split(" "));
|
||||
}
|
||||
// if (range.startOffset === range.startContainer.textContent.length) {
|
||||
// const nextSibling = hasNextSibling(range.startContainer) as Element;
|
||||
// if (nextSibling && nextSibling.nodeType !== 3) {
|
||||
// types = types.concat((nextSibling.getAttribute("data-type") || "").split(" "));
|
||||
// }
|
||||
// const nextSibling = hasNextSibling(range.startContainer) as Element;
|
||||
// if (nextSibling && nextSibling.nodeType !== 3) {
|
||||
// types = types.concat((nextSibling.getAttribute("data-type") || "").split(" "));
|
||||
// }
|
||||
// } else if (range.endOffset === 0) {
|
||||
// const previousSibling = hasPreviousSibling(range.startContainer) as Element;
|
||||
// if (previousSibling && previousSibling.nodeType !== 3) {
|
||||
// types = types.concat((previousSibling.getAttribute("data-type") || "").split(" "));
|
||||
// }
|
||||
// if (previousSibling && previousSibling.nodeType !== 3) {
|
||||
// types = types.concat((previousSibling.getAttribute("data-type") || "").split(" "));
|
||||
// }
|
||||
// }
|
||||
range.cloneContents().childNodes.forEach((item: HTMLElement) => {
|
||||
if (item.nodeType !== 3) {
|
||||
|
@ -195,12 +196,14 @@ export class Toolbar {
|
|||
case "sup":
|
||||
case "sub":
|
||||
case "kbd":
|
||||
case "inline-math":
|
||||
menuItemObj = new ToolbarItem(protyle, menuItem);
|
||||
break;
|
||||
case "block-ref":
|
||||
menuItemObj = new BlockRef(protyle, menuItem);
|
||||
break;
|
||||
case "inline-math":
|
||||
menuItemObj = new InlineMath(protyle, menuItem);
|
||||
break;
|
||||
case "|":
|
||||
menuItemObj = new Divider();
|
||||
break;
|
||||
|
@ -488,12 +491,12 @@ export class Toolbar {
|
|||
// return;
|
||||
// }
|
||||
let startElement = this.range.startContainer as Element;
|
||||
if (this.range.startContainer.nodeType === 3) {
|
||||
startElement = this.range.startContainer.parentElement;
|
||||
if (startElement.getAttribute("data-type") === "virtual-block-ref" && !["DIV", "TD", "TH"].includes(startElement.parentElement.tagName)) {
|
||||
startElement = startElement.parentElement;
|
||||
}
|
||||
}
|
||||
// if (this.range.startContainer.nodeType === 3) {
|
||||
// startElement = this.range.startContainer.parentElement;
|
||||
// if (startElement.getAttribute("data-type") === "virtual-block-ref" && !["DIV", "TD", "TH"].includes(startElement.parentElement.tagName)) {
|
||||
// startElement = startElement.parentElement;
|
||||
// }
|
||||
// }
|
||||
|
||||
// table 选中处理
|
||||
const tableElement = hasClosestByAttribute(startElement, "data-type", "NodeTable");
|
||||
|
@ -523,19 +526,19 @@ export class Toolbar {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.range.toString() === "" && action === "range" && getSelectionOffset(startElement, protyle.wysiwyg.element).end === startElement.textContent.length &&
|
||||
this.range.startContainer.nodeType === 3 && !this.range.startContainer.parentElement.getAttribute("contenteditable") &&
|
||||
types.length > 0) {
|
||||
// 跳出行内元素
|
||||
const textNode = document.createTextNode(Constants.ZWSP);
|
||||
this.range.startContainer.parentElement.after(textNode);
|
||||
this.range.selectNodeContents(textNode);
|
||||
this.range.collapse(false);
|
||||
if (types.includes(type)) {
|
||||
// 如果不是同一种行内元素,需进行后续的渲染操作
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if (this.range.toString() === "" && action === "range" && getSelectionOffset(startElement, protyle.wysiwyg.element).end === startElement.textContent.length &&
|
||||
// this.range.startContainer.nodeType === 3 && !this.range.startContainer.parentElement.getAttribute("contenteditable") &&
|
||||
// types.length > 0) {
|
||||
// // 跳出行内元素
|
||||
// const textNode = document.createTextNode(Constants.ZWSP);
|
||||
// this.range.startContainer.parentElement.after(textNode);
|
||||
// this.range.selectNodeContents(textNode);
|
||||
// this.range.collapse(false);
|
||||
// if (types.includes(type)) {
|
||||
// // 如果不是同一种行内元素,需进行后续的渲染操作
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// if (types.length > 0 && types.includes("link") && action === "range") {
|
||||
// // 链接快捷键不应取消,应该显示链接信息
|
||||
// linkMenu(protyle, this.range.startContainer.parentElement);
|
||||
|
@ -653,15 +656,15 @@ export class Toolbar {
|
|||
// this.range.selectNodeContents(refNode);
|
||||
// hintRef(refText, protyle, true);
|
||||
// break;
|
||||
case "inline-math":
|
||||
newElement = document.createElement("span");
|
||||
newElement.className = "render-node";
|
||||
newElement.setAttribute("contenteditable", "false");
|
||||
newElement.setAttribute("data-type", "inline-math");
|
||||
newElement.setAttribute("data-subtype", "math");
|
||||
newElement.setAttribute("data-content", startText + selectContents.textContent + endText);
|
||||
mathRender(newElement);
|
||||
break;
|
||||
// case "inline-math":
|
||||
// newElement = document.createElement("span");
|
||||
// newElement.className = "render-node";
|
||||
// newElement.setAttribute("contenteditable", "false");
|
||||
// newElement.setAttribute("data-type", "inline-math");
|
||||
// newElement.setAttribute("data-subtype", "math");
|
||||
// newElement.setAttribute("data-content", startText + selectContents.textContent + endText);
|
||||
// mathRender(newElement);
|
||||
// break;
|
||||
}
|
||||
// if (newElement) {
|
||||
// this.range.insertNode(newElement);
|
||||
|
|
|
@ -152,7 +152,7 @@ const listEnter = (protyle: IProtyle, blockElement: HTMLElement, range: Range) =
|
|||
selectNode.firstChild.remove();
|
||||
}
|
||||
// https://github.com/siyuan-note/siyuan/issues/3850
|
||||
if (editableElement?.lastElementChild?.getAttribute("data-type") === "inline-math" &&
|
||||
if (editableElement?.lastElementChild?.getAttribute("data-type").indexOf("inline-math") > -1 &&
|
||||
!hasNextSibling(editableElement?.lastElementChild)) {
|
||||
editableElement.insertAdjacentText("beforeend", "\n");
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ export const enter = (blockElement: HTMLElement, range: Range, protyle: IProtyle
|
|||
selectNode.firstChild.remove();
|
||||
}
|
||||
// https://github.com/siyuan-note/siyuan/issues/3850
|
||||
if (editableElement?.lastElementChild?.getAttribute("data-type") === "inline-math" &&
|
||||
if (editableElement?.lastElementChild?.getAttribute("data-type").indexOf("inline-math") > -1 &&
|
||||
!hasNextSibling(editableElement?.lastElementChild)) {
|
||||
editableElement.insertAdjacentText("beforeend", "\n");
|
||||
}
|
||||
|
|
|
@ -535,11 +535,11 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
|
|||
// https://github.com/siyuan-note/siyuan/issues/5185
|
||||
if (range.startOffset === 0 && range.startContainer.nodeType === 3) {
|
||||
const previousSibling = hasPreviousSibling(range.startContainer) as HTMLElement;
|
||||
if (previousSibling && previousSibling.nodeType !== 3 && previousSibling.getAttribute("data-type") === "inline-math") {
|
||||
if (previousSibling && previousSibling.nodeType !== 3 && previousSibling.getAttribute("data-type").indexOf("inline-math") > -1) {
|
||||
protyle.toolbar.showRender(protyle, previousSibling);
|
||||
return;
|
||||
} else if (!previousSibling && range.startContainer.parentElement.previousSibling.isSameNode(range.startContainer.parentElement.previousElementSibling) &&
|
||||
range.startContainer.parentElement.previousElementSibling.getAttribute("data-type") === "inline-math") {
|
||||
range.startContainer.parentElement.previousElementSibling.getAttribute("data-type").indexOf("inline-math") > -1) {
|
||||
protyle.toolbar.showRender(protyle, range.startContainer.parentElement.previousElementSibling);
|
||||
return;
|
||||
}
|
||||
|
@ -1185,7 +1185,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
|
|||
}
|
||||
if (matchHotKey(menuItem.hotkey, event)) {
|
||||
protyle.toolbar.range = getEditorRange(protyle.wysiwyg.element);
|
||||
if (["text", "a", "block-ref"].includes(menuItem.name)) {
|
||||
if (["text", "a", "block-ref", "inline-math"].includes(menuItem.name)) {
|
||||
protyle.toolbar.element.querySelector(`[data-type="${menuItem.name}"]`).dispatchEvent(new CustomEvent("block-ref" === menuItem.name ? getEventName() : "click"));
|
||||
} else {
|
||||
protyle.toolbar.setInlineMark(protyle, menuItem.name, "range");
|
||||
|
|
|
@ -442,7 +442,7 @@ export const removeBlock = (protyle: IProtyle, blockElement: Element, range: Ran
|
|||
// 非空块
|
||||
range.setEndAfter(editableElement.lastChild);
|
||||
// 数学公式会车后再删除 https://github.com/siyuan-note/siyuan/issues/3850
|
||||
if (previousLastEditElement?.lastElementChild?.getAttribute("data-type") === "inline-math") {
|
||||
if (previousLastEditElement?.lastElementChild?.getAttribute("data-type").indexOf("inline-math") > -1) {
|
||||
const lastSibling = hasNextSibling(previousLastEditElement?.lastElementChild);
|
||||
if (lastSibling && lastSibling.textContent === "\n") {
|
||||
lastSibling.remove();
|
||||
|
|
Loading…
Add table
Reference in a new issue