diff --git a/app/src/protyle/toolbar/BlockRef.ts b/app/src/protyle/toolbar/BlockRef.ts index b7f86f47e..f1291f3d7 100644 --- a/app/src/protyle/toolbar/BlockRef.ts +++ b/app/src/protyle/toolbar/BlockRef.ts @@ -1,5 +1,6 @@ import {ToolbarItem} from "./ToolbarItem"; import {hintRef} from "../hint/extend"; +import {fixTableRange} from "../util/selection"; export class BlockRef extends ToolbarItem { public element: HTMLElement; @@ -8,6 +9,7 @@ export class BlockRef extends ToolbarItem { super(protyle, menuItem); // 不能用 getEventName,否则会导致光标位置变动到点击的文档中 this.element.addEventListener("click", (event: MouseEvent & { changedTouches: MouseEvent[] }) => { + fixTableRange(protyle.toolbar.range); hintRef(protyle.toolbar.range.toString(), protyle, true); protyle.toolbar.element.classList.add("fn__none"); event.stopPropagation(); diff --git a/app/src/protyle/toolbar/InlineMath.ts b/app/src/protyle/toolbar/InlineMath.ts index db257c9bd..ea4b7e34a 100644 --- a/app/src/protyle/toolbar/InlineMath.ts +++ b/app/src/protyle/toolbar/InlineMath.ts @@ -4,6 +4,7 @@ import {updateTransaction} from "../wysiwyg/transaction"; import {hasClosestBlock} from "../util/hasClosest"; import {hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock"; import {mathRender} from "../markdown/mathRender"; +import {fixTableRange} from "../util/selection"; export class InlineMath extends ToolbarItem { public element: HTMLElement; @@ -19,10 +20,11 @@ export class InlineMath extends ToolbarItem { if (!nodeElement) { return; } - if (!["DIV", "TD", "TH"].includes(range.startContainer.parentElement.tagName) && range.startOffset === 0 && !hasPreviousSibling(range.startContainer)) { + fixTableRange(range); + if (!["DIV", "TD", "TH", "TR"].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)) { + if (!["DIV", "TD", "TH", "TR"].includes(range.endContainer.parentElement.tagName) && range.endOffset === range.endContainer.textContent.length && !hasNextSibling(range.endContainer)) { range.setEndAfter(range.endContainer.parentElement); } const wbrElement = document.createElement("wbr"); diff --git a/app/src/protyle/toolbar/InlineMemo.ts b/app/src/protyle/toolbar/InlineMemo.ts index 6b333e6ca..9aac1511d 100644 --- a/app/src/protyle/toolbar/InlineMemo.ts +++ b/app/src/protyle/toolbar/InlineMemo.ts @@ -3,6 +3,7 @@ import * as dayjs from "dayjs"; import {updateTransaction} from "../wysiwyg/transaction"; import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest"; import {hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock"; +import {fixTableRange} from "../util/selection"; export class InlineMemo extends ToolbarItem { public element: HTMLElement; @@ -28,10 +29,12 @@ export class InlineMemo extends ToolbarItem { return; } - if (!["DIV", "TD", "TH"].includes(range.startContainer.parentElement.tagName) && range.startOffset === 0 && !hasPreviousSibling(range.startContainer)) { + fixTableRange(range); + + if (!["DIV", "TD", "TH", "TR"].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)) { + if (!["DIV", "TD", "TH", "TR"].includes(range.endContainer.parentElement.tagName) && range.endOffset === range.endContainer.textContent.length && !hasNextSibling(range.endContainer)) { range.setEndAfter(range.endContainer.parentElement); } const wbrElement = document.createElement("wbr"); diff --git a/app/src/protyle/toolbar/Link.ts b/app/src/protyle/toolbar/Link.ts index 6bd47f15b..8042ef5c3 100644 --- a/app/src/protyle/toolbar/Link.ts +++ b/app/src/protyle/toolbar/Link.ts @@ -5,7 +5,7 @@ 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 {fixTableRange, focusByRange, focusByWbr} from "../util/selection"; export class Link extends ToolbarItem { public element: HTMLElement; @@ -28,10 +28,12 @@ export class Link extends ToolbarItem { return; } - if (!["DIV", "TD", "TH"].includes(range.startContainer.parentElement.tagName) && range.startOffset === 0 && !hasPreviousSibling(range.startContainer)) { + fixTableRange(range); + + if (!["DIV", "TD", "TH", "TR"].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)) { + if (!["DIV", "TD", "TH", "TR"].includes(range.endContainer.parentElement.tagName) && range.endOffset === range.endContainer.textContent.length && !hasNextSibling(range.endContainer)) { range.setEndAfter(range.endContainer.parentElement); } const wbrElement = document.createElement("wbr"); @@ -40,6 +42,7 @@ export class Link extends ToolbarItem { const newElement = document.createElement("span"); newElement.setAttribute("data-type", "a"); + newElement.setAttribute("data-href", ""); const rangeString = range.toString(); newElement.textContent = rangeString; range.extractContents(); diff --git a/app/src/protyle/toolbar/index.ts b/app/src/protyle/toolbar/index.ts index d1fb17504..29099f5dc 100644 --- a/app/src/protyle/toolbar/index.ts +++ b/app/src/protyle/toolbar/index.ts @@ -2,6 +2,7 @@ import {Divider} from "./Divider"; import {Font, hasSameTextStyle, setFontStyle} from "./Font"; import {ToolbarItem} from "./ToolbarItem"; import { + fixTableRange, focusByRange, focusByWbr, focusSideBlock, @@ -244,34 +245,7 @@ export class Toolbar { } const rangeTypes = this.getCurrentType(this.range); const selectText = this.range.toString(); - - // table 选中处理 - const tableElement = hasClosestByAttribute(this.range.startContainer, "data-type", "NodeTable"); - if (selectText !== "" && tableElement && this.range.commonAncestorContainer.nodeType !== 3) { - const parentTag = (this.range.commonAncestorContainer as Element).tagName; - if (parentTag !== "TH" && parentTag !== "TD") { - const startCellElement = hasClosestByMatchTag(this.range.startContainer, "TD") || hasClosestByMatchTag(this.range.startContainer, "TH"); - const endCellElement = hasClosestByMatchTag(this.range.endContainer, "TD") || hasClosestByMatchTag(this.range.endContainer, "TH"); - if (!startCellElement && !endCellElement) { - const cellElement = tableElement.querySelector("th") || tableElement.querySelector("td"); - this.range.setStartBefore(cellElement.firstChild); - this.range.setEndAfter(cellElement.lastChild); - } else if (startCellElement && - // 不能包含自身元素,否则对 cell 中的部分文字两次高亮后就会选中整个 cell。 https://github.com/siyuan-note/siyuan/issues/3649 第二点 - !startCellElement.contains(this.range.endContainer)) { - const cloneRange = this.range.cloneRange(); - this.range.setEndAfter(startCellElement.lastChild); - if (this.range.toString() === "" && endCellElement) { - this.range.setEnd(cloneRange.endContainer, cloneRange.endOffset); - this.range.setStartBefore(endCellElement.lastChild); - } - if (this.range.toString() === "") { - return; - } - } - } - } - + fixTableRange(this.range); let previousElement: HTMLElement; let nextElement: HTMLElement; let previousIndex: number; diff --git a/app/src/protyle/util/insertHTML.ts b/app/src/protyle/util/insertHTML.ts index bad8e70e4..2d8d0677f 100644 --- a/app/src/protyle/util/insertHTML.ts +++ b/app/src/protyle/util/insertHTML.ts @@ -3,7 +3,7 @@ import * as dayjs from "dayjs"; import {removeEmbed} from "../wysiwyg/removeEmbed"; import {transaction, updateTransaction} from "../wysiwyg/transaction"; import {getContenteditableElement} from "../wysiwyg/getBlock"; -import {focusBlock, getEditorRange, focusByWbr} from "./selection"; +import {focusBlock, getEditorRange, focusByWbr, fixTableRange} from "./selection"; import {mathRender} from "../markdown/mathRender"; import {Constants} from "../../constants"; @@ -12,24 +12,8 @@ export const insertHTML = (html: string, protyle: IProtyle, isBlock = false, spl return; } const range = getEditorRange(protyle.wysiwyg.element); - // table 选中处理 https://ld246.com/article/1624269001599 - const tableElement = hasClosestByAttribute(range.startContainer, "data-type", "NodeTable"); - if (range.toString() !== "" && tableElement && range.commonAncestorContainer.nodeType !== 3) { - const parentTag = (range.commonAncestorContainer as Element).tagName; - if (parentTag !== "TH" && parentTag !== "TD") { - let cellElement = hasClosestByMatchTag(range.startContainer, "TD") || hasClosestByMatchTag(range.startContainer, "TH"); - if (!cellElement) { - cellElement = tableElement.querySelector("th") || tableElement.querySelector("td"); - range.setStartBefore(cellElement.firstChild); - } - if (cellElement.lastChild) { - range.setEndAfter(cellElement.lastChild); - } else { - range.collapse(true); - } - } - } - if (tableElement && !isBlock) { + fixTableRange(range); + if (hasClosestByAttribute(range.startContainer, "data-type", "NodeTable") && !isBlock) { html = protyle.lute.BlockDOM2InlineBlockDOM(html); } let blockElement = hasClosestBlock(range.startContainer) as Element; diff --git a/app/src/protyle/util/selection.ts b/app/src/protyle/util/selection.ts index 7241237db..1cd361de7 100644 --- a/app/src/protyle/util/selection.ts +++ b/app/src/protyle/util/selection.ts @@ -5,7 +5,7 @@ import { hasPreviousSibling, isNotEditBlock } from "../wysiwyg/getBlock"; -import {hasClosestByMatchTag} from "./hasClosest"; +import {hasClosestByAttribute, hasClosestByMatchTag} from "./hasClosest"; import {countBlockWord, countSelectWord} from "../../layout/status"; const selectIsEditor = (editor: Element, range?: Range) => { @@ -21,6 +21,32 @@ const selectIsEditor = (editor: Element, range?: Range) => { return editor.isEqualNode(container) || editor.contains(container); }; +// table 选中处理 +export const fixTableRange = (range:Range) => { + const tableElement = hasClosestByAttribute(range.startContainer, "data-type", "NodeTable"); + if (range.toString() !== "" && tableElement && range.commonAncestorContainer.nodeType !== 3) { + const parentTag = (range.commonAncestorContainer as Element).tagName; + if (parentTag !== "TH" && parentTag !== "TD") { + const startCellElement = hasClosestByMatchTag(range.startContainer, "TD") || hasClosestByMatchTag(range.startContainer, "TH"); + const endCellElement = hasClosestByMatchTag(range.endContainer, "TD") || hasClosestByMatchTag(range.endContainer, "TH"); + if (!startCellElement && !endCellElement) { + const cellElement = tableElement.querySelector("th") || tableElement.querySelector("td"); + range.setStart(cellElement.firstChild, 0); + range.setEnd(cellElement.lastChild, cellElement.lastChild.textContent.length); + } else if (startCellElement && + // 不能包含自身元素,否则对 cell 中的部分文字两次高亮后就会选中整个 cell。 https://github.com/siyuan-note/siyuan/issues/3649 第二点 + !startCellElement.contains(range.endContainer)) { + const cloneRange = range.cloneRange(); + range.setEnd(startCellElement.lastChild, startCellElement.lastChild.textContent.length); + if (range.toString() === "" && endCellElement) { + range.setStart(endCellElement.firstChild, 0); + range.setEnd(cloneRange.endContainer, cloneRange.endOffset); + } + } + } + } +} + export const selectAll = (protyle: IProtyle, nodeElement: Element, range: Range) => { const editElement = getContenteditableElement(nodeElement); if (editElement) { diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts index 42be15bc8..fc81c7f3c 100644 --- a/app/src/protyle/wysiwyg/index.ts +++ b/app/src/protyle/wysiwyg/index.ts @@ -225,7 +225,7 @@ export class WYSIWYG { if (range.startContainer.parentElement.parentElement.getAttribute("data-type") === "NodeHeading") { // 复制标题 https://github.com/siyuan-note/insider/issues/297 tempElement.append(range.startContainer.parentElement.parentElement.cloneNode(true)); - } else if (!["DIV", "TD", "TH"].includes(range.startContainer.parentElement.tagName)) { + } else if (!["DIV", "TD", "TH", "TR"].includes(range.startContainer.parentElement.tagName)) { // 复制行内元素 https://github.com/siyuan-note/insider/issues/191 tempElement.append(range.startContainer.parentElement.cloneNode(true)); } else { @@ -986,7 +986,7 @@ export class WYSIWYG { transaction(protyle, doOperations, undoOperations); } else if (range.toString() !== "" && startContainer.isSameNode(range.endContainer) && range.startContainer.nodeType === 3 && range.endOffset === range.endContainer.textContent.length && range.startOffset === 0 && - !["DIV", "TD", "TH"].includes(range.startContainer.parentElement.tagName)) { + !["DIV", "TD", "TH", "TR"].includes(range.startContainer.parentElement.tagName)) { // 选中整个内联元素 tempElement.append(range.startContainer.parentElement); } else if (selectImgElement) {