diff --git a/app/src/protyle/gutter/index.ts b/app/src/protyle/gutter/index.ts index 94cf83163..358e47714 100644 --- a/app/src/protyle/gutter/index.ts +++ b/app/src/protyle/gutter/index.ts @@ -6,13 +6,12 @@ import {copySubMenu, openAttr, openWechatNotify} from "../../menus/commonMenuIte import {updateHotkeyTip, writeText} from "../util/compatibility"; import { transaction, - turnIntoTransaction, - turnsIntoTransaction, + turnsIntoOneTransaction, turnsIntoTransaction, updateBatchTransaction, updateTransaction } from "../wysiwyg/transaction"; import {removeBlock} from "../wysiwyg/remove"; -import {focusBlock, focusByRange, getEditorRange} from "../util/selection"; +import {focusBlock, focusByRange, focusByWbr, getEditorRange} from "../util/selection"; import {hideElements} from "../ui/hideElements"; import {processRender} from "../util/processCode"; import {highlightRender} from "../markdown/highlightRender"; @@ -20,13 +19,14 @@ import {blockRender} from "../markdown/blockRender"; import {removeEmbed} from "../wysiwyg/removeEmbed"; import {getContenteditableElement, getTopAloneElement, isNotEditBlock} from "../wysiwyg/getBlock"; import * as dayjs from "dayjs"; -import {fetchPost} from "../../util/fetch"; +import {fetchPost, fetchSyncPost} from "../../util/fetch"; import {cancelSB, insertEmptyBlock, jumpToParentNext} from "../../block/util"; import {scrollCenter} from "../../util/highlightById"; import {isMobile} from "../../util/functions"; import {confirmDialog} from "../../dialog/confirmDialog"; import {enableProtyle} from "../util/onGet"; import {countBlockWord} from "../../layout/status"; +import {Constants} from "../../constants"; export class Gutter { public element: HTMLElement; @@ -230,12 +230,104 @@ export class Gutter { }); } - private turnInto(options: { icon: string, label: string, protyle: IProtyle, nodeElement: Element, id: string, type: string, level?: number }) { + private turnsOneInto(options: { + icon: string, + label: string, + protyle: IProtyle, + nodeElement: Element, + accelerator?: string + id: string, + type: string, + level?: number + }) { return { icon: options.icon, label: options.label, - click() { - turnIntoTransaction(options); + accelerator: options.accelerator, + async click() { + if (!options.nodeElement.querySelector("wbr")) { + getContenteditableElement(options.nodeElement)?.insertAdjacentHTML("afterbegin", ""); + } + if (options.type === "CancelList" || options.type === "CancelBlockquote") { + for await(const item of options.nodeElement.querySelectorAll('[data-type="NodeHeading"][fold="1"]')) { + const itemId = item.getAttribute("data-node-id"); + item.removeAttribute("fold"); + const response = await fetchSyncPost("/api/transactions", { + session: options.protyle.id, + app: Constants.SIYUAN_APPID, + transactions: [{ + doOperations: [{ + action: "unfoldHeading", + id: itemId, + }], + undoOperations: [{ + action: "foldHeading", + id: itemId + }], + }] + }); + options.protyle.undo.add([{ + action: "unfoldHeading", + id: itemId, + }], [{ + action: "foldHeading", + id: itemId + }]); + item.insertAdjacentHTML("afterend", response.data[0].doOperations[0].retData); + } + } + const oldHTML = options.nodeElement.outerHTML; + const previousId = options.nodeElement.previousElementSibling?.getAttribute("data-node-id"); + const parentId = options.nodeElement.parentElement.getAttribute("data-node-id") || options.protyle.block.parentID; + // @ts-ignore + const newHTML = options.protyle.lute[options.type](options.nodeElement.outerHTML, options.level); + options.nodeElement.outerHTML = newHTML; + if (options.type === "CancelList" || options.type === "CancelBlockquote") { + const tempElement = document.createElement("template"); + tempElement.innerHTML = newHTML; + const doOperations: IOperation[] = [{ + action: "delete", + id: options.id + }]; + const undoOperations: IOperation[] = []; + let tempPreviousId = previousId; + Array.from(tempElement.content.children).forEach((item) => { + const tempId = item.getAttribute("data-node-id"); + doOperations.push({ + action: "insert", + data: item.outerHTML, + id: tempId, + previousID: tempPreviousId, + parentID: parentId + }); + undoOperations.push({ + action: "delete", + id: tempId + }); + tempPreviousId = tempId; + }); + undoOperations.push({ + action: "insert", + data: oldHTML, + id: options.id, + previousID: previousId, + parentID: parentId + }); + transaction(options.protyle, doOperations, undoOperations); + } else { + updateTransaction(options.protyle, options.id, newHTML, oldHTML); + } + focusByWbr(options.protyle.wysiwyg.element, getEditorRange(options.protyle.wysiwyg.element)); + options.protyle.wysiwyg.element.querySelectorAll('[data-type="block-ref"]').forEach(item => { + if (item.textContent === "") { + fetchPost("/api/block/getRefText", {id: item.getAttribute("data-id")}, (response) => { + item.textContent = response.data; + }); + } + }); + blockRender(options.protyle, options.protyle.wysiwyg.element); + processRender(options.protyle.wysiwyg.element); + highlightRender(options.protyle.wysiwyg.element); } }; } @@ -254,7 +346,7 @@ export class Gutter { label: options.label, accelerator: options.accelerator, click() { - turnsIntoTransaction(options); + turnsIntoOneTransaction(options); } }; } @@ -267,55 +359,14 @@ export class Gutter { type: string, level?: number | string, isContinue?: boolean + accelerator?: string }) { return { icon: options.icon, label: options.label, + accelerator: options.accelerator, click() { - let html = ""; - const doOperations: IOperation[] = []; - const undoOperations: IOperation[] = []; - options.selectsElement.forEach((item, index) => { - if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") { - setFold(options.protyle, item); - } - item.classList.remove("protyle-wysiwyg--select"); - html += item.outerHTML; - const id = item.getAttribute("data-node-id"); - undoOperations.push({ - action: "update", - id, - data: item.outerHTML - }); - - if ((options.type === "Blocks2Ps" || options.type === "Blocks2Hs") && !options.isContinue) { - // @ts-ignore - item.outerHTML = options.protyle.lute[options.type](item.outerHTML, options.level); - } else { - if (index === options.selectsElement.length - 1) { - const tempElement = document.createElement("div"); - // @ts-ignore - tempElement.innerHTML = options.protyle.lute[options.type](html, options.level); - item.outerHTML = tempElement.innerHTML; - } else { - item.remove(); - } - } - }); - undoOperations.forEach(item => { - const nodeElement = options.protyle.wysiwyg.element.querySelector(`[data-node-id="${item.id}"]`); - doOperations.push({ - action: "update", - id: item.id, - data: nodeElement.outerHTML - }); - }); - transaction(options.protyle, doOperations, undoOperations); - processRender(options.protyle.wysiwyg.element); - highlightRender(options.protyle.wysiwyg.element); - blockRender(options.protyle, options.protyle.wysiwyg.element); - focusBlock(options.protyle.wysiwyg.element.querySelector(`[data-node-id="${options.selectsElement[0].getAttribute("data-node-id")}"]`)); - hideElements(["gutter"], options.protyle); + turnsIntoTransaction(options); } }; } @@ -377,6 +428,7 @@ export class Gutter { turnIntoSubmenu.push(this.turnsInto({ icon: "iconParagraph", label: window.siyuan.languages.paragraph, + accelerator: window.siyuan.config.keymap.editor.heading.paragraph.custom, protyle, selectsElement, type: "Blocks2Ps", @@ -386,6 +438,7 @@ export class Gutter { turnIntoSubmenu.push(this.turnsInto({ icon: "iconH1", label: window.siyuan.languages.heading1, + accelerator: window.siyuan.config.keymap.editor.heading.heading1.custom, protyle, selectsElement, level: 1, @@ -395,6 +448,7 @@ export class Gutter { turnIntoSubmenu.push(this.turnsInto({ icon: "iconH2", label: window.siyuan.languages.heading2, + accelerator: window.siyuan.config.keymap.editor.heading.heading2.custom, protyle, selectsElement, level: 2, @@ -404,6 +458,7 @@ export class Gutter { turnIntoSubmenu.push(this.turnsInto({ icon: "iconH3", label: window.siyuan.languages.heading3, + accelerator: window.siyuan.config.keymap.editor.heading.heading3.custom, protyle, selectsElement, level: 3, @@ -413,6 +468,7 @@ export class Gutter { turnIntoSubmenu.push(this.turnsInto({ icon: "iconH4", label: window.siyuan.languages.heading4, + accelerator: window.siyuan.config.keymap.editor.heading.heading4.custom, protyle, selectsElement, level: 4, @@ -422,6 +478,7 @@ export class Gutter { turnIntoSubmenu.push(this.turnsInto({ icon: "iconH5", label: window.siyuan.languages.heading5, + accelerator: window.siyuan.config.keymap.editor.heading.heading5.custom, protyle, selectsElement, level: 5, @@ -431,6 +488,7 @@ export class Gutter { turnIntoSubmenu.push(this.turnsInto({ icon: "iconH6", label: window.siyuan.languages.heading6, + accelerator: window.siyuan.config.keymap.editor.heading.heading6.custom, protyle, selectsElement, level: 6, @@ -630,137 +688,138 @@ export class Gutter { selectsElement: [nodeElement], type: "Blocks2Blockquote" })); - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH1", label: window.siyuan.languages.heading1, + accelerator: window.siyuan.config.keymap.editor.heading.heading1.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 1, - type: "P2H" + type: "Blocks2Hs", })); - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH2", label: window.siyuan.languages.heading2, + accelerator: window.siyuan.config.keymap.editor.heading.heading2.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 2, - type: "P2H" + type: "Blocks2Hs", })); - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH3", label: window.siyuan.languages.heading3, + accelerator: window.siyuan.config.keymap.editor.heading.heading3.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 3, - type: "P2H" + type: "Blocks2Hs", })); - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH4", label: window.siyuan.languages.heading4, + accelerator: window.siyuan.config.keymap.editor.heading.heading4.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 4, - type: "P2H" + type: "Blocks2Hs", })); - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH5", label: window.siyuan.languages.heading5, + accelerator: window.siyuan.config.keymap.editor.heading.heading5.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 5, - type: "P2H" + type: "Blocks2Hs", })); - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH6", label: window.siyuan.languages.heading6, + accelerator: window.siyuan.config.keymap.editor.heading.heading6.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 6, - type: "P2H" + type: "Blocks2Hs", })); } else if (type === "NodeHeading" && !window.siyuan.config.readonly) { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconParagraph", label: window.siyuan.languages.paragraph, + accelerator: window.siyuan.config.keymap.editor.heading.paragraph.custom, protyle, - nodeElement, - id, - type: "H2P" + selectsElement: [nodeElement], + level: 6, + type: "Blocks2Ps", })); if (subType !== "h1") { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH1", label: window.siyuan.languages.heading1, + accelerator: window.siyuan.config.keymap.editor.heading.heading1.custom, protyle, - nodeElement, - id, - level: 1, - type: "HLevel" + selectsElement: [nodeElement], + level: 6, + type: "Blocks2Hs", })); } if (subType !== "h2") { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH2", label: window.siyuan.languages.heading2, + accelerator: window.siyuan.config.keymap.editor.heading.heading2.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 2, - type: "HLevel" + type: "Blocks2Hs", })); } if (subType !== "h3") { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH3", label: window.siyuan.languages.heading3, + accelerator: window.siyuan.config.keymap.editor.heading.heading3.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 3, - type: "HLevel" + type: "Blocks2Hs", })); } if (subType !== "h4") { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH4", label: window.siyuan.languages.heading4, + accelerator: window.siyuan.config.keymap.editor.heading.heading4.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 4, - type: "HLevel" + type: "Blocks2Hs", })); } if (subType !== "h5") { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH5", label: window.siyuan.languages.heading5, + accelerator: window.siyuan.config.keymap.editor.heading.heading5.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 5, - type: "HLevel" + type: "Blocks2Hs", })); } if (subType !== "h6") { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsInto({ icon: "iconH6", label: window.siyuan.languages.heading6, + accelerator: window.siyuan.config.keymap.editor.heading.heading6.custom, protyle, - nodeElement, - id, + selectsElement: [nodeElement], level: 6, - type: "HLevel" + type: "Blocks2Hs", })); } } else if (type === "NodeList" && !window.siyuan.config.readonly) { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsOneInto({ icon: "iconParagraph", label: window.siyuan.languages.paragraph, protyle, @@ -769,7 +828,7 @@ export class Gutter { type: "CancelList" })); if (nodeElement.getAttribute("data-subtype") === "o") { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsOneInto({ icon: "iconList", label: window.siyuan.languages.list, protyle, @@ -777,7 +836,7 @@ export class Gutter { id, type: "OL2UL" })); - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsOneInto({ icon: "iconCheck", label: window.siyuan.languages.check, protyle, @@ -786,7 +845,7 @@ export class Gutter { type: "UL2TL" })); } else if (nodeElement.getAttribute("data-subtype") === "t") { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsOneInto({ icon: "iconList", label: window.siyuan.languages.list, protyle, @@ -794,7 +853,7 @@ export class Gutter { id, type: "TL2UL" })); - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsOneInto({ icon: "iconOrderedList", label: window.siyuan.languages["ordered-list"], protyle, @@ -803,7 +862,7 @@ export class Gutter { type: "TL2OL" })); } else { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsOneInto({ icon: "iconOrderedList", label: window.siyuan.languages["ordered-list"], protyle, @@ -811,7 +870,7 @@ export class Gutter { id, type: "UL2OL" })); - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsOneInto({ icon: "iconCheck", label: window.siyuan.languages.check, protyle, @@ -821,7 +880,7 @@ export class Gutter { })); } } else if (type === "NodeBlockquote" && !window.siyuan.config.readonly) { - turnIntoSubmenu.push(this.turnInto({ + turnIntoSubmenu.push(this.turnsOneInto({ icon: "iconParagraph", label: window.siyuan.languages.paragraph, protyle, diff --git a/app/src/protyle/wysiwyg/keydown.ts b/app/src/protyle/wysiwyg/keydown.ts index bd820f033..21581ca78 100644 --- a/app/src/protyle/wysiwyg/keydown.ts +++ b/app/src/protyle/wysiwyg/keydown.ts @@ -32,9 +32,7 @@ import {matchHotKey} from "../util/hotKey"; import {enter} from "./enter"; import {fixTable} from "../util/table"; import { - phTransaction, - transaction, - turnsIntoTransaction, + transaction, turnsIntoOneTransaction, turnsIntoTransaction, updateBatchTransaction, updateTransaction } from "./transaction"; @@ -244,6 +242,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { !isCtrl(event) && event.key !== "Escape" && !event.shiftKey && !event.altKey && !/^F\d{1,2}$/.test(event.key) && event.key !== "Enter" && event.key !== "Tab" && event.key !== "Backspace" && event.key !== "Delete") { event.stopPropagation(); + hideElements(["select"], protyle); return false; } if (!isCtrl(event) && !event.shiftKey && event.key !== "Backspace" && event.key !== "PageUp" && event.key !== "PageDown" && event.key.indexOf("Arrow") === -1) { @@ -1064,38 +1063,78 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { } // h1 - h6 hotkey + if (matchHotKey(window.siyuan.config.keymap.editor.heading.paragraph.custom, event)) { + turnsIntoTransaction({ + protyle, + nodeElement, + type: "Blocks2Ps", + }) + event.preventDefault(); + event.stopPropagation(); + return true; + } if (matchHotKey(window.siyuan.config.keymap.editor.heading.heading1.custom, event)) { - phTransaction(protyle, range, nodeElement, 1); + turnsIntoTransaction({ + protyle, + nodeElement, + type: "Blocks2Hs", + level: 1 + }) event.preventDefault(); event.stopPropagation(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.heading.heading2.custom, event)) { - phTransaction(protyle, range, nodeElement, 2); + turnsIntoTransaction({ + protyle, + nodeElement, + type: "Blocks2Hs", + level: 2 + }) event.preventDefault(); event.stopPropagation(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.heading.heading3.custom, event)) { - phTransaction(protyle, range, nodeElement, 3); + turnsIntoTransaction({ + protyle, + nodeElement, + type: "Blocks2Hs", + level: 3 + }) event.preventDefault(); event.stopPropagation(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.heading.heading4.custom, event)) { - phTransaction(protyle, range, nodeElement, 4); + turnsIntoTransaction({ + protyle, + nodeElement, + type: "Blocks2Hs", + level: 4 + }) event.preventDefault(); event.stopPropagation(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.heading.heading5.custom, event)) { - phTransaction(protyle, range, nodeElement, 5); + turnsIntoTransaction({ + protyle, + nodeElement, + type: "Blocks2Hs", + level: 5 + }) event.preventDefault(); event.stopPropagation(); return true; } if (matchHotKey(window.siyuan.config.keymap.editor.heading.heading6.custom, event)) { - phTransaction(protyle, range, nodeElement, 6); + turnsIntoTransaction({ + protyle, + nodeElement, + type: "Blocks2Hs", + level: 6 + }) event.preventDefault(); event.stopPropagation(); return true; @@ -1474,7 +1513,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { if (selectsElement.length < 2) { return; } - turnsIntoTransaction({ + turnsIntoOneTransaction({ protyle, selectsElement, type: "BlocksMergeSuperBlock", level: "row" @@ -1489,7 +1528,7 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { if (selectsElement.length < 2) { return; } - turnsIntoTransaction({ + turnsIntoOneTransaction({ protyle, selectsElement, type: "BlocksMergeSuperBlock", level: "col" diff --git a/app/src/protyle/wysiwyg/transaction.ts b/app/src/protyle/wysiwyg/transaction.ts index fba948555..4b0bb4bfa 100644 --- a/app/src/protyle/wysiwyg/transaction.ts +++ b/app/src/protyle/wysiwyg/transaction.ts @@ -469,7 +469,7 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, focus: b } }; -export const turnsIntoTransaction = (options: { protyle: IProtyle, selectsElement: Element[], type: string, level?: string }) => { +export const turnsIntoOneTransaction = (options: { protyle: IProtyle, selectsElement: Element[], type: string, level?: string }) => { let parentElement: Element; const id = Lute.NewNodeID(); if (options.type === "BlocksMergeSuperBlock") { @@ -559,110 +559,97 @@ export const turnsIntoTransaction = (options: { protyle: IProtyle, selectsElemen hideElements(["gutter"], options.protyle); }; -export const turnIntoTransaction = async (options: { protyle: IProtyle, nodeElement: Element, id: string, type: string, level?: number }) => { - if (!options.nodeElement.querySelector("wbr")) { - getContenteditableElement(options.nodeElement)?.insertAdjacentHTML("afterbegin", ""); - } - if (options.type === "HLevel" && options.nodeElement.getAttribute("data-type") === "NodeHeading" && - options.nodeElement.getAttribute("fold") === "1") { - setFold(options.protyle, options.nodeElement); - } - if (options.type === "CancelList" || options.type === "CancelBlockquote") { - for await(const item of options.nodeElement.querySelectorAll('[data-type="NodeHeading"][fold="1"]')) { - const itemId = item.getAttribute("data-node-id"); - item.removeAttribute("fold"); - const response = await fetchSyncPost("/api/transactions", { - session: options.protyle.id, - app: Constants.SIYUAN_APPID, - transactions: [{ - doOperations: [{ - action: "unfoldHeading", - id: itemId, - }], - undoOperations: [{ - action: "foldHeading", - id: itemId - }], - }] - }); - options.protyle.undo.add([{ - action: "unfoldHeading", - id: itemId, - }], [{ - action: "foldHeading", - id: itemId - }]); - item.insertAdjacentHTML("afterend", response.data[0].doOperations[0].retData); +export const turnsIntoTransaction = (options: { + protyle: IProtyle, + selectsElement?: Element[], + nodeElement?: Element, + type: string, + level?: number | string, + isContinue?: boolean +}) => { + let selectsElement: Element[] = options.selectsElement; + // 通过快捷键触发 + if (options.nodeElement) { + selectsElement = Array.from(options.protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); + if (selectsElement.length === 0) { + selectsElement = [options.nodeElement]; } - } - const oldHTML = options.nodeElement.outerHTML; - const previousId = options.nodeElement.previousElementSibling?.getAttribute("data-node-id"); - const parentId = options.nodeElement.parentElement.getAttribute("data-node-id") || options.protyle.block.parentID; - // @ts-ignore - const newHTML = options.protyle.lute[options.type](options.nodeElement.outerHTML, options.level); - options.nodeElement.outerHTML = newHTML; - if (options.type === "CancelList" || options.type === "CancelBlockquote") { - const tempElement = document.createElement("template"); - tempElement.innerHTML = newHTML; - const doOperations: IOperation[] = [{ - action: "delete", - id: options.id - }]; - const undoOperations: IOperation[] = []; - let tempPreviousId = previousId; - Array.from(tempElement.content.children).forEach((item) => { - const tempId = item.getAttribute("data-node-id"); - doOperations.push({ - action: "insert", - data: item.outerHTML, - id: tempId, - previousID: tempPreviousId, - parentID: parentId - }); - undoOperations.push({ - action: "delete", - id: tempId - }); - tempPreviousId = tempId; + let isContinue = false; + let hasEmbedBlock = false; + let isList = false; + selectsElement.find((item, index) => { + if (item.classList.contains("li")) { + isList = true; + return true; + } + if (item.classList.contains("bq") || item.classList.contains("sb") || item.classList.contains("p")) { + hasEmbedBlock = true; + } + if (item.nextElementSibling && selectsElement[index + 1] && + item.nextElementSibling.isSameNode(selectsElement[index + 1])) { + isContinue = true; + } else if (index !== selectsElement.length - 1) { + isContinue = false; + return true; + } }); + if (isList || (hasEmbedBlock && options.type === "Blocks2Ps")) { + return; + } + if (selectsElement.length === 1 && options.type === "Blocks2Hs" && + selectsElement[0].getAttribute("data-type") === "NodeHeading" && + options.level === parseInt(selectsElement[0].getAttribute("data-subtype").substr(1))) { + // 快捷键同级转换,消除标题 + options.type = "Blocks2Ps"; + } + options.isContinue = isContinue; + } + + let html = ""; + const doOperations: IOperation[] = []; + const undoOperations: IOperation[] = []; + selectsElement.forEach((item, index) => { + if ((options.type === "Blocks2Ps" || options.type === "Blocks2Hs") && + item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") { + setFold(options.protyle, item); + } + item.classList.remove("protyle-wysiwyg--select"); + html += item.outerHTML; + const id = item.getAttribute("data-node-id"); undoOperations.push({ - action: "insert", - data: oldHTML, - id: options.id, - previousID: previousId, - parentID: parentId + action: "update", + id, + data: item.outerHTML }); - transaction(options.protyle, doOperations, undoOperations); - } else { - updateTransaction(options.protyle, options.id, newHTML, oldHTML); - } - focusByWbr(options.protyle.wysiwyg.element, getEditorRange(options.protyle.wysiwyg.element)); - options.protyle.wysiwyg.element.querySelectorAll('[data-type="block-ref"]').forEach(item => { - if (item.textContent === "") { - fetchPost("/api/block/getRefText", {id: item.getAttribute("data-id")}, (response) => { - item.textContent = response.data; - }); + + if ((options.type === "Blocks2Ps" || options.type === "Blocks2Hs") && !options.isContinue) { + // @ts-ignore + item.outerHTML = options.protyle.lute[options.type](item.outerHTML, options.level); + } else { + if (index === selectsElement.length - 1) { + const tempElement = document.createElement("div"); + // @ts-ignore + tempElement.innerHTML = options.protyle.lute[options.type](html, options.level); + item.outerHTML = tempElement.innerHTML; + } else { + item.remove(); + } } }); - blockRender(options.protyle, options.protyle.wysiwyg.element); + undoOperations.forEach(item => { + const nodeElement = options.protyle.wysiwyg.element.querySelector(`[data-node-id="${item.id}"]`); + doOperations.push({ + action: "update", + id: item.id, + data: nodeElement.outerHTML + }); + }); + transaction(options.protyle, doOperations, undoOperations); processRender(options.protyle.wysiwyg.element); highlightRender(options.protyle.wysiwyg.element); -}; - -export const phTransaction = (protyle: IProtyle, range: Range, nodeElement: HTMLElement, level: number) => { - const id = nodeElement.getAttribute("data-node-id"); - const nodeType = nodeElement.getAttribute("data-type"); - range.insertNode(document.createElement("wbr")); - if (nodeType === "NodeHeading") { - const subType = parseInt(nodeElement.getAttribute("data-subtype").substr(1)); - if (subType === level) { - turnIntoTransaction({protyle, nodeElement, id, type: "H2P"}); - } else { - turnIntoTransaction({protyle, nodeElement, id, type: "HLevel", level}); - } - } else if (nodeType === "NodeParagraph") { - turnIntoTransaction({protyle, nodeElement, id, type: "P2H", level}); - } + blockRender(options.protyle, options.protyle.wysiwyg.element); + focusBlock(options.protyle.wysiwyg.element.querySelector(`[data-node-id="${selectsElement[0].getAttribute("data-node-id")}"]`)); + hideElements(["gutter"], options.protyle); }; const updateRef = (protyle: IProtyle, id: string, index = 0) => {