From 88d602633511f36034a7add77b436041475611ed Mon Sep 17 00:00:00 2001 From: mathuo <{ID}+{username}@users.noreply.github.com> Date: Tue, 8 Jun 2021 22:29:44 +0100 Subject: [PATCH] refactor: cleaner code --- .../dockview/dockviewComponent.spec.ts | 42 ++-- .../src/__tests__/groupview/groupview.spec.ts | 46 ++--- packages/dockview/src/api/groupPanelApi.ts | 2 +- .../src/dockview/dockviewComponent.ts | 66 +++--- .../src/dockview/dockviewGroupPanel.ts | 10 +- .../dockview/src/gridview/basePanelView.ts | 9 +- .../dockview/src/gridview/gridviewPanel.ts | 7 +- packages/dockview/src/groupview/groupview.ts | 188 +++++++++--------- .../dockview/src/groupview/groupviewPanel.ts | 49 ++--- .../dockview/src/groupview/panel/content.ts | 81 +------- .../src/groupview/panel/hostedPanel.ts | 71 +++++++ .../src/groupview/titlebar/tabsContainer.ts | 10 +- packages/dockview/src/groupview/types.ts | 7 + .../dockview/src/react/dockview/dockview.tsx | 4 - 14 files changed, 295 insertions(+), 297 deletions(-) create mode 100644 packages/dockview/src/groupview/panel/hostedPanel.ts diff --git a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts index b5068e52a..5e00c6863 100644 --- a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts @@ -89,28 +89,28 @@ describe('dockviewComponent', () => { dockview.moveGroupOrPanel(group2, group1.id, 'panel3', Position.Center); expect(dockview.activeGroup).toBe(group2); - expect(dockview.activeGroup.group.activePanel).toBe(panel3); - expect(dockview.activeGroup.group.indexOf(panel3)).toBe(1); + expect(dockview.activeGroup.model.activePanel).toBe(panel3); + expect(dockview.activeGroup.model.indexOf(panel3)).toBe(1); dockview.moveToPrevious({ includePanel: true }); expect(dockview.activeGroup).toBe(group2); - expect(dockview.activeGroup.group.activePanel).toBe(panel1); + expect(dockview.activeGroup.model.activePanel).toBe(panel1); dockview.moveToNext({ includePanel: true }); expect(dockview.activeGroup).toBe(group2); - expect(dockview.activeGroup.group.activePanel).toBe(panel3); + expect(dockview.activeGroup.model.activePanel).toBe(panel3); dockview.moveToPrevious({ includePanel: false }); expect(dockview.activeGroup).toBe(group1); - expect(dockview.activeGroup.group.activePanel).toBe(panel4); + expect(dockview.activeGroup.model.activePanel).toBe(panel4); dockview.moveToPrevious({ includePanel: true }); expect(dockview.activeGroup).toBe(group1); - expect(dockview.activeGroup.group.activePanel).toBe(panel2); + expect(dockview.activeGroup.model.activePanel).toBe(panel2); dockview.moveToNext({ includePanel: false }); expect(dockview.activeGroup).toBe(group2); - expect(dockview.activeGroup.group.activePanel).toBe(panel3); + expect(dockview.activeGroup.model.activePanel).toBe(panel3); }); test('remove group', () => { @@ -142,8 +142,8 @@ describe('dockviewComponent', () => { expect(dockview.size).toBe(2); expect(dockview.totalPanels).toBe(4); - expect(panel1.group.group.size).toBe(2); - expect(panel2.group.group.size).toBe(2); + expect(panel1.group.model.size).toBe(2); + expect(panel2.group.model.size).toBe(2); dockview.removeGroup(panel1.group); @@ -263,13 +263,13 @@ describe('dockviewComponent', () => { const group = panel1.group; - expect(group.group.size).toBe(2); - expect(group.group.containsPanel(panel1)).toBeTruthy(); - expect(group.group.containsPanel(panel2)).toBeTruthy(); - expect(group.group.activePanel).toBe(panel2); + expect(group.model.size).toBe(2); + expect(group.model.containsPanel(panel1)).toBeTruthy(); + expect(group.model.containsPanel(panel2)).toBeTruthy(); + expect(group.model.activePanel).toBe(panel2); - expect(group.group.indexOf(panel1)).toBe(0); - expect(group.group.indexOf(panel2)).toBe(1); + expect(group.model.indexOf(panel1)).toBe(0); + expect(group.model.indexOf(panel2)).toBe(1); dockview.moveGroupOrPanel(group, group.id, 'panel1', Position.Right); @@ -277,12 +277,12 @@ describe('dockviewComponent', () => { expect(dockview.totalPanels).toBe(2); expect(panel1.group).not.toBe(panel2.group); - expect(panel1.group.group.size).toBe(1); - expect(panel2.group.group.size).toBe(1); - expect(panel1.group.group.containsPanel(panel1)).toBeTruthy(); - expect(panel2.group.group.containsPanel(panel2)).toBeTruthy(); - expect(panel1.group.group.activePanel).toBe(panel1); - expect(panel2.group.group.activePanel).toBe(panel2); + expect(panel1.group.model.size).toBe(1); + expect(panel2.group.model.size).toBe(1); + expect(panel1.group.model.containsPanel(panel1)).toBeTruthy(); + expect(panel2.group.model.containsPanel(panel2)).toBeTruthy(); + expect(panel1.group.model.activePanel).toBe(panel1); + expect(panel2.group.model.activePanel).toBe(panel2); await panel1.api.close(); diff --git a/packages/dockview/src/__tests__/groupview/groupview.spec.ts b/packages/dockview/src/__tests__/groupview/groupview.spec.ts index bfe6f6f95..9983d2229 100644 --- a/packages/dockview/src/__tests__/groupview/groupview.spec.ts +++ b/packages/dockview/src/__tests__/groupview/groupview.spec.ts @@ -235,7 +235,7 @@ describe('groupview', () => { activePanel: panel2, }); - expect(groupview2.group.activePanel).toBe(panel2); + expect(groupview2.model.activePanel).toBe(panel2); expect( groupview2.element.querySelector('.content-part-panel1') @@ -253,33 +253,33 @@ describe('groupview', () => { const panel2 = new TestPanel('panel2', jest.fn() as any); const panel3 = new TestPanel('panel3', jest.fn() as any); - groupview.group.openPanel(panel1); - groupview.group.openPanel(panel2); - groupview.group.openPanel(panel3); + groupview.model.openPanel(panel1); + groupview.model.openPanel(panel2); + groupview.model.openPanel(panel3); - groupview.group.openPanel(panel2); // set active + groupview.model.openPanel(panel2); // set active - groupview.group.moveToPrevious(); - expect(groupview.group.activePanel).toBe(panel1); + groupview.model.moveToPrevious(); + expect(groupview.model.activePanel).toBe(panel1); - groupview.group.moveToPrevious({ suppressRoll: true }); - expect(groupview.group.activePanel).toBe(panel1); + groupview.model.moveToPrevious({ suppressRoll: true }); + expect(groupview.model.activePanel).toBe(panel1); - groupview.group.moveToPrevious(); - expect(groupview.group.activePanel).toBe(panel3); + groupview.model.moveToPrevious(); + expect(groupview.model.activePanel).toBe(panel3); - groupview.group.moveToNext({ suppressRoll: true }); - expect(groupview.group.activePanel).toBe(panel3); + groupview.model.moveToNext({ suppressRoll: true }); + expect(groupview.model.activePanel).toBe(panel3); - groupview.group.moveToNext({ suppressRoll: false }); - expect(groupview.group.activePanel).toBe(panel1); + groupview.model.moveToNext({ suppressRoll: false }); + expect(groupview.model.activePanel).toBe(panel1); - groupview.group.moveToPrevious({ suppressRoll: false }); - expect(groupview.group.activePanel).toBe(panel3); + groupview.model.moveToPrevious({ suppressRoll: false }); + expect(groupview.model.activePanel).toBe(panel3); - groupview.group.moveToNext(); - groupview.group.moveToNext(); - expect(groupview.group.activePanel).toBe(panel2); + groupview.model.moveToNext(); + groupview.model.moveToNext(); + expect(groupview.model.activePanel).toBe(panel2); }); test('default', () => { @@ -298,12 +298,12 @@ describe('groupview', () => { const panel1 = new TestPanel('panel1', jest.fn() as any); const panel2 = new TestPanel('panel2', jest.fn() as any); - groupview.group.openPanel(panel1); - groupview.group.openPanel(panel2); + groupview.model.openPanel(panel1); + groupview.model.openPanel(panel2); const events: GroupDropEvent[] = []; - groupview.group.onDrop((event) => { + groupview.model.onDrop((event) => { events.push(event); }); diff --git a/packages/dockview/src/api/groupPanelApi.ts b/packages/dockview/src/api/groupPanelApi.ts index 3292c9849..9446aa345 100644 --- a/packages/dockview/src/api/groupPanelApi.ts +++ b/packages/dockview/src/api/groupPanelApi.ts @@ -98,7 +98,7 @@ export class DockviewPanelApiImpl if (!this.group) { throw new Error(`panel ${this.id} has no group`); } - return this.group.group.closePanel(this.panel); + return this.group.model.closePanel(this.panel); } public interceptOnCloseAction(interceptor: () => Promise) { diff --git a/packages/dockview/src/dockview/dockviewComponent.ts b/packages/dockview/src/dockview/dockviewComponent.ts index 5512d72c0..75eae4870 100644 --- a/packages/dockview/src/dockview/dockviewComponent.ts +++ b/packages/dockview/src/dockview/dockviewComponent.ts @@ -135,7 +135,8 @@ export interface LayoutDropEvent { export class DockviewComponent extends BaseGrid - implements IDockviewComponent { + implements IDockviewComponent +{ private readonly panels = new Map>(); private readonly dirtyPanels = new Set(); private readonly debouncedDeque = debounce( @@ -144,11 +145,11 @@ export class DockviewComponent ); // events private readonly _onTabInteractionEvent = new Emitter(); - readonly onTabInteractionEvent: Event = this - ._onTabInteractionEvent.event; + readonly onTabInteractionEvent: Event = + this._onTabInteractionEvent.event; private readonly _onTabContextMenu = new Emitter(); - readonly onTabContextMenu: Event = this - ._onTabContextMenu.event; + readonly onTabContextMenu: Event = + this._onTabContextMenu.event; // everything else private drag = new MutableDisposable(); private _deserializer: IPanelDeserializer | undefined; @@ -270,7 +271,7 @@ export class DockviewComponent const panel = this.panels.get(panelOptions.id)?.value; if (panel) { - this.drag.value = panel.group!.group.startActiveDrag(panel); + this.drag.value = panel.group!.model.startActiveDrag(panel); } const data = JSON.stringify({ @@ -309,7 +310,7 @@ export class DockviewComponent throw new Error(`Panel ${panel.id} has no associated group`); } this.doSetGroupActive(panel.group); - panel.group.group.openPanel(panel); + panel.group.model.openPanel(panel); } public moveToNext(options: MovementOptions = {}): void { @@ -322,12 +323,12 @@ export class DockviewComponent if (options.includePanel && options.group) { if ( - options.group.group.activePanel !== - options.group.group.panels[ - options.group.group.panels.length - 1 + options.group.model.activePanel !== + options.group.model.panels[ + options.group.model.panels.length - 1 ] ) { - options.group.group.moveToNext({ suppressRoll: true }); + options.group.model.moveToNext({ suppressRoll: true }); return; } } @@ -347,10 +348,10 @@ export class DockviewComponent if (options.includePanel && options.group) { if ( - options.group.group.activePanel !== - options.group.group.panels[0] + options.group.model.activePanel !== + options.group.model.panels[0] ) { - options.group.group.moveToPrevious({ suppressRoll: true }); + options.group.model.moveToPrevious({ suppressRoll: true }); return; } } @@ -511,7 +512,7 @@ export class DockviewComponent for (const entry of this.groups.entries()) { const [_, group] = entry; - const didCloseAll = await group.value.group.closeAllPanels(); + const didCloseAll = await group.value.model.closeAllPanels(); if (!didCloseAll) { return false; } @@ -523,7 +524,7 @@ export class DockviewComponent public setTabHeight(height: number | undefined): void { this.options.tabHeight = height; this.groups.forEach((value) => { - value.value.group.tabHeight = height; + value.value.model.tabHeight = height; }); } @@ -569,7 +570,7 @@ export class DockviewComponent if (referenceGroup) { const target = toTarget(options.position?.direction || 'within'); if (target === Position.Center) { - referenceGroup.group.openPanel(panel); + referenceGroup.model.openPanel(panel); } else { const location = getGridLocation(referenceGroup.element); const relativeLocation = getRelativeLocation( @@ -652,8 +653,9 @@ export class DockviewComponent const group = this.createGroup(); if (options) { - const referencePanel = this.panels.get(options.referencePanel) - ?.value; + const referencePanel = this.panels.get( + options.referencePanel + )?.value; if (!referencePanel) { throw new Error( @@ -684,9 +686,9 @@ export class DockviewComponent } public removeGroup(group: GroupviewPanel): void { - const panels = [...group.group.panels]; // reassign since group panels will mutate + const panels = [...group.model.panels]; // reassign since group panels will mutate panels.forEach((panel) => { - group.group.removePanel(panel); + group.model.removePanel(panel); this.unregisterPanel(panel); }); @@ -707,7 +709,7 @@ export class DockviewComponent group! = this.createGroup(); this.doAddGroup(group, location); - group.group.openPanel(panel); + group.model.openPanel(panel); } public moveGroupOrPanel( @@ -723,18 +725,18 @@ export class DockviewComponent if (!target || target === Position.Center) { const groupItem: IGroupPanel | undefined = - sourceGroup?.group.removePanel(itemId) || + sourceGroup?.model.removePanel(itemId) || this.panels.get(itemId)?.value; if (!groupItem) { throw new Error(`No panel with id ${itemId}`); } - if (sourceGroup?.group.size === 0) { + if (sourceGroup?.model.size === 0) { this.doRemoveGroup(sourceGroup); } - referenceGroup.group.openPanel(groupItem, { index }); + referenceGroup.model.openPanel(groupItem, { index }); } else { const referenceLocation = getGridLocation(referenceGroup.element); const targetLocation = getRelativeLocation( @@ -743,7 +745,7 @@ export class DockviewComponent target ); - if (sourceGroup && sourceGroup.group.size < 2) { + if (sourceGroup && sourceGroup.model.size < 2) { const [targetParentLocation, to] = tail(targetLocation); const sourceLocation = getGridLocation(sourceGroup.element); const [sourceParentLocation, from] = tail(sourceLocation); @@ -775,7 +777,7 @@ export class DockviewComponent } } else { const groupItem: IGroupPanel | undefined = - sourceGroup?.group.removePanel(itemId) || + sourceGroup?.model.removePanel(itemId) || this.panels.get(itemId)?.value; if (!groupItem) { @@ -820,19 +822,19 @@ export class DockviewComponent const view = new GroupviewPanel(this, id, options); if (typeof this.options.tabHeight === 'number') { - view.group.tabHeight = this.options.tabHeight; + view.model.tabHeight = this.options.tabHeight; } if (!this.groups.has(view.id)) { const disposable = new CompositeDisposable( - view.group.onMove((event) => { + view.model.onMove((event) => { const { groupId, itemId, target, index } = event; this.moveGroupOrPanel(view, groupId, itemId, target, index); }), - view.group.onDidGroupChange((event) => { + view.model.onDidGroupChange((event) => { this._onGridEvent.fire(event); }), - view.group.onDrop((event) => { + view.model.onDrop((event) => { const dragEvent = event.event; const dataTransfer = dragEvent.dataTransfer; @@ -883,7 +885,7 @@ export class DockviewComponent private findGroup(panel: IGroupPanel): GroupviewPanel | undefined { return Array.from(this.groups.values()).find((group) => - group.value.group.containsPanel(panel) + group.value.model.containsPanel(panel) )?.value; } diff --git a/packages/dockview/src/dockview/dockviewGroupPanel.ts b/packages/dockview/src/dockview/dockviewGroupPanel.ts index 8575941ec..e337c4e44 100644 --- a/packages/dockview/src/dockview/dockviewGroupPanel.ts +++ b/packages/dockview/src/dockview/dockviewGroupPanel.ts @@ -157,10 +157,10 @@ export class DockviewGroupPanel this._group = group; this.api.group = group; - this.mutableDisposable.value = this._group.group.onDidGroupChange( + this.mutableDisposable.value = this._group.model.onDidGroupChange( (ev) => { if (ev.kind === GroupChangeKind.GROUP_ACTIVE) { - const isVisible = !!this._group?.group.isPanelActive(this); + const isVisible = !!this._group?.model.isPanelActive(this); this.api._onDidActiveChange.fire({ isActive: isGroupActive && isVisible, }); @@ -171,7 +171,7 @@ export class DockviewGroupPanel } ); - const isPanelVisible = this._group.group.isPanelActive(this); + const isPanelVisible = this._group.model.isPanelActive(this); this.api._onDidActiveChange.fire({ isActive: isGroupActive && isPanelVisible, @@ -182,7 +182,7 @@ export class DockviewGroupPanel this.view?.updateParentGroup( this._group, - this._group.group.isPanelActive(this) + this._group.model.isPanelActive(this) ); } @@ -190,7 +190,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?.group.tabHeight || 0), + height: height - (this.group?.model.tabHeight || 0), }); this.view?.layout(width, height); diff --git a/packages/dockview/src/gridview/basePanelView.ts b/packages/dockview/src/gridview/basePanelView.ts index bc744aa85..6fd7a8bc5 100644 --- a/packages/dockview/src/gridview/basePanelView.ts +++ b/packages/dockview/src/gridview/basePanelView.ts @@ -27,7 +27,8 @@ export interface BasePanelViewExported { export abstract class BasePanelView extends CompositeDisposable - implements IPanel, BasePanelViewExported { + implements IPanel, BasePanelViewExported +{ private _height = 0; private _width = 0; private _element: HTMLElement; @@ -87,8 +88,10 @@ export abstract class BasePanelView this._height = height; this.api._onDidPanelDimensionChange.fire({ width, height }); - if (this.part && this.params) { - this.part.update(this.params.params); + if (this.part) { + if (this.params) { + this.part.update(this.params.params); + } } } diff --git a/packages/dockview/src/gridview/gridviewPanel.ts b/packages/dockview/src/gridview/gridviewPanel.ts index 3f07ba561..c0bb5288f 100644 --- a/packages/dockview/src/gridview/gridviewPanel.ts +++ b/packages/dockview/src/gridview/gridviewPanel.ts @@ -35,7 +35,8 @@ export interface IGridviewPanel export abstract class GridviewPanel extends BasePanelView - implements IGridPanelComponentView, IGridviewPanel { + implements IGridPanelComponentView, IGridviewPanel +{ private _evaluatedMinimumWidth = 0; private _evaluatedMaximumWidth = Number.MAX_SAFE_INTEGER; private _evaluatedMinimumHeight = 0; @@ -49,8 +50,8 @@ export abstract class GridviewPanel private _snap = false; private readonly _onDidChange = new Emitter(); - readonly onDidChange: Event = this._onDidChange - .event; + readonly onDidChange: Event = + this._onDidChange.event; get priority(): LayoutPriority | undefined { return this._priority; diff --git a/packages/dockview/src/groupview/groupview.ts b/packages/dockview/src/groupview/groupview.ts index 916e58a7a..2bd9613f8 100644 --- a/packages/dockview/src/groupview/groupview.ts +++ b/packages/dockview/src/groupview/groupview.ts @@ -16,12 +16,18 @@ import { addDisposableListener, Emitter, Event } from '../events'; import { IGridPanelView } from '../gridview/baseComponentGridview'; import { IViewSize } from '../gridview/gridview'; import { CompositeDisposable, Disposable, IDisposable } from '../lifecycle'; -import { PanelInitParameters, PanelUpdateEvent } from '../panel/types'; +import { + IFrameworkPart, + PanelInitParameters, + PanelUpdateEvent, +} from '../panel/types'; import { IGroupPanel } from './groupPanel'; import { ContentContainer, IContentContainer } from './panel/content'; import { ITabsContainer, TabsContainer } from './titlebar/tabsContainer'; import { IWatermarkRenderer } from './types'; import { GroupviewPanel } from './groupviewPanel'; +import { focusedElement } from '../focusedElement'; +import { BasePanelView } from '../gridview/basePanelView'; export enum GroupChangeKind { GROUP_ACTIVE = 'GROUP_ACTIVE', @@ -80,7 +86,7 @@ export interface IGroupview extends IDisposable, IGridPanelView { readonly isActive: boolean; readonly size: number; readonly panels: IGroupPanel[]; - tabHeight: number | undefined; + readonly tabHeight: number | undefined; // state isPanelActive: (panel: IGroupPanel) => boolean; activePanel: IGroupPanel | undefined; @@ -105,7 +111,7 @@ export interface IGroupview extends IDisposable, IGridPanelView { panel?: IGroupPanel; suppressRoll?: boolean; }): void; - isAncestor(element: Element): boolean; + isContentFocused(): boolean; updateActions(): void; } @@ -126,8 +132,8 @@ export class Groupview extends CompositeDisposable implements IGroupview { private mostRecentlyUsed: IGroupPanel[] = []; private readonly _onDidChange = new Emitter(); - readonly onDidChange: Event = this._onDidChange - .event; + readonly onDidChange: Event = + this._onDidChange.event; private _width = 0; private _height = 0; @@ -141,8 +147,8 @@ export class Groupview extends CompositeDisposable implements IGroupview { readonly onDrop: Event = this._onDrop.event; private readonly _onDidGroupChange = new Emitter(); - readonly onDidGroupChange: Event<{ kind: GroupChangeKind }> = this - ._onDidGroupChange.event; + readonly onDidGroupChange: Event<{ kind: GroupChangeKind }> = + this._onDidGroupChange.event; get element(): HTMLElement { throw new Error('not supported'); @@ -193,10 +199,92 @@ export class Groupview extends CompositeDisposable implements IGroupview { return Number.MAX_SAFE_INTEGER; } - isAncestor(element: Element): boolean { - return ( - element === this.contentContainer.element || - isAncestor(element, this.contentContainer.element) + constructor( + private readonly container: HTMLElement, + private accessor: IDockviewComponent, + public id: string, + private readonly options: GroupOptions, + private readonly parent: GroupviewPanel + ) { + super(); + + this.container.classList.add('groupview'); + + this.addDisposables(this._onMove, this._onDidGroupChange, this._onDrop); + + this.tabsContainer = new TabsContainer(this.accessor, this.parent, { + tabHeight: options.tabHeight, + }); + this.contentContainer = new ContentContainer(); + this.dropTarget = new Droptarget(this.contentContainer.element, { + isDirectional: true, + id: this.accessor.id, + isDisabled: () => { + // disable the drop target if we only have one tab, and that is also the tab we are moving + return ( + this._panels.length === 1 && + this.tabsContainer.hasActiveDragEvent + ); + }, + enableExternalDragEvents: + this.accessor.options.enableExternalDragEvents, + }); + + container.append( + this.tabsContainer.element, + this.contentContainer.element + ); + + this.addDisposables( + this._onMove, + this._onDidGroupChange, + this.tabsContainer.onDropEvent((event) => + this.handleDropEvent(event.event, event.index) + ), + this.contentContainer.onDidFocus(() => { + this.accessor.doSetGroupActive(this.parent, true); + }), + this.contentContainer.onDidBlur(() => { + // this._activePanel?.api._ondid + }), + this.dropTarget.onDidChange((event) => { + // if we've center dropped on ourself then ignore + if ( + event.position === Position.Center && + this.tabsContainer.hasActiveDragEvent + ) { + return; + } + + this.handleDropEvent(event); + }) + ); + + if (this.options?.panels) { + this.options.panels.forEach((panel) => { + this.doAddPanel(panel); + }); + } + + if (this.options?.activePanel) { + this.openPanel(this.options.activePanel); + } + } + + initialize() { + // must be run after the constructor otherwise this.parent may not be + // correctly initialized + this.setActive(this.isActive, true, true); + this.updateContainer(); + } + + isContentFocused() { + if (!focusedElement.element) { + return false; + } + return isAncestor( + focusedElement.element, + this.contentContainer.element ); } @@ -286,84 +374,6 @@ export class Groupview extends CompositeDisposable implements IGroupview { return this.panels.includes(panel); } - constructor( - private readonly container: HTMLElement, - private accessor: IDockviewComponent, - public id: string, - private readonly options: GroupOptions, - private readonly parent: GroupviewPanel - ) { - super(); - - this.container.classList.add('groupview'); - - this.addDisposables(this._onMove, this._onDidGroupChange, this._onDrop); - - this.tabsContainer = new TabsContainer(this.accessor, this.parent, { - tabHeight: options.tabHeight, - }); - this.contentContainer = new ContentContainer(); - this.dropTarget = new Droptarget(this.contentContainer.element, { - isDirectional: true, - id: this.accessor.id, - isDisabled: () => { - // disable the drop target if we only have one tab, and that is also the tab we are moving - return ( - this._panels.length === 1 && - this.tabsContainer.hasActiveDragEvent - ); - }, - enableExternalDragEvents: this.accessor.options - .enableExternalDragEvents, - }); - - container.append( - this.tabsContainer.element, - this.contentContainer.element - ); - - this.addDisposables( - this._onMove, - this._onDidGroupChange, - this.tabsContainer.onDropEvent((event) => - this.handleDropEvent(event.event, event.index) - ), - this.contentContainer.onDidFocus(() => { - this.accessor.doSetGroupActive(this.parent, true); - }), - this.contentContainer.onDidBlur(() => { - // this._activePanel?.api._ondid - }), - this.dropTarget.onDidChange((event) => { - // if we've center dropped on ourself then ignore - if ( - event.position === Position.Center && - this.tabsContainer.hasActiveDragEvent - ) { - return; - } - - this.handleDropEvent(event); - }) - ); - } - - bootstrap() { - if (this.options?.panels) { - this.options.panels.forEach((panel) => { - this.doAddPanel(panel); - }); - } - - if (this.options?.activePanel) { - this.openPanel(this.options.activePanel); - } - - this.setActive(this.isActive, true, true); - - this.updateContainer(); - } - init(params: PanelInitParameters) { //noop } diff --git a/packages/dockview/src/groupview/groupviewPanel.ts b/packages/dockview/src/groupview/groupviewPanel.ts index 49c3e5244..9133f7c6a 100644 --- a/packages/dockview/src/groupview/groupviewPanel.ts +++ b/packages/dockview/src/groupview/groupviewPanel.ts @@ -1,47 +1,30 @@ import { IFrameworkPart } from '../panel/types'; import { IDockviewComponent } from '../dockview/dockviewComponent'; -import { - GridviewPanelApiImpl, - GridviewPanelApi, -} from '../api/gridviewPanelApi'; +import { GridviewPanelApiImpl } from '../api/gridviewPanelApi'; import { Groupview, GroupOptions } from './groupview'; import { GridviewPanel } from '../gridview/gridviewPanel'; -interface IGroupApi extends GridviewPanelApi {} - -class GroupApi extends GridviewPanelApiImpl implements IGroupApi { - private _value?: Groupview; - - set group(value: Groupview) { - this._value = value; - } - - constructor(id: string) { - super(id); - } -} - export class GroupviewPanel extends GridviewPanel { - private readonly _group: Groupview; + private readonly _model: Groupview; - get group(): Groupview { - return this._group; + get model(): Groupview { + return this._model; } get minimumHeight() { - return this._group.minimumHeight; + return this._model.minimumHeight; } get maximumHeight() { - return this._group.maximumHeight; + return this._model.maximumHeight; } get minimumWidth() { - return this._group.minimumWidth; + return this._model.minimumWidth; } get maximumWidth() { - return this._group.maximumWidth; + return this._model.maximumWidth; } constructor( @@ -49,29 +32,27 @@ export class GroupviewPanel extends GridviewPanel { id: string, options: GroupOptions ) { - super(id, 'groupview_default', new GroupApi(id)); + super(id, 'groupview_default', new GridviewPanelApiImpl(id)); - this._group = new Groupview(this.element, accessor, id, options, this); - - (this.api as GroupApi).group = this._group; - this.group.bootstrap(); + this._model = new Groupview(this.element, accessor, id, options, this); + this.model.initialize(); } setActive(isActive: boolean): void { super.setActive(isActive); - this.group.setActive(isActive); + this.model.setActive(isActive); } layout(width: number, height: number) { super.layout(width, height); - this.group.layout(width, height); + this.model.layout(width, height); } getComponent(): IFrameworkPart { - return this._group; + return this._model; } toJSON(): any { - return this.group.toJSON(); + return this.model.toJSON(); } } diff --git a/packages/dockview/src/groupview/panel/content.ts b/packages/dockview/src/groupview/panel/content.ts index 26c526066..35418628f 100644 --- a/packages/dockview/src/groupview/panel/content.ts +++ b/packages/dockview/src/groupview/panel/content.ts @@ -7,13 +7,6 @@ import { Emitter, Event } from '../../events'; import { trackFocus } from '../../dom'; import { IGroupPanel } from '../groupPanel'; -export interface IRenderable { - id: string; - element: HTMLElement; - onDidFocus?: Event; - onDidBlur?: Event; -} - export interface IContentContainer extends IDisposable { onDidFocus: Event; onDidBlur: Event; @@ -25,76 +18,10 @@ export interface IContentContainer extends IDisposable { hide(): void; } -export interface HostedPanelOptions { - id: string; - parent?: HTMLElement; -} - -export class HostedPanel implements IRenderable, IDisposable { - private readonly _element: HTMLElement; - - get element() { - return this._element; - } - - get id() { - return this.panel.id; - } - - constructor( - private readonly panel: IGroupPanel, - private readonly options: HostedPanelOptions - ) { - if (!options.parent) { - options.parent = document.getElementById('app') as HTMLElement; - options.parent.style.position = 'relative'; - } - - this._element = document.createElement('div'); - this._element.style.visibility = 'hidden'; - this._element.style.overflow = 'hidden'; - // this._element.style.pointerEvents = 'none'; - this._element.id = `webivew-${options.id}`; - - options.parent.appendChild(this._element); - } - - hide() { - this._element.style.visibility = 'hidden'; - } - - show() { - this._element.style.visibility = 'visible'; - } - - layout( - element: HTMLElement, - dimension?: { width: number; height: number } - ) { - if (!this.element || !this.element.parentElement) { - return; - } - const frameRect = element.getBoundingClientRect(); - const containerRect = this.element.parentElement.getBoundingClientRect(); - this.element.style.position = 'absolute'; - this.element.style.top = `${frameRect.top - containerRect.top}px`; - this.element.style.left = `${frameRect.left - containerRect.left}px`; - this.element.style.width = `${ - dimension ? dimension.width : frameRect.width - }px`; - this.element.style.height = `${ - dimension ? dimension.height : frameRect.height - }px`; - } - - dispose() { - this._element.remove(); - } -} - export class ContentContainer extends CompositeDisposable - implements IContentContainer { + implements IContentContainer +{ private _element: HTMLElement; private panel: IGroupPanel | undefined; private disposable = new MutableDisposable(); @@ -145,8 +72,8 @@ export class ContentContainer const disposable = new CompositeDisposable(); if (this.panel.view) { - const _onDidFocus: Event = this.panel.view.content - .onDidFocus!; + const _onDidFocus: Event = + this.panel.view.content.onDidFocus!; const _onDidBlur: Event = this.panel.view.content.onDidBlur!; const { onDidFocus, onDidBlur } = trackFocus(this._element); diff --git a/packages/dockview/src/groupview/panel/hostedPanel.ts b/packages/dockview/src/groupview/panel/hostedPanel.ts new file mode 100644 index 000000000..59de605b1 --- /dev/null +++ b/packages/dockview/src/groupview/panel/hostedPanel.ts @@ -0,0 +1,71 @@ +import { IDisposable } from '../../lifecycle'; +import { IGroupPanel } from '../groupPanel'; +import { IRenderable } from '../types'; + +export interface HostedPanelOptions { + id: string; + parent?: HTMLElement; +} + +export class HostedPanel implements IRenderable, IDisposable { + private readonly _element: HTMLElement; + + get element() { + return this._element; + } + + get id() { + return this.panel.id; + } + + constructor( + private readonly panel: IGroupPanel, + private readonly options: HostedPanelOptions + ) { + if (!options.parent) { + options.parent = document.getElementById('app') as HTMLElement; + options.parent.style.position = 'relative'; + } + + this._element = document.createElement('div'); + this._element.style.visibility = 'hidden'; + this._element.style.overflow = 'hidden'; + // this._element.style.pointerEvents = 'none'; + this._element.id = `webivew-${options.id}`; + + options.parent.appendChild(this._element); + } + + hide() { + this._element.style.visibility = 'hidden'; + } + + show() { + this._element.style.visibility = 'visible'; + } + + layout( + element: HTMLElement, + dimension?: { width: number; height: number } + ) { + if (!this.element || !this.element.parentElement) { + return; + } + const frameRect = element.getBoundingClientRect(); + const containerRect = + this.element.parentElement.getBoundingClientRect(); + this.element.style.position = 'absolute'; + this.element.style.top = `${frameRect.top - containerRect.top}px`; + this.element.style.left = `${frameRect.left - containerRect.left}px`; + this.element.style.width = `${ + dimension ? dimension.width : frameRect.width + }px`; + this.element.style.height = `${ + dimension ? dimension.height : frameRect.height + }px`; + } + + dispose() { + this._element.remove(); + } +} diff --git a/packages/dockview/src/groupview/titlebar/tabsContainer.ts b/packages/dockview/src/groupview/titlebar/tabsContainer.ts index 53d0bee72..8dfbb2fc4 100644 --- a/packages/dockview/src/groupview/titlebar/tabsContainer.ts +++ b/packages/dockview/src/groupview/titlebar/tabsContainer.ts @@ -8,7 +8,6 @@ import { ITab, MouseEventKind, Tab } from '../tab'; import { removeClasses, addClasses } from '../../dom'; import { DroptargetEvent, Position } from '../../dnd/droptarget'; import { last } from '../../array'; -import { focusedElement } from '../../focusedElement'; import { IGroupPanel } from '../groupPanel'; import { IDockviewComponent } from '../../dockview/dockviewComponent'; import { LocalSelectionTransfer } from '../../dnd/dataTransfer'; @@ -40,7 +39,8 @@ export interface ITabsContainer extends IDisposable { export class TabsContainer extends CompositeDisposable - implements ITabsContainer { + implements ITabsContainer +{ private readonly _element: HTMLElement; private readonly tabContainer: HTMLElement; private readonly actionContainer: HTMLElement; @@ -281,8 +281,8 @@ export class TabsContainer const disposable = CompositeDisposable.from( tabToAdd.onChanged((event) => { const alreadyFocused = - panel.id === this.group.group.activePanel?.id && - this.group.group.isAncestor(focusedElement.element!); + panel.id === this.group.model.activePanel?.id && + this.group.model.isContentFocused(); this.accessor.fireMouseEvent({ ...event, panel, tab: true }); const isLeftClick = event.event.button === 0; @@ -293,7 +293,7 @@ export class TabsContainer switch (event.kind) { case MouseEventKind.CLICK: - this.group.group.openPanel(panel, { + this.group.model.openPanel(panel, { skipFocus: alreadyFocused, }); break; diff --git a/packages/dockview/src/groupview/types.ts b/packages/dockview/src/groupview/types.ts index 469424cbf..a1884b770 100644 --- a/packages/dockview/src/groupview/types.ts +++ b/packages/dockview/src/groupview/types.ts @@ -7,6 +7,13 @@ import { GroupviewPanel } from './groupviewPanel'; import { Event } from '../events'; import { WrappedTab } from '../dockview/components/tab/defaultTab'; +export interface IRenderable { + id: string; + element: HTMLElement; + onDidFocus?: Event; + onDidBlur?: Event; +} + export interface HeaderPartInitParameters { title: string; suppressClosable?: boolean; diff --git a/packages/dockview/src/react/dockview/dockview.tsx b/packages/dockview/src/react/dockview/dockview.tsx index db401876a..7ba463237 100644 --- a/packages/dockview/src/react/dockview/dockview.tsx +++ b/packages/dockview/src/react/dockview/dockview.tsx @@ -15,10 +15,6 @@ import { PanelCollection, PanelParameters } from '../types'; import { watchElementResize } from '../../dom'; import { IContentRenderer, ITabRenderer } from '../../groupview/types'; -export interface PanelCollection1 { - [name: string]: React.FunctionComponent; -} - export interface IGroupPanelBaseProps> extends PanelParameters { api: DockviewPanelApi;