diff --git a/app/src/layout/dock/Files.ts b/app/src/layout/dock/Files.ts index eaac79c9f..0cf019d2a 100644 --- a/app/src/layout/dock/Files.ts +++ b/app/src/layout/dock/Files.ts @@ -259,6 +259,8 @@ export class Files extends Model { this.getLeaf(target, notebookId); this.setCurrent(target, false); } + this.element.querySelector('[select-end="true"]')?.removeAttribute("select-end"); + this.element.querySelector('[select-start="true"]')?.removeAttribute("select-start"); window.siyuan.menus.menu.remove(); event.stopPropagation(); event.preventDefault(); diff --git a/app/src/protyle/wysiwyg/commonHotkey.ts b/app/src/protyle/wysiwyg/commonHotkey.ts index 055e0b2b9..c040a00ef 100644 --- a/app/src/protyle/wysiwyg/commonHotkey.ts +++ b/app/src/protyle/wysiwyg/commonHotkey.ts @@ -168,7 +168,7 @@ export const downSelect = (options: { options.event.preventDefault(); }; -export const getStartEndElement = (selectElements: NodeListOf) => { +export const getStartEndElement = (selectElements: NodeListOf | Element[]) => { let startElement; let endElement; selectElements.forEach(item => { diff --git a/app/src/protyle/wysiwyg/getBlock.ts b/app/src/protyle/wysiwyg/getBlock.ts index 3a8f34daa..517de8dad 100644 --- a/app/src/protyle/wysiwyg/getBlock.ts +++ b/app/src/protyle/wysiwyg/getBlock.ts @@ -182,3 +182,49 @@ export const hasPreviousSibling = (element: Node) => { } return false; }; + +export const getNextFileLi = (current: Element) => { + let nextElement = current.nextElementSibling + if (nextElement) { + if (nextElement.tagName === "LI") { + return nextElement + } else if (nextElement.tagName === "UL") { + return nextElement.firstElementChild + } + return false; + } + nextElement = current.parentElement + while (nextElement.tagName === "UL") { + if (!nextElement.nextElementSibling) { + nextElement = nextElement.parentElement + } else if (nextElement.nextElementSibling.tagName === "LI") { + return nextElement.nextElementSibling + } else if (nextElement.nextElementSibling.tagName === "UL") { + return nextElement.nextElementSibling.firstElementChild; + } + } + return false; +} + +export const getPreviousFileLi = (current: Element) => { + let previousElement = current.previousElementSibling + if (previousElement) { + if (previousElement.tagName === "LI") { + return previousElement + } else if (previousElement.tagName === "UL") { + return previousElement.lastElementChild + } + return false; + } + previousElement = current.parentElement + while (previousElement.tagName === "UL") { + if (!previousElement.previousElementSibling) { + previousElement = previousElement.parentElement + } else if (previousElement.previousElementSibling.tagName === "LI") { + return previousElement.previousElementSibling; + } else if (previousElement.previousElementSibling.tagName === "UL") { + return previousElement.previousElementSibling.lastElementChild; + } + } + return false; +} diff --git a/app/src/util/globalShortcut.ts b/app/src/util/globalShortcut.ts index 6dc3c1b91..c1ac373e3 100644 --- a/app/src/util/globalShortcut.ts +++ b/app/src/util/globalShortcut.ts @@ -39,6 +39,8 @@ import {deleteFile} from "../editor/deleteFile"; import {escapeHtml} from "./escape"; import {syncGuide} from "../sync/syncGuide"; import {showPopover} from "../block/popover"; +import {getStartEndElement} from "../protyle/wysiwyg/commonHotkey"; +import {getNextFileLi, getPreviousFileLi} from "../protyle/wysiwyg/getBlock"; const getRightBlock = (element: HTMLElement, x: number, y: number) => { let index = 1; @@ -953,99 +955,154 @@ const fileTreeKeydown = (event: KeyboardEvent) => { event.preventDefault(); return true; } - if ((event.key === "ArrowRight" && !liElements[0].querySelector(".b3-list-item__arrow--open") && !liElements[0].querySelector(".b3-list-item__toggle").classList.contains("fn__hidden")) || - (event.key === "ArrowLeft" && liElements[0].querySelector(".b3-list-item__arrow--open"))) { - files.getLeaf(liElements[0], notebookId); - liElements.forEach((item, index) => { - if (index !== 0) { - item.classList.remove("b3-list-item--focus") + if (event.shiftKey) { + if (event.key === "ArrowUp") { + const startEndElement = getStartEndElement(liElements); + if (startEndElement.startElement.getBoundingClientRect().top >= startEndElement.endElement.getBoundingClientRect().top) { + const previousElement = getPreviousFileLi(startEndElement.endElement) + if (previousElement) { + previousElement.classList.add("b3-list-item--focus"); + previousElement.setAttribute("select-end", "true"); + startEndElement.endElement.removeAttribute("select-end"); + + const previousRect = previousElement.getBoundingClientRect(); + const fileRect = files.element.getBoundingClientRect(); + if (previousRect.top < fileRect.top || previousRect.bottom > fileRect.bottom) { + previousElement.scrollIntoView(previousRect.top < fileRect.top); + } + } + } else { + startEndElement.endElement.classList.remove("b3-list-item--focus"); + startEndElement.endElement.removeAttribute("select-end"); + const previousElement = getPreviousFileLi(startEndElement.endElement); + if (previousElement) { + previousElement.setAttribute("select-end", "true"); + } } - }) - event.preventDefault(); - return true; - } - const fileRect = files.element.getBoundingClientRect(); - if (event.key === "ArrowLeft") { - let parentElement = liElements[0].parentElement.previousElementSibling; - if (parentElement) { - if (parentElement.tagName !== "LI") { - parentElement = files.element.querySelector(".b3-list-item"); - } - liElements.forEach((item, index) => { - item.classList.remove("b3-list-item--focus") - }) - parentElement.classList.add("b3-list-item--focus"); - const parentRect = parentElement.getBoundingClientRect(); - if (parentRect.top < fileRect.top || parentRect.bottom > fileRect.bottom) { - parentElement.scrollIntoView(parentRect.top < fileRect.top); + } else if (event.key === "ArrowDown") { + const startEndElement = getStartEndElement(liElements); + if (startEndElement.startElement.getBoundingClientRect().top <= startEndElement.endElement.getBoundingClientRect().top) { + const nextElement = getNextFileLi(startEndElement.endElement); + if (nextElement) { + nextElement.classList.add("b3-list-item--focus"); + nextElement.setAttribute("select-end", "true"); + startEndElement.endElement.removeAttribute("select-end"); + const nextRect = nextElement.getBoundingClientRect(); + const fileRect = files.element.getBoundingClientRect(); + if (nextRect.top < fileRect.top || nextRect.bottom > fileRect.bottom) { + nextElement.scrollIntoView(nextRect.top < fileRect.top); + } + } + } else { + startEndElement.endElement.classList.remove("b3-list-item--focus"); + startEndElement.endElement.removeAttribute("select-end"); + const nextElement = getNextFileLi(startEndElement.endElement); + if (nextElement) { + nextElement.setAttribute("select-end", "true"); + } } } - event.preventDefault(); - return true; - } - if (event.key === "ArrowDown" || event.key === "ArrowRight") { - let nextElement = liElements[0]; - while (nextElement) { - if (nextElement.nextElementSibling) { - if (nextElement.nextElementSibling.tagName === "UL") { - nextElement = nextElement.nextElementSibling.firstElementChild; - } else { - nextElement = nextElement.nextElementSibling; + return; + } else { + files.element.querySelector('[select-end="true"]')?.removeAttribute("select-end"); + files.element.querySelector('[select-start="true"]')?.removeAttribute("select-start"); + if ((event.key === "ArrowRight" && !liElements[0].querySelector(".b3-list-item__arrow--open") && !liElements[0].querySelector(".b3-list-item__toggle").classList.contains("fn__hidden")) || + (event.key === "ArrowLeft" && liElements[0].querySelector(".b3-list-item__arrow--open"))) { + files.getLeaf(liElements[0], notebookId); + liElements.forEach((item, index) => { + if (index !== 0) { + item.classList.remove("b3-list-item--focus") } - break; - } else { - if (nextElement.parentElement.classList.contains("fn__flex-1")) { + }) + event.preventDefault(); + return true; + } + if (event.key === "ArrowLeft") { + let parentElement = liElements[0].parentElement.previousElementSibling; + if (parentElement) { + if (parentElement.tagName !== "LI") { + parentElement = files.element.querySelector(".b3-list-item"); + } + liElements.forEach((item) => { + item.classList.remove("b3-list-item--focus") + }) + parentElement.classList.add("b3-list-item--focus"); + const parentRect = parentElement.getBoundingClientRect(); + const fileRect = files.element.getBoundingClientRect(); + if (parentRect.top < fileRect.top || parentRect.bottom > fileRect.bottom) { + parentElement.scrollIntoView(parentRect.top < fileRect.top); + } + } + event.preventDefault(); + return true; + } + if (event.key === "ArrowDown" || event.key === "ArrowRight") { + let nextElement = liElements[0]; + while (nextElement) { + if (nextElement.nextElementSibling) { + if (nextElement.nextElementSibling.tagName === "UL") { + nextElement = nextElement.nextElementSibling.firstElementChild; + } else { + nextElement = nextElement.nextElementSibling; + } break; } else { - nextElement = nextElement.parentElement; + if (nextElement.parentElement.classList.contains("fn__flex-1")) { + break; + } else { + nextElement = nextElement.parentElement; + } } } - } - if (nextElement.classList.contains("b3-list-item")) { - liElements.forEach((item, index) => { - item.classList.remove("b3-list-item--focus") - }) - nextElement.classList.add("b3-list-item--focus"); - const nextRect = nextElement.getBoundingClientRect(); - if (nextRect.top < fileRect.top || nextRect.bottom > fileRect.bottom) { - nextElement.scrollIntoView(nextRect.top < fileRect.top); - } - } - event.preventDefault(); - return true; - } - if (event.key === "ArrowUp") { - let previousElement = liElements[0]; - while (previousElement) { - if (previousElement.previousElementSibling) { - if (previousElement.previousElementSibling.tagName === "LI") { - previousElement = previousElement.previousElementSibling; - } else { - const liElements = previousElement.previousElementSibling.querySelectorAll(".b3-list-item"); - previousElement = liElements[liElements.length - 1]; + if (nextElement.classList.contains("b3-list-item")) { + liElements.forEach((item) => { + item.classList.remove("b3-list-item--focus") + }) + nextElement.classList.add("b3-list-item--focus"); + const nextRect = nextElement.getBoundingClientRect(); + const fileRect = files.element.getBoundingClientRect(); + if (nextRect.top < fileRect.top || nextRect.bottom > fileRect.bottom) { + nextElement.scrollIntoView(nextRect.top < fileRect.top); } - break; - } else { - if (previousElement.parentElement.classList.contains("fn__flex-1")) { + } + event.preventDefault(); + return true; + } + if (event.key === "ArrowUp") { + let previousElement = liElements[0]; + while (previousElement) { + if (previousElement.previousElementSibling) { + if (previousElement.previousElementSibling.tagName === "LI") { + previousElement = previousElement.previousElementSibling; + } else { + const liElements = previousElement.previousElementSibling.querySelectorAll(".b3-list-item"); + previousElement = liElements[liElements.length - 1]; + } break; } else { - previousElement = previousElement.parentElement; + if (previousElement.parentElement.classList.contains("fn__flex-1")) { + break; + } else { + previousElement = previousElement.parentElement; + } } } - } - if (previousElement.classList.contains("b3-list-item")) { - liElements.forEach((item, index) => { - item.classList.remove("b3-list-item--focus") - }) - previousElement.classList.add("b3-list-item--focus"); - const previousRect = previousElement.getBoundingClientRect(); - if (previousRect.top < fileRect.top || previousRect.bottom > fileRect.bottom) { - previousElement.scrollIntoView(previousRect.top < fileRect.top); + if (previousElement.classList.contains("b3-list-item")) { + liElements.forEach((item) => { + item.classList.remove("b3-list-item--focus") + }) + previousElement.classList.add("b3-list-item--focus"); + const previousRect = previousElement.getBoundingClientRect(); + const fileRect = files.element.getBoundingClientRect(); + if (previousRect.top < fileRect.top || previousRect.bottom > fileRect.bottom) { + previousElement.scrollIntoView(previousRect.top < fileRect.top); + } } + event.preventDefault(); + return true; } - event.preventDefault(); - return true; } + if (event.key === "Delete" || (event.key === "Backspace" && isMac())) { window.siyuan.menus.menu.remove(); liElements.forEach(item => {