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

View File

@ -4434,44 +4434,12 @@ describe('dockviewComponent', () => {
cb(); cb();
} }
}), }),
removeEventListener: jest.fn(),
close: 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 () => { test('add a popout group', async () => {
const container = document.createElement('div'); const container = document.createElement('div');
@ -4511,6 +4479,39 @@ describe('dockviewComponent', () => {
expect(dockview.panels.length).toBe(2); 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 () => { test('move from fixed to popout group and back', async () => {
const container = document.createElement('div'); const container = document.createElement('div');

View File

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

View File

@ -13,7 +13,7 @@ import {
import { tail, sequenceEquals, remove } from '../array'; import { tail, sequenceEquals, remove } from '../array';
import { DockviewPanel, IDockviewPanel } from './dockviewPanel'; import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
import { CompositeDisposable, Disposable, IDisposable } from '../lifecycle'; import { CompositeDisposable, Disposable, IDisposable } from '../lifecycle';
import { Event, Emitter } from '../events'; import { Event, Emitter, addDisposableWindowListener } from '../events';
import { Watermark } from './components/watermark/watermark'; import { Watermark } from './components/watermark/watermark';
import { IWatermarkRenderer, GroupviewPanelState } from './types'; import { IWatermarkRenderer, GroupviewPanelState } from './types';
import { sequentialNumberGenerator } from '../math'; import { sequentialNumberGenerator } from '../math';
@ -561,12 +561,15 @@ export class DockviewComponent
from: DockviewGroupPanel; from: DockviewGroupPanel;
to: DockviewGroupPanel; to: DockviewGroupPanel;
}) { }) {
const activePanel = options.from.activePanel;
const panels = [...options.from.panels].map((panel) => const panels = [...options.from.panels].map((panel) =>
options.from.model.removePanel(panel) options.from.model.removePanel(panel)
); );
panels.forEach((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; return;
} }
const gready = document.createElement('div');
gready.className = 'dv-overlay-render-container';
const overlayRenderContainer = new OverlayRenderContainer(
gready
);
const referenceGroup = const referenceGroup =
item instanceof DockviewPanel ? item.group : item; item instanceof DockviewPanel ? item.group : item;
const group = this.createGroup({ id: groupId }); const group = this.createGroup({ id: groupId });
group.model.renderContainer = overlayRenderContainer;
if (item instanceof DockviewPanel) { if (item instanceof DockviewPanel) {
const panel = referenceGroup.model.removePanel(item); const panel = referenceGroup.model.removePanel(item);
@ -637,9 +648,13 @@ export class DockviewComponent
from: referenceGroup, from: referenceGroup,
to: group, 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); popoutContainer.appendChild(group.element);
group.model.location = { group.model.location = {
@ -655,6 +670,19 @@ export class DockviewComponent
}; };
popoutWindowDisposable.addDisposables( 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(() => { Disposable.from(() => {
if (this.getPanel(referenceGroup.id)) { if (this.getPanel(referenceGroup.id)) {
moveGroupWithoutDestroying({ moveGroupWithoutDestroying({
@ -666,12 +694,16 @@ export class DockviewComponent
referenceGroup.api.setHidden(false); referenceGroup.api.setHidden(false);
} }
this.doRemoveGroup(group); this.doRemoveGroup(group, {
skipPopoutAssociated: true,
});
} else { } else {
const removedGroup = this.doRemoveGroup(group, { const removedGroup = this.doRemoveGroup(group, {
skipDispose: true, skipDispose: true,
skipActive: true, skipActive: true,
}); });
removedGroup.model.renderContainer =
this.overlayRenderContainer;
removedGroup.model.location = { type: 'grid' }; removedGroup.model.location = { type: 'grid' };
this.doAddGroup(removedGroup, [0]); this.doAddGroup(removedGroup, [0]);
} }
@ -1484,6 +1516,7 @@ export class DockviewComponent
| { | {
skipActive?: boolean; skipActive?: boolean;
skipDispose?: boolean; skipDispose?: boolean;
skipPopoutAssociated?: boolean;
} }
| undefined | undefined
): DockviewGroupPanel { ): DockviewGroupPanel {
@ -1523,7 +1556,9 @@ export class DockviewComponent
if (selectedGroup) { if (selectedGroup) {
if (!options?.skipDispose) { if (!options?.skipDispose) {
this.doRemoveGroup(selectedGroup.referenceGroup); if (!options?.skipPopoutAssociated) {
this.removeGroup(selectedGroup.referenceGroup);
}
selectedGroup.popoutGroup.dispose(); selectedGroup.popoutGroup.dispose();
this._groups.delete(group.id); this._groups.delete(group.id);

View File

@ -26,6 +26,7 @@ import { DockviewDropTargets, IWatermarkRenderer } from './types';
import { DockviewGroupPanel } from './dockviewGroupPanel'; import { DockviewGroupPanel } from './dockviewGroupPanel';
import { IDockviewPanel } from './dockviewPanel'; import { IDockviewPanel } from './dockviewPanel';
import { IHeaderActionsRenderer } from './options'; import { IHeaderActionsRenderer } from './options';
import { OverlayRenderContainer } from '../overlayRenderContainer';
interface GroupMoveEvent { interface GroupMoveEvent {
groupId: string; 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 { initialize(): void {
if (this.options.panels) { if (this.options.panels) {
this.options.panels.forEach((panel) => { this.options.panels.forEach((panel) => {