Skip to content

Commit

Permalink
feat: gready render mode
Browse files Browse the repository at this point in the history
  • Loading branch information
mathuo committed Nov 23, 2023
1 parent b17aa24 commit 82bf53b
Show file tree
Hide file tree
Showing 9 changed files with 383 additions and 157 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CompositeDisposable } from '../../../../lifecycle';
import { PanelUpdateEvent } from '../../../../panel/types';
import { IDockviewPanel } from '../../../../dockview/dockviewPanel';
import { IDockviewPanelModel } from '../../../../dockview/dockviewPanelModel';
import { DockviewComponent } from '../../../../dockview/dockviewComponent';

class TestContentRenderer
extends CompositeDisposable
Expand Down Expand Up @@ -56,7 +57,14 @@ describe('contentContainer', () => {
let blur = 0;

const disposable = new CompositeDisposable();
const cut = new ContentContainer();

const dockviewComponent = jest.fn<DockviewComponent, []>(() => {
return {
renderMode: 'destructive',
} as DockviewComponent;
});

const cut = new ContentContainer(dockviewComponent(), jest.fn() as any);

disposable.addDisposables(
cut.onDidFocus(() => {
Expand Down
50 changes: 35 additions & 15 deletions packages/dockview-core/src/dnd/dnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,43 @@ export class DragAndDropObserver extends CompositeDisposable {
this.registerListeners();
}

onDragEnter(e: DragEvent): void {
this.target = e.target;
this.callbacks.onDragEnter(e);
}

onDragOver(e: DragEvent): void {
e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)

if (this.callbacks.onDragOver) {
this.callbacks.onDragOver(e);
}
}

onDragLeave(e: DragEvent): void {
if (this.target === e.target) {
this.target = null;

this.callbacks.onDragLeave(e);
}
}

onDragEnd(e: DragEvent): void {
this.target = null;
this.callbacks.onDragEnd(e);
}

onDrop(e: DragEvent): void {
this.callbacks.onDrop(e);
}

private registerListeners(): void {
this.addDisposables(
addDisposableListener(
this.element,
'dragenter',
(e: DragEvent) => {
this.target = e.target;
this.callbacks.onDragEnter(e);
this.onDragEnter(e);
},
true
)
Expand All @@ -39,36 +68,27 @@ export class DragAndDropObserver extends CompositeDisposable {
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)

if (this.callbacks.onDragOver) {
this.callbacks.onDragOver(e);
}
this.onDragOver(e);
},
true
)
);

this.addDisposables(
addDisposableListener(this.element, 'dragleave', (e: DragEvent) => {
if (this.target === e.target) {
this.target = null;

this.callbacks.onDragLeave(e);
}
this.onDragLeave(e);
})
);

this.addDisposables(
addDisposableListener(this.element, 'dragend', (e: DragEvent) => {
this.target = null;
this.callbacks.onDragEnd(e);
this.onDragEnd(e);
})
);

this.addDisposables(
addDisposableListener(this.element, 'drop', (e: DragEvent) => {
this.callbacks.onDrop(e);
this.onDrop(e);
})
);
}
Expand Down
175 changes: 88 additions & 87 deletions packages/dockview-core/src/dnd/droptarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export class Droptarget extends CompositeDisposable {
private readonly _onDrop = new Emitter<DroptargetEvent>();
readonly onDrop: Event<DroptargetEvent> = this._onDrop.event;

readonly dnd: DragAndDropObserver;

private static USED_EVENT_ID = '__dockview_droptarget_event_is_used__';

get state(): Position | undefined {
Expand Down Expand Up @@ -90,98 +92,97 @@ export class Droptarget extends CompositeDisposable {
this.options.acceptedTargetZones
);

this.addDisposables(
this._onDrop,
new DragAndDropObserver(this.element, {
onDragEnter: () => undefined,
onDragOver: (e) => {
if (this._acceptedTargetZonesSet.size === 0) {
this.removeDropTarget();
return;
}

const width = this.element.clientWidth;
const height = this.element.clientHeight;

if (width === 0 || height === 0) {
return; // avoid div!0
}

const rect = (
e.currentTarget as HTMLElement
).getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;

const quadrant = this.calculateQuadrant(
this._acceptedTargetZonesSet,
x,
y,
width,
height
);

/**
* If the event has already been used by another DropTarget instance
* then don't show a second drop target, only one target should be
* active at any one time
*/
if (this.isAlreadyUsed(e) || quadrant === null) {
// no drop target should be displayed
this.removeDropTarget();
return;
}
this.dnd = new DragAndDropObserver(this.element, {
onDragEnter: () => undefined,
onDragOver: (e) => {
if (this._acceptedTargetZonesSet.size === 0) {
this.removeDropTarget();
return;
}

const width = this.element.clientWidth;
const height = this.element.clientHeight;

if (width === 0 || height === 0) {
return; // avoid div!0
}

const rect = (
e.currentTarget as HTMLElement
).getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;

const quadrant = this.calculateQuadrant(
this._acceptedTargetZonesSet,
x,
y,
width,
height
);

/**
* If the event has already been used by another DropTarget instance
* then don't show a second drop target, only one target should be
* active at any one time
*/
if (this.isAlreadyUsed(e) || quadrant === null) {
// no drop target should be displayed
this.removeDropTarget();
return;
}

if (typeof this.options.canDisplayOverlay === 'boolean') {
if (!this.options.canDisplayOverlay) {
this.removeDropTarget();
return;
}
} else if (!this.options.canDisplayOverlay(e, quadrant)) {
if (typeof this.options.canDisplayOverlay === 'boolean') {
if (!this.options.canDisplayOverlay) {
this.removeDropTarget();
return;
}

this.markAsUsed(e);

if (!this.targetElement) {
this.targetElement = document.createElement('div');
this.targetElement.className = 'drop-target-dropzone';
this.overlayElement = document.createElement('div');
this.overlayElement.className = 'drop-target-selection';
this._state = 'center';
this.targetElement.appendChild(this.overlayElement);

this.element.classList.add('drop-target');
this.element.append(this.targetElement);
}

this.toggleClasses(quadrant, width, height);

this.setState(quadrant);
},
onDragLeave: () => {
this.removeDropTarget();
},
onDragEnd: () => {
} else if (!this.options.canDisplayOverlay(e, quadrant)) {
this.removeDropTarget();
},
onDrop: (e) => {
e.preventDefault();

const state = this._state;

this.removeDropTarget();

if (state) {
// only stop the propagation of the event if we are dealing with it
// which is only when the target has state
e.stopPropagation();
this._onDrop.fire({ position: state, nativeEvent: e });
}
},
})
);
return;
}

this.markAsUsed(e);

if (!this.targetElement) {
this.targetElement = document.createElement('div');
this.targetElement.className = 'drop-target-dropzone';
this.overlayElement = document.createElement('div');
this.overlayElement.className = 'drop-target-selection';
this._state = 'center';
this.targetElement.appendChild(this.overlayElement);

this.element.classList.add('drop-target');
this.element.append(this.targetElement);
}

this.toggleClasses(quadrant, width, height);

this.setState(quadrant);
},
onDragLeave: () => {
this.removeDropTarget();
},
onDragEnd: () => {
this.removeDropTarget();
},
onDrop: (e) => {
e.preventDefault();

const state = this._state;

this.removeDropTarget();

if (state) {
// only stop the propagation of the event if we are dealing with it
// which is only when the target has state
e.stopPropagation();
this._onDrop.fire({ position: state, nativeEvent: e });
}
},
});

this.addDisposables(this._onDrop, this.dnd);
}

setTargetZones(acceptedTargetZones: Position[]): void {
Expand Down
Loading

0 comments on commit 82bf53b

Please sign in to comment.