siyuan/app/src/layout/Wnd.ts
Vanessa e5143a8474 🚨
2023-04-09 19:46:57 +08:00

932 lines
42 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {Layout} from "./index";
import {genUUID} from "../util/genID";
import {
getInstanceById,
getWndByLayout, JSONToCenter,
newCenterEmptyTab, pdfIsLoading,
resizeTabs,
setPanelFocus,
switchWnd
} from "./util";
import {Tab} from "./Tab";
import {Model} from "./Model";
import {Editor} from "../editor";
import {Graph} from "./dock/Graph";
import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName} from "../protyle/util/hasClosest";
import {Constants} from "../constants";
/// #if !BROWSER
import {webFrame, ipcRenderer} from "electron";
import {getCurrentWindow} from "@electron/remote";
import {setTabPosition} from "../window/setHeader";
/// #endif
import {Search} from "../search";
import {showMessage} from "../dialog/message";
import {openFileById, updatePanelByEditor} from "../editor/util";
import {scrollCenter} from "../util/highlightById";
import {getAllModels} from "./getAll";
import {clearCounter} from "./status";
import {saveScroll} from "../protyle/scroll/saveScroll";
import {Asset} from "../asset";
import {newFile} from "../util/newFile";
import {MenuItem} from "../menus/Menu";
import {escapeHtml} from "../util/escape";
import {isWindow} from "../util/functions";
import {hideAllElements} from "../protyle/ui/hideElements";
import {focusByOffset, getSelectionOffset} from "../protyle/util/selection";
export class Wnd {
public id: string;
public parent?: Layout;
public element: HTMLElement;
public headersElement: HTMLElement;
public children: Tab[] = [];
public resize?: TDirection;
constructor(resize?: TDirection, parentType?: TLayout) {
this.id = genUUID();
this.resize = resize;
this.element = document.createElement("div");
this.element.classList.add("fn__flex-1", "fn__flex");
let dragHTML = '<div class="layout-tab-container__drag fn__none"></div>';
if (parentType === "left" || parentType === "right" || parentType === "bottom") {
dragHTML = "";
}
this.element.innerHTML = `<div data-type="wnd" data-id="${this.id}" class="fn__flex-column fn__flex fn__flex-1">
<div class="fn__flex fn__none">
<ul class="fn__flex layout-tab-bar"></ul>
<ul class="layout-tab-bar layout-tab-bar--readonly fn__flex-1">
<li class="item item--readonly">
<span data-type="new" class="block__icon block__icon--show" title="${window.siyuan.languages.newFile}"><svg><use xlink:href="#iconAdd"></use></svg></span>
<span class="fn__flex-1"></span>
<span data-type="more" data-menu="true" class="block__icon block__icon--show" title="${window.siyuan.languages.more}"><svg><use xlink:href="#iconDown"></use></svg></span>
</li>
</ul>
</div>
<div class="layout-tab-container fn__flex-1">${dragHTML}</div>
</div>`;
this.headersElement = this.element.querySelector(".layout-tab-bar");
const dragElement = this.element.querySelector(".layout-tab-container__drag") as HTMLElement;
if (!dragElement) {
return;
}
this.headersElement.addEventListener("mousedown", (event) => {
// 点击鼠标滚轮关闭
if (event.button !== 1) {
return;
}
let target = event.target as HTMLElement;
while (target && !target.isEqualNode(this.headersElement)) {
if (target.tagName === "LI") {
this.removeTab(target.getAttribute("data-id"));
window.siyuan.menus.menu.remove();
event.stopPropagation();
event.preventDefault();
break;
}
target = target.parentElement;
}
});
this.headersElement.addEventListener("mousewheel", (event: WheelEvent) => {
this.headersElement.scrollLeft = this.headersElement.scrollLeft + event.deltaY;
}, {passive: true});
this.headersElement.parentElement.addEventListener("click", (event) => {
let target = event.target as HTMLElement;
while (target && !target.isEqualNode(this.headersElement)) {
if (target.classList.contains("block__icon") && target.getAttribute("data-type") === "new") {
setPanelFocus(this.headersElement.parentElement.parentElement);
newFile(undefined, undefined, undefined, true);
break;
} else if (target.classList.contains("block__icon") && target.getAttribute("data-type") === "more") {
this.renderTabList(event);
break;
} else if (target.tagName === "LI" && target.getAttribute("data-id") && !pdfIsLoading(this.element)) {
this.switchTab(target, true);
break;
}
target = target.parentElement;
}
});
this.headersElement.parentElement.addEventListener("dblclick", (event) => {
let target = event.target as HTMLElement;
while (target && !target.isEqualNode(this.headersElement)) {
if (window.siyuan.config.fileTree.openFilesUseCurrentTab && target.getAttribute("data-type") === "tab-header") {
target.classList.remove("item--unupdate");
break;
} else if (target.tagName === "SPAN" && target.className === "fn__flex-1" &&
isWindow() && this.headersElement.getBoundingClientRect().top <= 0) {
/// #if !BROWSER
const currentWindow = getCurrentWindow();
if (currentWindow.isMaximized()) {
currentWindow.unmaximize();
} else {
currentWindow.maximize();
}
/// #endif
break;
}
target = target.parentElement;
}
});
this.headersElement.parentElement.addEventListener("dragover", function (event: DragEvent & {
target: HTMLElement
}) {
const it = this as HTMLElement;
if (event.dataTransfer.types.includes(Constants.SIYUAN_DROP_FILE)) {
event.preventDefault();
it.classList.add("layout-tab-bars--drag");
return;
}
// 不能使用 !window.siyuan.dragElement因为移动页签到新窗口后再把主窗口页签拖拽新窗口页签上时该值为空
if (!event.dataTransfer.types.includes(Constants.SIYUAN_DROP_TAB)) {
return;
}
event.preventDefault();
let oldTabHeaderElement = window.siyuan.dragElement;
let exitDrag = false;
Array.from(it.firstElementChild.childNodes).find((item: HTMLElement) => {
if (item.style.opacity === "0.1") {
oldTabHeaderElement = item;
exitDrag = true;
return true;
}
});
if (!exitDrag && oldTabHeaderElement) {
if (oldTabHeaderElement.classList.contains("item--pin")) {
return;
}
oldTabHeaderElement = oldTabHeaderElement.cloneNode(true) as HTMLElement;
oldTabHeaderElement.setAttribute("data-clone", "true");
it.firstElementChild.append(oldTabHeaderElement);
return;
} else if (!exitDrag && !oldTabHeaderElement) { // 拖拽到新窗口
oldTabHeaderElement = document.createElement("li");
oldTabHeaderElement.style.opacity = "0.1";
oldTabHeaderElement.innerHTML = '<svg class="svg"><use xlink:href="#iconFile"></use></svg>';
oldTabHeaderElement.setAttribute("data-clone", "true");
it.firstElementChild.append(oldTabHeaderElement);
}
const newTabHeaderElement = hasClosestByAttribute(event.target, "data-type", "tab-header");
if (!newTabHeaderElement) {
if (!oldTabHeaderElement.classList.contains("item--pin")) {
it.classList.add("layout-tab-bars--drag");
}
return;
}
if (!newTabHeaderElement.isSameNode(oldTabHeaderElement) &&
((oldTabHeaderElement.classList.contains("item--pin") && newTabHeaderElement.classList.contains("item--pin")) ||
(!oldTabHeaderElement.classList.contains("item--pin") && !newTabHeaderElement.classList.contains("item--pin")))) {
const rect = newTabHeaderElement.getClientRects()[0];
if (event.clientX > rect.left + rect.width / 2) {
newTabHeaderElement.after(oldTabHeaderElement);
} else {
newTabHeaderElement.before(oldTabHeaderElement);
}
}
});
let dragleaveTimeout: number;
this.headersElement.parentElement.addEventListener("dragleave", function () {
clearTimeout(dragleaveTimeout);
// 窗口拖拽到新窗口时,不 drop 无法移除 clone 的元素
dragleaveTimeout = window.setTimeout(() => {
document.querySelectorAll(".layout-tab-bar li[data-clone='true']").forEach(item => {
item.remove();
});
}, 1000);
const it = this as HTMLElement;
it.classList.remove("layout-tab-bars--drag");
});
this.headersElement.parentElement.addEventListener("drop", function (event: DragEvent & {
target: HTMLElement
}) {
const it = this as HTMLElement;
if (event.dataTransfer.types.includes(Constants.SIYUAN_DROP_FILE)) {
// 文档树拖拽
setPanelFocus(it.parentElement);
event.dataTransfer.getData(Constants.SIYUAN_DROP_FILE).split(",").forEach(item => {
if (item) {
openFileById({
id: item,
action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL]
});
}
});
window.siyuan.dragElement = undefined;
it.classList.remove("layout-tab-bars--drag");
return;
}
const tabData = JSON.parse(event.dataTransfer.getData(Constants.SIYUAN_DROP_TAB));
let oldTab = getInstanceById(tabData.id) as Tab;
const wnd = getInstanceById(it.parentElement.getAttribute("data-id")) as Wnd;
/// #if !BROWSER
if (!oldTab) { // 从主窗口拖拽到页签新窗口
if (wnd instanceof Wnd) {
JSONToCenter(tabData, wnd);
oldTab = wnd.children[wnd.children.length - 1];
ipcRenderer.send(Constants.SIYUAN_SEND_WINDOWS, {cmd: "closetab", data: tabData.id});
it.querySelector("li[data-clone='true']").remove();
wnd.switchTab(oldTab.headElement);
}
}
/// #endif
it.classList.remove("layout-tab-bars--drag");
if (!oldTab) {
return;
}
const nextTabHeaderElement = (Array.from(it.firstElementChild.childNodes).find((item: HTMLElement) => {
if (item.style.opacity === "0.1") {
return true;
}
}) as HTMLElement)?.nextElementSibling;
if (!it.contains(oldTab.headElement)) {
// 从其他 Wnd 拖动过来
const cloneTabElement = it.querySelector("[data-clone='true']");
if (!cloneTabElement) {
return;
}
cloneTabElement.before(oldTab.headElement);
cloneTabElement.remove();
if (oldTab.model instanceof Asset) {
// https://github.com/siyuan-note/siyuan/issues/6890
const pdfViewerElement = oldTab.model.element.querySelector("#viewerContainer");
if (pdfViewerElement) {
pdfViewerElement.setAttribute("data-scrolltop", pdfViewerElement.scrollTop.toString());
}
}
// 对象顺序
wnd.moveTab(oldTab, nextTabHeaderElement ? nextTabHeaderElement.getAttribute("data-id") : undefined);
resizeTabs();
return;
}
let tempTab: Tab;
oldTab.parent.children.find((item, index) => {
if (item.id === oldTab.id) {
tempTab = oldTab.parent.children.splice(index, 1)[0];
return true;
}
});
if (nextTabHeaderElement) {
oldTab.parent.children.find((item, index) => {
if (item.id === nextTabHeaderElement.getAttribute("data-id")) {
oldTab.parent.children.splice(index, 0, tempTab);
return true;
}
});
} else {
oldTab.parent.children.push(tempTab);
}
});
this.element.addEventListener("dragenter", (event: DragEvent & { target: HTMLElement }) => {
if (event.dataTransfer.types.includes(Constants.SIYUAN_DROP_TAB)) {
const tabHeadersElement = hasClosestByClassName(event.target, "layout-tab-bar");
if (tabHeadersElement) {
return;
}
const tabPanelsElement = hasClosestByClassName(event.target, "layout-tab-container", true);
if (tabPanelsElement) {
dragElement.classList.remove("fn__none");
dragElement.setAttribute("style", "height:100%;width:100%;right:auto;bottom:auto");
}
}
});
dragElement.addEventListener("dragover", (event: DragEvent & { layerX: number, layerY: number }) => {
event.preventDefault();
if (!dragElement.nextElementSibling) {
return;
}
const rect = dragElement.parentElement.getBoundingClientRect();
const height = rect.height;
const width = rect.width;
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
if ((x <= width / 3 && (y <= height / 8 || y >= height * 7 / 8)) ||
(x <= width / 8 && (y > height / 8 || y < height * 7 / 8))) {
dragElement.setAttribute("style", "height:100%;width:50%;right:50%;bottom:0;left:0;top:0");
} else if ((x > width * 2 / 3 && (y <= height / 8 || y >= height * 7 / 8)) ||
(x >= width * 7 / 8 && (y > height / 8 || y < height * 7 / 8))) {
dragElement.setAttribute("style", "height:100%;width:50%;right:0;bottom:0;left:50%;top:0");
} else if (x > width / 3 && x < width * 2 / 3 && y <= height / 8) {
dragElement.setAttribute("style", "height:50%;width:100%;right:0;bottom:50%;left:0;top:0");
} else if (x > width / 3 && x < width * 2 / 3 && y >= height * 7 / 8) {
dragElement.setAttribute("style", "height:50%;width:100%;right:0;bottom:0;left:0;top:50%");
} else {
dragElement.setAttribute("style", "height:100%;width:100%;right:0;bottom:0;top:0;left:0");
}
});
dragElement.addEventListener("dragleave", () => {
dragElement.classList.add("fn__none");
});
dragElement.addEventListener("drop", (event: DragEvent & { target: HTMLElement }) => {
dragElement.classList.add("fn__none");
const targetWndElement = event.target.parentElement.parentElement;
const targetWnd = getInstanceById(targetWndElement.getAttribute("data-id")) as Wnd;
const tabData = JSON.parse(event.dataTransfer.getData(Constants.SIYUAN_DROP_TAB));
let oldTab = getInstanceById(tabData.id) as Tab;
/// #if !BROWSER
if (!oldTab) { // 从主窗口拖拽到页签新窗口
JSONToCenter(tabData, this);
oldTab = this.children[this.children.length - 1];
ipcRenderer.send(Constants.SIYUAN_SEND_WINDOWS, {cmd: "closetab", data: tabData.id});
}
/// #endif
if (!oldTab) {
return;
}
if (oldTab.model instanceof Asset) {
// https://github.com/siyuan-note/siyuan/issues/6890
const pdfViewerElement = oldTab.model.element.querySelector("#viewerContainer");
if (pdfViewerElement) {
pdfViewerElement.setAttribute("data-scrolltop", pdfViewerElement.scrollTop.toString());
}
}
if (dragElement.style.height === "50%" || dragElement.style.width === "50%") {
// split
if (dragElement.style.height === "50%") {
// split to bottom
const newWnd = targetWnd.split("tb");
newWnd.headersElement.append(oldTab.headElement);
newWnd.headersElement.parentElement.classList.remove("fn__none");
newWnd.moveTab(oldTab);
if (dragElement.style.bottom === "50%" && newWnd.element.previousElementSibling && targetWnd.element.parentElement) {
// 交换位置
switchWnd(newWnd, targetWnd);
}
} else if (dragElement.style.width === "50%") {
// split to right
const newWnd = targetWnd.split("lr");
newWnd.headersElement.append(oldTab.headElement);
newWnd.headersElement.parentElement.classList.remove("fn__none");
newWnd.moveTab(oldTab);
if (dragElement.style.right === "50%" && newWnd.element.previousElementSibling && targetWnd.element.parentElement) {
// 交换位置
switchWnd(newWnd, targetWnd);
}
}
resizeTabs();
/// #if !BROWSER
setTabPosition();
/// #endif
return;
}
if (targetWndElement.contains(document.querySelector(`[data-id="${tabData.id}"]`))) {
return;
}
if (targetWnd) {
targetWnd.headersElement.append(oldTab.headElement);
targetWnd.headersElement.parentElement.classList.remove("fn__none");
targetWnd.moveTab(oldTab);
resizeTabs();
}
});
}
public showHeading() {
const currentElement = this.headersElement.querySelector(".item--focus") as HTMLElement;
if (!currentElement) {
return;
}
if (currentElement.offsetLeft + currentElement.clientWidth > this.headersElement.scrollLeft + this.headersElement.clientWidth) {
this.headersElement.scrollLeft = currentElement.offsetLeft + currentElement.clientWidth - this.headersElement.clientWidth;
} else if (currentElement.offsetLeft < this.headersElement.scrollLeft) {
this.headersElement.scrollLeft = currentElement.offsetLeft;
}
}
public switchTab(target: HTMLElement, pushBack = false, update = true) {
setPanelFocus(this.headersElement.parentElement.parentElement);
let currentTab: Tab;
this.children.forEach((item) => {
if (target === item.headElement) {
if (item.headElement && item.headElement.classList.contains("fn__none")) {
// https://github.com/siyuan-note/siyuan/issues/267
} else {
if (item.headElement) {
item.headElement.classList.add("item--focus");
item.headElement.setAttribute("data-activetime", (new Date()).getTime().toString());
}
item.panelElement.classList.remove("fn__none");
}
currentTab = item;
} else {
item.headElement?.classList.remove("item--focus");
if (!item.panelElement.classList.contains("fn__none")) {
// 必须现判断,否则会触发 observer.observe(this.element, {attributeFilter: ["class"]}); 导致 https://ld246.com/article/1641198819303
item.panelElement.classList.add("fn__none");
}
}
});
if (currentTab && currentTab.headElement) {
const initData = currentTab.headElement.getAttribute("data-initdata");
if (initData) {
const json = JSON.parse(initData);
currentTab.addModel(new Editor({
tab: currentTab,
blockId: json.blockId,
mode: json.mode,
action: typeof json.action === "string" ? [json.action] : json.action,
scrollAttr: json.scrollAttr,
}));
currentTab.headElement.removeAttribute("data-initdata");
return;
}
}
if (currentTab && target === currentTab.headElement) {
if (currentTab.model instanceof Graph) {
currentTab.model.onGraph(false);
} else if (currentTab.model instanceof Asset && currentTab.model.pdfObject && currentTab.model.pdfObject.pdfViewer) {
// https://github.com/siyuan-note/siyuan/issues/5655
currentTab.model.pdfObject.pdfViewer.container.focus();
}
}
if (currentTab && currentTab.model instanceof Editor) {
const keepCursorId = currentTab.headElement.getAttribute("keep-cursor");
if (keepCursorId) {
// 在新页签中打开,但不跳转到新页签,但切换到新页签时需调整滚动
let nodeElement: HTMLElement;
Array.from(currentTab.model.editor.protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${keepCursorId}"]`)).find((item: HTMLElement) => {
if (!hasClosestByAttribute(item, "data-type", "NodeBlockQueryEmbed", true)) {
nodeElement = item;
return true;
}
});
if (nodeElement) {
if (!currentTab.model.editor.protyle.toolbar.range) {
const range = document.createRange();
range.selectNodeContents(nodeElement);
range.collapse();
currentTab.model.editor.protyle.toolbar.range = range;
}
scrollCenter(currentTab.model.editor.protyle, nodeElement, true);
} else {
openFileById({
id: keepCursorId,
action: [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT]
});
}
currentTab.headElement.removeAttribute("keep-cursor");
}
// focusin 触发前layout__wnd--active 和 tab 已设置,需在调用里面更新
if (update) {
updatePanelByEditor(currentTab.model.editor.protyle, true, pushBack);
}
} else {
updatePanelByEditor(undefined, false);
}
}
public addTab(tab: Tab, keepCursor = false) {
if (keepCursor) {
tab.headElement?.classList.remove("item--focus");
tab.panelElement.classList.add("fn__none");
}
let oldFocusIndex = 0;
this.children.forEach((item, index) => {
if (item.headElement && item.headElement.classList.contains("item--focus")) {
oldFocusIndex = index;
let nextElement = item.headElement.nextElementSibling;
while (nextElement && nextElement.classList.contains("item--pin")) {
oldFocusIndex++;
nextElement = nextElement.nextElementSibling;
}
}
if (!keepCursor) {
item.headElement?.classList.remove("item--focus");
item.panelElement.classList.add("fn__none");
}
});
this.children.splice(oldFocusIndex + 1, 0, tab);
if (tab.headElement) {
this.headersElement.parentElement.classList.remove("fn__none");
if (this.headersElement.childElementCount === 0) {
this.headersElement.append(tab.headElement);
} else {
this.headersElement.children[oldFocusIndex].after(tab.headElement);
}
tab.headElement.querySelector(".item__close").addEventListener("click", (event) => {
if (tab.headElement.classList.contains("item--pin")) {
tab.unpin();
} else {
tab.parent.removeTab(tab.id);
}
window.siyuan.menus.menu.remove();
event.stopPropagation();
event.preventDefault();
});
tab.headElement.setAttribute("data-activetime", (new Date()).getTime().toString());
}
const containerElement = this.element.querySelector(".layout-tab-container");
if (!containerElement.querySelector(".fn__flex-1")) {
// empty center
containerElement.append(tab.panelElement);
} else if (!containerElement.querySelector(".layout-tab-container__drag")) {
// Dock
containerElement.children[oldFocusIndex].after(tab.panelElement);
} else {
containerElement.children[oldFocusIndex + 1].after(tab.panelElement);
}
tab.parent = this;
if (tab.callback) {
tab.callback(tab);
}
// 移除 centerLayout 中的 empty
if (this.parent.type === "center" && this.children.length === 2 && !this.children[0].headElement) {
this.removeTab(this.children[0].id);
} else if (this.children.length > window.siyuan.config.fileTree.maxOpenTabCount) {
this.removeOverCounter(oldFocusIndex);
}
/// #if !BROWSER
setTabPosition();
/// #endif
}
private renderTabList(event: MouseEvent) {
window.siyuan.menus.menu.remove();
window.siyuan.menus.menu.element.classList.add("b3-menu--list");
Array.from(this.headersElement.children).forEach((item: HTMLElement) => {
const iconElement = item.querySelector(".item__icon");
const graphicElement = item.querySelector(".item__graphic");
window.siyuan.menus.menu.append(new MenuItem({
label: escapeHtml(item.querySelector(".item__text").textContent),
action: "iconCloseRound",
iconHTML: iconElement ? `<span class="b3-menu__icon">${iconElement.innerHTML}</span>` : "",
icon: graphicElement ? graphicElement.firstElementChild.getAttribute("xlink:href").substring(1) : "",
bind: (element) => {
element.addEventListener("click", (event) => {
if (hasClosestByClassName(event.target as Element, "b3-menu__action")) {
this.removeTab(item.getAttribute("data-id"));
if (element.previousElementSibling || element.nextElementSibling) {
element.remove();
} else {
window.siyuan.menus.menu.remove();
}
} else {
this.switchTab(item, true);
this.showHeading();
window.siyuan.menus.menu.remove();
}
event.preventDefault();
event.stopPropagation();
});
},
current: item.classList.contains("item--focus")
}).element);
});
window.siyuan.menus.menu.popup({
x: event.clientX,
y: event.clientY,
w: 16
});
}
private removeOverCounter(oldFocusIndex?: number) {
if (typeof oldFocusIndex === "undefined") {
this.children.forEach((item, index) => {
if (item.headElement && item.headElement.classList.contains("item--focus")) {
oldFocusIndex = index;
}
});
}
let removeId: string;
let openTime: string;
this.children.forEach((item, index) => {
if (item.headElement.classList.contains("item--pin") ||
item.headElement.classList.contains("item--focus") ||
index === oldFocusIndex) {
return;
}
if (!openTime) {
openTime = item.headElement.getAttribute("data-activetime");
removeId = this.children[index].id;
} else if (item.headElement.getAttribute("data-activetime") < openTime) {
openTime = item.headElement.getAttribute("data-activetime");
removeId = this.children[index].id;
}
});
if (removeId) {
this.removeTab(removeId);
}
}
private destroyModel(model: Model) {
if (!model) {
return;
}
if (model instanceof Editor && model.editor) {
window.siyuan.blockPanels.forEach((item) => {
if (item.element && model.editor.protyle.wysiwyg.element.contains(item.element)) {
item.destroy();
}
});
model.editor.destroy();
return;
}
if (model instanceof Search) {
if (model.edit) {
model.edit.destroy();
}
return;
}
if (model instanceof Asset) {
if (model.pdfObject && model.pdfObject.pdfLoadingTask) {
model.pdfObject.pdfLoadingTask.destroy();
}
}
model.send("closews", {});
}
private removeTabAction = (id: string, closeAll = false, hasSaveScroll = true, animate = true) => {
clearCounter();
this.children.find((item, index) => {
if (item.id === id) {
if (item.model instanceof Editor && hasSaveScroll) {
saveScroll(item.model.editor.protyle);
}
if (this.children.length === 1) {
this.destroyModel(this.children[0].model);
this.children = [];
if (["bottom", "left", "right"].includes(this.parent.type)) {
item.panelElement.remove();
} else {
this.remove();
}
// 关闭分屏页签后光标消失
const editors = getAllModels().editor;
if (editors.length === 0) {
updatePanelByEditor();
} else {
editors.forEach(item => {
if (!item.element.classList.contains("fn__none")) {
setPanelFocus(item.parent.parent.headersElement.parentElement.parentElement);
updatePanelByEditor(item.editor.protyle, true, true);
return;
}
});
}
/// #if !BROWSER
setTabPosition();
/// #endif
return;
}
if (item.headElement) {
if (item.headElement.classList.contains("item--focus")) {
let latestHeadElement: HTMLElement;
Array.from(item.headElement.parentElement.children).forEach((headItem: HTMLElement) => {
if (!headItem.isSameNode(item.headElement) &&
headItem.style.maxWidth !== "0px" // 不对比已移除但还在动画效果中的元素 https://github.com/siyuan-note/siyuan/issues/7878
) {
if (!latestHeadElement) {
latestHeadElement = headItem;
} else if (headItem.getAttribute("data-activetime") > latestHeadElement.getAttribute("data-activetime")) {
latestHeadElement = headItem;
}
}
});
if (latestHeadElement && !closeAll) {
this.switchTab(latestHeadElement, true);
}
}
if (animate) {
item.headElement.setAttribute("style", "max-width: 0px;");
setTimeout(() => {
item.headElement.remove();
}, Constants.TIMEOUT_TRANSITION);
} else {
item.headElement.remove();
}
}
item.panelElement.remove();
this.destroyModel(item.model);
this.children.splice(index, 1);
resizeTabs();
return true;
}
});
// 初始化移除窗口,但 centerLayout 还没有赋值 https://ld246.com/article/1658718634416
if (window.siyuan.layout.centerLayout) {
const wnd = getWndByLayout(window.siyuan.layout.centerLayout);
if (!wnd) {
/// #if !BROWSER
if (isWindow()) {
getCurrentWindow().destroy();
return;
}
/// #endif
const wnd = new Wnd();
window.siyuan.layout.centerLayout.addWnd(wnd);
wnd.addTab(newCenterEmptyTab());
}
}
/// #if !BROWSER
webFrame.clearCache();
getCurrentWindow().webContents.session.clearCache();
/// #endif
};
public removeTab(id: string, closeAll = false, needSaveScroll = true, animate = true) {
for (let index = 0; index < this.children.length; index++) {
const item = this.children[index];
if (item.id === id) {
if ((item.model instanceof Editor) && item.model.editor?.protyle) {
if (item.model.editor.protyle.upload.isUploading) {
showMessage(window.siyuan.languages.uploading);
return;
}
this.removeTabAction(id, closeAll, needSaveScroll, animate);
} else {
this.removeTabAction(id, closeAll, needSaveScroll, animate);
}
return;
}
}
}
public moveTab(tab: Tab, nextId?: string) {
let rangeData: {
id: string,
start: number,
end: number
};
if (tab.model instanceof Editor && tab.model.editor.protyle.toolbar.range) {
const blockElement = hasClosestBlock(tab.model.editor.protyle.toolbar.range.startContainer);
if (blockElement) {
const startEnd = getSelectionOffset(blockElement, undefined, tab.model.editor.protyle.toolbar.range);
rangeData = {
id: blockElement.getAttribute("data-node-id"),
start: startEnd.start,
end: startEnd.end
};
}
}
this.element.querySelector(".layout-tab-container").append(tab.panelElement);
if (rangeData && tab.model instanceof Editor) {
// DOM 移动后 range 会变化
const range = focusByOffset(tab.model.editor.protyle.wysiwyg.element.querySelector(`[data-node-id="${rangeData.id}"]`), rangeData.start, rangeData.end);
if (range) {
tab.model.editor.protyle.toolbar.range = range;
}
}
if (nextId) {
// 只能用 find https://github.com/siyuan-note/siyuan/issues/3455
this.children.find((item, index) => {
if (item.id === nextId) {
this.children.splice(index, 0, tab);
return true;
}
});
} else {
this.children.push(tab);
}
if (this.children.length > window.siyuan.config.fileTree.maxOpenTabCount) {
this.removeOverCounter();
}
this.switchTab(tab.headElement);
const oldWnd = tab.parent;
if (oldWnd.children.length === 1) {
oldWnd.children = [];
oldWnd.remove();
} else {
oldWnd.children.find((item, index) => {
if (item.id === tab.id) {
oldWnd.children.splice(index, 1);
resizeTabs();
return true;
}
});
if (!oldWnd.headersElement.querySelector(".item--focus")) {
let latestHeadElement: HTMLElement;
Array.from(oldWnd.headersElement.children).forEach((headItem: HTMLElement) => {
if (!latestHeadElement) {
latestHeadElement = headItem;
} else if (headItem.getAttribute("data-activetime") > latestHeadElement.getAttribute("data-activetime")) {
latestHeadElement = headItem;
}
});
if (latestHeadElement) {
oldWnd.switchTab(latestHeadElement, true);
}
}
}
tab.parent = this;
hideAllElements(["toolbar"]);
}
public split(direction: TDirection) {
if (this.children.length === 1 && !this.children[0].headElement) {
// 场景:没有打开的文档,点击标签面板打开
return this;
}
const wnd = new Wnd(direction);
if (direction === this.parent.direction) {
this.parent.addWnd(wnd, this.id);
} else if (this.parent.children.length === 1) {
// layout 仅含一个时,只需更新 direction
this.parent.direction = direction;
if (direction === "tb") {
this.parent.element.classList.add("fn__flex-column");
this.parent.element.classList.remove("fn__flex");
} else {
this.parent.element.classList.remove("fn__flex-column");
this.parent.element.classList.add("fn__flex");
}
this.parent.addWnd(wnd, this.id);
} else {
this.parent.children.find((item, index) => {
if (item.id === this.id) {
const layout = new Layout({
resize: item.resize,
direction,
});
this.parent.addLayout(layout, item.id);
const movedWnd = this.parent.children.splice(index, 1)[0];
if (movedWnd.resize) {
movedWnd.element.previousElementSibling.remove();
movedWnd.resize = undefined;
}
layout.addWnd.call(layout, movedWnd);
layout.addWnd.call(layout, wnd);
if (direction === "tb" && movedWnd.element.style.width) {
layout.element.style.width = movedWnd.element.style.width;
layout.element.classList.remove("fn__flex-1");
movedWnd.element.style.width = "";
movedWnd.element.classList.add("fn__flex-1");
} else if (direction === "lr" && movedWnd.element.style.height) {
layout.element.style.height = movedWnd.element.style.height;
layout.element.classList.remove("fn__flex-1");
movedWnd.element.style.height = "";
movedWnd.element.classList.add("fn__flex-1");
}
return true;
}
});
}
return wnd;
}
private remove() {
let layout = this.parent;
let element = this.element;
let id = this.id;
while (layout && layout.children.length === 1 && "center" !== layout.type) {
id = layout.id;
element = layout.element;
layout = layout.parent;
}
layout.children.find((item, index) => {
if (item.id === id) {
if (layout.children.length > 1) {
let previous = layout.children[index - 1];
if (index === 0) {
previous = layout.children[1];
}
if (layout.children.length === 2) {
if (layout.direction === "lr") {
previous.element.style.width = "";
} else {
previous.element.style.height = "";
}
previous.resize = undefined;
previous.element.classList.add("fn__flex-1");
} else if (!previous.element.classList.contains("fn__flex-1")) {
// 分屏后要均分 https://github.com/siyuan-note/siyuan/issues/5657
if (layout.direction === "lr") {
previous.element.style.width = (previous.element.clientWidth + element.clientWidth) + "px";
} else {
previous.element.style.height = (previous.element.clientHeight + element.clientHeight) + "px";
}
}
// https://github.com/siyuan-note/siyuan/issues/5844
if (layout.children.length > 2 && index === 0) {
layout.children[1].resize = undefined;
}
}
layout.children.splice(index, 1);
return true;
}
});
if (element.previousElementSibling && element.previousElementSibling.classList.contains("layout__resize")) {
element.previousElementSibling.remove();
} else if (element.nextElementSibling && element.nextElementSibling.classList.contains("layout__resize")) {
element.nextElementSibling.remove();
}
element.remove();
resizeTabs();
}
}