Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c44f711
refactor!: Update dragging APIs.
gonfunko Feb 4, 2026
15f8b63
fix: Fix bug that caused drags to always result in deletion
gonfunko Feb 4, 2026
d181216
refactor: Clean up block drag handling with new API
gonfunko Feb 4, 2026
a6e9d0a
chore: Format files
gonfunko Feb 6, 2026
b86dee1
feat: Add an `isBoundedElement` type predicate
gonfunko Feb 6, 2026
e74b0ef
feat: Make `Bubble` implement `IBoundedElement`
gonfunko Feb 6, 2026
b16d7de
fix: Fix jumping/scrolling when moving blocks
gonfunko Feb 11, 2026
8b2872f
feat: Add a `KeyboardMover`
gonfunko Feb 11, 2026
940b9bc
feat: Update the `BlockDragStrategy` to support constrained movement
gonfunko Feb 11, 2026
9ccb435
feat: Register keyboard shortcuts to drive movement
gonfunko Feb 11, 2026
daff5ac
feat: Display a move indicator on items that are being moved
gonfunko Feb 11, 2026
16efc9f
fix: Reenable move hints
gonfunko Feb 12, 2026
4139a3e
fix: Fix bugs that caused elements to be mispositioned by keyboard mo…
gonfunko Feb 13, 2026
44739ed
fix: Fix a bug that caused certain connections to be visited out of o…
gonfunko Feb 17, 2026
e0ddebb
fix: Fix a bug that caused blocks to become disconnected during const…
gonfunko Feb 17, 2026
79ea540
test: Add tests for keyboard-driven movement
gonfunko Feb 17, 2026
d6fe8e8
chore: Add exports
gonfunko Feb 17, 2026
5bbc742
chore: Run formatter
gonfunko Feb 17, 2026
a424017
chore: Make the linter happy
gonfunko Feb 17, 2026
691dda1
chore: Update closure compiler
gonfunko Feb 17, 2026
615c852
fix: Fix test suite on non-macOS
gonfunko Feb 17, 2026
d2e2266
fix: Don't scroll in response to arrow keys while moving items
gonfunko Feb 20, 2026
20f32f6
fix: Fix positioning of move indicator in RTL
gonfunko Feb 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 59 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 20 additions & 9 deletions packages/blockly/core/block_svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ import type {IBoundedElement} from './interfaces/i_bounded_element.js';
import {IContextMenu} from './interfaces/i_contextmenu.js';
import type {ICopyable} from './interfaces/i_copyable.js';
import {IDeletable} from './interfaces/i_deletable.js';
import type {IDragStrategy, IDraggable} from './interfaces/i_draggable.js';
import type {
DragDisposition,
IDragStrategy,
IDraggable,
} from './interfaces/i_draggable.js';
import type {IFocusableNode} from './interfaces/i_focusable_node.js';
import type {IFocusableTree} from './interfaces/i_focusable_tree.js';
import {IIcon} from './interfaces/i_icon.js';
Expand Down Expand Up @@ -1784,18 +1788,21 @@ export class BlockSvg
}

/** Starts a drag on the block. */
startDrag(e?: PointerEvent): void {
this.dragStrategy.startDrag(e);
startDrag(e?: PointerEvent | KeyboardEvent): IDraggable {
return this.dragStrategy.startDrag(e);
}

/** Drags the block to the given location. */
drag(newLoc: Coordinate, e?: PointerEvent): void {
drag(newLoc: Coordinate, e?: PointerEvent | KeyboardEvent): void {
this.dragStrategy.drag(newLoc, e);
}

/** Ends the drag on the block. */
endDrag(e?: PointerEvent): void {
this.dragStrategy.endDrag(e);
endDrag(
e: PointerEvent | KeyboardEvent | undefined,
disposition: DragDisposition,
): void {
this.dragStrategy.endDrag(e, disposition);
}

/** Moves the block back to where it was at the start of a drag. */
Expand Down Expand Up @@ -1854,9 +1861,13 @@ export class BlockSvg
/** See IFocusableNode.onNodeFocus. */
onNodeFocus(): void {
this.select();
this.workspace.scrollBoundsIntoView(
this.getBoundingRectangleWithoutChildren(),
);
if (getFocusManager().getFocusedNode() !== this) {
renderManagement.finishQueuedRenders().then(() => {
this.workspace.scrollBoundsIntoView(
this.getBoundingRectangleWithoutChildren(),
);
});
}
}

/** See IFocusableNode.onNodeBlur. */
Expand Down
13 changes: 12 additions & 1 deletion packages/blockly/core/blockly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,19 @@ import * as icons from './icons.js';
import {inject} from './inject.js';
import * as inputs from './inputs.js';
import {IFlyoutInflater} from './interfaces/i_flyout_inflater.js';
import {Direction, KeyboardMover} from './keyboard_nav/keyboard_mover.js';
import {MoveIndicator} from './keyboard_nav/move_indicator.js';
import {LabelFlyoutInflater} from './label_flyout_inflater.js';
import {SeparatorFlyoutInflater} from './separator_flyout_inflater.js';
import {FocusableTreeTraverser} from './utils/focusable_tree_traverser.js';

import {Input} from './inputs/input.js';
import {InsertionMarkerPreviewer} from './insertion_marker_previewer.js';
import {IAutoHideable} from './interfaces/i_autohideable.js';
import {IBoundedElement} from './interfaces/i_bounded_element.js';
import {
IBoundedElement,
isBoundedElement,
} from './interfaces/i_bounded_element.js';
import {IBubble} from './interfaces/i_bubble.js';
import {ICollapsibleToolboxItem} from './interfaces/i_collapsible_toolbox_item.js';
import {IComponent} from './interfaces/i_component.js';
Expand All @@ -137,6 +142,7 @@ import {IDeletable, isDeletable} from './interfaces/i_deletable.js';
import {IDeleteArea} from './interfaces/i_delete_area.js';
import {IDragTarget} from './interfaces/i_drag_target.js';
import {
DragDisposition,
IDragStrategy,
IDraggable,
isDraggable,
Expand Down Expand Up @@ -500,6 +506,8 @@ export {
BlockFlyoutInflater,
ButtonFlyoutInflater,
CodeGenerator,
Direction,
DragDisposition,
Field,
FieldCheckbox,
FieldCheckboxConfig,
Expand Down Expand Up @@ -584,6 +592,7 @@ export {
ImageProperties,
Input,
InsertionMarkerPreviewer,
KeyboardMover,
KeyboardNavigationController,
LabelFlyoutInflater,
LayerManager,
Expand All @@ -595,6 +604,7 @@ export {
MenuItem,
MenuOption,
MetricsManager,
MoveIndicator,
Msg,
Names,
Options,
Expand Down Expand Up @@ -626,6 +636,7 @@ export {
icons,
inject,
inputs,
isBoundedElement,
isCopyable,
isDeletable,
isDraggable,
Expand Down
37 changes: 34 additions & 3 deletions packages/blockly/core/bubbles/bubble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import * as browserEvents from '../browser_events.js';
import * as common from '../common.js';
import {BubbleDragStrategy} from '../dragging/bubble_drag_strategy.js';
import {getFocusManager} from '../focus_manager.js';
import {IBoundedElement} from '../interfaces/i_bounded_element.js';
import {IBubble} from '../interfaces/i_bubble.js';
import type {IDraggable} from '../interfaces/i_draggable.js';
import type {IFocusableNode} from '../interfaces/i_focusable_node.js';
import type {IFocusableTree} from '../interfaces/i_focusable_tree.js';
import type {IHasBubble} from '../interfaces/i_has_bubble.js';
Expand All @@ -29,7 +31,9 @@ import {WorkspaceSvg} from '../workspace_svg.js';
* bubble, where it has a "tail" that points to the block, and a "head" that
* displays arbitrary svg elements.
*/
export abstract class Bubble implements IBubble, ISelectable, IFocusableNode {
export abstract class Bubble
implements IBubble, ISelectable, IFocusableNode, IBoundedElement
{
/** The width of the border around the bubble. */
static readonly BORDER_WIDTH = 6;

Expand Down Expand Up @@ -274,6 +278,18 @@ export abstract class Bubble implements IBubble, ISelectable, IFocusableNode {
this.svgRoot.setAttribute('transform', `translate(${x}, ${y})`);
}

/**
* Moves the bubble by the given amounts in the x and y directions.
*
* @param dx The distance to move along the x axis.
* @param dy The distance to move along the y axis.
* @param _reason A description of why this move is happening.
*/
moveBy(dx: number, dy: number, _reason?: string[]) {
const origin = this.getRelativeToSurfaceXY();
this.moveTo(origin.x + dx, origin.y + dy);
}

/**
* Positions the bubble "optimally" so that the most of it is visible and
* it does not overlap the rect (if provided).
Expand Down Expand Up @@ -617,6 +633,21 @@ export abstract class Bubble implements IBubble, ISelectable, IFocusableNode {
);
}

/**
* Returns the bounds of this bubble.
*
* @returns A bounding box for this bubble.
*/
getBoundingRectangle(): Rect {
const origin = this.getRelativeToSurfaceXY();
return new Rect(
origin.y,
origin.y + this.size.height,
origin.x,
origin.x + this.size.width,
);
}

/** @internal */
getSvgRoot(): SVGElement {
return this.svgRoot;
Expand Down Expand Up @@ -664,8 +695,8 @@ export abstract class Bubble implements IBubble, ISelectable, IFocusableNode {
}

/** Starts a drag on the bubble. */
startDrag(): void {
this.dragStrategy.startDrag();
startDrag(): IDraggable {
return this.dragStrategy.startDrag();
}

/** Drags the bubble to the given location. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ export class RenderedWorkspaceComment
}

/** Starts a drag on the comment. */
startDrag(): void {
this.dragStrategy.startDrag();
startDrag(): IDraggable {
return this.dragStrategy.startDrag();
}

/** Drags the comment to the given location. */
Expand Down
2 changes: 1 addition & 1 deletion packages/blockly/core/comments/workspace_comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export class WorkspaceComment {

/** Returns the position of the comment in workspace coordinates. */
getRelativeToSurfaceXY(): Coordinate {
return this.location;
return this.location.clone();
}

/** Disposes of this comment. */
Expand Down
Loading
Loading