From 13d3db605b5e328d252940a4797cd4ac2682c758 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sat, 3 Jun 2023 20:18:42 +0100 Subject: [PATCH 1/3] feat: add left header actions --- .../dockview/components/tab/defaultTab.scss | 25 ---------- .../components/titlebar/tabsContainer.ts | 47 ++++++++++++++----- .../src/dockview/dockviewComponent.ts | 5 +- .../src/dockview/dockviewGroupPanelModel.ts | 26 +++++++--- .../dockview-core/src/dockview/options.ts | 5 +- packages/dockview/src/dockview/dockview.tsx | 30 +++++++++--- packages/docs/docs/components/dockview.mdx | 29 ++++++------ .../groupcontrol-dockview/src/app.tsx | 4 +- 8 files changed, 102 insertions(+), 69 deletions(-) diff --git a/packages/dockview-core/src/dockview/components/tab/defaultTab.scss b/packages/dockview-core/src/dockview/components/tab/defaultTab.scss index d820534b8..81208377e 100644 --- a/packages/dockview-core/src/dockview/components/tab/defaultTab.scss +++ b/packages/dockview-core/src/dockview/components/tab/defaultTab.scss @@ -46,30 +46,5 @@ padding: 0px 8px; flex-grow: 1; } - - .action-container { - text-align: right; - display: flex; - - .tab-list { - display: flex; - padding: 0px; - margin: 0px; - justify-content: flex-end; - - .tab-action { - padding: 4px; - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - - &:hover { - border-radius: 2px; - background-color: var(--dv-icon-hover-background-color); - } - } - } - } } } diff --git a/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts b/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts index debdf42e0..f1f2a9441 100644 --- a/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts +++ b/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts @@ -28,7 +28,8 @@ export interface ITabsContainer extends IDisposable { isActive: (tab: ITab) => boolean; closePanel: (panel: IDockviewPanel) => void; openPanel: (panel: IDockviewPanel, index?: number) => void; - setActionElement(element: HTMLElement | undefined): void; + setRightActionsElement(element: HTMLElement | undefined): void; + setLeftActionsElement(element: HTMLElement | undefined): void; hidden: boolean; show(): void; hide(): void; @@ -40,12 +41,14 @@ export class TabsContainer { private readonly _element: HTMLElement; private readonly tabContainer: HTMLElement; - private readonly actionContainer: HTMLElement; + private readonly rightActionsContainer: HTMLElement; + private readonly leftActionsContainer: HTMLElement; private readonly voidContainer: VoidContainer; private tabs: IValueDisposable[] = []; private selectedIndex = -1; - private actions: HTMLElement | undefined; + private rightActions: HTMLElement | undefined; + private leftActions: HTMLElement | undefined; private _hidden = false; @@ -79,17 +82,31 @@ export class TabsContainer this._element.style.display = 'none'; } - setActionElement(element: HTMLElement | undefined): void { - if (this.actions === element) { + setRightActionsElement(element: HTMLElement | undefined): void { + if (this.rightActions === element) { return; } - if (this.actions) { - this.actions.remove(); - this.actions = undefined; + if (this.rightActions) { + this.rightActions.remove(); + this.rightActions = undefined; } if (element) { - this.actionContainer.appendChild(element); - this.actions = element; + this.rightActionsContainer.appendChild(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.actionContainer.className = 'action-container'; + this.rightActionsContainer = document.createElement('div'); + this.rightActionsContainer.className = 'right-actions-container'; + + this.leftActionsContainer = document.createElement('div'); + this.leftActionsContainer.className = 'left-actions-container'; this.tabContainer = document.createElement('div'); this.tabContainer.className = 'tabs-container'; @@ -155,8 +175,9 @@ export class TabsContainer this.voidContainer = new VoidContainer(this.accessor, this.group); this._element.appendChild(this.tabContainer); + this._element.appendChild(this.leftActionsContainer); this._element.appendChild(this.voidContainer.element); - this._element.appendChild(this.actionContainer); + this._element.appendChild(this.rightActionsContainer); this.addDisposables( this.voidContainer, diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index 6d58c0b67..f837fbdf3 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -71,7 +71,8 @@ export type DockviewComponentUpdateOptions = Pick< | 'showDndOverlay' | 'watermarkFrameworkComponent' | 'defaultTabComponent' - | 'createGroupControlElement' + | 'createLeftHeaderActionsElement' + | 'createRightHeaderActionsElement' >; export interface DockviewDropEvent extends GroupviewDropEvent { @@ -717,7 +718,7 @@ export class DockviewComponent if (itemId === undefined) { if (sourceGroup) { - this.moveGroup(sourceGroup, referenceGroup, target); + this.moveGroup(sourceGroup, referenceGroup, target); } return; } diff --git a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts index 5e9773a73..6e31cdce3 100644 --- a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts +++ b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts @@ -138,6 +138,7 @@ export class DockviewGroupPanelModel private _isGroupActive = false; private _locked = false; private _control: IGroupControlRenderer | undefined; + private _lhs: IGroupControlRenderer | undefined; private mostRecentlyUsed: IDockviewPanel[] = []; @@ -319,16 +320,29 @@ export class DockviewGroupPanelModel this.setActive(this.isActive, true, true); this.updateContainer(); - if (this.accessor.options.createGroupControlElement) { - this._control = this.accessor.options.createGroupControlElement( - this.groupPanel - ); + if (this.accessor.options.createRightHeaderActionsElement) { + this._control = + this.accessor.options.createRightHeaderActionsElement( + this.groupPanel + ); this.addDisposables(this._control); this._control.init({ containerApi: new DockviewApi(this.accessor), api: this.groupPanel.api, }); - this.tabsContainer.setActionElement(this._control.element); + this.tabsContainer.setRightActionsElement(this._control.element); + } + + if (this.accessor.options.createLeftHeaderActionsElement) { + this._lhs = this.accessor.options.createLeftHeaderActionsElement( + this.groupPanel + ); + this.addDisposables(this._lhs); + this._lhs.init({ + containerApi: new DockviewApi(this.accessor), + api: this.groupPanel.api, + }); + this.tabsContainer.setLeftActionsElement(this._lhs.element); } } @@ -511,7 +525,7 @@ export class DockviewGroupPanelModel } updateActions(element: HTMLElement | undefined): void { - this.tabsContainer.setActionElement(element); + this.tabsContainer.setRightActionsElement(element); } public setActive( diff --git a/packages/dockview-core/src/dockview/options.ts b/packages/dockview-core/src/dockview/options.ts index 1e9b66db1..bab295646 100644 --- a/packages/dockview-core/src/dockview/options.ts +++ b/packages/dockview-core/src/dockview/options.ts @@ -79,7 +79,10 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions { styles?: ISplitviewStyles; defaultTabComponent?: string; showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean; - createGroupControlElement?: ( + createRightHeaderActionsElement?: ( + group: DockviewGroupPanel + ) => IGroupControlRenderer; + createLeftHeaderActionsElement?: ( group: DockviewGroupPanel ) => IGroupControlRenderer; singleTabMode?: 'fullwidth' | 'default'; diff --git a/packages/dockview/src/dockview/dockview.tsx b/packages/dockview/src/dockview/dockview.tsx index 8998c770d..e347a8aba 100644 --- a/packages/dockview/src/dockview/dockview.tsx +++ b/packages/dockview/src/dockview/dockview.tsx @@ -65,7 +65,8 @@ export interface IDockviewReactProps { className?: string; disableAutoResizing?: boolean; defaultTabComponent?: React.FunctionComponent; - groupControlComponent?: React.FunctionComponent; + rightHeaderActionsComponent?: React.FunctionComponent; + leftHeaderActionsComponent?: React.FunctionComponent; singleTabMode?: 'fullwidth' | 'default'; } @@ -150,10 +151,15 @@ export const DockviewReact = React.forwardRef( ? { separatorBorder: 'transparent' } : undefined, showDndOverlay: props.showDndOverlay, - createGroupControlElement: createGroupControlElement( - props.groupControlComponent, + createLeftHeaderActionsElement: createGroupControlElement( + props.leftHeaderActionsComponent, { addPortal } ), + createRightHeaderActionsElement: createGroupControlElement( + props.rightHeaderActionsComponent, + { addPortal } + ), + singleTabMode: props.singleTabMode, }); @@ -250,12 +256,24 @@ export const DockviewReact = React.forwardRef( return; } dockviewRef.current.updateOptions({ - createGroupControlElement: createGroupControlElement( - props.groupControlComponent, + createRightHeaderActionsElement: createGroupControlElement( + props.rightHeaderActionsComponent, { addPortal } ), }); - }, [props.groupControlComponent]); + }, [props.rightHeaderActionsComponent]); + + React.useEffect(() => { + if (!dockviewRef.current) { + return; + } + dockviewRef.current.updateOptions({ + createLeftHeaderActionsElement: createGroupControlElement( + props.leftHeaderActionsComponent, + { addPortal } + ), + }); + }, [props.leftHeaderActionsComponent]); return (
void | No | | | -| components | object | No | | | -| tabComponents | object | Yes | | | -| watermarkComponent | object | Yes | | | -| hideBorders | boolean | Yes | false | | -| className | string | Yes | '' | | -| disableAutoResizing | boolean | Yes | false | See Auto Resizing | -| onDidDrop | Event | Yes | false | | -| showDndOverlay | Event | Yes | false | | -| defaultTabComponent | object | Yes | | | -| groupControlComponent | object | Yes | | | -| singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | | +| Property | Type | Optional | Default | Description | +| --------------------------- | ------------------------------------ | -------- | --------- | ------------------------------------------------------------ | +| onReady | (event: SplitviewReadyEvent) => void | No | | | +| components | object | No | | | +| tabComponents | object | Yes | | | +| watermarkComponent | object | Yes | | | +| hideBorders | boolean | Yes | false | | +| className | string | Yes | '' | | +| disableAutoResizing | boolean | Yes | false | See Auto Resizing | +| onDidDrop | Event | Yes | false | | +| showDndOverlay | Event | Yes | false | | +| defaultTabComponent | object | Yes | | | +| leftHeaderActionsComponent | object | Yes | | | +| rightHeaderActionsComponent | object | Yes | | | +| singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | | ## Dockview API diff --git a/packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx b/packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx index fc4c868eb..0b24f7399 100644 --- a/packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx +++ b/packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx @@ -26,7 +26,7 @@ const components = { }, }; -const GroupControlComponent = (props: IDockviewGroupControlProps) => { +const RightHeaderActions = (props: IDockviewGroupControlProps) => { const isGroupActive = props.isGroupActive; const activePanel = props.activePanel; @@ -87,7 +87,7 @@ const DockviewGroupControl = () => { ); From d7baa93a9bccb83a788bb96c3cf310a9e1c7c036 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sat, 3 Jun 2023 20:34:51 +0100 Subject: [PATCH 2/3] feat: left header actions --- .../components}/panel/content.spec.ts | 14 +- .../components}/tab.spec.ts | 10 +- .../titlebar/tabsContainer.spec.ts | 146 +++++++++++++++++- .../dockviewGroupPanelModel.spec.ts | 0 .../dockview/components/tab/defaultTab.scss | 25 +++ .../src/dockview/dockviewGroupPanelModel.ts | 31 ++-- .../dockview-core/src/dockview/options.ts | 6 +- ....spec.ts => headerActionsRenderer.spec.ts} | 6 +- .../dockview/groupControlsRenderer.spec.ts | 6 +- packages/dockview/src/dockview/dockview.tsx | 18 +-- ...lsRenderer.ts => headerActionsRenderer.ts} | 12 +- packages/dockview/src/index.ts | 2 +- packages/docs/docs/components/dockview.mdx | 6 +- .../docs/sandboxes/demo-dockview/src/app.tsx | 41 ++++- .../groupcontrol-dockview/src/app.tsx | 6 +- 15 files changed, 260 insertions(+), 69 deletions(-) rename packages/dockview-core/src/__tests__/{groupview => dockview/components}/panel/content.spec.ts (88%) rename packages/dockview-core/src/__tests__/{groupview => dockview/components}/tab.spec.ts (95%) rename packages/dockview-core/src/__tests__/{groupview => dockview/components}/titlebar/tabsContainer.spec.ts (68%) rename packages/dockview-core/src/__tests__/{groupview => dockview}/dockviewGroupPanelModel.spec.ts (100%) rename packages/dockview/src/__tests__/dockview/{groupControlsRenderer.spec.ts => headerActionsRenderer.spec.ts} (88%) rename packages/dockview/src/dockview/{groupControlsRenderer.ts => headerActionsRenderer.ts} (90%) diff --git a/packages/dockview-core/src/__tests__/groupview/panel/content.spec.ts b/packages/dockview-core/src/__tests__/dockview/components/panel/content.spec.ts similarity index 88% rename from packages/dockview-core/src/__tests__/groupview/panel/content.spec.ts rename to packages/dockview-core/src/__tests__/dockview/components/panel/content.spec.ts index 58de628a3..34ba707a3 100644 --- a/packages/dockview-core/src/__tests__/groupview/panel/content.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/components/panel/content.spec.ts @@ -1,14 +1,14 @@ import { fireEvent } from '@testing-library/dom'; -import { Emitter, Event } from '../../../events'; -import { ContentContainer } from '../../../dockview/components/panel/content'; +import { Emitter, Event } from '../../../../events'; +import { ContentContainer } from '../../../../dockview/components/panel/content'; import { GroupPanelContentPartInitParameters, IContentRenderer, -} from '../../../dockview/types'; -import { CompositeDisposable } from '../../../lifecycle'; -import { PanelUpdateEvent } from '../../../panel/types'; -import { IDockviewPanel } from '../../../dockview/dockviewPanel'; -import { IDockviewPanelModel } from '../../../dockview/dockviewPanelModel'; +} from '../../../../dockview/types'; +import { CompositeDisposable } from '../../../../lifecycle'; +import { PanelUpdateEvent } from '../../../../panel/types'; +import { IDockviewPanel } from '../../../../dockview/dockviewPanel'; +import { IDockviewPanelModel } from '../../../../dockview/dockviewPanelModel'; class TestContentRenderer extends CompositeDisposable diff --git a/packages/dockview-core/src/__tests__/groupview/tab.spec.ts b/packages/dockview-core/src/__tests__/dockview/components/tab.spec.ts similarity index 95% rename from packages/dockview-core/src/__tests__/groupview/tab.spec.ts rename to packages/dockview-core/src/__tests__/dockview/components/tab.spec.ts index 012732e73..2aa671d00 100644 --- a/packages/dockview-core/src/__tests__/groupview/tab.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/components/tab.spec.ts @@ -1,9 +1,9 @@ import { fireEvent } from '@testing-library/dom'; -import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer'; -import { DockviewComponent } from '../../dockview/dockviewComponent'; -import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel'; -import { DockviewGroupPanelModel } from '../../dockview/dockviewGroupPanelModel'; -import { Tab } from '../../dockview/components/tab/tab'; +import { LocalSelectionTransfer, PanelTransfer } from '../../../dnd/dataTransfer'; +import { DockviewComponent } from '../../../dockview/dockviewComponent'; +import { DockviewGroupPanel } from '../../../dockview/dockviewGroupPanel'; +import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel'; +import { Tab } from '../../../dockview/components/tab/tab'; describe('tab', () => { test('that empty tab has inactive-tab class', () => { diff --git a/packages/dockview-core/src/__tests__/groupview/titlebar/tabsContainer.spec.ts b/packages/dockview-core/src/__tests__/dockview/components/titlebar/tabsContainer.spec.ts similarity index 68% rename from packages/dockview-core/src/__tests__/groupview/titlebar/tabsContainer.spec.ts rename to packages/dockview-core/src/__tests__/dockview/components/titlebar/tabsContainer.spec.ts index 0bcc96f50..77efaca4c 100644 --- a/packages/dockview-core/src/__tests__/groupview/titlebar/tabsContainer.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/components/titlebar/tabsContainer.spec.ts @@ -1,13 +1,13 @@ -import { DockviewComponent } from '../../../dockview/dockviewComponent'; -import { TabsContainer } from '../../../dockview/components/titlebar/tabsContainer'; -import { fireEvent } from '@testing-library/dom'; import { LocalSelectionTransfer, PanelTransfer, -} from '../../../dnd/dataTransfer'; -import { TestPanel } from '../dockviewGroupPanelModel.spec'; -import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel'; -import { DockviewGroupPanel } from '../../../dockview/dockviewGroupPanel'; +} from '../../../../dnd/dataTransfer'; +import { TabsContainer } from '../../../../dockview/components/titlebar/tabsContainer'; +import { DockviewComponent } from '../../../../dockview/dockviewComponent'; +import { DockviewGroupPanel } from '../../../../dockview/dockviewGroupPanel'; +import { DockviewGroupPanelModel } from '../../../../dockview/dockviewGroupPanelModel'; +import { fireEvent } from '@testing-library/dom'; +import { TestPanel } from '../../dockviewGroupPanelModel.spec'; describe('tabsContainer', () => { 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 ).toBe(0); }); + + test('left actions', () => { + const accessorMock = jest.fn(() => { + return (>{ + options: {}, + onDidAddPanel: jest.fn(), + onDidRemovePanel: jest.fn(), + }) as DockviewComponent; + }); + + const groupPanelMock = jest.fn(() => { + return (>{}) 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(() => { + return (>{ + options: {}, + onDidAddPanel: jest.fn(), + onDidRemovePanel: jest.fn(), + }) as DockviewComponent; + }); + + const groupPanelMock = jest.fn(() => { + return (>{}) 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); + }); }); diff --git a/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts similarity index 100% rename from packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts rename to packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts diff --git a/packages/dockview-core/src/dockview/components/tab/defaultTab.scss b/packages/dockview-core/src/dockview/components/tab/defaultTab.scss index 81208377e..d820534b8 100644 --- a/packages/dockview-core/src/dockview/components/tab/defaultTab.scss +++ b/packages/dockview-core/src/dockview/components/tab/defaultTab.scss @@ -46,5 +46,30 @@ padding: 0px 8px; flex-grow: 1; } + + .action-container { + text-align: right; + display: flex; + + .tab-list { + display: flex; + padding: 0px; + margin: 0px; + justify-content: flex-end; + + .tab-action { + padding: 4px; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + + &:hover { + border-radius: 2px; + background-color: var(--dv-icon-hover-background-color); + } + } + } + } } } diff --git a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts index 6e31cdce3..d6e4bf823 100644 --- a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts +++ b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts @@ -18,7 +18,7 @@ import { import { DockviewDropTargets, IWatermarkRenderer } from './types'; import { DockviewGroupPanel } from './dockviewGroupPanel'; import { IDockviewPanel } from './dockviewPanel'; -import { IGroupControlRenderer } from './options'; +import { IHeaderActionsRenderer } from './options'; export interface DndService { canDisplayOverlay( @@ -137,8 +137,8 @@ export class DockviewGroupPanelModel private watermark?: IWatermarkRenderer; private _isGroupActive = false; private _locked = false; - private _control: IGroupControlRenderer | undefined; - private _lhs: IGroupControlRenderer | undefined; + private _rightHeaderActions: IHeaderActionsRenderer | undefined; + private _leftHeaderActions: IHeaderActionsRenderer | undefined; private mostRecentlyUsed: IDockviewPanel[] = []; @@ -321,28 +321,33 @@ export class DockviewGroupPanelModel this.updateContainer(); if (this.accessor.options.createRightHeaderActionsElement) { - this._control = + this._rightHeaderActions = this.accessor.options.createRightHeaderActionsElement( this.groupPanel ); - this.addDisposables(this._control); - this._control.init({ + this.addDisposables(this._rightHeaderActions); + this._rightHeaderActions.init({ containerApi: new DockviewApi(this.accessor), api: this.groupPanel.api, }); - this.tabsContainer.setRightActionsElement(this._control.element); + this.tabsContainer.setRightActionsElement( + this._rightHeaderActions.element + ); } if (this.accessor.options.createLeftHeaderActionsElement) { - this._lhs = this.accessor.options.createLeftHeaderActionsElement( - this.groupPanel - ); - this.addDisposables(this._lhs); - this._lhs.init({ + 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._lhs.element); + this.tabsContainer.setLeftActionsElement( + this._leftHeaderActions.element + ); } } diff --git a/packages/dockview-core/src/dockview/options.ts b/packages/dockview-core/src/dockview/options.ts index bab295646..147cc5e48 100644 --- a/packages/dockview-core/src/dockview/options.ts +++ b/packages/dockview-core/src/dockview/options.ts @@ -19,7 +19,7 @@ import { Position } from '../dnd/droptarget'; import { IDockviewPanel } from './dockviewPanel'; import { FrameworkFactory } from '../panel/componentFactory'; -export interface IGroupControlRenderer extends IDisposable { +export interface IHeaderActionsRenderer extends IDisposable { readonly element: HTMLElement; init(params: { containerApi: DockviewApi; @@ -81,10 +81,10 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions { showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean; createRightHeaderActionsElement?: ( group: DockviewGroupPanel - ) => IGroupControlRenderer; + ) => IHeaderActionsRenderer; createLeftHeaderActionsElement?: ( group: DockviewGroupPanel - ) => IGroupControlRenderer; + ) => IHeaderActionsRenderer; singleTabMode?: 'fullwidth' | 'default'; parentElement?: HTMLElement; } diff --git a/packages/dockview/src/__tests__/dockview/groupControlsRenderer.spec.ts b/packages/dockview/src/__tests__/dockview/headerActionsRenderer.spec.ts similarity index 88% rename from packages/dockview/src/__tests__/dockview/groupControlsRenderer.spec.ts rename to packages/dockview/src/__tests__/dockview/headerActionsRenderer.spec.ts index dc645d007..ec6015403 100644 --- a/packages/dockview/src/__tests__/dockview/groupControlsRenderer.spec.ts +++ b/packages/dockview/src/__tests__/dockview/headerActionsRenderer.spec.ts @@ -3,9 +3,9 @@ import { DockviewGroupPanelApi, DockviewGroupPanelModel, } from 'dockview-core'; -import { ReactGroupControlsRendererPart } from '../../dockview/groupControlsRenderer'; +import { ReactHeaderActionsRendererPart } from '../../dockview/headerActionsRenderer'; -describe('groupControlsRenderer', () => { +describe('headerActionsRenderer', () => { test('#1', () => { const groupviewMock = jest.fn, []>( () => { @@ -28,7 +28,7 @@ describe('groupControlsRenderer', () => { const groupPanel = new groupPanelMock() as DockviewGroupPanel; - const cut = new ReactGroupControlsRendererPart( + const cut = new ReactHeaderActionsRendererPart( jest.fn(), { addPortal: jest.fn(), diff --git a/packages/dockview/src/__tests__/react/dockview/groupControlsRenderer.spec.ts b/packages/dockview/src/__tests__/react/dockview/groupControlsRenderer.spec.ts index d3a056372..85d55e8ae 100644 --- a/packages/dockview/src/__tests__/react/dockview/groupControlsRenderer.spec.ts +++ b/packages/dockview/src/__tests__/react/dockview/groupControlsRenderer.spec.ts @@ -3,9 +3,9 @@ import { DockviewGroupPanelApi, DockviewGroupPanelModel, } from 'dockview-core'; -import { ReactGroupControlsRendererPart } from '../../../dockview/groupControlsRenderer'; +import { ReactHeaderActionsRendererPart } from '../../../dockview/headerActionsRenderer'; -describe('groupControlsRenderer', () => { +describe('headerActionsRenderer', () => { test('#1', () => { const groupviewMock = jest.fn, []>( () => { @@ -28,7 +28,7 @@ describe('groupControlsRenderer', () => { const groupPanel = new groupPanelMock() as DockviewGroupPanel; - const cut = new ReactGroupControlsRendererPart( + const cut = new ReactHeaderActionsRendererPart( jest.fn(), { addPortal: jest.fn(), diff --git a/packages/dockview/src/dockview/dockview.tsx b/packages/dockview/src/dockview/dockview.tsx index e347a8aba..b741a61d3 100644 --- a/packages/dockview/src/dockview/dockview.tsx +++ b/packages/dockview/src/dockview/dockview.tsx @@ -4,12 +4,12 @@ import { DockviewDropEvent, DockviewDndOverlayEvent, GroupPanelFrameworkComponentFactory, - IGroupControlRenderer, DockviewPanelApi, DockviewApi, IContentRenderer, ITabRenderer, DockviewGroupPanel, + IHeaderActionsRenderer, } from 'dockview-core'; import { ReactPanelContentPart } from './reactContentPart'; import { ReactPanelHeaderPart } from './reactHeaderPart'; @@ -18,17 +18,17 @@ import { ReactPortalStore, usePortalsLifecycle } from '../react'; import { IWatermarkPanelProps, ReactWatermarkPart } from './reactWatermarkPart'; import { PanelCollection, PanelParameters } from '../types'; import { - IDockviewGroupControlProps, - ReactGroupControlsRendererPart, -} from './groupControlsRenderer'; + IDockviewHeaderActionsProps, + ReactHeaderActionsRendererPart, +} from './headerActionsRenderer'; function createGroupControlElement( - component: React.FunctionComponent | undefined, + component: React.FunctionComponent | undefined, store: ReactPortalStore -): ((groupPanel: DockviewGroupPanel) => IGroupControlRenderer) | undefined { +): ((groupPanel: DockviewGroupPanel) => IHeaderActionsRenderer) | undefined { return component ? (groupPanel: DockviewGroupPanel) => { - return new ReactGroupControlsRendererPart( + return new ReactHeaderActionsRendererPart( component, store, groupPanel @@ -65,8 +65,8 @@ export interface IDockviewReactProps { className?: string; disableAutoResizing?: boolean; defaultTabComponent?: React.FunctionComponent; - rightHeaderActionsComponent?: React.FunctionComponent; - leftHeaderActionsComponent?: React.FunctionComponent; + rightHeaderActionsComponent?: React.FunctionComponent; + leftHeaderActionsComponent?: React.FunctionComponent; singleTabMode?: 'fullwidth' | 'default'; } diff --git a/packages/dockview/src/dockview/groupControlsRenderer.ts b/packages/dockview/src/dockview/headerActionsRenderer.ts similarity index 90% rename from packages/dockview/src/dockview/groupControlsRenderer.ts rename to packages/dockview/src/dockview/headerActionsRenderer.ts index 9890d6ece..603d09061 100644 --- a/packages/dockview/src/dockview/groupControlsRenderer.ts +++ b/packages/dockview/src/dockview/headerActionsRenderer.ts @@ -10,24 +10,25 @@ import { PanelUpdateEvent, } from 'dockview-core'; -export interface IDockviewGroupControlProps { +export interface IDockviewHeaderActionsProps { api: DockviewGroupPanelApi; containerApi: DockviewApi; panels: IDockviewPanel[]; activePanel: IDockviewPanel | undefined; isGroupActive: boolean; + group: DockviewGroupPanel; } -export class ReactGroupControlsRendererPart { +export class ReactHeaderActionsRendererPart { private mutableDisposable = new DockviewMutableDisposable(); private _element: HTMLElement; - private _part?: ReactPart; + private _part?: ReactPart; get element(): HTMLElement { return this._element; } - get part(): ReactPart | undefined { + get part(): ReactPart | undefined { return this._part; } @@ -36,7 +37,7 @@ export class ReactGroupControlsRendererPart { } constructor( - private readonly component: React.FunctionComponent, + private readonly component: React.FunctionComponent, private readonly reactPortalStore: ReactPortalStore, private readonly _group: DockviewGroupPanel ) { @@ -77,6 +78,7 @@ export class ReactGroupControlsRendererPart { panels: this._group.model.panels, activePanel: this._group.model.activePanel, isGroupActive: this._group.api.isActive, + group: this._group, } ); } diff --git a/packages/dockview/src/index.ts b/packages/dockview/src/index.ts index acc7fec37..0f5e688b7 100644 --- a/packages/dockview/src/index.ts +++ b/packages/dockview/src/index.ts @@ -4,7 +4,7 @@ export * from './dockview/dockview'; export * from './dockview/defaultTab'; export * from './splitview/splitview'; export * from './gridview/gridview'; -export { IDockviewGroupControlProps } from './dockview/groupControlsRenderer'; +export { IDockviewHeaderActionsProps } from './dockview/headerActionsRenderer'; export { IWatermarkPanelProps } from './dockview/reactWatermarkPart'; export * from './paneview/paneview'; export * from './types'; diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index 9cc2a0cb0..11e6ba866 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -649,11 +649,11 @@ panel.group.locked = true; ### Group Controls Panel -`DockviewReact` accepts `leftHeaderActionsComponent` and `rightHeaderActionsComponent` which expect a React component with props `IDockviewGroupControlProps`. +`DockviewReact` accepts `leftHeaderActionsComponent` and `rightHeaderActionsComponent` which expect a React component with props `IDockviewHeaderActionsProps`. These controls are rendered of the left and right side of the space to the right of the tabs in the header bar. ```tsx -const Component: React.FunctionComponent = () => { +const Component: React.FunctionComponent = () => { return
{'...'}
; }; @@ -664,7 +664,7 @@ As a simple example the below uses the `groupControlComponent` to render a small is active and which panel is active in that group. ```tsx -const RightHeaderActionsComponent = (props: IDockviewGroupControlProps) => { +const RightHeaderActionsComponent = (props: IDockviewHeaderActionsProps) => { const isGroupActive = props.isGroupActive; const activePanel = props.activePanel; diff --git a/packages/docs/sandboxes/demo-dockview/src/app.tsx b/packages/docs/sandboxes/demo-dockview/src/app.tsx index 654cbd3f5..5ed911053 100644 --- a/packages/docs/sandboxes/demo-dockview/src/app.tsx +++ b/packages/docs/sandboxes/demo-dockview/src/app.tsx @@ -4,7 +4,7 @@ import { DockviewReadyEvent, IDockviewPanelHeaderProps, IDockviewPanelProps, - IDockviewGroupControlProps, + IDockviewHeaderActionsProps, } from 'dockview'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; @@ -134,7 +134,7 @@ const groupControlsComponents = { }, }; -const GroupControls = (props: IDockviewGroupControlProps) => { +const RightControls = (props: IDockviewHeaderActionsProps) => { const Component = React.useMemo(() => { if (!props.isGroupActive || !props.activePanel) { 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 ( +
+ +
+ ); +}; + const DockviewDemo = () => { const onReady = (event: DockviewReadyEvent) => { event.api.addPanel({ @@ -196,8 +226,6 @@ const DockviewDemo = () => { title: 'Panel 6', position: { referencePanel: 'panel_4', direction: 'below' }, }); - panel6.group.locked = true; - panel6.group.header.hidden = true; event.api.addPanel({ id: 'panel_7', component: 'default', @@ -211,8 +239,6 @@ const DockviewDemo = () => { position: { referencePanel: 'panel_7', direction: 'within' }, }); - event.api.addGroup(); - event.api.getPanel('panel_1')!.api.setActive(); }; @@ -220,7 +246,8 @@ const DockviewDemo = () => { diff --git a/packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx b/packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx index d49423905..059dd9448 100644 --- a/packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx +++ b/packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx @@ -1,7 +1,7 @@ import { DockviewReact, DockviewReadyEvent, - IDockviewGroupControlProps, + IDockviewHeaderActionsProps, IDockviewPanelProps, } from 'dockview'; import * as React from 'react'; @@ -26,7 +26,7 @@ const components = { }, }; -const RightHeaderActions = (props: IDockviewGroupControlProps) => { +const RightHeaderActions = (props: IDockviewHeaderActionsProps) => { const isGroupActive = props.isGroupActive; return ( @@ -43,7 +43,7 @@ const RightHeaderActions = (props: IDockviewGroupControlProps) => { ); }; -const LeftHeaderActions = (props: IDockviewGroupControlProps) => { +const LeftHeaderActions = (props: IDockviewHeaderActionsProps) => { const activePanel = props.activePanel; return ( From 105017245bdf36e56dda5d72b4f634c69bc8c59a Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:15:41 +0100 Subject: [PATCH 3/3] feat: left header actions --- packages/docs/docs/components/dockview.mdx | 2 +- .../package.json | 4 ++-- .../public/index.html | 0 .../src/app.scss | 0 .../src/app.tsx | 0 .../src/index.tsx | 0 .../src/styles.css | 0 .../tsconfig.json | 0 .../docs/versioned_docs/version-1.7.3/components/dockview.mdx | 2 +- 9 files changed, 4 insertions(+), 4 deletions(-) rename packages/docs/sandboxes/{groupcontrol-dockview => headeractions-dockview}/package.json (94%) rename packages/docs/sandboxes/{groupcontrol-dockview => headeractions-dockview}/public/index.html (100%) rename packages/docs/sandboxes/{groupcontrol-dockview => headeractions-dockview}/src/app.scss (100%) rename packages/docs/sandboxes/{groupcontrol-dockview => headeractions-dockview}/src/app.tsx (100%) rename packages/docs/sandboxes/{groupcontrol-dockview => headeractions-dockview}/src/index.tsx (100%) rename packages/docs/sandboxes/{groupcontrol-dockview => headeractions-dockview}/src/styles.css (100%) rename packages/docs/sandboxes/{groupcontrol-dockview => headeractions-dockview}/tsconfig.json (100%) diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index 11e6ba866..377bb7c03 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -18,7 +18,7 @@ import DockviewConstraints from '@site/sandboxes/constraints-dockview/src/app'; import DndDockview from '@site/sandboxes/dnd-dockview/src/app'; import NestedDockview from '@site/sandboxes/nested-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 DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app'; import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app'; diff --git a/packages/docs/sandboxes/groupcontrol-dockview/package.json b/packages/docs/sandboxes/headeractions-dockview/package.json similarity index 94% rename from packages/docs/sandboxes/groupcontrol-dockview/package.json rename to packages/docs/sandboxes/headeractions-dockview/package.json index 7c88c11f1..27f907944 100644 --- a/packages/docs/sandboxes/groupcontrol-dockview/package.json +++ b/packages/docs/sandboxes/headeractions-dockview/package.json @@ -1,5 +1,5 @@ { - "name": "groupcontrol-dockview", + "name": "headeractions-dockview", "description": "", "keywords": [ "dockview" @@ -29,4 +29,4 @@ "not ie <= 11", "not op_mini all" ] -} \ No newline at end of file +} diff --git a/packages/docs/sandboxes/groupcontrol-dockview/public/index.html b/packages/docs/sandboxes/headeractions-dockview/public/index.html similarity index 100% rename from packages/docs/sandboxes/groupcontrol-dockview/public/index.html rename to packages/docs/sandboxes/headeractions-dockview/public/index.html diff --git a/packages/docs/sandboxes/groupcontrol-dockview/src/app.scss b/packages/docs/sandboxes/headeractions-dockview/src/app.scss similarity index 100% rename from packages/docs/sandboxes/groupcontrol-dockview/src/app.scss rename to packages/docs/sandboxes/headeractions-dockview/src/app.scss diff --git a/packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx b/packages/docs/sandboxes/headeractions-dockview/src/app.tsx similarity index 100% rename from packages/docs/sandboxes/groupcontrol-dockview/src/app.tsx rename to packages/docs/sandboxes/headeractions-dockview/src/app.tsx diff --git a/packages/docs/sandboxes/groupcontrol-dockview/src/index.tsx b/packages/docs/sandboxes/headeractions-dockview/src/index.tsx similarity index 100% rename from packages/docs/sandboxes/groupcontrol-dockview/src/index.tsx rename to packages/docs/sandboxes/headeractions-dockview/src/index.tsx diff --git a/packages/docs/sandboxes/groupcontrol-dockview/src/styles.css b/packages/docs/sandboxes/headeractions-dockview/src/styles.css similarity index 100% rename from packages/docs/sandboxes/groupcontrol-dockview/src/styles.css rename to packages/docs/sandboxes/headeractions-dockview/src/styles.css diff --git a/packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json b/packages/docs/sandboxes/headeractions-dockview/tsconfig.json similarity index 100% rename from packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json rename to packages/docs/sandboxes/headeractions-dockview/tsconfig.json diff --git a/packages/docs/versioned_docs/version-1.7.3/components/dockview.mdx b/packages/docs/versioned_docs/version-1.7.3/components/dockview.mdx index 835dee189..e1f7a19f2 100644 --- a/packages/docs/versioned_docs/version-1.7.3/components/dockview.mdx +++ b/packages/docs/versioned_docs/version-1.7.3/components/dockview.mdx @@ -18,7 +18,7 @@ import DockviewConstraints from '@site/sandboxes/constraints-dockview/src/app'; import DndDockview from '@site/sandboxes/dnd-dockview/src/app'; import NestedDockview from '@site/sandboxes/nested-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 DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app'; import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app';