mirror of
https://github.com/mathuo/dockview
synced 2025-05-01 17:18:27 +00:00
Merge pull request #72 from mathuo/71-dockview-panel-disposal
work in progress
This commit is contained in:
commit
8b8c0fd79b
@ -0,0 +1,46 @@
|
||||
import { DefaultGroupPanelView } from '../../dockview/defaultGroupPanelView';
|
||||
import {
|
||||
IActionsRenderer,
|
||||
IContentRenderer,
|
||||
ITabRenderer,
|
||||
} from '../../groupview/types';
|
||||
|
||||
describe('defaultGroupPanelView', () => {
|
||||
test('dispose cleanup', () => {
|
||||
const contentMock = jest.fn<IContentRenderer, []>(() => {
|
||||
const partial: Partial<IContentRenderer> = {
|
||||
element: document.createElement('div'),
|
||||
dispose: jest.fn(),
|
||||
};
|
||||
return partial as IContentRenderer;
|
||||
});
|
||||
|
||||
const tabMock = jest.fn<ITabRenderer, []>(() => {
|
||||
const partial: Partial<IContentRenderer> = {
|
||||
element: document.createElement('div'),
|
||||
dispose: jest.fn(),
|
||||
};
|
||||
return partial as IContentRenderer;
|
||||
});
|
||||
|
||||
const actionsMock = jest.fn<IActionsRenderer, []>(() => {
|
||||
const partial: Partial<IContentRenderer> = {
|
||||
element: document.createElement('div'),
|
||||
dispose: jest.fn(),
|
||||
};
|
||||
return partial as IContentRenderer;
|
||||
});
|
||||
|
||||
const content = new contentMock();
|
||||
const tab = new tabMock();
|
||||
const actions = new actionsMock();
|
||||
|
||||
const cut = new DefaultGroupPanelView({ content, tab, actions });
|
||||
|
||||
cut.dispose();
|
||||
|
||||
expect(content.dispose).toHaveBeenCalled();
|
||||
expect(tab.dispose).toHaveBeenCalled();
|
||||
expect(actions.dispose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -25,10 +25,16 @@ import {
|
||||
DockviewPanelApiImpl,
|
||||
} from '../../api/groupPanelApi';
|
||||
import { DefaultTab } from '../../dockview/components/tab/defaultTab';
|
||||
import { Emitter } from '../../events';
|
||||
|
||||
class PanelContentPartTest implements IContentRenderer {
|
||||
element: HTMLElement = document.createElement('div');
|
||||
|
||||
readonly _onDidDispose = new Emitter<void>();
|
||||
readonly onDidDispose = this._onDidDispose.event;
|
||||
|
||||
isDisposed: boolean = false;
|
||||
|
||||
constructor(public readonly id: string, component: string) {
|
||||
this.element.classList.add(`testpanel-${id}`);
|
||||
}
|
||||
@ -58,8 +64,51 @@ class PanelContentPartTest implements IContentRenderer {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.isDisposed = true;
|
||||
this._onDidDispose.fire();
|
||||
}
|
||||
}
|
||||
|
||||
class PanelTabPartTest implements ITabRenderer {
|
||||
element: HTMLElement = document.createElement('div');
|
||||
|
||||
readonly _onDidDispose = new Emitter<void>();
|
||||
readonly onDidDispose = this._onDidDispose.event;
|
||||
|
||||
isDisposed: boolean = false;
|
||||
|
||||
constructor(public readonly id: string, component: string) {
|
||||
this.element.classList.add(`testpanel-${id}`);
|
||||
}
|
||||
|
||||
updateParentGroup(group: GroupviewPanel, isPanelVisible: boolean): void {
|
||||
//noop
|
||||
}
|
||||
|
||||
init(parameters: GroupPanelPartInitParameters): void {
|
||||
//noop
|
||||
}
|
||||
|
||||
layout(width: number, height: number): void {
|
||||
//noop
|
||||
}
|
||||
|
||||
update(event: PanelUpdateEvent): void {
|
||||
//noop
|
||||
}
|
||||
|
||||
toJSON(): object {
|
||||
return { id: this.id };
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
//noop
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.isDisposed = true;
|
||||
this._onDidDispose.fire();
|
||||
}
|
||||
}
|
||||
|
||||
class TestGroupPanelView implements IGroupPanelView {
|
||||
@ -1104,4 +1153,291 @@ describe('dockviewComponent', () => {
|
||||
|
||||
expect(container.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
test('panel is disposed of when closed', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const dockview = new DockviewComponent(container, {
|
||||
components: { default: PanelContentPartTest },
|
||||
});
|
||||
|
||||
dockview.layout(500, 1000);
|
||||
|
||||
const panel1 = dockview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
|
||||
panel1.api.close();
|
||||
|
||||
expect(panel1Spy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is disposed of when removed', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const dockview = new DockviewComponent(container, {
|
||||
components: { default: PanelContentPartTest },
|
||||
});
|
||||
|
||||
dockview.layout(500, 1000);
|
||||
|
||||
const panel1 = dockview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
|
||||
dockview.removePanel(panel1);
|
||||
|
||||
expect(panel1Spy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is not disposed of when moved to a new group', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const dockview = new DockviewComponent(container, {
|
||||
components: {
|
||||
default: PanelContentPartTest,
|
||||
},
|
||||
});
|
||||
|
||||
dockview.layout(500, 1000);
|
||||
|
||||
const panel1 = dockview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
const panel2 = dockview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
position: {
|
||||
referencePanel: 'panel1',
|
||||
direction: 'right',
|
||||
},
|
||||
});
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
dockview.moveGroupOrPanel(
|
||||
panel1.group,
|
||||
panel2.group.id,
|
||||
'panel2',
|
||||
Position.Left
|
||||
);
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
expect(panel2Spy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('panel is not disposed of when moved within another group', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const dockview = new DockviewComponent(container, {
|
||||
components: {
|
||||
default: PanelContentPartTest,
|
||||
},
|
||||
});
|
||||
|
||||
dockview.layout(500, 1000);
|
||||
|
||||
const panel1 = dockview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
const panel2 = dockview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
position: {
|
||||
referencePanel: 'panel1',
|
||||
direction: 'right',
|
||||
},
|
||||
});
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
dockview.moveGroupOrPanel(
|
||||
panel1.group,
|
||||
panel2.group.id,
|
||||
'panel2',
|
||||
Position.Center
|
||||
);
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
expect(panel2Spy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('panel is not disposed of when moved within another group', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const dockview = new DockviewComponent(container, {
|
||||
components: {
|
||||
default: PanelContentPartTest,
|
||||
},
|
||||
});
|
||||
|
||||
dockview.layout(500, 1000);
|
||||
|
||||
const panel1 = dockview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
const panel2 = dockview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
expect(panel1.group).toEqual(panel2.group);
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
dockview.moveGroupOrPanel(
|
||||
panel1.group,
|
||||
panel1.group.id,
|
||||
'panel1',
|
||||
Position.Center,
|
||||
0
|
||||
);
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
expect(panel2Spy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('panel is disposed of when group is disposed', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const dockview = new DockviewComponent(container, {
|
||||
components: {
|
||||
default: PanelContentPartTest,
|
||||
},
|
||||
});
|
||||
|
||||
dockview.layout(500, 1000);
|
||||
|
||||
const panel1 = dockview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
const panel2 = dockview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
expect(panel1.group).toEqual(panel2.group);
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
dockview.removeGroup(panel1.group);
|
||||
|
||||
expect(panel1Spy).toBeCalledTimes(1);
|
||||
expect(panel2Spy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is disposed of when component is disposed', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const dockview = new DockviewComponent(container, {
|
||||
components: {
|
||||
default: PanelContentPartTest,
|
||||
},
|
||||
});
|
||||
|
||||
dockview.layout(500, 1000);
|
||||
|
||||
const panel1 = dockview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
const panel2 = dockview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
expect(panel1.group).toEqual(panel2.group);
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
dockview.dispose();
|
||||
|
||||
expect(panel1Spy).toBeCalledTimes(1);
|
||||
expect(panel2Spy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is disposed of when from JSON is called', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const dockview = new DockviewComponent(container, {
|
||||
components: {
|
||||
default: PanelContentPartTest,
|
||||
},
|
||||
});
|
||||
dockview.deserializer = new ReactPanelDeserialzier(dockview);
|
||||
|
||||
dockview.layout(500, 1000);
|
||||
|
||||
const panel1 = dockview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
const panel2 = dockview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
tabComponent: 'default',
|
||||
});
|
||||
|
||||
expect(panel1.group).toEqual(panel2.group);
|
||||
|
||||
const groupSpy = jest.spyOn(panel1.group, 'dispose');
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
dockview.fromJSON({
|
||||
grid: {
|
||||
height: 0,
|
||||
width: 0,
|
||||
root: { type: 'branch', data: [] },
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
},
|
||||
panels: {},
|
||||
});
|
||||
|
||||
expect(groupSpy).toBeCalledTimes(1);
|
||||
expect(panel1Spy).toBeCalledTimes(1);
|
||||
expect(panel2Spy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
// group is disposed of when dockview is disposed
|
||||
// watermark is disposed of when removed
|
||||
// watermark is disposed of when dockview is disposed
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DockviewComponent } from '../..';
|
||||
import { DockviewApi } from '../../api/component.api';
|
||||
import { IGroupPanelView } from '../../dockview/defaultGroupPanelView';
|
||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||
|
||||
describe('dockviewGroupPanel', () => {
|
||||
@ -68,4 +69,31 @@ describe('dockviewGroupPanel', () => {
|
||||
|
||||
disposable.dispose();
|
||||
});
|
||||
|
||||
test('dispose cleanup', () => {
|
||||
const dockviewApiMock = jest.fn<DockviewApi, []>(() => {
|
||||
return {} as any;
|
||||
});
|
||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
||||
return {} as any;
|
||||
});
|
||||
const api = new dockviewApiMock();
|
||||
const accessor = new accessorMock();
|
||||
|
||||
const cut = new DockviewGroupPanel('fake-id', accessor, api);
|
||||
|
||||
const viewMock = jest.fn<IGroupPanelView, []>(() => {
|
||||
return {
|
||||
init: jest.fn(),
|
||||
dispose: jest.fn(),
|
||||
} as any;
|
||||
});
|
||||
const view = new viewMock();
|
||||
|
||||
cut.init({ params: {}, view, title: 'title' });
|
||||
|
||||
cut.dispose();
|
||||
|
||||
expect(view.dispose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -1666,4 +1666,99 @@ describe('gridview', () => {
|
||||
activePanel: 'panel_1',
|
||||
});
|
||||
});
|
||||
|
||||
test('panel is disposed of when component is disposed', () => {
|
||||
const gridview = new GridviewComponent(container, {
|
||||
proportionalLayout: false,
|
||||
orientation: Orientation.VERTICAL,
|
||||
components: { default: TestGridview },
|
||||
});
|
||||
|
||||
gridview.layout(1000, 1000);
|
||||
|
||||
gridview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
});
|
||||
gridview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
});
|
||||
|
||||
const panel1 = gridview.getPanel('panel1');
|
||||
const panel2 = gridview.getPanel('panel2');
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
gridview.dispose();
|
||||
|
||||
expect(panel1Spy).toHaveBeenCalledTimes(1);
|
||||
expect(panel2Spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is disposed of when removed', () => {
|
||||
const gridview = new GridviewComponent(container, {
|
||||
proportionalLayout: false,
|
||||
orientation: Orientation.VERTICAL,
|
||||
components: { default: TestGridview },
|
||||
});
|
||||
gridview.layout(1000, 1000);
|
||||
|
||||
gridview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
});
|
||||
gridview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
});
|
||||
|
||||
const panel1 = gridview.getPanel('panel1');
|
||||
const panel2 = gridview.getPanel('panel2');
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
gridview.removePanel(panel2);
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
expect(panel2Spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is disposed of when fromJSON is called', () => {
|
||||
const gridview = new GridviewComponent(container, {
|
||||
proportionalLayout: false,
|
||||
orientation: Orientation.VERTICAL,
|
||||
components: { default: TestGridview },
|
||||
});
|
||||
gridview.layout(1000, 1000);
|
||||
|
||||
gridview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
});
|
||||
gridview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
});
|
||||
|
||||
const panel1 = gridview.getPanel('panel1');
|
||||
const panel2 = gridview.getPanel('panel2');
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
gridview.fromJSON({
|
||||
grid: {
|
||||
height: 0,
|
||||
width: 0,
|
||||
root: { type: 'branch', data: [] },
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
},
|
||||
});
|
||||
|
||||
expect(panel1Spy).toHaveBeenCalledTimes(1);
|
||||
expect(panel2Spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
@ -305,4 +305,100 @@ describe('componentPaneview', () => {
|
||||
|
||||
expect(container.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
test('panel is disposed of when component is disposed', () => {
|
||||
const paneview = new PaneviewComponent(container, {
|
||||
components: {
|
||||
testPanel: TestPanel,
|
||||
},
|
||||
});
|
||||
|
||||
paneview.layout(1000, 1000);
|
||||
|
||||
paneview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'testPanel',
|
||||
title: 'Panel 1',
|
||||
});
|
||||
paneview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'testPanel',
|
||||
title: 'Panel 2',
|
||||
});
|
||||
|
||||
const panel1 = paneview.getPanel('panel1');
|
||||
const panel2 = paneview.getPanel('panel2');
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
paneview.dispose();
|
||||
|
||||
expect(panel1Spy).toHaveBeenCalledTimes(1);
|
||||
expect(panel2Spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is disposed of when removed', () => {
|
||||
const paneview = new PaneviewComponent(container, {
|
||||
components: {
|
||||
testPanel: TestPanel,
|
||||
},
|
||||
});
|
||||
|
||||
paneview.layout(1000, 1000);
|
||||
|
||||
paneview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'testPanel',
|
||||
title: 'Panel 1',
|
||||
});
|
||||
paneview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'testPanel',
|
||||
title: 'Panel 2',
|
||||
});
|
||||
|
||||
const panel1 = paneview.getPanel('panel1');
|
||||
const panel2 = paneview.getPanel('panel2');
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
paneview.removePanel(panel2);
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
expect(panel2Spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is disposed of when fromJSON is called', () => {
|
||||
const paneview = new PaneviewComponent(container, {
|
||||
components: {
|
||||
testPanel: TestPanel,
|
||||
},
|
||||
});
|
||||
|
||||
paneview.layout(1000, 1000);
|
||||
|
||||
paneview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'testPanel',
|
||||
title: 'Panel 1',
|
||||
});
|
||||
paneview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'testPanel',
|
||||
title: 'Panel 2',
|
||||
});
|
||||
|
||||
const panel1 = paneview.getPanel('panel1');
|
||||
const panel2 = paneview.getPanel('panel2');
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
paneview.fromJSON({ views: [], size: 0 });
|
||||
|
||||
expect(panel1Spy).toHaveBeenCalledTimes(1);
|
||||
expect(panel2Spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
@ -385,4 +385,101 @@ describe('componentSplitview', () => {
|
||||
|
||||
expect(container.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
test('panel is disposed of when component is disposed', () => {
|
||||
const splitview = new SplitviewComponent(container, {
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
components: {
|
||||
default: TestPanel,
|
||||
},
|
||||
});
|
||||
|
||||
splitview.layout(1000, 1000);
|
||||
|
||||
splitview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
});
|
||||
splitview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
});
|
||||
|
||||
const panel1 = splitview.getPanel('panel1');
|
||||
const panel2 = splitview.getPanel('panel2');
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
splitview.dispose();
|
||||
|
||||
expect(panel1Spy).toHaveBeenCalledTimes(1);
|
||||
expect(panel2Spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is disposed of when removed', () => {
|
||||
const splitview = new SplitviewComponent(container, {
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
components: {
|
||||
default: TestPanel,
|
||||
},
|
||||
});
|
||||
|
||||
splitview.layout(1000, 1000);
|
||||
|
||||
splitview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
});
|
||||
splitview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
});
|
||||
|
||||
const panel1 = splitview.getPanel('panel1');
|
||||
const panel2 = splitview.getPanel('panel2');
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
splitview.removePanel(panel2);
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
expect(panel2Spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('panel is disposed of when fromJSON is called', () => {
|
||||
const splitview = new SplitviewComponent(container, {
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
components: {
|
||||
default: TestPanel,
|
||||
},
|
||||
});
|
||||
|
||||
splitview.layout(1000, 1000);
|
||||
|
||||
splitview.addPanel({
|
||||
id: 'panel1',
|
||||
component: 'default',
|
||||
});
|
||||
splitview.addPanel({
|
||||
id: 'panel2',
|
||||
component: 'default',
|
||||
});
|
||||
|
||||
const panel1 = splitview.getPanel('panel1');
|
||||
const panel2 = splitview.getPanel('panel2');
|
||||
|
||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||
|
||||
splitview.fromJSON({
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
size: 0,
|
||||
views: [],
|
||||
});
|
||||
|
||||
expect(panel1Spy).toHaveBeenCalledTimes(1);
|
||||
expect(panel2Spy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
import { IGridviewPanel } from '../gridview/gridviewPanel';
|
||||
import { IGroupPanel } from '../groupview/groupPanel';
|
||||
import {
|
||||
AddPaneviewCompponentOptions,
|
||||
AddPaneviewComponentOptions,
|
||||
SerializedPaneview,
|
||||
IPaneviewComponent,
|
||||
} from '../paneview/paneviewComponent';
|
||||
@ -31,7 +31,6 @@ import { IView, Orientation, Sizing } from '../splitview/core/splitview';
|
||||
import { ISplitviewPanel } from '../splitview/splitviewPanel';
|
||||
import { GroupviewPanel, IGroupviewPanel } from '../groupview/groupviewPanel';
|
||||
import { Emitter, Event } from '../events';
|
||||
import { IDisposable } from '../lifecycle';
|
||||
import { PaneviewDropEvent } from '../react';
|
||||
|
||||
export interface CommonApi {
|
||||
@ -205,8 +204,8 @@ export class PaneviewApi implements CommonApi {
|
||||
this.component.layout(width, height);
|
||||
}
|
||||
|
||||
addPanel(options: AddPaneviewCompponentOptions): IDisposable {
|
||||
return this.component.addPanel(options);
|
||||
addPanel(options: AddPaneviewComponentOptions): void {
|
||||
this.component.addPanel(options);
|
||||
}
|
||||
|
||||
resizeToFit(): void {
|
||||
|
@ -59,7 +59,7 @@ export interface SerializedDockview {
|
||||
};
|
||||
panels: { [key: string]: GroupviewPanelState };
|
||||
activeGroup?: string;
|
||||
options: { tabHeight?: number };
|
||||
options?: { tabHeight?: number };
|
||||
}
|
||||
|
||||
export type DockviewComponentUpdateOptions = Pick<
|
||||
@ -453,7 +453,10 @@ export class DockviewComponent
|
||||
|
||||
removePanel(
|
||||
panel: IGroupPanel,
|
||||
options: { removeEmptyGroup: boolean } = { removeEmptyGroup: true }
|
||||
options: { removeEmptyGroup: boolean; skipDispose: boolean } = {
|
||||
removeEmptyGroup: true,
|
||||
skipDispose: false,
|
||||
}
|
||||
): void {
|
||||
const group = panel.group;
|
||||
|
||||
@ -465,6 +468,8 @@ export class DockviewComponent
|
||||
|
||||
group.model.removePanel(panel);
|
||||
|
||||
panel.dispose();
|
||||
|
||||
if (group.model.size === 0 && options.removeEmptyGroup) {
|
||||
this.removeGroup(group);
|
||||
}
|
||||
@ -526,6 +531,7 @@ export class DockviewComponent
|
||||
for (const panel of panels) {
|
||||
this.removePanel(panel, {
|
||||
removeEmptyGroup: false,
|
||||
skipDispose: false,
|
||||
});
|
||||
}
|
||||
|
||||
@ -654,6 +660,7 @@ export class DockviewComponent
|
||||
}
|
||||
|
||||
const view = new GroupviewPanel(this, id, options);
|
||||
view.init({ params: {}, containerApi: <any>null }); // required to initialized .part and allow for correct disposal of group
|
||||
|
||||
if (!this._groups.has(view.id)) {
|
||||
const disposable = new CompositeDisposable(
|
||||
|
@ -205,6 +205,7 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
||||
|
||||
if (item && !options?.skipDispose) {
|
||||
item.disposable.dispose();
|
||||
item.value.dispose();
|
||||
this._groups.delete(group.id);
|
||||
}
|
||||
|
||||
@ -318,6 +319,10 @@ export abstract class BaseGrid<T extends IGridPanelView>
|
||||
this._onDidRemoveGroup.dispose();
|
||||
this._onDidLayoutChange.dispose();
|
||||
|
||||
for (const group of this.groups) {
|
||||
group.dispose();
|
||||
}
|
||||
|
||||
this.gridview.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -127,6 +127,8 @@ export abstract class BasePanelView<T extends PanelApiImpl>
|
||||
|
||||
dispose() {
|
||||
super.dispose();
|
||||
|
||||
this.api.dispose();
|
||||
this.part?.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -22,16 +22,11 @@ import {
|
||||
IGridviewPanel,
|
||||
} from './gridviewPanel';
|
||||
import { BaseComponentOptions } from '../panel/types';
|
||||
import { GridviewPanelApiImpl } from '../api/gridviewPanelApi';
|
||||
import { GridviewApi } from '../api/component.api';
|
||||
import { Orientation, Sizing } from '../splitview/core/splitview';
|
||||
import { createComponent } from '../panel/componentFactory';
|
||||
import { Emitter, Event } from '../events';
|
||||
|
||||
interface PanelReference {
|
||||
api: GridviewPanelApiImpl;
|
||||
}
|
||||
|
||||
export interface SerializedGridview {
|
||||
grid: {
|
||||
height: number;
|
||||
@ -193,8 +188,13 @@ export class GridviewComponent
|
||||
) {
|
||||
const { grid, activePanel } = serializedGridview;
|
||||
|
||||
const groups = Array.from(this._groups.values()); // reassign since group panels will mutate
|
||||
for (const group of groups) {
|
||||
group.disposable.dispose();
|
||||
this.doRemoveGroup(group.value, { skipActive: true });
|
||||
}
|
||||
|
||||
this.gridview.clear();
|
||||
this._groups.clear();
|
||||
|
||||
const queue: Function[] = [];
|
||||
|
||||
@ -286,7 +286,7 @@ export class GridviewComponent
|
||||
this.doAddGroup(removedPanel, relativeLocation, options.size);
|
||||
}
|
||||
|
||||
public addPanel(options: AddComponentOptions): PanelReference {
|
||||
public addPanel(options: AddComponentOptions): void {
|
||||
let relativeLocation: number[] = options.location || [0];
|
||||
|
||||
if (options.position?.reference) {
|
||||
@ -342,8 +342,6 @@ export class GridviewComponent
|
||||
this.registerPanel(view);
|
||||
|
||||
this.doAddGroup(view, relativeLocation, options.size);
|
||||
|
||||
return { api: view.api };
|
||||
}
|
||||
|
||||
private registerPanel(panel: GridviewPanel) {
|
||||
@ -420,13 +418,6 @@ export class GridviewComponent
|
||||
|
||||
removeGroup(group: GridviewPanel) {
|
||||
super.removeGroup(group);
|
||||
|
||||
const panel = this._groups.get(group.id);
|
||||
|
||||
if (panel) {
|
||||
panel.disposable.dispose();
|
||||
this._groups.delete(group.id);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
|
@ -645,12 +645,14 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
super.dispose();
|
||||
|
||||
this.watermark?.dispose();
|
||||
|
||||
for (const panel of this.panels) {
|
||||
panel.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
|
||||
this.dropTarget.dispose();
|
||||
this.tabsContainer.dispose();
|
||||
this.contentContainer.dispose();
|
||||
|
@ -142,10 +142,18 @@ export class Paneview extends CompositeDisposable implements IDisposable {
|
||||
return this.splitview.getViews();
|
||||
}
|
||||
|
||||
public removePane(index: number) {
|
||||
public removePane(
|
||||
index: number,
|
||||
options: { skipDispose: boolean } = { skipDispose: false }
|
||||
) {
|
||||
const paneItem = this.paneItems.splice(index, 1)[0];
|
||||
this.splitview.removeView(index);
|
||||
paneItem.disposable.dispose();
|
||||
|
||||
if (!options.skipDispose) {
|
||||
paneItem.disposable.dispose();
|
||||
paneItem.pane.dispose();
|
||||
}
|
||||
|
||||
return paneItem;
|
||||
}
|
||||
|
||||
@ -154,7 +162,7 @@ export class Paneview extends CompositeDisposable implements IDisposable {
|
||||
return;
|
||||
}
|
||||
|
||||
const view = this.removePane(from);
|
||||
const view = this.removePane(from, { skipDispose: true });
|
||||
|
||||
this.skipAnimation = true;
|
||||
try {
|
||||
@ -196,6 +204,7 @@ export class Paneview extends CompositeDisposable implements IDisposable {
|
||||
|
||||
this.paneItems.forEach((paneItem) => {
|
||||
paneItem.disposable.dispose();
|
||||
paneItem.pane.dispose();
|
||||
});
|
||||
this.paneItems = [];
|
||||
|
||||
|
@ -78,7 +78,7 @@ export class PaneFramework extends DraggablePaneviewPanel {
|
||||
}
|
||||
}
|
||||
|
||||
export interface AddPaneviewCompponentOptions {
|
||||
export interface AddPaneviewComponentOptions {
|
||||
id: string;
|
||||
component: string;
|
||||
headerComponent?: string;
|
||||
@ -102,7 +102,7 @@ export interface IPaneviewComponent extends IDisposable {
|
||||
readonly onDidRemoveView: Event<PaneviewPanel>;
|
||||
readonly onDidDrop: Event<PaneviewDropEvent2>;
|
||||
readonly onDidLayoutChange: Event<void>;
|
||||
addPanel(options: AddPaneviewCompponentOptions): IDisposable;
|
||||
addPanel(options: AddPaneviewComponentOptions): IPaneviewPanel;
|
||||
layout(width: number, height: number): void;
|
||||
toJSON(): SerializedPaneview;
|
||||
fromJSON(
|
||||
@ -123,6 +123,7 @@ export class PaneviewComponent
|
||||
implements IPaneviewComponent
|
||||
{
|
||||
private _disposable = new MutableDisposable();
|
||||
private _viewDisposables = new Map<string, IDisposable>();
|
||||
private _paneview!: Paneview;
|
||||
|
||||
private readonly _onDidLayoutChange = new Emitter<void>();
|
||||
@ -217,7 +218,7 @@ export class PaneviewComponent
|
||||
this._options = { ...this.options, ...options };
|
||||
}
|
||||
|
||||
addPanel(options: AddPaneviewCompponentOptions): IDisposable {
|
||||
addPanel(options: AddPaneviewComponentOptions): IPaneviewPanel {
|
||||
const body = createComponent(
|
||||
options.id,
|
||||
options.component,
|
||||
@ -262,11 +263,7 @@ export class PaneviewComponent
|
||||
disableDnd: !!this.options.disableDnd,
|
||||
});
|
||||
|
||||
const disposable = new CompositeDisposable(
|
||||
view.onDidDrop((event) => {
|
||||
this._onDidDrop.fire(event);
|
||||
})
|
||||
);
|
||||
this.doAddPanel(view);
|
||||
|
||||
const size: Sizing | number =
|
||||
typeof options.size === 'number' ? options.size : Sizing.Distribute;
|
||||
@ -286,7 +283,7 @@ export class PaneviewComponent
|
||||
|
||||
view.orientation = this.paneview.orientation;
|
||||
|
||||
return disposable;
|
||||
return view;
|
||||
}
|
||||
|
||||
getPanels(): PaneviewPanel[] {
|
||||
@ -297,6 +294,8 @@ export class PaneviewComponent
|
||||
const views = this.getPanels();
|
||||
const index = views.findIndex((_) => _ === panel);
|
||||
this.paneview.removePane(index);
|
||||
|
||||
this.doRemovePanel(panel);
|
||||
}
|
||||
|
||||
movePanel(from: number, to: number): void {
|
||||
@ -362,6 +361,11 @@ export class PaneviewComponent
|
||||
|
||||
const queue: Function[] = [];
|
||||
|
||||
for (const [_, value] of this._viewDisposables.entries()) {
|
||||
value.dispose();
|
||||
}
|
||||
this._viewDisposables.clear();
|
||||
|
||||
this.paneview.dispose();
|
||||
|
||||
this.paneview = new Paneview(this.element, {
|
||||
@ -416,9 +420,7 @@ export class PaneviewComponent
|
||||
disableDnd: !!this.options.disableDnd,
|
||||
});
|
||||
|
||||
panel.onDidDrop((event) => {
|
||||
this._onDidDrop.fire(event);
|
||||
});
|
||||
this.doAddPanel(panel);
|
||||
|
||||
queue.push(() => {
|
||||
panel.init({
|
||||
@ -453,9 +455,31 @@ export class PaneviewComponent
|
||||
}
|
||||
}
|
||||
|
||||
private doAddPanel(panel: PaneFramework) {
|
||||
const disposable = panel.onDidDrop((event) => {
|
||||
this._onDidDrop.fire(event);
|
||||
});
|
||||
|
||||
this._viewDisposables.set(panel.id, disposable);
|
||||
}
|
||||
|
||||
private doRemovePanel(panel: PaneviewPanel) {
|
||||
const disposable = this._viewDisposables.get(panel.id);
|
||||
|
||||
if (disposable) {
|
||||
disposable.dispose();
|
||||
this._viewDisposables.delete(panel.id);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
for (const [_, value] of this._viewDisposables.entries()) {
|
||||
value.dispose();
|
||||
}
|
||||
this._viewDisposables.clear();
|
||||
|
||||
this.paneview.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
GridviewPanel,
|
||||
GridviewInitParameters,
|
||||
} from '../../gridview/gridviewPanel';
|
||||
import { IFrameworkPart } from '../../panel/types';
|
||||
import { ReactPart, ReactPortalStore } from '../react';
|
||||
import { IGridviewPanelProps } from './gridview';
|
||||
|
||||
@ -15,7 +16,7 @@ export class ReactGridPanelView extends GridviewPanel {
|
||||
super(id, component);
|
||||
}
|
||||
|
||||
getComponent() {
|
||||
getComponent(): IFrameworkPart {
|
||||
return new ReactPart(
|
||||
this.element,
|
||||
this.reactPortalStore,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
CompositeDisposable,
|
||||
IDisposable,
|
||||
IValueDisposable,
|
||||
MutableDisposable,
|
||||
} from '../lifecycle';
|
||||
import {
|
||||
@ -89,7 +90,7 @@ export class SplitviewComponent
|
||||
private _disposable = new MutableDisposable();
|
||||
private _splitview!: Splitview;
|
||||
private _activePanel: SplitviewPanel | undefined;
|
||||
private panels = new Map<string, IDisposable>();
|
||||
private panels = new Map<string, IValueDisposable<SplitviewPanel>>();
|
||||
private _options: SplitviewComponentOptions;
|
||||
|
||||
private readonly _onDidAddView = new Emitter<IView>();
|
||||
@ -229,7 +230,14 @@ export class SplitviewComponent
|
||||
|
||||
removePanel(panel: SplitviewPanel, sizing?: Sizing) {
|
||||
const disposable = this.panels.get(panel.id);
|
||||
disposable?.dispose();
|
||||
|
||||
if (!disposable) {
|
||||
throw new Error(`unknown splitview panel ${panel.id}`);
|
||||
}
|
||||
|
||||
disposable.disposable.dispose();
|
||||
disposable.value.dispose();
|
||||
|
||||
this.panels.delete(panel.id);
|
||||
|
||||
const index = this.getPanels().findIndex((_) => _ === panel);
|
||||
@ -313,7 +321,7 @@ export class SplitviewComponent
|
||||
this.setActive(view, true);
|
||||
});
|
||||
|
||||
this.panels.set(view.id, disposable);
|
||||
this.panels.set(view.id, { disposable, value: view });
|
||||
}
|
||||
|
||||
toJSON(): SerializedSplitview {
|
||||
@ -343,6 +351,11 @@ export class SplitviewComponent
|
||||
): void {
|
||||
const { views, orientation, size, activeView } = serializedSplitview;
|
||||
|
||||
for (const [_, value] of this.panels.entries()) {
|
||||
value.disposable.dispose();
|
||||
value.value.dispose();
|
||||
}
|
||||
this.panels.clear();
|
||||
this.splitview.dispose();
|
||||
|
||||
const queue: Function[] = [];
|
||||
@ -416,9 +429,10 @@ export class SplitviewComponent
|
||||
}
|
||||
|
||||
dispose() {
|
||||
Array.from(this.panels.values()).forEach((value) => {
|
||||
value.dispose();
|
||||
});
|
||||
for (const [_, value] of this.panels.entries()) {
|
||||
value.disposable.dispose();
|
||||
value.value.dispose();
|
||||
}
|
||||
this.panels.clear();
|
||||
|
||||
this.splitview.dispose();
|
||||
|
Loading…
Reference in New Issue
Block a user