import {escapeHtml} from "../../util/escape"; import {Tab} from "../Tab"; import {Model} from "../Model"; import {getDockByType, getInstanceById, setPanelFocus} from "../util"; import {Constants} from "../../constants"; import {getDisplayName, pathPosix, setNoteBook} from "../../util/pathName"; import {newFile} from "../../util/newFile"; import {initFileMenu, initNavigationMenu} from "../../menus/navigation"; import {MenuItem} from "../../menus/Menu"; import {Editor} from "../../editor"; import {showMessage} from "../../dialog/message"; import {fetchPost} from "../../util/fetch"; import {openEmojiPanel, unicode2Emoji} from "../../emoji"; import {newNotebook} from "../../util/mount"; import {confirmDialog} from "../../dialog/confirmDialog"; import {updateHotkeyTip} from "../../protyle/util/compatibility"; import {openFileById} from "../../editor/util"; import {hasClosestByTag, hasTopClosestByTag} from "../../protyle/util/hasClosest"; import {isTouchDevice} from "../../util/functions"; export class Files extends Model { public element: HTMLElement; public parent: Tab; private actionsElement: HTMLElement; public closeElement: HTMLElement; constructor(options: { tab: Tab }) { super({ type: "filetree", id: options.tab.id, msgCallback(data) { if (data) { switch (data.cmd) { case "moveDoc": this.onMove(data.data); break; case "mount": this.onMount(data); break; case "createnotebook": setNoteBook(); this.element.insertAdjacentHTML("beforeend", this.genNotebook(data.data.box)); break; case "unmount": case "removeDoc": this.onRemove(data); break; case "createdailynote": case "create": case "heading2doc": case "li2doc": this.onMkdir(data.data); break; case "renamenotebook": this.element.querySelector(`[data-url="${data.data.box}"] .b3-list-item__text`).innerHTML = escapeHtml(data.data.name); break; case "rename": this.onRename(data.data); break; } } }, }); options.tab.panelElement.classList.add("fn__flex-column", "file-tree", "sy__file"); options.tab.panelElement.innerHTML = `
`; this.actionsElement = options.tab.panelElement.firstElementChild as HTMLElement; this.element = this.actionsElement.nextElementSibling as HTMLElement; this.closeElement = options.tab.panelElement.lastElementChild as HTMLElement; this.closeElement.addEventListener("click", (event) => { setPanelFocus(this.element.parentElement); let target = event.target as HTMLElement; while (target && !target.isEqualNode(this.closeElement)) { const type = target.getAttribute("data-type"); if (target.classList.contains("b3-list-item__icon")) { event.preventDefault(); event.stopPropagation(); openEmojiPanel(target.parentElement.getAttribute("data-url"), target, true); break; } else if (type === "toggle") { if (this.closeElement.classList.contains("fn__flex-1")) { this.closeElement.lastElementChild.classList.add("fn__none"); this.closeElement.classList.remove("fn__flex-1"); target.querySelector("svg").classList.remove("b3-list-item__arrow--open"); } else { this.closeElement.lastElementChild.classList.remove("fn__none"); this.closeElement.classList.add("fn__flex-1"); target.querySelector("svg").classList.add("b3-list-item__arrow--open"); } window.siyuan.menus.menu.remove(); event.stopPropagation(); event.preventDefault(); break; } else if (type === "remove") { confirmDialog(window.siyuan.languages.deleteOpConfirm, `${window.siyuan.languages.confirmDelete} ${escapeHtml(target.parentElement.querySelector(".b3-list-item__text").textContent)}?`, () => { fetchPost("/api/notebook/removeNotebook", { notebook: target.getAttribute("data-url"), callback: Constants.CB_MOUNT_REMOVE }); }); window.siyuan.menus.menu.remove(); event.stopPropagation(); event.preventDefault(); break; } else if (type === "open") { fetchPost("/api/notebook/openNotebook", { notebook: target.getAttribute("data-url") }); window.siyuan.menus.menu.remove(); event.stopPropagation(); event.preventDefault(); break; } target = target.parentElement; } }); // 为了快捷键的 dispatch this.actionsElement.querySelector('[data-type="collapse"]').addEventListener("click", () => { Array.from(this.element.children).forEach(item => { const liElement = item.firstElementChild; const toggleElement = liElement.querySelector(".b3-list-item__arrow"); if (toggleElement.classList.contains("b3-list-item__arrow--open")) { toggleElement.classList.remove("b3-list-item__arrow--open"); liElement.nextElementSibling.remove(); } }); }); this.actionsElement.addEventListener("click", (event: MouseEvent & { target: HTMLElement }) => { let target = event.target as HTMLElement; while (target && !target.isEqualNode(this.actionsElement)) { const type = target.getAttribute("data-type"); if (type === "min") { getDockByType("file").toggleModel("file", false, true); event.preventDefault(); event.stopPropagation(); window.siyuan.menus.menu.remove(); break; } else if (type === "focus") { const element = document.querySelector(".layout__wnd--active > .fn__flex > .layout-tab-bar > .item--focus") || document.querySelector(".layout-tab-bar > .item--focus"); if (element) { const tab = getInstanceById(element.getAttribute("data-id")) as Tab; if (tab && tab.model instanceof Editor) { this.selectItem(tab.model.editor.protyle.notebookId, tab.model.editor.protyle.path); } } event.preventDefault(); break; } else if (type === "more") { this.initMoreMenu().popup({x: event.clientX, y: event.clientY}); event.preventDefault(); event.stopPropagation(); break; } target = target.parentElement; } setPanelFocus(this.element.parentElement); }); let clickTimeout: number; this.element.addEventListener("click", (event) => { let target = event.target as HTMLElement; const ulElement = hasTopClosestByTag(target, "UL"); let needFocus = true; if (ulElement) { const notebookId = ulElement.getAttribute("data-url"); while (target && !target.isEqualNode(this.element)) { if (!event.metaKey && !event.ctrlKey && target.classList.contains("b3-list-item__icon") && window.siyuan.config.system.container !== "ios") { event.preventDefault(); event.stopPropagation(); if (target.parentElement.getAttribute("data-type") === "navigation-file") { openEmojiPanel(target.parentElement.getAttribute("data-node-id"), target); } else { openEmojiPanel(target.parentElement.parentElement.getAttribute("data-url"), target, true); } break; } else if (!event.metaKey && !event.ctrlKey && target.classList.contains("b3-list-item__toggle")) { this.getLeaf(target.parentElement, notebookId); this.setCurrent(target.parentElement); event.preventDefault(); event.stopPropagation(); window.siyuan.menus.menu.remove(); break; } else if (!event.metaKey && !event.ctrlKey && target.classList.contains("b3-list-item__action")) { const type = target.getAttribute("data-type"); const pathString = target.parentElement.getAttribute("data-path"); if (!window.siyuan.config.readonly) { if (type === "new") { newFile(notebookId, pathString, true); } else if (type === "more-root") { initNavigationMenu(target.parentElement).popup({x: event.clientX, y: event.clientY}); } } if (type === "more-file") { initFileMenu(notebookId, pathString, target.parentElement).popup({ x: event.clientX, y: event.clientY }); } event.preventDefault(); event.stopPropagation(); break; } else if (target.tagName === "LI") { if (event.detail === 1) { needFocus = false; clickTimeout = window.setTimeout(() => { if (!event.metaKey && !event.ctrlKey) { this.setCurrent(target, false); if (target.getAttribute("data-type") === "navigation-file") { if (window.siyuan.altIsPressed) { openFileById({ id: target.getAttribute("data-node-id"), position: "right", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL] }); } else { openFileById({ id: target.getAttribute("data-node-id"), action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL] }); } } else if (target.getAttribute("data-type") === "navigation-root") { this.getLeaf(target, notebookId); setPanelFocus(this.element.parentElement); } } else { setPanelFocus(this.element.parentElement); target.classList.toggle("b3-list-item--focus"); } }, Constants.TIMEOUT_DBLCLICK); } else if (!event.metaKey && !event.ctrlKey && event.detail === 2) { clearTimeout(clickTimeout); 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(); break; } target = target.parentElement; } } if (needFocus) { setPanelFocus(this.element.parentElement); } }); this.element.addEventListener("dragstart", (event: DragEvent & { target: HTMLElement }) => { if (isTouchDevice()) { event.stopPropagation(); event.preventDefault(); return; } window.getSelection().removeAllRanges(); const liElement = hasClosestByTag(event.target, "LI"); if (liElement) { let selectElements: Element[] = Array.from(this.element.querySelectorAll(".b3-list-item--focus")) if (!liElement.classList.contains("b3-list-item--focus")) { selectElements.forEach((item) => { item.classList.remove("b3-list-item--focus"); }) liElement.classList.add("b3-list-item--focus"); selectElements = [liElement]; } let ids = "" selectElements.forEach((item: HTMLElement) => { item.style.opacity = "0.1"; ids += (item.getAttribute("data-node-id") || "") + "," }) const img = new Image(); // TODO: drag preview img.src = `data:image/png;base64,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`; event.dataTransfer.setDragImage(img, 10, 10); event.dataTransfer.setData(Constants.SIYUAN_DROP_FILE, ids); event.dataTransfer.dropEffect = "move"; const tempEelement = document.createElement("div") tempEelement.innerText = ids; window.siyuan.dragElement = tempEelement; } }); this.element.addEventListener("dragend", (event: DragEvent & { target: HTMLElement }) => { this.element.querySelectorAll(".b3-list-item--focus").forEach((item: HTMLElement) => { item.style.opacity = "" }) window.siyuan.dragElement = undefined; }); this.element.addEventListener("dragover", (event: DragEvent & { target: HTMLElement }) => { if (window.siyuan.config.readonly) { return; } const liElement = hasClosestByTag(event.target, "LI"); if (!liElement || !window.siyuan.dragElement || liElement.classList.contains("b3-list-item--focus")) { event.preventDefault(); return; } liElement.classList.remove("dragover__top", "dragover__bottom", "dragover"); if (window.siyuan.dragElement?.parentElement?.classList.contains("protyle-gutters")) { if (["NodeListItem", "NodeHeading"].includes(window.siyuan.dragElement.getAttribute("data-type"))) { // 块标拖拽 liElement.classList.add("dragover"); } event.preventDefault(); return; } let sourceOnlyRoot = true Array.from(this.element.querySelectorAll(".b3-list-item--focus")).find((item: HTMLElement) => { if (item.getAttribute("data-type") === "navigation-file") { sourceOnlyRoot = false return true; } }) const targetType = liElement.getAttribute("data-type"); if (sourceOnlyRoot && targetType !== "navigation-root") { event.preventDefault(); return; } if (window.siyuan.config.fileTree.sort === 6 && // 防止文档拖拽到笔记本外 !(!sourceOnlyRoot && targetType === "navigation-root")) { const nodeRect = liElement.getBoundingClientRect(); if (event.clientY > nodeRect.top + 20) { liElement.classList.add("dragover__bottom"); event.preventDefault(); } else if (event.clientY < nodeRect.bottom - 20) { liElement.classList.add("dragover__top"); event.preventDefault(); } } if (liElement.classList.contains("dragover__top") || liElement.classList.contains("dragover__bottom") || (targetType === "navigation-root" && sourceOnlyRoot)) { event.preventDefault(); return; } liElement.classList.add("dragover"); event.preventDefault(); }); this.element.addEventListener("dragleave", (event: DragEvent & { target: HTMLElement }) => { const liElement = hasClosestByTag(event.target, "LI"); if (liElement) { liElement.classList.remove("dragover", "dragover__bottom", "dragover__top"); } }); this.element.addEventListener("drop", async (event: DragEvent & { target: HTMLElement }) => { const newElement = hasClosestByTag(event.target, "LI"); if (!newElement) { return; } const newUlElement = hasTopClosestByTag(newElement, "UL"); if (!newUlElement) { return; } const toURL = newUlElement.getAttribute("data-url"); const toPath = newElement.getAttribute("data-path"); const gutterType = window.siyuan.dragElement?.getAttribute("data-type") if (newElement.classList.contains("dragover") && ["NodeListItem", "NodeHeading"].includes(gutterType)) { // 块标拖拽 if (gutterType === "NodeHeading") { fetchPost("/api/filetree/heading2Doc", { targetNoteBook: toURL, srcHeadingID: window.siyuan.dragElement.getAttribute("data-node-id"), targetPath: toPath, pushMode: 0, }); } else { fetchPost("/api/filetree/li2Doc", { pushMode: 0, srcListItemID: window.siyuan.dragElement.getAttribute("data-node-id"), targetNoteBook: toURL, targetPath: toPath }); } newElement.classList.remove("dragover", "dragover__bottom", "dragover__top"); window.siyuan.dragElement = undefined; return; } window.siyuan.dragElement = undefined; if (!event.dataTransfer.getData(Constants.SIYUAN_DROP_FILE)) { newElement.classList.remove("dragover", "dragover__bottom", "dragover__top"); return; } const selectRootElements: HTMLElement[] = [] const selectFileElements: HTMLElement[] = [] const fromPaths: string[] = [] this.element.querySelectorAll(".b3-list-item--focus").forEach((item: HTMLElement) => { if (item.getAttribute("data-type") === "navigation-root") { selectRootElements.push(item) } else { const dataPath = item.getAttribute("data-path") const isChild = fromPaths.find(item => { if (dataPath.startsWith(item.replace(".sy", ""))) { return true; } }) if (!isChild) { selectFileElements.push(item) fromPaths.push(dataPath) } } }); if (newElement.classList.contains("dragover")) { await fetchPost("/api/filetree/moveDocs", { toNotebook: toURL, fromPaths, toPath, }); } if ((newElement.classList.contains("dragover__bottom") || newElement.classList.contains("dragover__top")) && window.siyuan.config.fileTree.sort === 6) { if (selectRootElements.length === fromPaths.length) { if (newElement.classList.contains("dragover__top")) { selectRootElements.forEach(item => { newElement.parentElement.before(item.parentElement); }) } else { selectRootElements.reverse().forEach(item => { newElement.parentElement.after(item.parentElement); }) } const notebooks: string[] = []; Array.from(this.element.children).forEach(item => { notebooks.push(item.getAttribute("data-url")); }); fetchPost("/api/notebook/changeSortNotebook", { notebooks, }); } else { let hasMove = false; const toDir = pathPosix().dirname(toPath); if (fromPaths.length > 0) { await fetchPost("/api/filetree/moveDocs", { toNotebook: toURL, fromPaths, toPath: toDir === "/" ? "/" : toDir + ".sy", }); selectFileElements.forEach(item => { item.setAttribute("data-path", pathPosix().join(toDir, item.getAttribute("data-node-id") + ".sy")); }) hasMove = true; } if (newElement.classList.contains("dragover__top")) { selectFileElements.forEach(item => { let nextULElement; if (item.nextElementSibling && item.nextElementSibling.tagName === "UL") { nextULElement = item.nextElementSibling; } newElement.before(item); if (nextULElement) { item.after(nextULElement); } }) } else if (newElement.classList.contains("dragover__bottom")) { selectFileElements.reverse().forEach(item => { let nextULElement; if (item.nextElementSibling && item.nextElementSibling.tagName === "UL") { nextULElement = item.nextElementSibling; } if (newElement.nextElementSibling && newElement.nextElementSibling.tagName === "UL") { newElement.nextElementSibling.after(item); } else { newElement.after(item); } if (nextULElement) { item.after(nextULElement); } }) } const paths: string[] = []; Array.from(newElement.parentElement.children).forEach(item => { if (item.tagName === "LI") { paths.push(item.getAttribute("data-path")); } }); fetchPost("/api/filetree/changeSort", { paths, notebook: toURL }, () => { if (hasMove) { // 移动并排序后,会推送 moveDoc,但此时还没有 sort。 https://github.com/siyuan-note/siyuan/issues/4270 fetchPost("/api/filetree/listDocsByPath", { notebook: toURL, path: pathPosix().dirname(toPath), sort: window.siyuan.config.fileTree.sort, }, response => { if (response.data.path === "/" && response.data.files.length === 0) { showMessage(window.siyuan.languages.emptyContent); return; } this.onLsHTML(response.data); }); } }); } } newElement.classList.remove("dragover", "dragover__bottom", "dragover__top"); }); this.init(); setPanelFocus(this.element.parentElement); } private genNotebook(item: INotebook) { const emojiHTML = `${unicode2Emoji(item.icon || Constants.SIYUAN_IMAGE_NOTE)}`; if (item.closed) { return `
  • ${emojiHTML} ${escapeHtml(item.name)}
  • `; } else { return ``; } } private init(init = true) { let html = ""; let closeHtml = ""; window.siyuan.notebooks.forEach((item) => { if (item.closed) { closeHtml += this.genNotebook(item); } else { html += this.genNotebook(item); } }); this.element.innerHTML = html; this.closeElement.lastElementChild.innerHTML = closeHtml; if (!init) { return; } if (html === "") { this.closeElement.lastElementChild.classList.remove("fn__none"); this.closeElement.classList.add("fn__flex-1"); } else { this.closeElement.lastElementChild.classList.add("fn__none"); this.closeElement.classList.remove("fn__flex-1"); } } private onMkdir(data: { box: INotebook, path: string, }) { let targetElement = this.element.querySelector(`ul[data-url="${data.box.id}"]`); let folderPath = pathPosix().dirname(data.path) + ".sy"; while (folderPath !== "/") { targetElement = targetElement.querySelector(`li[data-path="${folderPath}"]`); if (targetElement) { break; } else { targetElement = this.element.querySelector(`ul[data-url="${data.box.id}"]`); // 向上查找 if (folderPath === "/.sy") { folderPath = "/"; // https://github.com/siyuan-note/siyuan/issues/3895 } else { folderPath = pathPosix().dirname(folderPath) + ".sy"; } } } if (targetElement.tagName === "UL") { // 日记不存在时,创建日记需要添加文档 targetElement = targetElement.firstElementChild as HTMLElement; } if (targetElement) { targetElement.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open"); targetElement.querySelector(".b3-list-item__toggle").classList.remove("fn__hidden"); const emojiElement = targetElement.querySelector(".b3-list-item__icon"); if (emojiElement.innerHTML === unicode2Emoji(Constants.SIYUAN_IMAGE_FILE)) { emojiElement.innerHTML = unicode2Emoji(Constants.SIYUAN_IMAGE_FOLDER); } if (targetElement.nextElementSibling && targetElement.nextElementSibling.tagName === "UL") { targetElement.nextElementSibling.remove(); } this.getLeaf(targetElement, data.box.id); } } private onRemove(data: IWebSocketData) { // "doc2heading" 后删除文件或挂载帮助文档前的 unmount if (data.cmd === "unmount") { setNoteBook((notebooks) => { const targetElement = this.element.querySelector(`ul[data-url="${data.data.box}"] li[data-path="${"/"}"]`); if (targetElement) { targetElement.parentElement.remove(); if (Constants.CB_MOUNT_REMOVE !== data.callback) { let closeHTML = ""; notebooks.find(item => { if (item.closed) { closeHTML += this.genNotebook(item); } }); this.closeElement.lastElementChild.innerHTML = closeHTML; } } }); if (Constants.CB_MOUNT_REMOVE === data.callback) { const removeElement = this.closeElement.querySelector(`li[data-url="${data.data.box}"]`); if (removeElement) { removeElement.remove(); } } return; } data.data.ids.forEach((item: string) => { const targetElement = this.element.querySelector(`li.b3-list-item[data-node-id="${item}"]`); if (targetElement) { // 子节点展开则删除 if (targetElement.nextElementSibling?.tagName === "UL") { targetElement.nextElementSibling.remove(); } // 移除当前节点 const parentElement = targetElement.parentElement.previousElementSibling as HTMLElement; if (targetElement.parentElement.childElementCount === 1) { if (parentElement) { const iconElement = parentElement.querySelector("svg"); iconElement.classList.remove("b3-list-item__arrow--open"); iconElement.parentElement.classList.add("fn__hidden"); const emojiElement = iconElement.parentElement.nextElementSibling; if (emojiElement.innerHTML === unicode2Emoji(Constants.SIYUAN_IMAGE_FOLDER)) { emojiElement.innerHTML = unicode2Emoji(Constants.SIYUAN_IMAGE_FILE); } } targetElement.parentElement.remove(); } else { targetElement.remove(); } } }) } private onMount(data: { data: { box: INotebook, existed?: boolean }, callback?: string }) { if (data.data.existed) { return; } const liElement = this.closeElement.querySelector(`li[data-url="${data.data.box.id}"]`) as HTMLElement; if (liElement) { liElement.remove(); } setNoteBook((notebooks: INotebook[]) => { const html = this.genNotebook(data.data.box); if (this.element.childElementCount === 0) { this.element.innerHTML = html; } else { let previousId; notebooks.find((item, index) => { if (item.id === data.data.box.id) { while (index > 0) { if (!notebooks[index - 1].closed) { previousId = notebooks[index - 1].id; break; } else { index--; } } return true; } }); if (previousId) { this.element.querySelector(`[data-url="${previousId}"]`).insertAdjacentHTML("afterend", html); } else { this.element.insertAdjacentHTML("afterbegin", html); } } if (data.callback === Constants.CB_MOUNT_HELP) { openFileById({ id: Constants.HELP_START_PATH[window.siyuan.config.appearance.lang as "zh_CN" | "en_US"], action: [Constants.CB_GET_FOCUS] }); } }); } public onRename(data: { path: string, title: string, box: string }) { const fileItemElement = this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${data.path}"]`); if (!fileItemElement) { return; } fileItemElement.setAttribute("data-name", Lute.EscapeHTMLStr(data.title)); fileItemElement.querySelector(".b3-list-item__text").innerHTML = escapeHtml(data.title); } private onMove(data: { fromNotebook: string, toNotebook: string, fromPath: string toPath: string }) { const sourceElement = this.element.querySelector(`ul[data-url="${data.fromNotebook}"] li[data-path="${data.fromPath}"]`) as HTMLElement; if (sourceElement) { if (sourceElement.nextElementSibling && sourceElement.nextElementSibling.tagName === "UL") { sourceElement.nextElementSibling.remove(); } if (sourceElement.parentElement.childElementCount === 1) { if (sourceElement.parentElement.previousElementSibling) { sourceElement.parentElement.previousElementSibling.querySelector(".b3-list-item__toggle").classList.add("fn__hidden"); sourceElement.parentElement.previousElementSibling.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open"); const emojiElement = sourceElement.parentElement.previousElementSibling.querySelector(".b3-list-item__icon"); if (emojiElement.innerHTML === unicode2Emoji(Constants.SIYUAN_IMAGE_FOLDER)) { emojiElement.innerHTML = unicode2Emoji(Constants.SIYUAN_IMAGE_FILE); } } sourceElement.parentElement.remove(); } else { sourceElement.remove(); } } const newElement = this.element.querySelector(`[data-url="${data.toNotebook}"] li[data-path="${data.toPath}"]`) as HTMLElement; // 更新移动到的新文件夹 if (newElement) { newElement.querySelector(".b3-list-item__toggle").classList.remove("fn__hidden"); const emojiElement = newElement.querySelector(".b3-list-item__icon"); if (emojiElement.innerHTML === unicode2Emoji(Constants.SIYUAN_IMAGE_FILE)) { emojiElement.innerHTML = unicode2Emoji(Constants.SIYUAN_IMAGE_FOLDER); } const arrowElement = newElement.querySelector(".b3-list-item__arrow"); if (arrowElement.classList.contains("b3-list-item__arrow--open")) { arrowElement.classList.remove("b3-list-item__arrow--open"); if (newElement.nextElementSibling && newElement.nextElementSibling.tagName === "UL") { newElement.nextElementSibling.remove(); } this.getLeaf(newElement, data.toNotebook); } } } private onLsHTML(data: { files: IFile[], box: string, path: string }) { let fileHTML = ""; data.files.forEach((item: IFile) => { fileHTML += this.genFileHTML(item); }); if (fileHTML === "") { return; } const liElement = this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${data.path}"]`); let nextElement = liElement.nextElementSibling; if (nextElement && nextElement.tagName === "UL") { // 文件展开时,刷新 nextElement.remove(); } liElement.querySelector(".b3-list-item__arrow").classList.add("b3-list-item__arrow--open"); liElement.insertAdjacentHTML("afterend", ``); nextElement = liElement.nextElementSibling; setTimeout(() => { nextElement.setAttribute("style", `height:${nextElement.childElementCount * liElement.clientHeight}px;`); setTimeout(() => { this.element.querySelectorAll(".file-tree__sliderDown").forEach(item => { item.classList.remove("file-tree__sliderDown"); item.removeAttribute("style"); }); }, 120); }, 2); } private onLsSelect(data: { files: IFile[], box: string, path: string }, filePath: string) { let fileHTML = ""; data.files.forEach((item: IFile) => { fileHTML += this.genFileHTML(item); if (filePath === item.path) { this.selectItem(data.box, filePath); } else if (filePath.startsWith(item.path.replace(".sy", ""))) { fetchPost("/api/filetree/listDocsByPath", { notebook: data.box, path: item.path, sort: window.siyuan.config.fileTree.sort }, response => { this.selectItem(response.data.box, filePath, response.data); }); } }); if (fileHTML === "") { return; } const liElement = this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${data.path}"]`); if (liElement.nextElementSibling && liElement.nextElementSibling.tagName === "UL") { // 文件展开时,刷新 liElement.nextElementSibling.remove(); } liElement.querySelector(".b3-list-item__arrow").classList.add("b3-list-item__arrow--open"); liElement.insertAdjacentHTML("afterend", ``); this.setCurrent(this.element.querySelector(`ul[data-url="${data.box}"] li[data-path="${filePath}"]`)); } private setCurrent(target: HTMLElement, isScroll = true) { if (!target) { return; } this.element.querySelectorAll("li").forEach((liItem) => { liItem.classList.remove("b3-list-item--focus"); }); target.classList.add("b3-list-item--focus"); if (isScroll) { this.element.scrollTop = target.offsetTop - this.element.clientHeight / 2 - this.actionsElement.clientHeight; } } public getLeaf(liElement: Element, notebookId: string) { const toggleElement = liElement.querySelector(".b3-list-item__arrow"); if (toggleElement.classList.contains("b3-list-item__arrow--open")) { toggleElement.classList.remove("b3-list-item__arrow--open"); liElement.nextElementSibling?.remove(); return; } fetchPost("/api/filetree/listDocsByPath", { notebook: notebookId, path: liElement.getAttribute("data-path"), sort: window.siyuan.config.fileTree.sort, }, response => { if (response.data.path === "/" && response.data.files.length === 0) { showMessage(window.siyuan.languages.emptyContent); return; } this.onLsHTML(response.data); }); } public selectItem(notebookId: string, filePath: string, data?: { files: IFile[], box: string, path: string }) { const treeElement = this.element.querySelector(`[data-url="${notebookId}"]`); if (!treeElement) { // 有文件树和编辑器的布局初始化时,文件树还未挂载 return; } let currentPath = filePath; let liElement: HTMLElement; while (!liElement) { liElement = treeElement.querySelector(`[data-path="${currentPath}"]`); if (!liElement) { const dirname = pathPosix().dirname(currentPath); if (dirname === "/") { currentPath = dirname; } else { currentPath = dirname + ".sy"; } } } if (liElement.getAttribute("data-path") === filePath) { this.setCurrent(liElement); return; } if (data && data.path === currentPath) { this.onLsSelect(data, filePath); } else { fetchPost("/api/filetree/listDocsByPath", { notebook: notebookId, path: currentPath, sort: window.siyuan.config.fileTree.sort }, response => { this.onLsSelect(response.data, filePath); }); } } private genFileHTML = (item: IFile) => { let countHTML = ""; if (item.count && item.count > 0) { countHTML = `${item.count}`; } return `
  • ${unicode2Emoji(item.icon || (item.subFileCount === 0 ? Constants.SIYUAN_IMAGE_FILE : Constants.SIYUAN_IMAGE_FOLDER))} ${getDisplayName(item.name, true, true)} ${countHTML}
  • `; }; private initMoreMenu() { window.siyuan.menus.menu.remove(); const clickEvent = (sort: number) => { window.siyuan.config.fileTree.sort = sort; fetchPost("/api/setting/setFiletree", { sort: window.siyuan.config.fileTree.sort, alwaysSelectOpenedFile: window.siyuan.config.fileTree.alwaysSelectOpenedFile, refCreateSavePath: window.siyuan.config.fileTree.refCreateSavePath, createDocNameTemplate: window.siyuan.config.fileTree.createDocNameTemplate, openFilesUseCurrentTab: window.siyuan.config.fileTree.openFilesUseCurrentTab, maxListCount: window.siyuan.config.fileTree.maxListCount, }, () => { setNoteBook(() => { this.init(false); }); }); }; if (!window.siyuan.config.readonly) { window.siyuan.menus.menu.append(new MenuItem({ icon: "iconFilesRoot", label: window.siyuan.languages.newNotebook, click: () => { newNotebook(); } }).element); } window.siyuan.menus.menu.append(new MenuItem({ icon: "iconRefresh", label: window.siyuan.languages.rebuildIndex, click: () => { if (!this.element.getAttribute("disabled")) { this.element.setAttribute("disabled", "disabled"); fetchPost("/api/filetree/refreshFiletree", {}, () => { this.element.removeAttribute("disabled"); this.init(false); }); } } }).element); if (!window.siyuan.config.readonly) { window.siyuan.menus.menu.append(new MenuItem({ icon: "iconSort", label: window.siyuan.languages.sort, type: "submenu", submenu: [{ icon: window.siyuan.config.fileTree.sort === 0 ? "iconSelect" : undefined, label: window.siyuan.languages.fileNameASC, click: () => { clickEvent(0); } }, { icon: window.siyuan.config.fileTree.sort === 1 ? "iconSelect" : undefined, label: window.siyuan.languages.fileNameDESC, click: () => { clickEvent(1); } }, { icon: window.siyuan.config.fileTree.sort === 4 ? "iconSelect" : undefined, label: window.siyuan.languages.fileNameNatASC, click: () => { clickEvent(4); } }, { icon: window.siyuan.config.fileTree.sort === 5 ? "iconSelect" : undefined, label: window.siyuan.languages.fileNameNatDESC, click: () => { clickEvent(5); } }, {type: "separator"}, { icon: window.siyuan.config.fileTree.sort === 9 ? "iconSelect" : undefined, label: window.siyuan.languages.createdASC, click: () => { clickEvent(9); } }, { icon: window.siyuan.config.fileTree.sort === 10 ? "iconSelect" : undefined, label: window.siyuan.languages.createdDESC, click: () => { clickEvent(10); } }, { icon: window.siyuan.config.fileTree.sort === 2 ? "iconSelect" : undefined, label: window.siyuan.languages.modifiedASC, click: () => { clickEvent(2); } }, { icon: window.siyuan.config.fileTree.sort === 3 ? "iconSelect" : undefined, label: window.siyuan.languages.modifiedDESC, click: () => { clickEvent(3); } }, {type: "separator"}, { icon: window.siyuan.config.fileTree.sort === 7 ? "iconSelect" : undefined, label: window.siyuan.languages.refCountASC, click: () => { clickEvent(7); } }, { icon: window.siyuan.config.fileTree.sort === 8 ? "iconSelect" : undefined, label: window.siyuan.languages.refCountDESC, click: () => { clickEvent(8); } }, {type: "separator"}, { icon: window.siyuan.config.fileTree.sort === 11 ? "iconSelect" : undefined, label: window.siyuan.languages.docSizeASC, click: () => { clickEvent(11); } }, { icon: window.siyuan.config.fileTree.sort === 12 ? "iconSelect" : undefined, label: window.siyuan.languages.docSizeDESC, click: () => { clickEvent(12); } }, {type: "separator"}, { icon: window.siyuan.config.fileTree.sort === 13 ? "iconSelect" : undefined, label: window.siyuan.languages.subDocCountASC, click: () => { clickEvent(13); } }, { icon: window.siyuan.config.fileTree.sort === 14 ? "iconSelect" : undefined, label: window.siyuan.languages.subDocCountDESC, click: () => { clickEvent(14); } }, {type: "separator"}, { icon: window.siyuan.config.fileTree.sort === 6 ? "iconSelect" : undefined, label: window.siyuan.languages.customSort, click: () => { clickEvent(6); } }] }).element); } return window.siyuan.menus.menu; } }