diff --git a/app/src/assets/template/mobile/index.tpl b/app/src/assets/template/mobile/index.tpl index d8948d6c0..7658ead7a 100644 --- a/app/src/assets/template/mobile/index.tpl +++ b/app/src/assets/template/mobile/index.tpl @@ -57,10 +57,19 @@
- - + + + + + + + + + + +
diff --git a/app/src/dialog/processSystem.ts b/app/src/dialog/processSystem.ts index e3a9b0634..e244f31fa 100644 --- a/app/src/dialog/processSystem.ts +++ b/app/src/dialog/processSystem.ts @@ -179,7 +179,7 @@ export const progressStatus = (data: IWebSocketData) => { const statusElement = document.querySelector("#status") as HTMLElement; statusElement.innerHTML = data.msg; statusElement.classList.remove("status--hide"); - if (document.querySelector("keyboardToolbar").classList.contains("fn__none")) { + if (document.querySelector("#keyboardToolbar").classList.contains("fn__none")) { statusElement.style.bottom = "0"; } else { statusElement.style.bottom = "30px"; diff --git a/app/src/mobile/util/showKeyboardToolbar.ts b/app/src/mobile/util/showKeyboardToolbar.ts index 0e2af79c0..78fd2adbb 100644 --- a/app/src/mobile/util/showKeyboardToolbar.ts +++ b/app/src/mobile/util/showKeyboardToolbar.ts @@ -1,6 +1,10 @@ import {getEventName} from "../../protyle/util/compatibility"; import {listIndent, listOutdent} from "../../protyle/wysiwyg/list"; import {hasClosestBlock, hasClosestByMatchTag} from "../../protyle/util/hasClosest"; +import {insertEmptyBlock} from "../../block/util"; +import {moveToDown, moveToUp} from "../../protyle/wysiwyg/move"; +import {Constants} from "../../constants"; +import {focusByRange} from "../../protyle/util/selection"; export const showKeyboardToolbar = (bottom = 0) => { const toolbarElement = document.getElementById("keyboardToolbar"); @@ -35,13 +39,47 @@ export const initKeyboardToolbar = () => { if (getSelection().rangeCount > 0) { range = getSelection().getRangeAt(0); } - if (!range) { + if (!range || (range && !protyle.wysiwyg.element.contains(range.startContainer))) { return; } const nodeElement = hasClosestBlock(range.startContainer); if (!nodeElement) { return; } + + if (type === "up") { + moveToUp(protyle, nodeElement, range); + focusByRange(range); + return; + } + if (type === "down") { + moveToDown(protyle, nodeElement, range) + focusByRange(range); + return; + } + + if (type === "before") { + insertEmptyBlock(protyle, "beforebegin"); + return; + } + if (type === "after") { + insertEmptyBlock(protyle, "afterend"); + return; + } + + if (type === "clear") { + if (range.toString()) { + protyle.toolbar.setInlineMark(protyle, "clear", "toolbar"); + } else if (range.startContainer.nodeType === 3 && range.startContainer.parentElement.tagName === "SPAN"){ + range.setStartAfter(range.startContainer.parentElement); + range.collapse(false); + range.insertNode(document.createTextNode(Constants.ZWSP)); + range.collapse(false); + focusByRange(range); + } + return; + } + if (!nodeElement.parentElement.classList.contains("li") || nodeElement.getAttribute("data-type") === "NodeCodeBlock") { return; } diff --git a/app/src/protyle/wysiwyg/keydown.ts b/app/src/protyle/wysiwyg/keydown.ts index 733886ae8..2ee28d8e0 100644 --- a/app/src/protyle/wysiwyg/keydown.ts +++ b/app/src/protyle/wysiwyg/keydown.ts @@ -60,6 +60,7 @@ import {highlightRender} from "../markdown/highlightRender"; import {countBlockWord} from "../../layout/status"; import {openMobileFileById} from "../../mobile/editor"; import {pasteAsPlainText} from "../util/paste"; +import {moveToDown, moveToUp} from "./move"; export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { editorElement.addEventListener("keydown", (event: KeyboardEvent & { target: HTMLElement }) => { @@ -1315,182 +1316,14 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { if (matchHotKey(window.siyuan.config.keymap.editor.general.moveToUp.custom, event)) { event.preventDefault(); event.stopPropagation(); - let previousElement: Element; - let sourceElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); - if (sourceElements.length === 0) { - let sourceElement = getTopAloneElement(nodeElement); - const foldElement = hasClosestByAttribute(sourceElement, "fold", "1"); - if (foldElement) { - sourceElement = foldElement; - } - if (sourceElement.previousElementSibling?.classList.contains("protyle-action")) { - sourceElement = getTopAloneElement(sourceElement.parentElement); - } - sourceElements = [sourceElement]; - } - const type = sourceElements[0].getAttribute("data-type"); - // 子列表 - if (type === "NodeListItem" && - !sourceElements[0].previousElementSibling && - sourceElements[0].parentElement.previousElementSibling?.previousElementSibling?.classList.contains("protyle-action")) { - if (sourceElements[0].parentElement.parentElement.previousElementSibling?.classList.contains("li")) { - previousElement = sourceElements[0].parentElement.parentElement.previousElementSibling.querySelector(".list"); - } - if (!previousElement) { - return; - } - } - if (type === "NodeList" && - sourceElements[0].previousElementSibling?.previousElementSibling?.classList.contains("protyle-action")) { - if (sourceElements[0].parentElement.previousElementSibling?.classList.contains("li")) { - previousElement = sourceElements[0].parentElement.previousElementSibling.querySelector(".list"); - } - if (!previousElement) { - return; - } - } - if (previousElement) { - previousElement = previousElement.lastElementChild.previousElementSibling; - const sourceParentElement = sourceElements[0].classList.contains("list") ? sourceElements[0] : sourceElements[0].parentElement; - range.insertNode(document.createElement("wbr")); - const html = previousElement.parentElement.parentElement.parentElement.outerHTML; - sourceElements.reverse().forEach(item => { - if (item.classList.contains("list")) { - previousElement.after(item.firstElementChild); - } else { - previousElement.after(item); - } - }); - if (sourceParentElement.childElementCount === 1) { - sourceParentElement.remove(); - } else if (sourceParentElement.getAttribute("data-subtype") === "o" && sourceParentElement.classList.contains("list")) { - updateListOrder(sourceParentElement, 1); - } - if (previousElement.getAttribute("data-subtype") === "o") { - updateListOrder(previousElement.parentElement); - } - - updateTransaction(protyle, previousElement.parentElement.parentElement.parentElement.getAttribute("data-node-id"), previousElement.parentElement.parentElement.parentElement.outerHTML, html); - preventScroll(protyle); - scrollCenter(protyle); - focusByWbr(previousElement.parentElement, range); - return; - } - if (!sourceElements[0].previousElementSibling || sourceElements[0].previousElementSibling?.classList.contains("protyle-action")) { - return; - } - previousElement = sourceElements[0].previousElementSibling; - if (sourceElements[0].getAttribute("data-subtype") === "o" && type === "NodeListItem") { - const html = sourceElements[0].parentElement.outerHTML; - sourceElements[sourceElements.length - 1].after(previousElement); - updateListOrder(sourceElements[0].parentElement, 1); - updateTransaction(protyle, sourceElements[0].parentElement.getAttribute("data-node-id"), sourceElements[0].parentElement.outerHTML, html); - } else { - const id = previousElement.getAttribute("data-node-id"); - transaction(protyle, [{ - action: "move", - id, - previousID: sourceElements[sourceElements.length - 1].getAttribute("data-node-id"), - }], [{ - action: "move", - id, - previousID: previousElement.previousElementSibling?.getAttribute("data-node-id"), - parentID: previousElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID - }]); - sourceElements[sourceElements.length - 1].after(previousElement); - } - preventScroll(protyle); - scrollCenter(protyle); + moveToUp(protyle, nodeElement, range); return; } if (matchHotKey(window.siyuan.config.keymap.editor.general.moveToDown.custom, event)) { event.preventDefault(); event.stopPropagation(); - let nextElement: Element; - let sourceElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); - if (sourceElements.length === 0) { - let sourceElement = getTopAloneElement(nodeElement); - const foldElement = hasClosestByAttribute(sourceElement, "fold", "1"); - if (foldElement) { - sourceElement = foldElement; - } - if (sourceElement.previousElementSibling?.classList.contains("protyle-action")) { - sourceElement = getTopAloneElement(sourceElement.parentElement); - } - sourceElements = [sourceElement]; - } - const type = sourceElements[0].getAttribute("data-type"); - // 子列表 - if (type === "NodeListItem" && - sourceElements[sourceElements.length - 1].nextElementSibling.classList.contains("protyle-attr") && - sourceElements[0].parentElement.parentElement?.classList.contains("li")) { - if (sourceElements[0].parentElement.parentElement.nextElementSibling?.classList.contains("li")) { - nextElement = sourceElements[0].parentElement.parentElement.nextElementSibling.querySelector(".list > .li"); - } - if (!nextElement) { - return; - } - } - if (type === "NodeList" && sourceElements[sourceElements.length - 1].nextElementSibling.classList.contains("protyle-attr") && - sourceElements[0].parentElement?.classList.contains("li")) { - if (sourceElements[0].parentElement.nextElementSibling?.classList.contains("li")) { - nextElement = sourceElements[0].parentElement.nextElementSibling.querySelector(".list > .li"); - } - if (!nextElement) { - return; - } - } - if (nextElement) { - const sourceParentElement = sourceElements[0].classList.contains("list") ? sourceElements[0] : sourceElements[0].parentElement; - range.insertNode(document.createElement("wbr")); - const html = nextElement.parentElement.parentElement.parentElement.outerHTML; - sourceElements.forEach(item => { - if (item.classList.contains("list")) { - nextElement.before(item.firstElementChild); - } else { - nextElement.before(item); - } - }); - if (sourceParentElement.childElementCount === 1) { - sourceParentElement.remove(); - } else if (sourceParentElement.getAttribute("data-subtype") === "o" && sourceParentElement.classList.contains("list")) { - updateListOrder(sourceParentElement, 1); - } - if (nextElement.getAttribute("data-subtype") === "o") { - updateListOrder(nextElement.parentElement, 1); - } - updateTransaction(protyle, nextElement.parentElement.parentElement.parentElement.getAttribute("data-node-id"), nextElement.parentElement.parentElement.parentElement.outerHTML, html); - preventScroll(protyle); - scrollCenter(protyle); - focusByWbr(nextElement.parentElement, range); - return; - } - if (!sourceElements[sourceElements.length - 1].nextElementSibling || sourceElements[sourceElements.length - 1].nextElementSibling?.classList.contains("protyle-attr")) { - return; - } - nextElement = sourceElements[sourceElements.length - 1].nextElementSibling; - if (nextElement.getAttribute("data-subtype") === "o" && nextElement.getAttribute("data-type") === "NodeListItem") { - const html = nextElement.parentElement.outerHTML; - sourceElements[0].before(nextElement); - updateListOrder(nextElement.parentElement, 1); - updateTransaction(protyle, nextElement.parentElement.getAttribute("data-node-id"), nextElement.parentElement.outerHTML, html); - } else { - const id = nextElement.getAttribute("data-node-id"); - transaction(protyle, [{ - action: "move", - id, - previousID: sourceElements[0].previousElementSibling?.getAttribute("data-node-id"), - parentID: nextElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID - }], [{ - action: "move", - id, - previousID: sourceElements[sourceElements.length - 1].getAttribute("data-node-id"), - }]); - sourceElements[0].before(nextElement); - } - preventScroll(protyle); - scrollCenter(protyle); + moveToDown(protyle, nodeElement, range); return; } diff --git a/app/src/protyle/wysiwyg/move.ts b/app/src/protyle/wysiwyg/move.ts new file mode 100644 index 000000000..b92a5fcfc --- /dev/null +++ b/app/src/protyle/wysiwyg/move.ts @@ -0,0 +1,183 @@ +import {getTopAloneElement} from "./getBlock"; +import {hasClosestByAttribute} from "../util/hasClosest"; +import {updateListOrder} from "./list"; +import {transaction, updateTransaction} from "./transaction"; +import {preventScroll} from "../scroll/preventScroll"; +import {scrollCenter} from "../../util/highlightById"; +import {focusByWbr} from "../util/selection"; + +export const moveToUp = (protyle:IProtyle, nodeElement:HTMLElement, range:Range) => { + let previousElement: Element; + let sourceElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); + if (sourceElements.length === 0) { + let sourceElement = getTopAloneElement(nodeElement); + const foldElement = hasClosestByAttribute(sourceElement, "fold", "1"); + if (foldElement) { + sourceElement = foldElement; + } + if (sourceElement.previousElementSibling?.classList.contains("protyle-action")) { + sourceElement = getTopAloneElement(sourceElement.parentElement); + } + sourceElements = [sourceElement]; + } + const type = sourceElements[0].getAttribute("data-type"); + // 子列表 + if (type === "NodeListItem" && + !sourceElements[0].previousElementSibling && + sourceElements[0].parentElement.previousElementSibling?.previousElementSibling?.classList.contains("protyle-action")) { + if (sourceElements[0].parentElement.parentElement.previousElementSibling?.classList.contains("li")) { + previousElement = sourceElements[0].parentElement.parentElement.previousElementSibling.querySelector(".list"); + } + if (!previousElement) { + return; + } + } + if (type === "NodeList" && + sourceElements[0].previousElementSibling?.previousElementSibling?.classList.contains("protyle-action")) { + if (sourceElements[0].parentElement.previousElementSibling?.classList.contains("li")) { + previousElement = sourceElements[0].parentElement.previousElementSibling.querySelector(".list"); + } + if (!previousElement) { + return; + } + } + if (previousElement) { + previousElement = previousElement.lastElementChild.previousElementSibling; + const sourceParentElement = sourceElements[0].classList.contains("list") ? sourceElements[0] : sourceElements[0].parentElement; + range.insertNode(document.createElement("wbr")); + const html = previousElement.parentElement.parentElement.parentElement.outerHTML; + sourceElements.reverse().forEach(item => { + if (item.classList.contains("list")) { + previousElement.after(item.firstElementChild); + } else { + previousElement.after(item); + } + }); + if (sourceParentElement.childElementCount === 1) { + sourceParentElement.remove(); + } else if (sourceParentElement.getAttribute("data-subtype") === "o" && sourceParentElement.classList.contains("list")) { + updateListOrder(sourceParentElement, 1); + } + if (previousElement.getAttribute("data-subtype") === "o") { + updateListOrder(previousElement.parentElement); + } + + updateTransaction(protyle, previousElement.parentElement.parentElement.parentElement.getAttribute("data-node-id"), previousElement.parentElement.parentElement.parentElement.outerHTML, html); + preventScroll(protyle); + scrollCenter(protyle); + focusByWbr(previousElement.parentElement, range); + return; + } + if (!sourceElements[0].previousElementSibling || sourceElements[0].previousElementSibling?.classList.contains("protyle-action")) { + return; + } + previousElement = sourceElements[0].previousElementSibling; + if (sourceElements[0].getAttribute("data-subtype") === "o" && type === "NodeListItem") { + const html = sourceElements[0].parentElement.outerHTML; + sourceElements[sourceElements.length - 1].after(previousElement); + updateListOrder(sourceElements[0].parentElement, 1); + updateTransaction(protyle, sourceElements[0].parentElement.getAttribute("data-node-id"), sourceElements[0].parentElement.outerHTML, html); + } else { + const id = previousElement.getAttribute("data-node-id"); + transaction(protyle, [{ + action: "move", + id, + previousID: sourceElements[sourceElements.length - 1].getAttribute("data-node-id"), + }], [{ + action: "move", + id, + previousID: previousElement.previousElementSibling?.getAttribute("data-node-id"), + parentID: previousElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID + }]); + sourceElements[sourceElements.length - 1].after(previousElement); + } + preventScroll(protyle); + scrollCenter(protyle); +} + +export const moveToDown = (protyle:IProtyle, nodeElement:HTMLElement, range:Range) => { + let nextElement: Element; + let sourceElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select")); + if (sourceElements.length === 0) { + let sourceElement = getTopAloneElement(nodeElement); + const foldElement = hasClosestByAttribute(sourceElement, "fold", "1"); + if (foldElement) { + sourceElement = foldElement; + } + if (sourceElement.previousElementSibling?.classList.contains("protyle-action")) { + sourceElement = getTopAloneElement(sourceElement.parentElement); + } + sourceElements = [sourceElement]; + } + const type = sourceElements[0].getAttribute("data-type"); + // 子列表 + if (type === "NodeListItem" && + sourceElements[sourceElements.length - 1].nextElementSibling.classList.contains("protyle-attr") && + sourceElements[0].parentElement.parentElement?.classList.contains("li")) { + if (sourceElements[0].parentElement.parentElement.nextElementSibling?.classList.contains("li")) { + nextElement = sourceElements[0].parentElement.parentElement.nextElementSibling.querySelector(".list > .li"); + } + if (!nextElement) { + return; + } + } + if (type === "NodeList" && sourceElements[sourceElements.length - 1].nextElementSibling.classList.contains("protyle-attr") && + sourceElements[0].parentElement?.classList.contains("li")) { + if (sourceElements[0].parentElement.nextElementSibling?.classList.contains("li")) { + nextElement = sourceElements[0].parentElement.nextElementSibling.querySelector(".list > .li"); + } + if (!nextElement) { + return; + } + } + if (nextElement) { + const sourceParentElement = sourceElements[0].classList.contains("list") ? sourceElements[0] : sourceElements[0].parentElement; + range.insertNode(document.createElement("wbr")); + const html = nextElement.parentElement.parentElement.parentElement.outerHTML; + sourceElements.forEach(item => { + if (item.classList.contains("list")) { + nextElement.before(item.firstElementChild); + } else { + nextElement.before(item); + } + }); + if (sourceParentElement.childElementCount === 1) { + sourceParentElement.remove(); + } else if (sourceParentElement.getAttribute("data-subtype") === "o" && sourceParentElement.classList.contains("list")) { + updateListOrder(sourceParentElement, 1); + } + if (nextElement.getAttribute("data-subtype") === "o") { + updateListOrder(nextElement.parentElement, 1); + } + updateTransaction(protyle, nextElement.parentElement.parentElement.parentElement.getAttribute("data-node-id"), nextElement.parentElement.parentElement.parentElement.outerHTML, html); + preventScroll(protyle); + scrollCenter(protyle); + focusByWbr(nextElement.parentElement, range); + return; + } + if (!sourceElements[sourceElements.length - 1].nextElementSibling || sourceElements[sourceElements.length - 1].nextElementSibling?.classList.contains("protyle-attr")) { + return; + } + nextElement = sourceElements[sourceElements.length - 1].nextElementSibling; + if (nextElement.getAttribute("data-subtype") === "o" && nextElement.getAttribute("data-type") === "NodeListItem") { + const html = nextElement.parentElement.outerHTML; + sourceElements[0].before(nextElement); + updateListOrder(nextElement.parentElement, 1); + updateTransaction(protyle, nextElement.parentElement.getAttribute("data-node-id"), nextElement.parentElement.outerHTML, html); + } else { + const id = nextElement.getAttribute("data-node-id"); + transaction(protyle, [{ + action: "move", + id, + previousID: sourceElements[0].previousElementSibling?.getAttribute("data-node-id"), + parentID: nextElement.parentElement.getAttribute("data-node-id") || protyle.block.parentID + }], [{ + action: "move", + id, + previousID: sourceElements[sourceElements.length - 1].getAttribute("data-node-id"), + }]); + sourceElements[0].before(nextElement); + } + preventScroll(protyle); + scrollCenter(protyle); +}