diff --git a/core/block.ts b/core/block.ts index a321ad141..ca1ebd82c 100644 --- a/core/block.ts +++ b/core/block.ts @@ -33,7 +33,6 @@ import * as fieldRegistry from './field_registry.js'; import {Input} from './inputs/input.js'; import {Align} from './inputs/align.js'; import type {IASTNodeLocation} from './interfaces/i_ast_node_location.js'; -import type {IDeletable} from './interfaces/i_deletable.js'; import {type IIcon} from './interfaces/i_icon.js'; import {isCommentIcon} from './interfaces/i_comment_icon.js'; import type {MutatorIcon} from './icons/mutator_icon.js'; @@ -56,7 +55,7 @@ import {IconType} from './icons/icon_types.js'; * Class for one block. * Not normally called directly, workspace.newBlock() is preferred. */ -export class Block implements IASTNodeLocation, IDeletable { +export class Block implements IASTNodeLocation { /** * An optional callback method to use whenever the block's parent workspace * changes. This is usually only called from the constructor, the block type @@ -318,7 +317,7 @@ export class Block implements IASTNodeLocation, IDeletable { * statement with the previous statement. Otherwise, dispose of all * children of this block. */ - dispose(healStack: boolean) { + dispose(healStack = false) { this.disposing = true; // Dispose of this change listener before unplugging. diff --git a/core/block_svg.ts b/core/block_svg.ts index 427436217..c7ab9289e 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -61,6 +61,7 @@ import * as renderManagement from './render_management.js'; import {IconType} from './icons/icon_types.js'; import {BlockCopyData, BlockPaster} from './clipboard/block_paster.js'; import {BlockDragStrategy} from './dragging/block_drag_strategy.js'; +import {IDeletable} from './blockly.js'; /** * Class for a block's SVG representation. @@ -72,7 +73,8 @@ export class BlockSvg IASTNodeLocationSvg, IBoundedElement, ICopyable, - IDraggable + IDraggable, + IDeletable { /** * Constant for identifying rows that are to be rendered inline. diff --git a/core/blockly.ts b/core/blockly.ts index 09db3c12c..678193f65 100644 --- a/core/blockly.ts +++ b/core/blockly.ts @@ -122,10 +122,10 @@ import {IConnectionChecker} from './interfaces/i_connection_checker.js'; import {IConnectionPreviewer} from './interfaces/i_connection_previewer.js'; import {IContextMenu} from './interfaces/i_contextmenu.js'; import {ICopyable, isCopyable} from './interfaces/i_copyable.js'; -import {IDeletable} from './interfaces/i_deletable.js'; +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 {IDraggable} from './interfaces/i_draggable.old.js'; +import {IDraggable, isDraggable} from './interfaces/i_draggable.js'; import {IFlyout} from './interfaces/i_flyout.js'; import {IHasBubble, hasBubble} from './interfaces/i_has_bubble.js'; import {IIcon, isIcon} from './interfaces/i_icon.js'; @@ -136,7 +136,7 @@ import {IObservable, isObservable} from './interfaces/i_observable.js'; import {IPaster, isPaster} from './interfaces/i_paster.js'; import {IPositionable} from './interfaces/i_positionable.js'; import {IRegistrable} from './interfaces/i_registrable.js'; -import {ISelectable} from './interfaces/i_selectable.js'; +import {ISelectable, isSelectable} from './interfaces/i_selectable.js'; import {ISelectableToolboxItem} from './interfaces/i_selectable_toolbox_item.js'; import {ISerializable, isSerializable} from './interfaces/i_serializable.js'; import {IStyleable} from './interfaces/i_styleable.js'; @@ -527,10 +527,10 @@ export {IConnectionPreviewer}; export {IContextMenu}; export {icons}; export {ICopyable, isCopyable}; -export {IDeletable}; +export {IDeletable, isDeletable}; export {IDeleteArea}; export {IDragTarget}; -export {IDraggable}; +export {IDraggable, isDraggable}; export {IFlyout}; export {IHasBubble, hasBubble}; export {IIcon, isIcon}; @@ -545,7 +545,7 @@ export {IObservable, isObservable}; export {IPaster, isPaster}; export {IPositionable}; export {IRegistrable}; -export {ISelectable}; +export {ISelectable, isSelectable}; export {ISelectableToolboxItem}; export {ISerializable, isSerializable}; export {IStyleable}; diff --git a/core/delete_area.ts b/core/delete_area.ts index 6d30a434c..4967927c4 100644 --- a/core/delete_area.ts +++ b/core/delete_area.ts @@ -15,7 +15,8 @@ import {BlockSvg} from './block_svg.js'; import {DragTarget} from './drag_target.js'; import type {IDeleteArea} from './interfaces/i_delete_area.js'; -import type {IDraggable} from './interfaces/i_draggable.old.js'; +import type {IDraggable} from './interfaces/i_draggable.js'; +import {isDeletable} from './interfaces/i_deletable.js'; /** * Abstract class for a component that can delete a block or bubble that is @@ -60,7 +61,7 @@ export class DeleteArea extends DragTarget implements IDeleteArea { const couldDeleteBlock = !block.getParent() && block.isDeletable(); this.updateWouldDelete_(couldDeleteBlock); } else { - this.updateWouldDelete_(element.isDeletable()); + this.updateWouldDelete_(isDeletable(element) && element.isDeletable()); } return this.wouldDelete_; } diff --git a/core/drag_target.ts b/core/drag_target.ts index 37e04e645..e973f2dd1 100644 --- a/core/drag_target.ts +++ b/core/drag_target.ts @@ -13,7 +13,7 @@ // Former goog.module ID: Blockly.DragTarget import type {IDragTarget} from './interfaces/i_drag_target.js'; -import type {IDraggable} from './interfaces/i_draggable.old.js'; +import type {IDraggable} from './interfaces/i_draggable.js'; import type {Rect} from './utils/rect.js'; /** @@ -39,8 +39,9 @@ export class DragTarget implements IDragTarget { * * @param _dragElement The block or bubble currently being dragged. */ - onDragEnter(_dragElement: IDraggable) {} - // no-op + onDragEnter(_dragElement: IDraggable) { + // no-op + } /** * Handles when a cursor with a block or bubble is dragged over this drag @@ -48,24 +49,27 @@ export class DragTarget implements IDragTarget { * * @param _dragElement The block or bubble currently being dragged. */ - onDragOver(_dragElement: IDraggable) {} - // no-op + onDragOver(_dragElement: IDraggable) { + // no-op + } /** * Handles when a cursor with a block or bubble exits this drag target. * * @param _dragElement The block or bubble currently being dragged. */ - onDragExit(_dragElement: IDraggable) {} - // no-op + onDragExit(_dragElement: IDraggable) { + // no-op + } /** * Handles when a block or bubble is dropped on this component. * Should not handle delete here. * * @param _dragElement The block or bubble currently being dragged. */ - onDrop(_dragElement: IDraggable) {} - // no-op + onDrop(_dragElement: IDraggable) { + // no-op + } /** * Returns the bounding rectangle of the drag target area in pixel units diff --git a/core/dragging/dragger.ts b/core/dragging/dragger.ts index ded1dda10..2be9ab1fd 100644 --- a/core/dragging/dragger.ts +++ b/core/dragging/dragger.ts @@ -43,7 +43,7 @@ export class Dragger implements IDragger { // Must check `wouldDelete` before calling other hooks on drag targets // since we have documented that we would do so. if (isDeletable(this.draggable)) { - (this.draggable as AnyDuringMigration).setDeleteStyle( + this.draggable.setDeleteStyle( this.wouldDeleteDraggable(e, this.draggable), ); } @@ -54,10 +54,10 @@ export class Dragger implements IDragger { protected updateDragTarget(e: PointerEvent) { const newDragTarget = this.workspace.getDragTarget(e); if (this.dragTarget !== newDragTarget) { - this.dragTarget?.onDragExit(this.draggable as AnyDuringMigration); - newDragTarget?.onDragEnter(this.draggable as AnyDuringMigration); + this.dragTarget?.onDragExit(this.draggable); + newDragTarget?.onDragEnter(this.draggable); } - newDragTarget?.onDragOver(this.draggable as AnyDuringMigration); + newDragTarget?.onDragOver(this.draggable); this.dragTarget = newDragTarget; } @@ -96,7 +96,7 @@ export class Dragger implements IDragger { onDragEnd(e: PointerEvent) { const dragTarget = this.workspace.getDragTarget(e); if (dragTarget) { - this.dragTarget?.onDrop(this.draggable as AnyDuringMigration); + this.dragTarget?.onDrop(this.draggable); } if (this.shouldReturnToStart(e, this.draggable)) { @@ -109,7 +109,7 @@ export class Dragger implements IDragger { isDeletable(this.draggable) && this.wouldDeleteDraggable(e, this.draggable) ) { - (this.draggable as AnyDuringMigration).dispose(); + this.draggable.dispose(); } } @@ -120,7 +120,7 @@ export class Dragger implements IDragger { protected shouldReturnToStart(e: PointerEvent, draggable: IDraggable) { const dragTarget = this.workspace.getDragTarget(e); if (!dragTarget) return false; - return dragTarget.shouldPreventMove(draggable as AnyDuringMigration); + return dragTarget.shouldPreventMove(draggable); } protected pixelsToWorkspaceUnits(pixelCoord: Coordinate): Coordinate { diff --git a/core/interfaces/i_deletable.ts b/core/interfaces/i_deletable.ts index 518d71baa..046770940 100644 --- a/core/interfaces/i_deletable.ts +++ b/core/interfaces/i_deletable.ts @@ -16,9 +16,19 @@ export interface IDeletable { * @returns True if deletable. */ isDeletable(): boolean; + + /** Disposes of this object, cleaning up any references or DOM elements. */ + dispose(): void; + + /** Visually indicates that the object is pending deletion. */ + setDeleteStyle(wouldDelete: boolean): void; } /** Returns whether the given object is an IDeletable. */ export function isDeletable(obj: any): obj is IDeletable { - return obj['isDeletable'] !== undefined; + return ( + obj['isDeletable'] !== undefined && + obj['dispose'] !== undefined && + obj['setDeleteStyle'] !== undefined + ); } diff --git a/core/interfaces/i_delete_area.ts b/core/interfaces/i_delete_area.ts index 6d4a6631d..86d2673bb 100644 --- a/core/interfaces/i_delete_area.ts +++ b/core/interfaces/i_delete_area.ts @@ -7,7 +7,7 @@ // Former goog.module ID: Blockly.IDeleteArea import type {IDragTarget} from './i_drag_target.js'; -import type {IDraggable} from './i_draggable.old.js'; +import type {IDraggable} from './i_draggable.js'; /** * Interface for a component that can delete a block or bubble that is dropped diff --git a/core/interfaces/i_drag_target.ts b/core/interfaces/i_drag_target.ts index d0bfecf53..3fa060232 100644 --- a/core/interfaces/i_drag_target.ts +++ b/core/interfaces/i_drag_target.ts @@ -6,7 +6,7 @@ import {Rect} from '../utils/rect.js'; -import {IDraggable} from './i_draggable.old.js'; +import {IDraggable} from './i_draggable.js'; // Former goog.module ID: Blockly.IDragTarget diff --git a/core/interfaces/i_draggable.ts b/core/interfaces/i_draggable.ts index 6f98e53c5..cb723e7b8 100644 --- a/core/interfaces/i_draggable.ts +++ b/core/interfaces/i_draggable.ts @@ -58,3 +58,15 @@ export interface IDragStrategy { /** Moves the draggable back to where it was at the start of the drag. */ revertDrag(): void; } + +/** Returns whether the given object is an IDraggable or not. */ +export function isDraggable(obj: any): obj is IDraggable { + return ( + obj.getRelativeToSurfaceXY !== undefined && + obj.isMovable !== undefined && + obj.startDrag !== undefined && + obj.drag !== undefined && + obj.endDrag !== undefined && + obj.revertDrag !== undefined + ); +} diff --git a/core/interfaces/i_selectable.ts b/core/interfaces/i_selectable.ts index cda47c7cd..8090ad94a 100644 --- a/core/interfaces/i_selectable.ts +++ b/core/interfaces/i_selectable.ts @@ -6,13 +6,10 @@ // Former goog.module ID: Blockly.ISelectable -import type {IDeletable} from './i_deletable.js'; -import type {IMovable} from './i_movable.js'; - /** * The interface for an object that is selectable. */ -export interface ISelectable extends IDeletable, IMovable { +export interface ISelectable { id: string; /** Select this. Highlight it visually. */ @@ -21,3 +18,12 @@ export interface ISelectable extends IDeletable, IMovable { /** Unselect this. Unhighlight it visually. */ unselect(): void; } + +/** Checks whether the given object is an ISelectable. */ +export function isSelectable(obj: Object): obj is ISelectable { + return ( + typeof (obj as any).id === 'string' && + (obj as any).select !== undefined && + (obj as any).unselect !== undefined + ); +} diff --git a/core/shortcut_items.ts b/core/shortcut_items.ts index 57b90a498..76e11dcd7 100644 --- a/core/shortcut_items.ts +++ b/core/shortcut_items.ts @@ -11,9 +11,11 @@ import * as clipboard from './clipboard.js'; import * as common from './common.js'; import {Gesture} from './gesture.js'; import {ICopyData, isCopyable} from './interfaces/i_copyable.js'; +import {isDeletable} from './interfaces/i_deletable.js'; import {KeyboardShortcut, ShortcutRegistry} from './shortcut_registry.js'; import {KeyCodes} from './utils/keycodes.js'; import type {WorkspaceSvg} from './workspace_svg.js'; +import {isDraggable} from './interfaces/i_draggable.js'; /** * Object holding the names of the default shortcut items. @@ -59,6 +61,7 @@ export function registerDelete() { return ( !workspace.options.readOnly && selected != null && + isDeletable(selected) && selected.isDeletable() ); }, @@ -105,7 +108,9 @@ export function registerCopy() { !workspace.options.readOnly && !Gesture.inProgress() && selected != null && + isDeletable(selected) && selected.isDeletable() && + isDraggable(selected) && selected.isMovable() && isCopyable(selected) ); diff --git a/core/toolbox/toolbox.ts b/core/toolbox/toolbox.ts index e49d0b9c4..e0fb62e23 100644 --- a/core/toolbox/toolbox.ts +++ b/core/toolbox/toolbox.ts @@ -24,7 +24,7 @@ import {DeleteArea} from '../delete_area.js'; import * as eventUtils from '../events/utils.js'; import type {IAutoHideable} from '../interfaces/i_autohideable.js'; import type {ICollapsibleToolboxItem} from '../interfaces/i_collapsible_toolbox_item.js'; -import type {IDraggable} from '../interfaces/i_draggable.old.js'; +import type {IDraggable} from '../interfaces/i_draggable.js'; import type {IFlyout} from '../interfaces/i_flyout.js'; import type {IKeyboardAccessible} from '../interfaces/i_keyboard_accessible.js'; import type {ISelectableToolboxItem} from '../interfaces/i_selectable_toolbox_item.js'; @@ -41,9 +41,9 @@ import * as dom from '../utils/dom.js'; import {Rect} from '../utils/rect.js'; import * as toolbox from '../utils/toolbox.js'; import type {WorkspaceSvg} from '../workspace_svg.js'; - import type {ToolboxCategory} from './category.js'; import {CollapsibleToolboxCategory} from './collapsible_category.js'; +import {isDeletable} from '../interfaces/i_deletable.js'; /** * Class for a Toolbox. @@ -540,7 +540,7 @@ export class Toolbox const block = element; this.updateWouldDelete_(!block.getParent() && block.isDeletable()); } else { - this.updateWouldDelete_(element.isDeletable()); + this.updateWouldDelete_(isDeletable(element) && element.isDeletable()); } return this.wouldDelete_; } diff --git a/core/trashcan.ts b/core/trashcan.ts index c89262263..b48777c4d 100644 --- a/core/trashcan.ts +++ b/core/trashcan.ts @@ -22,7 +22,7 @@ import type {Abstract} from './events/events_abstract.js'; import type {BlockDelete} from './events/events_block_delete.js'; import * as eventUtils from './events/utils.js'; import type {IAutoHideable} from './interfaces/i_autohideable.js'; -import type {IDraggable} from './interfaces/i_draggable.old.js'; +import type {IDraggable} from './interfaces/i_draggable.js'; import type {IFlyout} from './interfaces/i_flyout.js'; import type {IPositionable} from './interfaces/i_positionable.js'; import type {UiMetrics} from './metrics_manager.js';