From 4fb8a3a098c192e1d83f220502c0c0be69570abe Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Fri, 16 Jun 2023 20:48:16 +0100 Subject: [PATCH] feat: retain layout size after fromJSON --- .../dockview/dockviewComponent.spec.ts | 102 +++++ .../gridview/gridviewComponent.spec.ts | 348 +++++++++++++++++- .../paneview/paneviewComponent.spec.ts | 81 ++++ .../splitview/splitviewComponent.spec.ts | 55 ++- .../src/dockview/dockviewComponent.ts | 8 +- .../dockview-core/src/gridview/gridview.ts | 12 +- .../src/gridview/gridviewComponent.ts | 6 +- .../src/paneview/paneviewComponent.ts | 6 +- .../src/splitview/splitviewComponent.ts | 6 +- 9 files changed, 598 insertions(+), 26 deletions(-) diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index ae3659753..8091ff768 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -541,6 +541,8 @@ describe('dockviewComponent', () => { }, }); + // dockview.layout(1000, 1000, true); + expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ activeGroup: 'group-1', grid: { @@ -1723,6 +1725,9 @@ describe('dockviewComponent', () => { test_tab_id: PanelTabPartTest, }, }); + + dockview.layout(1000, 1000); + dockview.fromJSON({ activeGroup: 'group-1', grid: { @@ -1918,6 +1923,8 @@ describe('dockviewComponent', () => { orientation: Orientation.HORIZONTAL, }); + dockview.layout(1000, 1000); + expect(dockview.orientation).toBe(Orientation.HORIZONTAL); dockview.fromJSON({ @@ -2023,6 +2030,8 @@ describe('dockviewComponent', () => { orientation: Orientation.HORIZONTAL, }); + dockview.layout(1000, 1000); + expect(dockview.orientation).toBe(Orientation.HORIZONTAL); dockview.fromJSON({ @@ -2163,6 +2172,8 @@ describe('dockviewComponent', () => { orientation: Orientation.HORIZONTAL, }); + dockview.layout(1000, 1000); + expect(dockview.orientation).toBe(Orientation.HORIZONTAL); dockview.fromJSON({ @@ -2517,4 +2528,95 @@ describe('dockviewComponent', () => { expect(dockview.element.querySelectorAll('.view').length).toBe(2); }); + + test('that fromJSON layouts are resized to the current dimensions', async () => { + const container = document.createElement('div'); + + const dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); + + expect(dockview.orientation).toBe(Orientation.HORIZONTAL); + + dockview.layout(1000, 500); + + dockview.fromJSON({ + activeGroup: 'group-1', + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel1', 'panel2'], + id: 'group-1', + activeView: 'panel2', + }, + size: 2000, + }, + ], + size: 1000, + }, + height: 1000, + width: 2000, + orientation: Orientation.HORIZONTAL, + }, + panels: { + panel1: { + id: 'panel1', + contentComponent: 'default', + title: 'panel1', + }, + panel2: { + id: 'panel2', + contentComponent: 'default', + title: 'panel2', + }, + }, + }); + + expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ + activeGroup: 'group-1', + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel1', 'panel2'], + id: 'group-1', + activeView: 'panel2', + }, + size: 1000, + }, + ], + size: 500, + }, + height: 500, + width: 1000, + orientation: Orientation.HORIZONTAL, + }, + panels: { + panel1: { + id: 'panel1', + contentComponent: 'default', + title: 'panel1', + }, + panel2: { + id: 'panel2', + contentComponent: 'default', + title: 'panel2', + }, + }, + }); + }); }); diff --git a/packages/dockview-core/src/__tests__/gridview/gridviewComponent.spec.ts b/packages/dockview-core/src/__tests__/gridview/gridviewComponent.spec.ts index 083526119..267600ffb 100644 --- a/packages/dockview-core/src/__tests__/gridview/gridviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/gridview/gridviewComponent.spec.ts @@ -471,6 +471,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -528,7 +530,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); - // gridview.layout(800, 400); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -552,7 +555,6 @@ describe('gridview', () => { }, activePanel: 'panel_1', }); - // gridview.layout(800, 400, true); expect(JSON.parse(JSON.stringify(gridview.toJSON()))).toEqual({ grid: { @@ -587,7 +589,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); - // gridview.layout(800, 400); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -620,7 +623,6 @@ describe('gridview', () => { }, activePanel: 'panel_1', }); - // gridview.layout(800, 400, true); expect(JSON.parse(JSON.stringify(gridview.toJSON()))).toEqual({ grid: { @@ -664,7 +666,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); - // gridview.layout(800, 400); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -706,7 +709,6 @@ describe('gridview', () => { }, activePanel: 'panel_1', }); - // gridview.layout(800, 400, true); expect(JSON.parse(JSON.stringify(gridview.toJSON()))).toEqual({ grid: { @@ -759,7 +761,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); - // gridview.layout(800, 400); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -801,7 +804,6 @@ describe('gridview', () => { }, activePanel: 'panel_1', }); - // gridview.layout(800, 400, true); expect(JSON.parse(JSON.stringify(gridview.toJSON()))).toEqual({ grid: { @@ -854,6 +856,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -895,7 +899,6 @@ describe('gridview', () => { }, activePanel: 'panel_1', }); - gridview.layout(800, 400, true); expect(JSON.parse(JSON.stringify(gridview.toJSON()))).toEqual({ grid: { @@ -948,7 +951,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); - // gridview.layout(800, 400); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -1005,7 +1009,6 @@ describe('gridview', () => { }, activePanel: 'panel_1', }); - // gridview.layout(800, 400, true); expect(JSON.parse(JSON.stringify(gridview.toJSON()))).toEqual({ grid: { @@ -1198,6 +1201,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -1254,7 +1259,8 @@ describe('gridview', () => { }, activePanel: 'panel_1', }); - gridview.layout(800, 400, true); + + // gridview.layout(800, 400, true); expect(JSON.parse(JSON.stringify(gridview.toJSON()))).toEqual({ grid: { @@ -1322,6 +1328,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -1445,6 +1453,8 @@ describe('gridview', () => { components: { default: TestGridview }, }); + gridview.layout(800, 400); + gridview.fromJSON({ grid: { height: 400, @@ -1908,4 +1918,318 @@ describe('gridview', () => { return disposable.dispose(); }); + + test('that fromJSON layouts are resized to the current dimensions', async () => { + const container = document.createElement('div'); + + const gridview = new GridviewComponent({ + parentElement: container, + proportionalLayout: true, + orientation: Orientation.VERTICAL, + components: { default: TestGridview }, + }); + + gridview.layout(1600, 800); + + gridview.fromJSON({ + grid: { + height: 400, + width: 800, + orientation: Orientation.HORIZONTAL, + root: { + type: 'branch', + size: 400, + data: [ + { + type: 'leaf', + size: 200, + data: { + id: 'panel_1', + component: 'default', + snap: false, + }, + }, + { + type: 'branch', + size: 400, + data: [ + { + type: 'leaf', + size: 250, + data: { + id: 'panel_2', + component: 'default', + snap: false, + }, + }, + { + type: 'leaf', + size: 150, + data: { + id: 'panel_3', + component: 'default', + snap: false, + }, + }, + ], + }, + { + type: 'leaf', + size: 200, + data: { + id: 'panel_4', + component: 'default', + snap: false, + }, + }, + ], + }, + }, + activePanel: 'panel_1', + }); + + expect(JSON.parse(JSON.stringify(gridview.toJSON()))).toEqual({ + grid: { + height: 800, + width: 1600, + orientation: Orientation.HORIZONTAL, + root: { + type: 'branch', + size: 800, + data: [ + { + type: 'leaf', + size: 400, + data: { + id: 'panel_1', + component: 'default', + snap: false, + }, + }, + { + type: 'branch', + size: 800, + data: [ + { + type: 'leaf', + size: 500, + data: { + id: 'panel_2', + component: 'default', + snap: false, + }, + }, + { + type: 'leaf', + size: 300, + data: { + id: 'panel_3', + component: 'default', + snap: false, + }, + }, + ], + }, + { + type: 'leaf', + size: 400, + data: { + id: 'panel_4', + component: 'default', + snap: false, + }, + }, + ], + }, + }, + activePanel: 'panel_1', + }); + }); + + test('that a deep layout with fromJSON dimensions identical to the current dimensions loads', async () => { + const container = document.createElement('div'); + + const gridview = new GridviewComponent({ + parentElement: container, + proportionalLayout: true, + orientation: Orientation.VERTICAL, + components: { default: TestGridview }, + }); + + gridview.layout(5000, 5000); + + gridview.fromJSON({ + grid: { + height: 5000, + width: 5000, + orientation: Orientation.HORIZONTAL, + root: { + type: 'branch', + size: 5000, + data: [ + { + type: 'leaf', + size: 1000, + data: { + id: 'panel_1', + component: 'default', + snap: false, + }, + }, + { + type: 'branch', + size: 2000, + data: [ + { + type: 'branch', + size: 4000, + data: [ + { + type: 'leaf', + size: 1000, + data: { + id: 'panel_2', + component: 'default', + snap: false, + }, + }, + { + type: 'branch', + size: 1000, + data: [ + { + type: 'leaf', + size: 2000, + data: { + id: 'panel_3', + component: 'default', + snap: false, + }, + }, + { + type: 'leaf', + size: 2000, + data: { + id: 'panel_4', + component: 'default', + snap: false, + }, + }, + ], + }, + ], + }, + { + type: 'leaf', + size: 1000, + data: { + id: 'panel_5', + component: 'default', + snap: false, + }, + }, + ], + }, + { + type: 'leaf', + size: 2000, + data: { + id: 'panel_6', + component: 'default', + snap: false, + }, + }, + ], + }, + }, + activePanel: 'panel_1', + }); + + expect(JSON.parse(JSON.stringify(gridview.toJSON()))).toEqual({ + grid: { + height: 5000, + width: 5000, + orientation: Orientation.HORIZONTAL, + root: { + type: 'branch', + size: 5000, + data: [ + { + type: 'leaf', + size: 1000, + data: { + id: 'panel_1', + component: 'default', + snap: false, + }, + }, + { + type: 'branch', + size: 2000, + data: [ + { + type: 'branch', + size: 4000, + data: [ + { + type: 'leaf', + size: 1000, + data: { + id: 'panel_2', + component: 'default', + snap: false, + }, + }, + { + type: 'branch', + size: 1000, + data: [ + { + type: 'leaf', + size: 2000, + data: { + id: 'panel_3', + component: 'default', + snap: false, + }, + }, + { + type: 'leaf', + size: 2000, + data: { + id: 'panel_4', + component: 'default', + snap: false, + }, + }, + ], + }, + ], + }, + { + type: 'leaf', + size: 1000, + data: { + id: 'panel_5', + component: 'default', + snap: false, + }, + }, + ], + }, + { + type: 'leaf', + size: 2000, + data: { + id: 'panel_6', + component: 'default', + snap: false, + }, + }, + ], + }, + }, + activePanel: 'panel_1', + }); + }); }); diff --git a/packages/dockview-core/src/__tests__/paneview/paneviewComponent.spec.ts b/packages/dockview-core/src/__tests__/paneview/paneviewComponent.spec.ts index cb5763046..9ffd22e49 100644 --- a/packages/dockview-core/src/__tests__/paneview/paneviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/paneview/paneviewComponent.spec.ts @@ -408,4 +408,85 @@ describe('componentPaneview', () => { expect(panel1Spy).toHaveBeenCalledTimes(1); expect(panel2Spy).toHaveBeenCalledTimes(1); }); + + test('that fromJSON layouts are resized to the current dimensions', async () => { + const paneview = new PaneviewComponent({ + parentElement: container, + components: { + testPanel: TestPanel, + }, + }); + + paneview.layout(400, 600); + + paneview.fromJSON({ + size: 6, + views: [ + { + size: 1, + data: { + id: 'panel1', + component: 'testPanel', + title: 'Panel 1', + }, + expanded: true, + }, + { + size: 2, + data: { + id: 'panel2', + component: 'testPanel', + title: 'Panel 2', + }, + expanded: true, + }, + { + size: 3, + data: { + id: 'panel3', + component: 'testPanel', + title: 'Panel 3', + }, + expanded: true, + }, + ], + }); + + // heights slightly differ because header height isn't accounted for + expect(JSON.parse(JSON.stringify(paneview.toJSON()))).toEqual({ + size: 600, + views: [ + { + size: 122, + data: { + id: 'panel1', + component: 'testPanel', + title: 'Panel 1', + }, + expanded: true, + minimumSize: 100, + }, + { + size: 122, + data: { + id: 'panel2', + component: 'testPanel', + title: 'Panel 2', + }, + expanded: true, + minimumSize: 100, + }, + { + size: 356, + data: { + id: 'panel3', + component: 'testPanel', + title: 'Panel 3', + }, + expanded: true, + minimumSize: 100, + }, + ], + }); + }); }); diff --git a/packages/dockview-core/src/__tests__/splitview/splitviewComponent.spec.ts b/packages/dockview-core/src/__tests__/splitview/splitviewComponent.spec.ts index e43c94239..81525a26c 100644 --- a/packages/dockview-core/src/__tests__/splitview/splitviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/splitview/splitviewComponent.spec.ts @@ -330,7 +330,7 @@ describe('componentSplitview', () => { testPanel: TestPanel, }, }); - splitview.layout(600, 400); + splitview.layout(400, 6); splitview.fromJSON({ views: [ @@ -535,4 +535,57 @@ describe('componentSplitview', () => { expect(panel1Spy).toHaveBeenCalledTimes(1); expect(panel2Spy).toHaveBeenCalledTimes(1); }); + + test('that fromJSON layouts are resized to the current dimensions', async () => { + const splitview = new SplitviewComponent({ + parentElement: container, + orientation: Orientation.VERTICAL, + components: { + testPanel: TestPanel, + }, + }); + splitview.layout(400, 600); + + splitview.fromJSON({ + views: [ + { + size: 1, + data: { id: 'panel1', component: 'testPanel' }, + snap: false, + }, + { + size: 2, + data: { id: 'panel2', component: 'testPanel' }, + snap: true, + }, + { size: 3, data: { id: 'panel3', component: 'testPanel' } }, + ], + size: 6, + orientation: Orientation.VERTICAL, + activeView: 'panel1', + }); + + expect(JSON.parse(JSON.stringify(splitview.toJSON()))).toEqual({ + views: [ + { + size: 100, + data: { id: 'panel1', component: 'testPanel' }, + snap: false, + }, + { + size: 200, + data: { id: 'panel2', component: 'testPanel' }, + snap: true, + }, + { + size: 300, + data: { id: 'panel3', component: 'testPanel' }, + snap: false, + }, + ], + size: 600, + orientation: Orientation.VERTICAL, + activeView: 'panel1', + }); + }); }); diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index c180ad022..f62295de8 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -412,6 +412,10 @@ export class DockviewComponent throw new Error('root must be of type branch'); } + // take note of the existing dimensions + const width = this.width; + const height = this.height; + this.gridview.deserialize(grid, { fromJSON: (node: ISerializedLeafNode) => { const { id, locked, hideHeader, views, activeView } = node.data; @@ -453,6 +457,8 @@ export class DockviewComponent }, }); + this.layout(width, height); + if (typeof activeGroup === 'string') { const panel = this.getPanel(activeGroup); if (panel) { @@ -460,8 +466,6 @@ export class DockviewComponent } } - this.gridview.layout(this.width, this.height); - this._onDidLayoutFromJSON.fire(); } diff --git a/packages/dockview-core/src/gridview/gridview.ts b/packages/dockview-core/src/gridview/gridview.ts index 62c2ca9fb..cd49a5624 100644 --- a/packages/dockview-core/src/gridview/gridview.ts +++ b/packages/dockview-core/src/gridview/gridview.ts @@ -371,8 +371,7 @@ export class Gridview implements IDisposable { root, orientation, deserializer, - orthogonalSize, - true + orthogonalSize ) as BranchNode; } @@ -380,8 +379,7 @@ export class Gridview implements IDisposable { node: ISerializedNode, orientation: Orientation, deserializer: IViewDeserializer, - orthogonalSize: number, - isRoot = false + orthogonalSize: number ): Node { let result: Node; if (node.type === 'branch') { @@ -398,14 +396,12 @@ export class Gridview implements IDisposable { } as INodeDescriptor; }); - // HORIZONTAL => height=orthogonalsize width=size - // VERTICAL => height=size width=orthogonalsize result = new BranchNode( orientation, this.proportionalLayout, this.styles, - isRoot ? orthogonalSize : node.size, - isRoot ? node.size : orthogonalSize, + orthogonalSize, // <- size - flips at each depth + node.size, // <- orthogonal size - flips at each depth children ); } else { diff --git a/packages/dockview-core/src/gridview/gridviewComponent.ts b/packages/dockview-core/src/gridview/gridviewComponent.ts index b216cd8a9..40dea53aa 100644 --- a/packages/dockview-core/src/gridview/gridviewComponent.ts +++ b/packages/dockview-core/src/gridview/gridviewComponent.ts @@ -176,6 +176,10 @@ export class GridviewComponent const queue: Function[] = []; + // take note of the existing dimensions + const width = this.width; + const height = this.height; + this.gridview.deserialize(grid, { fromJSON: (node) => { const { data } = node; @@ -215,7 +219,7 @@ export class GridviewComponent }, }); - this.layout(this.width, this.height, true); + this.layout(width, height); queue.forEach((f) => f()); diff --git a/packages/dockview-core/src/paneview/paneviewComponent.ts b/packages/dockview-core/src/paneview/paneviewComponent.ts index 290316eb0..57c3b9beb 100644 --- a/packages/dockview-core/src/paneview/paneviewComponent.ts +++ b/packages/dockview-core/src/paneview/paneviewComponent.ts @@ -360,6 +360,10 @@ export class PaneviewComponent extends Resizable implements IPaneviewComponent { const queue: Function[] = []; + // take note of the existing dimensions + const width = this.width; + const height = this.height; + this.paneview = new Paneview(this.element, { orientation: Orientation.VERTICAL, descriptor: { @@ -437,7 +441,7 @@ export class PaneviewComponent extends Resizable implements IPaneviewComponent { }, }); - this.layout(this.width, this.height); + this.layout(width, height); queue.forEach((f) => f()); diff --git a/packages/dockview-core/src/splitview/splitviewComponent.ts b/packages/dockview-core/src/splitview/splitviewComponent.ts index 37573c40e..973a81d24 100644 --- a/packages/dockview-core/src/splitview/splitviewComponent.ts +++ b/packages/dockview-core/src/splitview/splitviewComponent.ts @@ -337,6 +337,10 @@ export class SplitviewComponent const queue: Function[] = []; + // take note of the existing dimensions + const width = this.width; + const height = this.height; + this.splitview = new Splitview(this.element, { orientation, proportionalLayout: this.options.proportionalLayout, @@ -387,7 +391,7 @@ export class SplitviewComponent }, }); - this.layout(this.width, this.height); + this.layout(width, height); queue.forEach((f) => f());