From 06ff21f99150d0386b9c31082487a29379a702a4 Mon Sep 17 00:00:00 2001 From: Vanessa Date: Thu, 15 Sep 2022 22:22:33 +0800 Subject: [PATCH] :sparkles: https://github.com/siyuan-note/siyuan/issues/2911 --- app/src/menus/protyle.ts | 6 +-- app/src/protyle/toolbar/Font.ts | 24 +++++----- app/src/protyle/toolbar/Link.ts | 18 +++---- app/src/protyle/toolbar/index.ts | 82 +++++++++++++++++++++----------- 4 files changed, 77 insertions(+), 53 deletions(-) diff --git a/app/src/menus/protyle.ts b/app/src/menus/protyle.ts index e286c5b1c..463931de7 100644 --- a/app/src/menus/protyle.ts +++ b/app/src/menus/protyle.ts @@ -686,7 +686,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText event.preventDefault(); event.stopPropagation(); if (linkElement.textContent === "" || linkElement.textContent === Constants.ZWSP) { - removeLink(linkElement, protyle.toolbar.range) + removeLink(linkElement, protyle.toolbar.range); } else { protyle.toolbar.range.selectNodeContents(linkElement); protyle.toolbar.range.collapse(false); @@ -729,7 +729,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText event.preventDefault(); event.stopPropagation(); if (!inputElement.value) { - removeLink(linkElement, protyle.toolbar.range) + removeLink(linkElement, protyle.toolbar.range); } else { protyle.toolbar.range.selectNodeContents(linkElement); protyle.toolbar.range.collapse(false); @@ -769,7 +769,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText event.preventDefault(); event.stopPropagation(); if (linkElement.textContent === "" || linkElement.textContent === Constants.ZWSP) { - removeLink(linkElement, protyle.toolbar.range) + removeLink(linkElement, protyle.toolbar.range); } else { protyle.toolbar.range.selectNodeContents(linkElement); protyle.toolbar.range.collapse(false); diff --git a/app/src/protyle/toolbar/Font.ts b/app/src/protyle/toolbar/Font.ts index 97cb00813..542fa1d98 100644 --- a/app/src/protyle/toolbar/Font.ts +++ b/app/src/protyle/toolbar/Font.ts @@ -1,9 +1,7 @@ import {getEventName, updateHotkeyTip} from "../util/compatibility"; import {ToolbarItem} from "./ToolbarItem"; -import {hasClosestBlock, hasClosestByMatchTag} from "../util/hasClosest"; -import {updateTransaction} from "../wysiwyg/transaction"; import {setPosition} from "../../util/setPosition"; -import {getSelectionPosition, focusByRange} from "../util/selection"; +import {getSelectionPosition} from "../util/selection"; import {Constants} from "../../constants"; export class Font extends ToolbarItem { @@ -141,17 +139,17 @@ export const setFontStyle = (textElement:HTMLElement, textOption:ITextOption) => break; } } -} +}; export const hasSameTextStyle = (currentElement: HTMLElement, sideElement: HTMLElement, textObj: ITextOption) => { if (!textObj) { return true; } let color = ""; - let webkitTextFillColor = "" - let webkitTextStroke = "" - let textShadow = "" - let backgroundColor = "" + let webkitTextFillColor = ""; + let webkitTextStroke = ""; + let textShadow = ""; + let backgroundColor = ""; if (currentElement.nodeType !== 3) { color = currentElement.style.color; webkitTextFillColor = currentElement.style.webkitTextFillColor; @@ -164,27 +162,27 @@ export const hasSameTextStyle = (currentElement: HTMLElement, sideElement: HTMLE webkitTextFillColor === sideElement.style.webkitTextFillColor && webkitTextStroke === sideElement.style.webkitTextStroke && textShadow === sideElement.style.textShadow && - backgroundColor === sideElement.style.backgroundColor + backgroundColor === sideElement.style.backgroundColor; } if (textObj.type === "backgroundColor") { return color === sideElement.style.color && webkitTextFillColor === sideElement.style.webkitTextFillColor && webkitTextStroke === sideElement.style.webkitTextStroke && textShadow === sideElement.style.textShadow && - textObj.color === sideElement.style.backgroundColor + textObj.color === sideElement.style.backgroundColor; } if (textObj.type === "style2") { return color === sideElement.style.color && "transparent" === sideElement.style.webkitTextFillColor && "0.2px var(--b3-theme-on-background)" === sideElement.style.webkitTextStroke && textShadow === sideElement.style.textShadow && - backgroundColor === sideElement.style.backgroundColor + backgroundColor === sideElement.style.backgroundColor; } if (textObj.type === "style4") { return color === sideElement.style.color && webkitTextFillColor === sideElement.style.webkitTextFillColor && webkitTextStroke === sideElement.style.webkitTextStroke && "1px 1px var(--b3-border-color), 2px 2px var(--b3-border-color), 3px 3px var(--b3-border-color), 4px 4px var(--b3-border-color)" === sideElement.style.textShadow && - backgroundColor === sideElement.style.backgroundColor + backgroundColor === sideElement.style.backgroundColor; } -} +}; diff --git a/app/src/protyle/toolbar/Link.ts b/app/src/protyle/toolbar/Link.ts index cb991f1dd..16f78f7e5 100644 --- a/app/src/protyle/toolbar/Link.ts +++ b/app/src/protyle/toolbar/Link.ts @@ -17,12 +17,12 @@ export class Link extends ToolbarItem { protyle.toolbar.element.classList.add("fn__none"); event.stopPropagation(); - const range = protyle.toolbar.range + const range = protyle.toolbar.range; const nodeElement = hasClosestBlock(range.startContainer); if (!nodeElement) { return; } - const aElement = hasClosestByAttribute(range.startContainer, "data-type", "a") + const aElement = hasClosestByAttribute(range.startContainer, "data-type", "a"); if (aElement) { linkMenu(protyle, aElement); return; @@ -38,9 +38,9 @@ export class Link extends ToolbarItem { range.insertNode(wbrElement); const html = nodeElement.outerHTML; - const newElement = document.createElement("span") - newElement.setAttribute("data-type", "a") - const rangeString = range.toString() + const newElement = document.createElement("span"); + newElement.setAttribute("data-type", "a"); + const rangeString = range.toString(); newElement.textContent = rangeString; range.extractContents(); range.insertNode(newElement); @@ -73,9 +73,9 @@ export class Link extends ToolbarItem { } export const removeLink = (linkElement: HTMLElement, range: Range) => { - const types = linkElement.getAttribute("data-type").split(" ") + const types = linkElement.getAttribute("data-type").split(" "); if (types.length === 1) { - const linkParentElement = linkElement.parentElement + const linkParentElement = linkElement.parentElement; linkElement.outerHTML = linkElement.innerHTML + ""; focusByWbr(linkParentElement, range); } else { @@ -86,9 +86,9 @@ export const removeLink = (linkElement: HTMLElement, range: Range) => { } }); linkElement.setAttribute("data-type", types.join(" ")); - linkElement.removeAttribute("data-href") + linkElement.removeAttribute("data-href"); range.selectNodeContents(linkElement); range.collapse(false); focusByRange(range); } -} +}; diff --git a/app/src/protyle/toolbar/index.ts b/app/src/protyle/toolbar/index.ts index b921d7d29..43cea967c 100644 --- a/app/src/protyle/toolbar/index.ts +++ b/app/src/protyle/toolbar/index.ts @@ -23,7 +23,7 @@ import {highlightRender} from "../markdown/highlightRender"; import {getContenteditableElement, hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock"; import {processRender} from "../util/processCode"; import {BlockRef} from "./BlockRef"; -import {hintMoveBlock, hintRef, hintRenderAssets, hintRenderTemplate, hintRenderWidget} from "../hint/extend"; +import {hintMoveBlock, hintRenderAssets, hintRenderTemplate, hintRenderWidget} from "../hint/extend"; import {blockRender} from "../markdown/blockRender"; /// #if !BROWSER import {clipboard, nativeImage, NativeImage} from "electron"; @@ -37,7 +37,6 @@ import {matchHotKey} from "../util/hotKey"; import {unicode2Emoji} from "../../emoji"; import {escapeHtml} from "../../util/escape"; import {hideElements} from "../ui/hideElements"; -import {linkMenu} from "../../menus/protyle"; import {renderAssetsPreview} from "../../asset/renderAssets"; import {electronUndo} from "../undo"; import {previewTemplate} from "./util"; @@ -145,7 +144,11 @@ export class Toolbar { return []; } if (!["DIV", "TD", "TH"].includes(startElement.tagName)) { - types = (startElement.getAttribute("data-type") || "").split(" "); + if (range.startContainer.textContent.length === range.startOffset && !hasNextSibling(range.startContainer)) { + // 光标在 span 结尾不算 type,否则如在粗体后 ctrl+b 就无法继续使用粗体了 + } else { + types = (startElement.getAttribute("data-type") || "").split(" "); + } } let endElement = range.endContainer as HTMLElement; if (endElement.nodeType === 3) { @@ -156,20 +159,20 @@ export class Toolbar { if (!endElement || endElement.nodeType === 3) { return []; } - if (!["DIV", "TD", "TH"].includes(endElement.tagName)) { + if (!["DIV", "TD", "TH"].includes(endElement.tagName) && !startElement.isSameNode(endElement)) { 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(" ")); - } - } 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 (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(" ")); + // } + // } 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(" ")); + // } + // } range.cloneContents().childNodes.forEach((item: HTMLElement) => { if (item.nodeType !== 3) { types = types.concat(item.getAttribute("data-type").split(" ")); @@ -260,14 +263,6 @@ export class Toolbar { } const rangeTypes = this.getCurrentType(); const selectText = this.range.toString(); - // 没选中时,相同 type 的文字中不进行操作 - if (selectText === "" && rangeTypes.includes(type) && this.range.startContainer.nodeType === 3) { - const currentNode = hasNextSibling(this.range.startContainer) as Element; - if (this.range.startOffset !== 0 && this.range.startContainer.parentElement.tagName === "SPAN" && - (currentNode || (!currentNode && this.range.startOffset < this.range.startContainer.textContent.length))) { - return; - } - } let previousElement: HTMLElement; let nextElement: HTMLElement; let previousIndex: number; @@ -303,14 +298,29 @@ export class Toolbar { this.mergeNode(contents.childNodes); const actionBtn = action === "toolbar" ? this.element.querySelector(`[data-type="${type}"]`) : undefined; let newNodes: Node[] = []; - if (selectText !== "" && ( - actionBtn?.classList.contains("protyle-toolbar__item--current") || - (action === "range" && rangeTypes.length > 0 && rangeTypes.includes(type) && (!textObj || textObj.type === "remove")) + if (actionBtn?.classList.contains("protyle-toolbar__item--current") || ( + action === "range" && rangeTypes.length > 0 && rangeTypes.includes(type) && (!textObj || textObj.type === "remove") )) { // 移除 if (actionBtn) { actionBtn.classList.remove("protyle-toolbar__item--current"); } + if (contents.childNodes.length === 0) { + rangeTypes.find((itemType, index) => { + if (type === itemType) { + rangeTypes.splice(index, 1); + return true; + } + }); + if (rangeTypes.length === 0) { + newNodes.push(document.createTextNode(Constants.ZWSP)); + } else { + const inlineElement = document.createElement("span"); + inlineElement.setAttribute("data-type", rangeTypes.join(" ")); + inlineElement.textContent = Constants.ZWSP; + newNodes.push(inlineElement); + } + } contents.childNodes.forEach((item: HTMLElement, index) => { if (item.nodeType !== 3) { const types = item.getAttribute("data-type").split(" "); @@ -356,9 +366,10 @@ export class Toolbar { } if (selectText === "") { const inlineElement = document.createElement("span"); - inlineElement.setAttribute("data-type", type); + rangeTypes.push(type) + inlineElement.setAttribute("data-type", [...new Set(rangeTypes)].join(" ")); inlineElement.textContent = Constants.ZWSP; - newNodes = [inlineElement]; + newNodes.push(inlineElement); } else { contents.childNodes.forEach((item: HTMLElement, index) => { if (item.nodeType === 3) { @@ -402,6 +413,21 @@ export class Toolbar { }); } } + if (this.range.startContainer.nodeType !== 3 && (this.range.startContainer as HTMLElement).tagName === "SPAN" && + this.range.startContainer.isSameNode(this.range.endContainer)) { + // 切割元素 + const startContainer = this.range.startContainer as HTMLElement; + const afterElement = document.createElement("span"); + const dataset = startContainer.dataset; + Object.keys(dataset).forEach(key => { + afterElement.setAttribute("data-" + key, dataset[key]); + }); + this.range.setEnd(startContainer.lastChild, startContainer.lastChild.textContent.length); + afterElement.append(this.range.extractContents()); + startContainer.after(afterElement); + this.range.setStartBefore(afterElement); + this.range.collapse(true); + } newNodes.forEach((item) => { this.range.insertNode(item); this.range.collapse(false);