diff --git a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts index 2bda5054f..5eb0806b5 100644 --- a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts @@ -7,7 +7,6 @@ import { import { PanelUpdateEvent } from '../../panel/types'; import { Orientation } from '../../splitview/core/splitview'; import { ReactPanelDeserialzier } from '../../react/deserializer'; -import { Position } from '../../dnd/droptarget'; import { GroupPanel } from '../../groupview/groupviewPanel'; import { CompositeDisposable } from '../../lifecycle'; import { @@ -459,7 +458,7 @@ describe('dockviewComponent', () => { await panel2.api.close(); - expect(dockview.size).toBe(1); // watermark + expect(dockview.size).toBe(0); expect(dockview.totalPanels).toBe(0); }); @@ -1206,55 +1205,6 @@ describe('dockviewComponent', () => { expect(dockview.totalPanels).toBe(0); }); - test('last group is retained for watermark', () => { - 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', - }); - - expect(dockview.size).toBe(1); - expect(dockview.totalPanels).toBe(1); - - const group = panel1.group; - - dockview.removePanel(panel1); - - expect(group.model.hasWatermark).toBeTruthy(); - expect(dockview.size).toBe(1); - expect(dockview.totalPanels).toBe(0); - - const panel2 = dockview.addPanel({ - id: 'panel2', - component: 'default', - tabComponent: 'default', - }); - - expect(group.model.hasWatermark).toBeFalsy(); - - const panel3 = dockview.addPanel({ - id: 'panel3', - component: 'default', - tabComponent: 'default', - }); - - expect(dockview.size).toBe(1); - expect(dockview.totalPanels).toBe(2); - - panel2.api.close(); - expect(group.model.hasWatermark).toBeFalsy(); - panel3.api.close(); - expect(group.model.hasWatermark).toBeTruthy(); - }); - test('panel is disposed of when removed', () => { const container = document.createElement('div'); @@ -2392,4 +2342,91 @@ describe('dockviewComponent', () => { options: {}, }); }); + + test('that a empty component has no groups', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent(container, { + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); + dockview.deserializer = new ReactPanelDeserialzier(dockview); + + expect(dockview.groups.length).toBe(0); + }); + + test('that deserializing an empty layout has zero groups and a watermark', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent(container, { + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); + dockview.deserializer = new ReactPanelDeserialzier(dockview); + + expect(dockview.groups.length).toBe(0); + + expect( + dockview.element.querySelectorAll('.dv-watermark-container').length + ).toBe(1); + + dockview.fromJSON({ + grid: { + orientation: Orientation.HORIZONTAL, + root: { + type: 'branch', + data: [], + }, + height: 100, + width: 100, + }, + panels: {}, + }); + + expect(dockview.groups.length).toBe(0); + + expect( + dockview.element.querySelectorAll('.dv-watermark-container').length + ).toBe(1); + }); + + test('empty', () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent(container, { + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); + dockview.deserializer = new ReactPanelDeserialzier(dockview); + + expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ + grid: { + height: 0, + width: 0, + orientation: Orientation.HORIZONTAL, + root: { + data: [], + type: 'branch', + size: 0, + }, + }, + options: {}, + panels: {}, + }); + }); }); diff --git a/packages/dockview/src/dockview/dockviewComponent.scss b/packages/dockview/src/dockview/dockviewComponent.scss index b9860c17f..db272fc48 100644 --- a/packages/dockview/src/dockview/dockviewComponent.scss +++ b/packages/dockview/src/dockview/dockviewComponent.scss @@ -1,3 +1,16 @@ +.dv-dockview { + position: relative; + background-color: var(--dv-group-view-background-color); + + .dv-watermark-container { + position: absolute; + top: 0px; + left: 0px; + height: 100%; + width: 100%; + } +} + .groupview { &.active-group { > .tabs-and-actions-container > .tabs-container > .tab { diff --git a/packages/dockview/src/dockview/dockviewComponent.ts b/packages/dockview/src/dockview/dockviewComponent.ts index 34458d1e9..f21ecb1d7 100644 --- a/packages/dockview/src/dockview/dockviewComponent.ts +++ b/packages/dockview/src/dockview/dockviewComponent.ts @@ -131,6 +131,7 @@ export class DockviewComponent private _deserializer: IPanelDeserializer | undefined; private _api: DockviewApi; private _options: Exclude; + private watermark: IWatermarkRenderer | null = null; private readonly _onDidDrop = new Emitter(); readonly onDidDrop: Event = this._onDidDrop.event; @@ -203,8 +204,16 @@ export class DockviewComponent styles: options.styles, }); + this.element.classList.add('dv-dockview'); + this.addDisposables( this._onDidDrop, + Event.any( + this.onDidAddGroup, + this.onDidRemoveGroup + )(() => { + this.updateWatermark(); + }), Event.any( this.onDidAddPanel, this.onDidRemovePanel, @@ -288,6 +297,8 @@ export class DockviewComponent ); this._api = new DockviewApi(this); + + this.updateWatermark(); } private orthogonalize(position: Position): GroupPanel { @@ -421,7 +432,7 @@ export class DockviewComponent this.clear(); if (!this.deserializer) { - throw new Error('invalid deserializer'); + throw new Error('no deserializer provided'); } const { grid, panels, options, activeGroup } = data; @@ -429,8 +440,8 @@ export class DockviewComponent this.tabHeight = options.tabHeight; } - if (!this.deserializer) { - throw new Error('no deserializer provided'); + if (grid.root.type !== 'branch' || !Array.isArray(grid.root.data)) { + throw new Error('root must be of type branch'); } this.gridview.deserialize(grid, { @@ -610,13 +621,7 @@ export class DockviewComponent panel.dispose(); - const retainGroupForWatermark = this.size === 1; - - if ( - !retainGroupForWatermark && - group.size === 0 && - options.removeEmptyGroup - ) { + if (group.size === 0 && options.removeEmptyGroup) { this.removeGroup(group); } } @@ -635,7 +640,32 @@ export class DockviewComponent ); } - addGroup(options: AddGroupOptions): GroupPanel { + private updateWatermark(): void { + if (this.groups.length === 0) { + if (!this.watermark) { + this.watermark = this.createWatermarkComponent(); + + this.watermark.init({ + containerApi: new DockviewApi(this), + params: {}, + title: '', + api: null as any, + }); + + const watermarkContainer = document.createElement('div'); + watermarkContainer.className = 'dv-watermark-container'; + watermarkContainer.appendChild(this.watermark.element); + + this.element.appendChild(watermarkContainer); + } + } else if (this.watermark) { + this.watermark.element.parentElement!.remove(); + this.watermark.dispose(); + this.watermark = null; + } + } + + addGroup(options?: AddGroupOptions): GroupPanel { const group = this.createGroup(); if (options) {