mirror of
https://github.com/mathuo/dockview
synced 2025-05-09 04:58:25 +00:00
Merge pull request #177 from mathuo/176-dnd-to-edge-of-dockview
176 dnd to edge of dockview
This commit is contained in:
commit
213d3ed235
@ -10,7 +10,11 @@ describe('groupPanelApi', () => {
|
|||||||
title: 'test_title',
|
title: 'test_title',
|
||||||
};
|
};
|
||||||
|
|
||||||
const accessor: Partial<DockviewComponent> = {};
|
const accessor: Partial<DockviewComponent> = {
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
const groupViewPanel = new GroupPanel(
|
const groupViewPanel = new GroupPanel(
|
||||||
<DockviewComponent>accessor,
|
<DockviewComponent>accessor,
|
||||||
'',
|
'',
|
||||||
@ -44,7 +48,11 @@ describe('groupPanelApi', () => {
|
|||||||
id: 'test_id',
|
id: 'test_id',
|
||||||
};
|
};
|
||||||
|
|
||||||
const accessor: Partial<DockviewComponent> = {};
|
const accessor: Partial<DockviewComponent> = {
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
const groupViewPanel = new GroupPanel(
|
const groupViewPanel = new GroupPanel(
|
||||||
<DockviewComponent>accessor,
|
<DockviewComponent>accessor,
|
||||||
'',
|
'',
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
import { Droptarget, Position } from '../../dnd/droptarget';
|
import {
|
||||||
|
calculateQuadrantAsPercentage,
|
||||||
|
calculateQuadrantAsPixels,
|
||||||
|
directionToPosition,
|
||||||
|
Droptarget,
|
||||||
|
Position,
|
||||||
|
} from '../../dnd/droptarget';
|
||||||
import { fireEvent } from '@testing-library/dom';
|
import { fireEvent } from '@testing-library/dom';
|
||||||
|
|
||||||
function createOffsetDragOverEvent(params: {
|
function createOffsetDragOverEvent(params: {
|
||||||
offsetX: number;
|
clientX: number;
|
||||||
offsetY: number;
|
clientY: number;
|
||||||
}): Event {
|
}): Event {
|
||||||
const event = new Event('dragover', {
|
const event = new Event('dragover', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
});
|
});
|
||||||
Object.defineProperty(event, 'offsetX', { get: () => params.offsetX });
|
Object.defineProperty(event, 'clientX', { get: () => params.clientX });
|
||||||
Object.defineProperty(event, 'offsetY', { get: () => params.offsetY });
|
Object.defineProperty(event, 'clientY', { get: () => params.clientY });
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,12 +33,23 @@ describe('droptarget', () => {
|
|||||||
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 200);
|
jest.spyOn(element, 'clientWidth', 'get').mockImplementation(() => 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('directionToPosition', () => {
|
||||||
|
expect(directionToPosition('above')).toBe('top');
|
||||||
|
expect(directionToPosition('below')).toBe('bottom');
|
||||||
|
expect(directionToPosition('left')).toBe('left');
|
||||||
|
expect(directionToPosition('right')).toBe('right');
|
||||||
|
expect(directionToPosition('within')).toBe('center');
|
||||||
|
expect(() => directionToPosition('bad_input' as any)).toThrow(
|
||||||
|
"invalid direction 'bad_input'"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('non-directional', () => {
|
test('non-directional', () => {
|
||||||
let position: Position | undefined = undefined;
|
let position: Position | undefined = undefined;
|
||||||
|
|
||||||
droptarget = new Droptarget(element, {
|
droptarget = new Droptarget(element, {
|
||||||
canDisplayOverlay: () => true,
|
canDisplayOverlay: () => true,
|
||||||
validOverlays: 'none',
|
acceptedTargetZones: ['center'],
|
||||||
});
|
});
|
||||||
|
|
||||||
droptarget.onDrop((event) => {
|
droptarget.onDrop((event) => {
|
||||||
@ -46,7 +63,7 @@ describe('droptarget', () => {
|
|||||||
'.drop-target-dropzone'
|
'.drop-target-dropzone'
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
fireEvent.drop(target);
|
fireEvent.drop(target);
|
||||||
expect(position).toBe(Position.Center);
|
expect(position).toBe('center');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('drop', () => {
|
test('drop', () => {
|
||||||
@ -54,7 +71,7 @@ describe('droptarget', () => {
|
|||||||
|
|
||||||
droptarget = new Droptarget(element, {
|
droptarget = new Droptarget(element, {
|
||||||
canDisplayOverlay: () => true,
|
canDisplayOverlay: () => true,
|
||||||
validOverlays: 'all',
|
acceptedTargetZones: ['top', 'left', 'right', 'bottom', 'center'],
|
||||||
});
|
});
|
||||||
|
|
||||||
droptarget.onDrop((event) => {
|
droptarget.onDrop((event) => {
|
||||||
@ -73,18 +90,21 @@ describe('droptarget', () => {
|
|||||||
|
|
||||||
fireEvent(
|
fireEvent(
|
||||||
target,
|
target,
|
||||||
createOffsetDragOverEvent({ offsetX: 19, offsetY: 0 })
|
createOffsetDragOverEvent({
|
||||||
|
clientX: 19,
|
||||||
|
clientY: 0,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(position).toBeUndefined();
|
expect(position).toBeUndefined();
|
||||||
fireEvent.drop(target);
|
fireEvent.drop(target);
|
||||||
expect(position).toBe(Position.Left);
|
expect(position).toBe('left');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default', () => {
|
test('default', () => {
|
||||||
droptarget = new Droptarget(element, {
|
droptarget = new Droptarget(element, {
|
||||||
canDisplayOverlay: () => true,
|
canDisplayOverlay: () => true,
|
||||||
validOverlays: 'all',
|
acceptedTargetZones: ['top', 'left', 'right', 'bottom', 'center'],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(droptarget.state).toBeUndefined();
|
expect(droptarget.state).toBeUndefined();
|
||||||
@ -106,57 +126,204 @@ describe('droptarget', () => {
|
|||||||
|
|
||||||
fireEvent(
|
fireEvent(
|
||||||
target,
|
target,
|
||||||
createOffsetDragOverEvent({ offsetX: 19, offsetY: 0 })
|
createOffsetDragOverEvent({ clientX: 19, clientY: 0 })
|
||||||
);
|
);
|
||||||
|
|
||||||
viewQuery = element.querySelectorAll(
|
viewQuery = element.querySelectorAll(
|
||||||
'.drop-target > .drop-target-dropzone > .drop-target-selection.left'
|
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
expect(droptarget.state).toBe(Position.Left);
|
expect(droptarget.state).toBe('left');
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
element
|
||||||
|
.getElementsByClassName('drop-target-selection')
|
||||||
|
.item(0) as HTMLDivElement
|
||||||
|
).style.transform
|
||||||
|
).toBe('translateX(-25%) scaleX(0.5)');
|
||||||
|
|
||||||
fireEvent(
|
fireEvent(
|
||||||
target,
|
target,
|
||||||
createOffsetDragOverEvent({ offsetX: 40, offsetY: 19 })
|
createOffsetDragOverEvent({ clientX: 40, clientY: 19 })
|
||||||
);
|
);
|
||||||
|
|
||||||
viewQuery = element.querySelectorAll(
|
viewQuery = element.querySelectorAll(
|
||||||
'.drop-target > .drop-target-dropzone > .drop-target-selection.top'
|
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
expect(droptarget.state).toBe(Position.Top);
|
expect(droptarget.state).toBe('top');
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
element
|
||||||
|
.getElementsByClassName('drop-target-selection')
|
||||||
|
.item(0) as HTMLDivElement
|
||||||
|
).style.transform
|
||||||
|
).toBe('translateY(-25%) scaleY(0.5)');
|
||||||
|
|
||||||
fireEvent(
|
fireEvent(
|
||||||
target,
|
target,
|
||||||
createOffsetDragOverEvent({ offsetX: 160, offsetY: 81 })
|
createOffsetDragOverEvent({ clientX: 160, clientY: 81 })
|
||||||
);
|
);
|
||||||
|
|
||||||
viewQuery = element.querySelectorAll(
|
viewQuery = element.querySelectorAll(
|
||||||
'.drop-target > .drop-target-dropzone > .drop-target-selection.bottom'
|
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
expect(droptarget.state).toBe(Position.Bottom);
|
expect(droptarget.state).toBe('bottom');
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
element
|
||||||
|
.getElementsByClassName('drop-target-selection')
|
||||||
|
.item(0) as HTMLDivElement
|
||||||
|
).style.transform
|
||||||
|
).toBe('translateY(25%) scaleY(0.5)');
|
||||||
|
|
||||||
fireEvent(
|
fireEvent(
|
||||||
target,
|
target,
|
||||||
createOffsetDragOverEvent({ offsetX: 161, offsetY: 0 })
|
createOffsetDragOverEvent({ clientX: 161, clientY: 0 })
|
||||||
);
|
);
|
||||||
|
|
||||||
viewQuery = element.querySelectorAll(
|
viewQuery = element.querySelectorAll(
|
||||||
'.drop-target > .drop-target-dropzone > .drop-target-selection.right'
|
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
||||||
);
|
);
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
expect(droptarget.state).toBe(Position.Right);
|
expect(droptarget.state).toBe('right');
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
element
|
||||||
|
.getElementsByClassName('drop-target-selection')
|
||||||
|
.item(0) as HTMLDivElement
|
||||||
|
).style.transform
|
||||||
|
).toBe('translateX(25%) scaleX(0.5)');
|
||||||
|
|
||||||
fireEvent(
|
fireEvent(
|
||||||
target,
|
target,
|
||||||
createOffsetDragOverEvent({ offsetX: 100, offsetY: 50 })
|
createOffsetDragOverEvent({ clientX: 100, clientY: 50 })
|
||||||
);
|
);
|
||||||
expect(droptarget.state).toBe(Position.Center);
|
expect(droptarget.state).toBe('center');
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
element
|
||||||
|
.getElementsByClassName('drop-target-selection')
|
||||||
|
.item(0) as HTMLDivElement
|
||||||
|
).style.transform
|
||||||
|
).toBe('');
|
||||||
|
|
||||||
fireEvent.dragLeave(target);
|
fireEvent.dragLeave(target);
|
||||||
expect(droptarget.state).toBeUndefined();
|
expect(droptarget.state).toBe('center');
|
||||||
viewQuery = element.querySelectorAll('.drop-target');
|
viewQuery = element.querySelectorAll('.drop-target');
|
||||||
expect(viewQuery.length).toBe(0);
|
expect(viewQuery.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('calculateQuadrantAsPercentage', () => {
|
||||||
|
test('variety of cases', () => {
|
||||||
|
const inputs: Array<{
|
||||||
|
directions: Position[];
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
result: Position | null;
|
||||||
|
}> = [
|
||||||
|
{ directions: ['left', 'right'], x: 19, y: 50, result: 'left' },
|
||||||
|
{
|
||||||
|
directions: ['left', 'right'],
|
||||||
|
x: 81,
|
||||||
|
y: 50,
|
||||||
|
result: 'right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
directions: ['top', 'bottom'],
|
||||||
|
x: 50,
|
||||||
|
y: 19,
|
||||||
|
result: 'top',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
directions: ['top', 'bottom'],
|
||||||
|
x: 50,
|
||||||
|
y: 81,
|
||||||
|
result: 'bottom',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
directions: ['left', 'right', 'top', 'bottom', 'center'],
|
||||||
|
x: 50,
|
||||||
|
y: 50,
|
||||||
|
result: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
directions: ['left', 'right', 'top', 'bottom'],
|
||||||
|
x: 50,
|
||||||
|
y: 50,
|
||||||
|
result: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const input of inputs) {
|
||||||
|
expect(
|
||||||
|
calculateQuadrantAsPercentage(
|
||||||
|
new Set(input.directions),
|
||||||
|
input.x,
|
||||||
|
input.y,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
20
|
||||||
|
)
|
||||||
|
).toBe(input.result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('calculateQuadrantAsPixels', () => {
|
||||||
|
test('variety of cases', () => {
|
||||||
|
const inputs: Array<{
|
||||||
|
directions: Position[];
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
result: Position | null;
|
||||||
|
}> = [
|
||||||
|
{ directions: ['left', 'right'], x: 19, y: 50, result: 'left' },
|
||||||
|
{
|
||||||
|
directions: ['left', 'right'],
|
||||||
|
x: 81,
|
||||||
|
y: 50,
|
||||||
|
result: 'right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
directions: ['top', 'bottom'],
|
||||||
|
x: 50,
|
||||||
|
y: 19,
|
||||||
|
result: 'top',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
directions: ['top', 'bottom'],
|
||||||
|
x: 50,
|
||||||
|
y: 81,
|
||||||
|
result: 'bottom',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
directions: ['left', 'right', 'top', 'bottom', 'center'],
|
||||||
|
x: 50,
|
||||||
|
y: 50,
|
||||||
|
result: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
directions: ['left', 'right', 'top', 'bottom'],
|
||||||
|
x: 50,
|
||||||
|
y: 50,
|
||||||
|
result: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const input of inputs) {
|
||||||
|
expect(
|
||||||
|
calculateQuadrantAsPixels(
|
||||||
|
new Set(input.directions),
|
||||||
|
input.x,
|
||||||
|
input.y,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
20
|
||||||
|
)
|
||||||
|
).toBe(input.result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,7 @@ class PanelContentPartTest implements IContentRenderer {
|
|||||||
|
|
||||||
isDisposed: boolean = false;
|
isDisposed: boolean = false;
|
||||||
|
|
||||||
constructor(public readonly id: string, component: string) {
|
constructor(public readonly id: string, public readonly component: string) {
|
||||||
this.element.classList.add(`testpanel-${id}`);
|
this.element.classList.add(`testpanel-${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ class PanelContentPartTest implements IContentRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toJSON(): object {
|
toJSON(): object {
|
||||||
return { id: this.id };
|
return { id: this.component };
|
||||||
}
|
}
|
||||||
|
|
||||||
focus(): void {
|
focus(): void {
|
||||||
@ -253,9 +253,9 @@ describe('dockviewComponent', () => {
|
|||||||
const panel4 = dockview.getGroupPanel('panel4');
|
const panel4 = dockview.getGroupPanel('panel4');
|
||||||
|
|
||||||
const group1 = panel1!.group;
|
const group1 = panel1!.group;
|
||||||
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', Position.Right);
|
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', 'right');
|
||||||
const group2 = panel1!.group;
|
const group2 = panel1!.group;
|
||||||
dockview.moveGroupOrPanel(group2, group1.id, 'panel3', Position.Center);
|
dockview.moveGroupOrPanel(group2, group1.id, 'panel3', 'center');
|
||||||
|
|
||||||
expect(dockview.activeGroup).toBe(group2);
|
expect(dockview.activeGroup).toBe(group2);
|
||||||
expect(dockview.activeGroup!.model.activePanel).toBe(panel3);
|
expect(dockview.activeGroup!.model.activePanel).toBe(panel3);
|
||||||
@ -302,12 +302,12 @@ describe('dockviewComponent', () => {
|
|||||||
component: 'default',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel1 = dockview.getGroupPanel('panel1');
|
const panel1 = dockview.getGroupPanel('panel1')!;
|
||||||
const panel2 = dockview.getGroupPanel('panel2');
|
const panel2 = dockview.getGroupPanel('panel2')!;
|
||||||
const group1 = panel1.group;
|
const group1 = panel1.group;
|
||||||
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', Position.Right);
|
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', 'right');
|
||||||
const group2 = panel1.group;
|
const group2 = panel1.group;
|
||||||
dockview.moveGroupOrPanel(group2, group1.id, 'panel3', Position.Center);
|
dockview.moveGroupOrPanel(group2, group1.id, 'panel3', 'center');
|
||||||
|
|
||||||
expect(dockview.size).toBe(2);
|
expect(dockview.size).toBe(2);
|
||||||
expect(dockview.totalPanels).toBe(4);
|
expect(dockview.totalPanels).toBe(4);
|
||||||
@ -345,10 +345,10 @@ describe('dockviewComponent', () => {
|
|||||||
component: 'default',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel1 = dockview.getGroupPanel('panel1');
|
const panel1 = dockview.getGroupPanel('panel1')!;
|
||||||
const panel2 = dockview.getGroupPanel('panel2');
|
const panel2 = dockview.getGroupPanel('panel2')!;
|
||||||
const panel3 = dockview.getGroupPanel('panel3');
|
const panel3 = dockview.getGroupPanel('panel3')!;
|
||||||
const panel4 = dockview.getGroupPanel('panel4');
|
const panel4 = dockview.getGroupPanel('panel4')!;
|
||||||
|
|
||||||
expect(panel1.api.isActive).toBeFalsy();
|
expect(panel1.api.isActive).toBeFalsy();
|
||||||
expect(panel2.api.isActive).toBeFalsy();
|
expect(panel2.api.isActive).toBeFalsy();
|
||||||
@ -370,9 +370,9 @@ describe('dockviewComponent', () => {
|
|||||||
expect(panel4.api.isActive).toBeFalsy();
|
expect(panel4.api.isActive).toBeFalsy();
|
||||||
|
|
||||||
const group1 = panel1.group;
|
const group1 = panel1.group;
|
||||||
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', Position.Right);
|
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', 'right');
|
||||||
const group2 = panel1.group;
|
const group2 = panel1.group;
|
||||||
dockview.moveGroupOrPanel(group2, group1.id, 'panel3', Position.Center);
|
dockview.moveGroupOrPanel(group2, group1.id, 'panel3', 'center');
|
||||||
|
|
||||||
expect(dockview.size).toBe(2);
|
expect(dockview.size).toBe(2);
|
||||||
expect(panel1.group).toBe(panel3.group);
|
expect(panel1.group).toBe(panel3.group);
|
||||||
@ -425,9 +425,8 @@ describe('dockviewComponent', () => {
|
|||||||
expect(dockview.size).toBe(1);
|
expect(dockview.size).toBe(1);
|
||||||
expect(dockview.totalPanels).toBe(2);
|
expect(dockview.totalPanels).toBe(2);
|
||||||
|
|
||||||
const panel1 = dockview.getGroupPanel('panel1');
|
const panel1 = dockview.getGroupPanel('panel1')!;
|
||||||
const panel2 = dockview.getGroupPanel('panel2');
|
const panel2 = dockview.getGroupPanel('panel2')!;
|
||||||
|
|
||||||
expect(panel1.group).toBe(panel2.group);
|
expect(panel1.group).toBe(panel2.group);
|
||||||
|
|
||||||
const group = panel1.group;
|
const group = panel1.group;
|
||||||
@ -440,7 +439,7 @@ describe('dockviewComponent', () => {
|
|||||||
expect(group.model.indexOf(panel1)).toBe(0);
|
expect(group.model.indexOf(panel1)).toBe(0);
|
||||||
expect(group.model.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', 'right');
|
||||||
|
|
||||||
expect(dockview.size).toBe(2);
|
expect(dockview.size).toBe(2);
|
||||||
expect(dockview.totalPanels).toBe(2);
|
expect(dockview.totalPanels).toBe(2);
|
||||||
@ -489,8 +488,8 @@ describe('dockviewComponent', () => {
|
|||||||
|
|
||||||
expect(viewQuery.length).toBe(1);
|
expect(viewQuery.length).toBe(1);
|
||||||
|
|
||||||
const group = dockview.getGroupPanel('panel1').group;
|
const group = dockview.getGroupPanel('panel1')!.group;
|
||||||
dockview.moveGroupOrPanel(group, group.id, 'panel1', Position.Right);
|
dockview.moveGroupOrPanel(group, group.id, 'panel1', 'right');
|
||||||
|
|
||||||
viewQuery = container.querySelectorAll(
|
viewQuery = container.querySelectorAll(
|
||||||
'.branch-node > .split-view-container > .view-container > .view'
|
'.branch-node > .split-view-container > .view-container > .view'
|
||||||
@ -975,7 +974,7 @@ describe('dockviewComponent', () => {
|
|||||||
panel2.group!,
|
panel2.group!,
|
||||||
panel5.group!.id,
|
panel5.group!.id,
|
||||||
panel5.id,
|
panel5.id,
|
||||||
Position.Center
|
'center'
|
||||||
);
|
);
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
{ type: 'REMOVE_PANEL', panel: panel5 },
|
{ type: 'REMOVE_PANEL', panel: panel5 },
|
||||||
@ -994,7 +993,7 @@ describe('dockviewComponent', () => {
|
|||||||
panel2.group!,
|
panel2.group!,
|
||||||
panel4.group!.id,
|
panel4.group!.id,
|
||||||
panel4.id,
|
panel4.id,
|
||||||
Position.Center
|
'center'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
@ -1314,7 +1313,7 @@ describe('dockviewComponent', () => {
|
|||||||
panel1.group,
|
panel1.group,
|
||||||
panel2.group.id,
|
panel2.group.id,
|
||||||
'panel2',
|
'panel2',
|
||||||
Position.Left
|
'left'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(panel1Spy).not.toHaveBeenCalled();
|
expect(panel1Spy).not.toHaveBeenCalled();
|
||||||
@ -1355,7 +1354,7 @@ describe('dockviewComponent', () => {
|
|||||||
panel1.group,
|
panel1.group,
|
||||||
panel2.group.id,
|
panel2.group.id,
|
||||||
'panel2',
|
'panel2',
|
||||||
Position.Center
|
'center'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(panel1Spy).not.toHaveBeenCalled();
|
expect(panel1Spy).not.toHaveBeenCalled();
|
||||||
@ -1394,7 +1393,7 @@ describe('dockviewComponent', () => {
|
|||||||
panel1.group,
|
panel1.group,
|
||||||
panel1.group.id,
|
panel1.group.id,
|
||||||
'panel1',
|
'panel1',
|
||||||
Position.Center,
|
'center',
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1515,6 +1514,53 @@ describe('dockviewComponent', () => {
|
|||||||
expect(panel2Spy).toBeCalledTimes(1);
|
expect(panel2Spy).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('move entire group into another group', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
const dockview = new DockviewComponent(container, {
|
||||||
|
components: { default: PanelContentPartTest },
|
||||||
|
});
|
||||||
|
|
||||||
|
dockview.layout(500, 1000);
|
||||||
|
|
||||||
|
const panel1 = dockview.addPanel({
|
||||||
|
id: 'panel1',
|
||||||
|
component: 'default',
|
||||||
|
tabComponent: 'default',
|
||||||
|
});
|
||||||
|
const panel2 = dockview.addPanel({
|
||||||
|
id: 'panel2',
|
||||||
|
component: 'default',
|
||||||
|
tabComponent: 'default',
|
||||||
|
position: {
|
||||||
|
referencePanel: panel1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const panel3 = dockview.addPanel({
|
||||||
|
id: 'panel3',
|
||||||
|
component: 'default',
|
||||||
|
tabComponent: 'default',
|
||||||
|
position: {
|
||||||
|
referencePanel: panel1,
|
||||||
|
direction: 'right',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel1Spy = jest.spyOn(panel1.group, 'dispose');
|
||||||
|
|
||||||
|
expect(dockview.groups.length).toBe(2);
|
||||||
|
|
||||||
|
dockview.moveGroupOrPanel(
|
||||||
|
panel3.group,
|
||||||
|
panel1.group.id,
|
||||||
|
undefined,
|
||||||
|
'center'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(dockview.groups.length).toBe(1);
|
||||||
|
expect(panel1Spy).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
test('fromJSON events should still fire', () => {
|
test('fromJSON events should still fire', () => {
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
||||||
@ -1635,8 +1681,6 @@ describe('dockviewComponent', () => {
|
|||||||
|
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
|
|
||||||
console.log(activePanel.map((_) => _?.id).join(' '));
|
|
||||||
|
|
||||||
expect(addGroup.length).toBe(4);
|
expect(addGroup.length).toBe(4);
|
||||||
expect(removeGroup.length).toBe(0);
|
expect(removeGroup.length).toBe(0);
|
||||||
expect(activeGroup.length).toBe(1);
|
expect(activeGroup.length).toBe(1);
|
||||||
@ -1973,4 +2017,379 @@ describe('dockviewComponent', () => {
|
|||||||
// load a layout with a default tab identifier when react default is present
|
// load a layout with a default tab identifier when react default is present
|
||||||
|
|
||||||
// load a layout with invialid panel identifier
|
// load a layout with invialid panel identifier
|
||||||
|
|
||||||
|
test('orthogonal realigment #1', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
const dockview = new DockviewComponent(container, {
|
||||||
|
components: {
|
||||||
|
default: PanelContentPartTest,
|
||||||
|
},
|
||||||
|
tabComponents: {
|
||||||
|
test_tab_id: PanelTabPartTest,
|
||||||
|
},
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
});
|
||||||
|
dockview.deserializer = new ReactPanelDeserialzier(dockview);
|
||||||
|
|
||||||
|
expect(dockview.orientation).toBe(Orientation.HORIZONTAL);
|
||||||
|
|
||||||
|
dockview.fromJSON({
|
||||||
|
activeGroup: 'group-1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dockview.orientation).toBe(Orientation.VERTICAL);
|
||||||
|
|
||||||
|
dockview.addPanel({
|
||||||
|
id: 'panel2',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'left',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dockview.orientation).toBe(Orientation.HORIZONTAL);
|
||||||
|
|
||||||
|
expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({
|
||||||
|
activeGroup: '1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2'],
|
||||||
|
id: '1',
|
||||||
|
activeView: 'panel2',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('orthogonal realigment #2', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
const dockview = new DockviewComponent(container, {
|
||||||
|
components: {
|
||||||
|
default: PanelContentPartTest,
|
||||||
|
},
|
||||||
|
tabComponents: {
|
||||||
|
test_tab_id: PanelTabPartTest,
|
||||||
|
},
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
});
|
||||||
|
dockview.deserializer = new ReactPanelDeserialzier(dockview);
|
||||||
|
|
||||||
|
expect(dockview.orientation).toBe(Orientation.HORIZONTAL);
|
||||||
|
|
||||||
|
dockview.fromJSON({
|
||||||
|
activeGroup: 'group-1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2'],
|
||||||
|
id: 'group-2',
|
||||||
|
activeView: 'panel2',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dockview.orientation).toBe(Orientation.VERTICAL);
|
||||||
|
|
||||||
|
dockview.addPanel({
|
||||||
|
id: 'panel3',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'left',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dockview.orientation).toBe(Orientation.HORIZONTAL);
|
||||||
|
|
||||||
|
expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({
|
||||||
|
activeGroup: '1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel3'],
|
||||||
|
id: '1',
|
||||||
|
activeView: 'panel3',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2'],
|
||||||
|
id: 'group-2',
|
||||||
|
activeView: 'panel2',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
panel3: {
|
||||||
|
id: 'panel3',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('orthogonal realigment #3', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
const dockview = new DockviewComponent(container, {
|
||||||
|
components: {
|
||||||
|
default: PanelContentPartTest,
|
||||||
|
},
|
||||||
|
tabComponents: {
|
||||||
|
test_tab_id: PanelTabPartTest,
|
||||||
|
},
|
||||||
|
orientation: Orientation.HORIZONTAL,
|
||||||
|
});
|
||||||
|
dockview.deserializer = new ReactPanelDeserialzier(dockview);
|
||||||
|
|
||||||
|
expect(dockview.orientation).toBe(Orientation.HORIZONTAL);
|
||||||
|
|
||||||
|
dockview.fromJSON({
|
||||||
|
activeGroup: 'group-1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dockview.orientation).toBe(Orientation.VERTICAL);
|
||||||
|
|
||||||
|
dockview.addPanel({
|
||||||
|
id: 'panel2',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'above',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dockview.addPanel({
|
||||||
|
id: 'panel3',
|
||||||
|
component: 'default',
|
||||||
|
position: {
|
||||||
|
direction: 'below',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dockview.orientation).toBe(Orientation.VERTICAL);
|
||||||
|
|
||||||
|
expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({
|
||||||
|
activeGroup: '2',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2'],
|
||||||
|
id: '1',
|
||||||
|
activeView: 'panel2',
|
||||||
|
},
|
||||||
|
size: 333,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 333,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel3'],
|
||||||
|
id: '2',
|
||||||
|
activeView: 'panel3',
|
||||||
|
},
|
||||||
|
size: 334,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
panel3: {
|
||||||
|
id: 'panel3',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
||||||
import { DockviewApi } from '../../api/component.api';
|
import { DockviewApi } from '../../api/component.api';
|
||||||
import { IGroupPanelView } from '../../dockview/defaultGroupPanelView';
|
import { IGroupPanelView } from '../../dockview/defaultGroupPanelView';
|
||||||
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
|
import { DockviewPanel } from '../../dockview/dockviewPanel';
|
||||||
import { GroupPanel } from '../../groupview/groupviewPanel';
|
import { GroupPanel } from '../../groupview/groupviewPanel';
|
||||||
|
|
||||||
describe('dockviewGroupPanel', () => {
|
describe('dockviewGroupPanel', () => {
|
||||||
@ -20,7 +20,7 @@ describe('dockviewGroupPanel', () => {
|
|||||||
const api = new dockviewApiMock();
|
const api = new dockviewApiMock();
|
||||||
const accessor = new accessorMock();
|
const accessor = new accessorMock();
|
||||||
const group = new groupMock();
|
const group = new groupMock();
|
||||||
const cut = new DockviewGroupPanel('fake-id', accessor, api, group);
|
const cut = new DockviewPanel('fake-id', accessor, api, group);
|
||||||
|
|
||||||
let latestTitle: string | undefined = undefined;
|
let latestTitle: string | undefined = undefined;
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ describe('dockviewGroupPanel', () => {
|
|||||||
const accessor = new accessorMock();
|
const accessor = new accessorMock();
|
||||||
const group = new groupMock();
|
const group = new groupMock();
|
||||||
|
|
||||||
const cut = new DockviewGroupPanel('fake-id', accessor, api, group);
|
const cut = new DockviewPanel('fake-id', accessor, api, group);
|
||||||
|
|
||||||
const viewMock = jest.fn<IGroupPanelView, []>(() => {
|
const viewMock = jest.fn<IGroupPanelView, []>(() => {
|
||||||
return {
|
return {
|
||||||
@ -86,7 +86,7 @@ describe('dockviewGroupPanel', () => {
|
|||||||
const api = new dockviewApiMock();
|
const api = new dockviewApiMock();
|
||||||
const accessor = new accessorMock();
|
const accessor = new accessorMock();
|
||||||
const group = new groupMock();
|
const group = new groupMock();
|
||||||
const cut = new DockviewGroupPanel('fake-id', accessor, api, group);
|
const cut = new DockviewPanel('fake-id', accessor, api, group);
|
||||||
|
|
||||||
expect(cut.params).toEqual(undefined);
|
expect(cut.params).toEqual(undefined);
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { GridviewPanelApiImpl } from '../../api/gridviewPanelApi';
|
||||||
import { GridviewComponent } from '../../gridview/gridviewComponent';
|
import { GridviewComponent } from '../../gridview/gridviewComponent';
|
||||||
import { GridviewPanel } from '../../gridview/gridviewPanel';
|
import { GridviewPanel } from '../../gridview/gridviewPanel';
|
||||||
import { CompositeDisposable } from '../../lifecycle';
|
import { CompositeDisposable } from '../../lifecycle';
|
||||||
@ -6,7 +7,9 @@ import { Orientation } from '../../splitview/core/splitview';
|
|||||||
|
|
||||||
class TestGridview extends GridviewPanel {
|
class TestGridview extends GridviewPanel {
|
||||||
constructor(id: string, componentName: string) {
|
constructor(id: string, componentName: string) {
|
||||||
super(id, componentName);
|
super(id, componentName, new GridviewPanelApiImpl(id));
|
||||||
|
|
||||||
|
this.api.initialize(this);
|
||||||
|
|
||||||
this.element.id = id;
|
this.element.id = id;
|
||||||
}
|
}
|
||||||
@ -65,7 +68,7 @@ describe('gridview', () => {
|
|||||||
|
|
||||||
expect(gridview.size).toBe(1);
|
expect(gridview.size).toBe(1);
|
||||||
|
|
||||||
const panel1 = gridview.getPanel('panel1');
|
const panel1 = gridview.getPanel('panel1')!;
|
||||||
|
|
||||||
gridview.removePanel(panel1);
|
gridview.removePanel(panel1);
|
||||||
|
|
||||||
@ -101,9 +104,9 @@ describe('gridview', () => {
|
|||||||
component: 'default',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel1 = gridview.getPanel('panel1');
|
const panel1 = gridview.getPanel('panel1')!;
|
||||||
const panel2 = gridview.getPanel('panel2');
|
const panel2 = gridview.getPanel('panel2')!;
|
||||||
const panel3 = gridview.getPanel('panel3');
|
const panel3 = gridview.getPanel('panel3')!;
|
||||||
|
|
||||||
expect(panel1.api.isActive).toBeFalsy();
|
expect(panel1.api.isActive).toBeFalsy();
|
||||||
expect(panel2.api.isActive).toBeFalsy();
|
expect(panel2.api.isActive).toBeFalsy();
|
||||||
@ -192,9 +195,9 @@ describe('gridview', () => {
|
|||||||
});
|
});
|
||||||
gridview.layout(800, 400, true);
|
gridview.layout(800, 400, true);
|
||||||
|
|
||||||
const panel1 = gridview.getPanel('panel_1');
|
const panel1 = gridview.getPanel('panel_1')!;
|
||||||
const panel2 = gridview.getPanel('panel_2');
|
const panel2 = gridview.getPanel('panel_2')!;
|
||||||
const panel3 = gridview.getPanel('panel_3');
|
const panel3 = gridview.getPanel('panel_3')!;
|
||||||
|
|
||||||
expect(panel1?.api.isVisible).toBeTruthy();
|
expect(panel1?.api.isVisible).toBeTruthy();
|
||||||
expect(panel1?.api.id).toBe('panel_1');
|
expect(panel1?.api.id).toBe('panel_1');
|
||||||
@ -330,7 +333,7 @@ describe('gridview', () => {
|
|||||||
component: 'default',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel1 = gridview.getPanel('panel_1');
|
const panel1 = gridview.getPanel('panel_1')!;
|
||||||
|
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
{
|
{
|
||||||
@ -349,7 +352,7 @@ describe('gridview', () => {
|
|||||||
component: 'default',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel2 = gridview.getPanel('panel_2');
|
const panel2 = gridview.getPanel('panel_2')!;
|
||||||
|
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
{
|
{
|
||||||
@ -368,7 +371,7 @@ describe('gridview', () => {
|
|||||||
component: 'default',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel3 = gridview.getPanel('panel_3');
|
const panel3 = gridview.getPanel('panel_3')!;
|
||||||
|
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
{
|
{
|
||||||
@ -1685,8 +1688,8 @@ describe('gridview', () => {
|
|||||||
component: 'default',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel1 = gridview.getPanel('panel1');
|
const panel1 = gridview.getPanel('panel1')!;
|
||||||
const panel2 = gridview.getPanel('panel2');
|
const panel2 = gridview.getPanel('panel2')!;
|
||||||
|
|
||||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||||
@ -1714,8 +1717,8 @@ describe('gridview', () => {
|
|||||||
component: 'default',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel1 = gridview.getPanel('panel1');
|
const panel1 = gridview.getPanel('panel1')!;
|
||||||
const panel2 = gridview.getPanel('panel2');
|
const panel2 = gridview.getPanel('panel2')!;
|
||||||
|
|
||||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||||
@ -1743,8 +1746,8 @@ describe('gridview', () => {
|
|||||||
component: 'default',
|
component: 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel1 = gridview.getPanel('panel1');
|
const panel1 = gridview.getPanel('panel1')!;
|
||||||
const panel2 = gridview.getPanel('panel2');
|
const panel2 = gridview.getPanel('panel2')!;
|
||||||
|
|
||||||
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
const panel1Spy = jest.spyOn(panel1, 'dispose');
|
||||||
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
const panel2Spy = jest.spyOn(panel2, 'dispose');
|
||||||
|
@ -4,7 +4,11 @@ import { GroupPanel } from '../../groupview/groupviewPanel';
|
|||||||
describe('gridviewPanel', () => {
|
describe('gridviewPanel', () => {
|
||||||
test('get panel', () => {
|
test('get panel', () => {
|
||||||
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
const accessorMock = jest.fn<DockviewComponent, []>(() => {
|
||||||
return {} as any;
|
return {
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
|
} as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
const accessor = new accessorMock();
|
const accessor = new accessorMock();
|
||||||
|
@ -225,6 +225,8 @@ describe('groupview', () => {
|
|||||||
id: 'dockview-1',
|
id: 'dockview-1',
|
||||||
removePanel: removePanelMock,
|
removePanel: removePanelMock,
|
||||||
removeGroup: removeGroupMock,
|
removeGroup: removeGroupMock,
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
}) as DockviewComponent;
|
}) as DockviewComponent;
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
@ -616,6 +618,8 @@ describe('groupview', () => {
|
|||||||
showDndOverlay: jest.fn(),
|
showDndOverlay: jest.fn(),
|
||||||
},
|
},
|
||||||
getPanel: jest.fn(),
|
getPanel: jest.fn(),
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
@ -671,6 +675,8 @@ describe('groupview', () => {
|
|||||||
},
|
},
|
||||||
getPanel: jest.fn(),
|
getPanel: jest.fn(),
|
||||||
doSetGroupActive: jest.fn(),
|
doSetGroupActive: jest.fn(),
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
@ -724,7 +730,7 @@ describe('groupview', () => {
|
|||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that should allow drop when not dropping on self for same component id', () => {
|
test('that should not allow drop when dropping on self for same component id', () => {
|
||||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||||
return {
|
return {
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
@ -733,6 +739,8 @@ describe('groupview', () => {
|
|||||||
},
|
},
|
||||||
getPanel: jest.fn(),
|
getPanel: jest.fn(),
|
||||||
doSetGroupActive: jest.fn(),
|
doSetGroupActive: jest.fn(),
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
@ -784,7 +792,7 @@ describe('groupview', () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
element.getElementsByClassName('drop-target-dropzone').length
|
element.getElementsByClassName('drop-target-dropzone').length
|
||||||
).toBe(1);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that should not allow drop when not dropping for different component id', () => {
|
test('that should not allow drop when not dropping for different component id', () => {
|
||||||
@ -796,6 +804,8 @@ describe('groupview', () => {
|
|||||||
},
|
},
|
||||||
getPanel: jest.fn(),
|
getPanel: jest.fn(),
|
||||||
doSetGroupActive: jest.fn(),
|
doSetGroupActive: jest.fn(),
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
|
@ -12,7 +12,11 @@ import { TestPanel } from '../groupview.spec';
|
|||||||
describe('tabsContainer', () => {
|
describe('tabsContainer', () => {
|
||||||
test('that an external event does not render a drop target and calls through to the group mode', () => {
|
test('that an external event does not render a drop target and calls through to the group mode', () => {
|
||||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||||
return {};
|
return {
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||||
return {
|
return {
|
||||||
@ -31,7 +35,7 @@ describe('tabsContainer', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||||
|
|
||||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
const emptySpace = cut.element
|
const emptySpace = cut.element
|
||||||
.getElementsByClassName('void-container')
|
.getElementsByClassName('void-container')
|
||||||
@ -62,6 +66,9 @@ describe('tabsContainer', () => {
|
|||||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||||
return {
|
return {
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||||
@ -83,7 +90,7 @@ describe('tabsContainer', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||||
|
|
||||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
const emptySpace = cut.element
|
const emptySpace = cut.element
|
||||||
.getElementsByClassName('void-container')
|
.getElementsByClassName('void-container')
|
||||||
@ -125,6 +132,9 @@ describe('tabsContainer', () => {
|
|||||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||||
return {
|
return {
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||||
@ -146,7 +156,7 @@ describe('tabsContainer', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||||
|
|
||||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
||||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||||
@ -185,6 +195,9 @@ describe('tabsContainer', () => {
|
|||||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||||
return {
|
return {
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||||
@ -206,7 +219,7 @@ describe('tabsContainer', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||||
|
|
||||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
||||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||||
@ -245,6 +258,9 @@ describe('tabsContainer', () => {
|
|||||||
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
|
||||||
return {
|
return {
|
||||||
id: 'testcomponentid',
|
id: 'testcomponentid',
|
||||||
|
onDidAddPanel: jest.fn(),
|
||||||
|
onDidRemovePanel: jest.fn(),
|
||||||
|
options: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
const groupviewMock = jest.fn<Partial<Groupview>, []>(() => {
|
||||||
@ -265,7 +281,7 @@ describe('tabsContainer', () => {
|
|||||||
const accessor = new accessorMock() as DockviewComponent;
|
const accessor = new accessorMock() as DockviewComponent;
|
||||||
const groupPanel = new groupPanelMock() as GroupPanel;
|
const groupPanel = new groupPanelMock() as GroupPanel;
|
||||||
|
|
||||||
const cut = new TabsContainer(accessor, groupPanel, {});
|
const cut = new TabsContainer(accessor, groupPanel);
|
||||||
|
|
||||||
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
cut.openPanel(new TestPanel('panel1', jest.fn() as any));
|
||||||
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
cut.openPanel(new TestPanel('panel2', jest.fn() as any));
|
||||||
|
@ -325,6 +325,10 @@ export class GridviewApi implements CommonApi<SerializedGridview> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DockviewApi implements CommonApi<SerializedDockview> {
|
export class DockviewApi implements CommonApi<SerializedDockview> {
|
||||||
|
get id(): string {
|
||||||
|
return this.component.id;
|
||||||
|
}
|
||||||
|
|
||||||
get width(): number {
|
get width(): number {
|
||||||
return this.component.width;
|
return this.component.width;
|
||||||
}
|
}
|
||||||
@ -435,8 +439,8 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
|
|||||||
return this.component.addPanel(options);
|
return this.component.addPanel(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
addEmptyGroup(options?: AddGroupOptions): void {
|
addGroup(options?: AddGroupOptions): IGroupviewPanel {
|
||||||
this.component.addEmptyGroup(options);
|
return this.component.addGroup(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
moveToNext(options?: MovementOptions): void {
|
moveToNext(options?: MovementOptions): void {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Emitter, Event } from '../events';
|
import { Emitter, Event } from '../events';
|
||||||
|
import { IPanel } from '../panel/types';
|
||||||
import { FunctionOrValue } from '../types';
|
import { FunctionOrValue } from '../types';
|
||||||
import { PanelApiImpl, PanelApi } from './panelApi';
|
import { PanelApiImpl, PanelApi } from './panelApi';
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ export class GridviewPanelApiImpl
|
|||||||
readonly onDidSizeChange: Event<SizeEvent> = this._onDidSizeChange.event;
|
readonly onDidSizeChange: Event<SizeEvent> = this._onDidSizeChange.event;
|
||||||
//
|
//
|
||||||
|
|
||||||
constructor(id: string) {
|
constructor(id: string, panel?: IPanel) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
@ -56,6 +57,10 @@ export class GridviewPanelApiImpl
|
|||||||
this._onDidConstraintsChange,
|
this._onDidConstraintsChange,
|
||||||
this._onDidSizeChange
|
this._onDidSizeChange
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (panel) {
|
||||||
|
this.initialize(panel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConstraints(value: GridConstraintChangeEvent) {
|
public setConstraints(value: GridConstraintChangeEvent) {
|
||||||
|
@ -71,6 +71,9 @@ export class DockviewPanelApiImpl
|
|||||||
|
|
||||||
constructor(private panel: IDockviewPanel, group: GroupPanel) {
|
constructor(private panel: IDockviewPanel, group: GroupPanel) {
|
||||||
super(panel.id);
|
super(panel.id);
|
||||||
|
|
||||||
|
this.initialize(panel);
|
||||||
|
|
||||||
this._group = group;
|
this._group = group;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Emitter, Event } from '../events';
|
import { Emitter, Event } from '../events';
|
||||||
import { CompositeDisposable } from '../lifecycle';
|
import { CompositeDisposable, MutableDisposable } from '../lifecycle';
|
||||||
|
import { IPanel, Parameters } from '../panel/types';
|
||||||
|
|
||||||
export interface FocusEvent {
|
export interface FocusEvent {
|
||||||
readonly isFocused: boolean;
|
readonly isFocused: boolean;
|
||||||
@ -25,6 +26,7 @@ export interface PanelApi {
|
|||||||
readonly onDidActiveChange: Event<ActiveEvent>;
|
readonly onDidActiveChange: Event<ActiveEvent>;
|
||||||
setVisible(isVisible: boolean): void;
|
setVisible(isVisible: boolean): void;
|
||||||
setActive(): void;
|
setActive(): void;
|
||||||
|
updateParameters(parameters: Parameters): void;
|
||||||
/**
|
/**
|
||||||
* The id of the panel that would have been assigned when the panel was created
|
* The id of the panel that would have been assigned when the panel was created
|
||||||
*/
|
*/
|
||||||
@ -61,6 +63,8 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
|||||||
private _width = 0;
|
private _width = 0;
|
||||||
private _height = 0;
|
private _height = 0;
|
||||||
|
|
||||||
|
private readonly panelUpdatesDisposable = new MutableDisposable();
|
||||||
|
|
||||||
readonly _onDidDimensionChange = new Emitter<PanelDimensionChangeEvent>({
|
readonly _onDidDimensionChange = new Emitter<PanelDimensionChangeEvent>({
|
||||||
replay: true,
|
replay: true,
|
||||||
});
|
});
|
||||||
@ -94,6 +98,10 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
|||||||
readonly _onActiveChange = new Emitter<void>();
|
readonly _onActiveChange = new Emitter<void>();
|
||||||
readonly onActiveChange: Event<void> = this._onActiveChange.event;
|
readonly onActiveChange: Event<void> = this._onActiveChange.event;
|
||||||
//
|
//
|
||||||
|
readonly _onUpdateParameters = new Emitter<Parameters>();
|
||||||
|
readonly onUpdateParameters: Event<Parameters> =
|
||||||
|
this._onUpdateParameters.event;
|
||||||
|
//
|
||||||
|
|
||||||
get isFocused() {
|
get isFocused() {
|
||||||
return this._isFocused;
|
return this._isFocused;
|
||||||
@ -118,6 +126,7 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
|
this.panelUpdatesDisposable,
|
||||||
this._onDidDimensionChange,
|
this._onDidDimensionChange,
|
||||||
this._onDidChangeFocus,
|
this._onDidChangeFocus,
|
||||||
this._onDidVisibilityChange,
|
this._onDidVisibilityChange,
|
||||||
@ -125,6 +134,7 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
|||||||
this._onFocusEvent,
|
this._onFocusEvent,
|
||||||
this._onActiveChange,
|
this._onActiveChange,
|
||||||
this._onVisibilityChange,
|
this._onVisibilityChange,
|
||||||
|
this._onUpdateParameters,
|
||||||
this.onDidFocusChange((event) => {
|
this.onDidFocusChange((event) => {
|
||||||
this._isFocused = event.isFocused;
|
this._isFocused = event.isFocused;
|
||||||
}),
|
}),
|
||||||
@ -141,6 +151,18 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public initialize(panel: IPanel): void {
|
||||||
|
this.panelUpdatesDisposable.value = this._onUpdateParameters.event(
|
||||||
|
(parameters) => {
|
||||||
|
panel.update({
|
||||||
|
params: {
|
||||||
|
params: parameters,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
setVisible(isVisible: boolean) {
|
setVisible(isVisible: boolean) {
|
||||||
this._onVisibilityChange.fire({ isVisible });
|
this._onVisibilityChange.fire({ isVisible });
|
||||||
}
|
}
|
||||||
@ -149,6 +171,10 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi {
|
|||||||
this._onActiveChange.fire();
|
this._onActiveChange.fire();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateParameters(parameters: Parameters): void {
|
||||||
|
this._onUpdateParameters.fire(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,11 @@ export interface IDragAndDropObserverCallbacks {
|
|||||||
onDragLeave: (e: DragEvent) => void;
|
onDragLeave: (e: DragEvent) => void;
|
||||||
onDrop: (e: DragEvent) => void;
|
onDrop: (e: DragEvent) => void;
|
||||||
onDragEnd: (e: DragEvent) => void;
|
onDragEnd: (e: DragEvent) => void;
|
||||||
|
|
||||||
onDragOver?: (e: DragEvent) => void;
|
onDragOver?: (e: DragEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DragAndDropObserver extends CompositeDisposable {
|
export class DragAndDropObserver extends CompositeDisposable {
|
||||||
// A helper to fix issues with repeated DRAG_ENTER / DRAG_LEAVE
|
private target: EventTarget | null = null;
|
||||||
// calls see https://github.com/microsoft/vscode/issues/14470
|
|
||||||
// when the element has child elements where the events are fired
|
|
||||||
// repeadedly.
|
|
||||||
private counter = 0;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private element: HTMLElement,
|
private element: HTMLElement,
|
||||||
@ -28,28 +23,37 @@ export class DragAndDropObserver extends CompositeDisposable {
|
|||||||
|
|
||||||
private registerListeners(): void {
|
private registerListeners(): void {
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(this.element, 'dragenter', (e: DragEvent) => {
|
addDisposableListener(
|
||||||
this.counter++;
|
this.element,
|
||||||
|
'dragenter',
|
||||||
|
(e: DragEvent) => {
|
||||||
|
this.target = e.target;
|
||||||
this.callbacks.onDragEnter(e);
|
this.callbacks.onDragEnter(e);
|
||||||
})
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(this.element, 'dragover', (e: DragEvent) => {
|
addDisposableListener(
|
||||||
|
this.element,
|
||||||
|
'dragover',
|
||||||
|
(e: DragEvent) => {
|
||||||
e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
|
e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
|
||||||
|
|
||||||
if (this.callbacks.onDragOver) {
|
if (this.callbacks.onDragOver) {
|
||||||
this.callbacks.onDragOver(e);
|
this.callbacks.onDragOver(e);
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(this.element, 'dragleave', (e: DragEvent) => {
|
addDisposableListener(this.element, 'dragleave', (e: DragEvent) => {
|
||||||
this.counter--;
|
if (this.target === e.target) {
|
||||||
|
this.target = null;
|
||||||
|
|
||||||
if (this.counter === 0) {
|
|
||||||
this.callbacks.onDragLeave(e);
|
this.callbacks.onDragLeave(e);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -57,14 +61,13 @@ export class DragAndDropObserver extends CompositeDisposable {
|
|||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(this.element, 'dragend', (e: DragEvent) => {
|
addDisposableListener(this.element, 'dragend', (e: DragEvent) => {
|
||||||
this.counter = 0;
|
this.target = null;
|
||||||
this.callbacks.onDragEnd(e);
|
this.callbacks.onDragEnd(e);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
addDisposableListener(this.element, 'drop', (e: DragEvent) => {
|
addDisposableListener(this.element, 'drop', (e: DragEvent) => {
|
||||||
this.counter = 0;
|
|
||||||
this.callbacks.onDrop(e);
|
this.callbacks.onDrop(e);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -19,22 +19,6 @@
|
|||||||
will-change: transform;
|
will-change: transform;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
&.left {
|
|
||||||
transform: translateX(-25%) scaleX(0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
&.right {
|
|
||||||
transform: translateX(25%) scaleX(0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
&.top {
|
|
||||||
transform: translateY(-25%) scaleY(0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.bottom {
|
|
||||||
transform: translateY(25%) scaleY(0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.small-top {
|
&.small-top {
|
||||||
border-top: 1px solid var(--dv-drag-over-border-color);
|
border-top: 1px solid var(--dv-drag-over-border-color);
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,36 @@ import { toggleClass } from '../dom';
|
|||||||
import { Emitter, Event } from '../events';
|
import { Emitter, Event } from '../events';
|
||||||
import { CompositeDisposable } from '../lifecycle';
|
import { CompositeDisposable } from '../lifecycle';
|
||||||
import { DragAndDropObserver } from './dnd';
|
import { DragAndDropObserver } from './dnd';
|
||||||
|
import { clamp } from '../math';
|
||||||
|
import { Direction } from '../gridview/baseComponentGridview';
|
||||||
|
|
||||||
export enum Position {
|
function numberOrFallback(maybeNumber: any, fallback: number): number {
|
||||||
Top = 'Top',
|
return typeof maybeNumber === 'number' ? maybeNumber : fallback;
|
||||||
Left = 'Left',
|
|
||||||
Bottom = 'Bottom',
|
|
||||||
Right = 'Right',
|
|
||||||
Center = 'Center',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Quadrant = 'top' | 'bottom' | 'left' | 'right';
|
export function directionToPosition(direction: Direction): Position {
|
||||||
|
switch (direction) {
|
||||||
|
case 'above':
|
||||||
|
return 'top';
|
||||||
|
case 'below':
|
||||||
|
return 'bottom';
|
||||||
|
case 'left':
|
||||||
|
return 'left';
|
||||||
|
case 'right':
|
||||||
|
return 'right';
|
||||||
|
case 'within':
|
||||||
|
return 'center';
|
||||||
|
default:
|
||||||
|
throw new Error(`invalid direction '${direction}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface DroptargetEvent {
|
export interface DroptargetEvent {
|
||||||
position: Position;
|
readonly position: Position;
|
||||||
nativeEvent: DragEvent;
|
readonly nativeEvent: DragEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DropTargetDirections = 'vertical' | 'horizontal' | 'all' | 'none';
|
export type Position = 'top' | 'bottom' | 'left' | 'right' | 'center';
|
||||||
|
|
||||||
function isBooleanValue(
|
function isBooleanValue(
|
||||||
canDisplayOverlay: CanDisplayOverlay
|
canDisplayOverlay: CanDisplayOverlay
|
||||||
@ -28,7 +41,7 @@ function isBooleanValue(
|
|||||||
|
|
||||||
export type CanDisplayOverlay =
|
export type CanDisplayOverlay =
|
||||||
| boolean
|
| boolean
|
||||||
| ((dragEvent: DragEvent, state: Quadrant | null) => boolean);
|
| ((dragEvent: DragEvent, state: Position) => boolean);
|
||||||
|
|
||||||
export class Droptarget extends CompositeDisposable {
|
export class Droptarget extends CompositeDisposable {
|
||||||
private target: HTMLElement | undefined;
|
private target: HTMLElement | undefined;
|
||||||
@ -38,27 +51,31 @@ export class Droptarget 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;
|
||||||
|
|
||||||
get state() {
|
get state(): Position | undefined {
|
||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
|
|
||||||
set validOverlays(value: DropTargetDirections) {
|
|
||||||
this.options.validOverlays = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
set canDisplayOverlay(value: CanDisplayOverlay) {
|
|
||||||
this.options.canDisplayOverlay = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly element: HTMLElement,
|
private readonly element: HTMLElement,
|
||||||
private readonly options: {
|
private readonly options: {
|
||||||
canDisplayOverlay: CanDisplayOverlay;
|
canDisplayOverlay: CanDisplayOverlay;
|
||||||
validOverlays: DropTargetDirections;
|
acceptedTargetZones: Position[];
|
||||||
|
overlayModel?: {
|
||||||
|
size?: { value: number; type: 'pixels' | 'percentage' };
|
||||||
|
activationSize?: {
|
||||||
|
value: number;
|
||||||
|
type: 'pixels' | 'percentage';
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
// use a set to take advantage of #<set>.has
|
||||||
|
const acceptedTargetZonesSet = new Set(
|
||||||
|
this.options.acceptedTargetZones
|
||||||
|
);
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onDrop,
|
this._onDrop,
|
||||||
new DragAndDropObserver(this.element, {
|
new DragAndDropObserver(this.element, {
|
||||||
@ -71,17 +88,25 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
return; // avoid div!0
|
return; // avoid div!0
|
||||||
}
|
}
|
||||||
|
|
||||||
const x = e.offsetX;
|
const rect = (
|
||||||
const y = e.offsetY;
|
e.currentTarget as HTMLElement
|
||||||
const xp = (100 * x) / width;
|
).getBoundingClientRect();
|
||||||
const yp = (100 * y) / height;
|
const x = e.clientX - rect.left;
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
const quadrant = this.calculateQuadrant(
|
const quadrant = this.calculateQuadrant(
|
||||||
this.options.validOverlays,
|
acceptedTargetZonesSet,
|
||||||
xp,
|
x,
|
||||||
yp
|
y,
|
||||||
|
width,
|
||||||
|
height
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (quadrant === null) {
|
||||||
|
this.removeDropTarget();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isBooleanValue(this.options.canDisplayOverlay)) {
|
if (isBooleanValue(this.options.canDisplayOverlay)) {
|
||||||
if (!this.options.canDisplayOverlay) {
|
if (!this.options.canDisplayOverlay) {
|
||||||
return;
|
return;
|
||||||
@ -95,14 +120,14 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
this.target.className = 'drop-target-dropzone';
|
this.target.className = 'drop-target-dropzone';
|
||||||
this.overlay = document.createElement('div');
|
this.overlay = document.createElement('div');
|
||||||
this.overlay.className = 'drop-target-selection';
|
this.overlay.className = 'drop-target-selection';
|
||||||
this._state = Position.Center;
|
this._state = 'center';
|
||||||
this.target.appendChild(this.overlay);
|
this.target.appendChild(this.overlay);
|
||||||
|
|
||||||
this.element.classList.add('drop-target');
|
this.element.classList.add('drop-target');
|
||||||
this.element.append(this.target);
|
this.element.append(this.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.validOverlays === 'none') {
|
if (this.options.acceptedTargetZones.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,10 +135,7 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSmallX = width < 100;
|
this.toggleClasses(quadrant, width, height);
|
||||||
const isSmallY = height < 100;
|
|
||||||
|
|
||||||
this.toggleClasses(quadrant, isSmallX, isSmallY);
|
|
||||||
|
|
||||||
this.setState(quadrant);
|
this.setState(quadrant);
|
||||||
},
|
},
|
||||||
@ -139,28 +161,69 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose(): void {
|
||||||
this.removeDropTarget();
|
this.removeDropTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleClasses(
|
private toggleClasses(
|
||||||
quadrant: Quadrant | null,
|
quadrant: Position,
|
||||||
isSmallX: boolean,
|
width: number,
|
||||||
isSmallY: boolean
|
height: number
|
||||||
) {
|
): void {
|
||||||
if (!this.overlay) {
|
if (!this.overlay) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isSmallX = width < 100;
|
||||||
|
const isSmallY = height < 100;
|
||||||
|
|
||||||
const isLeft = quadrant === 'left';
|
const isLeft = quadrant === 'left';
|
||||||
const isRight = quadrant === 'right';
|
const isRight = quadrant === 'right';
|
||||||
const isTop = quadrant === 'top';
|
const isTop = quadrant === 'top';
|
||||||
const isBottom = quadrant === 'bottom';
|
const isBottom = quadrant === 'bottom';
|
||||||
|
|
||||||
toggleClass(this.overlay, 'right', !isSmallX && isRight);
|
const rightClass = !isSmallX && isRight;
|
||||||
toggleClass(this.overlay, 'left', !isSmallX && isLeft);
|
const leftClass = !isSmallX && isLeft;
|
||||||
toggleClass(this.overlay, 'top', !isSmallY && isTop);
|
const topClass = !isSmallY && isTop;
|
||||||
toggleClass(this.overlay, 'bottom', !isSmallY && isBottom);
|
const bottomClass = !isSmallY && isBottom;
|
||||||
|
|
||||||
|
let size = 0.5;
|
||||||
|
|
||||||
|
if (this.options.overlayModel?.size?.type === 'percentage') {
|
||||||
|
size = clamp(this.options.overlayModel.size.value, 0, 100) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.overlayModel?.size?.type === 'pixels') {
|
||||||
|
if (rightClass || leftClass) {
|
||||||
|
size =
|
||||||
|
clamp(0, this.options.overlayModel.size.value, width) /
|
||||||
|
width;
|
||||||
|
}
|
||||||
|
if (topClass || bottomClass) {
|
||||||
|
size =
|
||||||
|
clamp(0, this.options.overlayModel.size.value, height) /
|
||||||
|
height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const translate = (1 - size) / 2;
|
||||||
|
const scale = size;
|
||||||
|
|
||||||
|
let transform: string;
|
||||||
|
|
||||||
|
if (rightClass) {
|
||||||
|
transform = `translateX(${100 * translate}%) scaleX(${scale})`;
|
||||||
|
} else if (leftClass) {
|
||||||
|
transform = `translateX(-${100 * translate}%) scaleX(${scale})`;
|
||||||
|
} else if (topClass) {
|
||||||
|
transform = `translateY(-${100 * translate}%) scaleY(${scale})`;
|
||||||
|
} else if (bottomClass) {
|
||||||
|
transform = `translateY(${100 * translate}%) scaleY(${scale})`;
|
||||||
|
} else {
|
||||||
|
transform = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.overlay.style.transform = transform;
|
||||||
|
|
||||||
toggleClass(this.overlay, 'small-right', isSmallX && isRight);
|
toggleClass(this.overlay, 'small-right', isSmallX && isRight);
|
||||||
toggleClass(this.overlay, 'small-left', isSmallX && isLeft);
|
toggleClass(this.overlay, 'small-left', isSmallX && isLeft);
|
||||||
@ -168,60 +231,61 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
toggleClass(this.overlay, 'small-bottom', isSmallY && isBottom);
|
toggleClass(this.overlay, 'small-bottom', isSmallY && isBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setState(quadrant: Quadrant | null) {
|
private setState(quadrant: Position): void {
|
||||||
switch (quadrant) {
|
switch (quadrant) {
|
||||||
case 'top':
|
case 'top':
|
||||||
this._state = Position.Top;
|
this._state = 'top';
|
||||||
break;
|
break;
|
||||||
case 'left':
|
case 'left':
|
||||||
this._state = Position.Left;
|
this._state = 'left';
|
||||||
break;
|
break;
|
||||||
case 'bottom':
|
case 'bottom':
|
||||||
this._state = Position.Bottom;
|
this._state = 'bottom';
|
||||||
break;
|
break;
|
||||||
case 'right':
|
case 'right':
|
||||||
this._state = Position.Right;
|
this._state = 'right';
|
||||||
break;
|
break;
|
||||||
default:
|
case 'center':
|
||||||
this._state = Position.Center;
|
this._state = 'center';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateQuadrant(
|
private calculateQuadrant(
|
||||||
overlayType: DropTargetDirections,
|
overlayType: Set<Position>,
|
||||||
xp: number,
|
x: number,
|
||||||
yp: number
|
y: number,
|
||||||
): Quadrant | null {
|
width: number,
|
||||||
switch (overlayType) {
|
height: number
|
||||||
case 'all':
|
): Position | null {
|
||||||
if (xp < 20) {
|
const isPercentage =
|
||||||
return 'left';
|
this.options.overlayModel?.activationSize === undefined ||
|
||||||
}
|
this.options.overlayModel?.activationSize?.type === 'percentage';
|
||||||
if (xp > 80) {
|
|
||||||
return 'right';
|
|
||||||
}
|
|
||||||
if (yp < 20) {
|
|
||||||
return 'top';
|
|
||||||
}
|
|
||||||
if (yp > 80) {
|
|
||||||
return 'bottom';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'vertical':
|
|
||||||
if (yp < 50) {
|
|
||||||
return 'top';
|
|
||||||
}
|
|
||||||
return 'bottom';
|
|
||||||
|
|
||||||
case 'horizontal':
|
const value = numberOrFallback(
|
||||||
if (xp < 50) {
|
this.options?.overlayModel?.activationSize?.value,
|
||||||
return 'left';
|
20
|
||||||
}
|
);
|
||||||
return 'right';
|
|
||||||
|
if (isPercentage) {
|
||||||
|
return calculateQuadrantAsPercentage(
|
||||||
|
overlayType,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return calculateQuadrantAsPixels(
|
||||||
|
overlayType,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeDropTarget() {
|
private removeDropTarget() {
|
||||||
@ -229,7 +293,67 @@ export class Droptarget extends CompositeDisposable {
|
|||||||
this._state = undefined;
|
this._state = undefined;
|
||||||
this.element.removeChild(this.target);
|
this.element.removeChild(this.target);
|
||||||
this.target = undefined;
|
this.target = undefined;
|
||||||
|
this.overlay = undefined;
|
||||||
this.element.classList.remove('drop-target');
|
this.element.classList.remove('drop-target');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function calculateQuadrantAsPercentage(
|
||||||
|
overlayType: Set<Position>,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
threshold: number
|
||||||
|
): Position | null {
|
||||||
|
const xp = (100 * x) / width;
|
||||||
|
const yp = (100 * y) / height;
|
||||||
|
|
||||||
|
if (overlayType.has('left') && xp < threshold) {
|
||||||
|
return 'left';
|
||||||
|
}
|
||||||
|
if (overlayType.has('right') && xp > 100 - threshold) {
|
||||||
|
return 'right';
|
||||||
|
}
|
||||||
|
if (overlayType.has('top') && yp < threshold) {
|
||||||
|
return 'top';
|
||||||
|
}
|
||||||
|
if (overlayType.has('bottom') && yp > 100 - threshold) {
|
||||||
|
return 'bottom';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overlayType.has('center')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'center';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateQuadrantAsPixels(
|
||||||
|
overlayType: Set<Position>,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
threshold: number
|
||||||
|
): Position | null {
|
||||||
|
if (overlayType.has('left') && x < threshold) {
|
||||||
|
return 'left';
|
||||||
|
}
|
||||||
|
if (overlayType.has('right') && x > width - threshold) {
|
||||||
|
return 'right';
|
||||||
|
}
|
||||||
|
if (overlayType.has('top') && y < threshold) {
|
||||||
|
return 'top';
|
||||||
|
}
|
||||||
|
if (overlayType.has('bottom') && y > height - threshold) {
|
||||||
|
return 'bottom';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overlayType.has('center')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'center';
|
||||||
|
}
|
||||||
|
@ -4,10 +4,10 @@ import {
|
|||||||
getGridLocation,
|
getGridLocation,
|
||||||
ISerializedLeafNode,
|
ISerializedLeafNode,
|
||||||
} from '../gridview/gridview';
|
} from '../gridview/gridview';
|
||||||
import { Position } from '../dnd/droptarget';
|
import { directionToPosition, Droptarget, Position } from '../dnd/droptarget';
|
||||||
import { tail, sequenceEquals } from '../array';
|
import { tail, sequenceEquals } from '../array';
|
||||||
import { GroupviewPanelState, IDockviewPanel } from '../groupview/groupPanel';
|
import { GroupviewPanelState, IDockviewPanel } from '../groupview/groupPanel';
|
||||||
import { DockviewGroupPanel } from './dockviewGroupPanel';
|
import { DockviewPanel } from './dockviewPanel';
|
||||||
import { CompositeDisposable } from '../lifecycle';
|
import { CompositeDisposable } from '../lifecycle';
|
||||||
import { Event, Emitter } from '../events';
|
import { Event, Emitter } from '../events';
|
||||||
import { Watermark } from './components/watermark/watermark';
|
import { Watermark } from './components/watermark/watermark';
|
||||||
@ -23,10 +23,15 @@ import {
|
|||||||
AddGroupOptions,
|
AddGroupOptions,
|
||||||
AddPanelOptions,
|
AddPanelOptions,
|
||||||
DockviewComponentOptions,
|
DockviewComponentOptions,
|
||||||
|
isGroupOptionsWithGroup,
|
||||||
|
isGroupOptionsWithPanel,
|
||||||
|
isPanelOptionsWithGroup,
|
||||||
|
isPanelOptionsWithPanel,
|
||||||
MovementOptions,
|
MovementOptions,
|
||||||
} from './options';
|
} from './options';
|
||||||
import {
|
import {
|
||||||
BaseGrid,
|
BaseGrid,
|
||||||
|
Direction,
|
||||||
IBaseGrid,
|
IBaseGrid,
|
||||||
toTarget,
|
toTarget,
|
||||||
} from '../gridview/baseComponentGridview';
|
} from '../gridview/baseComponentGridview';
|
||||||
@ -38,10 +43,10 @@ import {
|
|||||||
GroupPanelViewState,
|
GroupPanelViewState,
|
||||||
GroupviewDropEvent,
|
GroupviewDropEvent,
|
||||||
} from '../groupview/groupview';
|
} from '../groupview/groupview';
|
||||||
import { GroupPanel } from '../groupview/groupviewPanel';
|
import { GroupPanel, IGroupviewPanel } from '../groupview/groupviewPanel';
|
||||||
import { DefaultGroupPanelView } from './defaultGroupPanelView';
|
import { DefaultGroupPanelView } from './defaultGroupPanelView';
|
||||||
|
import { getPanelData } from '../dnd/dataTransfer';
|
||||||
const nextGroupId = sequentialNumberGenerator();
|
import { DockviewDropTargets } from '../groupview/dnd';
|
||||||
|
|
||||||
export interface PanelReference {
|
export interface PanelReference {
|
||||||
update: (event: { params: { [key: string]: any } }) => void;
|
update: (event: { params: { [key: string]: any } }) => void;
|
||||||
@ -83,6 +88,7 @@ export interface IDockviewComponent extends IBaseGrid<GroupPanel> {
|
|||||||
readonly totalPanels: number;
|
readonly totalPanels: number;
|
||||||
readonly panels: IDockviewPanel[];
|
readonly panels: IDockviewPanel[];
|
||||||
readonly onDidDrop: Event<DockviewDropEvent>;
|
readonly onDidDrop: Event<DockviewDropEvent>;
|
||||||
|
readonly orientation: Orientation;
|
||||||
tabHeight: number | undefined;
|
tabHeight: number | undefined;
|
||||||
deserializer: IPanelDeserializer | undefined;
|
deserializer: IPanelDeserializer | undefined;
|
||||||
updateOptions(options: DockviewComponentUpdateOptions): void;
|
updateOptions(options: DockviewComponentUpdateOptions): void;
|
||||||
@ -101,7 +107,7 @@ export interface IDockviewComponent extends IBaseGrid<GroupPanel> {
|
|||||||
getGroupPanel: (id: string) => IDockviewPanel | undefined;
|
getGroupPanel: (id: string) => IDockviewPanel | undefined;
|
||||||
createWatermarkComponent(): IWatermarkRenderer;
|
createWatermarkComponent(): IWatermarkRenderer;
|
||||||
// lifecycle
|
// lifecycle
|
||||||
addEmptyGroup(options?: AddGroupOptions): void;
|
addGroup(options?: AddGroupOptions): IGroupviewPanel;
|
||||||
closeAllGroups(): void;
|
closeAllGroups(): void;
|
||||||
// events
|
// events
|
||||||
moveToNext(options?: MovementOptions): void;
|
moveToNext(options?: MovementOptions): void;
|
||||||
@ -121,6 +127,7 @@ export class DockviewComponent
|
|||||||
extends BaseGrid<GroupPanel>
|
extends BaseGrid<GroupPanel>
|
||||||
implements IDockviewComponent
|
implements IDockviewComponent
|
||||||
{
|
{
|
||||||
|
private readonly nextGroupId = sequentialNumberGenerator();
|
||||||
private _deserializer: IPanelDeserializer | undefined;
|
private _deserializer: IPanelDeserializer | undefined;
|
||||||
private _api: DockviewApi;
|
private _api: DockviewApi;
|
||||||
private _options: Exclude<DockviewComponentOptions, 'orientation'>;
|
private _options: Exclude<DockviewComponentOptions, 'orientation'>;
|
||||||
@ -144,6 +151,10 @@ export class DockviewComponent
|
|||||||
readonly onDidActivePanelChange: Event<IDockviewPanel | undefined> =
|
readonly onDidActivePanelChange: Event<IDockviewPanel | undefined> =
|
||||||
this._onDidActivePanelChange.event;
|
this._onDidActivePanelChange.event;
|
||||||
|
|
||||||
|
get orientation(): Orientation {
|
||||||
|
return this.gridview.orientation;
|
||||||
|
}
|
||||||
|
|
||||||
get totalPanels(): number {
|
get totalPanels(): number {
|
||||||
return this.panels.length;
|
return this.panels.length;
|
||||||
}
|
}
|
||||||
@ -224,9 +235,88 @@ export class DockviewComponent
|
|||||||
this.options.watermarkComponent = Watermark;
|
this.options.watermarkComponent = Watermark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dropTarget = new Droptarget(this.element, {
|
||||||
|
canDisplayOverlay: (event, position) => {
|
||||||
|
const data = getPanelData();
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
if (data.viewId !== this.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.showDndOverlay) {
|
||||||
|
return this.options.showDndOverlay({
|
||||||
|
nativeEvent: event,
|
||||||
|
position: position,
|
||||||
|
target: DockviewDropTargets.Edge,
|
||||||
|
getData: getPanelData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
acceptedTargetZones: ['top', 'bottom', 'left', 'right'],
|
||||||
|
overlayModel: {
|
||||||
|
activationSize: { type: 'pixels', value: 10 },
|
||||||
|
size: { type: 'pixels', value: 20 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
dropTarget,
|
||||||
|
dropTarget.onDrop((event) => {
|
||||||
|
const data = getPanelData();
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
this.moveGroupOrPanel(
|
||||||
|
this.orthogonalize(event.position),
|
||||||
|
data.groupId,
|
||||||
|
data.panelId || undefined,
|
||||||
|
'center'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
this._api = new DockviewApi(this);
|
this._api = new DockviewApi(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private orthogonalize(position: Position): GroupPanel {
|
||||||
|
switch (position) {
|
||||||
|
case 'top':
|
||||||
|
case 'bottom':
|
||||||
|
if (this.gridview.orientation === Orientation.HORIZONTAL) {
|
||||||
|
// we need to add to a vertical splitview but the current root is a horizontal splitview.
|
||||||
|
// insert a vertical splitview at the root level and add the existing view as a child
|
||||||
|
this.gridview.insertOrthogonalSplitviewAtRoot();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
case 'right':
|
||||||
|
if (this.gridview.orientation === Orientation.VERTICAL) {
|
||||||
|
// we need to add to a horizontal splitview but the current root is a vertical splitview.
|
||||||
|
// insert a horiziontal splitview at the root level and add the existing view as a child
|
||||||
|
this.gridview.insertOrthogonalSplitviewAtRoot();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (position) {
|
||||||
|
case 'top':
|
||||||
|
case 'left':
|
||||||
|
return this.createGroupAtLocation([0]); // insert into first position
|
||||||
|
case 'bottom':
|
||||||
|
case 'right':
|
||||||
|
return this.createGroupAtLocation([this.gridview.length]); // insert into last position
|
||||||
|
default:
|
||||||
|
throw new Error(`unsupported position ${position}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateOptions(options: DockviewComponentUpdateOptions): void {
|
updateOptions(options: DockviewComponentUpdateOptions): void {
|
||||||
const hasOrientationChanged =
|
const hasOrientationChanged =
|
||||||
typeof options.orientation === 'string' &&
|
typeof options.orientation === 'string' &&
|
||||||
@ -265,9 +355,7 @@ export class DockviewComponent
|
|||||||
if (options.includePanel && options.group) {
|
if (options.includePanel && options.group) {
|
||||||
if (
|
if (
|
||||||
options.group.activePanel !==
|
options.group.activePanel !==
|
||||||
options.group.panels[
|
options.group.panels[options.group.panels.length - 1]
|
||||||
options.group.panels.length - 1
|
|
||||||
]
|
|
||||||
) {
|
) {
|
||||||
options.group.model.moveToNext({ suppressRoll: true });
|
options.group.model.moveToNext({ suppressRoll: true });
|
||||||
return;
|
return;
|
||||||
@ -275,7 +363,7 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
const location = getGridLocation(options.group.element);
|
const location = getGridLocation(options.group.element);
|
||||||
const next = <GroupPanel>this.gridview.next(location)?.view
|
const next = <GroupPanel>this.gridview.next(location)?.view;
|
||||||
this.doSetGroupActive(next);
|
this.doSetGroupActive(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,10 +376,7 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.includePanel && options.group) {
|
if (options.includePanel && options.group) {
|
||||||
if (
|
if (options.group.activePanel !== options.group.panels[0]) {
|
||||||
options.group.activePanel !==
|
|
||||||
options.group.panels[0]
|
|
||||||
) {
|
|
||||||
options.group.model.moveToPrevious({ suppressRoll: true });
|
options.group.model.moveToPrevious({ suppressRoll: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -326,7 +411,7 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
fromJSON(data: SerializedDockview): void {
|
fromJSON(data: SerializedDockview): void {
|
||||||
this.clear()
|
this.clear();
|
||||||
|
|
||||||
if (!this.deserializer) {
|
if (!this.deserializer) {
|
||||||
throw new Error('invalid deserializer');
|
throw new Error('invalid deserializer');
|
||||||
@ -341,11 +426,9 @@ export class DockviewComponent
|
|||||||
throw new Error('no deserializer provided');
|
throw new Error('no deserializer provided');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.gridview.deserialize(
|
this.gridview.deserialize(grid, {
|
||||||
grid,
|
fromJSON: (node: ISerializedLeafNode<GroupPanelViewState>) => {
|
||||||
{
|
const { id, locked, hideHeader, views, activeView } = node.data;
|
||||||
fromJSON:(node: ISerializedLeafNode<GroupPanelViewState>) => {
|
|
||||||
const { id, locked, hideHeader, views, activeView } = node.data
|
|
||||||
|
|
||||||
const group = this.createGroup({
|
const group = this.createGroup({
|
||||||
id,
|
id,
|
||||||
@ -356,26 +439,33 @@ export class DockviewComponent
|
|||||||
this._onDidAddGroup.fire(group);
|
this._onDidAddGroup.fire(group);
|
||||||
|
|
||||||
for (const child of views) {
|
for (const child of views) {
|
||||||
const panel = this.deserializer!.fromJSON(panels[child], group);
|
const panel = this.deserializer!.fromJSON(
|
||||||
|
panels[child],
|
||||||
|
group
|
||||||
|
);
|
||||||
|
|
||||||
const isActive = typeof activeView === 'string' && activeView === panel.id;
|
const isActive =
|
||||||
|
typeof activeView === 'string' &&
|
||||||
|
activeView === panel.id;
|
||||||
|
|
||||||
group.model.openPanel(panel, {
|
group.model.openPanel(panel, {
|
||||||
skipSetPanelActive: !isActive,
|
skipSetPanelActive: !isActive,
|
||||||
skipSetGroupActive:true
|
skipSetGroupActive: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!group.activePanel && group.panels.length > 0) {
|
if (!group.activePanel && group.panels.length > 0) {
|
||||||
group.model.openPanel(group.panels[group.panels.length - 1],{
|
group.model.openPanel(
|
||||||
skipSetGroupActive:true
|
group.panels[group.panels.length - 1],
|
||||||
});
|
{
|
||||||
|
skipSetGroupActive: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
)
|
|
||||||
|
|
||||||
if (typeof activeGroup === 'string') {
|
if (typeof activeGroup === 'string') {
|
||||||
const panel = this.getPanel(activeGroup);
|
const panel = this.getPanel(activeGroup);
|
||||||
@ -389,11 +479,11 @@ export class DockviewComponent
|
|||||||
this._onDidLayoutFromJSON.fire();
|
this._onDidLayoutFromJSON.fire();
|
||||||
}
|
}
|
||||||
|
|
||||||
clear():void {
|
clear(): void {
|
||||||
const groups = Array.from(this._groups.values()).map((_) => _.value);
|
const groups = Array.from(this._groups.values()).map((_) => _.value);
|
||||||
|
|
||||||
const hasActiveGroup = !!this.activeGroup;
|
const hasActiveGroup = !!this.activeGroup;
|
||||||
const hasActivePanel = !!this.activePanel
|
const hasActivePanel = !!this.activePanel;
|
||||||
|
|
||||||
for (const group of groups) {
|
for (const group of groups) {
|
||||||
// remove the group will automatically remove the panels
|
// remove the group will automatically remove the panels
|
||||||
@ -404,7 +494,7 @@ export class DockviewComponent
|
|||||||
this.doSetGroupActive(undefined);
|
this.doSetGroupActive(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( hasActivePanel) {
|
if (hasActivePanel) {
|
||||||
this._onDidActivePanelChange.fire(undefined);
|
this._onDidActivePanelChange.fire(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,10 +516,12 @@ export class DockviewComponent
|
|||||||
|
|
||||||
let referenceGroup: GroupPanel | undefined;
|
let referenceGroup: GroupPanel | undefined;
|
||||||
|
|
||||||
if (options.position?.referencePanel) {
|
if (options.position) {
|
||||||
const referencePanel = this.getGroupPanel(
|
if (isPanelOptionsWithPanel(options.position)) {
|
||||||
options.position.referencePanel
|
const referencePanel =
|
||||||
);
|
typeof options.position.referencePanel === 'string'
|
||||||
|
? this.getGroupPanel(options.position.referencePanel)
|
||||||
|
: options.position.referencePanel;
|
||||||
|
|
||||||
if (!referencePanel) {
|
if (!referencePanel) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -438,16 +530,38 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
referenceGroup = this.findGroup(referencePanel);
|
referenceGroup = this.findGroup(referencePanel);
|
||||||
|
} else if (isPanelOptionsWithGroup(options.position)) {
|
||||||
|
referenceGroup =
|
||||||
|
typeof options.position.referenceGroup === 'string'
|
||||||
|
? this._groups.get(options.position.referenceGroup)
|
||||||
|
?.value
|
||||||
|
: options.position.referenceGroup;
|
||||||
|
|
||||||
|
if (!referenceGroup) {
|
||||||
|
throw new Error(
|
||||||
|
`referencePanel ${options.position.referenceGroup} does not exist`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const group = this.orthogonalize(
|
||||||
|
directionToPosition(<Direction>options.position.direction)
|
||||||
|
);
|
||||||
|
const panel = this.createPanel(options, group);
|
||||||
|
group.model.openPanel(panel);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
referenceGroup = this.activeGroup;
|
referenceGroup = this.activeGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
let panel: IDockviewPanel
|
let panel: IDockviewPanel;
|
||||||
|
|
||||||
if (referenceGroup) {
|
if (referenceGroup) {
|
||||||
const target = toTarget(options.position?.direction || 'within');
|
const target = toTarget(
|
||||||
if (target === Position.Center) {
|
<Direction>options.position?.direction || 'within'
|
||||||
panel = this.createPanel(options, referenceGroup)
|
);
|
||||||
|
if (target === 'center') {
|
||||||
|
panel = this.createPanel(options, referenceGroup);
|
||||||
referenceGroup.model.openPanel(panel);
|
referenceGroup.model.openPanel(panel);
|
||||||
} else {
|
} else {
|
||||||
const location = getGridLocation(referenceGroup.element);
|
const location = getGridLocation(referenceGroup.element);
|
||||||
@ -457,11 +571,12 @@ export class DockviewComponent
|
|||||||
target
|
target
|
||||||
);
|
);
|
||||||
const group = this.createGroupAtLocation(relativeLocation);
|
const group = this.createGroupAtLocation(relativeLocation);
|
||||||
panel = this.createPanel(options, group)
|
panel = this.createPanel(options, group);
|
||||||
group.model.openPanel(panel);
|
group.model.openPanel(panel);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const group = this.createGroupAtLocation();
|
const group = this.createGroupAtLocation();
|
||||||
|
|
||||||
panel = this.createPanel(options, group);
|
panel = this.createPanel(options, group);
|
||||||
group.model.openPanel(panel);
|
group.model.openPanel(panel);
|
||||||
}
|
}
|
||||||
@ -513,13 +628,19 @@ export class DockviewComponent
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
addEmptyGroup(options: AddGroupOptions): void {
|
addGroup(options: AddGroupOptions): GroupPanel {
|
||||||
const group = this.createGroup();
|
const group = this.createGroup();
|
||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
const referencePanel = this.panels.find(
|
let referenceGroup: GroupPanel | undefined;
|
||||||
|
|
||||||
|
if (isGroupOptionsWithPanel(options)) {
|
||||||
|
const referencePanel =
|
||||||
|
typeof options.referencePanel === 'string'
|
||||||
|
? this.panels.find(
|
||||||
(panel) => panel.id === options.referencePanel
|
(panel) => panel.id === options.referencePanel
|
||||||
);
|
)
|
||||||
|
: options.referencePanel;
|
||||||
|
|
||||||
if (!referencePanel) {
|
if (!referencePanel) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -527,15 +648,32 @@ export class DockviewComponent
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const referenceGroup = this.findGroup(referencePanel);
|
referenceGroup = this.findGroup(referencePanel);
|
||||||
|
|
||||||
if (!referenceGroup) {
|
if (!referenceGroup) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`reference group for reference panel ${options.referencePanel} does not exist`
|
`reference group for reference panel ${options.referencePanel} does not exist`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (isGroupOptionsWithGroup(options)) {
|
||||||
|
referenceGroup =
|
||||||
|
typeof options.referenceGroup === 'string'
|
||||||
|
? this._groups.get(options.referenceGroup)?.value
|
||||||
|
: options.referenceGroup;
|
||||||
|
|
||||||
const target = toTarget(options.direction || 'within');
|
if (!referenceGroup) {
|
||||||
|
throw new Error(
|
||||||
|
`reference group ${options.referenceGroup} does not exist`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const group = this.orthogonalize(
|
||||||
|
directionToPosition(<Direction>options.direction)
|
||||||
|
);
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = toTarget(<Direction>options.direction || 'within');
|
||||||
|
|
||||||
const location = getGridLocation(referenceGroup.element);
|
const location = getGridLocation(referenceGroup.element);
|
||||||
const relativeLocation = getRelativeLocation(
|
const relativeLocation = getRelativeLocation(
|
||||||
@ -544,8 +682,10 @@ export class DockviewComponent
|
|||||||
target
|
target
|
||||||
);
|
);
|
||||||
this.doAddGroup(group, relativeLocation);
|
this.doAddGroup(group, relativeLocation);
|
||||||
|
return group;
|
||||||
} else {
|
} else {
|
||||||
this.doAddGroup(group);
|
this.doAddGroup(group);
|
||||||
|
return group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,15 +713,14 @@ export class DockviewComponent
|
|||||||
? this._groups.get(groupId)?.value
|
? this._groups.get(groupId)?.value
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
if (itemId === undefined) {
|
||||||
if(itemId === undefined) {
|
if (sourceGroup) {
|
||||||
if(sourceGroup) {
|
|
||||||
this.moveGroup(sourceGroup, referenceGroup, target);
|
this.moveGroup(sourceGroup, referenceGroup, target);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!target || target === Position.Center) {
|
if (!target || target === 'center') {
|
||||||
const groupItem: IDockviewPanel | undefined =
|
const groupItem: IDockviewPanel | undefined =
|
||||||
sourceGroup?.model.removePanel(itemId) ||
|
sourceGroup?.model.removePanel(itemId) ||
|
||||||
this.panels.find((panel) => panel.id === itemId);
|
this.panels.find((panel) => panel.id === itemId);
|
||||||
@ -603,7 +742,6 @@ export class DockviewComponent
|
|||||||
target
|
target
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
if (sourceGroup && sourceGroup.size < 2) {
|
if (sourceGroup && sourceGroup.size < 2) {
|
||||||
const [targetParentLocation, to] = tail(targetLocation);
|
const [targetParentLocation, to] = tail(targetLocation);
|
||||||
const sourceLocation = getGridLocation(sourceGroup.element);
|
const sourceLocation = getGridLocation(sourceGroup.element);
|
||||||
@ -655,49 +793,54 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private moveGroup(sourceGroup: GroupPanel, referenceGroup: GroupPanel, target: Position): void {
|
private moveGroup(
|
||||||
if(sourceGroup) {
|
sourceGroup: GroupPanel,
|
||||||
if (!target || target === Position.Center) {
|
referenceGroup: GroupPanel,
|
||||||
|
target: Position
|
||||||
|
): void {
|
||||||
|
if (sourceGroup) {
|
||||||
|
if (!target || target === 'center') {
|
||||||
const activePanel = sourceGroup.activePanel;
|
const activePanel = sourceGroup.activePanel;
|
||||||
const panels = [...sourceGroup.panels].map(p => sourceGroup.model.removePanel(p.id));
|
const panels = [...sourceGroup.panels].map((p) =>
|
||||||
|
sourceGroup.model.removePanel(p.id)
|
||||||
|
);
|
||||||
|
|
||||||
if (sourceGroup?.model.size === 0) {
|
if (sourceGroup?.model.size === 0) {
|
||||||
this.doRemoveGroup(sourceGroup);
|
this.doRemoveGroup(sourceGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const panel of panels) {
|
for (const panel of panels) {
|
||||||
referenceGroup.model.openPanel(panel,{skipSetPanelActive:panel !== activePanel});
|
referenceGroup.model.openPanel(panel, {
|
||||||
|
skipSetPanelActive: panel !== activePanel,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
|
|
||||||
this.gridview.removeView(getGridLocation(sourceGroup.element));
|
this.gridview.removeView(getGridLocation(sourceGroup.element));
|
||||||
|
|
||||||
const referenceLocation = getGridLocation(referenceGroup.element);
|
const referenceLocation = getGridLocation(
|
||||||
|
referenceGroup.element
|
||||||
|
);
|
||||||
const dropLocation = getRelativeLocation(
|
const dropLocation = getRelativeLocation(
|
||||||
this.gridview.orientation,
|
this.gridview.orientation,
|
||||||
referenceLocation,
|
referenceLocation,
|
||||||
target
|
target
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.gridview.addView(
|
||||||
|
sourceGroup,
|
||||||
this.gridview.addView(sourceGroup, Sizing.Distribute, dropLocation);
|
Sizing.Distribute,
|
||||||
|
dropLocation
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override doSetGroupActive(
|
doSetGroupActive(group: GroupPanel | undefined, skipFocus?: boolean): void {
|
||||||
group: GroupPanel | undefined,
|
|
||||||
skipFocus?: boolean
|
|
||||||
): void {
|
|
||||||
const isGroupAlreadyFocused = this._activeGroup === group;
|
const isGroupAlreadyFocused = this._activeGroup === group;
|
||||||
super.doSetGroupActive(group, skipFocus);
|
super.doSetGroupActive(group, skipFocus);
|
||||||
|
|
||||||
if (!isGroupAlreadyFocused && this._activeGroup?.activePanel) {
|
if (!isGroupAlreadyFocused && this._activeGroup?.activePanel) {
|
||||||
this._onDidActivePanelChange.fire(
|
this._onDidActivePanelChange.fire(this._activeGroup?.activePanel);
|
||||||
this._activeGroup?.activePanel
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,9 +862,9 @@ export class DockviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
id = nextGroupId.next();
|
id = this.nextGroupId.next();
|
||||||
while (this._groups.has(id)) {
|
while (this._groups.has(id)) {
|
||||||
id = nextGroupId.next();
|
id = this.nextGroupId.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,11 +878,13 @@ export class DockviewComponent
|
|||||||
this.moveGroupOrPanel(view, groupId, itemId, target, index);
|
this.moveGroupOrPanel(view, groupId, itemId, target, index);
|
||||||
}),
|
}),
|
||||||
view.model.onDidDrop((event) => {
|
view.model.onDidDrop((event) => {
|
||||||
this._onDidDrop.fire({ ...event, api: this._api, group: view });
|
this._onDidDrop.fire({
|
||||||
|
...event,
|
||||||
|
api: this._api,
|
||||||
|
group: view,
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
view.model.onDidAddPanel((event) => {
|
view.model.onDidAddPanel((event) => {
|
||||||
|
|
||||||
|
|
||||||
this._onDidAddPanel.fire(event.panel);
|
this._onDidAddPanel.fire(event.panel);
|
||||||
}),
|
}),
|
||||||
view.model.onDidRemovePanel((event) => {
|
view.model.onDidRemovePanel((event) => {
|
||||||
@ -764,13 +909,19 @@ export class DockviewComponent
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createPanel(options: AddPanelOptions, group: GroupPanel): IDockviewPanel {
|
private createPanel(
|
||||||
|
options: AddPanelOptions,
|
||||||
|
group: GroupPanel
|
||||||
|
): IDockviewPanel {
|
||||||
const view = new DefaultGroupPanelView({
|
const view = new DefaultGroupPanelView({
|
||||||
content: this.createContentComponent(options.id, options.component),
|
content: this.createContentComponent(options.id, options.component),
|
||||||
tab: this.createTabComponent(options.id, options.tabComponent || this.options.defaultTabComponent),
|
tab: this.createTabComponent(
|
||||||
|
options.id,
|
||||||
|
options.tabComponent || this.options.defaultTabComponent
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel = new DockviewGroupPanel(options.id, this, this._api, group);
|
const panel = new DockviewPanel(options.id, this, this._api, group);
|
||||||
panel.init({
|
panel.init({
|
||||||
view,
|
view,
|
||||||
title: options.title || options.id,
|
title: options.title || options.id,
|
||||||
@ -807,12 +958,10 @@ export class DockviewComponent
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private createGroupAtLocation(
|
private createGroupAtLocation(location: number[] = [0]): GroupPanel {
|
||||||
location: number[] = [0]
|
|
||||||
): GroupPanel {
|
|
||||||
const group = this.createGroup();
|
const group = this.createGroup();
|
||||||
this.doAddGroup(group, location);
|
this.doAddGroup(group, location);
|
||||||
return group
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
private findGroup(panel: IDockviewPanel): GroupPanel | undefined {
|
private findGroup(panel: IDockviewPanel): GroupPanel | undefined {
|
||||||
|
@ -12,7 +12,7 @@ import { Parameters } from '../panel/types';
|
|||||||
import { IGroupPanelView } from './defaultGroupPanelView';
|
import { IGroupPanelView } from './defaultGroupPanelView';
|
||||||
import { DockviewComponent } from './dockviewComponent';
|
import { DockviewComponent } from './dockviewComponent';
|
||||||
|
|
||||||
export class DockviewGroupPanel
|
export class DockviewPanel
|
||||||
extends CompositeDisposable
|
extends CompositeDisposable
|
||||||
implements IDockviewPanel
|
implements IDockviewPanel
|
||||||
{
|
{
|
||||||
@ -38,7 +38,7 @@ export class DockviewGroupPanel
|
|||||||
return this._group;
|
return this._group;
|
||||||
}
|
}
|
||||||
|
|
||||||
get view() {
|
get view(): IGroupPanelView | undefined {
|
||||||
return this._view;
|
return this._view;
|
||||||
}
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ import { FrameworkFactory } from '../types';
|
|||||||
import { DockviewDropTargets } from '../groupview/dnd';
|
import { DockviewDropTargets } from '../groupview/dnd';
|
||||||
import { PanelTransfer } from '../dnd/dataTransfer';
|
import { PanelTransfer } from '../dnd/dataTransfer';
|
||||||
import { IGroupControlRenderer } from '../react/dockview/groupControlsRenderer';
|
import { IGroupControlRenderer } from '../react/dockview/groupControlsRenderer';
|
||||||
|
import { Position } from '../dnd/droptarget';
|
||||||
|
|
||||||
export interface GroupPanelFrameworkComponentFactory {
|
export interface GroupPanelFrameworkComponentFactory {
|
||||||
content: FrameworkFactory<IContentRenderer>;
|
content: FrameworkFactory<IContentRenderer>;
|
||||||
@ -54,7 +55,8 @@ export interface ViewFactoryData {
|
|||||||
export interface DockviewDndOverlayEvent {
|
export interface DockviewDndOverlayEvent {
|
||||||
nativeEvent: DragEvent;
|
nativeEvent: DragEvent;
|
||||||
target: DockviewDropTargets;
|
target: DockviewDropTargets;
|
||||||
group: GroupPanel;
|
position: Position;
|
||||||
|
group?: GroupPanel;
|
||||||
getData: () => PanelTransfer | undefined;
|
getData: () => PanelTransfer | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +70,7 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
|
|||||||
defaultTabComponent?: string;
|
defaultTabComponent?: string;
|
||||||
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
|
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
|
||||||
createGroupControlElement?: (group: GroupPanel) => IGroupControlRenderer;
|
createGroupControlElement?: (group: GroupPanel) => IGroupControlRenderer;
|
||||||
|
singleTabMode?: 'fullwidth' | 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelOptions {
|
export interface PanelOptions {
|
||||||
@ -78,19 +81,81 @@ export interface PanelOptions {
|
|||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RelativePanel = {
|
||||||
|
direction?: Direction;
|
||||||
|
referencePanel: string | IDockviewPanel;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RelativeGroup = {
|
||||||
|
direction?: Direction;
|
||||||
|
referenceGroup: string | GroupPanel;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AbsolutePosition = {
|
||||||
|
direction: Omit<Direction, 'within'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AddPanelPositionOptions =
|
||||||
|
| RelativePanel
|
||||||
|
| RelativeGroup
|
||||||
|
| AbsolutePosition;
|
||||||
|
|
||||||
|
export function isPanelOptionsWithPanel(
|
||||||
|
data: AddPanelPositionOptions
|
||||||
|
): data is RelativePanel {
|
||||||
|
if ((data as RelativePanel).referencePanel) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPanelOptionsWithGroup(
|
||||||
|
data: AddPanelPositionOptions
|
||||||
|
): data is RelativeGroup {
|
||||||
|
if ((data as RelativeGroup).referenceGroup) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AddPanelOptions
|
export interface AddPanelOptions
|
||||||
extends Omit<PanelOptions, 'component' | 'tabComponent'> {
|
extends Omit<PanelOptions, 'component' | 'tabComponent'> {
|
||||||
component: string;
|
component: string;
|
||||||
tabComponent?: string;
|
tabComponent?: string;
|
||||||
position?: {
|
position?: AddPanelPositionOptions;
|
||||||
direction?: Direction;
|
|
||||||
referencePanel?: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddGroupOptions {
|
type AddGroupOptionsWithPanel = {
|
||||||
direction?: 'left' | 'right' | 'above' | 'below';
|
referencePanel: string | IDockviewPanel;
|
||||||
referencePanel: string;
|
direction?: Omit<Direction, 'within'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AddGroupOptionsWithGroup = {
|
||||||
|
referenceGroup: string | GroupPanel;
|
||||||
|
direction?: Omit<Direction, 'within'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AddGroupOptions =
|
||||||
|
| AddGroupOptionsWithGroup
|
||||||
|
| AddGroupOptionsWithPanel
|
||||||
|
| AbsolutePosition;
|
||||||
|
|
||||||
|
export function isGroupOptionsWithPanel(
|
||||||
|
data: AddGroupOptions
|
||||||
|
): data is AddGroupOptionsWithPanel {
|
||||||
|
if ((data as AddGroupOptionsWithPanel).referencePanel) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isGroupOptionsWithGroup(
|
||||||
|
data: AddGroupOptions
|
||||||
|
): data is AddGroupOptionsWithGroup {
|
||||||
|
if ((data as AddGroupOptionsWithGroup).referenceGroup) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MovementOptions2 {
|
export interface MovementOptions2 {
|
||||||
|
@ -15,19 +15,19 @@ const nextLayoutId = sequentialNumberGenerator();
|
|||||||
|
|
||||||
export type Direction = 'left' | 'right' | 'above' | 'below' | 'within';
|
export type Direction = 'left' | 'right' | 'above' | 'below' | 'within';
|
||||||
|
|
||||||
export function toTarget(direction: Direction) {
|
export function toTarget(direction: Direction): Position {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 'left':
|
case 'left':
|
||||||
return Position.Left;
|
return 'left';
|
||||||
case 'right':
|
case 'right':
|
||||||
return Position.Right;
|
return 'right';
|
||||||
case 'above':
|
case 'above':
|
||||||
return Position.Top;
|
return 'top';
|
||||||
case 'below':
|
case 'below':
|
||||||
return Position.Bottom;
|
return 'bottom';
|
||||||
case 'within':
|
case 'within':
|
||||||
default:
|
default:
|
||||||
return Position.Center;
|
return 'center';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
PanelInitParameters,
|
PanelInitParameters,
|
||||||
IPanel,
|
IPanel,
|
||||||
} from '../panel/types';
|
} from '../panel/types';
|
||||||
import { PanelApiImpl } from '../api/panelApi';
|
import { PanelApi, PanelApiImpl } from '../api/panelApi';
|
||||||
|
|
||||||
export interface BasePanelViewState {
|
export interface BasePanelViewState {
|
||||||
id: string;
|
id: string;
|
||||||
@ -14,7 +14,7 @@ export interface BasePanelViewState {
|
|||||||
params?: Record<string, any>;
|
params?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BasePanelViewExported<T extends PanelApiImpl> {
|
export interface BasePanelViewExported<T extends PanelApi> {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly api: T;
|
readonly api: T;
|
||||||
readonly width: number;
|
readonly width: number;
|
||||||
|
@ -9,13 +9,13 @@ import {
|
|||||||
Orientation,
|
Orientation,
|
||||||
Sizing,
|
Sizing,
|
||||||
} from '../splitview/core/splitview';
|
} from '../splitview/core/splitview';
|
||||||
import { Position } from '../dnd/droptarget';
|
|
||||||
import { tail } from '../array';
|
import { tail } from '../array';
|
||||||
import { LeafNode } from './leafNode';
|
import { LeafNode } from './leafNode';
|
||||||
import { BranchNode } from './branchNode';
|
import { BranchNode } from './branchNode';
|
||||||
import { Node } from './types';
|
import { Node } from './types';
|
||||||
import { Emitter, Event } from '../events';
|
import { Emitter, Event } from '../events';
|
||||||
import { IDisposable, MutableDisposable } from '../lifecycle';
|
import { IDisposable, MutableDisposable } from '../lifecycle';
|
||||||
|
import { Position } from '../dnd/droptarget';
|
||||||
|
|
||||||
function findLeaf(candiateNode: Node, last: boolean): LeafNode {
|
function findLeaf(candiateNode: Node, last: boolean): LeafNode {
|
||||||
if (candiateNode instanceof LeafNode) {
|
if (candiateNode instanceof LeafNode) {
|
||||||
@ -132,22 +132,19 @@ export function getRelativeLocation(
|
|||||||
const [rest, _index] = tail(location);
|
const [rest, _index] = tail(location);
|
||||||
let index = _index;
|
let index = _index;
|
||||||
|
|
||||||
if (direction === Position.Right || direction === Position.Bottom) {
|
if (direction === 'right' || direction === 'bottom') {
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...rest, index];
|
return [...rest, index];
|
||||||
} else {
|
} else {
|
||||||
const index =
|
const index = direction === 'right' || direction === 'bottom' ? 1 : 0;
|
||||||
direction === Position.Right || direction === Position.Bottom
|
|
||||||
? 1
|
|
||||||
: 0;
|
|
||||||
return [...location, index];
|
return [...location, index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDirectionOrientation(direction: Position): Orientation {
|
export function getDirectionOrientation(direction: Position): Orientation {
|
||||||
return direction === Position.Top || direction === Position.Bottom
|
return direction === 'top' || direction === 'bottom'
|
||||||
? Orientation.VERTICAL
|
? Orientation.VERTICAL
|
||||||
: Orientation.HORIZONTAL;
|
: Orientation.HORIZONTAL;
|
||||||
}
|
}
|
||||||
@ -276,6 +273,10 @@ export class Gridview implements IDisposable {
|
|||||||
readonly onDidChange: Event<{ size?: number; orthogonalSize?: number }> =
|
readonly onDidChange: Event<{ size?: number; orthogonalSize?: number }> =
|
||||||
this._onDidChange.event;
|
this._onDidChange.event;
|
||||||
|
|
||||||
|
public get length(): number {
|
||||||
|
return this._root ? this._root.children.length : 0;
|
||||||
|
}
|
||||||
|
|
||||||
public serialize() {
|
public serialize() {
|
||||||
const root = serializeBranchNode(this.getView(), this.orientation);
|
const root = serializeBranchNode(this.getView(), this.orientation);
|
||||||
|
|
||||||
@ -410,6 +411,43 @@ export class Gridview implements IDisposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the root is orientated as a VERTICAL node then nest the existing root within a new HORIZIONTAL root node
|
||||||
|
* If the root is orientated as a HORIZONTAL node then nest the existing root within a new VERITCAL root node
|
||||||
|
*/
|
||||||
|
public insertOrthogonalSplitviewAtRoot(): void {
|
||||||
|
if (!this._root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldRoot = this.root;
|
||||||
|
oldRoot.element.remove();
|
||||||
|
|
||||||
|
this._root = new BranchNode(
|
||||||
|
orthogonal(oldRoot.orientation),
|
||||||
|
this.proportionalLayout,
|
||||||
|
this.styles,
|
||||||
|
this.root.orthogonalSize,
|
||||||
|
this.root.size
|
||||||
|
);
|
||||||
|
|
||||||
|
if (oldRoot.children.length === 1) {
|
||||||
|
// can remove one level of redundant branching if there is only a single child
|
||||||
|
const childReference = oldRoot.children[0];
|
||||||
|
oldRoot.removeChild(0); // remove to prevent disposal when disposing of unwanted root
|
||||||
|
oldRoot.dispose();
|
||||||
|
this._root.addChild(childReference, Sizing.Distribute, 0);
|
||||||
|
} else {
|
||||||
|
this._root.addChild(oldRoot, Sizing.Distribute, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.appendChild(this._root.element);
|
||||||
|
|
||||||
|
this.disposable.value = this._root.onDidChange((e) => {
|
||||||
|
this._onDidChange.fire(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public next(location: number[]) {
|
public next(location: number[]) {
|
||||||
return this.progmaticSelect(location);
|
return this.progmaticSelect(location);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import {
|
|||||||
SerializedGridObject,
|
SerializedGridObject,
|
||||||
getGridLocation,
|
getGridLocation,
|
||||||
} from './gridview';
|
} from './gridview';
|
||||||
import { Position } from '../dnd/droptarget';
|
|
||||||
import { tail, sequenceEquals } from '../array';
|
import { tail, sequenceEquals } from '../array';
|
||||||
import { CompositeDisposable } from '../lifecycle';
|
import { CompositeDisposable } from '../lifecycle';
|
||||||
import { IPanelDeserializer } from '../dockview/deserializer';
|
import { IPanelDeserializer } from '../dockview/deserializer';
|
||||||
@ -25,6 +24,7 @@ import { BaseComponentOptions } from '../panel/types';
|
|||||||
import { Orientation, Sizing } from '../splitview/core/splitview';
|
import { Orientation, Sizing } from '../splitview/core/splitview';
|
||||||
import { createComponent } from '../panel/componentFactory';
|
import { createComponent } from '../panel/componentFactory';
|
||||||
import { Emitter, Event } from '../events';
|
import { Emitter, Event } from '../events';
|
||||||
|
import { Position } from '../dnd/droptarget';
|
||||||
|
|
||||||
export interface SerializedGridview {
|
export interface SerializedGridview {
|
||||||
grid: {
|
grid: {
|
||||||
@ -265,7 +265,7 @@ export class GridviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
const target = toTarget(options.direction);
|
const target = toTarget(options.direction);
|
||||||
if (target === Position.Center) {
|
if (target === 'center') {
|
||||||
throw new Error(`${target} not supported as an option`);
|
throw new Error(`${target} not supported as an option`);
|
||||||
} else {
|
} else {
|
||||||
const location = getGridLocation(referenceGroup.element);
|
const location = getGridLocation(referenceGroup.element);
|
||||||
@ -294,7 +294,7 @@ export class GridviewComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
const target = toTarget(options.position.direction);
|
const target = toTarget(options.position.direction);
|
||||||
if (target === Position.Center) {
|
if (target === 'center') {
|
||||||
throw new Error(`${target} not supported as an option`);
|
throw new Error(`${target} not supported as an option`);
|
||||||
} else {
|
} else {
|
||||||
const location = getGridLocation(referenceGroup.element);
|
const location = getGridLocation(referenceGroup.element);
|
||||||
|
@ -9,7 +9,10 @@ import {
|
|||||||
BasePanelViewExported,
|
BasePanelViewExported,
|
||||||
BasePanelViewState,
|
BasePanelViewState,
|
||||||
} from './basePanelView';
|
} from './basePanelView';
|
||||||
import { GridviewPanelApiImpl } from '../api/gridviewPanelApi';
|
import {
|
||||||
|
GridviewPanelApi,
|
||||||
|
GridviewPanelApiImpl,
|
||||||
|
} from '../api/gridviewPanelApi';
|
||||||
import { LayoutPriority } from '../splitview/core/splitview';
|
import { LayoutPriority } from '../splitview/core/splitview';
|
||||||
import { Emitter, Event } from '../events';
|
import { Emitter, Event } from '../events';
|
||||||
import { IViewSize } from './gridview';
|
import { IViewSize } from './gridview';
|
||||||
@ -26,7 +29,7 @@ export interface GridviewInitParameters extends PanelInitParameters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IGridviewPanel
|
export interface IGridviewPanel
|
||||||
extends BasePanelViewExported<GridviewPanelApiImpl> {
|
extends BasePanelViewExported<GridviewPanelApi> {
|
||||||
readonly minimumWidth: number;
|
readonly minimumWidth: number;
|
||||||
readonly maximumWidth: number;
|
readonly maximumWidth: number;
|
||||||
readonly minimumHeight: number;
|
readonly minimumHeight: number;
|
||||||
@ -123,13 +126,11 @@ export abstract class GridviewPanel
|
|||||||
return this.api.isActive;
|
return this.api.isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(id: string, component: string, api: GridviewPanelApiImpl) {
|
||||||
id: string,
|
|
||||||
component: string,
|
|
||||||
api = new GridviewPanelApiImpl(id)
|
|
||||||
) {
|
|
||||||
super(id, component, api);
|
super(id, component, api);
|
||||||
|
|
||||||
|
this.api.initialize(this); // TODO: required to by-pass 'super before this' requirement
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onDidChange,
|
this._onDidChange,
|
||||||
this.api.onVisibilityChange((event) => {
|
this.api.onVisibilityChange((event) => {
|
||||||
|
@ -2,4 +2,5 @@ export enum DockviewDropTargets {
|
|||||||
Tab,
|
Tab,
|
||||||
Panel,
|
Panel,
|
||||||
TabContainer,
|
TabContainer,
|
||||||
|
Edge,
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,11 @@ export interface IGroupview extends IDisposable, IGridPanelView {
|
|||||||
panel?: IDockviewPanel;
|
panel?: IDockviewPanel;
|
||||||
suppressRoll?: boolean;
|
suppressRoll?: boolean;
|
||||||
}): void;
|
}): void;
|
||||||
canDisplayOverlay(event: DragEvent, target: DockviewDropTargets): boolean;
|
canDisplayOverlay(
|
||||||
|
event: DragEvent,
|
||||||
|
position: Position,
|
||||||
|
target: DockviewDropTargets
|
||||||
|
): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Groupview extends CompositeDisposable implements IGroupview {
|
export class Groupview extends CompositeDisposable implements IGroupview {
|
||||||
@ -167,6 +171,8 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
|
|
||||||
set locked(value: boolean) {
|
set locked(value: boolean) {
|
||||||
this._locked = value;
|
this._locked = value;
|
||||||
|
|
||||||
|
toggleClass(this.container, 'locked-groupview', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isActive(): boolean {
|
get isActive(): boolean {
|
||||||
@ -226,45 +232,48 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
private accessor: DockviewComponent,
|
private accessor: DockviewComponent,
|
||||||
public id: string,
|
public id: string,
|
||||||
private readonly options: GroupOptions,
|
private readonly options: GroupOptions,
|
||||||
private readonly parent: GroupPanel
|
private readonly groupPanel: GroupPanel
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.container.classList.add('groupview');
|
this.container.classList.add('groupview');
|
||||||
|
|
||||||
this.tabsContainer = new TabsContainer(this.accessor, this.parent, {
|
this.tabsContainer = new TabsContainer(this.accessor, this.groupPanel);
|
||||||
tabHeight: options.tabHeight,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.contentContainer = new ContentContainer();
|
this.contentContainer = new ContentContainer();
|
||||||
|
|
||||||
this.dropTarget = new Droptarget(this.contentContainer.element, {
|
this.dropTarget = new Droptarget(this.contentContainer.element, {
|
||||||
validOverlays: 'all',
|
acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
|
||||||
canDisplayOverlay: (event, quadrant) => {
|
canDisplayOverlay: (event, position) => {
|
||||||
if (this.locked && !quadrant) {
|
if (this.locked && position === 'center') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = getPanelData();
|
const data = getPanelData();
|
||||||
|
|
||||||
if (
|
if (data && data.viewId === this.accessor.id) {
|
||||||
data &&
|
if (data.groupId === this.id) {
|
||||||
data.panelId === null &&
|
if (position === 'center') {
|
||||||
data.viewId === this.accessor.id &&
|
// don't allow to drop on self for center position
|
||||||
data.groupId !== this.id
|
return false;
|
||||||
) {
|
}
|
||||||
// prevent dropping on self for group dnd
|
if (data.panelId === null) {
|
||||||
return true;
|
// don't allow group move to drop anywhere on self
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data && data.viewId === this.accessor.id) {
|
|
||||||
const groupHasOnePanelAndIsActiveDragElement =
|
const groupHasOnePanelAndIsActiveDragElement =
|
||||||
this._panels.length === 1 && data.groupId === this.id;
|
this._panels.length === 1 && data.groupId === this.id;
|
||||||
|
|
||||||
return !groupHasOnePanelAndIsActiveDragElement;
|
return !groupHasOnePanelAndIsActiveDragElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.canDisplayOverlay(event, DockviewDropTargets.Panel);
|
return this.canDisplayOverlay(
|
||||||
|
event,
|
||||||
|
position,
|
||||||
|
DockviewDropTargets.Panel
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -284,10 +293,10 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this._onDidRemovePanel,
|
this._onDidRemovePanel,
|
||||||
this._onDidActivePanelChange,
|
this._onDidActivePanelChange,
|
||||||
this.tabsContainer.onDrop((event) => {
|
this.tabsContainer.onDrop((event) => {
|
||||||
this.handleDropEvent(event.event, Position.Center, event.index);
|
this.handleDropEvent(event.event, 'center', event.index);
|
||||||
}),
|
}),
|
||||||
this.contentContainer.onDidFocus(() => {
|
this.contentContainer.onDidFocus(() => {
|
||||||
this.accessor.doSetGroupActive(this.parent, true);
|
this.accessor.doSetGroupActive(this.groupPanel, true);
|
||||||
}),
|
}),
|
||||||
this.contentContainer.onDidBlur(() => {
|
this.contentContainer.onDidBlur(() => {
|
||||||
// noop
|
// noop
|
||||||
@ -316,12 +325,12 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
|
|
||||||
if (this.accessor.options.createGroupControlElement) {
|
if (this.accessor.options.createGroupControlElement) {
|
||||||
this._control = this.accessor.options.createGroupControlElement(
|
this._control = this.accessor.options.createGroupControlElement(
|
||||||
this.parent
|
this.groupPanel
|
||||||
);
|
);
|
||||||
this.addDisposables(this._control);
|
this.addDisposables(this._control);
|
||||||
this._control.init({
|
this._control.init({
|
||||||
containerApi: new DockviewApi(this.accessor),
|
containerApi: new DockviewApi(this.accessor),
|
||||||
api: this.parent.api,
|
api: this.groupPanel.api,
|
||||||
});
|
});
|
||||||
this.tabsContainer.setActionElement(this._control.element);
|
this.tabsContainer.setActionElement(this._control.element);
|
||||||
}
|
}
|
||||||
@ -441,11 +450,11 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
const skipSetGroupActive = !!options.skipSetGroupActive;
|
const skipSetGroupActive = !!options.skipSetGroupActive;
|
||||||
|
|
||||||
// ensure the group is updated before we fire any events
|
// ensure the group is updated before we fire any events
|
||||||
panel.updateParentGroup(this.parent, true);
|
panel.updateParentGroup(this.groupPanel, true);
|
||||||
|
|
||||||
if (this._activePanel === panel) {
|
if (this._activePanel === panel) {
|
||||||
if (!skipSetGroupActive) {
|
if (!skipSetGroupActive) {
|
||||||
this.accessor.doSetGroupActive(this.parent);
|
this.accessor.doSetGroupActive(this.groupPanel);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -457,7 +466,10 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!skipSetGroupActive) {
|
if (!skipSetGroupActive) {
|
||||||
this.accessor.doSetGroupActive(this.parent, !!options.skipFocus);
|
this.accessor.doSetGroupActive(
|
||||||
|
this.groupPanel,
|
||||||
|
!!options.skipFocus
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateContainer();
|
this.updateContainer();
|
||||||
@ -486,7 +498,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
this.doClose(panel);
|
this.doClose(panel);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.accessor.removeGroup(this.parent);
|
this.accessor.removeGroup(this.groupPanel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,7 +655,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
toggleClass(this.container, 'empty', this.isEmpty);
|
toggleClass(this.container, 'empty', this.isEmpty);
|
||||||
|
|
||||||
this.panels.forEach((panel) =>
|
this.panels.forEach((panel) =>
|
||||||
panel.updateParentGroup(this.parent, this.isActive)
|
panel.updateParentGroup(this.groupPanel, this.isActive)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.isEmpty && !this.watermark) {
|
if (this.isEmpty && !this.watermark) {
|
||||||
@ -658,14 +670,14 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
|
|
||||||
addDisposableListener(this.watermark.element, 'click', () => {
|
addDisposableListener(this.watermark.element, 'click', () => {
|
||||||
if (!this.isActive) {
|
if (!this.isActive) {
|
||||||
this.accessor.doSetGroupActive(this.parent);
|
this.accessor.doSetGroupActive(this.groupPanel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tabsContainer.hide();
|
this.tabsContainer.hide();
|
||||||
this.contentContainer.element.appendChild(this.watermark.element);
|
this.contentContainer.element.appendChild(this.watermark.element);
|
||||||
|
|
||||||
this.watermark.updateParentGroup(this.parent, true);
|
this.watermark.updateParentGroup(this.groupPanel, true);
|
||||||
}
|
}
|
||||||
if (!this.isEmpty && this.watermark) {
|
if (!this.isEmpty && this.watermark) {
|
||||||
this.watermark.element.remove();
|
this.watermark.element.remove();
|
||||||
@ -675,13 +687,18 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canDisplayOverlay(event: DragEvent, target: DockviewDropTargets): boolean {
|
canDisplayOverlay(
|
||||||
|
event: DragEvent,
|
||||||
|
position: Position,
|
||||||
|
target: DockviewDropTargets
|
||||||
|
): boolean {
|
||||||
// custom overlay handler
|
// custom overlay handler
|
||||||
if (this.accessor.options.showDndOverlay) {
|
if (this.accessor.options.showDndOverlay) {
|
||||||
return this.accessor.options.showDndOverlay({
|
return this.accessor.options.showDndOverlay({
|
||||||
nativeEvent: event,
|
nativeEvent: event,
|
||||||
target,
|
target,
|
||||||
group: this.accessor.getPanel(this.id)!,
|
group: this.accessor.getPanel(this.id)!,
|
||||||
|
position,
|
||||||
getData: getPanelData,
|
getData: getPanelData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -8,28 +8,16 @@ import {
|
|||||||
import { toggleClass } from '../dom';
|
import { toggleClass } from '../dom';
|
||||||
import { IDockviewComponent } from '../dockview/dockviewComponent';
|
import { IDockviewComponent } from '../dockview/dockviewComponent';
|
||||||
import { ITabRenderer } from './types';
|
import { ITabRenderer } from './types';
|
||||||
import { IDockviewPanel } from './groupPanel';
|
|
||||||
import { GroupPanel } from './groupviewPanel';
|
import { GroupPanel } from './groupviewPanel';
|
||||||
import { DroptargetEvent, Droptarget } from '../dnd/droptarget';
|
import { DroptargetEvent, Droptarget } from '../dnd/droptarget';
|
||||||
import { DockviewDropTargets } from './dnd';
|
import { DockviewDropTargets } from './dnd';
|
||||||
import { DragHandler } from '../dnd/abstractDragHandler';
|
import { DragHandler } from '../dnd/abstractDragHandler';
|
||||||
|
|
||||||
export enum MouseEventKind {
|
|
||||||
CLICK = 'CLICK',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LayoutMouseEvent {
|
|
||||||
readonly kind: MouseEventKind;
|
|
||||||
readonly event: MouseEvent;
|
|
||||||
readonly panel?: IDockviewPanel;
|
|
||||||
readonly tab?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ITab {
|
export interface ITab {
|
||||||
readonly panelId: string;
|
readonly panelId: string;
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
setContent: (element: ITabRenderer) => void;
|
setContent: (element: ITabRenderer) => void;
|
||||||
onChanged: Event<LayoutMouseEvent>;
|
onChanged: Event<MouseEvent>;
|
||||||
onDrop: Event<DroptargetEvent>;
|
onDrop: Event<DroptargetEvent>;
|
||||||
setActive(isActive: boolean): void;
|
setActive(isActive: boolean): void;
|
||||||
}
|
}
|
||||||
@ -39,13 +27,13 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
private readonly droptarget: Droptarget;
|
private readonly droptarget: Droptarget;
|
||||||
private content?: ITabRenderer;
|
private content?: ITabRenderer;
|
||||||
|
|
||||||
private readonly _onChanged = new Emitter<LayoutMouseEvent>();
|
private readonly _onChanged = new Emitter<MouseEvent>();
|
||||||
readonly onChanged: Event<LayoutMouseEvent> = this._onChanged.event;
|
readonly onChanged: Event<MouseEvent> = this._onChanged.event;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
public get element() {
|
public get element(): HTMLElement {
|
||||||
return this._element;
|
return this._element;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,20 +92,34 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
*/
|
*/
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
this._onChanged.fire({ kind: MouseEventKind.CLICK, event });
|
this._onChanged.fire(event);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.droptarget = new Droptarget(this._element, {
|
this.droptarget = new Droptarget(this._element, {
|
||||||
validOverlays: 'none',
|
acceptedTargetZones: ['center'],
|
||||||
canDisplayOverlay: (event) => {
|
canDisplayOverlay: (event, position) => {
|
||||||
|
if (this.group.locked) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const data = getPanelData();
|
const data = getPanelData();
|
||||||
|
|
||||||
if (data && this.accessor.id === data.viewId) {
|
if (data && this.accessor.id === data.viewId) {
|
||||||
|
if (
|
||||||
|
data.panelId === null &&
|
||||||
|
data.groupId === this.group.id
|
||||||
|
) {
|
||||||
|
// don't allow group move to drop on self
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return this.panelId !== data.panelId;
|
return this.panelId !== data.panelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.group.model.canDisplayOverlay(
|
return this.group.model.canDisplayOverlay(
|
||||||
event,
|
event,
|
||||||
|
position,
|
||||||
DockviewDropTargets.Tab
|
DockviewDropTargets.Tab
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -130,12 +132,12 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActive(isActive: boolean) {
|
public setActive(isActive: boolean): void {
|
||||||
toggleClass(this.element, 'active-tab', isActive);
|
toggleClass(this.element, 'active-tab', isActive);
|
||||||
toggleClass(this.element, 'inactive-tab', !isActive);
|
toggleClass(this.element, 'inactive-tab', !isActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setContent(part: ITabRenderer) {
|
public setContent(part: ITabRenderer): void {
|
||||||
if (this.content) {
|
if (this.content) {
|
||||||
this._element.removeChild(this.content.element);
|
this._element.removeChild(this.content.element);
|
||||||
}
|
}
|
||||||
@ -143,7 +145,7 @@ export class Tab extends CompositeDisposable implements ITab {
|
|||||||
this._element.appendChild(this.content.element);
|
this._element.appendChild(this.content.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose(): void {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
this.droptarget.dispose();
|
this.droptarget.dispose();
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,20 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.dv-single-tab.dv-full-width-single-tab {
|
||||||
|
.tabs-container {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.void-container {
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.void-container {
|
.void-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
@ -4,11 +4,12 @@ import {
|
|||||||
IValueDisposable,
|
IValueDisposable,
|
||||||
} from '../../lifecycle';
|
} from '../../lifecycle';
|
||||||
import { addDisposableListener, Emitter, Event } from '../../events';
|
import { addDisposableListener, Emitter, Event } from '../../events';
|
||||||
import { ITab, MouseEventKind, Tab } from '../tab';
|
import { ITab, Tab } from '../tab';
|
||||||
import { IDockviewPanel } from '../groupPanel';
|
import { IDockviewPanel } from '../groupPanel';
|
||||||
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
import { DockviewComponent } from '../../dockview/dockviewComponent';
|
||||||
import { GroupPanel } from '../groupviewPanel';
|
import { GroupPanel } from '../groupviewPanel';
|
||||||
import { VoidContainer } from './voidContainer';
|
import { VoidContainer } from './voidContainer';
|
||||||
|
import { toggleClass } from '../../dom';
|
||||||
|
|
||||||
export interface TabDropIndexEvent {
|
export interface TabDropIndexEvent {
|
||||||
event: DragEvent;
|
event: DragEvent;
|
||||||
@ -134,8 +135,7 @@ export class TabsContainer
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly accessor: DockviewComponent,
|
private readonly accessor: DockviewComponent,
|
||||||
private readonly group: GroupPanel,
|
private readonly group: GroupPanel
|
||||||
readonly options: { tabHeight?: number }
|
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@ -144,7 +144,34 @@ export class TabsContainer
|
|||||||
this._element = document.createElement('div');
|
this._element = document.createElement('div');
|
||||||
this._element.className = 'tabs-and-actions-container';
|
this._element.className = 'tabs-and-actions-container';
|
||||||
|
|
||||||
this.height = options.tabHeight;
|
this.height = accessor.options.tabHeight;
|
||||||
|
|
||||||
|
toggleClass(
|
||||||
|
this._element,
|
||||||
|
'dv-full-width-single-tab',
|
||||||
|
this.accessor.options.singleTabMode === 'fullwidth'
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addDisposables(
|
||||||
|
this.accessor.onDidAddPanel((e) => {
|
||||||
|
if (e.api.group === this.group) {
|
||||||
|
toggleClass(
|
||||||
|
this._element,
|
||||||
|
'dv-single-tab',
|
||||||
|
this.size === 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
this.accessor.onDidRemovePanel((e) => {
|
||||||
|
if (e.api.group === this.group) {
|
||||||
|
toggleClass(
|
||||||
|
this._element,
|
||||||
|
'dv-single-tab',
|
||||||
|
this.size === 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
this.actionContainer = document.createElement('div');
|
this.actionContainer = document.createElement('div');
|
||||||
this.actionContainer.className = 'action-container';
|
this.actionContainer.className = 'action-container';
|
||||||
@ -242,17 +269,15 @@ export class TabsContainer
|
|||||||
panel.id === this.group.model.activePanel?.id &&
|
panel.id === this.group.model.activePanel?.id &&
|
||||||
this.group.model.isContentFocused;
|
this.group.model.isContentFocused;
|
||||||
|
|
||||||
const isLeftClick = event.event.button === 0;
|
const isLeftClick = event.button === 0;
|
||||||
|
|
||||||
if (!isLeftClick || event.event.defaultPrevented) {
|
if (!isLeftClick || event.defaultPrevented) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.kind === MouseEventKind.CLICK) {
|
|
||||||
this.group.model.openPanel(panel, {
|
this.group.model.openPanel(panel, {
|
||||||
skipFocus: alreadyFocused,
|
skipFocus: alreadyFocused,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
tabToAdd.onDrop((event) => {
|
tabToAdd.onDrop((event) => {
|
||||||
this._onDrop.fire({
|
this._onDrop.fire({
|
||||||
|
@ -41,17 +41,26 @@ export class VoidContainer extends CompositeDisposable {
|
|||||||
const handler = new GroupDragHandler(this._element, accessor.id, group);
|
const handler = new GroupDragHandler(this._element, accessor.id, group);
|
||||||
|
|
||||||
this.voidDropTarget = new Droptarget(this._element, {
|
this.voidDropTarget = new Droptarget(this._element, {
|
||||||
validOverlays: 'none',
|
acceptedTargetZones: ['center'],
|
||||||
canDisplayOverlay: (event) => {
|
canDisplayOverlay: (event, position) => {
|
||||||
const data = getPanelData();
|
const data = getPanelData();
|
||||||
|
|
||||||
if (data && this.accessor.id === data.viewId) {
|
if (data && this.accessor.id === data.viewId) {
|
||||||
|
if (
|
||||||
|
data.panelId === null &&
|
||||||
|
data.groupId === this.group.id
|
||||||
|
) {
|
||||||
|
// don't allow group move to drop on self
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// don't show the overlay if the tab being dragged is the last panel of this group
|
// don't show the overlay if the tab being dragged is the last panel of this group
|
||||||
return last(this.group.panels)?.id !== data.panelId;
|
return last(this.group.panels)?.id !== data.panelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return group.model.canDisplayOverlay(
|
return group.model.canDisplayOverlay(
|
||||||
event,
|
event,
|
||||||
|
position,
|
||||||
DockviewDropTargets.Panel
|
DockviewDropTargets.Panel
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -27,7 +27,7 @@ export * from './react'; // TODO: should be conditional on whether user wants th
|
|||||||
|
|
||||||
export { Event } from './events';
|
export { Event } from './events';
|
||||||
export { IDisposable } from './lifecycle';
|
export { IDisposable } from './lifecycle';
|
||||||
export { Position } from './dnd/droptarget';
|
export { Position as DropTargetDirections } from './dnd/droptarget';
|
||||||
export {
|
export {
|
||||||
FocusEvent,
|
FocusEvent,
|
||||||
PanelDimensionChangeEvent,
|
PanelDimensionChangeEvent,
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
LocalSelectionTransfer,
|
LocalSelectionTransfer,
|
||||||
PaneTransfer,
|
PaneTransfer,
|
||||||
} from '../dnd/dataTransfer';
|
} from '../dnd/dataTransfer';
|
||||||
import { Droptarget, DroptargetEvent, Position } from '../dnd/droptarget';
|
import { Droptarget, DroptargetEvent } from '../dnd/droptarget';
|
||||||
import { Emitter } from '../events';
|
import { Emitter } from '../events';
|
||||||
import { IDisposable } from '../lifecycle';
|
import { IDisposable } from '../lifecycle';
|
||||||
import { Orientation } from '../splitview/core/splitview';
|
import { Orientation } from '../splitview/core/splitview';
|
||||||
@ -70,7 +70,10 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
|||||||
})(this.header);
|
})(this.header);
|
||||||
|
|
||||||
this.target = new Droptarget(this.element, {
|
this.target = new Droptarget(this.element, {
|
||||||
validOverlays: 'vertical',
|
acceptedTargetZones: ['top', 'bottom'],
|
||||||
|
overlayModel: {
|
||||||
|
activationSize: { type: 'percentage', value: 50 },
|
||||||
|
},
|
||||||
canDisplayOverlay: (event) => {
|
canDisplayOverlay: (event) => {
|
||||||
const data = getPaneData();
|
const data = getPaneData();
|
||||||
|
|
||||||
@ -139,16 +142,10 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
|||||||
const fromIndex = allPanels.indexOf(existingPanel);
|
const fromIndex = allPanels.indexOf(existingPanel);
|
||||||
let toIndex = containerApi.panels.indexOf(this);
|
let toIndex = containerApi.panels.indexOf(this);
|
||||||
|
|
||||||
if (
|
if (event.position === 'left' || event.position === 'top') {
|
||||||
event.position === Position.Left ||
|
|
||||||
event.position === Position.Top
|
|
||||||
) {
|
|
||||||
toIndex = Math.max(0, toIndex - 1);
|
toIndex = Math.max(0, toIndex - 1);
|
||||||
}
|
}
|
||||||
if (
|
if (event.position === 'right' || event.position === 'bottom') {
|
||||||
event.position === Position.Right ||
|
|
||||||
event.position === Position.Bottom
|
|
||||||
) {
|
|
||||||
if (fromIndex > toIndex) {
|
if (fromIndex > toIndex) {
|
||||||
toIndex++;
|
toIndex++;
|
||||||
}
|
}
|
||||||
|
@ -164,6 +164,8 @@ export abstract class PaneviewPanel
|
|||||||
) {
|
) {
|
||||||
super(id, component, new PaneviewPanelApiImpl(id));
|
super(id, component, new PaneviewPanelApiImpl(id));
|
||||||
this.api.pane = this; // TODO cannot use 'this' before 'super'
|
this.api.pane = this; // TODO cannot use 'this' before 'super'
|
||||||
|
this.api.initialize(this);
|
||||||
|
|
||||||
this._isExpanded = isExpanded;
|
this._isExpanded = isExpanded;
|
||||||
this._headerVisible = isHeaderVisible;
|
this._headerVisible = isHeaderVisible;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { DockviewComponent } from '../dockview/dockviewComponent';
|
import { DockviewComponent } from '../dockview/dockviewComponent';
|
||||||
import { GroupviewPanelState, IDockviewPanel } from '../groupview/groupPanel';
|
import { GroupviewPanelState, IDockviewPanel } from '../groupview/groupPanel';
|
||||||
import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel';
|
import { DockviewPanel } from '../dockview/dockviewPanel';
|
||||||
import { IPanelDeserializer } from '../dockview/deserializer';
|
import { IPanelDeserializer } from '../dockview/deserializer';
|
||||||
import { createComponent } from '../panel/componentFactory';
|
import { createComponent } from '../panel/componentFactory';
|
||||||
import { DockviewApi } from '../api/component.api';
|
import { DockviewApi } from '../api/component.api';
|
||||||
@ -56,7 +56,7 @@ export class ReactPanelDeserialzier implements IPanelDeserializer {
|
|||||||
tab,
|
tab,
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel = new DockviewGroupPanel(
|
const panel = new DockviewPanel(
|
||||||
panelId,
|
panelId,
|
||||||
this.layout,
|
this.layout,
|
||||||
new DockviewApi(this.layout),
|
new DockviewApi(this.layout),
|
||||||
|
@ -69,6 +69,7 @@ export interface IDockviewReactProps {
|
|||||||
disableAutoResizing?: boolean;
|
disableAutoResizing?: boolean;
|
||||||
defaultTabComponent?: React.FunctionComponent<IDockviewPanelHeaderProps>;
|
defaultTabComponent?: React.FunctionComponent<IDockviewPanelHeaderProps>;
|
||||||
groupControlComponent?: React.FunctionComponent<IDockviewGroupControlProps>;
|
groupControlComponent?: React.FunctionComponent<IDockviewGroupControlProps>;
|
||||||
|
singleTabMode?: 'fullwidth' | 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DockviewReact = React.forwardRef(
|
export const DockviewReact = React.forwardRef(
|
||||||
@ -161,6 +162,7 @@ export const DockviewReact = React.forwardRef(
|
|||||||
props.groupControlComponent,
|
props.groupControlComponent,
|
||||||
{ addPortal }
|
{ addPortal }
|
||||||
),
|
),
|
||||||
|
singleTabMode: props.singleTabMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
domRef.current?.appendChild(dockview.element);
|
domRef.current?.appendChild(dockview.element);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { GridviewApi } from '../../api/component.api';
|
import { GridviewApi } from '../../api/component.api';
|
||||||
|
import { GridviewPanelApiImpl } from '../../api/gridviewPanelApi';
|
||||||
import {
|
import {
|
||||||
GridviewPanel,
|
GridviewPanel,
|
||||||
GridviewInitParameters,
|
GridviewInitParameters,
|
||||||
@ -14,7 +15,7 @@ export class ReactGridPanelView extends GridviewPanel {
|
|||||||
private readonly reactComponent: React.FunctionComponent<IGridviewPanelProps>,
|
private readonly reactComponent: React.FunctionComponent<IGridviewPanelProps>,
|
||||||
private readonly reactPortalStore: ReactPortalStore
|
private readonly reactPortalStore: ReactPortalStore
|
||||||
) {
|
) {
|
||||||
super(id, component);
|
super(id, component, new GridviewPanelApiImpl(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponent(): IFrameworkPart {
|
getComponent(): IFrameworkPart {
|
||||||
|
@ -85,6 +85,8 @@ export abstract class SplitviewPanel
|
|||||||
constructor(id: string, componentName: string) {
|
constructor(id: string, componentName: string) {
|
||||||
super(id, componentName, new SplitviewPanelApiImpl(id));
|
super(id, componentName, new SplitviewPanelApiImpl(id));
|
||||||
|
|
||||||
|
this.api.initialize(this);
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onDidChange,
|
this._onDidChange,
|
||||||
this.api.onVisibilityChange((event) => {
|
this.api.onVisibilityChange((event) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user