diff --git a/app/src/assets/scss/business/_history.scss b/app/src/assets/scss/business/_history.scss index e22c76aaf..905d7d603 100644 --- a/app/src/assets/scss/business/_history.scss +++ b/app/src/assets/scss/business/_history.scss @@ -26,14 +26,15 @@ } &__diff { - width: 200px; + width: 256px; border-right: 1px solid var(--b3-border-color); padding: 8px 0; overflow: auto; + user-select: none; } - &__panel > .b3-list, - &__panel > .history__diff { + &__panel > .b3-list { width: 256px; + user-select: none; } } diff --git a/app/src/boot/globalEvent/keydown.ts b/app/src/boot/globalEvent/keydown.ts index a649d3746..3bdacc621 100644 --- a/app/src/boot/globalEvent/keydown.ts +++ b/app/src/boot/globalEvent/keydown.ts @@ -73,6 +73,7 @@ import {copyPNG} from "../../menus/util"; import {getContentByInlineHTML} from "../../protyle/wysiwyg/keydown"; import {searchKeydown} from "./searchKeydown"; import {openNewWindow} from "../../window/openNewWindow"; +import {historyKeydown} from "../../history/keydown"; const switchDialogEvent = (app: App, event: MouseEvent) => { event.preventDefault(); @@ -1064,6 +1065,7 @@ export const windowKeyDown = (app: App, event: KeyboardEvent) => { if (isNotCtrl(event) && event.key !== "Escape" && !event.shiftKey && !event.altKey && Constants.KEYCODELIST[event.keyCode] !== "PageUp" && Constants.KEYCODELIST[event.keyCode] !== "PageDown" && + event.key !== "Home" && event.key !== "End" && !/^F\d{1,2}$/.test(event.key) && event.key.indexOf("Arrow") === -1 && event.key !== "Enter" && event.key !== "Backspace" && event.key !== "Delete") { return; } @@ -1213,14 +1215,20 @@ export const windowKeyDown = (app: App, event: KeyboardEvent) => { return; } - if (event.key === "ArrowUp" || event.key === "ArrowDown") { - const viewCardsDialog = window.siyuan.dialogs.find(item => { - if (item.element.getAttribute("data-key") === Constants.DIALOG_VIEWCARDS) { - return true; + if (["Home", "End", "ArrowUp", "ArrowDown"].includes(event.key)) { + let matchDialog: Dialog; + // 需找到最顶层的,因此不能用 find + window.siyuan.dialogs.forEach(item => { + if ([Constants.DIALOG_VIEWCARDS, Constants.DIALOG_HISTORYCOMPARE].includes(item.element.getAttribute("data-key"))) { + matchDialog = item; } }); - if (viewCardsDialog) { - viewCardsDialog.element.dispatchEvent(new CustomEvent("click", {detail: event.key.toLowerCase()})); + if (matchDialog) { + if (matchDialog.element.getAttribute("data-key") === Constants.DIALOG_VIEWCARDS) { + matchDialog.element.dispatchEvent(new CustomEvent("click", {detail: event.key.toLowerCase()})); + } else if (matchDialog.element.getAttribute("data-key") === Constants.DIALOG_HISTORYCOMPARE) { + historyKeydown(event, matchDialog); + } event.preventDefault(); return; } diff --git a/app/src/card/viewCards.ts b/app/src/card/viewCards.ts index e49e9a644..9dfdf7584 100644 --- a/app/src/card/viewCards.ts +++ b/app/src/card/viewCards.ts @@ -89,6 +89,10 @@ export const viewCards = (app: App, deckID: string, title: string, deckType: "Tr currentElement = currentElement.previousElementSibling || currentElement.parentElement.lastElementChild; } else if (event.detail === "arrowdown") { currentElement = currentElement.nextElementSibling || currentElement.parentElement.firstElementChild; + } else if (event.detail === "home") { + currentElement = currentElement.parentElement.firstElementChild; + } else if (event.detail === "end") { + currentElement = currentElement.parentElement.lastElementChild; } const currentRect = currentElement.getBoundingClientRect(); const parentRect = currentElement.parentElement.getBoundingClientRect(); @@ -167,16 +171,16 @@ export const viewCards = (app: App, deckID: string, title: string, deckType: "Tr } else if (type === "resetAll") { confirmDialog(window.siyuan.languages.reset, window.siyuan.languages.resetCardTip.replace("${x}", dialog.element.querySelector(".counter").textContent), () => { - fetchPost("/api/riff/resetRiffCards", { - type: deckType === "" ? "deck" : deckType.toLowerCase(), - deckID: deckType === "" ? deckID : Constants.QUICK_DECK_ID, - id: deckID, - }, () => { - dialog.element.querySelectorAll(".ariaLabel.b3-list-item__meta").forEach(item => { - item.textContent = dayjs().format("YYYY-MM-DD"); + fetchPost("/api/riff/resetRiffCards", { + type: deckType === "" ? "deck" : deckType.toLowerCase(), + deckID: deckType === "" ? deckID : Constants.QUICK_DECK_ID, + id: deckID, + }, () => { + dialog.element.querySelectorAll(".ariaLabel.b3-list-item__meta").forEach(item => { + item.textContent = dayjs().format("YYYY-MM-DD"); + }); }); }); - }); event.stopPropagation(); event.preventDefault(); break; diff --git a/app/src/constants.ts b/app/src/constants.ts index 3179a4312..71adfa5b8 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -114,6 +114,7 @@ export abstract class Constants { public static readonly DIALOG_SEARCH = "dialog-search"; public static readonly DIALOG_REPLACE = "dialog-replace"; public static readonly DIALOG_GLOBALSEARCH = "dialog-globalsearch"; + public static readonly DIALOG_HISTORYCOMPARE = "dialog-historycompare"; // timeout public static readonly TIMEOUT_DBLCLICK = 190; diff --git a/app/src/history/diff.ts b/app/src/history/diff.ts index 180bf9f52..82e6a51b0 100644 --- a/app/src/history/diff.ts +++ b/app/src/history/diff.ts @@ -161,7 +161,14 @@ export const showDiff = (app: App, data: { id: string, time: string }[]) => { rightEditor = undefined; } }); + dialog.element.setAttribute("data-key", Constants.DIALOG_HISTORYCOMPARE) dialog.element.addEventListener("click", (event) => { + if (typeof event.detail === "string") { + renderCompare(app, dialog.element.querySelector(".history__diff .b3-list-item--focus")); + event.stopPropagation(); + event.preventDefault(); + return; + } let target = event.target as HTMLElement; while (target && target !== dialog.element) { if (target.classList.contains("b3-list-item") && !target.dataset.id) { diff --git a/app/src/history/keydown.ts b/app/src/history/keydown.ts new file mode 100644 index 000000000..53d0120f9 --- /dev/null +++ b/app/src/history/keydown.ts @@ -0,0 +1,52 @@ +import {Dialog} from "../dialog"; + +export const historyKeydown = (event: KeyboardEvent, dialog: Dialog) => { + let currentItem = dialog.element.querySelector(".history__diff .b3-list-item--focus") + const items = Array.from(dialog.element.querySelectorAll(".history__diff .b3-list-item[data-id]")) + if (items.length < 2) { + return; + } + if (!currentItem) { + currentItem = items[0]; + } else { + currentItem.classList.remove("b3-list-item--focus"); + if (event.key === "Home") { + currentItem = items[0] + } else if (event.key === "End") { + currentItem = items[items.length - 1] + } else { + items.find((item, index) => { + if (item.isSameNode(currentItem)) { + if (event.key === "ArrowUp") { + if (index === 0) { + currentItem = items[items.length - 1]; + } else { + currentItem = items[index - 1]; + } + } else if (event.key === "ArrowDown") { + if (index === items.length - 1) { + currentItem = items[0]; + } else { + currentItem = items[index + 1]; + } + } + return true; + } + }) + } + } + currentItem.classList.add("b3-list-item--focus"); + if (currentItem.parentElement.classList.contains("fn__none")) { + currentItem.parentElement.classList.remove("fn__none"); + currentItem.parentElement.previousElementSibling.querySelector("svg").classList.add("b3-list-item__arrow--open"); + } + const currentItemRect = currentItem.getBoundingClientRect(); + const historyDiffElement = dialog.element.querySelector(".history__diff"); + const historyDiffRect = historyDiffElement.getBoundingClientRect(); + if (currentItemRect.bottom > historyDiffRect.bottom) { + currentItem.scrollIntoView(false) + } else if (currentItemRect.top < historyDiffRect.top) { + currentItem.scrollIntoView() + } + dialog.element.dispatchEvent(new CustomEvent("click", {detail: event.key.toLowerCase()})); +}