mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 13:39:01 +01:00 
			
		
		
		
	feat: Make splits resizable
This commit is contained in:
		
							parent
							
								
									ed56ed2be0
								
							
						
					
					
						commit
						3254069999
					
				@ -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,94 @@ 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) {
 | 
				
			||||||
 | 
					        noteSplitMap.get(targetNtxIds)?.destroy();
 | 
				
			||||||
 | 
					        noteSplitMap.delete(targetNtxIds);
 | 
				
			||||||
 | 
					        targetNtxIds = targetNtxIds.filter(id => !ntxIds.includes(id));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (targetNtxIds && targetNtxIds.length >= 2) {
 | 
				
			||||||
 | 
					        noteSplitMap.set(targetNtxIds, undefined);
 | 
				
			||||||
 | 
					        createSplitInstance(targetNtxIds);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function moveNoteSplitResizer(ntxId: string) {
 | 
				
			||||||
 | 
					    const targetNtxIds = findKeyByNtxId(ntxId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (targetNtxIds) {
 | 
				
			||||||
 | 
					        noteSplitMap.get(targetNtxIds)?.destroy();
 | 
				
			||||||
 | 
					        noteSplitMap.set(targetNtxIds, undefined);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (targetNtxIds) {
 | 
				
			||||||
 | 
					        createSplitInstance(targetNtxIds);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function createSplitInstance(targetNtxIds: string[]) {
 | 
				
			||||||
 | 
					    const prevRafId = noteSplitRafMap.get(targetNtxIds);
 | 
				
			||||||
 | 
					    if (prevRafId) {
 | 
				
			||||||
 | 
					        cancelAnimationFrame(prevRafId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const rafId = requestAnimationFrame(() => {
 | 
				
			||||||
 | 
					        if (!splitNoteContainer){
 | 
				
			||||||
 | 
					            splitNoteContainer =  $("#center-pane").find(".split-note-container-widget")[0];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const splitPanels: HTMLElement[] = [];
 | 
				
			||||||
 | 
					        for (const el of splitNoteContainer.querySelectorAll(':scope > .note-split')) {
 | 
				
			||||||
 | 
					            const dataId = el.getAttribute('data-ntx-id');
 | 
				
			||||||
 | 
					            if (dataId && targetNtxIds.includes(dataId)) {
 | 
				
			||||||
 | 
					                splitPanels.push(el as HTMLElement);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1171,6 +1171,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;
 | 
				
			||||||
@ -1700,7 +1704,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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -90,7 +90,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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1092,6 +1092,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
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@ -1100,10 +1105,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;
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ import FlexContainer from "./flex_container.js";
 | 
				
			|||||||
import appContext, { type CommandData, type CommandListenerData, type EventData, type EventNames, type NoteSwitchedContext } from "../../components/app_context.js";
 | 
					import appContext, { type CommandData, type CommandListenerData, type EventData, type EventNames, type NoteSwitchedContext } from "../../components/app_context.js";
 | 
				
			||||||
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 splitService from "../../services/resizer.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface NoteContextEvent {
 | 
					interface NoteContextEvent {
 | 
				
			||||||
    noteContext: NoteContext;
 | 
					    noteContext: NoteContext;
 | 
				
			||||||
@ -51,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">) {
 | 
				
			||||||
@ -94,9 +99,11 @@ 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);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            splitService.delNoteSplitResizer([ntxId]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -136,6 +143,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() {
 | 
				
			||||||
@ -154,6 +163,8 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            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