chore(docs): run prettier --write on docs

This commit is contained in:
Greg Annandale
2026-03-31 16:05:47 +01:00
parent 231a9b1c8d
commit 45168a66bb
213 changed files with 4753 additions and 3847 deletions
+9
View File
@@ -35,6 +35,15 @@ npm run serve
The build folder is now served at http://localhost:3000/.
## Linting
```bash
# check formatting:
npm run format:check
# fix formatting:
npm run format
```
## Generating reference docs
The API reference pages are auto-generated from the Blockly TypeScript source using `@microsoft/api-extractor` and `@microsoft/api-documenter`. This is a separate step from the Docusaurus build and must be run from the `packages/blockly` directory:
@@ -33,11 +33,10 @@ Add a function to `index.js` named `registerHelloWorldItem`. Create a new regist
function registerHelloWorldItem() {
const helloWorldItem = {
displayText: 'Hello World',
preconditionFn: function(scope) {
preconditionFn: function (scope) {
return 'enabled';
},
callback: function(scope) {
},
callback: function (scope) {},
id: 'hello_world',
weight: 100,
};
@@ -52,10 +51,9 @@ function start() {
Blockly.ContextMenuItems.registerCommentOptions();
// Create main workspace.
workspace = Blockly.inject('blocklyDiv',
{
toolbox: toolboxSimple,
});
workspace = Blockly.inject('blocklyDiv', {
toolbox: toolboxSimple,
});
}
```
@@ -80,6 +78,10 @@ you will never need to make a new `ContextMenuRegistry`. Always use the singleto
Reload your web page and open a context menu on the workspace (right-click with a mouse, or press `Ctrl+Enter` (Windows) or `Command+Enter` (Mac) if you are navigating Blockly with the keyboard). You should see a new option labeled "Hello World" at the bottom of the context menu.
<CodelabImage> ![A context menu. The last option says "Hello World".](../../../static/images/codelabs/context-menu-option/hello_world.png) </CodelabImage>
<CodelabImage>
{' '}
![A context menu. The last option says "Hello
World".](../../../static/images/codelabs/context-menu-option/hello_world.png){' '}
</CodelabImage>
Next, drag a block onto the workspace and open a context menu on the block. You'll see "Hello World" at the bottom of the block's context menu. Finally, open a context menu on the workspace and create a comment, then open a context menu on the comment's header. "Hello World" should be at the bottom of the context menu.
@@ -30,5 +30,4 @@ As an example, update the help item's `callback` to add a block to the workspace
- Select the **Help** option.
- A text block should appear in the top left of the workspace.
![A text block containing the text "Now there is a block".](../../../static/images/codelabs/context-menu-option/there_is_a_block.png)
![A text block containing the text "Now there is a block".](../../../static/images/codelabs/context-menu-option/there_is_a_block.png)
@@ -11,6 +11,7 @@ description: Overview of the "Customizing context menus" codelab.
### What you'll learn
In this codelab you will learn how to:
- Add a context menu option to the workspace.
- Add a context menu option to all blocks.
- Use precondition functions to hide or disable context menu options.
@@ -18,11 +19,13 @@ In this codelab you will learn how to:
- Customize ordering and display text for context menu options.
### What you'll build
A very simple Blockly workspace with a few new context menu options.
### What you'll need
- A browser.
- A text editor.
- Basic knowledge of HTML, CSS, and JavaScript.
This codelab is focused on Blockly's context menus. Non-relevant concepts and code are glossed over and are provided for you to simply copy and paste.
This codelab is focused on Blockly's context menus. Non-relevant concepts and code are glossed over and are provided for you to simply copy and paste.
@@ -16,7 +16,7 @@ As an example, add this registry item. The display text depends on the block typ
```js
function registerDisplayItem() {
const displayItem = {
displayText: function(scope) {
displayText: function (scope) {
if (scope.focusedNode.type.startsWith('text')) {
return 'Text block';
} else if (scope.focusedNode.type.startsWith('controls')) {
@@ -25,13 +25,12 @@ function registerDisplayItem() {
return 'Some other block';
}
},
preconditionFn: function(scope) {
preconditionFn: function (scope) {
return scope.focusedNode instanceof Blockly.BlockSvg
? 'enabled'
: 'hidden';
},
callback: function(scope) {
},
callback: function (scope) {},
id: 'display_text_example',
weight: 100,
};
@@ -44,4 +43,4 @@ As usual, remember to call `registerDisplayItem()` from your `start` function.
### Test it
- Reload the workspace and open context menus on various blocks.
- The last context menu option's text should vary based on the block type.
- The last context menu option's text should vary based on the block type.
@@ -13,15 +13,14 @@ Disabling your context menu options half of the time is not useful, but you may
function registerHelpItem() {
const helpItem = {
displayText: 'Help! There are no blocks',
preconditionFn: function(scope) {
preconditionFn: function (scope) {
if (!(scope.focusedNode instanceof Blockly.WorkspaceSvg)) return 'hidden';
if (!scope.focusedNode.getTopBlocks().length) {
return 'enabled';
}
return 'hidden';
},
callback: function(scope) {
},
callback: function (scope) {},
id: 'help_no_blocks',
weight: 100,
};
@@ -34,4 +33,4 @@ Don't forget to call `registerHelpItem` from your `start` function.
### Test it
- Reload your page and open a context menu on the workspace. You should see an option labeled "Help! There are no blocks".
- Add a block to the workspace and open a context menu on the workspace again. The **Help** option should be gone.
- Add a block to the workspace and open a context menu on the workspace again. The **Help** option should be gone.
@@ -29,4 +29,4 @@ Use of the `preconditionFn` is not limited to checking the type of the Blockly c
Reload your workspace, check your watch, and open a context menu on the workspace to confirm the timing. The option will always be in the menu, but will sometimes be greyed out.
![A context menu. The last option says "Hello World" but the text is grey, indicating that it cannot be selected.](../../../static/images/codelabs/context-menu-option/hello_world_grey.png)
![A context menu. The last option says "Hello World" but the text is grey, indicating that it cannot be selected.](../../../static/images/codelabs/context-menu-option/hello_world_grey.png)
@@ -17,7 +17,7 @@ The `scope` argument is an object that is passed to `preconditionFn`. You'll use
The return value of `preconditionFn` is `'enabled'`, `'disabled'`, or `'hidden'`. An **enabled** option is shown with black text and is selectable. A **disabled** option is shown with grey text and is not selectable. A **hidden** option is not included in the context menu at all.
### Write the function
### Write the function
You can now test `scope.focusedNode` to display the "Hello World" option in workspace and block context menus, but not on any others. Change `preconditionFn` to:
@@ -22,7 +22,7 @@ function registerSeparators() {
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
weight: 99,
separator: true,
}
};
Blockly.ContextMenuRegistry.registry.register(workspaceSeparator);
const blockSeparator = {
@@ -30,7 +30,7 @@ function registerSeparators() {
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
weight: 99,
separator: true,
}
};
Blockly.ContextMenuRegistry.registry.register(blockSeparator);
}
```
@@ -24,13 +24,18 @@ git clone https://github.com/RaspberryPiFoundation/blockly.git
If you downloaded the source as a zip, unpacking it should give you a root folder named `blockly-main`.
The relevant files are in `docs/docs/codelabs/context-menu-option`. There are two versions of the app:
- `starter-code/`: The starter code that you'll build upon in this codelab.
- `complete-code/`: The code after completing the codelab, in case you get lost or want to compare to your version.
Each folder contains:
- `index.js` - The codelab's logic. To start, it just injects a simple workspace.
- `index.html` - A web page containing a simple blockly workspace.
To run the code, simple open `starter-code/index.html` in a browser. You should see a Blockly workspace with an always-open flyout.
<CodelabImage>![A web page with the text "Context Menu Codelab" and a simple Blockly workspace.](../../../static/images/codelabs/context-menu-option/starter_workspace.png)</CodelabImage>
<CodelabImage>
![A web page with the text "Context Menu Codelab" and a simple Blockly
workspace.](../../../static/images/codelabs/context-menu-option/starter_workspace.png)
</CodelabImage>
@@ -4,7 +4,6 @@ slug: /codelabs/context-menu-option/summary/index.html
description: Summary of the "Customizing context menus" codelab.
---
# Customizing context menus
## 11. Summary
@@ -15,4 +14,4 @@ In this codelab you have learned how to create and modify context menu options.
- [Context menu documentation](/guides/configure/web/context-menus)
- You can also define [block context menus](/guides/configure/web/context-menus#customize-per-block) directly on a block definition, which is equivalent to adding a precondition based on the type of the block.
- You can also define [block context menus](/guides/configure/web/context-menus#customize-per-block) directly on a block definition, which is equivalent to adding a precondition based on the type of the block.
@@ -24,7 +24,7 @@ Every registry item has an `id` that can be used to unregister it. You can use t
For instance, you can remove the item that deletes all blocks on the workspace:
```js
Blockly.ContextMenuRegistry.registry.unregister('workspaceDelete');
Blockly.ContextMenuRegistry.registry.unregister('workspaceDelete');
```
### Default items
+3 -1
View File
@@ -46,6 +46,7 @@ Note that different renderers use different numbers of `<path>` elements to
draw a block: Thrasos uses a single `<path>` element, Geras uses three `<path>`
elements, and Zelos uses one `<path>` for the outside of the block and one
`<path>` for each inline input.
#### Choose colours
The last step before writing your colour rules is to decide what colours to use. The Halloween theme in the themes codelab sets three colours:
@@ -62,6 +63,7 @@ How these colours are used depends on the renderer. The Thrasos renderer uses
the primary colour as the `fill` of the block, the tertiary colour as the
`stroke`, and the secondary colour as the `fill` when the block is a
[shadow block](/guides/configure/web/toolboxes/preset#shadow-blocks).
#### Add your rules
You're now ready to add your rules to set the `fill` and `stroke` of the logic blocks:
@@ -204,7 +206,7 @@ Your last step is to add similar rules for the loop, text, and list blocks:
}
.list_blocks:not(.blocklyDisabledPattern).blocklyShadow > .blocklyPath {
fill: #ad7Be9;
fill: #ad7be9;
stroke: none;
}
@@ -29,6 +29,7 @@ Unfortunately, the only thing that distinguishes this `<div>` from other categor
To solve this problem, you'll need to add a class to the
`blocklyToolboxCategory` `<div>` for the **Logic** category. Open the
`toolbox.js` file and find the definition of the **Logic** category:
```js
{
kind: 'category',
@@ -44,6 +45,7 @@ you're not using themes to assign category colours, you don't need the
classes to the **Logic** category's `<div>`: `logic_category` uniquely
identifies the `<div>` and `blocklyToolboxCategory` is used by Blockly's CSS to
define rules that apply to all categories.
```js
{
kind: 'category',
@@ -13,9 +13,9 @@ In this section, you will create CSS rules to assign the colours used by the [co
To start, create a file named `halloween.css` and add it to your `index.html` file:
```html
...
<script src="./index.js"></script>
<link rel="stylesheet" href="halloween.css" />
...
<script src="./index.js"></script>
<link rel="stylesheet" href="halloween.css" />
```
### Main workspace colour
@@ -142,6 +142,7 @@ apply to HTML elements, like the `<div>` used by the toolbox, and `fill` and
`stroke` apply to most SVG elements, like the `<path>` used by the flyout
background. (An exception to this is the top-level `<svg>` element that contains
Blockly, which uses `background-color` and `color`.)
### The !important declaration
You might have also noticed that some rules use an `!important` declaration while others don't. This is because Blockly sets colours in several different ways, some of which are easily overridden and some of which aren't.
@@ -16,12 +16,7 @@ The array block uses a mutator to dynamically change the number of inputs it has
The generated code looks like:
```json
[
1,
"two",
false,
true
]
[1, "two", false, true]
```
As with member blocks, there are no restrictions on the types of blocks connected to inputs.
@@ -33,8 +28,7 @@ Each value input on the block has a name: `ADD0`, `ADD1`, etc. Use `valueToCode`
```js
const values = [];
for (let i = 0; i < block.itemCount_; i++) {
const valueCode = generator.valueToCode(block, 'ADD' + i,
Order.ATOMIC);
const valueCode = generator.valueToCode(block, 'ADD' + i, Order.ATOMIC);
if (valueCode) {
values.push(valueCode);
}
@@ -48,8 +42,8 @@ To include empty inputs, use the string `'null'` as the value:
```js
const values = [];
for (let i = 0; i < block.itemCount_; i++) {
const valueCode = generator.valueToCode(block, 'ADD' + i,
Order.ATOMIC) || 'null';
const valueCode =
generator.valueToCode(block, 'ADD' + i, Order.ATOMIC) || 'null';
values.push(valueCode);
}
```
@@ -67,8 +61,10 @@ const valueString = values.join(',\n');
Next, use `prefixLines` to add indentation at the beginning of each line:
```js
const indentedValueString =
generator.prefixLines(valueString, generator.INDENT);
const indentedValueString = generator.prefixLines(
valueString,
generator.INDENT,
);
```
`INDENT` is a property on the generator. It defaults to two spaces, but language generators may override it to increase indent or change to tabs.
@@ -85,18 +81,19 @@ return [codeString, Order.ATOMIC];
Here is the final array block generator:
```js
jsonGenerator.forBlock['lists_create_with'] = function(block, generator) {
jsonGenerator.forBlock['lists_create_with'] = function (block, generator) {
const values = [];
for (let i = 0; i < block.itemCount_; i++) {
const valueCode = generator.valueToCode(block, 'ADD' + i,
Order.ATOMIC);
const valueCode = generator.valueToCode(block, 'ADD' + i, Order.ATOMIC);
if (valueCode) {
values.push(valueCode);
}
}
const valueString = values.join(',\n');
const indentedValueString =
generator.prefixLines(valueString, generator.INDENT);
const indentedValueString = generator.prefixLines(
valueString,
generator.INDENT,
);
const codeString = '[\n' + indentedValueString + '\n]';
return [codeString, Order.ATOMIC];
};
@@ -108,4 +105,4 @@ Test the block generator by adding an array to the onscreen blocks and populatin
What code does it generate if there are no inputs?
What if there are five inputs, one of which is empty?
What if there are five inputs, one of which is empty?
@@ -12,7 +12,7 @@ At its core, a block generator is a function that takes in a block (and optional
Each language generator has a property called `forBlock`, which is a dictionary object where all block generator functions must be placed. For instance, here is the code to add a block generator for blocks of type `sample_block` on a language generator object (`sampleGenerator`).
```js
sampleGenerator.forBlock['sample_block'] = function(block, generator) {
sampleGenerator.forBlock['sample_block'] = function (block, generator) {
return 'my code string';
};
```
@@ -26,7 +26,7 @@ A statement block's generator simply returns a string.
For example, this code defines a block generator that always returns the same function call.
```js
sampleGenerator.forBlock['left_turn_block'] = function(block, generator) {
sampleGenerator.forBlock['left_turn_block'] = function (block, generator) {
return 'turnLeft()';
};
```
@@ -40,7 +40,7 @@ A value block's generator returns an array containing a string and a [precedence
This code defines a block generator that always returns `1 + 1`:
```js
sampleGenerator.forBlock['two_block'] = function(block, generator) {
sampleGenerator.forBlock['two_block'] = function (block, generator) {
return ['1 + 1', Order.ADDITION];
};
```
@@ -61,4 +61,4 @@ In `src/generators/json.js`, declare a new enum called `Order` and add the `ATOM
const Order = {
ATOMIC: 0,
};
```
```
@@ -21,11 +21,15 @@ import CodelabImage from '@site/src/components/CodelabImage';
You will build a JSON generator that implements the [JSON language spec](https://www.json.org/json-en.html).
<CodelabImage>![Screenshot of the toolbox and workspace built in this codelab. It contains blocks that implement the JSON spec, like member, object, lists, strings, and numbers.](../../../static/images/codelabs/custom-generator/json_workspace.png)</CodelabImage>
<CodelabImage>
![Screenshot of the toolbox and workspace built in this codelab. It contains
blocks that implement the JSON spec, like member, object, lists, strings, and
numbers.](../../../static/images/codelabs/custom-generator/json_workspace.png)
</CodelabImage>
### What you'll need
- Familiarity with JSON and the JSON specification.
- Basic understanding of blocks and toolboxes in Blockly.
- NPM installed ([instructions](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)).
- Comfort using the command line/terminal.
- Comfort using the command line/terminal.
@@ -5,10 +5,10 @@
*/
import * as Blockly from 'blockly';
import {blocks} from './blocks/json';
import {jsonGenerator} from './generators/json';
import {save, load} from './serialization';
import {toolbox} from './toolbox';
import { blocks } from './blocks/json';
import { jsonGenerator } from './generators/json';
import { save, load } from './serialization';
import { toolbox } from './toolbox';
import './index.css';
// Register the blocks with Blockly
@@ -17,7 +17,7 @@ Blockly.common.defineBlocks(blocks);
// Set up UI elements and inject Blockly
const codeDiv = document.getElementById('generatedCode').firstChild;
const blocklyDiv = document.getElementById('blocklyDiv');
const ws = Blockly.inject(blocklyDiv, {toolbox});
const ws = Blockly.inject(blocklyDiv, { toolbox });
// This function resets the code div and shows the
// generated code from the workspace.
@@ -7,9 +7,10 @@ description: How to generate code for the blocks in a stack.
## 9. Generating a stack
### The scrub_ function
### The scrub\_ function
The `scrub_` function is called on every block from `blockToCode`. It takes in three arguments:
- `block` is the current block.
- `code` is the code generated for this block, which includes code from all attached value blocks.
- `opt_thisOnly` is an optional `boolean`. If true, code should be generated for this block but no subsequent blocks.
@@ -17,9 +18,8 @@ The `scrub_` function is called on every block from `blockToCode`. It takes in t
By default, `scrub_` simply returns the passed-in code. A common pattern is to override the function to also generate code for any blocks that follow the current block in a stack. In this case, the code will add commas and newlines between object members:
```js
jsonGenerator.scrub_ = function(block, code, thisOnly) {
const nextBlock =
block.nextConnection && block.nextConnection.targetBlock();
jsonGenerator.scrub_ = function (block, code, thisOnly) {
const nextBlock = block.nextConnection && block.nextConnection.targetBlock();
if (nextBlock && !thisOnly) {
return code + ',\n' + jsonGenerator.blockToCode(nextBlock);
}
@@ -27,8 +27,8 @@ jsonGenerator.scrub_ = function(block, code, thisOnly) {
};
```
### Testing scrub_
### Testing scrub\_
Create a stack of `member` blocks on the workspace. There should be generated code for all of the blocks, not just the first one.
Next, add an `object` block and drag the `member` blocks into it. This case tests `statementToCode`, and should generate code for all of of the blocks.
Next, add an `object` block and drag the `member` blocks into it. This case tests `statementToCode`, and should generate code for all of of the blocks.
@@ -27,14 +27,14 @@ Recall: the name of the value being fetched is `MEMBER_NAME` because that is how
### Input value
The **property value** is whatever is attached to the value input. A variety of blocks could be attached there: `logic_null`, `text`, `math_number`, `logic_boolean`, or even an array (`lists_create_with`). Use `valueToCode` to get the correct value:
The **property value** is whatever is attached to the value input. A variety of blocks could be attached there: `logic_null`, `text`, `math_number`, `logic_boolean`, or even an array (`lists_create_with`). Use `valueToCode` to get the correct value:
```js
const value = generator.valueToCode(block, 'MEMBER_VALUE',
Order.ATOMIC);
const value = generator.valueToCode(block, 'MEMBER_VALUE', Order.ATOMIC);
```
`valueToCode` does three things:
- Finds the blocks connected to the named value input (the second argument)
- Generates the code for that block
- Returns the code as a string
@@ -56,11 +56,10 @@ const code = `"${name}": ${value}`;
All together, here is block generator for the member block:
```js
jsonGenerator.forBlock['member'] = function(block, generator) {
jsonGenerator.forBlock['member'] = function (block, generator) {
const name = block.getFieldValue('MEMBER_NAME');
const value = generator.valueToCode(
block, 'MEMBER_VALUE', Order.ATOMIC);
const value = generator.valueToCode(block, 'MEMBER_VALUE', Order.ATOMIC);
const code = `"${name}": ${value}`;
return code;
};
```
```
@@ -28,6 +28,7 @@ The generated code looks like this:
We'll use `statementToCode` to get the code for the blocks attached to the statement input of the `object` block.
`statementToCode` does three things:
- Finds the first block connected to the named statement input (the second argument)
- Generates the code for that block
- Returns the code as a string
@@ -35,8 +36,7 @@ We'll use `statementToCode` to get the code for the blocks attached to the state
In this case the input name is `'MEMBERS'`.
```js
const statement_members =
generator.statementToCode(block, 'MEMBERS');
const statement_members = generator.statementToCode(block, 'MEMBERS');
```
### Format and return
@@ -47,6 +47,7 @@ Wrap the statements in curly brackets and return the code, using the default pre
const code = '{\n' + statement_members + '\n}';
return [code, Order.ATOMIC];
```
Note that `statementToCode` handles the indentation automatically.
### Test it
@@ -54,9 +55,8 @@ Note that `statementToCode` handles the indentation automatically.
Here is the full block generator:
```js
jsonGenerator.forBlock['object'] = function(block, generator) {
const statementMembers =
generator.statementToCode(block, 'MEMBERS');
jsonGenerator.forBlock['object'] = function (block, generator) {
const statementMembers = generator.statementToCode(block, 'MEMBERS');
const code = '{\n' + statementMembers + '\n}';
return [code, Order.ATOMIC];
};
@@ -70,4 +70,4 @@ Test it by generating code for an `object` block containing a single `member` bl
}
```
Next, add a second member block and rerun the generator. Did the resulting code change? Let's look at the next section to find out why not.
Next, add a second member block and rerun the generator. Did the resulting code change? Let's look at the next section to find out why not.
@@ -14,10 +14,11 @@ This codelab will demonstrate how to add code to the Blockly sample app to creat
### The application
Use the [`npx @blockly/create-package app`](https://www.npmjs.com/package/@blockly/create-package) command to create a standalone application that contains a sample setup of Blockly, including custom blocks and a display of the generated code and output.
1. Run `npx @blockly/create-package app custom-generator-codelab`. This will create a blockly application in the folder `custom-generator-codelab`.
1. `cd` into the new directory: `cd custom-generator-codelab`.
1. Run `npm start` to start the server and run the sample application.
1. The sample app will automatically run in the browser window that opens.
1. Run `npx @blockly/create-package app custom-generator-codelab`. This will create a blockly application in the folder `custom-generator-codelab`.
1. `cd` into the new directory: `cd custom-generator-codelab`.
1. Run `npm start` to start the server and run the sample application.
1. The sample app will automatically run in the browser window that opens.
The initial application has one custom block and includes JavaScript generator definitions for that block. Since this codelab will be creating a JSON generator instead, it will remove that custom block and add its own.
@@ -34,9 +35,10 @@ const storageKey = 'jsonGeneratorWorkspace';
This codelab will use two custom blocks, as well as five blocks from Blockly's standard set.
The custom blocks represent the *Object* and *Member* sections of the JSON specification.
The custom blocks represent the _Object_ and _Member_ sections of the JSON specification.
The blocks are:
- `object`
- `member`
- `math_number`
@@ -52,44 +54,46 @@ Create a new file in the `src/blocks/` directory called `json.js`. This will hol
```js
import * as Blockly from 'blockly';
export const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([{
"type": "object",
"message0": "{ %1 %2 }",
"args0": [
{
"type": "input_dummy"
},
{
"type": "input_statement",
"name": "MEMBERS"
}
],
"output": null,
"colour": 230,
},
{
"type": "member",
"message0": "%1 %2 %3",
"args0": [
{
"type": "field_input",
"name": "MEMBER_NAME",
"text": ""
},
{
"type": "field_label",
"name": "COLON",
"text": ":"
},
{
"type": "input_value",
"name": "MEMBER_VALUE"
}
],
"previousStatement": null,
"nextStatement": null,
"colour": 230,
}]);
export const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([
{
type: 'object',
message0: '{ %1 %2 }',
args0: [
{
type: 'input_dummy',
},
{
type: 'input_statement',
name: 'MEMBERS',
},
],
output: null,
colour: 230,
},
{
type: 'member',
message0: '%1 %2 %3',
args0: [
{
type: 'field_input',
name: 'MEMBER_NAME',
text: '',
},
{
type: 'field_label',
name: 'COLON',
text: ':',
},
{
type: 'input_value',
name: 'MEMBER_VALUE',
},
],
previousStatement: null,
nextStatement: null,
colour: 230,
},
]);
```
This code creates the block definitions, but it doesn't register the definitions with Blockly to make the blocks usable. We'll do that in `src/index.js`.
@@ -97,13 +101,13 @@ Currently, the app imports `blocks` from the original sample file, `text.js`. In
```js
// Remove this!
import {blocks} from './blocks/text';
import { blocks } from './blocks/text';
```
and add the import for the new blocks:
```js
import {blocks} from './blocks/json';
import { blocks } from './blocks/json';
```
Later in the file the block definitions are registered with Blockly (this code is already present and does not need to be added):
@@ -120,44 +124,48 @@ The file `src/toolbox.js` contains the original sample toolbox. Replace the enti
```js
export const toolbox = {
'kind': 'flyoutToolbox',
'contents': [
kind: 'flyoutToolbox',
contents: [
{
'kind': 'block',
'type': 'object'
kind: 'block',
type: 'object',
},
{
'kind': 'block',
'type': 'member'
kind: 'block',
type: 'member',
},
{
'kind': 'block',
'type': 'math_number'
kind: 'block',
type: 'math_number',
},
{
'kind': 'block',
'type': 'text'
kind: 'block',
type: 'text',
},
{
'kind': 'block',
'type': 'logic_boolean'
kind: 'block',
type: 'logic_boolean',
},
{
'kind': 'block',
'type': 'logic_null'
kind: 'block',
type: 'logic_null',
},
{
'kind': 'block',
'type': 'lists_create_with'
kind: 'block',
type: 'lists_create_with',
},
]
}
],
};
```
Our `index.js` file already handles importing the toolbox and using it in Blockly.
If the server is already running, refresh the page to see changes. Otherwise, run `npm start` to start the server. New blocks should now exist in the toolbox, like this:
<CodelabImage>![Screenshot of toolbox showing the added blocks, including the new member and object blocks, plus the built-in number, text, boolean, null, and list blocks.](../../../static/images/codelabs/custom-generator/toolbox_blocks.png)</CodelabImage>
<CodelabImage>
![Screenshot of toolbox showing the added blocks, including the new member and
object blocks, plus the built-in number, text, boolean, null, and list
blocks.](../../../static/images/codelabs/custom-generator/toolbox_blocks.png)
</CodelabImage>
The app is still trying to generate and run JavaScript for the workspace, instead of JSON. We will change that soon.
The app is still trying to generate and run JavaScript for the workspace, instead of JSON. We will change that soon.
@@ -9,6 +9,7 @@ description: Summary of the "Build a custom generator" codelab.
## 10. Summary
In this codelab you learned:
- How to build a custom language generator to generate JSON.
- How to define block generators for built in blocks and for custom blocks.
- How to use a custom generator in a sample app.
@@ -16,6 +17,7 @@ In this codelab you learned:
- How to generate code for stacks of blocks.
JSON is a simple language, and there are many additional features that could be implemented in a custom generator. Blockly's built-in language generators are a good place to learn more about some additional features:
- Variable definition and use.
- Function definition and use.
- Initialization and cleanup.
@@ -23,4 +25,4 @@ JSON is a simple language, and there are many additional features that could be
- Handling comments.
- Handling parentheses with operator precedence.
Blockly ships with five language generators: Python, Dart, JavaScript, PHP, and Lua. The language generators and block generators can be found in the [generators directory](https://github.com/RaspberryPiFoundation/blockly/tree/main/generators).
Blockly ships with five language generators: Python, Dart, JavaScript, PHP, and Lua. The language generators and block generators can be found in the [generators directory](https://github.com/RaspberryPiFoundation/blockly/tree/main/generators).
@@ -7,9 +7,10 @@ description: How to define and call a language generator.
## 3. The basics
A *language generator* defines the basic properties of a language, such as how indentation works. *Block generators* define how individual blocks are turned into code, and must be defined for every block used.
A _language generator_ defines the basic properties of a language, such as how indentation works. _Block generators_ define how individual blocks are turned into code, and must be defined for every block used.
A language generator has a single entry point: `workspaceToCode`. This function takes in a workspace and:
- Initializes the generator and any necessary state by calling `init`.
- Walks the list of top blocks on the workspace and calls `blockToCode` on each top block.
- Cleans up any leftover state by calling `finish`.
@@ -33,8 +34,8 @@ Next, hook up the new generator with the sample app. First, remove the old code
```js
// Remove these lines!
import {forBlock} from './generators/javascript';
import {javascriptGenerator} from 'blockly/javascript';
import { forBlock } from './generators/javascript';
import { javascriptGenerator } from 'blockly/javascript';
// Also remove this line! (further down)
Object.assign(javascriptGenerator.forBlock, forBlock);
@@ -43,7 +44,7 @@ Object.assign(javascriptGenerator.forBlock, forBlock);
Now import the new generator:
```js
import {jsonGenerator} from './generators/json';
import { jsonGenerator } from './generators/json';
```
Currently, there are two panels in the app next to the workspace. One shows the generated JavaScript code, and one executes it. The one panel showing the generated Javascript code will be changed to show the generated JSON code instead. Since JSON can't be directly executed, the panel that shows the execution will be left blank. Change the `runCode` function to the following:
@@ -74,4 +75,4 @@ Put a number block on the workspace and check the generator output area. It's em
Language "JSON" does not know how to generate code for block type "math_number".
```
This error occurs because there has to be a block generator for each type of block. Read the next section for more details.
This error occurs because there has to be a block generator for each type of block. Read the next section for more details.
@@ -17,14 +17,16 @@ It will use `getFieldValue` on several types of fields.
The simplest block in this example is the `logic_null` block.
<CodelabImage>![The null block simply returns "null".](../../../static/images/codelabs/custom-generator/null_block.png)</CodelabImage>
<CodelabImage>
![The null block simply returns
"null".](../../../static/images/codelabs/custom-generator/null_block.png)
</CodelabImage>
No matter what, it generates the code `'null'`. Notice that this is a string, because all generated code is a string.
Add the following code to `src/generators/json.js`:
```js
jsonGenerator.forBlock['logic_null'] = function(block) {
jsonGenerator.forBlock['logic_null'] = function (block) {
return ['null', Order.ATOMIC];
};
```
@@ -33,7 +35,10 @@ jsonGenerator.forBlock['logic_null'] = function(block) {
Next is the `text` block.
<CodelabImage>![The text block has an input for the user to type text into.](../../../static/images/codelabs/custom-generator/text_block.png)</CodelabImage>
<CodelabImage>
![The text block has an input for the user to type text
into.](../../../static/images/codelabs/custom-generator/text_block.png)
</CodelabImage>
Unlike `logic_null`, there is a single text input field on this block. Use `getFieldValue`:
@@ -44,7 +49,7 @@ const textValue = block.getFieldValue('TEXT');
Since this is a string in the generated code, wrap the value in quotation marks and return it:
```js
jsonGenerator.forBlock['text'] = function(block) {
jsonGenerator.forBlock['text'] = function (block) {
const textValue = block.getFieldValue('TEXT');
const code = `"${textValue}"`;
return [code, Order.ATOMIC];
@@ -55,14 +60,17 @@ jsonGenerator.forBlock['text'] = function(block) {
The `math_number` block has a number field.
<CodelabImage>![The number block has an input for a user to type a number](../../../static/images/codelabs/custom-generator/number_block.png)</CodelabImage>
<CodelabImage>
![The number block has an input for a user to type a
number](../../../static/images/codelabs/custom-generator/number_block.png)
</CodelabImage>
Like the `text` block, the `math_number` block can use `getFieldValue`. Unlike the text block, the function doesn't need to wrap it in additional quotation marks, because in the JSON code, it won't be a string.
However, like all generated code and as with `null` above, the function needs to return the code as a string from the generator.
```js
jsonGenerator.forBlock['math_number'] = function(block) {
jsonGenerator.forBlock['math_number'] = function (block) {
const code = String(block.getFieldValue('NUM'));
return [code, Order.ATOMIC];
};
@@ -72,13 +80,16 @@ jsonGenerator.forBlock['math_number'] = function(block) {
The `logic_boolean` block has a dropdown field named `BOOL`.
<CodelabImage>![The boolean block lets the user select 'true' or 'false' from a dropdown menu.](../../../static/images/codelabs/custom-generator/boolean_block.png)</CodelabImage>
<CodelabImage>
![The boolean block lets the user select 'true' or 'false' from a dropdown
menu.](../../../static/images/codelabs/custom-generator/boolean_block.png)
</CodelabImage>
Calling `getFieldValue` on a dropdown field returns the value of the selected option, which may not be the same as the display text. In this case the dropdown has two possible values: `TRUE` and `FALSE`.
```js
jsonGenerator.forBlock['logic_boolean'] = function(block) {
const code = (block.getFieldValue('BOOL') === 'TRUE') ? 'true' : 'false';
jsonGenerator.forBlock['logic_boolean'] = function (block) {
const code = block.getFieldValue('BOOL') === 'TRUE' ? 'true' : 'false';
return [code, Order.ATOMIC];
};
```
@@ -88,4 +99,4 @@ jsonGenerator.forBlock['logic_boolean'] = function(block) {
- Value blocks return an array containing the value as a string and the precedence.
- `getFieldValue` finds the field with the specified name and returns its value.
- The type of the return value from `getFieldValue` depends on the type of the field.
- Each field type must document what its value represents.
- Each field type must document what its value represents.
@@ -8,6 +8,7 @@ description: How to change connection shapes.
## 7. Change connection shapes
This step will define and use new shapes for previous/next connections and input/output connections. This takes three steps:
1. Define new shape objects.
1. Override `init()` to store the new shape objects.
1. Override `shapeFor(connection)` to return the new objects.
@@ -17,6 +18,7 @@ This step will define and use new shapes for previous/next connections and input
An outline path is drawn clockwise around the block, starting at the top left. As a result the previous connection is drawn from left-to-right, while the next connection is drawn from right-to-left.
Previous and next connections are defined by the same object. The object has four properties:
- `width`: The width of the connection.
- `height`: The height of the connection.
- `pathLeft`: The sub-path that describes the connection when drawn from left-to-right.
@@ -64,6 +66,7 @@ Define a new function called `makeRectangularPreviousConn()` and put it inside t
Just as previous/next connection shapes are drawn from left-to-right and right-to-left, input/output connection shapes are drawn from top-to-bottom and bottom-to-top.
Input and output connections are defined by the same object. The object has four properties:
- `width`: The width of the connection.
- `height`: The height of the connection.
- `pathUp`: The sub-path that describes the connection when drawn from top-to-bottom.
@@ -148,6 +151,6 @@ Next, override the `shapeFor(connection)` function in the `CustomConstantProvide
### The result
Return to the browser, click on the `Loops` entry, and drag out a repeat block. The resulting block should have rectangular connections for all four connection types.
Return to the browser, click on the `Loops` entry, and drag out a repeat block. The resulting block should have rectangular connections for all four connection types.
![[Screenshot of a custom renderer with notches, corners, and tabs with fundamentally different shapes than the defaults.]](../../../static/images/codelabs/custom-renderer/custom_notches.png)
![[Screenshot of a custom renderer with notches, corners, and tabs with fundamentally different shapes than the defaults.]](../../../static/images/codelabs/custom-renderer/custom_notches.png)
@@ -20,16 +20,16 @@ description: Overview of the "Build custom renderers" codelab.
This codelab builds and uses four renderers:
1. A minimal custom renderer that extends `Blockly.blockRendering.Renderer` but makes no modifications.
![Screenshot of a renderer with an appearance matching the base renderer.](../../../static/images/codelabs/custom-renderer/custom_renderer.png)
![Screenshot of a renderer with an appearance matching the base renderer.](../../../static/images/codelabs/custom-renderer/custom_renderer.png)
1. A custom renderer which sets new values for the rendering-related constants `NOTCH_WIDTH`, `NOTCH_HEIGHT`,`CORNER_RADIUS`, and `TAB_HEIGHT` found in `Blockly.blockRendering.ConstantProvider`.
![Screenshot of a custom renderer with notches, corners, and tabs that have similar shapes as the default but with different widths, heights, and radiuses.](../../../static/images/codelabs/custom-renderer/custom_constants.png)
![Screenshot of a custom renderer with notches, corners, and tabs that have similar shapes as the default but with different widths, heights, and radiuses.](../../../static/images/codelabs/custom-renderer/custom_constants.png)
1. A custom renderer which overrides the functions `Blockly.blockRendering.ConstantProvider.init()` and `Blockly.blockRendering.ConstantProvider.shapeFor(connection)` to define and return custom [SVG paths](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path).
![Screenshot of a custom renderer with notches, corners, and tabs with fundamentally different shapes than the defaults.](../../../static/images/codelabs/custom-renderer/custom_notches.png)
![Screenshot of a custom renderer with notches, corners, and tabs with fundamentally different shapes than the defaults.](../../../static/images/codelabs/custom-renderer/custom_notches.png)
1. A custom renderer which overrides the function `Blockly.blockRendering.ConstantProvider.shapeFor(connection)` to return different shapes for the input/output connections depending on whether the their type is a `Number`, `String`, or `Boolean`.
![Screenshot of a custom renderer with rectangles for the Number input/outputs and a puzzle tab for the Boolean input/output attached to an "if" block](../../../static/images/codelabs/custom-renderer/typed_connection_shapes.png)
![Screenshot of a custom renderer with rectangles for the Number input/outputs and a puzzle tab for the Boolean input/output attached to an "if" block](../../../static/images/codelabs/custom-renderer/typed_connection_shapes.png)
### What you'll need
- Basic understanding of renderers and toolboxes in Blockly.
- NPM installed ([instructions](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)).
- Comfort using the command line/terminal.
- Comfort using the command line/terminal.
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {Order} from 'blockly/javascript';
import { Order } from 'blockly/javascript';
// Export all the code generators for our custom blocks,
// but don't register them with Blockly yet.
@@ -5,11 +5,11 @@
*/
import * as Blockly from 'blockly';
import {blocks} from './blocks/text';
import {forBlock} from './generators/javascript';
import {javascriptGenerator} from 'blockly/javascript';
import {save, load} from './serialization';
import {toolbox} from './toolbox';
import { blocks } from './blocks/text';
import { forBlock } from './generators/javascript';
import { javascriptGenerator } from 'blockly/javascript';
import { save, load } from './serialization';
import { toolbox } from './toolbox';
import './renderers/custom';
import './index.css';
@@ -52,4 +52,4 @@ const ws = Blockly.inject(blocklyDiv, {
If the server is already running, refresh the page to see the new changes. Otherwise, run `npm start` to start the server. Once the server is running, click on the `Loops` entry in the browser and drag out a repeat block. The resulting block will use the same values already defined in the base `Blockly.blockRendering.Renderer`.
![Screenshot of a renderer with an appearance matching the base renderer.](../../../static/images/codelabs/custom-renderer/custom_renderer.png)
![Screenshot of a renderer with an appearance matching the base renderer.](../../../static/images/codelabs/custom-renderer/custom_renderer.png)
@@ -9,4 +9,4 @@ description: Introduction to the built-in renderers.
First, visit [the advanced playground](https://blockly-demo.appspot.com/static/tests/playgrounds/advanced_playground.html) to observe what the built-in renderers look like.
Click on the "Loops" entry and drag out a repeat block. Now, change the selection in the "renderer" drop down to observe the look of each built-in renderer. By default, the renderer named "Geras" is used.
Click on the "Loops" entry and drag out a repeat block. Now, change the selection in the "renderer" drop down to observe the look of each built-in renderer. By default, the renderer named "Geras" is used.
@@ -7,11 +7,12 @@ description: How to override the constants in a renderer.
## 5. Override constants
A **ConstantsProvider** holds all rendering-related constants. This includes sizing information and colours. Blockly provides a base **ConstantsProvider** with all required fields set to default values.
A **ConstantsProvider** holds all rendering-related constants. This includes sizing information and colours. Blockly provides a base **ConstantsProvider** with all required fields set to default values.
The **ConstantsProvider** `constructor()` sets all static properties, such as `NOTCH_WIDTH` and `NOTCH_HEIGHT`. For a full list of properties, see [constants.ts](https://github.com/RaspberryPiFoundation/blockly/blob/main/core/renderers/common/constants.ts).
Only override the necessary subset of the constants, rather than all of them. To do so:
- Define a constants provider that extends the base `ConstantProvider`.
- Call the superclass `super()` in the `constructor()`.
- Set individual properties.
@@ -69,6 +70,6 @@ To use the new **CustomConstantProvider**, override `makeConstants_()` inside th
### The result
Return to the browser, click on the `Loops` entry, and drag out a repeat block. The resulting block should have triangular previous and next connections, and skinny input and output connections. Note that the general shapes of the connections have not changed--only parameters such as width and height.
Return to the browser, click on the `Loops` entry, and drag out a repeat block. The resulting block should have triangular previous and next connections, and skinny input and output connections. Note that the general shapes of the connections have not changed--only parameters such as width and height.
![Screenshot of a custom renderer with notches, corners, and tabs that have similar shapes as the default but with different widths, heights, and radiuses.](../../../static/images/codelabs/custom-renderer/custom_constants.png)
![Screenshot of a custom renderer with notches, corners, and tabs that have similar shapes as the default but with different widths, heights, and radiuses.](../../../static/images/codelabs/custom-renderer/custom_constants.png)
@@ -12,10 +12,11 @@ This codelab will add code to the Blockly sample app to create and use a new cus
### The application
Use the Use the [`npx @blockly/create-package`](https://www.npmjs.com/package/@blockly/create-package) command to create a standalone application that contains a sample setup of Blockly, including custom blocks and a display of the generated code and output.
1. Run `npx @blockly/create-package app custom-renderer-codelab`. This will create a blockly application in the folder `custom-renderer-codelab`.
1. `cd` into the new directory: `cd custom-renderer-codelab`.
1. Run `npm start` to start the server and run the sample application.
1. The sample app will automatically run in the browser window that opens.
1. Run `npx @blockly/create-package app custom-renderer-codelab`. This will create a blockly application in the folder `custom-renderer-codelab`.
1. `cd` into the new directory: `cd custom-renderer-codelab`.
1. Run `npm start` to start the server and run the sample application.
1. The sample app will automatically run in the browser window that opens.
The initial application uses the default renderer and contains no code or definitions for a custom renderer.
@@ -26,4 +27,4 @@ Before setting up the rest of the application, change the storage key used for t
```js
// Use a unique storage key for this codelab
const storageKey = 'customRenderersWorkspace';
```
```
@@ -4,13 +4,13 @@ slug: /codelabs/custom-renderer/summary/index.html
description: Summary of the "Build custom renderers" codelab.
---
# Build custom renderers
## 9. Summary
Custom renderers are a powerful way to change the look and feel of Blockly. In this codelab you learned:
Custom renderers are a powerful way to change the look and feel of Blockly. In this codelab you learned:
- How to declare and register a custom renderer by extending `Blockly.blockRendering.Renderer`.
- How to override renderer constants such as `NOTCH_HEIGHT` in `Blockly.blockRendering.ConstantProvider`.
- How to modify connection shapes by creating custom [SVG paths](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path), storing them in `init()`, and finally returning them in `shapeFor(connection)`.
- How to update the mapping from connection to connection shape by adding logic in `shapeFor(connection)`.
- How to update the mapping from connection to connection shape by adding logic in `shapeFor(connection)`.
@@ -14,6 +14,7 @@ This step will create a renderer that sets connection shapes at runtime based on
Override the `shapeFor(connection)` function in the `CustomConstantProvider` class definition to return a different connection shape based on the `checks` returned from `connection.getCheck()`. Note the previous definition of `shapeFor(connection)` created in previous steps will need to be deleted.
The new definition of `shapeFor(connection)` will:
- Return a rectangular tab for inputs and outputs that accept `Number`s and `String`s.
- Return the default puzzle tab for all other inputs and outputs.
- Return the normal notch for all previous and next connections.
@@ -45,8 +46,9 @@ shapeFor(connection) {
### The result
Take these steps to fully test this change in the browser:
1. Click on the `Loops` entry and drag out a repeat block.
1. Click on the `Logic` entry and drag the conditional `if` block into the repeat block that was placed on the workspace in the previous step.
There should be an entry similar to the screenshot below, in which the `Number` inputs and outputs are rectangular, but the boolean input on the `if` block is a puzzle tab.
![Screenshot of a custom renderer with rectangles for the Number input/outputs and a puzzle tab for the Boolean input/output attached to an "if" block](../../../static/images/codelabs/custom-renderer/typed_connection_shapes.png)
![Screenshot of a custom renderer with rectangles for the Number input/outputs and a puzzle tab for the Boolean input/output attached to an "if" block](../../../static/images/codelabs/custom-renderer/typed_connection_shapes.png)
@@ -65,4 +65,4 @@ shapeFor(connection: RenderedConnection): Shape {
throw Error('Unknown connection type');
}
}
```
```
@@ -10,33 +10,41 @@ import CodelabImage from '@site/src/components/CodelabImage';
## 5. Add an icon to your category
We are going to add an icon to our "Logic" category by adding an icon library to
our `index.html` file, and setting the appropriate CSS class on our category definition.
our `index.html` file, and setting the appropriate CSS class on our category definition.
To start, we are going to grab an icon library and add it to `index.html`:
```
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
```
We are going to add a cog icon from this library to the "Logic" category.
To do this, we will add the appropriate CSS classes to our category definition.
In `index.html` scroll down to your toolbox definition and change the below line:
```xml
<category name="Logic" categorystyle="logic_category">
```
to be:
```xml
<category css-icon="customIcon fa fa-cog" name="Logic" categorystyle="logic_category">
```
All the classes used to create a category can be set similar to how we set the
icon class above. See the [Blockly toolbox documentation](/guides/configure/web/toolboxes/appearance#category-css) for more information.
### Add some CSS
If you open `index.html` you will notice that the gear icon is positioned
incorrectly and is a bit difficult to see. We will use the `customIcon` class to
change the color of the icon and use the `blocklyTreeRowContentContainer` class
to position the icon above the text.
In your `toolbox_style.css` file add:
```css
/* Changes color of the icon to white. */
.customIcon {
@@ -54,6 +62,7 @@ In your `toolbox_style.css` file add:
```
### Update setSelected
If you open `index.html` and click on the "Logic" category you will notice
that the white icon now blends into the white background.
@@ -68,11 +77,13 @@ this.iconDom_.style.color = this.colour_;
```
Add the below line to `setSelected` if the category has not been selected:
```js
this.iconDom_.style.color = 'white';
```
Your `setSelected` method should look similar to below:
```js
/** @override */
setSelected(isSelected){
@@ -96,10 +107,20 @@ Your `setSelected` method should look similar to below:
Blockly.utils.aria.State.SELECTED, isSelected);
}
```
### The result
If you open your `index.html` file, you should see a white gear above your "Logic"
label, and it should change to blue when the category has been selected.
<CodelabImage> ![A white gear above the word "Logic" on a blue background.](../../../static/images/codelabs/custom-toolbox/category_gear.png) </CodelabImage>
<CodelabImage>
{' '}
![A white gear above the word "Logic" on a blue
background.](../../../static/images/codelabs/custom-toolbox/category_gear.png){' '}
</CodelabImage>
<CodelabImage> ![A blue gear above the word "Logic" on a white background.](../../../static/images/codelabs/custom-toolbox/category_gear_selected.png) </CodelabImage>
<CodelabImage>
{' '}
![A blue gear above the word "Logic" on a white
background.](../../../static/images/codelabs/custom-toolbox/category_gear_selected.png){' '}
</CodelabImage>
@@ -8,15 +8,18 @@ import CodelabImage from '@site/src/components/CodelabImage';
# Customizing a Blockly toolbox
## 7. Adding a custom toolbox item
In the previous sections we modified the toolbox by extending the base category class.
In this section we will make a completely new toolbox item and add it to our toolbox.
For this example, we are going to create a toolbox label.
### Setup
In the same directory as `index.html` create a new file named `toolbox_label.js`.
Include this file in `index.html`:
```html
<script src="toolbox_label.js"></script>
```
@@ -32,20 +35,24 @@ class ToolboxLabel extends Blockly.ToolboxItem {
}
Blockly.registry.register(
Blockly.registry.Type.TOOLBOX_ITEM,
'toolboxlabel',
ToolboxLabel);
Blockly.registry.Type.TOOLBOX_ITEM,
'toolboxlabel',
ToolboxLabel,
);
```
By registering this toolbox item with the name "toolboxlabel" we can now use this
name in our toolbox definition to add our custom item to the toolbox.
Navigate to `index.html`, and scroll down to the toolbox definition. Add a
`<toolboxlabel>` element as the first item in your toolbox definition:
```xml
<toolboxlabel></toolboxlabel>
```
Your toolbox definition should now look something like:
```xml
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox-categories" style="display: none">
<toolboxlabel></toolboxlabel>
@@ -55,6 +62,7 @@ Your toolbox definition should now look something like:
```
### Initialize the toolbox item
In order to create a toolbox item we must implement one of the toolbox item interfaces.
For this example, we will be implementing the basic `IToolboxItem` interface.
@@ -64,6 +72,7 @@ Since we do not need our label to be selectable or collapsible, we can implement
the basic `IToolboxItem` interface.
First, we are going to add an init method that will create the dom for our toolbox label:
```js
/** @override */
init() {
@@ -85,9 +94,14 @@ Next, we are going to return this element:
If you open the `index.html` file you should see a label above your first category.
<CodelabImage> ![The toolbox with a label at the top.](../../../static/images/codelabs/custom-toolbox/toolbox_label.png) </CodelabImage>
<CodelabImage>
{' '}
![The toolbox with a label at the
top.](../../../static/images/codelabs/custom-toolbox/toolbox_label.png){' '}
</CodelabImage>
### Add attributes to the toolbox definition
The above code is rather limiting since it only allows us to create a toolbox
label with the text "Label".
To make it possible to create different labels with different text and colour we
@@ -95,6 +109,7 @@ are going to add `name` and `colour` attributes to our toolbox definition.
Open `index.html` and navigate to the toolbox definition. Change your
`toolboxlabel` element to look like the below line:
```xml
<toolboxlabel name="Custom Toolbox" colour="darkslategrey"></toolboxlabel>
```
@@ -110,6 +125,7 @@ this.label.style.color = this.toolboxItemDef_['colour'];
```
Remove the following line from your init method:
```js
this.label.textContent = 'Label';
```
@@ -119,9 +135,14 @@ All attributes on our toolbox definition get added to the `toolboxItemDef_`.
Open your `index.html` in a browser to see the updated label.
<CodelabImage> ![The toolbox with a label that now says "Custom Toolbox".](../../../static/images/codelabs/custom-toolbox/custom_label.png) </CodelabImage>
<CodelabImage>
{' '}
![The toolbox with a label that now says "Custom
Toolbox".](../../../static/images/codelabs/custom-toolbox/custom_label.png){' '}
</CodelabImage>
### Add some CSS
Similar to how we added `colour` and `name` above, we are going to add a custom
class to our label.
@@ -149,14 +170,20 @@ if (cssConfig) {
The above code will add the class to the label. Now, in `toolbox_style.css` add
the below CSS to make the label bold.
```css
.customLabel {
font-weight: bold;
font-weight: bold;
}
```
### The result
If you open `index.html` you should now see a bold dark gray label at the
top of your toolbox.
<CodelabImage> ![A toolbox with colored background and the blockly label above the category text.](../../../static/images/codelabs/custom-toolbox/final_toolbox.png) </CodelabImage>
<CodelabImage>
{' '}
![A toolbox with colored background and the blockly label above the category
text.](../../../static/images/codelabs/custom-toolbox/final_toolbox.png){' '}
</CodelabImage>
@@ -8,16 +8,19 @@ import CodelabImage from '@site/src/components/CodelabImage';
# Customizing a Blockly toolbox
## 6. Change the category HTML
If you only need to change the CSS, like we did in the previous section, then using the cssConfig is a great choice.
However, if you need to change the html, maybe to add text, an image, or anything else, you can override
the corresponding method that creates the dom. In this example, we'll add an `<img>`
to our category by overriding the `createIconDom_` method.
### Change the element for our icon
By default, the `createIconDom_` method adds a `<span>` element for the category
icon. We can override this to return an `<img>` element.
Add the following methods to `custom_category.js`:
```js
/** @override */
createIconDom_() {
@@ -31,7 +34,12 @@ createIconDom_() {
```
### The result
If you open `index.html` you should now see the blockly logo on top of all your
categories
<CodelabImage> ![A toolbox with the blockly logo on top of the category label.](../../../static/images/codelabs/custom-toolbox/image_toolbox.png) </CodelabImage>
<CodelabImage>
{' '}
![A toolbox with the blockly logo on top of the category
label.](../../../static/images/codelabs/custom-toolbox/image_toolbox.png){' '}
</CodelabImage>
@@ -15,16 +15,19 @@ In the default `ToolboxCategory` class, the `addColourBorder_` method adds a str
to the category name. We can override this method in order to add colour to the entire category div.
Add the following code to your `CustomCategory` class.
```js
/** @override */
addColourBorder_(colour){
this.rowDiv_.style.backgroundColor = colour;
}
```
The `colour` passed in is calculated from either the `categorystyle` or the `colour`
attribute set on the category definition.
For example, the "Logic" category definition looks like:
```xml
<category name="Logic" categorystyle="logic_category">
...
@@ -38,6 +41,7 @@ The logic_category style looks like:
"colour": "210"
}
```
For more information on Blockly styles please visit the [themes documentation](/guides/configure/web/appearance/themes#category-style).
### Add some CSS
@@ -45,7 +49,11 @@ For more information on Blockly styles please visit the [themes documentation](/
Open `index.html` to see your updated toolbox. Your toolbox should look
similar to the below toolbox.
<CodelabImage> ![A toolbox with colors that expand the across the entire category.](../../../static/images/codelabs/custom-toolbox/colored_toolbox.png) </CodelabImage>
<CodelabImage>
{' '}
![A toolbox with colors that expand the across the entire
category.](../../../static/images/codelabs/custom-toolbox/colored_toolbox.png){' '}
</CodelabImage>
We are going to add some CSS to make it easier to read, and to space out our categories.
@@ -57,6 +65,7 @@ and include it in `index.html`:
```
Copy and paste the following CSS into your `toolbox_style.css` file.
```css
/* Makes our label white. */
.blocklyToolboxCategoryLabel {
@@ -78,4 +87,8 @@ Copy and paste the following CSS into your `toolbox_style.css` file.
Open `index.html` to see your toolbox.
<CodelabImage> ![Toolbox with category corners that are rounded and white text.](../../../static/images/codelabs/custom-toolbox/styled_toolbox.png) </CodelabImage>
<CodelabImage>
{' '}
![Toolbox with category corners that are rounded and white
text.](../../../static/images/codelabs/custom-toolbox/styled_toolbox.png){' '}
</CodelabImage>
@@ -21,6 +21,7 @@ color of the div to white, and the text to the color of the category when it has
been selected.
Add the following code to `custom_category.js`:
```js
/** @override */
setSelected(isSelected){
@@ -44,7 +45,13 @@ setSelected(isSelected){
```
### The result
Open `index.html` and click on the "Logic" category. You should now see a white
category with a colored label.
<CodelabImage> ![A toolbox with all categories colored except for the first category that has a white background.](../../../static/images/codelabs/custom-toolbox/category_selected.png) </CodelabImage>
<CodelabImage>
{' '}
![A toolbox with all categories colored except for the first category that has
a white
background.](../../../static/images/codelabs/custom-toolbox/category_selected.png){' '}
</CodelabImage>
@@ -11,9 +11,11 @@ import CodelabImage from '@site/src/components/CodelabImage';
## 1. Codelab overview
### What you'll learn
This codelab will focus on customizing the Blockly toolbox.
In this codelab you will learn:
1. How to add a background color to a toolbox category.
1. How to change the look of a selected category.
1. How to add a custom CSS classes to a toolbox category.
@@ -26,11 +28,16 @@ Over the course of this codelab you will customize your toolbox categories as we
as create a custom toolbox item.
The resulting toolbox is shown below.
<CodelabImage> ![A toolbox with colored background and the blockly label above the category text.](../../../static/images/codelabs/custom-toolbox/final_toolbox.png) </CodelabImage>
<CodelabImage>
{' '}
![A toolbox with colored background and the blockly label above the category
text.](../../../static/images/codelabs/custom-toolbox/final_toolbox.png){' '}
</CodelabImage>
The code samples are written in ES6 syntax. You can find the code for the [completed custom toolbox](https://github.com/RaspberryPiFoundation/blockly/docs/docs/codelabs/custom-toolbox/complete-code) on GitHub.
### What you'll need
- A browser.
- A text editor.
- Basic knowledge of HTML, CSS, and JavaScript.
@@ -38,4 +45,4 @@ The code samples are written in ES6 syntax. You can find the code for the [compl
Throughout various parts of this codelab we will be talking about [toolbox definitions](/guides/configure/web/toolboxes/category?tab=xml).
The toolbox definition can be written in XML or JSON. We will be using an XML
toolbox definition that can be found in the provided code.
toolbox definition that can be found in the provided code.
@@ -10,6 +10,7 @@ import CodelabImage from '@site/src/components/CodelabImage';
## 2. Setup
### Download the sample code
You can get the sample code for this codelab by either downloading the zip here:
[Download zip](https://github.com/RaspberryPiFoundation/blockly/archive/main.zip)
@@ -23,10 +24,12 @@ git clone https://github.com/RaspberryPiFoundation/blockly.git
If you downloaded the source as a zip, unpacking it should give you a root folder named `blockly-main`.
The relevant files are in `docs/docs/codelabs/custom-toolbox`. There are two versions of the app:
- `starter-code/`: The starter code that you'll build upon in this codelab.
- `complete-code/`: The code after completing the codelab, in case you get lost or want to compare to your version.
Each folder contains:
- `index.js` - The codelab's logic. To start, it just injects a simple workspace.
- `index.html` - A web page containing a simple blockly workspace.
@@ -35,6 +38,7 @@ To run the code, simply open `starter-code/index.html` in a browser. You should
![A web page with the text "Toolbox Customization Codelab" and a Blockly workspace.](../../../static/images/codelabs/custom-toolbox/starter_workspace.png)
### Define and register a custom category
To start, create a file named `custom_category.js` in the `starter-code`
directory. Include your new file by adding a script tag to `index.html`.
@@ -45,6 +49,7 @@ directory. Include your new file by adding a script tag to `index.html`.
In order to create a custom category we will create a new category that extends
the default `Blockly.ToolboxCategory` class. Add the following code to your
`custom_category.js` file.
```js
class CustomCategory extends Blockly.ToolboxCategory {
/**
@@ -59,12 +64,16 @@ class CustomCategory extends Blockly.ToolboxCategory {
After defining your category you need to tell Blockly that it exists.
Register your category by adding the below code to the end of `custom_category.js`.
```js
Blockly.registry.register(
Blockly.registry.Type.TOOLBOX_ITEM,
Blockly.ToolboxCategory.registrationName,
CustomCategory, true);
Blockly.registry.Type.TOOLBOX_ITEM,
Blockly.ToolboxCategory.registrationName,
CustomCategory,
true,
);
```
By registering our `CustomCategory` with `Blockly.ToolboxCategory.registrationName`
we are overriding the default category in Blockly. Because we are overriding a
toolbox item instead of adding a new one, we must pass in `true` as the last
@@ -76,7 +85,11 @@ an error because we are overriding an existing class.
To test, open `index.html` in a browser. Your toolbox should look the same as it
did before.
<CodelabImage> ![The default toolbox. A list of categories with a strip of colour to the left.](../../../static/images/codelabs/custom-toolbox/base_toolbox.png) </CodelabImage>
<CodelabImage>
{' '}
![The default toolbox. A list of categories with a strip of colour to the
left.](../../../static/images/codelabs/custom-toolbox/base_toolbox.png){' '}
</CodelabImage>
However, if you run the below commands in your console you will see that
your toolbox is now using the `CustomCategory` class.
@@ -84,4 +97,4 @@ your toolbox is now using the `CustomCategory` class.
```js
var toolbox = Blockly.common.getMainWorkspace().getToolbox();
toolbox.getToolboxItems()[0];
```
```
@@ -4,16 +4,16 @@ slug: /codelabs/custom-toolbox/summary/index.html
description: Summary of the "Customizing a Blockly toolbox" codelab.
---
# Customizing a Blockly toolbox
## 8. Summary
The toolbox can be customized in a variety of ways to make it work for your application. In this codelab you learned:
* How to create a custom category.
* What method to override to change how the colour of the category is applied.
* What method to override to change the look of the category when selected.
* How to add an icon by adding a custom CSS class to the icon div.
* How to change what HTML Elements are used for different parts of a category.
* How to create a custom toolbox item.
You can find the code for the [completed custom toolbox](https://github.com/RaspberryPiFoundation/blockly/docs/docs/codelabs/custom-toolbox/complete-code) on GitHub.
- How to create a custom category.
- What method to override to change how the colour of the category is applied.
- What method to override to change the look of the category when selected.
- How to add an icon by adding a custom CSS class to the icon div.
- How to change what HTML Elements are used for different parts of a category.
- How to create a custom toolbox item.
You can find the code for the [completed custom toolbox](https://github.com/RaspberryPiFoundation/blockly/docs/docs/codelabs/custom-toolbox/complete-code) on GitHub.
@@ -9,7 +9,7 @@ description: How to add Blockly libraries to an app.
Now that you know what you'll be building, you need to add Blockly dependencies to your app.
Blockly releases are [published on npm](https://www.npmjs.com/package/blockly) on a quarterly basis. For this codelab you will import blockly using [unpkg](https://unpkg.com), which lets you import all of the files you need with a single script tag.
Blockly releases are [published on npm](https://www.npmjs.com/package/blockly) on a quarterly basis. For this codelab you will import blockly using [unpkg](https://unpkg.com), which lets you import all of the files you need with a single script tag.
### Add the script tag
@@ -20,7 +20,7 @@ Open `starter-code/index.html` in a text editor and scroll to the end. You can s
<script src="scripts/main.js"></script>
```
Add Blockly just before these two scripts. The order is important, because you will use Blockly objects later in `main.js`. Your imports should now look like this:
Add Blockly just before these two scripts. The order is important, because you will use Blockly objects later in `main.js`. Your imports should now look like this:
```html
<script src="https://unpkg.com/blockly/blockly_compressed.js"></script>
@@ -34,6 +34,7 @@ Add Blockly just before these two scripts. The order is important, because you w
### Default imports
Importing Blockly this way loads four default modules.
- Blockly core: The main Blockly library, which defines the basic Blockly UI and logic.
- Built-in block definitions: Common blocks such as loops, logic, math, and string manipulation.
- The JavaScript generator: Converts blocks into JavaScript, and contains block generators for all built-in blocks.
@@ -26,14 +26,20 @@ Blockly includes everything you need for defining and rendering blocks in a drag
MusicMaker, a web app where you can program buttons to play different sounds, using Blockly.
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/play_mode.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/play_mode.png){' '}
</CodelabImage>
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/d4_three_times.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/d4_three_times.png){' '}
</CodelabImage>
### What you'll need
- A browser
- A text editor
- Basic knowledge of HTML, CSS and JavaScript
- A browser
- A text editor
- Basic knowledge of HTML, CSS and JavaScript
This codelab is focused on Blockly. The app structure, non-relevant concepts and code are glossed over and are provided for you to simply copy and paste.
@@ -14,10 +14,14 @@ In this section you will learn how to add a workspace to your app, including how
### Parts of Blockly
A Blockly workspace has two main components:
- The area where the user assembles their blocks (the white area).
- A toolbox that contains all blocks that are available to the user (the grey area).
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/d4_three_times.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/d4_three_times.png){' '}
</CodelabImage>
The toolbox may be organized into categories, and may contain both single blocks and groups of blocks. A well-organized toolbox helps the user to explore the available blocks and understand the capabilities of the underlying system.
@@ -31,23 +35,23 @@ Open up `scripts/main.js` and scroll down to the end of the file. Then add the c
```js
const toolbox = {
'kind': 'flyoutToolbox',
'contents': [
kind: 'flyoutToolbox',
contents: [
{
'kind': 'block',
'type': 'controls_repeat_ext',
'inputs': {
'TIMES': {
'shadow': {
'type': 'math_number',
'fields': {
'NUM': 5
}
}
}
}
}
]
kind: 'block',
type: 'controls_repeat_ext',
inputs: {
TIMES: {
shadow: {
type: 'math_number',
fields: {
NUM: 5,
},
},
},
},
},
],
};
```
@@ -55,9 +59,10 @@ This JavaScript object defines a toolbox with a single "repeat loop" block.
### Injection
Adding a Blockly workspace to a page is called *injection*, because the workspace is injected into a `div` that already exists on the page.
Adding a Blockly workspace to a page is called _injection_, because the workspace is injected into a `div` that already exists on the page.
To do this you call the function `Blockly.inject(container, options)`, which takes two arguments:
- `container` is where the Blockly workspace should be placed on the page. It can be an `Element`, an ID string, or a CSS selector.
- `options` is a dictionary of configuration options.
@@ -76,7 +81,7 @@ Blockly.inject('blocklyDiv', {
toolbox: toolbox,
scrollbars: false,
horizontalLayout: true,
toolboxPosition: "end",
toolboxPosition: 'end',
});
```
@@ -93,6 +98,9 @@ The `options` struct gives you significant control over your Blockly instance. Y
Now refresh the page. Select the EDIT mode, then tap on one of the buttons. You should see a Blockly editor:
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/workspace_with_loop.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/workspace_with_loop.png){' '}
</CodelabImage>
Drag and drop the available loop block to the workspace to test it out.
Drag and drop the available loop block to the workspace to test it out.
@@ -11,9 +11,12 @@ import CodelabImage from '@site/src/components/CodelabImage';
Since this is a music maker app, we want a block that plays sounds. We could create one block per sound, but instead we will create a single block with a dropdown to select which note to play:
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/play_sound_block.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/play_sound_block.png){' '}
</CodelabImage>
In Blockly, a *block definition* describes how a block looks and behaves. This includes its text, colour, and shape. It may also include which other blocks it can connect to.
In Blockly, a _block definition_ describes how a block looks and behaves. This includes its text, colour, and shape. It may also include which other blocks it can connect to.
Blocks can be defined in either JavaScript or JSON. The developer site has a full article on [how to define a block](/guides/create-custom-blocks/define/block-definitions#how-to-create-block-definitions).
@@ -29,25 +32,25 @@ Create a JS file to define a new "play sound" block:
```js
Blockly.common.defineBlocksWithJsonArray([
{
"type": "play_sound",
"message0": "Play %1",
"args0": [
type: 'play_sound',
message0: 'Play %1',
args0: [
{
"type": "field_dropdown",
"name": "VALUE",
"options": [
["C4", "sounds/c4.m4a"],
["D4", "sounds/d4.m4a"],
["E4", "sounds/e4.m4a"],
["F4", "sounds/f4.m4a"],
["G4", "sounds/g4.m4a"]
]
}
type: 'field_dropdown',
name: 'VALUE',
options: [
['C4', 'sounds/c4.m4a'],
['D4', 'sounds/d4.m4a'],
['E4', 'sounds/e4.m4a'],
['F4', 'sounds/f4.m4a'],
['G4', 'sounds/g4.m4a'],
],
},
],
"previousStatement": null,
"nextStatement": null,
"colour": 355
}
previousStatement: null,
nextStatement: null,
colour: 355,
},
]);
```
@@ -57,7 +60,7 @@ Add a script tag to `index.html` to include your new block definition:
<script src="scripts/sound_blocks.js"></script>
```
Your sound block definitions must come after importing Blockly and before the other imports, since you will use Blockly functions in this file, and you will be using functions from this file in later files. Your imports should now look like this:
Your sound block definitions must come after importing Blockly and before the other imports, since you will use Blockly functions in this file, and you will be using functions from this file in later files. Your imports should now look like this:
```html
<script src="https://unpkg.com/blockly"></script>
@@ -72,34 +75,37 @@ Now we can update the toolbox to include the new sound block, by adding `{'kind'
```js
const toolbox = {
'kind': 'flyoutToolbox',
'contents': [
kind: 'flyoutToolbox',
contents: [
{
'kind': 'block',
'type': 'controls_repeat_ext',
'inputs': {
'TIMES': {
'shadow': {
'type': 'math_number',
'fields': {
'NUM': 5
}
}
}
}
kind: 'block',
type: 'controls_repeat_ext',
inputs: {
TIMES: {
shadow: {
type: 'math_number',
fields: {
NUM: 5,
},
},
},
},
},
{
'kind': 'block',
'type': 'play_sound'
}
]
kind: 'block',
type: 'play_sound',
},
],
};
```
Run the app one more time, and play around with the new `Play (sound)` block. It should look like this:
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/workspace_with_toolbox.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/workspace_with_toolbox.png){' '}
</CodelabImage>
### The block factory
This step discussed how to manually define custom blocks in Blockly. Once you've completed the entire codelab, we recommend that you check out our [block factory tool](/guides/create-custom-blocks/blockly-developer-tools), which helps automate part of this process.
This step discussed how to manually define custom blocks in Blockly. Once you've completed the entire codelab, we recommend that you check out our [block factory tool](/guides/create-custom-blocks/blockly-developer-tools), which helps automate part of this process.
@@ -15,13 +15,18 @@ To run the app, simply open `starter-code/index.html` in a browser.
By default, the app launches in "**Play Mode**". In this mode, you can see 9 buttons. None of them can do anything yet. The idea is to let the user define custom behaviors for each button, using Blockly.
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/play_mode.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/play_mode.png){' '}
</CodelabImage>
### Edit mode
By tapping the **EDIT** button, you can switch to edit mode. In this mode, tapping a button will display an editor, which is where you can program how sounds should play for that button. For now, the editor screen is empty.
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/edit_mode_unimplemented.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/edit_mode_unimplemented.png){' '}
</CodelabImage>
You can go back to the play mode by tapping **SAVE** and then **DONE** buttons.
@@ -17,26 +17,29 @@ This generated code will be run by the browser, effectively executing the blocks
Blockly can generate code from blocks for different languages, e.g. JavaScript, Python, or PHP.
A *language generator* defines the rules for generating a specific language (such as indentation). Because we are using the default imports, we don't need to add any new code to get the JavaScript generator.
A _language generator_ defines the rules for generating a specific language (such as indentation). Because we are using the default imports, we don't need to add any new code to get the JavaScript generator.
As previously mentioned, you can define your imports more carefully to get a [different generator](https://www.npmjs.com/package/blockly#blockly-generators).
### Add a block generator
When Blockly generates JavaScript code for blocks in a workspace, it translates each block into code. By default, it knows how to translate all library-provided default blocks into JavaScript code. However, for any custom blocks, we need to specify our own translation functions. These are called *block generators*.
When Blockly generates JavaScript code for blocks in a workspace, it translates each block into code. By default, it knows how to translate all library-provided default blocks into JavaScript code. However, for any custom blocks, we need to specify our own translation functions. These are called _block generators_.
Add the following code to the bottom of `scripts/sound_blocks.js`:
```js
javascript.javascriptGenerator.forBlock['play_sound'] = function(block) {
let value = '\'' + block.getFieldValue('VALUE') + '\'';
javascript.javascriptGenerator.forBlock['play_sound'] = function (block) {
let value = "'" + block.getFieldValue('VALUE') + "'";
return 'MusicMaker.queueSound(' + value + ');\n';
};
```
With this translation function, the following `play_sound` block:
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/play_sound_block.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/play_sound_block.png){' '}
</CodelabImage>
translates into the JavaScript code:
@@ -44,4 +47,4 @@ translates into the JavaScript code:
MusicMaker.queueSound('Sounds/c4.m4a');
```
For more information on block generators, read the [generating code](/guides/create-custom-blocks/code-generation/overview#block-code-generators) page on the developer site.
For more information on block generators, read the [generating code](/guides/create-custom-blocks/code-generation/overview#block-code-generators) page on the developer site.
@@ -22,7 +22,9 @@ Next, you need to generate the code out of that workspace, which you can do with
The user's code will consist of many `MusicMaker.queueSound` calls. At the end of our generated script, add a call to `MusicMaker.play` to play all the sounds added to the queue:
```js
let code = javascript.javascriptGenerator.workspaceToCode(Blockly.getMainWorkspace());
let code = javascript.javascriptGenerator.workspaceToCode(
Blockly.getMainWorkspace(),
);
code += 'MusicMaker.play();';
```
@@ -43,7 +45,9 @@ The end result should look like this:
```js
function handlePlay(event) {
loadWorkspace(event.target);
let code = javascript.javascriptGenerator.workspaceToCode(Blockly.getMainWorkspace());
let code = javascript.javascriptGenerator.workspaceToCode(
Blockly.getMainWorkspace(),
);
code += 'MusicMaker.play();';
try {
eval(code);
@@ -54,12 +58,16 @@ function handlePlay(event) {
```
### A note on eval
Executing scripts with eval is not always the safest option - we use it here for simplicity. If you intend to run the user's blocks in production, check out the <a href="https://github.com/NeilFraser/JS-Interpreter">JS Interpreter project</a>. This project is separate from Blockly, but was specifically written for Blockly.
### Test it
Run the app and try it out! Edit one of the buttons to play a D4 sound 3 times:
<CodelabImage> ![image](../../../static/images/codelabs/getting-started/d4_three_times.png) </CodelabImage>
<CodelabImage>
{' '}
![image](../../../static/images/codelabs/getting-started/d4_three_times.png){' '}
</CodelabImage>
Save and exit the edit mode. Now if you tap this button, you should hear the D4 sound played 3 times.
Save and exit the edit mode. Now if you tap this button, you should hear the D4 sound played 3 times.
@@ -17,7 +17,8 @@ Open `scripts/main.js`. Add the following code to the `save()` method:
```js
button.blocklySave = Blockly.serialization.workspaces.save(
Blockly.getMainWorkspace());
Blockly.getMainWorkspace(),
);
```
`workspaces.save` takes the Blockly workspace, exports its state to a JavaScript object and stores it in a `blocklySave` property on the button. This way the exported state for the block sequence gets associated with a particular button.
@@ -50,4 +51,4 @@ function enableBlocklyMode(e) {
}
```
Now, test the code. Edit the workspace for one of the buttons, add some blocks, save it, and reopen it. The workspace should still contain the blocks you added.
Now, test the code. Edit the workspace for one of the buttons, add some blocks, save it, and reopen it. The workspace should still contain the blocks you added.
@@ -22,10 +22,12 @@ git clone https://github.com/RaspberryPiFoundation/blockly.git
If you downloaded the source as a zip, unpacking it should give you a root folder named `blockly-main`.
The relevant files are in `docs/docs/codelabs/getting-started`. There are two versions of the app:
- `starter-code/`: The starter code that you'll build upon in this codelab.
- `complete-code/`: The code after completing the codelab, in case you get lost or want to compare to your version.
Each folder contains:
- `scripts/`
- `main.js` - The main logic for the app. In the starter project it has all the code needed to navigate and switch between views in the basic app.
- `music_maker.js` - A small library to play sounds. We will use it to actually play sounds in the browser.
@@ -12,4 +12,4 @@ And with that, you're done with the Blockly codelab! If you'd like to continue p
For more documentation, visit the [Blockly developer site](/guides/get-started/what-is-blockly).
Additionally, Blockly has an active [developer forum](https://groups.google.com/g/blockly). Please drop by and say hello. We're happy to answer any questions or give advice on best practices for building an app with Blockly. Feel free to show us your prototypes early; collectively we have a lot of experience and can offer hints which will save you time.
Additionally, Blockly has an active [developer forum](https://groups.google.com/g/blockly). Please drop by and say hello. We're happy to answer any questions or give advice on best practices for building an app with Blockly. Feel free to show us your prototypes early; collectively we have a lot of experience and can offer hints which will save you time.
+64 -33
View File
@@ -5,7 +5,6 @@ pagination_prev: null
slug: /codelabs/index
---
import { CodelabGrid, CodelabCard } from '@site/src/components/CodelabCards';
import Image from '@site/src/components/Image';
@@ -13,51 +12,83 @@ Blockly Codelabs provide a guided, tutorial, hands-on coding experience of Block
<CodelabGrid>
<CodelabCard
level="beginner"
title="Getting started with Blockly"
href="/codelabs/getting-started/codelab-overview">
<Image alt="Blockly icon" src="/images/codelabs/card_thumbnails_getting-started.png" srcDark="/images/codelabs/card_thumbnails_getting-started-dark.png" />
level="beginner"
title="Getting started with Blockly"
href="/codelabs/getting-started/codelab-overview">
<Image
alt="Blockly icon"
src="/images/codelabs/card_thumbnails_getting-started.png"
srcDark="/images/codelabs/card_thumbnails_getting-started-dark.png"
/>
</CodelabCard>
<CodelabCard
level="beginner"
title="Customizing a Blockly toolbox"
href="/codelabs/custom-toolbox/codelab-overview">
<Image alt="Blockly icon" src="/images/codelabs/card_thumbnails_blockly-toolbox.png" srcDark="/images/codelabs/card_thumbnails_blockly-toolbox-dark.png" />
level="beginner"
title="Customizing a Blockly toolbox"
href="/codelabs/custom-toolbox/codelab-overview">
<Image
alt="Blockly icon"
src="/images/codelabs/card_thumbnails_blockly-toolbox.png"
srcDark="/images/codelabs/card_thumbnails_blockly-toolbox-dark.png"
/>
</CodelabCard>
<CodelabCard
level="beginner"
title="Customizing your themes"
href="/codelabs/theme-extension-identifier/codelab-overview">
<Image alt="Blockly icon" src="/images/codelabs/card_thumbnails_themes.png" srcDark="/images/codelabs/card_thumbnails_themes-dark.png" />
level="beginner"
title="Customizing your themes"
href="/codelabs/theme-extension-identifier/codelab-overview">
<Image
alt="Blockly icon"
src="/images/codelabs/card_thumbnails_themes.png"
srcDark="/images/codelabs/card_thumbnails_themes-dark.png"
/>
</CodelabCard>
<CodelabCard
level="Intermediate"
title="Customizing context menus"
href="/codelabs/context-menu-option/codelab-overview">
<Image alt="Blockly icon" src="/images/codelabs/card_thumbnails_context-menus.png" srcDark="/images/codelabs/card_thumbnails_context-menus-dark.png" />
level="Intermediate"
title="Customizing context menus"
href="/codelabs/context-menu-option/codelab-overview">
<Image
alt="Blockly icon"
src="/images/codelabs/card_thumbnails_context-menus.png"
srcDark="/images/codelabs/card_thumbnails_context-menus-dark.png"
/>
</CodelabCard>
<CodelabCard
level="Intermediate"
title="Block validation and warnings"
href="/codelabs/validation-and-warnings/codelab-overview">
<Image alt="Blockly icon" src="/images/codelabs/card_thumbnails_validating-and-displaying.png" srcDark="/images/codelabs/card_thumbnails_validating-and-displaying-dark.png" />
level="Intermediate"
title="Block validation and warnings"
href="/codelabs/validation-and-warnings/codelab-overview">
<Image
alt="Blockly icon"
src="/images/codelabs/card_thumbnails_validating-and-displaying.png"
srcDark="/images/codelabs/card_thumbnails_validating-and-displaying-dark.png"
/>
</CodelabCard>
<CodelabCard
level="Intermediate"
title="Use CSS in Blockly"
href="/codelabs/css/codelab-overview">
<Image alt="Blockly icon" src="/images/codelabs/card_thumbnails_css.png" srcDark="/images/codelabs/card_thumbnails_css-dark.png" />
level="Intermediate"
title="Use CSS in Blockly"
href="/codelabs/css/codelab-overview">
<Image
alt="Blockly icon"
src="/images/codelabs/card_thumbnails_css.png"
srcDark="/images/codelabs/card_thumbnails_css-dark.png"
/>
</CodelabCard>
<CodelabCard
level="Advanced"
title="Build a custom generator"
href="/codelabs/custom-generator/codelab-overview">
<Image alt="Blockly icon" src="/images/codelabs/card_thumbnails_custom-generator.png" srcDark="/images/codelabs/card_thumbnails_custom-generator-dark.png" />
level="Advanced"
title="Build a custom generator"
href="/codelabs/custom-generator/codelab-overview">
<Image
alt="Blockly icon"
src="/images/codelabs/card_thumbnails_custom-generator.png"
srcDark="/images/codelabs/card_thumbnails_custom-generator-dark.png"
/>
</CodelabCard>
<CodelabCard
level="Advanced"
title="Build custom renderers"
href="/codelabs/custom-renderer/codelab-overview">
<Image alt="Blockly icon" src="/images/codelabs/card_thumbnails_custom-renderers.png" srcDark="/images/codelabs/card_thumbnails_custom-renderers-dark.png" />
level="Advanced"
title="Build custom renderers"
href="/codelabs/custom-renderer/codelab-overview">
<Image
alt="Blockly icon"
src="/images/codelabs/card_thumbnails_custom-renderers.png"
srcDark="/images/codelabs/card_thumbnails_custom-renderers-dark.png"
/>
</CodelabCard>
</CodelabGrid>
@@ -4,7 +4,6 @@ slug: /codelabs/theme-extension-identifier/codelab-overview/index.html
description: Overview of the "Customizing your themes" codelab.
---
# Customizing your themes
## 1. Codelab overview
@@ -12,17 +11,19 @@ description: Overview of the "Customizing your themes" codelab.
### What you'll learn
In this codelab you will learn how to:
- Add component styles.
- Add a category style.
- Add a block style.
### What you'll build
A very simple Blockly workspace with customized themes.
### What you'll need
- A browser
- Basic knowledge of HTML, CSS, and JavaScript.
This codelab is focused on Blockly's theme extension. Non-relevant concepts
are glossed over and provided for you to simple copy and paste.
are glossed over and provided for you to simple copy and paste.
@@ -13,60 +13,59 @@ For more information on block styles visit our themes [documentation](/guides/co
Update the Theme definition to have the block styles as below.
```js
Blockly.Themes.Halloween = Blockly.Theme.defineTheme('halloween', {
'base': Blockly.Themes.Classic,
'categoryStyles': {
'list_category': {
'colour': "#4a148c"
base: Blockly.Themes.Classic,
categoryStyles: {
list_category: {
colour: '#4a148c',
},
'logic_category': {
'colour': "#8b4513",
logic_category: {
colour: '#8b4513',
},
'loop_category': {
'colour': "#85E21F",
loop_category: {
colour: '#85E21F',
},
'text_category': {
'colour': "#FE9B13",
text_category: {
colour: '#FE9B13',
},
},
'blockStyles': {
'list_blocks': {
'colourPrimary': "#4a148c",
'colourSecondary':"#AD7BE9",
'colourTertiary':"#CDB6E9",
blockStyles: {
list_blocks: {
colourPrimary: '#4a148c',
colourSecondary: '#AD7BE9',
colourTertiary: '#CDB6E9',
},
'logic_blocks': {
'colourPrimary': "#8b4513",
'colourSecondary':"#ff0000",
'colourTertiary':"#C5EAFF"
logic_blocks: {
colourPrimary: '#8b4513',
colourSecondary: '#ff0000',
colourTertiary: '#C5EAFF',
},
'loop_blocks': {
'colourPrimary': "#85E21F",
'colourSecondary':"#ff0000",
'colourTertiary':"#C5EAFF"
loop_blocks: {
colourPrimary: '#85E21F',
colourSecondary: '#ff0000',
colourTertiary: '#C5EAFF',
},
'text_blocks': {
'colourPrimary': "#FE9B13",
'colourSecondary':"#ff0000",
'colourTertiary':"#C5EAFF"
text_blocks: {
colourPrimary: '#FE9B13',
colourSecondary: '#ff0000',
colourTertiary: '#C5EAFF',
},
},
'componentStyles': {
'workspaceBackgroundColour': '#ff7518',
'toolboxBackgroundColour': '#F9C10E',
'toolboxForegroundColour': '#fff',
'flyoutBackgroundColour': '#252526',
'flyoutForegroundColour': '#ccc',
'flyoutOpacity': 1,
'scrollbarColour': '#ff0000',
'insertionMarkerColour': '#fff',
'insertionMarkerOpacity': 0.3,
'scrollbarOpacity': 0.4,
'cursorColour': '#d0d0d0',
'blackBackground': '#333'
}
componentStyles: {
workspaceBackgroundColour: '#ff7518',
toolboxBackgroundColour: '#F9C10E',
toolboxForegroundColour: '#fff',
flyoutBackgroundColour: '#252526',
flyoutForegroundColour: '#ccc',
flyoutOpacity: 1,
scrollbarColour: '#ff0000',
insertionMarkerColour: '#fff',
insertionMarkerOpacity: 0.3,
scrollbarOpacity: 0.4,
cursorColour: '#d0d0d0',
blackBackground: '#333',
},
});
```
@@ -74,4 +73,4 @@ Blockly.Themes.Halloween = Blockly.Theme.defineTheme('halloween', {
Click on different blocks in the component and you should see the colours that you applied show up.
![Customized Block colors on the components](../../../static/images/codelabs/theme-extension/block_styles.png)
![Customized Block colors on the components](../../../static/images/codelabs/theme-extension/block_styles.png)
@@ -16,37 +16,36 @@ Update the Theme definition to have the category styles as below.
```js
Blockly.Themes.Halloween = Blockly.Theme.defineTheme('halloween', {
'base': Blockly.Themes.Classic,
'categoryStyles': {
'list_category': {
'colour': "#4a148c"
base: Blockly.Themes.Classic,
categoryStyles: {
list_category: {
colour: '#4a148c',
},
'logic_category': {
'colour': "#8b4513",
logic_category: {
colour: '#8b4513',
},
'loop_category': {
'colour': "#85E21F",
loop_category: {
colour: '#85E21F',
},
text_category: {
colour: '#FE9B13',
},
'text_category': {
'colour': "#FE9B13",
}
},
'componentStyles': {
'workspaceBackgroundColour': '#ff7518',
'toolboxBackgroundColour': '#F9C10E',
'toolboxForegroundColour': '#fff',
'flyoutBackgroundColour': '#252526',
'flyoutForegroundColour': '#ccc',
'flyoutOpacity': 1,
'scrollbarColour': '#ff0000',
'insertionMarkerColour': '#fff',
'insertionMarkerOpacity': 0.3,
'scrollbarOpacity': 0.4,
'cursorColour': '#d0d0d0',
'blackBackground': '#333'
}
componentStyles: {
workspaceBackgroundColour: '#ff7518',
toolboxBackgroundColour: '#F9C10E',
toolboxForegroundColour: '#fff',
flyoutBackgroundColour: '#252526',
flyoutForegroundColour: '#ccc',
flyoutOpacity: 1,
scrollbarColour: '#ff0000',
insertionMarkerColour: '#fff',
insertionMarkerOpacity: 0.3,
scrollbarOpacity: 0.4,
cursorColour: '#d0d0d0',
blackBackground: '#333',
},
});
```
### Test it
@@ -11,21 +11,21 @@ Within the Halloween theme definition, you can customize the colours of multiple
```js
Blockly.Themes.Halloween = Blockly.Theme.defineTheme('halloween', {
'base': Blockly.Themes.Classic,
'componentStyles': {
'workspaceBackgroundColour': '#ff7518',
'toolboxBackgroundColour': '#F9C10E',
'toolboxForegroundColour': '#fff',
'flyoutBackgroundColour': '#252526',
'flyoutForegroundColour': '#ccc',
'flyoutOpacity': 1,
'scrollbarColour': '#ff0000',
'insertionMarkerColour': '#fff',
'insertionMarkerOpacity': 0.3,
'scrollbarOpacity': 0.4,
'cursorColour': '#d0d0d0',
'blackBackground': '#333'
}
base: Blockly.Themes.Classic,
componentStyles: {
workspaceBackgroundColour: '#ff7518',
toolboxBackgroundColour: '#F9C10E',
toolboxForegroundColour: '#fff',
flyoutBackgroundColour: '#252526',
flyoutForegroundColour: '#ccc',
flyoutOpacity: 1,
scrollbarColour: '#ff0000',
insertionMarkerColour: '#fff',
insertionMarkerOpacity: 0.3,
scrollbarOpacity: 0.4,
cursorColour: '#d0d0d0',
blackBackground: '#333',
},
});
```
@@ -33,4 +33,4 @@ Blockly.Themes.Halloween = Blockly.Theme.defineTheme('halloween', {
Reload your web page. You should see a themed workspace!
![Fun with colours.](../../../static/images/codelabs/theme-extension/theme_components_workspace.png)
![Fun with colours.](../../../static/images/codelabs/theme-extension/theme_components_workspace.png)
@@ -22,13 +22,15 @@ git clone https://github.com/RaspberryPiFoundation/blockly.git
If you downloaded the source as a zip, unpacking it should give you a root folder named `blockly-main`.
The relevant files are in `docs/docs/codelabs/theme-extension-identifier`. There are two versions of the app:
- `starter-code/`: The starter code that you'll build upon in this codelab.
- `complete-code/`: The code after completing the codelab, in case you get lost or want to compare to your version.
Each folder contains:
- `index.js` - The codelab's logic. To start, it just injects a simple workspace.
- `index.html` - A web page containing a simple blockly workspace.
To run the code, simple open `starter-code/index.html` in a browser. You should see a Blockly workspace with a flyout.
![A web page with the text "Theme Extension Codelab" and a Blockly workspace.](../../../static/images/codelabs/theme-extension/starter_workspace.png)
![A web page with the text "Theme Extension Codelab" and a Blockly workspace.](../../../static/images/codelabs/theme-extension/starter_workspace.png)
@@ -4,7 +4,6 @@ slug: /codelabs/theme-extension-identifier/summary/index.html
description: Summary of the "Customizing your themes" codelab.
---
# Customizing your themes
## 7. Summary
@@ -15,4 +14,4 @@ In this codelab you have learned how to extend and customize themes for the bloc
- [Themes documentation](/guides/configure/web/appearance/themes)
- You can also customize the font styles. Details are available in the [documentation](/guides/configure/web/appearance/themes#font-styles).
- You can also customize the font styles. Details are available in the [documentation](/guides/configure/web/appearance/themes#font-styles).
@@ -16,22 +16,23 @@ Themes are a way to customize the look and feel of Blockly. Currently, we suppor
A theme can be created using the constructor or by using defineTheme. Using `defineTheme` makes it easy to extend a pre existing theme and set all values with a single object.
Add the below code in `index.js` right before function start()
```js
Blockly.Themes.Halloween = Blockly.Theme.defineTheme('halloween', {
'base': Blockly.Themes.Classic,
base: Blockly.Themes.Classic,
});
```
And then add a line to inject this theme in function start() in `index.js`
```js
function start() {
// Create main workspace.
workspace = Blockly.inject('blocklyDiv',
{
toolbox: toolboxCategories,
theme: Blockly.Themes.Halloween,
});
}
// Create main workspace.
workspace = Blockly.inject('blocklyDiv', {
toolbox: toolboxCategories,
theme: Blockly.Themes.Halloween,
});
}
```
:::note
@@ -11,20 +11,27 @@ import CodelabImage from '@site/src/components/CodelabImage';
## 1. Codelab overview
### What you'll learn
This codelab will show you how to ensure that custom blocks either:
1. have everything they need to be able to generate valid code, or
1. display a visual warning to the user that the block is currently invalid.
### What you'll build
In this codelab, you'll create a new custom block type that generates a list of numbers counting up within a given range. The block will validate itself and display a warning if the first number in the range is greater than the last number:
<CodelabImage>![Two instances of a custom block, one with a validation warning.](../../../static/images/codelabs/validation-and-warnings/final_range_blocks.png)</CodelabImage>
<CodelabImage>
![Two instances of a custom block, one with a validation
warning.](../../../static/images/codelabs/validation-and-warnings/final_range_blocks.png)
</CodelabImage>
You can find the code for the [completed custom block](https://github.com/RaspberryPiFoundation/blockly/docs/docs/codelabs/validation-and-warnings/complete-code/index.js) on GitHub.
### What you'll need
- A browser.
- A text editor.
- Basic knowledge of JavaScript.
- Basic understanding of the [Blockly toolbox](/guides/configure/web/toolboxes/toolbox).
- Basic understanding of [using JSON to define custom blocks](/guides/create-custom-blocks/define/structure-json).
- Basic understanding of [using JSON to define custom blocks](/guides/create-custom-blocks/define/structure-json).
@@ -14,16 +14,18 @@ However, there may be invalid conditions that can't be corrected automatically b
In the case of our custom block, we want our extension to be notified whenever either field is updated, so that it can check both of the fields to determine whether the block is currently valid. We can set that up with a general change listener by adding this code inside the extension function after the custom validator:
```js
// Validate the entire block whenever any part of it changes,
// and display a warning if the block cannot be made valid.
this.setOnChange(function(event) {
const first = this.getFieldValue('FIRST');
const last = this.getFieldValue('LAST');
const valid = (first < last);
this.setWarningText(valid
// Validate the entire block whenever any part of it changes,
// and display a warning if the block cannot be made valid.
this.setOnChange(function (event) {
const first = this.getFieldValue('FIRST');
const last = this.getFieldValue('LAST');
const valid = first < last;
this.setWarningText(
valid
? null
: `The first number (${first}) must be smaller than the last number (${last}).`);
});
: `The first number (${first}) must be smaller than the last number (${last}).`,
);
});
```
This change listener function will get called whenever any part of the block is updated. It has access to all of the block's current field values, as well as the block's parents and children, if any. In this case, it reads both field values and compares them to determine whether the block is valid. Then it calls `this.setWarningText(...)`, which can accept either `null` indicating that there is nothing wrong or a string describing the problem.
@@ -39,17 +41,17 @@ Depending on the severity of the issue, this might be sufficient for your custom
You can disable a block using `this.setDisabledReason(true, 'reason')`, although there are some caveats: disabled blocks can't be dragged out of the toolbox flyout, and the act of disabling a block usually adds an event to Blockly's undo history. That's probably not the behavior you want when validating a block, so you can avoid both of these effects with the following code, which you should put inside the change listener function after setting the warning text:
```js
// Disable invalid blocks (unless it's in a toolbox flyout,
// since you can't drag disabled blocks to your workspace).
if (!this.isInFlyout) {
const initialGroup = Blockly.Events.getGroup();
// Make it so the move and the disable event get undone together.
Blockly.Events.setGroup(event.group);
this.setDisabledReason(!valid, 'Invalid range');
Blockly.Events.setGroup(initialGroup);
}
// Disable invalid blocks (unless it's in a toolbox flyout,
// since you can't drag disabled blocks to your workspace).
if (!this.isInFlyout) {
const initialGroup = Blockly.Events.getGroup();
// Make it so the move and the disable event get undone together.
Blockly.Events.setGroup(event.group);
this.setDisabledReason(!valid, 'Invalid range');
Blockly.Events.setGroup(initialGroup);
}
```
Reload `index.html` one last time, drag out the custom block, and edit the fields to make the `FIRST` field greater than the `LAST` field again. This time, the block should be disabled, and it won't generate code even if it is combined with other blocks:
![A Blockly workspace with disabled custom blocks.](../../../static/images/codelabs/validation-and-warnings/disabled_block.png)
![A Blockly workspace with disabled custom blocks.](../../../static/images/codelabs/validation-and-warnings/disabled_block.png)
@@ -10,6 +10,7 @@ import CodelabImage from '@site/src/components/CodelabImage';
## 2. Setup
### Download the sample code
You can get the sample code for this codelab by either downloading the zip here:
[Download zip](https://github.com/RaspberryPiFoundation/blockly/archive/main.zip)
@@ -23,10 +24,12 @@ git clone https://github.com/RaspberryPiFoundation/blockly.git
If you downloaded the source as a zip, unpacking it should give you a root folder named `blockly-main`.
The relevant files are in `docs/docs/codelabs/validation-and-warnings`. There are two versions of the app:
- `starter-code/`: The starter code that you'll build upon in this codelab.
- `complete-code/`: The code after completing the codelab, in case you get lost or want to compare to your version.
Each folder contains:
- `index.js` - The codelab's logic. To start, it just injects a simple workspace.
- `index.html` - A web page containing a simple blockly workspace, an empty space where generated code will be displayed, and a button to execute the generated code.
@@ -35,33 +38,35 @@ Open the file `starter-code/index.html` in a browser to see what it looks like.
![A web page with the text "Validation and Warnings Codelab" and a Blockly workspace.](../../../static/images/codelabs/validation-and-warnings/starter_workspace.png)
Next, open the file `starter-code/index.js` in a text editor. You will be making changes to this file, but first let's take a look at the contents. The code in this file already does a few things:
1. It uses Blockly's toolbox JSON API to define a toolbox containing a few built-in blocks that will be useful for testing our custom block.
1. In a function called `start()`, it initializes a Blockly workspace with the above toolbox, and adds a change event listener that displays the generated JavaScript code whenever a block is moved or updated in the workspace.
1. In a function called `executeCode()`, it executes the generated JavaScript code.
### Define a custom block type
To prepare for adding validation, let's define a new custom block type named `list_range` with two number fields called `FIRST` and `LAST`. Copy the following code to the beginning of `index.js`:
```js
// Use Blockly's custom block JSON API to define a new block type.
Blockly.common.defineBlocksWithJsonArray([
{
'type': 'list_range',
'message0': 'create list of numbers from %1 up to %2',
'args0': [
type: 'list_range',
message0: 'create list of numbers from %1 up to %2',
args0: [
{
'type': 'field_number',
'name': 'FIRST',
'value': 0,
type: 'field_number',
name: 'FIRST',
value: 0,
},
{
'type': 'field_number',
'name': 'LAST',
'value': 5,
type: 'field_number',
name: 'LAST',
value: 5,
},
],
'output': 'Array',
'style': 'list_blocks',
output: 'Array',
style: 'list_blocks',
},
]);
```
@@ -77,14 +82,18 @@ Then, to make this block available from the toolbox, find the toolbox definition
Now, if you reload `index.html` and open the toolbox, you should see the new block at the top:
<CodelabImage>![A Blockly toolbox containing range, for-each, sum, print, and break blocks.](../../../static/images/codelabs/validation-and-warnings/completed_toolbox.png)</CodelabImage>
<CodelabImage>
![A Blockly toolbox containing range, for-each, sum, print, and break
blocks.](../../../static/images/codelabs/validation-and-warnings/completed_toolbox.png)
</CodelabImage>
### Generating JavaScript code for the custom block
You can drag this block out from the toolbox into the workspace, but if you try to use it, you'll find that Blockly doesn't know how to generate JavaScript code from this block yet and error messages will appear in the browser console when it tries to update the display of the generated code. To fix this, add the following code below the custom block definition:
```js
// Define how to generate JavaScript from the custom block.
javascript.javascriptGenerator['list_range'] = function(block) {
javascript.javascriptGenerator['list_range'] = function (block) {
const first = this.getFieldValue('FIRST');
const last = this.getFieldValue('LAST');
const numbers = [];
@@ -100,4 +109,4 @@ Reload `index.html` once again and try adding the new block to the workspace. Yo
![A Blockly workspace with some blocks, the generated code, and the output.](../../../static/images/codelabs/validation-and-warnings/generated_javascript.png)
Now we are ready to start adding validation!
Now we are ready to start adding validation!
@@ -9,21 +9,24 @@ description: Summary of the "Block validation and warnings" codelab.
## 5. Summary
In this codelab, you learned:
* How to use the field validators in Blockly's JSON API.
* How to create an extension that adds functionality to a block.
* How to create custom field validators.
* How to validate the entire block when any part of it changes.
* How to display a warning message on the block.
* How to disable a block (without adding an event to the undo history).
- How to use the field validators in Blockly's JSON API.
- How to create an extension that adds functionality to a block.
- How to create custom field validators.
- How to validate the entire block when any part of it changes.
- How to display a warning message on the block.
- How to disable a block (without adding an event to the undo history).
You can find the code for the [completed custom block](https://github.com/RaspberryPiFoundation/blockly/docs/docs/codelabs/validation-and-warnings/complete-code/index.js) on GitHub.
### Resources
For more information related topics, check out the documentation:
* [Defining the toolbox](/guides/configure/web/toolboxes/toolbox)
* [Defining custom blocks](/guides/create-custom-blocks/overview)
* [Generating code from blocks](/guides/create-custom-blocks/code-generation/overview)
* [Creating extensions](/guides/create-custom-blocks/define/extensions#extensions)
* [Listening for change events](/guides/configure/web/events)
* [Custom validators](/guides/create-custom-blocks/fields/validators)
* [Custom block style guide](/guides/design/blocks)
- [Defining the toolbox](/guides/configure/web/toolboxes/toolbox)
- [Defining custom blocks](/guides/create-custom-blocks/overview)
- [Generating code from blocks](/guides/create-custom-blocks/code-generation/overview)
- [Creating extensions](/guides/create-custom-blocks/define/extensions#extensions)
- [Listening for change events](/guides/configure/web/events)
- [Custom validators](/guides/create-custom-blocks/fields/validators)
- [Custom block style guide](/guides/design/blocks)
@@ -6,9 +6,11 @@ description: How to validate the fields in a block.
# Block validation and warnings
## 3. Validating blocks
When you're designing custom blocks, you may find that it doesn't make sense to use the block in certain ways. Depending on the intended purpose of your block, you may want to add constraints on the possible values that can be assigned to its fields, or on where it is used.
### Basic field constraints
Blockly generally allows users to enter negative values and decimal values for number fields, but for this custom block, let's make sure that only positive whole numbers are allowed. Add `'min': 0` and `'precision': 1` to the fields in the custom block definition so that they look like this:
```js
@@ -35,6 +37,7 @@ Then reload `index.html`, drag the custom block to your workspace, and try enter
If you want, you can also add a `'max'` constraint to a number field.
### Adding custom validation to a field
The built-in constraints are very convenient, but sometimes you might need to add custom constraints. For example, let's say that our custom block needs the first number of the range to be even, and the last number to be odd. We can easily implement the even constraint by setting the `'precision'` constraint of the `FIRST` field to `2`, but the odd constraint requires a custom validator.
So far, we've been using Blockly's JSON API for defining custom blocks, but Blockly also has a JavaScript API with more advanced features, and one of those features is defining custom validators. Fortunately, we don't have to convert our entire custom block definition to the JavaScript API in order to take advantage of these advanced features, because Blockly has a system for adding JavaScript extensions to blocks that were defined with the JSON API.
@@ -57,7 +60,7 @@ Blockly.common.defineBlocksWithJsonArray([
Then copy the following code to `index.js` underneath the custom block definition to register an implementation of that extension:
```js
Blockly.Extensions.register('list_range_validation', function() {
Blockly.Extensions.register('list_range_validation', function () {
// Validation code to be added here...
});
```
@@ -65,27 +68,27 @@ Blockly.Extensions.register('list_range_validation', function() {
The extension's function will be executed every time the custom block is instantiated, and the block instance itself will be implicitly available inside the function using JavaScript's `this` keyword. We can use it to access Blockly's JavaScript API for blocks, including validators. To add a validator on the `LAST` field, put the following code inside the extension function:
```js
this.getField('LAST').setValidator(function(newValue) {
// Validation of newValue for LAST field to be added here...
});
this.getField('LAST').setValidator(function (newValue) {
// Validation of newValue for LAST field to be added here...
});
```
Blockly validator functions are called whenever the user enters a new value for that field, and the new value is passed to the function as a parameter. The parameter might be an invalid value, but validator function can return a valid value to override the user input. To force the `LAST` field to be an odd number, put the following code inside the validator function:
```js
return Math.round((newValue - 1) / 2) * 2 + 1;
return Math.round((newValue - 1) / 2) * 2 + 1;
```
Afterwards, your extension code should look something like this:
```js
Blockly.Extensions.register('list_range_validation', function() {
Blockly.Extensions.register('list_range_validation', function () {
// Add custom validation.
this.getField('LAST').setValidator(function(newValue) {
this.getField('LAST').setValidator(function (newValue) {
// Force an odd number.
return Math.round((newValue - 1) / 2) * 2 + 1;
});
});
```
Reload `index.html` now, then drag the custom block to your workspace and try setting the `LAST` field to various values. Notice how it always turns into an odd number!
Reload `index.html` now, then drag the custom block to your workspace and try setting the `LAST` field to various values. Notice how it always turns into an odd number!
@@ -7,7 +7,10 @@ image: images/blockly_banner.png
import Image from '@site/src/components/Image';
<head>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
</head>
# Attribute Blockly
@@ -19,8 +22,12 @@ product in their website, app, or product. This page describes the best
practices for referring to Blockly and attributing it as well as appropriate
uses of the logo.
<a className="button button-primary downloadAsset"
href="/images/logos/blockly_logos_all.zip" target="_blank">Download all assets</a>
<a
className="button button-primary downloadAsset"
href="/images/logos/blockly_logos_all.zip"
target="_blank">
Download all assets
</a>
## How to refer to Blockly
@@ -29,7 +36,7 @@ uses of the logo.
Upon first reference in a paragraph, use the primary one-line description:
> Blockly is an open-source developer library from the Raspberry Pi Foundation, originally developed at Google.
It creates a visual programming interface that uses drag-and-drop blocks.
> It creates a visual programming interface that uses drag-and-drop blocks.
When referring to Blockly in the context of another app which uses Blockly
you should use the phrase "Built with Blockly".
@@ -39,13 +46,18 @@ press interviews, the app, and on your website.
### Reference do's and don'ts
<p><span className="compare-better">Do</span> — Call Blockly "beginner friendly"
<p>
<span className="compare-better">Do</span> — Call Blockly "beginner friendly"
instead of "kid friendly." Blockly is used for a variety of applications, not
all of which are kid focused.</p>
all of which are kid focused.
</p>
<p><span className="compare-worse">Don't</span> — Refer to Blockly as a language
<p>
<span className="compare-worse">Don't</span> — Refer to Blockly as a language
(for example, as a "block-based programming language"). Blockly is not a
language, it's a library that developers use to make a block-based visual programming interface.</p>
language, it's a library that developers use to make a block-based visual
programming interface.
</p>
## How to visually represent Blockly
@@ -57,27 +69,59 @@ following logos, depending on background color:
<table>
<theader>
<tr>
<th style={{backgroundColor: "white"}}>
<Image src="/images/logos/built-with-blockly-badge-white.png"
alt="Built on Blockly white"
width="240" />
<th style={{ backgroundColor: 'white' }}>
<Image
src="/images/logos/built-with-blockly-badge-white.png"
alt="Built on Blockly white"
width="240"
/>
</th>
<th style={{backgroundColor: "white"}}>
<Image src="/images/logos/built-with-blockly-badge-black.png"
alt="Built on Blockly black" width="250" />
<th style={{ backgroundColor: 'white' }}>
<Image
src="/images/logos/built-with-blockly-badge-black.png"
alt="Built on Blockly black"
width="250"
/>
</th>
</tr>
</theader>
<tbody>
<tr>
<td>For light backgrounds with overlapping shapes or varied patterns, and for solid dark backgrounds (no shapes or patterns) with high contrast.</td>
<td>
For light backgrounds with overlapping shapes or varied patterns, and
for solid dark backgrounds (no shapes or patterns) with high contrast.
</td>
<td>For dark backgrounds with overlapping shapes or varied patterns.</td>
</tr>
<tr>
<td><a className="assetDownloadLink" href="/images/logos/built-with-blockly-badge-white.png" target="_blank">PNG</a>
<a className="assetDownloadLink" href="/images/logos/built-with-blockly-badge-white.svg" target="_blank">SVG</a></td>
<td><a className="assetDownloadLink" href="/images/logos/built-with-blockly-badge-black.png" target="_blank">PNG</a>
<a className="assetDownloadLink" href="/images/logos/built-with-blockly-badge-black.svg" target="_blank">SVG</a></td>
<td>
<a
className="assetDownloadLink"
href="/images/logos/built-with-blockly-badge-white.png"
target="_blank">
PNG
</a>
<a
className="assetDownloadLink"
href="/images/logos/built-with-blockly-badge-white.svg"
target="_blank">
SVG
</a>
</td>
<td>
<a
className="assetDownloadLink"
href="/images/logos/built-with-blockly-badge-black.png"
target="_blank">
PNG
</a>
<a
className="assetDownloadLink"
href="/images/logos/built-with-blockly-badge-black.svg"
target="_blank">
SVG
</a>
</td>
</tr>
</tbody>
</table>
@@ -86,15 +130,28 @@ Please hyperlink the badge to Blockly's home page: [https://www.blockly.com](htt
### Badge sizing and spacing
<div style={{display: "flex", alignItems: "flex-start", gap: "20px"}}>
<div style={{flex: "1 1 50%"}}>
<p>To ensure legibility, badges should not be used with a height smaller than 24px.</p>
<p>For lockups, provide at least x-height and width x1 white space around the
logo. Never crowd or overlap the logo with other elements.</p>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '20px' }}>
<div style={{ flex: '1 1 50%' }}>
<p>
To ensure legibility, badges should not be used with a height smaller than
24px.
</p>
<p>
For lockups, provide at least x-height and width x1 white space around the
logo. Never crowd or overlap the logo with other elements.
</p>
</div>
<div style={{flex: "1 1 50%"}}>
<Image src="/images/logos/blockly-badge.png" className="blockly-badge-light" alt="Blockly Badge" />
<Image src="/images/logos/blockly-badge-dark.png" className="blockly-badge-dark" alt="Blockly Badge" />
<div style={{ flex: '1 1 50%' }}>
<Image
src="/images/logos/blockly-badge.png"
className="blockly-badge-light"
alt="Blockly Badge"
/>
<Image
src="/images/logos/blockly-badge-dark.png"
className="blockly-badge-dark"
alt="Blockly Badge"
/>
</div>
</div>
@@ -109,66 +166,134 @@ element on your page.
<table>
<tr>
<td style={{backgroundColor: "white", verticalAlign: "middle"}}>
<Image src="/images/logos/logo_standard.png"
alt="Standard Blockly logo" />
<td style={{ backgroundColor: 'white', verticalAlign: 'middle' }}>
<Image
src="/images/logos/logo_standard.png"
alt="Standard Blockly logo"
/>
</td>
<td>
<h2>Standard lockup</h2>
<p>The standard lockup can be used in slide decks and blog posts.</p>
<p>Whenever possible, the logo should be represented as a horizontal lockup with a full color logomark and neutral 700 (#5F6368) or solid white logotype.</p>
<a className="assetDownloadLink" href="/images/logos/logo_standard.png" target="_blank">PNG</a>
<a className="assetDownloadLink" href="/images/logos/logo_standard.svg" target="_blank">SVG</a>
<p>
Whenever possible, the logo should be represented as a horizontal lockup
with a full color logomark and neutral 700 (#5F6368) or solid white
logotype.
</p>
<a
className="assetDownloadLink"
href="/images/logos/logo_standard.png"
target="_blank">
PNG
</a>
<a
className="assetDownloadLink"
href="/images/logos/logo_standard.svg"
target="_blank">
SVG
</a>
</td>
</tr>
</table>
<table style={{tableLayout: "fixed"}}>
<table style={{ tableLayout: 'fixed' }}>
<tbody>
<tr>
<th style={{backgroundColor: "white", textAlign: "center"}}>
<Image src="/images/logos/logo_vertical.png"
alt="Blockly vertical logo"
height="120" className="logo-version" />
<th style={{ backgroundColor: 'white', textAlign: 'center' }}>
<Image
src="/images/logos/logo_vertical.png"
alt="Blockly vertical logo"
height="120"
className="logo-version"
/>
</th>
<th style={{backgroundColor: "white", textAlign: "center"}}>
<Image src="/images/logos/logo_only.png"
alt="Blockly logomark"
height="120" className="logo-version" />
<th style={{ backgroundColor: 'white', textAlign: 'center' }}>
<Image
src="/images/logos/logo_only.png"
alt="Blockly logomark"
height="120"
className="logo-version"
/>
</th>
<th style={{backgroundColor: "white", textAlign: "center"}}>
<Image src="/images/logos/logo_knockout_with_bg.png"
alt="Blockly knockout logo"
height="120" className="logo-version" />
<th style={{ backgroundColor: 'white', textAlign: 'center' }}>
<Image
src="/images/logos/logo_knockout_with_bg.png"
alt="Blockly knockout logo"
height="120"
className="logo-version"
/>
</th>
</tr>
<tr>
<td>When there is limited horizontal space, a vertical lockup can be used with the full color logomark and neutral 700 (#5F6368) or solid white logotype.</td>
<td>When there is limited vertical and horizontal space, the logomark can be used by itself without the logotype.</td>
<td>When the color of the logomark has poor contrast with the background, a solid white knockout version can be used.</td>
<td>
When there is limited horizontal space, a vertical lockup can be used
with the full color logomark and neutral 700 (#5F6368) or solid white
logotype.
</td>
<td>
When there is limited vertical and horizontal space, the logomark can be
used by itself without the logotype.
</td>
<td>
When the color of the logomark has poor contrast with the background, a
solid white knockout version can be used.
</td>
</tr>
<tr>
<td><a className="assetDownloadLink" href="/images/logos/logo_vertical.png" target="_blank">PNG</a>
<a className="assetDownloadLink" href="/images/logos/logo_vertical.svg" target="_blank">SVG</a></td>
<td><a className="assetDownloadLink" href="/images/logos/logo_only.png" target="_blank">PNG</a>
<a className="assetDownloadLink" href="/images/logos/logo_only.svg" target="_blank">SVG</a></td>
<td><a className="assetDownloadLink" href="/images/logos/logo_knockout_with_bg.png" target="_blank">PNG</a>
<a className="assetDownloadLink" href="/images/logos/logo_knockout_with_bg.svg" target="_blank">SVG</a></td>
<td>
<a
className="assetDownloadLink"
href="/images/logos/logo_vertical.png"
target="_blank">
PNG
</a>
<a
className="assetDownloadLink"
href="/images/logos/logo_vertical.svg"
target="_blank">
SVG
</a>
</td>
<td>
<a
className="assetDownloadLink"
href="/images/logos/logo_only.png"
target="_blank">
PNG
</a>
<a
className="assetDownloadLink"
href="/images/logos/logo_only.svg"
target="_blank">
SVG
</a>
</td>
<td>
<a
className="assetDownloadLink"
href="/images/logos/logo_knockout_with_bg.png"
target="_blank">
PNG
</a>
<a
className="assetDownloadLink"
href="/images/logos/logo_knockout_with_bg.svg"
target="_blank">
SVG
</a>
</td>
</tr>
</tbody>
</table>
### Logo colours
<table style={{tableLayout: "fixed"}}>
<table style={{ tableLayout: 'fixed' }}>
<tbody>
<tr>
<th style={{backgroundColor: "#4285f4", textAlign: "center"}}>
</th>
<th style={{backgroundColor: "#c8d1db", textAlign: "center"}}>
</th>
<th style={{backgroundColor: "#5f6368", textAlign: "center"}}>
</th>
<th style={{ backgroundColor: '#4285f4', textAlign: 'center' }}></th>
<th style={{ backgroundColor: '#c8d1db', textAlign: 'center' }}></th>
<th style={{ backgroundColor: '#5f6368', textAlign: 'center' }}></th>
</tr>
<tr>
<td>
@@ -187,13 +312,17 @@ element on your page.
</tbody>
</table>
<div style={{display: "flex", alignItems: "flex-start", gap: "20px"}}>
<div style={{flex: "1 1 60%"}}>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '20px' }}>
<div style={{ flex: '1 1 60%' }}>
<h3>Sizing and spacing</h3>
<p>To ensure legibility, the logo should not be used with a height
smaller than 24px.</p>
<p>For lockups, provide at least x-height x2 white space around the
logo. Never crowd or overlap the logo with other elements.</p>
<p>
To ensure legibility, the logo should not be used with a height smaller
than 24px.
</p>
<p>
For lockups, provide at least x-height x2 white space around the logo.
Never crowd or overlap the logo with other elements.
</p>
<h3>Common errors</h3>
<span className="compare-worse">Do not...</span>
<ul>
@@ -203,14 +332,23 @@ element on your page.
<li>Rotate or flip in any direction</li>
<li>Alter proportions, positioning, or placement</li>
<li>Replace the logotype with a different typeface</li>
<li>Use the old Blockly logo (existing uses should be updated when convenient)</li>
<li>Use the logo on a poorly contrasting background (e.g. the knockout logo on a light gray background)</li>
<li>
Use the old Blockly logo (existing uses should be updated when
convenient)
</li>
<li>
Use the logo on a poorly contrasting background (e.g. the knockout logo
on a light gray background)
</li>
<li>Overlap or crowd the logo with other elements</li>
<li>Redraw the logo</li>
</ul>
</div>
<div style={{flex: "1 1 40%"}}>
<Image src="/images/logos/logo_x_height.png"
alt="Height references for padding the logo" width="380" />
<div style={{ flex: '1 1 40%' }}>
<Image
src="/images/logos/logo_x_height.png"
alt="Height references for padding the logo"
width="380"
/>
</div>
</div>
@@ -22,62 +22,46 @@ code generators with any of the following methods.
<Tabs groupId="group-1">
<TabItem value="modules" label="Modules" default>
```js
import {javascriptGenerator} from 'blockly/javascript';
import {pythonGenerator} from 'blockly/python';
import {phpGenerator} from 'blockly/php';
import {luaGenerator} from 'blockly/lua';
import {dartGenerator} from 'blockly/dart';
// Generate code for all the blocks in the workspace.
const jsCode = javascriptGenerator.workspaceToCode(workspace);
const pythonCode = pythonGenerator.workspaceToCode(workspace);
const phpCode = phpGenerator.workspaceToCode(workspace);
const luaCode = luaGenerator.workspaceToCode(workspace);
const dartCode = dartGenerator.workspaceToCode(workspace);
```
```js import {javascriptGenerator} from 'blockly/javascript'; import{' '}
{pythonGenerator} from 'blockly/python'; import {phpGenerator} from
'blockly/php'; import {luaGenerator} from 'blockly/lua'; import{' '}
{dartGenerator} from 'blockly/dart'; // Generate code for all the blocks in
the workspace. const jsCode =
javascriptGenerator.workspaceToCode(workspace); const pythonCode =
pythonGenerator.workspaceToCode(workspace); const phpCode =
phpGenerator.workspaceToCode(workspace); const luaCode =
luaGenerator.workspaceToCode(workspace); const dartCode =
dartGenerator.workspaceToCode(workspace); ```
</TabItem>
<TabItem value="unpkg" label="Unpkg">
You must include the generator after you include Blockly.
```html
<script src="https://unpkg.com/blockly"></script>
<script src="https://unpkg.com/blockly/javascript_compressed"></script>
<script src="https://unpkg.com/blockly/python_compressed"></script>
<script src="https://unpkg.com/blockly/php_compressed"></script>
<script src="https://unpkg.com/blockly/lua_compressed"></script>
<script src="https://unpkg.com/blockly/dart_compressed"></script>
```
```js
// Generate code for all the blocks in the workspace.
const jsCode = javascript.javascriptGenerator.workspaceToCode(workspace);
const pythonCode = python.pythonGenerator.workspaceToCode(workspace);
const phpCode = php.phpGenerator.workspaceToCode(workspace);
const luaCode = lua.luaGenerator.workspaceToCode(workspace);
const dartCode = dart.dartGenerator.workspaceToCode(workspace);
```
You must include the generator after you include Blockly. ```html
<script src="https://unpkg.com/blockly"></script>
<script src="https://unpkg.com/blockly/javascript_compressed"></script>
<script src="https://unpkg.com/blockly/python_compressed"></script>
<script src="https://unpkg.com/blockly/php_compressed"></script>
<script src="https://unpkg.com/blockly/lua_compressed"></script>
<script src="https://unpkg.com/blockly/dart_compressed"></script>
``` ```js // Generate code for all the blocks in the workspace. const jsCode
= javascript.javascriptGenerator.workspaceToCode(workspace); const
pythonCode = python.pythonGenerator.workspaceToCode(workspace); const
phpCode = php.phpGenerator.workspaceToCode(workspace); const luaCode =
lua.luaGenerator.workspaceToCode(workspace); const dartCode =
dart.dartGenerator.workspaceToCode(workspace); ```
</TabItem>
<TabItem value="local-scripts" label="Local scripts">
You must include the generator after you include Blockly.
```html
<script src="blockly_compressed.js"></script>
<script src="javascript_compressed.js"></script>
<script src="python_compressed.js"></script>
<script src="php_compressed.js"></script>
<script src="lua_compressed.js"></script>
<script src="dart_compressed.js"></script>
```
```js
// Generate code for all the blocks in the workspace.
const jsCode = javascript.javascriptGenerator.workspaceToCode(workspace);
const pythonCode = python.pythonGenerator.workspaceToCode(workspace);
const phpCode = php.phpGenerator.workspaceToCode(workspace);
const luaCode = lua.luaGenerator.workspaceToCode(workspace);
const dartCode = dart.dartGenerator.workspaceToCode(workspace);
```
You must include the generator after you include Blockly. ```html
<script src="blockly_compressed.js"></script>
<script src="javascript_compressed.js"></script>
<script src="python_compressed.js"></script>
<script src="php_compressed.js"></script>
<script src="lua_compressed.js"></script>
<script src="dart_compressed.js"></script>
``` ```js // Generate code for all the blocks in the workspace. const jsCode
= javascript.javascriptGenerator.workspaceToCode(workspace); const
pythonCode = python.pythonGenerator.workspaceToCode(workspace); const
phpCode = php.phpGenerator.workspaceToCode(workspace); const luaCode =
lua.luaGenerator.workspaceToCode(workspace); const dartCode =
dart.dartGenerator.workspaceToCode(workspace); ```
</TabItem>
</Tabs>
## Generate code
@@ -121,7 +105,7 @@ start or after the end of the generated code.
```js
let code = javascriptGenerator.workspaceToCode(workspace);
// Add a preamble and a postscript to the code.
const preamble = 'import {MyLibrary} from \'/path/to/my/library\';\n'
const preamble = "import {MyLibrary} from '/path/to/my/library';\n";
const postscript = 'MyLibrary.myKickoffFunction();\n';
code = preamble + code + postscript;
```
@@ -11,7 +11,7 @@ generally to run within a web page (possibly the same, or a embedded WebView).
Like any generator, the first step is to include the JavaScript generator.
```js
import {javascriptGenerator} from 'blockly/javascript';
import { javascriptGenerator } from 'blockly/javascript';
```
To generate JavaScript from the workspace, call:
@@ -73,7 +73,7 @@ highlighted.
## Infinite Loops
Although the resulting code is guaranteed to be syntactically correct at all
times, it may contain infinite loops. Since solving the
times, it may contain infinite loops. Since solving the
[Halting problem](https://en.wikipedia.org/wiki/Halting_problem) is beyond
Blockly's scope (!) the best approach for dealing with these cases is to
maintain a counter and decrement it every time an iteration is performed.
@@ -83,7 +83,8 @@ example:
```js
window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
javascriptGenerator.INFINITE_LOOP_TRAP =
'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);
```
@@ -99,18 +100,18 @@ If you are serious about running the user's blocks properly, then the
[JS-Interpreter project](https://github.com/NeilFraser/JS-Interpreter) is the way to go.
This project is separate from Blockly, but was specifically written for Blockly.
* Execute code at any speed.
* Pause/resume/step-through execution.
* Highlight blocks as they execute.
* Completely isolated from browser's JavaScript.
- Execute code at any speed.
- Pause/resume/step-through execution.
- Highlight blocks as they execute.
- Completely isolated from browser's JavaScript.
### Run the Interpreter
First, download the JS-Interpreter from GitHub:
* [Download zip file](https://github.com/NeilFraser/JS-Interpreter/zipball/master)
* [Download tar file](https://github.com/NeilFraser/JS-Interpreter/tarball/master)
* [View on GitHub](https://github.com/NeilFraser/JS-Interpreter)
- [Download zip file](https://github.com/NeilFraser/JS-Interpreter/zipball/master)
- [Download tar file](https://github.com/NeilFraser/JS-Interpreter/tarball/master)
- [View on GitHub](https://github.com/NeilFraser/JS-Interpreter)
Then add it to your page:
@@ -155,18 +156,24 @@ But to start with, here is the API needed to support the alert and prompt blocks
```js
function initApi(interpreter, globalObject) {
// Add an API function for the alert() block.
var wrapper = function(text) {
var wrapper = function (text) {
return alert(arguments.length ? text : '');
};
interpreter.setProperty(globalObject, 'alert',
interpreter.createNativeFunction(wrapper));
interpreter.setProperty(
globalObject,
'alert',
interpreter.createNativeFunction(wrapper),
);
// Add an API function for the prompt() block.
wrapper = function(text) {
wrapper = function (text) {
return prompt(text);
};
interpreter.setProperty(globalObject, 'prompt',
interpreter.createNativeFunction(wrapper));
interpreter.setProperty(
globalObject,
'prompt',
interpreter.createNativeFunction(wrapper),
);
}
```
@@ -189,11 +196,14 @@ argument, and register it as a native function.
```js
function initApi(interpreter, globalObject) {
// Add an API function for highlighting blocks.
var wrapper = function(id) {
var wrapper = function (id) {
return workspace.highlightBlock(id);
};
interpreter.setProperty(globalObject, 'highlightBlock',
interpreter.createNativeFunction(wrapper));
interpreter.setProperty(
globalObject,
'highlightBlock',
interpreter.createNativeFunction(wrapper),
);
}
```
@@ -24,8 +24,8 @@ The saturation and values can be adapted for each application by calling the
following functions:
```js
Blockly.utils.colour.setHsvSaturation(0.45) // 0 (inclusive) to 1 (exclusive), defaulting to 0.45
Blockly.utils.colour.setHsvValue(0.65) // 0 (inclusive) to 1 (exclusive), defaulting to 0.65
Blockly.utils.colour.setHsvSaturation(0.45); // 0 (inclusive) to 1 (exclusive), defaulting to 0.45
Blockly.utils.colour.setHsvValue(0.65); // 0 (inclusive) to 1 (exclusive), defaulting to 0.65
```
Several colour pickers offer the HSV colour space, such as [HSV
@@ -65,15 +65,15 @@ Blockly includes nine colour constants in the string table, corresponding to the
toolbox categories, plus a distinct colour for dynamic variables:
```js
'%{BKY_LOGIC_HUE}'
'%{BKY_LOOPS_HUE}'
'%{BKY_MATH_HUE}'
'%{BKY_TEXTS_HUE}'
'%{BKY_LISTS_HUE}'
'%{BKY_COLOUR_HUE}'
'%{BKY_VARIABLES_HUE}'
'%{BKY_VARIABLES_DYNAMIC_HUE}'
'%{BKY_PROCEDURES_HUE}'
'%{BKY_LOGIC_HUE}';
'%{BKY_LOOPS_HUE}';
'%{BKY_MATH_HUE}';
'%{BKY_TEXTS_HUE}';
'%{BKY_LISTS_HUE}';
'%{BKY_COLOUR_HUE}';
'%{BKY_VARIABLES_HUE}';
'%{BKY_VARIABLES_DYNAMIC_HUE}';
'%{BKY_PROCEDURES_HUE}';
```
These string values can be used in both the JSON definitions and
@@ -13,9 +13,9 @@ are labeled with CSS classes that identify what they represent (e.g.
You can use CSS to style your application:
* Override Blockly's CSS rules with your own rules.
* Add your own CSS classes to Blockly components for additional specificity.
* Use CSS classes and rules to style custom components.
- Override Blockly's CSS rules with your own rules.
- Add your own CSS classes to Blockly components for additional specificity.
- Use CSS classes and rules to style custom components.
For a practical introduction to using CSS in blockly, see the [Use CSS in Blockly](/codelabs/css/codelab-overview/index.html)
@@ -33,22 +33,22 @@ provides finer-grained control than type (element) selectors.
Blockly uses CSS classes to provide the following kinds of information about the
HTML and SVG elements it uses.
* **Type.** Most Blockly CSS classes identify what an element represents. For
example, the root element of a block is labeled `blocklyBlock`. Some
elements are labeled with multiple classes, each more specific than the
last. For example, the root element of a text input field is labeled
`blocklyField`, `blocklyInputField`, and `blocklyTextInputField`. Type
classes remain the same for the life of a component.
- **Type.** Most Blockly CSS classes identify what an element represents. For
example, the root element of a block is labeled `blocklyBlock`. Some
elements are labeled with multiple classes, each more specific than the
last. For example, the root element of a text input field is labeled
`blocklyField`, `blocklyInputField`, and `blocklyTextInputField`. Type
classes remain the same for the life of a component.
* **State.** Blockly also uses CSS classes to specify the state of a
component. For example, when the cursor is on a text input field, its root
element is labeled with the `blocklyEditing` class. When the cursor is moved
away, this class is removed.
- **State.** Blockly also uses CSS classes to specify the state of a
component. For example, when the cursor is on a text input field, its root
element is labeled with the `blocklyEditing` class. When the cursor is moved
away, this class is removed.
* **Additional information.** Blockly uses a few CSS classes to provide
additional information. For example, the injection `<div>` has classes that
provide the name of the workspace's current renderer and theme. These
classes generally remain the same for the life of the application.
- **Additional information.** Blockly uses a few CSS classes to provide
additional information. For example, the injection `<div>` has classes that
provide the name of the workspace's current renderer and theme. These
classes generally remain the same for the life of the application.
The easiest way to discover what CSS classes Blockly uses is to open your
browser's developer tools and inspect the elements used by your application.
@@ -80,20 +80,22 @@ To add CSS classes to a custom block, pass a string or array of strings to the
`classes` key.
```js
Blockly.common.defineBlocksWithJsonArray([{
"type": "string_length",
"message0": 'length of %1',
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": "String",
}
],
"classes": "myStringLengthBlock",
"output": "Number",
"colour": 160,
}]);
Blockly.common.defineBlocksWithJsonArray([
{
type: 'string_length',
message0: 'length of %1',
args0: [
{
type: 'input_value',
name: 'VALUE',
check: 'String',
},
],
classes: 'myStringLengthBlock',
output: 'Number',
colour: 160,
},
]);
```
You can also add or remove a CSS class from a block's `<g>` element by calling
@@ -110,42 +112,44 @@ name to the label's constructor.
When constructing a custom component, use one of the following methods to add
custom CSS classes:
* If your component is a subclass of `Field` or `Icon`, override the
`initView` method. For example:
- If your component is a subclass of `Field` or `Icon`, override the
`initView` method. For example:
```js
class MyCustomField extends Blockly.FieldTextInput {
...
```js
class MyCustomField extends Blockly.FieldTextInput {
...
initView() {
super.initView();
initView() {
super.initView();
// Add custom CSS class so we can style the field.
if (this.fieldGroup_) {
Blockly.utils.dom.addClass(this.fieldGroup_, 'myCustomField');
}
// Add custom CSS class so we can style the field.
if (this.fieldGroup_) {
Blockly.utils.dom.addClass(this.fieldGroup_, 'myCustomField');
}
}
```
}
```
For more information, see [Customizing fields with
CSS](/guides/create-custom-blocks/fields/customizing-fields/creating#customizing-with-css)
or [Create the icon's
view](/guides/create-custom-blocks/icons/creating-custom-icons/basic-implementation#create-the-icons-view).
For more information, see [Customizing fields with
CSS](/guides/create-custom-blocks/fields/customizing-fields/creating#customizing-with-css)
or [Create the icon's
view](/guides/create-custom-blocks/icons/creating-custom-icons/basic-implementation#create-the-icons-view).
* When constructing an SVG element, pass your class to
`Blockly.utils.dom.createSvgElement`:
- When constructing an SVG element, pass your class to
`Blockly.utils.dom.createSvgElement`:
```js
this.svgRoot = Blockly.utils.dom.createSvgElement(Svg.G, {'class': 'myCustomComponent'});
```
```js
this.svgRoot = Blockly.utils.dom.createSvgElement(Svg.G, {
class: 'myCustomComponent',
});
```
* When constructing an HTML element, use `Blockly.utils.dom.addClass`:
- When constructing an HTML element, use `Blockly.utils.dom.addClass`:
```js
const myDiv = document.createElement('div');
Blockly.utils.dom.addClass(myDiv, 'myInformation');
```
```js
const myDiv = document.createElement('div');
Blockly.utils.dom.addClass(myDiv, 'myInformation');
```
To add or remove classes after construction, use `Blockly.utils.dom.addClass` or
`Blockly.utils.dom.removeClass`.
@@ -193,7 +197,9 @@ attributes**) or in CSS rules. Thus, all of the following do the same thing.
```css
/* External CSS file.*/
circle {fill:red;}
circle {
fill: red;
}
```
```svg
@@ -204,14 +210,14 @@ circle {fill:red;}
The list of SVG styling properties is related to but different from the list of
CSS properties:
* **Same concept, same name.** For example, both SVG and CSS use `direction`
to specify whether text is LTR or RTL.
* **Same concept, different name.** For example, SVG uses `fill` to specify
fill color; CSS uses `background-color`.
* **CSS only.** CSS has many properties that are not in SVG, such as `margin`
and `padding`.
* **SVG only.** SVG has a few properties that are not in CSS, such as `x` and
`y`.
- **Same concept, same name.** For example, both SVG and CSS use `direction`
to specify whether text is LTR or RTL.
- **Same concept, different name.** For example, SVG uses `fill` to specify
fill color; CSS uses `background-color`.
- **CSS only.** CSS has many properties that are not in SVG, such as `margin`
and `padding`.
- **SVG only.** SVG has a few properties that are not in CSS, such as `x` and
`y`.
Thus, if you're styling an SVG element, use [SVG styling
properties](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute#presentation_attributes).
@@ -237,25 +243,24 @@ these steps and stop when only one rule remains:
have an `!important` annotation.
1. Choose the rules with the highest
[specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascade/Specificity).
* [SVG presentation attributes](#svg-style) have a specificity of zero.
* Rules in a `<style>` tag or external stylesheet have their specificity
calculated normally.
* Inline styles (styles set by a `style` attribute) have a specificity
higher than any selector.
- [SVG presentation attributes](#svg-style) have a specificity of zero.
- Rules in a `<style>` tag or external stylesheet have their specificity
calculated normally.
- Inline styles (styles set by a `style` attribute) have a specificity
higher than any selector.
1. Choose the rule that appears last in the document.
1. If no rule applies, inherit the property's value from the element's parent.
This algorithm does not consider the following parts of the cascade:
* The [`transition`](https://developer.mozilla.org/en-US/docs/Web/CSS/transition)
property, which has the highest priority. Blockly uses a few of these.
* The [`@media`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media)
at-rule. Blockly uses one of these.
* Rules specified by the browser or user.
* The `@scope` and `@layer` at-rules and the `animation` property, which are
not used by Blockly.
- The [`transition`](https://developer.mozilla.org/en-US/docs/Web/CSS/transition)
property, which has the highest priority. Blockly uses a few of these.
- The [`@media`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media)
at-rule. Blockly uses one of these.
- Rules specified by the browser or user.
- The `@scope` and `@layer` at-rules and the `animation` property, which are
not used by Blockly.
## CSS rules
@@ -278,15 +283,15 @@ than rules with the same specificity that occur later in the page.
When Blockly is injected, it adds a `<style>` tag as a child of the `<head>`
tag. The rules in this tag come from:
* The `Blockly.css` namespace. To see these rules, open
[core/css.ts](https://github.com/RaspberryPiFoundation/blockly/blob/main/core/css.ts) and
search for `let content`.
* Individual components, which call `Blockly.css.register` to add
component-specific CSS rules. Because `css.register` adds these rules to the
end of the `content` string, they have higher priority than rules with the
same specificity that were added earlier. To see these rules, see the [calls
to
`Blockly.css.register`](https://github.com/search?q=repo%3ARaspberryPiFoundation%2Fblockly+css.register+path%3Acore&type=code).
- The `Blockly.css` namespace. To see these rules, open
[core/css.ts](https://github.com/RaspberryPiFoundation/blockly/blob/main/core/css.ts) and
search for `let content`.
- Individual components, which call `Blockly.css.register` to add
component-specific CSS rules. Because `css.register` adds these rules to the
end of the `content` string, they have higher priority than rules with the
same specificity that were added earlier. To see these rules, see the [calls
to
`Blockly.css.register`](https://github.com/search?q=repo%3ARaspberryPiFoundation%2Fblockly+css.register+path%3Acore&type=code).
If you do not want to use these rules, set the [`css` configuration
option](/guides/configure/web/configuration_struct#the-options-dictionary)
@@ -323,41 +328,41 @@ rules. Blockly generally creates them in [calls to
You can add your own CSS rules using the same methods as Blockly:
* Call `Blockly.css.register` before injecting Blockly. Your rules will be
added after Blockly's and have a higher priority than Blockly rules with the
same specificity.
* Add a `<style>` tag or link to an external style sheet as a later child of
the `<head>` tag. Because Blockly adds its rules as the first two children
of the `<head>` tag, your rules will have a higher priority than Blockly
rules with the same specificity.
* Use inline styles to add styles to elements in a custom component. These
rules will have a higher specificity than any rules with a selector.
* Use presentation attributes on SVG elements in a custom component. These
rules will have a lower specificity than any rules with a selector.
- Call `Blockly.css.register` before injecting Blockly. Your rules will be
added after Blockly's and have a higher priority than Blockly rules with the
same specificity.
- Add a `<style>` tag or link to an external style sheet as a later child of
the `<head>` tag. Because Blockly adds its rules as the first two children
of the `<head>` tag, your rules will have a higher priority than Blockly
rules with the same specificity.
- Use inline styles to add styles to elements in a custom component. These
rules will have a higher specificity than any rules with a selector.
- Use presentation attributes on SVG elements in a custom component. These
rules will have a lower specificity than any rules with a selector.
### Troubleshooting
If your CSS is not working, here are some possible causes:
* You are using CSS properties on an SVG element or SVG styling properties on
an HTML element. See [SVG styling properties versus CSS
properties](#svg-style).
- You are using CSS properties on an SVG element or SVG styling properties on
an HTML element. See [SVG styling properties versus CSS
properties](#svg-style).
* Your rule has a lower priority than another rule. This is usually due to a
lower [specificity](#simplified). Possible ways to fix this are:
* Use a class selector instead of a type (element) selector.
* Use multiple selectors.
* If possible, [add a custom class](#custom-css-classes) to your target
element and use this class in your rule.
* As a last resort, add an `!important` annotation to your rule. This is
your only choice if a competing rule is specified using an inline style
(`style` attribute).
- Your rule has a lower priority than another rule. This is usually due to a
lower [specificity](#simplified). Possible ways to fix this are:
- Use a class selector instead of a type (element) selector.
- Use multiple selectors.
- If possible, [add a custom class](#custom-css-classes) to your target
element and use this class in your rule.
- As a last resort, add an `!important` annotation to your rule. This is
your only choice if a competing rule is specified using an inline style
(`style` attribute).
* Your rule has the same specificity as another rule, but occurs earlier in
the page. If you cannot increase the specificity of your rule, move it later
in the page.
- Your rule has the same specificity as another rule, but occurs earlier in
the page. If you cannot increase the specificity of your rule, move it later
in the page.
There are two types of CSS rules you cannot override:
* Properties that are set inside a `transition` rule.
* `!important` rules specified by the browser.
- Properties that are set inside a `transition` rule.
- `!important` rules specified by the browser.
@@ -47,91 +47,100 @@ properties can be defined in either hue or hex values.
A block style is made up of four fields:
- `colourPrimary` (required) - The background colour of the block.
- `colourSecondary` (optional) - The background colour if the block is a
shadow block.
- `colourTertiary` (optional) - The border or highlight colour of the block.
- `hat` (optional) - Adds a hat to a block, if the value is set to `cap`.
Learn more about
[hats](/guides/design/applications#event-driven-program).
- `colourPrimary` (required) - The background colour of the block.
- `colourSecondary` (optional) - The background colour if the block is a
shadow block.
- `colourTertiary` (optional) - The border or highlight colour of the block.
- `hat` (optional) - Adds a hat to a block, if the value is set to `cap`.
Learn more about
[hats](/guides/design/applications#event-driven-program).
<Image alt="Block with arrows pointing to primary, secondary and tertiary colour" src="/images/ColourExplanation.png" srcDark="/images/ColourExplanation-dark.png" width={450} />
<Image
alt="Block with arrows pointing to primary, secondary and tertiary colour"
src="/images/ColourExplanation.png"
srcDark="/images/ColourExplanation-dark.png"
width={450}
/>
```js
const listBlocks = {
'colourPrimary': '#4a148c',
'colourSecondary':'#AD7BE9',
'colourTertiary':'#CDB6E9',
'hat': 'cap'
}
colourPrimary: '#4a148c',
colourSecondary: '#AD7BE9',
colourTertiary: '#CDB6E9',
hat: 'cap',
};
```
A theme will include a mapping of block style name to block style object:
```js
const blockStyles = {
'list_blocks': {
'colourPrimary': '#4a148c',
'colourSecondary':'#AD7BE9',
'colourTertiary':'#CDB6E9'
},
'logic_blocks': {
'colourPrimary': '#01579b',
'colourSecondary':'#64C7FF',
'colourTertiary':'#C5EAFF'
}
}
list_blocks: {
colourPrimary: '#4a148c',
colourSecondary: '#AD7BE9',
colourTertiary: '#CDB6E9',
},
logic_blocks: {
colourPrimary: '#01579b',
colourSecondary: '#64C7FF',
colourTertiary: '#C5EAFF',
},
};
```
### Category Style
A category style only holds a colour property.
- Colour (required) - The colour of the category in the toolbox. Usually these
colours should be the same as the `colourPrimary` on the majority of blocks
in the category. This makes it easy for users to tell which blocks belong in
a given category.
- Colour (required) - The colour of the category in the toolbox. Usually these
colours should be the same as the `colourPrimary` on the majority of blocks
in the category. This makes it easy for users to tell which blocks belong in
a given category.
<Image alt="Screenshot of toolbox with different category colours" src="/images/CategoryColours.png" srcDark="/images/CategoryColours-dark.png" />
<Image
alt="Screenshot of toolbox with different category colours"
src="/images/CategoryColours.png"
srcDark="/images/CategoryColours-dark.png"
/>
```js
const mathCategory = {
'colour':'290'
}
colour: '290',
};
```
A theme will include a mapping of category name to category style object:
```js
const categoryStyles = {
'list_category': {
'colour': '#4a148c'
},
'logic_category': {
'colour': '#01579b',
}
}
list_category: {
colour: '#4a148c',
},
logic_category: {
colour: '#01579b',
},
};
```
### Component Styles
A theme can set the colour or value of the below components:
* `workspaceBackgroundColour`: The workspace background colour
* `toolboxBackgroundColour`: Toolbox background colour
* `toolboxForegroundColour`: Toolbox category text colour
* `flyoutBackgroundColour`: Flyout background colour
* `flyoutForegroundColour`: Flyout label text colour
* `flyoutOpacity`: Flyout opacity
* `scrollbarColour`: Scrollbar colour
* `scrollbarOpacity`: Scrollbar opacity
* `insertionMarkerColour`: The insertion marker colour (Does not accept colour
names)
* `insertionMarkerOpacity`: The insertion marker opacity
* `markerColour`: The colour of the marker displayed in keyboard navigation
mode
* `cursorColour`: The colour of the cursor displayed in keyboard navigation
mode
- `workspaceBackgroundColour`: The workspace background colour
- `toolboxBackgroundColour`: Toolbox background colour
- `toolboxForegroundColour`: Toolbox category text colour
- `flyoutBackgroundColour`: Flyout background colour
- `flyoutForegroundColour`: Flyout label text colour
- `flyoutOpacity`: Flyout opacity
- `scrollbarColour`: Scrollbar colour
- `scrollbarOpacity`: Scrollbar opacity
- `insertionMarkerColour`: The insertion marker colour (Does not accept colour
names)
- `insertionMarkerOpacity`: The insertion marker opacity
- `markerColour`: The colour of the marker displayed in keyboard navigation
mode
- `cursorColour`: The colour of the cursor displayed in keyboard navigation
mode
Most other components can be changed by using the theme name in your CSS.
However, if there is a component you would like to change that is not already a
@@ -141,9 +150,9 @@ information.
```js
const componentStyle = {
'workspaceBackgroundColour': '#1e1e1e',
'toolboxBackgroundColour': '#333'
}
workspaceBackgroundColour: '#1e1e1e',
toolboxBackgroundColour: '#333',
};
```
### Font Styles
@@ -152,10 +161,10 @@ A font style is an object that holds the family, weight and size of a font.
```js
const fontStyle = {
'family': 'Georgia, serif',
'weight': 'bold',
'size': 12
}
family: 'Georgia, serif',
weight: 'bold',
size: 12,
};
```
### Start Hats
@@ -242,14 +251,10 @@ your block definition:
<Tabs groupId="javascript-json">
<TabItem value="json" label="JSON" default>
```js
"style":"logic_blocks"
```
```js "style":"logic_blocks" ```
</TabItem>
<TabItem value="javascript" label="JavaScript">
```js
this.setStyle('logic_blocks');
```
```js this.setStyle('logic_blocks'); ```
</TabItem>
</Tabs>
### Set your Theme
@@ -326,12 +331,12 @@ folder.
Blockly provides a number of themes for accessibility, specifically certain
types of colour vision deficiency:
- [High contrast](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-highcontrast/test/)
- [Deuteranopia/Protanopia](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-deuteranopia/test/)
- [Tritanopia](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-tritanopia/test/)
- [High contrast](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-highcontrast/test/)
- [Deuteranopia/Protanopia](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-deuteranopia/test/)
- [Tritanopia](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-tritanopia/test/)
We also have:
- Classic theme
- [Modern](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-modern/test/)
- [Dark mode](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-dark/test/)
- Classic theme
- [Modern](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-modern/test/)
- [Dark mode](https://raspberrypifoundation.github.io/blockly-samples/plugins/theme-dark/test/)
@@ -20,24 +20,24 @@ Users enter block comments with the comment icon's text editor.
By default, the comment icon is not displayed. There are two ways to display it:
* Call `Block.setCommentText` with a non-null string.
* Let the user display it by clicking "Add Comment" in the block's context
menu.
- Call `Block.setCommentText` with a non-null string.
- Let the user display it by clicking "Add Comment" in the block's context
menu.
To remove the comment icon:
* Call `Block.setCommentText` with a `null`.
* Let the user remove it by clicking "Remove Comment" in the block's context
menu.
- Call `Block.setCommentText` with a `null`.
- Let the user remove it by clicking "Remove Comment" in the block's context
menu.
Note that "Add Comment" and "Remove Comment" are displayed in the context menu
only if:
* The block is [editable].
* The block is not collapsed.
* The [`comments` configuration option][config-option] is set to `true`. If
you do not explicitly set this option, it defaults to `true` if the toolbox
has categories, `false` otherwise.
- The block is [editable].
- The block is not collapsed.
- The [`comments` configuration option][config-option] is set to `true`. If
you do not explicitly set this option, it defaults to `true` if the toolbox
has categories, `false` otherwise.
## Work with block comments programmatically
@@ -40,32 +40,32 @@ The configuration object implements
and has the following options. Note that several of these options change their
default value based on whether the provided toolbox has categories or not.
| Name | Type | Description |
|-----------------------|-----------|-------------------------------------------------------------|
| `collapse` | boolean | Whether block context menus include an item to collapse or expand blocks. Defaults to `true` if the toolbox has categories, `false` otherwise.
| `comments` | boolean | Whether block context menus include an item to add or remove comments. Defaults to `true` if the toolbox has categories,`false` otherwise.
| `css` | boolean | If false, don't inject CSS (providing CSS becomes the document's responsibility). Defaults to `true`.
| `disable` | boolean | Whether block context menus include an item to disable or enable blocks. Defaults to `true` if the toolbox has categories, `false` otherwise.
| `grid` | object | Configures a grid which blocks may snap to. See [Grid]. |
| `horizontalLayout` | boolean | If `true` toolbox is horizontal, if `false` toolbox is vertical. Defaults to `false`.
| `maxBlocks` | number | Maximum number of blocks that may be created. Useful for student exercises. Defaults to `Infinity`.
| `maxInstances` | object | Map from block types to maximum number of blocks of that type that may be created. Undeclared types default to `Infinity`.Example: `maxInstances\: {'controls_if'\: 3, 'math_number'\: 42}`
| `maxTrashcanContents` | number | Maximum number of deleted items that will appear in the trashcan flyout. `'0'` disables the feature. Defaults to `'32'`.
| `media` | string | Path from page (or frame) to the Blockly media directory. Defaults to `'https\://blockly-demo.appspot.com/static/media/'`. See [Media folder][media].
| `modalInputs` | boolean | If `true` show modal editors for text input fields and their subclasses when on mobile devices, and an inline editor on desktop. If `false` show an inline editor on both desktop and mobile. Defaults to `true`.
| `move` | object | Configures behavior for how users can move around the workspace. See [Move].
| `oneBasedIndex` | boolean | If `true` list and string operations should index from `1`, if `false` index from `0`. Defaults to `true`.
| `plugins` | object | An object mapping registry type names to replacement classes or the registered names of replacement classes. See [Inject your replacement class][replacement].
| `readOnly` | boolean | If `true`, prevent the user from editing. Suppresses the toolbox and trashcan. Defaults to `false`. See also [`setIsReadOnly`][setIsReadOnly] and [`isReadOnly`][isReadOnly].
| `renderer` | string | Determines the renderer used by blockly. Pre-packaged renderers include `'geras'` (the default), `'thrasos'`, and `'zelos'` (a Scratch-like renderer). For information about custom renderers, see [Create custom renderers][renderer].
| `rtl` | boolean | If `true`, mirror the editor (for Arabic or Hebrew locales).See [RTL demo]. Defaults to `false`.
| `scrollbars` | object or boolean | Sets whether the workspace has vertical or horizontal scrollbars. Takes an object where the `horizontal` property determines if horizontal scrolling is enabled and the `vertical` property determines if vertical scrolling is enabled. If a boolean is passed then it is equivalent to passing an object with both `horizontal` and `vertical` properties set as that value. Defaults to `true` if the toolbox has categories.
| `sounds` | boolean | If `false`, disables sounds. Defaults to `true`. |
| `theme` | Theme | Defaults to classic theme if no theme is provided. See [Themes].
| `toolbox` | string, XML or JSON | Tree structure of categories and blocks available to the user. See [defining the toolbox] for more information.
| `toolboxPosition` | string | If `'start'` toolbox is on top (if horizontal) or left (if vertical and LTR) or right (if vertical and RTL). If `'end'` toolbox is on opposite side. Defaults to `'start'`.
| `trashcan` | boolean | Displays or hides the trashcan. Defaults to `true` if the toolbox has categories, `false` otherwise.
| `zoom` | object | Configures zooming behaviour. See [Zoom]. |
| Name | Type | Description |
| --------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `collapse` | boolean | Whether block context menus include an item to collapse or expand blocks. Defaults to `true` if the toolbox has categories, `false` otherwise. |
| `comments` | boolean | Whether block context menus include an item to add or remove comments. Defaults to `true` if the toolbox has categories,`false` otherwise. |
| `css` | boolean | If false, don't inject CSS (providing CSS becomes the document's responsibility). Defaults to `true`. |
| `disable` | boolean | Whether block context menus include an item to disable or enable blocks. Defaults to `true` if the toolbox has categories, `false` otherwise. |
| `grid` | object | Configures a grid which blocks may snap to. See [Grid]. |
| `horizontalLayout` | boolean | If `true` toolbox is horizontal, if `false` toolbox is vertical. Defaults to `false`. |
| `maxBlocks` | number | Maximum number of blocks that may be created. Useful for student exercises. Defaults to `Infinity`. |
| `maxInstances` | object | Map from block types to maximum number of blocks of that type that may be created. Undeclared types default to `Infinity`.Example: `maxInstances\: {'controls_if'\: 3, 'math_number'\: 42}` |
| `maxTrashcanContents` | number | Maximum number of deleted items that will appear in the trashcan flyout. `'0'` disables the feature. Defaults to `'32'`. |
| `media` | string | Path from page (or frame) to the Blockly media directory. Defaults to `'https\://blockly-demo.appspot.com/static/media/'`. See [Media folder][media]. |
| `modalInputs` | boolean | If `true` show modal editors for text input fields and their subclasses when on mobile devices, and an inline editor on desktop. If `false` show an inline editor on both desktop and mobile. Defaults to `true`. |
| `move` | object | Configures behavior for how users can move around the workspace. See [Move]. |
| `oneBasedIndex` | boolean | If `true` list and string operations should index from `1`, if `false` index from `0`. Defaults to `true`. |
| `plugins` | object | An object mapping registry type names to replacement classes or the registered names of replacement classes. See [Inject your replacement class][replacement]. |
| `readOnly` | boolean | If `true`, prevent the user from editing. Suppresses the toolbox and trashcan. Defaults to `false`. See also [`setIsReadOnly`][setIsReadOnly] and [`isReadOnly`][isReadOnly]. |
| `renderer` | string | Determines the renderer used by blockly. Pre-packaged renderers include `'geras'` (the default), `'thrasos'`, and `'zelos'` (a Scratch-like renderer). For information about custom renderers, see [Create custom renderers][renderer]. |
| `rtl` | boolean | If `true`, mirror the editor (for Arabic or Hebrew locales).See [RTL demo]. Defaults to `false`. |
| `scrollbars` | object or boolean | Sets whether the workspace has vertical or horizontal scrollbars. Takes an object where the `horizontal` property determines if horizontal scrolling is enabled and the `vertical` property determines if vertical scrolling is enabled. If a boolean is passed then it is equivalent to passing an object with both `horizontal` and `vertical` properties set as that value. Defaults to `true` if the toolbox has categories. |
| `sounds` | boolean | If `false`, disables sounds. Defaults to `true`. |
| `theme` | Theme | Defaults to classic theme if no theme is provided. See [Themes]. |
| `toolbox` | string, XML or JSON | Tree structure of categories and blocks available to the user. See [defining the toolbox] for more information. |
| `toolboxPosition` | string | If `'start'` toolbox is on top (if horizontal) or left (if vertical and LTR) or right (if vertical and RTL). If `'end'` toolbox is on opposite side. Defaults to `'start'`. |
| `trashcan` | boolean | Displays or hides the trashcan. Defaults to `true` if the toolbox has categories, `false` otherwise. |
| `zoom` | object | Configures zooming behaviour. See [Zoom]. |
[Grid]: /guides/configure/web/grid
[media]: /guides/configure/web/media
@@ -15,7 +15,11 @@ response to a right-click or a long press on a touch device. If you use the
shown with a keyboard shortcut, which defaults to `Ctrl+Enter` on Windows or
`Command+Enter` on Mac.
<Image alt="The default context menu for a block" src="/images/context-menus/block-default-menu.png" width={200} />
<Image
alt="The default context menu for a block"
src="/images/context-menus/block-default-menu.png"
width={200}
/>
Context menus are a good place to add actions that the user performs
infrequently, such as downloading a screenshot. If you think an action will be
@@ -95,9 +99,9 @@ if (scope.focusedNode instanceof Blockly.BlockSvg) {
The `Scope` object has other optional properties that are no longer recommended
for use, but may still be set:
- `block` is set only if the component whose menu is shown is a `BlockSvg`.
- `workspace` is set only if the component is a `WorkspaceSvg`.
- `comment` is set only if the component is a `RenderedWorkspaceComment`.
- `block` is set only if the component whose menu is shown is a `BlockSvg`.
- `workspace` is set only if the component is a `WorkspaceSvg`.
- `comment` is set only if the component is a `RenderedWorkspaceComment`.
These properties don't cover all types of components that may have a context
menu, so you should prefer to use the `focusedNode` property.
@@ -129,11 +133,11 @@ should be displayed.
It should return one of a set of strings: `'enabled'`, `'disabled'`, or
`'hidden'`.
| Value | Description | Image |
| ------------ | ---------------------------------- | --------------------------------------------------- |
| Value | Description | Image |
| ------------ | ---------------------------------- | --------------------------------------------------------------------------------------------- |
| `'enabled'` | Shows that the item is active. | <Image alt="An enabled option" src="/images/context-menus/enabled-option.png" width={150} /> |
| `'disabled'` | Shows that the item is not active. | <Image alt="A disabled option" src="/images/context-menus/disabled-option.png" width={150} /> |
| `'hidden'` | Hides the item. | |
| `'hidden'` | Hides the item. | |
The `preconditionFn` is also passed a [`Scope`][Scope] which you can use to
determine what type of component the menu was opened on and the state of that
@@ -159,7 +163,7 @@ const collapseTemplate = {
return 'hidden';
},
// ...
}
};
```
### Display text
@@ -202,7 +206,7 @@ const collapseTemplate = {
return '';
},
// ...
}
};
```
### Weight
@@ -217,7 +221,7 @@ const collapseTemplate = {
// ...
weight: 10,
// ...
}
};
```
Weights for the built-in context menu items go in increasing order starting at 1
@@ -228,17 +232,17 @@ and increasing by 1.
The `callback` property is a function that performs the action of your context
menu item. It is passed several parameters:
- `scope`: A [`Scope`][Scope] object that provides a reference to the
component having its menu opened.
- `menuOpenEvent`: The `Event` that triggered opening the context menu. This
may be a `PointerEvent` or `KeyboardEvent`, depending on how the user opened
the menu.
- `menuSelectEvent`: The `Event` that selected this particular context menu
item from the menu. This may be a `PointerEvent` or `KeyboardEvent`,
depending on how the user selected the item.
- `location`: The `Coordinate` in [pixel coordinates][coordinate-systems]
where the menu was opened. This lets you, for example, create a new block at
the click location.
- `scope`: A [`Scope`][Scope] object that provides a reference to the
component having its menu opened.
- `menuOpenEvent`: The `Event` that triggered opening the context menu. This
may be a `PointerEvent` or `KeyboardEvent`, depending on how the user opened
the menu.
- `menuSelectEvent`: The `Event` that selected this particular context menu
item from the menu. This may be a `PointerEvent` or `KeyboardEvent`,
depending on how the user selected the item.
- `location`: The `Coordinate` in [pixel coordinates][coordinate-systems]
where the menu was opened. This lets you, for example, create a new block at
the click location.
```js
const collapseTemplate = {
@@ -248,7 +252,7 @@ const collapseTemplate = {
scope.focusedNode.collapse();
}
},
}
};
```
You can use `scope` to design templates that work differently depending on the
@@ -322,7 +326,9 @@ You can add a template to the registry by registering it. You should do this
once on page load. It can happen before or after you inject your workspace.
```js
const collapseTemplate = { /* properties from above */ };
const collapseTemplate = {
/* properties from above */
};
Blockly.ContextMenuRegistry.registry.register(collapseTemplate);
```
@@ -384,11 +390,11 @@ Objects in the array passed to blocks have the type
workspaces have the type `ContextMenuOption`. Blockly uses the following
properties from these objects:
* `text`: The display text.
* `enabled`: If `false`, display the item with grey text.
* `callback`: The function to be called when the item is clicked.
* `separator`: The item is a separator. Mutually exclusive with the other
three properties.
- `text`: The display text.
- `enabled`: If `false`, display the item with grey text.
- `callback`: The function to be called when the item is clicked.
- `separator`: The item is a separator. Mutually exclusive with the other
three properties.
See the reference documentation for property types and function signatures.
@@ -406,7 +412,7 @@ workspace.configureContextMenu = function (menuOptions, e) {
};
// Add the item to the end of the context menu.
menuOptions.push(item);
}
};
```
## Show a context menu on a custom object
@@ -13,7 +13,7 @@ Blockly classes.
The following Blockly classes can be replaced:
| Blockly class | Interface | Registry type name |
| Blockly class | Interface | Registry type name |
| ------------------------------------ | -------------------------------- | -------------------------- |
| [`Blockly.dragging.Dragger`] | [`Blockly.IDragger`] | `blockDragger` |
| [`Blockly.ConnectionChecker`] | [`Blockly.IConnectionChecker`] | `connectionChecker` |
@@ -37,12 +37,12 @@ calling [`setDragStrategy`][setDragStrategy].
```js
Blockly.Blocks['my_block'] = {
init: function() {
init: function () {
// Other initialization...
this.setDragStrategy(new MyDragStrategy());
// Other initialization...
}
}
},
};
```
[draggable]: /guides/configure/web/dragging/draggable
@@ -20,9 +20,9 @@ bubbles, and workspace comments.
Draggables have several responsibilities when executing drags:
* Moving svg elements to the drag layer.
* Translating svg elements.
* Firing move [events][events].
- Moving svg elements to the drag layer.
- Translating svg elements.
- Firing move [events][events].
## Implementation
@@ -229,6 +229,7 @@ For more information about copying pasting see [Copy paste][copy-paste].
[workspace-visual-glossary]: /guides/get-started/workspace-anatomy#workspace
[multiselect-plugin]: https://www.npmjs.com/package/@mit-app-inventor/blockly-plugin-workspace-multiselect
{/* This doesn't exist yet */}
[events]: /guides/configure/web/events
[IRenderedElement]: /reference/blockly.irenderedelement_interface
@@ -20,10 +20,10 @@ coordinates.
The dragger has several responsibilities when executing drags:
* Calling drag methods on the draggable.
* Calculating the position the draggable should move to in workspace
coordinates.
* Calling [drag target][drag-target] methods on any hovered drag targets.
- Calling drag methods on the draggable.
- Calculating the position the draggable should move to in workspace
coordinates.
- Calling [drag target][drag-target] methods on any hovered drag targets.
## Implementation
@@ -69,13 +69,13 @@ coordinates.
It should also update any drag targets that are being hovered over.
* `wouldDelete` should always be called before calling other hooks on the drag
target.
* `onDragExit` should always be called on the old drag target before calling
`onDragEnter` on the new drag target.
* `onDragOver` should be called after `onDragEnter` the first time the drag
target is hovered, and on each additional call to `onDrag` where the drag
target is still hovered.
- `wouldDelete` should always be called before calling other hooks on the drag
target.
- `onDragExit` should always be called on the old drag target before calling
`onDragEnter` on the new drag target.
- `onDragOver` should be called after `onDragEnter` the first time the drag
target is hovered, and on each additional call to `onDrag` where the drag
target is still hovered.
```js
onDrag(e, totalDelta) {
@@ -111,10 +111,10 @@ The `onEndDrag` method ends a drag. It should notify the draggable that the drag
has ended and any hovered drag target that the draggable has been dropped. It
should also dispose of the draggable if the drag target is a delete area.
* `onDrop` should always be called before other methods.
* `revertDrag` should be called if the drag target prevents drags.
* `endDrag` should be called after reverting the drag, but before disposing.
* `dispose` should be called if the drag target is a delete area.
- `onDrop` should always be called before other methods.
- `revertDrag` should be called if the drag target prevents drags.
- `endDrag` should be called after reverting the drag, but before disposing.
- `dispose` should be called if the drag target is a delete area.
```js
onDragEnd(e) {
@@ -166,7 +166,6 @@ const myWorkspace = Blockly.inject('blocklyDiv', {
blockDragger: MyDragger,
},
});
```
[draggable]: /guides/configure/web/dragging/draggable
@@ -9,29 +9,32 @@ import TabItem from '@theme/TabItem';
# Events
Every change on the workspace triggers an event. These events fully describe
Every change on the workspace triggers an event. These events fully describe
the before and after state of each change.
## Listen to events from the workspace
Workspaces have `addChangeListener` and `removeChangeListener` methods that can
be used to listen to the event stream. One example is the
be used to listen to the event stream. One example is the
[real-time generation of code](/guides/app-integration/run-code#continuous-updates).
Another example is the
[maximum block limit demo](https://raspberrypifoundation.github.io/blockly-samples/examples/max-blocks-demo/).
As is often the case, neither of these two examples care what the triggering
event was. They simply look at the current state of the workspace.
event was. They simply look at the current state of the workspace.
A more sophisticated event listener would look at the triggering event. The
A more sophisticated event listener would look at the triggering event. The
following example detects when the user creates their first comment,
issues an alert, then stops listening so that no further alerts are triggered.
```js
function onFirstComment(event) {
if (event.type == Blockly.Events.BLOCK_CHANGE &&
event.element == 'comment' &&
!event.oldValue && event.newValue) {
alert('Congratulations on creating your first comment!')
if (
event.type == Blockly.Events.BLOCK_CHANGE &&
event.element == 'comment' &&
!event.oldValue &&
event.newValue
) {
alert('Congratulations on creating your first comment!');
workspace.removeChangeListener(onFirstComment);
}
}
@@ -49,7 +52,7 @@ flyoutWorkspace.addChangeListener(onFirstComment);
## Listen to events from blocks
Blocks can have change listener functions that are called on any change to the
workspace (including those unrelated to the block). These are often used to
workspace (including those unrelated to the block). These are often used to
set the block's warning text, or similar user notification outside the
workspace.
@@ -119,7 +122,7 @@ The system calls the function, passing in the change event. Inside the function,
`this` refers to the block instance.
Because the function is called on any change, if used, developers should ensure
the listener runs quickly. One should also be wary of changes to the workspace
the listener runs quickly. One should also be wary of changes to the workspace
that might cascade or loop back to the listener.
See the `controls_flow_statements`, `logic_compare`, and `procedures_ifreturn`
@@ -28,44 +28,44 @@ Add an empty `div` somewhere in the page's body and set its size:
```js
const toolbox = {
"kind": "flyoutToolbox",
"contents": [
kind: 'flyoutToolbox',
contents: [
{
"kind": "block",
"type": "controls_if"
kind: 'block',
type: 'controls_if',
},
{
"kind": "block",
"type": "controls_repeat_ext"
kind: 'block',
type: 'controls_repeat_ext',
},
{
"kind": "block",
"type": "logic_compare"
kind: 'block',
type: 'logic_compare',
},
{
"kind": "block",
"type": "math_number"
kind: 'block',
type: 'math_number',
},
{
"kind": "block",
"type": "math_arithmetic"
kind: 'block',
type: 'math_arithmetic',
},
{
"kind": "block",
"type": "text"
kind: 'block',
type: 'text',
},
{
"kind": "block",
"type": "text_print"
kind: 'block',
type: 'text_print',
},
]
}
],
};
```
And finally, call the following to inject Blockly into your defined `div`.
```js
const workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
const workspace = Blockly.inject('blocklyDiv', { toolbox: toolbox });
```
The `workspace` variable is not currently used, but it will become
@@ -18,38 +18,38 @@ custom code works correctly with it.
The focus system has three parts:
* The `FocusManager` is a singleton that coordinates focus across all of
Blockly. It is used by Blockly and by custom code to find out what component
has Blockly focus, as well as to move Blockly focus to a different
component. It also listens for DOM focus events, synchronizes Blockly focus
and DOM focus, and manages the CSS classes that indicate which component has
focus.
- The `FocusManager` is a singleton that coordinates focus across all of
Blockly. It is used by Blockly and by custom code to find out what component
has Blockly focus, as well as to move Blockly focus to a different
component. It also listens for DOM focus events, synchronizes Blockly focus
and DOM focus, and manages the CSS classes that indicate which component has
focus.
The focus manager is primarily used by Blockly. It is sometimes used by
custom code to [interact with the focus system](#use-the-focus-manager).
The focus manager is primarily used by Blockly. It is sometimes used by
custom code to [interact with the focus system](#use-the-focus-manager).
* An `IFocusableTree` is an independent area of a Blockly editor, such as a
workspace or toolbox. It is composed of focusable nodes, such as blocks and
fields. Trees can also have subtrees. For example, a mutator workspace on a
block in the main workspace is a subtree of the main workspace.
- An `IFocusableTree` is an independent area of a Blockly editor, such as a
workspace or toolbox. It is composed of focusable nodes, such as blocks and
fields. Trees can also have subtrees. For example, a mutator workspace on a
block in the main workspace is a subtree of the main workspace.
`IFocusableTree` is primarily used by the focus manager. Unless you write a
custom toolbox, you probably won't need to [implement it](#custom-classes).
`IFocusableTree` is primarily used by the focus manager. Unless you write a
custom toolbox, you probably won't need to [implement it](#custom-classes).
* An `IFocusableNode` is a Blockly component that can have focus, such as a
block, field, or toolbox category. Focusable nodes have a DOM element that
displays the node and which has DOM focus when the node has Blockly focus.
Note that trees are also focusable nodes. For example, you can focus on the
workspace as a whole.
- An `IFocusableNode` is a Blockly component that can have focus, such as a
block, field, or toolbox category. Focusable nodes have a DOM element that
displays the node and which has DOM focus when the node has Blockly focus.
Note that trees are also focusable nodes. For example, you can focus on the
workspace as a whole.
The methods in `IFocusableNode` are primarily called by the focus manager.
The methods in `IFocusableNode` are primarily called by the focus manager.
`IFocusableNode` itself is used to represent the component that has focus.
For example, when a user selects an item on a block's context menu, the
block is passed to the item's callback function as an `IFocusableNode`.
`IFocusableNode` itself is used to represent the component that has focus.
For example, when a user selects an item on a block's context menu, the
block is passed to the item's callback function as an `IFocusableNode`.
If you write custom components, you may need to [implement
`IFocusableNode`](#custom-classes).
If you write custom components, you may need to [implement
`IFocusableNode`](#custom-classes).
## Types of focus
@@ -59,19 +59,19 @@ The focus system defines a number of different types of focus.
The two main types of focus are Blockly focus and DOM focus.
* **Blockly focus** specifies which Blockly component (block, field, toolbox
category, etc.) has focus. It is necessary for working at the level of
Blockly components. For example, the keyboard navigation plugin allows users
to use arrow keys to move from component to component, such as from a block
to a field. Similarly, the context menu system builds a menu that is
appropriate for the current component -- that is, it builds different menus
for workspaces, blocks, and workspace comments.
- **Blockly focus** specifies which Blockly component (block, field, toolbox
category, etc.) has focus. It is necessary for working at the level of
Blockly components. For example, the keyboard navigation plugin allows users
to use arrow keys to move from component to component, such as from a block
to a field. Similarly, the context menu system builds a menu that is
appropriate for the current component -- that is, it builds different menus
for workspaces, blocks, and workspace comments.
* **DOM focus** specifies which DOM element has focus. It is necessary for
working at the level of DOM elements. For example, [screen
readers](https://www.afb.org/blindness-and-low-vision/using-technology/assistive-technology-products/screen-readers)
present information about the element that currently has DOM focus and tabs
move (change focus) from DOM element to DOM element.
- **DOM focus** specifies which DOM element has focus. It is necessary for
working at the level of DOM elements. For example, [screen
readers](https://www.afb.org/blindness-and-low-vision/using-technology/assistive-technology-products/screen-readers)
present information about the element that currently has DOM focus and tabs
move (change focus) from DOM element to DOM element.
The focus manager keeps Blockly focus and DOM focus in sync, so when a node
(Blockly component) has Blockly focus, its underlying DOM element has DOM focus
@@ -122,21 +122,21 @@ arrow:
1. The [keyboard navigation
plugin](/guides/configure/web/keyboard-nav):
* Receives a key press event.
* Asks the navigation system (a part of core Blockly) to move focus to the
"next" component.
- Receives a key press event.
- Asks the navigation system (a part of core Blockly) to move focus to the
"next" component.
1. The navigation system:
* Asks the focus manager what component has Blockly focus. The focus
manager returns the block as an `IFocusableNode`.
* Determines that the `IFocusableNode` is a `BlockSvg` and looks at its
rules for navigating blocks, which state that it should move Blockly
focus from the block as a whole to the first field on the block.
* Tells the focus manager to move Blockly focus to the first field.
- Asks the focus manager what component has Blockly focus. The focus
manager returns the block as an `IFocusableNode`.
- Determines that the `IFocusableNode` is a `BlockSvg` and looks at its
rules for navigating blocks, which state that it should move Blockly
focus from the block as a whole to the first field on the block.
- Tells the focus manager to move Blockly focus to the first field.
1. The focus manager:
* Updates its state to set Blockly focus on the first field.
* Sets DOM focus on the field's DOM element.
* Moves the highlight class from the block's element to the field's
element.
- Updates its state to set Blockly focus on the first field.
- Sets DOM focus on the field's DOM element.
- Moves the highlight class from the block's element to the field's
element.
### Move focus with the mouse
@@ -156,20 +156,20 @@ manager:
Here are some other examples:
* When a user drags a block from the toolbox to the workspace, the mouse event
handler creates a new block and calls the focus manager to set Blockly focus
on that block.
- When a user drags a block from the toolbox to the workspace, the mouse event
handler creates a new block and calls the focus manager to set Blockly focus
on that block.
* When a block is deleted, its `dispose` method calls the focus manager to
move focus to the block's parent.
- When a block is deleted, its `dispose` method calls the focus manager to
move focus to the block's parent.
* [Keyboard shortcuts](/guides/configure/web/keyboard-shortcuts) use
`IFocusableNode` to identify the Blockly component to which the shortcut
applies.
- [Keyboard shortcuts](/guides/configure/web/keyboard-shortcuts) use
`IFocusableNode` to identify the Blockly component to which the shortcut
applies.
* [Context menus](/guides/configure/web/context-menus) use
`IFocusableNode` to identify the Blockly component on which the menu was
invoked.
- [Context menus](/guides/configure/web/context-menus) use
`IFocusableNode` to identify the Blockly component on which the menu was
invoked.
## Customizations and the focus system
@@ -191,57 +191,57 @@ the case.
Some classes clearly need to implement focus interfaces. These include:
* A class that implements a custom toolbox. This class needs to implement
`IFocusableTree` and `IFocusableNode`.
- A class that implements a custom toolbox. This class needs to implement
`IFocusableTree` and `IFocusableNode`.
* Classes that create a visible component (such as a field or icon) that users
can navigate to. These classes need to implement `IFocusableNode`.
- Classes that create a visible component (such as a field or icon) that users
can navigate to. These classes need to implement `IFocusableNode`.
Some classes need to implement `IFocusableNode` even though they do not create a
visible component or they create a visible component that users cannot navigate
to. These include:
* Classes that implement an interface that extends `IFocusableNode`.
- Classes that implement an interface that extends `IFocusableNode`.
For example, the move icon in the keyboard navigation plugin displays a
four-way arrow that indicates that the block can be moved with the arrow
keys. The icon itself is not visible (the four-way arrow is a bubble) and
users cannot navigate to it. However, the icon must implement
`IFocusableNode` because icons implement`IIcon` and `IIcon` extends
`IFocusableNode`.
For example, the move icon in the keyboard navigation plugin displays a
four-way arrow that indicates that the block can be moved with the arrow
keys. The icon itself is not visible (the four-way arrow is a bubble) and
users cannot navigate to it. However, the icon must implement
`IFocusableNode` because icons implement`IIcon` and `IIcon` extends
`IFocusableNode`.
* Classes used in an API that requires an `IFocusableNode`.
- Classes used in an API that requires an `IFocusableNode`.
For example, the `FlyoutSeparator` class creates a gap between two items in
a flyout. It does not create any DOM elements, so it doesn't have a visible
component and users can't navigate to it. However, it must implement
`IFocusableNode` because it is stored in a `FlyoutItem` and the `FlyoutItem`
constructor requires an `IFocusableNode`.
For example, the `FlyoutSeparator` class creates a gap between two items in
a flyout. It does not create any DOM elements, so it doesn't have a visible
component and users can't navigate to it. However, it must implement
`IFocusableNode` because it is stored in a `FlyoutItem` and the `FlyoutItem`
constructor requires an `IFocusableNode`.
* Classes that extend a class that implements `IFocusableNode`.
- Classes that extend a class that implements `IFocusableNode`.
For example, `ToolboxSeparator` extends `ToolboxItem`, which implements
`IFocusableNode`. Although toolbox separators have a visible component,
users cannot navigate to them because they cannot be acted on and have no
useful content.
For example, `ToolboxSeparator` extends `ToolboxItem`, which implements
`IFocusableNode`. Although toolbox separators have a visible component,
users cannot navigate to them because they cannot be acted on and have no
useful content.
Other classes create visible components that the user can navigate to, but do
not need to implement `IFocusableNode`. These include:
* Classes that create a visible component that manages its own focus, such as
a field editor or a dialog. (Note that such classes do need to [take
ephemeral focus](#ephemeral-focus) when they start and return it when they
end. Using `WidgetDiv` or `DropDownDiv` will handle this for you.)
- Classes that create a visible component that manages its own focus, such as
a field editor or a dialog. (Note that such classes do need to [take
ephemeral focus](#ephemeral-focus) when they start and return it when they
end. Using `WidgetDiv` or `DropDownDiv` will handle this for you.)
Finally, some classes do not interact with the focus system and do not need to
implement `IFocusableTree` or `IFocusableNode`. These include:
* Classes that create a visible component that users cannot navigate to or act
on and which contains no information that a screen reader might use. For
example, a purely decorative background in a game.
- Classes that create a visible component that users cannot navigate to or act
on and which contains no information that a screen reader might use. For
example, a purely decorative background in a game.
* Classes entirely unrelated to the focus system, such as classes that
implement `IMetricsManager` or `IVariableMap`.
- Classes entirely unrelated to the focus system, such as classes that
implement `IMetricsManager` or `IVariableMap`.
If you are uncertain whether your class will interact with the focus system,
test it with the keyboard navigation plugin. If this fails, you might need to
@@ -6,8 +6,8 @@ image: images/blockly_banner.png
# Grid option
Blockly's main workspace may optionally have a grid. Blocks can be made to
snap to the grid, enabling cleaner layout. This is particularly helpful in
Blockly's main workspace may optionally have a grid. Blocks can be made to
snap to the grid, enabling cleaner layout. This is particularly helpful in
larger applications with multiple code groupings spread out over a
large area.
@@ -17,20 +17,17 @@ options](/guides/configure/web/configuration_struct#the-options-dictionary).
Here is an example:
```js
var workspace = Blockly.inject('blocklyDiv',
{toolbox: document.getElementById('toolbox'),
grid:
{spacing: 20,
length: 3,
colour: '#ccc',
snap: true},
trashcan: true});
var workspace = Blockly.inject('blocklyDiv', {
toolbox: document.getElementById('toolbox'),
grid: { spacing: 20, length: 3, colour: '#ccc', snap: true },
trashcan: true,
});
```
## Spacing
The most important grid property is `spacing` which defines the distance
between the grid's points. The default value is 0, which results in no grid.
between the grid's points. The default value is 0, which results in no grid.
Here are examples of `spacing` set to 10, 20, and 40:
![Three different workspaces with grid spacing set to 10, 20, and
@@ -42,7 +39,7 @@ The `length` property is a number that defines the shape of the grid points.
A length of 0 results in an invisible grid (but still one that may be snapped
to), a length of 1 (the default value) results in dots, a longer length results
in crosses, and a length equal or greater than the spacing results in graph
paper. Here are examples of `length` set to 1, 5, and 20:
paper. Here are examples of `length` set to 1, 5, and 20:
![Three different workspaces with grid marker lengths of 1, 5, and 20. The first
length results in dots, the second in crosses, and the third in graph
@@ -51,8 +48,8 @@ paper.](/images/grid-length.png)
## Colour
The `colour` property is a string that sets the colour of the points.
Note the British spelling. Use any CSS-compatible format, including `#f00`,
`#ff0000`, or `rgb(255, 0, 0)`. The default value is `#888`.
Note the British spelling. Use any CSS-compatible format, including `#f00`,
`#ff0000`, or `rgb(255, 0, 0)`. The default value is `#888`.
Here are examples of `colour` set to `#000`, `#ccc`, and `#f00`:
![Three different workspaces with different grid
@@ -61,7 +58,7 @@ colours.](/images/grid-colour.png)
## Snap
The `snap` property is a boolean that sets whether blocks should snap to the
nearest grid point when placed on the workspace. The default value is `false`.
nearest grid point when placed on the workspace. The default value is `false`.
![A workspace showing a block snapped to the grid and a workspace showing a
block not snapped to the grid.](/images/grid-snap.png)
@@ -19,12 +19,12 @@ GitHub.
If you want to play with the plugin, visit the [accessibility plugin
playground](https://raspberrypifoundation.github.io/blockly-keyboard-experimentation/).
* To get started, click the workspace or press `tab` until you reach the
workspace.
* Use the arrow keys to move around.
* Press `T` to open the toolbox.
* Press `Enter` or `Space` to edit or confirm.
* Press `/` for a complete list of keyboard commands.
- To get started, click the workspace or press `tab` until you reach the
workspace.
- Use the arrow keys to move around.
- Press `T` to open the toolbox.
- Press `Enter` or `Space` to edit or confirm.
- Press `/` for a complete list of keyboard commands.
For more information about our accessibility projects, visit [our accessibility
page](https://developers.google.com/blockly/accessibility#projects).
@@ -84,7 +84,7 @@ const logFieldsShortcut = {
// ...
preconditionFn(workspace, scope) {
// This shortcut only applies to blocks.
return (scope.focusedNode instanceof Blockly.BlockSvg);
return scope.focusedNode instanceof Blockly.BlockSvg;
},
// ...
};
@@ -100,10 +100,10 @@ help menus that show applicable shortcuts.
This function executes the action associated with the shortcut. It is called
only if `preconditionFn` returns `true` or does not exist. Its parameters are:
* `workspace`: The current `WorkspaceSvg`.
* `e`: The `Event` that initiated the shortcut.
* `shortcut`: The `KeyboardShortcut` itself.
* `scope`: The `Scope` to which the shortcut applies.
- `workspace`: The current `WorkspaceSvg`.
- `e`: The `Event` that initiated the shortcut.
- `shortcut`: The `KeyboardShortcut` itself.
- `scope`: The `Scope` to which the shortcut applies.
It returns `true` if it succeeds and `false` if it fails.
@@ -159,8 +159,8 @@ If your keyboard shortcut is activated by a combination of keys, such as holding
```js
const ctrlC = Blockly.ShortcutRegistry.registry.createSerializedKey(
Blockly.utils.KeyCodes.C, // Keycode of main key
[Blockly.utils.KeyCodes.CTRL], // Array of modifier keys
Blockly.utils.KeyCodes.C, // Keycode of main key
[Blockly.utils.KeyCodes.CTRL], // Array of modifier keys
);
const copyShortcut = {
@@ -255,9 +255,11 @@ const allShortcuts = Blockly.ShortcutRegistry.registry.getRegistry();
const modLogFieldsShortcut = allShortcuts[logFieldsShortcut.name];
// Apply the shortcut only to math blocks,
modLogFieldsShortcut.preconditionFn = function (workspace, scope) {
return (scope.focusedNode instanceof Blockly.BlockSvg &&
scope.focusedNode.type.startsWith('math_'));
}
return (
scope.focusedNode instanceof Blockly.BlockSvg &&
scope.focusedNode.type.startsWith('math_')
);
};
// Delete the existing shortcut and add the modified shortcut.
Blockly.ShortcutRegistry.registry.unregister(logFieldsShortcut.name);
Blockly.ShortcutRegistry.registry.register(modLogFieldsShortcut);
@@ -24,7 +24,11 @@ The toolbox metrics are composed of the `height`, `width` and `position` of a
category toolbox. This does not include information on the flyout that is
attached to the toolbox.
<Image alt="The Blockly workspace with arrows showing the width and height of the toolbox." src="/images/metrics_manager/toolbox_metrics.png" width={300} />
<Image
alt="The Blockly workspace with arrows showing the width and height of the toolbox."
src="/images/metrics_manager/toolbox_metrics.png"
width={300}
/>
The `position` of the toolbox is of the type `Blockly.utils.toolbox.Position`.
@@ -37,7 +41,11 @@ flyout toolbox. It is important to note, that this is not the flyout that is
attached to the category toolbox. This only pertains to flyout toolboxes as
shown in the below photo.
<Image alt="The Blockly workspace with arrows showing the width and height of the flyout." src="/images/metrics_manager/flyout_toolbox_metrics.png" width={300} />
<Image
alt="The Blockly workspace with arrows showing the width and height of the flyout."
src="/images/metrics_manager/flyout_toolbox_metrics.png"
width={300}
/>
The `position` of the flyout is of the type `Blockly.utils.toolbox.Position`.
@@ -49,7 +57,11 @@ The SVG metrics are composed of the `width` and `height` of the workspace's
parent SVG. For the main workspace, this is the SVG with the `blocklySvg` class.
This SVG includes the visible workspace as well as the toolbox.
<Image alt="The Blockly workspace with a blue rectangle around it." src="/images/metrics_manager/svg_metrics.png" width={300} />
<Image
alt="The Blockly workspace with a blue rectangle around it."
src="/images/metrics_manager/svg_metrics.png"
width={300}
/>
### View Metrics
@@ -59,12 +71,20 @@ The view metrics are composed of the `height`, `width`, `top` and `left` of the
viewport. The viewport is the portion of the workspace that is visible. This
does not include either type of toolbox.
<Image alt="The Blockly workspace with a blue rectangle around the area not including the toolbox." src="/images/metrics_manager/view_metrics.png" width={300} />
<Image
alt="The Blockly workspace with a blue rectangle around the area not including the toolbox."
src="/images/metrics_manager/view_metrics.png"
width={300}
/>
The top left is relative to the workspace origin. As we drag around the
workspace the top and left position of the viewport are updated.
<Image alt="The Blockly workspace with a blue rectangle around the area not including the toolbox, and an origin shown offset from the top left corner." src="/images/metrics_manager/view_metrics_origin.png" width={300} />
<Image
alt="The Blockly workspace with a blue rectangle around the area not including the toolbox, and an origin shown offset from the top left corner."
src="/images/metrics_manager/view_metrics_origin.png"
width={300}
/>
### Absolute Metrics
@@ -74,8 +94,16 @@ The absolute metrics are composed of the `top` and `left` offset of the viewport
from the pareng SVG. Depending on where the toolbox is positioned on the
workspace, this is usually the width or height of the toolbox.
<Image alt="The Blockly workspace with a blue line to the right of the toolbox and on top of the workspace." src="/images/metrics_manager/absolute_metrics.png" width={300} />
<Image alt="The Blockly workspace with a horizontal toolbox. There is a blue line on the left of the workspace and below the toolbox." src="/images/metrics_manager/absolute_metrics_2.png" width={300} />
<Image
alt="The Blockly workspace with a blue line to the right of the toolbox and on top of the workspace."
src="/images/metrics_manager/absolute_metrics.png"
width={300}
/>
<Image
alt="The Blockly workspace with a horizontal toolbox. There is a blue line on the left of the workspace and below the toolbox."
src="/images/metrics_manager/absolute_metrics_2.png"
width={300}
/>
### Content Metrics
@@ -89,7 +117,11 @@ Content metrics do not take into account block comments, only [workspace
comments](/reference/blockly.comments_namespace.workspacecomment_class/).
:::
<Image alt="The Blockly workspace with a blue box around the contents of the workspace." src="/images/metrics_manager/content_metrics.png" width={300} />
<Image
alt="The Blockly workspace with a blue box around the contents of the workspace."
src="/images/metrics_manager/content_metrics.png"
width={300}
/>
### Scroll Metrics
@@ -99,7 +131,11 @@ The scroll metrics are composed of the `height`, `width`, `top` and `left` of
the scrollable area. For a movable workspace, the scrollable area is the content
area plus some padding.
<Image alt="The Blockly workspace with a large blue box surrounding it." src="/images/metrics_manager/scroll_metrics.png" width={300} />
<Image
alt="The Blockly workspace with a large blue box surrounding it."
src="/images/metrics_manager/scroll_metrics.png"
width={300}
/>
## Coordinate Systems
@@ -15,15 +15,16 @@ options](/guides/configure/web/configuration_struct#the-options-dictionary).
Here is an example:
```js
var workspace = Blockly.inject('blocklyDiv',
{move:{
scrollbars: {
horizontal: true,
vertical: true
},
drag: true,
wheel: false}
});
var workspace = Blockly.inject('blocklyDiv', {
move: {
scrollbars: {
horizontal: true,
vertical: true,
},
drag: true,
wheel: false,
},
});
```
## scrollbars
@@ -36,7 +37,6 @@ If a boolean is passed then it is equivalent to passing an object with both
(both horizontal and vertical scrolling enabled) if the workspace has
categories.
## drag
Determines if the workspace can be dragged with the mouse. Always `false` if
@@ -7,11 +7,11 @@ image: images/blockly_banner.png
# Resizable workspace
A good web application will resize Blockly to fill the available space on
screen rather than being a fixed size. There are several ways to do this,
screen rather than being a fixed size. There are several ways to do this,
including using an iframe, CSS, and JavaScript positioning.
This page demonstrates an overlay approach which is robust and flexible.
This is a three-step process. The first step is to define the area.
This is a three-step process. The first step is to define the area.
The second step is to inject Blockly.
The third step is to position Blockly over this area.
@@ -37,7 +37,7 @@ Blockly should now be running on the page, just not located where it should be
## 3. Position the workspace
The final step is to position the `blocklyDiv` element over the `blocklyArea`
element. To do this, remove any `height` and `width` styles from `blocklyDiv`
element. To do this, remove any `height` and `width` styles from `blocklyDiv`
and add absolute positioning:
```html
@@ -50,9 +50,10 @@ Then replace the injection script with one that also positions `blocklyDiv` over
```js
const blocklyArea = document.getElementById('blocklyArea');
const blocklyDiv = document.getElementById('blocklyDiv');
const workspace = Blockly.inject(blocklyDiv,
{toolbox: document.getElementById('toolbox')});
const onresize = function(e) {
const workspace = Blockly.inject(blocklyDiv, {
toolbox: document.getElementById('toolbox'),
});
const onresize = function (e) {
// Compute the absolute coordinates and dimensions of blocklyArea.
const element = blocklyArea;
let x = 0;
@@ -42,7 +42,7 @@ const state = Blockly.serialization.workspaces.save(myWorkspace);
Blockly.serialization.workspaces.load(state, myWorkspace);
```
These calls serialize or deserialize *all* of the individual systems
These calls serialize or deserialize _all_ of the individual systems
(represented by serializers) which are registered with the workspace.
#### Individual blocks
@@ -52,8 +52,10 @@ You can serialize or deserialize individual blocks by calling the `save` and
```js
const blockJson = Blockly.serialization.blocks.save(myBlock);
const duplicateBlock =
Blockly.serialization.blocks.append(blockJson, myWorkspace);
const duplicateBlock = Blockly.serialization.blocks.append(
blockJson,
myWorkspace,
);
```
#### Individual systems
@@ -75,7 +77,7 @@ The JSON system has an explicit deserialization order, which makes it easier to
prevent duplicating state within a save.
When `Blockly.serialization.workspaces.load` is called, serializers are given
state to deserialize in order of *priority*. This is explained further in the
state to deserialize in order of _priority_. This is explained further in the
[Serializers](/guides/configure/web/serialization#serializer-hooks)
section, and its purpose is to allow serializers to depend on state from other
systems.
@@ -85,7 +87,7 @@ The order for deserialization of built-in serializers is:
1. **Variable models are deserialized.**
2. **Procedure models are deserialized.**
3. **Blocks are deserialized.** Individual top-level blocks are
deserialized in an *arbitrary order*.
deserialized in an _arbitrary order_.
1. **Type is deserialized.** This constructs the block, triggers its init
method, and mixes in extensions.
2. **Attributes are deserialized.** This includes properties that can apply
@@ -95,12 +97,12 @@ The order for deserialization of built-in serializers is:
more info.
5. **The block is connected to its parent (if one exists).**
1. **Icons are deserialized.** Individual icons are deserialized in an
*arbitrary order*.
_arbitrary order_.
2. **Fields are deserialized.** Individual fields are deserialized in an
*arbitrary order*.
_arbitrary order_.
3. **Input blocks are deserialized.** This includes blocks connected to
value inputs and statement inputs. Individual inputs are deserialized in
an *arbitrary order*.
an _arbitrary order_.
4. **Next blocks are deserialized.**
### When to save extra state
@@ -150,27 +152,28 @@ a plugin.
```js
Blockly.serialization.registry.register(
'workspace-comments', // Name
{
save: saveFn, // Save function
load: loadFn, // Load function
clear: clearFn, // Clear function
priority: 10, // Priority
});
'workspace-comments', // Name
{
save: saveFn, // Save function
load: loadFn, // Load function
clear: clearFn, // Clear function
priority: 10, // Priority
},
);
```
When you register a serializer you must provide several things:
* A name for the serializer, which the data is also saved under.
* A function to `save` the state of the plugin/system associated with the
serializer.
* A function to `clear` the state.
* A function to `load` the state.
* A `priority`, which is used to determine the [deserialization
order](/guides/configure/web/serialization#deserialization-order).
- A name for the serializer, which the data is also saved under.
- A function to `save` the state of the plugin/system associated with the
serializer.
- A function to `clear` the state.
- A function to `load` the state.
- A `priority`, which is used to determine the [deserialization
order](/guides/configure/web/serialization#deserialization-order).
You can base the priority of your serializer on the [built-in
priorities](/reference/blockly.serialization_namespace.priorities_namespace)
You can base the priority of your serializer on the [built-in
priorities](/reference/blockly.serialization_namespace.priorities_namespace)
When `Blockly.serialization.workspaces.save` is called, each serializer's `save`
function will be called, and its data will be added to the final JSON output:
@@ -48,22 +48,22 @@ The following table lists the types, descriptions, and default CSS classes of
each part of a category toolbox. If you're having trouble visualizing this, open
the developer tools in your browser and inspect the toolbox.
| Part type | Description | Default CSS class |
| ------------------- | ------------------------------------------------------------------------ | ---------------------------------- |
| contents | The `div` that contains all categories. | `blocklyToolboxCategoryGroup` |
| container | The `div` that contains a single category and its subcategories. | `blocklyToolboxCategoryContainer` |
| row | The `div` that contains the category. | `blocklyToolboxCategory` | |
| rowcontentcontainer | The `div` that contains the label and the icon. | `blocklyTreeRowContentContainer` |
| icon | The `span` that contains the icon. | `blocklyToolboxCategoryIcon` | |
| label | The `span` that contains the label. | `blocklyToolboxCategoryLabel` |
| Part type | Description | Default CSS class |
| ------------------- | ---------------------------------------------------------------- | --------------------------------- | --- |
| contents | The `div` that contains all categories. | `blocklyToolboxCategoryGroup` |
| container | The `div` that contains a single category and its subcategories. | `blocklyToolboxCategoryContainer` |
| row | The `div` that contains the category. | `blocklyToolboxCategory` | |
| rowcontentcontainer | The `div` that contains the label and the icon. | `blocklyTreeRowContentContainer` |
| icon | The `span` that contains the icon. | `blocklyToolboxCategoryIcon` | |
| label | The `span` that contains the label. | `blocklyToolboxCategoryLabel` |
CSS is also used to label the state of some parts of the toolbox.
| State | Description | Default CSS class |
| ------------------- | ------------------------------------------------------------------------ | ---------------------------------- |
| selected | Added to the "row" `div` when the category is selected. | `blocklyToolboxSelected` |
| openicon | Added to the "icon" `span` when a category with subcategories is open. | `blocklyToolboxCategoryIconOpen` |
| closedicon | Added to the "icon" `span` when a category with subcategories is closed. | `blocklyToolboxCategoryIconClosed` |
| State | Description | Default CSS class |
| ---------- | ------------------------------------------------------------------------ | ---------------------------------- |
| selected | Added to the "row" `div` when the category is selected. | `blocklyToolboxSelected` |
| openicon | Added to the "icon" `span` when a category with subcategories is open. | `blocklyToolboxCategoryIconOpen` |
| closedicon | Added to the "icon" `span` when a category with subcategories is closed. | `blocklyToolboxCategoryIconClosed` |
### Custom CSS classes
@@ -125,9 +125,15 @@ In your CSS file, use the custom classes to assign row colors and the default
class to assign the label color:
```css
.myRow {background-color: green}
.yourRow {background-color: red}
.blocklyToolboxCategoryLabel {color: white}
.myRow {
background-color: green;
}
.yourRow {
background-color: red;
}
.blocklyToolboxCategoryLabel {
color: white;
}
```
## Themes
@@ -92,4 +92,4 @@ options](/guides/configure/web/configuration_struct#the-options-dictionary).
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
```
</TabItem>
</Tabs>
</Tabs>
@@ -25,8 +25,8 @@ When a category is disabled, a `'disabled'` property is added to the DOM
element, which allows you to control the look of a disabled category.
```css
.blocklyToolboxCategoryContainer[disabled="true"] {
opacity: .5;
.blocklyToolboxCategoryContainer[disabled='true'] {
opacity: 0.5;
}
```
@@ -82,4 +82,4 @@ categories are collapsed, and need to be clicked to be expanded.
<category name="..." expanded="true"></category>
```
</TabItem>
</Tabs>
</Tabs>
@@ -94,11 +94,11 @@ your category definition to make the category dynamic.
Blockly provides three built-in dynamic categories.
* `'VARIABLE'` creates a category for [*untyped* variables][untyped-variable].
* `'VARIABLE_DYNAMIC'` creates a category for
[*typed* variables][typed-variable]. It has buttons to create strings,
numbers, and colours.
* `'PROCEDURE'` creates a category for function blocks.
- `'VARIABLE'` creates a category for [_untyped_ variables][untyped-variable].
- `'VARIABLE_DYNAMIC'` creates a category for
[_typed_ variables][typed-variable]. It has buttons to create strings,
numbers, and colours.
- `'PROCEDURE'` creates a category for function blocks.
<Tabs groupId="code-format">
<TabItem value="json" label="JSON" default>
@@ -58,4 +58,4 @@ options](/guides/configure/web/configuration_struct#the-options-dictionary).
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
```
</TabItem>
</Tabs>
</Tabs>
@@ -14,7 +14,7 @@ workspace.updateToolbox(newTree);
```
As was the case during initial configuration, `newTree` may either be a tree of
nodes, a string representation, or a JSON object. The only restriction is that
nodes, a string representation, or a JSON object. The only restriction is that
the mode cannot be changed; that is if there were categories in the
initially-defined toolbox then the new toolbox must also have categories
(though the categories may change). Likewise, if the initially-defined toolbox
@@ -32,6 +32,6 @@ or a string representation.
Be aware that at this time updating the toolbox causes some minor UI resets:
* In a toolbox without categories, any fields changed by the user (such as a dropdown) will revert to the default.
- In a toolbox without categories, any fields changed by the user (such as a dropdown) will revert to the default.
Here is [a live demo](https://raspberrypifoundation.github.io/blockly-samples/examples/toolbox-demo/) of a tree with categories and block groups.
@@ -13,7 +13,7 @@ Categories may be nested within other categories. Here are two top-level
categories ('Core' and 'Custom'), the second of which contains two sub-categories,
each of which contain blocks:
Note that it is possible for a category to contain both sub-categories *and*
Note that it is possible for a category to contain both sub-categories _and_
blocks. In the following example, `Custom` has two sub-categories (`Move` and
`Turn`), as well as a block of its own (`start`).
@@ -90,4 +90,4 @@ blocks. In the following example, `Custom` has two sub-categories (`Move` and
</xml>
```
</TabItem>
</Tabs>
</Tabs>
@@ -187,14 +187,13 @@ the following code in the console.
<Tabs groupId="code-format">
<TabItem value="json" label="JSON" default>
```js
console.log(Blockly.serialization.workspaces.save(Blockly.getMainWorkspace()));
```
```js
console.log(Blockly.serialization.workspaces.save(Blockly.getMainWorkspace()));
```
</TabItem>
<TabItem value="xml" label="XML">
```xml
console.log(Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace()));
```
```xml console.log(Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace()));
```
</TabItem>
</Tabs>
You can then copy the configuration code and paste it into your toolbox. Be sure
@@ -232,10 +231,10 @@ information, so it needs to be included in the variable field directly.
Shadow blocks are placeholder blocks that perform several functions:
* They indicate the default values for their parent block.
* They allow users to type values directly without needing to fetch a number or string block.
* Unlike a regular block, they get replaced if the user drops a block on top of them.
* They inform the user of the type of value expected.
- They indicate the default values for their parent block.
- They allow users to type values directly without needing to fetch a number or string block.
- Unlike a regular block, they get replaced if the user drops a block on top of them.
- They inform the user of the type of value expected.
:::note
Shadow blocks may not include a variable field or have children that are
@@ -308,4 +307,4 @@ And here is a toolbox that uses these shadow blocks:
</xml>
```
</TabItem>
</Tabs>
</Tabs>
@@ -39,4 +39,4 @@ Where the ID is specified in the toolbox definition:
<category name="..." toolboxitemid="categoryId"></category>
```
</TabItem>
</Tabs>
</Tabs>

Some files were not shown because too many files have changed in this diff Show More