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 24, 2023
1 parent b17aa24 commit 4e3f785
Show file tree
Hide file tree
Showing 10 changed files with 402 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.dv-render-overlay {
position: absolute;
z-index: 1;
outline: 1px solid red;
outline-offset: -1;

&.dv-render-overlay-float {
z-index: 999;
}
}
Loading

0 comments on commit 4e3f785

Please sign in to comment.