diff --git a/app/src/block/util.ts b/app/src/block/util.ts index b27888257..f1db75795 100644 --- a/app/src/block/util.ts +++ b/app/src/block/util.ts @@ -6,6 +6,50 @@ import {transaction, updateTransaction} from "../protyle/wysiwyg/transaction"; import {scrollCenter} from "../util/highlightById"; import {Constants} from "../constants"; +export const cancelSB = (protyle: IProtyle, nodeElement: Element) => { + const doOperations: IOperation[] = []; + const undoOperations: IOperation[] = []; + let previousId = nodeElement.previousElementSibling ? nodeElement.previousElementSibling.getAttribute("data-node-id") : undefined; + nodeElement.classList.remove("protyle-wysiwyg--select"); + const id = nodeElement.getAttribute("data-node-id") + const sbElement = genSBElement(nodeElement.getAttribute("data-sb-layout"), id, nodeElement.lastElementChild.outerHTML); + undoOperations.push({ + action: "insert", + id, + data: sbElement.outerHTML, + previousID: nodeElement.previousElementSibling ? nodeElement.previousElementSibling.getAttribute("data-node-id") : undefined, + parentID: nodeElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID + }); + Array.from(nodeElement.children).forEach((item, index) => { + if (index === nodeElement.childElementCount - 1) { + doOperations.push({ + action: "delete", + id, + }); + nodeElement.lastElementChild.remove(); + nodeElement.outerHTML = nodeElement.innerHTML; + return; + } + doOperations.push({ + action: "move", + id: item.getAttribute("data-node-id"), + previousID: previousId, + parentID: nodeElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID + }); + undoOperations.push({ + action: "move", + id: item.getAttribute("data-node-id"), + previousID: item.previousElementSibling ? item.previousElementSibling.getAttribute("data-node-id") : undefined, + parentID: id + }); + previousId = item.getAttribute("data-node-id"); + }); + + return { + doOperations, undoOperations, previousId + } +} + export const genSBElement = (layout: string, id?: string, attrHTML?: string) => { const sbElement = document.createElement("div"); sbElement.setAttribute("data-node-id", id || Lute.NewNodeID()); diff --git a/app/src/protyle/gutter/index.ts b/app/src/protyle/gutter/index.ts index 2478ffee3..36a6aa5b0 100644 --- a/app/src/protyle/gutter/index.ts +++ b/app/src/protyle/gutter/index.ts @@ -16,7 +16,7 @@ import {removeEmbed} from "../wysiwyg/removeEmbed"; import {getContenteditableElement, getTopAloneElement, isNotEditBlock} from "../wysiwyg/getBlock"; import * as dayjs from "dayjs"; import {fetchPost} from "../../util/fetch"; -import {genSBElement, insertEmptyBlock} from "../../block/util"; +import {cancelSB, insertEmptyBlock} from "../../block/util"; import {scrollCenter} from "../../util/highlightById"; import {isMobile} from "../../util/functions"; import {confirmDialog} from "../../dialog/confirmDialog"; @@ -883,44 +883,9 @@ export class Gutter { window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.cancel + " " + window.siyuan.languages.superBlock, click() { - const doOperations: IOperation[] = []; - const undoOperations: IOperation[] = []; - let previousId = nodeElement.previousElementSibling ? nodeElement.previousElementSibling.getAttribute("data-node-id") : undefined; - nodeElement.classList.remove("protyle-wysiwyg--select"); - const sbElement = genSBElement(nodeElement.getAttribute("data-sb-layout"), id, nodeElement.lastElementChild.outerHTML); - undoOperations.push({ - action: "insert", - id, - data: sbElement.outerHTML, - previousID: nodeElement.previousElementSibling ? nodeElement.previousElementSibling.getAttribute("data-node-id") : undefined, - parentID: nodeElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID - }); - Array.from(nodeElement.children).forEach((item, index) => { - if (index === nodeElement.childElementCount - 1) { - doOperations.push({ - action: "delete", - id, - }); - nodeElement.lastElementChild.remove(); - nodeElement.outerHTML = nodeElement.innerHTML; - return; - } - doOperations.push({ - action: "move", - id: item.getAttribute("data-node-id"), - previousID: previousId, - parentID: nodeElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID - }); - undoOperations.push({ - action: "move", - id: item.getAttribute("data-node-id"), - previousID: item.previousElementSibling ? item.previousElementSibling.getAttribute("data-node-id") : undefined, - parentID: id - }); - previousId = item.getAttribute("data-node-id"); - }); - transaction(protyle, doOperations, undoOperations); - focusBlock(protyle.wysiwyg.element.querySelector(`[data-node-id="${previousId}"]`)); + const sbData = cancelSB(protyle, nodeElement); + transaction(protyle, sbData.doOperations, sbData.undoOperations); + focusBlock(protyle.wysiwyg.element.querySelector(`[data-node-id="${sbData.previousId}"]`)); hideElements(["gutter"], protyle); } }).element); diff --git a/app/src/protyle/wysiwyg/getBlock.ts b/app/src/protyle/wysiwyg/getBlock.ts index e5eb691ab..655724cb8 100644 --- a/app/src/protyle/wysiwyg/getBlock.ts +++ b/app/src/protyle/wysiwyg/getBlock.ts @@ -139,6 +139,7 @@ export const getTopAloneElement = (topSourceElement: Element) => { } else if (topSourceElement.parentElement.getAttribute("data-type") === "NodeListItem" && topSourceElement.parentElement.childElementCount === 3) { topSourceElement = topSourceElement.parentElement; } else { + topSourceElement = getTopAloneElement(topSourceElement); break; } } diff --git a/app/src/protyle/wysiwyg/remove.ts b/app/src/protyle/wysiwyg/remove.ts index e38b2fb52..069f236f4 100644 --- a/app/src/protyle/wysiwyg/remove.ts +++ b/app/src/protyle/wysiwyg/remove.ts @@ -8,7 +8,7 @@ import { getTopEmptyElement, hasNextSibling } from "./getBlock"; import {transaction, updateTransaction} from "./transaction"; -import {genEmptyElement} from "../../block/util"; +import {cancelSB, genEmptyElement} from "../../block/util"; import {listOutdent, updateListOrder} from "./list"; import {setFold, zoomOut} from "../../menus/protyle"; import {preventScroll} from "../scroll/preventScroll"; @@ -180,9 +180,11 @@ export const removeBlock = (protyle: IProtyle, blockElement: Element, range: Ran let sideElement = selectElements[0].previousElementSibling || selectElements[selectElements.length - 1].nextElementSibling; let listElement: Element; let topElementId: string; + let topParentElement: Element selectElements.find((item: HTMLElement) => { item.classList.remove("protyle-wysiwyg--select"); const topElement = getTopAloneElement(item); + topParentElement = topElement.parentElement topElementId = topElement.getAttribute("data-node-id"); const id = topElement.getAttribute("data-node-id"); deletes.push({ @@ -238,8 +240,7 @@ export const removeBlock = (protyle: IProtyle, blockElement: Element, range: Ran zoomOut(protyle, protyle.block.parent2ID, protyle.block.parent2ID); }, Constants.TIMEOUT_INPUT * 2 + 100); } else { - if ((sideElement.classList.contains("protyle-wysiwyg") && protyle.wysiwyg.element.childElementCount === 0) || - ((sideElement.classList.contains("bq") || sideElement.classList.contains("sb")) && sideElement.childElementCount === 1)) { + if ((sideElement.classList.contains("protyle-wysiwyg") && protyle.wysiwyg.element.childElementCount === 0)) { const emptyElement = genEmptyElement(false, true, topElementId); sideElement.insertAdjacentElement("afterbegin", emptyElement); deletes.push({ @@ -273,8 +274,14 @@ export const removeBlock = (protyle: IProtyle, blockElement: Element, range: Ran } } if (deletes.length > 0) { - transaction(protyle, deletes, inserts.reverse()); + if (topParentElement && topParentElement.getAttribute("data-type") === "NodeSuperBlock" && topParentElement.childElementCount === 2) { + const sbData = cancelSB(protyle, topParentElement); + transaction(protyle, deletes.concat(sbData.doOperations), sbData.undoOperations.concat(inserts.reverse())); + } else { + transaction(protyle, deletes, inserts.reverse()); + } } + hideElements(["util"], protyle); return; } @@ -371,29 +378,42 @@ export const removeBlock = (protyle: IProtyle, blockElement: Element, range: Ran return; } + const parentElement = blockElement.parentElement const editableElement = getContenteditableElement(blockElement); const previousLastElement = getLastBlock(previousElement) as HTMLElement; const isSelectNode = previousLastElement && (previousLastElement.classList.contains("table") || previousLastElement.classList.contains("render-node") || previousLastElement.classList.contains("iframe") || previousLastElement.classList.contains("hr") || previousLastElement.classList.contains("code-block")); if (isSelectNode) { if (previousLastElement.classList.contains("code-block")) { - focusBlock(previousLastElement, undefined, false); if (editableElement.textContent.trim() === "") { + const previousId = previousLastElement.getAttribute("data-node-id") const id = blockElement.getAttribute("data-node-id"); - transaction(protyle, [{ + const doOperations: IOperation[] = [{ action: "delete", id, - }], [{ + }] + const undoOperations: IOperation[] = [{ action: "insert", data: blockElement.outerHTML, id: id, - previousID: previousLastElement.getAttribute("data-node-id") - }]); + previousID: blockElement.previousElementSibling?.getAttribute("data-node-id"), + parentID: blockElement.parentElement.getAttribute("data-node-id") + }] blockElement.remove(); + // 取消超级块 + if (parentElement.getAttribute("data-type") === "NodeSuperBlock" && parentElement.childElementCount === 2) { + const sbData = cancelSB(protyle, parentElement); + transaction(protyle, doOperations.concat(sbData.doOperations), sbData.undoOperations.concat(undoOperations)); + } else { + transaction(protyle, doOperations, undoOperations); + } + focusBlock(protyle.wysiwyg.element.querySelector(`[data-node-id="${previousId}"]`), undefined, false); + } else { + focusBlock(previousLastElement, undefined, false); } return; } previousLastElement.classList.add("protyle-wysiwyg--select"); - if (previousLastElement.getAttribute("data-type") === "NodeBlockQueryEmbed" || editableElement.textContent !== "" || protyle.wysiwyg.element.childElementCount === 2) { + if (previousLastElement.getAttribute("data-type") === "NodeBlockQueryEmbed" || editableElement.textContent !== "") { focusByRange(range); return; } @@ -412,7 +432,12 @@ export const removeBlock = (protyle: IProtyle, blockElement: Element, range: Ran data: removeElement.outerHTML, id: removeId, // 不能使用 previousLastElement,否则在超级块下的元素前删除撤销错误 - previousID: previousElement.getAttribute("data-node-id"), + previousID: blockElement.previousElementSibling?.getAttribute("data-node-id"), + parentID: parentElement.getAttribute("data-node-id") + }]; + const doOperations: IOperation[] = [{ + action: "delete", + id: removeId, }]; if (isSelectNode) { @@ -441,14 +466,17 @@ export const removeBlock = (protyle: IProtyle, blockElement: Element, range: Ran // extractContents 内容过多时需要进行滚动条重置,否则位置会错位 protyle.contentElement.scrollTop = scroll; protyle.scroll.lastScrollTop = scroll - 1; + doOperations.push({ + action: "update", + data: previousLastElement.outerHTML, + id: newId, + }) } - transaction(protyle, [{ - action: "delete", - id: removeId, - }, { - action: "update", - data: previousLastElement.outerHTML, - id: newId, - }], undoOperations); - focusByWbr(previousLastElement, range); + if (parentElement.getAttribute("data-type") === "NodeSuperBlock" && parentElement.childElementCount === 2) { + const sbData = cancelSB(protyle, parentElement); + transaction(protyle, doOperations.concat(sbData.doOperations), sbData.undoOperations.concat(undoOperations)); + } else { + transaction(protyle, doOperations, undoOperations); + } + focusByWbr(protyle.wysiwyg.element, range); };