feat: popout group enhancements

This commit is contained in:
mathuo 2024-01-30 17:41:04 +00:00
parent c2791c6124
commit 0bca63b550
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
5 changed files with 121 additions and 56 deletions

View File

@ -62,16 +62,19 @@ describe('contentContainer', () => {
const disposable = new CompositeDisposable();
const dockviewComponent = jest.fn<DockviewComponent, []>(() => {
return {
renderer: 'onlyWhenVisibile',
overlayRenderContainer: new OverlayRenderContainer(
const overlayRenderContainer = new OverlayRenderContainer(
document.createElement('div')
),
} as DockviewComponent;
});
);
const cut = new ContentContainer(dockviewComponent(), jest.fn() as any);
const cut = new ContentContainer(
fromPartial<DockviewComponent>({
renderer: 'onlyWhenVisibile',
overlayRenderContainer,
}),
fromPartial<DockviewGroupPanelModel>({
renderContainer: overlayRenderContainer,
})
);
disposable.addDisposables(
cut.onDidFocus(() => {
@ -84,12 +87,12 @@ describe('contentContainer', () => {
const contentRenderer = new TestContentRenderer('id-1');
const panel = {
const panel = fromPartial<IDockviewPanel>({
view: {
content: contentRenderer,
} as Partial<IDockviewPanelModel>,
},
api: { renderer: 'onlyWhenVisibile' },
} as Partial<IDockviewPanel>;
});
cut.openPanel(panel as IDockviewPanel);
@ -151,13 +154,17 @@ describe('contentContainer', () => {
});
test("that panels renderered as 'onlyWhenVisibile' are removed when closed", () => {
const overlayRenderContainer = fromPartial<OverlayRenderContainer>({
detatch: jest.fn(),
});
const cut = new ContentContainer(
fromPartial<DockviewComponent>({
overlayRenderContainer: {
detatch: jest.fn(),
},
overlayRenderContainer,
}),
fromPartial<DockviewGroupPanelModel>({})
fromPartial<DockviewGroupPanelModel>({
renderContainer: overlayRenderContainer,
})
);
const panel1 = fromPartial<IDockviewPanel>({

View File

@ -4434,44 +4434,12 @@ describe('dockviewComponent', () => {
cb();
}
}),
removeEventListener: jest.fn(),
close: jest.fn(),
})
);
});
test('that can remove a popout group', async () => {
const container = document.createElement('div');
const dockview = new DockviewComponent({
parentElement: container,
components: {
default: PanelContentPartTest,
},
tabComponents: {
test_tab_id: PanelTabPartTest,
},
orientation: Orientation.HORIZONTAL,
});
dockview.layout(1000, 500);
const panel1 = dockview.addPanel({
id: 'panel_1',
component: 'default',
});
await dockview.addPopoutGroup(panel1);
expect(dockview.panels.length).toBe(1);
expect(dockview.groups.length).toBe(2);
expect(panel1.api.group.api.location.type).toBe('popout');
dockview.removePanel(panel1);
expect(dockview.panels.length).toBe(0);
expect(dockview.groups.length).toBe(0);
});
test('add a popout group', async () => {
const container = document.createElement('div');
@ -4511,6 +4479,39 @@ describe('dockviewComponent', () => {
expect(dockview.panels.length).toBe(2);
});
test('that can remove a popout group', async () => {
const container = document.createElement('div');
const dockview = new DockviewComponent({
parentElement: container,
components: {
default: PanelContentPartTest,
},
tabComponents: {
test_tab_id: PanelTabPartTest,
},
orientation: Orientation.HORIZONTAL,
});
dockview.layout(1000, 500);
const panel1 = dockview.addPanel({
id: 'panel_1',
component: 'default',
});
await dockview.addPopoutGroup(panel1);
expect(dockview.panels.length).toBe(1);
expect(dockview.groups.length).toBe(2);
expect(panel1.api.group.api.location.type).toBe('popout');
dockview.removePanel(panel1);
expect(dockview.panels.length).toBe(0);
expect(dockview.groups.length).toBe(0);
});
test('move from fixed to popout group and back', async () => {
const container = document.createElement('div');

View File

@ -133,7 +133,7 @@ export class ContentContainer
switch (panel.api.renderer) {
case 'onlyWhenVisibile':
this.accessor.overlayRenderContainer.detatch(panel);
this.group.renderContainer.detatch(panel);
if (this.panel) {
if (doRender) {
this._element.appendChild(
@ -149,7 +149,7 @@ export class ContentContainer
) {
this._element.removeChild(panel.view.content.element);
}
container = this.accessor.overlayRenderContainer.attach({
container = this.group.renderContainer.attach({
panel,
referenceContainer: this,
});

View File

@ -13,7 +13,7 @@ import {
import { tail, sequenceEquals, remove } from '../array';
import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
import { CompositeDisposable, Disposable, IDisposable } from '../lifecycle';
import { Event, Emitter } from '../events';
import { Event, Emitter, addDisposableWindowListener } from '../events';
import { Watermark } from './components/watermark/watermark';
import { IWatermarkRenderer, GroupviewPanelState } from './types';
import { sequentialNumberGenerator } from '../math';
@ -561,12 +561,15 @@ export class DockviewComponent
from: DockviewGroupPanel;
to: DockviewGroupPanel;
}) {
const activePanel = options.from.activePanel;
const panels = [...options.from.panels].map((panel) =>
options.from.model.removePanel(panel)
);
panels.forEach((panel) => {
options.to.model.openPanel(panel);
options.to.model.openPanel(panel, {
skipSetPanelActive: activePanel !== panel,
});
});
}
@ -624,10 +627,18 @@ export class DockviewComponent
return;
}
const gready = document.createElement('div');
gready.className = 'dv-overlay-render-container';
const overlayRenderContainer = new OverlayRenderContainer(
gready
);
const referenceGroup =
item instanceof DockviewPanel ? item.group : item;
const group = this.createGroup({ id: groupId });
group.model.renderContainer = overlayRenderContainer;
if (item instanceof DockviewPanel) {
const panel = referenceGroup.model.removePanel(item);
@ -637,9 +648,13 @@ export class DockviewComponent
from: referenceGroup,
to: group,
});
referenceGroup.api.setHidden(false);
referenceGroup.api.setHidden(true);
}
popoutContainer.classList.add('dv-dockview');
popoutContainer.style.overflow = 'hidden';
popoutContainer.appendChild(gready);
popoutContainer.appendChild(group.element);
group.model.location = {
@ -655,6 +670,19 @@ export class DockviewComponent
};
popoutWindowDisposable.addDisposables(
/**
* ResizeObserver seems slow here, I do not know why but we don't need it
* since we can reply on the window resize event as we will occupy the full
* window dimensions
*/
addDisposableWindowListener(
_window.window!,
'resize',
() => {
group.layout(window.innerWidth, window.innerHeight);
}
),
overlayRenderContainer,
Disposable.from(() => {
if (this.getPanel(referenceGroup.id)) {
moveGroupWithoutDestroying({
@ -666,12 +694,16 @@ export class DockviewComponent
referenceGroup.api.setHidden(false);
}
this.doRemoveGroup(group);
this.doRemoveGroup(group, {
skipPopoutAssociated: true,
});
} else {
const removedGroup = this.doRemoveGroup(group, {
skipDispose: true,
skipActive: true,
});
removedGroup.model.renderContainer =
this.overlayRenderContainer;
removedGroup.model.location = { type: 'grid' };
this.doAddGroup(removedGroup, [0]);
}
@ -1484,6 +1516,7 @@ export class DockviewComponent
| {
skipActive?: boolean;
skipDispose?: boolean;
skipPopoutAssociated?: boolean;
}
| undefined
): DockviewGroupPanel {
@ -1523,7 +1556,9 @@ export class DockviewComponent
if (selectedGroup) {
if (!options?.skipDispose) {
this.doRemoveGroup(selectedGroup.referenceGroup);
if (!options?.skipPopoutAssociated) {
this.removeGroup(selectedGroup.referenceGroup);
}
selectedGroup.popoutGroup.dispose();
this._groups.delete(group.id);

View File

@ -26,6 +26,7 @@ import { DockviewDropTargets, IWatermarkRenderer } from './types';
import { DockviewGroupPanel } from './dockviewGroupPanel';
import { IDockviewPanel } from './dockviewPanel';
import { IHeaderActionsRenderer } from './options';
import { OverlayRenderContainer } from '../overlayRenderContainer';
interface GroupMoveEvent {
groupId: string;
@ -421,6 +422,27 @@ export class DockviewGroupPanelModel
);
}
private _overwriteRenderContainer: OverlayRenderContainer | null = null;
set renderContainer(value: OverlayRenderContainer | null) {
this.panels.forEach((panel) => {
this.renderContainer.detatch(panel);
});
this._overwriteRenderContainer = value;
this.panels.forEach((panel) => {
this.rerender(panel);
});
}
get renderContainer(): OverlayRenderContainer {
return (
this._overwriteRenderContainer ??
this.accessor.overlayRenderContainer
);
}
initialize(): void {
if (this.options.panels) {
this.options.panels.forEach((panel) => {