diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index 0a258c8a7..e4326a260 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -1928,10 +1928,10 @@ packages: resolution: {integrity: sha512-m0+M53+HYMzqKxwNQZT143K7WwXEGUy9LY31l8dJphXx2P/FQod615mVbxHyqbDCG4J5bHdWm21qZ0e2DVY6CQ==} engines: {node: '>=14.0.0'} dependencies: - 7zip-bin: 5.1.1 '@develar/schema-utils': 2.6.5 '@electron/universal': 1.2.1 '@malept/flatpak-bundler': 0.4.0 + 7zip-bin: 5.1.1 async-exit-hook: 2.0.1 bluebird-lst: 1.0.9 builder-util: 23.3.3 @@ -2215,9 +2215,9 @@ packages: /builder-util/23.3.3: resolution: {integrity: sha512-MJZlUiq2PY5hjYv9+XNaoYdsITqvLgRDoHSFg/4nzpInbNxNjLQOolL04Zsyp+hgfcbFvMC4h0KkR1CMPHLWbA==} dependencies: - 7zip-bin: 5.1.1 '@types/debug': 4.1.7 '@types/fs-extra': 9.0.13 + 7zip-bin: 5.1.1 app-builder-bin: 4.0.0 bluebird-lst: 1.0.9 builder-util-runtime: 9.0.3 diff --git a/app/src/layout/dock/Backlink.ts b/app/src/layout/dock/Backlink.ts index a62586336..f17af1b2c 100644 --- a/app/src/layout/dock/Backlink.ts +++ b/app/src/layout/dock/Backlink.ts @@ -15,9 +15,9 @@ export class Backlink extends Model { public type: "pin" | "local"; public blockId: string; public rootId: string; // "local" 必传 - private tree: Tree; + public tree: Tree; private notebookId: string; - private mTree: Tree; + public mTree: Tree; public editors: Protyle[] = []; public status: { [key: string]: { @@ -143,12 +143,15 @@ export class Backlink extends Model { data: null, click: (element) => { this.toggleItem(element, false); + this.setFocus(); + this.mTree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); }, ctrlClick(element) { openFileById({ id: element.getAttribute("data-node-id"), action: [Constants.CB_GET_CONTEXT] }); + this.mTree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); }, altClick(element) { openFileById({ @@ -156,6 +159,7 @@ export class Backlink extends Model { position: "right", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); + this.mTree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); }, shiftClick(element) { openFileById({ @@ -163,9 +167,12 @@ export class Backlink extends Model { position: "bottom", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); + this.mTree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); }, toggleClick: (liElement) => { this.toggleItem(liElement, false); + this.setFocus(); + this.mTree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); } }); this.mTree = new Tree({ @@ -173,12 +180,15 @@ export class Backlink extends Model { data: null, click: (element) => { this.toggleItem(element, true); + this.setFocus(); + this.tree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); }, ctrlClick(element) { openFileById({ id: element.getAttribute("data-node-id"), action: [Constants.CB_GET_CONTEXT] }); + this.tree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); }, altClick(element) { openFileById({ @@ -186,6 +196,7 @@ export class Backlink extends Model { position: "right", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); + this.tree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); }, shiftClick(element) { openFileById({ @@ -193,9 +204,12 @@ export class Backlink extends Model { position: "bottom", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); + this.tree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); }, toggleClick: (liElement) => { this.toggleItem(liElement, true); + this.setFocus(); + this.tree.element.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus"); }, blockExtHTML: `` }); @@ -227,11 +241,7 @@ export class Backlink extends Model { }); }); this.element.addEventListener("click", (event) => { - if (this.type === "local") { - setPanelFocus(this.element.parentElement.parentElement); - } else { - setPanelFocus(this.element); - } + this.setFocus(); let target = event.target as HTMLElement; while (target && !target.isEqualNode(this.element)) { if (target.classList.contains("block__icon") && target.parentElement.parentElement.isSameNode(this.element)) { @@ -299,6 +309,14 @@ export class Backlink extends Model { } } + private setFocus() { + if (this.type === "local") { + setPanelFocus(this.element.parentElement.parentElement); + } else { + setPanelFocus(this.element); + } + } + private showSortMenu(type: string, sort: string) { const clickEvent = (currentSort: string) => { (type === "sort" ? this.tree : this.mTree).element.previousElementSibling.querySelector(`[data-type="${type}"]`).setAttribute("data-sort", currentSort); diff --git a/app/src/layout/dock/Bookmark.ts b/app/src/layout/dock/Bookmark.ts index 93c007ad8..e399c8a5b 100644 --- a/app/src/layout/dock/Bookmark.ts +++ b/app/src/layout/dock/Bookmark.ts @@ -13,7 +13,7 @@ import {escapeHtml} from "../../util/escape"; export class Bookmark extends Model { private openNodes: string[]; - private tree: Tree; + public tree: Tree; private element: Element; constructor(tab: Tab) { diff --git a/app/src/layout/dock/Outline.ts b/app/src/layout/dock/Outline.ts index bccb573ed..7bfeaa324 100644 --- a/app/src/layout/dock/Outline.ts +++ b/app/src/layout/dock/Outline.ts @@ -13,7 +13,7 @@ import {unicode2Emoji} from "../../emoji"; import {onGet} from "../../protyle/util/onGet"; export class Outline extends Model { - private tree: Tree; + public tree: Tree; public element: HTMLElement; public headerElement: HTMLElement; public type: "pin" | "local"; diff --git a/app/src/layout/dock/Tag.ts b/app/src/layout/dock/Tag.ts index 56b7b6c95..a8c486cc6 100644 --- a/app/src/layout/dock/Tag.ts +++ b/app/src/layout/dock/Tag.ts @@ -12,7 +12,7 @@ import {escapeHtml} from "../../util/escape"; export class Tag extends Model { private openNodes: string[]; - private tree: Tree; + public tree: Tree; private element: Element; constructor(tab: Tab) { diff --git a/app/src/layout/util.ts b/app/src/layout/util.ts index 5da8ffa6e..da2f18397 100644 --- a/app/src/layout/util.ts +++ b/app/src/layout/util.ts @@ -541,7 +541,7 @@ export const copyTab = (tab: Tab) => { }); }; -export const getInstanceById = (id: string) => { +export const getInstanceById = (id: string, layout =window.siyuan.layout.centerLayout) => { const _getInstanceById = (item: Layout | Wnd, id: string) => { if (item.id === id) { return item; @@ -557,7 +557,7 @@ export const getInstanceById = (id: string) => { } } }; - return _getInstanceById(window.siyuan.layout.centerLayout, id); + return _getInstanceById(layout, id); }; export const addResize = (obj: Layout | Wnd) => { diff --git a/app/src/protyle/gutter/index.ts b/app/src/protyle/gutter/index.ts index 1c8e9c48e..1ae0ccf17 100644 --- a/app/src/protyle/gutter/index.ts +++ b/app/src/protyle/gutter/index.ts @@ -1842,7 +1842,8 @@ export class Gutter { this.element.style.left = `${left}px`; if (left < this.element.parentElement.getBoundingClientRect().left) { this.element.style.width = "24px"; - this.element.style.left = `${rect.left - this.element.clientWidth - space / 2}px`; + // 需加 2,否则和折叠标题无法对齐 + this.element.style.left = `${rect.left - this.element.clientWidth - space / 2 + 3}px`; html = ""; Array.from(this.element.children).reverse().forEach((item, index) => { if (index !== 0) { diff --git a/app/src/search/util.ts b/app/src/search/util.ts index 7064e1c09..30e539f33 100644 --- a/app/src/search/util.ts +++ b/app/src/search/util.ts @@ -1,5 +1,5 @@ import {getAllModels} from "../layout/getAll"; -import {getInstanceById, getWndByLayout, resizeTabs} from "../layout/util"; +import {getInstanceById, getWndByLayout, resizeTabs, setPanelFocus} from "../layout/util"; import {Tab} from "../layout/Tab"; import {Search} from "./index"; import {Wnd} from "../layout/Wnd"; @@ -78,6 +78,7 @@ export const openGlobalSearch = (text: string, replace: boolean) => { } }); wnd.split("lr").addTab(tab); + setPanelFocus(tab.panelElement); }; export const genSearch = (config: ISearchOption, element: Element, closeCB?: () => void) => { diff --git a/app/src/util/Tree.ts b/app/src/util/Tree.ts index fe092b8d5..59729968b 100644 --- a/app/src/util/Tree.ts +++ b/app/src/util/Tree.ts @@ -11,10 +11,9 @@ export class Tree { private blockExtHTML: string; private topExtHTML: string; - private click: (element: HTMLElement, event: MouseEvent) => void; - + public click: (element: Element, event?: MouseEvent) => void; private ctrlClick: (element: HTMLElement) => void; - private toggleClick: (element: HTMLElement) => void; + private toggleClick: (element: Element) => void; private shiftClick: (element: HTMLElement) => void; private altClick: (element: HTMLElement) => void; private rightClick: (element: HTMLElement, event: MouseEvent) => void; @@ -83,8 +82,8 @@ data-treetype="${item.type}" data-type="${item.nodeType}" data-subtype="${item.subType}" ${item.label ? "data-label='" + item.label + "'" : ""}> - - + + ${iconHTML} ${item.name} @@ -129,8 +128,8 @@ data-type="${item.type}" data-subtype="${item.subType}" data-treetype="${type}" data-def-path="${item.defPath}"> - - + + ${iconHTML} ${item.content} @@ -144,7 +143,7 @@ data-def-path="${item.defPath}"> return html; } - private toggleBlocks(liElement: HTMLElement) { + public toggleBlocks(liElement: Element) { if (this.toggleClick) { this.toggleClick(liElement); return; @@ -194,7 +193,7 @@ data-def-path="${item.defPath}"> this.element.addEventListener("click", (event: MouseEvent & { target: HTMLElement }) => { let target = event.target as HTMLElement; while (target && !target.isEqualNode(this.element)) { - if (target.classList.contains("b3-list-item__toggle") && !target.firstElementChild.classList.contains("fn__hidden")) { + if (target.classList.contains("b3-list-item__toggle") && !target.classList.contains("fn__hidden")) { this.toggleBlocks(target.parentElement); this.setCurrent(target.parentElement); event.preventDefault(); diff --git a/app/src/util/globalShortcut.ts b/app/src/util/globalShortcut.ts index e854009e0..732d9f30d 100644 --- a/app/src/util/globalShortcut.ts +++ b/app/src/util/globalShortcut.ts @@ -42,6 +42,8 @@ import {getStartEndElement} from "../protyle/wysiwyg/commonHotkey"; import {getNextFileLi, getPreviousFileLi} from "../protyle/wysiwyg/getBlock"; import {editor} from "../config/editor"; import {hintMoveBlock} from "../protyle/hint/extend"; +import {Outline} from "../layout/dock/Outline"; +import {Backlink} from "../layout/dock/Backlink"; const getRightBlock = (element: HTMLElement, x: number, y: number) => { let index = 1; @@ -599,33 +601,6 @@ export const globalShortcut = () => { return; } - // 面板折叠展开操作 - if (!event.repeat && (matchHotKey(window.siyuan.config.keymap.editor.general.collapse.custom, event) || matchHotKey(window.siyuan.config.keymap.editor.general.expand.custom, event))) { - let activePanelElement = document.querySelector(".layout__tab--active"); - if (!activePanelElement) { - Array.from(document.querySelectorAll(".layout__wnd--active .layout-tab-container > div")).forEach(item => { - if (!item.classList.contains("fn__none")) { - activePanelElement = item; - return true; - } - }); - } - if (activePanelElement) { - if (matchHotKey(window.siyuan.config.keymap.editor.general.collapse.custom, event)) { - if (activePanelElement.querySelector('.block__icon[data-type="collapse"]')) { - activePanelElement.querySelector('.block__icon[data-type="collapse"]').dispatchEvent(new CustomEvent("click")); - } - } else if (matchHotKey(window.siyuan.config.keymap.editor.general.expand.custom, event)) { - if (activePanelElement.querySelector('.block__icon[data-type="expand"]')) { - activePanelElement.querySelector('.block__icon[data-type="expand"]').dispatchEvent(new CustomEvent("click")); - } - } - } - event.stopPropagation(); - event.preventDefault(); - return; - } - // close tab if (matchHotKey(window.siyuan.config.keymap.general.closeTab.custom, event) && !event.repeat) { event.preventDefault(); @@ -680,6 +655,11 @@ export const globalShortcut = () => { return; } + // 面板的操作 + if (panelTreeKeydown(event)) { + return; + } + let searchKey = ""; if (matchHotKey(window.siyuan.config.keymap.general.replace.custom, event)) { searchKey = window.siyuan.config.keymap.general.replace.custom; @@ -1147,3 +1127,169 @@ const fileTreeKeydown = (event: KeyboardEvent) => { return true; } }; + +const panelTreeKeydown = (event: KeyboardEvent) => { + // 面板折叠展开操作 + if (!matchHotKey(window.siyuan.config.keymap.editor.general.collapse.custom, event) && + !matchHotKey(window.siyuan.config.keymap.editor.general.expand.custom, event) && + !event.key.startsWith("Arrow") && event.key !== "Enter") { + return false; + } + let activePanelElement = document.querySelector(".layout__tab--active"); + if (!activePanelElement) { + Array.from(document.querySelectorAll(".layout__wnd--active .layout-tab-container > div")).find(item => { + if (!item.classList.contains("fn__none") && item.className.indexOf("sy__") > -1) { + activePanelElement = item; + return true; + } + }); + } + if (!activePanelElement) { + return false + } + if (activePanelElement.className.indexOf("sy__") === -1) { + return false; + } + if (!event.repeat && matchHotKey(window.siyuan.config.keymap.editor.general.collapse.custom, event)) { + const collapseElement = activePanelElement.querySelector('.block__icon[data-type="collapse"]'); + if (collapseElement) { + collapseElement.dispatchEvent(new CustomEvent("click")); + event.preventDefault(); + return true; + } + } + if (!event.repeat && matchHotKey(window.siyuan.config.keymap.editor.general.expand.custom, event)) { + const expandElement = activePanelElement.querySelector('.block__icon[data-type="expand"]'); + if (expandElement) { + expandElement.dispatchEvent(new CustomEvent("click")); + event.preventDefault(); + return true; + } + } + if (activePanelElement.classList.contains("sy__inbox") || + activePanelElement.classList.contains("sy__globalGraph") || + activePanelElement.classList.contains("sy__graph")) { + return false; + } + const model = (getInstanceById(activePanelElement.getAttribute("data-id"), window.siyuan.layout.layout) as Tab)?.model; + if (!model) { + return false; + } + let activeItemElement = activePanelElement.querySelector(".b3-list-item--focus") + if (!activeItemElement) { + activeItemElement = activePanelElement.querySelector(".b3-list .b3-list-item"); + if (activeItemElement) { + activeItemElement.classList.add("b3-list-item--focus") + } + return false; + } + + let tree = (model as Backlink).tree + if (activeItemElement.parentElement.parentElement.classList.contains("backlinkMList")) { + tree = (model as Backlink).mTree + } + if (event.key === "Enter") { + tree.click(activeItemElement); + event.preventDefault(); + return true; + } + const arrowElement = activeItemElement.querySelector(".b3-list-item__arrow") + if ((event.key === "ArrowRight" && !arrowElement.classList.contains("b3-list-item__arrow--open") && !arrowElement.parentElement.classList.contains("fn__hidden")) || + (event.key === "ArrowLeft" && arrowElement.classList.contains("b3-list-item__arrow--open") && !arrowElement.parentElement.classList.contains("fn__hidden"))) { + tree.toggleBlocks(activeItemElement); + event.preventDefault(); + return true; + } + const ulElement = hasClosestByClassName(activeItemElement, "b3-list"); + if (!ulElement) { + return false; + } + if (event.key === "ArrowLeft") { + let parentElement = activeItemElement.parentElement.previousElementSibling; + if (parentElement) { + if (parentElement.tagName !== "LI") { + parentElement = ulElement.querySelector(".b3-list-item"); + } + activeItemElement.classList.remove("b3-list-item--focus"); + parentElement.classList.add("b3-list-item--focus"); + const parentRect = parentElement.getBoundingClientRect(); + const scrollRect = ulElement.parentElement.getBoundingClientRect(); + if (parentRect.top < scrollRect.top || parentRect.bottom > scrollRect.bottom) { + parentElement.scrollIntoView(parentRect.top < scrollRect.top); + } + } + event.preventDefault(); + return true; + } + if (event.key === "ArrowDown" || event.key === "ArrowRight") { + let nextElement = activeItemElement; + while (nextElement) { + if (nextElement.nextElementSibling) { + if (nextElement.nextElementSibling.tagName === "UL") { + nextElement = nextElement.nextElementSibling.firstElementChild; + } else if (nextElement.nextElementSibling.classList.contains("protyle")) { + if (nextElement.nextElementSibling.nextElementSibling) { + nextElement = nextElement.nextElementSibling.nextElementSibling; + } + } else { + nextElement = nextElement.nextElementSibling; + } + break; + } else { + if (nextElement.parentElement.classList.contains("fn__flex-1")) { + break; + } else { + nextElement = nextElement.parentElement; + } + } + } + if (nextElement.classList.contains("b3-list-item") && !nextElement.classList.contains("b3-list-item--focus")) { + activeItemElement.classList.remove("b3-list-item--focus"); + nextElement.classList.add("b3-list-item--focus"); + const nextRect = nextElement.getBoundingClientRect(); + const scrollRect = ulElement.parentElement.getBoundingClientRect(); + if (nextRect.top < scrollRect.top || nextRect.bottom > scrollRect.bottom) { + nextElement.scrollIntoView(nextRect.top < scrollRect.top); + } + } + event.preventDefault(); + return true; + } + if (event.key === "ArrowUp") { + let previousElement = activeItemElement; + while (previousElement) { + if (previousElement.previousElementSibling) { + if (previousElement.previousElementSibling.tagName === "LI") { + previousElement = previousElement.previousElementSibling; + } else if (previousElement.previousElementSibling.classList.contains("protyle")) { + if (previousElement.previousElementSibling.previousElementSibling) { + previousElement = previousElement.previousElementSibling.previousElementSibling; + } + } else { + const liElements = previousElement.previousElementSibling.querySelectorAll(".b3-list-item"); + previousElement = liElements[liElements.length - 1]; + } + break; + } else { + if (previousElement.parentElement.classList.contains("fn__flex-1")) { + break; + } else { + previousElement = previousElement.parentElement; + } + } + } + if (previousElement.classList.contains("b3-list-item") && !previousElement.classList.contains("b3-list-item--focus")) { + activeItemElement.classList.remove("b3-list-item--focus"); + previousElement.classList.add("b3-list-item--focus"); + const previousRect = previousElement.getBoundingClientRect(); + const scrollRect = ulElement.parentElement.getBoundingClientRect(); + if (previousRect.top < scrollRect.top || previousRect.bottom > scrollRect.bottom) { + previousElement.scrollIntoView(previousRect.top < scrollRect.top); + } + } + event.preventDefault(); + return true; + } + return false +} +