mirror of
https://github.com/google/blockly.git
synced 2025-12-16 06:10:12 +01:00
chore: use prettier instead of clang-format (#7014)
* chore: add and configure prettier * chore: remove clang-format * chore: remove clang-format config * chore: lint additional ts files * chore: fix lint errors in blocks * chore: add prettier-ignore where needed * chore: ignore js blocks when formatting * chore: fix playground html syntax * chore: fix yaml spacing from merge * chore: convert text blocks to use arrow functions * chore: format everything with prettier * chore: fix lint unused imports in blocks
This commit is contained in:
committed by
GitHub
parent
af991f5e1b
commit
88ff901a72
@@ -11,11 +11,12 @@
|
||||
import * as goog from '../closure/goog/goog.js';
|
||||
goog.declareModuleId('Blockly.libraryBlocks.colour');
|
||||
|
||||
import type {BlockDefinition} from '../core/blocks.js';
|
||||
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
|
||||
import {
|
||||
createBlockDefinitionsFromJsonArray,
|
||||
defineBlocks,
|
||||
} from '../core/common.js';
|
||||
import '../core/field_colour.js';
|
||||
|
||||
|
||||
/**
|
||||
* A dictionary of the block definitions provided by this module.
|
||||
*/
|
||||
@@ -52,7 +53,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
{
|
||||
'type': 'colour_rgb',
|
||||
'message0':
|
||||
'%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3',
|
||||
'%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3',
|
||||
'args0': [
|
||||
{
|
||||
'type': 'input_value',
|
||||
@@ -82,8 +83,9 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
// Block for blending two colours together.
|
||||
{
|
||||
'type': 'colour_blend',
|
||||
'message0': '%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} ' +
|
||||
'%1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3',
|
||||
'message0':
|
||||
'%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} ' +
|
||||
'%1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3',
|
||||
'args0': [
|
||||
{
|
||||
'type': 'input_value',
|
||||
|
||||
415
blocks/lists.ts
415
blocks/lists.ts
@@ -18,17 +18,17 @@ import {Align} from '../core/inputs/input.js';
|
||||
import type {Block} from '../core/block.js';
|
||||
import type {Connection} from '../core/connection.js';
|
||||
import type {BlockSvg} from '../core/block_svg.js';
|
||||
import type {BlockDefinition} from '../core/blocks.js';
|
||||
import {ConnectionType} from '../core/connection_type.js';
|
||||
import type {FieldDropdown} from '../core/field_dropdown.js';
|
||||
import {Msg} from '../core/msg.js';
|
||||
import {Mutator} from '../core/mutator.js';
|
||||
import type {Workspace} from '../core/workspace.js';
|
||||
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
|
||||
import {
|
||||
createBlockDefinitionsFromJsonArray,
|
||||
defineBlocks,
|
||||
} from '../core/common.js';
|
||||
import '../core/field_dropdown.js';
|
||||
import {ValueInput} from '../core/inputs/value_input.js';
|
||||
|
||||
|
||||
/**
|
||||
* A dictionary of the block definitions provided by this module.
|
||||
*/
|
||||
@@ -117,9 +117,8 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
/** Type of a 'lists_create_with' block. */
|
||||
type CreateWithBlock = Block&ListCreateWithMixin;
|
||||
type CreateWithBlock = Block & ListCreateWithMixin;
|
||||
interface ListCreateWithMixin extends ListCreateWithMixinType {
|
||||
itemCount_: number;
|
||||
}
|
||||
@@ -129,22 +128,22 @@ const LISTS_CREATE_WITH = {
|
||||
/**
|
||||
* Block for creating a list with any number of elements of any type.
|
||||
*/
|
||||
init: function(this: CreateWithBlock) {
|
||||
init: function (this: CreateWithBlock) {
|
||||
this.setHelpUrl(Msg['LISTS_CREATE_WITH_HELPURL']);
|
||||
this.setStyle('list_blocks');
|
||||
this.itemCount_ = 3;
|
||||
this.updateShape_();
|
||||
this.setOutput(true, 'Array');
|
||||
this.setMutator(new Mutator(
|
||||
['lists_create_with_item'],
|
||||
this as unknown as BlockSvg)); // BUG(#6905)
|
||||
this.setMutator(
|
||||
new Mutator(['lists_create_with_item'], this as unknown as BlockSvg)
|
||||
); // BUG(#6905)
|
||||
this.setTooltip(Msg['LISTS_CREATE_WITH_TOOLTIP']);
|
||||
},
|
||||
/**
|
||||
* Create XML to represent list inputs.
|
||||
* Backwards compatible serialization implementation.
|
||||
*/
|
||||
mutationToDom: function(this: CreateWithBlock): Element {
|
||||
mutationToDom: function (this: CreateWithBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
container.setAttribute('items', String(this.itemCount_));
|
||||
return container;
|
||||
@@ -155,7 +154,7 @@ const LISTS_CREATE_WITH = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: CreateWithBlock, xmlElement: Element) {
|
||||
domToMutation: function (this: CreateWithBlock, xmlElement: Element) {
|
||||
const items = xmlElement.getAttribute('items');
|
||||
if (!items) throw new TypeError('element did not have items');
|
||||
this.itemCount_ = parseInt(items, 10);
|
||||
@@ -166,7 +165,7 @@ const LISTS_CREATE_WITH = {
|
||||
*
|
||||
* @return The state of this block, ie the item count.
|
||||
*/
|
||||
saveExtraState: function(this: CreateWithBlock): {itemCount: number} {
|
||||
saveExtraState: function (this: CreateWithBlock): {itemCount: number} {
|
||||
return {
|
||||
'itemCount': this.itemCount_,
|
||||
};
|
||||
@@ -176,7 +175,7 @@ const LISTS_CREATE_WITH = {
|
||||
*
|
||||
* @param state The state to apply to this block, ie the item count.
|
||||
*/
|
||||
loadExtraState: function(this: CreateWithBlock, state: AnyDuringMigration) {
|
||||
loadExtraState: function (this: CreateWithBlock, state: AnyDuringMigration) {
|
||||
this.itemCount_ = state['itemCount'];
|
||||
this.updateShape_();
|
||||
},
|
||||
@@ -186,32 +185,37 @@ const LISTS_CREATE_WITH = {
|
||||
* @param workspace Mutator's workspace.
|
||||
* @return Root block in mutator.
|
||||
*/
|
||||
decompose: function(this: CreateWithBlock, workspace: Workspace):
|
||||
ContainerBlock {
|
||||
const containerBlock =
|
||||
workspace.newBlock('lists_create_with_container') as ContainerBlock;
|
||||
(containerBlock as BlockSvg).initSvg();
|
||||
let connection = containerBlock.getInput('STACK')!.connection;
|
||||
for (let i = 0; i < this.itemCount_; i++) {
|
||||
const itemBlock =
|
||||
workspace.newBlock('lists_create_with_item') as ItemBlock;
|
||||
(itemBlock as BlockSvg).initSvg();
|
||||
if (!itemBlock.previousConnection) {
|
||||
throw new Error('itemBlock has no previousConnection');
|
||||
}
|
||||
connection!.connect(itemBlock.previousConnection);
|
||||
connection = itemBlock.nextConnection;
|
||||
}
|
||||
return containerBlock;
|
||||
},
|
||||
decompose: function (
|
||||
this: CreateWithBlock,
|
||||
workspace: Workspace
|
||||
): ContainerBlock {
|
||||
const containerBlock = workspace.newBlock(
|
||||
'lists_create_with_container'
|
||||
) as ContainerBlock;
|
||||
(containerBlock as BlockSvg).initSvg();
|
||||
let connection = containerBlock.getInput('STACK')!.connection;
|
||||
for (let i = 0; i < this.itemCount_; i++) {
|
||||
const itemBlock = workspace.newBlock(
|
||||
'lists_create_with_item'
|
||||
) as ItemBlock;
|
||||
(itemBlock as BlockSvg).initSvg();
|
||||
if (!itemBlock.previousConnection) {
|
||||
throw new Error('itemBlock has no previousConnection');
|
||||
}
|
||||
connection!.connect(itemBlock.previousConnection);
|
||||
connection = itemBlock.nextConnection;
|
||||
}
|
||||
return containerBlock;
|
||||
},
|
||||
/**
|
||||
* Reconfigure this block based on the mutator dialog's components.
|
||||
*
|
||||
* @param containerBlock Root block in mutator.
|
||||
*/
|
||||
compose: function(this: CreateWithBlock, containerBlock: Block) {
|
||||
let itemBlock: ItemBlock|null =
|
||||
containerBlock.getInputTargetBlock('STACK') as ItemBlock;
|
||||
compose: function (this: CreateWithBlock, containerBlock: Block) {
|
||||
let itemBlock: ItemBlock | null = containerBlock.getInputTargetBlock(
|
||||
'STACK'
|
||||
) as ItemBlock;
|
||||
// Count number of inputs.
|
||||
const connections: Connection[] = [];
|
||||
while (itemBlock) {
|
||||
@@ -241,9 +245,10 @@ const LISTS_CREATE_WITH = {
|
||||
*
|
||||
* @param containerBlock Root block in mutator.
|
||||
*/
|
||||
saveConnections: function(this: CreateWithBlock, containerBlock: Block) {
|
||||
let itemBlock: ItemBlock|null =
|
||||
containerBlock.getInputTargetBlock('STACK') as ItemBlock;
|
||||
saveConnections: function (this: CreateWithBlock, containerBlock: Block) {
|
||||
let itemBlock: ItemBlock | null = containerBlock.getInputTargetBlock(
|
||||
'STACK'
|
||||
) as ItemBlock;
|
||||
let i = 0;
|
||||
while (itemBlock) {
|
||||
if (itemBlock.isInsertionMarker()) {
|
||||
@@ -251,8 +256,8 @@ const LISTS_CREATE_WITH = {
|
||||
continue;
|
||||
}
|
||||
const input = this.getInput('ADD' + i);
|
||||
itemBlock.valueConnection_ =
|
||||
input?.connection!.targetConnection as Connection;
|
||||
itemBlock.valueConnection_ = input?.connection!
|
||||
.targetConnection as Connection;
|
||||
itemBlock = itemBlock.getNextBlock() as ItemBlock | null;
|
||||
i++;
|
||||
}
|
||||
@@ -260,12 +265,13 @@ const LISTS_CREATE_WITH = {
|
||||
/**
|
||||
* Modify this block to have the correct number of inputs.
|
||||
*/
|
||||
updateShape_: function(this: CreateWithBlock) {
|
||||
updateShape_: function (this: CreateWithBlock) {
|
||||
if (this.itemCount_ && this.getInput('EMPTY')) {
|
||||
this.removeInput('EMPTY');
|
||||
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
|
||||
this.appendDummyInput('EMPTY').appendField(
|
||||
Msg['LISTS_CREATE_EMPTY_TITLE']);
|
||||
Msg['LISTS_CREATE_EMPTY_TITLE']
|
||||
);
|
||||
}
|
||||
// Add new inputs.
|
||||
for (let i = 0; i < this.itemCount_; i++) {
|
||||
@@ -285,7 +291,7 @@ const LISTS_CREATE_WITH = {
|
||||
blocks['lists_create_with'] = LISTS_CREATE_WITH;
|
||||
|
||||
/** Type for a 'lists_create_with_container' block. */
|
||||
type ContainerBlock = Block&ContainerMutator;
|
||||
type ContainerBlock = Block & ContainerMutator;
|
||||
interface ContainerMutator extends ContainerMutatorType {}
|
||||
type ContainerMutatorType = typeof LISTS_CREATE_WITH_CONTAINER;
|
||||
|
||||
@@ -293,10 +299,11 @@ const LISTS_CREATE_WITH_CONTAINER = {
|
||||
/**
|
||||
* Mutator block for list container.
|
||||
*/
|
||||
init: function(this: ContainerBlock) {
|
||||
init: function (this: ContainerBlock) {
|
||||
this.setStyle('list_blocks');
|
||||
this.appendDummyInput().appendField(
|
||||
Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD']);
|
||||
Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD']
|
||||
);
|
||||
this.appendStatementInput('STACK');
|
||||
this.setTooltip(Msg['LISTS_CREATE_WITH_CONTAINER_TOOLTIP']);
|
||||
this.contextMenu = false;
|
||||
@@ -305,7 +312,7 @@ const LISTS_CREATE_WITH_CONTAINER = {
|
||||
blocks['lists_create_with_container'] = LISTS_CREATE_WITH_CONTAINER;
|
||||
|
||||
/** Type for a 'lists_create_with_item' block. */
|
||||
type ItemBlock = Block&ItemMutator;
|
||||
type ItemBlock = Block & ItemMutator;
|
||||
interface ItemMutator extends ItemMutatorType {
|
||||
valueConnection_?: Connection;
|
||||
}
|
||||
@@ -315,7 +322,7 @@ const LISTS_CREATE_WITH_ITEM = {
|
||||
/**
|
||||
* Mutator block for adding items.
|
||||
*/
|
||||
init: function(this: ItemBlock) {
|
||||
init: function (this: ItemBlock) {
|
||||
this.setStyle('list_blocks');
|
||||
this.appendDummyInput().appendField(Msg['LISTS_CREATE_WITH_ITEM_TITLE']);
|
||||
this.setPreviousStatement(true);
|
||||
@@ -327,7 +334,7 @@ const LISTS_CREATE_WITH_ITEM = {
|
||||
blocks['lists_create_with_item'] = LISTS_CREATE_WITH_ITEM;
|
||||
|
||||
/** Type for a 'lists_indexOf' block. */
|
||||
type IndexOfBlock = Block&IndexOfMutator;
|
||||
type IndexOfBlock = Block & IndexOfMutator;
|
||||
interface IndexOfMutator extends IndexOfMutatorType {}
|
||||
type IndexOfMutatorType = typeof LISTS_INDEXOF;
|
||||
|
||||
@@ -335,7 +342,7 @@ const LISTS_INDEXOF = {
|
||||
/**
|
||||
* Block for finding an item in the list.
|
||||
*/
|
||||
init: function(this: IndexOfBlock) {
|
||||
init: function (this: IndexOfBlock) {
|
||||
const OPERATORS = [
|
||||
[Msg['LISTS_INDEX_OF_FIRST'], 'FIRST'],
|
||||
[Msg['LISTS_INDEX_OF_LAST'], 'LAST'],
|
||||
@@ -343,8 +350,9 @@ const LISTS_INDEXOF = {
|
||||
this.setHelpUrl(Msg['LISTS_INDEX_OF_HELPURL']);
|
||||
this.setStyle('list_blocks');
|
||||
this.setOutput(true, 'Number');
|
||||
this.appendValueInput('VALUE').setCheck('Array').appendField(
|
||||
Msg['LISTS_INDEX_OF_INPUT_IN_LIST']);
|
||||
this.appendValueInput('VALUE')
|
||||
.setCheck('Array')
|
||||
.appendField(Msg['LISTS_INDEX_OF_INPUT_IN_LIST']);
|
||||
const operatorsDropdown = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: OPERATORS,
|
||||
@@ -352,18 +360,18 @@ const LISTS_INDEXOF = {
|
||||
if (!operatorsDropdown) throw new Error('field_dropdown not found');
|
||||
this.appendValueInput('FIND').appendField(operatorsDropdown, 'END');
|
||||
this.setInputsInline(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
const thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
this.setTooltip(() => {
|
||||
return Msg['LISTS_INDEX_OF_TOOLTIP'].replace(
|
||||
'%1', thisBlock.workspace.options.oneBasedIndex ? '0' : '-1');
|
||||
'%1',
|
||||
this.workspace.options.oneBasedIndex ? '0' : '-1'
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
blocks['lists_indexOf'] = LISTS_INDEXOF;
|
||||
|
||||
/** Type for a 'lists_getIndex' block. */
|
||||
type GetIndexBlock = Block&GetIndexMutator;
|
||||
type GetIndexBlock = Block & GetIndexMutator;
|
||||
interface GetIndexMutator extends GetIndexMutatorType {
|
||||
WHERE_OPTIONS: Array<[string, string]>;
|
||||
}
|
||||
@@ -373,7 +381,7 @@ const LISTS_GETINDEX = {
|
||||
/**
|
||||
* Block for getting element at index.
|
||||
*/
|
||||
init: function(this: GetIndexBlock) {
|
||||
init: function (this: GetIndexBlock) {
|
||||
const MODE = [
|
||||
[Msg['LISTS_GET_INDEX_GET'], 'GET'],
|
||||
[Msg['LISTS_GET_INDEX_GET_REMOVE'], 'GET_REMOVE'],
|
||||
@@ -393,18 +401,19 @@ const LISTS_GETINDEX = {
|
||||
options: MODE,
|
||||
}) as FieldDropdown;
|
||||
modeMenu.setValidator(
|
||||
/** @param value The input value. */
|
||||
function(this: FieldDropdown, value: string) {
|
||||
const isStatement = (value === 'REMOVE');
|
||||
(this.getSourceBlock() as GetIndexBlock)
|
||||
.updateStatement_(isStatement);
|
||||
return undefined;
|
||||
});
|
||||
this.appendValueInput('VALUE').setCheck('Array').appendField(
|
||||
Msg['LISTS_GET_INDEX_INPUT_IN_LIST']);
|
||||
/** @param value The input value. */
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const isStatement = value === 'REMOVE';
|
||||
(this.getSourceBlock() as GetIndexBlock).updateStatement_(isStatement);
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
this.appendValueInput('VALUE')
|
||||
.setCheck('Array')
|
||||
.appendField(Msg['LISTS_GET_INDEX_INPUT_IN_LIST']);
|
||||
this.appendDummyInput()
|
||||
.appendField(modeMenu, 'MODE')
|
||||
.appendField('', 'SPACE');
|
||||
.appendField(modeMenu, 'MODE')
|
||||
.appendField('', 'SPACE');
|
||||
this.appendDummyInput('AT');
|
||||
if (Msg['LISTS_GET_INDEX_TAIL']) {
|
||||
this.appendDummyInput('TAIL').appendField(Msg['LISTS_GET_INDEX_TAIL']);
|
||||
@@ -412,11 +421,9 @@ const LISTS_GETINDEX = {
|
||||
this.setInputsInline(true);
|
||||
this.setOutput(true);
|
||||
this.updateAt_(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
const thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
const mode = thisBlock.getFieldValue('MODE');
|
||||
const where = thisBlock.getFieldValue('WHERE');
|
||||
this.setTooltip(() => {
|
||||
const mode = this.getFieldValue('MODE');
|
||||
const where = this.getFieldValue('WHERE');
|
||||
let tooltip = '';
|
||||
switch (mode + ' ' + where) {
|
||||
case 'GET FROM_START':
|
||||
@@ -460,12 +467,13 @@ const LISTS_GETINDEX = {
|
||||
break;
|
||||
}
|
||||
if (where === 'FROM_START' || where === 'FROM_END') {
|
||||
const msg = (where === 'FROM_START') ?
|
||||
Msg['LISTS_INDEX_FROM_START_TOOLTIP'] :
|
||||
Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
|
||||
tooltip += ' ' +
|
||||
msg.replace(
|
||||
'%1', thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
|
||||
const msg =
|
||||
where === 'FROM_START'
|
||||
? Msg['LISTS_INDEX_FROM_START_TOOLTIP']
|
||||
: Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
|
||||
tooltip +=
|
||||
' ' +
|
||||
msg.replace('%1', this.workspace.options.oneBasedIndex ? '#1' : '#0');
|
||||
}
|
||||
return tooltip;
|
||||
});
|
||||
@@ -476,7 +484,7 @@ const LISTS_GETINDEX = {
|
||||
*
|
||||
* @return XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: GetIndexBlock): Element {
|
||||
mutationToDom: function (this: GetIndexBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
const isStatement = !this.outputConnection;
|
||||
container.setAttribute('statement', String(isStatement));
|
||||
@@ -489,12 +497,12 @@ const LISTS_GETINDEX = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: GetIndexBlock, xmlElement: Element) {
|
||||
domToMutation: function (this: GetIndexBlock, xmlElement: Element) {
|
||||
// Note: Until January 2013 this block did not have mutations,
|
||||
// so 'statement' defaults to false and 'at' defaults to true.
|
||||
const isStatement = (xmlElement.getAttribute('statement') === 'true');
|
||||
const isStatement = xmlElement.getAttribute('statement') === 'true';
|
||||
this.updateStatement_(isStatement);
|
||||
const isAt = (xmlElement.getAttribute('at') !== 'false');
|
||||
const isAt = xmlElement.getAttribute('at') !== 'false';
|
||||
this.updateAt_(isAt);
|
||||
},
|
||||
/**
|
||||
@@ -503,9 +511,9 @@ const LISTS_GETINDEX = {
|
||||
*
|
||||
* @return The state of this block, ie whether it's a statement.
|
||||
*/
|
||||
saveExtraState: function(this: GetIndexBlock): ({
|
||||
isStatement: boolean
|
||||
} | null) {
|
||||
saveExtraState: function (this: GetIndexBlock): {
|
||||
isStatement: boolean;
|
||||
} | null {
|
||||
if (!this.outputConnection) {
|
||||
return {
|
||||
isStatement: true,
|
||||
@@ -520,7 +528,7 @@ const LISTS_GETINDEX = {
|
||||
* @param state The state to apply to this block, ie whether it's a
|
||||
* statement.
|
||||
*/
|
||||
loadExtraState: function(this: GetIndexBlock, state: AnyDuringMigration) {
|
||||
loadExtraState: function (this: GetIndexBlock, state: AnyDuringMigration) {
|
||||
if (state['isStatement']) {
|
||||
this.updateStatement_(true);
|
||||
} else if (typeof state === 'string') {
|
||||
@@ -535,7 +543,7 @@ const LISTS_GETINDEX = {
|
||||
* @param newStatement True if the block should be a statement.
|
||||
* False if the block should be a value.
|
||||
*/
|
||||
updateStatement_: function(this: GetIndexBlock, newStatement: boolean) {
|
||||
updateStatement_: function (this: GetIndexBlock, newStatement: boolean) {
|
||||
const oldStatement = !this.outputConnection;
|
||||
if (newStatement !== oldStatement) {
|
||||
// TODO(#6920): The .unplug only has one parameter.
|
||||
@@ -556,7 +564,7 @@ const LISTS_GETINDEX = {
|
||||
*
|
||||
* @param isAt True if the input should exist.
|
||||
*/
|
||||
updateAt_: function(this: GetIndexBlock, isAt: boolean) {
|
||||
updateAt_: function (this: GetIndexBlock, isAt: boolean) {
|
||||
// Destroy old 'AT' and 'ORDINAL' inputs.
|
||||
this.removeInput('AT');
|
||||
this.removeInput('ORDINAL', true);
|
||||
@@ -565,7 +573,8 @@ const LISTS_GETINDEX = {
|
||||
this.appendValueInput('AT').setCheck('Number');
|
||||
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
|
||||
this.appendDummyInput('ORDINAL').appendField(
|
||||
Msg['ORDINAL_NUMBER_SUFFIX']);
|
||||
Msg['ORDINAL_NUMBER_SUFFIX']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.appendDummyInput('AT');
|
||||
@@ -575,24 +584,25 @@ const LISTS_GETINDEX = {
|
||||
options: this.WHERE_OPTIONS,
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @return Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function(this: FieldDropdown, value: string) {
|
||||
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as GetIndexBlock;
|
||||
block.updateAt_(newAt);
|
||||
// This menu has been destroyed and replaced. Update the
|
||||
// replacement.
|
||||
block.setFieldValue(value, 'WHERE');
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @return Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as GetIndexBlock;
|
||||
block.updateAt_(newAt);
|
||||
// This menu has been destroyed and replaced. Update the
|
||||
// replacement.
|
||||
block.setFieldValue(value, 'WHERE');
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
this.getInput('AT')!.appendField(menu, 'WHERE');
|
||||
if (Msg['LISTS_GET_INDEX_TAIL']) {
|
||||
this.moveInputBefore('TAIL', null);
|
||||
@@ -602,7 +612,7 @@ const LISTS_GETINDEX = {
|
||||
blocks['lists_getIndex'] = LISTS_GETINDEX;
|
||||
|
||||
/** Type for a 'lists_setIndex' block. */
|
||||
type SetIndexBlock = Block&SetIndexMutator;
|
||||
type SetIndexBlock = Block & SetIndexMutator;
|
||||
interface SetIndexMutator extends SetIndexMutatorType {
|
||||
WHERE_OPTIONS: Array<[string, string]>;
|
||||
}
|
||||
@@ -612,7 +622,7 @@ const LISTS_SETINDEX = {
|
||||
/**
|
||||
* Block for setting the element at index.
|
||||
*/
|
||||
init: function(this: SetIndexBlock) {
|
||||
init: function (this: SetIndexBlock) {
|
||||
const MODE = [
|
||||
[Msg['LISTS_SET_INDEX_SET'], 'SET'],
|
||||
[Msg['LISTS_SET_INDEX_INSERT'], 'INSERT'],
|
||||
@@ -626,15 +636,16 @@ const LISTS_SETINDEX = {
|
||||
];
|
||||
this.setHelpUrl(Msg['LISTS_SET_INDEX_HELPURL']);
|
||||
this.setStyle('list_blocks');
|
||||
this.appendValueInput('LIST').setCheck('Array').appendField(
|
||||
Msg['LISTS_SET_INDEX_INPUT_IN_LIST']);
|
||||
this.appendValueInput('LIST')
|
||||
.setCheck('Array')
|
||||
.appendField(Msg['LISTS_SET_INDEX_INPUT_IN_LIST']);
|
||||
const operationDropdown = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: MODE,
|
||||
}) as FieldDropdown;
|
||||
this.appendDummyInput()
|
||||
.appendField(operationDropdown, 'MODE')
|
||||
.appendField('', 'SPACE');
|
||||
.appendField(operationDropdown, 'MODE')
|
||||
.appendField('', 'SPACE');
|
||||
this.appendDummyInput('AT');
|
||||
this.appendValueInput('TO').appendField(Msg['LISTS_SET_INDEX_INPUT_TO']);
|
||||
this.setInputsInline(true);
|
||||
@@ -642,11 +653,9 @@ const LISTS_SETINDEX = {
|
||||
this.setNextStatement(true);
|
||||
this.setTooltip(Msg['LISTS_SET_INDEX_TOOLTIP']);
|
||||
this.updateAt_(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
const thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
const mode = thisBlock.getFieldValue('MODE');
|
||||
const where = thisBlock.getFieldValue('WHERE');
|
||||
this.setTooltip(() => {
|
||||
const mode = this.getFieldValue('MODE');
|
||||
const where = this.getFieldValue('WHERE');
|
||||
let tooltip = '';
|
||||
switch (mode + ' ' + where) {
|
||||
case 'SET FROM_START':
|
||||
@@ -677,9 +686,12 @@ const LISTS_SETINDEX = {
|
||||
break;
|
||||
}
|
||||
if (where === 'FROM_START' || where === 'FROM_END') {
|
||||
tooltip += ' ' +
|
||||
Msg['LISTS_INDEX_FROM_START_TOOLTIP'].replace(
|
||||
'%1', thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
|
||||
tooltip +=
|
||||
' ' +
|
||||
Msg['LISTS_INDEX_FROM_START_TOOLTIP'].replace(
|
||||
'%1',
|
||||
this.workspace.options.oneBasedIndex ? '#1' : '#0'
|
||||
);
|
||||
}
|
||||
return tooltip;
|
||||
});
|
||||
@@ -689,7 +701,7 @@ const LISTS_SETINDEX = {
|
||||
*
|
||||
* @return XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: SetIndexBlock): Element {
|
||||
mutationToDom: function (this: SetIndexBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
const isAt = this.getInput('AT') instanceof ValueInput;
|
||||
container.setAttribute('at', String(isAt));
|
||||
@@ -700,10 +712,10 @@ const LISTS_SETINDEX = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: SetIndexBlock, xmlElement: Element) {
|
||||
domToMutation: function (this: SetIndexBlock, xmlElement: Element) {
|
||||
// Note: Until January 2013 this block did not have mutations,
|
||||
// so 'at' defaults to true.
|
||||
const isAt = (xmlElement.getAttribute('at') !== 'false');
|
||||
const isAt = xmlElement.getAttribute('at') !== 'false';
|
||||
this.updateAt_(isAt);
|
||||
},
|
||||
|
||||
@@ -715,7 +727,7 @@ const LISTS_SETINDEX = {
|
||||
*
|
||||
* @return The state of this block.
|
||||
*/
|
||||
saveExtraState: function(this: SetIndexBlock): null {
|
||||
saveExtraState: function (this: SetIndexBlock): null {
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -724,14 +736,14 @@ const LISTS_SETINDEX = {
|
||||
* No extra state is needed or expected as it is already encoded in the
|
||||
* dropdown values.
|
||||
*/
|
||||
loadExtraState: function(this: SetIndexBlock) {},
|
||||
loadExtraState: function (this: SetIndexBlock) {},
|
||||
|
||||
/**
|
||||
* Create or delete an input for the numeric index.
|
||||
*
|
||||
* @param isAt True if the input should exist.
|
||||
*/
|
||||
updateAt_: function(this: SetIndexBlock, isAt: boolean) {
|
||||
updateAt_: function (this: SetIndexBlock, isAt: boolean) {
|
||||
// Destroy old 'AT' and 'ORDINAL' input.
|
||||
this.removeInput('AT');
|
||||
this.removeInput('ORDINAL', true);
|
||||
@@ -740,7 +752,8 @@ const LISTS_SETINDEX = {
|
||||
this.appendValueInput('AT').setCheck('Number');
|
||||
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
|
||||
this.appendDummyInput('ORDINAL').appendField(
|
||||
Msg['ORDINAL_NUMBER_SUFFIX']);
|
||||
Msg['ORDINAL_NUMBER_SUFFIX']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.appendDummyInput('AT');
|
||||
@@ -750,24 +763,25 @@ const LISTS_SETINDEX = {
|
||||
options: this.WHERE_OPTIONS,
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @return Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function(this: FieldDropdown, value: string) {
|
||||
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as SetIndexBlock;
|
||||
block.updateAt_(newAt);
|
||||
// This menu has been destroyed and replaced. Update the
|
||||
// replacement.
|
||||
block.setFieldValue(value, 'WHERE');
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @return Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as SetIndexBlock;
|
||||
block.updateAt_(newAt);
|
||||
// This menu has been destroyed and replaced. Update the
|
||||
// replacement.
|
||||
block.setFieldValue(value, 'WHERE');
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
this.moveInputBefore('AT', 'TO');
|
||||
if (this.getInput('ORDINAL')) {
|
||||
this.moveInputBefore('ORDINAL', 'TO');
|
||||
@@ -779,7 +793,7 @@ const LISTS_SETINDEX = {
|
||||
blocks['lists_setIndex'] = LISTS_SETINDEX;
|
||||
|
||||
/** Type for a 'lists_getSublist' block. */
|
||||
type GetSublistBlock = Block&GetSublistMutator;
|
||||
type GetSublistBlock = Block & GetSublistMutator;
|
||||
interface GetSublistMutator extends GetSublistMutatorType {
|
||||
WHERE_OPTIONS_1: Array<[string, string]>;
|
||||
WHERE_OPTIONS_2: Array<[string, string]>;
|
||||
@@ -790,7 +804,7 @@ const LISTS_GETSUBLIST = {
|
||||
/**
|
||||
* Block for getting sublist.
|
||||
*/
|
||||
init: function(this: GetSublistBlock) {
|
||||
init: function (this: GetSublistBlock) {
|
||||
this['WHERE_OPTIONS_1'] = [
|
||||
[Msg['LISTS_GET_SUBLIST_START_FROM_START'], 'FROM_START'],
|
||||
[Msg['LISTS_GET_SUBLIST_START_FROM_END'], 'FROM_END'],
|
||||
@@ -803,8 +817,9 @@ const LISTS_GETSUBLIST = {
|
||||
];
|
||||
this.setHelpUrl(Msg['LISTS_GET_SUBLIST_HELPURL']);
|
||||
this.setStyle('list_blocks');
|
||||
this.appendValueInput('LIST').setCheck('Array').appendField(
|
||||
Msg['LISTS_GET_SUBLIST_INPUT_IN_LIST']);
|
||||
this.appendValueInput('LIST')
|
||||
.setCheck('Array')
|
||||
.appendField(Msg['LISTS_GET_SUBLIST_INPUT_IN_LIST']);
|
||||
this.appendDummyInput('AT1');
|
||||
this.appendDummyInput('AT2');
|
||||
if (Msg['LISTS_GET_SUBLIST_TAIL']) {
|
||||
@@ -821,7 +836,7 @@ const LISTS_GETSUBLIST = {
|
||||
*
|
||||
* @return XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: GetSublistBlock): Element {
|
||||
mutationToDom: function (this: GetSublistBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
const isAt1 = this.getInput('AT1') instanceof ValueInput;
|
||||
container.setAttribute('at1', String(isAt1));
|
||||
@@ -834,9 +849,9 @@ const LISTS_GETSUBLIST = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: GetSublistBlock, xmlElement: Element) {
|
||||
const isAt1 = (xmlElement.getAttribute('at1') === 'true');
|
||||
const isAt2 = (xmlElement.getAttribute('at2') === 'true');
|
||||
domToMutation: function (this: GetSublistBlock, xmlElement: Element) {
|
||||
const isAt1 = xmlElement.getAttribute('at1') === 'true';
|
||||
const isAt2 = xmlElement.getAttribute('at2') === 'true';
|
||||
this.updateAt_(1, isAt1);
|
||||
this.updateAt_(2, isAt2);
|
||||
},
|
||||
@@ -849,7 +864,7 @@ const LISTS_GETSUBLIST = {
|
||||
*
|
||||
* @return The state of this block.
|
||||
*/
|
||||
saveExtraState: function(this: GetSublistBlock): null {
|
||||
saveExtraState: function (this: GetSublistBlock): null {
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -858,7 +873,7 @@ const LISTS_GETSUBLIST = {
|
||||
* No extra state is needed or expected as it is already encoded in the
|
||||
* dropdown values.
|
||||
*/
|
||||
loadExtraState: function(this: GetSublistBlock) {},
|
||||
loadExtraState: function (this: GetSublistBlock) {},
|
||||
|
||||
/**
|
||||
* Create or delete an input for a numeric index.
|
||||
@@ -867,7 +882,7 @@ const LISTS_GETSUBLIST = {
|
||||
* @param n Specify first or second input (1 or 2).
|
||||
* @param isAt True if the input should exist.
|
||||
*/
|
||||
updateAt_: function(this: GetSublistBlock, n: 1|2, isAt: boolean) {
|
||||
updateAt_: function (this: GetSublistBlock, n: 1 | 2, isAt: boolean) {
|
||||
// Create or delete an input for the numeric index.
|
||||
// Destroy old 'AT' and 'ORDINAL' inputs.
|
||||
this.removeInput('AT' + n);
|
||||
@@ -876,37 +891,37 @@ const LISTS_GETSUBLIST = {
|
||||
if (isAt) {
|
||||
this.appendValueInput('AT' + n).setCheck('Number');
|
||||
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
|
||||
this.appendDummyInput('ORDINAL' + n)
|
||||
.appendField(Msg['ORDINAL_NUMBER_SUFFIX']);
|
||||
this.appendDummyInput('ORDINAL' + n).appendField(
|
||||
Msg['ORDINAL_NUMBER_SUFFIX']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.appendDummyInput('AT' + n);
|
||||
}
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
// TODO(#6920): Rewrite this so that clang-format doesn't make such an
|
||||
// awful unreadable mess of it.
|
||||
options: this
|
||||
[('WHERE_OPTIONS_' + n) as ('WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2')],
|
||||
options:
|
||||
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @return Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function(this: FieldDropdown, value: string) {
|
||||
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as GetSublistBlock;
|
||||
block.updateAt_(n, newAt);
|
||||
// This menu has been destroyed and replaced.
|
||||
// Update the replacement.
|
||||
block.setFieldValue(value, 'WHERE' + n);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @return Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function (this: FieldDropdown, value: string) {
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as GetSublistBlock;
|
||||
block.updateAt_(n, newAt);
|
||||
// This menu has been destroyed and replaced.
|
||||
// Update the replacement.
|
||||
block.setFieldValue(value, 'WHERE' + n);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.getInput('AT' + n)!.appendField(menu, 'WHERE' + n);
|
||||
if (n === 1) {
|
||||
this.moveInputBefore('AT1', 'AT2');
|
||||
@@ -921,13 +936,13 @@ const LISTS_GETSUBLIST = {
|
||||
};
|
||||
blocks['lists_getSublist'] = LISTS_GETSUBLIST;
|
||||
|
||||
type SortBlock = Block|typeof blocks['lists_sort'];
|
||||
type SortBlock = Block | (typeof blocks)['lists_sort'];
|
||||
|
||||
blocks['lists_sort'] = {
|
||||
/**
|
||||
* Block for sorting a list.
|
||||
*/
|
||||
init: function(this: SortBlock) {
|
||||
init: function (this: SortBlock) {
|
||||
this.jsonInit({
|
||||
'message0': '%{BKY_LISTS_SORT_TITLE}',
|
||||
'args0': [
|
||||
@@ -962,15 +977,13 @@ blocks['lists_sort'] = {
|
||||
},
|
||||
};
|
||||
|
||||
type SplitBlock = Block|typeof blocks['lists_split'];
|
||||
type SplitBlock = Block | (typeof blocks)['lists_split'];
|
||||
|
||||
blocks['lists_split'] = {
|
||||
/**
|
||||
* Block for splitting text into a list, or joining a list into text.
|
||||
*/
|
||||
init: function(this: SplitBlock) {
|
||||
// Assign 'this' to a variable for use in the closures below.
|
||||
const thisBlock = this;
|
||||
init: function (this: SplitBlock) {
|
||||
const dropdown = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: [
|
||||
@@ -979,19 +992,21 @@ blocks['lists_split'] = {
|
||||
],
|
||||
});
|
||||
if (!dropdown) throw new Error('field_dropdown not found');
|
||||
dropdown.setValidator(function(newMode) {
|
||||
thisBlock.updateType_(newMode);
|
||||
dropdown.setValidator((newMode) => {
|
||||
this.updateType_(newMode);
|
||||
});
|
||||
this.setHelpUrl(Msg['LISTS_SPLIT_HELPURL']);
|
||||
this.setStyle('list_blocks');
|
||||
this.appendValueInput('INPUT').setCheck('String').appendField(
|
||||
dropdown, 'MODE');
|
||||
this.appendValueInput('DELIM').setCheck('String').appendField(
|
||||
Msg['LISTS_SPLIT_WITH_DELIMITER']);
|
||||
this.appendValueInput('INPUT')
|
||||
.setCheck('String')
|
||||
.appendField(dropdown, 'MODE');
|
||||
this.appendValueInput('DELIM')
|
||||
.setCheck('String')
|
||||
.appendField(Msg['LISTS_SPLIT_WITH_DELIMITER']);
|
||||
this.setInputsInline(true);
|
||||
this.setOutput(true, 'Array');
|
||||
this.setTooltip(function() {
|
||||
const mode = thisBlock.getFieldValue('MODE');
|
||||
this.setTooltip(() => {
|
||||
const mode = this.getFieldValue('MODE');
|
||||
if (mode === 'SPLIT') {
|
||||
return Msg['LISTS_SPLIT_TOOLTIP_SPLIT'];
|
||||
} else if (mode === 'JOIN') {
|
||||
@@ -1005,7 +1020,7 @@ blocks['lists_split'] = {
|
||||
*
|
||||
* @param newMode Either 'SPLIT' or 'JOIN'.
|
||||
*/
|
||||
updateType_: function(this: SplitBlock, newMode: string) {
|
||||
updateType_: function (this: SplitBlock, newMode: string) {
|
||||
const mode = this.getFieldValue('MODE');
|
||||
if (mode !== newMode) {
|
||||
const inputConnection = this.getInput('INPUT')!.connection;
|
||||
@@ -1034,7 +1049,7 @@ blocks['lists_split'] = {
|
||||
*
|
||||
* @return XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: SplitBlock): Element {
|
||||
mutationToDom: function (this: SplitBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
container.setAttribute('mode', this.getFieldValue('MODE'));
|
||||
return container;
|
||||
@@ -1044,7 +1059,7 @@ blocks['lists_split'] = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: SplitBlock, xmlElement: Element) {
|
||||
domToMutation: function (this: SplitBlock, xmlElement: Element) {
|
||||
this.updateType_(xmlElement.getAttribute('mode'));
|
||||
},
|
||||
|
||||
@@ -1056,7 +1071,7 @@ blocks['lists_split'] = {
|
||||
*
|
||||
* @return The state of this block.
|
||||
*/
|
||||
saveExtraState: function(this: SplitBlock): null {
|
||||
saveExtraState: function (this: SplitBlock): null {
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -1065,7 +1080,7 @@ blocks['lists_split'] = {
|
||||
* No extra state is needed or expected as it is already encoded in the
|
||||
* dropdown values.
|
||||
*/
|
||||
loadExtraState: function(this: SplitBlock) {},
|
||||
loadExtraState: function (this: SplitBlock) {},
|
||||
};
|
||||
|
||||
// Register provided blocks.
|
||||
|
||||
189
blocks/loops.ts
189
blocks/loops.ts
@@ -14,13 +14,19 @@ goog.declareModuleId('Blockly.libraryBlocks.loops');
|
||||
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
|
||||
import type {Block} from '../core/block.js';
|
||||
import * as ContextMenu from '../core/contextmenu.js';
|
||||
import type {ContextMenuOption, LegacyContextMenuOption} from '../core/contextmenu_registry.js';
|
||||
import type {
|
||||
ContextMenuOption,
|
||||
LegacyContextMenuOption,
|
||||
} from '../core/contextmenu_registry.js';
|
||||
import * as Events from '../core/events/events.js';
|
||||
import * as Extensions from '../core/extensions.js';
|
||||
import * as Variables from '../core/variables.js';
|
||||
import * as xmlUtils from '../core/utils/xml.js';
|
||||
import {Msg} from '../core/msg.js';
|
||||
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
|
||||
import {
|
||||
createBlockDefinitionsFromJsonArray,
|
||||
defineBlocks,
|
||||
} from '../core/common.js';
|
||||
import '../core/field_dropdown.js';
|
||||
import '../core/field_label.js';
|
||||
import '../core/field_number.js';
|
||||
@@ -29,7 +35,6 @@ import '../core/warning.js';
|
||||
import {FieldVariable} from '../core/field_variable.js';
|
||||
import {WorkspaceSvg} from '../core/workspace_svg.js';
|
||||
|
||||
|
||||
/**
|
||||
* A dictionary of the block definitions provided by this module.
|
||||
*/
|
||||
@@ -38,16 +43,20 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
{
|
||||
'type': 'controls_repeat_ext',
|
||||
'message0': '%{BKY_CONTROLS_REPEAT_TITLE}',
|
||||
'args0': [{
|
||||
'type': 'input_value',
|
||||
'name': 'TIMES',
|
||||
'check': 'Number',
|
||||
}],
|
||||
'args0': [
|
||||
{
|
||||
'type': 'input_value',
|
||||
'name': 'TIMES',
|
||||
'check': 'Number',
|
||||
},
|
||||
],
|
||||
'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
|
||||
'args1': [{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
}],
|
||||
'args1': [
|
||||
{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
},
|
||||
],
|
||||
'previousStatement': null,
|
||||
'nextStatement': null,
|
||||
'style': 'loop_blocks',
|
||||
@@ -59,18 +68,22 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
{
|
||||
'type': 'controls_repeat',
|
||||
'message0': '%{BKY_CONTROLS_REPEAT_TITLE}',
|
||||
'args0': [{
|
||||
'type': 'field_number',
|
||||
'name': 'TIMES',
|
||||
'value': 10,
|
||||
'min': 0,
|
||||
'precision': 1,
|
||||
}],
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_number',
|
||||
'name': 'TIMES',
|
||||
'value': 10,
|
||||
'min': 0,
|
||||
'precision': 1,
|
||||
},
|
||||
],
|
||||
'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
|
||||
'args1': [{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
}],
|
||||
'args1': [
|
||||
{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
},
|
||||
],
|
||||
'previousStatement': null,
|
||||
'nextStatement': null,
|
||||
'style': 'loop_blocks',
|
||||
@@ -97,10 +110,12 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
},
|
||||
],
|
||||
'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
|
||||
'args1': [{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
}],
|
||||
'args1': [
|
||||
{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
},
|
||||
],
|
||||
'previousStatement': null,
|
||||
'nextStatement': null,
|
||||
'style': 'loop_blocks',
|
||||
@@ -137,19 +152,18 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
},
|
||||
],
|
||||
'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
|
||||
'args1': [{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
}],
|
||||
'args1': [
|
||||
{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
},
|
||||
],
|
||||
'inputsInline': true,
|
||||
'previousStatement': null,
|
||||
'nextStatement': null,
|
||||
'style': 'loop_blocks',
|
||||
'helpUrl': '%{BKY_CONTROLS_FOR_HELPURL}',
|
||||
'extensions': [
|
||||
'contextMenu_newGetVariableBlock',
|
||||
'controls_for_tooltip',
|
||||
],
|
||||
'extensions': ['contextMenu_newGetVariableBlock', 'controls_for_tooltip'],
|
||||
},
|
||||
// Block for 'for each' loop.
|
||||
{
|
||||
@@ -168,10 +182,12 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
},
|
||||
],
|
||||
'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
|
||||
'args1': [{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
}],
|
||||
'args1': [
|
||||
{
|
||||
'type': 'input_statement',
|
||||
'name': 'DO',
|
||||
},
|
||||
],
|
||||
'previousStatement': null,
|
||||
'nextStatement': null,
|
||||
'style': 'loop_blocks',
|
||||
@@ -185,22 +201,21 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
{
|
||||
'type': 'controls_flow_statements',
|
||||
'message0': '%1',
|
||||
'args0': [{
|
||||
'type': 'field_dropdown',
|
||||
'name': 'FLOW',
|
||||
'options': [
|
||||
['%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}', 'BREAK'],
|
||||
['%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}', 'CONTINUE'],
|
||||
],
|
||||
}],
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_dropdown',
|
||||
'name': 'FLOW',
|
||||
'options': [
|
||||
['%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}', 'BREAK'],
|
||||
['%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}', 'CONTINUE'],
|
||||
],
|
||||
},
|
||||
],
|
||||
'previousStatement': null,
|
||||
'style': 'loop_blocks',
|
||||
'helpUrl': '%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}',
|
||||
'suppressPrefixSuffix': true,
|
||||
'extensions': [
|
||||
'controls_flow_tooltip',
|
||||
'controls_flow_in_loop_check',
|
||||
],
|
||||
'extensions': ['controls_flow_tooltip', 'controls_flow_in_loop_check'],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -215,8 +230,9 @@ const WHILE_UNTIL_TOOLTIPS = {
|
||||
};
|
||||
|
||||
Extensions.register(
|
||||
'controls_whileUntil_tooltip',
|
||||
Extensions.buildTooltipForDropdown('MODE', WHILE_UNTIL_TOOLTIPS));
|
||||
'controls_whileUntil_tooltip',
|
||||
Extensions.buildTooltipForDropdown('MODE', WHILE_UNTIL_TOOLTIPS)
|
||||
);
|
||||
|
||||
/**
|
||||
* Tooltips for the 'controls_flow_statements' block, keyed by FLOW value.
|
||||
@@ -229,14 +245,15 @@ const BREAK_CONTINUE_TOOLTIPS = {
|
||||
};
|
||||
|
||||
Extensions.register(
|
||||
'controls_flow_tooltip',
|
||||
Extensions.buildTooltipForDropdown('FLOW', BREAK_CONTINUE_TOOLTIPS));
|
||||
'controls_flow_tooltip',
|
||||
Extensions.buildTooltipForDropdown('FLOW', BREAK_CONTINUE_TOOLTIPS)
|
||||
);
|
||||
|
||||
/** Type of a block that has CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN */
|
||||
type CustomContextMenuBlock = Block&CustomContextMenuMixin;
|
||||
type CustomContextMenuBlock = Block & CustomContextMenuMixin;
|
||||
interface CustomContextMenuMixin extends CustomContextMenuMixinType {}
|
||||
type CustomContextMenuMixinType =
|
||||
typeof CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN;
|
||||
typeof CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN;
|
||||
|
||||
/**
|
||||
* Mixin to add a context menu item to create a 'variables_get' block.
|
||||
@@ -249,9 +266,10 @@ const CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = {
|
||||
*
|
||||
* @param options List of menu options to add to.
|
||||
*/
|
||||
customContextMenu: function(
|
||||
this: CustomContextMenuBlock,
|
||||
options: Array<ContextMenuOption|LegacyContextMenuOption>) {
|
||||
customContextMenu: function (
|
||||
this: CustomContextMenuBlock,
|
||||
options: Array<ContextMenuOption | LegacyContextMenuOption>
|
||||
) {
|
||||
if (this.isInFlyout) {
|
||||
return;
|
||||
}
|
||||
@@ -267,24 +285,26 @@ const CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = {
|
||||
options.push({
|
||||
enabled: true,
|
||||
text: Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName),
|
||||
callback: ContextMenu.callbackFactory(this, xmlBlock)
|
||||
callback: ContextMenu.callbackFactory(this, xmlBlock),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Extensions.registerMixin(
|
||||
'contextMenu_newGetVariableBlock',
|
||||
CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN);
|
||||
'contextMenu_newGetVariableBlock',
|
||||
CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN
|
||||
);
|
||||
|
||||
Extensions.register(
|
||||
'controls_for_tooltip',
|
||||
Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR'));
|
||||
'controls_for_tooltip',
|
||||
Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR')
|
||||
);
|
||||
|
||||
Extensions.register(
|
||||
'controls_forEach_tooltip',
|
||||
Extensions.buildTooltipWithFieldText(
|
||||
'%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR'));
|
||||
'controls_forEach_tooltip',
|
||||
Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR')
|
||||
);
|
||||
|
||||
/**
|
||||
* List of block types that are loops and thus do not need warnings.
|
||||
@@ -310,7 +330,7 @@ export const loopTypes: Set<string> = new Set([
|
||||
]);
|
||||
|
||||
/** Type of a block that has CONTROL_FLOW_IN_LOOP_CHECK_MIXIN */
|
||||
type ControlFlowInLoopBlock = Block&ControlFlowInLoopMixin;
|
||||
type ControlFlowInLoopBlock = Block & ControlFlowInLoopMixin;
|
||||
interface ControlFlowInLoopMixin extends ControlFlowInLoopMixinType {}
|
||||
type ControlFlowInLoopMixinType = typeof CONTROL_FLOW_IN_LOOP_CHECK_MIXIN;
|
||||
|
||||
@@ -324,23 +344,23 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
|
||||
*
|
||||
* @returns The nearest surrounding loop, or null if none.
|
||||
*/
|
||||
getSurroundLoop: function(this: ControlFlowInLoopBlock): Block |
|
||||
null {
|
||||
let block: Block|null = this;
|
||||
do {
|
||||
if (loopTypes.has(block.type)) {
|
||||
return block;
|
||||
}
|
||||
block = block.getSurroundParent();
|
||||
} while (block);
|
||||
return null;
|
||||
},
|
||||
getSurroundLoop: function (this: ControlFlowInLoopBlock): Block | null {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
let block: Block | null = this;
|
||||
do {
|
||||
if (loopTypes.has(block.type)) {
|
||||
return block;
|
||||
}
|
||||
block = block.getSurroundParent();
|
||||
} while (block);
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called whenever anything on the workspace changes.
|
||||
* Add warning if this flow block is not nested inside a loop.
|
||||
*/
|
||||
onchange: function(this: ControlFlowInLoopBlock, e: AbstractEvent) {
|
||||
onchange: function (this: ControlFlowInLoopBlock, e: AbstractEvent) {
|
||||
const ws = this.workspace as WorkspaceSvg;
|
||||
// Don't change state if:
|
||||
// * It's at the start of a drag.
|
||||
@@ -350,7 +370,8 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
|
||||
}
|
||||
const enabled = !!this.getSurroundLoop();
|
||||
this.setWarningText(
|
||||
enabled ? null : Msg['CONTROLS_FLOW_STATEMENTS_WARNING']);
|
||||
enabled ? null : Msg['CONTROLS_FLOW_STATEMENTS_WARNING']
|
||||
);
|
||||
if (!this.isInFlyout) {
|
||||
const group = Events.getGroup();
|
||||
// Makes it so the move and the disable event get undone together.
|
||||
@@ -362,7 +383,9 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
|
||||
};
|
||||
|
||||
Extensions.registerMixin(
|
||||
'controls_flow_in_loop_check', CONTROL_FLOW_IN_LOOP_CHECK_MIXIN);
|
||||
'controls_flow_in_loop_check',
|
||||
CONTROL_FLOW_IN_LOOP_CHECK_MIXIN
|
||||
);
|
||||
|
||||
// Register provided blocks.
|
||||
defineBlocks(blocks);
|
||||
|
||||
@@ -12,18 +12,18 @@ 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 {
|
||||
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.
|
||||
*/
|
||||
@@ -32,11 +32,13 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
{
|
||||
'type': 'math_number',
|
||||
'message0': '%1',
|
||||
'args0': [{
|
||||
'type': 'field_number',
|
||||
'name': 'NUM',
|
||||
'value': 0,
|
||||
}],
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_number',
|
||||
'name': 'NUM',
|
||||
'value': 0,
|
||||
},
|
||||
],
|
||||
'output': 'Number',
|
||||
'helpUrl': '%{BKY_MATH_NUMBER_HELPURL}',
|
||||
'style': 'math_blocks',
|
||||
@@ -428,13 +430,13 @@ const TOOLTIPS_BY_OP = {
|
||||
};
|
||||
|
||||
Extensions.register(
|
||||
'math_op_tooltip',
|
||||
Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP));
|
||||
'math_op_tooltip',
|
||||
Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP)
|
||||
);
|
||||
|
||||
/** Type of a block that has IS_DIVISBLEBY_MUTATOR_MIXIN */
|
||||
type DivisiblebyBlock = Block&DivisiblebyMixin;
|
||||
type DivisiblebyBlock = Block & DivisiblebyMixin;
|
||||
interface DivisiblebyMixin extends DivisiblebyMixinType {}
|
||||
;
|
||||
type DivisiblebyMixinType = typeof IS_DIVISIBLEBY_MUTATOR_MIXIN;
|
||||
|
||||
/**
|
||||
@@ -452,9 +454,9 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @returns XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: DivisiblebyBlock): Element {
|
||||
mutationToDom: function (this: DivisiblebyBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
const divisorInput = (this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY');
|
||||
const divisorInput = this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY';
|
||||
container.setAttribute('divisor_input', String(divisorInput));
|
||||
return container;
|
||||
},
|
||||
@@ -464,8 +466,8 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: DivisiblebyBlock, xmlElement: Element) {
|
||||
const divisorInput = (xmlElement.getAttribute('divisor_input') === 'true');
|
||||
domToMutation: function (this: DivisiblebyBlock, xmlElement: Element) {
|
||||
const divisorInput = xmlElement.getAttribute('divisor_input') === 'true';
|
||||
this.updateShape_(divisorInput);
|
||||
},
|
||||
|
||||
@@ -479,7 +481,7 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param divisorInput True if this block has a divisor input.
|
||||
*/
|
||||
updateShape_: function(this: DivisiblebyBlock, divisorInput: boolean) {
|
||||
updateShape_: function (this: DivisiblebyBlock, divisorInput: boolean) {
|
||||
// Add or remove a Value Input.
|
||||
const inputExists = this.getInput('DIVISOR');
|
||||
if (divisorInput) {
|
||||
@@ -497,29 +499,32 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
|
||||
* can update the block shape (add/remove divisor input) based on whether
|
||||
* property is "divisible by".
|
||||
*/
|
||||
const IS_DIVISIBLE_MUTATOR_EXTENSION = function(this: DivisiblebyBlock) {
|
||||
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.
|
||||
});
|
||||
/** @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(
|
||||
'math_is_divisibleby_mutator', IS_DIVISIBLEBY_MUTATOR_MIXIN,
|
||||
IS_DIVISIBLE_MUTATOR_EXTENSION);
|
||||
'math_is_divisibleby_mutator',
|
||||
IS_DIVISIBLEBY_MUTATOR_MIXIN,
|
||||
IS_DIVISIBLE_MUTATOR_EXTENSION
|
||||
);
|
||||
|
||||
// Update the tooltip of 'math_change' block to reference the variable.
|
||||
Extensions.register(
|
||||
'math_change_tooltip',
|
||||
Extensions.buildTooltipWithFieldText('%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR'));
|
||||
'math_change_tooltip',
|
||||
Extensions.buildTooltipWithFieldText('%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR')
|
||||
);
|
||||
|
||||
/** Type of a block that has LIST_MODES_MUTATOR_MIXIN */
|
||||
type ListModesBlock = Block&ListModesMixin;
|
||||
type ListModesBlock = Block & ListModesMixin;
|
||||
interface ListModesMixin extends ListModesMixinType {}
|
||||
;
|
||||
type ListModesMixinType = typeof LIST_MODES_MUTATOR_MIXIN;
|
||||
|
||||
/**
|
||||
@@ -532,7 +537,7 @@ const LIST_MODES_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param newOp Either 'MODE' or some op than returns a number.
|
||||
*/
|
||||
updateType_: function(this: ListModesBlock, newOp: string) {
|
||||
updateType_: function (this: ListModesBlock, newOp: string) {
|
||||
if (newOp === 'MODE') {
|
||||
this.outputConnection!.setCheck('Array');
|
||||
} else {
|
||||
@@ -545,7 +550,7 @@ const LIST_MODES_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @returns XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: ListModesBlock): Element {
|
||||
mutationToDom: function (this: ListModesBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
container.setAttribute('op', this.getFieldValue('OP'));
|
||||
return container;
|
||||
@@ -556,7 +561,7 @@ const LIST_MODES_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: ListModesBlock, xmlElement: Element) {
|
||||
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);
|
||||
@@ -572,17 +577,20 @@ const LIST_MODES_MUTATOR_MIXIN = {
|
||||
* Extension to 'math_on_list' blocks that allows support of
|
||||
* modes operation (outputs a list of numbers).
|
||||
*/
|
||||
const LIST_MODES_MUTATOR_EXTENSION = function(this: ListModesBlock) {
|
||||
this.getField(
|
||||
'OP')!.setValidator(function(this: ListModesBlock, newOp: string) {
|
||||
this.updateType_(newOp);
|
||||
return undefined;
|
||||
}.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(
|
||||
'math_modes_of_list_mutator', LIST_MODES_MUTATOR_MIXIN,
|
||||
LIST_MODES_MUTATOR_EXTENSION);
|
||||
'math_modes_of_list_mutator',
|
||||
LIST_MODES_MUTATOR_MIXIN,
|
||||
LIST_MODES_MUTATOR_EXTENSION
|
||||
);
|
||||
|
||||
// Register provided blocks.
|
||||
defineBlocks(blocks);
|
||||
|
||||
366
blocks/text.ts
366
blocks/text.ts
@@ -18,18 +18,19 @@ import {Align} from '../core/inputs/input.js';
|
||||
import type {Block} from '../core/block.js';
|
||||
import type {BlockSvg} from '../core/block_svg.js';
|
||||
import {Connection} from '../core/connection.js';
|
||||
import {ConnectionType} from '../core/connection_type.js';
|
||||
import {FieldImage} from '../core/field_image.js';
|
||||
import {FieldDropdown} from '../core/field_dropdown.js';
|
||||
import {FieldTextInput} from '../core/field_textinput.js';
|
||||
import {Msg} from '../core/msg.js';
|
||||
import {Mutator} from '../core/mutator.js';
|
||||
import type {Workspace} from '../core/workspace.js';
|
||||
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
|
||||
import {
|
||||
createBlockDefinitionsFromJsonArray,
|
||||
defineBlocks,
|
||||
} from '../core/common.js';
|
||||
import '../core/field_multilineinput.js';
|
||||
import '../core/field_variable.js';
|
||||
import { ValueInput } from '../core/inputs/value_input.js';
|
||||
|
||||
import {ValueInput} from '../core/inputs/value_input.js';
|
||||
|
||||
/**
|
||||
* A dictionary of the block definitions provided by this module.
|
||||
@@ -39,19 +40,18 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
{
|
||||
'type': 'text',
|
||||
'message0': '%1',
|
||||
'args0': [{
|
||||
'type': 'field_input',
|
||||
'name': 'TEXT',
|
||||
'text': '',
|
||||
}],
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_input',
|
||||
'name': 'TEXT',
|
||||
'text': '',
|
||||
},
|
||||
],
|
||||
'output': 'String',
|
||||
'style': 'text_blocks',
|
||||
'helpUrl': '%{BKY_TEXT_TEXT_HELPURL}',
|
||||
'tooltip': '%{BKY_TEXT_TEXT_TOOLTIP}',
|
||||
'extensions': [
|
||||
'text_quotes',
|
||||
'parent_tooltip_when_inline',
|
||||
],
|
||||
'extensions': ['text_quotes', 'parent_tooltip_when_inline'],
|
||||
},
|
||||
{
|
||||
'type': 'text_multiline',
|
||||
@@ -60,15 +60,15 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
{
|
||||
'type': 'field_image',
|
||||
'src':
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpP' +
|
||||
'U2iAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAdhgAAHYYBXaITgQAAABh0RVh0' +
|
||||
'U29mdHdhcmUAcGFpbnQubmV0IDQuMS42/U4J6AAAAP1JREFUOE+Vks0KQUEYhjm' +
|
||||
'RIja4ABtZ2dm5A3t3Ia6AUm7CylYuQRaUhZSlLZJiQbFAyRnPN33y01HOW08z88' +
|
||||
'73zpwzM4F3GWOCruvGIE4/rLaV+Nq1hVGMBqzhqlxgCys4wJA65xnogMHsQ5luj' +
|
||||
'nYHTejBBCK2mE4abjCgMGhNxHgDFWjDSG07kdfVa2pZMf4ZyMAdWmpZMfYOsLiD' +
|
||||
'MYMjlMB+K613QISRhTnITnsYg5yUd0DETmEoMlkFOeIT/A58iyK5E18BuTBfgYX' +
|
||||
'fwNJv4P9/oEBerLylOnRhygmGdPpTTBZAPkde61lbQe4moWUvYUZYLfUNftIY4z' +
|
||||
'wA5X2Z9AYnQrEAAAAASUVORK5CYII=',
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpP' +
|
||||
'U2iAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAdhgAAHYYBXaITgQAAABh0RVh0' +
|
||||
'U29mdHdhcmUAcGFpbnQubmV0IDQuMS42/U4J6AAAAP1JREFUOE+Vks0KQUEYhjm' +
|
||||
'RIja4ABtZ2dm5A3t3Ia6AUm7CylYuQRaUhZSlLZJiQbFAyRnPN33y01HOW08z88' +
|
||||
'73zpwzM4F3GWOCruvGIE4/rLaV+Nq1hVGMBqzhqlxgCys4wJA65xnogMHsQ5luj' +
|
||||
'nYHTejBBCK2mE4abjCgMGhNxHgDFWjDSG07kdfVa2pZMf4ZyMAdWmpZMfYOsLiD' +
|
||||
'MYMjlMB+K613QISRhTnITnsYg5yUd0DETmEoMlkFOeIT/A58iyK5E18BuTBfgYX' +
|
||||
'fwNJv4P9/oEBerLylOnRhygmGdPpTTBZAPkde61lbQe4moWUvYUZYLfUNftIY4z' +
|
||||
'wA5X2Z9AYnQrEAAAAASUVORK5CYII=',
|
||||
'width': 12,
|
||||
'height': 17,
|
||||
'alt': '\u00B6',
|
||||
@@ -83,9 +83,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
'style': 'text_blocks',
|
||||
'helpUrl': '%{BKY_TEXT_TEXT_HELPURL}',
|
||||
'tooltip': '%{BKY_TEXT_TEXT_TOOLTIP}',
|
||||
'extensions': [
|
||||
'parent_tooltip_when_inline',
|
||||
],
|
||||
'extensions': ['parent_tooltip_when_inline'],
|
||||
},
|
||||
{
|
||||
'type': 'text_join',
|
||||
@@ -95,7 +93,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
'helpUrl': '%{BKY_TEXT_JOIN_HELPURL}',
|
||||
'tooltip': '%{BKY_TEXT_JOIN_TOOLTIP}',
|
||||
'mutator': 'text_join_mutator',
|
||||
|
||||
},
|
||||
{
|
||||
'type': 'text_create_join_container',
|
||||
@@ -139,9 +136,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
'previousStatement': null,
|
||||
'nextStatement': null,
|
||||
'style': 'text_blocks',
|
||||
'extensions': [
|
||||
'text_append_tooltip',
|
||||
],
|
||||
'extensions': ['text_append_tooltip'],
|
||||
},
|
||||
{
|
||||
'type': 'text_length',
|
||||
@@ -186,14 +181,8 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
'type': 'field_dropdown',
|
||||
'name': 'END',
|
||||
'options': [
|
||||
[
|
||||
'%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}',
|
||||
'FIRST',
|
||||
],
|
||||
[
|
||||
'%{BKY_TEXT_INDEXOF_OPERATOR_LAST}',
|
||||
'LAST',
|
||||
],
|
||||
['%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}', 'FIRST'],
|
||||
['%{BKY_TEXT_INDEXOF_OPERATOR_LAST}', 'LAST'],
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -206,13 +195,11 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
'style': 'text_blocks',
|
||||
'helpUrl': '%{BKY_TEXT_INDEXOF_HELPURL}',
|
||||
'inputsInline': true,
|
||||
'extensions': [
|
||||
'text_indexOf_tooltip',
|
||||
],
|
||||
'extensions': ['text_indexOf_tooltip'],
|
||||
},
|
||||
{
|
||||
'type': 'text_charAt',
|
||||
'message0': '%{BKY_TEXT_CHARAT_TITLE}', // "in text %1 %2"
|
||||
'message0': '%{BKY_TEXT_CHARAT_TITLE}', // "in text %1 %2"
|
||||
'args0': [
|
||||
{
|
||||
'type': 'input_value',
|
||||
@@ -240,7 +227,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
]);
|
||||
|
||||
/** Type of a 'text_get_substring' block. */
|
||||
type GetSubstringBlock = Block&GetSubstringMixin;
|
||||
type GetSubstringBlock = Block & GetSubstringMixin;
|
||||
interface GetSubstringMixin extends GetSubstringType {
|
||||
WHERE_OPTIONS_1: Array<[string, string]>;
|
||||
WHERE_OPTIONS_2: Array<[string, string]>;
|
||||
@@ -251,7 +238,7 @@ const GET_SUBSTRING_BLOCK = {
|
||||
/**
|
||||
* Block for getting substring.
|
||||
*/
|
||||
init: function(this: GetSubstringBlock) {
|
||||
init: function (this: GetSubstringBlock) {
|
||||
this['WHERE_OPTIONS_1'] = [
|
||||
[Msg['TEXT_GET_SUBSTRING_START_FROM_START'], 'FROM_START'],
|
||||
[Msg['TEXT_GET_SUBSTRING_START_FROM_END'], 'FROM_END'],
|
||||
@@ -264,8 +251,9 @@ const GET_SUBSTRING_BLOCK = {
|
||||
];
|
||||
this.setHelpUrl(Msg['TEXT_GET_SUBSTRING_HELPURL']);
|
||||
this.setStyle('text_blocks');
|
||||
this.appendValueInput('STRING').setCheck('String').appendField(
|
||||
Msg['TEXT_GET_SUBSTRING_INPUT_IN_TEXT']);
|
||||
this.appendValueInput('STRING')
|
||||
.setCheck('String')
|
||||
.appendField(Msg['TEXT_GET_SUBSTRING_INPUT_IN_TEXT']);
|
||||
this.appendDummyInput('AT1');
|
||||
this.appendDummyInput('AT2');
|
||||
if (Msg['TEXT_GET_SUBSTRING_TAIL']) {
|
||||
@@ -283,7 +271,7 @@ const GET_SUBSTRING_BLOCK = {
|
||||
*
|
||||
* @returns XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: GetSubstringBlock): Element {
|
||||
mutationToDom: function (this: GetSubstringBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
const isAt1 = this.getInput('AT1') instanceof ValueInput;
|
||||
container.setAttribute('at1', `${isAt1}`);
|
||||
@@ -297,9 +285,9 @@ const GET_SUBSTRING_BLOCK = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: GetSubstringBlock, xmlElement: Element) {
|
||||
const isAt1 = (xmlElement.getAttribute('at1') === 'true');
|
||||
const isAt2 = (xmlElement.getAttribute('at2') === 'true');
|
||||
domToMutation: function (this: GetSubstringBlock, xmlElement: Element) {
|
||||
const isAt1 = xmlElement.getAttribute('at1') === 'true';
|
||||
const isAt2 = xmlElement.getAttribute('at2') === 'true';
|
||||
this.updateAt_(1, isAt1);
|
||||
this.updateAt_(2, isAt2);
|
||||
},
|
||||
@@ -316,7 +304,7 @@ const GET_SUBSTRING_BLOCK = {
|
||||
* @param n Which input to modify (either 1 or 2).
|
||||
* @param isAt True if the input includes a value connection, false otherwise.
|
||||
*/
|
||||
updateAt_: function(this: GetSubstringBlock, n: 1|2, isAt: boolean) {
|
||||
updateAt_: function (this: GetSubstringBlock, n: 1 | 2, isAt: boolean) {
|
||||
// Create or delete an input for the numeric index.
|
||||
// Destroy old 'AT' and 'ORDINAL' inputs.
|
||||
this.removeInput('AT' + n);
|
||||
@@ -325,8 +313,9 @@ const GET_SUBSTRING_BLOCK = {
|
||||
if (isAt) {
|
||||
this.appendValueInput('AT' + n).setCheck('Number');
|
||||
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
|
||||
this.appendDummyInput('ORDINAL' + n)
|
||||
.appendField(Msg['ORDINAL_NUMBER_SUFFIX']);
|
||||
this.appendDummyInput('ORDINAL' + n).appendField(
|
||||
Msg['ORDINAL_NUMBER_SUFFIX']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.appendDummyInput('AT' + n);
|
||||
@@ -339,27 +328,28 @@ const GET_SUBSTRING_BLOCK = {
|
||||
const menu = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options:
|
||||
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
|
||||
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
|
||||
}) as FieldDropdown;
|
||||
menu.setValidator(
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @return Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function(this: FieldDropdown, value: any): null|undefined {
|
||||
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as GetSubstringBlock;
|
||||
block.updateAt_(n, newAt);
|
||||
// This menu has been destroyed and replaced.
|
||||
// Update the replacement.
|
||||
block.setFieldValue(value, 'WHERE' + n);
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
/**
|
||||
* @param value The input value.
|
||||
* @return Null if the field has been replaced; otherwise undefined.
|
||||
*/
|
||||
function (this: FieldDropdown, value: any): null | undefined {
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
// The 'isAt' variable is available due to this function being a
|
||||
// closure.
|
||||
if (newAt !== isAt) {
|
||||
const block = this.getSourceBlock() as GetSubstringBlock;
|
||||
block.updateAt_(n, newAt);
|
||||
// This menu has been destroyed and replaced.
|
||||
// Update the replacement.
|
||||
block.setFieldValue(value, 'WHERE' + n);
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
|
||||
this.getInput('AT' + n)!.appendField(menu, 'WHERE' + n);
|
||||
if (n === 1) {
|
||||
@@ -377,7 +367,7 @@ blocks['text_changeCase'] = {
|
||||
/**
|
||||
* Block for changing capitalization.
|
||||
*/
|
||||
init: function(this: Block) {
|
||||
init: function (this: Block) {
|
||||
const OPERATORS = [
|
||||
[Msg['TEXT_CHANGECASE_OPERATOR_UPPERCASE'], 'UPPERCASE'],
|
||||
[Msg['TEXT_CHANGECASE_OPERATOR_LOWERCASE'], 'LOWERCASE'],
|
||||
@@ -385,12 +375,15 @@ blocks['text_changeCase'] = {
|
||||
];
|
||||
this.setHelpUrl(Msg['TEXT_CHANGECASE_HELPURL']);
|
||||
this.setStyle('text_blocks');
|
||||
this.appendValueInput('TEXT').setCheck('String').appendField(
|
||||
this.appendValueInput('TEXT')
|
||||
.setCheck('String')
|
||||
.appendField(
|
||||
fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: OPERATORS,
|
||||
}) as FieldDropdown,
|
||||
'CASE');
|
||||
'CASE'
|
||||
);
|
||||
this.setOutput(true, 'String');
|
||||
this.setTooltip(Msg['TEXT_CHANGECASE_TOOLTIP']);
|
||||
},
|
||||
@@ -400,7 +393,7 @@ blocks['text_trim'] = {
|
||||
/**
|
||||
* Block for trimming spaces.
|
||||
*/
|
||||
init: function(this: Block) {
|
||||
init: function (this: Block) {
|
||||
const OPERATORS = [
|
||||
[Msg['TEXT_TRIM_OPERATOR_BOTH'], 'BOTH'],
|
||||
[Msg['TEXT_TRIM_OPERATOR_LEFT'], 'LEFT'],
|
||||
@@ -408,12 +401,15 @@ blocks['text_trim'] = {
|
||||
];
|
||||
this.setHelpUrl(Msg['TEXT_TRIM_HELPURL']);
|
||||
this.setStyle('text_blocks');
|
||||
this.appendValueInput('TEXT').setCheck('String').appendField(
|
||||
this.appendValueInput('TEXT')
|
||||
.setCheck('String')
|
||||
.appendField(
|
||||
fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: OPERATORS,
|
||||
}) as FieldDropdown,
|
||||
'MODE');
|
||||
'MODE'
|
||||
);
|
||||
this.setOutput(true, 'String');
|
||||
this.setTooltip(Msg['TEXT_TRIM_TOOLTIP']);
|
||||
},
|
||||
@@ -423,7 +419,7 @@ blocks['text_print'] = {
|
||||
/**
|
||||
* Block for print statement.
|
||||
*/
|
||||
init: function(this: Block) {
|
||||
init: function (this: Block) {
|
||||
this.jsonInit({
|
||||
'message0': Msg['TEXT_PRINT_TITLE'],
|
||||
'args0': [
|
||||
@@ -441,7 +437,7 @@ blocks['text_print'] = {
|
||||
},
|
||||
};
|
||||
|
||||
type PromptCommonBlock = Block&PromptCommonMixin;
|
||||
type PromptCommonBlock = Block & PromptCommonMixin;
|
||||
interface PromptCommonMixin extends PromptCommonType {}
|
||||
type PromptCommonType = typeof PROMPT_COMMON;
|
||||
|
||||
@@ -455,7 +451,7 @@ const PROMPT_COMMON = {
|
||||
*
|
||||
* @param newOp The new output type. Should be either 'TEXT' or 'NUMBER'.
|
||||
*/
|
||||
updateType_: function(this: PromptCommonBlock, newOp: string) {
|
||||
updateType_: function (this: PromptCommonBlock, newOp: string) {
|
||||
this.outputConnection!.setCheck(newOp === 'NUMBER' ? 'Number' : 'String');
|
||||
},
|
||||
/**
|
||||
@@ -464,7 +460,7 @@ const PROMPT_COMMON = {
|
||||
*
|
||||
* @returns XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: PromptCommonBlock): Element {
|
||||
mutationToDom: function (this: PromptCommonBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
container.setAttribute('type', this.getFieldValue('TYPE'));
|
||||
return container;
|
||||
@@ -475,7 +471,7 @@ const PROMPT_COMMON = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: PromptCommonBlock, xmlElement: Element) {
|
||||
domToMutation: function (this: PromptCommonBlock, xmlElement: Element) {
|
||||
this.updateType_(xmlElement.getAttribute('type')!);
|
||||
},
|
||||
};
|
||||
@@ -485,29 +481,27 @@ blocks['text_prompt_ext'] = {
|
||||
/**
|
||||
* Block for prompt function (external message).
|
||||
*/
|
||||
init: function(this: PromptCommonBlock) {
|
||||
init: function (this: PromptCommonBlock) {
|
||||
const TYPES = [
|
||||
[Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'],
|
||||
[Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER'],
|
||||
];
|
||||
this.setHelpUrl(Msg['TEXT_PROMPT_HELPURL']);
|
||||
this.setStyle('text_blocks');
|
||||
// Assign 'this' to a variable for use in the closures below.
|
||||
const thisBlock = this;
|
||||
const dropdown = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: TYPES,
|
||||
}) as FieldDropdown;
|
||||
dropdown.setValidator(function(this: FieldDropdown, newOp: string) {
|
||||
thisBlock.updateType_(newOp);
|
||||
return undefined; // FieldValidators can't be void. Use option as-is.
|
||||
dropdown.setValidator((newOp: string) => {
|
||||
this.updateType_(newOp);
|
||||
return undefined; // FieldValidators can't be void. Use option as-is.
|
||||
});
|
||||
this.appendValueInput('TEXT').appendField(dropdown, 'TYPE');
|
||||
this.setOutput(true, 'String');
|
||||
this.setTooltip(function() {
|
||||
return (thisBlock.getFieldValue('TYPE') === 'TEXT') ?
|
||||
Msg['TEXT_PROMPT_TOOLTIP_TEXT'] :
|
||||
Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
|
||||
this.setTooltip(() => {
|
||||
return this.getFieldValue('TYPE') === 'TEXT'
|
||||
? Msg['TEXT_PROMPT_TOOLTIP_TEXT']
|
||||
: Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
|
||||
});
|
||||
},
|
||||
|
||||
@@ -517,7 +511,7 @@ blocks['text_prompt_ext'] = {
|
||||
// XML hooks are kept for backwards compatibility.
|
||||
};
|
||||
|
||||
type PromptBlock = Block&PromptCommonMixin&QuoteImageMixin;
|
||||
type PromptBlock = Block & PromptCommonMixin & QuoteImageMixin;
|
||||
|
||||
const TEXT_PROMPT_BLOCK = {
|
||||
...PROMPT_COMMON,
|
||||
@@ -525,40 +519,39 @@ const TEXT_PROMPT_BLOCK = {
|
||||
* Block for prompt function (internal message).
|
||||
* The 'text_prompt_ext' block is preferred as it is more flexible.
|
||||
*/
|
||||
init: function(this: PromptBlock) {
|
||||
init: function (this: PromptBlock) {
|
||||
this.mixin(QUOTE_IMAGE_MIXIN);
|
||||
const TYPES = [
|
||||
[Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'],
|
||||
[Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER'],
|
||||
];
|
||||
|
||||
// Assign 'this' to a variable for use in the closures below.
|
||||
const thisBlock = this;
|
||||
this.setHelpUrl(Msg['TEXT_PROMPT_HELPURL']);
|
||||
this.setStyle('text_blocks');
|
||||
const dropdown = fieldRegistry.fromJson({
|
||||
type: 'field_dropdown',
|
||||
options: TYPES,
|
||||
}) as FieldDropdown;
|
||||
dropdown.setValidator(function(this: FieldDropdown, newOp: string) {
|
||||
thisBlock.updateType_(newOp);
|
||||
return undefined; // FieldValidators can't be void. Use option as-is.
|
||||
dropdown.setValidator((newOp: string) => {
|
||||
this.updateType_(newOp);
|
||||
return undefined; // FieldValidators can't be void. Use option as-is.
|
||||
});
|
||||
this.appendDummyInput()
|
||||
.appendField(dropdown, 'TYPE')
|
||||
.appendField(this.newQuote_(true))
|
||||
.appendField(
|
||||
fieldRegistry.fromJson({
|
||||
type: 'field_input',
|
||||
text: '',
|
||||
}) as FieldTextInput,
|
||||
'TEXT')
|
||||
.appendField(this.newQuote_(false));
|
||||
.appendField(dropdown, 'TYPE')
|
||||
.appendField(this.newQuote_(true))
|
||||
.appendField(
|
||||
fieldRegistry.fromJson({
|
||||
type: 'field_input',
|
||||
text: '',
|
||||
}) as FieldTextInput,
|
||||
'TEXT'
|
||||
)
|
||||
.appendField(this.newQuote_(false));
|
||||
this.setOutput(true, 'String');
|
||||
this.setTooltip(function() {
|
||||
return (thisBlock.getFieldValue('TYPE') === 'TEXT') ?
|
||||
Msg['TEXT_PROMPT_TOOLTIP_TEXT'] :
|
||||
Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
|
||||
this.setTooltip(() => {
|
||||
return this.getFieldValue('TYPE') === 'TEXT'
|
||||
? Msg['TEXT_PROMPT_TOOLTIP_TEXT']
|
||||
: Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -569,7 +562,7 @@ blocks['text_count'] = {
|
||||
/**
|
||||
* Block for counting how many times one string appears within another string.
|
||||
*/
|
||||
init: function(this: Block) {
|
||||
init: function (this: Block) {
|
||||
this.jsonInit({
|
||||
'message0': Msg['TEXT_COUNT_MESSAGE0'],
|
||||
'args0': [
|
||||
@@ -597,7 +590,7 @@ blocks['text_replace'] = {
|
||||
/**
|
||||
* Block for replacing one string with another in the text.
|
||||
*/
|
||||
init: function(this: Block) {
|
||||
init: function (this: Block) {
|
||||
this.jsonInit({
|
||||
'message0': Msg['TEXT_REPLACE_MESSAGE0'],
|
||||
'args0': [
|
||||
@@ -630,7 +623,7 @@ blocks['text_reverse'] = {
|
||||
/**
|
||||
* Block for reversing a string.
|
||||
*/
|
||||
init: function(this: Block) {
|
||||
init: function (this: Block) {
|
||||
this.jsonInit({
|
||||
'message0': Msg['TEXT_REVERSE_MESSAGE0'],
|
||||
'args0': [
|
||||
@@ -650,7 +643,7 @@ blocks['text_reverse'] = {
|
||||
};
|
||||
|
||||
/** Type of a block that has QUOTE_IMAGE_MIXIN */
|
||||
type QuoteImageBlock = Block&QuoteImageMixin;
|
||||
type QuoteImageBlock = Block & QuoteImageMixin;
|
||||
interface QuoteImageMixin extends QuoteImageMixinType {}
|
||||
type QuoteImageMixinType = typeof QUOTE_IMAGE_MIXIN;
|
||||
|
||||
@@ -660,21 +653,21 @@ const QUOTE_IMAGE_MIXIN = {
|
||||
* quote).
|
||||
*/
|
||||
QUOTE_IMAGE_LEFT_DATAURI:
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' +
|
||||
'n0lEQVQI1z3OMa5BURSF4f/cQhAKjUQhuQmFNwGJEUi0RKN5rU7FHKhpjEH3TEMtkdBSCY' +
|
||||
'1EIv8r7nFX9e29V7EBAOvu7RPjwmWGH/VuF8CyN9/OAdvqIXYLvtRaNjx9mMTDyo+NjAN1' +
|
||||
'HNcl9ZQ5oQMM3dgDUqDo1l8DzvwmtZN7mnD+PkmLa+4mhrxVA9fRowBWmVBhFy5gYEjKMf' +
|
||||
'z9AylsaRRgGzvZAAAAAElFTkSuQmCC',
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' +
|
||||
'n0lEQVQI1z3OMa5BURSF4f/cQhAKjUQhuQmFNwGJEUi0RKN5rU7FHKhpjEH3TEMtkdBSCY' +
|
||||
'1EIv8r7nFX9e29V7EBAOvu7RPjwmWGH/VuF8CyN9/OAdvqIXYLvtRaNjx9mMTDyo+NjAN1' +
|
||||
'HNcl9ZQ5oQMM3dgDUqDo1l8DzvwmtZN7mnD+PkmLa+4mhrxVA9fRowBWmVBhFy5gYEjKMf' +
|
||||
'z9AylsaRRgGzvZAAAAAElFTkSuQmCC',
|
||||
/**
|
||||
* Image data URI of an LTR closing double quote (same as RTL opening double
|
||||
* quote).
|
||||
*/
|
||||
QUOTE_IMAGE_RIGHT_DATAURI:
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' +
|
||||
'qUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhg' +
|
||||
'gONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvB' +
|
||||
'O3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5Aos' +
|
||||
'lLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==',
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' +
|
||||
'qUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhg' +
|
||||
'gONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvB' +
|
||||
'O3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5Aos' +
|
||||
'lLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==',
|
||||
/**
|
||||
* Pixel width of QUOTE_IMAGE_LEFT_DATAURI and QUOTE_IMAGE_RIGHT_DATAURI.
|
||||
*/
|
||||
@@ -689,7 +682,7 @@ const QUOTE_IMAGE_MIXIN = {
|
||||
*
|
||||
* @param fieldName The name of the field to wrap with quotes.
|
||||
*/
|
||||
quoteField_: function(this: QuoteImageBlock, fieldName: string) {
|
||||
quoteField_: function (this: QuoteImageBlock, fieldName: string) {
|
||||
for (let i = 0, input; (input = this.inputList[i]); i++) {
|
||||
for (let j = 0, field; (field = input.fieldRow[j]); j++) {
|
||||
if (fieldName === field.name) {
|
||||
@@ -700,7 +693,8 @@ const QUOTE_IMAGE_MIXIN = {
|
||||
}
|
||||
}
|
||||
console.warn(
|
||||
'field named "' + fieldName + '" not found in ' + this.toDevString());
|
||||
'field named "' + fieldName + '" not found in ' + this.toDevString()
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -711,10 +705,11 @@ const QUOTE_IMAGE_MIXIN = {
|
||||
* Otherwise, a closing quote is used (” in LTR).
|
||||
* @returns The new field.
|
||||
*/
|
||||
newQuote_: function(this: QuoteImageBlock, open: boolean): FieldImage {
|
||||
newQuote_: function (this: QuoteImageBlock, open: boolean): FieldImage {
|
||||
const isLeft = this.RTL ? !open : open;
|
||||
const dataUri =
|
||||
isLeft ? this.QUOTE_IMAGE_LEFT_DATAURI : this.QUOTE_IMAGE_RIGHT_DATAURI;
|
||||
const dataUri = isLeft
|
||||
? this.QUOTE_IMAGE_LEFT_DATAURI
|
||||
: this.QUOTE_IMAGE_RIGHT_DATAURI;
|
||||
return fieldRegistry.fromJson({
|
||||
type: 'field_image',
|
||||
src: dataUri,
|
||||
@@ -728,20 +723,20 @@ const QUOTE_IMAGE_MIXIN = {
|
||||
/**
|
||||
* Wraps TEXT field with images of double quote characters.
|
||||
*/
|
||||
const QUOTES_EXTENSION = function(this: QuoteImageBlock) {
|
||||
const QUOTES_EXTENSION = function (this: QuoteImageBlock) {
|
||||
this.mixin(QUOTE_IMAGE_MIXIN);
|
||||
this.quoteField_('TEXT');
|
||||
};
|
||||
|
||||
/** Type of a block that has TEXT_JOIN_MUTATOR_MIXIN */
|
||||
type JoinMutatorBlock = Block&JoinMutatorMixin&QuoteImageMixin;
|
||||
type JoinMutatorBlock = Block & JoinMutatorMixin & QuoteImageMixin;
|
||||
interface JoinMutatorMixin extends JoinMutatorMixinType {}
|
||||
type JoinMutatorMixinType = typeof JOIN_MUTATOR_MIXIN;
|
||||
|
||||
/** Type of a item block in the text_join_mutator bubble. */
|
||||
type JoinItemBlock = BlockSvg&JoinItemMixin;
|
||||
type JoinItemBlock = BlockSvg & JoinItemMixin;
|
||||
interface JoinItemMixin {
|
||||
valueConnection_: Connection|null
|
||||
valueConnection_: Connection | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,7 +750,7 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @returns XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: JoinMutatorBlock): Element {
|
||||
mutationToDom: function (this: JoinMutatorBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
container.setAttribute('items', `${this.itemCount_}`);
|
||||
return container;
|
||||
@@ -766,7 +761,7 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: JoinMutatorBlock, xmlElement: Element) {
|
||||
domToMutation: function (this: JoinMutatorBlock, xmlElement: Element) {
|
||||
this.itemCount_ = parseInt(xmlElement.getAttribute('items')!, 10);
|
||||
this.updateShape_();
|
||||
},
|
||||
@@ -775,7 +770,7 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @returns The state of this block, ie the item count.
|
||||
*/
|
||||
saveExtraState: function(this: JoinMutatorBlock): {itemCount: number;} {
|
||||
saveExtraState: function (this: JoinMutatorBlock): {itemCount: number} {
|
||||
return {
|
||||
'itemCount': this.itemCount_,
|
||||
};
|
||||
@@ -785,7 +780,7 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param state The state to apply to this block, ie the item count.
|
||||
*/
|
||||
loadExtraState: function(this: JoinMutatorBlock, state: {[x: string]: any;}) {
|
||||
loadExtraState: function (this: JoinMutatorBlock, state: {[x: string]: any}) {
|
||||
this.itemCount_ = state['itemCount'];
|
||||
this.updateShape_();
|
||||
},
|
||||
@@ -795,14 +790,16 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
* @param workspace Mutator's workspace.
|
||||
* @returns Root block in mutator.
|
||||
*/
|
||||
decompose: function(this: JoinMutatorBlock, workspace: Workspace): Block {
|
||||
const containerBlock =
|
||||
workspace.newBlock('text_create_join_container') as BlockSvg;
|
||||
decompose: function (this: JoinMutatorBlock, workspace: Workspace): Block {
|
||||
const containerBlock = workspace.newBlock(
|
||||
'text_create_join_container'
|
||||
) as BlockSvg;
|
||||
containerBlock.initSvg();
|
||||
let connection = containerBlock.getInput('STACK')!.connection!;
|
||||
for (let i = 0; i < this.itemCount_; i++) {
|
||||
const itemBlock =
|
||||
workspace.newBlock('text_create_join_item') as JoinItemBlock;
|
||||
const itemBlock = workspace.newBlock(
|
||||
'text_create_join_item'
|
||||
) as JoinItemBlock;
|
||||
itemBlock.initSvg();
|
||||
connection.connect(itemBlock.previousConnection);
|
||||
connection = itemBlock.nextConnection;
|
||||
@@ -814,9 +811,10 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param containerBlock Root block in mutator.
|
||||
*/
|
||||
compose: function(this: JoinMutatorBlock, containerBlock: Block) {
|
||||
let itemBlock =
|
||||
containerBlock.getInputTargetBlock('STACK') as JoinItemBlock;
|
||||
compose: function (this: JoinMutatorBlock, containerBlock: Block) {
|
||||
let itemBlock = containerBlock.getInputTargetBlock(
|
||||
'STACK'
|
||||
) as JoinItemBlock;
|
||||
// Count number of inputs.
|
||||
const connections = [];
|
||||
while (itemBlock) {
|
||||
@@ -846,7 +844,7 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param containerBlock Root block in mutator.
|
||||
*/
|
||||
saveConnections: function(this: JoinMutatorBlock, containerBlock: Block) {
|
||||
saveConnections: function (this: JoinMutatorBlock, containerBlock: Block) {
|
||||
let itemBlock = containerBlock.getInputTargetBlock('STACK');
|
||||
let i = 0;
|
||||
while (itemBlock) {
|
||||
@@ -856,7 +854,7 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
}
|
||||
const input = this.getInput('ADD' + i);
|
||||
(itemBlock as JoinItemBlock).valueConnection_ =
|
||||
input && input.connection!.targetConnection;
|
||||
input && input.connection!.targetConnection;
|
||||
itemBlock = itemBlock.getNextBlock();
|
||||
i++;
|
||||
}
|
||||
@@ -864,13 +862,13 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
/**
|
||||
* Modify this block to have the correct number of inputs.
|
||||
*/
|
||||
updateShape_: function(this: JoinMutatorBlock) {
|
||||
updateShape_: function (this: JoinMutatorBlock) {
|
||||
if (this.itemCount_ && this.getInput('EMPTY')) {
|
||||
this.removeInput('EMPTY');
|
||||
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
|
||||
this.appendDummyInput('EMPTY')
|
||||
.appendField(this.newQuote_(true))
|
||||
.appendField(this.newQuote_(false));
|
||||
.appendField(this.newQuote_(true))
|
||||
.appendField(this.newQuote_(false));
|
||||
}
|
||||
// Add new inputs.
|
||||
for (let i = 0; i < this.itemCount_; i++) {
|
||||
@@ -891,7 +889,7 @@ const JOIN_MUTATOR_MIXIN = {
|
||||
/**
|
||||
* Performs final setup of a text_join block.
|
||||
*/
|
||||
const JOIN_EXTENSION = function(this: JoinMutatorBlock) {
|
||||
const JOIN_EXTENSION = function (this: JoinMutatorBlock) {
|
||||
// Add the quote mixin for the itemCount_ = 0 case.
|
||||
this.mixin(QUOTE_IMAGE_MIXIN);
|
||||
// Initialize the mutator values.
|
||||
@@ -903,22 +901,24 @@ const JOIN_EXTENSION = function(this: JoinMutatorBlock) {
|
||||
|
||||
// Update the tooltip of 'text_append' block to reference the variable.
|
||||
Extensions.register(
|
||||
'text_append_tooltip',
|
||||
Extensions.buildTooltipWithFieldText('%{BKY_TEXT_APPEND_TOOLTIP}', 'VAR'));
|
||||
'text_append_tooltip',
|
||||
Extensions.buildTooltipWithFieldText('%{BKY_TEXT_APPEND_TOOLTIP}', 'VAR')
|
||||
);
|
||||
|
||||
/**
|
||||
* Update the tooltip of 'text_append' block to reference the variable.
|
||||
*/
|
||||
const INDEXOF_TOOLTIP_EXTENSION = function(this: Block) {
|
||||
const INDEXOF_TOOLTIP_EXTENSION = function (this: Block) {
|
||||
this.setTooltip(() => {
|
||||
return Msg['TEXT_INDEXOF_TOOLTIP'].replace(
|
||||
'%1', this.workspace.options.oneBasedIndex ? '0' : '-1');
|
||||
'%1',
|
||||
this.workspace.options.oneBasedIndex ? '0' : '-1'
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** Type of a block that has TEXT_CHARAT_MUTATOR_MIXIN */
|
||||
type CharAtBlock = Block&CharAtMixin;
|
||||
type CharAtBlock = Block & CharAtMixin;
|
||||
interface CharAtMixin extends CharAtMixinType {}
|
||||
type CharAtMixinType = typeof CHARAT_MUTATOR_MIXIN;
|
||||
|
||||
@@ -933,7 +933,7 @@ const CHARAT_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @returns XML storage element.
|
||||
*/
|
||||
mutationToDom: function(this: CharAtBlock): Element {
|
||||
mutationToDom: function (this: CharAtBlock): Element {
|
||||
const container = xmlUtils.createElement('mutation');
|
||||
container.setAttribute('at', `${this.isAt_}`);
|
||||
return container;
|
||||
@@ -944,10 +944,10 @@ const CHARAT_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param xmlElement XML storage element.
|
||||
*/
|
||||
domToMutation: function(this: CharAtBlock, xmlElement: Element) {
|
||||
domToMutation: function (this: CharAtBlock, xmlElement: Element) {
|
||||
// Note: Until January 2013 this block did not have mutations,
|
||||
// so 'at' defaults to true.
|
||||
const isAt = (xmlElement.getAttribute('at') !== 'false');
|
||||
const isAt = xmlElement.getAttribute('at') !== 'false';
|
||||
this.updateAt_(isAt);
|
||||
},
|
||||
|
||||
@@ -961,7 +961,7 @@ const CHARAT_MUTATOR_MIXIN = {
|
||||
*
|
||||
* @param isAt True if the input should exist.
|
||||
*/
|
||||
updateAt_: function(this: CharAtBlock, isAt: boolean) {
|
||||
updateAt_: function (this: CharAtBlock, isAt: boolean) {
|
||||
// Destroy old 'AT' and 'ORDINAL' inputs.
|
||||
this.removeInput('AT', true);
|
||||
this.removeInput('ORDINAL', true);
|
||||
@@ -970,7 +970,8 @@ const CHARAT_MUTATOR_MIXIN = {
|
||||
this.appendValueInput('AT').setCheck('Number');
|
||||
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
|
||||
this.appendDummyInput('ORDINAL').appendField(
|
||||
Msg['ORDINAL_NUMBER_SUFFIX']);
|
||||
Msg['ORDINAL_NUMBER_SUFFIX']
|
||||
);
|
||||
}
|
||||
}
|
||||
if (Msg['TEXT_CHARAT_TAIL']) {
|
||||
@@ -985,30 +986,29 @@ const CHARAT_MUTATOR_MIXIN = {
|
||||
/**
|
||||
* Does the initial mutator update of text_charAt and adds the tooltip
|
||||
*/
|
||||
const CHARAT_EXTENSION = function(this: CharAtBlock) {
|
||||
const CHARAT_EXTENSION = function (this: CharAtBlock) {
|
||||
const dropdown = this.getField('WHERE') as FieldDropdown;
|
||||
dropdown.setValidator(function(this: FieldDropdown, value: any) {
|
||||
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
|
||||
dropdown.setValidator(function (this: FieldDropdown, value: any) {
|
||||
const newAt = value === 'FROM_START' || value === 'FROM_END';
|
||||
const block = this.getSourceBlock() as CharAtBlock;
|
||||
if (newAt !== block.isAt_) {
|
||||
block.updateAt_(newAt);
|
||||
}
|
||||
return undefined; // FieldValidators can't be void. Use option as-is.
|
||||
return undefined; // FieldValidators can't be void. Use option as-is.
|
||||
});
|
||||
this.updateAt_(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
const thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
const where = thisBlock.getFieldValue('WHERE');
|
||||
this.setTooltip(() => {
|
||||
const where = this.getFieldValue('WHERE');
|
||||
let tooltip = Msg['TEXT_CHARAT_TOOLTIP'];
|
||||
if (where === 'FROM_START' || where === 'FROM_END') {
|
||||
const msg = (where === 'FROM_START') ?
|
||||
Msg['LISTS_INDEX_FROM_START_TOOLTIP'] :
|
||||
Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
|
||||
const msg =
|
||||
where === 'FROM_START'
|
||||
? Msg['LISTS_INDEX_FROM_START_TOOLTIP']
|
||||
: Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
|
||||
if (msg) {
|
||||
tooltip += ' ' +
|
||||
msg.replace(
|
||||
'%1', thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
|
||||
tooltip +=
|
||||
' ' +
|
||||
msg.replace('%1', this.workspace.options.oneBasedIndex ? '#1' : '#0');
|
||||
}
|
||||
}
|
||||
return tooltip;
|
||||
@@ -1020,10 +1020,16 @@ Extensions.register('text_indexOf_tooltip', INDEXOF_TOOLTIP_EXTENSION);
|
||||
Extensions.register('text_quotes', QUOTES_EXTENSION);
|
||||
|
||||
Extensions.registerMutator(
|
||||
'text_join_mutator', JOIN_MUTATOR_MIXIN, JOIN_EXTENSION);
|
||||
'text_join_mutator',
|
||||
JOIN_MUTATOR_MIXIN,
|
||||
JOIN_EXTENSION
|
||||
);
|
||||
|
||||
Extensions.registerMutator(
|
||||
'text_charAt_mutator', CHARAT_MUTATOR_MIXIN, CHARAT_EXTENSION);
|
||||
'text_charAt_mutator',
|
||||
CHARAT_MUTATOR_MIXIN,
|
||||
CHARAT_EXTENSION
|
||||
);
|
||||
|
||||
// Register provided blocks.
|
||||
defineBlocks(blocks);
|
||||
|
||||
@@ -17,14 +17,19 @@ import * as Extensions from '../core/extensions.js';
|
||||
import * as Variables from '../core/variables.js';
|
||||
import * as xmlUtils from '../core/utils/xml.js';
|
||||
import type {Block} from '../core/block.js';
|
||||
import type {ContextMenuOption, LegacyContextMenuOption} from '../core/contextmenu_registry.js';
|
||||
import type {
|
||||
ContextMenuOption,
|
||||
LegacyContextMenuOption,
|
||||
} from '../core/contextmenu_registry.js';
|
||||
import {FieldVariable} from '../core/field_variable.js';
|
||||
import {Msg} from '../core/msg.js';
|
||||
import type {WorkspaceSvg} from '../core/workspace_svg.js';
|
||||
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
|
||||
import {
|
||||
createBlockDefinitionsFromJsonArray,
|
||||
defineBlocks,
|
||||
} from '../core/common.js';
|
||||
import '../core/field_label.js';
|
||||
|
||||
|
||||
/**
|
||||
* A dictionary of the block definitions provided by this module.
|
||||
*/
|
||||
@@ -71,10 +76,10 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
]);
|
||||
|
||||
/** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */
|
||||
type VariableBlock = Block&VariableMixin;
|
||||
type VariableBlock = Block & VariableMixin;
|
||||
interface VariableMixin extends VariableMixinType {}
|
||||
type VariableMixinType =
|
||||
typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN;
|
||||
typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN;
|
||||
|
||||
/**
|
||||
* Mixin to add context menu items to create getter/setter blocks for this
|
||||
@@ -87,9 +92,10 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
|
||||
*
|
||||
* @param options List of menu options to add to.
|
||||
*/
|
||||
customContextMenu: function(
|
||||
this: VariableBlock,
|
||||
options: Array<ContextMenuOption|LegacyContextMenuOption>) {
|
||||
customContextMenu: function (
|
||||
this: VariableBlock,
|
||||
options: Array<ContextMenuOption | LegacyContextMenuOption>
|
||||
) {
|
||||
if (!this.isInFlyout) {
|
||||
let oppositeType;
|
||||
let contextMenuMsg;
|
||||
@@ -113,12 +119,14 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
|
||||
options.push({
|
||||
enabled: this.workspace.remainingCapacity() > 0,
|
||||
text: contextMenuMsg.replace('%1', name),
|
||||
callback: ContextMenu.callbackFactory(this, xmlBlock)
|
||||
callback: ContextMenu.callbackFactory(this, xmlBlock),
|
||||
});
|
||||
// Getter blocks have the option to rename or delete that variable.
|
||||
} else {
|
||||
if (this.type === 'variables_get' ||
|
||||
this.type === 'variables_get_reporter') {
|
||||
if (
|
||||
this.type === 'variables_get' ||
|
||||
this.type === 'variables_get_reporter'
|
||||
) {
|
||||
const renameOption = {
|
||||
text: Msg['RENAME_VARIABLE'],
|
||||
enabled: true,
|
||||
@@ -144,8 +152,10 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
|
||||
* @param block The block with the variable to rename.
|
||||
* @returns A function that renames the variable.
|
||||
*/
|
||||
const renameOptionCallbackFactory = function(block: VariableBlock): () => void {
|
||||
return function() {
|
||||
const renameOptionCallbackFactory = function (
|
||||
block: VariableBlock
|
||||
): () => void {
|
||||
return function () {
|
||||
const workspace = block.workspace;
|
||||
const variableField = block.getField('VAR') as FieldVariable;
|
||||
const variable = variableField.getVariable()!;
|
||||
@@ -160,8 +170,10 @@ const renameOptionCallbackFactory = function(block: VariableBlock): () => void {
|
||||
* @param block The block with the variable to delete.
|
||||
* @returns A function that deletes the variable.
|
||||
*/
|
||||
const deleteOptionCallbackFactory = function(block: VariableBlock): () => void {
|
||||
return function() {
|
||||
const deleteOptionCallbackFactory = function (
|
||||
block: VariableBlock
|
||||
): () => void {
|
||||
return function () {
|
||||
const workspace = block.workspace;
|
||||
const variableField = block.getField('VAR') as FieldVariable;
|
||||
const variable = variableField.getVariable()!;
|
||||
@@ -171,8 +183,9 @@ const deleteOptionCallbackFactory = function(block: VariableBlock): () => void {
|
||||
};
|
||||
|
||||
Extensions.registerMixin(
|
||||
'contextMenu_variableSetterGetter',
|
||||
CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);
|
||||
'contextMenu_variableSetterGetter',
|
||||
CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN
|
||||
);
|
||||
|
||||
// Register provided blocks.
|
||||
defineBlocks(blocks);
|
||||
|
||||
@@ -18,14 +18,19 @@ import * as Variables from '../core/variables.js';
|
||||
import * as xml from '../core/utils/xml.js';
|
||||
import {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
|
||||
import type {Block} from '../core/block.js';
|
||||
import type {ContextMenuOption, LegacyContextMenuOption} from '../core/contextmenu_registry.js';
|
||||
import type {
|
||||
ContextMenuOption,
|
||||
LegacyContextMenuOption,
|
||||
} from '../core/contextmenu_registry.js';
|
||||
import {FieldVariable} from '../core/field_variable.js';
|
||||
import {Msg} from '../core/msg.js';
|
||||
import type {WorkspaceSvg} from '../core/workspace_svg.js';
|
||||
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
|
||||
import {
|
||||
createBlockDefinitionsFromJsonArray,
|
||||
defineBlocks,
|
||||
} from '../core/common.js';
|
||||
import '../core/field_label.js';
|
||||
|
||||
|
||||
/**
|
||||
* A dictionary of the block definitions provided by this module.
|
||||
*/
|
||||
@@ -34,11 +39,13 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
{
|
||||
'type': 'variables_get_dynamic',
|
||||
'message0': '%1',
|
||||
'args0': [{
|
||||
'type': 'field_variable',
|
||||
'name': 'VAR',
|
||||
'variable': '%{BKY_VARIABLES_DEFAULT_NAME}',
|
||||
}],
|
||||
'args0': [
|
||||
{
|
||||
'type': 'field_variable',
|
||||
'name': 'VAR',
|
||||
'variable': '%{BKY_VARIABLES_DEFAULT_NAME}',
|
||||
},
|
||||
],
|
||||
'output': null,
|
||||
'style': 'variable_dynamic_blocks',
|
||||
'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}',
|
||||
@@ -70,10 +77,10 @@ export const blocks = createBlockDefinitionsFromJsonArray([
|
||||
]);
|
||||
|
||||
/** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */
|
||||
type VariableBlock = Block&VariableMixin;
|
||||
type VariableBlock = Block & VariableMixin;
|
||||
interface VariableMixin extends VariableMixinType {}
|
||||
type VariableMixinType =
|
||||
typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN;
|
||||
typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN;
|
||||
|
||||
/**
|
||||
* Mixin to add context menu items to create getter/setter blocks for this
|
||||
@@ -86,9 +93,10 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
|
||||
*
|
||||
* @param options List of menu options to add to.
|
||||
*/
|
||||
customContextMenu: function(
|
||||
this: VariableBlock,
|
||||
options: Array<ContextMenuOption|LegacyContextMenuOption>) {
|
||||
customContextMenu: function (
|
||||
this: VariableBlock,
|
||||
options: Array<ContextMenuOption | LegacyContextMenuOption>
|
||||
) {
|
||||
// Getter blocks have the option to create a setter block, and vice versa.
|
||||
if (!this.isInFlyout) {
|
||||
let oppositeType;
|
||||
@@ -116,11 +124,13 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
|
||||
options.push({
|
||||
enabled: this.workspace.remainingCapacity() > 0,
|
||||
text: contextMenuMsg.replace('%1', name),
|
||||
callback: ContextMenu.callbackFactory(this, xmlBlock)
|
||||
callback: ContextMenu.callbackFactory(this, xmlBlock),
|
||||
});
|
||||
} else {
|
||||
if (this.type === 'variables_get_dynamic' ||
|
||||
this.type === 'variables_get_reporter_dynamic') {
|
||||
if (
|
||||
this.type === 'variables_get_dynamic' ||
|
||||
this.type === 'variables_get_reporter_dynamic'
|
||||
) {
|
||||
const renameOption = {
|
||||
text: Msg['RENAME_VARIABLE'],
|
||||
enabled: true,
|
||||
@@ -143,7 +153,7 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
|
||||
*
|
||||
* @param _e Change event.
|
||||
*/
|
||||
onchange: function(this: VariableBlock, _e: AbstractEvent) {
|
||||
onchange: function (this: VariableBlock, _e: AbstractEvent) {
|
||||
const id = this.getFieldValue('VAR');
|
||||
const variableModel = Variables.getVariable(this.workspace, id)!;
|
||||
if (this.type === 'variables_get_dynamic') {
|
||||
@@ -161,8 +171,8 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
|
||||
* @param block The block with the variable to rename.
|
||||
* @returns A function that renames the variable.
|
||||
*/
|
||||
const renameOptionCallbackFactory = function(block: VariableBlock) {
|
||||
return function() {
|
||||
const renameOptionCallbackFactory = function (block: VariableBlock) {
|
||||
return function () {
|
||||
const workspace = block.workspace;
|
||||
const variableField = block.getField('VAR') as FieldVariable;
|
||||
const variable = variableField.getVariable()!;
|
||||
@@ -177,8 +187,8 @@ const renameOptionCallbackFactory = function(block: VariableBlock) {
|
||||
* @param block The block with the variable to delete.
|
||||
* @returns A function that deletes the variable.
|
||||
*/
|
||||
const deleteOptionCallbackFactory = function(block: VariableBlock) {
|
||||
return function() {
|
||||
const deleteOptionCallbackFactory = function (block: VariableBlock) {
|
||||
return function () {
|
||||
const workspace = block.workspace;
|
||||
const variableField = block.getField('VAR') as FieldVariable;
|
||||
const variable = variableField.getVariable()!;
|
||||
@@ -188,8 +198,9 @@ const deleteOptionCallbackFactory = function(block: VariableBlock) {
|
||||
};
|
||||
|
||||
Extensions.registerMixin(
|
||||
'contextMenu_variableDynamicSetterGetter',
|
||||
CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);
|
||||
'contextMenu_variableDynamicSetterGetter',
|
||||
CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN
|
||||
);
|
||||
|
||||
// Register provided blocks.
|
||||
defineBlocks(blocks);
|
||||
|
||||
Reference in New Issue
Block a user