diff --git a/packages/dockview-demo/src/layout-grid/layoutGrid.tsx b/packages/dockview-demo/src/layout-grid/layoutGrid.tsx index 2cac597e0..5c8bc9a0c 100644 --- a/packages/dockview-demo/src/layout-grid/layoutGrid.tsx +++ b/packages/dockview-demo/src/layout-grid/layoutGrid.tsx @@ -25,6 +25,14 @@ import { selectedPanelAtom } from './footer'; import { ExampleFunctions } from './panels/exampleFunctions'; import { CompositeDisposable } from '../lifecycle'; +const WatermarkComponent = () => { + return ( +
+ Watermark component +
+ ); +}; + const Test = (props: IDockviewPanelProps) => { const [counter, setCounter] = React.useState(0); @@ -109,6 +117,7 @@ const components: PanelCollection = { @@ -140,7 +149,9 @@ export const TestGrid = (props: IGridviewPanelProps) => { }; const setSelectedPanel = useRecoilCallback( - ({ set }) => (value: string) => set(selectedPanelAtom, value), + ({ set }) => + (value: string) => + set(selectedPanelAtom, value), [] ); @@ -192,11 +203,12 @@ export const TestGrid = (props: IGridviewPanelProps) => { }; }, [api]); - const [coord, setCoord] = React.useState<{ - x: number; - y: number; - panel: IGroupPanel; - }>(undefined); + const [coord, setCoord] = + React.useState<{ + x: number; + y: number; + panel: IGroupPanel; + }>(undefined); const onTabContextMenu = React.useMemo( () => (event: TabContextMenuEvent) => { @@ -276,7 +288,6 @@ export const TestGrid = (props: IGridviewPanelProps) => { const Watermark = (props: IWatermarkPanelProps) => { const [groups, setGroups] = React.useState(props.containerApi.size); React.useEffect(() => { - console.log('mount'); const disposable = new CompositeDisposable( props.containerApi.onDidLayoutChange(() => { console.log(`groups2 ${props.containerApi.size}`); @@ -285,7 +296,6 @@ const Watermark = (props: IWatermarkPanelProps) => { ); return () => { - console.log('unmount'); disposable.dispose(); }; }, []); @@ -332,30 +342,7 @@ const Watermark = (props: IWatermarkPanelProps) => { justifyContent: 'center', }} > - {/* - - - */} + Watermark component ); diff --git a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts index 30a559fe1..665a38b7d 100644 --- a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts @@ -465,7 +465,7 @@ describe('dockviewComponent', () => { await panel2.api.close(); - expect(dockview.size).toBe(0); + expect(dockview.size).toBe(1); // watermark expect(dockview.totalPanels).toBe(0); }); @@ -1178,6 +1178,89 @@ describe('dockviewComponent', () => { expect(panel1Spy).toBeCalledTimes(1); }); + test('can add panel of same id if already 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', + }); + + expect(dockview.totalPanels).toBe(1); + + panel1.api.close(); + + expect(dockview.totalPanels).toBe(0); + + const panel1Again = dockview.addPanel({ + id: 'panel1', + component: 'default', + tabComponent: 'default', + }); + + expect(dockview.totalPanels).toBe(1); + + panel1Again.api.close(); + + 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'); diff --git a/packages/dockview/src/dockview/dockviewComponent.ts b/packages/dockview/src/dockview/dockviewComponent.ts index b8fe1286e..dd7b763c5 100644 --- a/packages/dockview/src/dockview/dockviewComponent.ts +++ b/packages/dockview/src/dockview/dockviewComponent.ts @@ -470,7 +470,13 @@ export class DockviewComponent panel.dispose(); - if (group.model.size === 0 && options.removeEmptyGroup) { + const retainGroupForWatermark = this.size === 1; + + if ( + !retainGroupForWatermark && + group.model.size === 0 && + options.removeEmptyGroup + ) { this.removeGroup(group); } } diff --git a/packages/dockview/src/groupview/groupview.ts b/packages/dockview/src/groupview/groupview.ts index 81341ee38..e78531e2c 100644 --- a/packages/dockview/src/groupview/groupview.ts +++ b/packages/dockview/src/groupview/groupview.ts @@ -150,38 +150,44 @@ export class Groupview extends CompositeDisposable implements IGroupview { this.layout(this._width, this._height); } - get isActive() { + get isActive(): boolean { return this._isGroupActive; } - get panels() { + get panels(): IGroupPanel[] { return this._panels; } - get size() { + get size(): number { return this._panels.length; } - get isEmpty() { + get isEmpty(): boolean { return this._panels.length === 0; } - get minimumHeight() { + get minimumHeight(): number { return 100; } - get maximumHeight() { + get maximumHeight(): number { return Number.MAX_SAFE_INTEGER; } - get minimumWidth() { + get minimumWidth(): number { return 100; } - get maximumWidth() { + get maximumWidth(): number { return Number.MAX_SAFE_INTEGER; } + get hasWatermark(): boolean { + return !!( + this.watermark && this.container.contains(this.watermark.element) + ); + } + constructor( private readonly container: HTMLElement, private accessor: IDockviewComponent,