mirror of
https://github.com/mathuo/dockview
synced 2025-05-03 18:18:25 +00:00
refactor: simplify dnd control
This commit is contained in:
parent
321f78ec0e
commit
3052857dfc
@ -3,9 +3,7 @@ import {
|
||||
calculateQuadrantAsPixels,
|
||||
directionToPosition,
|
||||
Droptarget,
|
||||
DropTargetDirections,
|
||||
Position,
|
||||
Quadrant,
|
||||
} from '../../dnd/droptarget';
|
||||
import { fireEvent } from '@testing-library/dom';
|
||||
|
||||
@ -36,11 +34,11 @@ describe('droptarget', () => {
|
||||
});
|
||||
|
||||
test('directionToPosition', () => {
|
||||
expect(directionToPosition('above')).toBe(Position.Top);
|
||||
expect(directionToPosition('below')).toBe(Position.Bottom);
|
||||
expect(directionToPosition('left')).toBe(Position.Left);
|
||||
expect(directionToPosition('right')).toBe(Position.Right);
|
||||
expect(directionToPosition('within')).toBe(Position.Center);
|
||||
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'"
|
||||
);
|
||||
@ -65,7 +63,7 @@ describe('droptarget', () => {
|
||||
'.drop-target-dropzone'
|
||||
) as HTMLElement;
|
||||
fireEvent.drop(target);
|
||||
expect(position).toBe(Position.Center);
|
||||
expect(position).toBe('center');
|
||||
});
|
||||
|
||||
test('drop', () => {
|
||||
@ -100,7 +98,7 @@ describe('droptarget', () => {
|
||||
|
||||
expect(position).toBeUndefined();
|
||||
fireEvent.drop(target);
|
||||
expect(position).toBe(Position.Left);
|
||||
expect(position).toBe('left');
|
||||
});
|
||||
|
||||
test('default', () => {
|
||||
@ -135,7 +133,7 @@ describe('droptarget', () => {
|
||||
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
||||
);
|
||||
expect(viewQuery.length).toBe(1);
|
||||
expect(droptarget.state).toBe(Position.Left);
|
||||
expect(droptarget.state).toBe('left');
|
||||
expect(
|
||||
(
|
||||
element
|
||||
@ -153,7 +151,7 @@ describe('droptarget', () => {
|
||||
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
||||
);
|
||||
expect(viewQuery.length).toBe(1);
|
||||
expect(droptarget.state).toBe(Position.Top);
|
||||
expect(droptarget.state).toBe('top');
|
||||
expect(
|
||||
(
|
||||
element
|
||||
@ -171,7 +169,7 @@ describe('droptarget', () => {
|
||||
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
||||
);
|
||||
expect(viewQuery.length).toBe(1);
|
||||
expect(droptarget.state).toBe(Position.Bottom);
|
||||
expect(droptarget.state).toBe('bottom');
|
||||
expect(
|
||||
(
|
||||
element
|
||||
@ -189,7 +187,7 @@ describe('droptarget', () => {
|
||||
'.drop-target > .drop-target-dropzone > .drop-target-selection'
|
||||
);
|
||||
expect(viewQuery.length).toBe(1);
|
||||
expect(droptarget.state).toBe(Position.Right);
|
||||
expect(droptarget.state).toBe('right');
|
||||
expect(
|
||||
(
|
||||
element
|
||||
@ -202,7 +200,7 @@ describe('droptarget', () => {
|
||||
target,
|
||||
createOffsetDragOverEvent({ clientX: 100, clientY: 50 })
|
||||
);
|
||||
expect(droptarget.state).toBe(Position.Center);
|
||||
expect(droptarget.state).toBe('center');
|
||||
expect(
|
||||
(
|
||||
element
|
||||
@ -212,7 +210,7 @@ describe('droptarget', () => {
|
||||
).toBe('');
|
||||
|
||||
fireEvent.dragLeave(target);
|
||||
expect(droptarget.state).toBe(Position.Center);
|
||||
expect(droptarget.state).toBe('center');
|
||||
viewQuery = element.querySelectorAll('.drop-target');
|
||||
expect(viewQuery.length).toBe(0);
|
||||
});
|
||||
@ -220,10 +218,10 @@ describe('droptarget', () => {
|
||||
describe('calculateQuadrantAsPercentage', () => {
|
||||
test('variety of cases', () => {
|
||||
const inputs: Array<{
|
||||
directions: DropTargetDirections[];
|
||||
directions: Position[];
|
||||
x: number;
|
||||
y: number;
|
||||
result: Quadrant | null | undefined;
|
||||
result: Position | null;
|
||||
}> = [
|
||||
{ directions: ['left', 'right'], x: 19, y: 50, result: 'left' },
|
||||
{
|
||||
@ -248,13 +246,13 @@ describe('droptarget', () => {
|
||||
directions: ['left', 'right', 'top', 'bottom', 'center'],
|
||||
x: 50,
|
||||
y: 50,
|
||||
result: null,
|
||||
result: 'center',
|
||||
},
|
||||
{
|
||||
directions: ['left', 'right', 'top', 'bottom'],
|
||||
x: 50,
|
||||
y: 50,
|
||||
result: undefined,
|
||||
result: null,
|
||||
},
|
||||
];
|
||||
|
||||
@ -276,10 +274,10 @@ describe('droptarget', () => {
|
||||
describe('calculateQuadrantAsPixels', () => {
|
||||
test('variety of cases', () => {
|
||||
const inputs: Array<{
|
||||
directions: DropTargetDirections[];
|
||||
directions: Position[];
|
||||
x: number;
|
||||
y: number;
|
||||
result: Quadrant | null | undefined;
|
||||
result: Position | null;
|
||||
}> = [
|
||||
{ directions: ['left', 'right'], x: 19, y: 50, result: 'left' },
|
||||
{
|
||||
@ -304,13 +302,13 @@ describe('droptarget', () => {
|
||||
directions: ['left', 'right', 'top', 'bottom', 'center'],
|
||||
x: 50,
|
||||
y: 50,
|
||||
result: null,
|
||||
result: 'center',
|
||||
},
|
||||
{
|
||||
directions: ['left', 'right', 'top', 'bottom'],
|
||||
x: 50,
|
||||
y: 50,
|
||||
result: undefined,
|
||||
result: null,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -253,9 +253,9 @@ describe('dockviewComponent', () => {
|
||||
const panel4 = dockview.getGroupPanel('panel4');
|
||||
|
||||
const group1 = panel1!.group;
|
||||
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', Position.Right);
|
||||
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', 'right');
|
||||
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!.model.activePanel).toBe(panel3);
|
||||
@ -305,9 +305,9 @@ describe('dockviewComponent', () => {
|
||||
const panel1 = dockview.getGroupPanel('panel1')!;
|
||||
const panel2 = dockview.getGroupPanel('panel2')!;
|
||||
const group1 = panel1.group;
|
||||
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', Position.Right);
|
||||
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', 'right');
|
||||
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.totalPanels).toBe(4);
|
||||
@ -370,9 +370,9 @@ describe('dockviewComponent', () => {
|
||||
expect(panel4.api.isActive).toBeFalsy();
|
||||
|
||||
const group1 = panel1.group;
|
||||
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', Position.Right);
|
||||
dockview.moveGroupOrPanel(group1, group1.id, 'panel1', 'right');
|
||||
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(panel1.group).toBe(panel3.group);
|
||||
@ -439,7 +439,7 @@ describe('dockviewComponent', () => {
|
||||
expect(group.model.indexOf(panel1)).toBe(0);
|
||||
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.totalPanels).toBe(2);
|
||||
@ -489,7 +489,7 @@ describe('dockviewComponent', () => {
|
||||
expect(viewQuery.length).toBe(1);
|
||||
|
||||
const group = dockview.getGroupPanel('panel1')!.group;
|
||||
dockview.moveGroupOrPanel(group, group.id, 'panel1', Position.Right);
|
||||
dockview.moveGroupOrPanel(group, group.id, 'panel1', 'right');
|
||||
|
||||
viewQuery = container.querySelectorAll(
|
||||
'.branch-node > .split-view-container > .view-container > .view'
|
||||
@ -974,7 +974,7 @@ describe('dockviewComponent', () => {
|
||||
panel2.group!,
|
||||
panel5.group!.id,
|
||||
panel5.id,
|
||||
Position.Center
|
||||
'center'
|
||||
);
|
||||
expect(events).toEqual([
|
||||
{ type: 'REMOVE_PANEL', panel: panel5 },
|
||||
@ -993,7 +993,7 @@ describe('dockviewComponent', () => {
|
||||
panel2.group!,
|
||||
panel4.group!.id,
|
||||
panel4.id,
|
||||
Position.Center
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(events).toEqual([
|
||||
@ -1313,7 +1313,7 @@ describe('dockviewComponent', () => {
|
||||
panel1.group,
|
||||
panel2.group.id,
|
||||
'panel2',
|
||||
Position.Left
|
||||
'left'
|
||||
);
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
@ -1354,7 +1354,7 @@ describe('dockviewComponent', () => {
|
||||
panel1.group,
|
||||
panel2.group.id,
|
||||
'panel2',
|
||||
Position.Center
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(panel1Spy).not.toHaveBeenCalled();
|
||||
@ -1393,7 +1393,7 @@ describe('dockviewComponent', () => {
|
||||
panel1.group,
|
||||
panel1.group.id,
|
||||
'panel1',
|
||||
Position.Center,
|
||||
'center',
|
||||
0
|
||||
);
|
||||
|
||||
@ -1554,7 +1554,7 @@ describe('dockviewComponent', () => {
|
||||
panel3.group,
|
||||
panel1.group.id,
|
||||
undefined,
|
||||
Position.Center
|
||||
'center'
|
||||
);
|
||||
|
||||
expect(dockview.groups.length).toBe(1);
|
||||
|
@ -730,7 +730,7 @@ describe('groupview', () => {
|
||||
).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>, []>(() => {
|
||||
return {
|
||||
id: 'testcomponentid',
|
||||
@ -792,7 +792,7 @@ describe('groupview', () => {
|
||||
|
||||
expect(
|
||||
element.getElementsByClassName('drop-target-dropzone').length
|
||||
).toBe(1);
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('that should not allow drop when not dropping for different component id', () => {
|
||||
|
@ -9,44 +9,29 @@ function numberOrFallback(maybeNumber: any, fallback: number): number {
|
||||
return typeof maybeNumber === 'number' ? maybeNumber : fallback;
|
||||
}
|
||||
|
||||
export enum Position {
|
||||
Top = 'Top',
|
||||
Left = 'Left',
|
||||
Bottom = 'Bottom',
|
||||
Right = 'Right',
|
||||
Center = 'Center',
|
||||
}
|
||||
|
||||
export function directionToPosition(direction: Direction): Position {
|
||||
switch (direction) {
|
||||
case 'above':
|
||||
return Position.Top;
|
||||
return 'top';
|
||||
case 'below':
|
||||
return Position.Bottom;
|
||||
return 'bottom';
|
||||
case 'left':
|
||||
return Position.Left;
|
||||
return 'left';
|
||||
case 'right':
|
||||
return Position.Right;
|
||||
return 'right';
|
||||
case 'within':
|
||||
return Position.Center;
|
||||
return 'center';
|
||||
default:
|
||||
throw new Error(`invalid direction '${direction}'`);
|
||||
}
|
||||
}
|
||||
|
||||
export type Quadrant = 'top' | 'bottom' | 'left' | 'right';
|
||||
|
||||
export interface DroptargetEvent {
|
||||
readonly position: Position;
|
||||
readonly nativeEvent: DragEvent;
|
||||
}
|
||||
|
||||
export type DropTargetDirections =
|
||||
| 'top'
|
||||
| 'bottom'
|
||||
| 'left'
|
||||
| 'right'
|
||||
| 'center';
|
||||
export type Position = 'top' | 'bottom' | 'left' | 'right' | 'center';
|
||||
|
||||
function isBooleanValue(
|
||||
canDisplayOverlay: CanDisplayOverlay
|
||||
@ -56,7 +41,7 @@ function isBooleanValue(
|
||||
|
||||
export type CanDisplayOverlay =
|
||||
| boolean
|
||||
| ((dragEvent: DragEvent, state: Quadrant | null) => boolean);
|
||||
| ((dragEvent: DragEvent, state: Position) => boolean);
|
||||
|
||||
export class Droptarget extends CompositeDisposable {
|
||||
private target: HTMLElement | undefined;
|
||||
@ -74,7 +59,7 @@ export class Droptarget extends CompositeDisposable {
|
||||
private readonly element: HTMLElement,
|
||||
private readonly options: {
|
||||
canDisplayOverlay: CanDisplayOverlay;
|
||||
acceptedTargetZones: DropTargetDirections[];
|
||||
acceptedTargetZones: Position[];
|
||||
overlayModel?: {
|
||||
size?: { value: number; type: 'pixels' | 'percentage' };
|
||||
activationSize?: {
|
||||
@ -117,7 +102,7 @@ export class Droptarget extends CompositeDisposable {
|
||||
height
|
||||
);
|
||||
|
||||
if (quadrant === undefined) {
|
||||
if (quadrant === null) {
|
||||
this.removeDropTarget();
|
||||
return;
|
||||
}
|
||||
@ -135,7 +120,7 @@ export class Droptarget extends CompositeDisposable {
|
||||
this.target.className = 'drop-target-dropzone';
|
||||
this.overlay = document.createElement('div');
|
||||
this.overlay.className = 'drop-target-selection';
|
||||
this._state = Position.Center;
|
||||
this._state = 'center';
|
||||
this.target.appendChild(this.overlay);
|
||||
|
||||
this.element.classList.add('drop-target');
|
||||
@ -181,7 +166,7 @@ export class Droptarget extends CompositeDisposable {
|
||||
}
|
||||
|
||||
private toggleClasses(
|
||||
quadrant: Quadrant | null,
|
||||
quadrant: Position,
|
||||
width: number,
|
||||
height: number
|
||||
): void {
|
||||
@ -246,33 +231,33 @@ export class Droptarget extends CompositeDisposable {
|
||||
toggleClass(this.overlay, 'small-bottom', isSmallY && isBottom);
|
||||
}
|
||||
|
||||
private setState(quadrant: Quadrant | null): void {
|
||||
private setState(quadrant: Position): void {
|
||||
switch (quadrant) {
|
||||
case 'top':
|
||||
this._state = Position.Top;
|
||||
this._state = 'top';
|
||||
break;
|
||||
case 'left':
|
||||
this._state = Position.Left;
|
||||
this._state = 'left';
|
||||
break;
|
||||
case 'bottom':
|
||||
this._state = Position.Bottom;
|
||||
this._state = 'bottom';
|
||||
break;
|
||||
case 'right':
|
||||
this._state = Position.Right;
|
||||
this._state = 'right';
|
||||
break;
|
||||
default:
|
||||
this._state = Position.Center;
|
||||
case 'center':
|
||||
this._state = 'center';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private calculateQuadrant(
|
||||
overlayType: Set<DropTargetDirections>,
|
||||
overlayType: Set<Position>,
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number
|
||||
): Quadrant | null | undefined {
|
||||
): Position | null {
|
||||
const isPercentage =
|
||||
this.options.overlayModel?.activationSize === undefined ||
|
||||
this.options.overlayModel?.activationSize?.type === 'percentage';
|
||||
@ -315,13 +300,13 @@ export class Droptarget extends CompositeDisposable {
|
||||
}
|
||||
|
||||
export function calculateQuadrantAsPercentage(
|
||||
overlayType: Set<DropTargetDirections>,
|
||||
overlayType: Set<Position>,
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
threshold: number
|
||||
): Quadrant | null | undefined {
|
||||
): Position | null {
|
||||
const xp = (100 * x) / width;
|
||||
const yp = (100 * y) / height;
|
||||
|
||||
@ -339,20 +324,20 @@ export function calculateQuadrantAsPercentage(
|
||||
}
|
||||
|
||||
if (!overlayType.has('center')) {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return 'center';
|
||||
}
|
||||
|
||||
export function calculateQuadrantAsPixels(
|
||||
overlayType: Set<DropTargetDirections>,
|
||||
overlayType: Set<Position>,
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
threshold: number
|
||||
): Quadrant | null | undefined {
|
||||
): Position | null {
|
||||
if (overlayType.has('left') && x < threshold) {
|
||||
return 'left';
|
||||
}
|
||||
@ -367,8 +352,8 @@ export function calculateQuadrantAsPixels(
|
||||
}
|
||||
|
||||
if (!overlayType.has('center')) {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return 'center';
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import {
|
||||
import { GroupPanel, IGroupviewPanel } from '../groupview/groupviewPanel';
|
||||
import { DefaultGroupPanelView } from './defaultGroupPanelView';
|
||||
import { getPanelData } from '../dnd/dataTransfer';
|
||||
import { DockviewDropTargets } from '../groupview/dnd';
|
||||
|
||||
export interface PanelReference {
|
||||
update: (event: { params: { [key: string]: any } }) => void;
|
||||
@ -235,8 +236,26 @@ export class DockviewComponent
|
||||
}
|
||||
|
||||
const dropTarget = new Droptarget(this.element, {
|
||||
canDisplayOverlay: () => {
|
||||
return true;
|
||||
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: {
|
||||
@ -250,16 +269,14 @@ export class DockviewComponent
|
||||
dropTarget.onDrop((event) => {
|
||||
const data = getPanelData();
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
if (data) {
|
||||
this.moveGroupOrPanel(
|
||||
this.orthogonalize(event.position),
|
||||
data.groupId,
|
||||
data.panelId || undefined,
|
||||
'center'
|
||||
);
|
||||
}
|
||||
|
||||
this.moveGroupOrPanel(
|
||||
this.orthogonalize(event.position),
|
||||
data.groupId,
|
||||
data.panelId || undefined,
|
||||
Position.Center
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
@ -268,16 +285,16 @@ export class DockviewComponent
|
||||
|
||||
private orthogonalize(position: Position): GroupPanel {
|
||||
switch (position) {
|
||||
case Position.Top:
|
||||
case Position.Bottom:
|
||||
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 Position.Left:
|
||||
case Position.Right:
|
||||
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
|
||||
@ -289,11 +306,11 @@ export class DockviewComponent
|
||||
}
|
||||
|
||||
switch (position) {
|
||||
case Position.Top:
|
||||
case Position.Left:
|
||||
case 'top':
|
||||
case 'left':
|
||||
return this.createGroupAtLocation([0]); // insert into first position
|
||||
case Position.Bottom:
|
||||
case Position.Right:
|
||||
case 'bottom':
|
||||
case 'right':
|
||||
return this.createGroupAtLocation([this.gridview.length]); // insert into last position
|
||||
default:
|
||||
throw new Error(`unsupported position ${position}`);
|
||||
@ -543,7 +560,7 @@ export class DockviewComponent
|
||||
const target = toTarget(
|
||||
<Direction>options.position?.direction || 'within'
|
||||
);
|
||||
if (target === Position.Center) {
|
||||
if (target === 'center') {
|
||||
panel = this.createPanel(options, referenceGroup);
|
||||
referenceGroup.model.openPanel(panel);
|
||||
} else {
|
||||
@ -703,7 +720,7 @@ export class DockviewComponent
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target || target === Position.Center) {
|
||||
if (!target || target === 'center') {
|
||||
const groupItem: IDockviewPanel | undefined =
|
||||
sourceGroup?.model.removePanel(itemId) ||
|
||||
this.panels.find((panel) => panel.id === itemId);
|
||||
@ -782,7 +799,7 @@ export class DockviewComponent
|
||||
target: Position
|
||||
): void {
|
||||
if (sourceGroup) {
|
||||
if (!target || target === Position.Center) {
|
||||
if (!target || target === 'center') {
|
||||
const activePanel = sourceGroup.activePanel;
|
||||
const panels = [...sourceGroup.panels].map((p) =>
|
||||
sourceGroup.model.removePanel(p.id)
|
||||
|
@ -14,6 +14,7 @@ import { FrameworkFactory } from '../types';
|
||||
import { DockviewDropTargets } from '../groupview/dnd';
|
||||
import { PanelTransfer } from '../dnd/dataTransfer';
|
||||
import { IGroupControlRenderer } from '../react/dockview/groupControlsRenderer';
|
||||
import { Position } from '../dnd/droptarget';
|
||||
|
||||
export interface GroupPanelFrameworkComponentFactory {
|
||||
content: FrameworkFactory<IContentRenderer>;
|
||||
@ -54,7 +55,8 @@ export interface ViewFactoryData {
|
||||
export interface DockviewDndOverlayEvent {
|
||||
nativeEvent: DragEvent;
|
||||
target: DockviewDropTargets;
|
||||
group: GroupPanel;
|
||||
position: Position;
|
||||
group?: GroupPanel;
|
||||
getData: () => PanelTransfer | undefined;
|
||||
}
|
||||
|
||||
|
@ -15,19 +15,19 @@ const nextLayoutId = sequentialNumberGenerator();
|
||||
|
||||
export type Direction = 'left' | 'right' | 'above' | 'below' | 'within';
|
||||
|
||||
export function toTarget(direction: Direction) {
|
||||
export function toTarget(direction: Direction): Position {
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
return Position.Left;
|
||||
return 'left';
|
||||
case 'right':
|
||||
return Position.Right;
|
||||
return 'right';
|
||||
case 'above':
|
||||
return Position.Top;
|
||||
return 'top';
|
||||
case 'below':
|
||||
return Position.Bottom;
|
||||
return 'bottom';
|
||||
case 'within':
|
||||
default:
|
||||
return Position.Center;
|
||||
return 'center';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,13 +9,13 @@ import {
|
||||
Orientation,
|
||||
Sizing,
|
||||
} from '../splitview/core/splitview';
|
||||
import { Position } from '../dnd/droptarget';
|
||||
import { tail } from '../array';
|
||||
import { LeafNode } from './leafNode';
|
||||
import { BranchNode } from './branchNode';
|
||||
import { Node } from './types';
|
||||
import { Emitter, Event } from '../events';
|
||||
import { IDisposable, MutableDisposable } from '../lifecycle';
|
||||
import { Position } from '../dnd/droptarget';
|
||||
|
||||
function findLeaf(candiateNode: Node, last: boolean): LeafNode {
|
||||
if (candiateNode instanceof LeafNode) {
|
||||
@ -132,22 +132,19 @@ export function getRelativeLocation(
|
||||
const [rest, _index] = tail(location);
|
||||
let index = _index;
|
||||
|
||||
if (direction === Position.Right || direction === Position.Bottom) {
|
||||
if (direction === 'right' || direction === 'bottom') {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return [...rest, index];
|
||||
} else {
|
||||
const index =
|
||||
direction === Position.Right || direction === Position.Bottom
|
||||
? 1
|
||||
: 0;
|
||||
const index = direction === 'right' || direction === 'bottom' ? 1 : 0;
|
||||
return [...location, index];
|
||||
}
|
||||
}
|
||||
|
||||
export function getDirectionOrientation(direction: Position): Orientation {
|
||||
return direction === Position.Top || direction === Position.Bottom
|
||||
return direction === 'top' || direction === 'bottom'
|
||||
? Orientation.VERTICAL
|
||||
: Orientation.HORIZONTAL;
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import {
|
||||
SerializedGridObject,
|
||||
getGridLocation,
|
||||
} from './gridview';
|
||||
import { Position } from '../dnd/droptarget';
|
||||
import { tail, sequenceEquals } from '../array';
|
||||
import { CompositeDisposable } from '../lifecycle';
|
||||
import { IPanelDeserializer } from '../dockview/deserializer';
|
||||
@ -25,6 +24,7 @@ import { BaseComponentOptions } from '../panel/types';
|
||||
import { Orientation, Sizing } from '../splitview/core/splitview';
|
||||
import { createComponent } from '../panel/componentFactory';
|
||||
import { Emitter, Event } from '../events';
|
||||
import { Position } from '../dnd/droptarget';
|
||||
|
||||
export interface SerializedGridview {
|
||||
grid: {
|
||||
@ -265,7 +265,7 @@ export class GridviewComponent
|
||||
}
|
||||
|
||||
const target = toTarget(options.direction);
|
||||
if (target === Position.Center) {
|
||||
if (target === 'center') {
|
||||
throw new Error(`${target} not supported as an option`);
|
||||
} else {
|
||||
const location = getGridLocation(referenceGroup.element);
|
||||
@ -294,7 +294,7 @@ export class GridviewComponent
|
||||
}
|
||||
|
||||
const target = toTarget(options.position.direction);
|
||||
if (target === Position.Center) {
|
||||
if (target === 'center') {
|
||||
throw new Error(`${target} not supported as an option`);
|
||||
} else {
|
||||
const location = getGridLocation(referenceGroup.element);
|
||||
|
@ -2,4 +2,5 @@ export enum DockviewDropTargets {
|
||||
Tab,
|
||||
Panel,
|
||||
TabContainer,
|
||||
Edge,
|
||||
}
|
||||
|
@ -110,7 +110,11 @@ export interface IGroupview extends IDisposable, IGridPanelView {
|
||||
panel?: IDockviewPanel;
|
||||
suppressRoll?: boolean;
|
||||
}): void;
|
||||
canDisplayOverlay(event: DragEvent, target: DockviewDropTargets): boolean;
|
||||
canDisplayOverlay(
|
||||
event: DragEvent,
|
||||
position: Position,
|
||||
target: DockviewDropTargets
|
||||
): boolean;
|
||||
}
|
||||
|
||||
export class Groupview extends CompositeDisposable implements IGroupview {
|
||||
@ -240,17 +244,23 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
||||
|
||||
this.dropTarget = new Droptarget(this.contentContainer.element, {
|
||||
acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
|
||||
canDisplayOverlay: (event, quadrant) => {
|
||||
if (this.locked && !quadrant) {
|
||||
canDisplayOverlay: (event, position) => {
|
||||
if (this.locked && position === 'center') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const data = getPanelData();
|
||||
|
||||
if (data && data.viewId === this.accessor.id) {
|
||||
if (data.panelId === null && data.groupId === this.id) {
|
||||
// don't allow group move to drop on self
|
||||
return false;
|
||||
if (data.groupId === this.id) {
|
||||
if (position === 'center') {
|
||||
// don't allow to drop on self for center position
|
||||
return false;
|
||||
}
|
||||
if (data.panelId === null) {
|
||||
// don't allow group move to drop anywhere on self
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const groupHasOnePanelAndIsActiveDragElement =
|
||||
@ -259,7 +269,11 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
||||
return !groupHasOnePanelAndIsActiveDragElement;
|
||||
}
|
||||
|
||||
return this.canDisplayOverlay(event, DockviewDropTargets.Panel);
|
||||
return this.canDisplayOverlay(
|
||||
event,
|
||||
position,
|
||||
DockviewDropTargets.Panel
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@ -279,7 +293,7 @@ export class Groupview extends CompositeDisposable implements IGroupview {
|
||||
this._onDidRemovePanel,
|
||||
this._onDidActivePanelChange,
|
||||
this.tabsContainer.onDrop((event) => {
|
||||
this.handleDropEvent(event.event, Position.Center, event.index);
|
||||
this.handleDropEvent(event.event, 'center', event.index);
|
||||
}),
|
||||
this.contentContainer.onDidFocus(() => {
|
||||
this.accessor.doSetGroupActive(this.groupPanel, true);
|
||||
@ -673,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
|
||||
if (this.accessor.options.showDndOverlay) {
|
||||
return this.accessor.options.showDndOverlay({
|
||||
nativeEvent: event,
|
||||
target,
|
||||
group: this.accessor.getPanel(this.id)!,
|
||||
position,
|
||||
getData: getPanelData,
|
||||
});
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ export class Tab extends CompositeDisposable implements ITab {
|
||||
|
||||
this.droptarget = new Droptarget(this._element, {
|
||||
acceptedTargetZones: ['center'],
|
||||
canDisplayOverlay: (event) => {
|
||||
canDisplayOverlay: (event, position) => {
|
||||
if (this.group.locked) {
|
||||
return false;
|
||||
}
|
||||
@ -119,6 +119,7 @@ export class Tab extends CompositeDisposable implements ITab {
|
||||
|
||||
return this.group.model.canDisplayOverlay(
|
||||
event,
|
||||
position,
|
||||
DockviewDropTargets.Tab
|
||||
);
|
||||
},
|
||||
|
@ -42,7 +42,7 @@ export class VoidContainer extends CompositeDisposable {
|
||||
|
||||
this.voidDropTarget = new Droptarget(this._element, {
|
||||
acceptedTargetZones: ['center'],
|
||||
canDisplayOverlay: (event) => {
|
||||
canDisplayOverlay: (event, position) => {
|
||||
const data = getPanelData();
|
||||
|
||||
if (data && this.accessor.id === data.viewId) {
|
||||
@ -60,6 +60,7 @@ export class VoidContainer extends CompositeDisposable {
|
||||
|
||||
return group.model.canDisplayOverlay(
|
||||
event,
|
||||
position,
|
||||
DockviewDropTargets.Panel
|
||||
);
|
||||
},
|
||||
|
@ -27,7 +27,7 @@ export * from './react'; // TODO: should be conditional on whether user wants th
|
||||
|
||||
export { Event } from './events';
|
||||
export { IDisposable } from './lifecycle';
|
||||
export { Position } from './dnd/droptarget';
|
||||
export { Position as DropTargetDirections } from './dnd/droptarget';
|
||||
export {
|
||||
FocusEvent,
|
||||
PanelDimensionChangeEvent,
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
LocalSelectionTransfer,
|
||||
PaneTransfer,
|
||||
} from '../dnd/dataTransfer';
|
||||
import { Droptarget, DroptargetEvent, Position } from '../dnd/droptarget';
|
||||
import { Droptarget, DroptargetEvent } from '../dnd/droptarget';
|
||||
import { Emitter } from '../events';
|
||||
import { IDisposable } from '../lifecycle';
|
||||
import { Orientation } from '../splitview/core/splitview';
|
||||
@ -142,16 +142,10 @@ export abstract class DraggablePaneviewPanel extends PaneviewPanel {
|
||||
const fromIndex = allPanels.indexOf(existingPanel);
|
||||
let toIndex = containerApi.panels.indexOf(this);
|
||||
|
||||
if (
|
||||
event.position === Position.Left ||
|
||||
event.position === Position.Top
|
||||
) {
|
||||
if (event.position === 'left' || event.position === 'top') {
|
||||
toIndex = Math.max(0, toIndex - 1);
|
||||
}
|
||||
if (
|
||||
event.position === Position.Right ||
|
||||
event.position === Position.Bottom
|
||||
) {
|
||||
if (event.position === 'right' || event.position === 'bottom') {
|
||||
if (fromIndex > toIndex) {
|
||||
toIndex++;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user