Skip to content

Commit 678dd98

Browse files
feat: monkeypatch dragger as needed to delete on aborted insertion
1 parent e8b5c40 commit 678dd98

File tree

2 files changed

+87
-9
lines changed

2 files changed

+87
-9
lines changed

src/actions/mover.ts

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@
55
*/
66

77
import type {BlockSvg, IDragger, IDragStrategy, Gesture} from 'blockly';
8-
import {Connection, registry, utils, WorkspaceSvg} from 'blockly';
8+
import {
9+
ASTNode,
10+
Connection,
11+
dragging,
12+
registry,
13+
utils,
14+
WorkspaceSvg,
15+
} from 'blockly';
916
import * as Constants from '../constants';
1017
import {Direction, getXYFromDirection} from '../drag_direction';
1118
import {KeyboardDragStrategy} from '../keyboard_drag_strategy';
@@ -42,6 +49,18 @@ export class Mover {
4249
*/
4350
oldGetGesture: ((e: PointerEvent) => Gesture | null) | null = null;
4451

52+
/**
53+
* The stashed wouldDeleteDraggable function, which is sometimes replaced
54+
* during a keyboard drag and is reset at the end of a keyboard drag.
55+
*/
56+
oldWouldDeleteDraggable: (() => boolean) | null = null;
57+
58+
/**
59+
* The stashed shouldReturnToStart function, which is sometimes replaced
60+
* during a keyboard drag and is reset at the end of a keyboard drag.
61+
*/
62+
oldShouldReturnToStart: (() => boolean) | null = null;
63+
4564
/**
4665
* The block's base drag strategy, which will be overridden during
4766
* keyboard drags and reset at the end of the drag.
@@ -146,21 +165,37 @@ export class Mover {
146165
const info = this.moves.get(workspace);
147166
if (!info) throw new Error('no move info for workspace');
148167

149-
// Monkey patch dragger to trigger call to draggable.revertDrag.
150-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
151-
(info.dragger as any).shouldReturnToStart = () => true;
152-
const blockSvg = info.block;
168+
const dragStrategy = info.block.getDragStrategy() as KeyboardDragStrategy;
169+
this.patchDragger(
170+
info.dragger as dragging.Dragger,
171+
dragStrategy.isNewBlock,
172+
);
153173

154174
// Explicitly call `hidePreview` because it is not called in revertDrag.
155-
// @ts-expect-error Access to private property dragStrategy.
156-
blockSvg.dragStrategy.connectionPreviewer.hidePreview();
175+
// @ts-expect-error Access to private property connectionPreviewer.
176+
dragStrategy.connectionPreviewer.hidePreview();
177+
178+
// Save the position so we can put the cursor in a reasonable spot.
179+
// @ts-expect-error Access to private property connectionCandidate.
180+
const target = dragStrategy.connectionCandidate?.neighbour;
181+
182+
// Prevent the stragegy connecting the block so we just delete one block.
183+
// @ts-expect-error Access to private property connectionCandidate.
184+
dragStrategy.connectionCandidate = null;
185+
157186
info.dragger.onDragEnd(
158187
info.fakePointerEvent('pointerup'),
159-
new utils.Coordinate(0, 0),
188+
info.startLocation,
160189
);
161190

191+
if (target) {
192+
const newNode = ASTNode.createConnectionNode(target);
193+
if (newNode) workspace.getCursor()?.setCurNode(newNode);
194+
}
195+
162196
this.unpatchWorkspace(workspace);
163197
this.unpatchDragStrategy(info.block);
198+
this.unpatchDragger(info.dragger as dragging.Dragger);
164199
this.moves.delete(workspace);
165200
return true;
166201
}
@@ -246,6 +281,7 @@ export class Mover {
246281
(workspace as any).getGesture = this.oldGetGesture;
247282
}
248283
}
284+
249285
/**
250286
* Monkeypatch: replace the block's drag strategy and cache the old value.
251287
*
@@ -271,6 +307,48 @@ export class Mover {
271307
this.oldDragStrategy = null;
272308
}
273309
}
310+
311+
/**
312+
* Monkeypatch: override either wouldDeleteDraggable or shouldReturnToStart,
313+
* based on whether this was an insertion of a new block or a movement of
314+
* an existing block.
315+
*
316+
* @param dragger The dragger to patch.
317+
* @param isNewBlock Whether the moving block was created for this action.
318+
*/
319+
private patchDragger(dragger: dragging.Dragger, isNewBlock: boolean) {
320+
if (isNewBlock) {
321+
// @ts-expect-error dragger.wouldDeleteDraggable is private.
322+
this.oldWouldDeleteDraggable = dragger.wouldDeleteDraggable;
323+
// Monkey patch dragger to trigger delete.
324+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
325+
(dragger as any).wouldDeleteDraggable = () => true;
326+
} else {
327+
// @ts-expect-error dragger.shouldReturnToStart is private.
328+
this.oldShouldReturnToStart = dragger.shouldReturnToStart;
329+
// Monkey patch dragger to trigger call to draggable.revertDrag.
330+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
331+
(dragger as any).shouldReturnToStart = () => true;
332+
}
333+
}
334+
335+
/**
336+
* Undo the monkeypatching of the block dragger.
337+
*
338+
* @param dragger The dragger to patch.
339+
*/
340+
private unpatchDragger(dragger: dragging.Dragger) {
341+
if (this.oldWouldDeleteDraggable) {
342+
// @ts-expect-error dragger.wouldDeleteDraggable is private.
343+
dragger.wouldDeleteDraggable = this.oldWouldDeleteDraggable;
344+
this.oldWouldDeleteDraggable = null;
345+
}
346+
if (this.oldShouldReturnToStart) {
347+
// @ts-expect-error dragger.shouldReturnToStart is private.
348+
dragger.shouldReturnToStart = this.oldShouldReturnToStart;
349+
this.oldShouldReturnToStart = null;
350+
}
351+
}
274352
}
275353

276354
/**

src/keyboard_drag_strategy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class KeyboardDragStrategy extends dragging.BlockDragStrategy {
4242
constructor(
4343
private block: BlockSvg,
4444
private navigation: Navigation,
45-
private isNewBlock: boolean,
45+
readonly isNewBlock: boolean,
4646
) {
4747
super(block);
4848
this.cursor = block.workspace.getCursor();

0 commit comments

Comments
 (0)