diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index 66f4f023e..8792a9bfa 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -5191,6 +5191,126 @@ describe('dockviewComponent', () => { expect(dockview.groups.length).toBe(2); expect(dockview.panels.length).toBe(3); }); + + test('persistance with custom url', async () => { + const container = document.createElement('div'); + + window.open = () => setupMockWindow(); + + const dockview = new DockviewComponent(container, { + createComponent(options) { + switch (options.name) { + case 'default': + return new PanelContentPartTest( + options.id, + options.name + ); + default: + throw new Error(`unsupported`); + } + }, + }); + + dockview.layout(1000, 500); + + dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); + + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + position: { direction: 'right' }, + }); + + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + position: { direction: 'right' }, + }); + + expect(await dockview.addPopoutGroup(panel2.group)).toBeTruthy(); + expect( + await dockview.addPopoutGroup(panel3.group, { + popoutUrl: '/custom.html', + }) + ).toBeTruthy(); + + const state = dockview.toJSON(); + + expect(state.popoutGroups).toEqual([ + { + data: { + activeView: 'panel_2', + id: '4', + views: ['panel_2'], + }, + gridReferenceGroup: '2', + position: { + height: 2001, + left: undefined, + top: undefined, + width: 1001, + }, + url: undefined, + }, + { + data: { + activeView: 'panel_3', + id: '5', + views: ['panel_3'], + }, + gridReferenceGroup: '3', + position: { + height: 2001, + left: undefined, + top: undefined, + width: 1001, + }, + url: '/custom.html', + }, + ]); + + dockview.clear(); + expect(dockview.groups.length).toBe(0); + + dockview.fromJSON(state); + await new Promise((resolve) => setTimeout(resolve, 0)); // popout views are completed as a promise so must complete microtask-queue + + expect(dockview.toJSON().popoutGroups).toEqual([ + { + data: { + activeView: 'panel_2', + id: '4', + views: ['panel_2'], + }, + gridReferenceGroup: '2', + position: { + height: 2001, + left: undefined, + top: undefined, + width: 1001, + }, + url: undefined, + }, + { + data: { + activeView: 'panel_3', + id: '5', + views: ['panel_3'], + }, + gridReferenceGroup: '3', + position: { + height: 2001, + left: undefined, + top: undefined, + width: 1001, + }, + url: '/custom.html', + }, + ]); + }); }); describe('maximized group', () => { diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index 5a20f5b84..9d749039f 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -98,6 +98,22 @@ function moveGroupWithoutDestroying(options: { }); } +export interface DockviewPopoutGroupOptions { + /** + * The position of the popout group + */ + position?: Box; + /** + * The same-origin path at which the popout window will be created + * + * Defaults to `/popout.html` if not provided + */ + popoutUrl?: string; + onDidOpen?: (event: { id: string; window: Window }) => void; + onWillClose?: (event: { id: string; window: Window }) => void; + overridePopoutGroup?: DockviewGroupPanel; +} + export interface PanelReference { update: (event: { params: { [key: string]: any } }) => void; remove: () => void; @@ -110,6 +126,7 @@ export interface SerializedFloatingGroup { export interface SerializedPopoutGroup { data: GroupPanelViewState; + url?: string; gridReferenceGroup?: string; position: Box | null; } @@ -566,13 +583,7 @@ export class DockviewComponent addPopoutGroup( itemToPopout: DockviewPanel | DockviewGroupPanel, - options?: { - position?: Box; - popoutUrl?: string; - onDidOpen?: (event: { id: string; window: Window }) => void; - onWillClose?: (event: { id: string; window: Window }) => void; - overridePopoutGroup?: DockviewGroupPanel; - } + options?: DockviewPopoutGroupOptions ): Promise { if ( itemToPopout instanceof DockviewPanel && @@ -608,7 +619,10 @@ export class DockviewComponent `${this.id}-${groupId}`, // unique id theme ?? '', { - url: options?.popoutUrl ?? '/popout.html', + url: + options?.popoutUrl ?? + this.options?.popoutUrl ?? + '/popout.html', left: window.screenX + box.left, top: window.screenY + box.top, width: box.width, @@ -709,6 +723,7 @@ export class DockviewComponent group.model.location = { type: 'popout', getWindow: () => _window.window!, + popoutUrl: options?.popoutUrl, }; if ( @@ -1198,6 +1213,10 @@ export class DockviewComponent data: group.popoutGroup.toJSON() as GroupPanelViewState, gridReferenceGroup: group.referenceGroup, position: group.window.dimensions(), + url: + group.popoutGroup.api.location.type === 'popout' + ? group.popoutGroup.api.location.popoutUrl + : undefined, }; } ); @@ -1321,7 +1340,7 @@ export class DockviewComponent const serializedPopoutGroups = data.popoutGroups ?? []; for (const serializedPopoutGroup of serializedPopoutGroups) { - const { data, position, gridReferenceGroup } = + const { data, position, gridReferenceGroup, url } = serializedPopoutGroup; const group = createGroupFromSerializedState(data); @@ -1335,6 +1354,7 @@ export class DockviewComponent overridePopoutGroup: gridReferenceGroup ? group : undefined, + popoutUrl: url, } ); } diff --git a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts index a377ed9ec..55b76585e 100644 --- a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts +++ b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts @@ -196,7 +196,7 @@ export interface IDockviewGroupPanelModel extends IPanel { export type DockviewGroupLocation = | { type: 'grid' } | { type: 'floating' } - | { type: 'popout'; getWindow: () => Window }; + | { type: 'popout'; getWindow: () => Window; popoutUrl?: string }; export class WillShowOverlayLocationEvent implements IDockviewEvent { get kind(): DockviewGroupDropLocation {