diff --git a/app/src/assets/scss/protyle/_protyle.scss b/app/src/assets/scss/protyle/_protyle.scss index 0d9bc6223..19c18c78f 100644 --- a/app/src/assets/scss/protyle/_protyle.scss +++ b/app/src/assets/scss/protyle/_protyle.scss @@ -115,6 +115,15 @@ min-height: 30px; z-index: 1; + .block__icon { + opacity: 1; + margin-left: 8px; + + &:disabled { + opacity: .38; + } + } + &__space { min-width: 8px; transition: var(--b3-transition); @@ -153,7 +162,6 @@ } &__icon { - margin-right: 8px; opacity: 1; border: 0; line-height: 24px; diff --git a/app/src/assets/scss/protyle/_wysiwyg.scss b/app/src/assets/scss/protyle/_wysiwyg.scss index 57fa157f8..ba83345a1 100644 --- a/app/src/assets/scss/protyle/_wysiwyg.scss +++ b/app/src/assets/scss/protyle/_wysiwyg.scss @@ -503,7 +503,7 @@ [data-node-id][fold="1"]:not(.li):not([data-type="NodeHeading"]) { @include text-clamp(1); - opacity: 0.54; + opacity: 0.38; font-size: 16px; height: 26px; line-height: 26px; diff --git a/app/src/protyle/breadcrumb/index.ts b/app/src/protyle/breadcrumb/index.ts index 8620ae7a1..aad2b2de0 100644 --- a/app/src/protyle/breadcrumb/index.ts +++ b/app/src/protyle/breadcrumb/index.ts @@ -28,8 +28,9 @@ import {Menu} from "../../plugin/Menu"; import {getNoContainerElement} from "../wysiwyg/getBlock"; import {openTitleMenu} from "../header/openTitleMenu"; import {emitOpenMenu} from "../../plugin/EventBus"; -import {isInAndroid, isMac, updateHotkeyTip} from "../util/compatibility"; +import {isInAndroid, isIPad, isMac, updateHotkeyTip} from "../util/compatibility"; import {resize} from "../util/resize"; +import {listIndent, listOutdent} from "../wysiwyg/list"; export class Breadcrumb { public element: HTMLElement; @@ -40,18 +41,25 @@ export class Breadcrumb { constructor(protyle: IProtyle) { const element = document.createElement("div"); element.className = "protyle-breadcrumb"; + let padHTML = "" + /// #if BROWSER + if (isIPad() || isInAndroid()) { + padHTML = ` + + +`; + } + /// #endif element.innerHTML = `${isMobile() ? `` : '
'} - - - - - -`; - +${padHTML} + + + +`; this.element = element.firstElementChild as HTMLElement; element.addEventListener("click", (event) => { /// #if !MOBILE @@ -133,6 +141,36 @@ export class Breadcrumb { target.classList.add("block__icon--active"); } break; + } else if (type === "undo") { + protyle.undo.undo(protyle); + event.preventDefault(); + event.stopPropagation(); + break; + } else if (type === "redo") { + protyle.undo.redo(protyle); + event.preventDefault(); + event.stopPropagation(); + break; + } else if (type === "outdent") { + if (protyle.toolbar.range) { + const blockElement = hasClosestBlock(protyle.toolbar.range.startContainer); + if (blockElement) { + listOutdent(protyle, [blockElement.parentElement], protyle.toolbar.range); + } + } + event.preventDefault(); + event.stopPropagation(); + break; + } else if (type === "indent") { + if (protyle.toolbar.range) { + const blockElement = hasClosestBlock(protyle.toolbar.range.startContainer); + if (blockElement) { + listIndent(protyle, [blockElement.parentElement], protyle.toolbar.range); + } + } + event.preventDefault(); + event.stopPropagation(); + break; } target = target.parentElement; } @@ -528,9 +566,7 @@ export class Breadcrumb { } public render(protyle: IProtyle, update = false) { - if (isMobile()) { - return; - } + /// #if !MOBILE let range: Range; let blockElement: Element; if (getSelection().rangeCount > 0) { @@ -614,6 +650,7 @@ export class Breadcrumb { this.element.scrollLeft = (this.element.lastElementChild as HTMLElement).offsetLeft - this.element.clientWidth + 14; } }); + /// #endif } public hide() { diff --git a/app/src/protyle/gutter/index.ts b/app/src/protyle/gutter/index.ts index 44fd266be..5802bf8cc 100644 --- a/app/src/protyle/gutter/index.ts +++ b/app/src/protyle/gutter/index.ts @@ -442,7 +442,7 @@ export class Gutter { }], [{ action: "foldHeading", id: itemId - }]); + }], options.protyle); item.insertAdjacentHTML("afterend", response.data[0].doOperations[0].retData); } } diff --git a/app/src/protyle/undo/index.ts b/app/src/protyle/undo/index.ts index 24441342f..673779697 100644 --- a/app/src/protyle/undo/index.ts +++ b/app/src/protyle/undo/index.ts @@ -4,7 +4,7 @@ import {Constants} from "../../constants"; import {hideElements} from "../ui/hideElements"; import {scrollCenter} from "../../util/highlightById"; import {matchHotKey} from "../util/hotKey"; -import { ipcRenderer } from "electron"; +import {ipcRenderer} from "electron"; interface IOperations { doOperations: IOperation[], @@ -32,9 +32,17 @@ export class Undo { this.render(protyle, state, false); this.hasUndo = true; this.redoStack.push(state); + if (protyle.breadcrumb) { + const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]') + if (undoElement) { + if (this.undoStack.length === 0) { + undoElement.setAttribute("disabled", "true"); + } + protyle.breadcrumb.element.parentElement.querySelector('[data-type="redo"]').removeAttribute("disabled"); + } + } } - public redo(protyle: IProtyle) { if (protyle.disabled) { return; @@ -45,6 +53,15 @@ export class Undo { const state = this.redoStack.pop(); this.render(protyle, state, true); this.undoStack.push(state); + if (protyle.breadcrumb) { + const redoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="redo"]') + if (redoElement) { + protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]').removeAttribute("disabled"); + if (this.redoStack.length === 0) { + redoElement.setAttribute("disabled", "true"); + } + } + } } private render(protyle: IProtyle, state: IOperations, redo: boolean) { @@ -52,7 +69,7 @@ export class Undo { protyle.wysiwyg.lastHTMLs = {}; if (!redo) { state.undoOperations.forEach(item => { - onTransaction(protyle, item, true); + onTransaction(protyle, item, true); }); transaction(protyle, state.undoOperations); } else { @@ -65,19 +82,25 @@ export class Undo { scrollCenter(protyle); } - public replace(doOperations: IOperation[]) { + public replace(doOperations: IOperation[], protyle: IProtyle) { // undo 引发 replace 导致 stack 错误 https://github.com/siyuan-note/siyuan/issues/9178 if (this.hasUndo && this.redoStack.length > 0) { this.undoStack.push(this.redoStack.pop()); this.redoStack = []; this.hasUndo = false; + if (protyle.breadcrumb) { + const redoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="redo"]') + if (redoElement) { + redoElement.setAttribute("disabled", "true"); + } + } } if (this.undoStack.length > 0) { this.undoStack[this.undoStack.length - 1].doOperations = doOperations; } } - public add( doOperations: IOperation[], undoOperations: IOperation[]) { + public add(doOperations: IOperation[], undoOperations: IOperation[], protyle: IProtyle) { this.undoStack.push({undoOperations, doOperations}); if (this.undoStack.length > Constants.SIZE_UNDO) { this.undoStack.shift(); @@ -86,6 +109,12 @@ export class Undo { this.redoStack = []; this.hasUndo = false; } + if (protyle.breadcrumb) { + const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]') + if (undoElement) { + undoElement.removeAttribute("disabled"); + } + } } public clear() { diff --git a/app/src/protyle/util/onGet.ts b/app/src/protyle/util/onGet.ts index 5303f2074..dd25c7793 100644 --- a/app/src/protyle/util/onGet.ts +++ b/app/src/protyle/util/onGet.ts @@ -314,6 +314,13 @@ export const disabledProtyle = (protyle: IProtyle) => { if (protyle.breadcrumb) { protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"] use').setAttribute("xlink:href", "#iconLock"); protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]').setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.tempUnlock : window.siyuan.languages.unlockEdit); + const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]') + if (undoElement && !undoElement.classList.contains("fn__none")) { + undoElement.classList.add("fn__none") + protyle.breadcrumb.element.parentElement.querySelector('[data-type="redo"]').classList.add("fn__none") + protyle.breadcrumb.element.parentElement.querySelector('[data-type="indent"]').classList.add("fn__none") + protyle.breadcrumb.element.parentElement.querySelector('[data-type="outdent"]').classList.add("fn__none") + } } hideTooltip(); }; @@ -357,6 +364,13 @@ export const enableProtyle = (protyle: IProtyle) => { if (protyle.breadcrumb) { protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"] use').setAttribute("xlink:href", "#iconUnlock"); protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]').setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.cancelTempUnlock : window.siyuan.languages.lockEdit); + const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]') + if (undoElement && undoElement.classList.contains("fn__none")) { + undoElement.classList.remove("fn__none") + protyle.breadcrumb.element.parentElement.querySelector('[data-type="redo"]').classList.remove("fn__none") + protyle.breadcrumb.element.parentElement.querySelector('[data-type="indent"]').classList.remove("fn__none") + protyle.breadcrumb.element.parentElement.querySelector('[data-type="outdent"]').classList.remove("fn__none") + } } hideTooltip(); }; diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts index 523c633cd..e74ea9bf3 100644 --- a/app/src/protyle/wysiwyg/index.ts +++ b/app/src/protyle/wysiwyg/index.ts @@ -1823,6 +1823,19 @@ export class WYSIWYG { if (range.toString() === "") { countSelectWord(range, protyle.block.rootID); } + if (protyle.breadcrumb) { + const indentElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="indent"]') + if (indentElement) { + const outdentElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="outdent"]'); + if (nodeElement.parentElement.classList.contains("li")) { + indentElement.removeAttribute("disabled"); + outdentElement.removeAttribute("disabled"); + } else { + indentElement.setAttribute("disabled", "true"); + outdentElement.setAttribute("disabled", "true"); + } + } + } } event.stopPropagation(); } @@ -2351,6 +2364,20 @@ export class WYSIWYG { /// #if !MOBILE pushBack(protyle, newRange); /// #endif + if (protyle.breadcrumb) { + const indentElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="indent"]') + const blockElement = hasClosestBlock(newRange.startContainer); + if (indentElement && blockElement) { + const outdentElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="outdent"]'); + if (blockElement.parentElement.classList.contains("li")) { + indentElement.removeAttribute("disabled"); + outdentElement.removeAttribute("disabled"); + } else { + indentElement.setAttribute("disabled", "true"); + outdentElement.setAttribute("disabled", "true"); + } + } + } }, (isMobile() || isInIOS()) ? 520 : 0); // Android/iPad 双击慢了出不来 protyle.hint.enableExtend = false; if (event.shiftKey) { diff --git a/app/src/protyle/wysiwyg/transaction.ts b/app/src/protyle/wysiwyg/transaction.ts index acf163ad3..52556edf4 100644 --- a/app/src/protyle/wysiwyg/transaction.ts +++ b/app/src/protyle/wysiwyg/transaction.ts @@ -1027,9 +1027,9 @@ export const transaction = (protyle: IProtyle, doOperations: IOperation[], undoO protyle.updated = true; if (needDebounce) { - protyle.undo.replace(doOperations); + protyle.undo.replace(doOperations, protyle); } else { - protyle.undo.add(doOperations, undoOperations); + protyle.undo.add(doOperations, undoOperations, protyle); } } if (needDebounce) {