feat: persistance and rendering

This commit is contained in:
mathuo 2023-02-28 22:43:21 +08:00
parent 37b0a062a4
commit f26a1cd404
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
17 changed files with 280 additions and 164 deletions

View File

@ -1,8 +1,9 @@
import { DefaultGroupPanelView } from '../../dockview/defaultGroupPanelView'; import { DefaultGroupPanelView } from '../../dockview/defaultGroupPanelView';
import { GroupPanel } from '../../groupview/groupviewPanel';
import { IContentRenderer, ITabRenderer } from '../../groupview/types'; import { IContentRenderer, ITabRenderer } from '../../groupview/types';
describe('defaultGroupPanelView', () => { describe('defaultGroupPanelView', () => {
test('dispose cleanup', () => { test('that dispose is called on content and tab renderers when present', () => {
const contentMock = jest.fn<IContentRenderer, []>(() => { const contentMock = jest.fn<IContentRenderer, []>(() => {
const partial: Partial<IContentRenderer> = { const partial: Partial<IContentRenderer> = {
element: document.createElement('div'), element: document.createElement('div'),
@ -22,11 +23,109 @@ describe('defaultGroupPanelView', () => {
const content = new contentMock(); const content = new contentMock();
const tab = new tabMock(); const tab = new tabMock();
const cut = new DefaultGroupPanelView({ content, tab }); const cut = new DefaultGroupPanelView({
content,
tab,
contentComponent: 'contentComponent',
});
cut.dispose(); cut.dispose();
expect(content.dispose).toHaveBeenCalled(); expect(content.dispose).toHaveBeenCalled();
expect(tab.dispose).toHaveBeenCalled(); expect(tab.dispose).toHaveBeenCalled();
}); });
test('that update is called on content and tab renderers when present', () => {
const contentMock = jest.fn<IContentRenderer, []>(() => {
const partial: Partial<IContentRenderer> = {
element: document.createElement('div'),
update: jest.fn(),
};
return partial as IContentRenderer;
});
const tabMock = jest.fn<ITabRenderer, []>(() => {
const partial: Partial<IContentRenderer> = {
element: document.createElement('div'),
update: jest.fn(),
};
return partial as IContentRenderer;
});
const content = new contentMock();
const tab = new tabMock();
const cut = new DefaultGroupPanelView({
content,
tab,
contentComponent: 'contentComponent',
});
cut.update({
params: {},
});
expect(content.update).toHaveBeenCalled();
expect(tab.update).toHaveBeenCalled();
});
test('test1', () => {
const contentMock = jest.fn<IContentRenderer, []>(() => {
const partial: Partial<IContentRenderer> = {
element: document.createElement('div'),
onGroupChange: jest.fn(),
onPanelVisibleChange: jest.fn(),
};
return partial as IContentRenderer;
});
const tabMock = jest.fn<ITabRenderer, []>(() => {
const partial: Partial<IContentRenderer> = {
element: document.createElement('div'),
onGroupChange: jest.fn(),
onPanelVisibleChange: jest.fn(),
};
return partial as IContentRenderer;
});
const content = new contentMock();
const tab = new tabMock();
let cut = new DefaultGroupPanelView({
content,
tab,
contentComponent: 'contentComponent',
});
const group1 = jest.fn() as any;
const group2 = jest.fn() as any;
cut.updateParentGroup(group1 as GroupPanel, false);
expect(content.onGroupChange).toHaveBeenNthCalledWith(1, group1);
expect(tab.onGroupChange).toHaveBeenNthCalledWith(1, group1);
expect(content.onPanelVisibleChange).toHaveBeenNthCalledWith(1, false);
expect(tab.onPanelVisibleChange).toHaveBeenNthCalledWith(1, false);
expect(content.onGroupChange).toHaveBeenCalledTimes(1);
expect(tab.onGroupChange).toHaveBeenCalledTimes(1);
expect(content.onPanelVisibleChange).toHaveBeenCalledTimes(1);
expect(tab.onPanelVisibleChange).toHaveBeenCalledTimes(1);
cut.updateParentGroup(group1 as GroupPanel, true);
expect(content.onPanelVisibleChange).toHaveBeenNthCalledWith(2, true);
expect(tab.onPanelVisibleChange).toHaveBeenNthCalledWith(2, true);
expect(content.onGroupChange).toHaveBeenCalledTimes(1);
expect(tab.onGroupChange).toHaveBeenCalledTimes(1);
expect(content.onPanelVisibleChange).toHaveBeenCalledTimes(2);
expect(tab.onPanelVisibleChange).toHaveBeenCalledTimes(2);
cut.updateParentGroup(group2 as GroupPanel, true);
expect(content.onGroupChange).toHaveBeenNthCalledWith(2, group2);
expect(tab.onGroupChange).toHaveBeenNthCalledWith(2, group2);
expect(content.onGroupChange).toHaveBeenCalledTimes(2);
expect(tab.onGroupChange).toHaveBeenCalledTimes(2);
expect(content.onPanelVisibleChange).toHaveBeenCalledTimes(2);
expect(tab.onPanelVisibleChange).toHaveBeenCalledTimes(2);
});
}); });

View File

@ -8,8 +8,6 @@ import { PanelUpdateEvent } from '../../../panel/types';
import { GroupPanel } from '../../../groupview/groupviewPanel'; import { GroupPanel } from '../../../groupview/groupviewPanel';
import { createCloseButton } from '../../../svg'; import { createCloseButton } from '../../../svg';
export const DEFAULT_TAB_IDENTIFIER = '__default__tab__';
export class DefaultTab extends CompositeDisposable implements ITabRenderer { export class DefaultTab extends CompositeDisposable implements ITabRenderer {
private _element: HTMLElement; private _element: HTMLElement;
@ -26,10 +24,6 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer {
return this._element; return this._element;
} }
get id() {
return DEFAULT_TAB_IDENTIFIER;
}
constructor() { constructor() {
super(); super();
@ -69,18 +63,13 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer {
this.render(); this.render();
} }
public toJSON() {
return { id: this.id };
}
focus() { focus() {
//noop //noop
} }
public init(params: GroupPanelPartInitParameters) { public init(params: GroupPanelPartInitParameters) {
this.params = params; this.params = params;
this._content.textContent = this._content.textContent = params.title;
typeof params.title === 'string' ? params.title : this.id;
addDisposableListener(this.action, 'click', (ev) => { addDisposableListener(this.action, 'click', (ev) => {
ev.preventDefault(); // ev.preventDefault(); //
@ -107,10 +96,7 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer {
private render() { private render() {
if (this._content.textContent !== this.params.title) { if (this._content.textContent !== this.params.title) {
this._content.textContent = this._content.textContent = this.params.title;
typeof this.params.title === 'string'
? this.params.title
: this.id;
} }
} }
} }

View File

@ -7,21 +7,32 @@ import {
} from '../groupview/types'; } from '../groupview/types';
import { GroupPanel } from '../groupview/groupviewPanel'; import { GroupPanel } from '../groupview/groupviewPanel';
import { IDisposable } from '../lifecycle'; import { IDisposable } from '../lifecycle';
import { createComponent } from '../panel/componentFactory';
import { IDockviewComponent } from './dockviewComponent';
export interface SerializedGroupPanelView {
tab?: { id: string };
content: { id: string };
}
export interface IGroupPanelView extends IDisposable { export interface IGroupPanelView extends IDisposable {
readonly contentComponent: string;
readonly tabComponent?: string;
readonly content: IContentRenderer; readonly content: IContentRenderer;
readonly tab?: ITabRenderer; readonly tab?: ITabRenderer;
update(event: GroupPanelUpdateEvent): void; update(event: GroupPanelUpdateEvent): void;
layout(width: number, height: number): void; layout(width: number, height: number): void;
init(params: GroupPanelPartInitParameters): void; init(params: GroupPanelPartInitParameters): void;
updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void; updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void;
toJSON(): {};
} }
export class DefaultGroupPanelView implements IGroupPanelView { export class DefaultGroupPanelView implements IGroupPanelView {
private readonly _content: IContentRenderer; private readonly _content: IContentRenderer;
private readonly _tab: ITabRenderer; private readonly _tab: ITabRenderer;
private _group: GroupPanel | null = null;
private _isPanelVisible: boolean | null = null;
get content(): IContentRenderer { get content(): IContentRenderer {
return this._content; return this._content;
} }
@ -30,9 +41,15 @@ export class DefaultGroupPanelView implements IGroupPanelView {
return this._tab; return this._tab;
} }
constructor(renderers: { content: IContentRenderer; tab?: ITabRenderer }) { constructor(
this._content = renderers.content; private readonly accessor: IDockviewComponent,
this._tab = renderers.tab ?? new DefaultTab(); private readonly id: string,
readonly contentComponent: string,
readonly tabComponent?: string
) {
this._content = this.createContentComponent(this.id, contentComponent);
this._tab =
this.createTabComponent(this.id, tabComponent) ?? new DefaultTab();
} }
init(params: GroupPanelPartInitParameters): void { init(params: GroupPanelPartInitParameters): void {
@ -41,35 +58,65 @@ export class DefaultGroupPanelView implements IGroupPanelView {
} }
updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void { updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void {
this._content.updateParentGroup(group, isPanelVisible); if (group !== this._group) {
this._tab?.updateParentGroup(group, isPanelVisible); this._group = group;
if (this._content.onGroupChange) {
this._content.onGroupChange(group);
}
if (this._tab.onGroupChange) {
this._tab.onGroupChange(group);
}
}
if (isPanelVisible !== this._isPanelVisible) {
this._isPanelVisible = isPanelVisible;
if (this._content.onPanelVisibleChange) {
this._content.onPanelVisibleChange(isPanelVisible);
}
if (this._tab.onPanelVisibleChange) {
this._tab.onPanelVisibleChange(isPanelVisible);
}
}
} }
layout(width: number, height: number): void { layout(width: number, height: number): void {
this.content.layout(width, height); this.content.layout?.(width, height);
} }
update(event: GroupPanelUpdateEvent): void { update(event: GroupPanelUpdateEvent): void {
this.content.update(event); this.content.update?.(event);
this.tab.update(event); this.tab.update?.(event);
}
toJSON(): {} {
let tab =
this.tab instanceof DefaultTab ? undefined : this.tab.toJSON();
if (tab && Object.keys(tab).length === 0) {
tab = undefined;
}
return {
content: this.content.toJSON(),
tab,
};
} }
dispose(): void { dispose(): void {
this.content.dispose(); this.content.dispose?.();
this.tab.dispose(); this.tab.dispose?.();
}
private createContentComponent(
id: string,
componentName: string
): IContentRenderer {
return createComponent(
id,
componentName,
this.accessor.options.components || {},
this.accessor.options.frameworkComponents,
this.accessor.options.frameworkComponentFactory?.content
);
}
private createTabComponent(
id: string,
componentName?: string
): ITabRenderer {
return createComponent(
id,
componentName,
this.accessor.options.tabComponents || {},
this.accessor.options.frameworkTabComponents,
this.accessor.options.frameworkComponentFactory?.tab,
() => new DefaultTab()
);
} }
} }

View File

@ -1,7 +1,7 @@
import { GroupviewPanelState, ITabRenderer } from '../groupview/types'; import { GroupviewPanelState, ITabRenderer } from '../groupview/types';
import { GroupPanel } from '../groupview/groupviewPanel'; import { GroupPanel } from '../groupview/groupviewPanel';
import { DockviewPanel, IDockviewPanel } from './dockviewPanel'; import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
import { DockviewComponent } from './dockviewComponent'; import { IDockviewComponent } from './dockviewComponent';
import { createComponent } from '../panel/componentFactory'; import { createComponent } from '../panel/componentFactory';
import { DefaultTab } from './components/tab/defaultTab'; import { DefaultTab } from './components/tab/defaultTab';
import { DefaultGroupPanelView } from './defaultGroupPanelView'; import { DefaultGroupPanelView } from './defaultGroupPanelView';
@ -12,7 +12,7 @@ export interface IPanelDeserializer {
} }
export class DefaultDockviewDeserialzier implements IPanelDeserializer { export class DefaultDockviewDeserialzier implements IPanelDeserializer {
constructor(private readonly layout: DockviewComponent) {} constructor(private readonly layout: IDockviewComponent) {}
public fromJSON( public fromJSON(
panelData: GroupviewPanelState, panelData: GroupviewPanelState,
@ -21,14 +21,21 @@ export class DefaultDockviewDeserialzier implements IPanelDeserializer {
const panelId = panelData.id; const panelId = panelData.id;
const params = panelData.params; const params = panelData.params;
const title = panelData.title; const title = panelData.title;
const viewData = panelData.view; const viewData = panelData.view!;
let tab: ITabRenderer; let tab: ITabRenderer;
if (viewData.tab?.id) { const contentComponent = viewData
? viewData.content.id
: panelData.contentComponent || 'unknown';
const tabComponent = viewData
? viewData.tab?.id
: panelData.tabComponent;
if (tabComponent) {
tab = createComponent( tab = createComponent(
viewData.tab.id, panelId,
viewData.tab.id, tabComponent,
this.layout.options.tabComponents, this.layout.options.tabComponents,
this.layout.options.frameworkTabComponents, this.layout.options.frameworkTabComponents,
this.layout.options.frameworkComponentFactory?.tab, this.layout.options.frameworkComponentFactory?.tab,
@ -36,7 +43,7 @@ export class DefaultDockviewDeserialzier implements IPanelDeserializer {
); );
} else if (this.layout.options.defaultTabComponent) { } else if (this.layout.options.defaultTabComponent) {
tab = createComponent( tab = createComponent(
this.layout.options.defaultTabComponent, panelId,
this.layout.options.defaultTabComponent, this.layout.options.defaultTabComponent,
this.layout.options.tabComponents, this.layout.options.tabComponents,
this.layout.options.frameworkTabComponents, this.layout.options.frameworkTabComponents,
@ -47,16 +54,12 @@ export class DefaultDockviewDeserialzier implements IPanelDeserializer {
tab = new DefaultTab(); tab = new DefaultTab();
} }
const view = new DefaultGroupPanelView({ const view = new DefaultGroupPanelView(
content: createComponent( this.layout,
viewData.content.id, panelId,
viewData.content.id, contentComponent,
this.layout.options.components, tabComponent
this.layout.options.frameworkComponents, );
this.layout.options.frameworkComponentFactory?.content
),
tab,
});
const panel = new DockviewPanel( const panel = new DockviewPanel(
panelId, panelId,
@ -67,7 +70,7 @@ export class DefaultDockviewDeserialzier implements IPanelDeserializer {
panel.init({ panel.init({
view, view,
title, title: title || panelId,
params: params || {}, params: params || {},
}); });

View File

@ -950,13 +950,16 @@ export class DockviewComponent
options: AddPanelOptions, options: AddPanelOptions,
group: GroupPanel group: GroupPanel
): IDockviewPanel { ): IDockviewPanel {
const view = new DefaultGroupPanelView({ const contentComponent = options.component;
content: this.createContentComponent(options.id, options.component), const tabComponent =
tab: this.createTabComponent( options.tabComponent || this.options.defaultTabComponent;
options.id,
options.tabComponent || this.options.defaultTabComponent const view = new DefaultGroupPanelView(
), this,
}); options.id,
contentComponent,
tabComponent
);
const panel = new DockviewPanel(options.id, this, this._api, group); const panel = new DockviewPanel(options.id, this, this._api, group);
panel.init({ panel.init({
@ -968,33 +971,6 @@ export class DockviewComponent
return panel; return panel;
} }
private createContentComponent(
id: string,
componentName: string
): IContentRenderer {
return createComponent(
id,
componentName,
this.options.components || {},
this.options.frameworkComponents,
this.options.frameworkComponentFactory?.content
);
}
private createTabComponent(
id: string,
componentName?: string
): ITabRenderer {
return createComponent(
id,
componentName,
this.options.tabComponents || {},
this.options.frameworkTabComponents,
this.options.frameworkComponentFactory?.tab,
() => new DefaultTab()
);
}
private createGroupAtLocation(location: number[] = [0]): GroupPanel { private createGroupAtLocation(location: number[] = [0]): GroupPanel {
const group = this.createGroup(); const group = this.createGroup();
this.doAddGroup(group, location); this.doAddGroup(group, location);

View File

@ -12,7 +12,7 @@ import { GroupPanel } from '../groupview/groupviewPanel';
import { CompositeDisposable, IDisposable } from '../lifecycle'; import { CompositeDisposable, IDisposable } from '../lifecycle';
import { IPanel, Parameters } from '../panel/types'; import { IPanel, Parameters } from '../panel/types';
import { IGroupPanelView } from './defaultGroupPanelView'; import { IGroupPanelView } from './defaultGroupPanelView';
import { DockviewComponent } from './dockviewComponent'; import { IDockviewComponent } from './dockviewComponent';
export interface IDockviewPanel extends IDisposable, IPanel { export interface IDockviewPanel extends IDisposable, IPanel {
readonly view?: IGroupPanelView; readonly view?: IGroupPanelView;
@ -56,7 +56,7 @@ export class DockviewPanel
constructor( constructor(
public readonly id: string, public readonly id: string,
accessor: DockviewComponent, accessor: IDockviewComponent,
private readonly containerApi: DockviewApi, private readonly containerApi: DockviewApi,
group: GroupPanel group: GroupPanel
) { ) {
@ -82,9 +82,7 @@ export class DockviewPanel
this._params = params.params; this._params = params.params;
this._view = params.view; this._view = params.view;
if (typeof params.title === 'string') { this.setTitle(params.title);
this.setTitle(params.title);
}
this.view?.init({ this.view?.init({
...params, ...params,
@ -100,7 +98,8 @@ export class DockviewPanel
public toJSON(): GroupviewPanelState { public toJSON(): GroupviewPanelState {
return <GroupviewPanelState>{ return <GroupviewPanelState>{
id: this.id, id: this.id,
view: this.view!.toJSON(), contentComponent: this.view?.contentComponent,
tabComponent: this.view?.tabComponent,
params: params:
Object.keys(this._params || {}).length > 0 Object.keys(this._params || {}).length > 0
? this._params ? this._params
@ -133,11 +132,9 @@ export class DockviewPanel
...event.params.params, ...event.params.params,
}; };
if (typeof params.title === 'string') { if (params.title !== this.title) {
if (params.title !== this.title) { this._title = params.title;
this._title = params.title; this.api._onDidTitleChange.fire({ title: this.title });
this.api._onDidTitleChange.fire({ title: this.title });
}
} }
this.view?.update({ this.view?.update({

View File

@ -235,14 +235,14 @@ export abstract class BaseGrid<T extends IGridPanelView>
if (this._activeGroup) { if (this._activeGroup) {
this._activeGroup.setActive(false); this._activeGroup.setActive(false);
if (!skipFocus) { if (!skipFocus) {
this._activeGroup.focus(); this._activeGroup.focus?.();
} }
} }
if (group) { if (group) {
group.setActive(true); group.setActive(true);
if (!skipFocus) { if (!skipFocus) {
group.focus(); group.focus?.();
} }
} }

View File

@ -40,15 +40,15 @@ export abstract class BasePanelView<T extends PanelApiImpl>
*/ */
protected abstract getComponent(): IFrameworkPart; protected abstract getComponent(): IFrameworkPart;
get element() { get element(): HTMLElement {
return this._element; return this._element;
} }
get width() { get width(): number {
return this._width; return this._width;
} }
get height() { get height(): number {
return this._height; return this._height;
} }
@ -83,11 +83,11 @@ export abstract class BasePanelView<T extends PanelApiImpl>
); );
} }
focus() { focus(): void {
this.api._onFocusEvent.fire(); this.api._onFocusEvent.fire();
} }
layout(width: number, height: number) { layout(width: number, height: number): void {
this._width = width; this._width = width;
this._height = height; this._height = height;
this.api._onDidDimensionChange.fire({ width, height }); this.api._onDidDimensionChange.fire({ width, height });
@ -104,7 +104,7 @@ export abstract class BasePanelView<T extends PanelApiImpl>
this.part = this.getComponent(); this.part = this.getComponent();
} }
update(event: PanelUpdateEvent) { update(event: PanelUpdateEvent): void {
this._params = { this._params = {
...this._params, ...this._params,
params: { params: {
@ -125,7 +125,7 @@ export abstract class BasePanelView<T extends PanelApiImpl>
}; };
} }
dispose() { dispose(): void {
super.dispose(); super.dispose();
this.api.dispose(); this.api.dispose();

View File

@ -77,7 +77,7 @@ export interface IHeader {
height: number | undefined; height: number | undefined;
} }
export interface IGroupview extends IDisposable, IGridPanelView { export interface IGroupview extends IGridPanelView {
readonly isActive: boolean; readonly isActive: boolean;
readonly size: number; readonly size: number;
readonly panels: IDockviewPanel[]; readonly panels: IDockviewPanel[];
@ -427,7 +427,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
} }
focus(): void { focus(): void {
this._activePanel?.focus(); this._activePanel?.focus?.();
} }
public openPanel( public openPanel(
@ -525,7 +525,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
): void { ): void {
if (!force && this.isActive === isGroupActive) { if (!force && this.isActive === isGroupActive) {
if (!skipFocus) { if (!skipFocus) {
this._activePanel?.focus(); this._activePanel?.focus?.();
} }
return; return;
} }
@ -545,7 +545,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
if (isGroupActive) { if (isGroupActive) {
if (!skipFocus) { if (!skipFocus) {
this._activePanel?.focus(); this._activePanel?.focus?.();
} }
} }
} }

View File

@ -74,9 +74,8 @@ export class ContentContainer
const disposable = new CompositeDisposable(); const disposable = new CompositeDisposable();
if (this.panel.view) { if (this.panel.view) {
const _onDidFocus: Event<void> = const _onDidFocus = this.panel.view.content.onDidFocus;
this.panel.view.content.onDidFocus!; const _onDidBlur = this.panel.view.content.onDidBlur;
const _onDidBlur: Event<void> = this.panel.view.content.onDidBlur!;
const { onDidFocus, onDidBlur } = trackFocus(this._element); const { onDidFocus, onDidBlur } = trackFocus(this._element);

View File

@ -9,7 +9,11 @@ import {
import { DockviewApi } from '../api/component.api'; import { DockviewApi } from '../api/component.api';
import { GroupPanel } from './groupviewPanel'; import { GroupPanel } from './groupviewPanel';
import { Event } from '../events'; import { Event } from '../events';
import { IGroupPanelView } from '../dockview/defaultGroupPanelView'; import {
IGroupPanelView,
SerializedGroupPanelView,
} from '../dockview/defaultGroupPanelView';
import { Optional } from '../types';
export interface IRenderable { export interface IRenderable {
id: string; id: string;
@ -19,7 +23,7 @@ export interface IRenderable {
} }
export interface HeaderPartInitParameters { export interface HeaderPartInitParameters {
title?: string; title: string;
} }
export interface GroupPanelPartInitParameters export interface GroupPanelPartInitParameters
@ -40,20 +44,28 @@ export interface IWatermarkRenderer extends IPanel {
updateParentGroup(group: GroupPanel, visible: boolean): void; updateParentGroup(group: GroupPanel, visible: boolean): void;
} }
export interface ITabRenderer extends IPanel { export interface ITabRenderer
extends Optional<
Omit<IPanel, 'id'>,
'dispose' | 'update' | 'layout' | 'toJSON'
> {
readonly element: HTMLElement; readonly element: HTMLElement;
init(parameters: GroupPanelPartInitParameters): void; init(parameters: GroupPanelPartInitParameters): void;
updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void; onGroupChange?(group: GroupPanel): void;
onPanelVisibleChange?(isPanelVisible: boolean): void;
} }
export interface IContentRenderer extends IPanel { export interface IContentRenderer
extends Optional<
Omit<IPanel, 'id'>,
'dispose' | 'update' | 'layout' | 'toJSON'
> {
readonly element: HTMLElement; readonly element: HTMLElement;
readonly actions?: HTMLElement;
readonly onDidFocus?: Event<void>; readonly onDidFocus?: Event<void>;
readonly onDidBlur?: Event<void>; readonly onDidBlur?: Event<void>;
updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void;
init(parameters: GroupPanelContentPartInitParameters): void; init(parameters: GroupPanelContentPartInitParameters): void;
layout(width: number, height: number): void; onGroupChange?(group: GroupPanel): void;
onPanelVisibleChange?(isPanelVisible: boolean): void;
} }
// watermark component // watermark component
@ -88,7 +100,9 @@ export type GroupPanelUpdateEvent = PanelUpdateEvent<{
export interface GroupviewPanelState { export interface GroupviewPanelState {
id: string; id: string;
view?: any; contentComponent?: string;
tabComponent?: string;
title?: string; title?: string;
params?: { [key: string]: any }; params?: { [key: string]: any };
view?: SerializedGroupPanelView; // depreciated
} }

View File

@ -22,7 +22,7 @@ export interface IPanel extends IDisposable {
layout(width: number, height: number): void; layout(width: number, height: number): void;
update(event: PanelUpdateEvent<Parameters>): void; update(event: PanelUpdateEvent<Parameters>): void;
toJSON(): object; toJSON(): object;
focus(): void; focus?(): void;
} }
export interface IFrameworkPart extends IDisposable { export interface IFrameworkPart extends IDisposable {

View File

@ -11,3 +11,5 @@ export type FunctionOrValue<T> = (() => T) | T;
export function isBooleanValue(value: any): value is boolean { export function isBooleanValue(value: any): value is boolean {
return typeof value === 'boolean'; return typeof value === 'boolean';
} }
export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;

View File

@ -11,7 +11,6 @@ import {
ITabRenderer, ITabRenderer,
watchElementResize, watchElementResize,
GroupPanel, GroupPanel,
DEFAULT_TAB_IDENTIFIER,
DefaultDockviewDeserialzier, DefaultDockviewDeserialzier,
} from 'dockview-core'; } from 'dockview-core';
import { ReactPanelContentPart } from './reactContentPart'; import { ReactPanelContentPart } from './reactContentPart';
@ -72,6 +71,8 @@ export interface IDockviewReactProps {
singleTabMode?: 'fullwidth' | 'default'; singleTabMode?: 'fullwidth' | 'default';
} }
const DEFAULT_REACT_TAB = 'props.defaultTabComponent';
export const DockviewReact = React.forwardRef( export const DockviewReact = React.forwardRef(
(props: IDockviewReactProps, ref: React.ForwardedRef<HTMLDivElement>) => { (props: IDockviewReactProps, ref: React.ForwardedRef<HTMLDivElement>) => {
const domRef = React.useRef<HTMLDivElement>(null); const domRef = React.useRef<HTMLDivElement>(null);
@ -144,16 +145,22 @@ export const DockviewReact = React.forwardRef(
const element = document.createElement('div'); const element = document.createElement('div');
const frameworkTabComponents = props.tabComponents || {};
if (props.defaultTabComponent) {
frameworkTabComponents[DEFAULT_REACT_TAB] =
props.defaultTabComponent;
}
const dockview = new DockviewComponent(element, { const dockview = new DockviewComponent(element, {
frameworkComponentFactory: factory, frameworkComponentFactory: factory,
frameworkComponents: props.components, frameworkComponents: props.components,
frameworkTabComponents: { frameworkTabComponents,
...(props.tabComponents || {}),
[DEFAULT_TAB_IDENTIFIER]: props.defaultTabComponent,
},
tabHeight: props.tabHeight, tabHeight: props.tabHeight,
watermarkFrameworkComponent: props.watermarkComponent, watermarkFrameworkComponent: props.watermarkComponent,
defaultTabComponent: DEFAULT_TAB_IDENTIFIER, defaultTabComponent: props.defaultTabComponent
? DEFAULT_REACT_TAB
: undefined,
styles: props.hideBorders styles: props.hideBorders
? { separatorBorder: 'transparent' } ? { separatorBorder: 'transparent' }
: undefined, : undefined,
@ -241,12 +248,19 @@ export const DockviewReact = React.forwardRef(
if (!dockviewRef.current) { if (!dockviewRef.current) {
return; return;
} }
const frameworkTabComponents = props.tabComponents || {};
if (props.defaultTabComponent) {
frameworkTabComponents[DEFAULT_REACT_TAB] =
props.defaultTabComponent;
}
dockviewRef.current.updateOptions({ dockviewRef.current.updateOptions({
defaultTabComponent: DEFAULT_TAB_IDENTIFIER, defaultTabComponent: props.defaultTabComponent
frameworkTabComponents: { ? DEFAULT_REACT_TAB
...(props.tabComponents || {}), : undefined,
[DEFAULT_TAB_IDENTIFIER]: props.defaultTabComponent, frameworkTabComponents,
},
}); });
}, [props.defaultTabComponent]); }, [props.defaultTabComponent]);

View File

@ -52,12 +52,6 @@ export class ReactPanelContentPart implements IContentRenderer {
); );
} }
public toJSON() {
return {
id: this.id,
};
}
public update(event: PanelUpdateEvent) { public update(event: PanelUpdateEvent) {
this.part?.update(event.params); this.part?.update(event.params);
} }

View File

@ -2,7 +2,6 @@ import * as React from 'react';
import { ReactPart, ReactPortalStore } from '../react'; import { ReactPart, ReactPortalStore } from '../react';
import { IGroupPanelBaseProps } from './dockview'; import { IGroupPanelBaseProps } from './dockview';
import { import {
DEFAULT_TAB_IDENTIFIER,
PanelUpdateEvent, PanelUpdateEvent,
GroupPanel, GroupPanel,
ITabRenderer, ITabRenderer,
@ -47,16 +46,6 @@ export class ReactPanelHeaderPart implements ITabRenderer {
this.part?.update(event.params); this.part?.update(event.params);
} }
public toJSON() {
if (this.id === DEFAULT_TAB_IDENTIFIER) {
return {};
}
return {
id: this.id,
};
}
public layout(_width: number, _height: number) { public layout(_width: number, _height: number) {
// noop - retrieval from api // noop - retrieval from api
} }

View File

@ -32,10 +32,6 @@ export class WebviewContentRenderer implements IContentRenderer {
this.parameters = parameters; this.parameters = parameters;
} }
public toJSON() {
return {};
}
public update(params: PanelUpdateEvent) { public update(params: PanelUpdateEvent) {
if (this.parameters) { if (this.parameters) {
this.parameters.params = params.params; this.parameters.params = params.params;