feat: add left header actions

This commit is contained in:
mathuo 2023-06-03 20:18:42 +01:00
parent 6434e68b5b
commit 13d3db605b
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
8 changed files with 102 additions and 69 deletions

View File

@ -46,30 +46,5 @@
padding: 0px 8px; padding: 0px 8px;
flex-grow: 1; 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);
}
}
}
}
} }
} }

View File

@ -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,

View File

@ -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 {

View File

@ -138,6 +138,7 @@ export class DockviewGroupPanelModel
private _isGroupActive = false; private _isGroupActive = false;
private _locked = false; private _locked = false;
private _control: IGroupControlRenderer | undefined; private _control: IGroupControlRenderer | undefined;
private _lhs: IGroupControlRenderer | undefined;
private mostRecentlyUsed: IDockviewPanel[] = []; private mostRecentlyUsed: IDockviewPanel[] = [];
@ -319,8 +320,9 @@ 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._control =
this.accessor.options.createRightHeaderActionsElement(
this.groupPanel this.groupPanel
); );
this.addDisposables(this._control); this.addDisposables(this._control);
@ -328,7 +330,19 @@ export class DockviewGroupPanelModel
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._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 { updateActions(element: HTMLElement | undefined): void {
this.tabsContainer.setActionElement(element); this.tabsContainer.setRightActionsElement(element);
} }
public setActive( public setActive(

View File

@ -79,7 +79,10 @@ 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
) => IGroupControlRenderer;
createLeftHeaderActionsElement?: (
group: DockviewGroupPanel group: DockviewGroupPanel
) => IGroupControlRenderer; ) => IGroupControlRenderer;
singleTabMode?: 'fullwidth' | 'default'; singleTabMode?: 'fullwidth' | 'default';

View File

@ -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<IDockviewGroupControlProps>;
leftHeaderActionsComponent?: React.FunctionComponent<IDockviewGroupControlProps>;
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

View File

@ -59,7 +59,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 | | |
@ -70,7 +70,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

View File

@ -26,7 +26,7 @@ const components = {
}, },
}; };
const GroupControlComponent = (props: IDockviewGroupControlProps) => { const RightHeaderActions = (props: IDockviewGroupControlProps) => {
const isGroupActive = props.isGroupActive; const isGroupActive = props.isGroupActive;
const activePanel = props.activePanel; const activePanel = props.activePanel;
@ -87,7 +87,7 @@ const DockviewGroupControl = () => {
<DockviewReact <DockviewReact
onReady={onReady} onReady={onReady}
components={components} components={components}
groupControlComponent={GroupControlComponent} rightHeaderActionsComponent={RightHeaderActions}
className="dockview-theme-abyss" className="dockview-theme-abyss"
/> />
); );