diff --git a/apps/client/src/services/resizer.ts b/apps/client/src/services/resizer.ts index e0dc40995..54a11e801 100644 --- a/apps/client/src/services/resizer.ts +++ b/apps/client/src/services/resizer.ts @@ -10,6 +10,10 @@ let leftInstance: ReturnType | null; let rightPaneWidth: number; let rightInstance: ReturnType | null; +const noteSplitMap = new Map | undefined>(); // key: a group of ntxIds, value: the corresponding Split instance +const noteSplitRafMap = new Map(); +let splitNoteContainer: HTMLElement | undefined; + function setupLeftPaneResizer(leftPaneVisible: boolean) { if (leftInstance) { 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(':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 { setupLeftPaneResizer, - setupRightPaneResizer + setupRightPaneResizer, + setupNoteSplitResizer, + delNoteSplitResizer, + moveNoteSplitResizer }; diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css index e20c63ee0..d3f3f4690 100644 --- a/apps/client/src/stylesheets/style.css +++ b/apps/client/src/stylesheets/style.css @@ -1243,6 +1243,10 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href cursor: row-resize; } +.hidden-ext.note-split + .gutter { + display: none; +} + #context-menu-cover.show { position: fixed; top: 0; @@ -1772,7 +1776,6 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu { } .note-split { - flex-basis: 0; /* so that each split has same width */ margin-left: auto; margin-right: auto; } diff --git a/apps/client/src/stylesheets/theme-next/shell.css b/apps/client/src/stylesheets/theme-next/shell.css index 54a74ca77..c4c0b6d4c 100644 --- a/apps/client/src/stylesheets/theme-next/shell.css +++ b/apps/client/src/stylesheets/theme-next/shell.css @@ -81,7 +81,7 @@ body.background-effects.zen #root-widget { * Gutter */ - .gutter { +.gutter { background: var(--gutter-color) !important; 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 */ } +.split-note-container-widget > .gutter { + background: var(--root-background) !important; + transition: background 150ms ease-out; +} + /* * 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; } -.note-split:not(.hidden-ext) + .note-split:not(.hidden-ext) { - border-left: 4px solid var(--root-background); -} - @keyframes note-entrance { from { opacity: 0; diff --git a/apps/client/src/widgets/containers/split_note_container.ts b/apps/client/src/widgets/containers/split_note_container.ts index 12d417973..8298d5989 100644 --- a/apps/client/src/widgets/containers/split_note_container.ts +++ b/apps/client/src/widgets/containers/split_note_container.ts @@ -3,7 +3,7 @@ import appContext, { type CommandData, type CommandListenerData, type EventData, import type BasicWidget from "../basic_widget.js"; import type NoteContext from "../../components/note_context.js"; import Component from "../../components/component.js"; - +import splitService from "../../services/resizer.js"; interface NoteContextEvent { noteContext: NoteContext; } @@ -52,6 +52,10 @@ export default class SplitNoteContainer extends FlexContainer { await widget.handleEvent("setNoteContext", { noteContext }); this.child(widget); + + if (noteContext.mainNtxId && noteContext.ntxId) { + splitService.setupNoteSplitResizer([noteContext.mainNtxId,noteContext.ntxId]); + } } async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }: EventData<"openNewNoteSplit">) { @@ -95,9 +99,9 @@ export default class SplitNoteContainer extends FlexContainer { } } - closeThisNoteSplitCommand({ ntxId }: CommandListenerData<"closeThisNoteSplit">) { + async closeThisNoteSplitCommand({ ntxId }: CommandListenerData<"closeThisNoteSplit">) { if (ntxId) { - appContext.tabManager.removeNoteContext(ntxId); + await appContext.tabManager.removeNoteContext(ntxId); } } @@ -137,6 +141,8 @@ export default class SplitNoteContainer extends FlexContainer { // activate context that now contains the original note await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]); + + splitService.moveNoteSplitResizer(ntxIds[leftIndex]); } activeContextChangedEvent() { @@ -157,6 +163,8 @@ export default class SplitNoteContainer extends FlexContainer { recursiveCleanup(widget); delete this.widgets[ntxId]; } + + splitService.delNoteSplitResizer(ntxIds); } contextsReopenedEvent({ ntxId, afterNtxId }: EventData<"contextsReopened">) {