mirror of
https://github.com/google/blockly.git
synced 2025-12-16 06:10:12 +01:00
refactor(blocks): Migrate blocks/logic.js to TypeScript (#7003)
* refactor(blocks): Auto-migration of blocks/logic.js to ts * fix(blocks): Manually migrate & fix types in logic.ts * chore(blocks): Fix import order * chore(blocks): Fixes for PR #7003 * chore(blocks): Lint removal * chore(blocks): Format blocks/logic.ts
This commit is contained in:
committed by
GitHub
parent
d7ccf8a5ee
commit
95dd63b2bd
@@ -4,44 +4,30 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Logic blocks for Blockly.
|
||||
* @suppress {checkTypes}
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.module('Blockly.libraryBlocks.logic');
|
||||
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
const AbstractEvent = goog.requireType('Blockly.Events.Abstract');
|
||||
const Events = goog.require('Blockly.Events');
|
||||
const Extensions = goog.require('Blockly.Extensions');
|
||||
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 {Msg} = goog.require('Blockly.Msg');
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
const {RenderedConnection} = goog.requireType('Blockly.RenderedConnection');
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
const {Workspace} = goog.requireType('Blockly.Workspace');
|
||||
const {createBlockDefinitionsFromJsonArray, defineBlocks} = goog.require('Blockly.common');
|
||||
/** @suppress {extraRequire} */
|
||||
goog.require('Blockly.Mutator');
|
||||
/** @suppress {extraRequire} */
|
||||
goog.require('Blockly.FieldDropdown');
|
||||
/** @suppress {extraRequire} */
|
||||
goog.require('Blockly.FieldLabel');
|
||||
import * as goog from '../closure/goog/goog.js';
|
||||
goog.declareModuleId('Blockly.libraryBlocks.logic');
|
||||
|
||||
import * as Events from '../core/events/events.js';
|
||||
import * as Extensions from '../core/extensions.js';
|
||||
import * as xmlUtils from '../core/utils/xml.js';
|
||||
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
|
||||
import type {Block} from '../core/block.js';
|
||||
import type {BlockSvg} from '../core/block_svg.js';
|
||||
import type {Connection} from '../core/connection.js';
|
||||
import {Msg} from '../core/msg.js';
|
||||
import type {Workspace} from '../core/workspace.js';
|
||||
import {
|
||||
createBlockDefinitionsFromJsonArray,
|
||||
defineBlocks,
|
||||
} from '../core/common.js';
|
||||
import '../core/field_dropdown.js';
|
||||
import '../core/field_label.js';
|
||||
import '../core/icons/mutator_icon.js';
|
||||
|
||||
/**
|
||||
* A dictionary of the block definitions provided by this module.
|
||||
* @type {!Object<string, !BlockDefinition>}
|
||||
*/
|
||||
const blocks = createBlockDefinitionsFromJsonArray([
|
||||
export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
// Block for boolean data type: true and false.
|
||||
{
|
||||
'type': 'logic_boolean',
|
||||
@@ -267,13 +253,12 @@ const blocks = createBlockDefinitionsFromJsonArray([
|
||||
'tooltip': '%{BKY_CONTROLS_IF_ELSE_TOOLTIP}',
|
||||
},
|
||||
]);
|
||||
exports.blocks = blocks;
|
||||
|
||||
/**
|
||||
* Tooltip text, keyed by block OP value. Used by logic_compare and
|
||||
* logic_operation blocks.
|
||||
*
|
||||
* @see {Extensions#buildTooltipForDropdown}
|
||||
* @readonly
|
||||
*/
|
||||
const TOOLTIPS_BY_OP = {
|
||||
// logic_compare
|
||||
@@ -290,14 +275,33 @@ const TOOLTIPS_BY_OP = {
|
||||
};
|
||||
|
||||
Extensions.register(
|
||||
'logic_op_tooltip',
|
||||
Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP));
|
||||
'logic_op_tooltip',
|
||||
Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP)
|
||||
);
|
||||
|
||||
/** Type of a block that has CONTROLS_IF_MUTATOR_MIXIN */
|
||||
type IfBlock = Block & IfMixin;
|
||||
interface IfMixin extends IfMixinType {}
|
||||
type IfMixinType = typeof CONTROLS_IF_MUTATOR_MIXIN;
|
||||
|
||||
// Types for quarks defined in JSON.
|
||||
/** Type of a controls_if_if (if mutator container) block. */
|
||||
interface ContainerBlock extends Block {}
|
||||
|
||||
/** Type of a controls_if_elseif or controls_if_else block. */
|
||||
interface ClauseBlock extends Block {
|
||||
valueConnection_?: Connection | null;
|
||||
statementConnection_?: Connection | null;
|
||||
}
|
||||
|
||||
/** Extra state for serialising controls_if blocks. */
|
||||
type IfExtraState = {
|
||||
elseIfCount?: number;
|
||||
hasElse?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutator methods added to controls_if blocks.
|
||||
* @mixin
|
||||
* @augments Block
|
||||
* @readonly
|
||||
*/
|
||||
const CONTROLS_IF_MUTATOR_MIXIN = {
|
||||
elseifCount_: 0,
|
||||
@@ -306,39 +310,39 @@ const CONTROLS_IF_MUTATOR_MIXIN = {
|
||||
/**
|
||||
* Create XML to represent the number of else-if and else inputs.
|
||||
* Backwards compatible serialization implementation.
|
||||
* @return {Element} XML storage element.
|
||||
* @this {Block}
|
||||
*
|
||||
* @returns XML storage element.
|
||||
*/
|
||||
mutationToDom: function() {
|
||||
mutationToDom: function (this: IfBlock): Element | null {
|
||||
if (!this.elseifCount_ && !this.elseCount_) {
|
||||
return null;
|
||||
}
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
if (this.elseifCount_) {
|
||||
container.setAttribute('elseif', this.elseifCount_);
|
||||
container.setAttribute('elseif', String(this.elseifCount_));
|
||||
}
|
||||
if (this.elseCount_) {
|
||||
container.setAttribute('else', 1);
|
||||
container.setAttribute('else', '1');
|
||||
}
|
||||
return container;
|
||||
},
|
||||
/**
|
||||
* Parse XML to restore the else-if and else inputs.
|
||||
* Backwards compatible serialization implementation.
|
||||
* @param {!Element} xmlElement XML storage element.
|
||||
* @this {Block}
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(xmlElement) {
|
||||
this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif'), 10) || 0;
|
||||
this.elseCount_ = parseInt(xmlElement.getAttribute('else'), 10) || 0;
|
||||
domToMutation: function (this: IfBlock, xmlElement: Element) {
|
||||
this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif')!, 10) || 0;
|
||||
this.elseCount_ = parseInt(xmlElement.getAttribute('else')!, 10) || 0;
|
||||
this.rebuildShape_();
|
||||
},
|
||||
/**
|
||||
* Returns the state of this block as a JSON serializable object.
|
||||
* @return {?{elseIfCount: (number|undefined), haseElse: (boolean|undefined)}}
|
||||
* The state of this block, ie the else if count and else state.
|
||||
*
|
||||
* @returns The state of this block, ie the else if count and else state.
|
||||
*/
|
||||
saveExtraState: function() {
|
||||
saveExtraState: function (this: IfBlock): IfExtraState | null {
|
||||
if (!this.elseifCount_ && !this.elseCount_) {
|
||||
return null;
|
||||
}
|
||||
@@ -353,86 +357,102 @@ const CONTROLS_IF_MUTATOR_MIXIN = {
|
||||
},
|
||||
/**
|
||||
* Applies the given state to this block.
|
||||
* @param {*} state The state to apply to this block, ie the else if count and
|
||||
*
|
||||
* @param state The state to apply to this block, ie the else if count
|
||||
and
|
||||
* else state.
|
||||
*/
|
||||
loadExtraState: function(state) {
|
||||
loadExtraState: function (this: IfBlock, state: IfExtraState) {
|
||||
this.elseifCount_ = state['elseIfCount'] || 0;
|
||||
this.elseCount_ = state['hasElse'] ? 1 : 0;
|
||||
this.updateShape_();
|
||||
},
|
||||
/**
|
||||
* Populate the mutator's dialog with this block's components.
|
||||
* @param {!Workspace} workspace Mutator's workspace.
|
||||
* @return {!Block} Root block in mutator.
|
||||
* @this {Block}
|
||||
*
|
||||
* @param workspace MutatorIcon's workspace.
|
||||
* @returns Root block in mutator.
|
||||
*/
|
||||
decompose: function(workspace) {
|
||||
decompose: function (this: IfBlock, workspace: Workspace): ContainerBlock {
|
||||
const containerBlock = workspace.newBlock('controls_if_if');
|
||||
containerBlock.initSvg();
|
||||
let connection = containerBlock.nextConnection;
|
||||
(containerBlock as BlockSvg).initSvg();
|
||||
let connection = containerBlock.nextConnection!;
|
||||
for (let i = 1; i <= this.elseifCount_; i++) {
|
||||
const elseifBlock = workspace.newBlock('controls_if_elseif');
|
||||
elseifBlock.initSvg();
|
||||
connection.connect(elseifBlock.previousConnection);
|
||||
connection = elseifBlock.nextConnection;
|
||||
(elseifBlock as BlockSvg).initSvg();
|
||||
connection.connect(elseifBlock.previousConnection!);
|
||||
connection = elseifBlock.nextConnection!;
|
||||
}
|
||||
if (this.elseCount_) {
|
||||
const elseBlock = workspace.newBlock('controls_if_else');
|
||||
elseBlock.initSvg();
|
||||
connection.connect(elseBlock.previousConnection);
|
||||
(elseBlock as BlockSvg).initSvg();
|
||||
connection.connect(elseBlock.previousConnection!);
|
||||
}
|
||||
return containerBlock;
|
||||
},
|
||||
/**
|
||||
* Reconfigure this block based on the mutator dialog's components.
|
||||
* @param {!Block} containerBlock Root block in mutator.
|
||||
* @this {Block}
|
||||
*
|
||||
* @param containerBlock Root block in mutator.
|
||||
*/
|
||||
compose: function(containerBlock) {
|
||||
let clauseBlock = containerBlock.nextConnection.targetBlock();
|
||||
compose: function (this: IfBlock, containerBlock: ContainerBlock) {
|
||||
let clauseBlock =
|
||||
containerBlock.nextConnection!.targetBlock() as ClauseBlock | null;
|
||||
// Count number of inputs.
|
||||
this.elseifCount_ = 0;
|
||||
this.elseCount_ = 0;
|
||||
const valueConnections = [null];
|
||||
const statementConnections = [null];
|
||||
let elseStatementConnection = null;
|
||||
// Connections arrays are passed to .reconnectChildBlocks_() which
|
||||
// takes 1-based arrays, so are initialised with a dummy value at
|
||||
// index 0 for convenience.
|
||||
const valueConnections: Array<Connection | null> = [null];
|
||||
const statementConnections: Array<Connection | null> = [null];
|
||||
let elseStatementConnection: Connection | null = null;
|
||||
while (clauseBlock) {
|
||||
if (clauseBlock.isInsertionMarker()) {
|
||||
clauseBlock = clauseBlock.getNextBlock();
|
||||
clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null;
|
||||
continue;
|
||||
}
|
||||
switch (clauseBlock.type) {
|
||||
case 'controls_if_elseif':
|
||||
this.elseifCount_++;
|
||||
valueConnections.push(clauseBlock.valueConnection_);
|
||||
statementConnections.push(clauseBlock.statementConnection_);
|
||||
// TODO(#6920): null valid, undefined not.
|
||||
valueConnections.push(
|
||||
clauseBlock.valueConnection_ as Connection | null
|
||||
);
|
||||
statementConnections.push(
|
||||
clauseBlock.statementConnection_ as Connection | null
|
||||
);
|
||||
break;
|
||||
case 'controls_if_else':
|
||||
this.elseCount_++;
|
||||
elseStatementConnection = clauseBlock.statementConnection_;
|
||||
elseStatementConnection =
|
||||
clauseBlock.statementConnection_ as Connection | null;
|
||||
break;
|
||||
default:
|
||||
throw TypeError('Unknown block type: ' + clauseBlock.type);
|
||||
}
|
||||
clauseBlock = clauseBlock.getNextBlock();
|
||||
clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null;
|
||||
}
|
||||
this.updateShape_();
|
||||
// Reconnect any child blocks.
|
||||
this.reconnectChildBlocks_(
|
||||
valueConnections, statementConnections, elseStatementConnection);
|
||||
valueConnections,
|
||||
statementConnections,
|
||||
elseStatementConnection
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Store pointers to any connected child blocks.
|
||||
* @param {!Block} containerBlock Root block in mutator.
|
||||
* @this {Block}
|
||||
*
|
||||
* @param containerBlock Root block in mutator.
|
||||
*/
|
||||
saveConnections: function(containerBlock) {
|
||||
let clauseBlock = containerBlock.nextConnection.targetBlock();
|
||||
saveConnections: function (this: IfBlock, containerBlock: ContainerBlock) {
|
||||
let clauseBlock =
|
||||
containerBlock!.nextConnection!.targetBlock() as ClauseBlock | null;
|
||||
let i = 1;
|
||||
while (clauseBlock) {
|
||||
if (clauseBlock.isInsertionMarker()) {
|
||||
clauseBlock = clauseBlock.getNextBlock();
|
||||
clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null;
|
||||
continue;
|
||||
}
|
||||
switch (clauseBlock.type) {
|
||||
@@ -440,53 +460,53 @@ const CONTROLS_IF_MUTATOR_MIXIN = {
|
||||
const inputIf = this.getInput('IF' + i);
|
||||
const inputDo = this.getInput('DO' + i);
|
||||
clauseBlock.valueConnection_ =
|
||||
inputIf && inputIf.connection.targetConnection;
|
||||
inputIf && inputIf.connection!.targetConnection;
|
||||
clauseBlock.statementConnection_ =
|
||||
inputDo && inputDo.connection.targetConnection;
|
||||
inputDo && inputDo.connection!.targetConnection;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
case 'controls_if_else': {
|
||||
const inputDo = this.getInput('ELSE');
|
||||
clauseBlock.statementConnection_ =
|
||||
inputDo && inputDo.connection.targetConnection;
|
||||
inputDo && inputDo.connection!.targetConnection;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw TypeError('Unknown block type: ' + clauseBlock.type);
|
||||
}
|
||||
clauseBlock = clauseBlock.getNextBlock();
|
||||
clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Reconstructs the block with all child blocks attached.
|
||||
* @this {Block}
|
||||
*/
|
||||
rebuildShape_: function() {
|
||||
const valueConnections = [null];
|
||||
const statementConnections = [null];
|
||||
let elseStatementConnection = null;
|
||||
rebuildShape_: function (this: IfBlock) {
|
||||
const valueConnections: Array<Connection | null> = [null];
|
||||
const statementConnections: Array<Connection | null> = [null];
|
||||
let elseStatementConnection: Connection | null = null;
|
||||
|
||||
if (this.getInput('ELSE')) {
|
||||
elseStatementConnection =
|
||||
this.getInput('ELSE').connection.targetConnection;
|
||||
this.getInput('ELSE')!.connection!.targetConnection;
|
||||
}
|
||||
for (let i = 1; this.getInput('IF' + i); i++) {
|
||||
const inputIf = this.getInput('IF' + i);
|
||||
const inputDo = this.getInput('DO' + i);
|
||||
valueConnections.push(inputIf.connection.targetConnection);
|
||||
statementConnections.push(inputDo.connection.targetConnection);
|
||||
valueConnections.push(inputIf!.connection!.targetConnection);
|
||||
statementConnections.push(inputDo!.connection!.targetConnection);
|
||||
}
|
||||
this.updateShape_();
|
||||
this.reconnectChildBlocks_(
|
||||
valueConnections, statementConnections, elseStatementConnection);
|
||||
valueConnections,
|
||||
statementConnections,
|
||||
elseStatementConnection
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Modify this block to have the correct number of inputs.
|
||||
* @this {Block}
|
||||
* @private
|
||||
*/
|
||||
updateShape_: function() {
|
||||
updateShape_: function (this: IfBlock) {
|
||||
// Delete everything.
|
||||
if (this.getInput('ELSE')) {
|
||||
this.removeInput('ELSE');
|
||||
@@ -497,28 +517,34 @@ const CONTROLS_IF_MUTATOR_MIXIN = {
|
||||
}
|
||||
// Rebuild block.
|
||||
for (let i = 1; i <= this.elseifCount_; i++) {
|
||||
this.appendValueInput('IF' + i).setCheck('Boolean').appendField(
|
||||
Msg['CONTROLS_IF_MSG_ELSEIF']);
|
||||
this.appendValueInput('IF' + i)
|
||||
.setCheck('Boolean')
|
||||
.appendField(Msg['CONTROLS_IF_MSG_ELSEIF']);
|
||||
this.appendStatementInput('DO' + i).appendField(
|
||||
Msg['CONTROLS_IF_MSG_THEN']);
|
||||
Msg['CONTROLS_IF_MSG_THEN']
|
||||
);
|
||||
}
|
||||
if (this.elseCount_) {
|
||||
this.appendStatementInput('ELSE').appendField(
|
||||
Msg['CONTROLS_IF_MSG_ELSE']);
|
||||
Msg['CONTROLS_IF_MSG_ELSE']
|
||||
);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Reconnects child blocks.
|
||||
* @param {!Array<?RenderedConnection>} valueConnections List of
|
||||
* value connections for 'if' input.
|
||||
* @param {!Array<?RenderedConnection>} statementConnections List of
|
||||
* statement connections for 'do' input.
|
||||
* @param {?RenderedConnection} elseStatementConnection Statement
|
||||
* connection for else input.
|
||||
* @this {Block}
|
||||
*
|
||||
* @param valueConnections 1-based array of value connections for
|
||||
* 'if' input. Value at index [0] ignored.
|
||||
* @param statementConnections 1-based array of statement
|
||||
* connections for 'do' input. Value at index [0] ignored.
|
||||
* @param elseStatementConnection Statement connection for else input.
|
||||
*/
|
||||
reconnectChildBlocks_: function(
|
||||
valueConnections, statementConnections, elseStatementConnection) {
|
||||
reconnectChildBlocks_: function (
|
||||
this: IfBlock,
|
||||
valueConnections: Array<Connection | null>,
|
||||
statementConnections: Array<Connection | null>,
|
||||
elseStatementConnection: Connection | null
|
||||
) {
|
||||
for (let i = 1; i <= this.elseifCount_; i++) {
|
||||
valueConnections[i]?.reconnect(this, 'IF' + i);
|
||||
statementConnections[i]?.reconnect(this, 'DO' + i);
|
||||
@@ -528,55 +554,70 @@ const CONTROLS_IF_MUTATOR_MIXIN = {
|
||||
};
|
||||
|
||||
Extensions.registerMutator(
|
||||
'controls_if_mutator', CONTROLS_IF_MUTATOR_MIXIN, null,
|
||||
['controls_if_elseif', 'controls_if_else']);
|
||||
'controls_if_mutator',
|
||||
CONTROLS_IF_MUTATOR_MIXIN,
|
||||
null as unknown as undefined, // TODO(#6920)
|
||||
['controls_if_elseif', 'controls_if_else']
|
||||
);
|
||||
|
||||
/**
|
||||
* "controls_if" extension function. Adds mutator, shape updating methods, and
|
||||
* dynamic tooltip to "controls_if" blocks.
|
||||
* @this {Block}
|
||||
* "controls_if" extension function. Adds mutator, shape updating methods,
|
||||
* and dynamic tooltip to "controls_if" blocks.
|
||||
*/
|
||||
const CONTROLS_IF_TOOLTIP_EXTENSION = function() {
|
||||
this.setTooltip(function() {
|
||||
if (!this.elseifCount_ && !this.elseCount_) {
|
||||
return Msg['CONTROLS_IF_TOOLTIP_1'];
|
||||
} else if (!this.elseifCount_ && this.elseCount_) {
|
||||
return Msg['CONTROLS_IF_TOOLTIP_2'];
|
||||
} else if (this.elseifCount_ && !this.elseCount_) {
|
||||
return Msg['CONTROLS_IF_TOOLTIP_3'];
|
||||
} else if (this.elseifCount_ && this.elseCount_) {
|
||||
return Msg['CONTROLS_IF_TOOLTIP_4'];
|
||||
}
|
||||
return '';
|
||||
}.bind(this));
|
||||
const CONTROLS_IF_TOOLTIP_EXTENSION = function (this: IfBlock) {
|
||||
this.setTooltip(
|
||||
function (this: IfBlock) {
|
||||
if (!this.elseifCount_ && !this.elseCount_) {
|
||||
return Msg['CONTROLS_IF_TOOLTIP_1'];
|
||||
} else if (!this.elseifCount_ && this.elseCount_) {
|
||||
return Msg['CONTROLS_IF_TOOLTIP_2'];
|
||||
} else if (this.elseifCount_ && !this.elseCount_) {
|
||||
return Msg['CONTROLS_IF_TOOLTIP_3'];
|
||||
} else if (this.elseifCount_ && this.elseCount_) {
|
||||
return Msg['CONTROLS_IF_TOOLTIP_4'];
|
||||
}
|
||||
return '';
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
Extensions.register('controls_if_tooltip', CONTROLS_IF_TOOLTIP_EXTENSION);
|
||||
|
||||
/** Type of a block that has LOGIC_COMPARE_ONCHANGE_MIXIN */
|
||||
type CompareBlock = Block & CompareMixin;
|
||||
interface CompareMixin extends CompareMixinType {
|
||||
prevBlocks_?: Array<Block | null>;
|
||||
}
|
||||
type CompareMixinType = typeof LOGIC_COMPARE_ONCHANGE_MIXIN;
|
||||
|
||||
/**
|
||||
* Adds dynamic type validation for the left and right sides of a logic_compare
|
||||
* block.
|
||||
* @mixin
|
||||
* @augments Block
|
||||
* @readonly
|
||||
* Adds dynamic type validation for the left and right sides of a
|
||||
* logic_compare block.
|
||||
*/
|
||||
const LOGIC_COMPARE_ONCHANGE_MIXIN = {
|
||||
/**
|
||||
* Called whenever anything on the workspace changes.
|
||||
* Prevent mismatched types from being compared.
|
||||
* @param {!AbstractEvent} e Change event.
|
||||
* @this {Block}
|
||||
*
|
||||
* @param e Change event.
|
||||
*/
|
||||
onchange: function(e) {
|
||||
onchange: function (this: CompareBlock, e: AbstractEvent) {
|
||||
if (!this.prevBlocks_) {
|
||||
this.prevBlocks_ = [null, null];
|
||||
}
|
||||
|
||||
const blockA = this.getInputTargetBlock('A');
|
||||
const blockB = this.getInputTargetBlock('B');
|
||||
// Disconnect blocks that existed prior to this change if they don't match.
|
||||
if (blockA && blockB &&
|
||||
!this.workspace.connectionChecker.doTypeChecks(
|
||||
blockA.outputConnection, blockB.outputConnection)) {
|
||||
// Disconnect blocks that existed prior to this change if they don't
|
||||
// match.
|
||||
if (
|
||||
blockA &&
|
||||
blockB &&
|
||||
!this.workspace.connectionChecker.doTypeChecks(
|
||||
blockA.outputConnection!,
|
||||
blockB.outputConnection!
|
||||
)
|
||||
) {
|
||||
// Mismatch between two inputs. Revert the block connections,
|
||||
// bumping away the newly connected block(s).
|
||||
Events.setGroup(e.group);
|
||||
@@ -585,7 +626,7 @@ const LOGIC_COMPARE_ONCHANGE_MIXIN = {
|
||||
blockA.unplug();
|
||||
if (prevA && !prevA.isDisposed() && !prevA.isShadow()) {
|
||||
// The shadow block is automatically replaced during unplug().
|
||||
this.getInput('A').connection.connect(prevA.outputConnection);
|
||||
this.getInput('A')!.connection!.connect(prevA.outputConnection!);
|
||||
}
|
||||
}
|
||||
const prevB = this.prevBlocks_[1];
|
||||
@@ -593,7 +634,7 @@ const LOGIC_COMPARE_ONCHANGE_MIXIN = {
|
||||
blockB.unplug();
|
||||
if (prevB && !prevB.isDisposed() && !prevB.isShadow()) {
|
||||
// The shadow block is automatically replaced during unplug().
|
||||
this.getInput('B').connection.connect(prevB.outputConnection);
|
||||
this.getInput('B')!.connection!.connect(prevB.outputConnection!);
|
||||
}
|
||||
}
|
||||
this.bumpNeighbours();
|
||||
@@ -607,43 +648,47 @@ const LOGIC_COMPARE_ONCHANGE_MIXIN = {
|
||||
/**
|
||||
* "logic_compare" extension function. Adds type left and right side type
|
||||
* checking to "logic_compare" blocks.
|
||||
* @this {Block}
|
||||
* @readonly
|
||||
*/
|
||||
const LOGIC_COMPARE_EXTENSION = function() {
|
||||
const LOGIC_COMPARE_EXTENSION = function (this: CompareBlock) {
|
||||
// Add onchange handler to ensure types are compatible.
|
||||
this.mixin(LOGIC_COMPARE_ONCHANGE_MIXIN);
|
||||
};
|
||||
|
||||
Extensions.register('logic_compare', LOGIC_COMPARE_EXTENSION);
|
||||
|
||||
/** Type of a block that has LOGIC_TERNARY_ONCHANGE_MIXIN */
|
||||
type TernaryBlock = Block & TernaryMixin;
|
||||
interface TernaryMixin extends TernaryMixinType {}
|
||||
type TernaryMixinType = typeof LOGIC_TERNARY_ONCHANGE_MIXIN;
|
||||
|
||||
/**
|
||||
* Adds type coordination between inputs and output.
|
||||
* @mixin
|
||||
* @augments Block
|
||||
* @readonly
|
||||
*/
|
||||
const LOGIC_TERNARY_ONCHANGE_MIXIN = {
|
||||
prevParentConnection_: null,
|
||||
prevParentConnection_: null as Connection | null,
|
||||
|
||||
/**
|
||||
* Called whenever anything on the workspace changes.
|
||||
* Prevent mismatched types.
|
||||
* @param {!AbstractEvent} e Change event.
|
||||
* @this {Block}
|
||||
*/
|
||||
onchange: function(e) {
|
||||
onchange: function (this: TernaryBlock, e: AbstractEvent) {
|
||||
const blockA = this.getInputTargetBlock('THEN');
|
||||
const blockB = this.getInputTargetBlock('ELSE');
|
||||
const parentConnection = this.outputConnection.targetConnection;
|
||||
// Disconnect blocks that existed prior to this change if they don't match.
|
||||
const parentConnection = this.outputConnection!.targetConnection;
|
||||
// Disconnect blocks that existed prior to this change if they don't
|
||||
// match.
|
||||
if ((blockA || blockB) && parentConnection) {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const block = (i === 1) ? blockA : blockB;
|
||||
if (block &&
|
||||
!block.workspace.connectionChecker.doTypeChecks(
|
||||
block.outputConnection, parentConnection)) {
|
||||
// Ensure that any disconnections are grouped with the causing event.
|
||||
const block = i === 1 ? blockA : blockB;
|
||||
if (
|
||||
block &&
|
||||
!block.workspace.connectionChecker.doTypeChecks(
|
||||
block.outputConnection!,
|
||||
parentConnection
|
||||
)
|
||||
) {
|
||||
// Ensure that any disconnections are grouped with the causing
|
||||
// event.
|
||||
Events.setGroup(e.group);
|
||||
if (parentConnection === this.prevParentConnection_) {
|
||||
this.unplug();
|
||||
Reference in New Issue
Block a user