diff --git a/packages/blockly/core/block_svg.ts b/packages/blockly/core/block_svg.ts index b5935297b8b..d1e75fa5f33 100644 --- a/packages/blockly/core/block_svg.ts +++ b/packages/blockly/core/block_svg.ts @@ -1819,6 +1819,36 @@ export class BlockSvg return this.dragStrategy.isMovable(); } + /** Returns whether the block fully fits within the boundaries of the workspace */ + isBlockFullyInBounds(): boolean { + let blockLeft; + let blockRight; + const {left, top, width, height} = this.workspace + .getMetricsManager() + .getViewMetrics(true); + + const xy = this.getRelativeToSurfaceXY(); + const {width: blockWidth, height: blockHeight} = this.getHeightWidth(); + + if (this.RTL) { + blockLeft = xy.x - blockWidth; + blockRight = xy.x; + } else { + blockLeft = xy.x; + blockRight = xy.x + blockWidth; + } + + const blockTop = xy.y; + const blockBottom = xy.y + blockHeight; + + return ( + blockLeft >= left && + blockRight <= left + width && + blockTop >= top && + blockBottom <= top + height + ); + } + /** Starts a drag on the block. */ startDrag(e?: PointerEvent): void { this.dragStrategy.startDrag(e); diff --git a/packages/blockly/core/workspace_svg.ts b/packages/blockly/core/workspace_svg.ts index c693225970f..b7a422ddb3b 100644 --- a/packages/blockly/core/workspace_svg.ts +++ b/packages/blockly/core/workspace_svg.ts @@ -2010,7 +2010,9 @@ export class WorkspaceSvg : block.getHeightWidth(); // Find the enter of the block in workspace units. - const blockCenterY = xy.y + heightWidth.height / 2; + const blockCenterY = block.isBlockFullyInBounds() + ? xy.y + heightWidth.height / 2 + : xy.y; // In RTL the block's position is the top right of the block, not top left. const multiplier = this.RTL ? -1 : 1; diff --git a/packages/blockly/tests/mocha/workspace_svg_test.js b/packages/blockly/tests/mocha/workspace_svg_test.js index b40a46941bd..9ea0564e3fc 100644 --- a/packages/blockly/tests/mocha/workspace_svg_test.js +++ b/packages/blockly/tests/mocha/workspace_svg_test.js @@ -314,6 +314,21 @@ suite('WorkspaceSvg', function () { this.clock, ); }); + test('centerOnBlock when block partially out of bounds', function () { + const block = this.workspace.newBlock('stack_block'); + block.initSvg(); + block.render(); + + sinon.stub(block, 'getRelativeToSurfaceXY').returns({x: 10, y: 80}); + sinon.stub(block, 'getHeightWidth').returns({width: 40, height: 40}); + + runViewportEventTest( + () => this.workspace.centerOnBlock(block.id), + this.changeListenerSpy, + this.workspace, + this.clock, + ); + }); }); suite('Blocks triggering viewport changes', function () { test('block move that triggers scroll', function () {