Drop-down-Felder

Im Drop-down-Feld wird ein String als Wert und ein String als Text gespeichert. Der Wert ist ein sprachneutraler Schlüssel, der für den Zugriff auf den Text verwendet wird und nicht übersetzt wird, wenn Blockly zwischen Sprachen wechselt. Der Text ist ein für Menschen lesbarer String, der dem Nutzer angezeigt wird.

Ein Block mit dem Label „drop down:“ (Drop-down-Menü:), ein Drop-down-Feld mit der Auswahl „first“ (Erster) und dem Label „item“ (Element).

Derselbe Block mit geöffnetem Drop-down-Menü. Das Drop-down-Menü enthält die Elemente „first“ und „second“.

Derselbe Block nach dem Minimieren. Es hat das Label „drop down: first item“ (Dropdown-Menü: erstes Element) und einen gezackten rechten Rand, um anzuzeigen, dass es minimiert ist.

Erstellung

Der Drop-down-Konstruktor akzeptiert einen Menügenerator und einen optionalen Validator. Der Menügenerator ist entweder ein Array mit Optionen (wobei jede Option einen menschenlesbaren Teil und einen sprachneutralen String enthält) oder eine Funktion, die ein Array mit Optionen generiert. Der für Menschen lesbare Teil jeder Option kann ein String, ein Bild oder ein HTML-Element sein. Das Array kann eine Mischung aus Optionen verschiedener Typen enthalten.

Einfache Text-Dropdown-Menüs

Dropdown-Menü mit zwei Textoptionen öffnen

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');
  }
};

Wenn die für Menschen lesbaren Informationen vom sprachneutralen Schlüssel getrennt werden, bleibt die Einstellung des Drop-down-Menüs sprachübergreifend erhalten. In einer englischen Version eines Blocks kann beispielsweise [['left', 'LEFT'], ['right', 'RIGHT]] definiert sein, während in einer deutschen Version desselben Blocks [['links', 'LEFT'], ['rechts', 'RIGHT]] definiert ist.

Bild-Drop-down-Menüs

Optionen in einem Drop-down-Menü können Bilder sein, die als Objekte mit den Attributen src, width, height und alt dargestellt werden.

Dropdown-Feld mit Bildern und Text

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');
  }
};

HTML-Dropdown-Menüs

Eine Option kann ein beliebiges HTML-Element sein, sofern es nicht zu groß ist und keine Maus- oder Tastaturereignisse verarbeitet. Es liegt in Ihrer Verantwortung, diese Regeln einzuhalten. Blockly erzwingt sie nicht.

Wenn das Drop-down-Menü geöffnet ist, wird das HTML-Element in der Liste angezeigt. Wenn sie geschlossen ist und das Element die ausgewählte Option ist, wird in der Liste (in absteigender Reihenfolge der Priorität) das Attribut title des Elements, das Attribut aria-label oder die Eigenschaft innerText angezeigt.

Dropdown-Feld mit Text- und HTML-Elementen

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);
  });

Dies geschieht mithilfe einer JSON-Erweiterung.

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');
  }
};

Dynamische Drop-down-Menüs

Drop-down-Feld mit Wochentagen

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;
      });
  });

Dies geschieht mithilfe einer JSON-Erweiterung.

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;
  }
};

Ein Drop-down-Menü kann auch mit einer Funktion anstelle einer Liste statischer Optionen bereitgestellt werden, wodurch die Optionen dynamisch sein können. Die Funktion sollte ein Array von Optionen im selben [human-readable-value, language-neutral-key]-Format wie statische Optionen zurückgeben. Jedes Mal, wenn auf das Drop-down-Menü geklickt wird, wird die Funktion ausgeführt und die Optionen werden neu berechnet.

Trennzeichen zwischen Zeilen

Verwenden Sie den String 'separator', um eine Zeile zwischen Optionen in einem Drop-down-Menü hinzuzufügen.

Drop-down-Feld mit einer Linie zwischen der zweiten und dritten Option

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");
  }
};

Serialisierung

JSON

Der JSON-Code für ein Drop-down-Feld sieht so aus:

{
  "fields": {
    "FIELDNAME": "LANGUAGE-NEUTRAL-KEY"
  }
}

Dabei ist FIELDNAME ein String, der auf ein Drop-down-Feld verweist, und der Wert ist der Wert, der auf das Feld angewendet werden soll. Der Wert sollte ein sprachneutraler Optionsschlüssel sein.

XML

Das XML für ein Drop-down-Feld sieht so aus:

<field name="FIELDNAME">LANGUAGE-NEUTRAL-KEY</field>

Das Attribut name des Felds enthält einen String, der auf ein Drop-down-Feld verweist, und der innere Text ist der Wert, der auf das Feld angewendet werden soll. Der innere Text muss ein gültiger sprachneutraler Optionsschlüssel sein.

Anpassung

Mit der Blockly.FieldDropdown.ARROW_CHAR-Property können Sie das Unicode-Zeichen für den Drop-down-Pfeil ändern.

Drop-down-Feld mit benutzerdefiniertem Pfeil

Die Eigenschaft ARROW_CHAR ist standardmäßig auf \u25BC (▼) unter Android und auf \u25BE (▾) in anderen Fällen gesetzt.

Dies ist eine globale Eigenschaft. Wenn sie festgelegt wird, werden alle Drop-down-Felder geändert.

Mit der Property Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH können Sie die maximale Höhe des Menüs ändern. Sie wird als Prozentsatz der Höhe des Darstellungsbereichs definiert. Der Darstellungsbereich ist das Fenster.

Die Eigenschaft MAX_MENU_HEIGHT_VH ist standardmäßig auf 0,45 gesetzt.

Dies ist eine globale Eigenschaft. Wenn sie festgelegt wird, werden alle Drop-down-Felder geändert.

Präfix-/Suffixabgleich

Wenn alle Optionen im Drop-down-Menü ein gemeinsames Präfix und/oder Suffix haben, werden diese Wörter automatisch herausgerechnet und als statischer Text eingefügt. Hier sind zwei Möglichkeiten, denselben Block zu erstellen (zuerst ohne Suffixabgleich und dann mit):

Ohne Suffix-Abgleich:

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');
  }
};

Mit Suffixübereinstimmung:

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');
  }
};

Drop-down-Feld mit

Ein Vorteil dieses Ansatzes ist, dass der Block leichter in andere Sprachen übersetzt werden kann. Im ursprünglichen Code sind die Strings 'hello', 'world' und 'computer' enthalten, im überarbeiteten Code die Strings 'hello world' und 'hello computer'. Übersetzer können Sätze viel leichter übersetzen als einzelne Wörter.

Ein weiterer Vorteil dieses Ansatzes ist, dass sich die Wortstellung zwischen den Sprachen oft ändert. Stellen Sie sich eine Sprache vor, in der 'world hello' und 'computer hello' verwendet werden. Der Algorithmus für den Suffixabgleich erkennt das gemeinsame 'hello' und zeigt es nach dem Drop-down-Menü an.

Manchmal schlägt der Abgleich von Präfix/Suffix jedoch fehl. In einigen Fällen sollten zwei Wörter immer zusammen verwendet werden und das Präfix sollte nicht herausgerechnet werden. Bei 'drive red car' und 'drive red truck' sollte beispielsweise nur 'drive' herausgerechnet werden, nicht 'drive red'. Das Unicode-Leerzeichen '\u00A0' kann anstelle eines regulären Leerzeichens verwendet werden, um den Prefix-/Suffix-Matcher zu unterdrücken. Das obige Beispiel kann also mit 'drive red\u00A0car' und 'drive red\u00A0truck' korrigiert werden.

Eine weitere Stelle, an der die Übereinstimmung von Präfixen/Suffixen fehlschlägt, sind Sprachen, in denen einzelne Wörter nicht durch Leerzeichen getrennt werden. Chinesisch ist ein gutes Beispiel. Der String '訪問中國' bedeutet 'visit China'. Beachten Sie, dass zwischen den Wörtern keine Leerzeichen stehen. Die letzten beiden Zeichen ('中國') ergeben zusammen das Wort für 'China'. Wenn sie jedoch getrennt werden, bedeuten sie 'centre' bzw. 'country'. Damit die Übereinstimmung mit Präfixen/Suffixen in Sprachen wie Chinesisch funktioniert, fügen Sie einfach ein Leerzeichen an der Stelle ein, an der die Trennung erfolgen soll. Beispiel: '訪問 中國' und '訪問 美國' ergeben "visit [China/USA]", während '訪問 中 國' und '訪問 美 國' "visit [centre/beautiful] country" ergeben.

Dropdown-Validierung erstellen

Der Wert eines Drop-down-Felds ist ein sprachneutraler String. Daher müssen alle Validatoren einen String akzeptieren und einen String zurückgeben, der eine verfügbare Option, null oder undefined ist.

Wenn Ihr Validator etwas anderes zurückgibt, ist das Verhalten von Blockly nicht definiert und Ihr Programm stürzt möglicherweise ab.

Sie könnten beispielsweise ein Drop-down-Feld mit drei Optionen und einem Validator so definieren:

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 gibt immer den Wert zurück, der an sie übergeben wurde. Sie ruft jedoch die Hilfsfunktion updateConnection auf, die je nach Drop-down-Wert Eingaben hinzufügt oder entfernt:

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');
  }
}

Ein animiertes GIF, das ein Drop-down-Feld mit drei Elementen zeigt: „weder“, „Aussage“ und „Wert“. Wenn „weder noch“ ausgewählt ist, hat sie keine Eingaben. Wenn „Aussage“ ausgewählt ist, ist ein Aussage-Eingang vorhanden. Wenn „value“ verbunden ist, hat es eine „value“-Eingabe.