diff --git a/app/src/assets/scss/_protyle.scss b/app/src/assets/scss/_protyle.scss index e016cdcce..5763328f6 100644 --- a/app/src/assets/scss/_protyle.scss +++ b/app/src/assets/scss/_protyle.scss @@ -5,12 +5,53 @@ .protyle-scroll { position: absolute; - right: -80px; - top: 50%; - transform: rotate(90deg); + right: 0; + height: 100%; + top: 0; + width: 24px; + svg { + height: 16px; + width: 16px; + } - .b3-slider { - width: 200px; + &__down, + &__up { + transition: var(--b3-transition); + position: absolute; + opacity: 0; + cursor: pointer; + color: var(--b3-border-color); + + &:hover { + color: var(--b3-theme-on-surface); + } + } + + &__up { + top: calc(50% - 114px); + } + + &__down { + bottom: calc(50% - 135px); + } + + &:hover { + .protyle-scroll__down, + .protyle-scroll__up { + opacity: 1; + } + } + + &__bar { + position: absolute; + right: -89px; + top: 50%; + transform: rotate(90deg); + z-index: 1; + + .b3-slider { + width: 200px; + } } } @@ -110,6 +151,7 @@ flex-shrink: 0; box-sizing: border-box; min-height: 30px; + z-index: 1; &__space { flex: 1; diff --git a/app/src/mobile/editor.ts b/app/src/mobile/editor.ts index af5e077c7..2e5240406 100644 --- a/app/src/mobile/editor.ts +++ b/app/src/mobile/editor.ts @@ -59,6 +59,7 @@ export const openMobileFileById = (id: string, action = [Constants.CB_GET_HL]) = blockId: id, action, render: { + scroll: true, background: true, gutter: true, }, diff --git a/app/src/protyle/index.ts b/app/src/protyle/index.ts index 9fd849d5e..a24efd788 100644 --- a/app/src/protyle/index.ts +++ b/app/src/protyle/index.ts @@ -72,7 +72,7 @@ export class Protyle { this.protyle.undo = new Undo(); this.protyle.wysiwyg = new WYSIWYG(this.protyle); this.protyle.toolbar = new Toolbar(this.protyle); - this.protyle.scroll = new Scroll(this.protyle); + this.protyle.scroll = new Scroll(this.protyle); // 不能使用 render.scroll 来判读是否初始化,除非重构后面用到的相关变量 if (this.protyle.options.render.gutter) { this.protyle.gutter = new Gutter(this.protyle); } diff --git a/app/src/protyle/scroll/index.ts b/app/src/protyle/scroll/index.ts index 3c3539091..2e8674498 100644 --- a/app/src/protyle/scroll/index.ts +++ b/app/src/protyle/scroll/index.ts @@ -1,25 +1,37 @@ import {Constants} from "../../constants"; import {onGet} from "../util/onGet"; import {fetchPost} from "../../util/fetch"; +import {updateHotkeyTip} from "../util/compatibility"; +import {hasClosestByClassName} from "../util/hasClosest"; +import {goEnd, goHome} from "../wysiwyg/commonHotkey"; export class Scroll { public element: HTMLElement; + private parentElement: HTMLElement; private inputElement: HTMLInputElement; public lastScrollTop: number; public keepLazyLoad: boolean; constructor(protyle: IProtyle) { - const divElement = document.createElement("div"); - divElement.innerHTML = ""; - divElement.className = "fn__none protyle-scroll b3-tooltips b3-tooltips__s"; - divElement.setAttribute("aria-label", "Blocks 1/1"); - this.element = divElement; - this.keepLazyLoad = false; + this.parentElement = document.createElement("div"); + this.parentElement.classList.add("protyle-scroll"); + this.parentElement.innerHTML = `
+ +
+
+ +
+
+ +
` + + this.element = this.parentElement.querySelector(".protyle-scroll__bar"); + this.keepLazyLoad = false; if (!protyle.options.render.scroll) { - this.element.classList.add("fn__none"); + this.parentElement.classList.add("fn__none"); } this.lastScrollTop = 0; - this.inputElement = divElement.firstElementChild as HTMLInputElement; + this.inputElement = this.element.firstElementChild as HTMLInputElement; this.inputElement.addEventListener("input", () => { this.element.setAttribute("aria-label", `Blocks ${this.inputElement.value}/${protyle.block.blockCount}`); }); @@ -31,13 +43,20 @@ export class Scroll { this.setIndex(protyle); }); /// #endif - this.inputElement.addEventListener("click", () => { - this.setIndex(protyle); + this.parentElement.addEventListener("click", (event) => { + const target = event.target as HTMLElement + if (hasClosestByClassName(target, "protyle-scroll__up")) { + goHome(protyle) + } else if (hasClosestByClassName(target, "protyle-scroll__down")) { + goEnd(protyle) + } else if (target.classList.contains("b3-slider")) { + this.setIndex(protyle); + } }); } private setIndex(protyle: IProtyle) { - if (protyle.wysiwyg.element.getAttribute("data-top") || !protyle.model) { + if (protyle.wysiwyg.element.getAttribute("data-top")) { return; } protyle.wysiwyg.element.setAttribute("data-top", protyle.wysiwyg.element.scrollTop.toString()); diff --git a/app/src/protyle/ui/initUI.ts b/app/src/protyle/ui/initUI.ts index ef53a271a..25fb8aa30 100644 --- a/app/src/protyle/ui/initUI.ts +++ b/app/src/protyle/ui/initUI.ts @@ -28,8 +28,8 @@ export const initUI = (protyle: IProtyle) => { if (protyle.upload) { protyle.element.appendChild(protyle.upload.element); } - if (protyle.scroll) { - protyle.element.appendChild(protyle.scroll.element); + if (protyle.options.render) { + protyle.element.appendChild(protyle.scroll.element.parentElement); } if (protyle.gutter) { protyle.element.appendChild(protyle.gutter.element); diff --git a/app/src/protyle/wysiwyg/commonHotkey.ts b/app/src/protyle/wysiwyg/commonHotkey.ts index 02541f4b5..1b79b436a 100644 --- a/app/src/protyle/wysiwyg/commonHotkey.ts +++ b/app/src/protyle/wysiwyg/commonHotkey.ts @@ -19,6 +19,8 @@ import {hideElements} from "../ui/hideElements"; import {countBlockWord} from "../../layout/status"; import {scrollCenter} from "../../util/highlightById"; import {transaction} from "./transaction"; +import {onGet} from "../util/onGet"; +import {Constants} from "../../constants"; export const commonHotkey = (protyle: IProtyle, event: KeyboardEvent) => { const target = event.target as HTMLElement; @@ -215,3 +217,38 @@ export const duplicateBlock = (nodeElements: Element[], protyle: IProtyle) => { focusBlock(focusElement); scrollCenter(protyle); }; + +export const goHome = (protyle:IProtyle) => { + if (protyle.wysiwyg.element.firstElementChild.getAttribute("data-node-index") === "0" || + protyle.wysiwyg.element.firstElementChild.getAttribute("data-eof") === "true" || + protyle.options.backlinkData) { + focusBlock(protyle.wysiwyg.element.firstElementChild); + protyle.contentElement.scrollTop = 0; + protyle.scroll.lastScrollTop = 1; + } else { + fetchPost("/api/filetree/getDoc", { + id: protyle.block.rootID, + mode: 0, + size: window.siyuan.config.editor.dynamicLoadBlocks, + }, getResponse => { + onGet(getResponse, protyle, [Constants.CB_GET_FOCUS]); + }); + } +} + +export const goEnd = (protyle:IProtyle) => { + if (!protyle.scroll.element.classList.contains("fn__none") && + protyle.wysiwyg.element.lastElementChild.getAttribute("data-eof") !== "true") { + fetchPost("/api/filetree/getDoc", { + id: protyle.block.rootID, + mode: 4, + size: window.siyuan.config.editor.dynamicLoadBlocks, + }, getResponse => { + onGet(getResponse, protyle, [Constants.CB_GET_FOCUS]); + }); + } else { + protyle.contentElement.scrollTop = protyle.contentElement.scrollHeight; + protyle.scroll.lastScrollTop = protyle.contentElement.scrollTop; + focusBlock(protyle.wysiwyg.element.lastElementChild, undefined, false); + } +} diff --git a/app/src/protyle/wysiwyg/keydown.ts b/app/src/protyle/wysiwyg/keydown.ts index eb98269a0..b3b42168e 100644 --- a/app/src/protyle/wysiwyg/keydown.ts +++ b/app/src/protyle/wysiwyg/keydown.ts @@ -42,14 +42,13 @@ import {isLocalPath} from "../../util/pathName"; /// #if !MOBILE import {openBy, openFileById} from "../../editor/util"; /// #endif -import {commonHotkey, downSelect, duplicateBlock, getStartEndElement, upSelect} from "./commonHotkey"; +import {commonHotkey, downSelect, duplicateBlock, getStartEndElement, goEnd, goHome, upSelect} from "./commonHotkey"; import {linkMenu, refMenu, setFold, zoomOut} from "../../menus/protyle"; import {removeEmbed} from "./removeEmbed"; import {openAttr} from "../../menus/commonMenuItem"; import {Constants} from "../../constants"; import {bindMenuKeydown} from "../../menus/Menu"; import {fetchPost} from "../../util/fetch"; -import {onGet} from "../util/onGet"; import {scrollCenter} from "../../util/highlightById"; import {BlockPanel} from "../../block/Panel"; import * as dayjs from "dayjs"; @@ -452,41 +451,14 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => { } // ctrl+home 光标移动到顶 if (!event.altKey && !event.shiftKey && isCtrl(event) && event.key === "Home") { - if (protyle.wysiwyg.element.firstElementChild.getAttribute("data-node-index") === "0" || - protyle.wysiwyg.element.firstElementChild.getAttribute("data-eof") === "true" || - protyle.options.backlinkData) { - focusBlock(protyle.wysiwyg.element.firstElementChild); - protyle.contentElement.scrollTop = 0; - protyle.scroll.lastScrollTop = 1; - } else { - fetchPost("/api/filetree/getDoc", { - id: protyle.block.rootID, - mode: 0, - size: window.siyuan.config.editor.dynamicLoadBlocks, - }, getResponse => { - onGet(getResponse, protyle, [Constants.CB_GET_FOCUS]); - }); - } + goHome(protyle); event.stopPropagation(); event.preventDefault(); return; } // ctrl+end 光标移动到尾 if (!event.altKey && !event.shiftKey && isCtrl(event) && event.key === "End") { - if (!protyle.scroll.element.classList.contains("fn__none") && - protyle.wysiwyg.element.lastElementChild.getAttribute("data-eof") !== "true") { - fetchPost("/api/filetree/getDoc", { - id: protyle.block.rootID, - mode: 4, - size: window.siyuan.config.editor.dynamicLoadBlocks, - }, getResponse => { - onGet(getResponse, protyle, [Constants.CB_GET_FOCUS]); - }); - } else { - protyle.contentElement.scrollTop = protyle.contentElement.scrollHeight; - protyle.scroll.lastScrollTop = protyle.contentElement.scrollTop; - focusBlock(protyle.wysiwyg.element.lastElementChild, undefined, false); - } + goEnd(protyle); event.stopPropagation(); event.preventDefault(); return; diff --git a/app/src/util/history.ts b/app/src/util/history.ts index 50dde67b7..c5dfa77c3 100644 --- a/app/src/util/history.ts +++ b/app/src/util/history.ts @@ -311,7 +311,6 @@ export const openHistory = () => { background: false, title: false, gutter: false, - scroll: false, breadcrumb: false, breadcrumbDocName: false, breadcrumbContext: false,