Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Custom toolbox label

150 views
Skip to first unread message

Max Stephen Russell

unread,
Feb 4, 2025, 11:53:41 AMFeb 4
to Blockly
In the early stage of building my app using Blockly's starter app, I have imported the custom toolbox codelab. 


At this point, I am able to color the custom label text, situated above the categories which are also behaving as expected, but the label font does not match the category font, and possibly some other styling is missing. The blue-highlighted comment below mentions "css-" prefixed attributes. But the toolbox definition does not allow me to use "css-label", and this is where I'm stuck, it seems. May I ask, what exactly is cssconfig, and where does "css-" come from, or is this even related to my problem?

toolbox.js
export const toolbox = {
  kind: 'categoryToolbox',
  contents: [
    {
      kind: 'toolboxlabel',
      name: 'Building Blocks',
      colour: 'darkslategrey',
      csslabel: 'customLabel',
    },
    {
      kind: 'category',
...

toolbox_label.js
export default class ToolboxLabel extends Blockly.ToolboxItem {
  /**
   * Constructor for a label in the toolbox.
...
  /**
   * Init method for the label.
   * @override
   */
  init() {
    // Create the label.
    this.label = document.createElement('label');
    // Set the name.
    this.label.textContent = this.toolboxItemDef_['name'];
    // Set the color.
    this.label.style.color = this.toolboxItemDef_['colour'];
    // Any attributes that begin with css- will get added to a cssconfig.
    const cssConfig = this.toolboxItemDef_['cssconfig'];
    // Add the class.
    if (cssConfig) {
      this.label.classList.add(cssConfig['label']);
    }
  }
...

Thank you,

-Steve

Aaron Dodson

unread,
Feb 5, 2025, 12:31:33 PMFeb 5
to Blockly
Hi Steve,

The codelab is written assuming that your toolbox definition is using the old XML format; in that case, as noted under "Add some CSS", any attributes in a <toolboxlabel> XML element that start with css- will be automatically parsed out and loaded into the toolboxItemDef_ object that your ToolboxLabel class is referencing.

In your case, it appears that you're using the JSON format for your toolbox definition. In that case, you would want to do the following (changes in bold):

export const toolbox = {
  kind: 'categoryToolbox',
  contents: [
    {
      kind: 'toolboxlabel',
      name: 'Building Blocks',
      colour: 'darkslategrey',
      cssconfig: {
label: 'customLabel',
}
    },
    {
      kind: 'category',
...

This corresponds with the following code:

  const cssConfig = this.toolboxItemDef_['cssconfig'];
    // Add the class.
    if (cssConfig) {
      this.label.classList.add(cssConfig['label']);
    }

which accesses the `cssconfig` property of the toolboxItemDef_, and then accesses the `label` property of that object.

I hope that helps - feel free to let me know if you have further questions!

- Aaron

Max Stephen Russell

unread,
Feb 5, 2025, 3:19:35 PMFeb 5
to Blockly
Aaron,

Thank you for the guidance and explanation. I really appreciate it. I knew the XML to JSON was questionable. However, this correction has had no visible effect on the label, i.e. the font. I wonder what I'm failing to see or include.

-Steve

Aaron Dodson

unread,
Feb 5, 2025, 5:22:09 PMFeb 5
to Blockly
Hi Steve,

Sure thing, happy to help! With regard to it having no effect, just to confirm you've added some CSS targeting the `.customLabel` selector? And the toolbox item does in fact have that class in the web inspector?

- Aaron

Max Stephen Russell

unread,
Feb 5, 2025, 6:57:57 PMFeb 5
to Blockly
Aaron,

I can now answer both questions with a Yes. I did not understand that I could target customLabel with css. Also I had thought I should somewhere discover the label's original styling in the imported codelab files. The codelab index.html has:

    <link
      rel="stylesheet"


which I wasn't sure how to bring over into my modular starter app nor if it was contributing to the custom label.

So between xml to Javascript and script tags to imports, some of these constructions and relationships leave me puzzled, but I have the solution I need. Thank you many times over. This makes my day!

-Steve

Max Stephen Russell

unread,
Feb 5, 2025, 8:52:59 PMFeb 5
to blo...@googlegroups.com
I mean I do realize that stylesheet is from Font Awesome, but I found the codelab and the label to be confounding when it comes to fully comprehending the actions and results.

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/blockly/25a6ef0c-05b4-40a5-a41e-0862b74a2374n%40googlegroups.com.

Aaron Dodson

unread,
Feb 7, 2025, 11:27:00 AMFeb 7
to Blockly
Glad to hear you got it sorted out, and agreed that the codelab could likely stand to be improved; we're working on improving the documentation in general, and your feedback here is very useful!

- Aaron

Max Stephen Russell

unread,
Feb 16, 2025, 1:27:34 PMFeb 16
to Blockly
Similar but different situation -- In the built-in dynamic VARIABLE category of my toolbox, what is the secret to inserting a label? This isn't working:

    {
      kind: 'category',
      name: 'Variables',
      categorystyle: 'variable_category',
      custom: 'VARIABLE',
      "contents": [
        {
          "kind": "label",
          "text": "Anything you want to measure",
          "web-class": "myLabelStyle",
        },
      ],
    },

Thanks.

-Steve

Max Stephen Russell

unread,
Feb 16, 2025, 11:59:18 PMFeb 16
to Blockly
More importantly, I want to remove the math_change block. So now I'm getting farther afield from this conversation topic, and I suppose I should  figure out how to create a category that would allow my customization and retain the behavior of custom:VARIABLE. So please disregard the question above. Thanks. -Steve

Max Stephen Russell

unread,
Apr 17, 2025, 12:10:32 PMApr 17
to Blockly
Could someone tell me why the string key displays as it is rather than resolving to the intended string, in this case which is based on the  custom toolbox codelab. Thank you. -Steve

export const toolbox = {
  "kind": "categoryToolbox",
  "contents": [
    {
      "kind": "toolboxlabel",
      "name": "%{BKY_RTL_CATEGORY_BUILDING_BLOCKS}", // "Building Blocks",
      "colour": "darkslategrey",
      "cssconfig": {
        "label": "customLabel"
      }
    },
On Wednesday, February 5, 2025 at 12:31:33 PM UTC-5 ado...@google.com wrote:

Ronald Bourret

unread,
Apr 17, 2025, 1:18:50 PMApr 17
to blo...@googlegroups.com
This usually happens when the localization token (RTL_CATEGORY_BUILDING_BLOCKS in this case) is not defined. Did you add it to Blockly.Msg? See Define localization tables and Load your own localization table.

Ronald Bourret
Technical Writer (Provided by Synergis)


--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.

Max Stephen Russell

unread,
Apr 17, 2025, 2:03:07 PMApr 17
to blo...@googlegroups.com
Yes, it is the only string in the table that misbehaves. But I will take yet another look.

Max Stephen Russell

unread,
Apr 17, 2025, 6:59:46 PMApr 17
to Blockly
The token works fine in other places, and other tokens don't work in its place. So I feel sure this is related to the toolbox codelab code I borrowed, and since only this custom label is involved and works just fine, I will not dig further into it and won't ask anyone else to do so either. Thanks. -Steve . . . However, I will go ahead and include toolbox_label.js here:

/* This file from custom-toolbox-codelab/toolbox_label.js */
/**
 * @license
 * Copyright 2020 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview The toolbox label built during the custom toolbox codelab, in es6.
 * @author aschmiedt@google.com (Abby Schmiedt)
 */

import * as Blockly from 'blockly';

export default class ToolboxLabel extends Blockly.ToolboxItem {
  /**
   * Constructor for a label in the toolbox.
   * @param {!Blockly.utils.toolbox.ToolboxItemInfo} toolboxItemDef The toolbox
   *    item definition. This comes directly from the toolbox definition.
   * @param {!Blockly.IToolbox} parentToolbox The toolbox that holds this
   *    toolbox item.
   * @override
   */
  constructor(toolboxItemDef, parentToolbox) {
    super(toolboxItemDef, parentToolbox);
    /**
     * The button element.
     * @type {?HTMLLabelElement}
     */
    this.label = null;
  }

  /**
   * Init method for the label.
   * @override
   */
  init() {
    // Create the label.
    this.label = document.createElement('label');
    // Set the name.
    this.label.textContent = this.toolboxItemDef_['name'];
    // Set the color.
    this.label.style.color = this.toolboxItemDef_['colour'];
    // Any attributes that begin with css- will get added to a cssconfig.
    const cssConfig = this.toolboxItemDef_['cssconfig'];
    // Add the class.
    if (cssConfig) {
      this.label.classList.add(cssConfig['label']);
    }
  }

  /**
   * Gets the div for the toolbox item.
   * @returns {HTMLLabelElement} The label element.
   * @override
   */
  getDiv() {
    return this.label;
  }
}

Blockly.registry.register(
  Blockly.registry.Type.TOOLBOX_ITEM,
  'toolboxlabel',
  ToolboxLabel
);


Ronald Bourret

unread,
Apr 17, 2025, 8:36:19 PMApr 17
to blo...@googlegroups.com
This might be a timing problem. You need to load your token into Blockly.Msg before injecting the workspace. This code works:

const toolbox = {
  kind: "categoryToolbox",
  contents: [
    {
      kind: "category",
      name: "%{BKY_FOO}",
      contents: [
        {
          kind: "block",
          type: "controls_if",
        },
      ],
    },
  ],
}

// Works correctly.
Blockly.Msg['FOO'] = "foobar";

Blockly.ContextMenuItems.registerCommentOptions()
const workspace = Blockly.inject("blocklyDiv", {
  toolbox: toolbox,
})


And this code does not:

const toolbox = {...}

Blockly.ContextMenuItems.registerCommentOptions()
const workspace = Blockly.inject("blocklyDiv", {
  toolbox: toolbox,
})

// Doesn't work.
Blockly.Msg['FOO'] = "foobar";

Ronald Bourret
Technical Writer (Provided by Synergis)

Ronald Bourret

unread,
Apr 17, 2025, 8:50:44 PMApr 17
to blo...@googlegroups.com
My fingers got ahead of my brain. I suspect the problem is that you need to call Blockly.parsing.replaceMessageReferences to replace the token reference with its value. Something like:

// Not tested.
this.label.textContent = Blockly.parsing.replaceMessageReferences(this.toolboxItemDef_['name']);

Ronald Bourret
Technical Writer (Provided by Synergis)

Max Stephen Russell

unread,
Apr 18, 2025, 10:34:19 AMApr 18
to Blockly
I'd like to try your example, but I don't know how I would implement it inside my JSON-defined toolbox. I did test the following though, with the exact same results I've been getting, i.e. the value string  is returned for another toolbox item such as category, but the token is returned for this custom label:

// toolbox.js
import * as Blockly from 'blockly';
// from Ronald: this.label.textContent = Blockly.parsing.replaceMessageReferences(this.toolboxItemDef_['name']);

const mainLabel = '%{BKY_RTL_CATEGORY_BUILDING_BLOCKS}';
const theMainLabel = Blockly.utils.parsing.replaceMessageReferences(mainLabel);

export const toolbox = {
  "kind": "categoryToolbox",
  "contents": [
    {
      "kind": "toolboxlabel",
      "name": "Building Blocks", // %{BKY_RTL_CATEGORY_BUILDING_BLOCKS}",
      "colour": "darkslategrey",
      "cssconfig": {
        "label": "customLabel"
      }
    },
    {
      "kind": "category",
      "name": "%{BKY_RTL_CATEGORY_START}",
...

I guess I should try to get this anomaly solved. Thanks, Ronald.  -Steve

Ronald Bourret

unread,
Apr 18, 2025, 12:38:09 PMApr 18
to blo...@googlegroups.com
You're almost there. Use "name": "%{BKY_RTL_CATEGORY_BUILDING_BLOCKS}" in your JSON and call replaceMessageReferences in your ToolboxLabel.init method. Here's my minimal running code:

class MyToolboxLabel extends Blockly.ToolboxItem {
  constructor(toolboxItemDef, parentToolbox) {
    super(toolboxItemDef, parentToolbox)
  }

  init() {

    this.label = document.createElement("label")
    // Parse token references in the "name" string
    this.label.textContent = Blockly.utils.parsing.replaceMessageReferences(
      this.toolboxItemDef_["name"],
    )

  }

  getDiv() {
    return this.label
  }
}

Blockly.registry.register(
  Blockly.registry.Type.TOOLBOX_ITEM,
  "mytoolboxlabel",
  MyToolboxLabel,
)


const toolbox = {
  kind: "categoryToolbox",
  contents: [
    {
      // Use MyToolboxLabel
      kind: "mytoolboxlabel",
      // Use a localization token in the name
      name: "%{BKY_FOO}",
    },
  ],
}

// Set the localization token.
Blockly.Msg["FOO"] = "foo"


const workspace = Blockly.inject("blocklyDiv", {
  toolbox: toolbox,
})

Ronald Bourret
Technical Writer (Provided by Synergis)

Max Stephen Russell

unread,
Apr 18, 2025, 1:14:59 PMApr 18
to Blockly
That's it, Ronald! And it works for additional custom labels in the same toolbox as well. Thank you very much. I originally thought replaceMessageReferences must belong in the toolbox_label.js JavaScript, but it was mush in my head. Thanks to you, it's clear now.

-Steve

Reply all
Reply to author
Forward
0 new messages