mirror of
https://github.com/mathuo/dockview
synced 2025-02-13 11:55:45 +00:00
Merge branch 'master' of https://github.com/mathuo/dockview into 326-possible-to-stop-floating-windows-being-dragged-outside-visible-dockview-area
This commit is contained in:
commit
16a49c7b27
@ -2,6 +2,7 @@ import { fireEvent } from '@testing-library/dom';
|
|||||||
import { GroupDragHandler } from '../../dnd/groupDragHandler';
|
import { GroupDragHandler } from '../../dnd/groupDragHandler';
|
||||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||||
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
|
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
|
||||||
|
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
||||||
|
|
||||||
describe('groupDragHandler', () => {
|
describe('groupDragHandler', () => {
|
||||||
test('that the dnd transfer object is setup and torndown', () => {
|
test('that the dnd transfer object is setup and torndown', () => {
|
||||||
@ -16,7 +17,11 @@ describe('groupDragHandler', () => {
|
|||||||
});
|
});
|
||||||
const group = new groupMock();
|
const group = new groupMock();
|
||||||
|
|
||||||
const cut = new GroupDragHandler(element, 'test_accessor_id', group);
|
const cut = new GroupDragHandler(
|
||||||
|
element,
|
||||||
|
{ id: 'test_accessor_id' } as DockviewComponent,
|
||||||
|
group
|
||||||
|
);
|
||||||
|
|
||||||
fireEvent.dragStart(element, new Event('dragstart'));
|
fireEvent.dragStart(element, new Event('dragstart'));
|
||||||
|
|
||||||
@ -54,7 +59,11 @@ describe('groupDragHandler', () => {
|
|||||||
});
|
});
|
||||||
const group = new groupMock();
|
const group = new groupMock();
|
||||||
|
|
||||||
const cut = new GroupDragHandler(element, 'accessor_id', group);
|
const cut = new GroupDragHandler(
|
||||||
|
element,
|
||||||
|
{ id: 'accessor_id' } as DockviewComponent,
|
||||||
|
group
|
||||||
|
);
|
||||||
|
|
||||||
const event = new KeyboardEvent('dragstart', { shiftKey: false });
|
const event = new KeyboardEvent('dragstart', { shiftKey: false });
|
||||||
|
|
||||||
@ -82,7 +91,11 @@ describe('groupDragHandler', () => {
|
|||||||
});
|
});
|
||||||
const group = new groupMock();
|
const group = new groupMock();
|
||||||
|
|
||||||
const cut = new GroupDragHandler(element, 'accessor_id', group);
|
const cut = new GroupDragHandler(
|
||||||
|
element,
|
||||||
|
{ id: 'accessor_id' } as DockviewComponent,
|
||||||
|
group
|
||||||
|
);
|
||||||
|
|
||||||
const event = new KeyboardEvent('dragstart', { shiftKey: false });
|
const event = new KeyboardEvent('dragstart', { shiftKey: false });
|
||||||
|
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
import { fireEvent } from '@testing-library/dom';
|
import { fireEvent } from '@testing-library/dom';
|
||||||
import { LocalSelectionTransfer, PanelTransfer } from '../../../dnd/dataTransfer';
|
import {
|
||||||
|
LocalSelectionTransfer,
|
||||||
|
PanelTransfer,
|
||||||
|
} from '../../../dnd/dataTransfer';
|
||||||
import { DockviewComponent } from '../../../dockview/dockviewComponent';
|
import { DockviewComponent } from '../../../dockview/dockviewComponent';
|
||||||
import { DockviewGroupPanel } from '../../../dockview/dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../../dockview/dockviewGroupPanel';
|
||||||
import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel';
|
import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel';
|
||||||
import { Tab } from '../../../dockview/components/tab/tab';
|
import { Tab } from '../../../dockview/components/tab/tab';
|
||||||
|
import { IDockviewPanel } from '../../../dockview/dockviewPanel';
|
||||||
|
|
||||||
describe('tab', () => {
|
describe('tab', () => {
|
||||||
test('that empty tab has inactive-tab class', () => {
|
test('that empty tab has inactive-tab class', () => {
|
||||||
const accessorMock = jest.fn();
|
const accessorMock = jest.fn();
|
||||||
const groupMock = jest.fn();
|
const groupMock = jest.fn();
|
||||||
|
|
||||||
const cut = new Tab('panelId', new accessorMock(), new groupMock());
|
const cut = new Tab(
|
||||||
|
{ id: 'panelId' } as IDockviewPanel,
|
||||||
|
new accessorMock(),
|
||||||
|
new groupMock()
|
||||||
|
);
|
||||||
|
|
||||||
expect(cut.element.className).toBe('tab inactive-tab');
|
expect(cut.element.className).toBe('tab inactive-tab');
|
||||||
});
|
});
|
||||||
@ -19,7 +27,11 @@ describe('tab', () => {
|
|||||||
const accessorMock = jest.fn();
|
const accessorMock = jest.fn();
|
||||||
const groupMock = jest.fn();
|
const groupMock = jest.fn();
|
||||||
|
|
||||||
const cut = new Tab('panelId', new accessorMock(), new groupMock());
|
const cut = new Tab(
|
||||||
|
{ id: 'panelId' } as IDockviewPanel,
|
||||||
|
new accessorMock(),
|
||||||
|
new groupMock()
|
||||||
|
);
|
||||||
|
|
||||||
cut.setActive(true);
|
cut.setActive(true);
|
||||||
expect(cut.element.className).toBe('tab active-tab');
|
expect(cut.element.className).toBe('tab active-tab');
|
||||||
@ -54,7 +66,11 @@ describe('tab', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
||||||
|
|
||||||
const cut = new Tab('panelId', accessor, groupPanel);
|
const cut = new Tab(
|
||||||
|
{ id: 'panelId' } as IDockviewPanel,
|
||||||
|
accessor,
|
||||||
|
groupPanel
|
||||||
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
@ -99,7 +115,11 @@ describe('tab', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
||||||
|
|
||||||
const cut = new Tab('panel1', accessor, groupPanel);
|
const cut = new Tab(
|
||||||
|
{ id: 'panel1' } as IDockviewPanel,
|
||||||
|
accessor,
|
||||||
|
groupPanel
|
||||||
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
@ -149,7 +169,11 @@ describe('tab', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
||||||
|
|
||||||
const cut = new Tab('panel1', accessor, groupPanel);
|
const cut = new Tab(
|
||||||
|
{ id: 'panel1' } as IDockviewPanel,
|
||||||
|
accessor,
|
||||||
|
groupPanel
|
||||||
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
@ -199,7 +223,11 @@ describe('tab', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
||||||
|
|
||||||
const cut = new Tab('panel1', accessor, groupPanel);
|
const cut = new Tab(
|
||||||
|
{ id: 'panel1' } as IDockviewPanel,
|
||||||
|
accessor,
|
||||||
|
groupPanel
|
||||||
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
@ -255,7 +283,11 @@ describe('tab', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
const groupPanel = new groupPanelMock() as DockviewGroupPanel;
|
||||||
|
|
||||||
const cut = new Tab('panel1', accessor, groupPanel);
|
const cut = new Tab(
|
||||||
|
{ id: 'panel1' } as IDockviewPanel,
|
||||||
|
accessor,
|
||||||
|
groupPanel
|
||||||
|
);
|
||||||
|
|
||||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||||
() => 100
|
() => 100
|
||||||
|
@ -13,6 +13,10 @@ import { IDockviewPanel } from '../../dockview/dockviewPanel';
|
|||||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
||||||
import { fireEvent } from '@testing-library/dom';
|
import { fireEvent } from '@testing-library/dom';
|
||||||
import { getPanelData } from '../../dnd/dataTransfer';
|
import { getPanelData } from '../../dnd/dataTransfer';
|
||||||
|
import {
|
||||||
|
GroupDragEvent,
|
||||||
|
TabDragEvent,
|
||||||
|
} from '../../dockview/components/titlebar/tabsContainer';
|
||||||
|
|
||||||
class PanelContentPartTest implements IContentRenderer {
|
class PanelContentPartTest implements IContentRenderer {
|
||||||
element: HTMLElement = document.createElement('div');
|
element: HTMLElement = document.createElement('div');
|
||||||
@ -3979,4 +3983,84 @@ describe('dockviewComponent', () => {
|
|||||||
});
|
});
|
||||||
expect(showDndOverlay).toBeCalledTimes(5);
|
expect(showDndOverlay).toBeCalledTimes(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('that dragging a tab triggers onWillDragPanel', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
const dockview = new DockviewComponent({
|
||||||
|
parentElement: container,
|
||||||
|
components: {
|
||||||
|
default: PanelContentPartTest,
|
||||||
|
},
|
||||||
|
tabComponents: {
|
||||||
|
test_tab_id: PanelTabPartTest,
|
||||||
|
},
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
});
|
||||||
|
|
||||||
|
dockview.layout(1000, 500);
|
||||||
|
|
||||||
|
dockview.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
const tabDragEvents: TabDragEvent[] = [];
|
||||||
|
const groupDragEvents: GroupDragEvent[] = [];
|
||||||
|
|
||||||
|
dockview.onWillDragPanel((event) => {
|
||||||
|
tabDragEvents.push(event);
|
||||||
|
});
|
||||||
|
dockview.onWillDragGroup((event) => {
|
||||||
|
groupDragEvents.push(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
const el = dockview.element.querySelector('.tab')!;
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
|
||||||
|
fireEvent.dragStart(el);
|
||||||
|
|
||||||
|
expect(tabDragEvents.length).toBe(1);
|
||||||
|
expect(groupDragEvents.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('that dragging a group triggers onWillDragGroup', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
const dockview = new DockviewComponent({
|
||||||
|
parentElement: container,
|
||||||
|
components: {
|
||||||
|
default: PanelContentPartTest,
|
||||||
|
},
|
||||||
|
tabComponents: {
|
||||||
|
test_tab_id: PanelTabPartTest,
|
||||||
|
},
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
});
|
||||||
|
|
||||||
|
dockview.layout(1000, 500);
|
||||||
|
|
||||||
|
dockview.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
const tabDragEvents: TabDragEvent[] = [];
|
||||||
|
const groupDragEvents: GroupDragEvent[] = [];
|
||||||
|
|
||||||
|
dockview.onWillDragPanel((event) => {
|
||||||
|
tabDragEvents.push(event);
|
||||||
|
});
|
||||||
|
dockview.onWillDragGroup((event) => {
|
||||||
|
groupDragEvents.push(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
const el = dockview.element.querySelector('.void-container')!;
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
|
||||||
|
fireEvent.dragStart(el);
|
||||||
|
|
||||||
|
expect(tabDragEvents.length).toBe(0);
|
||||||
|
expect(groupDragEvents.length).toBe(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -38,6 +38,10 @@ import {
|
|||||||
import { Emitter, Event } from '../events';
|
import { Emitter, Event } from '../events';
|
||||||
import { IDockviewPanel } from '../dockview/dockviewPanel';
|
import { IDockviewPanel } from '../dockview/dockviewPanel';
|
||||||
import { PaneviewDropEvent } from '../paneview/draggablePaneviewPanel';
|
import { PaneviewDropEvent } from '../paneview/draggablePaneviewPanel';
|
||||||
|
import {
|
||||||
|
GroupDragEvent,
|
||||||
|
TabDragEvent,
|
||||||
|
} from '../dockview/components/titlebar/tabsContainer';
|
||||||
|
|
||||||
export interface CommonApi<T = any> {
|
export interface CommonApi<T = any> {
|
||||||
readonly height: number;
|
readonly height: number;
|
||||||
@ -118,7 +122,9 @@ export class SplitviewApi implements CommonApi<SerializedSplitview> {
|
|||||||
return this.component.layout(width, height);
|
return this.component.layout(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
addPanel<T extends object = Parameters>(options: AddSplitviewComponentOptions<T>): ISplitviewPanel {
|
addPanel<T extends object = Parameters>(
|
||||||
|
options: AddSplitviewComponentOptions<T>
|
||||||
|
): ISplitviewPanel {
|
||||||
return this.component.addPanel(options);
|
return this.component.addPanel(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +219,9 @@ export class PaneviewApi implements CommonApi<SerializedPaneview> {
|
|||||||
this.component.layout(width, height);
|
this.component.layout(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
addPanel<T extends object = Parameters>(options: AddPaneviewComponentOptions<T>): IPaneviewPanel {
|
addPanel<T extends object = Parameters>(
|
||||||
|
options: AddPaneviewComponentOptions<T>
|
||||||
|
): IPaneviewPanel {
|
||||||
return this.component.addPanel(options);
|
return this.component.addPanel(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,7 +305,9 @@ export class GridviewApi implements CommonApi<SerializedGridviewComponent> {
|
|||||||
this.component.layout(width, height, force);
|
this.component.layout(width, height, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
addPanel<T extends object = Parameters>(options: AddComponentOptions<T>): IGridviewPanel {
|
addPanel<T extends object = Parameters>(
|
||||||
|
options: AddComponentOptions<T>
|
||||||
|
): IGridviewPanel {
|
||||||
return this.component.addPanel(options);
|
return this.component.addPanel(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,6 +412,14 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
return this.component.onDidDrop;
|
return this.component.onDidDrop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get onWillDragGroup(): Event<GroupDragEvent> {
|
||||||
|
return this.component.onWillDragGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
get onWillDragPanel(): Event<TabDragEvent> {
|
||||||
|
return this.component.onWillDragPanel;
|
||||||
|
}
|
||||||
|
|
||||||
get panels(): IDockviewPanel[] {
|
get panels(): IDockviewPanel[] {
|
||||||
return this.component.panels;
|
return this.component.panels;
|
||||||
}
|
}
|
||||||
@ -432,7 +450,9 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
this.component.layout(width, height, force);
|
this.component.layout(width, height, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
addPanel<T extends object = Parameters>(options: AddPanelOptions<T>): IDockviewPanel {
|
addPanel<T extends object = Parameters>(
|
||||||
|
options: AddPanelOptions<T>
|
||||||
|
): IDockviewPanel {
|
||||||
return this.component.addPanel(options);
|
return this.component.addPanel(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ export abstract class DragHandler extends CompositeDisposable {
|
|||||||
private readonly dataDisposable = new MutableDisposable();
|
private readonly dataDisposable = new MutableDisposable();
|
||||||
private readonly pointerEventsDisposable = new MutableDisposable();
|
private readonly pointerEventsDisposable = new MutableDisposable();
|
||||||
|
|
||||||
private readonly _onDragStart = new Emitter<void>();
|
private readonly _onDragStart = new Emitter<DragEvent>();
|
||||||
readonly onDragStart = this._onDragStart.event;
|
readonly onDragStart = this._onDragStart.event;
|
||||||
|
|
||||||
constructor(protected readonly el: HTMLElement) {
|
constructor(protected readonly el: HTMLElement) {
|
||||||
@ -25,7 +25,7 @@ export abstract class DragHandler extends CompositeDisposable {
|
|||||||
this.configure();
|
this.configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getData(dataTransfer?: DataTransfer | null): IDisposable;
|
abstract getData(event: DragEvent): IDisposable;
|
||||||
|
|
||||||
protected isCancelled(_event: DragEvent): boolean {
|
protected isCancelled(_event: DragEvent): boolean {
|
||||||
return false;
|
return false;
|
||||||
@ -35,7 +35,7 @@ export abstract class DragHandler extends CompositeDisposable {
|
|||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onDragStart,
|
this._onDragStart,
|
||||||
addDisposableListener(this.el, 'dragstart', (event) => {
|
addDisposableListener(this.el, 'dragstart', (event) => {
|
||||||
if (this.isCancelled(event)) {
|
if (event.defaultPrevented || this.isCancelled(event)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -60,24 +60,29 @@ export abstract class DragHandler extends CompositeDisposable {
|
|||||||
this.el.classList.add('dv-dragged');
|
this.el.classList.add('dv-dragged');
|
||||||
setTimeout(() => this.el.classList.remove('dv-dragged'), 0);
|
setTimeout(() => this.el.classList.remove('dv-dragged'), 0);
|
||||||
|
|
||||||
this.dataDisposable.value = this.getData(event.dataTransfer);
|
this.dataDisposable.value = this.getData(event);
|
||||||
|
this._onDragStart.fire(event);
|
||||||
|
|
||||||
if (event.dataTransfer) {
|
if (event.dataTransfer) {
|
||||||
event.dataTransfer.effectAllowed = 'move';
|
event.dataTransfer.effectAllowed = 'move';
|
||||||
|
|
||||||
/**
|
const hasData = event.dataTransfer.items.length > 0;
|
||||||
* Although this is not used by dockview many third party dnd libraries will check
|
|
||||||
* dataTransfer.types to determine valid drag events.
|
if (!hasData) {
|
||||||
*
|
/**
|
||||||
* For example: in react-dnd if dataTransfer.types is not set then the dragStart event will be cancelled
|
* Although this is not used by dockview many third party dnd libraries will check
|
||||||
* through .preventDefault(). Since this is applied globally to all drag events this would break dockviews
|
* dataTransfer.types to determine valid drag events.
|
||||||
* dnd logic. You can see the code at
|
*
|
||||||
* https://github.com/react-dnd/react-dnd/blob/main/packages/backend-html5/src/HTML5BackendImpl.ts#L542
|
* For example: in react-dnd if dataTransfer.types is not set then the dragStart event will be cancelled
|
||||||
*/
|
* through .preventDefault(). Since this is applied globally to all drag events this would break dockviews
|
||||||
event.dataTransfer.setData(
|
* dnd logic. You can see the code at
|
||||||
'text/plain',
|
* https://github.com/react-dnd/react-dnd/blob/main/packages/backend-html5/src/HTML5BackendImpl.ts#L542
|
||||||
'__dockview_internal_drag_event__'
|
*/
|
||||||
);
|
event.dataTransfer.setData(
|
||||||
|
'text/plain',
|
||||||
|
'__dockview_internal_drag_event__'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
addDisposableListener(this.el, 'dragend', () => {
|
addDisposableListener(this.el, 'dragend', () => {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DockviewComponent } from '../dockview/dockviewComponent';
|
||||||
import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel';
|
||||||
import { quasiPreventDefault } from '../dom';
|
import { quasiPreventDefault } from '../dom';
|
||||||
import { addDisposableListener } from '../events';
|
import { addDisposableListener } from '../events';
|
||||||
@ -12,7 +13,7 @@ export class GroupDragHandler extends DragHandler {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
private readonly accessorId: string,
|
private readonly accessor: DockviewComponent,
|
||||||
private readonly group: DockviewGroupPanel
|
private readonly group: DockviewGroupPanel
|
||||||
) {
|
) {
|
||||||
super(element);
|
super(element);
|
||||||
@ -43,9 +44,11 @@ export class GroupDragHandler extends DragHandler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getData(dataTransfer: DataTransfer | null): IDisposable {
|
getData(dragEvent: DragEvent): IDisposable {
|
||||||
|
const dataTransfer = dragEvent.dataTransfer;
|
||||||
|
|
||||||
this.panelTransfer.setData(
|
this.panelTransfer.setData(
|
||||||
[new PanelTransfer(this.accessorId, this.group.id, null)],
|
[new PanelTransfer(this.accessor.id, this.group.id, null)],
|
||||||
PanelTransfer.prototype
|
PanelTransfer.prototype
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -11,9 +11,37 @@ import { DockviewDropTargets, ITabRenderer } from '../../types';
|
|||||||
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
import { DockviewGroupPanel } from '../../dockviewGroupPanel';
|
||||||
import { DroptargetEvent, Droptarget } from '../../../dnd/droptarget';
|
import { DroptargetEvent, Droptarget } from '../../../dnd/droptarget';
|
||||||
import { DragHandler } from '../../../dnd/abstractDragHandler';
|
import { DragHandler } from '../../../dnd/abstractDragHandler';
|
||||||
|
import { IDockviewPanel } from '../../dockviewPanel';
|
||||||
|
|
||||||
|
class TabDragHandler extends DragHandler {
|
||||||
|
private readonly panelTransfer =
|
||||||
|
LocalSelectionTransfer.getInstance<PanelTransfer>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
element: HTMLElement,
|
||||||
|
private readonly accessor: DockviewComponent,
|
||||||
|
private readonly group: DockviewGroupPanel,
|
||||||
|
private readonly panel: IDockviewPanel
|
||||||
|
) {
|
||||||
|
super(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
getData(event: DragEvent): IDisposable {
|
||||||
|
this.panelTransfer.setData(
|
||||||
|
[new PanelTransfer(this.accessor.id, this.group.id, this.panel.id)],
|
||||||
|
PanelTransfer.prototype
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dispose: () => {
|
||||||
|
this.panelTransfer.clearData(PanelTransfer.prototype);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface ITab extends IDisposable {
|
export interface ITab extends IDisposable {
|
||||||
readonly panelId: string;
|
readonly panel: IDockviewPanel;
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
setContent: (element: ITabRenderer) => void;
|
setContent: (element: ITabRenderer) => void;
|
||||||
onChanged: Event<MouseEvent>;
|
onChanged: Event<MouseEvent>;
|
||||||
@ -24,7 +52,7 @@ export interface ITab extends IDisposable {
|
|||||||
export class Tab extends CompositeDisposable implements ITab {
|
export class Tab extends CompositeDisposable implements ITab {
|
||||||
private readonly _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private readonly droptarget: Droptarget;
|
private readonly droptarget: Droptarget;
|
||||||
private content?: ITabRenderer;
|
private content: ITabRenderer | undefined = undefined;
|
||||||
|
|
||||||
private readonly _onChanged = new Emitter<MouseEvent>();
|
private readonly _onChanged = new Emitter<MouseEvent>();
|
||||||
readonly onChanged: Event<MouseEvent> = this._onChanged.event;
|
readonly onChanged: Event<MouseEvent> = this._onChanged.event;
|
||||||
@ -32,12 +60,15 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
private readonly _onDropped = new Emitter<DroptargetEvent>();
|
private readonly _onDropped = new Emitter<DroptargetEvent>();
|
||||||
readonly onDrop: Event<DroptargetEvent> = this._onDropped.event;
|
readonly onDrop: Event<DroptargetEvent> = this._onDropped.event;
|
||||||
|
|
||||||
|
private readonly _onDragStart = new Emitter<DragEvent>();
|
||||||
|
readonly onDragStart = this._onDragStart.event;
|
||||||
|
|
||||||
public get element(): HTMLElement {
|
public get element(): HTMLElement {
|
||||||
return this._element;
|
return this._element;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly panelId: string,
|
public readonly panel: IDockviewPanel,
|
||||||
private readonly accessor: DockviewComponent,
|
private readonly accessor: DockviewComponent,
|
||||||
private readonly group: DockviewGroupPanel
|
private readonly group: DockviewGroupPanel
|
||||||
) {
|
) {
|
||||||
@ -50,38 +81,11 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
|
|
||||||
toggleClass(this.element, 'inactive-tab', true);
|
toggleClass(this.element, 'inactive-tab', true);
|
||||||
|
|
||||||
this.addDisposables(
|
const dragHandler = new TabDragHandler(
|
||||||
this._onChanged,
|
this._element,
|
||||||
this._onDropped,
|
this.accessor,
|
||||||
new (class Handler extends DragHandler {
|
this.group,
|
||||||
private readonly panelTransfer =
|
this.panel
|
||||||
LocalSelectionTransfer.getInstance<PanelTransfer>();
|
|
||||||
|
|
||||||
getData(): IDisposable {
|
|
||||||
this.panelTransfer.setData(
|
|
||||||
[new PanelTransfer(accessor.id, group.id, panelId)],
|
|
||||||
PanelTransfer.prototype
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispose: () => {
|
|
||||||
this.panelTransfer.clearData(
|
|
||||||
PanelTransfer.prototype
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})(this._element)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.addDisposables(
|
|
||||||
addDisposableListener(this._element, 'mousedown', (event) => {
|
|
||||||
if (event.defaultPrevented) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._onChanged.fire(event);
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.droptarget = new Droptarget(this._element, {
|
this.droptarget = new Droptarget(this._element, {
|
||||||
@ -102,7 +106,7 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.panelId !== data.panelId;
|
return this.panel.id !== data.panelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.group.model.canDisplayOverlay(
|
return this.group.model.canDisplayOverlay(
|
||||||
@ -114,6 +118,20 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
|
this._onChanged,
|
||||||
|
this._onDropped,
|
||||||
|
this._onDragStart,
|
||||||
|
dragHandler.onDragStart((event) => {
|
||||||
|
this._onDragStart.fire(event);
|
||||||
|
}),
|
||||||
|
dragHandler,
|
||||||
|
addDisposableListener(this._element, 'mousedown', (event) => {
|
||||||
|
if (event.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._onChanged.fire(event);
|
||||||
|
}),
|
||||||
this.droptarget.onDrop((event) => {
|
this.droptarget.onDrop((event) => {
|
||||||
this._onDropped.fire(event);
|
this._onDropped.fire(event);
|
||||||
}),
|
}),
|
||||||
|
@ -16,13 +16,26 @@ export interface TabDropIndexEvent {
|
|||||||
readonly index: number;
|
readonly index: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TabDragEvent {
|
||||||
|
readonly nativeEvent: DragEvent;
|
||||||
|
readonly panel: IDockviewPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupDragEvent {
|
||||||
|
readonly nativeEvent: DragEvent;
|
||||||
|
readonly group: DockviewGroupPanel;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ITabsContainer extends IDisposable {
|
export interface ITabsContainer extends IDisposable {
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
readonly panels: string[];
|
readonly panels: string[];
|
||||||
readonly size: number;
|
readonly size: number;
|
||||||
|
hidden: boolean;
|
||||||
delete: (id: string) => void;
|
delete: (id: string) => void;
|
||||||
indexOf: (id: string) => number;
|
indexOf: (id: string) => number;
|
||||||
onDrop: Event<TabDropIndexEvent>;
|
onDrop: Event<TabDropIndexEvent>;
|
||||||
|
onTabDragStart: Event<TabDragEvent>;
|
||||||
|
onGroupDragStart: Event<GroupDragEvent>;
|
||||||
setActive: (isGroupActive: boolean) => void;
|
setActive: (isGroupActive: boolean) => void;
|
||||||
setActivePanel: (panel: IDockviewPanel) => void;
|
setActivePanel: (panel: IDockviewPanel) => void;
|
||||||
isActive: (tab: ITab) => boolean;
|
isActive: (tab: ITab) => boolean;
|
||||||
@ -30,7 +43,6 @@ export interface ITabsContainer extends IDisposable {
|
|||||||
openPanel: (panel: IDockviewPanel, index?: number) => void;
|
openPanel: (panel: IDockviewPanel, index?: number) => void;
|
||||||
setRightActionsElement(element: HTMLElement | undefined): void;
|
setRightActionsElement(element: HTMLElement | undefined): void;
|
||||||
setLeftActionsElement(element: HTMLElement | undefined): void;
|
setLeftActionsElement(element: HTMLElement | undefined): void;
|
||||||
hidden: boolean;
|
|
||||||
show(): void;
|
show(): void;
|
||||||
hide(): void;
|
hide(): void;
|
||||||
}
|
}
|
||||||
@ -55,8 +67,15 @@ export class TabsContainer
|
|||||||
private readonly _onDrop = new Emitter<TabDropIndexEvent>();
|
private readonly _onDrop = new Emitter<TabDropIndexEvent>();
|
||||||
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
|
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
|
||||||
|
|
||||||
|
private readonly _onTabDragStart = new Emitter<TabDragEvent>();
|
||||||
|
readonly onTabDragStart: Event<TabDragEvent> = this._onTabDragStart.event;
|
||||||
|
|
||||||
|
private readonly _onGroupDragStart = new Emitter<GroupDragEvent>();
|
||||||
|
readonly onGroupDragStart: Event<GroupDragEvent> =
|
||||||
|
this._onGroupDragStart.event;
|
||||||
|
|
||||||
get panels(): string[] {
|
get panels(): string[] {
|
||||||
return this.tabs.map((_) => _.value.panelId);
|
return this.tabs.map((_) => _.value.panel.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
get size(): number {
|
get size(): number {
|
||||||
@ -122,7 +141,7 @@ export class TabsContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
public indexOf(id: string): number {
|
public indexOf(id: string): number {
|
||||||
return this.tabs.findIndex((tab) => tab.value.panelId === id);
|
return this.tabs.findIndex((tab) => tab.value.panel.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -131,7 +150,11 @@ export class TabsContainer
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.addDisposables(this._onDrop);
|
this.addDisposables(
|
||||||
|
this._onDrop,
|
||||||
|
this._onTabDragStart,
|
||||||
|
this._onGroupDragStart
|
||||||
|
);
|
||||||
|
|
||||||
this._element = document.createElement('div');
|
this._element = document.createElement('div');
|
||||||
this._element.className = 'tabs-and-actions-container';
|
this._element.className = 'tabs-and-actions-container';
|
||||||
@ -181,6 +204,12 @@ export class TabsContainer
|
|||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this.voidContainer,
|
this.voidContainer,
|
||||||
|
this.voidContainer.onDragStart((event) => {
|
||||||
|
this._onGroupDragStart.fire({
|
||||||
|
nativeEvent: event,
|
||||||
|
group: this.group,
|
||||||
|
});
|
||||||
|
}),
|
||||||
this.voidContainer.onDrop((event) => {
|
this.voidContainer.onDrop((event) => {
|
||||||
this._onDrop.fire({
|
this._onDrop.fire({
|
||||||
event: event.nativeEvent,
|
event: event.nativeEvent,
|
||||||
@ -260,7 +289,7 @@ export class TabsContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
public delete(id: string): void {
|
public delete(id: string): void {
|
||||||
const index = this.tabs.findIndex((tab) => tab.value.panelId === id);
|
const index = this.tabs.findIndex((tab) => tab.value.panel.id === id);
|
||||||
|
|
||||||
const tabToRemove = this.tabs.splice(index, 1)[0];
|
const tabToRemove = this.tabs.splice(index, 1)[0];
|
||||||
|
|
||||||
@ -273,7 +302,7 @@ export class TabsContainer
|
|||||||
|
|
||||||
public setActivePanel(panel: IDockviewPanel): void {
|
public setActivePanel(panel: IDockviewPanel): void {
|
||||||
this.tabs.forEach((tab) => {
|
this.tabs.forEach((tab) => {
|
||||||
const isActivePanel = panel.id === tab.value.panelId;
|
const isActivePanel = panel.id === tab.value.panel.id;
|
||||||
tab.value.setActive(isActivePanel);
|
tab.value.setActive(isActivePanel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -282,17 +311,20 @@ export class TabsContainer
|
|||||||
panel: IDockviewPanel,
|
panel: IDockviewPanel,
|
||||||
index: number = this.tabs.length
|
index: number = this.tabs.length
|
||||||
): void {
|
): void {
|
||||||
if (this.tabs.find((tab) => tab.value.panelId === panel.id)) {
|
if (this.tabs.find((tab) => tab.value.panel.id === panel.id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tabToAdd = new Tab(panel.id, this.accessor, this.group);
|
const tab = new Tab(panel, this.accessor, this.group);
|
||||||
if (!panel.view?.tab) {
|
if (!panel.view?.tab) {
|
||||||
throw new Error('invalid header component');
|
throw new Error('invalid header component');
|
||||||
}
|
}
|
||||||
tabToAdd.setContent(panel.view.tab);
|
tab.setContent(panel.view.tab);
|
||||||
|
|
||||||
const disposable = CompositeDisposable.from(
|
const disposable = new CompositeDisposable(
|
||||||
tabToAdd.onChanged((event) => {
|
tab.onDragStart((event) => {
|
||||||
|
this._onTabDragStart.fire({ nativeEvent: event, panel });
|
||||||
|
}),
|
||||||
|
tab.onChanged((event) => {
|
||||||
const isFloatingGroupsEnabled =
|
const isFloatingGroupsEnabled =
|
||||||
!this.accessor.options.disableFloatingGroups;
|
!this.accessor.options.disableFloatingGroups;
|
||||||
|
|
||||||
@ -306,10 +338,9 @@ export class TabsContainer
|
|||||||
) {
|
) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const panel = this.accessor.getGroupPanel(tabToAdd.panelId);
|
const panel = this.accessor.getGroupPanel(tab.panel.id);
|
||||||
|
|
||||||
const { top, left } =
|
const { top, left } = tab.element.getBoundingClientRect();
|
||||||
tabToAdd.element.getBoundingClientRect();
|
|
||||||
const { top: rootTop, left: rootLeft } =
|
const { top: rootTop, left: rootLeft } =
|
||||||
this.accessor.element.getBoundingClientRect();
|
this.accessor.element.getBoundingClientRect();
|
||||||
|
|
||||||
@ -338,15 +369,15 @@ export class TabsContainer
|
|||||||
skipFocus: alreadyFocused,
|
skipFocus: alreadyFocused,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
tabToAdd.onDrop((event) => {
|
tab.onDrop((event) => {
|
||||||
this._onDrop.fire({
|
this._onDrop.fire({
|
||||||
event: event.nativeEvent,
|
event: event.nativeEvent,
|
||||||
index: this.tabs.findIndex((x) => x.value === tabToAdd),
|
index: this.tabs.findIndex((x) => x.value === tab),
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const value: IValueDisposable<ITab> = { value: tabToAdd, disposable };
|
const value: IValueDisposable<ITab> = { value: tab, disposable };
|
||||||
|
|
||||||
this.addTab(value, index);
|
this.addTab(value, index);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,9 @@ export class VoidContainer extends CompositeDisposable {
|
|||||||
private readonly _onDrop = new Emitter<DroptargetEvent>();
|
private readonly _onDrop = new Emitter<DroptargetEvent>();
|
||||||
readonly onDrop: Event<DroptargetEvent> = this._onDrop.event;
|
readonly onDrop: Event<DroptargetEvent> = this._onDrop.event;
|
||||||
|
|
||||||
|
private readonly _onDragStart = new Emitter<DragEvent>();
|
||||||
|
readonly onDragStart = this._onDragStart.event;
|
||||||
|
|
||||||
get element(): HTMLElement {
|
get element(): HTMLElement {
|
||||||
return this._element;
|
return this._element;
|
||||||
}
|
}
|
||||||
@ -33,12 +36,13 @@ export class VoidContainer extends CompositeDisposable {
|
|||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onDrop,
|
this._onDrop,
|
||||||
|
this._onDragStart,
|
||||||
addDisposableListener(this._element, 'click', () => {
|
addDisposableListener(this._element, 'click', () => {
|
||||||
this.accessor.doSetGroupActive(this.group);
|
this.accessor.doSetGroupActive(this.group);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const handler = new GroupDragHandler(this._element, accessor.id, group);
|
const handler = new GroupDragHandler(this._element, accessor, group);
|
||||||
|
|
||||||
this.voidDropTarget = new Droptarget(this._element, {
|
this.voidDropTarget = new Droptarget(this._element, {
|
||||||
acceptedTargetZones: ['center'],
|
acceptedTargetZones: ['center'],
|
||||||
@ -68,6 +72,9 @@ export class VoidContainer extends CompositeDisposable {
|
|||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
handler,
|
handler,
|
||||||
|
handler.onDragStart((event) => {
|
||||||
|
this._onDragStart.fire(event);
|
||||||
|
}),
|
||||||
this.voidDropTarget.onDrop((event) => {
|
this.voidDropTarget.onDrop((event) => {
|
||||||
this._onDrop.fire(event);
|
this._onDrop.fire(event);
|
||||||
}),
|
}),
|
||||||
|
@ -51,6 +51,10 @@ import {
|
|||||||
DockviewFloatingGroupPanel,
|
DockviewFloatingGroupPanel,
|
||||||
IDockviewFloatingGroupPanel,
|
IDockviewFloatingGroupPanel,
|
||||||
} from './dockviewFloatingGroupPanel';
|
} from './dockviewFloatingGroupPanel';
|
||||||
|
import {
|
||||||
|
GroupDragEvent,
|
||||||
|
TabDragEvent,
|
||||||
|
} from './components/titlebar/tabsContainer';
|
||||||
|
|
||||||
const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
|
const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
|
||||||
|
|
||||||
@ -136,6 +140,8 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
|
|||||||
readonly onDidAddPanel: Event<IDockviewPanel>;
|
readonly onDidAddPanel: Event<IDockviewPanel>;
|
||||||
readonly onDidLayoutFromJSON: Event<void>;
|
readonly onDidLayoutFromJSON: Event<void>;
|
||||||
readonly onDidActivePanelChange: Event<IDockviewPanel | undefined>;
|
readonly onDidActivePanelChange: Event<IDockviewPanel | undefined>;
|
||||||
|
readonly onWillDragPanel: Event<TabDragEvent>;
|
||||||
|
readonly onWillDragGroup: Event<GroupDragEvent>;
|
||||||
addFloatingGroup(
|
addFloatingGroup(
|
||||||
item: IDockviewPanel | DockviewGroupPanel,
|
item: IDockviewPanel | DockviewGroupPanel,
|
||||||
coord?: { x: number; y: number }
|
coord?: { x: number; y: number }
|
||||||
@ -152,6 +158,13 @@ export class DockviewComponent
|
|||||||
private _options: Exclude<DockviewComponentOptions, 'orientation'>;
|
private _options: Exclude<DockviewComponentOptions, 'orientation'>;
|
||||||
private watermark: IWatermarkRenderer | null = null;
|
private watermark: IWatermarkRenderer | null = null;
|
||||||
|
|
||||||
|
private readonly _onWillDragPanel = new Emitter<TabDragEvent>();
|
||||||
|
readonly onWillDragPanel: Event<TabDragEvent> = this._onWillDragPanel.event;
|
||||||
|
|
||||||
|
private readonly _onWillDragGroup = new Emitter<GroupDragEvent>();
|
||||||
|
readonly onWillDragGroup: Event<GroupDragEvent> =
|
||||||
|
this._onWillDragGroup.event;
|
||||||
|
|
||||||
private readonly _onDidDrop = new Emitter<DockviewDropEvent>();
|
private readonly _onDidDrop = new Emitter<DockviewDropEvent>();
|
||||||
readonly onDidDrop: Event<DockviewDropEvent> = this._onDidDrop.event;
|
readonly onDidDrop: Event<DockviewDropEvent> = this._onDidDrop.event;
|
||||||
|
|
||||||
@ -210,6 +223,12 @@ export class DockviewComponent
|
|||||||
toggleClass(this.gridview.element, 'dv-dockview', true);
|
toggleClass(this.gridview.element, 'dv-dockview', true);
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
|
this._onWillDragPanel,
|
||||||
|
this._onWillDragGroup,
|
||||||
|
this._onDidActivePanelChange,
|
||||||
|
this._onDidAddPanel,
|
||||||
|
this._onDidRemovePanel,
|
||||||
|
this._onDidLayoutFromJSON,
|
||||||
this._onDidDrop,
|
this._onDidDrop,
|
||||||
Event.any(
|
Event.any(
|
||||||
this.onDidAddGroup,
|
this.onDidAddGroup,
|
||||||
@ -1211,6 +1230,12 @@ export class DockviewComponent
|
|||||||
|
|
||||||
if (!this._groups.has(view.id)) {
|
if (!this._groups.has(view.id)) {
|
||||||
const disposable = new CompositeDisposable(
|
const disposable = new CompositeDisposable(
|
||||||
|
view.model.onTabDragStart((event) => {
|
||||||
|
this._onWillDragPanel.fire(event);
|
||||||
|
}),
|
||||||
|
view.model.onGroupDragStart((event) => {
|
||||||
|
this._onWillDragGroup.fire(event);
|
||||||
|
}),
|
||||||
view.model.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);
|
||||||
@ -1286,13 +1311,4 @@ export class DockviewComponent
|
|||||||
group.value.model.containsPanel(panel)
|
group.value.model.containsPanel(panel)
|
||||||
)?.value;
|
)?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
|
||||||
this._onDidActivePanelChange.dispose();
|
|
||||||
this._onDidAddPanel.dispose();
|
|
||||||
this._onDidRemovePanel.dispose();
|
|
||||||
this._onDidLayoutFromJSON.dispose();
|
|
||||||
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,9 @@ import {
|
|||||||
IContentContainer,
|
IContentContainer,
|
||||||
} from './components/panel/content';
|
} from './components/panel/content';
|
||||||
import {
|
import {
|
||||||
|
GroupDragEvent,
|
||||||
ITabsContainer,
|
ITabsContainer,
|
||||||
|
TabDragEvent,
|
||||||
TabsContainer,
|
TabsContainer,
|
||||||
} from './components/titlebar/tabsContainer';
|
} from './components/titlebar/tabsContainer';
|
||||||
import { DockviewDropTargets, IWatermarkRenderer } from './types';
|
import { DockviewDropTargets, IWatermarkRenderer } from './types';
|
||||||
@ -160,6 +162,13 @@ export class DockviewGroupPanelModel
|
|||||||
private readonly _onDidDrop = new Emitter<GroupviewDropEvent>();
|
private readonly _onDidDrop = new Emitter<GroupviewDropEvent>();
|
||||||
readonly onDidDrop: Event<GroupviewDropEvent> = this._onDidDrop.event;
|
readonly onDidDrop: Event<GroupviewDropEvent> = this._onDidDrop.event;
|
||||||
|
|
||||||
|
private readonly _onTabDragStart = new Emitter<TabDragEvent>();
|
||||||
|
readonly onTabDragStart: Event<TabDragEvent> = this._onTabDragStart.event;
|
||||||
|
|
||||||
|
private readonly _onGroupDragStart = new Emitter<GroupDragEvent>();
|
||||||
|
readonly onGroupDragStart: Event<GroupDragEvent> =
|
||||||
|
this._onGroupDragStart.event;
|
||||||
|
|
||||||
private readonly _onDidAddPanel = new Emitter<GroupviewChangeEvent>();
|
private readonly _onDidAddPanel = new Emitter<GroupviewChangeEvent>();
|
||||||
readonly onDidAddPanel: Event<GroupviewChangeEvent> =
|
readonly onDidAddPanel: Event<GroupviewChangeEvent> =
|
||||||
this._onDidAddPanel.event;
|
this._onDidAddPanel.event;
|
||||||
@ -315,6 +324,14 @@ export class DockviewGroupPanelModel
|
|||||||
this.locked = options.locked || false;
|
this.locked = options.locked || false;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
|
this._onTabDragStart,
|
||||||
|
this._onGroupDragStart,
|
||||||
|
this.tabsContainer.onTabDragStart((event) => {
|
||||||
|
this._onTabDragStart.fire(event);
|
||||||
|
}),
|
||||||
|
this.tabsContainer.onGroupDragStart((event) => {
|
||||||
|
this._onGroupDragStart.fire(event);
|
||||||
|
}),
|
||||||
this.tabsContainer.onDrop((event) => {
|
this.tabsContainer.onDrop((event) => {
|
||||||
this.handleDropEvent(event.event, 'center', event.index);
|
this.handleDropEvent(event.event, 'center', event.index);
|
||||||
}),
|
}),
|
||||||
|
@ -23,10 +23,6 @@ export class CompositeDisposable {
|
|||||||
return this._isDisposed;
|
return this._isDisposed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static from(...args: IDisposable[]): CompositeDisposable {
|
|
||||||
return new CompositeDisposable(...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(...args: IDisposable[]) {
|
constructor(...args: IDisposable[]) {
|
||||||
this._disposables = args;
|
this._disposables = args;
|
||||||
}
|
}
|
||||||
|
@ -365,6 +365,10 @@ return (
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Intercepting Drag Events
|
||||||
|
|
||||||
|
You can intercept drag events to attach your own metadata using the `onWillDragPanel` and `onWillDragGroup` api methods.
|
||||||
|
|
||||||
<MultiFrameworkContainer sandboxId="dnd-dockview" react={DndDockview} />
|
<MultiFrameworkContainer sandboxId="dnd-dockview" react={DndDockview} />
|
||||||
|
|
||||||
### Third Party Dnd Libraries
|
### Third Party Dnd Libraries
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
DockviewApi,
|
||||||
DockviewDndOverlayEvent,
|
DockviewDndOverlayEvent,
|
||||||
DockviewDropEvent,
|
DockviewDropEvent,
|
||||||
DockviewReact,
|
DockviewReact,
|
||||||
@ -37,13 +38,19 @@ const DraggableElement = () => (
|
|||||||
}}
|
}}
|
||||||
draggable={true}
|
draggable={true}
|
||||||
>
|
>
|
||||||
Drag me
|
Drag me into the dock
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
|
const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
|
||||||
const onReady = (event: DockviewReadyEvent) => {
|
const [api, setApi] = React.useState<DockviewApi>();
|
||||||
event.api.addPanel({
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
id: 'panel_1',
|
id: 'panel_1',
|
||||||
component: 'default',
|
component: 'default',
|
||||||
params: {
|
params: {
|
||||||
@ -51,7 +58,7 @@ const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
event.api.addPanel({
|
api.addPanel({
|
||||||
id: 'panel_2',
|
id: 'panel_2',
|
||||||
component: 'default',
|
component: 'default',
|
||||||
params: {
|
params: {
|
||||||
@ -59,7 +66,7 @@ const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
event.api.addPanel({
|
api.addPanel({
|
||||||
id: 'panel_3',
|
id: 'panel_3',
|
||||||
component: 'default',
|
component: 'default',
|
||||||
params: {
|
params: {
|
||||||
@ -67,7 +74,7 @@ const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
event.api.addPanel({
|
api.addPanel({
|
||||||
id: 'panel_4',
|
id: 'panel_4',
|
||||||
component: 'default',
|
component: 'default',
|
||||||
params: {
|
params: {
|
||||||
@ -75,6 +82,45 @@ const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
|
|||||||
},
|
},
|
||||||
position: { referencePanel: 'panel_1', direction: 'right' },
|
position: { referencePanel: 'panel_1', direction: 'right' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const panelDragDisposable = api.onWillDragPanel((event) => {
|
||||||
|
const dataTransfer = event.nativeEvent.dataTransfer;
|
||||||
|
|
||||||
|
if (dataTransfer) {
|
||||||
|
dataTransfer.setData(
|
||||||
|
'text/plain',
|
||||||
|
'Some custom panel data transfer data'
|
||||||
|
);
|
||||||
|
dataTransfer.setData(
|
||||||
|
'text/json',
|
||||||
|
'{text: "Some custom panel data transfer data"}'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupDragDisposable = api.onWillDragGroup((event) => {
|
||||||
|
const dataTransfer = event.nativeEvent.dataTransfer;
|
||||||
|
|
||||||
|
if (dataTransfer) {
|
||||||
|
dataTransfer.setData(
|
||||||
|
'text/plain',
|
||||||
|
'Some custom group data transfer data'
|
||||||
|
);
|
||||||
|
dataTransfer.setData(
|
||||||
|
'text/json',
|
||||||
|
'{text: "Some custom group data transfer data"}'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
panelDragDisposable.dispose();
|
||||||
|
groupDragDisposable.dispose();
|
||||||
|
};
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
const onReady = (event: DockviewReadyEvent) => {
|
||||||
|
setApi(event.api);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDidDrop = (event: DockviewDropEvent) => {
|
const onDidDrop = (event: DockviewDropEvent) => {
|
||||||
@ -92,6 +138,20 @@ const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onDrop = (event: React.DragEvent) => {
|
||||||
|
const dataTransfer = event.dataTransfer;
|
||||||
|
|
||||||
|
let text = 'The following dataTransfer data was found:\n';
|
||||||
|
|
||||||
|
for (let i = 0; i < dataTransfer.items.length; i++) {
|
||||||
|
const item = dataTransfer.items[i];
|
||||||
|
const value = dataTransfer.getData(item.type);
|
||||||
|
text += `type=${item.type},data=${value}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(text);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -102,6 +162,17 @@ const DndDockview = (props: { renderVisibleOnly: boolean; theme?: string }) => {
|
|||||||
>
|
>
|
||||||
<div style={{ margin: '2px 0px' }}>
|
<div style={{ margin: '2px 0px' }}>
|
||||||
<DraggableElement />
|
<DraggableElement />
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '0px 4px',
|
||||||
|
backgroundColor: 'black',
|
||||||
|
borderRadius: '2px',
|
||||||
|
color: 'white',
|
||||||
|
}}
|
||||||
|
onDrop={onDrop}
|
||||||
|
>
|
||||||
|
Drop a tab or group here to inspect the attached metadata
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DockviewReact
|
<DockviewReact
|
||||||
components={components}
|
components={components}
|
||||||
|
Loading…
Reference in New Issue
Block a user