This commit is contained in:
Vanessa 2024-02-02 11:52:53 +08:00
parent 0dd00d2138
commit ad6fe1cd6e
8 changed files with 136 additions and 21 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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 = `<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() ?
`<button class="protyle-breadcrumb__icon" data-type="mobile-menu">${window.siyuan.languages.breadcrumb}</button>` :
'<div class="protyle-breadcrumb__bar"></div>'}
<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="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;
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() {

View file

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

View file

@ -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() {

View file

@ -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();
};

View file

@ -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) {

View file

@ -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) {