Skip to content
Merged
Changes from all commits
Commits
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
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');
} 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