import {Tab} from "../Tab"; import {Model} from "../Model"; import {getDisplayName} from "../../util/pathName"; import {Tree} from "../../util/Tree"; import {hasClosestByClassName} from "../../protyle/util/hasClosest"; import {getDockByType, setPanelFocus} from "../util"; import {fetchPost} from "../../util/fetch"; import {Constants} from "../../constants"; import {getAllModels} from "../getAll"; import {onGet} from "../../protyle/util/onGet"; import {updateHotkeyTip} from "../../protyle/util/compatibility"; import {openFileById} from "../../editor/util"; import {MenuItem} from "../../menus/Menu"; import Protyle from "../../protyle"; export class Backlink extends Model { public element: HTMLElement; public inputsElement: NodeListOf; public type: "pin" | "local"; public blockId: string; public rootId: string; // "local" 必传 private tree: Tree; private notebookId: string; private mTree: Tree; private editors: Protyle[] = []; constructor(options: { tab: Tab, blockId: string, rootId?: string, type: "pin" | "local" }) { super({ id: options.tab.id, callback() { if (this.type === "local") { fetchPost("/api/block/checkBlockExist", {id: this.blockId}, existResponse => { if (!existResponse.data) { this.parent.parent.removeTab(this.parent.id); } }); } }, msgCallback(data) { if (data && this.type === "local") { switch (data.cmd) { case "rename": if (this.blockId === data.data.id) { this.parent.updateTitle(data.data.title); } break; case "unmount": if (this.notebookId === data.data.box) { this.parent.parent.removeTab(this.parent.id); } break; case "remove": if (this.path?.indexOf(getDisplayName(data.data.path, false, true)) === 0) { this.parent.parent.removeTab(this.parent.id); } break; } } } }); this.blockId = options.blockId; this.rootId = options.rootId; this.type = options.type; this.element = options.tab.panelElement; this.element.classList.add("fn__flex-column", "file-tree", "sy__backlink"); this.element.innerHTML = `
`; this.inputsElement = this.element.querySelectorAll("input"); this.inputsElement.forEach((item) => { item.addEventListener("keydown", (event: KeyboardEvent) => { if (!event.isComposing && event.key === "Enter") { this.searchBacklinks(); } }); item.addEventListener("input", (event: KeyboardEvent) => { const inputElement = event.target as HTMLInputElement; if (inputElement.value === "") { inputElement.classList.remove("search__input--block"); } else { inputElement.classList.add("search__input--block"); } }); }); this.tree = new Tree({ element: this.element.querySelector(".backlinkList") as HTMLElement, data: null, click(element) { openFileById({ id: element.getAttribute("data-node-id"), action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); }, ctrlClick(element) { openFileById({ id: element.getAttribute("data-node-id"), keepCursor: true, action: [Constants.CB_GET_CONTEXT] }); }, altClick(element) { openFileById({ id: element.getAttribute("data-node-id"), position: "right", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); }, shiftClick(element) { openFileById({ id: element.getAttribute("data-node-id"), position: "bottom", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); }, toggleClick: (liElement) => { this.toggleItem(liElement); } }); this.mTree = new Tree({ element: this.element.querySelector(".backlinkMList") as HTMLElement, data: null, click: (element, event) => { const actionElement = hasClosestByClassName(event.target as HTMLElement, "b3-list-item__action"); if (actionElement) { if (actionElement.firstElementChild.classList.contains("fn__rotate")) { return; } window.siyuan.menus.menu.remove(); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.turnInto + " " + window.siyuan.languages.turnToStaticRef, click: () => { this.turnToRef(element, false); } }).element); window.siyuan.menus.menu.append(new MenuItem({ label: window.siyuan.languages.turnInto + " " + window.siyuan.languages.turnToDynamicRef, click: () => { this.turnToRef(element, true); } }).element); window.siyuan.menus.menu.popup({x: event.clientX, y: event.clientY}); } else { openFileById({ id: element.getAttribute("data-node-id"), action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); } }, ctrlClick(element) { openFileById({ id: element.getAttribute("data-node-id"), keepCursor: true, action: [Constants.CB_GET_CONTEXT] }); }, altClick(element) { openFileById({ id: element.getAttribute("data-node-id"), position: "right", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); }, shiftClick(element) { openFileById({ id: element.getAttribute("data-node-id"), position: "bottom", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT] }); }, blockExtHTML: `` }); this.tree.element.addEventListener("scroll", () => { this.tree.element.querySelectorAll(".protyle-gutters").forEach(item => { item.classList.add("fn__none"); item.innerHTML = ""; // https://ld246.com/article/1651935412480 this.tree.element.querySelectorAll(".protyle-wysiwyg--hl").forEach((hlItem) => { hlItem.classList.remove("protyle-wysiwyg--hl"); }); }); }); this.mTree.element.addEventListener("scroll", () => { this.mTree.element.querySelectorAll(".protyle-gutters").forEach(item => { item.classList.add("fn__none"); item.innerHTML = ""; // https://ld246.com/article/1651935412480 this.mTree.element.querySelectorAll(".protyle-wysiwyg--hl").forEach((hlItem) => { hlItem.classList.remove("protyle-wysiwyg--hl"); }); }); }); // 为了快捷键的 dispatch this.element.querySelector('[data-type="collapse"]').addEventListener("click", () => { this.tree.element.querySelectorAll(".protyle").forEach(item => { item.classList.add("fn__none"); }); this.tree.element.querySelectorAll(".b3-list-item__arrow").forEach(item => { item.classList.remove("b3-list-item__arrow--open"); }); }); this.element.addEventListener("click", (event) => { if (this.type === "local") { setPanelFocus(this.element.parentElement.parentElement); } else { setPanelFocus(this.element.firstElementChild); } let target = event.target as HTMLElement; while (target && !target.isEqualNode(this.element)) { if (target.classList.contains("block__icon")) { const type = target.getAttribute("data-type"); switch (type) { case "refresh": this.refresh(); break; case "mCollapse": this.mTree.element.querySelectorAll(".protyle").forEach(item => { item.classList.add("fn__none"); }); this.mTree.element.querySelectorAll(".b3-list-item__arrow").forEach(item => { item.classList.remove("b3-list-item__arrow--open"); }); break; case "min": getDockByType("backlink").toggleModel("backlink"); break; case "layout": if (this.mTree.element.style.flex) { if (this.mTree.element.style.height === "0px") { this.tree.element.classList.remove("fn__none"); this.mTree.element.removeAttribute("style"); target.setAttribute("aria-label", window.siyuan.languages.up); target.querySelector("use").setAttribute("xlink:href", "#iconUp"); } else { this.tree.element.classList.remove("fn__none"); this.mTree.element.removeAttribute("style"); target.setAttribute("aria-label", window.siyuan.languages.down); target.querySelector("use").setAttribute("xlink:href", "#iconDown"); } } else { if (target.getAttribute("aria-label") === window.siyuan.languages.down) { this.tree.element.classList.remove("fn__none"); this.mTree.element.setAttribute("style", "flex:none;height:0px"); target.setAttribute("aria-label", window.siyuan.languages.up); target.querySelector("use").setAttribute("xlink:href", "#iconUp"); } else { this.tree.element.classList.add("fn__none"); this.mTree.element.setAttribute("style", `flex:none;height:${this.element.clientHeight - this.tree.element.previousElementSibling.clientHeight * 2}px`); target.setAttribute("aria-label", window.siyuan.languages.down); target.querySelector("use").setAttribute("xlink:href", "#iconDown"); } } target.setAttribute("data-clicked", "true"); break; } } target = target.parentElement; } }); this.searchBacklinks(); if (this.type === "pin") { setPanelFocus(this.element.firstElementChild); } } private toggleItem(liElement: HTMLElement) { const svgElement = liElement.firstElementChild.firstElementChild; if (svgElement.classList.contains("b3-list-item__arrow--open")) { svgElement.classList.remove("b3-list-item__arrow--open"); liElement.nextElementSibling?.classList.add("fn__none"); } else { svgElement.classList.add("b3-list-item__arrow--open"); if (liElement.nextElementSibling && liElement.nextElementSibling.tagName === "DIV") { liElement.nextElementSibling.classList.remove("fn__none"); } else { fetchPost("/api/ref/getBacklinkDoc", { defID: this.blockId, refTreeID: liElement.getAttribute("data-node-id") }, (response) => { const editorElement = document.createElement("div"); editorElement.style.minHeight = "auto"; liElement.after(editorElement); const editor = new Protyle(editorElement, { blockId: "", backlinkData: response.data.backlinks, render: { background: false, title: false, gutter: true, scroll: false, breadcrumb: false, } }); this.editors.push(editor); }); } } } private turnToRef(element: HTMLElement, isDynamic: boolean) { element.querySelector(".b3-list-item__action").innerHTML = ''; this.element.querySelector('.block__icon[data-type="refresh"] svg').classList.add("fn__rotate"); fetchPost("/api/ref/createBacklink", { refID: element.getAttribute("data-node-id"), refText: decodeURIComponent(element.getAttribute("data-ref-text")), defID: this.blockId, pushMode: 0, isDynamic }, response => { if (response.data.defID === this.blockId) { this.searchBacklinks(true); } getAllModels().editor.forEach(item => { if (response.data.refRootID === item.editor.protyle.block.rootID) { fetchPost("/api/filetree/getDoc", { id: item.editor.protyle.block.id, size: Constants.SIZE_GET, }, getResponse => { onGet(getResponse, item.editor.protyle); }); } }); }); } public refresh() { fetchPost("/api/ref/refreshBacklink", { id: this.blockId, }, () => { this.searchBacklinks(); }); } private searchBacklinks(reload = false) { const element = this.element.querySelector('.block__icon[data-type="refresh"] svg'); if (element.classList.contains("fn__rotate") && !reload) { return; } element.classList.add("fn__rotate"); fetchPost("/api/ref/getBacklink", { k: this.inputsElement[0].value, mk: this.inputsElement[1].value, beforeLen: 12, id: this.blockId, }, response => { this.render(response.data); }); } public render(data: { box: string, backlinks: IBlockTree[], backmentions: IBlockTree[], linkRefsCount: number, mentionsCount: number, k: string, mk: string }) { if (!data) { data = { box: "", backlinks: [], backmentions: [], linkRefsCount: 0, mentionsCount: 0, k: "", mk: "" }; } this.editors.forEach(item => { item.destroy(); }); this.editors = []; this.element.querySelector('.block__icon[data-type="refresh"] svg').classList.remove("fn__rotate"); this.notebookId = data.box; this.inputsElement[0].value = data.k; this.inputsElement[1].value = data.mk; if (data.backlinks) { data.backlinks.forEach((item) => { delete item.blocks; delete item.children; }); } if (data.backmentions) { data.backmentions.forEach((item) => { delete item.blocks; delete item.children; }); } this.tree.updateData(data.backlinks); this.mTree.updateData(data.backmentions); const countElement = this.element.querySelector(".listCount"); if (data.linkRefsCount === 0) { countElement.classList.add("fn__none"); } else { countElement.classList.remove("fn__none"); countElement.textContent = data.linkRefsCount.toString(); this.toggleItem(this.tree.element.firstElementChild.firstElementChild as HTMLElement); } const mCountElement = this.element.querySelector(".listMCount"); if (data.mentionsCount === 0) { mCountElement.classList.add("fn__none"); } else { mCountElement.classList.remove("fn__none"); mCountElement.textContent = data.mentionsCount.toString(); } const layoutElement = this.element.querySelector("[data-type='layout']"); if (layoutElement.getAttribute("data-clicked")) { return; } if (data.mentionsCount === 0) { this.tree.element.classList.remove("fn__none"); this.mTree.element.setAttribute("style", "flex:none;height:0px"); layoutElement.setAttribute("aria-label", window.siyuan.languages.up); layoutElement.querySelector("use").setAttribute("xlink:href", "#iconUp"); return; } if (data.linkRefsCount === 0) { this.tree.element.classList.add("fn__none"); this.mTree.element.setAttribute("style", `flex:none;height:${this.element.clientHeight - this.tree.element.previousElementSibling.clientHeight * 2}px`); layoutElement.setAttribute("aria-label", window.siyuan.languages.down); layoutElement.querySelector("use").setAttribute("xlink:href", "#iconDown"); } else { this.tree.element.classList.remove("fn__none"); this.mTree.element.removeAttribute("style"); layoutElement.setAttribute("aria-label", window.siyuan.languages.down); layoutElement.querySelector("use").setAttribute("xlink:href", "#iconDown"); } } }