mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 15:19:01 +02:00
feat: Make splits resizable (#6866)
This commit is contained in:
commit
ac94ab6914
@ -10,6 +10,10 @@ let leftInstance: ReturnType<typeof Split> | null;
|
|||||||
let rightPaneWidth: number;
|
let rightPaneWidth: number;
|
||||||
let rightInstance: ReturnType<typeof Split> | null;
|
let rightInstance: ReturnType<typeof Split> | null;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
function setupLeftPaneResizer(leftPaneVisible: boolean) {
|
function setupLeftPaneResizer(leftPaneVisible: boolean) {
|
||||||
if (leftInstance) {
|
if (leftInstance) {
|
||||||
leftInstance.destroy();
|
leftInstance.destroy();
|
||||||
@ -83,7 +87,86 @@ function setupRightPaneResizer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (targetNtxIds) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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]);
|
||||||
|
if (!targetNtxIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
noteSplitMap.get(targetNtxIds)?.destroy();
|
||||||
|
noteSplitMap.delete(targetNtxIds);
|
||||||
|
targetNtxIds = targetNtxIds.filter(id => !ntxIds.includes(id));
|
||||||
|
|
||||||
|
if (targetNtxIds.length >= 2) {
|
||||||
|
noteSplitMap.set(targetNtxIds, undefined);
|
||||||
|
createSplitInstance(targetNtxIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveNoteSplitResizer(ntxId: string) {
|
||||||
|
const targetNtxIds = findKeyByNtxId(ntxId);
|
||||||
|
if (!targetNtxIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
noteSplitMap.get(targetNtxIds)?.destroy();
|
||||||
|
noteSplitMap.set(targetNtxIds, undefined);
|
||||||
|
createSplitInstance(targetNtxIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSplitInstance(targetNtxIds: string[]) {
|
||||||
|
const prevRafId = noteSplitRafMap.get(targetNtxIds);
|
||||||
|
if (prevRafId) {
|
||||||
|
cancelAnimationFrame(prevRafId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rafId = requestAnimationFrame(() => {
|
||||||
|
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') ?? ""));
|
||||||
|
const splitInstance = Split(splitPanels, {
|
||||||
|
gutterSize: DEFAULT_GUTTER_SIZE,
|
||||||
|
minSize: 150,
|
||||||
|
});
|
||||||
|
noteSplitMap.set(targetNtxIds, splitInstance);
|
||||||
|
noteSplitRafMap.delete(targetNtxIds);
|
||||||
|
});
|
||||||
|
noteSplitRafMap.set(targetNtxIds, rafId);
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setupLeftPaneResizer,
|
setupLeftPaneResizer,
|
||||||
setupRightPaneResizer
|
setupRightPaneResizer,
|
||||||
|
setupNoteSplitResizer,
|
||||||
|
delNoteSplitResizer,
|
||||||
|
moveNoteSplitResizer
|
||||||
};
|
};
|
||||||
|
@ -1243,6 +1243,10 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
|||||||
cursor: row-resize;
|
cursor: row-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden-ext.note-split + .gutter {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#context-menu-cover.show {
|
#context-menu-cover.show {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -1772,7 +1776,6 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.note-split {
|
.note-split {
|
||||||
flex-basis: 0; /* so that each split has same width */
|
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ body.background-effects.zen #root-widget {
|
|||||||
* Gutter
|
* Gutter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.gutter {
|
.gutter {
|
||||||
background: var(--gutter-color) !important;
|
background: var(--gutter-color) !important;
|
||||||
transition: background 150ms ease-out;
|
transition: background 150ms ease-out;
|
||||||
}
|
}
|
||||||
@ -1167,6 +1167,11 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
|
|||||||
/* will-change: opacity; -- causes some weird artifacts to the note menu in split view */
|
/* will-change: opacity; -- causes some weird artifacts to the note menu in split view */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.split-note-container-widget > .gutter {
|
||||||
|
background: var(--root-background) !important;
|
||||||
|
transition: background 150ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ribbon & note header
|
* Ribbon & note header
|
||||||
*/
|
*/
|
||||||
@ -1175,10 +1180,6 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
|
|||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-split:not(.hidden-ext) + .note-split:not(.hidden-ext) {
|
|
||||||
border-left: 4px solid var(--root-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes note-entrance {
|
@keyframes note-entrance {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@ -3,7 +3,7 @@ import appContext, { type CommandData, type CommandListenerData, type EventData,
|
|||||||
import type BasicWidget from "../basic_widget.js";
|
import type BasicWidget from "../basic_widget.js";
|
||||||
import type NoteContext from "../../components/note_context.js";
|
import type NoteContext from "../../components/note_context.js";
|
||||||
import Component from "../../components/component.js";
|
import Component from "../../components/component.js";
|
||||||
|
import splitService from "../../services/resizer.js";
|
||||||
interface NoteContextEvent {
|
interface NoteContextEvent {
|
||||||
noteContext: NoteContext;
|
noteContext: NoteContext;
|
||||||
}
|
}
|
||||||
@ -52,6 +52,10 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
await widget.handleEvent("setNoteContext", { noteContext });
|
await widget.handleEvent("setNoteContext", { noteContext });
|
||||||
|
|
||||||
this.child(widget);
|
this.child(widget);
|
||||||
|
|
||||||
|
if (noteContext.mainNtxId && noteContext.ntxId) {
|
||||||
|
splitService.setupNoteSplitResizer([noteContext.mainNtxId,noteContext.ntxId]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }: EventData<"openNewNoteSplit">) {
|
async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }: EventData<"openNewNoteSplit">) {
|
||||||
@ -95,9 +99,9 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closeThisNoteSplitCommand({ ntxId }: CommandListenerData<"closeThisNoteSplit">) {
|
async closeThisNoteSplitCommand({ ntxId }: CommandListenerData<"closeThisNoteSplit">) {
|
||||||
if (ntxId) {
|
if (ntxId) {
|
||||||
appContext.tabManager.removeNoteContext(ntxId);
|
await appContext.tabManager.removeNoteContext(ntxId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +141,8 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
|
|
||||||
// activate context that now contains the original note
|
// activate context that now contains the original note
|
||||||
await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]);
|
await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]);
|
||||||
|
|
||||||
|
splitService.moveNoteSplitResizer(ntxIds[leftIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
activeContextChangedEvent() {
|
activeContextChangedEvent() {
|
||||||
@ -157,6 +163,8 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
recursiveCleanup(widget);
|
recursiveCleanup(widget);
|
||||||
delete this.widgets[ntxId];
|
delete this.widgets[ntxId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
splitService.delNoteSplitResizer(ntxIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
contextsReopenedEvent({ ntxId, afterNtxId }: EventData<"contextsReopened">) {
|
contextsReopenedEvent({ ntxId, afterNtxId }: EventData<"contextsReopened">) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user