diff --git a/src/actions/mover.ts b/src/actions/mover.ts index 67e09ca7..979664f6 100644 --- a/src/actions/mover.ts +++ b/src/actions/mover.ts @@ -18,6 +18,7 @@ import { registry, utils, WorkspaceSvg, + ShortcutRegistry, } from 'blockly'; import * as Constants from '../constants'; import {Direction, getXYFromDirection} from '../drag_direction'; @@ -36,6 +37,11 @@ const UNCONSTRAINED_MOVE_DISTANCE = 20; */ const CONSTRAINED_ADDITIONAL_PADDING = 70; +/** + * Identifier for a keyboard shortcut that commits the in-progress move. + */ +const COMMIT_MOVE_SHORTCUT = 'commitMove'; + /** * Low-level code for moving blocks with keyboard shortcuts. */ @@ -140,6 +146,48 @@ export class Mover { // (otherwise dragging will break). getFocusManager().focusNode(block); block.getFocusableElement().addEventListener('blur', blurListener); + + // Register a keyboard shortcut under the key combos of all existing + // keyboard shortcuts that commits the move before allowing the real + // shortcut to proceed. This avoids all kinds of fun brokenness when + // deleting/copying/otherwise acting on a block in move mode. + const shortcutKeys = Object.values(ShortcutRegistry.registry.getRegistry()) + .flatMap((shortcut) => shortcut.keyCodes) + .filter((keyCode) => { + return ( + keyCode && + ![ + utils.KeyCodes.RIGHT, + utils.KeyCodes.LEFT, + utils.KeyCodes.UP, + utils.KeyCodes.DOWN, + utils.KeyCodes.ENTER, + utils.KeyCodes.ESC, + ].includes( + typeof keyCode === 'number' + ? keyCode + : parseInt(`${keyCode.split('+').pop()}`), + ) + ); + }) + // Convince TS there aren't undefined values. + .filter((keyCode): keyCode is string | number => !!keyCode); + + const commitMoveShortcut = { + name: COMMIT_MOVE_SHORTCUT, + preconditionFn: (workspace: WorkspaceSvg) => { + return !!this.moves.get(workspace); + }, + callback: (workspace: WorkspaceSvg) => { + this.finishMove(workspace); + return false; + }, + keyCodes: shortcutKeys, + allowCollision: true, + }; + + ShortcutRegistry.registry.register(commitMoveShortcut); + return true; } @@ -208,6 +256,7 @@ export class Mover { * @returns The info for the block. */ private preDragEndCleanup(workspace: WorkspaceSvg) { + ShortcutRegistry.registry.unregister(COMMIT_MOVE_SHORTCUT); clearMoveHints(workspace); const info = this.moves.get(workspace);