Skip to content

Commit

Permalink
fix: dragger implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
BeksOmega committed Mar 28, 2024
1 parent a89aa6a commit 661d6f6
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 30 deletions.
125 changes: 96 additions & 29 deletions core/dragging/dragger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,65 +4,133 @@
* SPDX-License-Identifier: Apache-2.0
*/

import {IDragTarget} from '../interfaces/i_drag_target.js';
import {IDeletable, isDeletable} from '../interfaces/i_deletable.js';
import {IDragger} from '../interfaces/i_dragger.js';
import {IDraggable} from '../interfaces/i_draggable.js';
import {Coordinate} from '../utils/coordinate.js';
import {WorkspaceSvg} from '../workspace_svg.js';
import {ComponentManager} from '../component_manager.js';
import {IDeleteArea} from '../interfaces/i_delete_area.js';

export class Dragger implements IDragger {
/** Starting location of the draggable, in workspace coordinates. */
private startLoc: Coordinate;

private dragTarget: IDragTarget | null = null;

constructor(
private draggable: IDraggable,
private workspace: WorkspaceSvg,
) {
this.startLoc = draggable.getLocation();
this.startLoc = draggable.getRelativeToSurfaceXY();
}

/**
* Handles any drag startup.
*
* @param e PointerEvent that started the drag.
*/
/** Handles any drag startup. */
onDragStart(e: PointerEvent) {
this.draggable.startDrag(e);
}

/**
* Handles dragging, including calculating where the element should
* actually be moved to.
* Handles calculating where the element should actually be moved to.
*
* @param e PointerEvent that continued the drag.
* @param totalDelta The total distance, in pixels, that the mouse
* has moved since the start of the drag.
* @param totalDelta The total amount in pixel coordinates the mouse has moved
* since the start of the drag.
*/
onDrag(e: PointerEvent, totalDelta: Coordinate) {
this.draggable.drag(this.newLoc(totalDelta), e);
this.moveDraggable(e, totalDelta);

// Must check `wouldDelete` before calling other hooks on drag targets
// since we have documented that we would do so.
if (isDeletable(this.draggable)) {
(this.draggable as AnyDuringMigration).setDeleteStyle(
this.wouldDeleteDraggable(e, this.draggable),
);
}
this.updateDragTarget(e);
}

/** Updates the drag target under the pointer (if there is one). */
protected updateDragTarget(e: PointerEvent) {
const newDragTarget = this.workspace.getDragTarget(e);
if (this.dragTarget !== newDragTarget) {
this.dragTarget?.onDragExit(this.draggable as AnyDuringMigration);
newDragTarget?.onDragEnter(this.draggable as AnyDuringMigration);
}
newDragTarget?.onDragOver(this.draggable as AnyDuringMigration);
this.dragTarget = newDragTarget;
}

/**
* Handles any drag cleanup.
*
* @param e PointerEvent that finished the drag.
* @param totalDelta The total distance, in pixels, that the mouse
* has moved since the start of the drag.
* Calculates the correct workspace coordinate for the movable and tells
* the draggable to go to that location.
*/
onDragEnd(e: PointerEvent, totalDelta: Coordinate) {
this.draggable.endDrag(this.newLoc(totalDelta), e);
private moveDraggable(e: PointerEvent, totalDelta: Coordinate) {
const delta = this.pixelsToWorkspaceUnits(totalDelta);
const newLoc = Coordinate.sum(this.startLoc, delta);
const dragTarget = this.workspace.getDragTarget(e);
this.draggable.drag(newLoc, dragTarget ?? undefined, e);

Check failure on line 71 in core/dragging/dragger.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 18.x)

Expected 1-2 arguments, but got 3.

Check failure on line 71 in core/dragging/dragger.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 20.x)

Expected 1-2 arguments, but got 3.
}

/**
* Calculates where the IDraggable should actually be moved to.
*
* @param totalDelta The total distance, in pixels, that the mouse
* has moved since the start of the drag.
* @returns The new location, in workspace coordinates.
* Returns true if we would delete the draggable if it was dropped
* at the current location.
*/
protected wouldDeleteDraggable(
e: PointerEvent,
draggable: IDraggable & IDeletable,
) {
const dragTarget = this.workspace.getDragTarget(e);
if (!dragTarget) return false;

const componentManager = this.workspace.getComponentManager();
const isDeleteArea = componentManager.hasCapability(
dragTarget.id,
ComponentManager.Capability.DELETE_AREA,
);
if (!isDeleteArea) return false;

return (dragTarget as IDeleteArea).wouldDelete(
draggable,
false,
// !!this.getConnectionCandidate(draggable, delta),
);
}

/** Handles any drag cleanup. */
onDragEnd(e: PointerEvent) {
const dragTarget = this.workspace.getDragTarget(e);
if (dragTarget) {
this.dragTarget?.onDrop(this.draggable as AnyDuringMigration);
}

if (this.shouldReturnToStart(e, this.draggable)) {
this.draggable.moveToStart();
}

this.draggable.endDrag(e);

if (
isDeletable(this.draggable) &&
this.wouldDeleteDraggable(e, this.draggable)
) {
(this.draggable as AnyDuringMigration).dispose();
}
}

/**
* Returns true if we should return the draggable to its original location
* at the end of the drag.
*/
protected newLoc(totalDelta: Coordinate): Coordinate {
protected shouldReturnToStart(e: PointerEvent, draggable: IDraggable) {
const dragTarget = this.workspace.getDragTarget(e);
if (!dragTarget) return false;
return dragTarget.shouldPreventMove(draggable as AnyDuringMigration);
}

protected pixelsToWorkspaceUnits(pixelCoord: Coordinate): Coordinate {
const result = new Coordinate(
totalDelta.x / this.workspace.scale,
totalDelta.y / this.workspace.scale,
pixelCoord.x / this.workspace.scale,
pixelCoord.y / this.workspace.scale,
);
if (this.workspace.isMutator) {
// If we're in a mutator, its scale is always 1, purely because of some
Expand All @@ -71,7 +139,6 @@ export class Dragger implements IDragger {
const mainScale = this.workspace.options.parentWorkspace!.scale;
result.scale(1 / mainScale);
}
result.translate(this.startLoc.x, this.startLoc.y);
return result;
}
}
5 changes: 5 additions & 0 deletions core/interfaces/i_deletable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ export interface IDeletable {
*/
isDeletable(): boolean;
}

/** Returns whether the given object is an IDeletable. */
export function isDeletable(obj: any): obj is IDeletable {
return obj['isDeletable'] !== undefined;
}
5 changes: 4 additions & 1 deletion core/interfaces/i_draggable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@ export interface IDragStrategy {
* @param e PointerEvent that finished the drag. Can be
* used to check modifier keys, etc.
*/
endDrag(newLoc: Coordinate, e?: PointerEvent): void;
endDrag(e?: PointerEvent): void;

/** Moves the draggable back to where it was at the start of the drag. */
moveToStart(): void;
}

0 comments on commit 661d6f6

Please sign in to comment.