Toolbox

Kotak alat adalah tempat di mana pengguna mendapatkan blok. Biasanya ditampilkan di satu sisi ruang kerja. Terkadang memiliki kategori, dan terkadang tidak.

Halaman ini berfokus terutama pada cara menentukan struktur toolbox (yaitu kategori yang dimilikinya dan blok yang terdapat di dalamnya). Jika Anda ingin mengetahui detail selengkapnya tentang cara mengubah UI toolbox, lihat Menyesuaikan codelab toolbox Blockly dan pembicaraan API Toolbox 2021.

Format

Dengan Blockly, Anda dapat menentukan struktur toolbox menggunakan beberapa format yang berbeda. Format baru yang direkomendasikan menggunakan JSON, dan format lama menggunakan XML.

Berikut berbagai cara untuk menentukan toolbox di atas:

JSON

Mulai Toolbox rilis September 2020 dapat ditentukan menggunakan JSON.

var toolbox = {
    "kind": "flyoutToolbox",
    "contents": [
      {
        "kind": "block",
        "type": "controls_if"
      },
      {
        "kind": "block",
        "type": "controls_whileUntil"
      }
    ]
  };
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});

XML

<xml id="toolbox" style="display: none">
  <block type="controls_if"></block>
  <block type="controls_whileUntil"></block>
</xml>
<script>
  var workspace = Blockly.inject('blocklyDiv',
      {toolbox: document.getElementById('toolbox')});
</script>

String XML

var toolbox = '<xml>' +
    '<block type="controls_if"></block>' +
    '<block type="controls_whileUntil"></block>' +
    '</xml>';
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});

Kategori

Blok di toolbox dapat diatur dalam kategori.

Berikut adalah cara untuk menentukan toolbox di atas, yang memiliki dua kategori ('Control' dan 'Logic'), yang masing-masing berisi blok:

JSON

{
  "kind": "categoryToolbox",
  "contents": [
    {
      "kind": "category",
      "name": "Control",
      "contents": [
        {
          "kind": "block",
          "type": "controls_if"
        },
      ]
    },
    {
      "kind": "category",
      "name": "Logic",
      "contents": [
        {
          "kind": "block",
          "type": "logic_compare"
        },
        {
          "kind": "block",
          "type": "logic_operation"
        },
        {
          "kind": "block",
          "type": "logic_boolean"
        }
      ]
    }
  ]
}

XML

<xml id="toolbox" style="display: none">
  <category name="Control">
    <block type="controls_if"></block>
  <category name="Logic">
    <block type="logic_compare"></block>
    <block type="logic_operation"></block>
    <block type="logic_boolean"></block>
  </category>
</xml>

Kategori bertingkat

Kategori dapat disusun bertingkat dalam kategori lain. Berikut adalah dua kategori level teratas ('Core' dan 'Kustom'), yang kedua berisi dua sub-kategori, yang masing-masing berisi blok:

Perhatikan bahwa kategori dapat berisi sub-kategori dan blok. Pada contoh di atas, 'Kustom' memiliki dua subkategori ('Pindahkan' dan 'Putar'), serta bloknya sendiri ('mulai').

JSON

{
  "kind": "categoryToolbox",
  "contents": [
    {
      "kind": "category",
      "name": "Core",
      "contents": [
        {
          "kind": "block",
          "type": "controls_if"
        },
        {
          "kind": "block",
          "type": "logic_compare"
        },
      ]
    },
    {
      "kind": "category",
      "name": "Custom",
      "contents": [
        {
          "kind": "block",
          "type": "start"
        },
        {
          "kind": "category",
          "name": "Move",
          "contents": [
            {
              "kind": "block",
              "type": "move_forward"
            }
          ]
        },
        {
          "kind": "category",
          "name": "Turn",
          "contents": [
            {
              "kind": "block",
              "type": "turn_left"
            }
          ]
        }
      ]
    }
  ]
}

XML

<xml id="toolbox" style="display: none">
  <category name="Core">
    <block type="controls_if"></block>
    <block type="logic_compare"></block>
  </category>
  <category name="Custom">
    <block type="start"></block>
    <category name="Move">
      <block type="move_forward"></block>
    </category>
    <category name="Turn">
      <block type="turn_left"></block>
    </category>
  </category>
</xml>

Kategori dinamis

Kategori dinamis adalah kategori yang diisi ulang secara dinamis berdasarkan sebuah fungsi setiap kali dibuka.

Blockly mendukung hal ini dengan memungkinkan Anda mengaitkan kategori dengan fungsi melalui kunci string yang terdaftar. Fungsi ini akan menampilkan definisi konten kategori (termasuk blok, tombol, label, dll.). Konten dapat ditetapkan sebagai JSON atau XML, meskipun JSON direkomendasikan.

Perhatikan juga bahwa fungsi ini menyediakan ruang kerja target sebagai parameter, sehingga blok dalam kategori dinamis Anda dapat didasarkan pada status ruang kerja.

JSON

Mulai rilis September 2021, Anda dapat menentukan status blok tanpa menggunakan 'blockxml'.

// Returns an array of objects.
var coloursFlyoutCallback = function(workspace) {
  // Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
  var colourList = getPalette();
  var blockList = [];
  for (var i = 0; i < colourList.length; i++) {
    blockList.push({
      'kind': 'block',
      'type': 'colour_picker',
      'fields': {
        'COLOUR': colourList[i]
      }
    });
  }
  return blockList;
};

// Associates the function with the string 'COLOUR_PALETTE'
myWorkspace.registerToolboxCategoryCallback(
    'COLOUR_PALETTE', coloursFlyoutCallback);

JSON lama

Sebelum rilis September 2021, Anda harus menggunakan properti 'blockxml' untuk menentukan status blok.

// Returns an array of objects.
var coloursFlyoutCallback = function(workspace) {
  // Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
  var colourList = getPalette();
  var blockList = [];
  for (var i = 0; i < colourList.length; i++) {
    blockList.push({
      'kind': 'block',
      'type': 'colour_picker', // Type is optional if you provide blockxml
      'blockxml': '<block type="colour_picker">' +
          '<field name="COLOUR">' + colourList[i] + '</field>' +
          '</block>'
    });
  }
  return blockList;
};

// Associates the function with the string 'COLOUR_PALETTE'
myWorkspace.registerToolboxCategoryCallback(
    'COLOUR_PALETTE', coloursFlyoutCallback);

XML

// Returns an arry of XML nodes.
var coloursFlyoutCallback = function(workspace) {
  // Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
  var colourList = getPalette();
  var blockList = [];
  for (var i = 0; i < colourList.length; i++) {
    var block = document.createElement('block');
    block.setAttribute('type', 'colour_picker');
    var field = document.createElement('field');
    field.setAttribute('name', 'COLOUR');
    field.innerText = colourList[i];
    block.appendChild(field);
    blockList.push(block);
  }
  return blockList;
};

// Associates the function with the string 'COLOUR_PALETTE'
myWorkspace.registerToolboxCategoryCallback(
    'COLOUR_PALETTE', coloursFlyoutCallback);

Setelah fungsi kategori dinamis dikaitkan dengan kunci string (alias terdaftar), Anda dapat menetapkan kunci string ini ke properti custom dari definisi kategori Anda untuk membuat kategori tersebut dinamis.

JSON

{
  "kind": "category",
  "name": "Colours",
  "custom": "COLOUR_PALETTE"
}

XML

<category name="Colours" custom="COLOUR_PALETTE"></category>

Kategori dinamis bawaan

Blockly menyediakan tiga kategori dinamis bawaan.

  • 'VARIABLE' membuat kategori untuk variabel untyped.
  • 'VARIABLE_DYNAMIC' membuat kategori untuk variabel berjenis. Alat ini memiliki tombol untuk membuat {i>string<i}, angka, dan warna.
  • 'PROCEDURE' membuat kategori untuk blok fungsi.

JSON

{
  "kind": "category",
  "name": "Variables",
  "custom": "VARIABLE"
},
{
  "kind": "category",
  "name": "Variables",
  "custom": "VARIABLE_DYNAMIC"
},
{
  "kind": "category",
  "name": "Functions",
  "custom": "PROCEDURE"
}

XML

<category name="Variables" custom="VARIABLE"></category>
<category name="Variables" custom="VARIABLE_DYNAMIC"></category>
<category name="Functions" custom="PROCEDURE"></category>

Catatan: Kata 'prosedur' digunakan di seluruh codebase Blockly, tetapi kata 'fungsi' dianggap lebih mudah dipahami oleh siswa. Kami mohon maaf atas ketidakcocokan ini.

Menonaktifkan

Kategori yang dinonaktifkan tidak akan mengizinkan pengguna untuk membuka kategori dan akan dilewati selama navigasi keyboard.

var category = toolbox.getToolboxItems()[0];
category.setDisabled('true');

Jika kategori dinonaktifkan, properti 'disabled' ditambahkan ke elemen DOM, yang memungkinkan Anda mengontrol tampilan kategori yang dinonaktifkan.

.blocklyToolboxCategory[disabled="true"] {
  opacity: .5;
}

Menyembunyikan

Kategori tersembunyi tidak akan ditampilkan sebagai bagian dari toolbox. Kategori tersembunyi nantinya dapat ditampilkan melalui JavaScript.

JSON

{
  "kind": "category",
  "name": "...",
  "hidden": "true"
}

XML

<category name="..." hidden="true"></category>

JavaScript

var category = toolbox.getToolboxItems()[0];
category.hide();
// etc...
category.show();

Meluaskan

Hal ini hanya berlaku untuk kategori yang berisi kategori bertingkat lainnya.

Kategori yang diperluas akan menampilkan sub-kategorinya. Secara default, kategori bertingkat diciutkan, dan perlu diklik agar dapat diperluas.

JSON

{
  "kind": "category",
  "name": "...",
  "expanded": "true"
}

XML

<category name="..." expanded="true"></sep>

Gaya visual

Blockly menyediakan UI kategori default, dan di dalamnya beberapa opsi dasar untuk gaya visual. Jika Anda menginginkan informasi tentang cara melakukan penataan gaya/konfigurasi lanjutan lebih lanjut untuk UI, lihat Menyesuaikan codelab toolbox Blockly dan pembicaraan Toolbox API 2021.

Tema

Tema memungkinkan Anda menentukan semua warna ruang kerja Anda sekaligus, termasuk warna kategori.

Untuk menggunakannya, Anda harus mengaitkan kategori Anda dengan gaya kategori tertentu:

JSON

{
  "kind": "category",
  "name": "Logic",
  "categorystyle": "logic_category"
}

XML

<category name="Logic" categorystyle="logic_category"></category>

Warna

Anda juga dapat menentukan warna secara langsung, tetapi hal ini tidak disarankan. Warna adalah angka string (0-360) yang menentukan hue. Perhatikan ejaan Inggris.

JSON

{
  "contents": [
    {
      "kind": "category",
      "name": "Logic",
      "colour": "210"
    },
    {
      "kind": "category",
      "name": "Loops",
      "colour": "120"
    }
  ]
}

XML

<xml id="toolbox" style="display: none">
  <category name="Logic" colour="210">...</category>
  <category name="Loops" colour="120">...</category>
  <category name="Math" colour="230">...</category>
  <category name="Colour" colour="20">...</category>
  <category name="Variables" colour="330" custom="VARIABLE"></category>
  <category name="Functions" colour="290" custom="PROCEDURE"></category>
</xml>

Perhatikan bahwa kami juga mendukung penggunaan referensi warna yang dapat dilokalkan.

Kategori CSS

Jika Anda ingin penyesuaian yang lebih andal, Blockly juga memungkinkan Anda menentukan class CSS untuk berbagai elemen UI default. Anda kemudian dapat menggunakan CSS untuk menata gayanya.

Jenis elemen berikut dapat menerapkan class CSS:

  • container - Class untuk div induk untuk kategori. blocklyToolboxCategory default.
  • baris - Kelas untuk div yang berisi label dan ikon kategori. blocklyTreeRow default.
  • ikon - Kelas untuk ikon kategori. blocklyTreeIcon default.
  • label - Kelas untuk label kategori. blocklyTreeLabel default.
  • selected - Class yang akan ditambahkan ke kategori saat dipilih. blocklyTreeSelected default.
  • openicon - Class yang ditambahkan ke ikon saat kategori memiliki kategori bertingkat dan terbuka. blocklyTreeIconOpen default.
  • closeicon - Class yang ditambahkan ke ikon saat kategori memiliki kategori bertingkat dan ditutup. blocklyTreeIconClosed default.

Berikut ini cara menentukan class menggunakan salah satu format:

JSON

Tetapkan class CSS jenis elemen tertentu menggunakan properti cssConfig.

{
  "kind": "category",
  "name": "...",
  "cssConfig": {
    "container": "yourClassName"
  }
}

XML

Menyetel class CSS jenis elemen tertentu dengan menambahkan awalan 'css-'.

<category name="..." css-container="yourClassName"></category>

Mengakses

Ada dua cara untuk mengakses kategori secara terprogram. Anda dapat mengaksesnya berdasarkan indeks (dengan 0 adalah kategori teratas):

var category = toolbox.getToolboxItems()[0];

Atau berdasarkan ID:

var category = toolbox.getToolboxItemById('categoryId');

Dengan ID yang ditentukan dalam definisi toolbox:

JSON

{
  "kind": "category",
  "name": "...",
  "toolboxitemid": "categoryId"
}

XML

<category name="..." toolboxitemid="categoryId"></category>

Blok Preset

Definisi toolbox dapat berisi blok yang kolomnya ditetapkan ke nilai default, atau memiliki blok yang sudah terhubung bersama.

Berikut ini empat blok:

  1. Blok logic_boolean sederhana tanpa nilai preset:
  2. Blok math_number yang telah diubah untuk menampilkan angka 42, bukan default 0:
  3. Blok controls_for yang memiliki tiga blok math_number yang terhubung dengannya:
  4. Blok math_arithmetic yang memiliki dua blok bayangan math_number yang terhubung dengannya:

Berikut adalah definisi toolbox yang berisi empat blok tersebut:

JSON

Mulai rilis September 2021, Anda dapat menentukan status blok tanpa menggunakan 'blockxml'.

{
  "kind": "flyoutToolbox",
  "contents": [
    {
      "kind": "block",
      "type": "logic_boolean"
    },
    {
      "kind": "block",
      "type": "math_number",
      "fields": {
        "NUM": 42
      }
    },
    {
      "kind": "block",
      "type": "controls_for",
      "inputs": {
        "FROM": {
          "block": {
            "type": "math_number",
            "fields": {
              "NUM": 1
            }
          }
        },
        "TO": {
          "block": {
            "type": "math_number",
            "fields": {
              "NUM": 10
            }
          }
        },
        "BY": {
          "block": {
            "type": "math_number",
            "fields": {
              "NUM": 1
            }
          }
        },
      }
    },
    {
      "kind": "block",
      "type": "math_arithmetic",
      "fields": {
        "OP": "ADD"
      },
      "inputs": {
        "A": {
          "shadow": {
            "type": "math_number",
            "fields": {
              "NUM": 1
            }
          }
        },
        "B": {
          "shadow": {
            "type": "math_number",
            "fields": {
              "NUM": 1
            }
          }
        }
      }
    },
  ]
}

JSON lama

Sebelum rilis September 2021, Anda harus menggunakan properti 'blockxml' untuk menentukan status blok.

{
  "kind": "flyoutToolbox",
  "contents": [
    {
      "kind": "block",
      "type": "logic_boolean"
    },
    {
      "kind": "block",
      "blockxml":
          '<block type="math_number">' +
          '<field name="NUM">42</field>' +
          '</block>'
    },
    {
      "kind": "block",
      "blockxml":
          '<block type="controls_for">' +
            '<value name="FROM">' +
              '<block type="math_number">' +
                '<field name="NUM">1</field>' +
              '</block>' +
            '</value>' +
            '<value name="TO">' +
              '<block type="math_number">' +
                '<field name="NUM">10</field>' +
              '</block>' +
            '</value>' +
            '<value name="BY">' +
              '<block type="math_number">' +
                '<field name="NUM">1</field>' +
              '</block>' +
            '</value>' +
          '</block>'
    },
    {
      "kind": "block",
      "blockxml":
          '<block type="math_arithmetic">' +
            '<field name="OP">ADD</field>' +
            '<value name="A">' +
              '<shadow type="math_number">' +
                '<field name="NUM">1</field>' +
              '</shadow>' +
            '</value>' +
            '<value name="B">' +
              '<shadow type="math_number">' +
                '<field name="NUM">1</field>' +
              '</shadow>' +
            '</value>' +
          '</block>'
    },
  ]
}

XML

<xml id="toolbox" style="display: none">
  <block type="logic_boolean"></block>

  <block type="math_number">
    <field name="NUM">42</field>
  </block>

  <block type="controls_for">
    <value name="FROM">
      <block type="math_number">
        <field name="NUM">1</field>
      </block>
    </value>
    <value name="TO">
      <block type="math_number">
        <field name="NUM">10</field>
      </block>
    </value>
    <value name="BY">
      <block type="math_number">
        <field name="NUM">1</field>
      </block>
    </value>
  </block>

  <block type="math_arithmetic">
    <field name="OP">ADD</field>
    <value name="A">
      <shadow type="math_number">
        <field name="NUM">1</field>
      </shadow>
    </value>
    <value name="B">
      <shadow type="math_number">
        <field name="NUM">1</field>
      </shadow>
    </value>
  </block>
</xml>

Menuliskan definisi ini secara manual bisa membuat... Sebagai gantinya, Anda dapat memuat blok Anda ke ruang kerja, lalu menjalankan kode berikut untuk mendapatkan definisinya. Panggilan ini berfungsi karena toolbox menggunakan format yang sama untuk blok dengan sistem serialisasi.

JSON

console.log(Blockly.serialization.workspaces.save(Blockly.getMainWorkspace()));

XML

console.log(Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace()));

Anda juga dapat menghapus properti x, y, dan id karena properti tersebut diabaikan oleh toolbox.

Blok bayangan

Blok bayangan adalah blok placeholder yang melakukan beberapa fungsi:

  • Kode tersebut menunjukkan nilai default untuk blok induknya.
  • Keduanya memungkinkan pengguna untuk mengetik nilai secara langsung tanpa perlu mengambil blok angka atau string.
  • Tidak seperti blok biasa, mereka akan diganti jika pengguna meletakkan blok di atasnya.
  • Mereka menginformasikan pengguna tentang jenis nilai yang diharapkan.

Blok yang dinonaktifkan

Blok yang dinonaktifkan tidak dapat ditarik dari toolbox. Pemblokiran dapat dinonaktifkan satu per satu menggunakan properti disabled opsional.

JSON

{
  "kind": "flyoutToolbox",
  "contents": [
    {
      "kind": "block",
      "type":"math_number"
    },
    {
      "kind": "block",
      "type": "math_arithmetic"
    },
    {
      "kind": "block",
      "type": "math_single",
      "disabled": "true"
    }
  ]
}

XML

<xml id="toolbox" style="display: none">
  <block type="math_number"></block>
  <block type="math_arithmetic"></block>
  <block type="math_single" disabled="true"></block>
</xml>

Anda juga dapat menonaktifkan atau mengaktifkan pemblokiran secara terprogram menggunakan setEnabled.

Kolom variabel

Kolom variabel mungkin perlu ditentukan secara berbeda saat berada di toolbox vs saat hanya diserialisasi.

Secara khusus, saat kolom variabel biasanya diserialisasi ke JSON, kolom tersebut hanya berisi ID variabel yang diwakilinya, karena nama dan jenis variabel diserialisasi secara terpisah. Namun, toolbox tidak berisi informasi tersebut, sehingga harus dimasukkan dalam bidang variabel secara langsung.

{
  "kind": "flyoutToolbox",
  "content": [
    {
      "type": "controls_for",
      "fields": {
        "VAR": {
          "name": "index",
          "type": "Number"
        }
      }
    }
  ]
}

Pemisah

Menambahkan pemisah di antara dua kategori akan membuat garis dan ruang ekstra di antara dua kategori tersebut.

Anda dapat mengubah class untuk pemisah di definisi toolbox JSON atau XML.

JSON

{
  "kind": "sep",
  "cssConfig": {
    "container": "yourClassName"
  }
}

XML

<sep css-container="yourClassName"></sep>

Menambahkan pemisah di antara dua blok akan membuat jarak di antara blok. Secara default, setiap blok dipisahkan dari tetangganya yang lebih rendah sebesar 24 piksel. Pemisahan ini dapat diubah menggunakan atribut 'gap', yang akan menggantikan gap default.

Hal ini memungkinkan Anda membuat grup blok yang logis di toolbox.

JSON

{
  "kind": "flyoutToolbox",
  "contents": [
    {
      "kind": "block",
      "type":"math_number"
    },
    {
      "kind": "sep",
      "gap": "32"
    },
    {
      "kind": "block",
      "blockxml": "<block type='math_arithmetic'><field name='OP'>ADD</field></block>"
    },
    {
      "kind": "sep",
      "gap": "8"
    },
    {
      "kind": "block",
      "blockxml": "<block type='math_arithmetic'><field name='OP'>MINUS</field></block>"
    }
  ]
}

XML

<xml id="toolbox" style="display: none">
  <block type="math_number"></block>
  <sep gap="32"></sep>
  <block type="math_arithmetic">
    <field name="OP">ADD</field>
  </block>
  <sep gap="8"></sep>
  <block type="math_arithmetic">
    <field name="OP">MINUS</field>
  </block>
</xml>

Tombol dan Label

Anda dapat meletakkan tombol atau label di mana pun Anda dapat menempatkan blok dalam toolbox.

JSON

{
  "kind": "flyoutToolbox",
  "contents": [
    {
      "kind": "block",
      "type":"logic_operation"
    },
    {
      "kind": "label",
      "text": "A label",
      "web-class": "myLabelStyle"
    },
    {
      "kind": "label",
      "text": "Another label"
    },
    {
      "kind": "block",
      "type": "logic_negate"
    },
    {
      "kind": "button",
      "text": "A button",
      "callbackKey": "myFirstButtonPressed"
    },
    {
      "kind": "block",
      "type": "logic_boolean"
    }
  ]
}

XML

<xml id="toolbox" style="display: none">
  <block type="logic_operation"></block>
  <label text="A label" web-class="myLabelStyle"></label>
  <label text="Another label"></label>
  <block type="logic_negate"></block>
  <button text="A button" callbackKey="myFirstButtonPressed"></button>
  <block type="logic_boolean"></block>
</xml>
    <style>
    .myLabelStyle>.blocklyFlyoutLabelText {
      font-style: italic;
      fill: green;
    }
    </style>

Anda dapat menentukan nama kelas CSS untuk diterapkan pada tombol atau label. Dalam contoh di atas, label pertama menggunakan gaya kustom, sedangkan label kedua menggunakan gaya default.

Tombol harus memiliki fungsi callback; label tidak boleh. Untuk menetapkan callback bagi klik tombol tertentu, gunakan

yourWorkspace.registerButtonCallback(yourCallbackKey, yourFunction).

Fungsi Anda akan menerima tombol yang diklik sebagai argumen. Tombol "Buat variabel..." dalam kategori variabel adalah contoh yang baik untuk tombol dengan callback.

Mengubah Toolbox

Aplikasi dapat mengubah blok yang tersedia di toolbox kapan saja dengan satu panggilan fungsi:

workspace.updateToolbox(newTree);

Seperti yang terjadi selama konfigurasi awal, newTree dapat berupa hierarki node, representasi string, atau objek JSON. Satu-satunya batasan adalah mode tidak dapat diubah; yaitu jika ada kategori di toolbox yang pertama kali ditentukan, toolbox baru juga harus memiliki kategori (meskipun kategorinya dapat berubah). Demikian pula, jika {i>toolbox<i} yang pertama ditentukan tidak memiliki kategori apa pun, maka {i>toolbox<i} baru mungkin tidak memiliki kategori.

Konten dari satu kategori dapat diperbarui dengan:

var category = workspace.getToolbox().getToolboxItems()[0];
category.updateFlyoutContents(flyoutContents);

Dalam hal ini, flyoutContents dapat berupa daftar blok yang ditentukan menggunakan JSON, pohon node, atau representasi string.

Perlu diketahui bahwa saat ini mengupdate toolbox menyebabkan beberapa kali reset UI kecil:

  • Dalam {i>toolbox<i} tanpa kategori, bidang apa pun yang diubah oleh pengguna (seperti {i>dropdown)<i} akan dikembalikan ke {i>default<i}.

Berikut adalah demo langsung hierarki dengan kategori dan grup blok.