Merge pull request #72 from mathuo/71-dockview-panel-disposal

work in progress
This commit is contained in:
mathuo 2022-04-23 23:01:53 +01:00 committed by GitHub
commit 8b8c0fd79b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 798 additions and 46 deletions

View File

@ -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();
});
});

View File

@ -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
});

View File

@ -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();
});
});

View File

@ -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);
});
});

View File

@ -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);
});
});

View File

@ -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);
});
});

View File

@ -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 {

View File

@ -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(

View File

@ -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();
}
}

View File

@ -127,6 +127,8 @@ export abstract class BasePanelView<T extends PanelApiImpl>
dispose() {
super.dispose();
this.api.dispose();
this.part?.dispose();
}
}

View File

@ -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() {

View File

@ -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();

View File

@ -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 = [];

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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();