mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-05-16 00:50:56 +08:00
902 lines
35 KiB
TypeScript
902 lines
35 KiB
TypeScript
import {Layout} from "./index";
|
|
import {Wnd} from "./Wnd";
|
|
import {Tab} from "./Tab";
|
|
import {Model} from "./Model";
|
|
import {Graph} from "./dock/Graph";
|
|
import {Editor} from "../editor";
|
|
import {Files} from "./dock/Files";
|
|
import {Outline} from "./dock/Outline";
|
|
import {Bookmark} from "./dock/Bookmark";
|
|
import {Tag} from "./dock/Tag";
|
|
import {getAllModels, getAllTabs} from "./getAll";
|
|
import {Asset} from "../asset";
|
|
import {Search} from "../search";
|
|
import {Dock} from "./dock";
|
|
import {focusByOffset, focusByRange, getSelectionOffset} from "../protyle/util/selection";
|
|
import {hideElements} from "../protyle/ui/hideElements";
|
|
import {fetchPost} from "../util/fetch";
|
|
import {hasClosestBlock, hasClosestByClassName} from "../protyle/util/hasClosest";
|
|
import {getContenteditableElement} from "../protyle/wysiwyg/getBlock";
|
|
import {Constants} from "../constants";
|
|
import {saveScroll} from "../protyle/scroll/saveScroll";
|
|
import {Backlink} from "./dock/Backlink";
|
|
import {openFileById} from "../editor/util";
|
|
import {isWindow} from "../util/functions";
|
|
/// #if !BROWSER
|
|
import {setTabPosition} from "../window/setHeader";
|
|
/// #endif
|
|
import {showMessage} from "../dialog/message";
|
|
import {getIdZoomInByPath} from "../util/pathName";
|
|
import {Custom} from "./dock/Custom";
|
|
import {newCardModel} from "../card/newCardTab";
|
|
import {App} from "../index";
|
|
import {afterLoadPlugin} from "../plugin/loader";
|
|
import {setTitle} from "../dialog/processSystem";
|
|
import {newCenterEmptyTab, resizeTabs} from "./tabUtil";
|
|
import {setStorageVal} from "../protyle/util/compatibility";
|
|
|
|
export const setPanelFocus = (element: Element) => {
|
|
if (element.getAttribute("data-type") === "wnd") {
|
|
setTitle(element.querySelector('.layout-tab-bar .item--focus[data-type="tab-header"] .item__text')?.textContent || window.siyuan.languages.siyuanNote);
|
|
}
|
|
if (element.classList.contains("layout__tab--active") || element.classList.contains("layout__wnd--active")) {
|
|
return;
|
|
}
|
|
document.querySelectorAll(".layout__tab--active").forEach(item => {
|
|
item.classList.remove("layout__tab--active");
|
|
});
|
|
document.querySelectorAll(".dock__item--activefocus").forEach(item => {
|
|
item.classList.remove("dock__item--activefocus");
|
|
});
|
|
document.querySelectorAll(".layout__wnd--active").forEach(item => {
|
|
item.classList.remove("layout__wnd--active");
|
|
});
|
|
if (element.getAttribute("data-type") === "wnd") {
|
|
element.classList.add("layout__wnd--active");
|
|
} else {
|
|
element.classList.add("layout__tab--active");
|
|
Array.from(element.classList).find(item => {
|
|
if (item.startsWith("sy__")) {
|
|
document.querySelector(`.dock__item[data-type="${item.substring(4)}"]`).classList.add("dock__item--activefocus");
|
|
return true;
|
|
}
|
|
});
|
|
const blockElement = hasClosestBlock(document.activeElement);
|
|
if (blockElement) {
|
|
const editElement = getContenteditableElement(blockElement) as HTMLElement;
|
|
if (editElement) {
|
|
editElement.blur();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
export const switchWnd = (newWnd: Wnd, targetWnd: Wnd) => {
|
|
// DOM 移动后 range 会变化
|
|
const rangeDatas: {
|
|
id: string,
|
|
start: number,
|
|
end: number
|
|
}[] = [];
|
|
targetWnd.children.forEach((item) => {
|
|
if (item.model instanceof Editor && item.model.editor.protyle.toolbar.range) {
|
|
const blockElement = hasClosestBlock(item.model.editor.protyle.toolbar.range.startContainer);
|
|
if (blockElement) {
|
|
const startEnd = getSelectionOffset(blockElement, undefined, item.model.editor.protyle.toolbar.range);
|
|
rangeDatas.push({
|
|
id: blockElement.getAttribute("data-node-id"),
|
|
start: startEnd.start,
|
|
end: startEnd.end
|
|
});
|
|
}
|
|
}
|
|
});
|
|
newWnd.element.after(targetWnd.element);
|
|
targetWnd.children.forEach((item) => {
|
|
if (item.model instanceof Editor) {
|
|
const rangeData = rangeDatas.splice(0, 1)[0];
|
|
if (!rangeData) {
|
|
return;
|
|
}
|
|
const range = focusByOffset(item.model.editor.protyle.wysiwyg.element.querySelector(`[data-node-id="${rangeData.id}"]`), rangeData.start, rangeData.end);
|
|
if (range) {
|
|
item.model.editor.protyle.toolbar.range = range;
|
|
}
|
|
}
|
|
});
|
|
// 分隔线
|
|
newWnd.element.after(newWnd.element.previousElementSibling);
|
|
newWnd.parent.children.find((item, index) => {
|
|
if (item.id === newWnd.id) {
|
|
const tempResize = newWnd.parent.children[index].resize;
|
|
newWnd.parent.children[index].resize = newWnd.parent.children[index - 1].resize;
|
|
newWnd.parent.children[index - 1].resize = tempResize;
|
|
const temp = item;
|
|
newWnd.parent.children[index] = newWnd.parent.children[index - 1];
|
|
newWnd.parent.children[index - 1] = temp;
|
|
return true;
|
|
}
|
|
});
|
|
/// #if !BROWSER
|
|
setTabPosition();
|
|
/// #endif
|
|
};
|
|
|
|
export const getWndByLayout: (layout: Layout) => Wnd = (layout: Layout) => {
|
|
for (let i = 0; i < layout.children.length; i++) {
|
|
const item = layout.children[i];
|
|
if (item instanceof Wnd) {
|
|
return item;
|
|
} else {
|
|
return getWndByLayout(item);
|
|
}
|
|
}
|
|
};
|
|
|
|
const dockToJSON = (dock: Dock) => {
|
|
const json = [];
|
|
const subDockToJSON = (index: number) => {
|
|
const data: IDockTab[] = [];
|
|
dock.element.querySelectorAll(`span[data-index="${index}"]`).forEach(item => {
|
|
data.push({
|
|
type: item.getAttribute("data-type"),
|
|
size: {
|
|
height: parseInt(item.getAttribute("data-height")),
|
|
width: parseInt(item.getAttribute("data-width")),
|
|
},
|
|
title: item.getAttribute("data-title"),
|
|
show: item.classList.contains("dock__item--active"),
|
|
icon: item.querySelector("use").getAttribute("xlink:href").substring(1),
|
|
hotkey: item.getAttribute("data-hotkey") || "",
|
|
hotkeyLangId: item.getAttribute("data-hotkeyLangId") || ""
|
|
});
|
|
});
|
|
return data;
|
|
};
|
|
const data0 = subDockToJSON(0);
|
|
const data2 = subDockToJSON(1);
|
|
if (data0.length > 0 || data2.length > 0) {
|
|
// https://github.com/siyuan-note/siyuan/issues/5641
|
|
json.push(data0);
|
|
}
|
|
if (data2.length > 0) {
|
|
json.push(data2);
|
|
}
|
|
return {
|
|
pin: dock.pin,
|
|
data: json
|
|
};
|
|
};
|
|
|
|
export const resetLayout = () => {
|
|
fetchPost("/api/system/setUILayout", {layout: {}}, () => {
|
|
window.siyuan.storage[Constants.LOCAL_FILEPOSITION] = {};
|
|
setStorageVal(Constants.LOCAL_FILEPOSITION, window.siyuan.storage[Constants.LOCAL_FILEPOSITION]);
|
|
window.siyuan.storage[Constants.LOCAL_DIALOGPOSITION] = {};
|
|
setStorageVal(Constants.LOCAL_DIALOGPOSITION, window.siyuan.storage[Constants.LOCAL_DIALOGPOSITION]);
|
|
window.location.reload();
|
|
});
|
|
};
|
|
|
|
let saveCount = 0;
|
|
export const saveLayout = () => {
|
|
const breakObj = {};
|
|
let layoutJSON: any = {};
|
|
if (isWindow()) {
|
|
layoutJSON = {
|
|
layout: {},
|
|
};
|
|
layoutToJSON(window.siyuan.layout.layout, layoutJSON.layout, breakObj);
|
|
} else {
|
|
const useElement = document.querySelector("#barDock use");
|
|
if (useElement) {
|
|
layoutJSON = {
|
|
hideDock: useElement.getAttribute("xlink:href") === "#iconDock",
|
|
layout: {},
|
|
bottom: dockToJSON(window.siyuan.layout.bottomDock),
|
|
left: dockToJSON(window.siyuan.layout.leftDock),
|
|
right: dockToJSON(window.siyuan.layout.rightDock),
|
|
};
|
|
layoutToJSON(window.siyuan.layout.layout, layoutJSON.layout, breakObj);
|
|
}
|
|
}
|
|
|
|
if (Object.keys(breakObj).length > 0 && saveCount < 10) {
|
|
saveCount++;
|
|
setTimeout(() => {
|
|
saveLayout();
|
|
}, Constants.TIMEOUT_LOAD * saveCount);
|
|
} else {
|
|
saveCount = 0;
|
|
if (isWindow()) {
|
|
sessionStorage.setItem("layout", JSON.stringify(layoutJSON));
|
|
} else {
|
|
fetchPost("/api/system/setUILayout", {
|
|
layout: layoutJSON,
|
|
errorExit: false // 后台不接受该参数,用于请求发生错误时退出程序
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
export const exportLayout = (options: {
|
|
cb: () => void,
|
|
errorExit: boolean
|
|
}) => {
|
|
if (isWindow()) {
|
|
const layoutJSON: any = {
|
|
layout: {},
|
|
};
|
|
layoutToJSON(window.siyuan.layout.layout, layoutJSON.layout);
|
|
getAllModels().editor.forEach(item => {
|
|
saveScroll(item.editor.protyle);
|
|
});
|
|
sessionStorage.setItem("layout", JSON.stringify(layoutJSON));
|
|
options.cb();
|
|
return;
|
|
}
|
|
const useElement = document.querySelector("#barDock use");
|
|
if (!useElement) {
|
|
return;
|
|
}
|
|
const layoutJSON: any = {
|
|
hideDock: useElement.getAttribute("xlink:href") === "#iconDock",
|
|
layout: {},
|
|
bottom: dockToJSON(window.siyuan.layout.bottomDock),
|
|
left: dockToJSON(window.siyuan.layout.leftDock),
|
|
right: dockToJSON(window.siyuan.layout.rightDock),
|
|
};
|
|
layoutToJSON(window.siyuan.layout.layout, layoutJSON.layout);
|
|
getAllModels().editor.forEach(item => {
|
|
saveScroll(item.editor.protyle);
|
|
});
|
|
fetchPost("/api/system/setUILayout", {
|
|
layout: layoutJSON,
|
|
errorExit: options.errorExit // 后台不接受该参数,用于请求发生错误时退出程序
|
|
}, () => {
|
|
options.cb();
|
|
});
|
|
};
|
|
|
|
export const getAllLayout = () => {
|
|
const layoutJSON: any = {
|
|
hideDock: document.querySelector("#barDock use").getAttribute("xlink:href") === "#iconDock",
|
|
layout: {},
|
|
bottom: dockToJSON(window.siyuan.layout.bottomDock),
|
|
left: dockToJSON(window.siyuan.layout.leftDock),
|
|
right: dockToJSON(window.siyuan.layout.rightDock),
|
|
};
|
|
layoutToJSON(window.siyuan.layout.layout, layoutJSON.layout);
|
|
return layoutJSON;
|
|
};
|
|
|
|
const initInternalDock = (dockItem: IDockTab[]) => {
|
|
dockItem.forEach((existSubItem) => {
|
|
if (existSubItem.hotkeyLangId) {
|
|
existSubItem.title = window.siyuan.languages[existSubItem.hotkeyLangId];
|
|
existSubItem.hotkey = window.siyuan.config.keymap.general[existSubItem.hotkeyLangId].custom;
|
|
}
|
|
});
|
|
};
|
|
|
|
const JSONToDock = (json: any, app: App) => {
|
|
json.left.data.forEach((existItem: IDockTab[]) => {
|
|
initInternalDock(existItem);
|
|
});
|
|
json.right.data.forEach((existItem: IDockTab[]) => {
|
|
initInternalDock(existItem);
|
|
});
|
|
json.bottom.data.forEach((existItem: IDockTab[]) => {
|
|
initInternalDock(existItem);
|
|
});
|
|
window.siyuan.layout.centerLayout = window.siyuan.layout.layout.children[0].children[1] as Layout;
|
|
window.siyuan.layout.leftDock = new Dock({position: "Left", data: json.left, app});
|
|
window.siyuan.layout.rightDock = new Dock({position: "Right", data: json.right, app});
|
|
window.siyuan.layout.bottomDock = new Dock({position: "Bottom", data: json.bottom, app});
|
|
};
|
|
|
|
export const JSONToCenter = (app: App, json: ILayoutJSON, layout?: Layout | Wnd | Tab | Model) => {
|
|
let child: Layout | Wnd | Tab | Model;
|
|
if (json.instance === "Layout") {
|
|
if (!layout) {
|
|
window.siyuan.layout.layout = new Layout({
|
|
element: document.getElementById("layouts"),
|
|
direction: json.direction,
|
|
size: json.size,
|
|
type: json.type,
|
|
resize: json.resize
|
|
});
|
|
} else {
|
|
child = new Layout({
|
|
direction: json.direction,
|
|
size: json.size,
|
|
type: json.type,
|
|
resize: json.resize
|
|
});
|
|
(layout as Layout).addLayout(child);
|
|
}
|
|
} else if (json.instance === "Wnd") {
|
|
child = new Wnd(app, json.resize, (layout as Layout).type);
|
|
(layout as Layout).addWnd(child);
|
|
if (json.width) {
|
|
child.element.classList.remove("fn__flex-1");
|
|
child.element.style.width = json.width;
|
|
}
|
|
if (json.height) {
|
|
child.element.classList.remove("fn__flex-1");
|
|
child.element.style.height = json.height;
|
|
}
|
|
} else if (json.instance === "Tab") {
|
|
if (!json.title) {
|
|
child = newCenterEmptyTab(app);
|
|
} else {
|
|
let title = json.title;
|
|
if (json.lang) {
|
|
title = window.siyuan.languages[json.lang];
|
|
}
|
|
child = new Tab({
|
|
icon: json.icon,
|
|
docIcon: json.docIcon,
|
|
title
|
|
});
|
|
}
|
|
if (json.pin) {
|
|
child.headElement.classList.add("item--pin");
|
|
if (json.docIcon || json.icon) {
|
|
child.headElement.querySelector(".item__text").classList.add("fn__none");
|
|
}
|
|
}
|
|
if (json.active && child.headElement) {
|
|
child.headElement.setAttribute("data-init-active", "true");
|
|
}
|
|
(layout as Wnd).addTab(child, false, false);
|
|
(layout as Wnd).showHeading();
|
|
} else if (json.instance === "Editor" && json.blockId) {
|
|
if (window.siyuan.config.fileTree.openFilesUseCurrentTab) {
|
|
(layout as Tab).headElement.classList.add("item--unupdate");
|
|
}
|
|
(layout as Tab).headElement.setAttribute("data-initdata", JSON.stringify(json));
|
|
} else if (json.instance === "Asset") {
|
|
(layout as Tab).addModel(new Asset({
|
|
app,
|
|
tab: (layout as Tab),
|
|
path: json.path,
|
|
page: json.page,
|
|
}));
|
|
} else if (json.instance === "Backlink") {
|
|
(layout as Tab).addModel(new Backlink({
|
|
app,
|
|
tab: (layout as Tab),
|
|
blockId: json.blockId,
|
|
rootId: json.rootId,
|
|
type: json.type as "pin" | "local",
|
|
}));
|
|
} else if (json.instance === "Bookmark") {
|
|
(layout as Tab).addModel(new Bookmark(app, (layout as Tab)));
|
|
} else if (json.instance === "Files") {
|
|
(layout as Tab).addModel(new Files({
|
|
app,
|
|
tab: (layout as Tab),
|
|
}));
|
|
} else if (json.instance === "Graph") {
|
|
(layout as Tab).addModel(new Graph({
|
|
app,
|
|
tab: (layout as Tab),
|
|
blockId: json.blockId,
|
|
rootId: json.rootId,
|
|
type: json.type as "pin" | "local" | "global",
|
|
}));
|
|
} else if (json.instance === "Outline") {
|
|
(layout as Tab).addModel(new Outline({
|
|
app,
|
|
tab: (layout as Tab),
|
|
blockId: json.blockId,
|
|
type: json.type as "pin" | "local",
|
|
isPreview: json.isPreview,
|
|
}));
|
|
} else if (json.instance === "Tag") {
|
|
(layout as Tab).addModel(new Tag(app, (layout as Tab)));
|
|
} else if (json.instance === "Search") {
|
|
(layout as Tab).addModel(new Search({
|
|
app,
|
|
tab: (layout as Tab),
|
|
config: json.config
|
|
}));
|
|
} else if (json.instance === "Custom") {
|
|
if (window.siyuan.config.fileTree.openFilesUseCurrentTab) {
|
|
(layout as Tab).headElement.classList.add("item--unupdate");
|
|
}
|
|
(layout as Tab).headElement.setAttribute("data-initdata", JSON.stringify(json));
|
|
}
|
|
if (json.children) {
|
|
if (Array.isArray(json.children)) {
|
|
json.children.forEach((item: any) => {
|
|
JSONToCenter(app, item, layout ? child : window.siyuan.layout.layout);
|
|
});
|
|
} else {
|
|
JSONToCenter(app, json.children, child);
|
|
}
|
|
}
|
|
};
|
|
|
|
export const JSONToLayout = (app: App, isStart: boolean) => {
|
|
JSONToCenter(app, window.siyuan.config.uiLayout.layout, undefined);
|
|
JSONToDock(window.siyuan.config.uiLayout, app);
|
|
// 启动时不打开页签,需要移除没有钉住的页签
|
|
if (window.siyuan.config.fileTree.closeTabsOnStart) {
|
|
/// #if BROWSER
|
|
if (!sessionStorage.getItem(Constants.LOCAL_SESSION_FIRSTLOAD)) {
|
|
getAllTabs().forEach(item => {
|
|
if (item.headElement && !item.headElement.classList.contains("item--pin")) {
|
|
item.parent.removeTab(item.id, false, false);
|
|
}
|
|
});
|
|
sessionStorage.setItem(Constants.LOCAL_SESSION_FIRSTLOAD, "true");
|
|
}
|
|
/// #else
|
|
if (isStart) {
|
|
getAllTabs().forEach(item => {
|
|
if (item.headElement && !item.headElement.classList.contains("item--pin")) {
|
|
item.parent.removeTab(item.id, false, false);
|
|
}
|
|
});
|
|
}
|
|
/// #endif
|
|
}
|
|
app.plugins.forEach(item => {
|
|
afterLoadPlugin(item);
|
|
});
|
|
// 移除没有插件的 tab
|
|
document.querySelectorAll('li[data-type="tab-header"]').forEach((item: HTMLElement) => {
|
|
const initData = item.getAttribute("data-initdata");
|
|
if (initData) {
|
|
const initDataObj = JSON.parse(initData);
|
|
if (initDataObj.instance === "Custom" && initDataObj.customModelType !== "siyuan-card") {
|
|
let hasPlugin = false;
|
|
app.plugins.find(plugin => {
|
|
if (Object.keys(plugin.models).includes(initDataObj.customModelType)) {
|
|
hasPlugin = true;
|
|
return true;
|
|
}
|
|
});
|
|
if (!hasPlugin) {
|
|
const tabId = item.getAttribute("data-id");
|
|
const tab = getInstanceById(tabId) as Tab;
|
|
if (tab) {
|
|
tab.parent.removeTab(tabId, false, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
const idZoomIn = getIdZoomInByPath();
|
|
if (idZoomIn.id) {
|
|
openFileById({
|
|
app,
|
|
id: idZoomIn.id,
|
|
action: idZoomIn.isZoomIn ? [Constants.CB_GET_ALL, Constants.CB_GET_FOCUS] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL],
|
|
zoomIn: idZoomIn.isZoomIn
|
|
});
|
|
} else {
|
|
document.querySelectorAll('li[data-type="tab-header"][data-init-active="true"]').forEach((item: HTMLElement) => {
|
|
item.removeAttribute("data-init-active");
|
|
const tab = getInstanceById(item.getAttribute("data-id")) as Tab;
|
|
tab.parent.switchTab(item, false, false, true, false);
|
|
});
|
|
}
|
|
resizeTopBar();
|
|
};
|
|
|
|
export const layoutToJSON = (layout: Layout | Wnd | Tab | Model, json: any, breakObj?: IObject) => {
|
|
if (layout instanceof Layout) {
|
|
json.direction = layout.direction;
|
|
if (layout.parent) {
|
|
if (layout.element.classList.contains("fn__flex-1")) {
|
|
json.size = "auto";
|
|
} else {
|
|
json.size = (layout.parent.direction === "tb" ? layout.element.clientHeight : layout.element.clientWidth) + "px";
|
|
}
|
|
}
|
|
json.resize = layout.resize;
|
|
json.type = layout.type;
|
|
json.instance = "Layout";
|
|
} else if (layout instanceof Wnd) {
|
|
json.resize = layout.resize;
|
|
json.height = layout.element.style.height;
|
|
json.width = layout.element.style.width;
|
|
json.instance = "Wnd";
|
|
} else if (layout instanceof Tab) {
|
|
if (layout.headElement) {
|
|
json.title = layout.title;
|
|
json.icon = layout.icon;
|
|
json.docIcon = layout.docIcon;
|
|
json.pin = layout.headElement.classList.contains("item--pin");
|
|
if (layout.model instanceof Files) {
|
|
json.lang = "fileTree";
|
|
} else if (layout.model instanceof Backlink && layout.model.type === "pin") {
|
|
json.lang = "backlinks";
|
|
} else if (layout.model instanceof Bookmark) {
|
|
json.lang = "bookmark";
|
|
} else if (layout.model instanceof Graph && layout.model.type !== "local") {
|
|
json.lang = "graphView";
|
|
} else if (layout.model instanceof Outline && layout.model.type !== "local") {
|
|
json.lang = "outline";
|
|
} else if (layout.model instanceof Tag) {
|
|
json.lang = "tag";
|
|
}
|
|
if (layout.headElement.classList.contains("item--focus")) {
|
|
json.active = true;
|
|
}
|
|
}
|
|
json.instance = "Tab";
|
|
} else if (layout instanceof Editor) {
|
|
if (!layout.editor.protyle.notebookId && breakObj) {
|
|
breakObj.editor = "true";
|
|
}
|
|
json.notebookId = layout.editor.protyle.notebookId;
|
|
json.blockId = layout.editor.protyle.block.id;
|
|
json.rootId = layout.editor.protyle.block.rootID;
|
|
json.mode = layout.editor.protyle.preview.element.classList.contains("fn__none") ? "wysiwyg" : "preview";
|
|
json.action = layout.editor.protyle.block.showAll ? Constants.CB_GET_ALL : Constants.CB_GET_SCROLL;
|
|
json.instance = "Editor";
|
|
} else if (layout instanceof Asset) {
|
|
json.path = layout.path;
|
|
if (layout.pdfObject) {
|
|
json.page = layout.pdfObject.page;
|
|
}
|
|
json.instance = "Asset";
|
|
} else if (layout instanceof Backlink) {
|
|
json.blockId = layout.blockId;
|
|
json.rootId = layout.rootId;
|
|
json.type = layout.type;
|
|
json.instance = "Backlink";
|
|
} else if (layout instanceof Bookmark) {
|
|
json.instance = "Bookmark";
|
|
} else if (layout instanceof Files) {
|
|
json.instance = "Files";
|
|
} else if (layout instanceof Graph) {
|
|
json.blockId = layout.blockId;
|
|
json.rootId = layout.rootId;
|
|
json.type = layout.type;
|
|
json.instance = "Graph";
|
|
} else if (layout instanceof Outline) {
|
|
json.blockId = layout.blockId;
|
|
json.type = layout.type;
|
|
json.isPreview = layout.isPreview;
|
|
json.instance = "Outline";
|
|
} else if (layout instanceof Tag) {
|
|
json.instance = "Tag";
|
|
} else if (layout instanceof Search) {
|
|
json.instance = "Search";
|
|
json.config = layout.config;
|
|
} else if (layout instanceof Custom) {
|
|
json.instance = "Custom";
|
|
json.customModelType = layout.type;
|
|
json.customModelData = Object.assign({}, layout.data);
|
|
// https://github.com/siyuan-note/siyuan/issues/9250
|
|
delete json.customModelData.editor;
|
|
}
|
|
|
|
if (layout instanceof Layout || layout instanceof Wnd) {
|
|
if (layout instanceof Layout &&
|
|
(layout.type === "bottom" || layout.type === "left" || layout.type === "right")) {
|
|
// 四周布局使用默认值,清空内容,重置时使用 dock 数据
|
|
if (layout.type === "bottom") {
|
|
json.children = [{
|
|
"instance": "Wnd",
|
|
"children": []
|
|
}, {
|
|
"instance": "Wnd",
|
|
"resize": "lr",
|
|
"children": []
|
|
}];
|
|
} else {
|
|
json.children = [{
|
|
"instance": "Wnd",
|
|
"children": []
|
|
}, {
|
|
"instance": "Wnd",
|
|
"resize": "tb",
|
|
"children": []
|
|
}];
|
|
}
|
|
} else {
|
|
json.children = [];
|
|
layout.children.forEach((item: Layout | Wnd | Tab) => {
|
|
const itemJSON = {};
|
|
json.children.push(itemJSON);
|
|
layoutToJSON(item, itemJSON, breakObj);
|
|
});
|
|
}
|
|
} else if (layout instanceof Tab) {
|
|
if (layout.model) {
|
|
json.children = {};
|
|
layoutToJSON(layout.model, json.children, breakObj);
|
|
} else if (layout.headElement) {
|
|
// 当前页签没有激活时编辑器没有初始化
|
|
json.children = JSON.parse(layout.headElement.getAttribute("data-initdata") || "{}");
|
|
} else {
|
|
// 关闭所有页签
|
|
json.children = {};
|
|
}
|
|
}
|
|
};
|
|
|
|
export const resizeTopBar = () => {
|
|
const toolbarElement = document.querySelector("#toolbar");
|
|
if (!toolbarElement) {
|
|
return;
|
|
}
|
|
const dragElement = toolbarElement.querySelector("#drag") as HTMLElement;
|
|
|
|
dragElement.style.padding = "";
|
|
const barMoreElement = toolbarElement.querySelector("#barMore");
|
|
barMoreElement.classList.remove("fn__none");
|
|
barMoreElement.removeAttribute("data-hideids");
|
|
|
|
Array.from(toolbarElement.querySelectorAll('[data-hide="true"]')).forEach((item) => {
|
|
item.classList.remove("fn__none");
|
|
item.removeAttribute("data-hide");
|
|
});
|
|
|
|
let afterDragElement = dragElement.nextElementSibling;
|
|
const hideIds: string[] = [];
|
|
while (toolbarElement.scrollWidth > toolbarElement.clientWidth + 2) {
|
|
hideIds.push(afterDragElement.id);
|
|
afterDragElement.classList.add("fn__none");
|
|
afterDragElement.setAttribute("data-hide", "true");
|
|
afterDragElement = afterDragElement.nextElementSibling;
|
|
if (afterDragElement.id === "barMore") {
|
|
break;
|
|
}
|
|
}
|
|
|
|
let beforeDragElement = dragElement.previousElementSibling;
|
|
while (toolbarElement.scrollWidth > toolbarElement.clientWidth + 2) {
|
|
hideIds.push(beforeDragElement.id);
|
|
beforeDragElement.classList.add("fn__none");
|
|
beforeDragElement.setAttribute("data-hide", "true");
|
|
beforeDragElement = beforeDragElement.previousElementSibling;
|
|
if (beforeDragElement.id === "barWorkspace") {
|
|
break;
|
|
}
|
|
}
|
|
if (hideIds.length > 0) {
|
|
barMoreElement.classList.remove("fn__none");
|
|
} else {
|
|
barMoreElement.classList.add("fn__none");
|
|
}
|
|
barMoreElement.setAttribute("data-hideids", hideIds.join(","));
|
|
|
|
const width = dragElement.clientWidth;
|
|
const dragRect = dragElement.getBoundingClientRect();
|
|
const left = dragRect.left;
|
|
const right = window.innerWidth - dragRect.right;
|
|
if (left > right && left - right < width / 3) {
|
|
dragElement.style.paddingRight = (left - right) + "px";
|
|
} else if (left < right && right - left < width / 3) {
|
|
dragElement.style.paddingLeft = (right - left) + "px";
|
|
}
|
|
window.siyuan.storage[Constants.LOCAL_PLUGINTOPUNPIN].forEach((id: string) => {
|
|
toolbarElement.querySelector("#" + id)?.classList.add("fn__none");
|
|
});
|
|
};
|
|
|
|
export const newModelByInitData = (app: App, tab: Tab, json: any) => {
|
|
let model: Model;
|
|
if (json.instance === "Custom") {
|
|
if (json.customModelType === "siyuan-card") {
|
|
model = newCardModel({
|
|
app,
|
|
tab: tab,
|
|
data: json.customModelData
|
|
});
|
|
} else {
|
|
app.plugins.find(item => {
|
|
if (item.models[json.customModelType]) {
|
|
model = item.models[json.customModelType]({
|
|
tab: tab,
|
|
data: json.customModelData
|
|
});
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
} else if (json.instance === "Editor") {
|
|
model = new Editor({
|
|
app,
|
|
tab,
|
|
rootId: json.rootId,
|
|
blockId: json.blockId,
|
|
mode: json.mode,
|
|
action: typeof json.action === "string" ? [json.action] : json.action,
|
|
});
|
|
}
|
|
return model;
|
|
};
|
|
|
|
export const pdfIsLoading = (element: HTMLElement) => {
|
|
const isLoading = element.querySelector('.layout-tab-container > [data-loading="true"]') ? true : false;
|
|
if (isLoading) {
|
|
showMessage(window.siyuan.languages.pdfIsLoading);
|
|
}
|
|
return isLoading;
|
|
};
|
|
|
|
export const getInstanceById = (id: string, layout = window.siyuan.layout.centerLayout) => {
|
|
const _getInstanceById = (item: Layout | Wnd, id: string) => {
|
|
if (item.id === id) {
|
|
return item;
|
|
}
|
|
if (!item.children) {
|
|
return;
|
|
}
|
|
let ret: Tab | Layout | Wnd;
|
|
for (let i = 0; i < item.children.length; i++) {
|
|
ret = _getInstanceById(item.children[i] as Layout, id) as Tab;
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
};
|
|
return _getInstanceById(layout, id);
|
|
};
|
|
|
|
export const addResize = (obj: Layout | Wnd) => {
|
|
if (!obj.resize) {
|
|
return;
|
|
}
|
|
|
|
const getMinSize = (element: HTMLElement) => {
|
|
let minSize = 227;
|
|
Array.from(element.querySelectorAll(".file-tree")).find((item) => {
|
|
if (item.classList.contains("sy__backlink") || item.classList.contains("sy__graph")
|
|
|| item.classList.contains("sy__globalGraph") || item.classList.contains("sy__inbox")) {
|
|
if (!item.classList.contains("fn__none") && !hasClosestByClassName(item, "fn__none")) {
|
|
minSize = 320;
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
return minSize;
|
|
};
|
|
const resizeWnd = (resizeElement: HTMLElement, direction: string) => {
|
|
const setSize = (item: HTMLElement, direction: string) => {
|
|
if (item.classList.contains("fn__flex-1")) {
|
|
if (direction === "lr") {
|
|
item.style.width = item.clientWidth + "px";
|
|
} else {
|
|
item.style.height = item.clientHeight + "px";
|
|
}
|
|
item.classList.remove("fn__flex-1");
|
|
}
|
|
};
|
|
|
|
let range: Range;
|
|
resizeElement.addEventListener("mousedown", (event: MouseEvent) => {
|
|
getAllModels().editor.forEach((item) => {
|
|
if (item.editor && item.editor.protyle && item.element.parentElement) {
|
|
hideElements(["gutter"], item.editor.protyle);
|
|
}
|
|
});
|
|
|
|
if (getSelection().rangeCount > 0) {
|
|
range = getSelection().getRangeAt(0);
|
|
}
|
|
const documentSelf = document;
|
|
const nextElement = resizeElement.nextElementSibling as HTMLElement;
|
|
const previousElement = resizeElement.previousElementSibling as HTMLElement;
|
|
nextElement.style.overflow = "auto"; // 拖动时 layout__resize 会出现 https://github.com/siyuan-note/siyuan/issues/6221
|
|
previousElement.style.overflow = "auto";
|
|
if (!nextElement.nextElementSibling || nextElement.nextElementSibling.classList.contains("layout__dockresize")) {
|
|
setSize(nextElement, direction);
|
|
} else {
|
|
setSize(previousElement, direction);
|
|
}
|
|
const x = event[direction === "lr" ? "clientX" : "clientY"];
|
|
const previousSize = direction === "lr" ? previousElement.clientWidth : previousElement.clientHeight;
|
|
const nextSize = direction === "lr" ? nextElement.clientWidth : nextElement.clientHeight;
|
|
|
|
documentSelf.ondragstart = () => {
|
|
// 文件树拖拽会产生透明效果
|
|
document.querySelectorAll(".sy__file .b3-list-item").forEach((item: HTMLElement) => {
|
|
if (item.style.opacity === "0.1") {
|
|
item.style.opacity = "";
|
|
}
|
|
});
|
|
return false;
|
|
};
|
|
|
|
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
|
|
moveEvent.preventDefault();
|
|
moveEvent.stopPropagation();
|
|
const previousNowSize = (previousSize + (moveEvent[direction === "lr" ? "clientX" : "clientY"] - x));
|
|
const nextNowSize = (nextSize - (moveEvent[direction === "lr" ? "clientX" : "clientY"] - x));
|
|
if (previousNowSize < 8 || nextNowSize < 8) {
|
|
return;
|
|
}
|
|
if (window.siyuan.layout.leftDock?.layout.element.isSameNode(previousElement) &&
|
|
previousNowSize < getMinSize(previousElement)) {
|
|
return;
|
|
}
|
|
if (window.siyuan.layout.rightDock?.layout.element.isSameNode(nextElement) &&
|
|
nextNowSize < getMinSize(nextElement)) {
|
|
return;
|
|
}
|
|
if (window.siyuan.layout.bottomDock?.layout.element.isSameNode(nextElement) &&
|
|
nextNowSize < 64) {
|
|
return;
|
|
}
|
|
if (!previousElement.classList.contains("fn__flex-1")) {
|
|
previousElement.style[direction === "lr" ? "width" : "height"] = previousNowSize + "px";
|
|
}
|
|
if (!nextElement.classList.contains("fn__flex-1")) {
|
|
nextElement.style[direction === "lr" ? "width" : "height"] = nextNowSize + "px";
|
|
}
|
|
};
|
|
|
|
documentSelf.onmouseup = () => {
|
|
documentSelf.onmousemove = null;
|
|
documentSelf.onmouseup = null;
|
|
documentSelf.ondragstart = null;
|
|
documentSelf.onselectstart = null;
|
|
documentSelf.onselect = null;
|
|
adjustLayout(isWindow() ? window.siyuan.layout.centerLayout : undefined);
|
|
resizeTabs();
|
|
if (!isWindow()) {
|
|
window.siyuan.layout.leftDock.setSize();
|
|
window.siyuan.layout.bottomDock.setSize();
|
|
window.siyuan.layout.rightDock.setSize();
|
|
}
|
|
if (range) {
|
|
focusByRange(range);
|
|
}
|
|
nextElement.style.overflow = "";
|
|
previousElement.style.overflow = "";
|
|
};
|
|
});
|
|
};
|
|
|
|
const resizeElement = document.createElement("div");
|
|
if (obj.resize === "lr") {
|
|
resizeElement.classList.add("layout__resize--lr");
|
|
}
|
|
resizeElement.classList.add("layout__resize");
|
|
obj.element.insertAdjacentElement("beforebegin", resizeElement);
|
|
resizeWnd(resizeElement, obj.resize);
|
|
};
|
|
|
|
export const adjustLayout = (layout: Layout = window.siyuan.layout.centerLayout.parent) => {
|
|
layout.children.forEach((item: Layout | Wnd) => {
|
|
item.element.style.maxWidth = "";
|
|
if (!item.element.style.width && !item.element.classList.contains("layout__center")) {
|
|
item.element.style.minWidth = "8px";
|
|
} else {
|
|
item.element.style.minWidth = "";
|
|
}
|
|
});
|
|
let lastItem: HTMLElement;
|
|
let index = Math.floor(window.innerWidth / 24);
|
|
// +2 由于某些分辨率下 scrollWidth 会大于 clientWidth
|
|
while (layout.element.scrollWidth > layout.element.clientWidth + 2 && index > 0) {
|
|
layout.children.find((item: Layout | Wnd) => {
|
|
if (item.element.style.width && item.element.style.width !== "0px") {
|
|
item.element.style.maxWidth = Math.max(Math.min(item.element.clientWidth, window.innerWidth) - 8, 64) + "px";
|
|
lastItem = item.element;
|
|
}
|
|
if (layout.element.scrollWidth <= layout.element.clientWidth + 2) {
|
|
return true;
|
|
}
|
|
});
|
|
index--;
|
|
}
|
|
if (lastItem) {
|
|
lastItem.style.maxWidth = Math.max(Math.min(lastItem.clientWidth, window.innerWidth) - 8, 64) + "px";
|
|
}
|
|
layout.children.forEach((item: Layout | Wnd) => {
|
|
if (item instanceof Layout && item.size !== "0px") {
|
|
adjustLayout(item);
|
|
}
|
|
});
|
|
};
|