mirror of
https://github.com/mathuo/dockview
synced 2025-02-10 18:35:46 +00:00
Merge pull request #264 from mathuo/263-left-header-actions
feat: add left header actions
This commit is contained in:
commit
e0f167050c
@ -1,14 +1,14 @@
|
|||||||
import { fireEvent } from '@testing-library/dom';
|
import { fireEvent } from '@testing-library/dom';
|
||||||
import { Emitter, Event } from '../../../events';
|
import { Emitter, Event } from '../../../../events';
|
||||||
import { ContentContainer } from '../../../dockview/components/panel/content';
|
import { ContentContainer } from '../../../../dockview/components/panel/content';
|
||||||
import {
|
import {
|
||||||
GroupPanelContentPartInitParameters,
|
GroupPanelContentPartInitParameters,
|
||||||
IContentRenderer,
|
IContentRenderer,
|
||||||
} from '../../../dockview/types';
|
} from '../../../../dockview/types';
|
||||||
import { CompositeDisposable } from '../../../lifecycle';
|
import { CompositeDisposable } from '../../../../lifecycle';
|
||||||
import { PanelUpdateEvent } from '../../../panel/types';
|
import { PanelUpdateEvent } from '../../../../panel/types';
|
||||||
import { IDockviewPanel } from '../../../dockview/dockviewPanel';
|
import { IDockviewPanel } from '../../../../dockview/dockviewPanel';
|
||||||
import { IDockviewPanelModel } from '../../../dockview/dockviewPanelModel';
|
import { IDockviewPanelModel } from '../../../../dockview/dockviewPanelModel';
|
||||||
|
|
||||||
class TestContentRenderer
|
class TestContentRenderer
|
||||||
extends CompositeDisposable
|
extends CompositeDisposable
|
@ -1,9 +1,9 @@
|
|||||||
import { fireEvent } from '@testing-library/dom';
|
import { fireEvent } from '@testing-library/dom';
|
||||||
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
|
import { LocalSelectionTransfer, PanelTransfer } from '../../../dnd/dataTransfer';
|
||||||
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
import { DockviewComponent } from '../../../dockview/dockviewComponent';
|
||||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../../dockview/dockviewGroupPanel';
|
||||||
import { DockviewGroupPanelModel } from '../../dockview/dockviewGroupPanelModel';
|
import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel';
|
||||||
import { Tab } from '../../dockview/components/tab/tab';
|
import { Tab } from '../../../dockview/components/tab/tab';
|
||||||
|
|
||||||
describe('tab', () => {
|
describe('tab', () => {
|
||||||
test('that empty tab has inactive-tab class', () => {
|
test('that empty tab has inactive-tab class', () => {
|
@ -1,13 +1,13 @@
|
|||||||
import { DockviewComponent } from '../../../dockview/dockviewComponent';
|
|
||||||
import { TabsContainer } from '../../../dockview/components/titlebar/tabsContainer';
|
|
||||||
import { fireEvent } from '@testing-library/dom';
|
|
||||||
import {
|
import {
|
||||||
LocalSelectionTransfer,
|
LocalSelectionTransfer,
|
||||||
PanelTransfer,
|
PanelTransfer,
|
||||||
} from '../../../dnd/dataTransfer';
|
} from '../../../../dnd/dataTransfer';
|
||||||
import { TestPanel } from '../dockviewGroupPanelModel.spec';
|
import { TabsContainer } from '../../../../dockview/components/titlebar/tabsContainer';
|
||||||
import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel';
|
import { DockviewComponent } from '../../../../dockview/dockviewComponent';
|
||||||
import { DockviewGroupPanel } from '../../../dockview/dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../../../dockview/dockviewGroupPanel';
|
||||||
|
import { DockviewGroupPanelModel } from '../../../../dockview/dockviewGroupPanelModel';
|
||||||
|
import { fireEvent } from '@testing-library/dom';
|
||||||
|
import { TestPanel } from '../../dockviewGroupPanelModel.spec';
|
||||||
|
|
||||||
describe('tabsContainer', () => {
|
describe('tabsContainer', () => {
|
||||||
test('that an external event does not render a drop target and calls through to the group mode', () => {
|
test('that an external event does not render a drop target and calls through to the group mode', () => {
|
||||||
@ -331,4 +331,136 @@ describe('tabsContainer', () => {
|
|||||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('left actions', () => {
|
||||||
|
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
||||||
|
return (<Partial<DockviewComponent>>{
|
||||||
|
options: {},
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
}) as DockviewComponent;
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
|
return (<Partial<DockviewGroupPanel>>{}) as DockviewGroupPanel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const accessor = new accessorMock();
|
||||||
|
const groupPanel = new groupPanelMock();
|
||||||
|
|
||||||
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
|
let query = cut.element.querySelectorAll(
|
||||||
|
'.tabs-and-actions-container > .left-actions-container'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(query[0].children.length).toBe(0);
|
||||||
|
|
||||||
|
// add left action
|
||||||
|
|
||||||
|
const left = document.createElement('div');
|
||||||
|
left.className = 'test-left-actions-element';
|
||||||
|
cut.setLeftActionsElement(left);
|
||||||
|
|
||||||
|
query = cut.element.querySelectorAll(
|
||||||
|
'.tabs-and-actions-container > .left-actions-container'
|
||||||
|
);
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(query[0].children.item(0)?.className).toBe(
|
||||||
|
'test-left-actions-element'
|
||||||
|
);
|
||||||
|
expect(query[0].children.length).toBe(1);
|
||||||
|
|
||||||
|
// add left action
|
||||||
|
|
||||||
|
const left2 = document.createElement('div');
|
||||||
|
left2.className = 'test-left-actions-element-2';
|
||||||
|
cut.setLeftActionsElement(left2);
|
||||||
|
|
||||||
|
query = cut.element.querySelectorAll(
|
||||||
|
'.tabs-and-actions-container > .left-actions-container'
|
||||||
|
);
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(query[0].children.item(0)?.className).toBe(
|
||||||
|
'test-left-actions-element-2'
|
||||||
|
);
|
||||||
|
expect(query[0].children.length).toBe(1);
|
||||||
|
|
||||||
|
// remove left action
|
||||||
|
|
||||||
|
cut.setLeftActionsElement(undefined);
|
||||||
|
query = cut.element.querySelectorAll(
|
||||||
|
'.tabs-and-actions-container > .left-actions-container'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(query[0].children.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('right actions', () => {
|
||||||
|
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
||||||
|
return (<Partial<DockviewComponent>>{
|
||||||
|
options: {},
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
}) as DockviewComponent;
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupPanelMock = jest.fn<DockviewGroupPanel, []>(() => {
|
||||||
|
return (<Partial<DockviewGroupPanel>>{}) as DockviewGroupPanel;
|
||||||
|
});
|
||||||
|
|
||||||
|
const accessor = new accessorMock();
|
||||||
|
const groupPanel = new groupPanelMock();
|
||||||
|
|
||||||
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
|
let query = cut.element.querySelectorAll(
|
||||||
|
'.tabs-and-actions-container > .right-actions-container'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(query[0].children.length).toBe(0);
|
||||||
|
|
||||||
|
// add right action
|
||||||
|
|
||||||
|
const right = document.createElement('div');
|
||||||
|
right.className = 'test-right-actions-element';
|
||||||
|
cut.setRightActionsElement(right);
|
||||||
|
|
||||||
|
query = cut.element.querySelectorAll(
|
||||||
|
'.tabs-and-actions-container > .right-actions-container'
|
||||||
|
);
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(query[0].children.item(0)?.className).toBe(
|
||||||
|
'test-right-actions-element'
|
||||||
|
);
|
||||||
|
expect(query[0].children.length).toBe(1);
|
||||||
|
|
||||||
|
// add right action
|
||||||
|
|
||||||
|
const right2 = document.createElement('div');
|
||||||
|
right2.className = 'test-right-actions-element-2';
|
||||||
|
cut.setRightActionsElement(right2);
|
||||||
|
|
||||||
|
query = cut.element.querySelectorAll(
|
||||||
|
'.tabs-and-actions-container > .right-actions-container'
|
||||||
|
);
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(query[0].children.item(0)?.className).toBe(
|
||||||
|
'test-right-actions-element-2'
|
||||||
|
);
|
||||||
|
expect(query[0].children.length).toBe(1);
|
||||||
|
|
||||||
|
// remove right action
|
||||||
|
|
||||||
|
cut.setRightActionsElement(undefined);
|
||||||
|
query = cut.element.querySelectorAll(
|
||||||
|
'.tabs-and-actions-container > .right-actions-container'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(query[0].children.length).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
@ -28,7 +28,8 @@ export interface ITabsContainer extends IDisposable {
|
|||||||
isActive: (tab: ITab) => boolean;
|
isActive: (tab: ITab) => boolean;
|
||||||
closePanel: (panel: IDockviewPanel) => void;
|
closePanel: (panel: IDockviewPanel) => void;
|
||||||
openPanel: (panel: IDockviewPanel, index?: number) => void;
|
openPanel: (panel: IDockviewPanel, index?: number) => void;
|
||||||
setActionElement(element: HTMLElement | undefined): void;
|
setRightActionsElement(element: HTMLElement | undefined): void;
|
||||||
|
setLeftActionsElement(element: HTMLElement | undefined): void;
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
show(): void;
|
show(): void;
|
||||||
hide(): void;
|
hide(): void;
|
||||||
@ -40,12 +41,14 @@ export class TabsContainer
|
|||||||
{
|
{
|
||||||
private readonly _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private readonly tabContainer: HTMLElement;
|
private readonly tabContainer: HTMLElement;
|
||||||
private readonly actionContainer: HTMLElement;
|
private readonly rightActionsContainer: HTMLElement;
|
||||||
|
private readonly leftActionsContainer: HTMLElement;
|
||||||
private readonly voidContainer: VoidContainer;
|
private readonly voidContainer: VoidContainer;
|
||||||
|
|
||||||
private tabs: IValueDisposable<ITab>[] = [];
|
private tabs: IValueDisposable<ITab>[] = [];
|
||||||
private selectedIndex = -1;
|
private selectedIndex = -1;
|
||||||
private actions: HTMLElement | undefined;
|
private rightActions: HTMLElement | undefined;
|
||||||
|
private leftActions: HTMLElement | undefined;
|
||||||
|
|
||||||
private _hidden = false;
|
private _hidden = false;
|
||||||
|
|
||||||
@ -79,17 +82,31 @@ export class TabsContainer
|
|||||||
this._element.style.display = 'none';
|
this._element.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
setActionElement(element: HTMLElement | undefined): void {
|
setRightActionsElement(element: HTMLElement | undefined): void {
|
||||||
if (this.actions === element) {
|
if (this.rightActions === element) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.actions) {
|
if (this.rightActions) {
|
||||||
this.actions.remove();
|
this.rightActions.remove();
|
||||||
this.actions = undefined;
|
this.rightActions = undefined;
|
||||||
}
|
}
|
||||||
if (element) {
|
if (element) {
|
||||||
this.actionContainer.appendChild(element);
|
this.rightActionsContainer.appendChild(element);
|
||||||
this.actions = element;
|
this.rightActions = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLeftActionsElement(element: HTMLElement | undefined): void {
|
||||||
|
if (this.leftActions === element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.leftActions) {
|
||||||
|
this.leftActions.remove();
|
||||||
|
this.leftActions = undefined;
|
||||||
|
}
|
||||||
|
if (element) {
|
||||||
|
this.leftActionsContainer.appendChild(element);
|
||||||
|
this.leftActions = element;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,8 +163,11 @@ export class TabsContainer
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.actionContainer = document.createElement('div');
|
this.rightActionsContainer = document.createElement('div');
|
||||||
this.actionContainer.className = 'action-container';
|
this.rightActionsContainer.className = 'right-actions-container';
|
||||||
|
|
||||||
|
this.leftActionsContainer = document.createElement('div');
|
||||||
|
this.leftActionsContainer.className = 'left-actions-container';
|
||||||
|
|
||||||
this.tabContainer = document.createElement('div');
|
this.tabContainer = document.createElement('div');
|
||||||
this.tabContainer.className = 'tabs-container';
|
this.tabContainer.className = 'tabs-container';
|
||||||
@ -155,8 +175,9 @@ export class TabsContainer
|
|||||||
this.voidContainer = new VoidContainer(this.accessor, this.group);
|
this.voidContainer = new VoidContainer(this.accessor, this.group);
|
||||||
|
|
||||||
this._element.appendChild(this.tabContainer);
|
this._element.appendChild(this.tabContainer);
|
||||||
|
this._element.appendChild(this.leftActionsContainer);
|
||||||
this._element.appendChild(this.voidContainer.element);
|
this._element.appendChild(this.voidContainer.element);
|
||||||
this._element.appendChild(this.actionContainer);
|
this._element.appendChild(this.rightActionsContainer);
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this.voidContainer,
|
this.voidContainer,
|
||||||
|
@ -71,7 +71,8 @@ export type DockviewComponentUpdateOptions = Pick<
|
|||||||
| 'showDndOverlay'
|
| 'showDndOverlay'
|
||||||
| 'watermarkFrameworkComponent'
|
| 'watermarkFrameworkComponent'
|
||||||
| 'defaultTabComponent'
|
| 'defaultTabComponent'
|
||||||
| 'createGroupControlElement'
|
| 'createLeftHeaderActionsElement'
|
||||||
|
| 'createRightHeaderActionsElement'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export interface DockviewDropEvent extends GroupviewDropEvent {
|
export interface DockviewDropEvent extends GroupviewDropEvent {
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
import { DockviewDropTargets, IWatermarkRenderer } from './types';
|
import { DockviewDropTargets, IWatermarkRenderer } from './types';
|
||||||
import { DockviewGroupPanel } from './dockviewGroupPanel';
|
import { DockviewGroupPanel } from './dockviewGroupPanel';
|
||||||
import { IDockviewPanel } from './dockviewPanel';
|
import { IDockviewPanel } from './dockviewPanel';
|
||||||
import { IGroupControlRenderer } from './options';
|
import { IHeaderActionsRenderer } from './options';
|
||||||
|
|
||||||
export interface DndService {
|
export interface DndService {
|
||||||
canDisplayOverlay(
|
canDisplayOverlay(
|
||||||
@ -137,7 +137,8 @@ export class DockviewGroupPanelModel
|
|||||||
private watermark?: IWatermarkRenderer;
|
private watermark?: IWatermarkRenderer;
|
||||||
private _isGroupActive = false;
|
private _isGroupActive = false;
|
||||||
private _locked = false;
|
private _locked = false;
|
||||||
private _control: IGroupControlRenderer | undefined;
|
private _rightHeaderActions: IHeaderActionsRenderer | undefined;
|
||||||
|
private _leftHeaderActions: IHeaderActionsRenderer | undefined;
|
||||||
|
|
||||||
private mostRecentlyUsed: IDockviewPanel[] = [];
|
private mostRecentlyUsed: IDockviewPanel[] = [];
|
||||||
|
|
||||||
@ -319,16 +320,34 @@ export class DockviewGroupPanelModel
|
|||||||
this.setActive(this.isActive, true, true);
|
this.setActive(this.isActive, true, true);
|
||||||
this.updateContainer();
|
this.updateContainer();
|
||||||
|
|
||||||
if (this.accessor.options.createGroupControlElement) {
|
if (this.accessor.options.createRightHeaderActionsElement) {
|
||||||
this._control = this.accessor.options.createGroupControlElement(
|
this._rightHeaderActions =
|
||||||
|
this.accessor.options.createRightHeaderActionsElement(
|
||||||
this.groupPanel
|
this.groupPanel
|
||||||
);
|
);
|
||||||
this.addDisposables(this._control);
|
this.addDisposables(this._rightHeaderActions);
|
||||||
this._control.init({
|
this._rightHeaderActions.init({
|
||||||
containerApi: new DockviewApi(this.accessor),
|
containerApi: new DockviewApi(this.accessor),
|
||||||
api: this.groupPanel.api,
|
api: this.groupPanel.api,
|
||||||
});
|
});
|
||||||
this.tabsContainer.setActionElement(this._control.element);
|
this.tabsContainer.setRightActionsElement(
|
||||||
|
this._rightHeaderActions.element
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.accessor.options.createLeftHeaderActionsElement) {
|
||||||
|
this._leftHeaderActions =
|
||||||
|
this.accessor.options.createLeftHeaderActionsElement(
|
||||||
|
this.groupPanel
|
||||||
|
);
|
||||||
|
this.addDisposables(this._leftHeaderActions);
|
||||||
|
this._leftHeaderActions.init({
|
||||||
|
containerApi: new DockviewApi(this.accessor),
|
||||||
|
api: this.groupPanel.api,
|
||||||
|
});
|
||||||
|
this.tabsContainer.setLeftActionsElement(
|
||||||
|
this._leftHeaderActions.element
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,7 +530,7 @@ export class DockviewGroupPanelModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateActions(element: HTMLElement | undefined): void {
|
updateActions(element: HTMLElement | undefined): void {
|
||||||
this.tabsContainer.setActionElement(element);
|
this.tabsContainer.setRightActionsElement(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActive(
|
public setActive(
|
||||||
|
@ -19,7 +19,7 @@ import { Position } from '../dnd/droptarget';
|
|||||||
import { IDockviewPanel } from './dockviewPanel';
|
import { IDockviewPanel } from './dockviewPanel';
|
||||||
import { FrameworkFactory } from '../panel/componentFactory';
|
import { FrameworkFactory } from '../panel/componentFactory';
|
||||||
|
|
||||||
export interface IGroupControlRenderer extends IDisposable {
|
export interface IHeaderActionsRenderer extends IDisposable {
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
init(params: {
|
init(params: {
|
||||||
containerApi: DockviewApi;
|
containerApi: DockviewApi;
|
||||||
@ -79,9 +79,12 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
|
|||||||
styles?: ISplitviewStyles;
|
styles?: ISplitviewStyles;
|
||||||
defaultTabComponent?: string;
|
defaultTabComponent?: string;
|
||||||
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
|
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
|
||||||
createGroupControlElement?: (
|
createRightHeaderActionsElement?: (
|
||||||
group: DockviewGroupPanel
|
group: DockviewGroupPanel
|
||||||
) => IGroupControlRenderer;
|
) => IHeaderActionsRenderer;
|
||||||
|
createLeftHeaderActionsElement?: (
|
||||||
|
group: DockviewGroupPanel
|
||||||
|
) => IHeaderActionsRenderer;
|
||||||
singleTabMode?: 'fullwidth' | 'default';
|
singleTabMode?: 'fullwidth' | 'default';
|
||||||
parentElement?: HTMLElement;
|
parentElement?: HTMLElement;
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ import {
|
|||||||
DockviewGroupPanelApi,
|
DockviewGroupPanelApi,
|
||||||
DockviewGroupPanelModel,
|
DockviewGroupPanelModel,
|
||||||
} from 'dockview-core';
|
} from 'dockview-core';
|
||||||
import { ReactGroupControlsRendererPart } from '../../dockview/groupControlsRenderer';
|
import { ReactHeaderActionsRendererPart } from '../../dockview/headerActionsRenderer';
|
||||||
|
|
||||||
describe('groupControlsRenderer', () => {
|
describe('headerActionsRenderer', () => {
|
||||||
test('#1', () => {
|
test('#1', () => {
|
||||||
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
|
||||||
() => {
|
() => {
|
||||||
@ -28,7 +28,7 @@ describe('groupControlsRenderer', () => {
|
|||||||
|
|
||||||
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
||||||
|
|
||||||
const cut = new ReactGroupControlsRendererPart(
|
const cut = new ReactHeaderActionsRendererPart(
|
||||||
jest.fn(),
|
jest.fn(),
|
||||||
{
|
{
|
||||||
addPortal: jest.fn(),
|
addPortal: jest.fn(),
|
@ -4,12 +4,12 @@ import {
|
|||||||
DockviewDropEvent,
|
DockviewDropEvent,
|
||||||
DockviewDndOverlayEvent,
|
DockviewDndOverlayEvent,
|
||||||
GroupPanelFrameworkComponentFactory,
|
GroupPanelFrameworkComponentFactory,
|
||||||
IGroupControlRenderer,
|
|
||||||
DockviewPanelApi,
|
DockviewPanelApi,
|
||||||
DockviewApi,
|
DockviewApi,
|
||||||
IContentRenderer,
|
IContentRenderer,
|
||||||
ITabRenderer,
|
ITabRenderer,
|
||||||
DockviewGroupPanel,
|
DockviewGroupPanel,
|
||||||
|
IHeaderActionsRenderer,
|
||||||
} from 'dockview-core';
|
} from 'dockview-core';
|
||||||
import { ReactPanelContentPart } from './reactContentPart';
|
import { ReactPanelContentPart } from './reactContentPart';
|
||||||
import { ReactPanelHeaderPart } from './reactHeaderPart';
|
import { ReactPanelHeaderPart } from './reactHeaderPart';
|
||||||
@ -18,17 +18,17 @@ import { ReactPortalStore, usePortalsLifecycle } from '../react';
|
|||||||
import { IWatermarkPanelProps, ReactWatermarkPart } from './reactWatermarkPart';
|
import { IWatermarkPanelProps, ReactWatermarkPart } from './reactWatermarkPart';
|
||||||
import { PanelCollection, PanelParameters } from '../types';
|
import { PanelCollection, PanelParameters } from '../types';
|
||||||
import {
|
import {
|
||||||
IDockviewGroupControlProps,
|
IDockviewHeaderActionsProps,
|
||||||
ReactGroupControlsRendererPart,
|
ReactHeaderActionsRendererPart,
|
||||||
} from './groupControlsRenderer';
|
} from './headerActionsRenderer';
|
||||||
|
|
||||||
function createGroupControlElement(
|
function createGroupControlElement(
|
||||||
component: React.FunctionComponent<IDockviewGroupControlProps> | undefined,
|
component: React.FunctionComponent<IDockviewHeaderActionsProps> | undefined,
|
||||||
store: ReactPortalStore
|
store: ReactPortalStore
|
||||||
): ((groupPanel: DockviewGroupPanel) => IGroupControlRenderer) | undefined {
|
): ((groupPanel: DockviewGroupPanel) => IHeaderActionsRenderer) | undefined {
|
||||||
return component
|
return component
|
||||||
? (groupPanel: DockviewGroupPanel) => {
|
? (groupPanel: DockviewGroupPanel) => {
|
||||||
return new ReactGroupControlsRendererPart(
|
return new ReactHeaderActionsRendererPart(
|
||||||
component,
|
component,
|
||||||
store,
|
store,
|
||||||
groupPanel
|
groupPanel
|
||||||
@ -65,7 +65,8 @@ export interface IDockviewReactProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
disableAutoResizing?: boolean;
|
disableAutoResizing?: boolean;
|
||||||
defaultTabComponent?: React.FunctionComponent<IDockviewPanelHeaderProps>;
|
defaultTabComponent?: React.FunctionComponent<IDockviewPanelHeaderProps>;
|
||||||
groupControlComponent?: React.FunctionComponent<IDockviewGroupControlProps>;
|
rightHeaderActionsComponent?: React.FunctionComponent<IDockviewHeaderActionsProps>;
|
||||||
|
leftHeaderActionsComponent?: React.FunctionComponent<IDockviewHeaderActionsProps>;
|
||||||
singleTabMode?: 'fullwidth' | 'default';
|
singleTabMode?: 'fullwidth' | 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,10 +151,15 @@ export const DockviewReact = React.forwardRef(
|
|||||||
? { separatorBorder: 'transparent' }
|
? { separatorBorder: 'transparent' }
|
||||||
: undefined,
|
: undefined,
|
||||||
showDndOverlay: props.showDndOverlay,
|
showDndOverlay: props.showDndOverlay,
|
||||||
createGroupControlElement: createGroupControlElement(
|
createLeftHeaderActionsElement: createGroupControlElement(
|
||||||
props.groupControlComponent,
|
props.leftHeaderActionsComponent,
|
||||||
{ addPortal }
|
{ addPortal }
|
||||||
),
|
),
|
||||||
|
createRightHeaderActionsElement: createGroupControlElement(
|
||||||
|
props.rightHeaderActionsComponent,
|
||||||
|
{ addPortal }
|
||||||
|
),
|
||||||
|
|
||||||
singleTabMode: props.singleTabMode,
|
singleTabMode: props.singleTabMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -250,12 +256,24 @@ export const DockviewReact = React.forwardRef(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dockviewRef.current.updateOptions({
|
dockviewRef.current.updateOptions({
|
||||||
createGroupControlElement: createGroupControlElement(
|
createRightHeaderActionsElement: createGroupControlElement(
|
||||||
props.groupControlComponent,
|
props.rightHeaderActionsComponent,
|
||||||
{ addPortal }
|
{ addPortal }
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}, [props.groupControlComponent]);
|
}, [props.rightHeaderActionsComponent]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!dockviewRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dockviewRef.current.updateOptions({
|
||||||
|
createLeftHeaderActionsElement: createGroupControlElement(
|
||||||
|
props.leftHeaderActionsComponent,
|
||||||
|
{ addPortal }
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}, [props.leftHeaderActionsComponent]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -10,24 +10,25 @@ import {
|
|||||||
PanelUpdateEvent,
|
PanelUpdateEvent,
|
||||||
} from 'dockview-core';
|
} from 'dockview-core';
|
||||||
|
|
||||||
export interface IDockviewGroupControlProps {
|
export interface IDockviewHeaderActionsProps {
|
||||||
api: DockviewGroupPanelApi;
|
api: DockviewGroupPanelApi;
|
||||||
containerApi: DockviewApi;
|
containerApi: DockviewApi;
|
||||||
panels: IDockviewPanel[];
|
panels: IDockviewPanel[];
|
||||||
activePanel: IDockviewPanel | undefined;
|
activePanel: IDockviewPanel | undefined;
|
||||||
isGroupActive: boolean;
|
isGroupActive: boolean;
|
||||||
|
group: DockviewGroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ReactGroupControlsRendererPart {
|
export class ReactHeaderActionsRendererPart {
|
||||||
private mutableDisposable = new DockviewMutableDisposable();
|
private mutableDisposable = new DockviewMutableDisposable();
|
||||||
private _element: HTMLElement;
|
private _element: HTMLElement;
|
||||||
private _part?: ReactPart<IDockviewGroupControlProps>;
|
private _part?: ReactPart<IDockviewHeaderActionsProps>;
|
||||||
|
|
||||||
get element(): HTMLElement {
|
get element(): HTMLElement {
|
||||||
return this._element;
|
return this._element;
|
||||||
}
|
}
|
||||||
|
|
||||||
get part(): ReactPart<IDockviewGroupControlProps> | undefined {
|
get part(): ReactPart<IDockviewHeaderActionsProps> | undefined {
|
||||||
return this._part;
|
return this._part;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ export class ReactGroupControlsRendererPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly component: React.FunctionComponent<IDockviewGroupControlProps>,
|
private readonly component: React.FunctionComponent<IDockviewHeaderActionsProps>,
|
||||||
private readonly reactPortalStore: ReactPortalStore,
|
private readonly reactPortalStore: ReactPortalStore,
|
||||||
private readonly _group: DockviewGroupPanel
|
private readonly _group: DockviewGroupPanel
|
||||||
) {
|
) {
|
||||||
@ -77,6 +78,7 @@ export class ReactGroupControlsRendererPart {
|
|||||||
panels: this._group.model.panels,
|
panels: this._group.model.panels,
|
||||||
activePanel: this._group.model.activePanel,
|
activePanel: this._group.model.activePanel,
|
||||||
isGroupActive: this._group.api.isActive,
|
isGroupActive: this._group.api.isActive,
|
||||||
|
group: this._group,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -4,7 +4,7 @@ export * from './dockview/dockview';
|
|||||||
export * from './dockview/defaultTab';
|
export * from './dockview/defaultTab';
|
||||||
export * from './splitview/splitview';
|
export * from './splitview/splitview';
|
||||||
export * from './gridview/gridview';
|
export * from './gridview/gridview';
|
||||||
export { IDockviewGroupControlProps } from './dockview/groupControlsRenderer';
|
export { IDockviewHeaderActionsProps } from './dockview/headerActionsRenderer';
|
||||||
export { IWatermarkPanelProps } from './dockview/reactWatermarkPart';
|
export { IWatermarkPanelProps } from './dockview/reactWatermarkPart';
|
||||||
export * from './paneview/paneview';
|
export * from './paneview/paneview';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
@ -18,7 +18,7 @@ import DockviewConstraints from '@site/sandboxes/constraints-dockview/src/app';
|
|||||||
import DndDockview from '@site/sandboxes/dnd-dockview/src/app';
|
import DndDockview from '@site/sandboxes/dnd-dockview/src/app';
|
||||||
import NestedDockview from '@site/sandboxes/nested-dockview/src/app';
|
import NestedDockview from '@site/sandboxes/nested-dockview/src/app';
|
||||||
import EventsDockview from '@site/sandboxes/events-dockview/src/app';
|
import EventsDockview from '@site/sandboxes/events-dockview/src/app';
|
||||||
import DockviewGroupControl from '@site/sandboxes/groupcontrol-dockview/src/app';
|
import DockviewGroupControl from '@site/sandboxes/headeractions-dockview/src/app';
|
||||||
import CustomHeadersDockview from '@site/sandboxes/customheader-dockview/src/app';
|
import CustomHeadersDockview from '@site/sandboxes/customheader-dockview/src/app';
|
||||||
import DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app';
|
import DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app';
|
||||||
import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app';
|
import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app';
|
||||||
@ -60,7 +60,7 @@ import { DockviewReact } from 'dockview';
|
|||||||
```
|
```
|
||||||
|
|
||||||
| Property | Type | Optional | Default | Description |
|
| Property | Type | Optional | Default | Description |
|
||||||
| --------------------- | ------------------------------------ | -------- | --------- | ------------------------------------------------------------ |
|
| --------------------------- | ------------------------------------ | -------- | --------- | ------------------------------------------------------------ |
|
||||||
| onReady | (event: SplitviewReadyEvent) => void | No | | |
|
| onReady | (event: SplitviewReadyEvent) => void | No | | |
|
||||||
| components | object | No | | |
|
| components | object | No | | |
|
||||||
| tabComponents | object | Yes | | |
|
| tabComponents | object | Yes | | |
|
||||||
@ -71,7 +71,8 @@ import { DockviewReact } from 'dockview';
|
|||||||
| onDidDrop | Event | Yes | false | |
|
| onDidDrop | Event | Yes | false | |
|
||||||
| showDndOverlay | Event | Yes | false | |
|
| showDndOverlay | Event | Yes | false | |
|
||||||
| defaultTabComponent | object | Yes | | |
|
| defaultTabComponent | object | Yes | | |
|
||||||
| groupControlComponent | object | Yes | | |
|
| leftHeaderActionsComponent | object | Yes | | |
|
||||||
|
| rightHeaderActionsComponent | object | Yes | | |
|
||||||
| singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | |
|
| singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | |
|
||||||
|
|
||||||
## Dockview API
|
## Dockview API
|
||||||
@ -683,22 +684,22 @@ panel.group.locked = true;
|
|||||||
|
|
||||||
### Group Controls Panel
|
### Group Controls Panel
|
||||||
|
|
||||||
`DockviewReact` accepts a prop `groupControlComponent` which expects a React component whos props are `IDockviewGroupControlProps`.
|
`DockviewReact` accepts `leftHeaderActionsComponent` and `rightHeaderActionsComponent` which expect a React component with props `IDockviewHeaderActionsProps`.
|
||||||
This control will be rendered inside the header bar on the right hand side for each group of tabs.
|
These controls are rendered of the left and right side of the space to the right of the tabs in the header bar.
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
const Component: React.FunctionComponent<IDockviewGroupControlProps> = () => {
|
const Component: React.FunctionComponent<IDockviewHeaderActionsProps> = () => {
|
||||||
return <div>{'...'}</div>;
|
return <div>{'...'}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
return <DockviewReact {...props} groupControlComponent={Component} />;
|
return <DockviewReact {...props} leftHeaderActionsComponent={Component} rightHeaderActionsComponent={...} />;
|
||||||
```
|
```
|
||||||
|
|
||||||
As a simple example the below uses the `groupControlComponent` to render a small control that indicates whether the group
|
As a simple example the below uses the `groupControlComponent` to render a small control that indicates whether the group
|
||||||
is active and which panel is active in that group.
|
is active and which panel is active in that group.
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
const GroupControlComponent = (props: IDockviewGroupControlProps) => {
|
const RightHeaderActionsComponent = (props: IDockviewHeaderActionsProps) => {
|
||||||
const isGroupActive = props.isGroupActive;
|
const isGroupActive = props.isGroupActive;
|
||||||
const activePanel = props.activePanel;
|
const activePanel = props.activePanel;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
DockviewReadyEvent,
|
DockviewReadyEvent,
|
||||||
IDockviewPanelHeaderProps,
|
IDockviewPanelHeaderProps,
|
||||||
IDockviewPanelProps,
|
IDockviewPanelProps,
|
||||||
IDockviewGroupControlProps,
|
IDockviewHeaderActionsProps,
|
||||||
} from 'dockview';
|
} from 'dockview';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactDOM from 'react-dom';
|
import * as ReactDOM from 'react-dom';
|
||||||
@ -134,7 +134,7 @@ const groupControlsComponents = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const GroupControls = (props: IDockviewGroupControlProps) => {
|
const RightControls = (props: IDockviewHeaderActionsProps) => {
|
||||||
const Component = React.useMemo(() => {
|
const Component = React.useMemo(() => {
|
||||||
if (!props.isGroupActive || !props.activePanel) {
|
if (!props.isGroupActive || !props.activePanel) {
|
||||||
return null;
|
return null;
|
||||||
@ -161,6 +161,36 @@ const GroupControls = (props: IDockviewGroupControlProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
|
||||||
|
const LeftControls = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const onClick = () => {
|
||||||
|
props.containerApi.addPanel({
|
||||||
|
id: `id_${Date.now().toString()}`,
|
||||||
|
component: 'default',
|
||||||
|
title: `Tab ${counter++}`,
|
||||||
|
position: {
|
||||||
|
referenceGroup: props.group,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="group-control"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '0px 8px',
|
||||||
|
height: '100%',
|
||||||
|
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon onClick={onClick} icon="add" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const DockviewDemo = () => {
|
const DockviewDemo = () => {
|
||||||
const onReady = (event: DockviewReadyEvent) => {
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
event.api.addPanel({
|
event.api.addPanel({
|
||||||
@ -196,8 +226,6 @@ const DockviewDemo = () => {
|
|||||||
title: 'Panel 6',
|
title: 'Panel 6',
|
||||||
position: { referencePanel: 'panel_4', direction: 'below' },
|
position: { referencePanel: 'panel_4', direction: 'below' },
|
||||||
});
|
});
|
||||||
panel6.group.locked = true;
|
|
||||||
panel6.group.header.hidden = true;
|
|
||||||
event.api.addPanel({
|
event.api.addPanel({
|
||||||
id: 'panel_7',
|
id: 'panel_7',
|
||||||
component: 'default',
|
component: 'default',
|
||||||
@ -211,8 +239,6 @@ const DockviewDemo = () => {
|
|||||||
position: { referencePanel: 'panel_7', direction: 'within' },
|
position: { referencePanel: 'panel_7', direction: 'within' },
|
||||||
});
|
});
|
||||||
|
|
||||||
event.api.addGroup();
|
|
||||||
|
|
||||||
event.api.getPanel('panel_1')!.api.setActive();
|
event.api.getPanel('panel_1')!.api.setActive();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -220,7 +246,8 @@ const DockviewDemo = () => {
|
|||||||
<DockviewReact
|
<DockviewReact
|
||||||
components={components}
|
components={components}
|
||||||
defaultTabComponent={headerComponents.default}
|
defaultTabComponent={headerComponents.default}
|
||||||
groupControlComponent={GroupControls}
|
rightHeaderActionsComponent={RightControls}
|
||||||
|
leftHeaderActionsComponent={LeftControls}
|
||||||
onReady={onReady}
|
onReady={onReady}
|
||||||
className="dockview-theme-abyss"
|
className="dockview-theme-abyss"
|
||||||
/>
|
/>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "groupcontrol-dockview",
|
"name": "headeractions-dockview",
|
||||||
"description": "",
|
"description": "",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"dockview"
|
"dockview"
|
@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
DockviewReact,
|
DockviewReact,
|
||||||
DockviewReadyEvent,
|
DockviewReadyEvent,
|
||||||
IDockviewGroupControlProps,
|
IDockviewHeaderActionsProps,
|
||||||
IDockviewPanelProps,
|
IDockviewPanelProps,
|
||||||
} from 'dockview';
|
} from 'dockview';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
@ -26,9 +26,8 @@ const components = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const GroupControlComponent = (props: IDockviewGroupControlProps) => {
|
const RightHeaderActions = (props: IDockviewHeaderActionsProps) => {
|
||||||
const isGroupActive = props.isGroupActive;
|
const isGroupActive = props.isGroupActive;
|
||||||
const activePanel = props.activePanel;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dockview-groupcontrol-demo">
|
<div className="dockview-groupcontrol-demo">
|
||||||
@ -40,6 +39,15 @@ const GroupControlComponent = (props: IDockviewGroupControlProps) => {
|
|||||||
>
|
>
|
||||||
{isGroupActive ? 'Group Active' : 'Group Inactive'}
|
{isGroupActive ? 'Group Active' : 'Group Inactive'}
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const LeftHeaderActions = (props: IDockviewHeaderActionsProps) => {
|
||||||
|
const activePanel = props.activePanel;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="dockview-groupcontrol-demo">
|
||||||
<span className="dockview-groupcontrol-demo-active-panel">{`activePanel: ${
|
<span className="dockview-groupcontrol-demo-active-panel">{`activePanel: ${
|
||||||
activePanel?.id || 'null'
|
activePanel?.id || 'null'
|
||||||
}`}</span>
|
}`}</span>
|
||||||
@ -87,7 +95,8 @@ const DockviewGroupControl = () => {
|
|||||||
<DockviewReact
|
<DockviewReact
|
||||||
onReady={onReady}
|
onReady={onReady}
|
||||||
components={components}
|
components={components}
|
||||||
groupControlComponent={GroupControlComponent}
|
leftHeaderActionsComponent={LeftHeaderActions}
|
||||||
|
rightHeaderActionsComponent={RightHeaderActions}
|
||||||
className="dockview-theme-abyss"
|
className="dockview-theme-abyss"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
@ -18,7 +18,7 @@ import DockviewConstraints from '@site/sandboxes/constraints-dockview/src/app';
|
|||||||
import DndDockview from '@site/sandboxes/dnd-dockview/src/app';
|
import DndDockview from '@site/sandboxes/dnd-dockview/src/app';
|
||||||
import NestedDockview from '@site/sandboxes/nested-dockview/src/app';
|
import NestedDockview from '@site/sandboxes/nested-dockview/src/app';
|
||||||
import EventsDockview from '@site/sandboxes/events-dockview/src/app';
|
import EventsDockview from '@site/sandboxes/events-dockview/src/app';
|
||||||
import DockviewGroupControl from '@site/sandboxes/groupcontrol-dockview/src/app';
|
import DockviewGroupControl from '@site/sandboxes/headeractions-dockview/src/app';
|
||||||
import CustomHeadersDockview from '@site/sandboxes/customheader-dockview/src/app';
|
import CustomHeadersDockview from '@site/sandboxes/customheader-dockview/src/app';
|
||||||
import DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app';
|
import DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app';
|
||||||
import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app';
|
import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app';
|
||||||
|
Loading…
Reference in New Issue
Block a user