diff --git a/packages/dockview/src/dnd/droptarget.ts b/packages/dockview/src/dnd/droptarget.ts index a136c978f..50e6612fa 100644 --- a/packages/dockview/src/dnd/droptarget.ts +++ b/packages/dockview/src/dnd/droptarget.ts @@ -26,7 +26,9 @@ function isBooleanValue( return typeof canDisplayOverlay === 'boolean'; } -export type CanDisplayOverlay = boolean | ((dragEvent: DragEvent) => boolean); +export type CanDisplayOverlay = + | boolean + | ((dragEvent: DragEvent, state: Quadrant | null) => boolean); export class Droptarget extends CompositeDisposable { private target: HTMLElement | undefined; @@ -62,11 +64,29 @@ export class Droptarget extends CompositeDisposable { new DragAndDropObserver(this.element, { onDragEnter: () => undefined, onDragOver: (e) => { + const width = this.element.clientWidth; + const height = this.element.clientHeight; + + if (width === 0 || height === 0) { + return; // avoid div!0 + } + + const x = e.offsetX; + const y = e.offsetY; + const xp = (100 * x) / width; + const yp = (100 * y) / height; + + const quadrant = this.calculateQuadrant( + this.options.validOverlays, + xp, + yp + ); + if (isBooleanValue(this.options.canDisplayOverlay)) { if (!this.options.canDisplayOverlay) { return; } - } else if (!this.options.canDisplayOverlay(e)) { + } else if (!this.options.canDisplayOverlay(e, quadrant)) { return; } @@ -90,24 +110,6 @@ export class Droptarget extends CompositeDisposable { return; } - const width = this.target.clientWidth; - const height = this.target.clientHeight; - - if (width === 0 || height === 0) { - return; // avoid div!0 - } - - const x = e.offsetX; - const y = e.offsetY; - const xp = (100 * x) / width; - const yp = (100 * y) / height; - - const quadrant = this.calculateQuadrant( - this.options.validOverlays, - xp, - yp - ); - const isSmallX = width < 100; const isSmallY = height < 100; diff --git a/packages/dockview/src/dockview/deserializer.ts b/packages/dockview/src/dockview/deserializer.ts index fffe0eece..cfd05883c 100644 --- a/packages/dockview/src/dockview/deserializer.ts +++ b/packages/dockview/src/dockview/deserializer.ts @@ -4,6 +4,7 @@ import { IViewDeserializer, } from '../gridview/gridview'; import { GroupviewPanelState, IGroupPanel } from '../groupview/groupPanel'; +import { GroupPanelViewState } from '../groupview/groupview'; import { DockviewComponent } from './dockviewComponent'; export interface IPanelDeserializer { @@ -18,9 +19,10 @@ export class DefaultDeserializer implements IViewDeserializer { } ) {} - public fromJSON(node: ISerializedLeafNode): IGridView { - const children = node.data.views; - const active = node.data.activeView; + public fromJSON(node: ISerializedLeafNode): IGridView { + const data = node.data; + const children = data.views; + const active = data.activeView; const panels: IGroupPanel[] = []; @@ -33,7 +35,18 @@ export class DefaultDeserializer implements IViewDeserializer { return this.layout.createGroup({ panels, activePanel: panels.find((p) => p.id === active), - id: node.data.id, + id: data.id, + locked: !!data.locked, + headerHidden: !!data.headerHidden, }); } } + +/** + * isGroup + * + * + * panel.group.locked = true + * panel.group.header.hiddden = true + * + */ diff --git a/packages/dockview/src/dockview/dockviewComponent.ts b/packages/dockview/src/dockview/dockviewComponent.ts index 66cd71801..41d83a58b 100644 --- a/packages/dockview/src/dockview/dockviewComponent.ts +++ b/packages/dockview/src/dockview/dockviewComponent.ts @@ -188,7 +188,7 @@ export class DockviewComponent set tabHeight(height: number | undefined) { this.options.tabHeight = height; this._groups.forEach((value) => { - value.value.model.tabHeight = height; + value.value.model.header.height = height; }); } @@ -704,7 +704,7 @@ export class DockviewComponent view.initialize(); if (typeof this.options.tabHeight === 'number') { - view.model.tabHeight = this.options.tabHeight; + view.model.header.height = this.options.tabHeight; } return view; diff --git a/packages/dockview/src/dockview/dockviewGroupPanel.ts b/packages/dockview/src/dockview/dockviewGroupPanel.ts index 4e6366056..35049f5d2 100644 --- a/packages/dockview/src/dockview/dockviewGroupPanel.ts +++ b/packages/dockview/src/dockview/dockviewGroupPanel.ts @@ -169,7 +169,7 @@ export class DockviewGroupPanel // the obtain the correct dimensions of the content panel we must deduct the tab height this.api._onDidPanelDimensionChange.fire({ width, - height: height - (this.group?.model.tabHeight || 0), + height: height - (this.group?.model.header.height || 0), }); this.view?.layout(width, height); diff --git a/packages/dockview/src/gridview/gridview.ts b/packages/dockview/src/gridview/gridview.ts index cbbb504a7..8f7bfb5a3 100644 --- a/packages/dockview/src/gridview/gridview.ts +++ b/packages/dockview/src/gridview/gridview.ts @@ -241,9 +241,9 @@ const serializeBranchNode = ( }; }; -export interface ISerializedLeafNode { +export interface ISerializedLeafNode { type: 'leaf'; - data: any; + data: T; size: number; visible?: boolean; } diff --git a/packages/dockview/src/groupview/groupview.ts b/packages/dockview/src/groupview/groupview.ts index e78531e2c..eb7b9a32a 100644 --- a/packages/dockview/src/groupview/groupview.ts +++ b/packages/dockview/src/groupview/groupview.ts @@ -48,36 +48,47 @@ interface GroupMoveEvent { index?: number; } -export interface GroupOptions { +interface CoreGroupOptions { + locked?: boolean; + headerHidden?: boolean; +} + +export interface GroupOptions extends CoreGroupOptions { readonly panels?: IGroupPanel[]; readonly activePanel?: IGroupPanel; readonly id?: string; tabHeight?: number; } -export interface GroupviewChangeEvent { - readonly kind: GroupChangeKind2; - readonly panel?: IGroupPanel; -} - -export interface GroupPanelViewState { +export interface GroupPanelViewState extends CoreGroupOptions { views: string[]; activeView?: string; id: string; } +export interface GroupviewChangeEvent { + readonly kind: GroupChangeKind2; + readonly panel?: IGroupPanel; +} + export interface GroupviewDropEvent { nativeEvent: DragEvent; position: Position; index?: number; } +export interface IHeader { + hidden: boolean; + height: number | undefined; +} + export interface IGroupview extends IDisposable, IGridPanelView { readonly isActive: boolean; readonly size: number; readonly panels: IGroupPanel[]; - readonly tabHeight: number | undefined; readonly activePanel: IGroupPanel | undefined; + locked: boolean; + readonly header: IHeader; readonly onDidDrop: Event; // state isPanelActive: (panel: IGroupPanel) => boolean; @@ -111,6 +122,7 @@ export class Groupview extends CompositeDisposable implements IGroupview { private _activePanel?: IGroupPanel; private watermark?: IWatermarkRenderer; private _isGroupActive = false; + private _locked = false; private mostRecentlyUsed: IGroupPanel[] = []; @@ -141,13 +153,12 @@ export class Groupview extends CompositeDisposable implements IGroupview { return this._activePanel; } - get tabHeight(): number | undefined { - return this.tabsContainer.height; + get locked(): boolean { + return this._locked; } - set tabHeight(height: number | undefined) { - this.tabsContainer.height = height; - this.layout(this._width, this._height); + set locked(value: boolean) { + this._locked = value; } get isActive(): boolean { @@ -188,6 +199,10 @@ export class Groupview extends CompositeDisposable implements IGroupview { ); } + get header(): IHeader { + return this.tabsContainer; + } + constructor( private readonly container: HTMLElement, private accessor: IDockviewComponent, @@ -210,9 +225,14 @@ export class Groupview extends CompositeDisposable implements IGroupview { tabHeight: options.tabHeight, }); this.contentContainer = new ContentContainer(); + this.dropTarget = new Droptarget(this.contentContainer.element, { validOverlays: 'all', - canDisplayOverlay: (event) => { + canDisplayOverlay: (event, quadrant) => { + if (this.locked && !quadrant) { + return false; + } + const data = getPanelData(); if (data) { @@ -231,6 +251,9 @@ export class Groupview extends CompositeDisposable implements IGroupview { this.contentContainer.element ); + this.header.hidden = !!options.headerHidden; + this.locked = !!options.locked; + this.addDisposables( this._onMove, this._onDidGroupChange, @@ -281,11 +304,21 @@ export class Groupview extends CompositeDisposable implements IGroupview { } public toJSON(): GroupPanelViewState { - return { + const result: GroupPanelViewState = { views: this.tabsContainer.panels, activeView: this._activePanel?.id, id: this.id, }; + + if (this.locked) { + result.locked = true; + } + + if (this.header.hidden) { + result.headerHidden = true; + } + + return result; } public moveToNext(options?: { diff --git a/packages/dockview/src/groupview/groupviewPanel.ts b/packages/dockview/src/groupview/groupviewPanel.ts index 7000bf65b..73fa1b023 100644 --- a/packages/dockview/src/groupview/groupviewPanel.ts +++ b/packages/dockview/src/groupview/groupviewPanel.ts @@ -1,17 +1,17 @@ import { IFrameworkPart } from '../panel/types'; import { IDockviewComponent } from '../dockview/dockviewComponent'; import { GridviewPanelApiImpl } from '../api/gridviewPanelApi'; -import { Groupview, GroupOptions } from './groupview'; +import { Groupview, GroupOptions, IGroupview } from './groupview'; import { GridviewPanel, IGridviewPanel } from '../gridview/gridviewPanel'; export interface IGroupviewPanel extends IGridviewPanel { - model: Groupview; + model: IGroupview; } export class GroupviewPanel extends GridviewPanel implements IGroupviewPanel { private readonly _model: Groupview; - get model(): Groupview { + get model(): IGroupview { return this._model; } @@ -42,7 +42,7 @@ export class GroupviewPanel extends GridviewPanel implements IGroupviewPanel { } initialize() { - this.model.initialize(); + this._model.initialize(); } setActive(isActive: boolean): void { diff --git a/packages/dockview/src/groupview/titlebar/tabsContainer.ts b/packages/dockview/src/groupview/titlebar/tabsContainer.ts index ad76cb276..f5bf91315 100644 --- a/packages/dockview/src/groupview/titlebar/tabsContainer.ts +++ b/packages/dockview/src/groupview/titlebar/tabsContainer.ts @@ -33,6 +33,7 @@ export interface ITabsContainer extends IDisposable { closePanel: (panel: IGroupPanel) => void; openPanel: (panel: IGroupPanel, index?: number) => void; setActionElement(element: HTMLElement | undefined): void; + hidden: boolean; show(): void; hide(): void; } @@ -55,6 +56,7 @@ export class TabsContainer private actions: HTMLElement | undefined; private _height: number | undefined; + private _hidden = false; private readonly _onDrop = new Emitter(); readonly onDrop: Event = this._onDrop.event; @@ -85,12 +87,23 @@ export class TabsContainer } } - show() { - this.element.style.display = ''; + get hidden(): boolean { + return this._hidden; } - hide() { - this.element.style.display = 'none'; + set hidden(value: boolean) { + this._hidden = value; + this.element.style.display = value ? 'none' : ''; + } + + show(): void { + if (!this.hidden) { + this.element.style.display = ''; + } + } + + hide(): void { + this._element.style.display = 'none'; } setActionElement(element: HTMLElement | undefined): void {