mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 14:32:59 -05:00
Merge pull request #2143 from cwillisf/non-droppable-extension-menus
support non-droppable menus in extensions
This commit is contained in:
commit
c6b63a8f09
14 changed files with 734 additions and 452 deletions
|
@ -120,6 +120,124 @@ class SomeBlocks {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Defining a Menu
|
||||||
|
|
||||||
|
To display a drop-down menu for a block argument, specify the `menu` property of that argument and a matching item in
|
||||||
|
the `menus` section of your extension's definition:
|
||||||
|
|
||||||
|
```js
|
||||||
|
return {
|
||||||
|
// ...
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
arguments: {
|
||||||
|
FOO: {
|
||||||
|
type: ArgumentType.NUMBER,
|
||||||
|
menu: 'fooMenu'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
menus: {
|
||||||
|
fooMenu: {
|
||||||
|
items: ['a', 'b', 'c']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The items in a menu may be specified with an array or with the name of a function which returns an array. The two
|
||||||
|
simplest forms for menu definitions are:
|
||||||
|
|
||||||
|
```js
|
||||||
|
getInfo () {
|
||||||
|
return {
|
||||||
|
menus: {
|
||||||
|
staticMenu: ['static 1', 'static 2', 'static 3'],
|
||||||
|
dynamicMenu: 'getDynamicMenuItems'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// this member function will be called each time the menu opens
|
||||||
|
getDynamicMenuItems () {
|
||||||
|
return ['dynamic 1', 'dynamic 2', 'dynamic 3'];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The examples above are shorthand for these equivalent definitions:
|
||||||
|
|
||||||
|
```js
|
||||||
|
getInfo () {
|
||||||
|
return {
|
||||||
|
menus: {
|
||||||
|
staticMenu: {
|
||||||
|
items: ['static 1', 'static 2', 'static 3']
|
||||||
|
},
|
||||||
|
dynamicMenu: {
|
||||||
|
items: 'getDynamicMenuItems'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// this member function will be called each time the menu opens
|
||||||
|
getDynamicMenuItems () {
|
||||||
|
return ['dynamic 1', 'dynamic 2', 'dynamic 3'];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If a menu item needs a label that doesn't match its value -- for example, if the label needs to be displayed in the
|
||||||
|
user's language but the value needs to stay constant -- the menu item may be an object instead of a string. This works
|
||||||
|
for both static and dynamic menu items:
|
||||||
|
|
||||||
|
```js
|
||||||
|
menus: {
|
||||||
|
staticMenu: [
|
||||||
|
{
|
||||||
|
text: formatMessage(/* ... */),
|
||||||
|
value: 42
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Accepting reporters ("droppable" menus)
|
||||||
|
|
||||||
|
By default it is not possible to specify the value of a dropdown menu by inserting a reporter block. While we
|
||||||
|
encourage extension authors to make their menus accept reporters when possible, doing so requires careful
|
||||||
|
consideration to avoid confusion and frustration on the part of those using the extension.
|
||||||
|
|
||||||
|
A few of these considerations include:
|
||||||
|
|
||||||
|
* The valid values for the menu should not change when the user changes the Scratch language setting.
|
||||||
|
* In particular, changing languages should never break a working project.
|
||||||
|
* The average Scratch user should be able to figure out the valid values for this input without referring to extension
|
||||||
|
documentation.
|
||||||
|
* One way to ensure this is to make an item's text match or include the item's value. For example, the official Music
|
||||||
|
extension contains menu items with names like "(1) Piano" with value 1, "(8) Cello" with value 8, and so on.
|
||||||
|
* The block should accept any value as input, even "invalid" values.
|
||||||
|
* Scratch has no concept of a runtime error!
|
||||||
|
* For a command block, sometimes the best option is to do nothing.
|
||||||
|
* For a reporter, returning zero or the empty string might make sense.
|
||||||
|
* The block should be forgiving in its interpretation of inputs.
|
||||||
|
* For example, if the block expects a string and receives a number it may make sense to interpret the number as a
|
||||||
|
string instead of treating it as invalid input.
|
||||||
|
|
||||||
|
The `acceptReporters` flag indicates that the user can drop a reporter onto the menu input:
|
||||||
|
|
||||||
|
```js
|
||||||
|
menus: {
|
||||||
|
staticMenu: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: [/*...*/]
|
||||||
|
},
|
||||||
|
dynamicMenu: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: 'getDynamicMenuItems'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Annotated Example
|
## Annotated Example
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -311,7 +429,16 @@ class SomeBlocks {
|
||||||
|
|
||||||
// Dynamic menu: returns an array as above.
|
// Dynamic menu: returns an array as above.
|
||||||
// Called each time the menu is opened.
|
// Called each time the menu is opened.
|
||||||
menuB: 'getItemsForMenuB'
|
menuB: 'getItemsForMenuB',
|
||||||
|
|
||||||
|
// The examples above are shorthand for setting only the `items` property in this full form:
|
||||||
|
menuC: {
|
||||||
|
// This flag makes a "droppable" menu: the menu will allow dropping a reporter in for the input.
|
||||||
|
acceptReporters: true,
|
||||||
|
|
||||||
|
// The `item` property may be an array or function name as in previous menu examples.
|
||||||
|
items: [/*...*/] || 'getItemsForMenuC'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Optional: translations (UNSTABLE - NOT YET SUPPORTED)
|
// Optional: translations (UNSTABLE - NOT YET SUPPORTED)
|
||||||
|
|
|
@ -786,10 +786,7 @@ class Runtime extends EventEmitter {
|
||||||
name: maybeFormatMessage(extensionInfo.name),
|
name: maybeFormatMessage(extensionInfo.name),
|
||||||
showStatusButton: extensionInfo.showStatusButton,
|
showStatusButton: extensionInfo.showStatusButton,
|
||||||
blockIconURI: extensionInfo.blockIconURI,
|
blockIconURI: extensionInfo.blockIconURI,
|
||||||
menuIconURI: extensionInfo.menuIconURI,
|
menuIconURI: extensionInfo.menuIconURI
|
||||||
customFieldTypes: {},
|
|
||||||
blocks: [],
|
|
||||||
menus: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (extensionInfo.color1) {
|
if (extensionInfo.color1) {
|
||||||
|
@ -830,8 +827,6 @@ class Runtime extends EventEmitter {
|
||||||
const categoryInfo = this._blockInfo.find(info => info.id === extensionInfo.id);
|
const categoryInfo = this._blockInfo.find(info => info.id === extensionInfo.id);
|
||||||
if (categoryInfo) {
|
if (categoryInfo) {
|
||||||
categoryInfo.name = maybeFormatMessage(extensionInfo.name);
|
categoryInfo.name = maybeFormatMessage(extensionInfo.name);
|
||||||
categoryInfo.blocks = [];
|
|
||||||
categoryInfo.menus = [];
|
|
||||||
this._fillExtensionCategory(categoryInfo, extensionInfo);
|
this._fillExtensionCategory(categoryInfo, extensionInfo);
|
||||||
|
|
||||||
this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo);
|
this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo);
|
||||||
|
@ -839,18 +834,24 @@ class Runtime extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read extension information, convert menus, blocks and custom field types
|
* Read extension information, convert menus, blocks and custom field types
|
||||||
* and store the results in the provided category object.
|
* and store the results in the provided category object.
|
||||||
* @param {CategoryInfo} categoryInfo - the category to be filled
|
* @param {CategoryInfo} categoryInfo - the category to be filled
|
||||||
* @param {ExtensionMetadata} extensionInfo - the extension metadata to read
|
* @param {ExtensionMetadata} extensionInfo - the extension metadata to read
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_fillExtensionCategory (categoryInfo, extensionInfo) {
|
_fillExtensionCategory (categoryInfo, extensionInfo) {
|
||||||
|
categoryInfo.blocks = [];
|
||||||
|
categoryInfo.customFieldTypes = {};
|
||||||
|
categoryInfo.menus = [];
|
||||||
|
categoryInfo.menuInfo = {};
|
||||||
|
|
||||||
for (const menuName in extensionInfo.menus) {
|
for (const menuName in extensionInfo.menus) {
|
||||||
if (extensionInfo.menus.hasOwnProperty(menuName)) {
|
if (extensionInfo.menus.hasOwnProperty(menuName)) {
|
||||||
const menuItems = extensionInfo.menus[menuName];
|
const menuInfo = extensionInfo.menus[menuName];
|
||||||
const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuItems, categoryInfo);
|
const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuInfo, categoryInfo);
|
||||||
categoryInfo.menus.push(convertedMenu);
|
categoryInfo.menus.push(convertedMenu);
|
||||||
|
categoryInfo.menuInfo[menuName] = menuInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const fieldTypeName in extensionInfo.customFieldTypes) {
|
for (const fieldTypeName in extensionInfo.customFieldTypes) {
|
||||||
|
@ -890,21 +891,16 @@ class Runtime extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block.
|
* Convert the given extension menu items into the scratch-blocks style of list of pairs.
|
||||||
* @param {string} menuName - the name of the menu
|
* If the menu is dynamic (e.g. the passed in argument is a function), return the input unmodified.
|
||||||
* @param {array} menuItems - the list of items for this menu
|
* @param {object} menuItems - an array of menu items or a function to retrieve such an array
|
||||||
* @param {CategoryInfo} categoryInfo - the category for this block
|
* @returns {object} - an array of 2 element arrays or the original input function
|
||||||
* @returns {object} - a JSON-esque object ready for scratch-blocks' consumption
|
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_buildMenuForScratchBlocks (menuName, menuItems, categoryInfo) {
|
_convertMenuItems (menuItems) {
|
||||||
const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id);
|
if (typeof menuItems !== 'function') {
|
||||||
let options = null;
|
|
||||||
if (typeof menuItems === 'function') {
|
|
||||||
options = menuItems;
|
|
||||||
} else {
|
|
||||||
const extensionMessageContext = this.makeMessageContextForTarget();
|
const extensionMessageContext = this.makeMessageContextForTarget();
|
||||||
options = menuItems.map(item => {
|
return menuItems.map(item => {
|
||||||
const formattedItem = maybeFormatMessage(item, extensionMessageContext);
|
const formattedItem = maybeFormatMessage(item, extensionMessageContext);
|
||||||
switch (typeof formattedItem) {
|
switch (typeof formattedItem) {
|
||||||
case 'string':
|
case 'string':
|
||||||
|
@ -916,6 +912,22 @@ class Runtime extends EventEmitter {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return menuItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block.
|
||||||
|
* @param {string} menuName - the name of the menu
|
||||||
|
* @param {object} menuInfo - a description of this menu and its items
|
||||||
|
* @property {*} items - an array of menu items or a function to retrieve such an array
|
||||||
|
* @property {boolean} [acceptReporters] - if true, allow dropping reporters onto this menu
|
||||||
|
* @param {CategoryInfo} categoryInfo - the category for this block
|
||||||
|
* @returns {object} - a JSON-esque object ready for scratch-blocks' consumption
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_buildMenuForScratchBlocks (menuName, menuInfo, categoryInfo) {
|
||||||
|
const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id);
|
||||||
|
const menuItems = this._convertMenuItems(menuInfo.items);
|
||||||
return {
|
return {
|
||||||
json: {
|
json: {
|
||||||
message0: '%1',
|
message0: '%1',
|
||||||
|
@ -925,12 +937,13 @@ class Runtime extends EventEmitter {
|
||||||
colour: categoryInfo.color1,
|
colour: categoryInfo.color1,
|
||||||
colourSecondary: categoryInfo.color2,
|
colourSecondary: categoryInfo.color2,
|
||||||
colourTertiary: categoryInfo.color3,
|
colourTertiary: categoryInfo.color3,
|
||||||
outputShape: ScratchBlocksConstants.OUTPUT_SHAPE_ROUND,
|
outputShape: menuInfo.acceptReporters ?
|
||||||
|
ScratchBlocksConstants.OUTPUT_SHAPE_ROUND : ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE,
|
||||||
args0: [
|
args0: [
|
||||||
{
|
{
|
||||||
type: 'field_dropdown',
|
type: 'field_dropdown',
|
||||||
name: menuName,
|
name: menuName,
|
||||||
options: options
|
options: menuItems
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1227,29 +1240,51 @@ class Runtime extends EventEmitter {
|
||||||
argJSON.check = argTypeInfo.check;
|
argJSON.check = argTypeInfo.check;
|
||||||
}
|
}
|
||||||
|
|
||||||
const shadowType = (argInfo.menu ?
|
let valueName;
|
||||||
this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id) :
|
let shadowType;
|
||||||
argTypeInfo.shadowType);
|
let fieldName;
|
||||||
const fieldType = argInfo.menu || argTypeInfo.fieldType;
|
if (argInfo.menu) {
|
||||||
|
const menuInfo = context.categoryInfo.menuInfo[argInfo.menu];
|
||||||
|
if (menuInfo.acceptReporters) {
|
||||||
|
valueName = placeholder;
|
||||||
|
shadowType = this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id);
|
||||||
|
fieldName = argInfo.menu;
|
||||||
|
} else {
|
||||||
|
argJSON.type = 'field_dropdown';
|
||||||
|
argJSON.options = this._convertMenuItems(menuInfo.items);
|
||||||
|
valueName = null;
|
||||||
|
shadowType = null;
|
||||||
|
fieldName = placeholder;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valueName = placeholder;
|
||||||
|
shadowType = argTypeInfo.shadowType;
|
||||||
|
fieldName = argTypeInfo.fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
// <value> is the ScratchBlocks name for a block input.
|
// <value> is the ScratchBlocks name for a block input.
|
||||||
context.inputList.push(`<value name="${placeholder}">`);
|
if (valueName) {
|
||||||
|
context.inputList.push(`<value name="${placeholder}">`);
|
||||||
|
}
|
||||||
|
|
||||||
// The <shadow> is a placeholder for a reporter and is visible when there's no reporter in this input.
|
// The <shadow> is a placeholder for a reporter and is visible when there's no reporter in this input.
|
||||||
// Boolean inputs don't need to specify a shadow in the XML.
|
// Boolean inputs don't need to specify a shadow in the XML.
|
||||||
if (shadowType) {
|
if (shadowType) {
|
||||||
context.inputList.push(`<shadow type="${shadowType}">`);
|
context.inputList.push(`<shadow type="${shadowType}">`);
|
||||||
|
}
|
||||||
|
|
||||||
// <field> is a text field that the user can type into. Some shadows, like the color picker, don't allow
|
// A <field> displays a dynamic value: a user-editable text field, a drop-down menu, etc.
|
||||||
// text input and therefore don't need a field element.
|
if (fieldName) {
|
||||||
if (fieldType) {
|
context.inputList.push(`<field name="${fieldName}">${defaultValue}</field>`);
|
||||||
context.inputList.push(`<field name="${fieldType}">${defaultValue}</field>`);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (shadowType) {
|
||||||
context.inputList.push('</shadow>');
|
context.inputList.push('</shadow>');
|
||||||
}
|
}
|
||||||
|
|
||||||
context.inputList.push('</value>');
|
if (valueName) {
|
||||||
|
context.inputList.push('</value>');
|
||||||
|
}
|
||||||
|
|
||||||
const argsName = `args${context.outLineNum}`;
|
const argsName = `args${context.outLineNum}`;
|
||||||
const blockArgs = (context.blockJSON[argsName] = context.blockJSON[argsName] || []);
|
const blockArgs = (context.blockJSON[argsName] = context.blockJSON[argsName] || []);
|
||||||
|
|
|
@ -294,7 +294,7 @@ class ExtensionManager {
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}, []);
|
}, []);
|
||||||
extensionInfo.menus = extensionInfo.menus || [];
|
extensionInfo.menus = extensionInfo.menus || {};
|
||||||
extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus);
|
extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus);
|
||||||
return extensionInfo;
|
return extensionInfo;
|
||||||
}
|
}
|
||||||
|
@ -309,15 +309,24 @@ class ExtensionManager {
|
||||||
_prepareMenuInfo (serviceName, menus) {
|
_prepareMenuInfo (serviceName, menus) {
|
||||||
const menuNames = Object.getOwnPropertyNames(menus);
|
const menuNames = Object.getOwnPropertyNames(menus);
|
||||||
for (let i = 0; i < menuNames.length; i++) {
|
for (let i = 0; i < menuNames.length; i++) {
|
||||||
const item = menuNames[i];
|
const menuName = menuNames[i];
|
||||||
// If the value is a string, it should be the name of a function in the
|
let menuInfo = menus[menuName];
|
||||||
// extension object to call to populate the menu whenever it is opened.
|
|
||||||
// Set up the binding for the function object here so
|
// If the menu description is in short form (items only) then normalize it to general form: an object with
|
||||||
// we can use it later when converting the menu for Scratch Blocks.
|
// its items listed in an `items` property.
|
||||||
if (typeof menus[item] === 'string') {
|
if (!menuInfo.items) {
|
||||||
|
menuInfo = {
|
||||||
|
items: menuInfo
|
||||||
|
};
|
||||||
|
menus[menuName] = menuInfo;
|
||||||
|
}
|
||||||
|
// If `items` is a string, it should be the name of a function in the extension object. Calling the
|
||||||
|
// function should return an array of items to populate the menu when it is opened.
|
||||||
|
if (typeof menuInfo.items === 'string') {
|
||||||
|
const menuItemFunctionName = menuInfo.items;
|
||||||
const serviceObject = dispatch.services[serviceName];
|
const serviceObject = dispatch.services[serviceName];
|
||||||
const menuName = menus[item];
|
// Bind the function here so we can pass a simple item generation function to Scratch Blocks later.
|
||||||
menus[item] = this._getExtensionMenuItems.bind(this, serviceObject, menuName);
|
menuInfo.items = this._getExtensionMenuItems.bind(this, serviceObject, menuItemFunctionName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return menus;
|
return menus;
|
||||||
|
@ -326,11 +335,11 @@ class ExtensionManager {
|
||||||
/**
|
/**
|
||||||
* Fetch the items for a particular extension menu, providing the target ID for context.
|
* Fetch the items for a particular extension menu, providing the target ID for context.
|
||||||
* @param {object} extensionObject - the extension object providing the menu.
|
* @param {object} extensionObject - the extension object providing the menu.
|
||||||
* @param {string} menuName - the name of the menu function to call.
|
* @param {string} menuItemFunctionName - the name of the menu function to call.
|
||||||
* @returns {Array} menu items ready for scratch-blocks.
|
* @returns {Array} menu items ready for scratch-blocks.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getExtensionMenuItems (extensionObject, menuName) {
|
_getExtensionMenuItems (extensionObject, menuItemFunctionName) {
|
||||||
// Fetch the items appropriate for the target currently being edited. This assumes that menus only
|
// Fetch the items appropriate for the target currently being edited. This assumes that menus only
|
||||||
// collect items when opened by the user while editing a particular target.
|
// collect items when opened by the user while editing a particular target.
|
||||||
const editingTarget = this.runtime.getEditingTarget() || this.runtime.getTargetForStage();
|
const editingTarget = this.runtime.getEditingTarget() || this.runtime.getTargetForStage();
|
||||||
|
@ -338,7 +347,7 @@ class ExtensionManager {
|
||||||
const extensionMessageContext = this.runtime.makeMessageContextForTarget(editingTarget);
|
const extensionMessageContext = this.runtime.makeMessageContextForTarget(editingTarget);
|
||||||
|
|
||||||
// TODO: Fix this to use dispatch.call when extensions are running in workers.
|
// TODO: Fix this to use dispatch.call when extensions are running in workers.
|
||||||
const menuFunc = extensionObject[menuName];
|
const menuFunc = extensionObject[menuItemFunctionName];
|
||||||
const menuItems = menuFunc.call(extensionObject, editingTargetID).map(
|
const menuItems = menuFunc.call(extensionObject, editingTargetID).map(
|
||||||
item => {
|
item => {
|
||||||
item = maybeFormatMessage(item, extensionMessageContext);
|
item = maybeFormatMessage(item, extensionMessageContext);
|
||||||
|
@ -356,7 +365,7 @@ class ExtensionManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!menuItems || menuItems.length < 1) {
|
if (!menuItems || menuItems.length < 1) {
|
||||||
throw new Error(`Extension menu returned no items: ${menuName}`);
|
throw new Error(`Extension menu returned no items: ${menuItemFunctionName}`);
|
||||||
}
|
}
|
||||||
return menuItems;
|
return menuItems;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1413,206 +1413,227 @@ class Scratch3BoostBlocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
MOTOR_ID: [
|
MOTOR_ID: {
|
||||||
{
|
acceptReporters: true,
|
||||||
text: 'A',
|
items: [
|
||||||
value: BoostMotorLabel.A
|
{
|
||||||
},
|
text: 'A',
|
||||||
{
|
value: BoostMotorLabel.A
|
||||||
text: 'B',
|
},
|
||||||
value: BoostMotorLabel.B
|
{
|
||||||
},
|
text: 'B',
|
||||||
{
|
value: BoostMotorLabel.B
|
||||||
text: 'C',
|
},
|
||||||
value: BoostMotorLabel.C
|
{
|
||||||
},
|
text: 'C',
|
||||||
{
|
value: BoostMotorLabel.C
|
||||||
text: 'D',
|
},
|
||||||
value: BoostMotorLabel.D
|
{
|
||||||
},
|
text: 'D',
|
||||||
{
|
value: BoostMotorLabel.D
|
||||||
text: 'AB',
|
},
|
||||||
value: BoostMotorLabel.AB
|
{
|
||||||
},
|
text: 'AB',
|
||||||
{
|
value: BoostMotorLabel.AB
|
||||||
text: 'ABCD',
|
},
|
||||||
value: BoostMotorLabel.ALL
|
{
|
||||||
}
|
text: 'ABCD',
|
||||||
],
|
value: BoostMotorLabel.ALL
|
||||||
MOTOR_REPORTER_ID: [
|
}
|
||||||
{
|
]
|
||||||
text: 'A',
|
},
|
||||||
value: BoostMotorLabel.A
|
MOTOR_REPORTER_ID: {
|
||||||
},
|
acceptReporters: true,
|
||||||
{
|
items: [
|
||||||
text: 'B',
|
{
|
||||||
value: BoostMotorLabel.B
|
text: 'A',
|
||||||
},
|
value: BoostMotorLabel.A
|
||||||
{
|
},
|
||||||
text: 'C',
|
{
|
||||||
value: BoostMotorLabel.C
|
text: 'B',
|
||||||
},
|
value: BoostMotorLabel.B
|
||||||
{
|
},
|
||||||
text: 'D',
|
{
|
||||||
value: BoostMotorLabel.D
|
text: 'C',
|
||||||
}
|
value: BoostMotorLabel.C
|
||||||
],
|
},
|
||||||
MOTOR_DIRECTION: [
|
{
|
||||||
{
|
text: 'D',
|
||||||
text: formatMessage({
|
value: BoostMotorLabel.D
|
||||||
id: 'boost.motorDirection.forward',
|
}
|
||||||
default: 'this way',
|
]
|
||||||
description: 'label for forward element in motor direction menu for LEGO Boost extension'
|
},
|
||||||
}),
|
MOTOR_DIRECTION: {
|
||||||
value: BoostMotorDirection.FORWARD
|
acceptReporters: true,
|
||||||
},
|
items: [
|
||||||
{
|
{
|
||||||
text: formatMessage({
|
text: formatMessage({
|
||||||
id: 'boost.motorDirection.backward',
|
id: 'boost.motorDirection.forward',
|
||||||
default: 'that way',
|
default: 'this way',
|
||||||
description: 'label for backward element in motor direction menu for LEGO Boost extension'
|
description:
|
||||||
}),
|
'label for forward element in motor direction menu for LEGO Boost extension'
|
||||||
value: BoostMotorDirection.BACKWARD
|
}),
|
||||||
},
|
value: BoostMotorDirection.FORWARD
|
||||||
{
|
},
|
||||||
text: formatMessage({
|
{
|
||||||
id: 'boost.motorDirection.reverse',
|
text: formatMessage({
|
||||||
default: 'reverse',
|
id: 'boost.motorDirection.backward',
|
||||||
description: 'label for reverse element in motor direction menu for LEGO Boost extension'
|
default: 'that way',
|
||||||
}),
|
description:
|
||||||
value: BoostMotorDirection.REVERSE
|
'label for backward element in motor direction menu for LEGO Boost extension'
|
||||||
}
|
}),
|
||||||
],
|
value: BoostMotorDirection.BACKWARD
|
||||||
TILT_DIRECTION: [
|
},
|
||||||
{
|
{
|
||||||
text: formatMessage({
|
text: formatMessage({
|
||||||
id: 'boost.tiltDirection.up',
|
id: 'boost.motorDirection.reverse',
|
||||||
default: 'up',
|
default: 'reverse',
|
||||||
description: 'label for up element in tilt direction menu for LEGO Boost extension'
|
description:
|
||||||
}),
|
'label for reverse element in motor direction menu for LEGO Boost extension'
|
||||||
value: BoostTiltDirection.UP
|
}),
|
||||||
},
|
value: BoostMotorDirection.REVERSE
|
||||||
{
|
}
|
||||||
text: formatMessage({
|
]
|
||||||
id: 'boost.tiltDirection.down',
|
},
|
||||||
default: 'down',
|
TILT_DIRECTION: {
|
||||||
description: 'label for down element in tilt direction menu for LEGO Boost extension'
|
acceptReporters: true,
|
||||||
}),
|
items: [
|
||||||
value: BoostTiltDirection.DOWN
|
{
|
||||||
},
|
text: formatMessage({
|
||||||
{
|
id: 'boost.tiltDirection.up',
|
||||||
text: formatMessage({
|
default: 'up',
|
||||||
id: 'boost.tiltDirection.left',
|
description: 'label for up element in tilt direction menu for LEGO Boost extension'
|
||||||
default: 'left',
|
}),
|
||||||
description: 'label for left element in tilt direction menu for LEGO Boost extension'
|
value: BoostTiltDirection.UP
|
||||||
}),
|
},
|
||||||
value: BoostTiltDirection.LEFT
|
{
|
||||||
},
|
text: formatMessage({
|
||||||
{
|
id: 'boost.tiltDirection.down',
|
||||||
text: formatMessage({
|
default: 'down',
|
||||||
id: 'boost.tiltDirection.right',
|
description: 'label for down element in tilt direction menu for LEGO Boost extension'
|
||||||
default: 'right',
|
}),
|
||||||
description: 'label for right element in tilt direction menu for LEGO Boost extension'
|
value: BoostTiltDirection.DOWN
|
||||||
}),
|
},
|
||||||
value: BoostTiltDirection.RIGHT
|
{
|
||||||
}
|
text: formatMessage({
|
||||||
],
|
id: 'boost.tiltDirection.left',
|
||||||
TILT_DIRECTION_ANY: [
|
default: 'left',
|
||||||
{
|
description: 'label for left element in tilt direction menu for LEGO Boost extension'
|
||||||
text: formatMessage({
|
}),
|
||||||
id: 'boost.tiltDirection.up',
|
value: BoostTiltDirection.LEFT
|
||||||
default: 'up'
|
},
|
||||||
}),
|
{
|
||||||
value: BoostTiltDirection.UP
|
text: formatMessage({
|
||||||
},
|
id: 'boost.tiltDirection.right',
|
||||||
{
|
default: 'right',
|
||||||
text: formatMessage({
|
description: 'label for right element in tilt direction menu for LEGO Boost extension'
|
||||||
id: 'boost.tiltDirection.down',
|
}),
|
||||||
default: 'down'
|
value: BoostTiltDirection.RIGHT
|
||||||
}),
|
}
|
||||||
value: BoostTiltDirection.DOWN
|
]
|
||||||
},
|
},
|
||||||
{
|
TILT_DIRECTION_ANY: {
|
||||||
text: formatMessage({
|
acceptReporters: true,
|
||||||
id: 'boost.tiltDirection.left',
|
items: [
|
||||||
default: 'left'
|
{
|
||||||
}),
|
text: formatMessage({
|
||||||
value: BoostTiltDirection.LEFT
|
id: 'boost.tiltDirection.up',
|
||||||
},
|
default: 'up'
|
||||||
{
|
}),
|
||||||
text: formatMessage({
|
value: BoostTiltDirection.UP
|
||||||
id: 'boost.tiltDirection.right',
|
},
|
||||||
default: 'right'
|
{
|
||||||
}),
|
text: formatMessage({
|
||||||
value: BoostTiltDirection.RIGHT
|
id: 'boost.tiltDirection.down',
|
||||||
},
|
default: 'down'
|
||||||
{
|
}),
|
||||||
text: formatMessage({
|
value: BoostTiltDirection.DOWN
|
||||||
id: 'boost.tiltDirection.any',
|
},
|
||||||
default: 'any',
|
{
|
||||||
description: 'label for any element in tilt direction menu for LEGO Boost extension'
|
text: formatMessage({
|
||||||
}),
|
id: 'boost.tiltDirection.left',
|
||||||
value: BoostTiltDirection.ANY
|
default: 'left'
|
||||||
}
|
}),
|
||||||
],
|
value: BoostTiltDirection.LEFT
|
||||||
COLOR: [
|
},
|
||||||
{
|
{
|
||||||
text: formatMessage({
|
text: formatMessage({
|
||||||
id: 'boost.color.red',
|
id: 'boost.tiltDirection.right',
|
||||||
default: 'red',
|
default: 'right'
|
||||||
description: 'the color red'
|
}),
|
||||||
}),
|
value: BoostTiltDirection.RIGHT
|
||||||
value: BoostColor.RED
|
},
|
||||||
},
|
{
|
||||||
{
|
text: formatMessage({
|
||||||
text: formatMessage({
|
id: 'boost.tiltDirection.any',
|
||||||
id: 'boost.color.blue',
|
default: 'any',
|
||||||
default: 'blue',
|
description: 'label for any element in tilt direction menu for LEGO Boost extension'
|
||||||
description: 'the color blue'
|
}),
|
||||||
}),
|
value: BoostTiltDirection.ANY
|
||||||
value: BoostColor.BLUE
|
}
|
||||||
},
|
]
|
||||||
{
|
},
|
||||||
text: formatMessage({
|
COLOR: {
|
||||||
id: 'boost.color.green',
|
acceptReporters: true,
|
||||||
default: 'green',
|
items: [
|
||||||
description: 'the color green'
|
{
|
||||||
}),
|
text: formatMessage({
|
||||||
value: BoostColor.GREEN
|
id: 'boost.color.red',
|
||||||
},
|
default: 'red',
|
||||||
{
|
description: 'the color red'
|
||||||
text: formatMessage({
|
}),
|
||||||
id: 'boost.color.yellow',
|
value: BoostColor.RED
|
||||||
default: 'yellow',
|
},
|
||||||
description: 'the color yellow'
|
{
|
||||||
}),
|
text: formatMessage({
|
||||||
value: BoostColor.YELLOW
|
id: 'boost.color.blue',
|
||||||
},
|
default: 'blue',
|
||||||
{
|
description: 'the color blue'
|
||||||
text: formatMessage({
|
}),
|
||||||
id: 'boost.color.white',
|
value: BoostColor.BLUE
|
||||||
default: 'white',
|
},
|
||||||
desription: 'the color white'
|
{
|
||||||
}),
|
text: formatMessage({
|
||||||
value: BoostColor.WHITE
|
id: 'boost.color.green',
|
||||||
},
|
default: 'green',
|
||||||
{
|
description: 'the color green'
|
||||||
text: formatMessage({
|
}),
|
||||||
id: 'boost.color.black',
|
value: BoostColor.GREEN
|
||||||
default: 'black',
|
},
|
||||||
description: 'the color black'
|
{
|
||||||
}),
|
text: formatMessage({
|
||||||
value: BoostColor.BLACK
|
id: 'boost.color.yellow',
|
||||||
},
|
default: 'yellow',
|
||||||
{
|
description: 'the color yellow'
|
||||||
text: formatMessage({
|
}),
|
||||||
id: 'boost.color.any',
|
value: BoostColor.YELLOW
|
||||||
default: 'any color',
|
},
|
||||||
description: 'any color'
|
{
|
||||||
}),
|
text: formatMessage({
|
||||||
value: BoostColor.ANY
|
id: 'boost.color.white',
|
||||||
}
|
default: 'white',
|
||||||
]
|
desription: 'the color white'
|
||||||
|
}),
|
||||||
|
value: BoostColor.WHITE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: formatMessage({
|
||||||
|
id: 'boost.color.black',
|
||||||
|
default: 'black',
|
||||||
|
description: 'the color black'
|
||||||
|
}),
|
||||||
|
value: BoostColor.BLACK
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: formatMessage({
|
||||||
|
id: 'boost.color.any',
|
||||||
|
default: 'any color',
|
||||||
|
description: 'any color'
|
||||||
|
}),
|
||||||
|
value: BoostColor.ANY
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1137,8 +1137,14 @@ class Scratch3Ev3Blocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
motorPorts: this._formatMenu(Ev3MotorMenu),
|
motorPorts: {
|
||||||
sensorPorts: this._formatMenu(Ev3SensorMenu)
|
acceptReporters: true,
|
||||||
|
items: this._formatMenu(Ev3MotorMenu)
|
||||||
|
},
|
||||||
|
sensorPorts: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this._formatMenu(Ev3SensorMenu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -757,11 +757,26 @@ class Scratch3GdxForBlocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
pushPullOptions: this.PUSH_PULL_MENU,
|
pushPullOptions: {
|
||||||
gestureOptions: this.GESTURE_MENU,
|
acceptReporters: true,
|
||||||
axisOptions: this.AXIS_MENU,
|
items: this.PUSH_PULL_MENU
|
||||||
tiltOptions: this.TILT_MENU,
|
},
|
||||||
tiltAnyOptions: this.TILT_MENU_ANY
|
gestureOptions: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this.GESTURE_MENU
|
||||||
|
},
|
||||||
|
axisOptions: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this.AXIS_MENU
|
||||||
|
},
|
||||||
|
tiltOptions: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this.TILT_MENU
|
||||||
|
},
|
||||||
|
tiltAnyOptions: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this.TILT_MENU_ANY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,55 +207,61 @@ class Scratch3MakeyMakeyBlocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
KEY: [
|
KEY: {
|
||||||
{
|
acceptReporters: true,
|
||||||
text: formatMessage({
|
items: [
|
||||||
id: 'makeymakey.spaceKey',
|
{
|
||||||
default: 'space',
|
text: formatMessage({
|
||||||
description: 'The space key on a computer keyboard.'
|
id: 'makeymakey.spaceKey',
|
||||||
}),
|
default: 'space',
|
||||||
value: KEY_ID_SPACE
|
description: 'The space key on a computer keyboard.'
|
||||||
},
|
}),
|
||||||
{
|
value: KEY_ID_SPACE
|
||||||
text: formatMessage({
|
},
|
||||||
id: 'makeymakey.upArrow',
|
{
|
||||||
default: 'up arrow',
|
text: formatMessage({
|
||||||
description: 'The up arrow key on a computer keyboard.'
|
id: 'makeymakey.upArrow',
|
||||||
}),
|
default: 'up arrow',
|
||||||
value: KEY_ID_UP
|
description: 'The up arrow key on a computer keyboard.'
|
||||||
},
|
}),
|
||||||
{
|
value: KEY_ID_UP
|
||||||
text: formatMessage({
|
},
|
||||||
id: 'makeymakey.downArrow',
|
{
|
||||||
default: 'down arrow',
|
text: formatMessage({
|
||||||
description: 'The down arrow key on a computer keyboard.'
|
id: 'makeymakey.downArrow',
|
||||||
}),
|
default: 'down arrow',
|
||||||
value: KEY_ID_DOWN
|
description: 'The down arrow key on a computer keyboard.'
|
||||||
},
|
}),
|
||||||
{
|
value: KEY_ID_DOWN
|
||||||
text: formatMessage({
|
},
|
||||||
id: 'makeymakey.rightArrow',
|
{
|
||||||
default: 'right arrow',
|
text: formatMessage({
|
||||||
description: 'The right arrow key on a computer keyboard.'
|
id: 'makeymakey.rightArrow',
|
||||||
}),
|
default: 'right arrow',
|
||||||
value: KEY_ID_RIGHT
|
description: 'The right arrow key on a computer keyboard.'
|
||||||
},
|
}),
|
||||||
{
|
value: KEY_ID_RIGHT
|
||||||
text: formatMessage({
|
},
|
||||||
id: 'makeymakey.leftArrow',
|
{
|
||||||
default: 'left arrow',
|
text: formatMessage({
|
||||||
description: 'The left arrow key on a computer keyboard.'
|
id: 'makeymakey.leftArrow',
|
||||||
}),
|
default: 'left arrow',
|
||||||
value: KEY_ID_LEFT
|
description: 'The left arrow key on a computer keyboard.'
|
||||||
},
|
}),
|
||||||
{text: 'w', value: 'w'},
|
value: KEY_ID_LEFT
|
||||||
{text: 'a', value: 'a'},
|
},
|
||||||
{text: 's', value: 's'},
|
{text: 'w', value: 'w'},
|
||||||
{text: 'd', value: 'd'},
|
{text: 'a', value: 'a'},
|
||||||
{text: 'f', value: 'f'},
|
{text: 's', value: 's'},
|
||||||
{text: 'g', value: 'g'}
|
{text: 'd', value: 'd'},
|
||||||
],
|
{text: 'f', value: 'f'},
|
||||||
SEQUENCE: this.buildSequenceMenu(this.DEFAULT_SEQUENCES)
|
{text: 'g', value: 'g'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
SEQUENCE: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this.buildSequenceMenu(this.DEFAULT_SEQUENCES)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -746,12 +746,30 @@ class Scratch3MicroBitBlocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
buttons: this.BUTTONS_MENU,
|
buttons: {
|
||||||
gestures: this.GESTURES_MENU,
|
acceptReporters: true,
|
||||||
pinState: this.PIN_STATE_MENU,
|
items: this.BUTTONS_MENU
|
||||||
tiltDirection: this.TILT_DIRECTION_MENU,
|
},
|
||||||
tiltDirectionAny: this.TILT_DIRECTION_ANY_MENU,
|
gestures: {
|
||||||
touchPins: ['0', '1', '2']
|
acceptReporters: true,
|
||||||
|
items: this.GESTURES_MENU
|
||||||
|
},
|
||||||
|
pinState: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this.PIN_STATE_MENU
|
||||||
|
},
|
||||||
|
tiltDirection: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this.TILT_DIRECTION_MENU
|
||||||
|
},
|
||||||
|
tiltDirectionAny: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this.TILT_DIRECTION_ANY_MENU
|
||||||
|
},
|
||||||
|
touchPins: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: ['0', '1', '2']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -911,8 +911,14 @@ class Scratch3MusicBlocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
DRUM: this._buildMenu(this.DRUM_INFO),
|
DRUM: {
|
||||||
INSTRUMENT: this._buildMenu(this.INSTRUMENT_INFO)
|
acceptReporters: true,
|
||||||
|
items: this._buildMenu(this.DRUM_INFO)
|
||||||
|
},
|
||||||
|
INSTRUMENT: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this._buildMenu(this.INSTRUMENT_INFO)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -477,7 +477,10 @@ class Scratch3PenBlocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
colorParam: this._initColorParam()
|
colorParam: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this._initColorParam()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -469,8 +469,14 @@ class Scratch3Text2SpeechBlocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
voices: this.getVoiceMenu(),
|
voices: {
|
||||||
languages: this.getLanguageMenu()
|
acceptReporters: true,
|
||||||
|
items: this.getVoiceMenu()
|
||||||
|
},
|
||||||
|
languages: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this.getLanguageMenu()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,10 @@ class Scratch3TranslateBlocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
languages: this._supportedLanguages
|
languages: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this._supportedLanguages
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -488,9 +488,18 @@ class Scratch3VideoSensingBlocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
ATTRIBUTE: this._buildMenu(this.ATTRIBUTE_INFO),
|
ATTRIBUTE: {
|
||||||
SUBJECT: this._buildMenu(this.SUBJECT_INFO),
|
acceptReporters: true,
|
||||||
VIDEO_STATE: this._buildMenu(this.VIDEO_STATE_INFO)
|
items: this._buildMenu(this.ATTRIBUTE_INFO)
|
||||||
|
},
|
||||||
|
SUBJECT: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this._buildMenu(this.SUBJECT_INFO)
|
||||||
|
},
|
||||||
|
VIDEO_STATE: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: this._buildMenu(this.VIDEO_STATE_INFO)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1133,139 +1133,157 @@ class Scratch3WeDo2Blocks {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
menus: {
|
menus: {
|
||||||
MOTOR_ID: [
|
MOTOR_ID: {
|
||||||
{
|
acceptReporters: true,
|
||||||
text: formatMessage({
|
items: [
|
||||||
id: 'wedo2.motorId.default',
|
{
|
||||||
default: 'motor',
|
text: formatMessage({
|
||||||
description: 'label for motor element in motor menu for LEGO WeDo 2 extension'
|
id: 'wedo2.motorId.default',
|
||||||
}),
|
default: 'motor',
|
||||||
value: WeDo2MotorLabel.DEFAULT
|
description: 'label for motor element in motor menu for LEGO WeDo 2 extension'
|
||||||
},
|
}),
|
||||||
{
|
value: WeDo2MotorLabel.DEFAULT
|
||||||
text: formatMessage({
|
},
|
||||||
id: 'wedo2.motorId.a',
|
{
|
||||||
default: 'motor A',
|
text: formatMessage({
|
||||||
description: 'label for motor A element in motor menu for LEGO WeDo 2 extension'
|
id: 'wedo2.motorId.a',
|
||||||
}),
|
default: 'motor A',
|
||||||
value: WeDo2MotorLabel.A
|
description: 'label for motor A element in motor menu for LEGO WeDo 2 extension'
|
||||||
},
|
}),
|
||||||
{
|
value: WeDo2MotorLabel.A
|
||||||
text: formatMessage({
|
},
|
||||||
id: 'wedo2.motorId.b',
|
{
|
||||||
default: 'motor B',
|
text: formatMessage({
|
||||||
description: 'label for motor B element in motor menu for LEGO WeDo 2 extension'
|
id: 'wedo2.motorId.b',
|
||||||
}),
|
default: 'motor B',
|
||||||
value: WeDo2MotorLabel.B
|
description: 'label for motor B element in motor menu for LEGO WeDo 2 extension'
|
||||||
},
|
}),
|
||||||
{
|
value: WeDo2MotorLabel.B
|
||||||
text: formatMessage({
|
},
|
||||||
id: 'wedo2.motorId.all',
|
{
|
||||||
default: 'all motors',
|
text: formatMessage({
|
||||||
description: 'label for all motors element in motor menu for LEGO WeDo 2 extension'
|
id: 'wedo2.motorId.all',
|
||||||
}),
|
default: 'all motors',
|
||||||
value: WeDo2MotorLabel.ALL
|
description: 'label for all motors element in motor menu for LEGO WeDo 2 extension'
|
||||||
}
|
}),
|
||||||
],
|
value: WeDo2MotorLabel.ALL
|
||||||
MOTOR_DIRECTION: [
|
}
|
||||||
{
|
]
|
||||||
text: formatMessage({
|
},
|
||||||
id: 'wedo2.motorDirection.forward',
|
MOTOR_DIRECTION: {
|
||||||
default: 'this way',
|
acceptReporters: true,
|
||||||
description: 'label for forward element in motor direction menu for LEGO WeDo 2 extension'
|
items: [
|
||||||
}),
|
{
|
||||||
value: WeDo2MotorDirection.FORWARD
|
text: formatMessage({
|
||||||
},
|
id: 'wedo2.motorDirection.forward',
|
||||||
{
|
default: 'this way',
|
||||||
text: formatMessage({
|
description:
|
||||||
id: 'wedo2.motorDirection.backward',
|
'label for forward element in motor direction menu for LEGO WeDo 2 extension'
|
||||||
default: 'that way',
|
}),
|
||||||
description: 'label for backward element in motor direction menu for LEGO WeDo 2 extension'
|
value: WeDo2MotorDirection.FORWARD
|
||||||
}),
|
},
|
||||||
value: WeDo2MotorDirection.BACKWARD
|
{
|
||||||
},
|
text: formatMessage({
|
||||||
{
|
id: 'wedo2.motorDirection.backward',
|
||||||
text: formatMessage({
|
default: 'that way',
|
||||||
id: 'wedo2.motorDirection.reverse',
|
description:
|
||||||
default: 'reverse',
|
'label for backward element in motor direction menu for LEGO WeDo 2 extension'
|
||||||
description: 'label for reverse element in motor direction menu for LEGO WeDo 2 extension'
|
}),
|
||||||
}),
|
value: WeDo2MotorDirection.BACKWARD
|
||||||
value: WeDo2MotorDirection.REVERSE
|
},
|
||||||
}
|
{
|
||||||
],
|
text: formatMessage({
|
||||||
TILT_DIRECTION: [
|
id: 'wedo2.motorDirection.reverse',
|
||||||
{
|
default: 'reverse',
|
||||||
text: formatMessage({
|
description:
|
||||||
id: 'wedo2.tiltDirection.up',
|
'label for reverse element in motor direction menu for LEGO WeDo 2 extension'
|
||||||
default: 'up',
|
}),
|
||||||
description: 'label for up element in tilt direction menu for LEGO WeDo 2 extension'
|
value: WeDo2MotorDirection.REVERSE
|
||||||
}),
|
}
|
||||||
value: WeDo2TiltDirection.UP
|
]
|
||||||
},
|
},
|
||||||
{
|
TILT_DIRECTION: {
|
||||||
text: formatMessage({
|
acceptReporters: true,
|
||||||
id: 'wedo2.tiltDirection.down',
|
items: [
|
||||||
default: 'down',
|
{
|
||||||
description: 'label for down element in tilt direction menu for LEGO WeDo 2 extension'
|
text: formatMessage({
|
||||||
}),
|
id: 'wedo2.tiltDirection.up',
|
||||||
value: WeDo2TiltDirection.DOWN
|
default: 'up',
|
||||||
},
|
description: 'label for up element in tilt direction menu for LEGO WeDo 2 extension'
|
||||||
{
|
}),
|
||||||
text: formatMessage({
|
value: WeDo2TiltDirection.UP
|
||||||
id: 'wedo2.tiltDirection.left',
|
},
|
||||||
default: 'left',
|
{
|
||||||
description: 'label for left element in tilt direction menu for LEGO WeDo 2 extension'
|
text: formatMessage({
|
||||||
}),
|
id: 'wedo2.tiltDirection.down',
|
||||||
value: WeDo2TiltDirection.LEFT
|
default: 'down',
|
||||||
},
|
description: 'label for down element in tilt direction menu for LEGO WeDo 2 extension'
|
||||||
{
|
}),
|
||||||
text: formatMessage({
|
value: WeDo2TiltDirection.DOWN
|
||||||
id: 'wedo2.tiltDirection.right',
|
},
|
||||||
default: 'right',
|
{
|
||||||
description: 'label for right element in tilt direction menu for LEGO WeDo 2 extension'
|
text: formatMessage({
|
||||||
}),
|
id: 'wedo2.tiltDirection.left',
|
||||||
value: WeDo2TiltDirection.RIGHT
|
default: 'left',
|
||||||
}
|
description: 'label for left element in tilt direction menu for LEGO WeDo 2 extension'
|
||||||
],
|
}),
|
||||||
TILT_DIRECTION_ANY: [
|
value: WeDo2TiltDirection.LEFT
|
||||||
{
|
},
|
||||||
text: formatMessage({
|
{
|
||||||
id: 'wedo2.tiltDirection.up',
|
text: formatMessage({
|
||||||
default: 'up'
|
id: 'wedo2.tiltDirection.right',
|
||||||
}),
|
default: 'right',
|
||||||
value: WeDo2TiltDirection.UP
|
description: 'label for right element in tilt direction menu for LEGO WeDo 2 extension'
|
||||||
},
|
}),
|
||||||
{
|
value: WeDo2TiltDirection.RIGHT
|
||||||
text: formatMessage({
|
}
|
||||||
id: 'wedo2.tiltDirection.down',
|
]
|
||||||
default: 'down'
|
},
|
||||||
}),
|
TILT_DIRECTION_ANY: {
|
||||||
value: WeDo2TiltDirection.DOWN
|
acceptReporters: true,
|
||||||
},
|
items: [
|
||||||
{
|
{
|
||||||
text: formatMessage({
|
text: formatMessage({
|
||||||
id: 'wedo2.tiltDirection.left',
|
id: 'wedo2.tiltDirection.up',
|
||||||
default: 'left'
|
default: 'up'
|
||||||
}),
|
}),
|
||||||
value: WeDo2TiltDirection.LEFT
|
value: WeDo2TiltDirection.UP
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: formatMessage({
|
text: formatMessage({
|
||||||
id: 'wedo2.tiltDirection.right',
|
id: 'wedo2.tiltDirection.down',
|
||||||
default: 'right'
|
default: 'down'
|
||||||
}),
|
}),
|
||||||
value: WeDo2TiltDirection.RIGHT
|
value: WeDo2TiltDirection.DOWN
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: formatMessage({
|
text: formatMessage({
|
||||||
id: 'wedo2.tiltDirection.any',
|
id: 'wedo2.tiltDirection.left',
|
||||||
default: 'any',
|
default: 'left'
|
||||||
description: 'label for any element in tilt direction menu for LEGO WeDo 2 extension'
|
}),
|
||||||
}),
|
value: WeDo2TiltDirection.LEFT
|
||||||
value: WeDo2TiltDirection.ANY
|
},
|
||||||
}
|
{
|
||||||
],
|
text: formatMessage({
|
||||||
OP: ['<', '>']
|
id: 'wedo2.tiltDirection.right',
|
||||||
|
default: 'right'
|
||||||
|
}),
|
||||||
|
value: WeDo2TiltDirection.RIGHT
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: formatMessage({
|
||||||
|
id: 'wedo2.tiltDirection.any',
|
||||||
|
default: 'any',
|
||||||
|
description: 'label for any element in tilt direction menu for LEGO WeDo 2 extension'
|
||||||
|
}),
|
||||||
|
value: WeDo2TiltDirection.ANY
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
OP: {
|
||||||
|
acceptReporters: true,
|
||||||
|
items: ['<', '>']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue