Le champ de menu déroulant stocke une chaîne comme valeur et une chaîne comme texte. La valeur est une clé neutre qui sera utilisée pour accéder au texte et ne sera pas traduite lorsque Blockly passera d'une langue à une autre. Le texte est une chaîne lisible qui sera affichée à l'utilisateur.
Champ de menu déroulant

Champ de menu déroulant avec l'éditeur ouvert

Champ de menu déroulant sur un bloc réduit

Création
Le constructeur de menu déroulant accepte un générateur de menu et un validateur facultatif . Le générateur de menu est un tableau d'options (où chaque option contient une partie lisible et une chaîne neutre) ou une fonction qui génère un tableau d'options. La partie lisible de chaque option peut être une chaîne, une image ou un élément HTML, et le tableau peut contenir un mélange d'options de différents types.
Menus déroulants de texte simple

JSON
{
"type": "example_dropdown",
"message0": "drop down: %1",
"args0": [
{
"type": "field_dropdown",
"name": "FIELDNAME",
"options": [
[ "first item", "ITEM1" ],
[ "second item", "ITEM2" ]
]
}
]
}
JavaScript
Blockly.Blocks['example_dropdown'] = {
init: function() {
this.appendDummyInput()
.appendField('drop down:')
.appendField(new Blockly.FieldDropdown([
['first item', 'ITEM1'],
['second item', 'ITEM2']
]), 'FIELDNAME');
}
};
En séparant les informations lisibles de la clé neutre, le paramètre du menu déroulant peut être conservé d'une langue à l'autre. Par
exemple, une version anglaise d'un bloc peut définir [['left', 'LEFT'], ['right',
'RIGHT]], tandis qu'une version allemande du même bloc définirait [['links',
'LEFT'], ['rechts', 'RIGHT]].
Menus déroulants d'images
Les options d'un menu déroulant peuvent être des images, représentées sous forme d'objets avec les propriétés src, width, height et alt.

JSON
{
"type": "image_dropdown",
"message0": "flag %1",
"args0": [
{
"type": "field_dropdown",
"name": "FLAG",
"options": [
["none", "NONE"],
[{"src": "canada.png", "width": 50, "height": 25, "alt": "Canada"}, "CANADA"],
[{"src": "usa.png", "width": 50, "height": 25, "alt": "USA"}, "USA"],
[{"src": "mexico.png", "width": 50, "height": 25, "alt": "Mexico"}, "MEXICO"]
]
}
]
}
JavaScript
Blockly.Blocks['image_dropdown'] = {
init: function() {
var input = this.appendDummyInput()
.appendField('flag');
var options = [
['none', 'NONE'],
[{'src': 'canada.png', 'width': 50, 'height': 25, 'alt': 'Canada'}, 'CANADA'],
[{'src': 'usa.png', 'width': 50, 'height': 25, 'alt': 'USA'}, 'USA'],
[{'src': 'mexico.png', 'width': 50, 'height': 25, 'alt': 'Mexico'}, 'MEXICO']
];
input.appendField(new Blockly.FieldDropdown(options), 'FLAG');
}
};
Menus déroulants HTML
Une option peut être n'importe quel élément HTML, à condition qu'elle ne soit pas trop volumineuse et qu'elle ne tente pas de gérer les événements de la souris ou du clavier. (Il vous incombe de respecter ces règles. Blockly ne les applique pas.)
Lorsque le menu déroulant est ouvert, la liste affiche l'élément HTML. Lorsqu'il est fermé et que l'élément est l'option sélectionnée, la liste affiche (par ordre de préférence décroissant) l'attribut title de l'élément, son attribut aria-label ou sa propriété innerText.

JSON
{
"type": "flags_with_text_dropdown",
"message0": "flag with text %1",
"args0": [
{
"type": "field_dropdown",
"name": "FLAG_WITH_TEXT",
"options": [
["x", "X"], // Placeholder. An empty array throws an exception.
]
}
],
// Use an extension to add the HTML element options.
"extensions": ["flag_with_text_extension"]
}
Blockly.Extensions.register('flag_with_text_extension',
function() {
function createFlagWithTextDiv(text, src) {
const div = document.createElement('div');
div.setAttribute('style', 'width: 75px;');
div.setAttribute('title', text);
const img = document.createElement('img');
img.setAttribute('src', src);
img.setAttribute('style', 'height: 25px; display: block; margin: auto;');
div.appendChild(img);
const para = document.createElement('p');
para.innerText = text;
para.setAttribute('style', 'text-align: center; margin: 5px;');
div.appendChild(para);
return div;
}
const canadaDiv = createFlagWithTextDiv('Canada', 'canada.png');
const usaDiv = createFlagWithTextDiv('USA', 'usa.png');
const mexicoDiv = createFlagWithTextDiv('Mexico', 'mexico.png');
const options = [
['none', 'NONE'],
[canadaDiv, 'CANADA'],
[usaDiv, 'USA'],
[mexicoDiv, 'MEXICO']
];
this.getField('FLAG_WITH_TEXT').setOptions(options);
});
Cela se fait à l'aide d'une extension JSON extension.
JavaScript
function createFlagWithTextDiv(text, src) {
const div = document.createElement('div');
div.setAttribute('style', 'width: 75px;');
div.setAttribute('title', text);
const img = document.createElement('img');
img.setAttribute('src', src);
img.setAttribute('style', 'height: 25px; display: block; margin: auto;');
div.appendChild(img);
const para = document.createElement('p');
para.innerText = text;
para.setAttribute('style', 'text-align: center; margin: 5px;');
div.appendChild(para);
return div;
}
const canadaDiv = createFlagWithTextDiv('Canada', 'canada.png');
const usaDiv = createFlagWithTextDiv('USA', 'usa.png');
const mexicoDiv = createFlagWithTextDiv('Mexico', 'mexico.png');
Blockly.Blocks['flags_with_text_dropdown'] = {
init: function() {
const input = this.appendDummyInput()
.appendField('flag with text');
const options = [
['none', 'NONE'],
[canadaDiv, 'CANADA'],
[usaDiv, 'USA'],
[mexicoDiv, 'MEXICO']
];
input.appendField(new Blockly.FieldDropdown(options), 'FLAG_WITH_TEXT');
}
};
Menus déroulants dynamiques

JSON
{
"type": "dynamic_dropdown",
"message0": "day %1",
"args0": [
{
"type": "field_dropdown",
"name": "DAY",
"options": [
["x", "X"], // Placeholder. An empty array throws an exception.
]
}
],
// Use an extension to set the menu function.
"extensions": ["dynamic_menu_extension"]
}
Blockly.Extensions.register('dynamic_menu_extension',
function() {
this.getField('DAY').setOptions(
function() {
var options = [];
var now = Date.now();
for(var i = 0; i < 7; i++) {
var dateString = String(new Date(now)).substring(0, 3);
options.push([dateString, dateString.toUpperCase()]);
now += 24 * 60 * 60 * 1000;
}
return options;
});
});
Cela se fait à l'aide d'une extension JSON extension.
JavaScript
Blockly.Blocks['dynamic_dropdown'] = {
init: function() {
var input = this.appendDummyInput()
.appendField('day')
.appendField(new Blockly.FieldDropdown(
this.generateOptions), 'DAY');
},
generateOptions: function() {
var options = [];
var now = Date.now();
for(var i = 0; i < 7; i++) {
var dateString = String(new Date(now)).substring(0, 3);
options.push([dateString, dateString.toUpperCase()]);
now += 24 * 60 * 60 * 1000;
}
return options;
}
};
Un menu déroulant peut également être fourni avec une fonction au lieu d'une liste d'options statiques, ce qui permet aux options d'être dynamiques. La fonction doit renvoyer un tableau d'options au même format [human-readable-value, language-neutral-key] que les options statiques. Chaque fois que l'utilisateur clique sur le menu déroulant, la fonction est exécutée et les options sont recalculées.
Séparateurs
Utilisez la chaîne 'separator' pour ajouter une ligne entre les options d'un menu déroulant.

JSON
{
"type": "separator_dropdown",
"message0": "food %1",
"args0": [
{
"type": "field_dropdown",
"name": "FOOD",
"options": [
["water", "WATER"],
["juice", "JUICE"],
"separator",
["salad", "SALAD"],
["soup", "SOUP"],
]
}
]
}
JavaScript
Blockly.Blocks["separator_dropdown"] = {
init: function() {
var input = this.appendDummyInput()
.appendField("food1");
var options = [
["water", "WATER"],
["juice", "JUICE"],
"separator",
["salad", "SALAD"],
["soup", "SOUP"],
];
input.appendField(new Blockly.FieldDropdown(options), "FOOD");
}
};
Sérialisation
JSON
Le code JSON d'un champ de menu déroulant se présente comme suit :
{
"fields": {
"FIELDNAME": "LANGUAGE-NEUTRAL-KEY"
}
}
Où FIELDNAME est une chaîne faisant référence à un champ de menu déroulant, et la valeur est celle à appliquer au champ. La valeur doit être une clé d'option neutre.
XML
Le code XML d'un champ de menu déroulant se présente comme suit :
<field name="FIELDNAME">LANGUAGE-NEUTRAL-KEY</field>
Où l'attribut name du champ contient une chaîne faisant référence à un champ de menu déroulant, et le texte interne est la valeur à appliquer au champ. Le texte interne doit être une clé d'option neutre valide.
Personnalisation
Flèche du menu déroulant
La propriété Blockly.FieldDropdown.ARROW_CHAR permet de modifier le caractère Unicode représentant la flèche du menu déroulant.

La propriété ARROW_CHAR est définie par défaut sur \u25BC (▼) sur Android et sur \u25BE (▾) dans les autres cas.
Il s'agit d'une propriété globale. Par conséquent, elle modifie tous les champs de menu déroulant lorsqu'elle est définie.
Hauteur du menu
La propriété Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH permet de modifier la hauteur maximale du menu. Elle est définie comme un pourcentage de la hauteur de la fenêtre d'affichage, qui correspond à la fenêtre.
La propriété MAX_MENU_HEIGHT_VH est définie par défaut sur 0,45.
Il s'agit d'une propriété globale. Par conséquent, elle modifie tous les champs de menu déroulant lorsqu'elle est définie.
Correspondance des préfixes/suffixes
Si toutes les options du menu déroulant partagent des mots de préfixe et/ou de suffixe communs, ces mots sont automatiquement factorisés et insérés en tant que texte statique. Voici deux façons de créer le même bloc (la première sans correspondance de suffixe et la seconde avec) :
Sans correspondance de suffixe :
JSON
{
"type": "dropdown_no_matching",
"message0": "hello %1",
"args0": [
{
"type": "field_dropdown",
"name": "MODE",
"options": [
["world", "WORLD"],
["computer", "CPU"]
]
}
]
}
JavaScript
Blockly.Blocks['dropdown_no_matching'] = {
init: function() {
var options = [
['world', 'WORLD'],
['computer', 'CPU']
];
this.appendDummyInput()
.appendField('hello')
.appendField(new Blockly.FieldDropdown(options), 'MODE');
}
};
Avec correspondance de suffixe :
JSON
{
"type": "dropdown_with_matching",
"message0": "%1",
"args0": [
{
"type": "field_dropdown",
"name": "MODE",
"options": [
["hello world", "WORLD"],
["hello computer", "CPU"]
]
}
]
}
JavaScript
Blockly.Blocks['dropdown_with_matching'] = {
init: function() {
var options = [
['hello world', 'WORLD'],
['hello computer', 'CPU']
];
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown(options), 'MODE');
}
};

L'un des avantages de cette approche est que le bloc est plus facile à traduire dans d'autres langues. Le code précédent contient les chaînes 'hello', 'world' et
'computer', tandis que le code révisé contient les chaînes 'hello world' et
'hello computer'. Les traducteurs ont beaucoup plus de facilité à traduire des expressions que des mots isolés.
Un autre avantage de cette approche est que l'ordre des mots change souvent d'une langue à l'autre. Imaginez une langue qui utilise 'world hello' et 'computer hello'.
L'algorithme de correspondance de suffixe détecte le 'hello' commun et l'affiche
après le menu déroulant.
Toutefois, la correspondance des préfixes/suffixes échoue parfois. Dans certains cas, deux mots doivent toujours être associés et le préfixe ne doit pas être factorisé.
Par exemple 'drive red car' et 'drive red truck' ne devraient probablement
avoir 'drive' factorisé, et non 'drive red'. L'espace insécable Unicode
'\u00A0'peut être utilisé à la place d'un espace normal pour supprimer le
matcher de préfixe/suffixe. Ainsi, l'exemple ci-dessus peut être corrigé avec
'drive red\u00A0car' et 'drive red\u00A0truck'.
La correspondance des préfixes/suffixes échoue également dans les langues qui ne séparent pas les mots individuels par des espaces. Le chinois en est un bon exemple. La chaîne
'訪問中國' signifie 'visit China'. Notez l'absence d'espaces entre les mots.
Collectivement, les deux derniers caractères ('中國') sont le mot pour 'China',
mais s'ils sont divisés, ils signifient respectivement 'centre' et 'country'. Pour que la correspondance des préfixes/suffixes fonctionne dans des langues telles que le chinois, insérez simplement un espace à l'endroit où la coupure doit avoir lieu. Par exemple '訪問 中國' et
'訪問 美國' donneraient "visit [China/USA]", tandis que '訪問 中 國' et
'訪問 美 國' donneraient "visit [centre/beautiful] country".
Créer un validateur de menu déroulant
La valeur d'un champ de menu déroulant est une chaîne neutre. Par conséquent, tous les validateurs doivent
accepter une chaîne et renvoyer une chaîne qui est une option disponible, null, ou
undefined.
Si votre programme de validation renvoie autre chose, le comportement de Blockly n'est pas défini et votre programme peut planter.
Par exemple, vous pouvez définir un champ de menu déroulant avec trois options et un validateur comme suit :
validate: function(newValue) {
this.getSourceBlock().updateConnections(newValue);
return newValue;
},
init: function() {
var options = [
['has neither', 'NEITHER'],
['has statement', 'STATEMENT'],
['has value', 'VALUE'],
];
this.appendDummyInput()
// Pass the field constructor the options list, the validator, and the name.
.appendField(new Blockly.FieldDropdown(options, this.validate), 'MODE');
}
validate renvoie toujours la valeur qui lui a été transmise, mais il appelle la fonction d'assistance updateConnection, qui ajoute ou supprime des entrées en fonction de la valeur du menu déroulant :
updateConnections: function(newValue) {
this.removeInput('STATEMENT', /* no error */ true);
this.removeInput('VALUE', /* no error */ true);
if (newValue == 'STATEMENT') {
this.appendStatementInput('STATEMENT');
} else if (newValue == 'VALUE') {
this.appendValueInput('VALUE');
}
}
