refactor: cleaner code

This commit is contained in:
mathuo 2021-06-08 22:29:44 +01:00
parent 374d59f2cb
commit 88d6026335
14 changed files with 295 additions and 297 deletions

View File

@ -89,28 +89,28 @@ describe('dockviewComponent', () => {
dockview.moveGroupOrPanel(group2, group1.id, 'panel3', Position.Center); dockview.moveGroupOrPanel(group2, group1.id, 'panel3', Position.Center);
expect(dockview.activeGroup).toBe(group2); expect(dockview.activeGroup).toBe(group2);
expect(dockview.activeGroup.group.activePanel).toBe(panel3); expect(dockview.activeGroup.model.activePanel).toBe(panel3);
expect(dockview.activeGroup.group.indexOf(panel3)).toBe(1); expect(dockview.activeGroup.model.indexOf(panel3)).toBe(1);
dockview.moveToPrevious({ includePanel: true }); dockview.moveToPrevious({ includePanel: true });
expect(dockview.activeGroup).toBe(group2); expect(dockview.activeGroup).toBe(group2);
expect(dockview.activeGroup.group.activePanel).toBe(panel1); expect(dockview.activeGroup.model.activePanel).toBe(panel1);
dockview.moveToNext({ includePanel: true }); dockview.moveToNext({ includePanel: true });
expect(dockview.activeGroup).toBe(group2); expect(dockview.activeGroup).toBe(group2);
expect(dockview.activeGroup.group.activePanel).toBe(panel3); expect(dockview.activeGroup.model.activePanel).toBe(panel3);
dockview.moveToPrevious({ includePanel: false }); dockview.moveToPrevious({ includePanel: false });
expect(dockview.activeGroup).toBe(group1); expect(dockview.activeGroup).toBe(group1);
expect(dockview.activeGroup.group.activePanel).toBe(panel4); expect(dockview.activeGroup.model.activePanel).toBe(panel4);
dockview.moveToPrevious({ includePanel: true }); dockview.moveToPrevious({ includePanel: true });
expect(dockview.activeGroup).toBe(group1); expect(dockview.activeGroup).toBe(group1);
expect(dockview.activeGroup.group.activePanel).toBe(panel2); expect(dockview.activeGroup.model.activePanel).toBe(panel2);
dockview.moveToNext({ includePanel: false }); dockview.moveToNext({ includePanel: false });
expect(dockview.activeGroup).toBe(group2); expect(dockview.activeGroup).toBe(group2);
expect(dockview.activeGroup.group.activePanel).toBe(panel3); expect(dockview.activeGroup.model.activePanel).toBe(panel3);
}); });
test('remove group', () => { test('remove group', () => {
@ -142,8 +142,8 @@ describe('dockviewComponent', () => {
expect(dockview.size).toBe(2); expect(dockview.size).toBe(2);
expect(dockview.totalPanels).toBe(4); expect(dockview.totalPanels).toBe(4);
expect(panel1.group.group.size).toBe(2); expect(panel1.group.model.size).toBe(2);
expect(panel2.group.group.size).toBe(2); expect(panel2.group.model.size).toBe(2);
dockview.removeGroup(panel1.group); dockview.removeGroup(panel1.group);
@ -263,13 +263,13 @@ describe('dockviewComponent', () => {
const group = panel1.group; const group = panel1.group;
expect(group.group.size).toBe(2); expect(group.model.size).toBe(2);
expect(group.group.containsPanel(panel1)).toBeTruthy(); expect(group.model.containsPanel(panel1)).toBeTruthy();
expect(group.group.containsPanel(panel2)).toBeTruthy(); expect(group.model.containsPanel(panel2)).toBeTruthy();
expect(group.group.activePanel).toBe(panel2); expect(group.model.activePanel).toBe(panel2);
expect(group.group.indexOf(panel1)).toBe(0); expect(group.model.indexOf(panel1)).toBe(0);
expect(group.group.indexOf(panel2)).toBe(1); expect(group.model.indexOf(panel2)).toBe(1);
dockview.moveGroupOrPanel(group, group.id, 'panel1', Position.Right); dockview.moveGroupOrPanel(group, group.id, 'panel1', Position.Right);
@ -277,12 +277,12 @@ describe('dockviewComponent', () => {
expect(dockview.totalPanels).toBe(2); expect(dockview.totalPanels).toBe(2);
expect(panel1.group).not.toBe(panel2.group); expect(panel1.group).not.toBe(panel2.group);
expect(panel1.group.group.size).toBe(1); expect(panel1.group.model.size).toBe(1);
expect(panel2.group.group.size).toBe(1); expect(panel2.group.model.size).toBe(1);
expect(panel1.group.group.containsPanel(panel1)).toBeTruthy(); expect(panel1.group.model.containsPanel(panel1)).toBeTruthy();
expect(panel2.group.group.containsPanel(panel2)).toBeTruthy(); expect(panel2.group.model.containsPanel(panel2)).toBeTruthy();
expect(panel1.group.group.activePanel).toBe(panel1); expect(panel1.group.model.activePanel).toBe(panel1);
expect(panel2.group.group.activePanel).toBe(panel2); expect(panel2.group.model.activePanel).toBe(panel2);
await panel1.api.close(); await panel1.api.close();

View File

@ -235,7 +235,7 @@ describe('groupview', () => {
activePanel: panel2, activePanel: panel2,
}); });
expect(groupview2.group.activePanel).toBe(panel2); expect(groupview2.model.activePanel).toBe(panel2);
expect( expect(
groupview2.element.querySelector('.content-part-panel1') groupview2.element.querySelector('.content-part-panel1')
@ -253,33 +253,33 @@ describe('groupview', () => {
const panel2 = new TestPanel('panel2', jest.fn() as any); const panel2 = new TestPanel('panel2', jest.fn() as any);
const panel3 = new TestPanel('panel3', jest.fn() as any); const panel3 = new TestPanel('panel3', jest.fn() as any);
groupview.group.openPanel(panel1); groupview.model.openPanel(panel1);
groupview.group.openPanel(panel2); groupview.model.openPanel(panel2);
groupview.group.openPanel(panel3); groupview.model.openPanel(panel3);
groupview.group.openPanel(panel2); // set active groupview.model.openPanel(panel2); // set active
groupview.group.moveToPrevious(); groupview.model.moveToPrevious();
expect(groupview.group.activePanel).toBe(panel1); expect(groupview.model.activePanel).toBe(panel1);
groupview.group.moveToPrevious({ suppressRoll: true }); groupview.model.moveToPrevious({ suppressRoll: true });
expect(groupview.group.activePanel).toBe(panel1); expect(groupview.model.activePanel).toBe(panel1);
groupview.group.moveToPrevious(); groupview.model.moveToPrevious();
expect(groupview.group.activePanel).toBe(panel3); expect(groupview.model.activePanel).toBe(panel3);
groupview.group.moveToNext({ suppressRoll: true }); groupview.model.moveToNext({ suppressRoll: true });
expect(groupview.group.activePanel).toBe(panel3); expect(groupview.model.activePanel).toBe(panel3);
groupview.group.moveToNext({ suppressRoll: false }); groupview.model.moveToNext({ suppressRoll: false });
expect(groupview.group.activePanel).toBe(panel1); expect(groupview.model.activePanel).toBe(panel1);
groupview.group.moveToPrevious({ suppressRoll: false }); groupview.model.moveToPrevious({ suppressRoll: false });
expect(groupview.group.activePanel).toBe(panel3); expect(groupview.model.activePanel).toBe(panel3);
groupview.group.moveToNext(); groupview.model.moveToNext();
groupview.group.moveToNext(); groupview.model.moveToNext();
expect(groupview.group.activePanel).toBe(panel2); expect(groupview.model.activePanel).toBe(panel2);
}); });
test('default', () => { test('default', () => {
@ -298,12 +298,12 @@ describe('groupview', () => {
const panel1 = new TestPanel('panel1', jest.fn() as any); const panel1 = new TestPanel('panel1', jest.fn() as any);
const panel2 = new TestPanel('panel2', jest.fn() as any); const panel2 = new TestPanel('panel2', jest.fn() as any);
groupview.group.openPanel(panel1); groupview.model.openPanel(panel1);
groupview.group.openPanel(panel2); groupview.model.openPanel(panel2);
const events: GroupDropEvent[] = []; const events: GroupDropEvent[] = [];
groupview.group.onDrop((event) => { groupview.model.onDrop((event) => {
events.push(event); events.push(event);
}); });

View File

@ -98,7 +98,7 @@ export class DockviewPanelApiImpl
if (!this.group) { if (!this.group) {
throw new Error(`panel ${this.id} has no 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<boolean>) { public interceptOnCloseAction(interceptor: () => Promise<boolean>) {

View File

@ -135,7 +135,8 @@ export interface LayoutDropEvent {
export class DockviewComponent export class DockviewComponent
extends BaseGrid<GroupviewPanel> extends BaseGrid<GroupviewPanel>
implements IDockviewComponent { implements IDockviewComponent
{
private readonly panels = new Map<string, IValueDisposable<IGroupPanel>>(); private readonly panels = new Map<string, IValueDisposable<IGroupPanel>>();
private readonly dirtyPanels = new Set<IGroupPanel>(); private readonly dirtyPanels = new Set<IGroupPanel>();
private readonly debouncedDeque = debounce( private readonly debouncedDeque = debounce(
@ -144,11 +145,11 @@ export class DockviewComponent
); );
// events // events
private readonly _onTabInteractionEvent = new Emitter<LayoutMouseEvent>(); private readonly _onTabInteractionEvent = new Emitter<LayoutMouseEvent>();
readonly onTabInteractionEvent: Event<LayoutMouseEvent> = this readonly onTabInteractionEvent: Event<LayoutMouseEvent> =
._onTabInteractionEvent.event; this._onTabInteractionEvent.event;
private readonly _onTabContextMenu = new Emitter<TabContextMenuEvent>(); private readonly _onTabContextMenu = new Emitter<TabContextMenuEvent>();
readonly onTabContextMenu: Event<TabContextMenuEvent> = this readonly onTabContextMenu: Event<TabContextMenuEvent> =
._onTabContextMenu.event; this._onTabContextMenu.event;
// everything else // everything else
private drag = new MutableDisposable(); private drag = new MutableDisposable();
private _deserializer: IPanelDeserializer | undefined; private _deserializer: IPanelDeserializer | undefined;
@ -270,7 +271,7 @@ export class DockviewComponent
const panel = this.panels.get(panelOptions.id)?.value; const panel = this.panels.get(panelOptions.id)?.value;
if (panel) { if (panel) {
this.drag.value = panel.group!.group.startActiveDrag(panel); this.drag.value = panel.group!.model.startActiveDrag(panel);
} }
const data = JSON.stringify({ const data = JSON.stringify({
@ -309,7 +310,7 @@ export class DockviewComponent
throw new Error(`Panel ${panel.id} has no associated group`); throw new Error(`Panel ${panel.id} has no associated group`);
} }
this.doSetGroupActive(panel.group); this.doSetGroupActive(panel.group);
panel.group.group.openPanel(panel); panel.group.model.openPanel(panel);
} }
public moveToNext(options: MovementOptions = {}): void { public moveToNext(options: MovementOptions = {}): void {
@ -322,12 +323,12 @@ export class DockviewComponent
if (options.includePanel && options.group) { if (options.includePanel && options.group) {
if ( if (
options.group.group.activePanel !== options.group.model.activePanel !==
options.group.group.panels[ options.group.model.panels[
options.group.group.panels.length - 1 options.group.model.panels.length - 1
] ]
) { ) {
options.group.group.moveToNext({ suppressRoll: true }); options.group.model.moveToNext({ suppressRoll: true });
return; return;
} }
} }
@ -347,10 +348,10 @@ export class DockviewComponent
if (options.includePanel && options.group) { if (options.includePanel && options.group) {
if ( if (
options.group.group.activePanel !== options.group.model.activePanel !==
options.group.group.panels[0] options.group.model.panels[0]
) { ) {
options.group.group.moveToPrevious({ suppressRoll: true }); options.group.model.moveToPrevious({ suppressRoll: true });
return; return;
} }
} }
@ -511,7 +512,7 @@ export class DockviewComponent
for (const entry of this.groups.entries()) { for (const entry of this.groups.entries()) {
const [_, group] = entry; const [_, group] = entry;
const didCloseAll = await group.value.group.closeAllPanels(); const didCloseAll = await group.value.model.closeAllPanels();
if (!didCloseAll) { if (!didCloseAll) {
return false; return false;
} }
@ -523,7 +524,7 @@ export class DockviewComponent
public setTabHeight(height: number | undefined): void { public setTabHeight(height: number | undefined): void {
this.options.tabHeight = height; this.options.tabHeight = height;
this.groups.forEach((value) => { this.groups.forEach((value) => {
value.value.group.tabHeight = height; value.value.model.tabHeight = height;
}); });
} }
@ -569,7 +570,7 @@ export class DockviewComponent
if (referenceGroup) { if (referenceGroup) {
const target = toTarget(options.position?.direction || 'within'); const target = toTarget(options.position?.direction || 'within');
if (target === Position.Center) { if (target === Position.Center) {
referenceGroup.group.openPanel(panel); referenceGroup.model.openPanel(panel);
} else { } else {
const location = getGridLocation(referenceGroup.element); const location = getGridLocation(referenceGroup.element);
const relativeLocation = getRelativeLocation( const relativeLocation = getRelativeLocation(
@ -652,8 +653,9 @@ export class DockviewComponent
const group = this.createGroup(); const group = this.createGroup();
if (options) { if (options) {
const referencePanel = this.panels.get(options.referencePanel) const referencePanel = this.panels.get(
?.value; options.referencePanel
)?.value;
if (!referencePanel) { if (!referencePanel) {
throw new Error( throw new Error(
@ -684,9 +686,9 @@ export class DockviewComponent
} }
public removeGroup(group: GroupviewPanel): void { 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) => { panels.forEach((panel) => {
group.group.removePanel(panel); group.model.removePanel(panel);
this.unregisterPanel(panel); this.unregisterPanel(panel);
}); });
@ -707,7 +709,7 @@ export class DockviewComponent
group! = this.createGroup(); group! = this.createGroup();
this.doAddGroup(group, location); this.doAddGroup(group, location);
group.group.openPanel(panel); group.model.openPanel(panel);
} }
public moveGroupOrPanel( public moveGroupOrPanel(
@ -723,18 +725,18 @@ export class DockviewComponent
if (!target || target === Position.Center) { if (!target || target === Position.Center) {
const groupItem: IGroupPanel | undefined = const groupItem: IGroupPanel | undefined =
sourceGroup?.group.removePanel(itemId) || sourceGroup?.model.removePanel(itemId) ||
this.panels.get(itemId)?.value; this.panels.get(itemId)?.value;
if (!groupItem) { if (!groupItem) {
throw new Error(`No panel with id ${itemId}`); throw new Error(`No panel with id ${itemId}`);
} }
if (sourceGroup?.group.size === 0) { if (sourceGroup?.model.size === 0) {
this.doRemoveGroup(sourceGroup); this.doRemoveGroup(sourceGroup);
} }
referenceGroup.group.openPanel(groupItem, { index }); referenceGroup.model.openPanel(groupItem, { index });
} else { } else {
const referenceLocation = getGridLocation(referenceGroup.element); const referenceLocation = getGridLocation(referenceGroup.element);
const targetLocation = getRelativeLocation( const targetLocation = getRelativeLocation(
@ -743,7 +745,7 @@ export class DockviewComponent
target target
); );
if (sourceGroup && sourceGroup.group.size < 2) { if (sourceGroup && sourceGroup.model.size < 2) {
const [targetParentLocation, to] = tail(targetLocation); const [targetParentLocation, to] = tail(targetLocation);
const sourceLocation = getGridLocation(sourceGroup.element); const sourceLocation = getGridLocation(sourceGroup.element);
const [sourceParentLocation, from] = tail(sourceLocation); const [sourceParentLocation, from] = tail(sourceLocation);
@ -775,7 +777,7 @@ export class DockviewComponent
} }
} else { } else {
const groupItem: IGroupPanel | undefined = const groupItem: IGroupPanel | undefined =
sourceGroup?.group.removePanel(itemId) || sourceGroup?.model.removePanel(itemId) ||
this.panels.get(itemId)?.value; this.panels.get(itemId)?.value;
if (!groupItem) { if (!groupItem) {
@ -820,19 +822,19 @@ export class DockviewComponent
const view = new GroupviewPanel(this, id, options); const view = new GroupviewPanel(this, id, options);
if (typeof this.options.tabHeight === 'number') { if (typeof this.options.tabHeight === 'number') {
view.group.tabHeight = this.options.tabHeight; view.model.tabHeight = this.options.tabHeight;
} }
if (!this.groups.has(view.id)) { if (!this.groups.has(view.id)) {
const disposable = new CompositeDisposable( const disposable = new CompositeDisposable(
view.group.onMove((event) => { view.model.onMove((event) => {
const { groupId, itemId, target, index } = event; const { groupId, itemId, target, index } = event;
this.moveGroupOrPanel(view, groupId, itemId, target, index); this.moveGroupOrPanel(view, groupId, itemId, target, index);
}), }),
view.group.onDidGroupChange((event) => { view.model.onDidGroupChange((event) => {
this._onGridEvent.fire(event); this._onGridEvent.fire(event);
}), }),
view.group.onDrop((event) => { view.model.onDrop((event) => {
const dragEvent = event.event; const dragEvent = event.event;
const dataTransfer = dragEvent.dataTransfer; const dataTransfer = dragEvent.dataTransfer;
@ -883,7 +885,7 @@ export class DockviewComponent
private findGroup(panel: IGroupPanel): GroupviewPanel | undefined { private findGroup(panel: IGroupPanel): GroupviewPanel | undefined {
return Array.from(this.groups.values()).find((group) => return Array.from(this.groups.values()).find((group) =>
group.value.group.containsPanel(panel) group.value.model.containsPanel(panel)
)?.value; )?.value;
} }

View File

@ -157,10 +157,10 @@ export class DockviewGroupPanel
this._group = group; this._group = group;
this.api.group = group; this.api.group = group;
this.mutableDisposable.value = this._group.group.onDidGroupChange( this.mutableDisposable.value = this._group.model.onDidGroupChange(
(ev) => { (ev) => {
if (ev.kind === GroupChangeKind.GROUP_ACTIVE) { if (ev.kind === GroupChangeKind.GROUP_ACTIVE) {
const isVisible = !!this._group?.group.isPanelActive(this); const isVisible = !!this._group?.model.isPanelActive(this);
this.api._onDidActiveChange.fire({ this.api._onDidActiveChange.fire({
isActive: isGroupActive && isVisible, 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({ this.api._onDidActiveChange.fire({
isActive: isGroupActive && isPanelVisible, isActive: isGroupActive && isPanelVisible,
@ -182,7 +182,7 @@ export class DockviewGroupPanel
this.view?.updateParentGroup( this.view?.updateParentGroup(
this._group, 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 // the obtain the correct dimensions of the content panel we must deduct the tab height
this.api._onDidPanelDimensionChange.fire({ this.api._onDidPanelDimensionChange.fire({
width, width,
height: height - (this.group?.group.tabHeight || 0), height: height - (this.group?.model.tabHeight || 0),
}); });
this.view?.layout(width, height); this.view?.layout(width, height);

View File

@ -27,7 +27,8 @@ export interface BasePanelViewExported<T extends PanelApiImpl> {
export abstract class BasePanelView<T extends PanelApiImpl> export abstract class BasePanelView<T extends PanelApiImpl>
extends CompositeDisposable extends CompositeDisposable
implements IPanel, BasePanelViewExported<T> { implements IPanel, BasePanelViewExported<T>
{
private _height = 0; private _height = 0;
private _width = 0; private _width = 0;
private _element: HTMLElement; private _element: HTMLElement;
@ -87,8 +88,10 @@ export abstract class BasePanelView<T extends PanelApiImpl>
this._height = height; this._height = height;
this.api._onDidPanelDimensionChange.fire({ width, height }); this.api._onDidPanelDimensionChange.fire({ width, height });
if (this.part && this.params) { if (this.part) {
this.part.update(this.params.params); if (this.params) {
this.part.update(this.params.params);
}
} }
} }

View File

@ -35,7 +35,8 @@ export interface IGridviewPanel
export abstract class GridviewPanel export abstract class GridviewPanel
extends BasePanelView<GridviewPanelApiImpl> extends BasePanelView<GridviewPanelApiImpl>
implements IGridPanelComponentView, IGridviewPanel { implements IGridPanelComponentView, IGridviewPanel
{
private _evaluatedMinimumWidth = 0; private _evaluatedMinimumWidth = 0;
private _evaluatedMaximumWidth = Number.MAX_SAFE_INTEGER; private _evaluatedMaximumWidth = Number.MAX_SAFE_INTEGER;
private _evaluatedMinimumHeight = 0; private _evaluatedMinimumHeight = 0;
@ -49,8 +50,8 @@ export abstract class GridviewPanel
private _snap = false; private _snap = false;
private readonly _onDidChange = new Emitter<IViewSize | undefined>(); private readonly _onDidChange = new Emitter<IViewSize | undefined>();
readonly onDidChange: Event<IViewSize | undefined> = this._onDidChange readonly onDidChange: Event<IViewSize | undefined> =
.event; this._onDidChange.event;
get priority(): LayoutPriority | undefined { get priority(): LayoutPriority | undefined {
return this._priority; return this._priority;

View File

@ -16,12 +16,18 @@ import { addDisposableListener, Emitter, Event } from '../events';
import { IGridPanelView } from '../gridview/baseComponentGridview'; import { IGridPanelView } from '../gridview/baseComponentGridview';
import { IViewSize } from '../gridview/gridview'; import { IViewSize } from '../gridview/gridview';
import { CompositeDisposable, Disposable, IDisposable } from '../lifecycle'; import { CompositeDisposable, Disposable, IDisposable } from '../lifecycle';
import { PanelInitParameters, PanelUpdateEvent } from '../panel/types'; import {
IFrameworkPart,
PanelInitParameters,
PanelUpdateEvent,
} from '../panel/types';
import { IGroupPanel } from './groupPanel'; import { IGroupPanel } from './groupPanel';
import { ContentContainer, IContentContainer } from './panel/content'; import { ContentContainer, IContentContainer } from './panel/content';
import { ITabsContainer, TabsContainer } from './titlebar/tabsContainer'; import { ITabsContainer, TabsContainer } from './titlebar/tabsContainer';
import { IWatermarkRenderer } from './types'; import { IWatermarkRenderer } from './types';
import { GroupviewPanel } from './groupviewPanel'; import { GroupviewPanel } from './groupviewPanel';
import { focusedElement } from '../focusedElement';
import { BasePanelView } from '../gridview/basePanelView';
export enum GroupChangeKind { export enum GroupChangeKind {
GROUP_ACTIVE = 'GROUP_ACTIVE', GROUP_ACTIVE = 'GROUP_ACTIVE',
@ -80,7 +86,7 @@ export interface IGroupview extends IDisposable, IGridPanelView {
readonly isActive: boolean; readonly isActive: boolean;
readonly size: number; readonly size: number;
readonly panels: IGroupPanel[]; readonly panels: IGroupPanel[];
tabHeight: number | undefined; readonly tabHeight: number | undefined;
// state // state
isPanelActive: (panel: IGroupPanel) => boolean; isPanelActive: (panel: IGroupPanel) => boolean;
activePanel: IGroupPanel | undefined; activePanel: IGroupPanel | undefined;
@ -105,7 +111,7 @@ export interface IGroupview extends IDisposable, IGridPanelView {
panel?: IGroupPanel; panel?: IGroupPanel;
suppressRoll?: boolean; suppressRoll?: boolean;
}): void; }): void;
isAncestor(element: Element): boolean; isContentFocused(): boolean;
updateActions(): void; updateActions(): void;
} }
@ -126,8 +132,8 @@ export class Groupview extends CompositeDisposable implements IGroupview {
private mostRecentlyUsed: IGroupPanel[] = []; private mostRecentlyUsed: IGroupPanel[] = [];
private readonly _onDidChange = new Emitter<IViewSize | undefined>(); private readonly _onDidChange = new Emitter<IViewSize | undefined>();
readonly onDidChange: Event<IViewSize | undefined> = this._onDidChange readonly onDidChange: Event<IViewSize | undefined> =
.event; this._onDidChange.event;
private _width = 0; private _width = 0;
private _height = 0; private _height = 0;
@ -141,8 +147,8 @@ export class Groupview extends CompositeDisposable implements IGroupview {
readonly onDrop: Event<GroupDropEvent> = this._onDrop.event; readonly onDrop: Event<GroupDropEvent> = this._onDrop.event;
private readonly _onDidGroupChange = new Emitter<GroupChangeEvent>(); private readonly _onDidGroupChange = new Emitter<GroupChangeEvent>();
readonly onDidGroupChange: Event<{ kind: GroupChangeKind }> = this readonly onDidGroupChange: Event<{ kind: GroupChangeKind }> =
._onDidGroupChange.event; this._onDidGroupChange.event;
get element(): HTMLElement { get element(): HTMLElement {
throw new Error('not supported'); throw new Error('not supported');
@ -193,10 +199,92 @@ export class Groupview extends CompositeDisposable implements IGroupview {
return Number.MAX_SAFE_INTEGER; return Number.MAX_SAFE_INTEGER;
} }
isAncestor(element: Element): boolean { constructor(
return ( private readonly container: HTMLElement,
element === this.contentContainer.element || private accessor: IDockviewComponent,
isAncestor(element, this.contentContainer.element) 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); 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) { init(params: PanelInitParameters) {
//noop //noop
} }

View File

@ -1,47 +1,30 @@
import { IFrameworkPart } from '../panel/types'; import { IFrameworkPart } from '../panel/types';
import { IDockviewComponent } from '../dockview/dockviewComponent'; import { IDockviewComponent } from '../dockview/dockviewComponent';
import { import { GridviewPanelApiImpl } from '../api/gridviewPanelApi';
GridviewPanelApiImpl,
GridviewPanelApi,
} from '../api/gridviewPanelApi';
import { Groupview, GroupOptions } from './groupview'; import { Groupview, GroupOptions } from './groupview';
import { GridviewPanel } from '../gridview/gridviewPanel'; 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 { export class GroupviewPanel extends GridviewPanel {
private readonly _group: Groupview; private readonly _model: Groupview;
get group(): Groupview { get model(): Groupview {
return this._group; return this._model;
} }
get minimumHeight() { get minimumHeight() {
return this._group.minimumHeight; return this._model.minimumHeight;
} }
get maximumHeight() { get maximumHeight() {
return this._group.maximumHeight; return this._model.maximumHeight;
} }
get minimumWidth() { get minimumWidth() {
return this._group.minimumWidth; return this._model.minimumWidth;
} }
get maximumWidth() { get maximumWidth() {
return this._group.maximumWidth; return this._model.maximumWidth;
} }
constructor( constructor(
@ -49,29 +32,27 @@ export class GroupviewPanel extends GridviewPanel {
id: string, id: string,
options: GroupOptions 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._model = new Groupview(this.element, accessor, id, options, this);
this.model.initialize();
(this.api as GroupApi).group = this._group;
this.group.bootstrap();
} }
setActive(isActive: boolean): void { setActive(isActive: boolean): void {
super.setActive(isActive); super.setActive(isActive);
this.group.setActive(isActive); this.model.setActive(isActive);
} }
layout(width: number, height: number) { layout(width: number, height: number) {
super.layout(width, height); super.layout(width, height);
this.group.layout(width, height); this.model.layout(width, height);
} }
getComponent(): IFrameworkPart { getComponent(): IFrameworkPart {
return this._group; return this._model;
} }
toJSON(): any { toJSON(): any {
return this.group.toJSON(); return this.model.toJSON();
} }
} }

View File

@ -7,13 +7,6 @@ import { Emitter, Event } from '../../events';
import { trackFocus } from '../../dom'; import { trackFocus } from '../../dom';
import { IGroupPanel } from '../groupPanel'; import { IGroupPanel } from '../groupPanel';
export interface IRenderable {
id: string;
element: HTMLElement;
onDidFocus?: Event<void>;
onDidBlur?: Event<void>;
}
export interface IContentContainer extends IDisposable { export interface IContentContainer extends IDisposable {
onDidFocus: Event<void>; onDidFocus: Event<void>;
onDidBlur: Event<void>; onDidBlur: Event<void>;
@ -25,76 +18,10 @@ export interface IContentContainer extends IDisposable {
hide(): void; 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 export class ContentContainer
extends CompositeDisposable extends CompositeDisposable
implements IContentContainer { implements IContentContainer
{
private _element: HTMLElement; private _element: HTMLElement;
private panel: IGroupPanel | undefined; private panel: IGroupPanel | undefined;
private disposable = new MutableDisposable(); private disposable = new MutableDisposable();
@ -145,8 +72,8 @@ export class ContentContainer
const disposable = new CompositeDisposable(); const disposable = new CompositeDisposable();
if (this.panel.view) { if (this.panel.view) {
const _onDidFocus: Event<void> = this.panel.view.content const _onDidFocus: Event<void> =
.onDidFocus!; this.panel.view.content.onDidFocus!;
const _onDidBlur: Event<void> = this.panel.view.content.onDidBlur!; const _onDidBlur: Event<void> = this.panel.view.content.onDidBlur!;
const { onDidFocus, onDidBlur } = trackFocus(this._element); const { onDidFocus, onDidBlur } = trackFocus(this._element);

View File

@ -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();
}
}

View File

@ -8,7 +8,6 @@ import { ITab, MouseEventKind, Tab } from '../tab';
import { removeClasses, addClasses } from '../../dom'; import { removeClasses, addClasses } from '../../dom';
import { DroptargetEvent, Position } from '../../dnd/droptarget'; import { DroptargetEvent, Position } from '../../dnd/droptarget';
import { last } from '../../array'; import { last } from '../../array';
import { focusedElement } from '../../focusedElement';
import { IGroupPanel } from '../groupPanel'; import { IGroupPanel } from '../groupPanel';
import { IDockviewComponent } from '../../dockview/dockviewComponent'; import { IDockviewComponent } from '../../dockview/dockviewComponent';
import { LocalSelectionTransfer } from '../../dnd/dataTransfer'; import { LocalSelectionTransfer } from '../../dnd/dataTransfer';
@ -40,7 +39,8 @@ export interface ITabsContainer extends IDisposable {
export class TabsContainer export class TabsContainer
extends CompositeDisposable extends CompositeDisposable
implements ITabsContainer { implements ITabsContainer
{
private readonly _element: HTMLElement; private readonly _element: HTMLElement;
private readonly tabContainer: HTMLElement; private readonly tabContainer: HTMLElement;
private readonly actionContainer: HTMLElement; private readonly actionContainer: HTMLElement;
@ -281,8 +281,8 @@ export class TabsContainer
const disposable = CompositeDisposable.from( const disposable = CompositeDisposable.from(
tabToAdd.onChanged((event) => { tabToAdd.onChanged((event) => {
const alreadyFocused = const alreadyFocused =
panel.id === this.group.group.activePanel?.id && panel.id === this.group.model.activePanel?.id &&
this.group.group.isAncestor(focusedElement.element!); this.group.model.isContentFocused();
this.accessor.fireMouseEvent({ ...event, panel, tab: true }); this.accessor.fireMouseEvent({ ...event, panel, tab: true });
const isLeftClick = event.event.button === 0; const isLeftClick = event.event.button === 0;
@ -293,7 +293,7 @@ export class TabsContainer
switch (event.kind) { switch (event.kind) {
case MouseEventKind.CLICK: case MouseEventKind.CLICK:
this.group.group.openPanel(panel, { this.group.model.openPanel(panel, {
skipFocus: alreadyFocused, skipFocus: alreadyFocused,
}); });
break; break;

View File

@ -7,6 +7,13 @@ import { GroupviewPanel } from './groupviewPanel';
import { Event } from '../events'; import { Event } from '../events';
import { WrappedTab } from '../dockview/components/tab/defaultTab'; import { WrappedTab } from '../dockview/components/tab/defaultTab';
export interface IRenderable {
id: string;
element: HTMLElement;
onDidFocus?: Event<void>;
onDidBlur?: Event<void>;
}
export interface HeaderPartInitParameters { export interface HeaderPartInitParameters {
title: string; title: string;
suppressClosable?: boolean; suppressClosable?: boolean;

View File

@ -15,10 +15,6 @@ import { PanelCollection, PanelParameters } from '../types';
import { watchElementResize } from '../../dom'; import { watchElementResize } from '../../dom';
import { IContentRenderer, ITabRenderer } from '../../groupview/types'; import { IContentRenderer, ITabRenderer } from '../../groupview/types';
export interface PanelCollection1<T extends IDockviewPanelProps> {
[name: string]: React.FunctionComponent<T>;
}
export interface IGroupPanelBaseProps<T extends {} = Record<string, any>> export interface IGroupPanelBaseProps<T extends {} = Record<string, any>>
extends PanelParameters<T> { extends PanelParameters<T> {
api: DockviewPanelApi; api: DockviewPanelApi;