ช่องรายการแบบเลื่อนลง

ฟิลด์แบบเลื่อนลงจะจัดเก็บสตริงเป็นค่าและสตริงเป็นข้อความ ค่านี้เป็นคีย์ที่ไม่ขึ้นอยู่กับภาษาซึ่งจะใช้สำหรับการเข้าถึงข้อความและ จะไม่ได้รับการแปลเมื่อ Blockly เปลี่ยนภาษา ข้อความคือ สตริงที่มนุษย์อ่านได้ซึ่งจะแสดงต่อผู้ใช้

บล็อกที่มีป้ายกำกับ "drop down:", ช่องแบบเลื่อนลงที่มี "first" เลือกอยู่
และป้ายกำกับ "item"

บล็อกเดียวกันโดยที่เมนูแบบเลื่อนลงเปิดอยู่ เมนูแบบเลื่อนลงมีรายการ "first"
และ "second"

บล็อกเดียวกันหลังจากยุบ มีป้ายกำกับ "เมนูแบบเลื่อนลง: รายการแรก"
และขอบด้านขวาเป็นรอยหยักเพื่อแสดงว่า
ยุบอยู่

การสร้างวิดีโอ

ตัวสร้างเมนูแบบเลื่อนลงจะรับเครื่องมือสร้างเมนูและเครื่องมือตรวจสอบที่ไม่บังคับ ตัวสร้างเมนูเป็นได้ทั้ง อาร์เรย์ของตัวเลือก (โดยแต่ละตัวเลือกจะมีส่วนที่มนุษย์อ่านได้และสตริงที่ไม่ขึ้นกับภาษา) หรือฟังก์ชันที่สร้างอาร์เรย์ของตัวเลือก ส่วนที่มนุษย์อ่านได้ของแต่ละตัวเลือกอาจเป็นสตริง รูปภาพ หรือองค์ประกอบ HTML และอาร์เรย์อาจมีตัวเลือกแบบผสมที่มีประเภทแตกต่างกัน

เมนูแบบเลื่อนลงของข้อความธรรมดา

เปิดเมนูแบบเลื่อนลงที่มีตัวเลือกข้อความ 2 รายการ

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

การแยกข้อมูลที่มนุษย์อ่านได้ออกจากคีย์ที่ไม่ขึ้นอยู่กับภาษา จะช่วยให้ระบบรักษาการตั้งค่าของเมนูแบบเลื่อนลงไว้ได้ในทุกภาษา ตัวอย่างเช่น บล็อกเวอร์ชันภาษาอังกฤษอาจกำหนด [['left', 'LEFT'], ['right', 'RIGHT]] ในขณะที่บล็อกเดียวกันเวอร์ชันภาษาเยอรมันจะกำหนด [['links', 'LEFT'], ['rechts', 'RIGHT]]

เมนูแบบเลื่อนลงของรูปภาพ

ตัวเลือกในเมนูแบบเลื่อนลงอาจเป็นรูปภาพ ซึ่งแสดงเป็นออบเจ็กต์ที่มีพร็อพเพอร์ตี้ src, width, height และ 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');
  }
};

เมนูแบบเลื่อนลง HTML

ตัวเลือกอาจเป็นองค์ประกอบ HTML ใดก็ได้ ตราบใดที่ไม่ได้มีขนาดใหญ่เกินไปและไม่ได้ พยายามจัดการเหตุการณ์ของเมาส์หรือคีย์บอร์ด (คุณมีหน้าที่ ปฏิบัติตามกฎเหล่านี้ โดย Blockly จะไม่บังคับใช้)

เมื่อเมนูแบบเลื่อนลงเปิดอยู่ รายการจะแสดงองค์ประกอบ HTML เมื่อปิดอยู่ และองค์ประกอบเป็นตัวเลือกที่เลือก รายการจะแสดง (ตามลำดับความสำคัญจากมากไปน้อย ) แอตทริบิวต์ title ขององค์ประกอบ แอตทริบิวต์ aria-label หรือ พร็อพเพอร์ตี้ innerText

ฟิลด์แบบเลื่อนลงที่มีข้อความและองค์ประกอบ HTML

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

โดยทำได้โดยใช้ส่วนขยาย JSON

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

เมนูแบบเลื่อนลงแบบไดนามิก

ฟิลด์เมนูแบบเลื่อนลงที่มีวันในสัปดาห์

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

โดยทำได้โดยใช้ส่วนขยาย JSON

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

นอกจากนี้ คุณยังระบุเมนูแบบเลื่อนลงด้วยฟังก์ชันแทนรายการตัวเลือกแบบคงที่ได้ด้วย ซึ่งจะช่วยให้ตัวเลือกเป็นแบบไดนามิก ฟังก์ชันควรแสดงผลอาร์เรย์ของตัวเลือกใน[human-readable-value, language-neutral-key] รูปแบบเดียวกับตัวเลือกแบบคงที่ ทุกครั้งที่คลิกเมนูแบบเลื่อนลง ฟังก์ชันจะ ทำงานและระบบจะคำนวณตัวเลือกใหม่

ตัวคั่น

ใช้สตริง 'separator' เพื่อเพิ่มบรรทัดระหว่างตัวเลือกในเมนูแบบเลื่อนลง

ฟิลด์แบบเลื่อนลงที่มีเส้นคั่นระหว่างตัวเลือกที่ 2 กับ 3

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

การเรียงอันดับ

JSON

JSON สำหรับฟิลด์แบบเลื่อนลงจะมีลักษณะดังนี้

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

โดย FIELDNAME คือสตริงที่อ้างอิงช่องแบบเลื่อนลง และ ค่าคือค่าที่จะใช้กับช่อง ค่าควรเป็นคีย์ตัวเลือกที่ไม่ขึ้นอยู่กับภาษา

XML

XML สำหรับฟิลด์แบบเลื่อนลงจะมีลักษณะดังนี้

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

โดยที่แอตทริบิวต์ name ของฟิลด์มีสตริงที่อ้างอิงฟิลด์ดรอปดาวน์ และข้อความด้านในคือค่าที่จะใช้กับฟิลด์ ข้อความภายใน ควรเป็นคีย์ตัวเลือกที่เป็นกลางทางภาษาที่ถูกต้อง

การปรับแต่ง

คุณใช้พร็อพเพอร์ตี้ Blockly.FieldDropdown.ARROW_CHAR เพื่อเปลี่ยน อักขระ Unicode ที่แสดงลูกศรเมนูแบบเลื่อนลงได้

ฟิลด์แบบเลื่อนลงที่มีลูกศรที่กำหนดเอง

พร็อพเพอร์ตี้ ARROW_CHAR จะมีค่าเริ่มต้นเป็น \u25BC (▼) ใน Android และ \u25BE (▾) ในกรณีอื่นๆ

ซึ่งเป็นพร็อพเพอร์ตี้ส่วนกลาง ดังนั้นจึงจะแก้ไขฟิลด์แบบเลื่อนลงทั้งหมดเมื่อตั้งค่า

คุณใช้พร็อพเพอร์ตี้ Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH เพื่อเปลี่ยน ความสูงสูงสุดของเมนูได้ โดยกำหนดเป็นเปอร์เซ็นต์ของความสูงของวิวพอร์ต ซึ่งวิวพอร์ตคือหน้าต่าง

พร็อพเพอร์ตี้ MAX_MENU_HEIGHT_VH มีค่าเริ่มต้นเป็น 0.45

ซึ่งเป็นพร็อพเพอร์ตี้ส่วนกลาง ดังนั้นจึงจะแก้ไขฟิลด์แบบเลื่อนลงทั้งหมดเมื่อตั้งค่า

การจับคู่คำนำหน้า/คำต่อท้าย

หากตัวเลือกทั้งหมดในเมนูแบบเลื่อนลงมีคำนำหน้าและ/หรือคำต่อท้ายร่วมกัน ระบบจะแยกคำเหล่านี้ออกโดยอัตโนมัติและแทรกเป็นข้อความแบบคงที่ ตัวอย่างเช่น วิธีสร้างบล็อกเดียวกันมี 2 วิธีดังนี้ (วิธีแรกไม่มี การจับคู่คำต่อท้าย และวิธีที่ 2 มีการจับคู่คำต่อท้าย)

หากไม่มีการจับคู่คำต่อท้าย

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

เมื่อใช้การจับคู่คำต่อท้าย

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

ฟิลด์แบบเลื่อนลงที่มี

ข้อดีอย่างหนึ่งของแนวทางนี้คือการบล็อกจะแปลเป็นภาษาอื่นๆ ได้ง่ายกว่า โค้ดก่อนหน้ามีสตริง 'hello', 'world' และ 'computer' ในขณะที่โค้ดที่แก้ไขแล้วมีสตริง 'hello world' และ 'hello computer' นักแปลจะแปลวลีได้ง่ายกว่าการแปลคำแบบแยกกันมาก

ข้อดีอีกอย่างของแนวทางนี้คือลำดับคำมักจะเปลี่ยนแปลงไปในแต่ละ ภาษา ลองนึกถึงภาษาที่ใช้ 'world hello' และ 'computer hello' อัลกอริทึมการจับคู่คำต่อท้ายจะตรวจหาคำต่อท้ายที่ใช้ร่วมกัน 'hello' และแสดงคำต่อท้ายนั้น หลังจากเมนูแบบเลื่อนลง

อย่างไรก็ตาม บางครั้งการจับคู่คำนำหน้า/คำต่อท้ายอาจไม่สำเร็จ มีบางกรณีที่ คำ 2 คำควรอยู่ด้วยกันเสมอและไม่ควรแยกคำนำหน้าออก ตัวอย่างเช่น 'drive red car' และ 'drive red truck' ควรมีเพียง 'drive' เท่านั้นที่แยกออกมา ไม่ใช่ 'drive red' คุณอาจใช้ช่องว่างที่ไม่ตัดคำของ Unicode '\u00A0' แทนช่องว่างปกติเพื่อระงับตัวจับคู่คำนำหน้า/คำต่อท้าย ดังนั้น ตัวอย่างข้างต้นจึงแก้ไขได้ด้วย 'drive red\u00A0car' และ 'drive red\u00A0truck'

อีกกรณีที่การจับคู่คำนำหน้า/คำต่อท้ายไม่สำเร็จคือในภาษาที่ไม่ได้ แยกคำแต่ละคำด้วยการเว้นวรรค ภาษาจีนเป็นตัวอย่างที่ดี สตริง '訪問中國' หมายถึง 'visit China' โปรดสังเกตว่าไม่มีการเว้นวรรคระหว่างคำ เมื่อรวมกันแล้ว อักขระ 2 ตัวสุดท้าย ('中國') คือคำสำหรับ 'China' แต่หากแยกกัน อักขระทั้งสองจะหมายถึง 'centre' และ 'country' ตามลำดับ หากต้องการให้การจับคู่คำนำหน้า/คำต่อท้ายทำงานในภาษาต่างๆ เช่น จีน เพียงแทรกช่องว่างในตำแหน่งที่ควรมีการเว้นวรรค เช่น '訪問 中國' และ '訪問 美國' จะได้ผลลัพธ์เป็น "visit [China/USA]" ส่วน '訪問 中 國' และ '訪問 美 國' จะได้ผลลัพธ์เป็น "visit [centre/beautiful] country"

การสร้างเครื่องมือตรวจสอบแบบเลื่อนลง

ค่าของฟิลด์แบบเลื่อนลงคือสตริงที่เป็นกลางทางภาษา ดังนั้นเครื่องมือตรวจสอบใดๆ จะต้อง ยอมรับสตริงและส่งคืนสตริงที่เป็นตัวเลือกที่ใช้ได้ null หรือ undefined

หากโปรแกรมตรวจสอบแสดงผลอย่างอื่น พฤติกรรมของ Blockly จะไม่แน่นอนและ โปรแกรมอาจขัดข้อง

เช่น คุณอาจกำหนดฟิลด์เมนูแบบเลื่อนลงที่มี 3 ตัวเลือกและตัวตรวจสอบความถูกต้องดังนี้

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 จะส่งคืนค่าที่ส่งมาเสมอ แต่จะเรียกใช้ฟังก์ชันตัวช่วย updateConnection ซึ่งจะเพิ่มหรือนำอินพุตออกตามค่าของเมนูแบบเลื่อนลง

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

GIF แบบเคลื่อนไหวที่แสดงฟิลด์เมนูแบบเลื่อนลงที่มี 3 รายการ ได้แก่ &quot;ไม่&quot; &quot;ข้อความ&quot; และ &quot;ค่า&quot; เมื่อเลือก &quot;ทั้งสอง&quot; จะไม่มีอินพุต เมื่อเลือก &quot;ข้อความ&quot; จะมีอินพุตข้อความ เมื่อเชื่อมต่อ &quot;value&quot; แล้ว
จะมีอินพุต &quot;value&quot;