Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(blocks): Migrate blocks/math.js to TypeScript #6900

Merged
merged 7 commits into from
Mar 17, 2023
138 changes: 65 additions & 73 deletions blocks/math.js → blocks/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,28 @@

/**
* @fileoverview Math blocks for Blockly.
* @suppress {checkTypes}
*/
'use strict';

goog.module('Blockly.libraryBlocks.math');

const Extensions = goog.require('Blockly.Extensions');
// N.B.: Blockly.FieldDropdown needed for type AND side-effects.
/* eslint-disable-next-line no-unused-vars */
const FieldDropdown = goog.require('Blockly.FieldDropdown');
const xmlUtils = goog.require('Blockly.utils.xml');
/* eslint-disable-next-line no-unused-vars */
const {Block} = goog.requireType('Blockly.Block');
// const {BlockDefinition} = goog.requireType('Blockly.blocks');
// TODO (6248): Properly import the BlockDefinition type.
/* eslint-disable-next-line no-unused-vars */
const BlockDefinition = Object;
const {createBlockDefinitionsFromJsonArray, defineBlocks} = goog.require('Blockly.common');
/** @suppress {extraRequire} */
goog.require('Blockly.FieldLabel');
/** @suppress {extraRequire} */
goog.require('Blockly.FieldNumber');
/** @suppress {extraRequire} */
goog.require('Blockly.FieldVariable');

import * as goog from '../closure/goog/goog.js';
goog.declareModuleId('Blockly.libraryBlocks.math');

import * as Extensions from '../core/extensions.js';
import type {Field} from '../core/field.js';
import type {FieldDropdown} from '../core/field_dropdown.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {Block} from '../core/block.js';
import type {BlockDefinition} from '../core/blocks.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import '../core/field_dropdown.js';
import '../core/field_label.js';
import '../core/field_number.js';
import '../core/field_variable.js';


/**
* A dictionary of the block definitions provided by this module.
* @type {!Object<string, !BlockDefinition>}
*/
const blocks = createBlockDefinitionsFromJsonArray([
export const blocks = createBlockDefinitionsFromJsonArray([
// Block for numeric value.
{
'type': 'math_number',
Expand Down Expand Up @@ -391,11 +382,11 @@ const blocks = createBlockDefinitionsFromJsonArray([
'helpUrl': '%{BKY_MATH_ATAN2_HELPURL}',
},
]);
exports.blocks = blocks;

/**
* Mapping of math block OP value to tooltip message for blocks
* math_arithmetic, math_simple, math_trig, and math_on_lists.
*
* @see {Extensions#buildTooltipForDropdown}
* @package
* @readonly
Expand Down Expand Up @@ -440,10 +431,15 @@ Extensions.register(
'math_op_tooltip',
Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP));

/** Type of a block that has IS_DIVISBLEBY_MUTATOR_MIXIN */
type DivisiblebyBlock = Block&DivisiblebyMixin;
interface DivisiblebyMixin extends DivisiblebyMixinType {};
type DivisiblebyMixinType = typeof IS_DIVISIBLEBY_MUTATOR_MIXIN;

/**
* Mixin for mutator functions in the 'math_is_divisibleby_mutator'
* extension.
*
* @mixin
* @augments Block
* @package
Expand All @@ -452,22 +448,22 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
/**
* Create XML to represent whether the 'divisorInput' should be present.
* Backwards compatible serialization implementation.
* @return {!Element} XML storage element.
* @this {Block}
*
* @returns XML storage element.
*/
mutationToDom: function() {
mutationToDom: function(this: DivisiblebyBlock): Element {
const container = xmlUtils.createElement('mutation');
const divisorInput = (this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY');
container.setAttribute('divisor_input', divisorInput);
container.setAttribute('divisor_input', String(divisorInput));
return container;
},
/**
* Parse XML to restore the 'divisorInput'.
* Backwards compatible serialization implementation.
* @param {!Element} xmlElement XML storage element.
* @this {Block}
*
* @param xmlElement XML storage element.
*/
domToMutation: function(xmlElement) {
domToMutation: function(this: DivisiblebyBlock, xmlElement: Element) {
const divisorInput = (xmlElement.getAttribute('divisor_input') === 'true');
this.updateShape_(divisorInput);
},
Expand All @@ -479,11 +475,10 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {

/**
* Modify this block to have (or not have) an input for 'is divisible by'.
* @param {boolean} divisorInput True if this block has a divisor input.
* @private
* @this {Block}
*
* @param divisorInput True if this block has a divisor input.
*/
updateShape_: function(divisorInput) {
updateShape_: function(this: DivisiblebyBlock, divisorInput: boolean) {
// Add or remove a Value Input.
const inputExists = this.getInput('DIVISOR');
if (divisorInput) {
Expand All @@ -500,20 +495,15 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
* 'math_is_divisibleby_mutator' extension to the 'math_property' block that
* can update the block shape (add/remove divisor input) based on whether
* property is "divisible by".
* @this {Block}
* @package
*/
const IS_DIVISIBLE_MUTATOR_EXTENSION = function() {
this.getField('PROPERTY')
.setValidator(
/**
* @this {FieldDropdown}
* @param {*} option The selected dropdown option.
*/
function(option) {
const divisorInput = (option === 'DIVISIBLE_BY');
this.getSourceBlock().updateShape_(divisorInput);
});
const IS_DIVISIBLE_MUTATOR_EXTENSION = function(this: DivisiblebyBlock) {
this.getField('PROPERTY')!.setValidator(
/** @param option The selected dropdown option. */
function(this: FieldDropdown, option: string) {
const divisorInput = (option === 'DIVISIBLE_BY');
(this.getSourceBlock() as DivisiblebyBlock).updateShape_(divisorInput);
return undefined; // FieldValidators can't be void. Use option as-is.
});
};

Extensions.registerMutator(
Expand All @@ -525,47 +515,49 @@ Extensions.register(
'math_change_tooltip',
Extensions.buildTooltipWithFieldText('%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR'));

/** Type of a block that has LIST_MODES_MUTATOR_MIXIN */
type ListModesBlock = Block&ListModesMixin;
interface ListModesMixin extends ListModesMixinType {};
type ListModesMixinType = typeof LIST_MODES_MUTATOR_MIXIN;

/**
* Mixin with mutator methods to support alternate output based if the
* 'math_on_list' block uses the 'MODE' operation.
* @mixin
* @augments Block
* @package
* @readonly
*/
const LIST_MODES_MUTATOR_MIXIN = {
/**
* Modify this block to have the correct output type.
* @param {string} newOp Either 'MODE' or some op than returns a number.
* @private
* @this {Block}
*
* @param newOp Either 'MODE' or some op than returns a number.
*/
updateType_: function(newOp) {
updateType_: function(this: ListModesBlock, newOp: string) {
if (newOp === 'MODE') {
this.outputConnection.setCheck('Array');
this.outputConnection!.setCheck('Array');
cpcallen marked this conversation as resolved.
Show resolved Hide resolved
} else {
this.outputConnection.setCheck('Number');
this.outputConnection!.setCheck('Number');
}
},
/**
* Create XML to represent the output type.
* Backwards compatible serialization implementation.
* @return {!Element} XML storage element.
* @this {Block}
*
* @returns XML storage element.
*/
mutationToDom: function() {
mutationToDom: function(this: ListModesBlock): Element {
const container = xmlUtils.createElement('mutation');
container.setAttribute('op', this.getFieldValue('OP'));
return container;
},
/**
* Parse XML to restore the output type.
* Backwards compatible serialization implementation.
* @param {!Element} xmlElement XML storage element.
* @this {Block}
*
* @param xmlElement XML storage element.
*/
domToMutation: function(xmlElement) {
this.updateType_(xmlElement.getAttribute('op'));
domToMutation: function(this: ListModesBlock, xmlElement: Element) {
const op = xmlElement.getAttribute('op');
if (op === null) throw new TypeError('xmlElement had no op attribute');
this.updateType_(op);
},

// This block does not need JSO serialization hooks (saveExtraState and
Expand All @@ -577,13 +569,13 @@ const LIST_MODES_MUTATOR_MIXIN = {
/**
* Extension to 'math_on_list' blocks that allows support of
* modes operation (outputs a list of numbers).
* @this {Block}
* @package
*/
const LIST_MODES_MUTATOR_EXTENSION = function() {
this.getField('OP').setValidator(function(newOp) {
this.updateType_(newOp);
}.bind(this));
const LIST_MODES_MUTATOR_EXTENSION = function(this: ListModesBlock) {
this.getField('OP')!.setValidator(
function(this: ListModesBlock, newOp: string) {
this.updateType_(newOp);
return undefined;
}.bind(this));
};

Extensions.registerMutator(
Expand Down