Merge pull request #138 from mathuo/137-group-controls-section

feat: group controls renderer
This commit is contained in:
mathuo 2022-06-11 23:08:30 +01:00 committed by GitHub
commit fbc8fce942
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 314 additions and 185 deletions

View File

@ -1,9 +1,5 @@
import { DefaultGroupPanelView } from '../../dockview/defaultGroupPanelView';
import {
IActionsRenderer,
IContentRenderer,
ITabRenderer,
} from '../../groupview/types';
import { IContentRenderer, ITabRenderer } from '../../groupview/types';
describe('defaultGroupPanelView', () => {
test('dispose cleanup', () => {
@ -23,24 +19,14 @@ describe('defaultGroupPanelView', () => {
return partial as IContentRenderer;
});
const actionsMock = jest.fn<IActionsRenderer, []>(() => {
const partial: Partial<IContentRenderer> = {
element: document.createElement('div'),
dispose: jest.fn(),
};
return partial as IContentRenderer;
});
const content = new contentMock();
const tab = new tabMock();
const actions = new actionsMock();
const cut = new DefaultGroupPanelView({ content, tab, actions });
const cut = new DefaultGroupPanelView({ content, tab });
cut.dispose();
expect(content.dispose).toHaveBeenCalled();
expect(tab.dispose).toHaveBeenCalled();
expect(actions.dispose).toHaveBeenCalled();
});
});

View File

@ -152,7 +152,7 @@ class TestGroupPanel implements IDockviewPanel {
public readonly title: string,
accessor: DockviewComponent
) {
this.api = new DockviewPanelApiImpl(this, this._group);
this.api = new DockviewPanelApiImpl(this, this._group!);
this._group = new GroupPanel(accessor, id, {});
this.view = new TestGroupPanelView(
new PanelContentPartTest(id, 'component')
@ -162,9 +162,8 @@ class TestGroupPanel implements IDockviewPanel {
get params(): Record<string, any> {
return {};
}
get group(): GroupPanel | undefined {
return this._group;
get group(): GroupPanel {
return this._group!;
}
updateParentGroup(group: GroupPanel, isGroupActive: boolean): void {

View File

@ -11,11 +11,7 @@ import {
IWatermarkRenderer,
} from '../../groupview/types';
import { PanelUpdateEvent } from '../../panel/types';
import {
GroupChangeKind2,
GroupOptions,
Groupview,
} from '../../groupview/groupview';
import { GroupOptions, Groupview } from '../../groupview/groupview';
import { DockviewPanelApi } from '../../api/groupPanelApi';
import {
DefaultGroupPanelView,
@ -24,6 +20,13 @@ import {
import { GroupPanel } from '../../groupview/groupviewPanel';
import { fireEvent } from '@testing-library/dom';
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
import { CompositeDisposable } from '../../lifecycle';
enum GroupChangeKind2 {
ADD_PANEL,
REMOVE_PANEL,
PANEL_ACTIVE,
}
class Watermark implements IWatermarkRenderer {
public readonly element = document.createElement('div');
@ -193,7 +196,7 @@ export class TestPanel implements IDockviewPanel {
toJSON(): GroupviewPanelState {
return {
id: this.id,
view: this._view.toJSON(),
view: this._view?.toJSON(),
title: this._params?.title,
};
}
@ -269,10 +272,29 @@ describe('groupview', () => {
const events: Array<{
kind: GroupChangeKind2;
panel?: IDockviewPanel;
}> = [];
const disposable = groupview2.model.onDidGroupChange((e) => {
events.push(e);
});
const disposable = new CompositeDisposable(
groupview2.model.onDidAddPanel((e) => {
events.push({
kind: GroupChangeKind2.ADD_PANEL,
panel: e.panel,
});
}),
groupview2.model.onDidRemovePanel((e) => {
events.push({
kind: GroupChangeKind2.REMOVE_PANEL,
panel: e.panel,
});
}),
groupview2.model.onDidActivePanelChange((e) => {
events.push({
kind: GroupChangeKind2.PANEL_ACTIVE,
panel: e.panel,
});
})
);
groupview2.initialize();
@ -301,10 +323,29 @@ describe('groupview', () => {
test('panel events flow', () => {
let events: Array<{
kind: GroupChangeKind2;
panel?: IDockviewPanel;
}> = [];
const disposable = groupview.model.onDidGroupChange((e) => {
events.push(e);
});
const disposable = new CompositeDisposable(
groupview.model.onDidAddPanel((e) => {
events.push({
kind: GroupChangeKind2.ADD_PANEL,
panel: e.panel,
});
}),
groupview.model.onDidRemovePanel((e) => {
events.push({
kind: GroupChangeKind2.REMOVE_PANEL,
panel: e.panel,
});
}),
groupview.model.onDidActivePanelChange((e) => {
events.push({
kind: GroupChangeKind2.PANEL_ACTIVE,
panel: e.panel,
});
})
);
const panel1 = new TestPanel('panel1', jest.fn() as any);
const panel2 = new TestPanel('panel2', jest.fn() as any);

View File

@ -1,7 +1,6 @@
import { DefaultTab } from './components/tab/defaultTab';
import {
GroupPanelPartInitParameters,
IActionsRenderer,
IContentRenderer,
ITabRenderer,
} from '../groupview/types';
@ -12,7 +11,6 @@ import { GroupPanelUpdateEvent } from '../groupview/groupPanel';
export interface IGroupPanelView extends IDisposable {
readonly content: IContentRenderer;
readonly tab?: ITabRenderer;
readonly actions?: IActionsRenderer;
update(event: GroupPanelUpdateEvent): void;
layout(width: number, height: number): void;
init(params: GroupPanelPartInitParameters): void;
@ -23,37 +21,18 @@ export interface IGroupPanelView extends IDisposable {
export class DefaultGroupPanelView implements IGroupPanelView {
private readonly _content: IContentRenderer;
private readonly _tab: ITabRenderer;
private readonly _actions: IActionsRenderer | undefined;
get content() {
get content(): IContentRenderer {
return this._content;
}
get tab() {
get tab(): ITabRenderer {
return this._tab;
}
get actions() {
return this._actions;
}
constructor(renderers: {
content: IContentRenderer;
tab?: ITabRenderer;
actions?: IActionsRenderer;
}) {
constructor(renderers: { content: IContentRenderer; tab?: ITabRenderer }) {
this._content = renderers.content;
this._tab = renderers.tab ?? new DefaultTab();
this._actions =
renderers.actions ||
(this.content.actions
? {
element: this.content.actions,
dispose: () => {
//
},
}
: undefined);
}
init(params: GroupPanelPartInitParameters): void {
@ -85,6 +64,5 @@ export class DefaultGroupPanelView implements IGroupPanelView {
dispose(): void {
this.content.dispose();
this.tab.dispose();
this.actions?.dispose();
}
}

View File

@ -36,7 +36,6 @@ import { LayoutMouseEvent, MouseEventKind } from '../groupview/tab';
import { Orientation } from '../splitview/core/splitview';
import { DefaultTab } from './components/tab/defaultTab';
import {
GroupChangeKind2,
GroupOptions,
GroupPanelViewState,
GroupviewDropEvent,
@ -73,6 +72,7 @@ export type DockviewComponentUpdateOptions = Pick<
| 'showDndOverlay'
| 'watermarkFrameworkComponent'
| 'defaultTabComponent'
| 'createGroupControlElement'
>;
export interface DockviewDropEvent extends GroupviewDropEvent {
@ -716,22 +716,14 @@ export class DockviewComponent
view.model.onDidDrop((event) => {
this._onDidDrop.fire({ ...event, api: this._api, group: view });
}),
view.model.onDidGroupChange((event) => {
switch (event.kind) {
case GroupChangeKind2.ADD_PANEL:
if (event.panel) {
this._onDidAddPanel.fire(event.panel);
}
break;
case GroupChangeKind2.REMOVE_PANEL:
if (event.panel) {
this._onDidRemovePanel.fire(event.panel);
}
break;
case GroupChangeKind2.PANEL_ACTIVE:
this._onDidActivePanelChange.fire(event.panel);
break;
}
view.model.onDidAddPanel((event) => {
this._onDidAddPanel.fire(event.panel);
}),
view.model.onDidRemovePanel((event) => {
this._onDidRemovePanel.fire(event.panel);
}),
view.model.onDidActivePanelChange((event) => {
this._onDidActivePanelChange.fire(event.panel);
})
);

View File

@ -13,6 +13,7 @@ import { ISplitviewStyles, Orientation } from '../splitview/core/splitview';
import { FrameworkFactory } from '../types';
import { DockviewDropTargets } from '../groupview/dnd';
import { PanelTransfer } from '../dnd/dataTransfer';
import { IGroupControlRenderer } from '../react/dockview/groupControlsRenderer';
export interface GroupPanelFrameworkComponentFactory {
content: FrameworkFactory<IContentRenderer>;
@ -66,6 +67,7 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
styles?: ISplitviewStyles;
defaultTabComponent?: string;
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
createGroupControlElement?: (group: GroupPanel) => IGroupControlRenderer;
}
export interface PanelOptions {

View File

@ -14,12 +14,7 @@ import { ITabsContainer, TabsContainer } from './titlebar/tabsContainer';
import { IWatermarkRenderer } from './types';
import { GroupPanel } from './groupviewPanel';
import { DockviewDropTargets } from './dnd';
export enum GroupChangeKind2 {
ADD_PANEL = 'ADD_PANEL',
REMOVE_PANEL = 'REMOVE_PANEL',
PANEL_ACTIVE = 'PANEL_ACTIVE',
}
import { IGroupControlRenderer } from '../react/dockview/groupControlsRenderer';
export interface DndService {
canDisplayOverlay(
@ -67,15 +62,14 @@ export interface GroupPanelViewState extends CoreGroupOptions {
}
export interface GroupviewChangeEvent {
readonly kind: GroupChangeKind2;
readonly panel?: IDockviewPanel;
readonly panel: IDockviewPanel;
}
export interface GroupviewDropEvent {
nativeEvent: DragEvent;
position: Position;
readonly nativeEvent: DragEvent;
readonly position: Position;
readonly index?: number;
getData(): PanelTransfer | undefined;
index?: number;
}
export interface IHeader {
@ -91,7 +85,9 @@ export interface IGroupview extends IDisposable, IGridPanelView {
readonly header: IHeader;
readonly isContentFocused: boolean;
readonly onDidDrop: Event<GroupviewDropEvent>;
readonly onDidGroupChange: Event<GroupviewChangeEvent>;
readonly onDidAddPanel: Event<GroupviewChangeEvent>;
readonly onDidRemovePanel: Event<GroupviewChangeEvent>;
readonly onDidActivePanelChange: Event<GroupviewChangeEvent>;
readonly onMove: Event<GroupMoveEvent>;
locked: boolean;
// state
@ -121,10 +117,11 @@ export class Groupview extends CompositeDisposable implements IGroupview {
private readonly tabsContainer: ITabsContainer;
private readonly contentContainer: IContentContainer;
private readonly dropTarget: Droptarget;
private _activePanel?: IDockviewPanel;
private _activePanel: IDockviewPanel | undefined;
private watermark?: IWatermarkRenderer;
private _isGroupActive = false;
private _locked = false;
private _control: IGroupControlRenderer | undefined;
private mostRecentlyUsed: IDockviewPanel[] = [];
@ -140,13 +137,22 @@ export class Groupview extends CompositeDisposable implements IGroupview {
private readonly _onMove = new Emitter<GroupMoveEvent>();
readonly onMove: Event<GroupMoveEvent> = this._onMove.event;
private readonly _onDidGroupChange = new Emitter<GroupviewChangeEvent>();
readonly onDidGroupChange: Event<GroupviewChangeEvent> =
this._onDidGroupChange.event;
private readonly _onDidDrop = new Emitter<GroupviewDropEvent>();
readonly onDidDrop: Event<GroupviewDropEvent> = this._onDidDrop.event;
private readonly _onDidAddPanel = new Emitter<GroupviewChangeEvent>();
readonly onDidAddPanel: Event<GroupviewChangeEvent> =
this._onDidAddPanel.event;
private readonly _onDidRemovePanel = new Emitter<GroupviewChangeEvent>();
readonly onDidRemovePanel: Event<GroupviewChangeEvent> =
this._onDidRemovePanel.event;
private readonly _onDidActivePanelChange =
new Emitter<GroupviewChangeEvent>();
readonly onDidActivePanelChange: Event<GroupviewChangeEvent> =
this._onDidActivePanelChange.event;
get element(): HTMLElement {
throw new Error('not supported');
}
@ -226,16 +232,10 @@ export class Groupview extends CompositeDisposable implements IGroupview {
this.container.classList.add('groupview');
this.addDisposables(
this._onMove,
this._onDidGroupChange,
this._onDidChange,
this._onDidDrop
);
this.tabsContainer = new TabsContainer(this.accessor, this.parent, {
tabHeight: options.tabHeight,
});
this.contentContainer = new ContentContainer();
this.dropTarget = new Droptarget(this.contentContainer.element, {
@ -268,7 +268,11 @@ export class Groupview extends CompositeDisposable implements IGroupview {
this.addDisposables(
this._onMove,
this._onDidGroupChange,
this._onDidChange,
this._onDidDrop,
this._onDidAddPanel,
this._onDidRemovePanel,
this._onDidActivePanelChange,
this.tabsContainer.onDrop((event) => {
this.handleDropEvent(event.event, Position.Center, event.index);
}),
@ -284,7 +288,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
);
}
initialize() {
initialize(): void {
if (this.options?.panels) {
this.options.panels.forEach((panel) => {
this.doAddPanel(panel);
@ -299,9 +303,21 @@ export class Groupview extends CompositeDisposable implements IGroupview {
// correctly initialized
this.setActive(this.isActive, true, true);
this.updateContainer();
if (this.accessor.options.createGroupControlElement) {
this._control = this.accessor.options.createGroupControlElement(
this.parent
);
this.addDisposables(this._control);
this._control.init({
containerApi: new DockviewApi(this.accessor),
api: this.parent.api,
});
this.tabsContainer.setActionElement(this._control.element);
}
}
public indexOf(panel: IDockviewPanel) {
public indexOf(panel: IDockviewPanel): number {
return this.tabsContainer.indexOf(panel.id);
}
@ -326,7 +342,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
public moveToNext(options?: {
panel?: IDockviewPanel;
suppressRoll?: boolean;
}) {
}): void {
if (!options) {
options = {};
}
@ -352,7 +368,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
public moveToPrevious(options?: {
panel?: IDockviewPanel;
suppressRoll?: boolean;
}) {
}): void {
if (!options) {
options = {};
}
@ -379,19 +395,19 @@ export class Groupview extends CompositeDisposable implements IGroupview {
this.openPanel(this.panels[normalizedIndex]);
}
public containsPanel(panel: IDockviewPanel) {
public containsPanel(panel: IDockviewPanel): boolean {
return this.panels.includes(panel);
}
init(_params: PanelInitParameters) {
init(_params: PanelInitParameters): void {
//noop
}
update(_params: PanelUpdateEvent) {
update(_params: PanelUpdateEvent): void {
//noop
}
focus() {
focus(): void {
this._activePanel?.focus();
}
@ -403,7 +419,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
skipSetPanelActive?: boolean;
skipSetGroupActive?: boolean;
} = {}
) {
): void {
if (
typeof options.index !== 'number' ||
options.index > this.panels.length
@ -452,7 +468,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
return this._removePanel(panelToRemove);
}
public closeAllPanels() {
public closeAllPanels(): void {
if (this.panels.length > 0) {
// take a copy since we will be edting the array as we iterate through
const arrPanelCpy = [...this.panels];
@ -468,25 +484,23 @@ export class Groupview extends CompositeDisposable implements IGroupview {
this.doClose(panel);
}
private doClose(panel: IDockviewPanel) {
private doClose(panel: IDockviewPanel): void {
this.accessor.removePanel(panel);
}
public isPanelActive(panel: IDockviewPanel) {
public isPanelActive(panel: IDockviewPanel): boolean {
return this._activePanel === panel;
}
updateActions() {
if (this.isActive && this._activePanel?.view?.actions) {
this.tabsContainer.setActionElement(
this._activePanel.view.actions.element
);
} else {
this.tabsContainer.setActionElement(undefined);
}
updateActions(element: HTMLElement | undefined): void {
this.tabsContainer.setActionElement(element);
}
public setActive(isGroupActive: boolean, skipFocus = false, force = false) {
public setActive(
isGroupActive: boolean,
skipFocus = false,
force = false
): void {
if (!force && this.isActive === isGroupActive) {
if (!skipFocus) {
this._activePanel?.focus();
@ -514,7 +528,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
}
}
public layout(width: number, height: number) {
public layout(width: number, height: number): void {
this._width = width;
this._height = height;
@ -525,7 +539,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
}
}
private _removePanel(panel: IDockviewPanel) {
private _removePanel(panel: IDockviewPanel): IDockviewPanel {
const isActivePanel = this._activePanel === panel;
this.doRemovePanel(panel);
@ -543,7 +557,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
return panel;
}
private doRemovePanel(panel: IDockviewPanel) {
private doRemovePanel(panel: IDockviewPanel): void {
const index = this.panels.indexOf(panel);
if (this._activePanel === panel) {
@ -560,17 +574,14 @@ export class Groupview extends CompositeDisposable implements IGroupview {
);
}
this._onDidGroupChange.fire({
kind: GroupChangeKind2.REMOVE_PANEL,
panel,
});
this._onDidRemovePanel.fire({ panel });
}
private doAddPanel(
panel: IDockviewPanel,
index: number = this.panels.length,
skipSetActive = false
) {
): void {
const existingPanel = this._panels.indexOf(panel);
const hasExistingPanel = existingPanel > -1;
@ -591,13 +602,10 @@ export class Groupview extends CompositeDisposable implements IGroupview {
this.updateMru(panel);
this.panels.splice(index, 0, panel);
this._onDidGroupChange.fire({
kind: GroupChangeKind2.ADD_PANEL,
panel,
});
this._onDidAddPanel.fire({ panel });
}
private doSetActivePanel(panel: IDockviewPanel | undefined) {
private doSetActivePanel(panel: IDockviewPanel | undefined): void {
this._activePanel = panel;
if (panel) {
@ -607,14 +615,11 @@ export class Groupview extends CompositeDisposable implements IGroupview {
this.updateMru(panel);
this._onDidGroupChange.fire({
kind: GroupChangeKind2.PANEL_ACTIVE,
panel,
});
this._onDidActivePanelChange.fire({ panel });
}
}
private updateMru(panel: IDockviewPanel) {
private updateMru(panel: IDockviewPanel): void {
if (this.mostRecentlyUsed.includes(panel)) {
this.mostRecentlyUsed.splice(
this.mostRecentlyUsed.indexOf(panel),
@ -624,8 +629,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
this.mostRecentlyUsed = [panel, ...this.mostRecentlyUsed];
}
private updateContainer() {
this.updateActions();
private updateContainer(): void {
toggleClass(this.container, 'empty', this.isEmpty);
this.panels.forEach((panel) =>
@ -680,7 +684,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
event: DragEvent,
position: Position,
index?: number
) {
): void {
const data = getPanelData();
if (data) {
@ -716,7 +720,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
}
}
public dispose() {
public dispose(): void {
super.dispose();
this.watermark?.dispose();

View File

@ -1,4 +1,3 @@
import { IDisposable } from '../lifecycle';
import { IDockviewComponent } from '../dockview/dockviewComponent';
import { DockviewPanelApi } from '../api/groupPanelApi';
import { PanelInitParameters, IPanel } from '../panel/types';
@ -42,10 +41,6 @@ export interface ITabRenderer extends IPanel {
updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void;
}
export interface IActionsRenderer extends IDisposable {
readonly element: HTMLElement;
}
export interface IContentRenderer extends IPanel {
readonly element: HTMLElement;
readonly actions?: HTMLElement;

View File

@ -12,15 +12,36 @@ import {
TabContextMenuEvent,
} from '../../dockview/options';
import { DockviewPanelApi } from '../../api/groupPanelApi';
import { usePortalsLifecycle } from '../react';
import { ReactPortalStore, usePortalsLifecycle } from '../react';
import { DockviewApi } from '../../api/component.api';
import { IWatermarkPanelProps, ReactWatermarkPart } from './reactWatermarkPart';
import { PanelCollection, PanelParameters } from '../types';
import { watchElementResize } from '../../dom';
import { IContentRenderer, ITabRenderer } from '../../groupview/types';
import {
IDockviewGroupControlProps,
IGroupControlRenderer,
ReactGroupControlsRendererPart,
} from './groupControlsRenderer';
import { GroupPanel } from '../../groupview/groupviewPanel';
export const DEFAULT_TAB_IDENTIFIER = '__default__tab__';
function createGroupControlElement(
component: React.FunctionComponent<IDockviewGroupControlProps> | undefined,
store: ReactPortalStore
): ((groupPanel: GroupPanel) => IGroupControlRenderer) | undefined {
return component
? (groupPanel: GroupPanel) => {
return new ReactGroupControlsRendererPart(
component,
store,
groupPanel
);
}
: undefined;
}
export interface IGroupPanelBaseProps<T extends {} = Record<string, any>>
extends PanelParameters<T> {
api: DockviewPanelApi;
@ -50,6 +71,7 @@ export interface IDockviewReactProps {
className?: string;
disableAutoResizing?: boolean;
defaultTabComponent?: React.FunctionComponent<IDockviewPanelHeaderProps>;
groupControlComponent?: React.FunctionComponent<IDockviewGroupControlProps>;
}
export const DockviewReact = React.forwardRef(
@ -138,6 +160,10 @@ export const DockviewReact = React.forwardRef(
? { separatorBorder: 'transparent' }
: undefined,
showDndOverlay: props.showDndOverlay,
createGroupControlElement: createGroupControlElement(
props.groupControlComponent,
{ addPortal }
),
});
domRef.current?.appendChild(dockview.element);
@ -243,6 +269,18 @@ export const DockviewReact = React.forwardRef(
});
}, [props.defaultTabComponent]);
React.useEffect(() => {
if (!dockviewRef.current) {
return;
}
dockviewRef.current.updateOptions({
createGroupControlElement: createGroupControlElement(
props.groupControlComponent,
{ addPortal }
),
});
}, [props.groupControlComponent]);
return (
<div
className={props.className}

View File

@ -0,0 +1,117 @@
import * as React from 'react';
import { ReactPart, ReactPortalStore } from '../react';
import { PanelUpdateEvent } from '../../panel/types';
import { GroupPanel, GroupviewPanelApi } from '../../groupview/groupviewPanel';
import { DockviewApi } from '../../api/component.api';
import {
CompositeDisposable,
IDisposable,
MutableDisposable,
} from '../../lifecycle';
import { IDockviewPanel } from '../../groupview/groupPanel';
export interface IDockviewGroupControlProps {
api: GroupviewPanelApi;
containerApi: DockviewApi;
panels: IDockviewPanel[];
activePanel: IDockviewPanel | undefined;
isGroupActive: boolean;
}
export interface IGroupControlRenderer extends IDisposable {
readonly element: HTMLElement;
init(params: { containerApi: DockviewApi; api: GroupviewPanelApi }): void;
}
export class ReactGroupControlsRendererPart {
private mutableDisposable = new MutableDisposable();
private _element: HTMLElement;
private _part?: ReactPart<IDockviewGroupControlProps>;
get element(): HTMLElement {
return this._element;
}
get part(): ReactPart<IDockviewGroupControlProps> | undefined {
return this._part;
}
get group(): GroupPanel {
return this._group;
}
constructor(
private readonly component: React.FunctionComponent<IDockviewGroupControlProps>,
private readonly reactPortalStore: ReactPortalStore,
private readonly _group: GroupPanel
) {
this._element = document.createElement('div');
this._element.className = 'dockview-react-part';
}
focus() {
// TODO
}
public init(parameters: {
containerApi: DockviewApi;
api: GroupviewPanelApi;
}): void {
this.mutableDisposable.value = new CompositeDisposable(
this._group.model.onDidAddPanel(() => {
this.updatePanels();
}),
this._group.model.onDidRemovePanel(() => {
this.updatePanels();
}),
this._group.model.onDidActivePanelChange(() => {
this.updateActivePanel();
}),
parameters.api.onDidActiveChange(() => {
this.updateGroupActive();
})
);
this._part = new ReactPart(
this.element,
this.reactPortalStore,
this.component,
{
api: parameters.api,
containerApi: parameters.containerApi,
panels: this._group.model.panels,
activePanel: this._group.model.activePanel,
isGroupActive: this._group.api.isActive,
}
);
}
public update(event: PanelUpdateEvent) {
this._part?.update(event.params);
}
public dispose() {
this.mutableDisposable.dispose();
this._part?.dispose();
}
private updatePanels() {
this.update({ params: { panels: this._group.model.panels } });
}
private updateActivePanel() {
this.update({
params: {
activePanel: this._group.model.activePanel,
},
});
}
private updateGroupActive() {
this.update({
params: {
isGroupActive: this._group.api.isActive,
},
});
}
}

View File

@ -2,34 +2,17 @@ import * as React from 'react';
import {
IContentRenderer,
GroupPanelContentPartInitParameters,
ITabRenderer,
} from '../../groupview/types';
import { ReactPart, ReactPortalStore } from '../react';
import { IDockviewPanelProps } from '../dockview/dockview';
import { PanelUpdateEvent } from '../../panel/types';
import { DockviewPanelApi } from '../../api/groupPanelApi';
import { DockviewApi } from '../../api/component.api';
import { GroupPanel } from '../../groupview/groupviewPanel';
import { Emitter, Event } from '../../events';
export interface IGroupPanelActionbarProps {
api: DockviewPanelApi;
containerApi: DockviewApi;
}
export interface ReactContentPartContext {
api: DockviewPanelApi;
containerApi: DockviewApi;
actionsPortalElement: HTMLElement;
tabPortalElement: ITabRenderer;
}
export class ReactPanelContentPart implements IContentRenderer {
private _element: HTMLElement;
private part?: ReactPart<IDockviewPanelProps>;
//
private _actionsElement: HTMLElement;
private actionsPart?: ReactPart<any>;
private _group: GroupPanel | undefined;
private readonly _onDidFocus = new Emitter<void>();
@ -42,10 +25,6 @@ export class ReactPanelContentPart implements IContentRenderer {
return this._element;
}
get actions(): HTMLElement {
return this._actionsElement;
}
constructor(
public readonly id: string,
private readonly component: React.FunctionComponent<IDockviewPanelProps>,
@ -53,9 +32,6 @@ export class ReactPanelContentPart implements IContentRenderer {
) {
this._element = document.createElement('div');
this._element.className = 'dockview-react-part';
this._actionsElement = document.createElement('div');
this._actionsElement.className = 'dockview-react-part';
}
focus() {
@ -63,13 +39,6 @@ export class ReactPanelContentPart implements IContentRenderer {
}
public init(parameters: GroupPanelContentPartInitParameters): void {
const context: ReactContentPartContext = {
api: parameters.api,
containerApi: parameters.containerApi,
actionsPortalElement: this._actionsElement,
tabPortalElement: parameters.tab,
};
this.part = new ReactPart(
this.element,
this.reactPortalStore,
@ -78,8 +47,7 @@ export class ReactPanelContentPart implements IContentRenderer {
params: parameters.params,
api: parameters.api,
containerApi: parameters.containerApi,
},
context
}
);
}
@ -108,6 +76,5 @@ export class ReactPanelContentPart implements IContentRenderer {
this._onDidFocus.dispose();
this._onDidBlur.dispose();
this.part?.dispose();
this.actionsPart?.dispose();
}
}

View File

@ -2,8 +2,7 @@ export * from './dockview/dockview';
export * from './dockview/defaultTab';
export * from './splitview/splitview';
export * from './gridview/gridview';
export * from './dockview/reactContentPart';
export * from './dockview/reactHeaderPart';
export { IDockviewGroupControlProps } from './dockview/groupControlsRenderer';
export { IWatermarkPanelProps } from './dockview/reactWatermarkPart';
export * from './paneview/paneview';
export * from './types';

View File

@ -65,6 +65,7 @@ export const ReactPartContext = React.createContext<{}>({});
export class ReactPart<P extends object, C extends object = {}>
implements IFrameworkPart
{
private _initialProps: Record<string, any> = {};
private componentInstance?: IPanelWrapperRef;
private ref?: { portal: React.ReactPortal; disposable: IDisposable };
private disposed = false;
@ -84,7 +85,12 @@ export class ReactPart<P extends object, C extends object = {}>
throw new Error('invalid operation: resource is already disposed');
}
this.componentInstance?.update(props);
if (!this.componentInstance) {
// if the component is yet to be mounted store the props
this._initialProps = { ...this._initialProps, ...props };
} else {
this.componentInstance.update(props);
}
}
private createPortal() {
@ -111,6 +117,11 @@ export class ReactPart<P extends object, C extends object = {}>
componentProps: this.parameters as unknown as {},
ref: (element: IPanelWrapperRef) => {
this.componentInstance = element;
if (Object.keys(this._initialProps).length > 0) {
this.componentInstance.update(this._initialProps);
this._initialProps = {}; // don't keep a reference to the users object once no longer required
}
},
}
);