2021-06-05 14:01:21 +02:00
|
|
|
import options from "./options.js";
|
2025-10-21 17:57:12 +03:00
|
|
|
import Split from "@triliumnext/split.js";
|
2021-06-05 14:01:21 +02:00
|
|
|
|
2025-03-18 19:57:41 +02:00
|
|
|
export const DEFAULT_GUTTER_SIZE = 5;
|
|
|
|
|
|
2025-05-18 16:24:13 +08:00
|
|
|
let leftPaneWidth: number;
|
|
|
|
|
let reservedPx: number;
|
|
|
|
|
let layoutOrientation: string;
|
2024-12-19 22:19:35 +02:00
|
|
|
let leftInstance: ReturnType<typeof Split> | null;
|
2025-05-18 16:24:13 +08:00
|
|
|
let rightPaneWidth: number;
|
2024-12-19 22:19:35 +02:00
|
|
|
let rightInstance: ReturnType<typeof Split> | null;
|
2021-06-05 14:01:21 +02:00
|
|
|
|
2025-09-02 09:28:53 +08:00
|
|
|
const noteSplitMap = new Map<string[], ReturnType<typeof Split> | undefined>(); // key: a group of ntxIds, value: the corresponding Split instance
|
|
|
|
|
const noteSplitRafMap = new Map<string[], number>();
|
|
|
|
|
let splitNoteContainer: HTMLElement | undefined;
|
|
|
|
|
|
2024-12-19 22:19:35 +02:00
|
|
|
function setupLeftPaneResizer(leftPaneVisible: boolean) {
|
2021-06-05 14:01:21 +02:00
|
|
|
if (leftInstance) {
|
|
|
|
|
leftInstance.destroy();
|
|
|
|
|
leftInstance = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$("#left-pane").toggle(leftPaneVisible);
|
|
|
|
|
|
2025-05-18 16:24:13 +08:00
|
|
|
layoutOrientation = layoutOrientation ?? options.get("layoutOrientation");
|
|
|
|
|
reservedPx = reservedPx ?? (layoutOrientation === "vertical" ? ($("#launcher-pane").outerWidth() || 0) : 0);
|
|
|
|
|
// Window resizing causes `window.innerWidth` to change, so `reservedWidth` needs to be recalculated each time.
|
|
|
|
|
const reservedWidth = reservedPx / window.innerWidth * 100;
|
2021-06-05 14:01:21 +02:00
|
|
|
if (!leftPaneVisible) {
|
2025-05-18 20:19:30 +08:00
|
|
|
$("#rest-pane").css("width", layoutOrientation === "vertical" ? `${100 - reservedWidth}%` : "100%");
|
2021-06-05 14:01:21 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-18 16:24:13 +08:00
|
|
|
leftPaneWidth = leftPaneWidth ?? (options.getInt("leftPaneWidth") ?? 0);
|
2021-06-05 14:01:21 +02:00
|
|
|
if (!leftPaneWidth || leftPaneWidth < 5) {
|
|
|
|
|
leftPaneWidth = 5;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-18 16:24:13 +08:00
|
|
|
const restPaneWidth = 100 - leftPaneWidth - reservedWidth;
|
2021-06-05 14:01:21 +02:00
|
|
|
if (leftPaneVisible) {
|
2025-05-10 09:19:41 +08:00
|
|
|
// Delayed initialization ensures that all DOM elements are fully rendered and part of the layout,
|
|
|
|
|
// preventing Split.js from retrieving incorrect dimensions due to #left-pane not being rendered yet,
|
|
|
|
|
// which would cause the minSize setting to have no effect.
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
leftInstance = Split(["#left-pane", "#rest-pane"], {
|
2025-05-18 16:24:13 +08:00
|
|
|
sizes: [leftPaneWidth, restPaneWidth],
|
2025-05-10 09:19:41 +08:00
|
|
|
gutterSize: DEFAULT_GUTTER_SIZE,
|
|
|
|
|
minSize: [150, 300],
|
2025-10-21 18:07:45 +03:00
|
|
|
rtl: glob.isRtl,
|
2025-05-18 16:24:13 +08:00
|
|
|
onDragEnd: (sizes) => {
|
|
|
|
|
leftPaneWidth = Math.round(sizes[0]);
|
|
|
|
|
options.save("leftPaneWidth", Math.round(sizes[0]));
|
|
|
|
|
}
|
2025-05-10 09:19:41 +08:00
|
|
|
});
|
2021-06-05 14:01:21 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setupRightPaneResizer() {
|
|
|
|
|
if (rightInstance) {
|
2021-06-05 22:40:17 +02:00
|
|
|
rightInstance.destroy();
|
|
|
|
|
rightInstance = null;
|
2021-06-05 14:01:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const rightPaneVisible = $("#right-pane").is(":visible");
|
|
|
|
|
|
|
|
|
|
if (!rightPaneVisible) {
|
2025-01-09 18:07:02 +02:00
|
|
|
$("#center-pane").css("width", "100%");
|
2021-06-05 14:01:21 +02:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-18 16:24:13 +08:00
|
|
|
rightPaneWidth = rightPaneWidth ?? (options.getInt("rightPaneWidth") ?? 0);
|
2021-06-05 14:01:21 +02:00
|
|
|
if (!rightPaneWidth || rightPaneWidth < 5) {
|
|
|
|
|
rightPaneWidth = 5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rightPaneVisible) {
|
2025-01-09 18:07:02 +02:00
|
|
|
rightInstance = Split(["#center-pane", "#right-pane"], {
|
2021-06-05 14:01:21 +02:00
|
|
|
sizes: [100 - rightPaneWidth, rightPaneWidth],
|
2025-03-18 19:57:41 +02:00
|
|
|
gutterSize: DEFAULT_GUTTER_SIZE,
|
2025-05-18 16:24:13 +08:00
|
|
|
minSize: [300, 180],
|
2025-10-21 18:07:45 +03:00
|
|
|
rtl: glob.isRtl,
|
2025-05-18 16:24:13 +08:00
|
|
|
onDragEnd: (sizes) => {
|
|
|
|
|
rightPaneWidth = Math.round(sizes[1]);
|
|
|
|
|
options.save("rightPaneWidth", Math.round(sizes[1]));
|
|
|
|
|
}
|
2021-06-05 14:01:21 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-02 09:28:53 +08:00
|
|
|
function findKeyByNtxId(ntxId: string): string[] | undefined {
|
|
|
|
|
// Find the corresponding key in noteSplitMap based on ntxId
|
|
|
|
|
for (const key of noteSplitMap.keys()) {
|
|
|
|
|
if (key.includes(ntxId)) return key;
|
|
|
|
|
}
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setupNoteSplitResizer(ntxIds: string[]) {
|
|
|
|
|
let targetNtxIds: string[] | undefined;
|
|
|
|
|
for (const ntxId of ntxIds) {
|
|
|
|
|
targetNtxIds = findKeyByNtxId(ntxId);
|
2025-10-21 17:57:12 +03:00
|
|
|
if (targetNtxIds) break;
|
2025-09-02 09:28:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (targetNtxIds) {
|
|
|
|
|
noteSplitMap.get(targetNtxIds)?.destroy();
|
|
|
|
|
for (const id of ntxIds) {
|
|
|
|
|
if (!targetNtxIds.includes(id)) {
|
|
|
|
|
targetNtxIds.push(id)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
targetNtxIds = [...ntxIds];
|
|
|
|
|
}
|
|
|
|
|
noteSplitMap.set(targetNtxIds, undefined);
|
|
|
|
|
createSplitInstance(targetNtxIds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function delNoteSplitResizer(ntxIds: string[]) {
|
|
|
|
|
let targetNtxIds = findKeyByNtxId(ntxIds[0]);
|
2025-09-02 20:17:01 +08:00
|
|
|
if (!targetNtxIds) {
|
|
|
|
|
return;
|
2025-09-02 09:28:53 +08:00
|
|
|
}
|
2025-09-02 20:17:01 +08:00
|
|
|
|
|
|
|
|
noteSplitMap.get(targetNtxIds)?.destroy();
|
|
|
|
|
noteSplitMap.delete(targetNtxIds);
|
|
|
|
|
targetNtxIds = targetNtxIds.filter(id => !ntxIds.includes(id));
|
|
|
|
|
|
|
|
|
|
if (targetNtxIds.length >= 2) {
|
2025-09-02 09:28:53 +08:00
|
|
|
noteSplitMap.set(targetNtxIds, undefined);
|
|
|
|
|
createSplitInstance(targetNtxIds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function moveNoteSplitResizer(ntxId: string) {
|
|
|
|
|
const targetNtxIds = findKeyByNtxId(ntxId);
|
2025-09-02 20:17:01 +08:00
|
|
|
if (!targetNtxIds) {
|
|
|
|
|
return;
|
2025-09-02 09:28:53 +08:00
|
|
|
}
|
2025-09-02 20:17:01 +08:00
|
|
|
noteSplitMap.get(targetNtxIds)?.destroy();
|
|
|
|
|
noteSplitMap.set(targetNtxIds, undefined);
|
|
|
|
|
createSplitInstance(targetNtxIds);
|
2025-09-02 09:28:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createSplitInstance(targetNtxIds: string[]) {
|
|
|
|
|
const prevRafId = noteSplitRafMap.get(targetNtxIds);
|
|
|
|
|
if (prevRafId) {
|
|
|
|
|
cancelAnimationFrame(prevRafId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const rafId = requestAnimationFrame(() => {
|
2025-09-02 22:05:26 +08:00
|
|
|
splitNoteContainer = splitNoteContainer ?? $("#center-pane").find(".split-note-container-widget")[0];
|
|
|
|
|
const splitPanels = [...splitNoteContainer.querySelectorAll<HTMLElement>(':scope > .note-split')]
|
|
|
|
|
.filter(el => targetNtxIds.includes(el.getAttribute('data-ntx-id') ?? ""));
|
2025-09-02 09:28:53 +08:00
|
|
|
const splitInstance = Split(splitPanels, {
|
2025-10-21 18:07:45 +03:00
|
|
|
rtl: glob.isRtl,
|
2025-09-02 09:28:53 +08:00
|
|
|
gutterSize: DEFAULT_GUTTER_SIZE,
|
|
|
|
|
minSize: 150,
|
|
|
|
|
});
|
|
|
|
|
noteSplitMap.set(targetNtxIds, splitInstance);
|
|
|
|
|
noteSplitRafMap.delete(targetNtxIds);
|
|
|
|
|
});
|
|
|
|
|
noteSplitRafMap.set(targetNtxIds, rafId);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-05 14:01:21 +02:00
|
|
|
export default {
|
|
|
|
|
setupLeftPaneResizer,
|
2025-09-02 09:28:53 +08:00
|
|
|
setupRightPaneResizer,
|
|
|
|
|
setupNoteSplitResizer,
|
|
|
|
|
delNoteSplitResizer,
|
|
|
|
|
moveNoteSplitResizer
|
2021-06-05 14:01:21 +02:00
|
|
|
};
|