refactor: convert blocksvg and block to ES6 classes (#5952)

* refact: move super call to top of block svg

* refact: run conversion script on block svg and block

* fix: make debug build happy

* fix: tests

* style: format

* fix: cleanup from rebase

* fix: use new.target instead of a new parameter

* fix: add more overridden casted methods to BlockSvg

* style: fix typos

* style: move override tags to the end of JSDoc

* fix: cleanup from rebase
This commit is contained in:
Beka Westberg
2022-02-28 09:48:37 -08:00
committed by GitHub
parent cb4521b645
commit fa14e9d6de
22 changed files with 3987 additions and 3790 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -460,7 +460,8 @@ const BlockDragger = class {
const initIconData = function(block) {
// Build a list of icons that need to be moved and where they started.
const dragIconData = [];
const descendants = block.getDescendants(false);
const descendants =
/** @type {!Array<!BlockSvg>} */ (block.getDescendants(false));
for (let i = 0, descendant; (descendant = descendants[i]); i++) {
const icons = descendant.getIcons();

File diff suppressed because it is too large Load Diff

View File

@@ -120,7 +120,6 @@ class Comment extends Icon {
*/
this.paragraphElement_ = null;
this.createIcon();
}

View File

@@ -27,6 +27,8 @@ const userAgent = goog.require('Blockly.utils.userAgent');
const svgMath = goog.require('Blockly.utils.svgMath');
/* eslint-disable-next-line no-unused-vars */
const {Block} = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const {BlockSvg} = goog.requireType('Blockly.BlockSvg');
const {config} = goog.require('Blockly.config');
const {Coordinate} = goog.require('Blockly.utils.Coordinate');
const {MenuItem} = goog.require('Blockly.MenuItem');
@@ -262,7 +264,8 @@ const callbackFactory = function(block, xml) {
eventUtils.disable();
let newBlock;
try {
newBlock = Xml.domToBlock(xml, block.workspace);
newBlock =
/** @type {!BlockSvg} */ (Xml.domToBlock(xml, block.workspace));
// Move the new block next to the old block.
const xy = block.getRelativeToSurfaceXY();
if (block.RTL) {

View File

@@ -239,8 +239,7 @@ const addDeletableBlocks_ = function(block, deleteList) {
if (block.isDeletable()) {
Array.prototype.push.apply(deleteList, block.getDescendants(false));
} else {
const children = /* eslint-disable-next-line indent */
/** @type {!Array<!BlockSvg>} */ (block.getChildren(false));
const children = block.getChildren(false);
for (let i = 0; i < children.length; i++) {
addDeletableBlocks_(children[i], deleteList);
}

View File

@@ -103,9 +103,12 @@ class BlockChange extends BlockBase {
console.warn('Can\'t change non-existent block: ' + this.blockId);
return;
}
if (block.mutator) {
// Assume the block is rendered so that then we can check.
const blockSvg = /** @type {!BlockSvg} */ (block);
if (blockSvg.mutator) {
// Close the mutator (if open) since we don't want to update it.
block.mutator.setVisible(false);
blockSvg.mutator.setVisible(false);
}
const value = forward ? this.newValue : this.oldValue;
switch (this.element) {

View File

@@ -513,11 +513,11 @@ exports.buildTooltipWithFieldText = buildTooltipWithFieldText;
* @this {Block}
*/
const extensionParentTooltip = function() {
this.tooltipWhenNotConnected = this.tooltip;
const tooltipWhenNotConnected = this.tooltip;
this.setTooltip(function() {
const parent = this.getParent();
return (parent && parent.getInputsInline() && parent.tooltip) ||
this.tooltipWhenNotConnected;
tooltipWhenNotConnected;
}.bind(this));
};
register('parent_tooltip_when_inline', extensionParentTooltip);

View File

@@ -206,6 +206,14 @@ class Flyout extends DeleteArea {
*/
this.containerVisible_ = true;
/**
* A map from blocks to the rects which are beneath them to act as input
* targets.
* @type {!WeakMap<!BlockSvg, !SVGElement>}
* @private
*/
this.rectMap_ = new WeakMap();
/**
* Corner radius of the flyout background.
* @type {number}
@@ -1045,7 +1053,7 @@ class Flyout extends DeleteArea {
// Add the rectangles under the blocks, so that the blocks' tooltips work.
this.workspace_.getCanvas().insertBefore(rect, block.getSvgRoot());
block.flyoutRect_ = rect;
this.rectMap_.set(block, rect);
this.mats_[index] = rect;
return rect;
}

View File

@@ -360,8 +360,8 @@ class HorizontalFlyout extends Flyout {
if (this.height_ !== flyoutHeight) {
for (let i = 0, block; (block = blocks[i]); i++) {
if (block.flyoutRect_) {
this.moveRectToBlock_(block.flyoutRect_, block);
if (this.rectMap_.has(block)) {
this.moveRectToBlock_(this.rectMap_.get(block), block);
}
}

View File

@@ -350,8 +350,8 @@ class VerticalFlyout extends Flyout {
}
block.moveBy(newX - oldX, 0);
}
if (block.flyoutRect_) {
this.moveRectToBlock_(block.flyoutRect_, block);
if (this.rectMap_.has(block)) {
this.moveRectToBlock_(this.rectMap_.get(block), block);
}
}
if (this.RTL) {

View File

@@ -367,11 +367,9 @@ class Mutator extends Icon {
// Save the initial connections, then listen for further changes.
if (this.block_.saveConnections) {
const thisRootBlock = this.rootBlock_;
const mutatorBlock =
/** @type {{saveConnections: function(!Block)}} */ (this.block_);
mutatorBlock.saveConnections(this.rootBlock_);
this.block_.saveConnections(thisRootBlock);
this.sourceListener_ = function() {
mutatorBlock.saveConnections(thisRootBlock);
this.block_.saveConnections(thisRootBlock);
};
this.block_.workspace.addChangeListener(this.sourceListener_);
}

View File

@@ -27,6 +27,8 @@ const {Block} = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const {BubbleOpen} = goog.requireType('Blockly.Events.BubbleOpen');
/* eslint-disable-next-line no-unused-vars */
const {BlockSvg} = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const {Field} = goog.requireType('Blockly.Field');
const {Msg} = goog.require('Blockly.Msg');
const {Names} = goog.require('Blockly.Names');
@@ -162,8 +164,9 @@ const isNameUsed = function(name, workspace, opt_exclude) {
if (blocks[i] === opt_exclude) {
continue;
}
if (blocks[i].getProcedureDef) {
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
// Assume it is a procedure block so we can check.
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
if (procedureBlock.getProcedureDef) {
const procName = procedureBlock.getProcedureDef();
if (Names.equals(procName[0], name)) {
return true;
@@ -193,8 +196,9 @@ const rename = function(name) {
// Rename any callers.
const blocks = this.getSourceBlock().workspace.getAllBlocks(false);
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].renameProcedure) {
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
// Assume it is a procedure so we can check.
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
if (procedureBlock.renameProcedure) {
procedureBlock.renameProcedure(
/** @type {string} */ (oldName), legalName);
}
@@ -335,13 +339,13 @@ const mutatorOpenListener = function(e) {
return;
}
const workspaceId = /** @type {string} */ (bubbleEvent.workspaceId);
const block =
Workspace.getById(workspaceId).getBlockById(bubbleEvent.blockId);
const block = /** @type {!BlockSvg} */
(Workspace.getById(workspaceId).getBlockById(bubbleEvent.blockId));
const type = block.type;
if (type !== 'procedures_defnoreturn' && type !== 'procedures_defreturn') {
return;
}
const workspace = block.mutator.getWorkspace();
const workspace = /** @type {!WorkspaceSvg} */ (block.mutator.getWorkspace());
updateMutatorFlyout(workspace);
workspace.addChangeListener(mutatorChangeListener);
};
@@ -376,8 +380,9 @@ const getCallers = function(name, workspace) {
const blocks = workspace.getAllBlocks(false);
// Iterate through every block and check the name.
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].getProcedureCall) {
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
// Assume it is a procedure block so we can check.
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
if (procedureBlock.getProcedureCall) {
const procName = procedureBlock.getProcedureCall();
// Procedure name may be null if the block is only half-built.
if (procName && Names.equals(procName, name)) {
@@ -433,8 +438,9 @@ const getDefinition = function(name, workspace) {
// rely on getProcedureDef.
const blocks = workspace.getAllBlocks(false);
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].getProcedureDef) {
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
// Assume it is a procedure block so we can check.
const procedureBlock = /** @type {!ProcedureBlock} */ (blocks[i]);
if (procedureBlock.getProcedureDef) {
const tuple = procedureBlock.getProcedureDef();
if (tuple && Names.equals(tuple[0], name)) {
return blocks[i]; // Can't use procedureBlock var due to type check.

View File

@@ -53,6 +53,9 @@ class RenderedConnection extends Connection {
constructor(source, type) {
super(source, type);
/** @type {!BlockSvg} */
this.sourceBlock_;
/**
* Connection database for connections of this type on the current
* workspace.
@@ -139,7 +142,7 @@ class RenderedConnection extends Connection {
/**
* Move the block(s) belonging to the connection to a point where they don't
* visually interfere with the specified connection.
* @param {!Connection} staticConnection The connection to move away
* @param {!RenderedConnection} staticConnection The connection to move away
* from.
* @package
*/
@@ -436,7 +439,8 @@ class RenderedConnection extends Connection {
setTimeout(function() {
if (!block.isDisposed() && !block.getParent()) {
eventUtils.setGroup(group);
this.bumpAwayFrom(otherConnection);
this.bumpAwayFrom(
/** @type {!RenderedConnection} */ (otherConnection));
eventUtils.setGroup(false);
}
}.bind(this), config.bumpDelay);
@@ -452,15 +456,18 @@ class RenderedConnection extends Connection {
*/
disconnectInternal_(parentBlock, childBlock) {
super.disconnectInternal_(parentBlock, childBlock);
const renderedParent = /** @type {!BlockSvg} */ (parentBlock);
const renderedChild = /** @type {!BlockSvg} */ (childBlock);
// Rerender the parent so that it may reflow.
if (parentBlock.rendered) {
parentBlock.render();
if (renderedParent.rendered) {
renderedParent.render();
}
if (childBlock.rendered) {
childBlock.updateDisabled();
childBlock.render();
if (renderedChild.rendered) {
renderedChild.updateDisabled();
renderedChild.render();
// Reset visibility, since the child is now a top block.
childBlock.getSvgRoot().style.display = 'block';
renderedChild.getSvgRoot().style.display = 'block';
}
}
@@ -506,9 +513,12 @@ class RenderedConnection extends Connection {
connect_(childConnection) {
super.connect_(childConnection);
const renderedChildConnection = /** @type {!RenderedConnection} */
(childConnection);
const parentConnection = this;
const parentBlock = parentConnection.getSourceBlock();
const childBlock = childConnection.getSourceBlock();
const childBlock = renderedChildConnection.getSourceBlock();
const parentRendered = parentBlock.rendered;
const childRendered = childBlock.rendered;

View File

@@ -19,7 +19,7 @@
goog.module('Blockly.blockRendering.IPathObject');
/* eslint-disable-next-line no-unused-vars */
const {Block} = goog.requireType('Blockly.Block');
const {BlockSvg} = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const {ConstantProvider} = goog.requireType('Blockly.blockRendering.ConstantProvider');
/* eslint-disable-next-line no-unused-vars */
@@ -78,7 +78,7 @@ IPathObject.prototype.setPath;
/**
* Apply the stored colours to the block's path, taking into account whether
* the paths belong to a shadow block.
* @param {!Block} block The source block.
* @param {!BlockSvg} block The source block.
* @package
*/
IPathObject.prototype.applyColour;

View File

@@ -18,7 +18,7 @@ goog.module('Blockly.blockRendering.PathObject');
const dom = goog.require('Blockly.utils.dom');
/* eslint-disable-next-line no-unused-vars */
const {Block} = goog.requireType('Blockly.Block');
const {BlockSvg} = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const {Connection} = goog.requireType('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
@@ -144,7 +144,7 @@ class PathObject {
/**
* Apply the stored colours to the block's path, taking into account whether
* the paths belong to a shadow block.
* @param {!Block} block The source block.
* @param {!BlockSvg} block The source block.
* @package
*/
applyColour(block) {

View File

@@ -23,6 +23,8 @@ const serializationRegistry = goog.require('Blockly.serialization.registry');
const {BadConnectionCheck, MissingBlockType, MissingConnection, RealChildOfShadow} = goog.require('Blockly.serialization.exceptions');
/* eslint-disable-next-line no-unused-vars */
const {Block} = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const {BlockSvg} = goog.requireType('Blockly.BlockSvg');
// eslint-disable-next-line no-unused-vars
const {Connection} = goog.requireType('Blockly.Connection');
// eslint-disable-next-line no-unused-vars
@@ -350,9 +352,10 @@ const appendInternal = function(state, workspace, {
// Adding connections to the connection db is expensive. This defers that
// operation to decrease load time.
if (workspace.rendered) {
const blockSvg = /** @type {!BlockSvg} */ (block);
setTimeout(() => {
if (!block.disposed) {
block.setConnectionTracking(true);
if (!blockSvg.disposed) {
blockSvg.setConnectionTracking(true);
}
}, 1);
}
@@ -513,9 +516,10 @@ const loadIcons = function(block, state) {
block.setCommentText(comment['text']);
block.commentModel.pinned = comment['pinned'];
block.commentModel.size = new Size(comment['width'], comment['height']);
if (comment['pinned'] && block.getCommentIcon && !block.isInFlyout) {
if (comment['pinned'] && block.rendered && !block.isInFlyout) {
// Give the block a chance to be positioned and rendered before showing.
setTimeout(() => block.getCommentIcon().setVisible(true), 1);
const blockSvg = /** @type {!BlockSvg} */ (block);
setTimeout(() => blockSvg.getCommentIcon().setVisible(true), 1);
}
}
};
@@ -607,12 +611,13 @@ const loadConnection = function(connection, connectionState) {
*/
const initBlock = function(block, rendered) {
if (rendered) {
const blockSvg = /** @type {!BlockSvg} */ (block);
// Adding connections to the connection db is expensive. This defers that
// operation to decrease load time.
block.setConnectionTracking(false);
blockSvg.setConnectionTracking(false);
block.initSvg();
block.render(false);
blockSvg.initSvg();
blockSvg.render(false);
} else {
block.initModel();
}

View File

@@ -1563,14 +1563,14 @@ class WorkspaceSvg extends Workspace {
let blockX = 0;
let blockY = 0;
if (xmlBlock) {
block = Xml.domToBlock(xmlBlock, this);
block = /** @type {!BlockSvg} */ (Xml.domToBlock(xmlBlock, this));
blockX = parseInt(xmlBlock.getAttribute('x'), 10);
if (this.RTL) {
blockX = -blockX;
}
blockY = parseInt(xmlBlock.getAttribute('y'), 10);
} else if (jsonBlock) {
block = blocks.append(jsonBlock, this);
block = /** @type {!BlockSvg} */ (blocks.append(jsonBlock, this));
blockX = jsonBlock['x'] || 10;
if (this.RTL) {
blockX = this.getWidth() - blockX;
@@ -2425,6 +2425,16 @@ class WorkspaceSvg extends Workspace {
return /** @type {BlockSvg} */ (super.getBlockById(id));
}
/**
* Find all blocks in workspace. Blocks are optionally sorted
* by position; top to bottom (with slight LTR or RTL bias).
* @param {boolean} ordered Sort the list if true.
* @return {!Array<!BlockSvg>} Array of blocks.
*/
getAllBlocks(ordered) {
return /** @type {!Array<!BlockSvg>} */ (super.getAllBlocks(ordered));
}
/**
* Finds the top-level blocks and returns them. Blocks are optionally sorted
* by position; top to bottom (with slight LTR or RTL bias).

View File

@@ -21,6 +21,8 @@ const utilsXml = goog.require('Blockly.utils.xml');
/* eslint-disable-next-line no-unused-vars */
const {Block} = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const {BlockSvg} = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const {Connection} = goog.requireType('Blockly.Connection');
/* eslint-disable-next-line no-unused-vars */
const {Field} = goog.requireType('Blockly.Field');
@@ -599,10 +601,10 @@ const domToBlock = function(xmlBlock, workspace) {
try {
topBlock = domToBlockHeadless(xmlBlock, workspace);
// Generate list of all blocks.
const blocks = topBlock.getDescendants(false);
if (workspace.rendered) {
// Wait to track connections to speed up assembly.
topBlock.setConnectionTracking(false);
const topBlockSvg = /** @type {!BlockSvg} */ (topBlock);
const blocks = topBlock.getDescendants(false);
topBlockSvg.setConnectionTracking(false);
// Render each block.
for (let i = blocks.length - 1; i >= 0; i--) {
blocks[i].initSvg();
@@ -613,15 +615,16 @@ const domToBlock = function(xmlBlock, workspace) {
// Populating the connection database may be deferred until after the
// blocks have rendered.
setTimeout(function() {
if (!topBlock.disposed) {
topBlock.setConnectionTracking(true);
if (!topBlockSvg.disposed) {
topBlockSvg.setConnectionTracking(true);
}
}, 1);
topBlock.updateDisabled();
topBlockSvg.updateDisabled();
// Allow the scrollbars to resize and move based on the new contents.
// TODO(@picklesrus): #387. Remove when domToBlock avoids resizing.
workspace.resizeContents();
} else {
const blocks = topBlock.getDescendants(false);
for (let i = blocks.length - 1; i >= 0; i--) {
blocks[i].initModel();
}
@@ -778,8 +781,9 @@ const applyCommentTagNodes = function(xmlChildren, block) {
}
if (pinned && block.getCommentIcon && !block.isInFlyout) {
const blockSvg = /** @type {BlockSvg} */ (block);
setTimeout(function() {
block.getCommentIcon().setVisible(true);
blockSvg.getCommentIcon().setVisible(true);
}, 1);
}
}
@@ -953,8 +957,11 @@ const domToBlockHeadless = function(
applyNextTagNodes(xmlChildNameMap.next, workspace, block);
if (shouldCallInitSvg) {
// InitSvg needs to be called after variable fields are loaded.
block.initSvg();
// This shouldn't even be called here
// (ref: https://github.com/google/blockly/pull/4296#issuecomment-884226021
// But the XML serializer/deserializer is iceboxed so I'm not going to fix
// it.
(/** @type {!BlockSvg} */ (block)).initSvg();
}
const inline = xmlBlock.getAttribute('inline');