mirror of
https://github.com/mathuo/dockview
synced 2025-07-19 16:36:04 +00:00
Merge pull request #136 from mathuo/133-dnd-enhancements
feat: dnd changes
This commit is contained in:
commit
a92fb3f554
@ -16,16 +16,14 @@ import {
|
||||
GroupOptions,
|
||||
Groupview,
|
||||
} from '../../groupview/groupview';
|
||||
import {
|
||||
DockviewPanelApi,
|
||||
DockviewPanelApiImpl,
|
||||
} from '../../api/groupPanelApi';
|
||||
import { DockviewPanelApi } from '../../api/groupPanelApi';
|
||||
import {
|
||||
DefaultGroupPanelView,
|
||||
IGroupPanelView,
|
||||
} from '../../dockview/defaultGroupPanelView';
|
||||
import { GroupPanel } from '../../groupview/groupviewPanel';
|
||||
import { DockviewApi } from '../../api/component.api';
|
||||
import { fireEvent } from '@testing-library/dom';
|
||||
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
|
||||
|
||||
class Watermark implements IWatermarkRenderer {
|
||||
public readonly element = document.createElement('div');
|
||||
@ -134,7 +132,7 @@ class TestHeaderPart implements ITabRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
class TestPanel implements IDockviewPanel {
|
||||
export class TestPanel implements IDockviewPanel {
|
||||
private _view: IGroupPanelView | undefined;
|
||||
private _group: GroupPanel | undefined;
|
||||
private _params: IGroupPanelInitParameters;
|
||||
@ -148,7 +146,7 @@ class TestPanel implements IDockviewPanel {
|
||||
}
|
||||
|
||||
get group() {
|
||||
return this._group;
|
||||
return this._group!;
|
||||
}
|
||||
|
||||
get view() {
|
||||
@ -544,7 +542,7 @@ describe('groupview', () => {
|
||||
);
|
||||
const contentContainer = groupviewContainer
|
||||
.getElementsByClassName('content-container')
|
||||
.item(0).childNodes;
|
||||
.item(0)!.childNodes;
|
||||
|
||||
const panel1 = new TestPanel('id_1', null);
|
||||
|
||||
@ -568,4 +566,246 @@ describe('groupview', () => {
|
||||
expect(contentContainer.length).toBe(1);
|
||||
expect(contentContainer.item(0)).toBe(panel3.view.content.element);
|
||||
});
|
||||
|
||||
test('that should not show drop target is external event', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
options: {
|
||||
showDndOverlay: jest.fn(),
|
||||
},
|
||||
getPanel: jest.fn(),
|
||||
};
|
||||
});
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
const cut = new Groupview(
|
||||
container,
|
||||
accessor,
|
||||
'groupviewid',
|
||||
{},
|
||||
new groupPanelMock() as GroupPanel
|
||||
);
|
||||
|
||||
const element = container
|
||||
.getElementsByClassName('content-container')
|
||||
.item(0)!;
|
||||
|
||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
||||
|
||||
fireEvent.dragEnter(element);
|
||||
fireEvent.dragOver(element);
|
||||
|
||||
expect(accessor.options.showDndOverlay).toBeCalledTimes(1);
|
||||
|
||||
expect(
|
||||
element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('that should not show drop target if dropping on self', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
options: {
|
||||
showDndOverlay: jest.fn(),
|
||||
},
|
||||
getPanel: jest.fn(),
|
||||
doSetGroupActive: jest.fn(),
|
||||
};
|
||||
});
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
const cut = new Groupview(
|
||||
container,
|
||||
accessor,
|
||||
'groupviewid',
|
||||
{},
|
||||
new groupPanelMock() as GroupPanel
|
||||
);
|
||||
|
||||
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
||||
|
||||
const element = container
|
||||
.getElementsByClassName('content-container')
|
||||
.item(0)!;
|
||||
|
||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[new PanelTransfer('testcomponentid', 'groupviewid', 'panel1')],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(element);
|
||||
fireEvent.dragOver(element);
|
||||
|
||||
expect(accessor.options.showDndOverlay).toBeCalledTimes(0);
|
||||
|
||||
expect(
|
||||
element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('that should allow drop when not dropping on self for same component id', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
options: {
|
||||
showDndOverlay: jest.fn(),
|
||||
},
|
||||
getPanel: jest.fn(),
|
||||
doSetGroupActive: jest.fn(),
|
||||
};
|
||||
});
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
const cut = new Groupview(
|
||||
container,
|
||||
accessor,
|
||||
'groupviewid',
|
||||
{},
|
||||
new groupPanelMock() as GroupPanel
|
||||
);
|
||||
|
||||
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||
|
||||
const element = container
|
||||
.getElementsByClassName('content-container')
|
||||
.item(0)!;
|
||||
|
||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[new PanelTransfer('testcomponentid', 'groupviewid', 'panel1')],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(element);
|
||||
fireEvent.dragOver(element);
|
||||
|
||||
expect(accessor.options.showDndOverlay).toBeCalledTimes(0);
|
||||
|
||||
expect(
|
||||
element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test('that should not allow drop when not dropping for different component id', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
options: {
|
||||
showDndOverlay: jest.fn(),
|
||||
},
|
||||
getPanel: jest.fn(),
|
||||
doSetGroupActive: jest.fn(),
|
||||
};
|
||||
});
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
const cut = new Groupview(
|
||||
container,
|
||||
accessor,
|
||||
'groupviewid',
|
||||
{},
|
||||
new groupPanelMock() as GroupPanel
|
||||
);
|
||||
|
||||
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||
|
||||
const element = container
|
||||
.getElementsByClassName('content-container')
|
||||
.item(0)!;
|
||||
|
||||
jest.spyOn(element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 100);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[new PanelTransfer('anothercomponentid', 'groupviewid', 'panel1')],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(element);
|
||||
fireEvent.dragOver(element);
|
||||
|
||||
expect(accessor.options.showDndOverlay).toBeCalledTimes(1);
|
||||
|
||||
expect(
|
||||
element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,8 @@
|
||||
import { fireEvent } from '@testing-library/dom';
|
||||
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
|
||||
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
||||
import { Groupview } from '../../groupview/groupview';
|
||||
import { GroupPanel } from '../../groupview/groupviewPanel';
|
||||
import { Tab } from '../../groupview/tab';
|
||||
|
||||
describe('tab', () => {
|
||||
@ -22,4 +27,251 @@ describe('tab', () => {
|
||||
cut.setActive(false);
|
||||
expect(cut.element.className).toBe('tab inactive-tab');
|
||||
});
|
||||
|
||||
test('that an external event does not render a drop target and calls through to the group model', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new Tab('panelId', accessor, groupPanel);
|
||||
|
||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(cut.element);
|
||||
fireEvent.dragOver(cut.element);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalled();
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('that if you drag over yourself no drop target is shown', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new Tab('panel1', accessor, groupPanel);
|
||||
|
||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[new PanelTransfer('testcomponentid', 'anothergroupid', 'panel1')],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(cut.element);
|
||||
fireEvent.dragOver(cut.element);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('that if you drag over another tab a drop target is shown', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new Tab('panel1', accessor, groupPanel);
|
||||
|
||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[new PanelTransfer('testcomponentid', 'anothergroupid', 'panel2')],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(cut.element);
|
||||
fireEvent.dragOver(cut.element);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test('that dropping on a tab with the same id but from a different component should not render a drop over and call through to the group model', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new Tab('panel1', accessor, groupPanel);
|
||||
|
||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[
|
||||
new PanelTransfer(
|
||||
'anothercomponentid',
|
||||
'anothergroupid',
|
||||
'panel1'
|
||||
),
|
||||
],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(cut.element);
|
||||
fireEvent.dragOver(cut.element);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(1);
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('that dropping on a tab from a different component should not render a drop over and call through to the group model', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new Tab('panel1', accessor, groupPanel);
|
||||
|
||||
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(cut.element, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[
|
||||
new PanelTransfer(
|
||||
'anothercomponentid',
|
||||
'anothergroupid',
|
||||
'panel2'
|
||||
),
|
||||
],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(cut.element);
|
||||
fireEvent.dragOver(cut.element);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(1);
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,305 @@
|
||||
import { DockviewComponent } from '../../../dockview/dockviewComponent';
|
||||
import { GroupPanel } from '../../../groupview/groupviewPanel';
|
||||
import { TabsContainer } from '../../../groupview/titlebar/tabsContainer';
|
||||
import { fireEvent } from '@testing-library/dom';
|
||||
import { Groupview } from '../../../groupview/groupview';
|
||||
import {
|
||||
LocalSelectionTransfer,
|
||||
PanelTransfer,
|
||||
} from '../../../dnd/dataTransfer';
|
||||
import { TestPanel } from '../groupview.spec';
|
||||
|
||||
describe('tabsContainer', () => {
|
||||
test('that an external event does not render a drop target and calls through to the group mode', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
||||
|
||||
const emptySpace = cut.element
|
||||
.getElementsByClassName('void-container')
|
||||
.item(0);
|
||||
|
||||
if (!emptySpace) {
|
||||
fail('element not found');
|
||||
}
|
||||
|
||||
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(emptySpace);
|
||||
fireEvent.dragOver(emptySpace);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalled();
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('that a drag over event from another tab should render a drop target', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
||||
|
||||
const emptySpace = cut.element
|
||||
.getElementsByClassName('void-container')
|
||||
.item(0);
|
||||
|
||||
if (!emptySpace) {
|
||||
fail('element not found');
|
||||
}
|
||||
|
||||
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[
|
||||
new PanelTransfer(
|
||||
'testcomponentid',
|
||||
'anothergroupid',
|
||||
'anotherpanelid'
|
||||
),
|
||||
],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(emptySpace);
|
||||
fireEvent.dragOver(emptySpace);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test('that dropping the last tab should render no drop target', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
||||
|
||||
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||
|
||||
const emptySpace = cut.element
|
||||
.getElementsByClassName('void-container')
|
||||
.item(0);
|
||||
|
||||
if (!emptySpace) {
|
||||
fail('element not found');
|
||||
}
|
||||
|
||||
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[new PanelTransfer('testcomponentid', 'anothergroupid', 'panel2')],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(emptySpace);
|
||||
fireEvent.dragOver(emptySpace);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('that dropping the first tab should render a drop target', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
||||
|
||||
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||
|
||||
const emptySpace = cut.element
|
||||
.getElementsByClassName('void-container')
|
||||
.item(0);
|
||||
|
||||
if (!emptySpace) {
|
||||
fail('element not found');
|
||||
}
|
||||
|
||||
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[new PanelTransfer('testcomponentid', 'anothergroupid', 'panel1')],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(emptySpace);
|
||||
fireEvent.dragOver(emptySpace);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test('that dropping a tab from another component should not render a drop target', () => {
|
||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
};
|
||||
});
|
||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||
return {
|
||||
canDisplayOverlay: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const groupView = new groupviewMock() as Groupview;
|
||||
|
||||
const groupPanelMock = jest.fn<Partial<GroupPanel>, []>(() => {
|
||||
return {
|
||||
id: 'testgroupid',
|
||||
model: groupView,
|
||||
};
|
||||
});
|
||||
|
||||
const accessor = new accessorMock() as DockviewComponent;
|
||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||
|
||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
||||
|
||||
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||
|
||||
const emptySpace = cut.element
|
||||
.getElementsByClassName('void-container')
|
||||
.item(0);
|
||||
|
||||
if (!emptySpace) {
|
||||
fail('element not found');
|
||||
}
|
||||
|
||||
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
|
||||
() => 100
|
||||
);
|
||||
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[
|
||||
new PanelTransfer(
|
||||
'anothercomponentid',
|
||||
'anothergroupid',
|
||||
'panel1'
|
||||
),
|
||||
],
|
||||
PanelTransfer.prototype
|
||||
);
|
||||
|
||||
fireEvent.dragEnter(emptySpace);
|
||||
fireEvent.dragOver(emptySpace);
|
||||
|
||||
expect(groupView.canDisplayOverlay).toBeCalledTimes(1);
|
||||
|
||||
expect(
|
||||
cut.element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
@ -12,6 +12,7 @@ import { GroupPanel } from '../groupview/groupviewPanel';
|
||||
import { ISplitviewStyles, Orientation } from '../splitview/core/splitview';
|
||||
import { FrameworkFactory } from '../types';
|
||||
import { DockviewDropTargets } from '../groupview/dnd';
|
||||
import { PanelTransfer } from '../dnd/dataTransfer';
|
||||
|
||||
export interface GroupPanelFrameworkComponentFactory {
|
||||
content: FrameworkFactory<IContentRenderer>;
|
||||
@ -53,6 +54,7 @@ export interface DockviewDndOverlayEvent {
|
||||
nativeEvent: DragEvent;
|
||||
target: DockviewDropTargets;
|
||||
group: GroupPanel;
|
||||
getData: () => PanelTransfer | undefined;
|
||||
}
|
||||
|
||||
export interface DockviewComponentOptions extends DockviewRenderFunctions {
|
||||
|
@ -247,7 +247,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
||||
|
||||
const data = getPanelData();
|
||||
|
||||
if (data) {
|
||||
if (data && data.viewId === this.accessor.id) {
|
||||
const groupHasOnePanelAndIsActiveDragElement =
|
||||
this._panels.length === 1 && data.groupId === this.id;
|
||||
|
||||
@ -670,6 +670,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
||||
nativeEvent: event,
|
||||
target,
|
||||
group: this.accessor.getPanel(this.id)!,
|
||||
getData: getPanelData,
|
||||
});
|
||||
}
|
||||
return false;
|
||||
|
@ -52,7 +52,7 @@ export class Tab extends CompositeDisposable implements ITab {
|
||||
|
||||
constructor(
|
||||
public readonly panelId: string,
|
||||
accessor: IDockviewComponent,
|
||||
private readonly accessor: IDockviewComponent,
|
||||
private readonly group: GroupPanel
|
||||
) {
|
||||
super();
|
||||
@ -119,7 +119,7 @@ export class Tab extends CompositeDisposable implements ITab {
|
||||
validOverlays: 'none',
|
||||
canDisplayOverlay: (event) => {
|
||||
const data = getPanelData();
|
||||
if (data) {
|
||||
if (data && this.accessor.id === data.viewId) {
|
||||
return this.panelId !== data.panelId;
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ export class TabsContainer
|
||||
canDisplayOverlay: (event) => {
|
||||
const data = getPanelData();
|
||||
|
||||
if (data) {
|
||||
if (data && this.accessor.id === data.viewId) {
|
||||
// don't show the overlay if the tab being dragged is the last panel of this group
|
||||
return last(this.tabs)?.value.panelId !== data.panelId;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ export * from './dockview/dockviewComponent';
|
||||
export * from './gridview/gridviewComponent';
|
||||
export * from './splitview/splitviewComponent';
|
||||
export * from './paneview/paneviewComponent';
|
||||
export { PaneviewComponentOptions } from './paneview/options';
|
||||
|
||||
export * from './gridview/gridviewPanel';
|
||||
export * from './splitview/splitviewPanel';
|
||||
|
@ -8,6 +8,7 @@ import { Droptarget, DroptargetEvent, Position } from '../dnd/droptarget';
|
||||
import { Emitter } from '../events';
|
||||
import { IDisposable } from '../lifecycle';
|
||||
import { Orientation } from '../splitview/core/splitview';
|
||||
import { IPaneviewComponent } from './paneviewComponent';
|
||||
import {
|
||||
IPaneviewPanel,
|
||||
PanePanelInitParameter,
|
||||
@ -27,6 +28,7 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
||||
readonly onDidDrop = this._onDidDrop.event;
|
||||
|
||||
constructor(
|
||||
private readonly accessor: IPaneviewComponent,
|
||||
id: string,
|
||||
component: string,
|
||||
headerComponent: string | undefined,
|
||||
@ -47,12 +49,13 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
||||
}
|
||||
|
||||
const id = this.id;
|
||||
const accessorId = this.accessor.id;
|
||||
this.header.draggable = true;
|
||||
|
||||
this.handler = new (class PaneDragHandler extends DragHandler {
|
||||
getData(): IDisposable {
|
||||
LocalSelectionTransfer.getInstance().setData(
|
||||
[new PaneTransfer('paneview', id)],
|
||||
[new PaneTransfer(accessorId, id)],
|
||||
PaneTransfer.prototype
|
||||
);
|
||||
|
||||
@ -68,14 +71,27 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
||||
|
||||
this.target = new Droptarget(this.element, {
|
||||
validOverlays: 'vertical',
|
||||
canDisplayOverlay: () => {
|
||||
canDisplayOverlay: (event) => {
|
||||
const data = getPaneData();
|
||||
|
||||
if (!data) {
|
||||
return true;
|
||||
if (data) {
|
||||
if (
|
||||
data.paneId !== this.id &&
|
||||
data.viewId === this.accessor.id
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return data.paneId !== this.id;
|
||||
if (this.accessor.options.showDndOverlay) {
|
||||
return this.accessor.options.showDndOverlay({
|
||||
nativeEvent: event,
|
||||
getData: getPaneData,
|
||||
panel: this,
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
@ -92,11 +108,13 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
||||
private onDrop(event: DroptargetEvent) {
|
||||
const data = getPaneData();
|
||||
|
||||
if (!data) {
|
||||
if (!data || data.viewId !== this.accessor.id) {
|
||||
// if there is no local drag event for this panel
|
||||
// or if the drag event was creating by another Paneview instance
|
||||
this._onDidDrop.fire({
|
||||
...event,
|
||||
panel: this,
|
||||
getData: () => getPaneData(),
|
||||
getData: getPaneData,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -107,10 +125,11 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
||||
|
||||
const existingPanel = containerApi.getPanel(panelId);
|
||||
if (!existingPanel) {
|
||||
// if the panel doesn't exist
|
||||
this._onDidDrop.fire({
|
||||
...event,
|
||||
panel: this,
|
||||
getData: () => getPaneData(),
|
||||
getData: getPaneData,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { FrameworkFactory } from '../types';
|
||||
import { PaneviewDndOverlayEvent } from './paneviewComponent';
|
||||
import { IPaneBodyPart, IPaneHeaderPart, PaneviewPanel } from './paneviewPanel';
|
||||
|
||||
export interface PaneviewComponentOptions {
|
||||
@ -23,4 +24,5 @@ export interface PaneviewComponentOptions {
|
||||
body: FrameworkFactory<IPaneBodyPart>;
|
||||
};
|
||||
disableDnd?: boolean;
|
||||
showDndOverlay?: (event: PaneviewDndOverlayEvent) => boolean;
|
||||
}
|
||||
|
@ -24,6 +24,16 @@ import {
|
||||
PaneviewDropEvent2,
|
||||
} from './draggablePaneviewPanel';
|
||||
import { DefaultHeader } from './defaultPaneviewHeader';
|
||||
import { sequentialNumberGenerator } from '../math';
|
||||
import { PaneTransfer } from '../dnd/dataTransfer';
|
||||
|
||||
const nextLayoutId = sequentialNumberGenerator();
|
||||
|
||||
export interface PaneviewDndOverlayEvent {
|
||||
nativeEvent: DragEvent;
|
||||
panel: IPaneviewPanel;
|
||||
getData: () => PaneTransfer | undefined;
|
||||
}
|
||||
|
||||
export interface SerializedPaneviewPanel {
|
||||
snap?: boolean;
|
||||
@ -57,9 +67,11 @@ export class PaneFramework extends DraggablePaneviewPanel {
|
||||
orientation: Orientation;
|
||||
isExpanded: boolean;
|
||||
disableDnd: boolean;
|
||||
accessor: IPaneviewComponent;
|
||||
}
|
||||
) {
|
||||
super(
|
||||
options.accessor,
|
||||
options.id,
|
||||
options.component,
|
||||
options.headerComponent,
|
||||
@ -94,11 +106,13 @@ export interface AddPaneviewComponentOptions {
|
||||
}
|
||||
|
||||
export interface IPaneviewComponent extends IDisposable {
|
||||
readonly id: string;
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
readonly minimumSize: number;
|
||||
readonly maximumSize: number;
|
||||
readonly panels: IPaneviewPanel[];
|
||||
readonly options: PaneviewComponentOptions;
|
||||
readonly onDidAddView: Event<PaneviewPanel>;
|
||||
readonly onDidRemoveView: Event<PaneviewPanel>;
|
||||
readonly onDidDrop: Event<PaneviewDropEvent2>;
|
||||
@ -120,6 +134,8 @@ export class PaneviewComponent
|
||||
extends CompositeDisposable
|
||||
implements IPaneviewComponent
|
||||
{
|
||||
private readonly _id = nextLayoutId.next();
|
||||
private _options: PaneviewComponentOptions;
|
||||
private _disposable = new MutableDisposable();
|
||||
private _viewDisposables = new Map<string, IDisposable>();
|
||||
private _paneview!: Paneview;
|
||||
@ -139,6 +155,10 @@ export class PaneviewComponent
|
||||
private readonly _onDidRemoveView = new Emitter<PaneviewPanel>();
|
||||
readonly onDidRemoveView = this._onDidRemoveView.event;
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get panels(): PaneviewPanel[] {
|
||||
return this.paneview.getPanes();
|
||||
}
|
||||
@ -179,9 +199,7 @@ export class PaneviewComponent
|
||||
: this.paneview.orthogonalSize;
|
||||
}
|
||||
|
||||
private _options: PaneviewComponentOptions;
|
||||
|
||||
get options() {
|
||||
get options(): PaneviewComponentOptions {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
@ -267,6 +285,7 @@ export class PaneviewComponent
|
||||
orientation: Orientation.VERTICAL,
|
||||
isExpanded: !!options.isExpanded,
|
||||
disableDnd: !!this.options.disableDnd,
|
||||
accessor: this,
|
||||
});
|
||||
|
||||
this.doAddPanel(view);
|
||||
@ -400,6 +419,7 @@ export class PaneviewComponent
|
||||
orientation: Orientation.VERTICAL,
|
||||
isExpanded: !!view.expanded,
|
||||
disableDnd: !!this.options.disableDnd,
|
||||
accessor: this,
|
||||
});
|
||||
|
||||
this.doAddPanel(panel);
|
||||
|
@ -137,6 +137,7 @@ export const DockviewReact = React.forwardRef(
|
||||
styles: props.hideBorders
|
||||
? { separatorBorder: 'transparent' }
|
||||
: undefined,
|
||||
showDndOverlay: props.showDndOverlay,
|
||||
});
|
||||
|
||||
domRef.current?.appendChild(dockview.element);
|
||||
|
@ -3,6 +3,7 @@ import { PaneviewPanelApi } from '../../api/paneviewPanelApi';
|
||||
import {
|
||||
PaneviewComponent,
|
||||
IPaneviewComponent,
|
||||
PaneviewDndOverlayEvent,
|
||||
} from '../../paneview/paneviewComponent';
|
||||
import { usePortalsLifecycle } from '../react';
|
||||
import { PaneviewApi } from '../../api/component.api';
|
||||
@ -33,6 +34,7 @@ export interface IPaneviewReactProps {
|
||||
className?: string;
|
||||
disableAutoResizing?: boolean;
|
||||
disableDnd?: boolean;
|
||||
showDndOverlay?: (event: PaneviewDndOverlayEvent) => boolean;
|
||||
onDidDrop?(event: PaneviewDropEvent): void;
|
||||
}
|
||||
|
||||
@ -85,6 +87,7 @@ export const PaneviewReact = React.forwardRef(
|
||||
createComponent,
|
||||
},
|
||||
},
|
||||
showDndOverlay: props.showDndOverlay,
|
||||
});
|
||||
|
||||
const api = new PaneviewApi(paneview);
|
||||
@ -144,6 +147,15 @@ export const PaneviewReact = React.forwardRef(
|
||||
};
|
||||
}, [props.onDidDrop]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!paneviewRef.current) {
|
||||
return;
|
||||
}
|
||||
paneviewRef.current.updateOptions({
|
||||
showDndOverlay: props.showDndOverlay,
|
||||
});
|
||||
}, [props.showDndOverlay]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={props.className}
|
||||
|
Loading…
x
Reference in New Issue
Block a user