Merge pull request #783 from mathuo/777-keeping-header-actions-even-when-no-tabs-are-open

feat: ensure group always exists
This commit is contained in:
mathuo 2024-12-10 19:20:31 +00:00 committed by GitHub
commit c5025424c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 24 additions and 150 deletions

View File

@ -1,23 +0,0 @@
import { DockviewApi } from '../../../../api/component.api';
import { Watermark } from '../../../../dockview/components/watermark/watermark';
import { fromPartial } from '@total-typescript/shoehorn';
describe('watermark', () => {
test('that the group is closed when the close button is clicked', () => {
const cut = new Watermark();
const api = fromPartial<DockviewApi>({
removeGroup: jest.fn(),
});
const group = jest.fn() as any;
cut.init({ containerApi: api, group });
const closeEl = cut.element.querySelector('.dv-close-action')!;
expect(closeEl).toBeTruthy();
closeEl.dispatchEvent(new Event('click'));
expect(api.removeGroup).toHaveBeenCalledWith(group);
});
});

View File

@ -9,39 +9,20 @@ import {
} from '../../dockview/types'; } from '../../dockview/types';
import { PanelUpdateEvent, Parameters } from '../../panel/types'; import { PanelUpdateEvent, Parameters } from '../../panel/types';
import { import {
DockviewGroupLocation,
DockviewGroupPanelModel, DockviewGroupPanelModel,
GroupOptions, GroupOptions,
} from '../../dockview/dockviewGroupPanelModel'; } from '../../dockview/dockviewGroupPanelModel';
import { fireEvent } from '@testing-library/dom'; import { fireEvent } from '@testing-library/dom';
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer'; import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
import { CompositeDisposable } from '../../lifecycle'; import { CompositeDisposable } from '../../lifecycle';
import { import { DockviewPanelApi } from '../../api/dockviewPanelApi';
ActiveGroupEvent,
DockviewPanelApi,
GroupChangedEvent,
RendererChangedEvent,
} from '../../api/dockviewPanelApi';
import { IDockviewPanel } from '../../dockview/dockviewPanel'; import { IDockviewPanel } from '../../dockview/dockviewPanel';
import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel'; import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel'; import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
import { WatermarkRendererInitParameters } from '../../dockview/types'; import { WatermarkRendererInitParameters } from '../../dockview/types';
import { createOffsetDragOverEvent } from '../__test_utils__/utils'; import { createOffsetDragOverEvent } from '../__test_utils__/utils';
import { import { OverlayRenderContainer } from '../../overlay/overlayRenderContainer';
DockviewPanelRenderer, import { Emitter } from '../../events';
OverlayRenderContainer,
} from '../../overlay/overlayRenderContainer';
import { DockviewGroupPanelFloatingChangeEvent } from '../../api/dockviewGroupPanelApi';
import { SizeEvent } from '../../api/gridviewPanelApi';
import {
PanelDimensionChangeEvent,
FocusEvent,
VisibilityEvent,
ActiveEvent,
WillFocusEvent,
} from '../../api/panelApi';
import { Position } from '../../dnd/droptarget';
import { Emitter, Event } from '../../events';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
enum GroupChangeKind2 { enum GroupChangeKind2 {
@ -513,14 +494,14 @@ describe('dockviewGroupPanelModel', () => {
groupview.model.closeAllPanels(); groupview.model.closeAllPanels();
expect(removePanelMock).toBeCalledWith(panel1); expect(removePanelMock).toHaveBeenCalledWith(panel1, undefined);
expect(removePanelMock).toBeCalledWith(panel2); expect(removePanelMock).toHaveBeenCalledWith(panel2, undefined);
expect(removePanelMock).toBeCalledWith(panel3); expect(removePanelMock).toHaveBeenCalledWith(panel3, undefined);
}); });
test('closeAllPanels with no panels', () => { test('closeAllPanels with no panels', () => {
groupview.model.closeAllPanels(); groupview.model.closeAllPanels();
expect(removeGroupMock).toBeCalledWith(groupview); expect(removeGroupMock).toHaveBeenCalledWith(groupview);
}); });
test('that group is set on panel during onDidAddPanel event', () => { test('that group is set on panel during onDidAddPanel event', () => {

View File

@ -1,42 +1,4 @@
.dv-watermark { .dv-watermark {
display: flex; display: flex;
width: 100%; height: 100%;
&.dv-has-actions {
.dv-watermark-title {
.dv-actions-container {
display: none;
}
}
}
.dv-watermark-title {
height: 35px;
width: 100%;
display: flex;
}
.dv-watermark-content {
flex-grow: 1;
}
.dv-actions-container {
display: flex;
align-items: center;
padding: 0px 8px;
.dv-close-action {
padding: 4px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
cursor: pointer;
color: var(--dv-activegroup-hiddenpanel-tab-color);
&:hover {
border-radius: 2px;
background-color: var(--dv-icon-hover-background-color);
}
}
}
} }

View File

@ -2,20 +2,13 @@ import {
IWatermarkRenderer, IWatermarkRenderer,
WatermarkRendererInitParameters, WatermarkRendererInitParameters,
} from '../../types'; } from '../../types';
import { addDisposableListener } from '../../../events';
import { toggleClass } from '../../../dom';
import { CompositeDisposable } from '../../../lifecycle'; import { CompositeDisposable } from '../../../lifecycle';
import { IDockviewGroupPanel } from '../../dockviewGroupPanel';
import { createCloseButton } from '../../../svg';
import { DockviewApi } from '../../../api/component.api';
export class Watermark export class Watermark
extends CompositeDisposable extends CompositeDisposable
implements IWatermarkRenderer implements IWatermarkRenderer
{ {
private readonly _element: HTMLElement; private readonly _element: HTMLElement;
private _group: IDockviewGroupPanel | undefined;
private _api: DockviewApi | undefined;
get element(): HTMLElement { get element(): HTMLElement {
return this._element; return this._element;
@ -25,50 +18,9 @@ export class Watermark
super(); super();
this._element = document.createElement('div'); this._element = document.createElement('div');
this._element.className = 'dv-watermark'; this._element.className = 'dv-watermark';
const title = document.createElement('div');
title.className = 'dv-watermark-title';
const emptySpace = document.createElement('span');
emptySpace.style.flexGrow = '1';
const content = document.createElement('div');
content.className = 'dv-watermark-content';
this._element.appendChild(title);
this._element.appendChild(content);
const actionsContainer = document.createElement('div');
actionsContainer.className = 'dv-actions-container';
const closeAnchor = document.createElement('div');
closeAnchor.className = 'dv-close-action';
closeAnchor.appendChild(createCloseButton());
actionsContainer.appendChild(closeAnchor);
title.appendChild(emptySpace);
title.appendChild(actionsContainer);
this.addDisposables(
addDisposableListener(closeAnchor, 'click', (event: MouseEvent) => {
event.preventDefault();
if (this._group) {
this._api?.removeGroup(this._group);
}
})
);
} }
init(_params: WatermarkRendererInitParameters): void { init(_params: WatermarkRendererInitParameters): void {
this._api = _params.containerApi; // noop
this._group = _params.group;
this.render();
}
private render(): void {
const isOneGroup = !!(this._api && this._api.size <= 1);
toggleClass(this.element, 'dv-has-actions', isOneGroup);
} }
} }

View File

@ -1662,11 +1662,10 @@ export class DockviewComponent
panel: IDockviewPanel, panel: IDockviewPanel,
options: { options: {
removeEmptyGroup: boolean; removeEmptyGroup: boolean;
skipDispose: boolean; skipDispose?: boolean;
skipSetActiveGroup?: boolean; skipSetActiveGroup?: boolean;
} = { } = {
removeEmptyGroup: true, removeEmptyGroup: true,
skipDispose: false,
} }
): void { ): void {
const group = panel.group; const group = panel.group;

View File

@ -9,12 +9,6 @@
outline: none; outline: none;
} }
&.dv-empty {
> .dv-tabs-and-actions-container {
display: none;
}
}
> .dv-content-container { > .dv-content-container {
flex-grow: 1; flex-grow: 1;
min-height: 0; min-height: 0;

View File

@ -787,7 +787,15 @@ export class DockviewGroupPanelModel
} }
private doClose(panel: IDockviewPanel): void { private doClose(panel: IDockviewPanel): void {
this.accessor.removePanel(panel); const isLast =
this.panels.length === 1 && this.accessor.groups.length === 1;
this.accessor.removePanel(
panel,
isLast && this.accessor.options.noPanelsOverlay === 'emptyGroup'
? { removeEmptyGroup: false }
: undefined
);
} }
public isPanelActive(panel: IDockviewPanel): boolean { public isPanelActive(panel: IDockviewPanel): boolean {
@ -955,8 +963,6 @@ export class DockviewGroupPanelModel
} }
private updateContainer(): void { private updateContainer(): void {
toggleClass(this.container, 'dv-empty', this.isEmpty);
this.panels.forEach((panel) => panel.runEvents()); this.panels.forEach((panel) => panel.runEvents());
if (this.isEmpty && !this.watermark) { if (this.isEmpty && !this.watermark) {
@ -973,14 +979,12 @@ export class DockviewGroupPanelModel
} }
}); });
this.tabsContainer.hide();
this.contentContainer.element.appendChild(this.watermark.element); this.contentContainer.element.appendChild(this.watermark.element);
} }
if (!this.isEmpty && this.watermark) { if (!this.isEmpty && this.watermark) {
this.watermark.element.remove(); this.watermark.element.remove();
this.watermark.dispose?.(); this.watermark.dispose?.();
this.watermark = undefined; this.watermark = undefined;
this.tabsContainer.show();
} }
} }

View File

@ -59,6 +59,10 @@ export interface DockviewOptions {
* Pixel gap between groups * Pixel gap between groups
*/ */
gap?: number; gap?: number;
/**
* Define the behaviour of the dock when there are no panels to display. Defaults to `watermark`.
*/
noPanelsOverlay?: 'emptyGroup' | 'watermark';
} }
export interface DockviewDndOverlayEvent { export interface DockviewDndOverlayEvent {
@ -111,6 +115,7 @@ export const PROPERTY_KEYS: (keyof DockviewOptions)[] = (() => {
disableDnd: undefined, disableDnd: undefined,
gap: undefined, gap: undefined,
className: undefined, className: undefined,
noPanelsOverlay: undefined,
}; };
return Object.keys(properties) as (keyof DockviewOptions)[]; return Object.keys(properties) as (keyof DockviewOptions)[];