diff --git a/codelabs/css/blocks-arrow-right.png b/codelabs/css/blocks-arrow-right.png new file mode 100644 index 000000000..3ff0f5e76 Binary files /dev/null and b/codelabs/css/blocks-arrow-right.png differ diff --git a/codelabs/css/blocks-arrow-wrong.png b/codelabs/css/blocks-arrow-wrong.png new file mode 100644 index 000000000..130a2b940 Binary files /dev/null and b/codelabs/css/blocks-arrow-wrong.png differ diff --git a/codelabs/css/blocks-disabled-right.png b/codelabs/css/blocks-disabled-right.png new file mode 100644 index 000000000..40881b355 Binary files /dev/null and b/codelabs/css/blocks-disabled-right.png differ diff --git a/codelabs/css/blocks-disabled-wrong.png b/codelabs/css/blocks-disabled-wrong.png new file mode 100644 index 000000000..b63a55f99 Binary files /dev/null and b/codelabs/css/blocks-disabled-wrong.png differ diff --git a/codelabs/css/blocks-logic.png b/codelabs/css/blocks-logic.png new file mode 100644 index 000000000..8a703717d Binary files /dev/null and b/codelabs/css/blocks-logic.png differ diff --git a/codelabs/css/blocks-loops.png b/codelabs/css/blocks-loops.png new file mode 100644 index 000000000..b829b7b13 Binary files /dev/null and b/codelabs/css/blocks-loops.png differ diff --git a/codelabs/css/categories-halloween.png b/codelabs/css/categories-halloween.png new file mode 100644 index 000000000..a856466f6 Binary files /dev/null and b/codelabs/css/categories-halloween.png differ diff --git a/codelabs/css/categories-logic.png b/codelabs/css/categories-logic.png new file mode 100644 index 000000000..039ecda02 Binary files /dev/null and b/codelabs/css/categories-logic.png differ diff --git a/codelabs/css/components-halloween.png b/codelabs/css/components-halloween.png new file mode 100644 index 000000000..a6b00f65b Binary files /dev/null and b/codelabs/css/components-halloween.png differ diff --git a/codelabs/css/components-mutator.png b/codelabs/css/components-mutator.png new file mode 100644 index 000000000..f6583f79a Binary files /dev/null and b/codelabs/css/components-mutator.png differ diff --git a/codelabs/css/components-workspace.png b/codelabs/css/components-workspace.png new file mode 100644 index 000000000..d4d3231b5 Binary files /dev/null and b/codelabs/css/components-workspace.png differ diff --git a/codelabs/css/css.md b/codelabs/css/css.md new file mode 100644 index 000000000..4738e0288 --- /dev/null +++ b/codelabs/css/css.md @@ -0,0 +1,740 @@ +author: Blockly Team summary: How to use CSS in Blockly. id: css-identifier +categories: blockly,codelab,css status: Published feedback link: +https://github.com/google/blockly-samples/issues/new/choose + +# Use CSS in Blockly + +## Codelab overview + +### What you'll learn + +In this codelab you will learn how to use CSS to customize the colours of: + +- Components +- Categories +- Blocks + +### What you'll build + +A simple Blockly workspace that uses the same Halloween colours as the +[Customizing your themes](https://blocklycodelabs.dev/codelabs/theme-extension-identifier/index.html#0) +codelab. + +### What you'll need + +- A browser +- Basic knowledge of HTML, CSS, SVG, and JavaScript. +- Basic knowledge of your browser's developer tools. +- Basic understanding of Blockly, including workspace components, category + toolboxes, block definitions, and themes. + +This codelab is focused on using CSS with Blockly. Non-relevant concepts are +glossed over and provided for you to simply copy and paste. + +## Setup + +### Download the sample code + +You can get the sample code for this code by either downloading the zip here: + +[Download zip](https://github.com/RaspberryPiFoundation/blockly-samples/archive/master.zip) + +or by cloning this git repo: + +```bash +git clone https://github.com/RaspberryPiFoundation/blockly-samples.git +``` + +If you downloaded the source as a zip, unpacking it should give you a root +folder named `blockly-samples-master`. + +The relevant files are in `examples/css-codelab`. 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.html` - A web page containing a simple Blockly workspace. +- `toolbox.js` - A toolbox with multiple categories. +- `index.js` - Code to inject a simple workspace. + +The `complete-code` folder also contains the `halloween.css` file you'll create. + +To run the code, simply open `starter-code/index.html` in a browser. You should +see a Blockly workspace with multiple categories. + +![A web page with the text "CSS Codelab" and a Blockly editor with seven +categories.](setup-starter.png) + +## A tour of Blockly's elements + +In this section, you'll explore the HTML and SVG elements used by Blockly, as +well as the classes assigned to these elements. + +In your Blockly editor, drag a `count with` block from the **Loops** category +and an `if do` block from the **Logic** category onto your workspace and open +your browser's +[developer tools](https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Tools_and_setup/What_are_browser_developer_tools). +Your screen should look something like this: + +![A Blockly editor with two blocks on the workspace and the developer tools +open.](tour-dev-tools.png) + +Using the element inspector, explore the elements used by Blockly. For example, +see if you can find the SVG `` element used to draw the `count with` +block. If you're having trouble finding things, the following outline might +help. (Note that it omits some elements and most attributes.) + +``` +Description DOM elements +------------------------------- ---------------------------------------------- + element + App-specific container
+ Injection element
+ Toolbox
+ Main SVG element + Main workspace + Trash + Block canvas + Block 1 + element + Child block 1 + Child block 2 + ... ... + Field 1 + Field 2 + ... ... + Block 2 + Bubble canvas + Scrollbar background + Scrollbars, flyouts, etc. s + Widget, dropdown, tooltip divs
s +``` + +One thing that's important to notice are the `blocklyXxxx` classes assigned to +most elements. These explain how Blockly uses each element and will be the +targets of many of your CSS rules. If you look closely, you'll notice that many +elements have multiple classes -- for example, the `` element for the +dropdown field in the `counts with` block has `blocklyField`, +`blocklyDropdownField`, `blocklyVariableField`, and `blocklyEditableField` +classes. Having multiple classes allows you to write CSS rules that affect +different ranges of elements. + +## Components + +In this section, you will create CSS rules to assign the colours used by the +[components section](https://blocklycodelabs.dev/codelabs/theme-extension-identifier/index.html#3) +of the themes codelab to various components. + +To start, create a file named `halloween.css` and add it to your `index.html` +file: + +```html + ... + + +``` + +### Main workspace colour + +Your first rule will set the background colour of the main workspace. In your +Blockly editor, find the `` element with the `blocklyMainBackground` +class: + +``` + +
+
+ + + +``` + +This seems like a good target for your rule, except that the `fill` property is +already used to set the grid pattern. Instead, we'll set the `background-color` +property of the `blocklySvg` element. To do this, add the following rule to +`halloween.css`: + +```css +/**************/ +/* COMPONENTS */ +/**************/ + +.blocklySvg { + background-color: #ff7518; +} +``` + +Refresh your page and notice that the workspace background is now orange: + +![A Blockly editor with an orange workspace.](components-workspace.png) + +### Mutator workspace colour + +Now drag an `if do` block onto the workspace and click the mutator (gear) icon. +Notice that the mutator workspace's background colour is unchanged. This is +because it's a different workspace. See if you can find the `` that draws +the mutator workspace. (Here's a hint: It's on the bubble canvas, which is where +the bubbles used by mutators, comments, and warnings are drawn.) + +Don't worry if you didn't find it right away -- Blockly's DOM tree is fairly +complex: + +``` + +
+
+ + + + + + + + +``` + +Next, add a CSS rule to set the workspace's background colour: + +```css +.blocklyMutatorBackground { + fill: #ff7518; +} +``` + +To see your new colours, reload the web page, drag the `if do` block out again, +and reopen the mutator. You should see that the mutator workspace background +colour is orange, matching the main workspace background: + +![An "if do" block with a mutator that has an orange background.](components-mutator.png) + +### Other component colours + +Themes allow you to define the colours of many (but not all) components. The +following table shows what classes and properties to use to set the same colours +as the component styles in themes: + +| Component style | Selectors (properties) | +| --------------------------- | ---------------------------------------------------------------------------------------------------- | +| `workspaceBackgroundColour` | `.blocklySvg (background-color)`, `.blocklyMutatorBackground (fill)` | +| `toolboxBackgroundColour` | `.blocklyToolbox (background-color)` | +| `toolboxForegroundColour` | `.blocklyToolbox (color)` | +| `flyoutBackgroundColour` | `.blocklyFlyoutBackground (fill)` | +| `flyoutForegroundColour` | `.blocklyFlyoutLabel > .blocklyFlyoutLabelText (fill)`, `.blocklyFlyoutButton > .blocklyText (fill)` | +| `flyoutOpacity` | `.blocklyFlyoutBackground (fill-opacity)` | +| `scrollbarColour` | `.blocklyScrollbarHandle (fill)` | +| `scrollbarOpacity` | `.blocklyScrollbarHandle (fill-opacity)` | +| `insertionMarkerColour` | `.blocklyInsertionMarker > .blocklyPath (fill)` | +| `insertionMarkerOpacity` | `.blocklyInsertionMarker > .blocklyPath (fill-opacity)` | + +Add the following rules to `halloween.css`: + +```css +.blocklyToolbox { + background-color: #f9c10e; + color: #fff; +} + +.blocklyFlyoutBackground { + fill: #252526; + fill-opacity: 1; +} + +.blocklyFlyoutLabel > .blocklyFlyoutLabelText { + fill: #ccc !important; +} + +.blocklyFlyoutButton > .blocklyText { + fill: #ccc !important; +} + +.blocklyScrollbarHandle { + fill: #ff0000; + fill-opacity: 0.4; +} + +.blocklyInsertionMarker > .blocklyPath { + fill: #fff !important; + fill-opacity: 0.3 !important; + stroke: none; +} +``` + +Now reload your page. You should see a Blockly editor with a yellow toolbox and +red scrollbars: + +![A Blockly editor with Halloween colors.](components-halloween.png) + +### fill vs background-color + +You might have noticed that some rules use `background-color` and `color` and +others use `fill` and `stroke`. This is because `background-color` and `color` +apply to HTML elements, like the `
` used by the toolbox, and `fill` and +`stroke` apply to most SVG elements, like the `` used by the flyout +background. (An exception to this is the top-level 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. + +* **Presentation attributes:** These are attributes on SVG elements, such as + `fill` and `stroke`. They have a specificity of 0 and are overridden by any + rules you write. As you will see later, block colours use presentation + attributes. + +* **`` tag. Your rules override these + if they have the same or higher specificity. For example, the rule for + `.blocklyScrollbarHandle` has the same specificity as Blockly's rule for + this class, but overrides Blockly's rule because it occurs later in the + document. On the other hand, the rule for + `.blocklyFlyoutLabel > .blocklyFlyoutLabelText` has a lower specificity than + Blockly's rule and must override it with an `!important` declaration. + +* **Inline styles:** These rules are included via a `style` attribute and can + only be overridden by an `!important` declaration. As you will see later, + the colour of the arrow in a dropdown field is set with an inline style and + must be overridden with `!important`. + +The easiest way to determine how a rule is set is to highlight the appropriate +element in the element inspector and look at the corresponding style +information. In a few cases, this isn't possible. For example, an insertion +marker is created only when you drag a child near its parent and is deleted when +you let go of the parent to highlight the insertion marker's element. In these +cases, you will need to +[search Blockly's rules](https://developers.google.com/blockly/guides/configure/web/appearance/css#blockly_css_rules). + +## Toolbox categories + +In this section, you will create CSS rules to assign the colours used by the +[categories section](https://blocklycodelabs.dev/codelabs/theme-extension-identifier/index.html#4) +of the themes codelab to the toolbox's categories. + +### Identify the category element + +Your first rule will set the colour of the **Logic** category. This rule needs +to uniquely identify the element used by the **Logic** category, so open the +developer tools and find the `blocklyToolboxCategory` `
` for the **Logic** +category: + +``` + +
+
+
+
+
+
+``` + +Unfortunately, the only thing that distinguishes this `
` from other +category `
`s is a generated `id` attribute (`blockly-1`). This isn't stable +enough to use in a CSS rule -- for example, if you switched the order of two +categories you'd also have to switch the selectors in their rules. + +To solve this problem, you'll need to add a class to the +`blocklyToolboxCategory` `
` for the **Logic** category. Open the +`toolbox.js` file and find the definition of the **Logic** category: + +```js + { + kind: 'category', + name: 'Logic', + categorystyle: 'logic_category', + contents: [...], + }, +``` + +The `categorystyle` property assigns a style that is used by a theme. Because +you're not using themes to assign category colours, you don't need the +`categorystyle` property. Delete it and add a `cssConfig` property that adds two +classes to the **Logic** category's `
`: `logic_category` uniquely +identifies the `
` and `blocklyToolboxCategory` is used by Blockly's CSS to +define rules that apply to all categories. + +```js + { + kind: 'category', + name: 'Logic', + cssConfig: { + row: 'blocklyToolboxCategory logic_category', + }, + contents: [...], + }, +``` + +For a complete explanation of how `cssConfig` works, see +[Custom CSS classes](https://developers.google.com/blockly/guides/configure/web/toolboxes/appearance#custom_css_classes) +in the toolbox documentation. + +### Add your rules + +Next, add the following rules, which set the row colour and its colour when +selected: + +```css +/**************/ +/* CATEGORIES */ +/**************/ + +.logic_category { + border-left: 8px solid #8b4513; +} + +.logic_category.blocklyToolboxSelected { + background-color: #8b4513 !important; +} +``` + +Refresh your web page and click the **Logic** category. The row is highlighted +with your new colour: + +![A Blockly editor with the Logic category open. The category name is written in +white on a brown background.](categories-logic.png) + +### Update the other categories + +Before you can write rules for the remaining categories, you need to replace +`categorystyle` with `cssConfig` in each of their definitions: + +```js + { + kind: 'category', + name: 'Loops', + cssConfig: { + row: 'blocklyToolboxCategory loop_category', + }, + contents: [...], + }, + + // Repeat for remaining categories +``` + +Next, add the following rules to `halloween.css`. These rules use the colours +from themes codelab for the **Loops**, **Text**, and **Lists** categories and +the colours from the Classic theme (the default theme) for the **Math**, +**Variables**, and **Functions** categories. + +```css +.loop_category { + border-left: 8px solid #85e21f; +} + +.loop_category.blocklyToolboxSelected { + background-color: #85e21f !important; +} + +.math_category { + border-left: 8px solid #5b67a5; +} + +.math_category.blocklyToolboxSelected { + background-color: #5b67a5 !important; +} + +.text_category { + border-left: 8px solid #fe9b13; +} + +.text_category.blocklyToolboxSelected { + background-color: #fe9b13 !important; +} + +.list_category { + border-left: 8px solid #4a148c; +} + +.list_category.blocklyToolboxSelected { + background-color: #4a148c !important; +} + +.variable_category { + border-left: 8px solid #a55b80; +} + +.variable_category.blocklyToolboxSelected { + background-color: #a55b80 !important; +} + +.procedure_category { + border-left: 8px solid #b88cc0; +} + +.procedure_category.blocklyToolboxSelected { + background-color: #b88cc0 !important; +} +``` + +Refresh your web page. You should see the new colours beside each category: + +![A Blockly editor with Halloween colours next to each category.](categories-halloween.png) + + +## Blocks + +In this section, you will create CSS rules to assign the colours used by the +[blocks section](https://blocklycodelabs.dev/codelabs/theme-extension-identifier/index.html#5) +of the themes codelab to the logic, loops, text, and lists blocks. This is a bit +more complex than setting component or category colours and you'll do it in +several steps. + +### Block fill and stroke + +Your first step is to set the `fill` and `stroke` of the logic blocks. + +Note that setting the `fill` and `stroke` is specific to the +[renderer](https://developers.google.com/blockly/guides/create-custom-blocks/renderers/overview), +you are using. (In this codelab, you are using the Thrasos renderer.) An +important consequence of this is that you need different CSS for different +renderers. + +#### Identify the block element + +Drag an `if do` block onto the workspace and find it with the element inspector: + +``` + +
+
+ + + + +``` + +Notice that the block's `` element has classes for the block's type +(`controls_if`) and style (`logic_blocks`). These are the values of the +[`type` and `style` properties in the block's definition](https://github.com/RaspberryPiFoundation/blockly/blob/1c280d10cc1dcad7d50a1678211871058d4e9cfb/blocks/logic.ts#L50). +You will use the style class to assign the same colour to all of the logic +blocks. + +(If you were building blocks from scratch and wanted to avoid themes, you would +assign this class with the `classes` property in the block definition. However, +because these are standard blocks and they were built with themes in mind, using +the style class works just as well.) + +#### Choose an element to use + +Next, you need to decide what element to use in your colour rule. The `` +element identifies the block but doesn't draw it. Instead, you can use the `` +element's first child. This is a `` element with `fill` and `stroke` +presentation attributes, which are easily overridden. + +Note that different renderers use different numbers of `` elements to +draw a block: Thrasos uses a single `` element, Geras uses three `` +elements, and Zelos uses one `` for the outside of the block and one +`` 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: + +``` + 'logic_blocks': { + 'colourPrimary': "#8b4513", + 'colourSecondary':"#ff0000", + 'colourTertiary':"#c5eaff" + }, +``` + +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](https://developers.google.com/blockly/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: + +```css +/**********/ +/* BLOCKS */ +/**********/ + +/* LOGIC BLOCKS */ + +.logic_blocks > .blocklyPath { + fill: #8b4513; + stroke: #c5eaff; +} + +.logic_blocks.blocklyShadow > .blocklyPath { + fill: #ff0000; + stroke: none; +} +``` + +Refresh your web page and open the **Logic** category. You should see that the +logic blocks are now rendered in autumnal brown instead of blue: + +![A Blockly editor with the Logic category open. The blocks in the flyout are +brown.](blocks-logic.png) + +### Disabled blocks + +Your next step is to handle disabled blocks. Drag an `if do` block and any block +from the **Loops** category onto the workspace. Right-click on each block and +disable it using the context menu. Notice that the loop block has a cross-hatch +pattern while the `if do` block does not: + +![An "if do" block and a "repeat 10 times" block, both of which are disabled. +The "if do" block is solid brown and the "repeat 10 times" block has a +cross-hatch pattern.](blocks-disabled-wrong.png) + +This is because the rules you just added have the same specificity as the +Blockly rules that set the cross-hatch pattern. (You can see this if you click +on the `if do` block's `` element and inspect its styles.) Because your +rules occur later in the document, they take precedence. To use the standard +CSS for disabled blocks, add a `:not(.blocklyDisabledPattern)` to your rules: + +```css +/* LOGIC BLOCKS */ + +.logic_blocks:not(.blocklyDisabledPattern) > .blocklyPath { + fill: #8b4513; + stroke: #c5eaff; +} + +.logic_blocks:not(.blocklyDisabledPattern).blocklyShadow > .blocklyPath { + fill: #ff0000; + stroke: none; +} +``` + +Refresh your page, drag the `if do` block onto the workspace, and disable it. It +should now use the disabled pattern: + +![A disabled "if do" block with a cross-hatch pattern.](blocks-disabled-right.png) + +### Dropdown arrows + +You now need to handle dropdown arrows. Drag a logic comparison block onto the +workspace and look closely at the inverted triangle in the dropdown field -- +it's blue even though the rest of the block is brown: + +![A brown logic comparison block with a blue dropdown arrow.](blocks-arrow-wrong.png) + +If you look at the triangle with the element inspector, you'll see that it's a +character in an SVG `` element: + +``` + +
+
+ + + + + + + +``` + +You can also see that its colour is set with a `style` attribute, which can only +be overridden with an `!important` declaration. To do this, add the following +rules: + +```css +.logic_blocks > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #8b4513 !important; +} + +.logic_blocks.blocklyShadow > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #ff0000 !important; +} +``` + +Reload your page and drag the comparison block out again. The arrow should be +the same colour as the rest of the block: + +![A brown logic comparison block with a brown dropdown arrow.](blocks-arrow-right.png) + +### Loop, text, and list blocks + +Your last step is to add similar rules for the loop, text, and list blocks: + +```css +/* LOOP BLOCKS */ + +.loop_blocks:not(.blocklyDisabledPattern) > .blocklyPath { + fill: #85e21f; + stroke: #c5eaff; +} + +.loop_blocks:not(.blocklyDisabledPattern).blocklyShadow > .blocklyPath { + fill: #ff0000; + stroke: none; +} + +.loop_blocks > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #85e21f !important; +} + +.loop_blocks.blocklyShadow > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #ff0000 !important; +} + +/* TEXT BLOCKS */ + +.text_blocks:not(.blocklyDisabledPattern) > .blocklyPath { + fill: #fe9b13; + stroke: #c5eaff; +} + +.text_blocks:not(.blocklyDisabledPattern).blocklyShadow > .blocklyPath { + fill: #ff0000; + stroke: none; +} + +.text_blocks > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #fe9b13 !important; +} + +.text_blocks.blocklyShadow > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #ff0000 !important; +} + +/* LIST BLOCKS */ + +.list_blocks:not(.blocklyDisabledPattern) > .blocklyPath { + fill: #4a148c; + stroke: #cdb6e9; +} + +.list_blocks:not(.blocklyDisabledPattern).blocklyShadow > .blocklyPath { + fill: #ad7Be9; + stroke: none; +} + +.list_blocks > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #4a148c !important; +} + +.list_blocks.blocklyShadow > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #ad7be9 !important; +} +``` + +And that's it! Reload your page and explore the blocks in your Halloween-themed +editor: + +![A Blockly editor with Halloween colors: a yellow toolbox, orange workspace, +red scrollbars, and neon green loop blocks.](blocks-loops.png) + +## Summary + +In this codelab, you learned how to use CSS to set the colours of your Blockly +editor. + +For more information, see +[Style with CSS](https://developers.google.com/blockly/guides/configure/web/appearance/css) +in the Blockly user guides. + diff --git a/codelabs/css/setup-starter.png b/codelabs/css/setup-starter.png new file mode 100644 index 000000000..ff7b2b16f Binary files /dev/null and b/codelabs/css/setup-starter.png differ diff --git a/codelabs/css/tour-dev-tools.png b/codelabs/css/tour-dev-tools.png new file mode 100644 index 000000000..ad8241e5d Binary files /dev/null and b/codelabs/css/tour-dev-tools.png differ diff --git a/eslint.config.js b/eslint.config.js index 4ce99166d..0d4e2b8d9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -26,6 +26,7 @@ module.exports = [ 'examples/blockly-rtc', 'examples/blockly-vue3', 'examples/context-menu-codelab', + 'examples/css-codelab', 'examples/custom-dialogs-demo', 'examples/custom-toolbox-codelab', 'examples/devsite-demo', diff --git a/examples/css-codelab/complete-code/halloween.css b/examples/css-codelab/complete-code/halloween.css new file mode 100644 index 000000000..052cf0dde --- /dev/null +++ b/examples/css-codelab/complete-code/halloween.css @@ -0,0 +1,184 @@ +/**************/ +/* COMPONENTS */ +/**************/ + +.blocklySvg { + background-color: #ff7518; +} + +.blocklyMutatorBackground { + fill: #ff7518; +} + +.blocklyToolbox { + background-color: #f9c10e; + color: #fff; +} + +.blocklyFlyoutBackground { + fill: #252526; + fill-opacity: 1; +} + +.blocklyFlyoutLabel > .blocklyFlyoutLabelText { + fill: #ccc !important; +} + +.blocklyFlyoutButton > .blocklyText { + fill: #ccc !important; +} + +.blocklyScrollbarHandle { + fill: #ff0000; + fill-opacity: 0.4; +} + +.blocklyInsertionMarker > .blocklyPath { + fill: #fff !important; + fill-opacity: 0.3 !important; + stroke: none; +} + +/**************/ +/* CATEGORIES */ +/**************/ + +.logic_category { + border-left: 8px solid #8b4513; +} + +.logic_category.blocklyToolboxSelected { + background-color: #8b4513 !important; +} + +.loop_category { + border-left: 8px solid #85e21f; +} + +.loop_category.blocklyToolboxSelected { + background-color: #85e21f !important; +} + +.math_category { + border-left: 8px solid #5b67a5; +} + +.math_category.blocklyToolboxSelected { + background-color: #5b67a5 !important; +} + +.text_category { + border-left: 8px solid #fe9b13; +} + +.text_category.blocklyToolboxSelected { + background-color: #fe9b13 !important; +} + +.list_category { + border-left: 8px solid #4a148c; +} + +.list_category.blocklyToolboxSelected { + background-color: #4a148c !important; +} + +.variable_category { + border-left: 8px solid #a55b80; +} + +.variable_category.blocklyToolboxSelected { + background-color: #a55b80 !important; +} + +.procedure_category { + border-left: 8px solid #b88cc0; +} + +.procedure_category.blocklyToolboxSelected { + background-color: #b88cc0 !important; +} + +/**********/ +/* BLOCKS */ +/**********/ + +/* LOGIC BLOCKS */ + +.logic_blocks:not(.blocklyDisabledPattern) > .blocklyPath { + fill: #8b4513; + stroke: #c5eaff; +} + +.logic_blocks:not(.blocklyDisabledPattern).blocklyShadow > .blocklyPath { + fill: #ff0000; + stroke: none; +} + +.logic_blocks > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #8b4513 !important; +} + +.logic_blocks.blocklyShadow > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #ff0000 !important; +} + +/* LOOP BLOCKS */ + +.loop_blocks:not(.blocklyDisabledPattern) > .blocklyPath { + fill: #85e21f; + stroke: #c5eaff; +} + +.loop_blocks:not(.blocklyDisabledPattern).blocklyShadow > .blocklyPath { + fill: #ff0000; + stroke: none; +} + +.loop_blocks > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #85e21f !important; +} + +.loop_blocks.blocklyShadow > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #ff0000 !important; +} + +/* TEXT BLOCKS */ + +.text_blocks:not(.blocklyDisabledPattern) > .blocklyPath { + fill: #fe9b13; + stroke: #c5eaff; +} + +.text_blocks:not(.blocklyDisabledPattern).blocklyShadow > .blocklyPath { + fill: #ff0000; + stroke: none; +} + +.text_blocks > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #fe9b13 !important; +} + +.text_blocks.blocklyShadow > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #ff0000 !important; +} + +/* LIST BLOCKS */ + +.list_blocks:not(.blocklyDisabledPattern) > .blocklyPath { + fill: #4a148c; + stroke: #cdb6e9; +} + +.list_blocks:not(.blocklyDisabledPattern).blocklyShadow > .blocklyPath { + fill: #ad7be9; + stroke: none; +} + +.list_blocks > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #4a148c !important; +} + +.list_blocks.blocklyShadow > .blocklyDropdownField .blocklyDropdownText tspan { + fill: #ad7be9 !important; +} diff --git a/examples/css-codelab/complete-code/index.html b/examples/css-codelab/complete-code/index.html new file mode 100644 index 000000000..1b77752f1 --- /dev/null +++ b/examples/css-codelab/complete-code/index.html @@ -0,0 +1,41 @@ + + + + + CSS Codelab + + + + + + + + + +

CSS Codelab

+
+ + diff --git a/examples/css-codelab/complete-code/index.js b/examples/css-codelab/complete-code/index.js new file mode 100644 index 000000000..294d45869 --- /dev/null +++ b/examples/css-codelab/complete-code/index.js @@ -0,0 +1,11 @@ +'use strict'; + +let workspace = null; + +function start() { + // Create main workspace. + workspace = Blockly.inject('blocklyDiv', { + toolbox: toolboxCategories, + renderer: 'thrasos', + }); +} diff --git a/examples/css-codelab/complete-code/toolbox.js b/examples/css-codelab/complete-code/toolbox.js new file mode 100644 index 000000000..f2e48271a --- /dev/null +++ b/examples/css-codelab/complete-code/toolbox.js @@ -0,0 +1,800 @@ +const toolboxCategories = { + kind: 'categoryToolbox', + contents: [ + { + kind: 'category', + name: 'Logic', + cssConfig: { + row: 'blocklyToolboxCategory logic_category', + }, + contents: [ + { + type: 'controls_if', + kind: 'block', + }, + { + type: 'logic_compare', + kind: 'block', + fields: { + OP: 'EQ', + }, + }, + { + type: 'logic_operation', + kind: 'block', + fields: { + OP: 'AND', + }, + }, + { + type: 'logic_negate', + kind: 'block', + }, + { + type: 'logic_boolean', + kind: 'block', + fields: { + BOOL: 'TRUE', + }, + }, + { + type: 'logic_null', + kind: 'block', + enabled: false, + }, + { + type: 'logic_ternary', + kind: 'block', + }, + ], + }, + { + kind: 'category', + name: 'Loops', + cssConfig: { + row: 'blocklyToolboxCategory loop_category', + }, + contents: [ + { + type: 'controls_repeat_ext', + kind: 'block', + inputs: { + TIMES: { + shadow: { + type: 'math_number', + fields: { + NUM: 10, + }, + }, + }, + }, + }, + { + type: 'controls_repeat', + kind: 'block', + enabled: false, + fields: { + TIMES: 10, + }, + }, + { + type: 'controls_whileUntil', + kind: 'block', + fields: { + MODE: 'WHILE', + }, + }, + { + type: 'controls_for', + kind: 'block', + fields: { + VAR: { + name: 'i', + }, + }, + inputs: { + FROM: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + TO: { + shadow: { + type: 'math_number', + fields: { + NUM: 10, + }, + }, + }, + BY: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + }, + }, + { + type: 'controls_forEach', + kind: 'block', + fields: { + VAR: { + name: 'j', + }, + }, + }, + { + type: 'controls_flow_statements', + kind: 'block', + enabled: false, + fields: { + FLOW: 'BREAK', + }, + }, + ], + }, + { + kind: 'category', + name: 'Math', + cssConfig: { + row: 'blocklyToolboxCategory math_category', + }, + contents: [ + { + type: 'math_number', + kind: 'block', + fields: { + NUM: 123, + }, + }, + { + type: 'math_arithmetic', + kind: 'block', + fields: { + OP: 'ADD', + }, + inputs: { + A: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + B: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + }, + }, + { + type: 'math_single', + kind: 'block', + fields: { + OP: 'ROOT', + }, + inputs: { + NUM: { + shadow: { + type: 'math_number', + fields: { + NUM: 9, + }, + }, + }, + }, + }, + { + type: 'math_trig', + kind: 'block', + fields: { + OP: 'SIN', + }, + inputs: { + NUM: { + shadow: { + type: 'math_number', + fields: { + NUM: 45, + }, + }, + }, + }, + }, + { + type: 'math_constant', + kind: 'block', + fields: { + CONSTANT: 'PI', + }, + }, + { + type: 'math_number_property', + kind: 'block', + fields: { + PROPERTY: 'EVEN', + }, + inputs: { + NUMBER_TO_CHECK: { + shadow: { + type: 'math_number', + fields: { + NUM: 0, + }, + }, + }, + }, + }, + { + type: 'math_round', + kind: 'block', + fields: { + OP: 'ROUND', + }, + inputs: { + NUM: { + shadow: { + type: 'math_number', + fields: { + NUM: 3.1, + }, + }, + }, + }, + }, + { + type: 'math_on_list', + kind: 'block', + fields: { + OP: 'SUM', + }, + }, + { + type: 'math_modulo', + kind: 'block', + inputs: { + DIVIDEND: { + shadow: { + type: 'math_number', + fields: { + NUM: 64, + }, + }, + }, + DIVISOR: { + shadow: { + type: 'math_number', + fields: { + NUM: 10, + }, + }, + }, + }, + }, + { + type: 'math_constrain', + kind: 'block', + inputs: { + VALUE: { + shadow: { + type: 'math_number', + fields: { + NUM: 50, + }, + }, + }, + LOW: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + HIGH: { + shadow: { + type: 'math_number', + fields: { + NUM: 100, + }, + }, + }, + }, + }, + { + type: 'math_random_int', + kind: 'block', + inputs: { + FROM: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + TO: { + shadow: { + type: 'math_number', + fields: { + NUM: 100, + }, + }, + }, + }, + }, + { + type: 'math_random_float', + kind: 'block', + }, + { + type: 'math_atan2', + kind: 'block', + inputs: { + X: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + Y: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + }, + }, + ], + }, + { + kind: 'category', + name: 'Text', + cssConfig: { + row: 'blocklyToolboxCategory text_category', + }, + contents: [ + { + type: 'text', + kind: 'block', + fields: { + TEXT: '', + }, + }, + { + type: 'text_join', + kind: 'block', + }, + { + type: 'text_append', + kind: 'block', + fields: { + name: 'item', + }, + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + { + type: 'text_length', + kind: 'block', + inputs: { + VALUE: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_isEmpty', + kind: 'block', + inputs: { + VALUE: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + { + type: 'text_indexOf', + kind: 'block', + fields: { + END: 'FIRST', + }, + inputs: { + VALUE: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'text', + }, + }, + }, + }, + FIND: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_charAt', + kind: 'block', + fields: { + WHERE: 'FROM_START', + }, + inputs: { + VALUE: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'text', + }, + }, + }, + }, + }, + }, + { + type: 'text_getSubstring', + kind: 'block', + fields: { + WHERE1: 'FROM_START', + WHERE2: 'FROM_START', + }, + inputs: { + STRING: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'text', + }, + }, + }, + }, + }, + }, + { + type: 'text_changeCase', + kind: 'block', + fields: { + CASE: 'UPPERCASE', + }, + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_trim', + kind: 'block', + fields: { + MODE: 'BOTH', + }, + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_count', + kind: 'block', + inputs: { + SUB: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + { + type: 'text_replace', + kind: 'block', + inputs: { + FROM: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + TO: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + { + type: 'text_reverse', + kind: 'block', + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + + { + type: 'text_print', + kind: 'block', + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_prompt_ext', + kind: 'block', + fields: { + TYPE: 'TEXT', + }, + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + ], + }, + { + kind: 'category', + name: 'Lists', + cssConfig: { + row: 'blocklyToolboxCategory list_category', + }, + contents: [ + { + type: 'lists_create_with', + kind: 'block', + extraState: { + itemCount: 0, + }, + }, + { + type: 'lists_create_with', + kind: 'block', + }, + { + type: 'lists_repeat', + kind: 'block', + inputs: { + NUM: { + shadow: { + type: 'math_number', + fields: { + NUM: 5, + }, + }, + }, + }, + }, + { + type: 'lists_length', + kind: 'block', + }, + { + type: 'lists_isEmpty', + kind: 'block', + }, + { + type: 'lists_indexOf', + kind: 'block', + + fields: { + END: 'FIRST', + }, + inputs: { + VALUE: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'list', + }, + }, + }, + }, + }, + }, + { + type: 'lists_getIndex', + kind: 'block', + fields: { + MODE: 'GET', + WHERE: 'FROM_START', + }, + inputs: { + VALUE: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'list', + }, + }, + }, + }, + }, + }, + { + type: 'lists_setIndex', + kind: 'block', + fields: { + MODE: 'SET', + WHERE: 'FROM_START', + }, + inputs: { + LIST: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'list', + }, + }, + }, + }, + }, + }, + { + type: 'lists_getSublist', + kind: 'block', + fields: { + WHERE1: 'FROM_START', + WHERE2: 'FROM_START', + }, + inputs: { + LIST: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'list', + }, + }, + }, + }, + }, + }, + { + type: 'lists_split', + kind: 'block', + + fields: { + MODE: 'SPLIT', + }, + inputs: { + DELIM: { + shadow: { + type: 'text', + fields: { + TEXT: ',', + }, + }, + }, + }, + }, + { + type: 'lists_sort', + kind: 'block', + + fields: { + TYPE: 'NUMERIC', + DIRECTION: '1', + }, + }, + { + type: 'lists_reverse', + kind: 'block', + }, + ], + }, + { + kind: 'sep', + }, + { + kind: 'category', + name: 'Variables', + custom: 'VARIABLE', + cssConfig: { + row: 'blocklyToolboxCategory variable_category', + }, + }, + { + kind: 'category', + name: 'Functions', + custom: 'PROCEDURE', + cssConfig: { + row: 'blocklyToolboxCategory procedure_category', + }, + }, + ], +}; diff --git a/examples/css-codelab/starter-code/index.html b/examples/css-codelab/starter-code/index.html new file mode 100644 index 000000000..7d0907387 --- /dev/null +++ b/examples/css-codelab/starter-code/index.html @@ -0,0 +1,40 @@ + + + + + CSS Codelab + + + + + + + + +

CSS Codelab

+
+ + diff --git a/examples/css-codelab/starter-code/index.js b/examples/css-codelab/starter-code/index.js new file mode 100644 index 000000000..294d45869 --- /dev/null +++ b/examples/css-codelab/starter-code/index.js @@ -0,0 +1,11 @@ +'use strict'; + +let workspace = null; + +function start() { + // Create main workspace. + workspace = Blockly.inject('blocklyDiv', { + toolbox: toolboxCategories, + renderer: 'thrasos', + }); +} diff --git a/examples/css-codelab/starter-code/toolbox.js b/examples/css-codelab/starter-code/toolbox.js new file mode 100644 index 000000000..2c10d89dd --- /dev/null +++ b/examples/css-codelab/starter-code/toolbox.js @@ -0,0 +1,786 @@ +const toolboxCategories = { + kind: 'categoryToolbox', + contents: [ + { + kind: 'category', + name: 'Logic', + categorystyle: 'logic_category', + contents: [ + { + type: 'controls_if', + kind: 'block', + }, + { + type: 'logic_compare', + kind: 'block', + fields: { + OP: 'EQ', + }, + }, + { + type: 'logic_operation', + kind: 'block', + fields: { + OP: 'AND', + }, + }, + { + type: 'logic_negate', + kind: 'block', + }, + { + type: 'logic_boolean', + kind: 'block', + fields: { + BOOL: 'TRUE', + }, + }, + { + type: 'logic_null', + kind: 'block', + enabled: false, + }, + { + type: 'logic_ternary', + kind: 'block', + }, + ], + }, + { + kind: 'category', + name: 'Loops', + categorystyle: 'loop_category', + contents: [ + { + type: 'controls_repeat_ext', + kind: 'block', + inputs: { + TIMES: { + shadow: { + type: 'math_number', + fields: { + NUM: 10, + }, + }, + }, + }, + }, + { + type: 'controls_repeat', + kind: 'block', + enabled: false, + fields: { + TIMES: 10, + }, + }, + { + type: 'controls_whileUntil', + kind: 'block', + fields: { + MODE: 'WHILE', + }, + }, + { + type: 'controls_for', + kind: 'block', + fields: { + VAR: { + name: 'i', + }, + }, + inputs: { + FROM: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + TO: { + shadow: { + type: 'math_number', + fields: { + NUM: 10, + }, + }, + }, + BY: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + }, + }, + { + type: 'controls_forEach', + kind: 'block', + fields: { + VAR: { + name: 'j', + }, + }, + }, + { + type: 'controls_flow_statements', + kind: 'block', + enabled: false, + fields: { + FLOW: 'BREAK', + }, + }, + ], + }, + { + kind: 'category', + name: 'Math', + categorystyle: 'math_category', + contents: [ + { + type: 'math_number', + kind: 'block', + fields: { + NUM: 123, + }, + }, + { + type: 'math_arithmetic', + kind: 'block', + fields: { + OP: 'ADD', + }, + inputs: { + A: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + B: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + }, + }, + { + type: 'math_single', + kind: 'block', + fields: { + OP: 'ROOT', + }, + inputs: { + NUM: { + shadow: { + type: 'math_number', + fields: { + NUM: 9, + }, + }, + }, + }, + }, + { + type: 'math_trig', + kind: 'block', + fields: { + OP: 'SIN', + }, + inputs: { + NUM: { + shadow: { + type: 'math_number', + fields: { + NUM: 45, + }, + }, + }, + }, + }, + { + type: 'math_constant', + kind: 'block', + fields: { + CONSTANT: 'PI', + }, + }, + { + type: 'math_number_property', + kind: 'block', + fields: { + PROPERTY: 'EVEN', + }, + inputs: { + NUMBER_TO_CHECK: { + shadow: { + type: 'math_number', + fields: { + NUM: 0, + }, + }, + }, + }, + }, + { + type: 'math_round', + kind: 'block', + fields: { + OP: 'ROUND', + }, + inputs: { + NUM: { + shadow: { + type: 'math_number', + fields: { + NUM: 3.1, + }, + }, + }, + }, + }, + { + type: 'math_on_list', + kind: 'block', + fields: { + OP: 'SUM', + }, + }, + { + type: 'math_modulo', + kind: 'block', + inputs: { + DIVIDEND: { + shadow: { + type: 'math_number', + fields: { + NUM: 64, + }, + }, + }, + DIVISOR: { + shadow: { + type: 'math_number', + fields: { + NUM: 10, + }, + }, + }, + }, + }, + { + type: 'math_constrain', + kind: 'block', + inputs: { + VALUE: { + shadow: { + type: 'math_number', + fields: { + NUM: 50, + }, + }, + }, + LOW: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + HIGH: { + shadow: { + type: 'math_number', + fields: { + NUM: 100, + }, + }, + }, + }, + }, + { + type: 'math_random_int', + kind: 'block', + inputs: { + FROM: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + TO: { + shadow: { + type: 'math_number', + fields: { + NUM: 100, + }, + }, + }, + }, + }, + { + type: 'math_random_float', + kind: 'block', + }, + { + type: 'math_atan2', + kind: 'block', + inputs: { + X: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + Y: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + }, + }, + ], + }, + { + kind: 'category', + name: 'Text', + categorystyle: 'text_category', + contents: [ + { + type: 'text', + kind: 'block', + fields: { + TEXT: '', + }, + }, + { + type: 'text_join', + kind: 'block', + }, + { + type: 'text_append', + kind: 'block', + fields: { + name: 'item', + }, + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + { + type: 'text_length', + kind: 'block', + inputs: { + VALUE: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_isEmpty', + kind: 'block', + inputs: { + VALUE: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + { + type: 'text_indexOf', + kind: 'block', + fields: { + END: 'FIRST', + }, + inputs: { + VALUE: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'text', + }, + }, + }, + }, + FIND: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_charAt', + kind: 'block', + fields: { + WHERE: 'FROM_START', + }, + inputs: { + VALUE: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'text', + }, + }, + }, + }, + }, + }, + { + type: 'text_getSubstring', + kind: 'block', + fields: { + WHERE1: 'FROM_START', + WHERE2: 'FROM_START', + }, + inputs: { + STRING: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'text', + }, + }, + }, + }, + }, + }, + { + type: 'text_changeCase', + kind: 'block', + fields: { + CASE: 'UPPERCASE', + }, + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_trim', + kind: 'block', + fields: { + MODE: 'BOTH', + }, + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_count', + kind: 'block', + inputs: { + SUB: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + { + type: 'text_replace', + kind: 'block', + inputs: { + FROM: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + TO: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + { + type: 'text_reverse', + kind: 'block', + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: '', + }, + }, + }, + }, + }, + + { + type: 'text_print', + kind: 'block', + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + { + type: 'text_prompt_ext', + kind: 'block', + fields: { + TYPE: 'TEXT', + }, + inputs: { + TEXT: { + shadow: { + type: 'text', + fields: { + TEXT: 'abc', + }, + }, + }, + }, + }, + ], + }, + { + kind: 'category', + name: 'Lists', + categorystyle: 'list_category', + contents: [ + { + type: 'lists_create_with', + kind: 'block', + extraState: { + itemCount: 0, + }, + }, + { + type: 'lists_create_with', + kind: 'block', + }, + { + type: 'lists_repeat', + kind: 'block', + inputs: { + NUM: { + shadow: { + type: 'math_number', + fields: { + NUM: 5, + }, + }, + }, + }, + }, + { + type: 'lists_length', + kind: 'block', + }, + { + type: 'lists_isEmpty', + kind: 'block', + }, + { + type: 'lists_indexOf', + kind: 'block', + + fields: { + END: 'FIRST', + }, + inputs: { + VALUE: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'list', + }, + }, + }, + }, + }, + }, + { + type: 'lists_getIndex', + kind: 'block', + fields: { + MODE: 'GET', + WHERE: 'FROM_START', + }, + inputs: { + VALUE: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'list', + }, + }, + }, + }, + }, + }, + { + type: 'lists_setIndex', + kind: 'block', + fields: { + MODE: 'SET', + WHERE: 'FROM_START', + }, + inputs: { + LIST: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'list', + }, + }, + }, + }, + }, + }, + { + type: 'lists_getSublist', + kind: 'block', + fields: { + WHERE1: 'FROM_START', + WHERE2: 'FROM_START', + }, + inputs: { + LIST: { + block: { + type: 'variables_get', + fields: { + VAR: { + name: 'list', + }, + }, + }, + }, + }, + }, + { + type: 'lists_split', + kind: 'block', + + fields: { + MODE: 'SPLIT', + }, + inputs: { + DELIM: { + shadow: { + type: 'text', + fields: { + TEXT: ',', + }, + }, + }, + }, + }, + { + type: 'lists_sort', + kind: 'block', + + fields: { + TYPE: 'NUMERIC', + DIRECTION: '1', + }, + }, + { + type: 'lists_reverse', + kind: 'block', + }, + ], + }, + { + kind: 'sep', + }, + { + kind: 'category', + name: 'Variables', + custom: 'VARIABLE', + categorystyle: 'variable_category', + }, + { + kind: 'category', + name: 'Functions', + custom: 'PROCEDURE', + categorystyle: 'procedure_category', + }, + ], +};