Jelajahi Sumber

:art: fix https://github.com/siyuan-note/siyuan/issues/6804

Vanessa 1 tahun lalu
induk
melakukan
ad6fe1cd6e

+ 9 - 1
app/src/assets/scss/protyle/_protyle.scss

@@ -115,6 +115,15 @@
   min-height: 30px;
   min-height: 30px;
   z-index: 1;
   z-index: 1;
 
 
+  .block__icon {
+    opacity: 1;
+    margin-left: 8px;
+
+    &:disabled {
+        opacity: .38;
+    }
+  }
+
   &__space {
   &__space {
     min-width: 8px;
     min-width: 8px;
     transition: var(--b3-transition);
     transition: var(--b3-transition);
@@ -153,7 +162,6 @@
   }
   }
 
 
   &__icon {
   &__icon {
-    margin-right: 8px;
     opacity: 1;
     opacity: 1;
     border: 0;
     border: 0;
     line-height: 24px;
     line-height: 24px;

+ 1 - 1
app/src/assets/scss/protyle/_wysiwyg.scss

@@ -503,7 +503,7 @@
 
 
   [data-node-id][fold="1"]:not(.li):not([data-type="NodeHeading"]) {
   [data-node-id][fold="1"]:not(.li):not([data-type="NodeHeading"]) {
     @include text-clamp(1);
     @include text-clamp(1);
-    opacity: 0.54;
+    opacity: 0.38;
     font-size: 16px;
     font-size: 16px;
     height: 26px;
     height: 26px;
     line-height: 26px;
     line-height: 26px;

+ 48 - 11
app/src/protyle/breadcrumb/index.ts

@@ -28,8 +28,9 @@ import {Menu} from "../../plugin/Menu";
 import {getNoContainerElement} from "../wysiwyg/getBlock";
 import {getNoContainerElement} from "../wysiwyg/getBlock";
 import {openTitleMenu} from "../header/openTitleMenu";
 import {openTitleMenu} from "../header/openTitleMenu";
 import {emitOpenMenu} from "../../plugin/EventBus";
 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 {resize} from "../util/resize";
+import {listIndent, listOutdent} from "../wysiwyg/list";
 
 
 export class Breadcrumb {
 export class Breadcrumb {
     public element: HTMLElement;
     public element: HTMLElement;
@@ -40,18 +41,25 @@ export class Breadcrumb {
     constructor(protyle: IProtyle) {
     constructor(protyle: IProtyle) {
         const element = document.createElement("div");
         const element = document.createElement("div");
         element.className = "protyle-breadcrumb";
         element.className = "protyle-breadcrumb";
+        let padHTML = ""
+        /// #if BROWSER
+        if (isIPad() || isInAndroid()) {
+            padHTML = `<button class="block__icon fn__flex-center ariaLabel" disabled aria-label="${window.siyuan.languages.undo}" data-type="undo"><svg><use xlink:href="#iconUndo"></use></svg></button>
+<button class="block__icon fn__flex-center ariaLabel" disabled aria-label="${window.siyuan.languages.redo}" data-type="redo"><svg><use xlink:href="#iconRedo"></use></svg></button>
+<button class="block__icon fn__flex-center ariaLabel" disabled aria-label="${window.siyuan.languages.outdent}" data-type="outdent"><svg><use xlink:href="#iconOutdent"></use></svg></button>
+<button class="block__icon fn__flex-center ariaLabel" disabled aria-label="${window.siyuan.languages.indent}" data-type="indent"><svg><use xlink:href="#iconIndent"></use></svg></button>`;
+        }
+        /// #endif
         element.innerHTML = `${isMobile() ?
         element.innerHTML = `${isMobile() ?
             `<button class="protyle-breadcrumb__icon" data-type="mobile-menu">${window.siyuan.languages.breadcrumb}</button>` :
             `<button class="protyle-breadcrumb__icon" data-type="mobile-menu">${window.siyuan.languages.breadcrumb}</button>` :
             '<div class="protyle-breadcrumb__bar"></div>'}
             '<div class="protyle-breadcrumb__bar"></div>'}
 <span class="protyle-breadcrumb__space"></span>
 <span class="protyle-breadcrumb__space"></span>
 <button class="protyle-breadcrumb__icon fn__none ariaLabel" aria-label="${updateHotkeyTip(window.siyuan.config.keymap.editor.general.exitFocus.custom)}" data-type="exit-focus">${window.siyuan.languages.exitFocus}</button>
 <button class="protyle-breadcrumb__icon fn__none ariaLabel" aria-label="${updateHotkeyTip(window.siyuan.config.keymap.editor.general.exitFocus.custom)}" data-type="exit-focus">${window.siyuan.languages.exitFocus}</button>
-<button class="block__icon block__icon--show fn__flex-center ariaLabel${window.siyuan.config.readonly ? " fn__none" : ""}" aria-label="${window.siyuan.languages.lockEdit}" data-type="readonly"><svg><use xlink:href="#iconUnlock"></use></svg></button>
-<span class="fn__space${window.siyuan.config.readonly ? " fn__none" : ""}"></span>
-<button class="block__icon block__icon--show fn__flex-center ariaLabel" data-type="doc" aria-label="${isMac() ? window.siyuan.languages.gutterTip2 : window.siyuan.languages.gutterTip2.replace("⇧", "Shift+")}"><svg><use xlink:href="#iconFile"></use></svg></button>
-<span class="fn__space"></span>
-<button class="block__icon block__icon--show fn__flex-center ariaLabel" data-type="more" aria-label="${window.siyuan.languages.more}"><svg><use xlink:href="#iconMore"></use></svg></button>
-<button class="block__icon block__icon--show fn__flex-center fn__none ariaLabel" style="margin-left: 8px" data-type="context" aria-label="${window.siyuan.languages.context}"><svg><use xlink:href="#iconAlignCenter"></use></svg></button>`;
-
+${padHTML}
+<button class="block__icon fn__flex-center ariaLabel${window.siyuan.config.readonly ? " fn__none" : ""}" aria-label="${window.siyuan.languages.lockEdit}" data-type="readonly"><svg><use xlink:href="#iconUnlock"></use></svg></button>
+<button class="block__icon fn__flex-center ariaLabel" data-type="doc" aria-label="${isMac() ? window.siyuan.languages.gutterTip2 : window.siyuan.languages.gutterTip2.replace("⇧", "Shift+")}"><svg><use xlink:href="#iconFile"></use></svg></button>
+<button class="block__icon fn__flex-center ariaLabel" data-type="more" aria-label="${window.siyuan.languages.more}"><svg><use xlink:href="#iconMore"></use></svg></button>
+<button class="block__icon fn__flex-center fn__none ariaLabel" data-type="context" aria-label="${window.siyuan.languages.context}"><svg><use xlink:href="#iconAlignCenter"></use></svg></button>`;
         this.element = element.firstElementChild as HTMLElement;
         this.element = element.firstElementChild as HTMLElement;
         element.addEventListener("click", (event) => {
         element.addEventListener("click", (event) => {
             /// #if !MOBILE
             /// #if !MOBILE
@@ -133,6 +141,36 @@ export class Breadcrumb {
                         target.classList.add("block__icon--active");
                         target.classList.add("block__icon--active");
                     }
                     }
                     break;
                     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;
                 target = target.parentElement;
             }
             }
@@ -528,9 +566,7 @@ export class Breadcrumb {
     }
     }
 
 
     public render(protyle: IProtyle, update = false) {
     public render(protyle: IProtyle, update = false) {
-        if (isMobile()) {
-            return;
-        }
+        /// #if !MOBILE
         let range: Range;
         let range: Range;
         let blockElement: Element;
         let blockElement: Element;
         if (getSelection().rangeCount > 0) {
         if (getSelection().rangeCount > 0) {
@@ -614,6 +650,7 @@ export class Breadcrumb {
                 this.element.scrollLeft = (this.element.lastElementChild as HTMLElement).offsetLeft - this.element.clientWidth + 14;
                 this.element.scrollLeft = (this.element.lastElementChild as HTMLElement).offsetLeft - this.element.clientWidth + 14;
             }
             }
         });
         });
+        /// #endif
     }
     }
 
 
     public hide() {
     public hide() {

+ 1 - 1
app/src/protyle/gutter/index.ts

@@ -442,7 +442,7 @@ export class Gutter {
                         }], [{
                         }], [{
                             action: "foldHeading",
                             action: "foldHeading",
                             id: itemId
                             id: itemId
-                        }]);
+                        }], options.protyle);
                         item.insertAdjacentHTML("afterend", response.data[0].doOperations[0].retData);
                         item.insertAdjacentHTML("afterend", response.data[0].doOperations[0].retData);
                     }
                     }
                 }
                 }

+ 34 - 5
app/src/protyle/undo/index.ts

@@ -4,7 +4,7 @@ import {Constants} from "../../constants";
 import {hideElements} from "../ui/hideElements";
 import {hideElements} from "../ui/hideElements";
 import {scrollCenter} from "../../util/highlightById";
 import {scrollCenter} from "../../util/highlightById";
 import {matchHotKey} from "../util/hotKey";
 import {matchHotKey} from "../util/hotKey";
-import { ipcRenderer } from "electron";
+import {ipcRenderer} from "electron";
 
 
 interface IOperations {
 interface IOperations {
     doOperations: IOperation[],
     doOperations: IOperation[],
@@ -32,9 +32,17 @@ export class Undo {
         this.render(protyle, state, false);
         this.render(protyle, state, false);
         this.hasUndo = true;
         this.hasUndo = true;
         this.redoStack.push(state);
         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) {
     public redo(protyle: IProtyle) {
         if (protyle.disabled) {
         if (protyle.disabled) {
             return;
             return;
@@ -45,6 +53,15 @@ export class Undo {
         const state = this.redoStack.pop();
         const state = this.redoStack.pop();
         this.render(protyle, state, true);
         this.render(protyle, state, true);
         this.undoStack.push(state);
         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) {
     private render(protyle: IProtyle, state: IOperations, redo: boolean) {
@@ -52,7 +69,7 @@ export class Undo {
         protyle.wysiwyg.lastHTMLs = {};
         protyle.wysiwyg.lastHTMLs = {};
         if (!redo) {
         if (!redo) {
             state.undoOperations.forEach(item => {
             state.undoOperations.forEach(item => {
-               onTransaction(protyle, item, true);
+                onTransaction(protyle, item, true);
             });
             });
             transaction(protyle, state.undoOperations);
             transaction(protyle, state.undoOperations);
         } else {
         } else {
@@ -65,19 +82,25 @@ export class Undo {
         scrollCenter(protyle);
         scrollCenter(protyle);
     }
     }
 
 
-    public replace(doOperations: IOperation[]) {
+    public replace(doOperations: IOperation[], protyle: IProtyle) {
         // undo 引发 replace 导致 stack 错误 https://github.com/siyuan-note/siyuan/issues/9178
         // undo 引发 replace 导致 stack 错误 https://github.com/siyuan-note/siyuan/issues/9178
         if (this.hasUndo && this.redoStack.length > 0) {
         if (this.hasUndo && this.redoStack.length > 0) {
             this.undoStack.push(this.redoStack.pop());
             this.undoStack.push(this.redoStack.pop());
             this.redoStack = [];
             this.redoStack = [];
             this.hasUndo = false;
             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) {
         if (this.undoStack.length > 0) {
             this.undoStack[this.undoStack.length - 1].doOperations = doOperations;
             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});
         this.undoStack.push({undoOperations, doOperations});
         if (this.undoStack.length > Constants.SIZE_UNDO) {
         if (this.undoStack.length > Constants.SIZE_UNDO) {
             this.undoStack.shift();
             this.undoStack.shift();
@@ -86,6 +109,12 @@ export class Undo {
             this.redoStack = [];
             this.redoStack = [];
             this.hasUndo = false;
             this.hasUndo = false;
         }
         }
+        if (protyle.breadcrumb) {
+            const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]')
+            if (undoElement) {
+                undoElement.removeAttribute("disabled");
+            }
+        }
     }
     }
 
 
     public clear() {
     public clear() {

+ 14 - 0
app/src/protyle/util/onGet.ts

@@ -314,6 +314,13 @@ export const disabledProtyle = (protyle: IProtyle) => {
     if (protyle.breadcrumb) {
     if (protyle.breadcrumb) {
         protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"] use').setAttribute("xlink:href", "#iconLock");
         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);
         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();
     hideTooltip();
 };
 };
@@ -357,6 +364,13 @@ export const enableProtyle = (protyle: IProtyle) => {
     if (protyle.breadcrumb) {
     if (protyle.breadcrumb) {
         protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"] use').setAttribute("xlink:href", "#iconUnlock");
         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);
         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();
     hideTooltip();
 };
 };

+ 27 - 0
app/src/protyle/wysiwyg/index.ts

@@ -1823,6 +1823,19 @@ export class WYSIWYG {
                     if (range.toString() === "") {
                     if (range.toString() === "") {
                         countSelectWord(range, protyle.block.rootID);
                         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();
                 event.stopPropagation();
             }
             }
@@ -2351,6 +2364,20 @@ export class WYSIWYG {
                 /// #if !MOBILE
                 /// #if !MOBILE
                 pushBack(protyle, newRange);
                 pushBack(protyle, newRange);
                 /// #endif
                 /// #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 双击慢了出不来
             }, (isMobile() || isInIOS()) ? 520 : 0); // Android/iPad 双击慢了出不来
             protyle.hint.enableExtend = false;
             protyle.hint.enableExtend = false;
             if (event.shiftKey) {
             if (event.shiftKey) {

+ 2 - 2
app/src/protyle/wysiwyg/transaction.ts

@@ -1027,9 +1027,9 @@ export const transaction = (protyle: IProtyle, doOperations: IOperation[], undoO
         protyle.updated = true;
         protyle.updated = true;
 
 
         if (needDebounce) {
         if (needDebounce) {
-            protyle.undo.replace(doOperations);
+            protyle.undo.replace(doOperations, protyle);
         } else {
         } else {
-            protyle.undo.add(doOperations, undoOperations);
+            protyle.undo.add(doOperations, undoOperations, protyle);
         }
         }
     }
     }
     if (needDebounce) {
     if (needDebounce) {