55 */
66
77import 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' ;
916import * as Constants from '../constants' ;
1017import { Direction , getXYFromDirection } from '../drag_direction' ;
1118import { 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/**
0 commit comments