A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 120 lines 5.3 kB view raw
1import {Constants} from "../../constants"; 2import {onGet} from "../util/onGet"; 3import {fetchPost} from "../../util/fetch"; 4import {updateHotkeyTip} from "../util/compatibility"; 5import {hasClosestByClassName} from "../util/hasClosest"; 6import {goEnd, goHome} from "../wysiwyg/commonHotkey"; 7import {showTooltip} from "../../dialog/tooltip"; 8 9export class Scroll { 10 public element: HTMLElement; 11 private parentElement: HTMLElement; 12 private inputElement: HTMLInputElement; 13 public lastScrollTop: number; 14 public keepLazyLoad: boolean; // 保持加载内容 15 16 constructor(protyle: IProtyle) { 17 this.parentElement = document.createElement("div"); 18 this.parentElement.classList.add("protyle-scroll"); 19 this.parentElement.innerHTML = `<div class="protyle-scroll__up ariaLabel" data-position="north" aria-label="${updateHotkeyTip("⌘Home")}"> 20 <svg><use xlink:href="#iconUp"></use></svg> 21</div> 22<div class="fn__none protyle-scroll__bar ariaLabel" data-position="2west" aria-label="Blocks 1/1"> 23 <input class="b3-slider" type="range" max="1" min="1" step="1" value="1" /> 24</div> 25<div class="protyle-scroll__down ariaLabel" aria-label="${updateHotkeyTip("⌘End")}"> 26 <svg><use xlink:href="#iconDown"></use></svg> 27</div>`; 28 29 this.element = this.parentElement.querySelector(".protyle-scroll__bar"); 30 this.keepLazyLoad = false; 31 if (!protyle.options.render.scroll) { 32 this.parentElement.classList.add("fn__none"); 33 } 34 this.lastScrollTop = 0; 35 this.inputElement = this.element.firstElementChild as HTMLInputElement; 36 this.inputElement.addEventListener("input", () => { 37 this.element.setAttribute("aria-label", `Blocks ${this.inputElement.value}/${protyle.block.blockCount}`); 38 showTooltip(this.element.getAttribute("aria-label"), this.element); 39 }); 40 /// #if BROWSER 41 this.inputElement.addEventListener("change", () => { 42 this.setIndex(protyle); 43 }); 44 this.inputElement.addEventListener("touchend", () => { 45 this.setIndex(protyle); 46 }); 47 /// #endif 48 this.parentElement.addEventListener("click", (event) => { 49 const target = event.target as HTMLElement; 50 if (hasClosestByClassName(target, "protyle-scroll__up")) { 51 goHome(protyle); 52 } else if (hasClosestByClassName(target, "protyle-scroll__down")) { 53 goEnd(protyle); 54 } else if (target.classList.contains("b3-slider")) { 55 this.setIndex(protyle); 56 } 57 }); 58 this.parentElement.addEventListener("mousewheel", (event: WheelEvent) => { 59 if (event.deltaY !== 0 && protyle.scroll.lastScrollTop !== -1) { 60 protyle.contentElement.scrollTop += event.deltaY; 61 } 62 }, {passive: true}); 63 } 64 65 private setIndex(protyle: IProtyle) { 66 if (protyle.wysiwyg.element.getAttribute("data-top")) { 67 return; 68 } 69 protyle.wysiwyg.element.setAttribute("data-top", protyle.wysiwyg.element.scrollTop.toString()); 70 protyle.contentElement.style.overflow = "hidden"; 71 fetchPost("/api/filetree/getDoc", { 72 index: parseInt(this.inputElement.value), 73 id: protyle.block.parentID, 74 mode: 0, 75 size: window.siyuan.config.editor.dynamicLoadBlocks, 76 }, getResponse => { 77 onGet({ 78 data: getResponse, 79 protyle, 80 action: [Constants.CB_GET_FOCUSFIRST, Constants.CB_GET_UNCHANGEID], 81 afterCB: () => { 82 setTimeout(() => { 83 protyle.contentElement.style.overflow = ""; 84 }, Constants.TIMEOUT_INPUT); // 需和 onGet 中的 preventScroll 保持一致 85 showTooltip(this.element.getAttribute("aria-label"), this.element); 86 } 87 }); 88 }); 89 } 90 91 public updateIndex(protyle: IProtyle, id: string, cb?: (index: number) => void) { 92 fetchPost("/api/block/getBlockIndex", {id}, (response) => { 93 if (!response.data) { 94 return; 95 } 96 const inputElement = protyle.scroll.element.querySelector(".b3-slider") as HTMLInputElement; 97 inputElement.value = response.data; 98 protyle.scroll.element.setAttribute("aria-label", `Blocks ${response.data}/${protyle.block.blockCount}`); 99 if (cb) { 100 cb(response.data); 101 } 102 }); 103 } 104 105 public update(protyle: IProtyle) { 106 if (typeof protyle.block.blockCount === "number") { 107 this.inputElement.setAttribute("max", protyle.block.blockCount.toString()); 108 this.element.setAttribute("aria-label", `Blocks ${this.inputElement.value}/${protyle.block.blockCount}`); 109 } 110 if (protyle.block.showAll) { 111 this.element.classList.add("fn__none"); 112 } else { 113 if (protyle.block.scroll && !protyle.contentElement.classList.contains("fn__none")) { 114 this.element.classList.remove("fn__none"); 115 } else { 116 this.element.classList.add("fn__none"); 117 } 118 } 119 } 120}