diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index 91b3c10f0..29d95128a 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -1,4 +1,5 @@ { + "color": "Color", "confirmPassword": "I have already remembered the password", "passwordNoMatch": "The passwords entered twice do not match", "cloudConfigTip": "Please configure in [Settings - Cloud]", @@ -247,7 +248,7 @@ "importDataTip": "Import the exported zip archive, overwriting the workspace/data/ folder by path", "includeChildDoc": "Include child documents", "text": "Text", - "lastUsed": "Recently used fonts", + "lastUsed": "Apariencia usada recientemente", "removedNotebook": "Removed notebook", "querySyntax": "Query Syntax", "rollback": "Rollback", diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index 4b80d9b8c..23cd0e9a6 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -1,4 +1,5 @@ { + "color": "Color", "confirmPassword": "Ya he recordado la contraseña", "passwordNoMatch": "Las contraseñas ingresadas dos veces no coinciden", "cloudConfigTip": "Configure en [Configuración - Nube]", @@ -247,7 +248,7 @@ "importDataTip": "Importar el archivo zip exportado, sobrescribiendo la carpeta workspace/data/ por la ruta", "includeChildDoc": "Incluir los documentos de los niños", "text": "Texto", - "lastUsed": "Fuentes utilizadas recientemente", + "lastUsed": "Fuentes utilizadas apariencia", "removedNotebook": "Cuaderno de notas eliminado", "querySyntax": "Sintaxis de consulta", "rollback": "Rollback", diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index 87f692b0f..5158b9b66 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -1,4 +1,5 @@ { + "color": "Couleur", "confirmPassword": "J'ai déjà retenu le mot de passe", "passwordNoMatch": "Les mots de passe saisis deux fois ne correspondent pas", "cloudConfigTip": "Veuillez configurer dans [Paramètres - Cloud]", @@ -247,7 +248,7 @@ "importDataTip": "Importer l'archive zip exportée, en écrasant le dossier workspace/data/ par le chemin", "includeChildDoc": "Inclure les documents enfants", "text": "Texte", - "lastUsed": "Polices récemment utilisées", + "lastUsed": "Apparence récemment utilisée", "removedNotebook": "Cahier supprimé", "querySyntax": "Syntaxe de la requête", "rollback": "Rollback", diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index d04bc1d88..3500b09c1 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -1,4 +1,5 @@ { + "color": "顏色", "confirmPassword": "我已經牢記密碼了", "passwordNoMatch": "兩次輸入的密碼不一致", "cloudConfigTip": "請在 [設置 - 雲端] 中進行配置", @@ -247,7 +248,7 @@ "importDataTip": "將導出的 zip 壓縮包導入,按路徑覆蓋 工作空間/data/ 文件夾", "includeChildDoc": "包含子文檔", "text": "文字", - "lastUsed": "最近使用過的字體", + "lastUsed": "最近使用過的外觀", "removedNotebook": "已刪除的筆記本", "querySyntax": "查詢語法", "rollback": "回滾", diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index a71c5a452..fb8a1a0a4 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -1,4 +1,5 @@ { + "color": "颜色", "confirmPassword": "我已经牢记密码了", "passwordNoMatch": "两次输入的密码不一致", "cloudConfigTip": "请在 [设置 - 云端] 中进行配置", @@ -247,7 +248,7 @@ "importDataTip": "将导出的 zip 压缩包导入,按路径覆盖 工作空间/data/ 文件夹", "includeChildDoc": "包含子文档", "text": "文本", - "lastUsed": "最近使用过的字体", + "lastUsed": "最近使用过的外观", "removedNotebook": "已删除的笔记本", "querySyntax": "查询语法", "rollback": "回滚", diff --git a/app/src/constants.ts b/app/src/constants.ts index 8a0ec171c..eb148338e 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -196,7 +196,7 @@ export abstract class Constants { moveToDown: {default: "⇧⌘↓", custom: "⇧⌘↓"}, }, insert: { - font: {default: "⌥⌘X", custom: "⌥⌘X"}, + appearance: {default: "⌥⌘X", custom: "⌥⌘X"}, lastUsed: {default: "⌥X", custom: "⌥X"}, ref: {default: "⌥[", custom: "⌥["}, kbd: {default: "⌘'", custom: "⌘'"}, diff --git a/app/src/protyle/gutter/index.ts b/app/src/protyle/gutter/index.ts index 7d4637a24..cb0586af1 100644 --- a/app/src/protyle/gutter/index.ts +++ b/app/src/protyle/gutter/index.ts @@ -39,6 +39,8 @@ import {AIActions} from "../../ai/actions"; import {activeBlur} from "../../mobile/util/keyboardToolbar"; import {hideTooltip} from "../../dialog/tooltip"; import {App} from "../../index"; +import {appearanceMenu} from "../toolbar/Font"; +import {setPosition} from "../../util/setPosition"; export class Gutter { public element: HTMLElement; @@ -676,7 +678,19 @@ export class Gutter { window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element); const appearanceElement = new MenuItem({ label: window.siyuan.languages.appearance, - submenu: this.genCardStyle(selectsElement, protyle).concat(this.genFontStyle(selectsElement, protyle)).concat(this.genBGStyle(selectsElement, protyle)) + click() { + protyle.toolbar.element.classList.add("fn__none"); + protyle.toolbar.subElement.innerHTML = ""; + protyle.toolbar.subElement.style.width = ""; + protyle.toolbar.subElement.style.padding = ""; + protyle.toolbar.subElement.append(appearanceMenu(protyle, selectsElement)); + protyle.toolbar.subElement.classList.remove("fn__none"); + protyle.toolbar.subElementCloseCB = undefined; + /// #if !MOBILE + const position = selectsElement[0].getBoundingClientRect(); + setPosition(protyle.toolbar.subElement, position.left, position.top); + /// #endif + } }).element; window.siyuan.menus.menu.append(appearanceElement); if (!isMobile()) { @@ -1461,7 +1475,19 @@ export class Gutter { if (!protyle.disabled) { const appearanceElement = new MenuItem({ label: window.siyuan.languages.appearance, - submenu: this.genCardStyle([nodeElement], protyle).concat(this.genFontStyle([nodeElement], protyle)).concat(this.genBGStyle([nodeElement], protyle)) + click() { + protyle.toolbar.element.classList.add("fn__none"); + protyle.toolbar.subElement.innerHTML = ""; + protyle.toolbar.subElement.style.width = ""; + protyle.toolbar.subElement.style.padding = ""; + protyle.toolbar.subElement.append(appearanceMenu(protyle, [nodeElement])); + protyle.toolbar.subElement.classList.remove("fn__none"); + protyle.toolbar.subElementCloseCB = undefined; + /// #if !MOBILE + const position = nodeElement.getBoundingClientRect(); + setPosition(protyle.toolbar.subElement, position.left, position.top); + /// #endif + } }).element; window.siyuan.menus.menu.append(appearanceElement); if (!isMobile()) { @@ -1622,47 +1648,6 @@ export class Gutter { }).element); } - private genBGStyle(nodeElements: Element[], protyle: IProtyle) { - const styles: IMenu[] = []; - const isM = isMobile(); - ["var(--b3-font-background1)", "var(--b3-font-background2)", "var(--b3-font-background3)", "var(--b3-font-background4)", - "var(--b3-font-background5)", "var(--b3-font-background6)", "var(--b3-font-background7)", "var(--b3-font-background8)", - "var(--b3-font-background9)", "var(--b3-font-background10)", "var(--b3-font-background11)", "var(--b3-font-background12)", - "var(--b3-font-background13)"].forEach((item, index) => { - styles.push({ - iconHTML: isM ? `` : Constants.ZWSP, - label: isM ? `${window.siyuan.languages.colorPrimary} ${index + 1}` : `
- -
`, - click: () => { - this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.backgroundColor = item; - }); - } - }); - }); - styles.push({ - type: "separator" - }); - styles.push({ - iconHTML: isM ? 'A' : Constants.ZWSP, - label: isM ? window.siyuan.languages.clearFontStyle : `
- A -
`, - click: () => { - this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.color = ""; - e.style.webkitTextFillColor = ""; - e.style.webkitTextStroke = ""; - e.style.textShadow = ""; - e.style.backgroundColor = ""; - e.style.fontSize = ""; - }); - } - }); - return styles; - } - private genWidths(nodeElements: Element[], protyle: IProtyle) { const styles: IMenu[] = []; ["25%", "33%", "50%", "67%", "75%"].forEach((item) => { @@ -1740,77 +1725,6 @@ export class Gutter { }).element); } - private genFontStyle(nodeElements: Element[], protyle: IProtyle) { - const styles: IMenu[] = []; - const isM = isMobile(); - ["var(--b3-font-color1)", "var(--b3-font-color2)", "var(--b3-font-color3)", "var(--b3-font-color4)", - "var(--b3-font-color5)", "var(--b3-font-color6)", "var(--b3-font-color7)", "var(--b3-font-color8)", - "var(--b3-font-color9)", "var(--b3-font-color10)", "var(--b3-font-color11)", "var(--b3-font-color12)", - "var(--b3-font-color13)"].forEach((item, index) => { - styles.push({ - iconHTML: isM ? `A` : Constants.ZWSP, - label: isM ? `${window.siyuan.languages.colorFont} ${index + 1}` : `
- A -
`, - click: () => { - this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.color = item; - }); - } - }); - }); - styles.push({ - type: "separator" - }); - return styles; - } - - private genCardStyle(nodeElements: Element[], protyle: IProtyle) { - const styles: IMenu[] = []; - const isM = isMobile(); - ["error", "warning", "info", "success"].forEach((item) => { - styles.push({ - iconHTML: isM ? `A` : Constants.ZWSP, - label: isM ? window.siyuan.languages[item + "Style"] : `
- A -
`, - click: () => { - this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.color = `var(--b3-card-${item}-color)`; - e.style.backgroundColor = `var(--b3-card-${item}-background)`; - }); - } - }); - }); - styles.push({ - type: "separator" - }); - return styles.concat([{ - iconHTML: isM ? 'A' : Constants.ZWSP, - label: isM ? window.siyuan.languages.hollow : `
- A -
`, - click: () => { - this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.webkitTextStroke = "0.2px var(--b3-theme-on-background)"; - e.style.webkitTextFillColor = "transparent"; - }); - } - }, { - iconHTML: isM ? ' A' : Constants.ZWSP, - label: isM ? window.siyuan.languages.shadow : `
- A -
`, - click: () => { - this.genClick(nodeElements, protyle, (e: HTMLElement) => { - e.style.textShadow = "1px 1px var(--b3-theme-surface-lighter), 2px 2px var(--b3-theme-surface-lighter), 3px 3px var(--b3-theme-surface-lighter), 4px 4px var(--b3-theme-surface-lighter)"; - }); - } - }, { - type: "separator" - }]); - } - private genCopyTextRef(selectsElement: Element[]): false | IMenu { if (isNotEditBlock(selectsElement[0])) { return false; diff --git a/app/src/protyle/toolbar/Font.ts b/app/src/protyle/toolbar/Font.ts index 10b393747..783929be8 100644 --- a/app/src/protyle/toolbar/Font.ts +++ b/app/src/protyle/toolbar/Font.ts @@ -1,9 +1,10 @@ import {setStorageVal, updateHotkeyTip} from "../util/compatibility"; import {ToolbarItem} from "./ToolbarItem"; import {setPosition} from "../../util/setPosition"; -import {focusByRange, getSelectionPosition} from "../util/selection"; +import {focusBlock, focusByRange, getSelectionPosition} from "../util/selection"; import {Constants} from "../../constants"; import {hasClosestByAttribute} from "../util/hasClosest"; +import {updateBatchTransaction} from "../wysiwyg/transaction"; export class Font extends ToolbarItem { public element: HTMLElement; @@ -18,7 +19,7 @@ export class Font extends ToolbarItem { protyle.toolbar.subElement.innerHTML = ""; protyle.toolbar.subElement.style.width = ""; protyle.toolbar.subElement.style.padding = ""; - protyle.toolbar.subElement.append(fontMenu(protyle)); + protyle.toolbar.subElement.append(appearanceMenu(protyle)); protyle.toolbar.subElement.classList.remove("fn__none"); protyle.toolbar.subElementCloseCB = undefined; focusByRange(protyle.toolbar.range); @@ -30,7 +31,7 @@ export class Font extends ToolbarItem { } } -const fontMenu = (protyle: IProtyle) => { +export const appearanceMenu = (protyle: IProtyle, nodeElements?: Element[]) => { let colorHTML = ""; ["var(--b3-font-color1)", "var(--b3-font-color2)", "var(--b3-font-color3)", "var(--b3-font-color4)", "var(--b3-font-color5)", "var(--b3-font-color6)", "var(--b3-font-color7)", "var(--b3-font-color8)", @@ -76,20 +77,39 @@ const fontMenu = (protyle: IProtyle) => { case "fontSize": lastColorHTML += ``; break; + case "style1": + lastColorHTML += ``; + break; } }); lastColorHTML += ""; } - let textElement = protyle.toolbar.range.cloneContents().querySelector('[data-type~="text"]') as HTMLElement; + let textElement: HTMLElement let fontSize = "16px"; - if (!textElement) { - textElement = hasClosestByAttribute(protyle.toolbar.range.startContainer, "data-type", "text") as HTMLElement; + if (nodeElements) { + if (nodeElements.length === 1) { + textElement = nodeElements[0] as HTMLElement + } + } else { + textElement = protyle.toolbar.range.cloneContents().querySelector('[data-type~="text"]') as HTMLElement; + if (!textElement) { + textElement = hasClosestByAttribute(protyle.toolbar.range.startContainer, "data-type", "text") as HTMLElement; + } } if (textElement) { fontSize = textElement.style.fontSize || "16px"; } element.innerHTML = `${lastColorHTML}
+
${window.siyuan.languages.color}
+
+
+ + + + +
+
${window.siyuan.languages.colorFont}
@@ -136,10 +156,38 @@ const fontMenu = (protyle: IProtyle) => { while (target && !target.isEqualNode(element)) { const dataType = target.getAttribute("data-type"); if (target.tagName === "BUTTON") { - if (dataType === "clear") { - protyle.toolbar.setInlineMark(protyle, "clear", "range", {type: "text"}); + if (nodeElements) { + updateBatchTransaction(nodeElements, protyle, (e: HTMLElement) => { + if (dataType === "clear") { + e.style.color = ""; + e.style.webkitTextFillColor = ""; + e.style.webkitTextStroke = ""; + e.style.textShadow = ""; + e.style.backgroundColor = ""; + e.style.fontSize = ""; + } else if (dataType === "style1") { + e.style.backgroundColor = target.style.backgroundColor; + e.style.color = target.style.color; + } else if (dataType === "style2") { + e.style.webkitTextStroke = "0.2px var(--b3-theme-on-background)"; + e.style.webkitTextFillColor = "transparent"; + } else if (dataType === "style4") { + e.style.textShadow = "1px 1px var(--b3-theme-surface-lighter), 2px 2px var(--b3-theme-surface-lighter), 3px 3px var(--b3-theme-surface-lighter), 4px 4px var(--b3-theme-surface-lighter)"; + } else if (dataType === "color") { + e.style.color = target.style.color; + } else if (dataType === "backgroundColor") { + e.style.backgroundColor = target.style.backgroundColor; + } + }); + focusBlock(nodeElements[0]); } else { - fontEvent(protyle, dataType, target.style.backgroundColor || target.style.color || target.textContent); + if (dataType === "clear") { + protyle.toolbar.setInlineMark(protyle, "clear", "range", {type: "text"}); + } else if (dataType === "style1") { + fontEvent(protyle, dataType, target.style.backgroundColor + Constants.ZWSP + target.style.color); + } else { + fontEvent(protyle, dataType, target.style.backgroundColor || target.style.color || target.textContent); + } } break; } @@ -147,7 +195,14 @@ const fontMenu = (protyle: IProtyle) => { } }); element.querySelector("select").addEventListener("change", function (event: Event) { - fontEvent(protyle, "fontSize", (event.target as HTMLSelectElement).value); + if (nodeElements) { + updateBatchTransaction(nodeElements, protyle, (e: HTMLElement) => { + e.style.fontSize = (event.target as HTMLSelectElement).value; + }); + focusBlock(nodeElements[0]); + } else { + fontEvent(protyle, "fontSize", (event.target as HTMLSelectElement).value); + } }); return element; }; @@ -164,8 +219,8 @@ export const fontEvent = (protyle: IProtyle, type?: string, color?: string) => { setStorageVal(Constants.LOCAL_FONTSTYLES, window.siyuan.storage[Constants.LOCAL_FONTSTYLES]); } else { if (localFontStyles.length === 0) { - type = "color"; - color = "var(--b3-font-color1)"; + type = "style1"; + color = "var(--b3-card-error-color)" + Constants.ZWSP + "var(--b3-card-error-background)"; } else { const fontStyles = localFontStyles[0].split(Constants.ZWSP); type = fontStyles[0]; @@ -214,6 +269,10 @@ export const setFontStyle = (textElement: HTMLElement, textOption: ITextOption) case "backgroundColor": textElement.style.backgroundColor = textOption.color; break; + case "style1": + textElement.style.backgroundColor = textOption.color.split(Constants.ZWSP)[0]; + textElement.style.color = textOption.color.split(Constants.ZWSP)[1]; + break; case "style2": textElement.style.webkitTextStroke = "0.2px var(--b3-theme-on-background)"; textElement.style.webkitTextFillColor = "transparent"; @@ -304,6 +363,14 @@ export const hasSameTextStyle = (currentElement: HTMLElement, sideElement: HTMLE fontSize === sideElement.style.fontSize && textObj.color === sideElement.style.backgroundColor; } + if (textObj.type === "style1") { + return textObj.color.split(Constants.ZWSP)[0] === sideElement.style.color && + webkitTextFillColor === sideElement.style.webkitTextFillColor && + webkitTextStroke === sideElement.style.webkitTextStroke && + textShadow === sideElement.style.textShadow && + fontSize === sideElement.style.fontSize && + textObj.color.split(Constants.ZWSP)[1] === sideElement.style.backgroundColor; + } if (textObj.type === "style2") { return color === sideElement.style.color && "transparent" === sideElement.style.webkitTextFillColor && diff --git a/app/src/protyle/util/Options.ts b/app/src/protyle/util/Options.ts index 0147e8c28..23c803e95 100644 --- a/app/src/protyle/util/Options.ts +++ b/app/src/protyle/util/Options.ts @@ -229,8 +229,8 @@ export class Options { tipPosition: "n", }, { name: "text", - lang: "font", - hotkey: window.siyuan.config.keymap.editor.insert.font.custom, + lang: "appearance", + hotkey: window.siyuan.config.keymap.editor.insert.appearance.custom, icon: "iconFont", tipPosition: "n", }, {