mirror of
https://github.com/google/blockly.git
synced 2025-12-16 06:10:12 +01:00
refactor(blocks): Migrate blocks/loops.js to TypeScript (#6957)
* refactor(blocks): Auto-migration of blocks/loops.js to ts * fix(blocks): Manually migrate types and fix imports in loops.ts * chore: respond to PR comments
This commit is contained in:
@@ -6,44 +6,33 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Loop blocks for Blockly.
|
* @fileoverview Loop blocks for Blockly.
|
||||||
* @suppress {checkTypes}
|
|
||||||
*/
|
*/
|
||||||
'use strict';
|
|
||||||
|
|
||||||
goog.module('Blockly.libraryBlocks.loops');
|
import * as goog from '../closure/goog/goog.js';
|
||||||
|
goog.declareModuleId('Blockly.libraryBlocks.loops');
|
||||||
|
|
||||||
/* eslint-disable-next-line no-unused-vars */
|
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
|
||||||
const AbstractEvent = goog.requireType('Blockly.Events.Abstract');
|
import type {Block} from '../core/block.js';
|
||||||
const ContextMenu = goog.require('Blockly.ContextMenu');
|
import * as ContextMenu from '../core/contextmenu.js';
|
||||||
const Events = goog.require('Blockly.Events');
|
import * as Events from '../core/events/events.js';
|
||||||
const Extensions = goog.require('Blockly.Extensions');
|
import * as Extensions from '../core/extensions.js';
|
||||||
const Variables = goog.require('Blockly.Variables');
|
import * as Variables from '../core/variables.js';
|
||||||
const xmlUtils = goog.require('Blockly.utils.xml');
|
import * as xmlUtils from '../core/utils/xml.js';
|
||||||
/* eslint-disable-next-line no-unused-vars */
|
import {Msg} from '../core/msg.js';
|
||||||
const {Block} = goog.requireType('Blockly.Block');
|
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
|
||||||
// const {BlockDefinition} = goog.requireType('Blockly.blocks');
|
import '../core/field_dropdown.js';
|
||||||
// TODO (6248): Properly import the BlockDefinition type.
|
import '../core/field_label.js';
|
||||||
/* eslint-disable-next-line no-unused-vars */
|
import '../core/field_number.js';
|
||||||
const BlockDefinition = Object;
|
import '../core/field_variable.js';
|
||||||
const {Msg} = goog.require('Blockly.Msg');
|
import '../core/warning.js';
|
||||||
const {createBlockDefinitionsFromJsonArray, defineBlocks} = goog.require('Blockly.common');
|
import {FieldVariable} from '../core/field_variable.js';
|
||||||
/** @suppress {extraRequire} */
|
import {WorkspaceSvg} from '../core/workspace_svg.js';
|
||||||
goog.require('Blockly.FieldDropdown');
|
|
||||||
/** @suppress {extraRequire} */
|
|
||||||
goog.require('Blockly.FieldLabel');
|
|
||||||
/** @suppress {extraRequire} */
|
|
||||||
goog.require('Blockly.FieldNumber');
|
|
||||||
/** @suppress {extraRequire} */
|
|
||||||
goog.require('Blockly.FieldVariable');
|
|
||||||
/** @suppress {extraRequire} */
|
|
||||||
goog.require('Blockly.Warning');
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dictionary of the block definitions provided by this module.
|
* A dictionary of the block definitions provided by this module.
|
||||||
* @type {!Object<string, !BlockDefinition>}
|
|
||||||
*/
|
*/
|
||||||
const blocks = createBlockDefinitionsFromJsonArray([
|
export const blocks = createBlockDefinitionsFromJsonArray([
|
||||||
// Block for repeat n times (external number).
|
// Block for repeat n times (external number).
|
||||||
{
|
{
|
||||||
'type': 'controls_repeat_ext',
|
'type': 'controls_repeat_ext',
|
||||||
@@ -213,12 +202,11 @@ const blocks = createBlockDefinitionsFromJsonArray([
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
exports.blocks = blocks;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tooltips for the 'controls_whileUntil' block, keyed by MODE value.
|
* Tooltips for the 'controls_whileUntil' block, keyed by MODE value.
|
||||||
|
*
|
||||||
* @see {Extensions#buildTooltipForDropdown}
|
* @see {Extensions#buildTooltipForDropdown}
|
||||||
* @readonly
|
|
||||||
*/
|
*/
|
||||||
const WHILE_UNTIL_TOOLTIPS = {
|
const WHILE_UNTIL_TOOLTIPS = {
|
||||||
'WHILE': '%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}',
|
'WHILE': '%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}',
|
||||||
@@ -231,8 +219,8 @@ Extensions.register(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tooltips for the 'controls_flow_statements' block, keyed by FLOW value.
|
* Tooltips for the 'controls_flow_statements' block, keyed by FLOW value.
|
||||||
|
*
|
||||||
* @see {Extensions#buildTooltipForDropdown}
|
* @see {Extensions#buildTooltipForDropdown}
|
||||||
* @readonly
|
|
||||||
*/
|
*/
|
||||||
const BREAK_CONTINUE_TOOLTIPS = {
|
const BREAK_CONTINUE_TOOLTIPS = {
|
||||||
'BREAK': '%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}',
|
'BREAK': '%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}',
|
||||||
@@ -243,36 +231,42 @@ Extensions.register(
|
|||||||
'controls_flow_tooltip',
|
'controls_flow_tooltip',
|
||||||
Extensions.buildTooltipForDropdown('FLOW', BREAK_CONTINUE_TOOLTIPS));
|
Extensions.buildTooltipForDropdown('FLOW', BREAK_CONTINUE_TOOLTIPS));
|
||||||
|
|
||||||
|
/** Type of a block that has CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN */
|
||||||
|
type CustomContextMenuBlock = Block&CustomContextMenuMixin;
|
||||||
|
interface CustomContextMenuMixin extends CustomContextMenuMixinType {}
|
||||||
|
type CustomContextMenuMixinType =
|
||||||
|
typeof CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mixin to add a context menu item to create a 'variables_get' block.
|
* Mixin to add a context menu item to create a 'variables_get' block.
|
||||||
* Used by blocks 'controls_for' and 'controls_forEach'.
|
* Used by blocks 'controls_for' and 'controls_forEach'.
|
||||||
* @mixin
|
|
||||||
* @augments Block
|
|
||||||
* @package
|
|
||||||
* @readonly
|
|
||||||
*/
|
*/
|
||||||
const CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = {
|
const CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = {
|
||||||
/**
|
/**
|
||||||
* Add context menu option to create getter block for the loop's variable.
|
* Add context menu option to create getter block for the loop's variable.
|
||||||
* (customContextMenu support limited to web BlockSvg.)
|
* (customContextMenu support limited to web BlockSvg.)
|
||||||
* @param {!Array} options List of menu options to add to.
|
*
|
||||||
* @this {Block}
|
* @param options List of menu options to add to.
|
||||||
*/
|
*/
|
||||||
customContextMenu: function(options) {
|
customContextMenu: function(
|
||||||
|
this: CustomContextMenuBlock, options: Array<any>) {
|
||||||
if (this.isInFlyout) {
|
if (this.isInFlyout) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const variable = this.getField('VAR').getVariable();
|
const varField = this.getField('VAR') as FieldVariable;
|
||||||
|
const variable = varField.getVariable()!;
|
||||||
const varName = variable.name;
|
const varName = variable.name;
|
||||||
if (!this.isCollapsed() && varName !== null) {
|
if (!this.isCollapsed() && varName !== null) {
|
||||||
const option = {enabled: true};
|
|
||||||
option.text = Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName);
|
|
||||||
const xmlField = Variables.generateVariableFieldDom(variable);
|
const xmlField = Variables.generateVariableFieldDom(variable);
|
||||||
const xmlBlock = xmlUtils.createElement('block');
|
const xmlBlock = xmlUtils.createElement('block');
|
||||||
xmlBlock.setAttribute('type', 'variables_get');
|
xmlBlock.setAttribute('type', 'variables_get');
|
||||||
xmlBlock.appendChild(xmlField);
|
xmlBlock.appendChild(xmlField);
|
||||||
option.callback = ContextMenu.callbackFactory(this, xmlBlock);
|
|
||||||
options.push(option);
|
options.push({
|
||||||
|
enabled: true,
|
||||||
|
text: Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName),
|
||||||
|
callback: ContextMenu.callbackFactory(this, xmlBlock)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -304,34 +298,32 @@ Extensions.register(
|
|||||||
*
|
*
|
||||||
* // Else if using blockly_compressed + blockss_compressed.js in browser:
|
* // Else if using blockly_compressed + blockss_compressed.js in browser:
|
||||||
* Blockly.libraryBlocks.loopTypes.add('custom_loop');
|
* Blockly.libraryBlocks.loopTypes.add('custom_loop');
|
||||||
*
|
|
||||||
* @type {!Set<string>}
|
|
||||||
*/
|
*/
|
||||||
const loopTypes = new Set([
|
export const loopTypes: Set<string> = new Set([
|
||||||
'controls_repeat',
|
'controls_repeat',
|
||||||
'controls_repeat_ext',
|
'controls_repeat_ext',
|
||||||
'controls_forEach',
|
'controls_forEach',
|
||||||
'controls_for',
|
'controls_for',
|
||||||
'controls_whileUntil',
|
'controls_whileUntil',
|
||||||
]);
|
]);
|
||||||
exports.loopTypes = loopTypes;
|
|
||||||
|
/** Type of a block that has CONTROL_FLOW_IN_LOOP_CHECK_MIXIN */
|
||||||
|
type ControlFlowInLoopBlock = Block&ControlFlowInLoopMixin;
|
||||||
|
interface ControlFlowInLoopMixin extends ControlFlowInLoopMixinType {}
|
||||||
|
type ControlFlowInLoopMixinType = typeof CONTROL_FLOW_IN_LOOP_CHECK_MIXIN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mixin adds a check to make sure the 'controls_flow_statements' block
|
* This mixin adds a check to make sure the 'controls_flow_statements' block
|
||||||
* is contained in a loop. Otherwise a warning is added to the block.
|
* is contained in a loop. Otherwise a warning is added to the block.
|
||||||
* @mixin
|
|
||||||
* @augments Block
|
|
||||||
* @public
|
|
||||||
* @readonly
|
|
||||||
*/
|
*/
|
||||||
const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
|
const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
|
||||||
/**
|
/**
|
||||||
* Is this block enclosed (at any level) by a loop?
|
* Is this block enclosed (at any level) by a loop?
|
||||||
* @return {Block} The nearest surrounding loop, or null if none.
|
*
|
||||||
* @this {Block}
|
* @returns The nearest surrounding loop, or null if none.
|
||||||
*/
|
*/
|
||||||
getSurroundLoop: function() {
|
getSurroundLoop: function(this: ControlFlowInLoopBlock): Block | null {
|
||||||
let block = this;
|
let block: Block|null = this;
|
||||||
do {
|
do {
|
||||||
if (loopTypes.has(block.type)) {
|
if (loopTypes.has(block.type)) {
|
||||||
return block;
|
return block;
|
||||||
@@ -344,18 +336,16 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
|
|||||||
/**
|
/**
|
||||||
* Called whenever anything on the workspace changes.
|
* Called whenever anything on the workspace changes.
|
||||||
* Add warning if this flow block is not nested inside a loop.
|
* Add warning if this flow block is not nested inside a loop.
|
||||||
* @param {!AbstractEvent} e Move event.
|
|
||||||
* @this {Block}
|
|
||||||
*/
|
*/
|
||||||
onchange: function(e) {
|
onchange: function(this: ControlFlowInLoopBlock, e: AbstractEvent) {
|
||||||
|
const ws = this.workspace as WorkspaceSvg;
|
||||||
// Don't change state if:
|
// Don't change state if:
|
||||||
// * It's at the start of a drag.
|
// * It's at the start of a drag.
|
||||||
// * It's not a move event.
|
// * It's not a move event.
|
||||||
if (!this.workspace.isDragging || this.workspace.isDragging() ||
|
if (!ws.isDragging || ws.isDragging() || e.type !== Events.BLOCK_MOVE) {
|
||||||
e.type !== Events.BLOCK_MOVE) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const enabled = this.getSurroundLoop(this);
|
const enabled = !!this.getSurroundLoop();
|
||||||
this.setWarningText(
|
this.setWarningText(
|
||||||
enabled ? null : Msg['CONTROLS_FLOW_STATEMENTS_WARNING']);
|
enabled ? null : Msg['CONTROLS_FLOW_STATEMENTS_WARNING']);
|
||||||
if (!this.isInFlyout) {
|
if (!this.isInFlyout) {
|
||||||
Reference in New Issue
Block a user