diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index c1e30befa..de3ef63f3 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -934,6 +934,9 @@ describe('dockviewComponent', () => { }), dockview.onDidActivePanelChange((panel) => { events.push({ type: 'ACTIVE_PANEL', panel }); + }), + dockview.onDidMovePanel(({ panel }) => { + events.push({ type: 'MOVE_PANEL', panel }); }) ); @@ -1016,7 +1019,10 @@ describe('dockviewComponent', () => { to: { group: panel2.group, position: 'center' }, }); - expect(events).toEqual([{ type: 'ACTIVE_GROUP', group: panel2.group }]); + expect(events).toEqual([ + { type: 'ACTIVE_GROUP', group: panel2.group }, + { type: 'MOVE_PANEL', panel: panel5 }, + ]); events = []; @@ -1030,6 +1036,7 @@ describe('dockviewComponent', () => { expect(events).toEqual([ { type: 'REMOVE_GROUP', group: groupReferenceBeforeMove }, { type: 'ACTIVE_PANEL', panel: panel4 }, + { type: 'MOVE_PANEL', panel: panel4 }, ]); for (const panel of dockview.panels) { @@ -1771,6 +1778,7 @@ describe('dockviewComponent', () => { let addPanel: IDockviewPanel[] = []; let removePanel: IDockviewPanel[] = []; let activePanel: (IDockviewPanel | undefined)[] = []; + let movedPanels: IDockviewPanel[] = []; let layoutChange = 0; let layoutChangeFromJson = 0; @@ -1793,6 +1801,9 @@ describe('dockviewComponent', () => { dockview.onDidActivePanelChange((event) => { activePanel.push(event); }), + dockview.onDidMovePanel((event) => { + movedPanels.push(event.panel); + }), dockview.onDidLayoutChange(() => { layoutChange++; }), @@ -1884,6 +1895,7 @@ describe('dockviewComponent', () => { expect(addPanel.length).toBe(5); expect(removePanel.length).toBe(0); expect(activePanel.length).toBe(1); + expect(movedPanels.length).toBe(0); expect(layoutChange).toBe(1); expect(layoutChangeFromJson).toBe(1); @@ -1918,6 +1930,7 @@ describe('dockviewComponent', () => { expect(addPanel.length).toBe(0); expect(removePanel.length).toBe(5); expect(activePanel.length).toBe(1); + expect(movedPanels.length).toBe(0); expect(layoutChange).toBe(1); expect(layoutChangeFromJson).toBe(1); @@ -4924,7 +4937,6 @@ describe('dockviewComponent', () => { describe('that emits onDidLayoutChange', () => { let dockview: DockviewComponent; - let panel1: DockviewPanel; beforeEach(() => { jest.useFakeTimers(); @@ -4943,11 +4955,6 @@ describe('dockviewComponent', () => { } }, }); - - panel1 = dockview.addPanel({ - id: 'panel_1', - component: 'default', - }); }); afterEach(() => { @@ -4955,7 +4962,63 @@ describe('dockviewComponent', () => { jest.useRealTimers(); }); + test('when panels or groups change', () => { + const didLayoutChangeHandler = jest.fn(); + dockview.onDidLayoutChange(didLayoutChangeHandler); + + // add panel + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + position: { referenceGroup: panel1.group }, + }); + jest.runAllTimers(); + expect(didLayoutChangeHandler).toHaveBeenCalledTimes(1); + + // add group + const group = dockview.addGroup(); + jest.runAllTimers(); + expect(didLayoutChangeHandler).toHaveBeenCalledTimes(2); + + // remove group + group.api.close(); + jest.runAllTimers(); + expect(didLayoutChangeHandler).toHaveBeenCalledTimes(3); + + // active panel + panel1.api.setActive(); + jest.runAllTimers(); + expect(didLayoutChangeHandler).toHaveBeenCalledTimes(4); + + // move panel + dockview.moveGroupOrPanel({ + from: { + groupId: panel1.group.api.id, + panelId: panel1.api.id, + }, + to: { + group: panel1.group, + position: 'center', + index: 1, + }, + }); + + // remove panel + panel2.api.close(); + jest.runAllTimers(); + expect(didLayoutChangeHandler).toHaveBeenCalledTimes(5); + }); + test('that emits onDidPanelTitleChange and onDidLayoutChange when the panel set a title', () => { + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); + const didLayoutChangeHandler = jest.fn(); const { dispose: disposeDidLayoutChangeHandler } = dockview.onDidLayoutChange(didLayoutChangeHandler); @@ -4970,6 +5033,11 @@ describe('dockviewComponent', () => { }); test('that emits onDidPanelParametersChange and onDidLayoutChange when the panel updates parameters', () => { + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); + const didLayoutChangeHandler = jest.fn(); const { dispose: disposeDidLayoutChangeHandler } = dockview.onDidLayoutChange(didLayoutChangeHandler); diff --git a/packages/dockview-core/src/api/component.api.ts b/packages/dockview-core/src/api/component.api.ts index 4a6c80531..6d76f1225 100644 --- a/packages/dockview-core/src/api/component.api.ts +++ b/packages/dockview-core/src/api/component.api.ts @@ -1,6 +1,7 @@ import { FloatingGroupOptions, IDockviewComponent, + MovePanelEvent, SerializedDockview, } from '../dockview/dockviewComponent'; import { @@ -637,6 +638,10 @@ export class DockviewApi implements CommonApi { return this.component.onDidRemovePanel; } + get onDidMovePanel(): Event { + return this.component.onDidMovePanel; + } + /** * Invoked after a layout is deserialzied using the `fromJSON` method. */ diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index b5c77e78d..5374e626d 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -148,6 +148,11 @@ export interface SerializedDockview { popoutGroups?: SerializedPopoutGroup[]; } +export interface MovePanelEvent { + panel: IDockviewPanel; + from: DockviewGroupPanel; +} + type MoveGroupOptions = { from: { group: DockviewGroupPanel }; to: { group: DockviewGroupPanel; position: Position }; @@ -194,6 +199,7 @@ export interface IDockviewComponent extends IBaseGrid { readonly onDidAddGroup: Event; readonly onDidActiveGroupChange: Event; readonly onUnhandledDragOverEvent: Event; + readonly onDidMovePanel: Event; readonly options: DockviewComponentOptions; updateOptions(options: DockviewOptions): void; moveGroupOrPanel(options: MoveGroupOrPanelOptions): void; @@ -283,9 +289,8 @@ export class DockviewComponent readonly onDidActivePanelChange: Event = this._onDidActivePanelChange.event; - private readonly _onDidMovePanel = new Emitter<{ - panel: IDockviewPanel; - }>(); + private readonly _onDidMovePanel = new Emitter(); + readonly onDidMovePanel = this._onDidMovePanel.event; private readonly _floatingGroups: DockviewFloatingGroupPanel[] = []; private readonly _popoutGroups: { @@ -296,8 +301,6 @@ export class DockviewComponent }[] = []; private readonly _rootDropTarget: Droptarget; - private _ignoreEvents = 0; - private readonly _onDidRemoveGroup = new Emitter(); readonly onDidRemoveGroup: Event = this._onDidRemoveGroup.event; @@ -407,9 +410,12 @@ export class DockviewComponent )(() => { this.updateWatermark(); }), - Event.any( + Event.any( this.onDidAddPanel, this.onDidRemovePanel, + this.onDidAddGroup, + this.onDidRemove, + this.onDidMovePanel, this.onDidActivePanelChange )(() => { this._bufferOnDidLayoutChange.fire(); @@ -1844,6 +1850,7 @@ export class DockviewComponent this._onDidMovePanel.fire({ panel: removedPanel, + from: sourceGroup, }); } else { /** @@ -1880,6 +1887,12 @@ export class DockviewComponent // if a group has one tab - we are essentially moving the 'group' // which is equivalent to swapping two views in this case this.gridview.moveView(sourceParentLocation, from, to); + + this._onDidMovePanel.fire({ + panel: this.getGroupPanel(sourceItemId)!, + from: sourceGroup, + }); + return; } } @@ -1904,6 +1917,11 @@ export class DockviewComponent ); this.movingLock(() => this.doAddGroup(targetGroup, location)); this.doSetGroupAndPanelActive(targetGroup); + + this._onDidMovePanel.fire({ + panel: this.getGroupPanel(sourceItemId)!, + from: sourceGroup, + }); } else { /** * The group we are removing from has many panels, we need to remove the panels we are moving, @@ -1934,6 +1952,11 @@ export class DockviewComponent }) ); this.doSetGroupAndPanelActive(group); + + this._onDidMovePanel.fire({ + panel: removedPanel, + from: sourceGroup, + }); } } } @@ -1968,10 +1991,6 @@ export class DockviewComponent }); this.doSetGroupAndPanelActive(to); - - panels.forEach((panel) => { - this._onDidMovePanel.fire({ panel }); - }); } else { switch (from.api.location.type) { case 'grid': @@ -2006,11 +2025,11 @@ export class DockviewComponent ); this.gridview.addView(from, Sizing.Distribute, dropLocation); - - from.panels.forEach((panel) => { - this._onDidMovePanel.fire({ panel }); - }); } + + from.panels.forEach((panel) => { + this._onDidMovePanel.fire({ panel, from }); + }); } override doSetGroupActive(group: DockviewGroupPanel | undefined): void { diff --git a/packages/docs/sandboxes/react/dockview/demo-dockview/src/app.tsx b/packages/docs/sandboxes/react/dockview/demo-dockview/src/app.tsx index d77773b24..df7a5995c 100644 --- a/packages/docs/sandboxes/react/dockview/demo-dockview/src/app.tsx +++ b/packages/docs/sandboxes/react/dockview/demo-dockview/src/app.tsx @@ -26,7 +26,7 @@ const components = { overflow: 'auto', color: 'white', position: 'relative', - border: 'var(--demo-border)', + // border: '5px dashed purple', }} > {/* */} @@ -147,6 +147,10 @@ const DockviewDemo = (props: { theme?: string }) => { addLogLine(`Group Added ${event.id}`); }); + event.api.onDidMovePanel((event) => { + addLogLine(`Panel Moved ${event.panel.id}`); + }); + event.api.onDidRemoveGroup((event) => { setGroups((_) => { const next = [..._];