From f3f0181cbb2a9ce770dfe805279be5ad05aa6368 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Mon, 27 Jul 2026 15:04:33 -0700 Subject: [PATCH 1/2] chore: moved field plugins into monorepo --- packages/plugins/field-angle/CHANGELOG.md | 501 ++++++++ packages/plugins/field-angle/README.md | 112 ++ packages/plugins/field-angle/package.json | 55 + .../readme-media/clockwise_false.gif | Bin 0 -> 39421 bytes .../readme-media/clockwise_true.gif | Bin 0 -> 38124 bytes .../field-angle/readme-media/collapsed.png | Bin 0 -> 2033 bytes .../field-angle/readme-media/compass.gif | Bin 0 -> 31504 bytes .../field-angle/readme-media/offset_right.png | Bin 0 -> 6737 bytes .../field-angle/readme-media/offset_top.png | Bin 0 -> 6543 bytes .../field-angle/readme-media/on_block.png | Bin 0 -> 1945 bytes .../field-angle/readme-media/protractor.gif | Bin 0 -> 40593 bytes .../field-angle/readme-media/round_70.gif | Bin 0 -> 114103 bytes .../field-angle/readme-media/validator.gif | Bin 0 -> 60197 bytes .../field-angle/readme-media/with_editor.png | Bin 0 -> 6793 bytes .../plugins/field-angle/readme-media/wrap.gif | Bin 0 -> 54192 bytes .../plugins/field-angle/src/field_angle.ts | 800 +++++++++++++ packages/plugins/field-angle/src/index.ts | 7 + .../test/field_angle_test.mocha.js | 328 ++++++ packages/plugins/field-angle/test/index.html | 18 + packages/plugins/field-angle/test/index.ts | 332 ++++++ packages/plugins/field-angle/tsconfig.json | 16 + packages/plugins/field-bitmap/CHANGELOG.md | 692 +++++++++++ packages/plugins/field-bitmap/README.md | 109 ++ packages/plugins/field-bitmap/package.json | 56 + .../plugins/field-bitmap/src/field-bitmap.ts | 1027 +++++++++++++++++ packages/plugins/field-bitmap/src/index.ts | 7 + .../field-bitmap/test/field_test.mocha.js | 215 ++++ packages/plugins/field-bitmap/test/index.html | 18 + packages/plugins/field-bitmap/test/index.js | 336 ++++++ packages/plugins/field-bitmap/tsconfig.json | 16 + .../field-colour-hsv-sliders/CHANGELOG.md | 673 +++++++++++ .../field-colour-hsv-sliders/README.md | 88 ++ .../field-colour-hsv-sliders/package.json | 60 + .../readme-media/hsv_sliders_screenshot.png | Bin 0 -> 59431 bytes .../src/field_colour_hsv_sliders.ts | 864 ++++++++++++++ .../field-colour-hsv-sliders/src/index.ts | 7 + .../field_colour_hsv_sliders_test.mocha.js | 142 +++ ...d_colour_hsv_sliders_test_helpers.mocha.js | 36 + .../field-colour-hsv-sliders/test/index.html | 18 + .../field-colour-hsv-sliders/test/index.ts | 206 ++++ .../field-colour-hsv-sliders/tsconfig.json | 16 + packages/plugins/field-colour/CHANGELOG.md | 545 +++++++++ packages/plugins/field-colour/README.md | 308 +++++ packages/plugins/field-colour/package.json | 61 + .../field-colour/readme-media/collapsed.png | Bin 0 -> 2140 bytes .../field-colour/readme-media/customized.png | Bin 0 -> 2337 bytes .../field-colour/readme-media/on_block.png | Bin 0 -> 1464 bytes .../field-colour/readme-media/validator.gif | Bin 0 -> 173464 bytes .../field-colour/readme-media/with_editor.png | Bin 0 -> 3870 bytes .../field-colour/src/blocks/colourBlend.ts | 294 +++++ .../field-colour/src/blocks/colourPicker.ts | 141 +++ .../field-colour/src/blocks/colourRandom.ts | 171 +++ .../field-colour/src/blocks/colourRgb.ts | 249 ++++ .../field-colour/src/blocks/generatorsType.ts | 24 + .../plugins/field-colour/src/field_colour.ts | 524 +++++++++ packages/plugins/field-colour/src/index.ts | 33 + .../field-colour/test/blocks_test.mocha.js | 228 ++++ .../test/field_colour_test.mocha.js | 389 +++++++ .../field-colour/test/golden/golden.dart | 65 ++ .../field-colour/test/golden/golden.js | 46 + .../field-colour/test/golden/golden.lua | 35 + .../field-colour/test/golden/golden.php | 47 + .../field-colour/test/golden/golden.py | 32 + packages/plugins/field-colour/test/index.html | 18 + packages/plugins/field-colour/test/index.ts | 274 +++++ packages/plugins/field-colour/tsconfig.json | 17 + packages/plugins/field-date/.gitignore | 24 + packages/plugins/field-date/CHANGELOG.md | 677 +++++++++++ packages/plugins/field-date/README.md | 59 + packages/plugins/field-date/package.json | 57 + packages/plugins/field-date/src/field_date.ts | 286 +++++ packages/plugins/field-date/src/index.ts | 7 + packages/plugins/field-date/src/utils.ts | 20 + .../field-date/test/field_date_test.mocha.js | 267 +++++ packages/plugins/field-date/test/index.html | 18 + packages/plugins/field-date/test/index.ts | 231 ++++ packages/plugins/field-date/tsconfig.json | 16 + .../field-dependent-dropdown/CHANGELOG.md | 500 ++++++++ .../field-dependent-dropdown/README.md | 105 ++ .../field-dependent-dropdown/package.json | 56 + .../src/dependent_dropdown_options_change.ts | 233 ++++ .../src/field_dependent_dropdown.ts | 319 +++++ .../field-dependent-dropdown/src/index.ts | 7 + .../test/field_dependent_dropdown.mocha.js | 302 +++++ .../field_dependent_dropdown_test_block.ts | 130 +++ .../field-dependent-dropdown/test/index.html | 18 + .../field-dependent-dropdown/test/index.ts | 63 + .../field-dependent-dropdown/tsconfig.json | 16 + .../plugins/field-grid-dropdown/CHANGELOG.md | 681 +++++++++++ .../plugins/field-grid-dropdown/README.md | 92 ++ .../plugins/field-grid-dropdown/package.json | 52 + .../readme-media/dropdown-images.png | Bin 0 -> 14349 bytes .../readme-media/dropdown.png | Bin 0 -> 8813 bytes .../plugins/field-grid-dropdown/src/grid.ts | 391 +++++++ .../field-grid-dropdown/src/grid_item.ts | 198 ++++ .../plugins/field-grid-dropdown/src/index.ts | 283 +++++ .../test/field_test.mocha.js | 218 ++++ .../field-grid-dropdown/test/index.html | 18 + .../plugins/field-grid-dropdown/test/index.ts | 468 ++++++++ .../plugins/field-grid-dropdown/tsconfig.json | 16 + .../plugins/field-multilineinput/CHANGELOG.md | 511 ++++++++ .../plugins/field-multilineinput/README.md | 237 ++++ .../plugins/field-multilineinput/package.json | 55 + .../readme-media/collapsed.png | Bin 0 -> 3564 bytes .../readme-media/on_block.png | Bin 0 -> 7365 bytes .../readme-media/spellcheck.gif | Bin 0 -> 255733 bytes .../readme-media/validator.gif | Bin 0 -> 90957 bytes .../readme-media/with_editor.png | Bin 0 -> 6690 bytes .../src/blocks/generatorsType.ts | 24 + .../src/blocks/textMultiline.ts | 169 +++ .../src/field_multilineinput.ts | 781 +++++++++++++ .../plugins/field-multilineinput/src/index.ts | 16 + .../test/block_test.mocha.js | 94 ++ .../test/field_multilineinput_test.mocha.js | 300 +++++ .../test/golden/golden.dart | 4 + .../test/golden/golden.js | 2 + .../test/golden/golden.lua | 2 + .../test/golden/golden.php | 2 + .../test/golden/golden.py | 2 + .../field-multilineinput/test/index.html | 18 + .../field-multilineinput/test/index.ts | 242 ++++ .../field-multilineinput/tsconfig.json | 16 + packages/plugins/field-slider/CHANGELOG.md | 684 +++++++++++ packages/plugins/field-slider/README.md | 59 + packages/plugins/field-slider/package.json | 54 + .../plugins/field-slider/src/field_slider.ts | 326 ++++++ packages/plugins/field-slider/src/index.ts | 7 + .../test/field_slider_test.mocha.js | 404 +++++++ .../test/field_slider_test_helpers.mocha.js | 42 + packages/plugins/field-slider/test/index.html | 18 + packages/plugins/field-slider/test/index.ts | 274 +++++ packages/plugins/field-slider/tsconfig.json | 16 + 132 files changed, 20443 insertions(+) create mode 100644 packages/plugins/field-angle/CHANGELOG.md create mode 100644 packages/plugins/field-angle/README.md create mode 100644 packages/plugins/field-angle/package.json create mode 100644 packages/plugins/field-angle/readme-media/clockwise_false.gif create mode 100644 packages/plugins/field-angle/readme-media/clockwise_true.gif create mode 100644 packages/plugins/field-angle/readme-media/collapsed.png create mode 100644 packages/plugins/field-angle/readme-media/compass.gif create mode 100644 packages/plugins/field-angle/readme-media/offset_right.png create mode 100644 packages/plugins/field-angle/readme-media/offset_top.png create mode 100644 packages/plugins/field-angle/readme-media/on_block.png create mode 100644 packages/plugins/field-angle/readme-media/protractor.gif create mode 100644 packages/plugins/field-angle/readme-media/round_70.gif create mode 100644 packages/plugins/field-angle/readme-media/validator.gif create mode 100644 packages/plugins/field-angle/readme-media/with_editor.png create mode 100644 packages/plugins/field-angle/readme-media/wrap.gif create mode 100644 packages/plugins/field-angle/src/field_angle.ts create mode 100644 packages/plugins/field-angle/src/index.ts create mode 100644 packages/plugins/field-angle/test/field_angle_test.mocha.js create mode 100644 packages/plugins/field-angle/test/index.html create mode 100644 packages/plugins/field-angle/test/index.ts create mode 100644 packages/plugins/field-angle/tsconfig.json create mode 100644 packages/plugins/field-bitmap/CHANGELOG.md create mode 100644 packages/plugins/field-bitmap/README.md create mode 100644 packages/plugins/field-bitmap/package.json create mode 100644 packages/plugins/field-bitmap/src/field-bitmap.ts create mode 100644 packages/plugins/field-bitmap/src/index.ts create mode 100644 packages/plugins/field-bitmap/test/field_test.mocha.js create mode 100644 packages/plugins/field-bitmap/test/index.html create mode 100644 packages/plugins/field-bitmap/test/index.js create mode 100644 packages/plugins/field-bitmap/tsconfig.json create mode 100644 packages/plugins/field-colour-hsv-sliders/CHANGELOG.md create mode 100644 packages/plugins/field-colour-hsv-sliders/README.md create mode 100644 packages/plugins/field-colour-hsv-sliders/package.json create mode 100644 packages/plugins/field-colour-hsv-sliders/readme-media/hsv_sliders_screenshot.png create mode 100644 packages/plugins/field-colour-hsv-sliders/src/field_colour_hsv_sliders.ts create mode 100644 packages/plugins/field-colour-hsv-sliders/src/index.ts create mode 100644 packages/plugins/field-colour-hsv-sliders/test/field_colour_hsv_sliders_test.mocha.js create mode 100644 packages/plugins/field-colour-hsv-sliders/test/field_colour_hsv_sliders_test_helpers.mocha.js create mode 100644 packages/plugins/field-colour-hsv-sliders/test/index.html create mode 100644 packages/plugins/field-colour-hsv-sliders/test/index.ts create mode 100644 packages/plugins/field-colour-hsv-sliders/tsconfig.json create mode 100644 packages/plugins/field-colour/CHANGELOG.md create mode 100644 packages/plugins/field-colour/README.md create mode 100644 packages/plugins/field-colour/package.json create mode 100644 packages/plugins/field-colour/readme-media/collapsed.png create mode 100644 packages/plugins/field-colour/readme-media/customized.png create mode 100644 packages/plugins/field-colour/readme-media/on_block.png create mode 100644 packages/plugins/field-colour/readme-media/validator.gif create mode 100644 packages/plugins/field-colour/readme-media/with_editor.png create mode 100644 packages/plugins/field-colour/src/blocks/colourBlend.ts create mode 100644 packages/plugins/field-colour/src/blocks/colourPicker.ts create mode 100644 packages/plugins/field-colour/src/blocks/colourRandom.ts create mode 100644 packages/plugins/field-colour/src/blocks/colourRgb.ts create mode 100644 packages/plugins/field-colour/src/blocks/generatorsType.ts create mode 100644 packages/plugins/field-colour/src/field_colour.ts create mode 100644 packages/plugins/field-colour/src/index.ts create mode 100644 packages/plugins/field-colour/test/blocks_test.mocha.js create mode 100644 packages/plugins/field-colour/test/field_colour_test.mocha.js create mode 100644 packages/plugins/field-colour/test/golden/golden.dart create mode 100644 packages/plugins/field-colour/test/golden/golden.js create mode 100644 packages/plugins/field-colour/test/golden/golden.lua create mode 100644 packages/plugins/field-colour/test/golden/golden.php create mode 100644 packages/plugins/field-colour/test/golden/golden.py create mode 100644 packages/plugins/field-colour/test/index.html create mode 100644 packages/plugins/field-colour/test/index.ts create mode 100644 packages/plugins/field-colour/tsconfig.json create mode 100644 packages/plugins/field-date/.gitignore create mode 100644 packages/plugins/field-date/CHANGELOG.md create mode 100644 packages/plugins/field-date/README.md create mode 100644 packages/plugins/field-date/package.json create mode 100644 packages/plugins/field-date/src/field_date.ts create mode 100644 packages/plugins/field-date/src/index.ts create mode 100644 packages/plugins/field-date/src/utils.ts create mode 100644 packages/plugins/field-date/test/field_date_test.mocha.js create mode 100644 packages/plugins/field-date/test/index.html create mode 100644 packages/plugins/field-date/test/index.ts create mode 100644 packages/plugins/field-date/tsconfig.json create mode 100644 packages/plugins/field-dependent-dropdown/CHANGELOG.md create mode 100644 packages/plugins/field-dependent-dropdown/README.md create mode 100644 packages/plugins/field-dependent-dropdown/package.json create mode 100644 packages/plugins/field-dependent-dropdown/src/dependent_dropdown_options_change.ts create mode 100644 packages/plugins/field-dependent-dropdown/src/field_dependent_dropdown.ts create mode 100644 packages/plugins/field-dependent-dropdown/src/index.ts create mode 100644 packages/plugins/field-dependent-dropdown/test/field_dependent_dropdown.mocha.js create mode 100644 packages/plugins/field-dependent-dropdown/test/field_dependent_dropdown_test_block.ts create mode 100644 packages/plugins/field-dependent-dropdown/test/index.html create mode 100644 packages/plugins/field-dependent-dropdown/test/index.ts create mode 100644 packages/plugins/field-dependent-dropdown/tsconfig.json create mode 100644 packages/plugins/field-grid-dropdown/CHANGELOG.md create mode 100644 packages/plugins/field-grid-dropdown/README.md create mode 100644 packages/plugins/field-grid-dropdown/package.json create mode 100644 packages/plugins/field-grid-dropdown/readme-media/dropdown-images.png create mode 100644 packages/plugins/field-grid-dropdown/readme-media/dropdown.png create mode 100644 packages/plugins/field-grid-dropdown/src/grid.ts create mode 100644 packages/plugins/field-grid-dropdown/src/grid_item.ts create mode 100644 packages/plugins/field-grid-dropdown/src/index.ts create mode 100644 packages/plugins/field-grid-dropdown/test/field_test.mocha.js create mode 100644 packages/plugins/field-grid-dropdown/test/index.html create mode 100644 packages/plugins/field-grid-dropdown/test/index.ts create mode 100644 packages/plugins/field-grid-dropdown/tsconfig.json create mode 100644 packages/plugins/field-multilineinput/CHANGELOG.md create mode 100644 packages/plugins/field-multilineinput/README.md create mode 100644 packages/plugins/field-multilineinput/package.json create mode 100644 packages/plugins/field-multilineinput/readme-media/collapsed.png create mode 100644 packages/plugins/field-multilineinput/readme-media/on_block.png create mode 100644 packages/plugins/field-multilineinput/readme-media/spellcheck.gif create mode 100644 packages/plugins/field-multilineinput/readme-media/validator.gif create mode 100644 packages/plugins/field-multilineinput/readme-media/with_editor.png create mode 100644 packages/plugins/field-multilineinput/src/blocks/generatorsType.ts create mode 100644 packages/plugins/field-multilineinput/src/blocks/textMultiline.ts create mode 100644 packages/plugins/field-multilineinput/src/field_multilineinput.ts create mode 100644 packages/plugins/field-multilineinput/src/index.ts create mode 100644 packages/plugins/field-multilineinput/test/block_test.mocha.js create mode 100644 packages/plugins/field-multilineinput/test/field_multilineinput_test.mocha.js create mode 100644 packages/plugins/field-multilineinput/test/golden/golden.dart create mode 100644 packages/plugins/field-multilineinput/test/golden/golden.js create mode 100644 packages/plugins/field-multilineinput/test/golden/golden.lua create mode 100644 packages/plugins/field-multilineinput/test/golden/golden.php create mode 100644 packages/plugins/field-multilineinput/test/golden/golden.py create mode 100644 packages/plugins/field-multilineinput/test/index.html create mode 100644 packages/plugins/field-multilineinput/test/index.ts create mode 100644 packages/plugins/field-multilineinput/tsconfig.json create mode 100644 packages/plugins/field-slider/CHANGELOG.md create mode 100644 packages/plugins/field-slider/README.md create mode 100644 packages/plugins/field-slider/package.json create mode 100644 packages/plugins/field-slider/src/field_slider.ts create mode 100644 packages/plugins/field-slider/src/index.ts create mode 100644 packages/plugins/field-slider/test/field_slider_test.mocha.js create mode 100644 packages/plugins/field-slider/test/field_slider_test_helpers.mocha.js create mode 100644 packages/plugins/field-slider/test/index.html create mode 100644 packages/plugins/field-slider/test/index.ts create mode 100644 packages/plugins/field-slider/tsconfig.json diff --git a/packages/plugins/field-angle/CHANGELOG.md b/packages/plugins/field-angle/CHANGELOG.md new file mode 100644 index 00000000000..96cb91e9822 --- /dev/null +++ b/packages/plugins/field-angle/CHANGELOG.md @@ -0,0 +1,501 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [13.1.0](https://github.com/RaspberryPiFoundation/blockly-samples/compare/v13.0.0...v13.1.0) (2026-06-30) + +### ⚠ BREAKING CHANGES + +* make field-multilineinput keyboard navigable and accessible (#2729) +* Bump Blockly dependency to v13 (#2704) + +### Features + +* make field-multilineinput keyboard navigable and accessible ([#2729](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2729)) + +### Bug Fixes + +* Improve accessibility of `FieldAngle` ([#2697](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2697)) + +### Miscellaneous Chores + +* Bump Blockly dependency to v13 ([#2704](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2704)) + + +## [6.0.9](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-angle@6.0.8...@blockly/field-angle@6.0.9) (2026-04-09) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [6.0.8](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-angle@6.0.7...@blockly/field-angle@6.0.8) (2026-01-15) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [6.0.7](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-angle@6.0.6...@blockly/field-angle@6.0.7) (2026-01-05) + + +### Bug Fixes + +* update plugin URLs to RPF ([#2665](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2665)) ([6c34039](https://github.com/RaspberryPiFoundation/blockly-samples/commit/6c3403960b8070eb8e576bd5702bdb53a09ac475)) + + + +## [6.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-angle@6.0.5...@blockly/field-angle@6.0.6) (2026-01-05) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [6.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-angle@6.0.4...@blockly/field-angle@6.0.5) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [6.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-angle@6.0.3...@blockly/field-angle@6.0.4) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [6.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-angle@6.0.2...@blockly/field-angle@6.0.3) (2025-10-02) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [6.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-angle@6.0.1...@blockly/field-angle@6.0.2) (2025-07-17) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [6.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-angle@6.0.0...@blockly/field-angle@6.0.1) (2025-05-22) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [6.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.16...@blockly/field-angle@6.0.0) (2025-05-16) + + +### ⚠ BREAKING CHANGES + +* update all plugins to blockly v12 (#2538) + +### Features + +* update all plugins to blockly v12 ([#2538](https://github.com/google/blockly-samples/issues/2538)) ([420ba31](https://github.com/google/blockly-samples/commit/420ba3124b260b2c06e8e1d61be44e870780d95f)) + + + +## [5.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.15...@blockly/field-angle@5.0.16) (2025-05-16) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.14...@blockly/field-angle@5.0.15) (2025-05-15) + + +### Bug Fixes + +* Fix ephemeral focus being taken twice ([#2521](https://github.com/google/blockly-samples/issues/2521)) ([29b5a0d](https://github.com/google/blockly-samples/commit/29b5a0d896e7e6f7eb83867bd5b2577bf808e839)), closes [#2514](https://github.com/google/blockly-samples/issues/2514) [#2515](https://github.com/google/blockly-samples/issues/2515) [#2514](https://github.com/google/blockly-samples/issues/2514) [#2515](https://github.com/google/blockly-samples/issues/2515) [#2527](https://github.com/google/blockly-samples/issues/2527) + + + + + +## [5.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.13...@blockly/field-angle@5.0.14) (2025-02-13) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.12...@blockly/field-angle@5.0.13) (2024-12-19) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.11...@blockly/field-angle@5.0.12) (2024-12-03) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.10...@blockly/field-angle@5.0.11) (2024-11-07) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.9...@blockly/field-angle@5.0.10) (2024-09-26) + + +### Bug Fixes + +* **field-*:** Remove unneeded `fieldRegistry.unregister` calls ([#2454](https://github.com/google/blockly-samples/issues/2454)) ([b3ba30e](https://github.com/google/blockly-samples/commit/b3ba30e23dddf0bd98c266659aa229ba6ba685b0)), closes [#2453](https://github.com/google/blockly-samples/issues/2453) + + + + + +## [5.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.8...@blockly/field-angle@5.0.9) (2024-08-29) + + +### Bug Fixes + +* predeploy plugins ([#2449](https://github.com/google/blockly-samples/issues/2449)) ([6b36d8b](https://github.com/google/blockly-samples/commit/6b36d8b344a969f79d89bbc7dcee29ae554759f9)) + + + + + +## [5.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.7...@blockly/field-angle@5.0.8) (2024-08-22) + + +### Bug Fixes + +* lerna v8 breaking our whole shebangle ([#2446](https://github.com/google/blockly-samples/issues/2446)) ([b8b4c21](https://github.com/google/blockly-samples/commit/b8b4c21d4eaf81f527336ae46f6679ff99ac23c3)) + + + + + +## [5.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.6...@blockly/field-angle@5.0.7) (2024-08-08) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.5...@blockly/field-angle@5.0.6) (2024-08-01) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.4...@blockly/field-angle@5.0.5) (2024-07-25) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.3...@blockly/field-angle@5.0.4) (2024-07-11) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.2...@blockly/field-angle@5.0.3) (2024-07-04) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.1...@blockly/field-angle@5.0.2) (2024-06-06) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [5.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-angle@5.0.0...@blockly/field-angle@5.0.1) (2024-05-30) + + +### Bug Fixes + +* multiple blockly instances ([#2375](https://github.com/google/blockly-samples/issues/2375)) ([b231e59](https://github.com/google/blockly-samples/commit/b231e598f2f5f5b0abbfd01d981e35572ad50a26)) + + + +## [5.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-angle@4.0.4...@blockly/field-angle@5.0.0) (2024-05-21) + + +### ⚠ BREAKING CHANGES + +* update all plugins to v11 of blockly + +### Features + +* update all plugins to v11 of blockly ([5fcd3e7](https://github.com/google/blockly-samples/commit/5fcd3e7d53eaadffe9bda9a378b404d34b2f8be2)) +* upgrade ts version, fix field validators ([b2d2191](https://github.com/google/blockly-samples/commit/b2d2191ff11b30347b33f95acf6e58f7ce54d004)) + + +### Bug Fixes + +* Call blocks handle both manual disabling and disabled defs ([#2334](https://github.com/google/blockly-samples/issues/2334)) ([5eade55](https://github.com/google/blockly-samples/commit/5eade55779c4022d14ad4472ff32c93c78199887)) +* remove some uses of generateFieldTextBlocks ([#2357](https://github.com/google/blockly-samples/issues/2357)) ([53b3fa4](https://github.com/google/blockly-samples/commit/53b3fa4604aabb1fc5240d41c8d1d389d4b15301)) + + + +## [4.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-angle@4.0.3...@blockly/field-angle@4.0.4) (2024-05-16) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [4.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-angle@4.0.2...@blockly/field-angle@4.0.3) (2024-05-09) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [4.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-angle@4.0.1...@blockly/field-angle@4.0.2) (2024-04-25) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [4.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-angle@4.0.0...@blockly/field-angle@4.0.1) (2024-04-11) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [4.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.13...@blockly/field-angle@4.0.0) (2024-04-04) + + +### ⚠ BREAKING CHANGES + +* The multiline text input field no longer registers itself on load. The developer must either manually register the field or install blocks, which will install the field. This is part of a move to have no side effects in field and block definitions, so that tree-shaking can remove unwanted fields and blocks. +* The angle field no longer registers itself on load. The developer must manually register the field. This is part of a move to have no side effects in field and block definitions, so that tree-shaking can remove unwanted fields and blocks. + +### Features + +* add block definitions to fields ([#2299](https://github.com/google/blockly-samples/issues/2299)) ([c197af6](https://github.com/google/blockly-samples/commit/c197af6c67bc989bbdb4e83a62abc08167bcbbfe)), closes [#2162](https://github.com/google/blockly-samples/issues/2162) [#2202](https://github.com/google/blockly-samples/issues/2202) [#2221](https://github.com/google/blockly-samples/issues/2221) [#2211](https://github.com/google/blockly-samples/issues/2211) [#2220](https://github.com/google/blockly-samples/issues/2220) [#2232](https://github.com/google/blockly-samples/issues/2232) [#2233](https://github.com/google/blockly-samples/issues/2233) [#2296](https://github.com/google/blockly-samples/issues/2296) + + + +## [3.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.12...@blockly/field-angle@3.0.13) (2024-03-28) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.11...@blockly/field-angle@3.0.12) (2024-02-08) + + +### Bug Fixes + +* Display rounded number in field angle. ([#2191](https://github.com/google/blockly-samples/issues/2191)) ([68d60b7](https://github.com/google/blockly-samples/commit/68d60b7868f0a89c09afd16d64e7f2f16a719d7f)) + + + +## [3.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.10...@blockly/field-angle@3.0.11) (2023-12-14) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.9...@blockly/field-angle@3.0.10) (2023-12-07) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.8...@blockly/field-angle@3.0.9) (2023-11-09) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.7...@blockly/field-angle@3.0.8) (2023-11-02) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.6...@blockly/field-angle@3.0.7) (2023-10-30) + + +### Bug Fixes + +* **packaging:** Don't advertise non-existent ESM entrypoints ([#2022](https://github.com/google/blockly-samples/issues/2022)) ([d019198](https://github.com/google/blockly-samples/commit/d0191984399b784e2928b8fb4c58257bfa857655)), closes [#1877](https://github.com/google/blockly-samples/issues/1877) +* publish type for field angle ([#1956](https://github.com/google/blockly-samples/issues/1956)) ([f0e8dbf](https://github.com/google/blockly-samples/commit/f0e8dbfacfe1a7207380c83a347603a3dd845d58)) +* update tsconfigs to publish types accurately ([df4b084](https://github.com/google/blockly-samples/commit/df4b0844af712f5025a2ec842458b828f3147676)) +* use includes rather than excludes ([40efc25](https://github.com/google/blockly-samples/commit/40efc255329e3ca476ccc247b95a2d05dd77b45e)) + + + +## [3.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.5...@blockly/field-angle@3.0.6) (2023-09-21) + + +### Bug Fixes + +* Fire intermediate events instead of change events while dragging angle field ([#1933](https://github.com/google/blockly-samples/issues/1933)) ([dc79b14](https://github.com/google/blockly-samples/commit/dc79b14e4bc402e76cff960a4d72d4ac3a4365a5)) + + + +## [3.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.4...@blockly/field-angle@3.0.5) (2023-09-14) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.3...@blockly/field-angle@3.0.4) (2023-08-31) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.2...@blockly/field-angle@3.0.3) (2023-08-17) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.1...@blockly/field-angle@3.0.2) (2023-07-24) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-angle@3.0.0...@blockly/field-angle@3.0.1) (2023-07-20) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [3.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-angle@2.0.1...@blockly/field-angle@3.0.0) (2023-07-13) + + +### ⚠ BREAKING CHANGES + +* updated `plugins/field-date` to TypeScript (#1705) + +### Features + +* updated `plugins/field-date` to TypeScript ([#1705](https://github.com/google/blockly-samples/issues/1705)) ([e5531ff](https://github.com/google/blockly-samples/commit/e5531fffe188ee361a16fe48ed126b34e51a8d30)) + + + +## [2.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-angle@2.0.0...@blockly/field-angle@2.0.1) (2023-07-06) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [2.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-angle@1.2.2...@blockly/field-angle@2.0.0) (2023-06-28) + + +### ⚠ BREAKING CHANGES + +* update all plugins to use blockly-v10.0.0 (#1744) + +### Features + +* update all plugins to use blockly-v10.0.0 ([#1744](https://github.com/google/blockly-samples/issues/1744)) ([6cc88cb](https://github.com/google/blockly-samples/commit/6cc88cbef39d4ad664a668d3d46eb29ba7292f9c)) + + + +## [1.2.2](https://github.com/google/blockly-samples/compare/@blockly/field-angle@1.2.1...@blockly/field-angle@1.2.2) (2023-06-22) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [1.2.1](https://github.com/google/blockly-samples/compare/@blockly/field-angle@1.2.0...@blockly/field-angle@1.2.1) (2023-06-15) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## [1.2.0](https://github.com/google/blockly-samples/compare/@blockly/field-angle@1.1.1...@blockly/field-angle@1.2.0) (2023-05-25) + + +### Features + +* Revise fields ([#1700](https://github.com/google/blockly-samples/issues/1700)) ([c326f7b](https://github.com/google/blockly-samples/commit/c326f7b1ef54fe45387046c693ebd5afe2b2c571)) + + + +## [1.1.1](https://github.com/google/blockly-samples/compare/@blockly/field-angle@1.1.0...@blockly/field-angle@1.1.1) (2023-05-18) + +**Note:** Version bump only for package @blockly/field-angle + + + + + +## 1.1.0 (2023-05-11) + + +### Features + +* Add angle, colour, and multiline fields. (Rebase of [#1674](https://github.com/google/blockly-samples/issues/1674)) ([#1693](https://github.com/google/blockly-samples/issues/1693)) ([1b447f4](https://github.com/google/blockly-samples/commit/1b447f41d6293f14e846b8a4d82d289a5637a99a)) diff --git a/packages/plugins/field-angle/README.md b/packages/plugins/field-angle/README.md new file mode 100644 index 00000000000..d9f0b9afdf8 --- /dev/null +++ b/packages/plugins/field-angle/README.md @@ -0,0 +1,112 @@ +# @blockly/field-angle [![Built on Blockly](https://tinyurl.com/built-on-blockly)](https://github.com/google/blockly) + +A [Blockly](https://www.npmjs.com/package/blockly) angle field. + +## Installation + +### Yarn + +``` +yarn add @blockly/field-angle +``` + +### npm + +``` +npm install @blockly/field-angle --save +``` + +## Usage + +### Installation + +You must register this field with Blockly. You can do this by calling +`registerFieldAngle` before instantiating your blocks. If another field is +registered under the same name, this field will overwrite it. + +### Parameters + +This field accepts up to 9 parameters, in addition to the 4 accepted by the +[number field][number-field]: + +- "value" to specify the default value of the angle field. Defaults to 0. +- "mode" to specify the basic setup of the angle field. Either Mode.COMPASS + or Mode.PROTRACTOR. COMPASS specifies "clockwise" should be "true", and the + "offset" should be "90". PROTRACTOR specifies that "clockwise" should be + "false" and the "offset" should be 0. These settings can be overridden by + the following options. Defaults to PROTRACTOR. +- "clockwise" to specify whether the value of the angle field should increase + in the clockwise direction (if true) or in the counter-clockwise direction + (if false). Defaults to false. +- "offset" to specify where the minimum/maximum displayed value of the angle + field should be. The offset is in degree units that consider the circle to + be 360 degrees. A 0 offset (or multiple of 360) specifies the right side of + the circle as the minimum/maximum value. More positive values rotate + clockwise and more negative values rotate counter clockwise (regardless of + the "clockwise" setting). Defaults to 0. +- "displayMin" to specify the minimum displayed value of the angle field. The + minimum displayed value may not actually be a selectable value. For example, + you may have a full 0-360 degree circle ("displayMin" of 0), but only be + able to select angle values from 90-270. Defaults to 0. +- "displayMax" to specify the maximum displayed value of the angle field. The + maximum displayed value may not actually be a selectable value. For example, + you may have a full 0-360 degree circle ("displayMax" of 360), but only be + able to select angle values from 90-270. Defaults to 360. +- "minorTick" to specify the distance between small tick marks on the angle + picker. The minorTick is in units from your displayMin-displayMax. The ticks + start at your "min" rounded up to a multiple of your "minorTick". The + ticks end at you "max" rounded down to a multiple of your "minorTick". + Defaults to 15. +- "majorTick" to specify the distance between big tick marks on the angle + picker. The majorTick is in units from your displayMin-displayMax. The ticks + start at your "min" rounded up to a multiple of your "majorTick". The + ticks end at you "max" rounded down to a multiple of your "majorTick". + Defaults to 45. +- "symbol" to specify the unit symbol to append to your number. Defaults to °. + If this is used to specify "radians" or similar, + `Blockly.Msg['ARIA_LABEL_FIELD_ANGLE']` should be overridden accordingly to + ensure that the screenreader description remains consistent. + +### JavaScript + +```js +import * as Blockly from 'blockly'; +import {registerFieldAngle} from '@blockly/field-angle'; + +registerFieldAngle(); +Blockly.Blocks['test_field_angle'] = { + init: function () { + this.appendDummyInput() + .appendField('angle: ') + .appendField(new FieldAngle(90), 'FIELDNAME'); + }, +}; +``` + +### JSON + +```js +import * as Blockly from 'blockly'; +import {registerFieldAngle} from '@blockly/field-angle'; + +registerFieldAngle(); +Blockly.defineBlocksWithJsonArray([ + { + type: 'test_field_angle', + message0: 'angle: %1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + value: 50, + }, + ], + }, +]); +``` + +## License + +Apache 2.0 + +[number-field]: https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/number#creation diff --git a/packages/plugins/field-angle/package.json b/packages/plugins/field-angle/package.json new file mode 100644 index 00000000000..99dc05c47e4 --- /dev/null +++ b/packages/plugins/field-angle/package.json @@ -0,0 +1,55 @@ +{ + "name": "@blockly/field-angle", + "version": "13.1.0", + "description": "A Blockly angle field.", + "scripts": { + "audit:fix": "blockly-scripts auditFix", + "build": "blockly-scripts build", + "clean": "blockly-scripts clean", + "lint": "eslint .", + "predeploy": "blockly-scripts predeploy", + "prepublishOnly": "npm run clean && npm run build", + "start": "blockly-scripts start", + "test": "blockly-scripts test" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "unpkg": "./dist/index.js", + "author": "Blockly Team", + "keywords": [ + "blockly", + "field", + "angle" + ], + "homepage": "https://github.com/RaspberryPiFoundation/blockly-samples/tree/master/plugins/field-angle#readme", + "bugs": { + "url": "https://github.com/RaspberryPiFoundation/blockly-samples/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/RaspberryPiFoundation/blockly-samples.git", + "directory": "plugins/field-angle" + }, + "license": "Apache-2.0", + "directories": { + "dist": "dist", + "src": "src" + }, + "files": [ + "dist", + "src" + ], + "devDependencies": { + "@blockly/dev-scripts": "^13.1.0", + "@blockly/dev-tools": "^13.1.0", + "chai": "^6.2.2", + "sinon": "^22.0.0", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "blockly": "^13.2.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugins/field-angle/readme-media/clockwise_false.gif b/packages/plugins/field-angle/readme-media/clockwise_false.gif new file mode 100644 index 0000000000000000000000000000000000000000..daa54b7fa95114aba52ea660585cf7c439aabd30 GIT binary patch literal 39421 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 literal 0 HcmV?d00001 diff --git a/packages/plugins/field-angle/readme-media/clockwise_true.gif b/packages/plugins/field-angle/readme-media/clockwise_true.gif new file mode 100644 index 0000000000000000000000000000000000000000..58e103c131786b7cd1390037be9992a527e800e7 GIT binary patch literal 38124 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 literal 0 HcmV?d00001 diff --git a/packages/plugins/field-angle/readme-media/collapsed.png b/packages/plugins/field-angle/readme-media/collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..a84dae0d298cea143242ee20d8bb8489b52fb712 GIT binary patch literal 2033 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�pdE^W+dus!E$dwyd><7~ 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 literal 0 HcmV?d00001 diff --git a/packages/plugins/field-angle/readme-media/round_70.gif b/packages/plugins/field-angle/readme-media/round_70.gif new file mode 100644 index 0000000000000000000000000000000000000000..430c55be4b6590b7dd9d8b0e012cecf8f5cadf5a GIT binary patch literal 114103 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 g27oZ)~ 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 Am;e9( literal 0 HcmV?d00001 diff --git a/packages/plugins/field-angle/readme-media/validator.gif b/packages/plugins/field-angle/readme-media/validator.gif new file mode 100644 index 0000000000000000000000000000000000000000..4257c56eafa8e1b6d3a78f5c51bba390c0a38d58 GIT binary patch literal 60197 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 Mb(mVUicQS_1MwO)`2YX_ literal 0 HcmV?d00001 diff --git a/packages/plugins/field-angle/readme-media/wrap.gif b/packages/plugins/field-angle/readme-media/wrap.gif new file mode 100644 index 0000000000000000000000000000000000000000..146f7b29e3ee6067b45955f5898a15522f8fbd47 GIT binary patch literal 54192 zcmeFa2UL^my0-fz1PDDqr~-!Gkt#*N(5p%>ib(G$O^S^q^q$a>A|fbuL=;e_(m`y1 z4LeOKq5`6HJdd*0I&=56&)8?|{r`J8zA=ZBHm8wX&;8u*^#wBvQ!Q=JhsXtF5kc1b z)Zkj{Q(Nv*TWD3Ey{$fdU1PX*`>WHM&r7#&UN7J`IjXPX6`yCTJt^LPk*u8%s2{S= zXx}btcWZ};z3zqKzU}Fu!xzIxuSdMU5qbX#x&AD*?qd1PhKqyunqNM;_4@JcKVNV9 zZ^QL|plM{Fad`0dyQdv*pFMd0=Edm9@c8(L$;s(Y)01DON59QHS)A!!o@rVA+_XM> zYkltS`h55LeBau)!IkeL%Zp!rEdGFBi;IiP%gZY(E32!kYin!k>+2hT`UU^8_Sb89 zW8+S#kSNq$0|Jhf7uO|AF zWw(igy^(>9iK>z^9ST7Z(Paz{A30Q79x8Azb9Vs|* z%QEw8Rb3hUYC&&$ubt`1AsD3#S=3kG%agIc;C8RR=6;c~cVC&s^|KF4wIZkA+`E46 z@hM^gv#@1D?Z6rHV0Hn!YrQye`iz-k%NrLQg>^0kz1^!J|KjTYHdVKkv?`jYiJVes z-_sE)zjC6rf*2hGwL$kFGIYQ3>ifGSIu;RYm)Z74Ws0>r8k3yaz?IaRD(Wn@pP$3G zDU8!wjLN4pGb&?!t6}O1i7)6QtA-bjU&$RQY1*6Y-_No0eua!gcLu-U5;H%svT1It zH&6b+qn75#@uPgxg|^$hzP>LIpRQ1x`_7U|Y})w7P!)5PjG26L@xbFdf2=Ofj$N0= z?bo4B)5tzW{CB)?bCOv%U!dlB0)~$Y_LEK-o$9Yd9a%t8pIRT|2^&u04+8LJw9ap*OAVS`LhY`ch z_hrloBZ93qqA%84IFo3=tnEQF6^k3z*D*&ZM62zxl9`TI)T@sQ`NSBZ2}{ZeL_KAa zNX!mhQ6k!`>O~1r>}>PdBW{M;R5~vmb>9+&@_X(l_ePr{*gZ3?$QhNlU2< z(&t}Q#9kTfJI|~?$vk!W5^v|HBbE&E7wU*y!MWF_A4H1X>rh{bkhhDK9 zh3t~cHzmR|+^QW{N1M8=lZrW~$K7{&0TB1Tki%xq^91Mc>Cf7UO5)f@HHW5*wDPaT{; z+df37S>Il191Mvz`|$G8_SaE-lRsXg?-7MJOo+X>&SyuNX5S1a@*P}yi~0U`ua7Fi zPkYDny<8yR=3&NtUg)Z`%OA_^OZxqlWl>R;!3^JT>=vfm5+I23_6(eR?; zq~XP`x+SBlT~0@g&V5TMH8SnR(fM#5M;o&)KIBPYy}ffGZO%ZU7RM&V{IqnElV>X( zC%5e5mhYWAx9TO_H(I#2Jh;VR!|021!-mn8r+3?kbN1?YHpEI=qKhjAzND(;SdqJ(ii(?~)) zKE0Y@OFxO#-@HTc)oij>^$7D=0!e6PHf2NW4Ux^RspFQwTq>LKFk?b;r<9J0o}id0 z_bGD|antZL#|K_-uhh25265_fq!KX_#BNnfVkXDZ=#eHDgKhfZnOx?!^a^2QmZ(ZP ze3Q4Zq74~OQ$Zi_5y!j@Fve!m&`~o8)~2=Bw2Fkz4w1m>grQh$hBI-MRfMhP<~M~0 zvtu%SBt&KVOyALRi!eUin&(@0^D?2s8;Fd8%Vd`PdRleM2YP)EODju4o)bPDhd1IB z!=R2ah5KfWHd)#3C1Dzm`KViXplqMd=e8-dtE?M6NMk)+${P3ZlZ0X7tzM*r(N-#1 zj7VltsLy}c;;VEy@V*^o7>(bs`S40TtR%8ZN#)(8jA^aN(*CvGglTmfj1=L1K-F;h ze3Xo_)k_qMHSv_mAn|LoIBodGBl>1C%1m4pBhW%*2(^%YG&)4aH9a}aUM_7d9`_hG zY>U=(rYTk7`^qdo24aswg5_BX`4=PHNT;AL~)5rdVTrrmvShmPA`DBhsnf z;yCjos+NZnZR?!dzgWk8AuChbF|gUm?73=gY-d2Soy+qdowOoaXYjtopk2XUdB&Hl zJ@&s4hySzutEr*#SOwA(PI@O&!md4@+*U6;Ri9sr>Y`%gDqr*+*?wVbEzz4}!KKCI zX5EdM8GmQ1#~f~j4J8^I{XVwTz0Y4gzdUy=lx(?r^Hx1~)r~(CgA-Q!j|rwW`pE~= z$yDwgWiG0JRHI0LQ*P_H)8$LtMEQe`t8Z?aHeTQ!3Jn<%Cm$)Czx}pc_J}|nc|a_n z<;(JAqvlfge#@s#v)8`#KP`E?va(nxj6HN>(px(rodssL{poIQ-lejr?C%pNX?0XV=6b$`Lm98 zn{e1Ryr$0c#8|v;$8FUOgCu_&rK|p32OeF^47B!p<b`RL8BEKX|Ig%-Zrmg9)Gfzfa}0A1%I?k~%Ubkw*sWY&w3f(!_QX3OjmR;N#%}-}_G|o7`eQTC?vQ^>iVT$+Ku=N@L3%9-~(N1IEGdd84n63~7(>{Na z5`TU7j48*Z8ya2MrRA58*%mGz&+-n5iRl?k{#x}%x%( zS^JMaW}1&OW8e4c!}wHaccAcl*8IZ(2h|K)ia*t5M9k1}8otc9-K`U|@F?V?FzXOI zEkcK(+(J5}$2WVCjGUekmu?+$JP0Lqb{9@MnmLHm1omzjRlcF`7a5twX z9vRQfUhB`h8=3#&&T?F}_7{_lw8)egnqHXuouREet$2;%^VJ?LFTPYeICl8!v(tnnc#WQ-*oA zGPy`jOB@)$9}znnr1H^2H8x(jo7C`(9DdXNiqn4Fa}kYVXY(chNgj{U0`iw@ZV!2q zvUv9!neNw;!Ji`euyQ7|jO;rclc>+{)5(+ABZ>2=-RaeYJ2ZrQa%RWKb=`+*F^sDF zEX@PkYW6*o)Q=m|d(54J71iH3rxW~3+VozHXfn^GRl4kwl};Pw!DOlVmK1Mk#vYZhbEz;5 z&XDvA4M?h&Bpod9exu>ju9c})YvX$^X*$W}&GE;>}ao9V82UCjOCflKy|j4v@N@%E#HkjbLL_*#z$$GtidR& zCo-0ci3ipKvbVQezxi&hueqx}bzi)R<_muDm;77n63qh~sdgW1f*fs&@=t`=(?lX@ z^z#qTs1n zQIsk9X^sQlG|b1;ACJ0!*0k3w(IKwjK|p~^bAfzqf!lJyJ4TX+cA=3>p-)_)UK)O9 zT;VA?279us z?unN;gW>42*LBisi|FT=R6Nqc8R^WO9qQ(aS!p{^)wuj(|C3YpgzrUR=gX6ZBwd)j@NRKQgITdFjQ&;9$P}Vh7u7s}`)vg$Kt@s#MF#dg%eUB$a$cXPoputUGk*XeW&Br=XqG0zP_(S z%i&DRJm(&_hzPAdC?$~|)6p>nFnE+qpy=>OQ~-`Z!aQbR)*rRV>>oa-0Ut_~QQ>13{NAeG5fk$n{%F=#Kj$BOeQ~vB?B~y( zM1;~k@$+YmBhvlzXEz1;Iac%Y=LCWzB91k{lF{Y}JH+ni#`?rrJ9z*1$$p^A`_{{w zfLX|+em>|8)B(%@ety36vpYNSG$kQB#BSwq#Jsi13@!U7BjXyLYfWEwgO_Imhg+8r z2lfDmDvC17{CrkD-G6AQ&e>b7X=~nyJ-nPuxhf)ZN2P3ni2YW-`(y z(vo2UG#?Isn?zX_CroCXkl89bl^F9Ah5DG9)FL1-<-2cnmlcc%7#A=%#w)2XI{v#b z0lgUtyV;v@a>-f8{_f3~xQfkbq>NO4HT_HGb5-3rf?HCBUV4b3sWPIxF8At*=sa0r zcIDd2VN$6UVO`5-=19(_XWe8k8;_asiF41CYz7rn8DDjZ~dtg1F<(V z_^t=N4E-ZHn}u}AwdAZO%R{2_^wSIerRVl%yb6sxdB^e4<%9b@X2=7K8-BYkw5-OE z2d=-^$v-rf1rCbA*_)Cs{GiCd3h;5+PSo8%Iwg}CUX zhpCW-mZve#_SlhRq3Fqx6hgAhXsSeEz-XGx+1k-`g`1P38Oq%;9M=%ONtzR;O9X%#BJ95Wgj6>_)u%zUML>zKF)!yiOc zWA|RZkvGkmu&*jMt%KttgR)X{>RDo%%|jtClGtGE6x$t zj*b`P<`ip>b;Sy9`{{a_QB#r7&>znro@yj@X{1KbcR_b%mk6f51L=@a4OF3NRilwS zYIy4lj}*lpL!PHlBt}16N9KcE;_#2x2Y0q(2jvvRTHnS_TwZj1cf@|Ijad1Jc`t&H1S@wne(2D>?C@Q4=T^w%*1Y4gp)=A{A}%$x5z>VQHR zHZWaa#=vv|Xu^;HFu`ZS1bU&X`A$hO9cVT7YnH%l08m;eYQlhl`2iCJ#s-WW7(AbY zJ?#(#SSigfUgKy7V@Y06vb!|*1?kvW66IYI<$Y>$OLPQGjspJ!r-FQ7bUl_9g1H6L z3r5$~u#l=izmLfY<$*pknQ6H}dns;v0s0?vGM=X-0^C(aWT&syltvR@q$Zsb6MJ+# zw*AOqgpz3Ih}@SJgpoO)PwOF{_?}NI^zno-Fh|P*UQebbzeyyG`%6rwB`>RPbJsEI zJ#w_Jv`ZmD0D3#en$knAMZx>e1P9g~3dspGDT*|iNKPz| zGARoU&h+(Zj6Hnjm|lLUBXG7q{^;odKgBK5Flb{!Jb|`Nkzw6GyZ^fw`j7wafA|Re z8Z!uvP>G^rM9Y$JOT@~};~6blC5fE_haU3qtVGh`c&5R;z1M&utbSrmroP6C%+@fZ zjU{xXigSqc@-Nl9_yy@k~?QNJlESy!(TuYhyiBajOdJ z=K2r!i`9bPJ!rl@@fbHjC>e|;CW?J+kv%8z zU)Eo1W(y^x@hvKLSr4NaaJ}yCEi3}9{hUIryVe`IRV-FE{NBGi-khuAx|A(V^)QHg zZ}s$2^R<}cBnY0o$Cl;k_HN@KSN6=iV(zxT=3e)AkP~RKN)LrArlxLpp%H;A?;mJlMsL$-9 zX0bGOyS7Ox6~nZU&%kW%U}k5j6lhD&E?eoF&uPM$xJS;k(}$j~4k2w3l`*&7qDRxB zQGJwb(1>9E@D>xrmT^&YrcMEb!3H52oD4F>q*xuLRJYQvC=$?!Q0H8!v3x_6=bpQs zttez?z*7GfG40f+hvZT{x_IsznL<=HkQl4m#sh3I~o@qElIq1cdV@w)gX7d_^m!_@vyFx{l*tF&Yw+9pGR zeSk}VT8g(Pd{Y7|e_;->gBZRU$u}ALE4H7t19Sp#UnIl>Pdkr9Lom+_vCQ-KC_JnS zY&{zi0C)wk0(l{y+98R6SAZ-)Co8}O7zB`opuJ{l{5ij=392dPIAn?U!AK-X7B?0JM;bd`EPHNTN4bWT`%+#^- zB@mxC@c7fmcC2Y`2X@czHeHgF`esTTy;AXGw*_SIbtQ#0y&VAYbs6bJRmCq}u8_U~ z=qm?<-#%yt;;-)6wc_Kss3`MVLE(ys2&8O)`xGgvLP7$PI3WJlz!MjnN{$<^%3XD| zTQJyB7N@%s=(j8>Hd1#Aus-LwixzIWwnO`erTNGn^HqM{<%IZ;1<5nvepx>5>r7Zk z<;%y9e2NR*;OG0v#IzwSJds6u+t&!OdL=3x68Cay;+mBO1a?U7s~-DdLagp}f$^|J z%ZA*Yh}c0khg81K!3L>+=Aid~h^_ybAMhKdP5qUXM1-9e+Yt7bvu+F(p`cLAV*H3Q z#j`z)U}S0SVo0(;A*u(fvBTuvA~DAb;}Rl0Lmo47De)e66@}ZxT)ocRmSTkxi2C6+ zZG&QBLNE^M)YjV8YU}IFYiXi#_fGD&4|;a;^a~TzR!3&*{s@I9ET%Pk@%;mr_fw3V zPc;19Fs(Go5D^|=`zrT5a_MiYJgTBtn-?un)#!#9E5QrA2T{U z+t!1V_r?sX7_?mV-B=lAvN~9ojLUUcIDGIMhXe_}){7tLt72^~`Lv$?*1@7I)7sAM zWk=`K#;0_bh#*3Q!d@hG=EeK?wDYP&E`~=b!v@`KAG^8e)J26**vbil@gGdjyAf+v zvsRPH|HV#_*(1%>M>G96Nrsgw9 zod{GVN|`>2YPCAM;Mj+#Ma7I;Y`bQguwT7Ql_rNtBTfprm59%D%4bz(I)?DS>SsXKCi{6@Jyj6V38sb* z7#U&0h1zxt2YP#DR-)Otij(Rn`p3FRs|E90lkpBAJzH8Aa^~p~T+UUoel)VBp>^8m z#)3}|(y+7=T!%Q=m^C!$yrwGZ#;K5mo-;=w^cH9m!i*%6?q6f;YU3u*y)oQxj&i`E zk^PlNaSDgit2>bR7M8DUGD|AG5rhwfL3u4!97~(nXU1HD7`-ZSE32liTMdtqkCAyC zYh{-LUkASaF6TWiE)lvA&Uz<-ot^aNdaOuypTd*1{%wS}Dy{4_QXlUdtFX43Jk4hLj8;c$40MYv41`m>*$< z;slrsD27@Vkju}=1*8QMYO8Al>;(-6h0!_SIq6>USKF~&A#lFhfJ?ypO2|~MM_REeQ@`#I;@{1pwohh z^B{P%VK4D=E8uL%V#sMWN#)+k5pkak#yqb_tUSccc9jwS!k}IKwf6qAr1qD9HdX_9 z3St+096YUI=Yfxw^4exbqk12JJ(}uu0KOJP!kMT5AVg-69k2mvP-=t$KL!Ws z4DuSJvyp}>$Xp8^t`N$8r8ZZm@zkWosIYhKSD)x;K$ciKCax)(m#WxHv6tll$VkkDrkn5*9G& zCo}U(Xz-e;$y8c0?EY(9IOtE;baf^&@Md`(khCd3PZ zTcwy7^u@4ouL|<51o)G{Tn=ZNM|Sg zynoWsEvF{~1)&n1i3|V=ZZI-FPl$)zd_z#6G$aTJI+YanF+cfj-_7MD@>h39h-P<0 z3D6`!uX;;J=qDBn_ym;la0@ra9EKhf;@Fam#D&OkYPbO}JO8VMcs?wCxbZA-6dF=$ zmo6d??KX$}W?RT?q@Fw>Ado^0V*(eKUh?$E)(%d3m9PEt8Od5_Z;U z_bn-jN!P9a*F5PVsc|ZpBb6Q$I)wF-vddqRTE0xB7zxY8?`9kjUe%McRi4?rNsJyr zNm^I9F~QnPLoB0Yw8&g#7$(2$sC!S&n9~VrW#UuA+8GM<^7QcnBMFQ&VK$laTMncF zI-4D0m3`lN=`(!^xiYUBk+Puu$ltm4_L#<+yz{+?9k(cyldQKP zhby`rMb{oQqU3mOH_NT9IL^Y9PDmj)xoX^A+`?I&SShfU%1wLMOBbS(=T~V=7Lr)O%cXJ9{Uu}}!OVi=woP{ODQtxCjSeIPgTUB9y z^aDhbwK(H8?2#@Nk}UlZbw)~zg-R!{u*#}CPp z(zuI?hw_dF$P$R$=bMJOJqC>LJ>m5Q4m*kV+BWt2A?iOx7f9x zU5a%+rGC)vyQP&Q>#*g`W8aZM4!R}O!2=D)B)SpLvimRiw@#@N1;Wq_y=FeF1oJ=Q zmxl+~=tyl}58z{cIuN%y>0+$Nh;l10Gvo6lvjdo_4)12xVUx~`8win|7I(~LlhJ>w zCjZQ6*kM*J;l;4CaQVBAlMVp&1-) z0m0!w3=XV-lFam6Kvm!`02B_Ou35+fUg10nX3q-_j?FVDz@nv~B%JgEG=Y`!9D=|* zYM3#Q6powV>9sqv9fe(xmfErK?mdgKYX!XDI4+4y)#|FSbZz4S!ks;dP z4BIx%ahew$P5@EX!d8+yl6b%z5A?KTM9{p^RMLon0RzIDS!TgaqSsKe7aImknaG5= zJr~-0t<3<%Dm6PU(`sl_Wh1Fnri?O7s8J0yeJf~N`G{Rl4UP2Oxa3^O(SIk?b^8Ug za&|_YkbTk9m~3W&!N0GX6p+J<8mYUm=MJX`2xxW}w+@V=5rQp0hdGfz`uuu#h0ytH z?KehW-Z;dRP6?q2%|^4h${_SX27&zkuh?hP6QQb(u>g@T#x;Sumv zl<{&(X344T-=A&W#el7AW8&OCo4{ErZJE@9F=0$}rRy+Gd&q)^e zwzW!@_!Nfw6}qL>NtL=Mg(1Zhlm(f{xbRZCg8b4P#i56#cC(P!pNr93b+#rUqGhyz z3Vbah&g+b|L08>G5xhOO)P{MXwl@(F=76Z_!1Odl7c{daTAh>g;_=J4L5faOT@vs0 zFO$w}josz~DH+S#Ij^PPu;Hvv8>tIP%qW=+3C;Yhe3_j3HcZi5HDupQ1i^&~G+DT9 z??BYFc>i?nvlsLNU1aZ{7TLeJ1@jN!N%rZwI#3pr0st*w71Au=7Tf}`3T!PcAOHh3 zfwj;)0$%};Kv0ORAQM0+%?P%INDKk;S9!Uy4#5mqyD@)1)7NdY`vl1V0y7X7>P(2u zfXrX988WkD&4(lkq-DsZW622+F9B>22sh^LLrw-SVfAY-9JoNVgEMTP8qf|93mz1N zb%6I9NWA-8*VWaam0XgS9!pJH%1Q(C00e@~JUmugeE)bH#A6!^i(A6NOS#$05`@{k zw%{lXBvGb%X)sSFeom}JhSdZIc0@$1T3Z5@*LLcy_<4h8uz{iTM6t-T3$BZaKG>o6 z?y5To1Q2@fiim7td-eOL>k)^B$j5WNy~ZnYU{b9F2Ylh@g$i@c*bp9z66@_BQj-Q0 zA1e1GSXB)9O#qSSBR`=NjY!{CauE1(^CugWV1&B1VEP1lj=c`IW` zx$7*<6FHO(VWBl0tzX7~sv;az!Egh@*Yx~^BPy5=chyv2I?*EZhLjW`wLa6=hVpWG z&o0QZE5d>>9H5MZggll=fvVC#NU~dm0P%Q=e0YPK3+hQ29WXbbznn}xYRN9JOo{*M z>9WDb2D1(vv-A*C2+40KCnj9CCQ=Y1_`7qoGo=4NxSaS^RsKaUz%uf>xDd?PIqA9J zwj7laQU?Dv(@VYA#M?6kLSu!dRLJ-8q_(9K?nQ`^itQ+&mZ-XlqC{l>fzB)IY7@)Z$}JrJWu4tlo@`Id4@c@Kxc&1Io#_Y zUeWmjy@ZRFdC66zw4@W6HA2B{lmpb0oNc?#6%$LLp2aiK^c)5*jMOK2AT7&M~Pho6aA!g;o-VeTdQe3%xmwj&*x4fj6Ktzmg)-CE3e$dT+Bq>e{eE0j(LV!maH_cwMBQ%zh%EwGF$a zv$#j*Xz?J!7F2$qAA-Y>s3M0tXRGkRWwO#IgLW?%W>g-V?+7XM>QS}lX?5OAAefT7iSA(oyH7JO1$#d5yO#HZK%;=cJ6%l#4rwv&2 z1`U5CsdnP64L1+A1udK7Nbu;ovcr%St%&xM6%1l@|e|3YmPNkG!-E?1T^@o-@U{CkCmeU!M9C?o;D zSE(42Y68``L^E2J#)VBJH)V-itFxdzblt@nxlt67LgABIl!+O^hUK!F$PSt!7H4oP zQ^!UEi#E9=;?0O3QSDZ&@4?m?Cn6rkR7@JGmC(wOZ^$M)&c8Wze%CI0pms6&S85j+ zGryv{BWP*A41?eO%LGgvm_0CTHnmq6IWRv!0)SBg(H+`>&4drL2W9|_6Nv5*@?j3a zIDskht8Mw6_MvJShPjdu4+8_H2tSGq5C#z3bjl#2gHr}F8fayi0nRW{6cAP@O~7S? z;XxFY1FaU~dWo+$9E8Cs7=(5h5-_V^$U(5bA}pLGD+`7iv^EgzA>Mzg+d=07qJtqL4rY=8mk?-ccLfA0#KrGPibME^EdXW)*kqum!3bG6-~xIYj1W-O zU;ta8SV1}i^$Z%8+x+}snQd@!!pr~-4GI*{)1cUTpF{!O49qg{%fSEI)XkvsP(oNc z!o$HSgUtf8GLX(-(EyvQPF7)44cj!xU^b{BtgsD1R{~Q3#te+=|DAFD@A*^zZR!}1 znQ;91gjtrPgkLv<%BY;CQ26w9^QmO0T?nW!ylou{ZYbpy!rrTwG(aiS>mc)EBy=%a zu<4BemozDn$H9px+lqR81|Ke^D;rg5TW!P9?#68Y!Gx@HCP-Lj*swA~YZtRa^NmX; zh`yS>!u{tc23E9+58)p5^51J4aQ{%IMvkc%GFM>f=MqQRy&PuTPTzX)p4~u!b>4vC zLXz;&kwh$aWh47GR6}yz>$FbHog3oW`Mh9b z>(pl#PMXF@$0y+tyX;a?%Aovmp_7m?K+^zu+88Jmj+ z@lz{2r0OOH1Z1(KgzV?L3hAsdIPC*I^uzhpN5~~}wt2hdn%{dnbT9bONCm^Tg)*qR z54rI)B4;tWM&)C{2|Z2DljyE%eqotyX}mJQ+Zm*HZ^04IdAk~tcm*-|X=xt^!`W6= z^f5Gj5yh2Yo0o1{mxjP?fQ%)FotP2k7;I&VWSLT74;?2KPi8=qhEws5A(qK#EIQ22 z=o0xd!X+%|*4~Ni9+nf3q8pkuCKbL_CUW;Woi*X5^O!s5>&U8XcD=w?72$c}boL(N z9JS9C4*pJ;2-AhMCHdJSHYt9HvB96K6I0-<%|GGnrq%W9g!{MB1+;}k57^ywxPZK% zacw#W(5Dm~)&q0`d^gkmFY^Ab!vVO!R><1F5d7=g92k-aPzI{zgxEv422+6=;s7k( zOyvM9nx8wc7%&>)b)K%@XuEZgV6EjX3WOa(&?z_Art?~vueL<5%? za7^>|fHIqpp7ZjdVCbrId=C67vjbi8BJ^{;J;3eA3pWGELiz?Z7pkZQHz&BUBiHXh zK_rpt=MHH8#~mK<4f;3Ewk2T1)p1K+1bVW7K4J5wdpaI{gw$rllAR+3<`;4XZn|d* z8tOM7Br&4ZwJcTYefmnZ6r)2#37A?|&V3(+Eb*jvR%LqVC{+_Xt85PCCJsx`Tcp@t zKPe%Q_rZ~jYe+BWrv%bNnbs|gF_Ms++@z=NM=%et(c%n;&+8c=W zmReXpia(RPx*bON4teTep;`udgV0@N8j_`cs6pn!nd1AIwOJnhOgIlKskt=I0J$S6 z%?lMVL7$&Aki%q&nZ+BBCfkx~i>aFJyHI^YYy)?wGKg#cunX}7If~qsx^D;+U z?4=8n4nB^AGOL(;iO{>1)TES$$4PQvu>yWY+7dQr^E1fdTt&Iq%CjZu;dS>)3#jVS zrD@?5E=jk~=WD#kmDPxI#1-uTFZnXKt)452?&cm z_G#$tRQm8dtOAzWM_koObK%!yqoCzZ;~Ktt5d*%5d1d z?rhCbttZN5o~C&HSu5Cs>_#{czTa&Ov?d_m0xp5hKwdcV+e9|p1OoU5j&DL4n7bK! z0k4}#-6SSZ3|Irv4*;}zs}}$QSO7%$dxFV7KIR{fz<=W-fISawj5?JFS^*;m&bSwc zpp)pg2s+G$i5j8l5rsLJSGMucyFr6Iz`g{ghZ z+$!-B&5b4&*A*&~8xbK7>@X4Cj$%_q%ghpp7-lXuom5<AW1=vfIbvL0W_gN0mxtQ7Y3^<#4cE7p-}}cKp=txCP*vr zK3If*Et1cNpizZA5nuuRAXKYxybZ|3jP6zonUxEh5AeKB<|c4N5KmP zxIkSCMgr&vkjuaqh1dn_Fodkz0s^oaL-d2>1qrS+&<`qLz!pfPkj_8~1&I_iQP4_Z zQ-*yRBvKxn;HDaSwIIEDuft9*LGVR^T;PSmnhb&{)W=|of=UYFDCnagg2EyUnG~!7 zh+S|P7wh8mF)bOEVh|1>hk{4?T0tJ1(oJ6!lG>(I3JWr5rC@{t4Z#kD!W6_&Fi3%< z;CX_408S|^%WH;2ASQ4VJW>!!!5sy&6#PSX8>+39RES*04ga+q>y_-90j2i zjsig${gF=t%M`59G_M07j)Gze2@b?j2zn5Z(?d-+#ZC}M!5Y0IAOM9d6s(|+f`|Zu z0&LA7ltKv$u?{3sa6vmnwt`s-x+&P9;E{q*3YMv^qyjXy;FN+Lx}1>$a0jw)20f5V z!6pTp0bEl^g;3-Ew>*dcl(w*pUi^}{E;Lyh^W!D8?)OU^vEzr^PzZ{E@)#!087pML zOB|`oR2pE(qFWSj2oXgo(28_-=uhKdPCgue=9MXSd#&XYLALfR@o1um^7`^Q3$(u@jsnP!#hn{kIoN8j2NdH(z zAL+O8O{t(XMCAHGTk!(NK%4XK;H0Hga zGv@1gPiJGs4&m618@mkFlzDi4&6J(?Xi&zm=dwP!paiBaER+#A z!ju+<$`HSWeYuV2a2R8jMo_T;K9RM3j4I`{Jqdru$3P;dh$n)D|A0`vL>AT6naY1N z(X)4}yS4VHH$Ehs%DbOcnW%UoR$#=tOu{%_i7w1G{iKQ94`Y+lh?k#IRqa$U8}Eo= zVu>N^h_83w{(NhBKVH$^{5Ic2YaE?W2Xl3XZ>23EvWY^cc2%L-B~CDl%)m~!RL4ro zK#_A%8)zA|%#xj0wQ4W57E5lFKSqrx*!?Emh2s{pF-f4SIL=beTI7e}G5$6k9E!1a zG_@oPXY|2SCqUwJ1EcaRWI?Cvp`I6f7e1RIHun)a(|i@8#M8O}eu;O33!LUbHu5;3 z%;L0O^#fdI4eqQa=cnF9|8Xs~B;8|d%!6ax3U7_;hLiAG%g0zPOqw7Go@lR-fP zj!ixHZ+8UnA__V%8r+cmNQ!8%n8<#DM68s;8KrH1Y*Trl@}IrnY|5)B4Iz=SmZwJgd+&o5LgrQjBYIlv`%_0a}Dlew6>o=-^X_L(d!zJr@~zG3wxz z$bBCKOJ@O z(~+o8$KyW5ADv2!pGiyllAZo7FJ~d2`aPct0RKPd%3Un@>s6QwMeE;x{V2?ZYoRdr zTT$LzQQp_${F#!%vC^W~O-;?s z&9`pdx_$e0OH0cifBbRh&Yin=@3ywKwzai&w72ziwcYP&dw8$yabMfugSICR+n+sZ ze-78<_7{&kUi5bi^>_Zd2D*j@x`zgPUkvuWc=F)IlSeO}4m^GSbl}B{hc92>fA!|x zyZ1dKqupa4x;{>HPEK`Bed_o$(>e2{^YhovFJHU9&UVkv^~}xPo15#KpL;kz_xRiF z;J4Wq3$t&&&yFt6PAtuSU7lT9{kpO`ySDaqZEbdaeQteyeq-a??`vUWHHN zT;lT2pUW^B6#sh6#`#fPOu1E?%&LgN{$(^cNpK+*NwUJ<-o`F{J=G1be&?QOO6O(} zps8EvU~%2gk~(X7a?LrtyAx3gWfo2kOGl{Ok(6UD>0$%t_81IOhQg(cYh98Ag?qWm zp5?f949;j#O*O^r71uIkZYp*sp)QojFq1c7S5$+esKR&Ux zVkV?1w(%aZ`=Ay6<-x#~Mq-jS^?gS8)8cyJ=A<3dpZxEhiC@p(qD9z#ApXHF+NmS< zGo*KBBbZ>n3 zO?Qe8l*DOqeLtD!UMMThvpq5J37efwI9-Wf_r3B`2jsNmuA$?G=gN+WHBLwqhZ5-u zy4PR&mnC~~R$6Mf42+kioD8fDcd9*4W81;Y99ZqN+fs~8`r7%5b7k%3K`}9VF(b7# zPE7vM8@viZwwxM1ucj||JE9JlT-HwibhXD-G}usM$W^?q$&$08hEc#Ji9=89zATT< zlFd7O2X9mdPmio@=qZD}tusXJ*wxQh7leHhu1<&rw_U+}czLCP%as3oxmMuFiZRZ|+!!_P;*lOm zY};~Qi~hu^>R_s>qq2c&hovs9zG!sscpu*CL1rhH?VA>#M*=tIjk#DENplws&oEv! zy=<)>$+f%g^+JSp=J!2_L9gyNuI&fjmOtmZH<~GLCo`hPjl7>xwL}#=+*FI&;ob1q zT)j;_>rUr+{2~!mykGMzZ^Nj@JxeVEnfK@Y78>{NV>v-p)m?4re0Nu{WbnO?$(PJW zHnmxa^wwIFF%y#CxtAvR1#*P6jX zLR8R(@w6ZD{*wg)$5LR~!Ttiv%in4U*k7QDfE5PL*It6-9g?4*lv1yza%%W0}!VnDsSE)1zQhzcVOIsuK-=|uT#0RcHkL9FAGH}tVIyK zK@)-P3DzR;@n9!{WdYVJI9UTD8MYzVCZKtRZ3vFQU>Acu2-Yqrn8DM7HH}b$?5d|$wt8LB0QaSDRwIy599u}6D}RZz95VF>~gJby$@Jo{{yxR-*WJIrdg9N~ z=p-||6qR?V+8|SeaAC4{WX!xoTt8y67F}hQ$xybXa+uT5gEZDlhnQ!(d0<6k1d*yY zq(lrtbW`@fb8JTh9}(FwC!p5C*?9Pd8rj*=|sp6Z2c zOBL;;SFY`L#=H>LWZ;xHEIt!stcyB@A-wXXFg~!bZO?vip6nALximp$I$R#s5xr9( z(D$gLYD@={kL_$5&N*53BW~*w*^yZ|_59gl;p$lj=3xH1k7ZVZq|{PF+kP^Nwbx?e z)YrK{A|oFAy)B%Az7P%BMX)^M|W!dA>*tUB0XdaXk9; zi6Gw#J#n~d(+zHOTqYa-7`KRUzMoc7^HLANbdRQjfkhvD|R7>VW!cP_f!#%f$cwJd#1 zu{plf&O$uSc)owz9XX+=#A|d1Scq{L8ddp@1i!?mj&XN08#)IYYb73kRtzKavEFlY z*TfNWJ27B(xLqh&Yha`|JwlnvqKm_~hpQ@JBWna^`H10Y4Ue8jkChZ`SP$bqD?97N8eSewMvheoZ;yaXz}f&pSP+`i6ihmpY|Tr3i)B8506GZ(2pRdUvd z55kJ@{~l~G#-LM&J`QFW^l>oWHXC#pa(^q)VeI`oJvuNK5DZldQ1~CmYX7z5{l{1P z|Md~TW+7YDf8VEv!LrGzxAv3TDJU`1V_0I9aXLnUAy$iU#w1&0o7_w-r_Vi_sC3nu ziw&`jsz~2d);~~bU4md-YD>OJ?MCtI@XjvVBMB%YUlFQyW#E%)Mw~gWa-_`^F*?IV z^1vImq4%O>Y6-+PItm>QM=4ym(32w|Rs&0h8s9;z`_E+>|gXkHBr2Jc@ z#^3+;e_3$+?Q{Q~cm1o)$lu-wpQv%gNSem$aioA68$dAk6->(NRHhM3&xGuNaFSS}N z-ZlBrO8nlczi>zIYop#wo!(@v-o)u0A4|0-3bZD3w5C%uXO3_G5~lIhTYb(=ZNX;S z4`cadP3aX?i4`T$6=~sBF@aSP-qkJmH9pQYJjWU*>lz2k8W-y-FUN`i=h9aEq6F`P zoZy@q;iG}Xdn<)kdv%^2w0|7u-J2BMnwQyFb@uZ0j|M|S`}PxSI_*us^6{Fyjwl{`#L|ea%pVk>W7v3iIs-QmB#6n=1iD|UJ-gI9x7;_s{BVA`|J(Ajh2_^jR)!Z>CYDw{udFVtt}gxl0TTc0 zXGr|@=LbL`0u{-ZjrGa(wU6*mh@SR;ZAO0mTmYNOyD1xjzkV)&m(d=6Ji9A zFV+c9-GbS}bnMnmuHjqJhd;92;tPCO=Jx0h#~sU;h_=?37_=w82M=~-J>m%_oL^z8Dh&*g&;wC5hZ z{FddjkNGPbrqJaneeVE?p1ts?-I(koXTqT5vnPAzEwu1_LJ?YhR!l)nb}2&c^P;Jo z=}j-J_%HAaW+1m*o?FQFX?dFpywY+o*Ph;deY0_@F>l~F&0OmIc&_#Ksqs7;Q(4J8 z8(mLHyuFBzFSAoMiJsRLmru{@?7-PoxLbb!!{dU@?=20J$~7ztV@~YCTkDd2@b+#r zLmsDoi-|Hyd!PxAEZ+VDbx#zNe`(TlfvHmW=Yb}8{fen`m9qURvZwza_TB=j&2`-t z4j$YB1b25W1qumPptw81wJq+FK#*V!?k%nrw1q0}P`pqDO0gPHZz=s>7JIFK?S0R_ zXRmSpd&WKY-x=SaDc+2J?=zqI%sHRrIRd{58#PQ*x^aNLPhC#7!B)l5wZdjzn0dEB z19RSZiTL#^231?J7PYM;FuRS?mTSZPB_uG1TA87yDrD6zlcm6u=B{M(Ad4W;pYh*CfoLWzX!x$Vv-rf zPt%j!BRLfy$!yAFILBy%+9w{M@s?;Da=HH6tP&S^b$|4|^PyJ}w^`f!GQPa4K@+&Z zAnCJa-4pF&Qm3dsl-Wlb(s$ojFPDFyD*jqt%e+|i#*9{;Luk1Tu>?KSsQP1t*^wnN z`a+us&L(CC{(H%fJVttwmne6}!d9HmR%k9mb=uA|Tc%BXo{2CwdvnAz<3}zw9C69; z{SyjNPUR=@!%m;pj2Z$v5h?P{O4X!`Vo|GS*cFCerG7a>w}lZ_$G@uVc&m&wwj_w$ zIqhyn!bog}38ql$xZK7}WQY8OSejI6y>M3GJ5g8+R6j#eYKT4 zEb{jriTsMcH;f~?7x!8e_Uqong!L#tQ}3oB8f-sB#{t%x1~!6n4bNZSM<{{ zh!T1;5$;IGFsiMbX+1-jcpa`wkzsi)gg%L>4ULjeu|*2#_t`)H9796v+ebmGG)6AY z2*?;$O2%qBR#U^gDUR8GJH#H-fR8mrHo=gTim&4iWeQD{EggSgTgDm7T$w~!N)#N) zd`D!RjNVOGp`Kp9cVPM1AR0n=sT{smaKlni9D}Bcf%n040t7_Y z2(a!{Yx!E+8;Hc-31Y6Q!m~0dG8QPTGsOm6vvouzv1c-o!aM%1!>2YeG(LTuyEbsPyIJ2jvxd z6%_9%Ds?C+)hH?DojQe8Rt{4+<#t*@|CFqx{n?tfABeDLWD5vMLjD_x9JxR@Y+5hHgYQOZ9_+z%`6 zlPu|zEbEh^?3=3Mld9>NYT%Y?=8|gTl;+@&?rfjsYM1L_S?Fb4?5$Vksdd?1tI-?^D;GQ3kWaMQ{QH!y$9~A?DT_683*{x4>?&MbF)9?UHS~hKPT6phd_lnUyiTi z;R+a;d??C&UzEFBlDAQs_pChsaYey$W#LkF@j_kcTx0o6^W~YVS7vWi&vw+$-fW!h zZkfAtZSG#{LSN_7(5cKfXCwE;?mO6 zqeqXHmzP&oRvtfo{N%}#r%#_gd-m-4^XD&KyjWdbU0YjQUtfRu^5q6tB=zdm=H}+s z*4EC>&YL%H-oJl;aB%PuG?4$pz9>-c!Q=NT|BrwE_-zvMBRF$j#m5d+7uqo$kmD^&JAG>zZ$+Q_5w^3{M*<#gJ8N=T53He z6W7sbmPjwU8#*dIr5sPk%2g2^B4QbzSga5SFKD(Y&>}O~V9Dpda>K`LnnT1=XsR<% z+5o29?)I!Z@|E$1z1;6>fc`Z_;?6|)=1kvlfO1l~+)9e7_+2jzwi%u=ZE*WaGYs+#9zwxotJ~zBr~lnHwa|$sABQoz zS-6R~ey^vloX|;mStJjuSzHl6wZ0{gZfmNa5zid0&!5JlIp&+pJWeiv)0MMvtu$Tq zscF>9ZR9WDE95jTN--GY!#NY$H#WMh>s9B$2a1!9*s9W-h4FlZW3?GszWA$m{nGXL zuc*|wklbZir|HRMdUo7B(C=tZVbHI>w90136&~Q0$`M|i__i)j z;5M`je?B)^=+WgTje==>=K69ai92u^5c z?sa0JUT7>Lyh^TvR)OD4CFisPyO%@O_3f1Q)MI!dfZ7*eUOu` zm|9uy6k>Cn9C;2WgWkLHYj(ncwi}*i^rF)z0o$K(y(}=x$&al$3cX$+%34y7PI9nm4=ni^L3cnS3dmKp)I3IftBsR z!#753j5^E^o4?swf0JSvgO2idA>|*u&xr`@g!BQ7&xt(!54s10N&}|9zq=7o{!l0k zBt!$*`kz&l|7L&ww=xv}{^tKXUIAzYgwyah-X{@8NUS_cht^D{rdG$oXR9;`NtktG z^-zf6EEa;ug-ASqUk;VgF!ckII54<8 zx%pH5Ou9lZVttLl1Tf9Z8q6-iD04b74x9)@zMeo}35J+pEO<9E;1s(T8=mND`y7`Hj#aOp1S#m7e8^0x_Pcex?N;ZuJ-aQhZI4y{v%WrNq~3 zASV1}M%pMQ=Dd|T$jJn=)VajCd35YhTx17GM2QXu>6%$?&b>G5PsW|V_%azA8E$I@ z2B5&*0w$h8)|NQ|mMMNFXjj|O_$c6jnZYK2Id5M~_!K6#AXpn@e%1!~GzIuR!lIY4 zn3uQ|d=R221o;#b-yIRWoQx^)^#Deq98VN*y&QXqeC>gvwb0Y;VPYHzu)lKN7w2aR zoG~j{bV-r^HsKzS$a*-YyE}kf=4G{0IB#pf=${DX5AyI}cD$LL-X9YVrmi5+ z`B5tNk{hZeFyMJAwmKS-hH|_c6$<3CC$<|4Q}e#qh!tG&6gJ_1{T}|i5hMeaXD^YQ z0D|zK^I1#m|7|~{2wguJIYdM|5>h!VmP^PJe7qkLNyf&rG{c7k+a(AsZa*Efa39a- z*o6DP*F9$xmNvAJk73WI!Vw?pC^oU8gLvr_=?Iy(CWfyrOLx4G8IbgOqkj@~!R!KYaJCCm@+F%j)c z*v9_4YyOm%&RIT-pe|~Hvi)jDfF9|9O^;~`&dT4mKE=E!KRidMbf?`ysGM^ym;Z8d zn>mGY?9!<2y--N0V6P8xiqBhD7!_Gf1$vghwk>d$h30$ijI^Ax2&oiQ@?|BWoTJuI zPJ^&_68R7&ikBx=dy2qoRTBnY!y z>)E*`QS}Hxh^#Q=JXJ^obp#=zpElw??_`DC*T5}~I0juUS%qRdGy#OC#uIXhFc89F zLW*btQ3D8w>&=b2o%)J+1Gr>l)_0$#l%|8F#-rNkXgIldA6rXP$__!{wU+VpWjdRc z=oYsT3Bfn58RWC`ZKG0NR3?+3(_g#gbq@w~WRmpFbuB9ijp;rOe_~6+mMs3(gTJ-W zw0m8>&`9&m%^$X0WJBWb-BtF?@b|XdmIe?6dvq5c6u*TaAj^&xi?!6U!oqslLZ^x7&){cE&a7eUrQLf&=j9lQ6}@Ck$I*-PgsYuP5DEMW zeN}9nBGUbr0ECmGIIDy{LYwg>LR)L&rVFHIFpE_r9Fb)D-2JUn){ClF*S>dunn|4$~$TWHn%|Lh+ zk1gT7%G0ah6`hDZ{Yv4_$tNcTC*ki4&avPnEg1|5Pkc9^o?zYVKpg;;1XPJ*(sj^Pss3JT6PP^O#&P+`$P8$KKzelsGd$iubnq8})3pjef9x`QJ&+0UdV zz#CMm$Enz2Pj^sxKm}_J3;>@2s^3(6bc(NWn!5uigUP-o4Hx|ZvIX_0%*z9mvPbCS zx(A|2(7xJ0s16F+xAas{zNo3;FeYxGG(T~!1Cc%`M?j}@BGQiy4+4b?6f01&fT$lx z^~rU9Vz2gY~c7yuRl5Hfv4 z4h1#mM3oO>13|R`Rp_(+SwDAs5cUj&`Bi@2pgse^{tp*N5V`{N{zovzpOO-j@zFr? z53B$nMG)8mfc77R69Ov$NRkGo0gyNNEg}>I0H(URf*_P1mdA2?U}^`IVS=0cl%gsS z@>5bUgW?0E`aj@IU>!@2?{T0C5bT2>XQ1x?mWl=He&7iJGXFz0RiOQUm5~N24AA=n z`9E+20MPNn-Uj#szQrC#L4U<(eM^i7z5(DK0Ahcj^j|{9Nr=k=xjm>GK#UL6L&d&c zpo{?Z{dW&HAn*s8eE=B_5$Zs>e@I6Qg#3p(+CODwKpHg2iU!($fNtgz^nhu!eJ_b_0Hgq604<78ClWJH?ps&0pa@(_T?#+CYm@j~xv?_s2rwe~F3sxWS`| zMg*|$!^ z!S^w%w<^h1iADX0_$Uw~gJny`At7)2G~Fn>tjFj|rzyuW1^&;IPo3JsUr20>gUj*eUR9j>ri=XK6f zwJ$|ggK1R#Y@{jKddX)@1X&OW;8JJn#D;4y*x|!8D6j%ZHUUx7JYr9tiaeO|sTpEp zXMH-T9mQZBLP_9}Cy6!`rddQ(L5`sEp|HdvPIr>`Uc5a^;#J?%oxGz4Y>y4p>)kAWJa?>pp2BR8AnVANGhGFu4~$N#a&tA zUN_sSqqr%@VOyL6_M9;WplWh99w8CE*j&ty{a6F+5G!Hb;X=#?i{ zmbb%mY6MUcgw4pZW=m>NQ4`ErdcD*UB5Q>b)H@2*=$s)#krT{1eszh4+Gib%#NB2+uQX)2 zB0Y^fl1|+Y>P7Y{)wnoEZ|3jJYBQgS(1LK)I2NAGC7MaFo_yunMHO7I~-e`fFIBy4(mIBuiCZ-|t48zq+Or|Z41h^MNPC)lI`b9OH;D%75qCMgd|eeo)> z6CFb4GQO4|QWL_hfPfLBup-L(6r#isS{0z$!S9`Ypp0ClS|uS6Sfiv5UPm$zn^K+r zL_ko!+V4t8_%~H37|owlNc8^|T)otYs(8PS=shE$ZcDx!D6Tqx_KHx}!WfoIiOjfE zCTs>Q;Swx5hbb(N`C>kcPXXMsfXywR-65Sv-%n7>SxVPM!{WTL-9>AsU?-PAchm(x zSN}j)pGbF)1a}Xtha1iVh4*yM^KdHka4dJXyX26!)Zd>hUR^w_^>uOl%W>W8A z+2rli;)}W#;C}sr*R>G;>k)x%(V^`z=WoV%-i>m65NXpFVKEqPHWF$&9%MRo-fYg- zV%gnl)!t^y((H|?@d1b;)6@N?r}OQs&iCU>`^WL6bA0K6OZSKV*&haaKhEj@Fx3BH zWB{6SP=HZ((H$|#qo|XJDcwgw%;A?zd1X8b#eOY>iosS z?TfeP7k{767yLhj1ip_7dlMJEgNfh9qBqm9+v%x0+39aCWxm5_?d4?e=N{WRK$$%e zDjf6ng}Hl0xo?W|)=KkM%JLU47tU1`&(xI6)RoONRLnJBS-4jHsI7kaX3NT*8;^TC zpA7e`jNM(D>Rp^3SbR9R_;7e(W^{38Y;kscac**PetKzPW@&Nm(bD37$kji7vG!R)@V_p?ZU-$dTwpLMabXWO8-+m*Hx5k@XxdP+{M*e z+$m0~X8dOzEf7LK$8KIn=1^H!(w{nLdG_EhqzCNG7u^fpXGk>`x4D6G#e5ns5VsEQ zkNqZ5e~!bZ&ER(K2hYh@(yq27WNaA)7sgz+dkHE1vx|oP2Rpw{P-MEvJRv#ixb6Hk zqjs@PdCMpc5fUa0*2O#~|Kw6}i<#fJT5(bizE%dPvY4z4!>4-8!2(HH7d)WQpW zP(1>OGu#GW{l9JqZhU#gA}2iOcxvO-%#7 z#K~R1=G+VR#p&U7Dt>afZ)}CFEdp+7Zr7GdGlX&Z)$ro!Yy)36XB4iW^{!C=a+7gT zS$caW(>K_y9?M)ku~+JBQnzxQ@4&m|t5na`XO&QbGFhHG%5y0>n&Ck%MS$(4PYmUw zvUt`^`UltXlq=b?`(+a%IAT7#d_lXXEgt^49{Y{shMQg^%))oxc<`4h1PkMe%~|2^ ztQFe33TF*wr*f_7Gd-sss~vQwSh@~9ESQa%nZgTH8!-LiIByrp_)N`D*7R(af~N|j zhoVHTR9@Fop=d?oV*R1Gl9h*zmp9V-(&O}HX`DcqC&wMPuD;sQnW^w6`6XqP9MKL> z)7vLzoW5whd1_=g(KEw4GF2tOXRvNXHK?SL-626L(S;?Te2D8!JdF$}n*ZkEj76p{ zrU+C|?j%@z%3>;d+m^y|b-}Go%nzR1{o+@x`z@x?m)GrrJ1g&uu{2O!iQ2c~VC-5c zjFLSo(a%<`D~)@t6DMPukQe3S9Em(D;SYU4rM>g@ZYI-=qlx_wKXQ-c!wLMahl*S5 zMm*n5>7>yZ8q(%|#UuJ&*{D7r=#O&<$p356Rsm`=2T0m)O&_?izXGChQXZUxCte58 zn*hLk9C~>$3DRdk4>+6@2bw`ZxrN(XV?C`BUF`t>7v^XMxWH!_X&~qlv|XT&0}Wl( z1>d2hIM6Cac1y$yCQWrFUq^1Lrd&fL9} z8S8y+I}?}e>`)!x4Z6#osE|7ek%0XIZ6IjMJk3o(8wiXGua?^ZgSeKOJeU-R_jU&@ zA@C)nqnv9l`hp%4bhV&21YyJ5+uJ@CW}rO;)E$_H0;6+;r`~8%Jm5i>aLJ&}?2ZWq zT_@-qLC=EoZ~%^n>>v$3IP6Yzq>7+aZ-yK&Od#YGflmT-j)1lVE^yEuc14E*3KrNY z7E+SFfB%kT;Qq|6qmf+KW_DbLF~J=JD`tygU=!&qN5<9qoH91z21+HJMZ4T1BX5c*xY*D2`m)A zV*z^bA#I&!*X&m_(m+W7^Igzozi0&um$2I-mq6F%YiU-Jo4TBe1)Ue@!hqQ#_`J`~ zvzuRb2SM?;7AFHJXK`*8Gc}%nrIY$^^f`am^ocn(;xDB^=y>IRD`~iVA;ol&{o~*< zdX;bObtEHr5esV$88mZ^F)e>{xqb(Vu&^s&DBrH50ZB|oMCY;pvL8cqicDm6biTm? zf}o~Q^igOc#28+QeuF0Qg)6a)q59!}q9<(Mq2)m1aP1c<)#sU*>^F$5t37B<`#w6UJ!05ZnwbjO)Hq|^Va|K zZJHZ+4Qn(K%Dy@afikNFkwKWzGrbh-STvGEJ-RoUQrc&Y9;Oak@5@4FLWt<(_XD+Q zR7XXSv;t~%N%Rc0A{4~bSC=5fEatt)NHf!37h-cbT#I;~cOOE{I0w?lS{A`(I-SbZ zVsf#&B7<}R&t&-NP1a1^$<^xuYd94>b84E!qS|Vjjzn_CYZ`elyEP45K@_#*ELA+( zc(Dc3St=bWQ6dJ|8H502Tn@Y44pD0{t(qBb#aGTyU?nvZyeJX3$o6-xA=cUQx4I$a z4yF~`6oXPo-A-{+T9UVwNp~T5I%4GMRq+`pG3S~zM22dEfR>1eJ_T`(N&?jDZ>us8 zDMPgxN(jogEr*$?QWwf;(}ov!Q|5G83-$WQaZ;(pXzG(X)6*=QX8yhx9su}cj z(RE%pXx`n#$Ymg);hr-bdLu_1?_Z z=-M-12xRu|Wp+9%sJm}IOl#43-H{NVS!`YV^Yc)bbWJVn*#pFR{ESN`Y^X8$>%%j2 zO<0IK45KG8$gtmHB*6^lr_d(gB}5EzrFnCu$zTXh_2(gpc_aQ3Y0+bF*tYy9Y&pv8vg#|PZ2Ra2JLqLr@K!|tOl4wa>AYkj(W~2!s zH6mW6QYD@vSi;LHTJ}+R(GjseN0XnP>|^Ta)q&LG$)n78X)I9#JW$zW$cCF+pih$2 zg|;&ENtyl4Pv{9r1l#Ljzn58Xu7es2itUN*;$((*9NGU{d@R4m}A28T2_ zs=+B97~mr(qa+|84o-bQb8>MA9he#mb1?IB^AZ!&|2I0M|NX1}cYg)IdL(kHll4dt zVlfe02#DCHpr(@u>|Gnc62X8Gq7s2lXW^%yTivf2O5)QewIyfp9L^H6DGePX@X{}l zB&RS$SqM$%sh%g?V?}9ARPs~O39u0gnCFtSGBal`fsP$h}}$aKX0ZqSNId_u3Gzh7jMD(107^ zer*vR9g$8qqpf>l%0~pjW-@huoyzyj$$0g6I~_~-KVj>Gsyw-sXmX=Jf5X{ zyh3}wiSj(~M}4)m{s!0`q{ffar+z5N9m+@_N=Y6H3xOv;6y$%LQ3DLnH?S1h-}iHT z^rwu}k2zVN^M0deewS5#EXsRVT=1-6;%xMkgjW!)=_<7?CWKu`M8lo&8~4U;_Kw~k7zU4#k&)5S(Xp|y@$vDA ziHXU{$*HNS>FMc*4h8$u z+rhQ>{cC%@>-!JZ4|>->^u7Ghzwu${)rZl|4^vwo7Ir>7d-HK?@8kOqfCu^tnvj2i z{`ouE=07k%)NmYdy)bHHZ%}yw64b=~>nr$#WT_{^OUkkR8cQ8zaCp#`;5<1hn zN-Vc5F~3@~onms_IZY)jWIWHEdU>|l`qk23^938-mV!$=FTcqdR2gDg%KiGrH*cM? zc*?!Aa!sE6m4LcmcYalDw8{JG2ak%Q-qcM6UCVu%X(!=D`{Ild0bDh%he_z8L)_ul3vR4_+>{RvJ#_ryR zV%XsIUE$2<&nDv)d5MszUtPsh)K{+KiMsSmvkWN~Y7iQQ%ZvaI#Gbr#?2Zhz zE)?u3qBAMyN=5PTgCXsdM}J zlbCjG1VrI;ji@4#YCZYHPe@VzbRRz3f-t9pG`>vIlr85XZMP=W+3#Wcbw}v|49msn zdymuj@PxXrU2bSrLwPaOEXysqSH-qlZ`g~qTpx8_PvgTBf7=zj&&*(3dfnJRAfn5- zOz$e@ZQ5>Uk_ew3itRwkF#2 z4_c*SS|22@vI+LzC!7%I&x1R%^^?yC+`cU zp&j7G?)Mzu^`zX(n4f5tBGeI1!8t5Uv}&4>%p~tzWtjb{x&LrB<*mxci5iuk3UgTU zmHmk!`?xMwdy%{H+|uIfpB^RmoKj$xW(fv*pc39NgQ8ZagC~KEy2CZ)p-+s0w5)oF zKDoLCW|}_JwylG71v>!g8+vM8mn^K-vD)gRf>nFY(N%1H@g*3WKx z^fr(<8P=Nj&K7Rp*!Kk4JaTSK!1@-^kjyW=pcGG38iuqu=4+HlKx}svsh_LD#vKmh zi(22jJUygmI6_gc$YApg2p!p9rm}~4a|G|m<%M4sQ=e+T@&3BJZqt!h76vyOvG|+C zfDFR@``F^Yrf8iWkwx^&p1mh)+#_q%DdW&8gSsl?)hOd%D-&2H9abS7RVE!@ER89U zPR^B1!%JVv5--dUDZ>d~N#$?E@ZE^#?TX>)iR8H(&fOQxJ#>M4%%5k{oA;py-@G&b zBU^zdW`ZvagkEZiY^#Xwo)Uf|FZfo5@0|qCdtuH!KGr=>rad_Q9y9%W7~OkD+IRFc zZ|P`W(^BtJQ*BdGZcoG^G~>l|3bmDHW|gHJug>y#@`vDlNll zT82|}42twH1qLPsCT0Z|xFRd75(oP!E>2}09uM0*Tj<4G;eod! zg6>8I_r_cth(AA^=rf+`HkI!5Fw1rpZ!wo^wwPPu_q|J2fc4o@kY}`Tau~gyMLGb&~;lwiVwIt_rQSPVWybq=MZ_5fd z%1f6oSB_LR-l@KRy{5CY?tXUTP;%2arg<{q>NNW5bkeoywCj`Et>Z=QBV`@Km0g1k zw+62D^mW~N@aL?h{{H@ffq}un!J(m{zf!dR6N|y)7cZW!t*>ruoOlWVJN5SM+mo5a zhYugX*y7WtPk{aU`t|F#Z{I-C`tjQXl&*iVdYu$4jo%%GT#f#>tQ&$l5nz*?PA}D zV&ke8?$nEB<*776_ltM8MGapLe0s1#cJydXtJvhigUIg)=|f>!7u6T^?kX>CPJbUP zukRosE_q<9l}k{}6r4v7hjA9}e96NU44s|lDP=OB=Pdu0!;n~!0 z5_IKKxy&hirjETb9$!0@I+CXoEi%Jlz^46_41=}9CzKZJn+kCUGzlTYpEe3{T9)dQ z@m^#g9u9oH>=-N(R$=#~kDCBrBgrmc~>muh!B;Uu>CMw#q2wPfKrK5Z)n6;U- zp~Lm4S`jmn!t+%IETZ|ARobW{sv{i=QC&+UIf{WscU;iQvTfWY?6cx)IxFJ+M>*}e z2l}wm?>a81j$A%{CY=q|^9)vdD>mu3r|tEb&V5xBYMNy>8xn{+t#8{|{PDZEOPi2t z3!RneZIhWF4lxf@;5AqJ6O1w zeyGW}A@;0BCcaHij=`p$Q@)))b=3D&KPbd)Fstmxn4TOxvFYy~kXN z_LG_3_rLx$NgJ2b?vfb#^z(a7m(VF2Zjbl(X5iO3CKMbJr5AM9;up;n)}|o8a?YcKng&*-53Taoh;4{&QnCY67nc+mb!x?G4I`8r|Jy4K7QO5d0Crn_rpo zKTsi5>o;_3_j2?*%kw)`->fy76$kBK);8encXxXZm#pi+vpo&DRLptz8)>-^W)gFj zI!ih6r^Zg^?1YB@=$>tPEG!r*RKi2y;9ugX3FV#`8=&SuA_(1X#3>>MH2N{5v#=R@ zZW$gzja5?D!vX38A|nD4O@v}5Z~K+|V=IJRI^7D|<9C>dC&;ed&uC88}3l3}K0ox>&o`W{%r z!6uQos1QKRaIkUx03+pu*xu+cWieK;X9R2>0aOi)3Jz9^faM^7rvWnBLp|LS+6M49 zVCM)RY5u!+{Qv&_|Ic{^pbsFD91ut)0bwGPkakh_`jERo}^|CjElz_)TK)Di| zLBvPsk=Yp73t=J?N#hWm-GH$2SbM3N&DG+_As+S6oS9c>N@7n25t-S#)Jky;s#iX_ z+EiFMR|!rTckL`1a?d&VxTY2<4Jm9q6oE=YcesX#(UGIk4usybq+p{iK?+2YizidO z7aUptlt`{AN&?GVldNjrv67)n$B=9DKupC85EJRu9!!^j7E=}SezFE2d zT2T8BcG*Av`j>pOGJP^qUDA@xlH!*o#PY>N(nW?u0vCn&y#)E31$b@wc})3u z^!d28c)8B-aGm1el;z=+;N}qGV&~;#=j3E(<6wt#urqP6GV;UeMc@pgtPG;;FhMS6 z9&R`ys3NtII)-Nr%#Dm~ z%*-9FtUVoU{84rx9uDEYE@6IdApt(Y7XpHUf`WsCLqbAALqo&D!otJDBO)RqBO{}t zqN1atV`5@rV`Jmu;^O1u6A}{8Xfy_cNlZ*kN=m|FvB}BFDJdzbsi_$$DcLD0dDx_) zBup75;W9eDG9k7mF1juzvOYSZAu_BfBBUieuqEtbb7(+gs82(vXI-d!ZKz{yh+S2P zMP-OlRgiw|1-&L;y)Ngo_bfCA4b&!)N(;&|%ktt+q=jFI^RJ5Xt_yQ-2ywj<U{sxM-$6W z<`$o?%&on6_;O=&iI=Wt)GIjGVKXmK`o^{ow5%~FXqsV6?W*<9C0-mLI9dpX-2`d6~0K|)*olN2NN zb9d47^13tdHs=DluC)#Fkezb_hHM>!Df11Z0MrJHQ85Y+Uw*u&IULTyJ~)=VLo!@{ zb9JNVhdJ$2FPZvxv1g8)mVml2-0cTd4TT0ryJX~vX#|yK&H=F%zwLTk&U$sddx4C}XZK$PnpinG7 z3(H$})nYhU?ClNXKKR?N8!G24x~dISubJX#gx+-{Fziydx+s_gI!4{fq};ZuRp-+- zmwtGQJ}(qlbNwV0oi2Jvv8a3H+pcP0AF`s^r*~T7skDBfP^JXzF4S;ukpGNIOQ$J1 zCuoqJQYX`OU32ZyC|l8bO5a|1^T9a(mE3eOqo`Ql>+;;b@HU=xWW7PlX079{&340Lj;^JI}u{o`t)( ze0AziFgqC;{N}t(fW!gk?MdPqK%OTen-lN}BsKqlJOSVYuoHk!fJ;x1C%~LQS;9=s z3NRXx-Y4hm_iInH``lD!XV5A>9yw}oP&}|8$$Qc0ca9lVu@`E9<&o71;w&t zF9Jf$Ncfh5>nRxL@JIcD|Nhe_ z{~FE!oiUHCwXm&|q^+lttslZBP~SS- z)H2@M8slJ%b+Jx$wZXZ8O)GYIFNY#elQJ*8N*|s20KKb07VQx}HzWOfqAuQx4t@|5 z(ia;#5Fa*_5I&p`K7tM(MTd_i22CWNpGx(f#<|aAI?i3PT)-PH;q{iYw4Y+NR-+Ib z7ZIC2h#gmrw>Ijp4OF+aPj6``ZK^7~IxWAYD6=guwJRn5R$Sz*jO1G-xwk4x?=)2R zv=IkJdLQkq_B~zRU+{S!cJX~w$h+9^*XWp?r1&fhC7d{bVsRavoKTm8JDZt?2%$+laA zH+%2i8STD5)!8@KHn4PKc;&{(;~Qho+D9LEjy=9LwsL!X`ToSxz|_Ln^xWUi{{9mh z6=(!OObZBT`44H-6Atws2-IWI1OkMbxaWCPlsK2&@birt{(C?)m@yd+5c2DGA zL!2XFH9}LR$}!pnDq&(I%@%dqZ<{)Fv)E1*J;dWI{5+eKy)J0!=INTupXp+`gOnGx zdaUY4cVi{bCO@#xPx}*7mr!6tum9Z9UKnG;9^2E%dDByO)uq@h(;uWtK8!TBRk?(8 zonky~X`zru0Jn_ET_J$QRSUv6y$-6hhrPTSiLZDymglVM#8jr5C6xuGnX*ks6{aV% zL@m4G$kS8t?2mCm=_w>l#g#ZwI{4!Z(y_?3L}J#+Dnujp`7brViEOlu>9c&5Q+gEIK>5SAI6Ual&O*rF@bSNdu5ELRZQGGp+>N-8D zcZ4Iq!QqWHj)bCu+S6y@jYplK)xJXaYZW(}TD>TID^btV#jjRt?!K_Pe?;LYQh@Ly zm$tF`E_SsjSkC{Nrqefx&Q70dQDIG^DEQ5y>xsDfdWh8<)T;CMv6?(bxgDd+6ov2V zZW~vRNh{5!y}P^Ow>xH_B{eK9+0aPg+atOnyw`i#M@-aUi&;tLUg-tGDcybNvg`B_ z*yg5qRvn4`ySh}f`yz#i@1s(zo>yf%br0PS>MHaIMX#}x^vK+lop`!m_t-DJ+I{u& z8~Mg-#HtFjGOt5F)-`qRFX}A4bEJ^eO;GqC{+)SYxK+C-S69@|KQ48GB<$z4WnF<+ zA62v>TXG(!%$kqWd!LS*enfY%*0QVj{MNO3<=BQ$G7nqEjgK0uR83%oF(H$CWR|FITdHy+0X3hy0pd?$wIoO|P=&MpJzWa0>WL8;u%tk*2KJ;< zk}?8Nnda^W;3~kXz)uPswP1Ow1V0x5tpLe_C8HpB0UVP6x`OSbaB?Pob^!oy^(9ZS z({pCIxdILqIAOCKoMg!80i^AW2|Ppow7o+JR;Rx-&!+GQ={x z{V0M`Q${HH%xVgfRHdC}TZSJ&LqkFtfX_koqTyT+7BFhR-81!+9&Mx{~kPz2xP$O+M&Euj7*Pv#F5z_Gm_ z;Lm^YI);@0s`pCkHi{YL3YkUmTYB-DneiAQc?^_z^ksOoL<9^( zxy^*Q>;*Wz`Pf2vS+TqyCQ}n-h>b(Ey z3%;sg{pK~0E6V08FOSvL zJZNg{ynel{t*yPiy`!U}v$M0StLtCcxb%k#pm%8O{_y1ek?G!%>A}&-|4h2Ja3Wp% zEnn~?T<|zs@FZFA&)I?}@q!>>aC38eYwP&h-Ud;FySuxucX!{seho4Qfj;2G;j*{C ze{doZ0M14r5I7bG96K7n|NIFSErE?oVEfYE(b0R5F8u4)9w1ErPytY`4}5tz+J@01 z{=WCOl@@%C%QGXgvD`;}Jzrd_j#paP52lWh(eQC=#CB-S)q>T>X4h|jq21IpR}uRD zwXmacHl50~a8MytKs5bqjREye2(KQ7zQJ#5+_)6|p;A5W2yQO0!%%O?E$4b$L4URi5r0HA?&xEBS+l^>nlHbB z))Rph(hq};cE40lQ@|}^M;~4o#C-l(Ah50CdL|okbnZaD+k~e-ary^|05pjAQ`ip~ z^QWUx4u~xI9p4zJ9B*5i-qH)p3{z{AWvuPz5wmoZuD)4{&(f;Vp66#5gU4ZotNNJU zB|D+Fp0H8=O8xZ;9o|H1sBWAdOR3P;D!s=%EPUF|zM^ajZoY&SdNc8A@us}UH4@)y zu zi%IBrZDvmtU#3&>k>|LuHy)GRU=kTLEOvCB+@M&r(>_=iZrRF4%GPT9slm0A^xPP$ zORE71Yffu0>`E%h>#L$U`F$iU4#gO9S>-0&a>sOV@G#t_^*hN8QR*JaThrFTuVGie z_olQFjCY6O*4qZ} z5Rb=yw2IO*mZymhB=%|3Yr}#D>HT9-^&adIZ`wLO#Rm~H_;&l+^-4WByebr+7R7q| z^lJ`@4(`FP9UU;cF8mqwSdM^fba$k}HB-?n6E&lG3U{QDpYA7wbxqzUEb&i&R!cw8 zsVv&^;vdMI%TpG)ri{o$d_notOQKy#?yo55wTgg9{IjT2MzfDgiZzury)5d#jGTeZ z-{M>xD_+qy7S=RVR1fu@EwUhcqNN!3rLXZ-)x159Hgq*;bccyRQ^E7Y^ifZ0K~>pU z7Mr9`iC|wmYFr2{Kp5j22a(1Lr@$D-_F4O~R(Kw2U71ZPvAP(id)LIP)6CO8SMcDbDZWzUqT@(aSY8Y)Sx?srx35rN6|;7PA4Pw$FLBr zbHYOW0HS634bg(pKj>iroCQ!7pjZHB0gwfd6fm(TK=ztn006kaF9*;qz>ZD+{;#t$ z3ccLHwj*HM0}ERLoOL{{38+_qS>a@i0J8$LstKi!w6qe2^GmV|fe;peP=T)CmajX& ztO$MyfJcW6&I!W7+>S$ond?O=Sb_BK$G=~J|LRu&S_vUg(>p=5>~z{bronu8<+A z#FHyD5)!IX!vqiz$b2a&jU%KNgG2*=s;(r!ri4(N4w_{)NFS5{3f~q8tn6KciW5sf zt3HS=wLqDnre-YuOAtu}>MPIwMF@HFFRxcnQJn8Xw^|-g_O~!^Vbsagjldm3hE+3~u1$xFC>VT3M16 ze!=Zddo}P_CB@zzQ3pa+;FFZwekJV+5i^=YjZ;r!?S(28Xiq{U&1AT zgg#RvJwIoI{_cjWwS|sW27$iLQ~j+TE;jB?mRTt=z;%_G66a)X;Nff?>7{qAt_XN% zFBfGz9Bhw|3I^6^UPw+NpM60?9k8n}3mWk%#oz-RoRt8QWJX4nO{(mb%2 z7}%S!AWv&?>}Fh4(1V+GS*h`$KQ=Vb1+HjxObBr98W`wzG?zKsTUM23g{kwl{qWzc%2(?V`E}Nv2j7b$~-gF{-nX^Bw-7>IV|PP59I4%)HRz>k^j18we<{M+Jw&W)7rqm1@OL--(3uR9A=?f=Zd;1p3 zJuduzG}+Z?zSSR7vpF|3Y76Oe%_M`9h)oi0sQ+8Zf6OrIp_D2;-8mcTBlJ3_X zt_yBg_K=)x$f{4Jou-##@_|R5)D-z3;o|GLi)ud#r~ zW#T#6-h(JZ>oF|C_7t#u@5i0%p!CzCfsX!8u<_MHe%IiY701!txIiqB(R3FIp4bJ} z3!J(|#>z~uK9Q&0HQ{6T;cN}P*87!8#J=0K{V~h1eTLBHy@HEG4OQC+#1Nh8MZpm7 z3LbmN=<TuyQ=Rsl-j8}!8C8iTqtqywFsT# zjl7B678L-x)yy~FZf;~AWgySb?wn&?|L4jqhv+rA9C-`FE#6w62>^c6cI(Kv4qanX zP{Z0$>)?_tG6caYdBo_FA%toGHaFVI+otsvxlt@zY6msYrVK=6Dd=Gxw7tPP^3svQ zlF@zt>eX`oc5^V)tVu!eSh%!y;3{v0exDzB=h#rhdaS1D;$xRZRsh5AmLxqKMp*LksTfbfut3$&4AW6LTA`CZT|N3Iq1( z{mLR@RcLAL0$K^DrG>x}30CvgXhw>rUy@|_fuYr{R8Ff6U?wCYF~ ztHMn87pY>`bBWnb#z4ek@#n{?pC%qybKrhA4KSwj+`hcVHO>W!vI3KtPsII6<$+P4 zKos{nqkc$-c>;fB{K$`@^I3Ighv&W{WrF&_@h)^%dq#AYe{Fm}N9u9et^L^pu=dhk z=kT12=r)=a>HDU3v9TsJvAwmKOrHu$7VNG?Z?}l$A0&AWL>{NzeInuXZa%CJJC)?j zIsFyU#Vgtv68-R8^RU5r(`9Oq-xz+XkkieN#?694*_r>_PzHleF-V8?eyell&=o;H z1Z@-aPpd7mtN8}{0I2Rz?4hCyeg6PO9j>*Ecqihx{LfI`q1Aw91;&@2mTIWz&_coN z0@d2anh&KK8VOtMJPCZuCB01$N>0v4UHc^w&{aBpz_UW=m$%?^_^5BH;p z!v&Ot>BK0;cZv^J3o3O-;>;JrZ;<4ea= this.displayMax) { + throw Error('Display min must be larger than display max'); + } + if (this.minorTick < 0 || this.majorTick < 0) { + throw Error('Ticks cannot be negative'); + } + } + + override getAriaTypeName() { + return Blockly.Msg['ARIA_TYPE_FIELD_ANGLE']; + } + + override getAriaValue() { + return Blockly.Msg['ARIA_LABEL_FIELD_ANGLE'].replace( + '%1', + super.getAriaValue() ?? '', + ); + } + + /** + * Create the block UI for this field. + * + * @internal + */ + override initView() { + super.initView(); + if (this.symbol) { + // Add the degree symbol to the left of the number, + // even in RTL (https://github.com/google/blockly/issues/2380). + this.symbolElement = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.TSPAN, + {class: 'blocklyAngleSymbol'}, + ); + this.symbolElement.appendChild(document.createTextNode(this.symbol)); + this.getTextElement().appendChild(this.symbolElement); + } + } + + /** + * Updates the angle when the field rerenders. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override render_() { + super.render_(); + this.updateGraph(); + } + + /** + * Create and show the angle field's editor. + * + * @param e Optional mouse event that triggered the field to open, + * or undefined if triggered programmatically. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override showEditor_(e?: Event) { + // Mobile browsers have issues with in-line textareas (focus & keyboards). + // Also, don't let the parent take ephemeral focus since the drop-down div + // below will handle it, instead. + const noFocus = + Blockly.utils.userAgent.MOBILE || + Blockly.utils.userAgent.ANDROID || + Blockly.utils.userAgent.IPAD; + + const editor = this.dropdownCreate(); + Blockly.DropDownDiv.getContentDiv().appendChild(editor); + + const sourceBlock = this.getSourceBlock(); + if (sourceBlock instanceof Blockly.BlockSvg) { + Blockly.DropDownDiv.setColour( + sourceBlock.style.colourPrimary, + sourceBlock.style.colourTertiary, + ); + } + + Blockly.DropDownDiv.showPositionedByField( + this, + this.dropdownDispose.bind(this), + ); + + super.showEditor_(e, noFocus, false); + + this.updateGraph(); + } + + /** + * Creates the angle dropdown editor. + * + * @returns The newly created slider. + */ + private dropdownCreate(): SVGSVGElement { + const svg = Blockly.utils.dom.createSvgElement(Blockly.utils.Svg.SVG, { + 'xmlns': Blockly.utils.dom.SVG_NS, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'xmlns:html': Blockly.utils.dom.HTML_NS, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'xmlns:xlink': Blockly.utils.dom.XLINK_NS, + 'version': '1.1', + 'height': FieldAngle.HALF * 2 + 'px', + 'width': FieldAngle.HALF * 2 + 'px', + }); + svg.style.touchAction = 'none'; + const circle = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.CIRCLE, + { + cx: FieldAngle.HALF, + cy: FieldAngle.HALF, + r: FieldAngle.RADIUS, + class: 'blocklyAngleCircle', + }, + svg, + ); + this.gauge = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.PATH, + { + class: 'blocklyAngleGauge', + }, + svg, + ); + this.line = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.LINE, + { + x1: FieldAngle.HALF, + y1: FieldAngle.HALF, + class: 'blocklyAngleLine', + }, + svg, + ); + + // Draw markers around the edge. + const minValueDegrees = Blockly.utils.math.toDegrees( + this.fieldAngleToRadians(this.min_), + ); + const maxValueDegrees = Blockly.utils.math.toDegrees( + this.fieldAngleToRadians(this.max_), + ); + + /** + * Draw a set of ticks on the gauge. + * + * @param tickAngle Angle between each tick. + * @param length Length of the tick (minor=5, major=10). + */ + const drawTicks = (tickAngle: number, length: number) => { + let min = Math.ceil(minValueDegrees / tickAngle) * tickAngle; + let max = Math.floor(maxValueDegrees / tickAngle) * tickAngle; + + if (this.clockwise) { + if (min < max) { + min += 360; + } + } else { + if (min > max) { + max += 360; + } + } + if (max === min) { + // Technically this could actually be zero, but more likely it's whole. + max += 360; + } + if (min > max) { + [min, max] = [max, min]; + } + for (let angle = min; angle <= max; angle += tickAngle) { + Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.LINE, + { + x1: FieldAngle.HALF + FieldAngle.RADIUS, + y1: FieldAngle.HALF, + x2: FieldAngle.HALF + FieldAngle.RADIUS - length, + y2: FieldAngle.HALF, + class: 'blocklyAngleMarks', + transform: + 'rotate(' + + -angle + + ',' + + FieldAngle.HALF + + ',' + + FieldAngle.HALF + + ')', + }, + svg, + ); + } + }; + + const displayRange = this.displayMax - this.displayMin; + const minorTickAngle = (360 / displayRange) * this.minorTick; + if (minorTickAngle) { + drawTicks(minorTickAngle, 5); + } + const majorTickAngle = (360 / displayRange) * this.majorTick; + if (majorTickAngle) { + drawTicks(majorTickAngle, 10); + } + + // The angle picker is different from other fields in that it updates on + // mousemove even if it's not in the middle of a drag. In future we may + // change this behaviour. + this.boundEvents.push( + Blockly.browserEvents.conditionalBind(svg, 'click', this, this.hide), + ); + // On touch devices, the picker's value is only updated with a drag. Add + // a click handler on the drag surface to update the value if the surface + // is clicked. + this.boundEvents.push( + Blockly.browserEvents.conditionalBind( + circle, + 'pointerdown', + this, + this.onMouseMove_, + true, + ), + ); + this.boundEvents.push( + Blockly.browserEvents.conditionalBind( + circle, + 'pointermove', + this, + this.onMouseMove_, + true, + ), + ); + return svg; + } + + /** + * Disposes of events belonging to the angle editor. + */ + private dropdownDispose() { + for (const event of this.boundEvents) { + Blockly.browserEvents.unbind(event); + } + this.boundEvents.length = 0; + this.gauge = null; + this.line = null; + } + + /** Hide the editor. */ + private hide() { + this.recomputeAriaContext(); + Blockly.DropDownDiv.hideIfOwner(this); + Blockly.WidgetDiv.hide(); + } + + /** + * Set the angle to match the mouse's position. + * + * @param e Mouse move event. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected onMouseMove_(e: PointerEvent) { + // Calculate angle. + const bBox = this.gauge?.ownerSVGElement?.getBoundingClientRect(); + if (!bBox) { + // This can't happen, but TypeScript thinks it can and lint forbids `!.`. + return; + } + const dx = e.clientX - bBox.left - FieldAngle.HALF; + const dy = e.clientY - bBox.top - FieldAngle.HALF; + let angle = Math.atan2(-dy, dx); + if (isNaN(angle)) { + // This shouldn't happen, but let's not let this error propagate further. + return; + } + angle = this.radiansToFieldAngle(angle); + this.displayMouseOrKeyboardValue(angle); + } + + /** + * Convert an on-screen angle into a value for this field. + * + * @param angle Radians where 0: East, π/2: North, π or -π: West, -π/2: South. + * @returns Angle value for this field, scaled and offset as specified. + */ + private radiansToFieldAngle(angle: number): number { + // Convert angle from radians (-π to π) to turns (-0.5 to 0.5). + angle /= 2 * Math.PI; + // Compensate for offset. + angle -= this.offset / 360; + // Flip if clockwise. + if (this.clockwise) { + angle *= -1; + } + // Normalize to positive. + angle %= 1; + if (angle < 0) { + angle += 1; + } + // Convert angle from turns (0.0 to 1.0) to the display min/max range. + angle *= this.displayMax - this.displayMin; + angle += this.displayMin; + return angle; + } + + /** + * Convert a value for this field into an on-screen angle. + * + * @param angle Angle value for this field, scaled and offset as specified. + * @returns Radians where 0: East, π/2: North, π or -π: West, -π/2: South. + */ + private fieldAngleToRadians(angle: number): number { + // Convert angle from the display min/max range to turns (0.0 to 1.0). + angle -= this.displayMin; + angle /= this.displayMax - this.displayMin; + // Flip if clockwise. + if (this.clockwise) { + angle *= -1; + } + // Compensate for offset. + angle += this.offset / 360; + // Normalize to span equally across zero (-0.5 to 0.5). + angle %= 1; + if (angle > 0.5) { + angle -= 1; + } + if (angle < -0.5) { + angle += 1; + } + // Convert angle from turns to radians. + angle *= 2 * Math.PI; + return angle; + } + + /** + * Handles and displays values that are input via mouse or arrow key input. + * These values need to be rounded and wrapped before being displayed so + * that the text input's value is appropriate. + * + * @param angle New angle. + */ + private displayMouseOrKeyboardValue(angle: number) { + const validAngle = this.doClassValidation_(angle); + if (validAngle !== null && validAngle !== this.value_) { + // Intermediate value changes from user input are not confirmed until the + // user closes the editor, and may be numerous. Inhibit reporting these as + // normal block change events, and instead report them as special + // intermediate changes that do not get recorded in undo history. + const oldValue = this.value_; + this.setEditorValue_(validAngle, false); + if ( + this.sourceBlock_ && + Blockly.Events.isEnabled() && + this.value_ !== oldValue + ) { + Blockly.Events.fire( + new (Blockly.Events.get( + Blockly.Events.BLOCK_FIELD_INTERMEDIATE_CHANGE, + ))(this.sourceBlock_, this.name || null, oldValue, this.value_), + ); + Blockly.utils.aria.announceDynamicAriaState(this.getAriaValue()); + } + } + } + + /** Redraw the graph with the current angle. */ + private updateGraph() { + if (!this.gauge || !this.line) { + return; + } + let angle = Number(this.getText()); + if (isNaN(angle)) { + // This shouldn't happen, but let's not let this error propagate further. + return; + } + angle = this.fieldAngleToRadians(angle); + + let path = `M ${FieldAngle.HALF},${FieldAngle.HALF}`; + let x2 = FieldAngle.HALF; + let y2 = FieldAngle.HALF; + if (!isNaN(angle)) { + const angle1 = Blockly.utils.math.toRadians(this.offset); + const x1 = Math.cos(angle1) * FieldAngle.RADIUS; + const y1 = Math.sin(angle1) * -FieldAngle.RADIUS; + x2 += Math.cos(angle) * FieldAngle.RADIUS; + y2 -= Math.sin(angle) * FieldAngle.RADIUS; + // Don't ask how the flag calculations work. They just do. + const clockwiseFlag = Number(this.clockwise); + let largeFlag = Math.abs(Math.floor((angle - angle1) / Math.PI) % 2); + if (clockwiseFlag) { + largeFlag = 1 - largeFlag; + } + path += + ` l ${x1},${y1} A ${FieldAngle.RADIUS},${FieldAngle.RADIUS} 0 ` + + `${largeFlag} ${clockwiseFlag} ${x2},${y2} z`; + } + this.gauge.setAttribute('d', path); + this.line.setAttribute('x2', `${x2}`); + this.line.setAttribute('y2', `${y2}`); + } + + /** + * Handle key down to the editor. + * + * @param e Keyboard event. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override onHtmlInputKeyDown_(e: KeyboardEvent) { + super.onHtmlInputKeyDown_(e); + const block = this.getSourceBlock(); + if (!block) { + throw new Error( + 'The field has not yet been attached to its input. ' + + 'Call appendField to attach it.', + ); + } + + let multiplier = 0; + switch (e.key) { + case 'ArrowLeft': + // decrement (increment in RTL) + multiplier = block.RTL ? 1 : -1; + break; + case 'ArrowRight': + // increment (decrement in RTL) + multiplier = block.RTL ? -1 : 1; + break; + case 'ArrowDown': + // decrement + multiplier = -1; + break; + case 'ArrowUp': + // increment + multiplier = 1; + break; + } + if (multiplier) { + const value = this.getValue() as number; + this.displayMouseOrKeyboardValue(value + multiplier * this.precision_); + e.preventDefault(); + e.stopPropagation(); + } + } + + /** + * Ensure that the input value is a valid angle. + * + * @param newValue The input value. + * @returns A valid angle, or null if invalid. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doClassValidation_( + newValue: number, + ): number | null | undefined; + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doClassValidation_(newValue?: number): number | null; + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doClassValidation_( + newValue?: number, + ): number | null | undefined { + // The obvious approach would be to call super.doClassValidation_ to handle + // min/max limitations. However angle pickers out of range need to clamp + // to the closest min/max point, which may involve a wrap to the opposite + // end of the numeric scale. + // E.g. min/max is 0/180 on a 0/360 display, and value is 365. + // FieldNumber would clamp 365 to max (180), but 0 is closer on the dial. + if (newValue === null) { + return null; + } + let value = Number(newValue); + if (isNaN(value) || !isFinite(value)) { + return null; + } + // Get the value in range. + value = this.wrapValue(value); + // Round to nearest multiple of precision. + if (this.precision_) { + value = Math.round(value / this.precision_) * this.precision_; + } + // Deal with 6.6000000000000005 IEEE float errors. + // Clean up floating point errors. + value = Number(value.toFixed(10)); + // Clamp the value between min and max, noting wrapping. + const displayRange = this.displayMax - this.displayMin; + const valueRange = this.max_ - this.min_; + if (value < this.min_) { + const undershoot = this.min_ - value; + const overshoot = displayRange - undershoot - valueRange; + value = undershoot < overshoot ? this.min_ : this.max_; + } + if (value > this.max_) { + const overshoot = value - this.max_; + const undershoot = displayRange - overshoot - valueRange; + value = undershoot < overshoot ? this.min_ : this.max_; + } + return value; + } + + /** + * Wraps the value so that it is in the min/max display range (e.g. 0 to 360). + * + * @param value The value to wrap. + * @returns The wrapped value. + */ + private wrapValue(value: number): number { + const displayRange = this.displayMax - this.displayMin; + value %= displayRange; + while (value < this.displayMin) { + value += displayRange; + } + while (value >= this.displayMax) { + value -= displayRange; + } + return value; + } + + /** + * Construct a FieldAngle from a JSON arg object. + * + * @param options A JSON object with options + * (value, mode, clockwise, offset, min, max, precision). + * @returns The new field instance. + * @nocollapse + * @internal + */ + static fromJson(options: FieldAngleFromJsonConfig): FieldAngle { + // `this` might be a subclass of FieldAngle if that class doesn't override + // the static fromJson method. + return new this(options.value, undefined, options); + } + + /** + * Hides the angle editor and updates the ARIA label. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override widgetDispose_() { + super.widgetDispose_(); + this.recomputeAriaContext(); + } +} + +/** Register the field and any dependencies. */ +export function registerFieldAngle() { + Blockly.fieldRegistry.register('field_angle', FieldAngle); +} + +FieldAngle.prototype.DEFAULT_VALUE = 0; + +/** + * CSS for angle field. + */ +Blockly.Css.register(` +.blocklyAngleCircle { + stroke: #444; + stroke-width: 1; + fill: #ddd; + fill-opacity: 0.8; +} + +.blocklyAngleMarks { + stroke: #444; + stroke-width: 1; +} + +.blocklyAngleGauge { + fill: #f88; + fill-opacity: 0.8; + pointer-events: none; +} + +.blocklyAngleLine { + stroke: #f00; + stroke-width: 2; + stroke-linecap: round; + pointer-events: none; +} + +.blocklyAngleSymbol { + dominant-baseline: central; +} +`); + +/** + * The two main modes of the angle field. + * Compass specifies: + * - clockwise: true + * - offset: 90 + * - min: 0 + * - max: 360 + * - precision: 15 + * - displayMin: 0 + * - displayMax: 360 + * - minorTick: 15 + * - majorTick: 45 + * - symbol: '°' + * + * Protractor specifies: + * - clockwise: false + * - offset: 0 + * - min: 0 + * - max: 360 + * - precision: 15 + * - displayMin: 0 + * - displayMax: 360 + * - minorTick: 15 + * - majorTick: 45 + * - symbol: '°' + */ +export enum Mode { + COMPASS = 'compass', + PROTRACTOR = 'protractor', +} + +/** + * Extra configuration options for the angle field. + */ +export interface FieldAngleConfig extends Blockly.FieldNumberConfig { + mode?: Mode; + clockwise?: boolean; + offset?: number; + displayMin?: number; + displayMax?: number; + minorTick?: number; + majorTick?: number; + symbol?: string; +} + +/** + * fromJson configuration options for the angle field. + */ +export interface FieldAngleFromJsonConfig extends FieldAngleConfig { + value?: number; +} + +/** + * A function that is called to validate changes to the field's value before + * they are set. + * + * @see {@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/validators#return_values} + * @param newValue The value to be validated. + * @returns One of three instructions for setting the new value: `T`, `null`, + * or `undefined`. + * + * - `T` to set this function's returned value instead of `newValue`. + * + * - `null` to invoke `doValueInvalid_` and not set a value. + * + * - `undefined` to set `newValue` as is. + */ +export type FieldAngleValidator = Blockly.FieldNumberValidator; diff --git a/packages/plugins/field-angle/src/index.ts b/packages/plugins/field-angle/src/index.ts new file mode 100644 index 00000000000..ab5c4477016 --- /dev/null +++ b/packages/plugins/field-angle/src/index.ts @@ -0,0 +1,7 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './field_angle'; diff --git a/packages/plugins/field-angle/test/field_angle_test.mocha.js b/packages/plugins/field-angle/test/field_angle_test.mocha.js new file mode 100644 index 00000000000..3b519343eee --- /dev/null +++ b/packages/plugins/field-angle/test/field_angle_test.mocha.js @@ -0,0 +1,328 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +const {testHelpers} = require('@blockly/dev-tools'); +const {FieldAngle, registerFieldAngle} = require('../src/index'); +const {assert} = require('chai'); + +const { + assertFieldValue, + FieldCreationTestCase, + FieldValueTestCase, + runConstructorSuiteTests, + runFromJsonSuiteTests, + runSetValueTests, +} = testHelpers; + +suite('FieldAngle', function () { + setup(function () { + registerFieldAngle(); + }); + + /** + * Configuration for field tests with invalid values. + * @type {Array} + */ + const invalidValueTestCases = [ + {title: 'Undefined', value: undefined}, + {title: 'Null', value: null}, + {title: 'NaN', value: NaN}, + {title: 'Non-Parsable String', value: 'bad'}, + {title: 'Infinity', value: Infinity}, + {title: 'Negative Infinity', value: -Infinity}, + {title: 'Infinity String', value: 'Infinity'}, + {title: 'Negative Infinity String', value: '-Infinity'}, + ]; + /** + * Configuration for field tests with valid values. + * @type {Array} + */ + const validValueTestCases = [ + {title: 'Integer', value: 16, expectedValue: 15}, + {title: 'Float', value: 14.5, expectedValue: 15}, + {title: 'Integer String', value: '30', expectedValue: 30}, + {title: 'Float String', value: '0.75', expectedValue: 0}, + {title: '> 360°', value: 360 + 16, expectedValue: 15}, + {title: '< 0°', value: -16, expectedValue: 360 - 15}, + ]; + const addArgsAndJson = function (testCase) { + testCase.args = [testCase.value]; + testCase.json = {value: testCase.value}; + }; + invalidValueTestCases.forEach(addArgsAndJson); + validValueTestCases.forEach(addArgsAndJson); + + /** + * The expected default value for the field being tested. + * @type {*} + */ + const defaultFieldValue = 0; + /** + * Asserts that the field property values are set to default. + * @param {FieldAngle} field The field to check. + */ + const assertFieldDefault = function (field) { + assertFieldValue(field, defaultFieldValue); + }; + /** + * Asserts that the field properties are correct based on the test case. + * @param {FieldAngle} field The field to check. + * @param {!FieldValueTestCase} testCase The test case. + */ + const validTestCaseAssertField = function (field, testCase) { + assertFieldValue(field, testCase.expectedValue); + }; + + runConstructorSuiteTests( + FieldAngle, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + runFromJsonSuiteTests( + FieldAngle, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + suite('setValue', function () { + /** + * Create a mock block that may be used as the source block for a field. + * @returns {!Object} Mock block. + */ + function createBlockMock() { + return { + id: 'test', + rendered: false, + workspace: { + rendered: false, + }, + renameVarById: Blockly.Block.prototype.renameVarById, + updateVarName: Blockly.Block.prototype.updateVarName, + isShadow: () => false, + isDeadOrDying: () => false, + }; + } + + suite('Empty -> New Value', function () { + setup(function () { + this.field = new FieldAngle(); + }); + runSetValueTests( + validValueTestCases, + invalidValueTestCases, + defaultFieldValue, + ); + test('With source block', function () { + this.field.setSourceBlock(createBlockMock()); + this.field.setValue(12.5); + assertFieldValue(this.field, 15); + }); + }); + suite('Value -> New Value', function () { + setup(function () { + this.field = new FieldAngle(12.5); + }); + runSetValueTests(validValueTestCases, invalidValueTestCases, 15); + test('With source block', function () { + this.field.setSourceBlock(createBlockMock()); + this.field.setValue(-15); + assertFieldValue(this.field, 360 - 15); + }); + }); + }); + + suite('Validators', function () { + setup(function () { + this.field = new FieldAngle(15); + }); + const testSuites = [ + { + title: 'Null Validator', + validator: function () { + return null; + }, + value: 30, + expectedValue: 15, + }, + { + title: 'Force multiple of 90 Validator', + validator: function (newValue) { + return Math.round(newValue / 90) * 90; + }, + value: 60, + expectedValue: 90, + }, + { + title: 'Returns Undefined Validator', + validator: function () {}, + value: 30, + expectedValue: 30, + }, + ]; + testSuites.forEach(function (suiteInfo) { + suite(suiteInfo.title, function () { + setup(function () { + this.field.setValidator(suiteInfo.validator); + }); + test('New Value', function () { + this.field.setValue(suiteInfo.value); + assertFieldValue( + this.field, + suiteInfo.expectedValue, + suiteInfo.expectedText, + ); + }); + }); + }); + }); + + suite('Customizations', function () { + suite('Clockwise', function () { + test('JS Configuration', function () { + const field = new FieldAngle(0, null, { + clockwise: true, + }); + assert.isTrue(field.clockwise); + }); + test('JSON Definition', function () { + const field = FieldAngle.fromJson({ + value: 0, + clockwise: true, + }); + assert.isTrue(field.clockwise); + }); + }); + + suite('Offset', function () { + test('JS Configuration', function () { + const field = new FieldAngle(0, null, { + offset: 90, + }); + assert.equal(field.offset, 90); + }); + test('JSON Definition', function () { + const field = FieldAngle.fromJson({ + value: 0, + offset: 90, + }); + assert.equal(field.offset, 90); + }); + }); + + suite('Min/Max', function () { + test('JS Configuration', function () { + const field = new FieldAngle(0, null, { + min: -180, + max: 180, + }); + assert.equal(field.getMin(), -180); + assert.equal(field.getMax(), 180); + }); + test('JSON Definition', function () { + const field = FieldAngle.fromJson({ + value: 0, + min: -180, + max: 180, + }); + assert.equal(field.getMin(), -180); + assert.equal(field.getMax(), 180); + }); + }); + + suite('Precision', function () { + test('JS Configuration', function () { + const field = new FieldAngle(0, null, { + precision: 30, + }); + assert.equal(field.getPrecision(), 30); + }); + test('JSON Definition', function () { + const field = FieldAngle.fromJson({ + value: 0, + precision: 30, + }); + assert.equal(field.getPrecision(), 30); + }); + }); + + suite('Mode', function () { + suite('Compass', function () { + test('JS Configuration', function () { + const field = new FieldAngle(0, null, { + mode: 'compass', + }); + assert.equal(field.offset, 90); + assert.isTrue(field.clockwise); + }); + test('JS Configuration', function () { + const field = FieldAngle.fromJson({ + value: 0, + mode: 'compass', + }); + assert.equal(field.offset, 90); + assert.isTrue(field.clockwise); + }); + }); + suite('Protractor', function () { + test('JS Configuration', function () { + const field = new FieldAngle(0, null, { + mode: 'protractor', + }); + assert.equal(field.offset, 0); + assert.isFalse(field.clockwise); + }); + test('JS Configuration', function () { + const field = FieldAngle.fromJson({ + value: 0, + mode: 'protractor', + }); + assert.equal(field.offset, 0); + assert.isFalse(field.clockwise); + }); + }); + }); + }); + + suite('Serialization', function () { + setup(function () { + this.workspace = new Blockly.Workspace(); + Blockly.defineBlocksWithJsonArray([ + { + type: 'row_block', + message0: '%1', + args0: [ + { + type: 'input_value', + name: 'INPUT', + }, + ], + output: null, + }, + ]); + + this.assertValue = (value) => { + const block = this.workspace.newBlock('row_block'); + const field = new FieldAngle(value); + block.getInput('INPUT').appendField(field, 'ANGLE'); + const jso = Blockly.serialization.blocks.save(block); + assert.deepEqual(jso['fields'], {ANGLE: value}); + }; + }); + + teardown(function () { + this.workspace.dispose(); + }); + + test('Simple', function () { + this.assertValue(90); + }); + }); +}); diff --git a/packages/plugins/field-angle/test/index.html b/packages/plugins/field-angle/test/index.html new file mode 100644 index 00000000000..c6879bd868c --- /dev/null +++ b/packages/plugins/field-angle/test/index.html @@ -0,0 +1,18 @@ + + + + + Blockly Field Angle Playground + + + + +

+ + + diff --git a/packages/plugins/field-angle/test/index.ts b/packages/plugins/field-angle/test/index.ts new file mode 100644 index 00000000000..643a45a4d58 --- /dev/null +++ b/packages/plugins/field-angle/test/index.ts @@ -0,0 +1,332 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Angle field test playground. + */ + +import * as Blockly from 'blockly'; +import {createPlayground} from '@blockly/dev-tools'; +import {registerFieldAngle} from '../src/index'; + +/** + * An array of blocks that are defined only for the purposes of + * manually and visually testing the angle field. + */ +const testBlockDefinitions = [ + { + type: 'test_standard_field_values', + message0: '%1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_angle`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values', + message0: '%1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + value: 50, + alt: { + type: 'field_label', + text: `No field_angle`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_compass', + message0: 'compass %1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + mode: 'compass', + value: 90, + alt: { + type: 'field_label', + text: `No field_angle`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_protractor', + message0: 'protractor %1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + mode: 'protractor', + value: 90, + alt: { + type: 'field_label', + text: `No field_angle`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_round', + message0: 'round %1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + precision: 0.1, + value: 123, + alt: { + type: 'field_label', + text: `No field_angle`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_radians', + message0: 'radians %1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + precision: 0.1, + clockwise: true, + value: 0, + min: 0, + max: Math.PI, + displayMin: -Math.PI, + displayMax: Math.PI, + minorTick: Math.PI / 8, + majorTick: Math.PI, + symbol: ' rad', + alt: { + type: 'field_label', + text: `No field_angle`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_quadrant', + message0: 'quadrant %1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + precision: 1, + value: 0, + min: 0, + max: 2, + displayMin: 0, + displayMax: 8, + minorTick: 0, + majorTick: 1, + symbol: '', + alt: { + type: 'field_label', + text: `No field_angle`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_standard_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_angle`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_angle', + name: 'FIELDNAME', + value: 50, + alt: { + type: 'field_label', + text: `No field_angle`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_parent_block', + message0: 'parent %1', + args0: [ + { + type: 'input_value', + name: 'INPUT', + }, + ], + previousStatement: null, + nextStatement: null, + style: 'loop_blocks', + }, +]; + +Blockly.defineBlocksWithJsonArray(testBlockDefinitions); + +/** + * A test toolbox containing the exported blocks and a variety of + * test blocks to exercise the angle field in different contexts + * (on a shadow block, as the only field on a block, etc). + * These are in a simple toolbox, rather than a category toolbox, so that + * they are all instantiated every time the test page is opened. + */ +const jsonToolbox = { + contents: [ + { + kind: 'label', + text: 'Test blocks: default field values', + }, + { + kind: 'block', + type: 'test_standard_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_standard_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values_and_label', + }, + }, + }, + }, + { + kind: 'label', + text: 'Test blocks: custom field values', + }, + { + kind: 'block', + type: 'test_custom_field_values', + }, + { + kind: 'block', + type: 'test_custom_field_values_compass', + }, + { + kind: 'block', + type: 'test_custom_field_values_protractor', + }, + { + kind: 'block', + type: 'test_custom_field_values_round', + }, + { + kind: 'block', + type: 'test_custom_field_values_radians', + }, + { + kind: 'block', + type: 'test_custom_field_values_quadrant', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_custom_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values_and_label', + }, + }, + }, + }, + ], +}; + +/** + * Create a workspace. + * + * @param blocklyDiv The blockly container div. + * @param options The Blockly options. + * @returns The created workspace. + */ +function createWorkspace( + blocklyDiv: HTMLElement, + options: Blockly.BlocklyOptions, +): Blockly.WorkspaceSvg { + const workspace = Blockly.inject(blocklyDiv, options); + return workspace; +} + +document.addEventListener('DOMContentLoaded', function () { + registerFieldAngle(); + const defaultOptions: Blockly.BlocklyOptions = { + toolbox: jsonToolbox, + }; + const rootElement = document.getElementById('root'); + if (rootElement) { + createPlayground(rootElement, createWorkspace, defaultOptions); + } +}); diff --git a/packages/plugins/field-angle/tsconfig.json b/packages/plugins/field-angle/tsconfig.json new file mode 100644 index 00000000000..99858c525f1 --- /dev/null +++ b/packages/plugins/field-angle/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "module": "es2015", + "moduleResolution": "bundler", + "target": "es6", + "strict": true + }, + // NOTE: `test/**/*` is automatically included in `blockly-scripts start`. + // Only src matters for production builds. + "include": ["src"] +} diff --git a/packages/plugins/field-bitmap/CHANGELOG.md b/packages/plugins/field-bitmap/CHANGELOG.md new file mode 100644 index 00000000000..0c39a8df9e2 --- /dev/null +++ b/packages/plugins/field-bitmap/CHANGELOG.md @@ -0,0 +1,692 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [6.0.9](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-bitmap@6.0.8...@blockly/field-bitmap@6.0.9) (2026-04-09) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [6.0.8](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-bitmap@6.0.7...@blockly/field-bitmap@6.0.8) (2026-01-15) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [6.0.7](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-bitmap@6.0.6...@blockly/field-bitmap@6.0.7) (2026-01-05) + + +### Bug Fixes + +* update plugin URLs to RPF ([#2665](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2665)) ([6c34039](https://github.com/RaspberryPiFoundation/blockly-samples/commit/6c3403960b8070eb8e576bd5702bdb53a09ac475)) + + + +## [6.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@6.0.5...@blockly/field-bitmap@6.0.6) (2026-01-05) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [6.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@6.0.4...@blockly/field-bitmap@6.0.5) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [6.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@6.0.3...@blockly/field-bitmap@6.0.4) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [6.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@6.0.2...@blockly/field-bitmap@6.0.3) (2025-10-02) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [6.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@6.0.1...@blockly/field-bitmap@6.0.2) (2025-07-17) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [6.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@6.0.0...@blockly/field-bitmap@6.0.1) (2025-05-22) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [6.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.17...@blockly/field-bitmap@6.0.0) (2025-05-16) + + +### ⚠ BREAKING CHANGES + +* update all plugins to blockly v12 (#2538) + +### Features + +* update all plugins to blockly v12 ([#2538](https://github.com/google/blockly-samples/issues/2538)) ([420ba31](https://github.com/google/blockly-samples/commit/420ba3124b260b2c06e8e1d61be44e870780d95f)) + + + +## [5.0.17](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.16...@blockly/field-bitmap@5.0.17) (2025-05-16) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.15...@blockly/field-bitmap@5.0.16) (2025-05-15) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.14...@blockly/field-bitmap@5.0.15) (2025-02-27) + + +### Bug Fixes + +* [#2463](https://github.com/google/blockly-samples/issues/2463) update event handling for field-bitmap to support touch ([c105002](https://github.com/google/blockly-samples/commit/c1050025e41dd456c822301555b1b3a20577d03e)) + + + + + +## [5.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.13...@blockly/field-bitmap@5.0.14) (2025-02-13) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.12...@blockly/field-bitmap@5.0.13) (2024-12-19) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.11...@blockly/field-bitmap@5.0.12) (2024-12-03) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.10...@blockly/field-bitmap@5.0.11) (2024-11-07) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.9...@blockly/field-bitmap@5.0.10) (2024-09-26) + + +### Bug Fixes + +* **field-*:** Remove unneeded `fieldRegistry.unregister` calls ([#2454](https://github.com/google/blockly-samples/issues/2454)) ([b3ba30e](https://github.com/google/blockly-samples/commit/b3ba30e23dddf0bd98c266659aa229ba6ba685b0)), closes [#2453](https://github.com/google/blockly-samples/issues/2453) + + + + + +## [5.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.8...@blockly/field-bitmap@5.0.9) (2024-08-29) + + +### Bug Fixes + +* predeploy plugins ([#2449](https://github.com/google/blockly-samples/issues/2449)) ([6b36d8b](https://github.com/google/blockly-samples/commit/6b36d8b344a969f79d89bbc7dcee29ae554759f9)) + + + + + +## [5.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.7...@blockly/field-bitmap@5.0.8) (2024-08-22) + + +### Bug Fixes + +* lerna v8 breaking our whole shebangle ([#2446](https://github.com/google/blockly-samples/issues/2446)) ([b8b4c21](https://github.com/google/blockly-samples/commit/b8b4c21d4eaf81f527336ae46f6679ff99ac23c3)) + + + + + +## [5.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.6...@blockly/field-bitmap@5.0.7) (2024-08-08) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.5...@blockly/field-bitmap@5.0.6) (2024-08-01) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.4...@blockly/field-bitmap@5.0.5) (2024-07-25) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.3...@blockly/field-bitmap@5.0.4) (2024-07-11) + + +### Reverts + +* Revert "chore(deps): Bump the npm_and_yarn group across 54 directories with 19 updates (#2403)" (#2410) ([bad8ffb](https://github.com/google/blockly-samples/commit/bad8ffbf85caa4e5b68d2f010cd0deaa9e3dd98f)), closes [#2403](https://github.com/google/blockly-samples/issues/2403) [#2410](https://github.com/google/blockly-samples/issues/2410) + + + +## [5.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.2...@blockly/field-bitmap@5.0.3) (2024-07-04) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.1...@blockly/field-bitmap@5.0.2) (2024-06-06) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [5.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@5.0.0...@blockly/field-bitmap@5.0.1) (2024-05-30) + + +### Bug Fixes + +* multiple blockly instances ([#2375](https://github.com/google/blockly-samples/issues/2375)) ([b231e59](https://github.com/google/blockly-samples/commit/b231e598f2f5f5b0abbfd01d981e35572ad50a26)) +* update pixel with new values ([#2373](https://github.com/google/blockly-samples/issues/2373)) ([d387b9e](https://github.com/google/blockly-samples/commit/d387b9ebcd0b42275231815fa18f61f61eeb04e4)) + + + +## [5.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.1.1...@blockly/field-bitmap@5.0.0) (2024-05-21) + + +### ⚠ BREAKING CHANGES + +* update all plugins to v11 of blockly + +### Features + +* update all plugins to v11 of blockly ([5fcd3e7](https://github.com/google/blockly-samples/commit/5fcd3e7d53eaadffe9bda9a378b404d34b2f8be2)) +* upgrade ts version, fix field validators ([b2d2191](https://github.com/google/blockly-samples/commit/b2d2191ff11b30347b33f95acf6e58f7ce54d004)) + + +### Bug Fixes + +* Call blocks handle both manual disabling and disabled defs ([#2334](https://github.com/google/blockly-samples/issues/2334)) ([5eade55](https://github.com/google/blockly-samples/commit/5eade55779c4022d14ad4472ff32c93c78199887)) +* remove some uses of generateFieldTextBlocks ([#2357](https://github.com/google/blockly-samples/issues/2357)) ([53b3fa4](https://github.com/google/blockly-samples/commit/53b3fa4604aabb1fc5240d41c8d1d389d4b15301)) + + + +## [4.1.1](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.1.0...@blockly/field-bitmap@4.1.1) (2024-05-20) + + +### Bug Fixes + +* improve bitmap field editor styles for large images and/or no buttons ([#2360](https://github.com/google/blockly-samples/issues/2360)) ([4cc25b3](https://github.com/google/blockly-samples/commit/4cc25b3a1374936fdc8eff56d14d3f9429c56504)) + + + +## [4.1.0](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.18...@blockly/field-bitmap@4.1.0) (2024-05-16) + + +### Features + +* Allow configuration of buttons, pixel colors, and fixed field height for Bitmap field ([#2351](https://github.com/google/blockly-samples/issues/2351)) ([432e277](https://github.com/google/blockly-samples/commit/432e277687bc0883806302164054f0a7b9ac3484)) + + + +## [4.0.18](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.17...@blockly/field-bitmap@4.0.18) (2024-05-09) + + +### Bug Fixes + +* Fix incorrect Blockly imports ([#2339](https://github.com/google/blockly-samples/issues/2339)) ([9af40b9](https://github.com/google/blockly-samples/commit/9af40b9ca075275af2b48cedcc1750d458084eb3)) + + + +## [4.0.17](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.16...@blockly/field-bitmap@4.0.17) (2024-04-25) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.15...@blockly/field-bitmap@4.0.16) (2024-04-18) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.14...@blockly/field-bitmap@4.0.15) (2024-04-04) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.13...@blockly/field-bitmap@4.0.14) (2024-03-28) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.12...@blockly/field-bitmap@4.0.13) (2024-02-29) + + +### Bug Fixes + +* Fix but preventing clearing/randomizing nonsquare bitmap fields. ([#2226](https://github.com/google/blockly-samples/issues/2226)) ([c32266b](https://github.com/google/blockly-samples/commit/c32266b2b7685c0d4d6646e5bcedb085c435e0b2)) + + + +## [4.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.11...@blockly/field-bitmap@4.0.12) (2024-02-08) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.10...@blockly/field-bitmap@4.0.11) (2023-12-14) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.9...@blockly/field-bitmap@4.0.10) (2023-12-07) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.8...@blockly/field-bitmap@4.0.9) (2023-11-16) + + +### Bug Fixes + +* resolved 15 ESLint warnings ([#2065](https://github.com/google/blockly-samples/issues/2065)) ([2436337](https://github.com/google/blockly-samples/commit/243633746542bb1518fe2893c0421a5a6f79fd32)) + + + +## [4.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.7...@blockly/field-bitmap@4.0.8) (2023-11-09) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.6...@blockly/field-bitmap@4.0.7) (2023-11-02) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.5...@blockly/field-bitmap@4.0.6) (2023-10-30) + + +### Bug Fixes + +* progress for Have field-bitmap fire intermediate change events ([#2012](https://github.com/google/blockly-samples/issues/2012)) ([7050875](https://github.com/google/blockly-samples/commit/7050875971ebb038702232c5bb642a0393592ab7)) + + + +## [4.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.4...@blockly/field-bitmap@4.0.5) (2023-09-14) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.3...@blockly/field-bitmap@4.0.4) (2023-08-17) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.2...@blockly/field-bitmap@4.0.3) (2023-07-27) + + +### Bug Fixes + +* Use the normal, non-drag cursor when hovering over a bitmap field. ([#1810](https://github.com/google/blockly-samples/issues/1810)) ([a4f8e8d](https://github.com/google/blockly-samples/commit/a4f8e8d1c22e38fa00a27bbeaca3ef9f637cbe82)) + + + +## [4.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.1...@blockly/field-bitmap@4.0.2) (2023-07-24) + + +### Bug Fixes + +* Fix the positioning of the dropdown div relative to the bitmap field. ([#1809](https://github.com/google/blockly-samples/issues/1809)) ([54a6ee6](https://github.com/google/blockly-samples/commit/54a6ee64ff03b6cb11e070d130a88d656aeced22)) + + + +## [4.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@4.0.0...@blockly/field-bitmap@4.0.1) (2023-07-20) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [4.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@3.0.1...@blockly/field-bitmap@4.0.0) (2023-07-13) + + +### ⚠ BREAKING CHANGES + +* updated `plugins/field-date` to TypeScript (#1705) + +### Features + +* updated `plugins/field-date` to TypeScript ([#1705](https://github.com/google/blockly-samples/issues/1705)) ([e5531ff](https://github.com/google/blockly-samples/commit/e5531fffe188ee361a16fe48ed126b34e51a8d30)) + + + +## [3.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@3.0.0...@blockly/field-bitmap@3.0.1) (2023-07-06) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [3.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.19...@blockly/field-bitmap@3.0.0) (2023-06-28) + + +### ⚠ BREAKING CHANGES + +* update all plugins to use blockly-v10.0.0 (#1744) + +### Features + +* update all plugins to use blockly-v10.0.0 ([#1744](https://github.com/google/blockly-samples/issues/1744)) ([6cc88cb](https://github.com/google/blockly-samples/commit/6cc88cbef39d4ad664a668d3d46eb29ba7292f9c)) + + + +## [2.0.19](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.18...@blockly/field-bitmap@2.0.19) (2023-06-22) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.18](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.17...@blockly/field-bitmap@2.0.18) (2023-05-18) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.17](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.16...@blockly/field-bitmap@2.0.17) (2023-05-11) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.15...@blockly/field-bitmap@2.0.16) (2023-05-04) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.14...@blockly/field-bitmap@2.0.15) (2023-04-27) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.13...@blockly/field-bitmap@2.0.14) (2023-03-23) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.12...@blockly/field-bitmap@2.0.13) (2023-03-09) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.11...@blockly/field-bitmap@2.0.12) (2023-03-02) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.10...@blockly/field-bitmap@2.0.11) (2023-02-23) + + +### Bug Fixes + +* remove prepublishOnly scripts ([#1579](https://github.com/google/blockly-samples/issues/1579)) ([27da6cd](https://github.com/google/blockly-samples/commit/27da6cd04c38f6ba417f4e7446bb6218c475448d)) + + + +## [2.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.9...@blockly/field-bitmap@2.0.10) (2023-02-23) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.8...@blockly/field-bitmap@2.0.9) (2023-02-16) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.7...@blockly/field-bitmap@2.0.8) (2023-02-09) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.6...@blockly/field-bitmap@2.0.7) (2023-01-26) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.5...@blockly/field-bitmap@2.0.6) (2023-01-05) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.4...@blockly/field-bitmap@2.0.5) (2022-12-15) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.3...@blockly/field-bitmap@2.0.4) (2022-12-01) + + +### Bug Fixes + +* **field-bitmap:** Fix crash when dismissing FieldBitmap dropdown ([#1394](https://github.com/google/blockly-samples/issues/1394)) ([9a2ea9c](https://github.com/google/blockly-samples/commit/9a2ea9cfe43bd7d87b809c0b0dc8a807e958630d)), closes [#1386](https://github.com/google/blockly-samples/issues/1386) + + + +## [2.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.2...@blockly/field-bitmap@2.0.3) (2022-11-10) + + +### Bug Fixes + +* field-bitmap now serializing correctly ([#1361](https://github.com/google/blockly-samples/issues/1361)) ([304a875](https://github.com/google/blockly-samples/commit/304a875648bac4c7d78747a298dc8b2a1d28ba5b)) + + + +## [2.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.1...@blockly/field-bitmap@2.0.2) (2022-10-27) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@2.0.0...@blockly/field-bitmap@2.0.1) (2022-10-13) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## [2.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-bitmap@1.0.7...@blockly/field-bitmap@2.0.0) (2022-10-05) + + +### ⚠ BREAKING CHANGES + +* update peer and devDependencies of all plugins to require Blockly v9 (#1314) + +### Bug Fixes + +* references to deprecated functions in v9 ([#1313](https://github.com/google/blockly-samples/issues/1313)) ([cb2e679](https://github.com/google/blockly-samples/commit/cb2e67987e0b62a77c26adc660cc6ade1ba67954)) + + +### Miscellaneous Chores + +* update peer and devDependencies of all plugins to require Blockly v9 ([#1314](https://github.com/google/blockly-samples/issues/1314)) ([03d4912](https://github.com/google/blockly-samples/commit/03d4912c42c8de0f30493037ccc28dddaea0f266)) + + + +## 1.0.7 (2022-08-11) + + +### Bug Fixes + +* Remove the deprecated block-extension-tooltip ([#1215](https://github.com/google/blockly-samples/issues/1215)) ([a044478](https://github.com/google/blockly-samples/commit/a044478c86a73e3065bc866e427f175cbec6fc13)) + + + + + +## 1.0.6 (2022-08-04) + + +### Bug Fixes + +* fix the name of the package of suggested-blocks ([#1214](https://github.com/google/blockly-samples/issues/1214)) ([35aa8ec](https://github.com/google/blockly-samples/commit/35aa8ec73a60a4eb5b1e80cb2fc71dcd83d05e27)) + + + + + +## 1.0.4 (2022-07-21) + +**Note:** Version bump only for package @blockly/field-bitmap + + + + + +## 1.0.3 (2022-07-06) + + +### Bug Fixes + +* Update blockly-react to use functional components instead of class components. ([#1178](https://github.com/google/blockly-samples/issues/1178)) ([fa21187](https://github.com/google/blockly-samples/commit/fa21187cdbe4ec3a5c69f185540dd68a98eb69d7)) + + + + + +## 1.0.2 (2022-06-27) + + +### Bug Fixes + +* Update package import for keyboard navigation demo ([#1170](https://github.com/google/blockly-samples/issues/1170)) ([69c1725](https://github.com/google/blockly-samples/commit/69c1725b775279fcc397dc178935208d5f42b08c)) + + + + + +## 1.0.1 (2022-06-21) + +**Note:** Version bump only for package @blockly/field-bitmap diff --git a/packages/plugins/field-bitmap/README.md b/packages/plugins/field-bitmap/README.md new file mode 100644 index 00000000000..3b8cee4efd9 --- /dev/null +++ b/packages/plugins/field-bitmap/README.md @@ -0,0 +1,109 @@ +# @blockly/field-bitmap [![Built on Blockly](https://tinyurl.com/built-on-blockly)](https://github.com/google/blockly) + +A [Blockly](https://www.npmjs.com/package/blockly) field that allows for user-inputted pixel grids. The image value is stored as a 2D array of 1s and 0s, and supports any size. The user can paint over pixels with their mouse, or randomize the grid. + +Note: this field does not support serialization to XML. + +## Installation + +### Yarn + +``` +yarn add @blockly/field-bitmap +``` + +### npm + +``` +npm install @blockly/field-bitmap --save +``` + +## Usage + +This field accepts up to 6 parameters: + +- `"value"` to specify an initial value. Must be a 2D rectangular array of 1s and 0s. + If not provided, the default is an empty grid of the specified size. +- `"width"` to specify an initial width, if there is no initial value. + If not provided, the default is a width of 5. +- `"height"` to specify an initial height, if there is no initial value. + If not provided, the default is a height of 5. +- `fieldHeight"` to specify a static field height. If provided, the individual pixels + will be resized to fit inside the field. This only affects the field as it is + seen on a block and not the pop-up editor. Good for larger images. (_Note: If this + results in fractional pixel sizes, the overall field height may not exactly match + the specified value on all browsers._) +- `"colours"` to override the default colours, Default values: + `{filled: '#363d80', empty: '#fff'}` +- `"buttons"` to show or hide the "Randomize" and/or "Clear" buttons. If either is + omitted, the button will be shown. Default values: + `{randomize: true, clear: true}` + +### JavaScript + +```js +import * as Blockly from 'blockly'; +import {FieldBitmap} from 'blockly-field-bitmap'; +Blockly.Blocks["test_field_bitmap"] = { + init: function () { + this.appendDummyInput() + .appendField("bitmap: ") + .appendField(new FieldBitmap(...), "FIELDNAME"); + } +}; +``` + +### JSON + +Example with default value: + +```js +import * as Blockly from 'blockly'; +import '@blockly/field-bitmap'; +Blockly.defineBlocksWithJsonArray([ + { + type: 'test_field_bitmap', + message0: 'bitmap: %1', + args0: [ + { + type: 'field_bitmap', + name: 'FIELDNAME', + value: [ + [0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 0, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 0], + [0, 1, 0, 0, 0, 1, 0], + [0, 0, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + ], + }, + ], + }, +]); +``` + +Example with width and height: + +```js +import * as Blockly from 'blockly'; +import '@blockly/field-bitmap'; +Blockly.defineBlocksWithJsonArray([ + { + type: 'test_field_bitmap', + message0: 'bitmap: %1', + args0: [ + { + type: 'field_bitmap', + name: 'FIELDNAME', + width: 8, + height: 8, + }, + ], + }, +]); +``` + +## License + +Apache 2.0 diff --git a/packages/plugins/field-bitmap/package.json b/packages/plugins/field-bitmap/package.json new file mode 100644 index 00000000000..0d7cbd4fd67 --- /dev/null +++ b/packages/plugins/field-bitmap/package.json @@ -0,0 +1,56 @@ +{ + "name": "@blockly/field-bitmap", + "version": "13.1.0", + "private": true, + "description": "A field that lets users input a pixel grid with their mouse.", + "scripts": { + "audit:fix": "blockly-scripts auditFix", + "build": "blockly-scripts build", + "clean": "blockly-scripts clean", + "lint": "eslint .", + "predeploy": "blockly-scripts predeploy", + "start": "blockly-scripts start", + "test": "blockly-scripts test" + }, + "main": "./dist/index.js", + "module": "./src/index.js", + "unpkg": "./dist/index.js", + "author": "", + "keywords": [ + "blockly", + "blockly-plugin", + "blockly-field", + "bitmap" + ], + "homepage": "https://github.com/RaspberryPiFoundation/blockly-samples/tree/master/field-bitmap#readme", + "bugs": { + "url": "https://github.com/RaspberryPiFoundation/blockly-samples/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/RaspberryPiFoundation/blockly-samples.git", + "directory": "field-bitmap" + }, + "license": "Apache-2.0", + "directories": { + "dist": "dist", + "src": "src" + }, + "files": [ + "dist", + "src" + ], + "devDependencies": { + "@blockly/dev-scripts": "^13.1.0", + "@blockly/dev-tools": "^13.1.0", + "chai": "^6.2.2", + "mocha": "^11.7.6", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "blockly": "^13.2.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugins/field-bitmap/src/field-bitmap.ts b/packages/plugins/field-bitmap/src/field-bitmap.ts new file mode 100644 index 00000000000..5c061e9590a --- /dev/null +++ b/packages/plugins/field-bitmap/src/field-bitmap.ts @@ -0,0 +1,1027 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from 'blockly/core'; + +export const DEFAULT_HEIGHT = 5; +export const DEFAULT_WIDTH = 5; +const DEFAULT_PIXEL_SIZE = 15; +const DEFAULT_PIXEL_COLOURS: PixelColours = { + empty: '#fff', + filled: '#363d80', +}; +const DEFAULT_BUTTONS: Buttons = { + randomize: true, + clear: true, +}; + +/** + * Field for inputting a small bitmap image. + * Includes a grid of clickable pixels that's exported as a bitmap. + */ +export class FieldBitmap extends Blockly.Field { + private initialValue: number[][] | null = null; + private imgHeight: number; + private imgWidth: number; + /** + * Array holding info needed to unbind events. + * Used for disposing. + */ + private boundEvents: Blockly.browserEvents.Data[] = []; + /** References to UI elements */ + private pixelGrid: HTMLDivElement | null = null; + private editorPixels: HTMLButtonElement[][] | null = null; + private blockDisplayPixels: SVGElement[][] | null = null; + /** Index of the keyboard-focused pixel in row-major order, or -1. */ + private focusedPixelIndex = -1; + /** Stateful variables */ + private pointerIsDown = false; + private valToPaintWith?: number; + buttonOptions: Buttons; + pixelSize: number; + pixelColours: {empty: string; filled: string}; + fieldHeight?: number; + + protected override ariaTypeName = Blockly.Msg['ARIA_TYPE_FIELD_BITMAP']; + + /** + * Constructor for the bitmap field. + * + * @param value 2D rectangular array of 1s and 0s. + * @param validator A function that is called to validate. + * @param config Config A map of options used to configure the field. + */ + constructor( + value: number[][] | typeof Blockly.Field.SKIP_SETUP, + validator?: Blockly.FieldValidator, + config?: FieldBitmapFromJsonConfig, + ) { + super(value, validator, config); + + this.SERIALIZABLE = true; + this.buttonOptions = {...DEFAULT_BUTTONS, ...config?.buttons}; + this.pixelColours = {...DEFAULT_PIXEL_COLOURS, ...config?.colours}; + + // Configure value, height, and width + const currentValue = this.getValue(); + if (currentValue !== null) { + this.imgHeight = currentValue.length; + this.imgWidth = currentValue[0].length || 0; + } else { + this.imgHeight = config?.height ?? DEFAULT_HEIGHT; + this.imgWidth = config?.width ?? DEFAULT_WIDTH; + // Set a default empty value + this.setValue(this.getEmptyArray()); + } + this.fieldHeight = config?.fieldHeight; + if (this.fieldHeight) { + this.pixelSize = this.fieldHeight / this.imgHeight; + } else { + this.pixelSize = DEFAULT_PIXEL_SIZE; + } + } + + /** + * Constructs a FieldBitmap from a JSON arg object. + * + * @param options A JSON object with options. + * @returns The new field instance. + */ + static fromJson(options: FieldBitmapFromJsonConfig) { + // `this` might be a subclass of FieldBitmap if that class doesn't override the static fromJson method. + return new this( + options.value ?? Blockly.Field.SKIP_SETUP, + undefined, + options, + ); + } + + /** + * Returns the width of the image in pixels. + * + * @returns The width in pixels. + */ + getImageWidth() { + return this.imgWidth; + } + + /** + * Returns the height of the image in pixels. + * + * @returns The height in pixels. + */ + getImageHeight() { + return this.imgHeight; + } + + /** + * Updates the ARIA roles and label for this field. + */ + override recomputeAriaContext(): boolean { + const shouldCustomize = super.recomputeAriaContext(); + if (!shouldCustomize) return false; + const focusableElement = this.getFocusableElement(); + Blockly.utils.aria.setState( + focusableElement, + Blockly.utils.aria.State.HASPOPUP, + 'grid', + ); + Blockly.utils.aria.setState( + focusableElement, + Blockly.utils.aria.State.EXPANDED, + !!this.pixelGrid, + ); + return true; + } + + /** + * Validates that a new value meets the requirements for a valid bitmap array. + * + * @param newValue The new value to be tested. + * @returns The new value if it's valid, or null. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doClassValidation_( + newValue: number[][], + ): number[][] | null | undefined; + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doClassValidation_( + newValue?: number[][], + ): number[][] | null; + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doClassValidation_( + newValue?: number[][], + ): number[][] | null | undefined { + if (!newValue) { + return null; + } + // Check if the new value is an array + if (!Array.isArray(newValue)) { + return null; + } + const newHeight = newValue.length; + // The empty list is not an acceptable bitmap + if (newHeight == 0) { + return null; + } + + // Check that the width matches the existing width of the image if it + // already has a value. + const newWidth = newValue[0].length; + for (const row of newValue) { + if (!Array.isArray(row)) { + return null; + } + if (row.length !== newWidth) { + return null; + } + } + + // Check if all contents of the arrays are either 0 or 1 + for (const row of newValue) { + for (const cell of row) { + if (cell !== 0 && cell !== 1) { + return null; + } + } + } + return newValue; + } + + /** + * Called when a new value has been validated and is about to be set. + * + * @param newValue The value that's about to be set. + */ + // eslint-disable-next-line + protected override doValueUpdate_(newValue: number[][]) { + super.doValueUpdate_(newValue); + if (newValue) { + this.imgHeight = newValue.length; + this.imgWidth = newValue[0] ? newValue[0].length : 0; + // If the field height is static, adjust the pixel size to fit. + if (this.fieldHeight) { + this.pixelSize = this.fieldHeight / this.imgHeight; + } else { + this.pixelSize = DEFAULT_PIXEL_SIZE; + } + } + this.recomputeAriaContext(); + } + + /** + * Show the bitmap editor dialog. + * + * @param e Optional mouse event that triggered the field to open, or + * undefined if triggered programmatically. + */ + // eslint-disable-next-line + protected override showEditor_(e?: Event) { + const editor = this.dropdownCreate(); + Blockly.DropDownDiv.getContentDiv().appendChild(editor); + Blockly.DropDownDiv.showPositionedByField( + this, + this.dropdownDispose.bind(this), + ); + this.focusPixelAt(0); + this.recomputeAriaContext(); + } + + /** + * Updates the block display and editor dropdown when the field re-renders. + */ + // eslint-disable-next-line + protected override render_() { + super.render_(); + + if (!this.getValue()) { + return; + } + + if (this.blockDisplayPixels) { + const display = this.blockDisplayPixels; + this.forAllCells((r, c) => { + const pixel = this.getPixel(r, c); + display[r][c].style.fill = pixel + ? this.pixelColours.filled + : this.pixelColours.empty; + }); + } + if (this.editorPixels) { + this.forAllCells((r, c) => { + this.updateEditorPixelDisplay(r, c, this.getPixel(r, c)); + }); + } + } + + override getAriaValue(): string | null { + // Get a label for the bitmap's dimensions and the number of pixels that are on. + const value = this.getValue(); + if (!value) { + return null; + } + const height = value.length; + const width = value[0].length; + let onCount = 0; + for (const row of value) { + for (const cell of row) { + if (cell === 1) { + onCount++; + } + } + } + + return (Blockly.Msg['FIELD_BITMAP_ARIA_VALUE'] ?? '%1 by %2, %3 pixels on') + .replace('%1', String(width)) + .replace('%2', String(height)) + .replace('%3', String(onCount)); + } + + /** + * Determines whether the field is editable. + * + * @returns True since it is always editable. + */ + override updateEditable() { + const editable = super.updateEditable(); + // Blockly.Field's implementation sets these classes as appropriate, but + // since this field has no text they just mess up the rendering of the grid + // lines. + const svgRoot = this.getSvgRoot(); + if (svgRoot) { + Blockly.utils.dom.removeClass(svgRoot, 'blocklyNonEditableField'); + Blockly.utils.dom.removeClass(svgRoot, 'blocklyEditableField'); + } + return editable; + } + + /** + * Gets the rectangle built out of dimensions matching SVG's element. + * + * @returns The newly created rectangle of same size as the SVG element. + */ + override getScaledBBox() { + const boundingBox = this.getSvgRoot()?.getBoundingClientRect(); + if (!boundingBox) { + throw new Error('Tried to retrieve a bounding box without a rect'); + } + return new Blockly.utils.Rect( + boundingBox.top, + boundingBox.bottom, + boundingBox.left, + boundingBox.right, + ); + } + + /** + * Creates the bitmap editor and add event listeners. + * + * @returns The newly created dropdown menu. + */ + private dropdownCreate() { + const dropdownEditor = this.createElementWithClassname( + 'div', + 'dropdownEditor', + ); + if (this.buttonOptions.randomize || this.buttonOptions.clear) { + dropdownEditor.classList.add('has-buttons'); + } + + // This prevents the normal max-height from adding a scroll bar for large images. + Blockly.DropDownDiv.getContentDiv().classList.add('contains-bitmap-editor'); + + this.bindEvent(dropdownEditor, 'pointermove', this.onPointerMove); + this.bindEvent(dropdownEditor, 'pointerup', this.onPointerEnd); + this.bindEvent(dropdownEditor, 'pointerleave', this.onPointerEnd); + this.bindEvent(dropdownEditor, 'pointerdown', this.onPointerStart); + this.bindEvent(dropdownEditor, 'pointercancel', this.onPointerEnd); + this.bindEvent(dropdownEditor, 'keydown', this.onEditorKeyDown); + // Stop the browser from handling touch events and cancelling the event. + this.bindEvent(dropdownEditor, 'touchmove', (e: Event) => { + e.preventDefault(); + }); + + const rtl = !!this.getSourceBlock()?.workspace.RTL; + this.pixelGrid = this.createPixelGrid(rtl); + dropdownEditor.appendChild(this.pixelGrid); + + // Add control buttons below the pixel grid + if (this.buttonOptions.randomize) { + this.addControlButton( + dropdownEditor, + // For backwards compatibility, use the old message if it exists, otherwise use the new message. + Blockly.Msg['BUTTON_LABEL_RANDOMIZE'] ?? + Blockly.Msg['FIELD_BITMAP_BUTTON_LABEL_RANDOMIZE'], + this.randomizePixels, + ); + } + if (this.buttonOptions.clear) { + this.addControlButton( + dropdownEditor, + // For backwards compatibility, use the old message if it exists, otherwise use the new message. + Blockly.Msg['BUTTON_LABEL_CLEAR'] ?? + Blockly.Msg['FIELD_BITMAP_BUTTON_LABEL_CLEAR'], + this.clearPixels, + ); + } + + // Store the initial value at the start of the edit. + this.initialValue = this.getValue(); + + return dropdownEditor; + } + + /** + * Builds the accessible pixel grid DOM. + * + * @param rtl Whether the workspace is RTL. + * @returns The grid root element. + */ + private createPixelGrid(rtl: boolean): HTMLDivElement { + const grid = document.createElement('div'); + grid.className = 'bitmapPixelGrid'; + grid.tabIndex = 0; + Blockly.utils.aria.setRole(grid, Blockly.utils.aria.Role.GRID); + grid.style.setProperty('--bitmap-columns', `${this.imgWidth}`); + + this.boundEvents.push( + Blockly.browserEvents.bind(grid, 'keydown', this, (e: KeyboardEvent) => { + this.onPixelGridKeyDown(e, rtl); + }), + ); + + this.editorPixels = []; + for (let r = 0; r < this.imgHeight; r++) { + const row = document.createElement('div'); + row.className = 'bitmapPixelRow'; + Blockly.utils.aria.setRole(row, Blockly.utils.aria.Role.ROW); + grid.appendChild(row); + + this.editorPixels.push([]); + for (let c = 0; c < this.imgWidth; c++) { + const cell = document.createElement('div'); + Blockly.utils.aria.setRole(cell, Blockly.utils.aria.Role.GRIDCELL); + + const button = document.createElement('button'); + button.type = 'button'; + button.id = Blockly.utils.idGenerator.getNextUniqueId(); + button.className = 'pixelButton'; + button.setAttribute('data-row', r.toString()); + button.setAttribute('data-col', c.toString()); + // Keyboard activation is handled on the grid; avoid a second toggle + // from the native button click after pointer paint. + button.addEventListener('click', (e) => e.preventDefault()); + + cell.appendChild(button); + row.appendChild(cell); + this.editorPixels[r].push(button); + this.updateEditorPixelDisplay(r, c, this.getPixel(r, c)); + } + } + return grid; + } + + /** + * Handles editor-level keyboard shortcuts. + * Ctrl/Cmd+Enter commits and closes; Escape reverts and closes. + * + * @param e The keydown event. + */ + private onEditorKeyDown(e: KeyboardEvent) { + const isEscape = e.key === 'Escape'; + const isCommit = e.key === 'Enter' && (e.ctrlKey || e.metaKey); + if (!isEscape && !isCommit) return; + + if (isEscape && this.initialValue !== null) { + this.setValue(this.initialValue, false); + } + Blockly.DropDownDiv.hideIfOwner(this); + Blockly.getFocusManager().focusNode(this); + e.preventDefault(); + e.stopPropagation(); + } + + /** + * Handles keyboard navigation and activation inside the pixel grid. + * + * @param e The keydown event. + * @param rtl Whether the workspace is RTL. + */ + private onPixelGridKeyDown(e: KeyboardEvent, rtl: boolean) { + if ( + !this.editorPixels || + e.shiftKey || + e.ctrlKey || + e.metaKey || + e.altKey + ) { + return; + } + + const length = this.imgWidth * this.imgHeight; + if (!length) return; + + if (this.focusedPixelIndex < 0) { + this.focusedPixelIndex = 0; + } + + switch (e.key) { + case 'ArrowUp': + case 'ArrowDown': + case 'ArrowLeft': + case 'ArrowRight': { + const next = this.getNextPixelIndex(this.focusedPixelIndex, e.key, rtl); + if (next === null) { + (this.getSourceBlock()?.workspace as Blockly.WorkspaceSvg | null) + ?.getAudioManager() + .playErrorBeep(); + e.preventDefault(); + e.stopPropagation(); + return; + } + this.focusPixelAt(next); + break; + } + case 'PageUp': + case 'Home': + this.focusPixelAt(0); + break; + case 'PageDown': + case 'End': + this.focusPixelAt(length - 1); + break; + case 'Enter': + case ' ': + case 'Space': { + const [r, c] = this.indexToCoords(this.focusedPixelIndex); + this.togglePixel(r, c); + e.preventDefault(); + e.stopPropagation(); + return; + } + default: + return; + } + e.preventDefault(); + e.stopPropagation(); + } + + /** + * Returns the next pixel index for an arrow key, or null at hard edges. + * + * @param current Current focused index. + * @param key Arrow key. + * @param rtl Whether the workspace is RTL. + * @returns Next index, or null if navigation should no-op. + */ + private getNextPixelIndex( + current: number, + key: string, + rtl: boolean, + ): number | null { + const col = current % this.imgWidth; + const row = Math.floor(current / this.imgWidth); + const length = this.imgWidth * this.imgHeight; + + let effectiveKey = key; + if (rtl) { + if (key === 'ArrowLeft') effectiveKey = 'ArrowRight'; + else if (key === 'ArrowRight') effectiveKey = 'ArrowLeft'; + } + + switch (effectiveKey) { + case 'ArrowLeft': + return col > 0 ? current - 1 : null; + case 'ArrowRight': + return col < this.imgWidth - 1 && current + 1 < length + ? current + 1 + : null; + case 'ArrowUp': + return row > 0 ? current - this.imgWidth : null; + case 'ArrowDown': + return current + this.imgWidth < length + ? current + this.imgWidth + : null; + default: + return null; + } + } + + /** + * Focuses the pixel button at the given row-major index. + * + * @param index Pixel index. + */ + private focusPixelAt(index: number) { + if (!this.editorPixels || !this.pixelGrid) return; + const length = this.imgWidth * this.imgHeight; + if (index < 0 || index >= length) return; + + this.focusedPixelIndex = index; + const [r, c] = this.indexToCoords(index); + const button = this.editorPixels[r][c]; + button.focus({preventScroll: true}); + Blockly.utils.aria.setState( + this.pixelGrid, + Blockly.utils.aria.State.ACTIVEDESCENDANT, + button.id, + ); + } + + /** + * Converts a pixel index to coordinates. + * + * @param index Pixel index. + * @returns Row and column. + */ + private indexToCoords(index: number): [number, number] { + return [Math.floor(index / this.imgWidth), index % this.imgWidth]; + } + + /** + * Builds an accessible label for a pixel cell. + * + * @param r Row index (0-based). + * @param c Column index (0-based). + * @param pixelValue Pixel value (0 or 1). + * @returns Localized aria label. + */ + private getPixelAriaLabel(r: number, c: number, pixelValue: number): string { + const state = pixelValue + ? (Blockly.Msg['FIELD_BITMAP_PIXEL_ON'] ?? 'on') + : (Blockly.Msg['FIELD_BITMAP_PIXEL_OFF'] ?? 'off'); + return (Blockly.Msg['FIELD_BITMAP_PIXEL_LABEL'] ?? '%1, row %2, column %3') + .replace('%1', state) + .replace('%2', String(r + 1)) + .replace('%3', String(c + 1)); + } + + /** + * Toggles a pixel and updates the editor presentation. + * + * @param r Row index. + * @param c Column index. + * @returns The new pixel value. + */ + private togglePixel(r: number, c: number): number { + const newPixelValue = 1 - this.getPixel(r, c); + this.setPixel(r, c, newPixelValue); + return newPixelValue; + } + + /** + * Syncs one editor pixel's colour, pressed state, and aria label. + * + * @param r Row index. + * @param c Column index. + * @param pixelValue Pixel value (0 or 1). + */ + private updateEditorPixelDisplay(r: number, c: number, pixelValue: number) { + const button = this.editorPixels?.[r]?.[c]; + if (!button) return; + button.style.background = pixelValue + ? this.pixelColours.filled + : this.pixelColours.empty; + button.setAttribute('aria-pressed', String(!!pixelValue)); + Blockly.utils.aria.setState( + button, + Blockly.utils.aria.State.LABEL, + this.getPixelAriaLabel(r, c, pixelValue), + ); + } + + /** + * Initializes the on-block display. + */ + override initView() { + this.createBorderRect_(); + // Invisible fill so the rect only provides keyboard-nav focus stroke. + this.getBorderRect().style.fill = 'none'; + // Don't intercept clicks meant for the pixel rects beneath. + this.getBorderRect().style.pointerEvents = 'none'; + if (this.fieldGroup_) { + Blockly.utils.dom.addClass(this.fieldGroup_, 'blocklyField'); + } + this.blockDisplayPixels = []; + for (let r = 0; r < this.imgHeight; r++) { + const row = []; + for (let c = 0; c < this.imgWidth; c++) { + const square = Blockly.utils.dom.createSvgElement( + 'rect', + { + x: c * this.pixelSize, + y: r * this.pixelSize, + width: this.pixelSize, + height: this.pixelSize, + fill: this.pixelColours.empty, + fill_opacity: 1, // eslint-disable-line + }, + this.getSvgRoot(), + ); + row.push(square); + } + this.blockDisplayPixels.push(row); + } + // SVG strokes are centered on the path; paint the border above the + // pixels so the inner half of the focus ring isn't covered. + this.getSvgRoot()?.appendChild(this.getBorderRect()); + this.recomputeAriaContext(); + } + + /** + * Updates the size of the block based on the size of the underlying image. + */ + // eslint-disable-next-line + protected override updateSize_() { + { + const newWidth = this.pixelSize * this.imgWidth; + const newHeight = this.pixelSize * this.imgHeight; + if (this.borderRect_) { + this.borderRect_.setAttribute('width', String(newWidth)); + this.borderRect_.setAttribute('height', String(newHeight)); + } + + this.size_.width = newWidth; + this.size_.height = newHeight; + } + } + + /** + * Create control button. + * + * @param parent Parent HTML element to which control button will be added. + * @param buttonText Text of the control button. + * @param onClick Callback that will be attached to the control button. + */ + private addControlButton( + parent: HTMLElement, + buttonText: string, + onClick: () => void, + ) { + const button = this.createElementWithClassname('button', 'controlButton'); + button.innerText = buttonText; + parent.appendChild(button); + this.bindEvent(button, 'click', onClick); + } + + /** + * Disposes of events belonging to the bitmap editor. + */ + private dropdownDispose() { + if ( + this.getSourceBlock() && + this.initialValue !== null && + this.initialValue !== this.getValue() + ) { + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))( + this.sourceBlock_, + 'field', + this.name || null, + this.initialValue, + this.getValue(), + ), + ); + } + + for (const event of this.boundEvents) { + Blockly.browserEvents.unbind(event); + } + this.boundEvents.length = 0; + // Keep aria-expanded accurate on later recomputes. + this.pixelGrid = null; + this.editorPixels = null; + this.focusedPixelIndex = -1; + this.pointerIsDown = false; + this.valToPaintWith = undefined; + // Set this.initialValue back to null. + this.initialValue = null; + + Blockly.DropDownDiv.getContentDiv().classList.remove( + 'contains-bitmap-editor', + ); + this.recomputeAriaContext(); + } + + /** + * Constructs an array of zeros with the specified width and height. + * + * @returns The new value. + */ + private getEmptyArray(): number[][] { + const newVal: number[][] = []; + for (let r = 0; r < this.imgHeight; r++) { + newVal.push([]); + for (let c = 0; c < this.imgWidth; c++) { + newVal[r].push(0); + } + } + return newVal; + } + + /** + * Toggles the pixel under the pointer and starts a drag-paint gesture. + * + * @param e The down event. + */ + private onPointerStart(e: PointerEvent) { + if (e.button !== 0) return; + const pixelCoords = this.getPixelCoordsFromElement(e.target as Element); + if (pixelCoords) { + const newPixelValue = this.togglePixel(pixelCoords.r, pixelCoords.c); + this.pointerIsDown = true; + this.valToPaintWith = newPixelValue; + this.focusPixelAt(pixelCoords.r * this.imgWidth + pixelCoords.c); + // Keep receiving move/up outside the editor so drag-paint can continue + // (and pointerleave does not end the gesture). + (e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId); + e.preventDefault(); + } + } + + /** + * Moves focus (and paints when dragging) as the pointer moves over pixels. + * + * @param e The move event. + */ + private onPointerMove(e: PointerEvent) { + // Same guard as field-grid-dropdown: ignore moves with no pointer delta + // (e.g. content scrolled under a still pointer). + if (!(e.movementX || e.movementY)) { + return; + } + const currentElement = document.elementFromPoint(e.clientX, e.clientY); + const pixelCoords = this.getPixelCoordsFromElement(currentElement); + if (pixelCoords) { + this.focusPixelAt(pixelCoords.r * this.imgWidth + pixelCoords.c); + if (this.pointerIsDown) { + this.updatePixelValue(pixelCoords.r, pixelCoords.c); + } + } + if (this.pointerIsDown) { + e.preventDefault(); + } + } + + /** + * Reads row/column indices from a pixel button or a descendant. + * + * @param element Element under the pointer. + * @returns Row and column, or null if not a pixel. + */ + private getPixelCoordsFromElement( + element: Element | null, + ): {r: number; c: number} | null { + const pixelButton = element?.closest('.pixelButton'); + const rowIndex = pixelButton?.getAttribute('data-row'); + const colIndex = pixelButton?.getAttribute('data-col'); + if (rowIndex == null || colIndex == null) { + return null; + } + return {r: parseInt(rowIndex), c: parseInt(colIndex)}; + } + + /** + * Sets the specified pixel in the editor to the current value being painted. + * + * @param r Row number of grid. + * @param c Column number of grid. + */ + private updatePixelValue(r: number, c: number) { + if ( + this.valToPaintWith !== undefined && + this.getPixel(r, c) !== this.valToPaintWith + ) { + this.setPixel(r, c, this.valToPaintWith); + } + } + + /** + * Resets pointer state (e.g. After either a pointerup event or if the + * gesture is canceled). + * + * @param e The pointer event that ended the gesture. + */ + private onPointerEnd(e: PointerEvent) { + if ( + e.currentTarget instanceof HTMLElement && + e.currentTarget.hasPointerCapture?.(e.pointerId) + ) { + e.currentTarget.releasePointerCapture(e.pointerId); + } + this.pointerIsDown = false; + this.valToPaintWith = undefined; + } + + /** + * Sets all the pixels in the image to a random value. + */ + private randomizePixels() { + const getRandBinary = () => Math.floor(Math.random() * 2); + this.forAllCells((r, c) => { + this.setPixel(r, c, getRandBinary()); + }); + } + + /** + * Sets all the pixels to 0. + */ + private clearPixels() { + const cleared = this.getEmptyArray(); + this.fireIntermediateChangeEvent(cleared); + this.setValue(cleared, false); + this.forAllCells((r, c) => { + this.updateEditorPixelDisplay(r, c, 0); + }); + } + + /** + * Sets the value of a particular pixel. + * + * @param r Row number of grid. + * @param c Column number of grid. + * @param newValue Value of the pixel. + */ + private setPixel(r: number, c: number, newValue: number) { + const newGrid = JSON.parse(JSON.stringify(this.getValue())); + newGrid[r][c] = newValue; + this.fireIntermediateChangeEvent(newGrid); + this.setValue(newGrid, false); + this.updateEditorPixelDisplay(r, c, newValue); + } + + private getPixel(row: number, column: number): number { + const value = this.getValue(); + if (!value) { + throw new Error( + 'Attempted to retrieve a pixel value when no value is set', + ); + } + + return value[row][column]; + } + + /** + * Calls a given function for all cells in the image, with the cell + * coordinates as the arguments. + * + * @param func A function to be applied. + */ + private forAllCells(func: (row: number, col: number) => void) { + for (let r = 0; r < this.imgHeight; r++) { + for (let c = 0; c < this.imgWidth; c++) { + func(r, c); + } + } + } + + /** + * Creates a new element with the specified type and class. + * + * @param elementType Type of html element. + * @param className ClassName of html element. + * @returns The created element. + */ + private createElementWithClassname(elementType: string, className: string) { + const newElt = document.createElement(elementType); + newElt.className = className; + return newElt; + } + + /** + * Binds an event listener to the specified element. + * + * @param element Specified element. + * @param eventName Name of the event to bind. + * @param callback Function to be called on specified event. + */ + private bindEvent( + element: HTMLElement, + eventName: string, + callback: (e: E) => void, + ) { + this.boundEvents.push( + Blockly.browserEvents.bind(element, eventName, this, callback), + ); + } + + private fireIntermediateChangeEvent(newValue: number[][]) { + if (this.getSourceBlock()) { + Blockly.Events.fire( + new (Blockly.Events.get( + Blockly.Events.BLOCK_FIELD_INTERMEDIATE_CHANGE, + ))(this.getSourceBlock(), this.name || null, this.getValue(), newValue), + ); + } + } +} + +interface Buttons { + readonly randomize: boolean; + readonly clear: boolean; +} +interface PixelColours { + readonly empty: string; + readonly filled: string; +} + +export interface FieldBitmapFromJsonConfig extends Blockly.FieldConfig { + value?: number[][]; + width?: number; + height?: number; + buttons?: Buttons; + fieldHeight?: number; + colours?: PixelColours; +} + +Blockly.fieldRegistry.register('field_bitmap', FieldBitmap); + +/** + * CSS for bitmap field. + */ +Blockly.Css.register(` +.dropdownEditor { + align-items: center; + flex-direction: column; + display: flex; + justify-content: center; +} +.dropdownEditor.has-buttons { + margin-bottom: 20px; +} +.bitmapPixelGrid { + display: grid; + margin: 20px; + grid-template-columns: repeat(var(--bitmap-columns), min-content); +} +.bitmapPixelRow { + display: contents; +} +.bitmapPixelGrid [role="gridcell"] { + padding: 0; + margin: 0; + line-height: 0; +} +.pixelButton { + width: ${DEFAULT_PIXEL_SIZE}px; + height: ${DEFAULT_PIXEL_SIZE}px; + border: 1px solid #000; + cursor: pointer; +} +.pixelButton:focus { + outline: var(--blockly-selection-width) solid var(--blockly-active-node-color); + outline-offset: -2px; + box-shadow: none; + position: relative; + z-index: 1; +} +.controlButton { + margin: 5px 0; +} +.blocklyDropDownContent.contains-bitmap-editor { + max-height: none; +} +`); diff --git a/packages/plugins/field-bitmap/src/index.ts b/packages/plugins/field-bitmap/src/index.ts new file mode 100644 index 00000000000..1e196ea9b27 --- /dev/null +++ b/packages/plugins/field-bitmap/src/index.ts @@ -0,0 +1,7 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './field-bitmap'; diff --git a/packages/plugins/field-bitmap/test/field_test.mocha.js b/packages/plugins/field-bitmap/test/field_test.mocha.js new file mode 100644 index 00000000000..d809bb21b22 --- /dev/null +++ b/packages/plugins/field-bitmap/test/field_test.mocha.js @@ -0,0 +1,215 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +const {testHelpers} = require('@blockly/dev-tools'); +const {assert} = require('chai'); +const {FieldBitmap, DEFAULT_HEIGHT, DEFAULT_WIDTH} = require('../src/index'); + +/** + * Helper method to reformat raw test cases to a format that the + * various tests can use. + * @param {!Array} testCases raw testcases that + * need to be reformatted + * @returns {!Array} the reformatted test cases + * that can be used by various tests + */ +function processTestCases(testCases) { + return testCases.map((x) => ({ + title: x.title, + value: x.value, + args: [x.value, null, {}], + json: {value: x.value}, + })); +} + +const { + FieldCreationTestCase, + FieldValueTestCase, + runConstructorSuiteTests, + runFromJsonSuiteTests, +} = testHelpers; + +suite('FieldBitmap', function () { + /** + * Configuration for field tests with invalid values. + * @type {Array} + */ + const invalidValueTestCases = processTestCases([ + {title: 'Undefined', value: undefined}, + {title: 'Null', value: null}, + {title: 'NaN', value: NaN}, + {title: 'Non-Parsable String', value: 'bad'}, + {title: 'Not an array', value: 1}, + {title: 'Not a 2D array', value: [0, 1, 0, 1]}, + { + title: 'Not a rectangle', + value: [ + [1, 1, 1], + [1, 1, 1, 1, 1, 1], + [1, 1, 1], + ], + }, + { + title: 'Contains non-binary number', + value: [ + [1, 99, 1], + [1, 1, 1], + [1, 1, 1], + ], + }, + { + title: 'Contains bad value', + value: [ + [1, 'b', 1], + [1, 1, 1], + [1, 1, 1], + ], + }, + ]); + + /** + * Configuration for field tests with valid values. + * @type {Array} + */ + const validValueTestCases = processTestCases([ + { + title: '3x3 solid', + value: [ + [1, 1, 1], + [1, 1, 1], + [1, 1, 1], + ], + }, + { + title: '4x4 solid', + value: [ + [1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1], + ], + }, + { + title: '3x3 checkerboard', + value: [ + [1, 0, 1], + [0, 1, 0], + [1, 0, 1], + ], + }, + { + title: '4x4 empty', + value: [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + ], + }, + ]); + + /** + * The expected default value for the field being tested. + * @type {*} + */ + const defaultFieldValue = [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ]; + /** + * Asserts that the field property values are set to default. + * @param {FieldBitmap} field The field to check. + */ + const assertFieldDefault = function (field) { + assert.deepEqual(field.getValue(), defaultFieldValue); + }; + /** + * Asserts that the field properties are correct based on the test case. + * @param {FieldBitmap} field The field to check. + * @param {FieldValueTestCase} testCase The test case. + */ + const validTestCaseAssertField = function (field, testCase) { + const expectedValue = testCase.value; + assert.deepEqual(field.getValue(), expectedValue); + assert.equal(field.getImageHeight(), expectedValue.length); + assert.equal(field.getImageWidth(), expectedValue[0].length); + }; + + /** + * Reimplementation of the function from TestHelpers that supports array + * equality testing. + * @param {*} validValueTestCases test cases for valid field values + * @param {*} invalidValueTestCases test cases for invalid field values + */ + function runSetValueTests(validValueTestCases, invalidValueTestCases) { + const createFieldForTestCase = (testCase) => + FieldBitmap.fromJson(`{ + width: ${ + Array.isArray(testCase.value) ? testCase.value.length : DEFAULT_WIDTH + }, + height:${ + testCase.value && Array.isArray(testCase.value[0]) + ? testCase.value[0].length + : DEFAULT_HEIGHT + }, + }`); + + /** + * Creates test callback for invalid setValue test. + * @param {!FieldValueTestCase} testCase The test case information. + * @returns {!Function} The test callback. + */ + const createInvalidSetValueTestCallback = (testCase) => { + return function () { + const field = createFieldForTestCase(testCase); + field.setValue(testCase.value); + assertFieldDefault(field); + }; + }; + /** + * Creates test callback for valid setValue test. + * @param {!FieldValueTestCase} testCase The test case information. + * @returns {!Function} The test callback. + */ + const createValidSetValueTestCallback = (testCase) => { + return function () { + const field = createFieldForTestCase(testCase); + field.setValue(testCase.value); + validTestCaseAssertField(field, testCase); + }; + }; + + testHelpers.runTestCases( + invalidValueTestCases, + createInvalidSetValueTestCallback, + ); + testHelpers.runTestCases( + validValueTestCases, + createValidSetValueTestCallback, + ); + } + + runConstructorSuiteTests( + FieldBitmap, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + runFromJsonSuiteTests( + FieldBitmap, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + runSetValueTests(validValueTestCases, invalidValueTestCases); +}); diff --git a/packages/plugins/field-bitmap/test/index.html b/packages/plugins/field-bitmap/test/index.html new file mode 100644 index 00000000000..10cd0a0c1da --- /dev/null +++ b/packages/plugins/field-bitmap/test/index.html @@ -0,0 +1,18 @@ + + + + + Blockly Field Test + + + + +
+ + + diff --git a/packages/plugins/field-bitmap/test/index.js b/packages/plugins/field-bitmap/test/index.js new file mode 100644 index 00000000000..8eef0a91a7b --- /dev/null +++ b/packages/plugins/field-bitmap/test/index.js @@ -0,0 +1,336 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Field test. + */ + +import '../src/field-bitmap'; + +import {createPlayground} from '@blockly/dev-tools'; +import * as Blockly from 'blockly'; + +/** + * An array of blocks that are defined only for the purposes of + * manually and visually testing the bitmap field. + */ +const testBlockDefinitions = [ + { + type: 'test_standard_field_values', + message0: '%1', + args0: [ + { + type: 'field_bitmap', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_bitmap`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values', + message0: '%1', + args0: [ + { + type: 'field_bitmap', + name: 'FIELDNAME', + value: [ + [1, 0, 1], + [0, 1, 0], + [0, 0, 0], + ], + width: 3, + height: 3, + alt: { + type: 'field_label', + text: `No field_bitmap`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_standard_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_bitmap', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_bitmap`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_bitmap', + name: 'FIELDNAME', + value: [ + [1, 0, 1], + [0, 1, 0], + [0, 0, 0], + ], + width: 3, + height: 3, + alt: { + type: 'field_label', + text: `No field_bitmap`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_colours_no_buttons', + message0: '%1', + args0: [ + { + type: 'field_bitmap', + name: 'FIELDNAME', + value: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + fieldHeight: 50, + buttons: { + randomize: false, + clear: false, + }, + colours: { + filled: '#4888f4', + empty: '#cad2dc', + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_colours_no_buttons_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_bitmap', + name: 'FIELDNAME', + value: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + fieldHeight: 50, + buttons: { + randomize: false, + clear: false, + }, + colours: { + filled: '#4888f4', + empty: '#cad2dc', + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_parent_block', + message0: 'parent %1', + args0: [ + { + type: 'input_value', + name: 'INPUT', + }, + ], + previousStatement: null, + nextStatement: null, + style: 'loop_blocks', + }, +]; + +Blockly.defineBlocksWithJsonArray(testBlockDefinitions); + +/** + * A test toolbox containing the exported blocks and a variety of + * test blocks to exercise the bitmap field in different contexts + * (on a shadow block, as the only field on a block, etc). + * These are in a simple toolbox, rather than a category toolbox, so that + * they are all instantiated every time the test page is opened. + */ +const jsonToolbox = { + contents: [ + { + kind: 'label', + text: 'Test blocks: default field values', + }, + { + kind: 'block', + type: 'test_standard_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_standard_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values_and_label', + }, + }, + }, + }, + { + kind: 'label', + text: 'Test blocks: custom field values', + }, + { + kind: 'block', + type: 'test_custom_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_custom_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values_and_label', + }, + }, + }, + }, + { + kind: 'label', + text: 'Test blocks: Static field height, custom colours, no buttons', + }, + { + kind: 'block', + type: 'test_custom_colours_no_buttons', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_colours_no_buttons', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_custom_colours_no_buttons_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_colours_no_buttons_and_label', + }, + }, + }, + }, + ], +}; + +/** + * Create a workspace. + * @param {HTMLElement} blocklyDiv The blockly container div. + * @param {!Blockly.BlocklyOptions} options The Blockly options. + * @returns {!Blockly.WorkspaceSvg} The created workspace. + */ +function createWorkspace(blocklyDiv, options) { + const workspace = Blockly.inject(blocklyDiv, options); + return workspace; +} + +document.addEventListener('DOMContentLoaded', function () { + const defaultOptions = { + toolbox: jsonToolbox, + }; + createPlayground( + document.getElementById('root'), + createWorkspace, + defaultOptions, + ); +}); diff --git a/packages/plugins/field-bitmap/tsconfig.json b/packages/plugins/field-bitmap/tsconfig.json new file mode 100644 index 00000000000..99858c525f1 --- /dev/null +++ b/packages/plugins/field-bitmap/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "module": "es2015", + "moduleResolution": "bundler", + "target": "es6", + "strict": true + }, + // NOTE: `test/**/*` is automatically included in `blockly-scripts start`. + // Only src matters for production builds. + "include": ["src"] +} diff --git a/packages/plugins/field-colour-hsv-sliders/CHANGELOG.md b/packages/plugins/field-colour-hsv-sliders/CHANGELOG.md new file mode 100644 index 00000000000..64a51a089bc --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/CHANGELOG.md @@ -0,0 +1,673 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [6.0.12](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.11...@blockly/field-colour-hsv-sliders@6.0.12) (2026-04-09) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.11](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.10...@blockly/field-colour-hsv-sliders@6.0.11) (2026-01-15) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.10](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.9...@blockly/field-colour-hsv-sliders@6.0.10) (2026-01-05) + + +### Bug Fixes + +* update plugin URLs to RPF ([#2665](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2665)) ([6c34039](https://github.com/RaspberryPiFoundation/blockly-samples/commit/6c3403960b8070eb8e576bd5702bdb53a09ac475)) + + + +## [6.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.8...@blockly/field-colour-hsv-sliders@6.0.9) (2026-01-05) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.7...@blockly/field-colour-hsv-sliders@6.0.8) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.6...@blockly/field-colour-hsv-sliders@6.0.7) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.5...@blockly/field-colour-hsv-sliders@6.0.6) (2025-10-30) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.4...@blockly/field-colour-hsv-sliders@6.0.5) (2025-10-02) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.3...@blockly/field-colour-hsv-sliders@6.0.4) (2025-07-17) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.2...@blockly/field-colour-hsv-sliders@6.0.3) (2025-07-03) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.1...@blockly/field-colour-hsv-sliders@6.0.2) (2025-06-19) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@6.0.0...@blockly/field-colour-hsv-sliders@6.0.1) (2025-05-22) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [6.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.19...@blockly/field-colour-hsv-sliders@6.0.0) (2025-05-16) + + +### ⚠ BREAKING CHANGES + +* update all plugins to blockly v12 (#2538) + +### Features + +* update all plugins to blockly v12 ([#2538](https://github.com/google/blockly-samples/issues/2538)) ([420ba31](https://github.com/google/blockly-samples/commit/420ba3124b260b2c06e8e1d61be44e870780d95f)) + + + +## [5.0.19](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.18...@blockly/field-colour-hsv-sliders@5.0.19) (2025-05-16) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.18](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.17...@blockly/field-colour-hsv-sliders@5.0.18) (2025-05-15) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.17](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.16...@blockly/field-colour-hsv-sliders@5.0.17) (2025-05-08) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.15...@blockly/field-colour-hsv-sliders@5.0.16) (2025-03-27) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.14...@blockly/field-colour-hsv-sliders@5.0.15) (2025-02-13) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.13...@blockly/field-colour-hsv-sliders@5.0.14) (2025-01-23) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.12...@blockly/field-colour-hsv-sliders@5.0.13) (2024-12-19) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.11...@blockly/field-colour-hsv-sliders@5.0.12) (2024-12-03) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.10...@blockly/field-colour-hsv-sliders@5.0.11) (2024-11-07) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.9...@blockly/field-colour-hsv-sliders@5.0.10) (2024-09-26) + + +### Bug Fixes + +* **field-*:** Remove unneeded `fieldRegistry.unregister` calls ([#2454](https://github.com/google/blockly-samples/issues/2454)) ([b3ba30e](https://github.com/google/blockly-samples/commit/b3ba30e23dddf0bd98c266659aa229ba6ba685b0)), closes [#2453](https://github.com/google/blockly-samples/issues/2453) + + + + + +## [5.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.8...@blockly/field-colour-hsv-sliders@5.0.9) (2024-08-29) + + +### Bug Fixes + +* predeploy plugins ([#2449](https://github.com/google/blockly-samples/issues/2449)) ([6b36d8b](https://github.com/google/blockly-samples/commit/6b36d8b344a969f79d89bbc7dcee29ae554759f9)) + + + + + +## [5.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.7...@blockly/field-colour-hsv-sliders@5.0.8) (2024-08-22) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.6...@blockly/field-colour-hsv-sliders@5.0.7) (2024-08-08) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.5...@blockly/field-colour-hsv-sliders@5.0.6) (2024-08-01) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.4...@blockly/field-colour-hsv-sliders@5.0.5) (2024-07-25) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.3...@blockly/field-colour-hsv-sliders@5.0.4) (2024-07-11) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.2...@blockly/field-colour-hsv-sliders@5.0.3) (2024-07-04) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.1...@blockly/field-colour-hsv-sliders@5.0.2) (2024-06-06) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [5.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@5.0.0...@blockly/field-colour-hsv-sliders@5.0.1) (2024-05-30) + + +### Bug Fixes + +* multiple blockly instances ([#2375](https://github.com/google/blockly-samples/issues/2375)) ([b231e59](https://github.com/google/blockly-samples/commit/b231e598f2f5f5b0abbfd01d981e35572ad50a26)) + + + +## [5.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.2.7...@blockly/field-colour-hsv-sliders@5.0.0) (2024-05-21) + + +### ⚠ BREAKING CHANGES + +* update all plugins to v11 of blockly + +### Features + +* update all plugins to v11 of blockly ([5fcd3e7](https://github.com/google/blockly-samples/commit/5fcd3e7d53eaadffe9bda9a378b404d34b2f8be2)) +* upgrade ts version, fix field validators ([b2d2191](https://github.com/google/blockly-samples/commit/b2d2191ff11b30347b33f95acf6e58f7ce54d004)) + + +### Bug Fixes + +* Call blocks handle both manual disabling and disabled defs ([#2334](https://github.com/google/blockly-samples/issues/2334)) ([5eade55](https://github.com/google/blockly-samples/commit/5eade55779c4022d14ad4472ff32c93c78199887)) +* remove some uses of generateFieldTextBlocks ([#2357](https://github.com/google/blockly-samples/issues/2357)) ([53b3fa4](https://github.com/google/blockly-samples/commit/53b3fa4604aabb1fc5240d41c8d1d389d4b15301)) + + + +## [4.2.7](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.2.6...@blockly/field-colour-hsv-sliders@4.2.7) (2024-05-16) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.2.6](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.2.5...@blockly/field-colour-hsv-sliders@4.2.6) (2024-05-09) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.2.5](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.2.4...@blockly/field-colour-hsv-sliders@4.2.5) (2024-04-25) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.2.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.2.3...@blockly/field-colour-hsv-sliders@4.2.4) (2024-04-11) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.2.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.2.2...@blockly/field-colour-hsv-sliders@4.2.3) (2024-04-04) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.2.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.2.1...@blockly/field-colour-hsv-sliders@4.2.2) (2024-03-28) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.2.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.2.0...@blockly/field-colour-hsv-sliders@4.2.1) (2024-02-08) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.2.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.9...@blockly/field-colour-hsv-sliders@4.2.0) (2024-02-01) + + +### Features + +* Use intermediate change events for HSV sliders. ([#2173](https://github.com/google/blockly-samples/issues/2173)) ([ed56b79](https://github.com/google/blockly-samples/commit/ed56b79be5507790572000f028a49d7bf317896d)) + + + +## [4.1.9](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.8...@blockly/field-colour-hsv-sliders@4.1.9) (2023-12-14) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.1.8](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.7...@blockly/field-colour-hsv-sliders@4.1.8) (2023-12-07) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.1.7](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.6...@blockly/field-colour-hsv-sliders@4.1.7) (2023-11-09) + + +### Bug Fixes + +* add types for filed-colour-hsv-sliders ([#2063](https://github.com/google/blockly-samples/issues/2063)) ([e630704](https://github.com/google/blockly-samples/commit/e6307043f812624e00207084815554c5cebe4dc9)) + + + +## [4.1.6](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.5...@blockly/field-colour-hsv-sliders@4.1.6) (2023-11-02) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.1.5](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.4...@blockly/field-colour-hsv-sliders@4.1.5) (2023-10-30) + + +### Bug Fixes + +* **packaging:** Don't advertise non-existent ESM entrypoints ([#2022](https://github.com/google/blockly-samples/issues/2022)) ([d019198](https://github.com/google/blockly-samples/commit/d0191984399b784e2928b8fb4c58257bfa857655)), closes [#1877](https://github.com/google/blockly-samples/issues/1877) +* update tsconfigs to publish types accurately ([df4b084](https://github.com/google/blockly-samples/commit/df4b0844af712f5025a2ec842458b828f3147676)) +* use includes rather than excludes ([40efc25](https://github.com/google/blockly-samples/commit/40efc255329e3ca476ccc247b95a2d05dd77b45e)) + + + +## [4.1.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.3...@blockly/field-colour-hsv-sliders@4.1.4) (2023-09-21) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.1.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.2...@blockly/field-colour-hsv-sliders@4.1.3) (2023-09-14) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.1.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.1...@blockly/field-colour-hsv-sliders@4.1.2) (2023-08-31) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.1.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.1.0...@blockly/field-colour-hsv-sliders@4.1.1) (2023-08-17) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.1.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.0.2...@blockly/field-colour-hsv-sliders@4.1.0) (2023-08-03) + + +### Features + +* Update the field-colour-hsv-sliders package to use the field-colour package. ([#1830](https://github.com/google/blockly-samples/issues/1830)) ([e756bf4](https://github.com/google/blockly-samples/commit/e756bf488c5cf094e6fd236b70482b6bc23cae4f)) + + + +## [4.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.0.1...@blockly/field-colour-hsv-sliders@4.0.2) (2023-07-24) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@4.0.0...@blockly/field-colour-hsv-sliders@4.0.1) (2023-07-20) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [4.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@3.0.1...@blockly/field-colour-hsv-sliders@4.0.0) (2023-07-13) + + +### ⚠ BREAKING CHANGES + +* updated `plugins/field-date` to TypeScript (#1705) + +### Features + +* updated `plugins/field-date` to TypeScript ([#1705](https://github.com/google/blockly-samples/issues/1705)) ([e5531ff](https://github.com/google/blockly-samples/commit/e5531fffe188ee361a16fe48ed126b34e51a8d30)) + + + +## [3.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@3.0.0...@blockly/field-colour-hsv-sliders@3.0.1) (2023-07-06) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [3.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.1.5...@blockly/field-colour-hsv-sliders@3.0.0) (2023-06-28) + + +### ⚠ BREAKING CHANGES + +* update all plugins to use blockly-v10.0.0 (#1744) + +### Features + +* update all plugins to use blockly-v10.0.0 ([#1744](https://github.com/google/blockly-samples/issues/1744)) ([6cc88cb](https://github.com/google/blockly-samples/commit/6cc88cbef39d4ad664a668d3d46eb29ba7292f9c)) + + +### Bug Fixes + +* renaming build problems ([7087469](https://github.com/google/blockly-samples/commit/7087469ccdaf27e040cd67a386c9284aa6b1cd67)) +* update all usages of generators in script tags for blockly v10 ([#1734](https://github.com/google/blockly-samples/issues/1734)) ([19faf29](https://github.com/google/blockly-samples/commit/19faf29428e3903a60da7c2679818cf8c63b7309)) + + + +## [2.1.5](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.1.4...@blockly/field-colour-hsv-sliders@2.1.5) (2023-06-22) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.1.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.1.3...@blockly/field-colour-hsv-sliders@2.1.4) (2023-05-18) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.1.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.1.2...@blockly/field-colour-hsv-sliders@2.1.3) (2023-05-11) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.1.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.1.1...@blockly/field-colour-hsv-sliders@2.1.2) (2023-05-04) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.1.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.1.0...@blockly/field-colour-hsv-sliders@2.1.1) (2023-04-27) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.1.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.12...@blockly/field-colour-hsv-sliders@2.1.0) (2023-03-23) + + +### Features + +* Create plugin for dependent dropdown fields. ([#1555](https://github.com/google/blockly-samples/issues/1555)) ([1c473d6](https://github.com/google/blockly-samples/commit/1c473d6b2c4aa3754cf332c07585af60342537ef)) + + + +## [2.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.11...@blockly/field-colour-hsv-sliders@2.0.12) (2023-03-09) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.10...@blockly/field-colour-hsv-sliders@2.0.11) (2023-03-02) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.9...@blockly/field-colour-hsv-sliders@2.0.10) (2023-02-23) + + +### Bug Fixes + +* remove prepublishOnly scripts ([#1579](https://github.com/google/blockly-samples/issues/1579)) ([27da6cd](https://github.com/google/blockly-samples/commit/27da6cd04c38f6ba417f4e7446bb6218c475448d)) + + + +## [2.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.8...@blockly/field-colour-hsv-sliders@2.0.9) (2023-02-23) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.7...@blockly/field-colour-hsv-sliders@2.0.8) (2023-02-16) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.6...@blockly/field-colour-hsv-sliders@2.0.7) (2023-02-09) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.5...@blockly/field-colour-hsv-sliders@2.0.6) (2023-01-26) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.4...@blockly/field-colour-hsv-sliders@2.0.5) (2023-01-12) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.3...@blockly/field-colour-hsv-sliders@2.0.4) (2023-01-05) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.2...@blockly/field-colour-hsv-sliders@2.0.3) (2022-12-15) + +**Note:** Version bump only for package @blockly/field-colour-hsv-sliders + + + + + +## [2.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.1...@blockly/field-colour-hsv-sliders@2.0.2) (2022-10-27) + + +### Bug Fixes + +* disable eye dropper instead of using fallback ([#1337](https://github.com/google/blockly-samples/issues/1337)) ([7fb03d9](https://github.com/google/blockly-samples/commit/7fb03d94346ada497d0822df00364c38ea9bd4ff)) + + + +## [2.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@2.0.0...@blockly/field-colour-hsv-sliders@2.0.1) (2022-10-13) + + +### Bug Fixes + +* Reverting invalid changes to hsv slider css. ([#1329](https://github.com/google/blockly-samples/issues/1329)) ([925858d](https://github.com/google/blockly-samples/commit/925858dd1ab7befcf051bf64d045dc71ea4ed997)) + + + +## [2.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour-hsv-sliders@1.1.0...@blockly/field-colour-hsv-sliders@2.0.0) (2022-10-05) + + +### ⚠ BREAKING CHANGES + +* update peer and devDependencies of all plugins to require Blockly v9 (#1314) + +### Bug Fixes + +* Added css documentation for workspace search plugin. ([#1282](https://github.com/google/blockly-samples/issues/1282)) ([5dd7d2d](https://github.com/google/blockly-samples/commit/5dd7d2d2bed3d1e4920e27b795a06fff08e85297)) +* references to deprecated functions in v9 ([#1313](https://github.com/google/blockly-samples/issues/1313)) ([cb2e679](https://github.com/google/blockly-samples/commit/cb2e67987e0b62a77c26adc660cc6ade1ba67954)) + + +### Miscellaneous Chores + +* update peer and devDependencies of all plugins to require Blockly v9 ([#1314](https://github.com/google/blockly-samples/issues/1314)) ([03d4912](https://github.com/google/blockly-samples/commit/03d4912c42c8de0f30493037ccc28dddaea0f266)) + + + +# 1.1.0 (2022-09-08) + + +### Features + +* created plugin for colour hsv sliders field. ([#1234](https://github.com/google/blockly-samples/issues/1234)) ([d46f28e](https://github.com/google/blockly-samples/commit/d46f28e50200eb060d75171077262c071481580d)) diff --git a/packages/plugins/field-colour-hsv-sliders/README.md b/packages/plugins/field-colour-hsv-sliders/README.md new file mode 100644 index 00000000000..0c4c589e61e --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/README.md @@ -0,0 +1,88 @@ +# @blockly/field-colour-hsv-sliders [![Built on Blockly](https://tinyurl.com/built-on-blockly)](https://github.com/google/blockly) + +A [Blockly](https://www.npmjs.com/package/blockly) colour field using HSV sliders for editing. + +![A Blockly workspace showing the HSV sliders.](readme-media/hsv_sliders_screenshot.png) + +## Installation + +### Yarn + +``` +yarn add @blockly/field-colour-hsv-sliders +``` + +### npm + +``` +npm install @blockly/field-colour-hsv-sliders --save +``` + +## Usage + +This plugin adds a field type `FieldColourHsvSliders` that is registered to the name `'field_colour_hsv_sliders'`. This field is an extension of the `Blockly.FieldColour` field and outputs values in the same hexadecimal string format `'#rrggbb'` even though the UI represents the colour in the HSV colour space. See the [Blockly.FieldColour documentation](https://www.npmjs.com/package/@blockly/field-colour#creation) on what parameters and configurations this field supports, although unlike `Blockly.FieldColour`, this field does not use colour swatches and thus ignores options related to the swatches such as `'colourOptions'`. + +The UI also provides an eye dropper button that can be used to take a colour from anywhere on the screen [on supported platforms](https://caniuse.com/?search=eyedropper). Chrome, Edge, and Opera support the eye dropper feature, except [on Chromebooks](https://bugs.chromium.org/p/chromium/issues/detail?id=1348921) and on mobile devices. There is also [a known bug in Chrome on some versions of Linux](https://bugs.chromium.org/p/chromium/issues/detail?id=1227633) where the eye dropper may be visible but does not work correctly. + +To use it, you'll need to add this field to a block type definition, and add that block to your toolbox. See below for an example of defining a block type that uses this field. + +### JSON + +```js +import * as Blockly from 'blockly'; +import '@blockly/field-colour-hsv-sliders'; +import {javascriptGenerator, Order} from 'blockly/javascript'; + +Blockly.defineBlocksWithJsonArray([ + { + type: 'colour_hsv_sliders', + message0: 'hsv %1', + args0: [ + { + type: 'field_colour_hsv_sliders', + name: 'COLOUR', + colour: '#ff0000', + }, + ], + output: 'Colour', + style: 'colour_blocks', + }, +]); + +javascriptGenerator.forBlock['colour_hsv_sliders'] = function ( + block, + generator, +) { + const code = generator.quote_(block.getFieldValue('COLOUR')); + return [code, Order.ATOMIC]; +}; +``` + +### JavaScript + +```js +import * as Blockly from 'blockly'; +import {FieldColourHsvSliders} from '@blockly/field-colour-hsv-sliders'; +import {javascriptGenerator, Order} from 'blockly/javascript'; + +Blockly.Blocks['colour_hsv_sliders'] = { + init: function () { + this.appendDummyInput() + .appendField('hsv ') + .appendField(new FieldColourHsvSliders('#ff0000'), 'COLOUR'); + this.setOutput(true, 'Colour'); + this.setStyle('colour_blocks'); + }, +}; +javascriptGenerator.forBlock['colour_hsv_sliders'] = function ( + block, + generator, +) { + const code = generator.quote_(block.getFieldValue('COLOUR')); + return [code, Order.ATOMIC]; +}; +``` + +## License + +Apache 2.0 diff --git a/packages/plugins/field-colour-hsv-sliders/package.json b/packages/plugins/field-colour-hsv-sliders/package.json new file mode 100644 index 00000000000..eec665c432f --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/package.json @@ -0,0 +1,60 @@ +{ + "name": "@blockly/field-colour-hsv-sliders", + "version": "13.1.0", + "private": true, + "description": "A Blockly colour field using HSV sliders.", + "scripts": { + "audit:fix": "blockly-scripts auditFix", + "build": "blockly-scripts build", + "clean": "blockly-scripts clean", + "lint": "eslint .", + "predeploy": "blockly-scripts predeploy", + "start": "blockly-scripts start", + "test": "blockly-scripts test" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "unpkg": "./dist/index.js", + "author": "Blockly Team", + "keywords": [ + "blockly", + "colour", + "field", + "hsv", + "slider" + ], + "homepage": "https://github.com/RaspberryPiFoundation/blockly-samples/tree/master/plugins/field-colour-hsv-sliders#readme", + "bugs": { + "url": "https://github.com/RaspberryPiFoundation/blockly-samples/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/RaspberryPiFoundation/blockly-samples.git", + "directory": "plugins/field-colour-hsv-sliders" + }, + "license": "Apache-2.0", + "directories": { + "dist": "dist", + "src": "src" + }, + "files": [ + "dist", + "src" + ], + "dependencies": { + "@blockly/field-colour": "^6.0.12" + }, + "devDependencies": { + "@blockly/dev-scripts": "^13.1.0", + "@blockly/dev-tools": "^13.1.0", + "jsdom": "^29.1.1", + "jsdom-global": "^3.0.2", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "blockly": "^13.2.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugins/field-colour-hsv-sliders/readme-media/hsv_sliders_screenshot.png b/packages/plugins/field-colour-hsv-sliders/readme-media/hsv_sliders_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..409e5c6b80bed2ff35ae6458d11ab05e962ca7fb GIT binary patch literal 59431 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 literal 0 HcmV?d00001 diff --git a/packages/plugins/field-colour-hsv-sliders/src/field_colour_hsv_sliders.ts b/packages/plugins/field-colour-hsv-sliders/src/field_colour_hsv_sliders.ts new file mode 100644 index 00000000000..2d6b8195f3f --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/src/field_colour_hsv_sliders.ts @@ -0,0 +1,864 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Colour input field with HSV sliders. + */ + +import * as Blockly from 'blockly/core'; +import {FieldColour} from '@blockly/field-colour'; + +// Experimental API: https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper +declare interface EyeDropper { + open: () => Promise<{sRGBHex: string}>; +} +declare global { + interface Window { + EyeDropper?: {new (): EyeDropper}; + } +} + +/** + * A structure with three properties r, g, and b, representing the amount of + * red, green, and blue light in the sRGB colour space where 1 is the maximum + * amount of light that can be displayed. + */ +class RgbColour { + /** The red component of the colour, ranging from 0 to 1. */ + r: number; + + /** The green component of the colour, ranging from 0 to 1. */ + g: number; + + /** The blue component of the colour, ranging from 0 to 1. */ + b: number; + + /** + * The RgbColour constructor. + * + * @param r The initial amount of red. Defaults to 0. + * @param g The initial amount of green. Defaults to 0. + * @param b The initial amount of blue. Defaults to 0. + */ + constructor(r = 0, g = 0, b = 0) { + this.r = r; + this.g = g; + this.b = b; + } + + /** + * Given a number from 0 to 1, returns a two-digit hexadecimal string from + * '00' to 'ff'. + * + * @param x The amount of light in a component from 0 to 1. + * @returns A hexadecimal representation from '00' to 'ff'. + */ + static componentToHex(x: number): string { + if (x <= 0) return '00'; + if (x >= 1) return 'ff'; + return ('0' + ((x * 255 + 0.5) >>> 0).toString(16)).slice(-2); + } + + /** + * Returns a hexadecimal string in the format #rrggbb representing the colour. + * + * @returns A hexadecimal representation of this colour. + */ + toHex(): string { + return ( + '#' + + RgbColour.componentToHex(this.r) + + RgbColour.componentToHex(this.g) + + RgbColour.componentToHex(this.b) + ); + } + + /** + * Updates the properties of this instance to represent the same colour as the + * provided string in the hexadecimal format #rrggbb. + * + * @param hex A hexadecimal string in the format '#rrggbb'. + * @returns This instance after updating it. + */ + loadFromHex(hex: string): RgbColour { + this.r = parseInt(hex.slice(1, 3), 16) / 255; + this.g = parseInt(hex.slice(3, 5), 16) / 255; + this.b = parseInt(hex.slice(5, 7), 16) / 255; + return this; + } + + /** + * Updates the properties of this instance to represent the same colour as the + * provided HsvColour but in the sRGB colour space. + * + * @param hsv An HSV representation of a colour to copy. + * @returns This instance after updating it. + */ + loadFromHsv(hsv: HsvColour): RgbColour { + const hue: number = (hsv.h - Math.floor(hsv.h)) * 6; + this.r = + hsv.v * (1 - hsv.s * Math.max(0, Math.min(1, 2 - Math.abs(hue - 3)))); + this.g = + hsv.v * (1 - hsv.s * Math.max(0, Math.min(1, Math.abs(hue - 2) - 1))); + this.b = + hsv.v * (1 - hsv.s * Math.max(0, Math.min(1, Math.abs(hue - 4) - 1))); + return this; + } +} + +/** + * A structure with three properties h, s, and v, representing the hue, + * saturation, and brightness in a colour. All three properties range from 0 + * to 1. + */ +class HsvColour { + /** The hue of the colour, ranging from 0 to 1. */ + h: number; + + /** The saturation of the colour, ranging from 0 to 1. */ + s: number; + + /** The brightness of the colour, ranging from 0 to 1. */ + v: number; + + /** + * The HsvColour constructor. + * + * @param h The initial hue of the colour. Defaults to 0. + * @param s The initial amount of saturation. Defaults to 0. + * @param v The initial amount of brightness. Defaults to 0. + */ + constructor(h = 0, s = 0, v = 0) { + this.h = h; + this.s = s; + this.v = v; + } + + /** + * Updates the properties of this instance to represent the same colour as the + * provided RgbColour but in the HSV colour space. + * + * @param rgb An RGB representation of a colour to copy. + * @returns This instance after updating it. + */ + loadFromRgb(rgb: RgbColour): HsvColour { + const max: number = Math.max(Math.max(rgb.r, rgb.g), rgb.b); + const min: number = Math.min(Math.min(rgb.r, rgb.g), rgb.b); + this.v = max; + if (min === max) { + this.h = 0; + this.s = 0; + return this; + } + + const delta: number = max - min; + this.s = delta / max; + + let hue: number; + if (rgb.r === max) { + hue = (rgb.g - rgb.b) / delta; + } else if (rgb.g === max) { + hue = 2 + (rgb.b - rgb.r) / delta; + } else { + hue = 4 + (rgb.r - rgb.g) / delta; + } + hue /= 6; + this.h = hue - Math.floor(hue); + return this; + } + + /** + * Updates the properties of this instance to copy the provided HsvColour. + * + * @param other An HSV representation of a colour to copy. + * @returns This instance after updating it. + */ + copy(other: HsvColour): HsvColour { + this.h = other.h; + this.s = other.s; + this.v = other.v; + return this; + } +} + +/** + * Class for a colour input field that displays HSV slider widgets when clicked. + */ +export class FieldColourHsvSliders extends FieldColour { + /** The maximum value of the hue slider range. */ + private static readonly HUE_SLIDER_MAX = 360; + + /** The maximum value of the saturation slider range. */ + private static readonly SATURATION_SLIDER_MAX = 100; + + /** The maximum value of the brightness slider range. */ + private static readonly BRIGHTNESS_SLIDER_MAX = 100; + + /** + * The gradient control point positions should align with the center of the + * slider thumb when the corresponding colour is selected. When the slider + * is at the minimum or maximum value, the distance of center of the thumb + * from the edge of the track will be the thumb's radius, so that's how far + * the minimum and maximum control points should be. + */ + static readonly THUMB_RADIUS = 12; + + /** Helper colour structures to allow manipulation in the HSV colour space. */ + private static readonly helperHsv: HsvColour = new HsvColour(); + + /** Helper colour structures to support conversion to the RGB colour space. */ + private static readonly helperRgb: RgbColour = new RgbColour(); + + /** + * The intial value of the field when the user opened an editor to change its + * value. When the editor is disposed, an event will be fired that uses this + * as the event's oldValue. + */ + protected valueWhenEditorWasOpened: string | null = null; + + /** Array holding info needed to unbind events. Used for disposing. */ + private hsvBoundEvents: Blockly.browserEvents.Data[] = []; + + /** HTML span element to display the current hue. */ + private hueReadout: HTMLSpanElement | null = null; + + /** HTML range input element for editing hue. */ + private hueSlider: HTMLInputElement | null = null; + + /** HTML span element to display the current saturation. */ + private saturationReadout: HTMLSpanElement | null = null; + + /** HTML range input element for editing saturation. */ + private saturationSlider: HTMLInputElement | null = null; + + /** HTML span element to display the current brightness. */ + private brightnessReadout: HTMLSpanElement | null = null; + + /** HTML range input element for editing brightness. */ + private brightnessSlider: HTMLInputElement | null = null; + + /** HTML div element containing all the labels and sliders. */ + private dropdownContainer: HTMLDivElement | null = null; + + /* eslint-disable @typescript-eslint/naming-convention */ + /** + * Create and show the colour field's editor. + * + * @override + */ + protected showEditor_(): void { + /* eslint-enable @typescript-eslint/naming-convention */ + this.createDropdownSliders(); + if (!this.dropdownContainer || !this.hueSlider) { + throw new Error('Failed to initialize the HSV sliders.'); + } + Blockly.DropDownDiv.getContentDiv().appendChild(this.dropdownContainer); + + Blockly.DropDownDiv.showPositionedByField( + this, + this.dropdownDisposeSliders.bind(this), + ); + + this.valueWhenEditorWasOpened = this.value_; + + // Focus so we can start receiving keyboard events. + setTimeout(() => { + this.hueSlider?.focus({preventScroll: true}); + }, 250); + } + + /** + * Creates a row with a slider label and a readout to display the slider + * value, appends it to the provided container, and returns the readout. + * + * @param name The display name of the slider. + * @param container Where the row will be inserted. + * @returns The readout, so that it can be updated. + */ + private static createLabelInContainer( + name: string, + container: HTMLElement, + ): HTMLSpanElement { + const label: HTMLDivElement = document.createElement('div'); + const labelText: HTMLSpanElement = document.createElement('span'); + const readout: HTMLSpanElement = document.createElement('span'); + label.classList.add('fieldColourSliderLabel'); + labelText.textContent = name; + label.appendChild(labelText); + label.appendChild(readout); + container.appendChild(label); + return readout; + } + + /** + * Creates a slider, appends it to the provided container, and returns it. + * + * @param max The maximum value of the slider. + * @param step The minimum step size of the slider. + * @param container Where the row slider be inserted. + * @returns The slider. + */ + private createSliderInContainer( + max: number, + step: number, + container: HTMLElement, + ): HTMLInputElement { + const slider: HTMLInputElement = document.createElement('input'); + slider.classList.add('fieldColourSlider'); + slider.type = 'range'; + slider.min = String(0); + slider.max = String(max); + slider.step = String(step); + slider.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { + Blockly.DropDownDiv.hideIfOwner(this); + Blockly.getFocusManager().focusNode(this); + } else if (e.key === 'Escape') { + this.setValue(this.valueWhenEditorWasOpened); + } + }); + container.appendChild(slider); + return slider; + } + + /** Creates the colour picker slider editor and adds event listeners. */ + private createDropdownSliders(): void { + const container: HTMLDivElement = document.createElement('div'); + container.classList.add('fieldColourSliderContainer'); + + this.hueReadout = FieldColourHsvSliders.createLabelInContainer( + 'Hue', + container, + ); + this.hueSlider = this.createSliderInContainer( + FieldColourHsvSliders.HUE_SLIDER_MAX, + 2, + container, + ); + Blockly.utils.aria.setState( + this.hueSlider, + Blockly.utils.aria.State.LABEL, + 'Hue', + ); + this.saturationReadout = FieldColourHsvSliders.createLabelInContainer( + 'Saturation', + container, + ); + this.saturationSlider = this.createSliderInContainer( + FieldColourHsvSliders.SATURATION_SLIDER_MAX, + 1, + container, + ); + Blockly.utils.aria.setState( + this.saturationSlider, + Blockly.utils.aria.State.LABEL, + 'Saturation', + ); + this.brightnessReadout = FieldColourHsvSliders.createLabelInContainer( + 'Brightness', + container, + ); + this.brightnessSlider = this.createSliderInContainer( + FieldColourHsvSliders.BRIGHTNESS_SLIDER_MAX, + 1, + container, + ); + Blockly.utils.aria.setState( + this.brightnessSlider, + Blockly.utils.aria.State.LABEL, + 'Brightness', + ); + + this.hsvBoundEvents.push( + Blockly.browserEvents.conditionalBind( + this.hueSlider, + 'input', + this, + this.onSliderChange, + ), + ); + this.hsvBoundEvents.push( + Blockly.browserEvents.conditionalBind( + this.saturationSlider, + 'input', + this, + this.onSliderChange, + ), + ); + this.hsvBoundEvents.push( + Blockly.browserEvents.conditionalBind( + this.brightnessSlider, + 'input', + this, + this.onSliderChange, + ), + ); + + if (window.EyeDropper) { + // If the browser supports the eyedropper API, create a button for it. + const button: HTMLButtonElement = document.createElement('button'); + button.classList.add('fieldColourEyedropper'); + button.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + this.setValue(this.valueWhenEditorWasOpened); + } + }); + container.appendChild(document.createElement('hr')); + container.appendChild(button); + this.hsvBoundEvents.push( + Blockly.browserEvents.conditionalBind( + button, + 'click', + this, + this.onEyedropperEvent, + ), + ); + } + + this.dropdownContainer = container; + + this.updateSliderValues(); + } + + /** + * Updates the ARIA roles and label for this field. + */ + override recomputeAriaContext(): boolean { + const shouldCustomize = super.recomputeAriaContext(); + if (!shouldCustomize) return false; + Blockly.utils.aria.setState( + this.getFocusableElement(), + Blockly.utils.aria.State.HASPOPUP, + 'menu', + ); + return true; + } + + /** Disposes of events and DOM-references belonging to the colour editor. */ + private dropdownDisposeSliders(): void { + for (const event of this.hsvBoundEvents) { + Blockly.browserEvents.unbind(event); + } + this.hsvBoundEvents.length = 0; + this.hueReadout = null; + this.hueSlider = null; + this.saturationReadout = null; + this.saturationSlider = null; + this.brightnessReadout = null; + this.brightnessSlider = null; + this.dropdownContainer = null; + + if ( + this.sourceBlock_ && + Blockly.Events.isEnabled() && + this.valueWhenEditorWasOpened !== null && + this.valueWhenEditorWasOpened !== this.value_ + ) { + // When closing a field input widget, fire an event indicating that the + // user has completed a sequence of changes. The value may have changed + // multiple times while the editor was open, but this will fire an event + // containing the value when the editor was opened as well as the new one. + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))( + this.sourceBlock_, + 'field', + this.name || null, + this.valueWhenEditorWasOpened, + this.value_, + ), + ); + this.valueWhenEditorWasOpened = null; + } + } + + /** + * A helper function that converts a colour, specified by the provided hue, + * saturation, and brightness parameters, into a hexadecimal string in the + * format "#rrggbb". + * + * @param hue The hue of the colour. + * @param saturation The saturation of the colour. + * @param brightness The brightness of the colour. + * @returns A hexadecimal representation of the colour in the format "#rrggbb" + */ + private static hsvToHex( + hue: number, + saturation: number, + brightness: number, + ): string { + FieldColourHsvSliders.helperHsv.h = hue; + FieldColourHsvSliders.helperHsv.s = saturation; + FieldColourHsvSliders.helperHsv.v = brightness; + return FieldColourHsvSliders.helperRgb + .loadFromHsv(FieldColourHsvSliders.helperHsv) + .toHex(); + } + + /** + * Updates the value of this field based on the editor sliders. + * + * @param event Unused. + */ + private onSliderChange(event?: Event): void { + if (!this.hueSlider || !this.saturationSlider || !this.brightnessSlider) { + throw new Error('The HSV sliders are missing.'); + } + const hue: number = + parseFloat(this.hueSlider.value) / FieldColourHsvSliders.HUE_SLIDER_MAX; + const saturation: number = + parseFloat(this.saturationSlider.value) / + FieldColourHsvSliders.SATURATION_SLIDER_MAX; + const brightness: number = + parseFloat(this.brightnessSlider.value) / + FieldColourHsvSliders.BRIGHTNESS_SLIDER_MAX; + this.setIntermediateValue( + FieldColourHsvSliders.hsvToHex(hue, saturation, brightness), + ); + this.renderSliders(); + } + + /** + * Updates the value of this field and editor sliders using an eyedropper. + * + * @param event Unused. + */ + private onEyedropperEvent(event?: Event): void { + if (window.EyeDropper) { + const eyeDropper: EyeDropper = new window.EyeDropper(); + eyeDropper.open().then( + (result) => { + this.setIntermediateValue(result.sRGBHex); + this.updateSliderValues(); + }, + // Prevent throwing by handling promise rejection and doing nothing. + () => {}, + ); + } + } + + private setIntermediateValue(value: string): void { + // Intermediate value changes from user input are not confirmed until the + // user closes the editor, and may be numerous. Inhibit reporting these as + // normal block change events, and instead report them as special + // intermediate changes that do not get recorded in undo history. + const oldValue = this.value_; + // Change the field's value without firing the normal change event. + this.setValue(value, false); + if ( + this.sourceBlock_ && + Blockly.Events.isEnabled() && + this.value_ !== oldValue + ) { + // Fire a special event indicating that the value changed but the change + // isn't complete yet and normal field change listeners can wait. + Blockly.Events.fire( + new (Blockly.Events.get( + Blockly.Events.BLOCK_FIELD_INTERMEDIATE_CHANGE, + ))(this.sourceBlock_, this.name || null, oldValue, this.value_), + ); + } + } + + /** + * Updates the gradient backgrounds of the slider tracks and readouts based + * on the slider values. + */ + private renderSliders(): void { + if ( + !this.hueSlider || + !this.hueReadout || + !this.saturationSlider || + !this.saturationReadout || + !this.brightnessSlider || + !this.brightnessReadout + ) { + throw new Error('The HSV sliders are missing.'); + } + this.hueReadout.textContent = this.hueSlider.value; + this.saturationReadout.textContent = this.saturationSlider.value; + this.brightnessReadout.textContent = this.brightnessSlider.value; + + const h: number = + parseFloat(this.hueSlider.value) / FieldColourHsvSliders.HUE_SLIDER_MAX; + const s: number = + parseFloat(this.saturationSlider.value) / + FieldColourHsvSliders.SATURATION_SLIDER_MAX; + const v: number = + parseFloat(this.brightnessSlider.value) / + FieldColourHsvSliders.BRIGHTNESS_SLIDER_MAX; + + // The hue slider needs intermediate gradient control points to include all + // colours of the rainbow. + let hueGradient = 'linear-gradient(to right, '; + hueGradient += + FieldColourHsvSliders.hsvToHex(0 / 6, s, v) + + ` ${FieldColourHsvSliders.THUMB_RADIUS}px, `; + hueGradient += FieldColourHsvSliders.hsvToHex(1 / 6, s, v) + ', '; + hueGradient += FieldColourHsvSliders.hsvToHex(2 / 6, s, v) + ', '; + hueGradient += FieldColourHsvSliders.hsvToHex(3 / 6, s, v) + ', '; + hueGradient += FieldColourHsvSliders.hsvToHex(4 / 6, s, v) + ', '; + hueGradient += FieldColourHsvSliders.hsvToHex(5 / 6, s, v) + ', '; + hueGradient += + FieldColourHsvSliders.hsvToHex(6 / 6, s, v) + + ` calc(100% - ${FieldColourHsvSliders.THUMB_RADIUS}px))`; + this.hueSlider.style.setProperty('--slider-track-background', hueGradient); + + // The saturation slider only needs gradient control points at each end. + let saturationGradient = 'linear-gradient(to right, '; + saturationGradient += + FieldColourHsvSliders.hsvToHex(h, 0, v) + + ` ${FieldColourHsvSliders.THUMB_RADIUS}px, `; + saturationGradient += + FieldColourHsvSliders.hsvToHex(h, 1, v) + + ` calc(100% - ${FieldColourHsvSliders.THUMB_RADIUS}px))`; + this.saturationSlider.style.setProperty( + '--slider-track-background', + saturationGradient, + ); + + // The brightness slider only needs gradient control points at each end. + let brightnessGradient = 'linear-gradient(to right, '; + brightnessGradient += + FieldColourHsvSliders.hsvToHex(h, s, 0) + + ` ${FieldColourHsvSliders.THUMB_RADIUS}px, `; + brightnessGradient += + FieldColourHsvSliders.hsvToHex(h, s, 1) + + ` calc(100% - ${FieldColourHsvSliders.THUMB_RADIUS}px))`; + this.brightnessSlider.style.setProperty( + '--slider-track-background', + brightnessGradient, + ); + } + + /** Updates slider values based on the current value of the field. */ + private updateSliderValues(): void { + if (!this.hueSlider || !this.saturationSlider || !this.brightnessSlider) { + return; + } + + const hsv: HsvColour = FieldColourHsvSliders.helperHsv.loadFromRgb( + FieldColourHsvSliders.helperRgb.loadFromHex(this.getValue() ?? ''), + ); + + this.hueSlider.value = String(hsv.h * FieldColourHsvSliders.HUE_SLIDER_MAX); + this.saturationSlider.value = String( + hsv.s * FieldColourHsvSliders.SATURATION_SLIDER_MAX, + ); + this.brightnessSlider.value = String( + hsv.v * FieldColourHsvSliders.BRIGHTNESS_SLIDER_MAX, + ); + + this.renderSliders(); + } + + override getAriaValue() { + const hsv: HsvColour = FieldColourHsvSliders.helperHsv.loadFromRgb( + FieldColourHsvSliders.helperRgb.loadFromHex(this.getValue() ?? ''), + ); + + const components = []; + const brightness = hsv.v * 100; + if (brightness === 0) { + return 'black'; + } else if (brightness <= 25) { + components.push('very dark'); + } else if (brightness < 75) { + components.push('dark'); + } + + const saturation = hsv.s * 100; + if (saturation === 0) { + if (brightness === 100) { + return 'white'; + } + components.push('grey'); + return components.join(' '); + } else if (saturation <= 30) { + components.push(brightness <= 60 ? 'greyish' : 'pale'); + } else if (saturation >= 75) { + components.push('vibrant'); + } + + const hue = hsv.h * 360; + if (hue >= 330 || hue <= 10) { + components.push('red'); + } else if (hue <= 45) { + components.push('orange'); + } else if (hue <= 65) { + components.push('yellow'); + } else if (hue <= 160) { + components.push('green'); + } else if (hue <= 190) { + components.push('cyan'); + } else if (hue <= 260) { + components.push('blue'); + } else if (hue <= 290) { + components.push('purple'); + } else { + components.push('fuchsia'); + } + + return components.join(' '); + } +} + +Blockly.fieldRegistry.register( + 'field_colour_hsv_sliders', + FieldColourHsvSliders, +); + +// CSS for colour slider fields. +Blockly.Css.register(` +.fieldColourSliderContainer { + padding: 4px; + font-family: Roboto, Arial, sans-serif; + color: #5c5c5c; +} +.fieldColourSliderContainer hr { + border: none; + border-top: 1px solid #bbb; +} +.fieldColourSliderLabel { + display: flex; + justify-content: space-between; +} +.fieldColourEyedropper { + appearance: none; + position: relative; + border: none; + border-radius: 4px; + background: transparent; + font: inherit; + color: inherit; + cursor: pointer; + width: 100%; + margin: 0; + display: flex; + justify-content: center; + align-items: center; +} +.fieldColourEyedropper:hover { + background: rgba(0,0,0,0.1) +} +.fieldColourEyedropper input { + opacity: 0; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} +.fieldColourEyedropper::before { + content: "Eyedropper"; +} +.fieldColourEyedropper::after { + content: ""; + margin-left: 8px; + width: 24px; + height: 24px; + background: currentColor; + pointer-events: none; + -webkit-mask-image: var(--customize-dial-symbol); + -webkit-mask-repeat: no-repeat; + -webkit-mask-position: center; + mask-image: var(--customize-dial-symbol); + mask-repeat: no-repeat; + mask-position: center; + --customize-dial-symbol: url('data:image/svg+xml,\ + \ + \ + \ + '); +} +.blocklyKeyboardNavigation .fieldColourEyedropper:focus { + outline: none; + border: var(--blockly-selection-width) solid var(--blockly-active-node-color); + border-radius: 4px; +} +.fieldColourSlider { + -webkit-appearance: none; + width: 150px; + height: 24px; + margin: 4px 8px 24px 8px; + padding: 0; +} +.fieldColourSlider:last-child { + margin-bottom: 4px; +} +.fieldColourSlider:focus { + outline: none; +} +/* Webkit */ +.fieldColourSlider::-webkit-slider-runnable-track { + background: var(--slider-track-background); + border-radius: 8px; + height: 16px; +} +.fieldColourSlider::-webkit-slider-thumb { + -webkit-appearance: none; + background: #fff; + border-radius: 50%; + box-shadow: 0 0 0 4px rgba(0,0,0,.15); + cursor: pointer; + width: ${FieldColourHsvSliders.THUMB_RADIUS * 2}px; + height: ${FieldColourHsvSliders.THUMB_RADIUS * 2}px; + margin-top: -4px; +} +/* Firefox */ +.fieldColourSlider::-moz-range-track { + background: var(--slider-track-background); + border-radius: 8px; + height: 16px; +} +.fieldColourSlider::-moz-range-thumb { + background: #fff; + border: none; + border-radius: 50%; + box-shadow: 0 0 0 4px rgba(0,0,0,.15); + cursor: pointer; + width: ${FieldColourHsvSliders.THUMB_RADIUS * 2}px; + height: ${FieldColourHsvSliders.THUMB_RADIUS * 2}px; +} +.fieldColourSlider::-moz-focus-outer { + /* override the focus border style */ + border: 0; +} +/* IE */ +.fieldColourSlider::-ms-track { + background: var(--slider-track-background); + border-radius: 12px; + width: 100%; + height: 24px; + /* remove default tick marks */ + color: transparent; +} +.fieldColourSlider::-ms-fill-lower { + background: transparent; +} +.fieldColourSlider::-ms-fill-upper { + background: transparent; +} +.fieldColourSlider::-ms-thumb { + background: #fff; + border: none; + border-radius: 50%; + box-shadow: 0 0 0 4px rgba(0,0,0,.15); + cursor: pointer; + width: ${FieldColourHsvSliders.THUMB_RADIUS * 2}px; + height: ${FieldColourHsvSliders.THUMB_RADIUS * 2}px; +} +.blocklyKeyboardNavigation .fieldColourSlider:focus { + border: var(--blockly-selection-width) solid var(--blockly-active-node-color); + border-radius: 16px; + padding: 1px; +} +`); diff --git a/packages/plugins/field-colour-hsv-sliders/src/index.ts b/packages/plugins/field-colour-hsv-sliders/src/index.ts new file mode 100644 index 00000000000..b5899eff30c --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/src/index.ts @@ -0,0 +1,7 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './field_colour_hsv_sliders'; diff --git a/packages/plugins/field-colour-hsv-sliders/test/field_colour_hsv_sliders_test.mocha.js b/packages/plugins/field-colour-hsv-sliders/test/field_colour_hsv_sliders_test.mocha.js new file mode 100644 index 00000000000..352aa274476 --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/test/field_colour_hsv_sliders_test.mocha.js @@ -0,0 +1,142 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +const { + assertColourHsvSlidersField, + assertColourHsvSlidersFieldDefault, +} = require('./field_colour_hsv_sliders_test_helpers.mocha'); +const {testHelpers} = require('@blockly/dev-tools'); +const {FieldColourHsvSliders} = require('../src/index'); + +const { + FieldCreationTestCase, + FieldValueTestCase, + runConstructorSuiteTests, + runFromJsonSuiteTests, + runSetValueTests, +} = testHelpers; + +suite('FieldColourHsvSliders', function () { + setup(function () { + this.jsdomCleanup = require('jsdom-global')( + '
', + ); + this.field = new FieldColourHsvSliders(); + }); + + teardown(function () { + this.jsdomCleanup(); + }); + + /** + * Configuration for field tests with invalid values. + * @type {Array} + */ + const invalidValueTestCases = [ + {title: 'Undefined', value: undefined}, + {title: 'Null', value: null}, + {title: 'NaN', value: NaN}, + {title: 'Non-Parsable String', value: 'wrong'}, + {title: 'Integer', value: 1}, + {title: 'Float', value: 1.5}, + {title: 'Integer String', value: '1'}, + {title: 'Float String', value: '1.5'}, + {title: 'Infinity', value: Infinity}, + {title: 'Negative Infinity', value: -Infinity}, + {title: 'Infinity String', value: 'Infinity'}, + {title: 'Negative Infinity String', value: '-Infinity'}, + ]; + /** + * Configuration for field tests with valid values. + * @type {Array} + */ + const validValueTestCases = [ + { + title: 'Red', + value: '#ff0000', + expectedValue: '#ff0000', + expectedText: '#f00', + }, + { + title: 'Black', + value: '#000000', + expectedValue: '#000000', + expectedText: '#000', + }, + { + title: 'White', + value: '#ffffff', + expectedValue: '#ffffff', + expectedText: '#fff', + }, + { + title: 'Gray', + value: '#7f7f7f', + expectedValue: '#7f7f7f', + expectedText: '#7f7f7f', + }, + { + title: 'Shortened String', + value: 'bad', + expectedValue: '#bbaadd', + expectedText: '#bad', + }, + ]; + const addArgsAndJson = function (testCase) { + testCase.args = [testCase.value]; + testCase.json = {colour: testCase.value}; + }; + invalidValueTestCases.forEach(addArgsAndJson); + validValueTestCases.forEach(addArgsAndJson); + + /** + * Asserts that the field properties are correct based on the test case. + * @param {FieldColourHsvSliders} field The field to check. + * @param {FieldValueTestCase} testCase The test case. + */ + const validTestCaseAssertField = function (field, testCase) { + assertColourHsvSlidersField( + field, + testCase.expectedValue, + testCase.expectedText, + ); + }; + + runConstructorSuiteTests( + FieldColourHsvSliders, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertColourHsvSlidersFieldDefault, + ); + + runFromJsonSuiteTests( + FieldColourHsvSliders, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertColourHsvSlidersFieldDefault, + ); + + suite('setValue', function () { + suite('Empty -> New Value', function () { + runSetValueTests( + validValueTestCases, + invalidValueTestCases, + '#ffffff', + '#fff', + ); + }); + suite('Value -> New Value', function () { + runSetValueTests( + validValueTestCases, + invalidValueTestCases, + '#ffffff', + '#fff', + ); + }); + }); +}); diff --git a/packages/plugins/field-colour-hsv-sliders/test/field_colour_hsv_sliders_test_helpers.mocha.js b/packages/plugins/field-colour-hsv-sliders/test/field_colour_hsv_sliders_test_helpers.mocha.js new file mode 100644 index 00000000000..74e5f80df15 --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/test/field_colour_hsv_sliders_test_helpers.mocha.js @@ -0,0 +1,36 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +const {assertFieldValue} = require('@blockly/dev-tools').testHelpers; +const {FieldColourHsvSliders} = require('../src/index'); + +/** + * Assert the colour hsv sliders field options. + * @param {FieldColourHsvSliders} colourHsvSlidersField The colour hsv sliders + * field. + * @param {string} expectedValue The expected value. + * @param {string} expectedText The expected text. + */ +function assertColourHsvSlidersField( + colourHsvSlidersField, + expectedValue, + expectedText, +) { + assertFieldValue(colourHsvSlidersField, expectedValue, expectedText); +} + +/** + * Assert the slider field's value is the default value. + * @param {FieldColourHsvSliders} colourHsvSlidersField The slider field. + */ +function assertColourHsvSlidersFieldDefault(colourHsvSlidersField) { + assertColourHsvSlidersField(colourHsvSlidersField, '#ffffff', '#fff'); +} + +module.exports = { + assertColourHsvSlidersField, + assertColourHsvSlidersFieldDefault, +}; diff --git a/packages/plugins/field-colour-hsv-sliders/test/index.html b/packages/plugins/field-colour-hsv-sliders/test/index.html new file mode 100644 index 00000000000..e80b2952ae2 --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/test/index.html @@ -0,0 +1,18 @@ + + + + + Blockly Field Colour HSV Sliders Playground + + + + +
+ + + diff --git a/packages/plugins/field-colour-hsv-sliders/test/index.ts b/packages/plugins/field-colour-hsv-sliders/test/index.ts new file mode 100644 index 00000000000..0be3ab2d7bc --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/test/index.ts @@ -0,0 +1,206 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Colour HSV Sliders field test playground. + */ + +import * as Blockly from 'blockly'; +import {createPlayground} from '@blockly/dev-tools'; +import '../src/index'; + +/** + * An array of blocks that are defined only for the purposes of + * manually and visually testing the HSV slider field. + */ +const testBlockDefinitions = [ + { + type: 'test_standard_field_values', + message0: '%1', + args0: [ + { + type: 'field_colour_hsv_sliders', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_colour_hsv_sliders`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values', + message0: '%1', + args0: [ + { + type: 'field_colour_hsv_sliders', + name: 'FIELDNAME', + colour: '#ff0000', + alt: { + type: 'field_label', + text: `No field_colour_hsv_sliders`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_standard_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_colour_hsv_sliders', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_colour_hsv_sliders`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_colour_hsv_sliders', + name: 'FIELDNAME', + colour: '#ff0000', + alt: { + type: 'field_label', + text: `No field_colour_hsv_sliders`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_parent_block', + message0: 'parent %1', + args0: [ + { + type: 'input_value', + name: 'INPUT', + }, + ], + previousStatement: null, + nextStatement: null, + style: 'loop_blocks', + }, +]; + +Blockly.defineBlocksWithJsonArray(testBlockDefinitions); + +/** + * A test toolbox containing the exported blocks and a variety of + * test blocks to exercise the colour field in different contexts + * (on a shadow block, as the only field on a block, etc). + * These are in a simple toolbox, rather than a category toolbox, so that + * they are all instantiated every time the test page is opened. + */ +const jsonToolbox = { + contents: [ + { + kind: 'label', + text: 'Test blocks: default field values', + }, + { + kind: 'block', + type: 'test_standard_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_standard_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values_and_label', + }, + }, + }, + }, + { + kind: 'label', + text: 'Test blocks: custom field values', + }, + { + kind: 'block', + type: 'test_custom_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_custom_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values_and_label', + }, + }, + }, + }, + ], +}; + +/** + * Create a workspace. + * + * @param blocklyDiv The blockly container div. + * @param options The Blockly options. + * @returns The created workspace. + */ +function createWorkspace( + blocklyDiv: HTMLElement, + options: Blockly.BlocklyOptions, +): Blockly.WorkspaceSvg { + const workspace = Blockly.inject(blocklyDiv, options); + return workspace; +} + +document.addEventListener('DOMContentLoaded', function () { + const defaultOptions: Blockly.BlocklyOptions = { + toolbox: jsonToolbox, + }; + const rootElement = document.getElementById('root'); + if (rootElement) { + createPlayground(rootElement, createWorkspace, defaultOptions); + } +}); diff --git a/packages/plugins/field-colour-hsv-sliders/tsconfig.json b/packages/plugins/field-colour-hsv-sliders/tsconfig.json new file mode 100644 index 00000000000..99858c525f1 --- /dev/null +++ b/packages/plugins/field-colour-hsv-sliders/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "module": "es2015", + "moduleResolution": "bundler", + "target": "es6", + "strict": true + }, + // NOTE: `test/**/*` is automatically included in `blockly-scripts start`. + // Only src matters for production builds. + "include": ["src"] +} diff --git a/packages/plugins/field-colour/CHANGELOG.md b/packages/plugins/field-colour/CHANGELOG.md new file mode 100644 index 00000000000..6342a4a510a --- /dev/null +++ b/packages/plugins/field-colour/CHANGELOG.md @@ -0,0 +1,545 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [13.1.0](https://github.com/RaspberryPiFoundation/blockly-samples/compare/v13.0.0...v13.1.0) (2026-06-30) + +### ⚠ BREAKING CHANGES + +* Bump Blockly dependency to v13 (#2704) + +### Bug Fixes + +* Improve accessibility of grid and colour fields ([#2724](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2724)) + +### Miscellaneous Chores + +* Bump Blockly dependency to v13 ([#2704](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2704)) + + +## [6.0.12](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-colour@6.0.11...@blockly/field-colour@6.0.12) (2026-04-09) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.11](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-colour@6.0.10...@blockly/field-colour@6.0.11) (2026-01-15) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.10](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-colour@6.0.9...@blockly/field-colour@6.0.10) (2026-01-05) + + +### Bug Fixes + +* update plugin URLs to RPF ([#2665](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2665)) ([6c34039](https://github.com/RaspberryPiFoundation/blockly-samples/commit/6c3403960b8070eb8e576bd5702bdb53a09ac475)) + + + +## [6.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-colour@6.0.8...@blockly/field-colour@6.0.9) (2026-01-05) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-colour@6.0.7...@blockly/field-colour@6.0.8) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-colour@6.0.6...@blockly/field-colour@6.0.7) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-colour@6.0.5...@blockly/field-colour@6.0.6) (2025-10-30) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-colour@6.0.4...@blockly/field-colour@6.0.5) (2025-10-02) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour@6.0.3...@blockly/field-colour@6.0.4) (2025-07-17) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour@6.0.2...@blockly/field-colour@6.0.3) (2025-07-03) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour@6.0.1...@blockly/field-colour@6.0.2) (2025-06-19) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour@6.0.0...@blockly/field-colour@6.0.1) (2025-05-22) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [6.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.19...@blockly/field-colour@6.0.0) (2025-05-16) + + +### ⚠ BREAKING CHANGES + +* update all plugins to blockly v12 (#2538) + +### Features + +* update all plugins to blockly v12 ([#2538](https://github.com/google/blockly-samples/issues/2538)) ([420ba31](https://github.com/google/blockly-samples/commit/420ba3124b260b2c06e8e1d61be44e870780d95f)) + + + +## [5.0.19](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.18...@blockly/field-colour@5.0.19) (2025-05-16) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.18](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.17...@blockly/field-colour@5.0.18) (2025-05-15) + + +### Reverts + +* Revert "fix: Add `getClass()` to `FieldColour`. (#2506)" ([144b6d0](https://github.com/google/blockly-samples/commit/144b6d0c4b939e1f329986220e30ebdfcf93453c)), closes [#2506](https://github.com/google/blockly-samples/issues/2506) + + + + + +## [5.0.17](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.16...@blockly/field-colour@5.0.17) (2025-05-08) + + +### Bug Fixes + +* Add `getClass()` to `FieldColour`. ([#2506](https://github.com/google/blockly-samples/issues/2506)) ([f5a577b](https://github.com/google/blockly-samples/commit/f5a577b8fe25b6fbf1502ca85902c22777fd8270)) + + + + + +## [5.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.15...@blockly/field-colour@5.0.16) (2025-03-27) + + +### Bug Fixes + +* Don't call blur() when mouse leaves the colour picker. ([#2499](https://github.com/google/blockly-samples/issues/2499)) ([e8d4a87](https://github.com/google/blockly-samples/commit/e8d4a874c44cdb0e1ad05b08122251b9e758655c)) + + + + + +## [5.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.14...@blockly/field-colour@5.0.15) (2025-02-13) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.13...@blockly/field-colour@5.0.14) (2025-01-23) + + +### Bug Fixes + +* Fix bug that prevented keyboard navigation of color swatches. ([#2479](https://github.com/google/blockly-samples/issues/2479)) ([5945e7c](https://github.com/google/blockly-samples/commit/5945e7c0412faedc08c422f9f538ef22193cc659)) + + + + + +## [5.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.12...@blockly/field-colour@5.0.13) (2024-12-19) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.11...@blockly/field-colour@5.0.12) (2024-12-03) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.10...@blockly/field-colour@5.0.11) (2024-11-07) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.9...@blockly/field-colour@5.0.10) (2024-09-26) + + +### Bug Fixes + +* **field-*:** Remove unneeded `fieldRegistry.unregister` calls ([#2454](https://github.com/google/blockly-samples/issues/2454)) ([b3ba30e](https://github.com/google/blockly-samples/commit/b3ba30e23dddf0bd98c266659aa229ba6ba685b0)), closes [#2453](https://github.com/google/blockly-samples/issues/2453) + + + + + +## [5.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.8...@blockly/field-colour@5.0.9) (2024-08-29) + + +### Bug Fixes + +* add documentation about locales for colour blocks ([#2452](https://github.com/google/blockly-samples/issues/2452)) ([e06f13d](https://github.com/google/blockly-samples/commit/e06f13de06cb1bea9358ad46306051f4eb1bd05a)) +* predeploy plugins ([#2449](https://github.com/google/blockly-samples/issues/2449)) ([6b36d8b](https://github.com/google/blockly-samples/commit/6b36d8b344a969f79d89bbc7dcee29ae554759f9)) + + + + + +## [5.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.7...@blockly/field-colour@5.0.8) (2024-08-22) + + +### Bug Fixes + +* lerna v8 breaking our whole shebangle ([#2446](https://github.com/google/blockly-samples/issues/2446)) ([b8b4c21](https://github.com/google/blockly-samples/commit/b8b4c21d4eaf81f527336ae46f6679ff99ac23c3)) + + + + + +## [5.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.6...@blockly/field-colour@5.0.7) (2024-08-08) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.5...@blockly/field-colour@5.0.6) (2024-08-01) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.4...@blockly/field-colour@5.0.5) (2024-07-25) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.3...@blockly/field-colour@5.0.4) (2024-07-11) + + +### Reverts + +* Revert "chore(deps): Bump the npm_and_yarn group across 54 directories with 19 updates (#2403)" (#2410) ([bad8ffb](https://github.com/google/blockly-samples/commit/bad8ffbf85caa4e5b68d2f010cd0deaa9e3dd98f)), closes [#2403](https://github.com/google/blockly-samples/issues/2403) [#2410](https://github.com/google/blockly-samples/issues/2410) + + + +## [5.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.2...@blockly/field-colour@5.0.3) (2024-07-04) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.1...@blockly/field-colour@5.0.2) (2024-06-06) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [5.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour@5.0.0...@blockly/field-colour@5.0.1) (2024-05-30) + + +### Bug Fixes + +* multiple blockly instances ([#2375](https://github.com/google/blockly-samples/issues/2375)) ([b231e59](https://github.com/google/blockly-samples/commit/b231e598f2f5f5b0abbfd01d981e35572ad50a26)) + + + +## [5.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour@4.0.4...@blockly/field-colour@5.0.0) (2024-05-21) + + +### ⚠ BREAKING CHANGES + +* update all plugins to v11 of blockly + +### Features + +* update all plugins to v11 of blockly ([5fcd3e7](https://github.com/google/blockly-samples/commit/5fcd3e7d53eaadffe9bda9a378b404d34b2f8be2)) +* upgrade ts version, fix field validators ([b2d2191](https://github.com/google/blockly-samples/commit/b2d2191ff11b30347b33f95acf6e58f7ce54d004)) + + +### Bug Fixes + +* Call blocks handle both manual disabling and disabled defs ([#2334](https://github.com/google/blockly-samples/issues/2334)) ([5eade55](https://github.com/google/blockly-samples/commit/5eade55779c4022d14ad4472ff32c93c78199887)) + + + +## [4.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour@4.0.3...@blockly/field-colour@4.0.4) (2024-05-16) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [4.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour@4.0.2...@blockly/field-colour@4.0.3) (2024-05-09) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [4.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour@4.0.1...@blockly/field-colour@4.0.2) (2024-04-25) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [4.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour@4.0.0...@blockly/field-colour@4.0.1) (2024-04-11) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [4.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.13...@blockly/field-colour@4.0.0) (2024-04-04) + + +### ⚠ BREAKING CHANGES + +* The multiline text input field no longer registers itself on load. The developer must either manually register the field or install blocks, which will install the field. This is part of a move to have no side effects in field and block definitions, so that tree-shaking can remove unwanted fields and blocks. +* The angle field no longer registers itself on load. The developer must manually register the field. This is part of a move to have no side effects in field and block definitions, so that tree-shaking can remove unwanted fields and blocks. + +### Features + +* add block definitions to fields ([#2299](https://github.com/google/blockly-samples/issues/2299)) ([c197af6](https://github.com/google/blockly-samples/commit/c197af6c67bc989bbdb4e83a62abc08167bcbbfe)), closes [#2162](https://github.com/google/blockly-samples/issues/2162) [#2202](https://github.com/google/blockly-samples/issues/2202) [#2221](https://github.com/google/blockly-samples/issues/2221) [#2211](https://github.com/google/blockly-samples/issues/2211) [#2220](https://github.com/google/blockly-samples/issues/2220) [#2232](https://github.com/google/blockly-samples/issues/2232) [#2233](https://github.com/google/blockly-samples/issues/2233) [#2296](https://github.com/google/blockly-samples/issues/2296) + + + +## [3.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.12...@blockly/field-colour@3.0.13) (2024-03-28) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.11...@blockly/field-colour@3.0.12) (2024-02-08) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.10...@blockly/field-colour@3.0.11) (2023-12-14) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.9...@blockly/field-colour@3.0.10) (2023-12-07) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.8...@blockly/field-colour@3.0.9) (2023-11-09) + + +### Bug Fixes + +* zelos full block colour field rendering incorrectly ([#1932](https://github.com/google/blockly-samples/issues/1932)) ([49fe34f](https://github.com/google/blockly-samples/commit/49fe34fa887af1c6295fbcd294839d20fb86b0c8)) + + + +## [3.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.7...@blockly/field-colour@3.0.8) (2023-11-02) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.6...@blockly/field-colour@3.0.7) (2023-10-30) + + +### Bug Fixes + +* Field Colour Plugin types ([#1964](https://github.com/google/blockly-samples/issues/1964)) ([8aee09e](https://github.com/google/blockly-samples/commit/8aee09e9375cbcff65cc7b39530daaa3bdf84182)) +* **packaging:** Don't advertise non-existent ESM entrypoints ([#2022](https://github.com/google/blockly-samples/issues/2022)) ([d019198](https://github.com/google/blockly-samples/commit/d0191984399b784e2928b8fb4c58257bfa857655)), closes [#1877](https://github.com/google/blockly-samples/issues/1877) +* update tsconfigs to publish types accurately ([df4b084](https://github.com/google/blockly-samples/commit/df4b0844af712f5025a2ec842458b828f3147676)) +* use includes rather than excludes ([40efc25](https://github.com/google/blockly-samples/commit/40efc255329e3ca476ccc247b95a2d05dd77b45e)) + + + +## [3.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.5...@blockly/field-colour@3.0.6) (2023-09-21) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.4...@blockly/field-colour@3.0.5) (2023-09-14) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.3...@blockly/field-colour@3.0.4) (2023-08-31) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.2...@blockly/field-colour@3.0.3) (2023-08-17) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.1...@blockly/field-colour@3.0.2) (2023-07-24) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour@3.0.0...@blockly/field-colour@3.0.1) (2023-07-20) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [3.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour@2.0.1...@blockly/field-colour@3.0.0) (2023-07-13) + + +### ⚠ BREAKING CHANGES + +* updated `plugins/field-date` to TypeScript (#1705) + +### Features + +* updated `plugins/field-date` to TypeScript ([#1705](https://github.com/google/blockly-samples/issues/1705)) ([e5531ff](https://github.com/google/blockly-samples/commit/e5531fffe188ee361a16fe48ed126b34e51a8d30)) + + + +## [2.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour@2.0.0...@blockly/field-colour@2.0.1) (2023-07-06) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [2.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour@1.2.1...@blockly/field-colour@2.0.0) (2023-06-28) + + +### ⚠ BREAKING CHANGES + +* update all plugins to use blockly-v10.0.0 (#1744) + +### Features + +* update all plugins to use blockly-v10.0.0 ([#1744](https://github.com/google/blockly-samples/issues/1744)) ([6cc88cb](https://github.com/google/blockly-samples/commit/6cc88cbef39d4ad664a668d3d46eb29ba7292f9c)) + + + +## [1.2.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour@1.2.0...@blockly/field-colour@1.2.1) (2023-06-22) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## [1.2.0](https://github.com/google/blockly-samples/compare/@blockly/field-colour@1.1.1...@blockly/field-colour@1.2.0) (2023-05-25) + + +### Features + +* Revise fields ([#1700](https://github.com/google/blockly-samples/issues/1700)) ([c326f7b](https://github.com/google/blockly-samples/commit/c326f7b1ef54fe45387046c693ebd5afe2b2c571)) + + + +## [1.1.1](https://github.com/google/blockly-samples/compare/@blockly/field-colour@1.1.0...@blockly/field-colour@1.1.1) (2023-05-18) + +**Note:** Version bump only for package @blockly/field-colour + + + + + +## 1.1.0 (2023-05-11) + + +### Features + +* Add angle, colour, and multiline fields. (Rebase of [#1674](https://github.com/google/blockly-samples/issues/1674)) ([#1693](https://github.com/google/blockly-samples/issues/1693)) ([1b447f4](https://github.com/google/blockly-samples/commit/1b447f41d6293f14e846b8a4d82d289a5637a99a)) diff --git a/packages/plugins/field-colour/README.md b/packages/plugins/field-colour/README.md new file mode 100644 index 00000000000..1fb1f42fbbc --- /dev/null +++ b/packages/plugins/field-colour/README.md @@ -0,0 +1,308 @@ +# @blockly/field-colour [![Built on Blockly](https://tinyurl.com/built-on-blockly)](https://github.com/google/blockly) + +A [Blockly](https://www.npmjs.com/package/blockly) field and blocks for +choosing and combining colours. + +## Installation + +### Yarn + +``` +yarn add @blockly/field-colour +``` + +### npm + +``` +npm install @blockly/field-colour --save +``` + +## Usage + +If you want to use this field in a block definition, you must install it by +calling `registerFieldColour` before instantiating your blocks. If another +field is registered under the same name (`field_colour`), this field will +overwrite it. + +If you [install the blocks](#blocks) in this package, the field will +automatically be installed. + +### Field + +The colour field stores a string as its `value`, and a string as its `text`. Its +`value` is a string with the format `#rrggbb`, while its `text` may be a string +with the format `#rgb` if possible. + +#### Colour field + +![](https://github.com/RaspberryPiFoundation/blockly-samples/raw/master/plugins/field-colour/readme-media/on_block.png) + +#### Colour field with editor open + +![](https://github.com/RaspberryPiFoundation/blockly-samples/raw/master/plugins/field-colour/readme-media/with_editor.png) + +#### Colour field on collapsed block + +![](https://github.com/RaspberryPiFoundation/blockly-samples/raw/master/plugins/field-colour/readme-media/collapsed.png) + +### Creation + +#### JavaScript + +```js +import * as Blockly from 'blockly'; +import {registerFieldColour} from '@blockly/field-colour'; + +registerFieldColour(); +Blockly.Blocks['test_field_colour'] = { + init: function () { + this.appendDummyInput() + .appendField('colour: ') + .appendField( + new FieldColour('#ff4040', null, { + colourOptions: [ + '#ff4040', + '#ff8080', + '#ffc0c0', + '#4040ff', + '#8080ff', + '#c0c0ff', + ], + colourTitles: [ + 'dark pink', + 'pink', + 'light pink', + 'dark blue', + 'blue', + 'light blue', + ], + columns: 3, + }), + 'FIELDNAME', + ); + }, +}; +``` + +#### JSON + +```js +import * as Blockly from 'blockly'; +import {registerFieldColour} from '@blockly/field-colour'; + +registerFieldColour(); +Blockly.defineBlocksWithJsonArray([ + { + type: 'test_field_colour', + message0: 'colour: %1', + args0: [ + { + type: 'field_colour', + name: 'FIELDNAME', + colour: '#ff4040', + colourOptions: [ + '#ff4040', + '#ff8080', + '#ffc0c0', + '#4040ff', + '#8080ff', + '#c0c0ff', + ], + colourTitles: [ + 'dark pink', + 'pink', + 'light pink', + 'dark blue', + 'blue', + 'light blue', + ], + columns: 3, + }, + ], + }, +]); +``` + +The colour constructor takes in the following: + +- an optional `value` +- an optional [validator](#creating-a-colour-validator) +- an optional map of options, including: + - `colourOptions` + - `colourTitles` + - `columns` + +The `value` should be a string in the format `#rrggbb`. If no `value` +is given or the given `value` is invalid, the first entry in the +default colours array will be used. + +The following options can also be set in JSON: + +- `colourOptions` +- `colourTitles` +- `columns` + +Or they can be set using [JavaScript hooks](#editor-options). + +## Customization + +### Editor options + +The `setColours` +function can be used to set the colour options of a colour field. It takes in an +array of colour strings, which must be defined in `#rrggbb` format, and an +optional array of tooltips. If the tooltip array is not provided, the default +tooltip array will be used. + +Tooltips and colours are matched based on array index, not based on value. If +the colours array is longer than the tooltip array, the tooltips for the extra +colours will be their `#rrggbb` value. + +The setColumns function sets the number of columns in the colour picker. + +#### JSON + +```js +{ + "type": "example_colour", + "message0": "colour: %1", + "args0": [ + { + "type": "field_colour", + "name": "COLOUR", + "colour": "#ff4040" + } + ], + "extensions": ["set_colours_extension"] +} +``` + +```js +Blockly.Extensions.register('set_colours_extension', function () { + var field = this.getField('COLOUR'); + field.setColours( + ['#ff4040', '#ff8080', '#ffc0c0', '#4040ff', '#8080ff', '#c0c0ff'], + ['dark pink', 'pink', 'light pink', 'dark blue', 'blue', 'light blue'], + ); + field.setColumns(3); +}); +``` + +This is done using a JSON +[extension](https://developers.google.com/blockly/guides/create-custom-blocks/define/extensions). + +#### JavaScript + +```js +Blockly.Blocks['example_colour'] = { + init: function () { + var field = new Blockly.FieldColour('#ff4040'); + field.setColours( + ['#ff4040', '#ff8080', '#ffc0c0', '#4040ff', '#8080ff', '#c0c0ff'], + ['dark pink', 'pink', 'light pink', 'dark blue', 'blue', 'light blue'], + ); + field.setColumns(3); + this.appendDummyInput().appendField('colour:').appendField(field, 'COLOUR'); + }, +}; +``` + +![Customized colour field editor](https://github.com/RaspberryPiFoundation/blockly-samples/raw/master/plugins/field-colour/readme-media/customized.png) + +#### Creating a colour validator + +Note: For information on validators in general see [Validators](https://developers.google.com/blockly/guides/create-custom-blocks/fields/validators). + +A colour field's value is a `#rrggbb` format string, so any validators must +accept a `#rrggbb` format string, and return a `#rrggbb` format string, `null`, +or `undefined`. + +Here is an example of a validator that changes the colour of the block to match +the colour of the field. + +``` +function(newValue) { + this.getSourceBlock().setColour(newValue); + return newValue; +} +``` + +#### Block changing colour based on its colour field + +![](https://github.com/RaspberryPiFoundation/blockly-samples/raw/master/plugins/field-colour/readme-media/validator.gif) + +### Blocks + +This package also provides four blocks related to the colour field. Each block +has generators in JavaScript, Python, PHP, Lua, and Dart. + +- "colour_blend" takes in two colours and a ratio and outputs a single colour. +- "colour_picker" is a simple block with just the colour field and an output. +- "colour_random" generates a random colour. +- "colour_rgb" generates a colour based on red, green, and blue values. + +You can install all four blocks by calling `installAllBlocks`. This will +install the blocks and all of their dependencies, including the colour field. +When calling `installAllBlocks`—or any of the individual `installSomeBlock` +functions—you can supply one or more `CodeGenerator` instances (e.g. +`javascriptGenerator`), and the install function will also install the correct +generator function for each block for the corresponding language(s). + +```js +import {javascriptGenerator} from 'blockly/javascript'; +import {dartGenerator} from 'blockly/dart'; +import {phpGenerator} from 'blockly/php'; +import {pythonGenerator} from 'blockly/python'; +import {luaGenerator} from 'blockly/lua'; +import {installAllBlocks as installColourBlocks} from '@blockly/field-colour'; + +// Installs all four blocks, the colour field, and all language generators. +installColourBlocks({ + javascript: javascriptGenerator, + dart: dartGenerator, + lua: luaGenerator, + python: pythonGenerator, + php: phpGenerator, +}); +``` + +If you only want to install a single block, you can call that block's +`installBlock` function. The `generators` parameter is the same. + +```js +import {javascriptGenerator} from 'blockly/javascript'; +import {colourBlend} from '@blockly/field-colour'; + +// Installs the colour_blend block, the colour field, +// and the generator for colour_blend in JavaScript. +colourBlend.installBlock({ + javascript: javascriptGenerator, +}); +``` + +#### Message files and locales + +The blocks in this package contain text that can be localized into multiple +languages. As of August 2024, the relevant messages are included in the core +Blockly language files. For information on Blockly's approach to localization, +see [Localization](https://developers.google.com/blockly/guides/configure/web/translations) +in the developer's guide. + +If your blocks show `%{BKY_COLOUR_BLEND_TITLE}` or similar text instead +of the expected text, make sure that you either: + +- Import the default Blockly modules, which includes the English langfiles, or +- Explicitly import a language and call `setLocale` before using these blocks. + +For more information, see [Load a Blockly localization +table](https://developers.google.com/blockly/guides/configure/web/translations#load-blockly). + +### API Reference + +- `setColours`: Sets the colour options, and optionally the titles for the + options. The colours should be an array of `#rrggbb` strings. +- `setColumns`: Sets the number of columns the dropdown should have. + +## License + +Apache 2.0 diff --git a/packages/plugins/field-colour/package.json b/packages/plugins/field-colour/package.json new file mode 100644 index 00000000000..d0fa4a946b8 --- /dev/null +++ b/packages/plugins/field-colour/package.json @@ -0,0 +1,61 @@ +{ + "name": "@blockly/field-colour", + "version": "13.1.0", + "description": "A Blockly colour field.", + "scripts": { + "audit:fix": "blockly-scripts auditFix", + "build": "blockly-scripts build", + "clean": "blockly-scripts clean", + "lint": "eslint .", + "predeploy": "blockly-scripts predeploy", + "prepublishOnly": "npm run clean && npm run build", + "start": "blockly-scripts start", + "test": "blockly-scripts test" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "unpkg": "./dist/index.js", + "author": "Blockly Team", + "keywords": [ + "blockly", + "field", + "colour" + ], + "homepage": "https://github.com/RaspberryPiFoundation/blockly-samples/tree/master/plugins/field-colour#readme", + "bugs": { + "url": "https://github.com/RaspberryPiFoundation/blockly-samples/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/RaspberryPiFoundation/blockly-samples.git", + "directory": "plugins/field-colour" + }, + "license": "Apache-2.0", + "directories": { + "dist": "dist", + "src": "src" + }, + "files": [ + "dist", + "src" + ], + "devDependencies": { + "@blockly/dev-scripts": "^13.1.0", + "@blockly/dev-tools": "^13.1.0", + "@typescript-eslint/parser": "^8.61.1", + "chai": "^6.2.2", + "jsdom": "^29.1.1", + "jsdom-global": "^3.0.2", + "sinon": "^22.0.0", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "blockly": "^13.2.0" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@blockly/field-grid-dropdown": "^13.1.0" + } +} diff --git a/packages/plugins/field-colour/readme-media/collapsed.png b/packages/plugins/field-colour/readme-media/collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..3787dc73aecf791d46013185cdab1272ad6ceab6 GIT binary patch literal 2140 zcmV-i2&4CjP)SvOy`k5rFekRGPpGmUnXOgV? znIx-zCdsOwNwVr^lH8On!YqOd2rmmPh zsz%Ax=Qu9nzbU%=D}>hs#g%1OlBmyd+~b8yc}sufn}LvcfiC)H<-p3S{MpC$eDIU{ zYXBZB`BW7t+a^)}@#C98zF@BMeq>NtQqONTCAKAlc*`vTvW@O=sDI1aQxd1@~f33H!y^MCEv z8q*H#g~fRBgOA4E2!zDZHZz8~PPk3*T}f(7B>HA?<{bRv8;>RJf4Pu;lT&H)zF3hzwK z_}SVqxpaNA>!qVmn?Sw3n@Pgl_m2a(`%on~p#LZ80*UvL3qVtml=U$HpjVfX%!t`x zG!ON+H5)27M$8VQ+2L?Y8+-Ll0DyK&T@^EHG&_vuiD^n>>(B}g;GbXBpTBheuh&m* zJ{{L4ar*+F#5WQYB@dNVBU4XfV zhoLr=S2XQrlBW8)o>rCjs>3t@K-Xif>uGa!4(NKU8ijkp*yrLqZ5?=O0@|AG4btgB zN4FDbYqqzxbb+6MT_9Cu^Ako3?_qSSG;!0fwi+7w&SHbjJz*SF+gfE~=AnM*daU5@ z3;+P*WhG@twRj{Wv9S~zdSRVyP}b@(oFJH;c(dwe^U?pr>puJNFrhY;|7cg;dD2q9 zsiRcQK5z{1tx=PeXWoK@_AD1pu^{Q=YjY04R}> z%(Af3krF2>zka9gdc5xLr84p1_h0?-o^yVt<&x=-ELNRPayZ8{??_O!lT8Sb0+kgCQuzNPKzHP|||eCONI zrJJs&fnwdW{hLOv?i&nQS>2@v0JJqX76+%VjPe4h%XPd}V`VB+qR&@E%QvMU|&yH2=M8(waf7XBQ0G(#^SnKT8I_$XO92hd9+1_ffw<2!E z>FWMeZEMxotr~zCxyq<(cNlMTB^uv(REhbiVZ=|vHU@?qMJqx*^;3w0(oFeCC~F*A6Qm zZFVW&#G&^gOMP=003fnhEW5rL^ne)!FuQ!n<1G?4z>CBJUG%T%+C@fjK<-zPYXGn@ zhviQY_%FyxdBVyQb^JAMy%*oH=u^^czJS4ANVJFM0X$fLMfFC? zL3?BynD)?8oNz~8F%Tp*6VzF2zlciI63JVK4jw1*{aN+|XNirM` zZ)|MH<#NH5Mfb6d)GD%kbOp;YUlVsJ;B>w}s!g5Nh S@;YSz0000$3 literal 0 HcmV?d00001 diff --git a/packages/plugins/field-colour/readme-media/customized.png b/packages/plugins/field-colour/readme-media/customized.png new file mode 100644 index 0000000000000000000000000000000000000000..161860b9d18df83b07b66e670f9cc71281cd5dd9 GIT binary patch literal 2337 zcmZ{mdowUw^!%0q7T^0ZU zIjFP4xt)yJQIyQCohU$LTmt}^A5aHdSZWo&%H8e2gi1Cm)sjTvPO8ch%V5{AG9!0X zOX|+T>>S~N>do*9pBC4)7>6cN7YO&v+0?o83FIhh*hk9op|W@VOhXL3FS=4^Ild{gXyjuoDxlCGvb?H6iryb@{m@&_qY40^geng`_t64x88%&Vb$cy- zd@a88eY<@5BSuX=8$YV%7`m41^h@A+V87b4MtwN#$lk%q(=-;iG)5C6T~WE->zi=8 z(s<;u;Srl(!z0859K(rmz{yi-D_`45^w_i&)`(*_(SZdmc z#64Ay5~c$XP0g@iH+=gx|1S(L1GiUXgL#Acb-0*NH*!{^!!^f`wt{;|VOs{;@-5+zy)E5iKo3@(+iAZc!2dr@BkxJBAnWB&nk zikWujZgO0*HH&l@HnI8Si;rzIt_krv(ivNr=N2E8z1vm<3_ldfZO`KVVVnGmf3e{B;2Y4q}9lT z1UbDx!S$Hu=#rj%Z20!xUJoP@F~VyH$$*)k`ImJn-EW6;8Y#NjhWLVbx!B-3bReLbFOC24j?nHT#ESm!{hl>3|LVU_n zT0I-iEu~(;V1kt$YgqT87!&md{eFS)7*?@};p^iA^Y0p8wtUgk1BMcGq1H7D-d2|; z33bi{UFH)o6I-{vtI@om>u$!apc4e$7?)@iYK(_+A(&WL1crvzxSri?RY+5U5{C2? zU;;2Roffk5Uy`bd(_YUmHye%2j%Fo9Je#o6Vu?guqr40L`;Nz=P%B@k$#eN&^_$k2 z8=X&|?gNk&m%BSo?d>UIP7|X1`TOl_5GL;g-2GSuV5eV61OAw-WJ~B3Ui{mQ7Z7wyxppUZTPL##;^6_*`vG% zWOC-pk6{iXoSTT_*IzK?G+76mQBXr@sW}C=at<@Ra8`_r>EB+bwRDuvlgdpgM+L4yp3rRIUvil zzv?O8ee~HYRfr4zh9Dj=bPkFC>J*U(iMh9%jq8Zu9?mWPohnGq_* z$Hy;P8p-5S zBdJUJt*05@Uf>Ej^~Si6!R46Y!&PX*{CPpDe&MDj5Yhvo^?JceH{&ICH61K= zKCP>4C#R-rj5>9Fr{&+2{lEC#DcHXnqhA^~MEy&9e7c7W06|wq^|G4Wa^dHk+RNgq zwtTL!nX^`1Gfgrj%eSFbSW~O-A{2y0Q2Z=OG}rFQtonr8DmeN^rcFefJRcbJn0cdQQpb@Xr`+lA%)69hSr+5i9m literal 0 HcmV?d00001 diff --git a/packages/plugins/field-colour/readme-media/on_block.png b/packages/plugins/field-colour/readme-media/on_block.png new file mode 100644 index 0000000000000000000000000000000000000000..ee3a66d96e6fd1110a26cb7f635cb894d40eb20c GIT binary patch literal 1464 zcmV;p1xNacP)*q{rjr<=lh#$;ksWeiJ8+md z!k!&?u&YMllsMim9McOa+Z%Drgi_L8F)o z8imyE#u#IaEf&kosimubO*1UN6E=ZZQ7~4%=xWh8N0wzpQ3!%S8vA05aW(h9n6G570f`r`G9}Se7jm3SO^QYYd8_m`bI7xIDS^Fn?#3 z!FUG@zemV2*67V-G91UrvWyVYQqW?tT={eEAuCSbdAQjTZf_`xq9_U>WVKqg6tr5c z_vW+r=h-cb%m{nZi@t2hPg()AP+846+$C96R8#vOYttSu9ByAbfJ24}9M{v|<7y{oyzOKx@~@_8O-D^P$0(me?_v;i?W- z11wDSr)pN(*ErjqS=#W;YT+p4yjg~+@Rn*=B%{_O7ztIeBmHON01%8s8zY^`a6A}^ z20RnjJNlApW2_6F5V5l@(PSSV=tS*Sb=GyHftJZHZvH(dE>-Ud_2b{4`d!h*d02Zb? z`jP+;J_nI#V-q$R7$g*Q;)=MQaux4D2Dv za{sBBdg+xsWjF5UW_9gj`AR}dpd+u>RM6Dk>eA{KTTFqV;%}Zd=tM#2!ja;`?U@wW zle8Rt_0lh6b{m-&%C|!Ed+pIB=dR|7D^;n5(yh_-?gPKpzkAQOU*FY>DELOh{@XK) zlhe!kyl)xf`|8K{)xVHS7hmR1ooEaluPcbTEr@12vZ2bt`^OqzJM91F+uv^8Ub0xo zo$#iKAUB$mYGpG|Gjn%wEIGTp%;BBz(i7n0kKQSIP7wtKLCEEDHk-}ia2N@NWm$w! zWo4xiCp^!KqDa%U)vABfBFl0*ohAsv<9KI0BD+)WjUYEyId~avzQN|xo6IqCz+GUndHp5uHU7o{8HlSCk$i?atwih#6qKc zdU`G{E~ciYE-x>^sVNYH0RZ6q{CvHxZn3}r|e7=C*-QArmE(S+O=jP_t*4CDmmcYG>>FH^3X9rx^TUl8F31|?#F*`d89)sZV z26(ZD#bSvwaCsRdK0H4MFFfoN8t80I5Y$v9|OcS_x8Z` z^~;_fF|p^(b&c{08WolG+Y1Xfh##@K3hr%e&CPAk&w~R4;L;M?bsjm%7pRhQaAG3g zKWM(Hik0;a3kz>w-w+J;3?zP|g$?lJ1SIam{yrP)os-k^V+;oD>q8Zlt_%;iwst?4 zQQGY4x|o;%aX1hFR*=Y@o&8V7-z8uY;Q08`(h70Q!Tx@bxV@*RELU%>j*LM5|N4Jp z2X2G38ZWfo%FAlL5QT|cAtA1L`;?4~nm7vmxBh>B69^RmA)(?@Dot(iq`D@kk19=T z^JTiDlBiUc-Vw+qW{yRbWpst|$OmvKmuGfI^1sd3?=H{kjS>0Wlc-#gjY*LBxsL6w z$mvfJqU3|a%ita)QpFGfk?M`A9z?_bI%lc&5%_e|@I6rsU&|`%Da_ZfPXbzEl!MCn8419#m|%)P%xFe!+vnkMN8F4F9NRZC^3_IQq zqk8DYkN4yA$@PVY#`^GRiphS0_-1w+kx$WfMYt|KWPqueUN_EKF+STL z;%3iz1c5MWsyzJ?ogb~79rwV59iB?UM!g*C%V7Q$Vw&&hnVQ$FeBbRio?0*JPpd+f zd2syLTFi5vPbOExIM(iGSkH=Nn@8B{m?Z;o<~{e#U&g zWZ%kBpqh~V+agA>I1frwKQ_M+msiZOmIyF^A~P#7{6kRfPCpW;GWR$W%FBv7v#4q{ z*1O3p|9#pL0(VV#gUC$^fTekUHCtu$Bjti$HMCmGt;(fWy~zw|m;BLilwA3O`900z zQmQ`7`rTOLf4}cSzFk<0AZ=>vO5yT|*M&9A6$d`tN}c+;ED+6$Ex09DnNOK;j$KEl z==cd4M8iZijOp(Vc3)Xzg3B&g4K8cP%%f#_II2_eTC-m0LDJRR@1=q>N#4khc**L^ z6CNwO_yzM_du4LF6>B$cd3=jvm$?L(vIu>IVzr_y=4hk1ElS1}dlVeOXTz1=ybz`c zJ6(*@wmMykg)&jg1@m9^!9&^QD0pb2tj^YQbBfQ_3v0Iiuv07FrdnlmgtT}tNkd4; zZ^%NpU(my?(-g?zVXc#-0=L7M&|)cJSD3K(1}T`VHD8l6shtdvF{#amQQkDs^l)X6 zW|E~~m7-!M6E}gkD$pasTIeEPXt|NI2k|KGsRog_vAxytvlZnb!9F&*B8-Al72Xf0 zDdoAs9g89p2_va`q#%~~kVl@)nuOG@ndF)P2?-UCyT5hnmCoJdOJ2Ae9hwB9qSMNp z4}k~_Xx^^K3qH6NkuY=ck{4*A?z0Z~ndpOkKzbOcWXv0u;!npz$~?d$&wq`C!L;P| zGF0}Ou>%=I;Wm>Yosk^PjyqN13tlQyI2jLI#+MFG$~dDGLPd{Y;22)Jva=`*Jl*>b*%=Q3$bL!h%@_T9aPXL~WtblD=l%4p+TVI<*L znk$P+X*gv*5<03(Ii_YeK8;Y4s6@OdbPw|Fr3qwE=pz{n?rw6+1R{WNBafYAqCm`( z&Pw%C!cuu2y_qLf0uoUaeAXnCvBNy%JI&-OrSASxkSna3xF^ci?$ZX=VoBjtl+sjk z{(fOz_Y^)+T|WyWEgf*V)}3a_HzYrMLLt!_PGu96(ddQ!Nv3WRM*dPW#a|-z%AMTR zZ@MC+9_eMIcX;k!=!=kERoYMFW^197IAdZkZO-J2Zl>ZhQuo$uCYjV%BIRQ0_mzO> zT>S-?ZNNijN%~0?U!dN{(J(WdMDj9P!`}rfkJnBe7CT@H43B%p8_y0)+~^C9C{-s~ zIgU#G)C-MSdnY=d9hHgX!5#TqA+#a`PgruP$c386RCMR`16qkb5LeTepfP{3B#@7#r4;dEo+6<{Nw!%H-W0HIiOGNrgXCA+F?=XEywP%I?ajAKAk!<_~7foN}bp zr4_fYg*7t{@{>^Kj`1*rxmnEcyHmX(BfZL2pl~;gG=Qg>^eQS}PJ)M|Lhh)Qt*_MQ z2^z=XbkfGdQ0AwohQFF{($23@7NCvBvm6JL(1%&eQ4B!HRWnJh{638fX4rJs1sdxgiHBA2F#$+WqZX%bHT2<~zB4jD;LS*pUFRQFyu zJ1K{$I}px3iU8~A^VW}Sq3hPUrQFcwWFV>ho z1bn2EIBmM(zZW%LA4Z|W-pe>xP9dXd>go33iZMVJhq+LHR4`flBiS%<1sz7MX@pwI zgk((ytqHwN9j26pkh@{~wBMRQ%8Qk*m?y32)tyYBG^)#EFods?=aT~=)s;Ewo924w zQxkpF)vrE$)aT@D@(WS=3%xqeCAJt$kxb=bsdk$}o2)hL9+kJh3lrVT1F@OMMe;|9 zulI^Y25zgD)KJ>4M$){_Qh4-4)(EQSUprrobU|4;an8+A#5f4^Z`p81V3!#5c{r`D zl7ZX22J5=4bu=GOOcp**1~|Ln1(&|RmeFG#bWNPkDxxx9kY*aocAkba)(-e;9Hetz zt}4B%9g68Y$d1SBQ?Pnwd#gJ2ip)D>@@G6FoS###@VPyfrPy(25E}L3ScM~c@4GW-#`R5i zJb58cHR}qV&hfk`VNmXw?Rc`-5SY9zhqfny${|VD>z_if)UUeG70D{~-(+&S`fH|M zr8A1y53iD`G$2ujiHQ* z^$t1n-r-LOLghqzA`{2Tyesy+ms0aYjFev7o&yd)_Jz}2w_(7+nfPR{ zUpJ@yBuP5qQ8z5fNy*$I=JMG@YLP|yx~#O^3DZP{D|Xm@`Pkct&Bs{6$3(~HtAmee ztdBXu=lg_@YX^xHD>J7iiJ>2pfF{X}ha@+wJsq@2bXSW-r9Z0ZCk^1 z^!u;Fm_JR+56;yemb>opIflq#M?SVc{IQU$sVq_}ENnYYwh~v^m|VQcNZ95{gb;t& zj6P~8u=%*Z3}|5sAz^pcVT$_1UfOx%1qsXhoIivZ2!;ys@pcGgKG={XB*Z=B3(jYU zEp$&J^gt){$RYG3Hq`4bvua2>ODHoZuc&Zn=Tz58$E#u=)sG)B%tGf~(fxnhkj3 z1};7TPridoaUePy-1-gf{qgo-N{HqK!3aD?8wAfmFa+G|+1&gEf~O#u1cGBA_z#@? z>g|0E1lvweJwe=W5S#-5A8!w8JcdM85>lU%~xOa7+LM3qf!dJY50NwjdY_&in$gw&0Ef2u9&Y48Vh7 z5Ss%YO@T83;GBCx0`dKm|NcQwPTWpTVnMJ0kDmq6P9QqN+k+NFmw?zna6ApfIe@bs z;6CxE?g@gIAljEGH9_p1OI#2*@#%8vCpZEH!88!;2K!||umdF6d3#+uzwidZTJY2z z>~B0gbOFI~aPpp)*Hr*OZg1OP3~C=AyMtg)LiEl3eP;ltkPuA|f?MFOBM26Q$A7?s zTyP>8M91Ps4MDUs2v&oK8Q@q3IAs9LDTAxF;ARatrU!16fdlJpMa~HUKxuK(v2X-YalI-plKnnYlDkFyk?bAX*yS z$L#KgUyghNC+>q&jUYM>1T$X0W(McE!DFZY#LoZ6>i;6^bgW;+$rQldc-j`!7=0*Q zu21|gSvP6Z#;witCSQLoXTT?fid|UrTI&Cjbt{;cMO0KbRiaii9zK@mB3q~+$~qbQ z>q#m%-a)d+f8ec6QVYgQjM@Tjs#X`e%To>I8}$Abc7KpV6Ht~%lQC0gPftBTly!5B zG+w;Cxu|=yZGMc|DkG_73tb`XC-iDOExzub%zvw(*&nhpUXcsDrT(XvtTF1EJ_^#T zis4H7JU3dFTD>`qnDn|;{^Zb&86rMH;H^86Q~4XY|KeJ01Z0jwR3TTO1@X(%GRlHA z^$R3(MeZ8lCH_zvez>tpqCZ*e+zO$Yhx!`61)TqEyq~yl&p3AsQ9hipH()ut?D*)6 zr-v)Jk#hLHaCg1AItQT>=hvfVdhga^c-7~=t~bl?Yy}^d?-OaxSM(vqe*SmEage*o zv8j}aBiE?{%SiTkgFNbejZD3GUJvs~{PyV%xnhz|&HbFAnG6y`R;nDH^eOivd4}-o z_RxQJ5I3gBm+&vfiXukKai+=a)Mj_j#QotfCmwag`hJ0snK>}RNW<@Ab8uR3E|h`;1ZM>v@ct;ooll z$qeg~{X~w1j22d&jgY>RPYYXtIF!y)%RNAplHROhu@K@yTurq|{-Kc8nzrU*-C}xw zV7@&$Eu2aBS{i)Xi1Lo9NHevSwFo8utYU^#uWEZpJ?c~+HG2{6}I!p%aM@$ zk29AFLJkq)C7!t+ka~#kL-fP{o9=xGEfT&TA}A0cq@Q6Bh78N8&8euZsID!quPbY5 zC~vK;XsM}cs;+CUYHF@-X{>E+X=!b0Cw5y?M{8qu69Q9PJy=scT3t0&RXtZ(gRiV# zscc@aY+0%5nyJQ&Rre3o4D=xeQT0Qu4Z|%B!wtxh+N!CBs=4N>`KD@oWA$Qv%~D;> zMpflzWyNMi#a4LUf)`{Mh ziO%Nf=0Uw&50A7Z&SR*AN6k_11RH&MtEIplSE4W%r`>;IjP$=r{vV zmjL<_=syR>jsffefI9&2JHW~juyqV2m;1-3het>MHL>=NP6=lhD;JlmmjK}s zI01mu3*hV&IN1k|cYtF8aIyp(;ek{9Ya_es<44OA=L=JpxKUt!1ehHFrh0*aZUEhV*4w?0?pa26&tkeKFsRY~uHnJX zp`os!p`O9PzQO;88W`;F?d$8spnHgZZ!elSK@XwPqiDR~A?>t=XIo-fq zZ1n@%Xke!c*lhv!8vx?%@u&(otp+Y?0iXo{Isl-X*u*oJ_Wxw1=|CcgRvP>Nw9@F- z%!pRn9qFL|tTg{p-~X(%WWN8bG%vx=oz6rn4aM`Hm4+PVV@iN~3Tnw!<_=~k`kq|x zsWglH&q}k57`dOBtz#jF?HR>o-hUB&1i_0GeE1jj-YiCdmo4;?-&XW_A?WgoQv-on-evT}9 z?FrlcM8xB64>}mi-;Lo#1cWh%ou7?KmMRVB!(~pDCZD8s66yVWcjtcW*mMo(v-X}ZDGuqV5UHcph0v_1y@fD^KW|j-KR;qsmNnQkP2ug^?c@v(2zx_w zU+Zh0()}#Ih%CPO(pG9-lYeH*abI2cdZ6j4Yxm+4ukWY`1SUHA@ljeXfsjz8^P;~V&WFBmVqfS*x2HMG#qgPAw8l$R#?y0nR@K*yz{I_b4{ z-2wMib@}}71BIFaOnk}Bq<3G2+YKofu*jBw$zoF;Qch%(EmyjqYRE?@qH>Yz`4;;U zx>-5$-eR+A;%lJ54+f)kNIp4V>Ly}QMRnkQ3KJiumQcO1Rkza#-L5}qrL`hGTrT)g zw~{S6P`gSYw9`z8(Az-}TGMx0>BKI!$f<<}n(N24zVCLhRs^$fm_uBUaa&xSm6?jotdQPmrqyU1_Ji;R38|gEGt@Q2(Oe`%uCFLUrBnro@d`G z+1vPv)|t?jVsJ4ks~YKyE2sdJ_lIQW91q^SdhU2Q4o#8Xz$$J?FU%_nyRI#MY?2x( zzC-7XZ!?*z)9|!wtzDJ)or8@q&0IKFVxvWvLzTCd9kbeN&R>C|9MBK&LVMLcv&0c6XoTh1(jT)4G@%mJqrcn)Y%QtX*a%6fqDTgFQ71@%V6t3!B}H= zgG?3f)ZJ_D6t5i&cD+Mhl3CV%ubkX=LbLVyF;$1KAv2}hVmf!7sV667FYvP(A6sv1 z=gWAT;2$P;$Yxh-bcE91*;OvwEOU2fUkwk{(r0C{3G?J4dlQ(>XL4IP&eOb5H9SD* zGv`gmmr9Myr1TCDF3TqZMql1Un%lTh+=6@Zxrs&%v3(IxkL!@N4Uc|0(G30cwDYkX zJf_llv6lC#JB7o~i0=oS5@8+H&q#;;#}RJ#L)klI<*rd;)b8H?QHr)ui;VvScrEf( ztdZ)~?vl)X&zRE*FZ`H%&362RPcm*^4c;o2+^w}FXyc~j46`+W8Qpn)j#RgjgCydx zDYa;7^nF-mDAE0+Pt5BH(2WY`T*`ff=vKd*EGB$UT0oT=ISi}4o+2`n``nD<&DDSV z*&6!e%C9+IM;AZJx_T+_*t4fU?doXu6U>^Nlb}*=^+EE34pX%+Dq1Q5m0n7tf|5v@ z*ExTf3*k2`)QVKz7T(gzQ~d%JOvrze*BX&OnQ5Wk-&0K=rWy}*PBZ?1g8h^@Dhu-# zszb?6_UMk46Lj6bKI#3?_qDnrbyTQMHDEj`T7sfwv zRE_8muEVxW&z_Cd5Cna#eO2}G6g3FcjV+T0z4|@ns^kN~#dcXvQ>r@0wG$n%y2Unw zt?~k@dHNEEj^5%svB&kRFMc=<_Zse((^BjNmpCsG&7t4bwUd~VUp3P+fUA8ygkt;8 zbiypjwUheoq!JID-p6w*mZmcd?C<%;%rA0@W(t`dW>M#PvIh>0^uKp#c(`yI8nLar zr`z7$bujvZnhx0S?OK__c_zu&_PdSk{;w~{Zj|UWi-bJ%r6zph5OXl#Jreq)8uNLx zIIwGPcQ@1*YbaPzgL+wA=3=9dznP)a1Fg1o_aqn#4eRu#E!hHI4Qii6KI)(!{CgSn zMTq{+Fl)Cm%Vxy*sL8eG>^+M%Vm{r4pCs-$bpDnpf3#QlEkhL9{|pdIC8sx%A9n2H zd})_HNT=J>K&lhEwO=b9Mg~8gXkED3nz~f@iI?sZ)xSQwB#h}d;Tj{-tD1Yr%)#t# zS=}zq)Mh#r`Y!@8Z$=kxh%v7#uQGgoM0w3x_TMggQtvs06lqo2|K}-k@{eS7pc9-{ zi^KemueaytT1$c7Y!sJwb&SPQUwEv=(l7I`ZJ=zSonBjZK{6 zq_50&4Y$pj{VThXoLeVWtiVPfc)H*zed_Y9abFl?L5Q80X%=QD(-q^UL4u_ye*=5CFyf{xcqrQcKXPd2VHs!e!lta{ZQ>b*hf zy_WE9y6Y((>n(w4n*7v*gv;b(w?FZhmeg8Q4+oOEl z5dJRweqK~QKDmDB4t_xn8X;8vR{Z`^O1}U6{QuV0gQ63OE5m4;sTgn#bit-i+Bdyf&+prB}ato}!M!Tr>%*>CTwUnWmEhtlg)3?L7jO%C`BpMZ&jUMBsfzicpL$nLurDyn@os!Jj!Q74AkF~-zUx8x4J__v<~ zQqPbzkKohT5Pxr7Q_mZ$c5FX&6%lcXj!)!G;S}w#3X%BC5Aj*uu-35nd}Mq_;xD(j(`53-=^Gpvkx?m?1`!{@6U@PC&*}^K?m?XVU8#b#7_GwP_;jX<{7d;wtG+ z|D?ZO&(Mlbd*73$bC#wnn69szZpe}P=2?~yN9NYkOa&UcFSV)P)>F+m(k$ZBjn1;I zpJh|&<{Uca0H<*|m&lxO`^T2+*>IYidPwfBK<TuI)WTs<5@xTdI5lh zb!ZlL{weJ0fuROS7@J8@JB0%rMR4DTTsuTJ_~GfQs)(2xBaf7Eqz0ok$p)3!fEx)T zoP>)~Y#dI)=teTkBR*M|Ewmgq6|J6(>?o=V+R6+2dmFlgiW}>VfsF3c3{nD&_=~Vd(PHDqKNg$=v zIIq;Ct?KC^;bba$e_r@`_z^!;tz67Y^t?)>7xq-L`dJ-J&Z+vFYNb+L^^57s*XPw5 z1%=v@H6LiDO(?64pw%Xl)slhLt_9WK11qg)YhFWZep*!hI+wN&tPw4!aSE((*{CtA zgSppLd#WN{Ss;9Ai~M_K0(%jk>R_P>72!~%yecxX)fQ0&t zy87(t`n>acFU|&E$%ZdK>r(?8LJJxqdK;oQ8mws>?eyw@Z&ZzOQZ3ijte)58PS^cv zZv3&)s10rWBi|UL*JR?8D%idtpDA+C-(=#L%#5>o$qRwx?)V??9W(McZ>U z?CPU-rp&k`$95&52lBCA(-p8QlBI<5vNwrkgjS08&K){I9q$r5^uKoy(#eguI*o-o zKT36erR&UjM`d2$`5oO!z*Aatb=eAa5j-e=J9jw*brEbSoUvW5gf4*ahQdP%<)wxq z43YaKq5=w0gjVv93se|gw;8rGs<88iR97rr*RRB`#P3~B7hS37E_Z!YrVz@v9+ew} z3g+rAB%mVoyDgo&c+u<6g>Jn-wdvAzx zLiFRQlrxFwjfgILBTD<`Zai1dS0jogE{rb$wN}`-(b^aBU2=g;aD%kJGf~49-hWt# zwue*N!YR)SyEa7ozG+hErIO$I*5~6q5SrMxK|rx+0wgS919jP<^qT|IQfjuEl!pY& zv2)jN9?BmglJv?=z_OL+(~JdIZVsW~hz zOz)^h`O}E<;$pb(JB87O%?rz(jUn>8Nh69ky9F;t-rZ5umLJ{lpw~$nO~ej!2-9<2 zqBq`*tvHXF8Vsn+^i1lbu|ep$dh}&tFA1i1wQy|6ZD2y}w`~LLSJFgv{X`meBI{zJ zNot}nDE8@CGzLr9qaV0AGXx0F5XvdWTxKSMX9%|B*qNEx&6#51Y5e6> zW$^T}!F0|Gs`Z9h0%n%LOSN-zZcliQkWO*rGItU@N06hqn3)4M=LlCRNM+{9H0GCF zW)T?J$I^Ma*?Icp+0C1{^~>3P1Kf_m+-VZ-s9_GA!Cl;(C%=U!b)BcOnx@&B=Wm>6 zg5fsRaV#=(Y{j@!4DJ>%#{KEjt|U6&M!mlS80R2Y`k zWtLYAmfwUd3l%Rv=vx-uS_U37tVqCCmep5etX8gutgsZXaP+O*+FIddSfy@+eFf%C zw3oITU^*Ie8|6!94KPE7`Hic~6pb*+8_oe3chyOfe zg_GQHBgu{~irlACN;yy|Il#Rn&zYyv(#@u1f&f(|hM_)dY--$Ug>xZ#wl3X_;`N255A$t7t!Y(!l6G{MmQ7k%KxC+^FkSLD+Mmx&MP zC*}Z_)XVA61Jw5Ao2Cog_F?5K!1xC+lme{x&p>Xgr;>Y+({o(cfq!y;mBPZpbfSb; z2)ItJ@-yo7LT51RD;F|$(Zvp}yXwhOj^RBj|Ah4n>#x3DM#r%-&I-j^e`@n(h5}Iz z;R~91`RUK>I`_fA}0B9+M zNa^cJZpKRFV3UG3WPF0O_ZHKgg*`74K8UQerZC?(RAWuAeAnOt0xrxa)^in1mMKQ&&;S4#$-RR5x5pX^`+??x5 zNolnDFSJZ4(tIwn%XgK8Q9(qO92eLg`Qo~}h#d9(%u+ep2kj#A^iLK`{7nu}Z z@|7uEd!_L3#SLwfvKOo$y>{E>d8w)XC#|{tAFW{_(wZuW@5Niei}!*@?*&&41gF0U zj=mEdP!z;S3bsBMERzz|NA{P5Rt_SE_7 z@xjeQ-^25xr`H!RZ!1qPNB6(ZZZ57wT2Ji%%E{H$!PV8}f6P6Jqsw;}=PyofU+p4o z%(ER0i~kyz`h2Vk)o+e}k4kwvnDJ^lUuCQO*;(C#iyq<2Nnv0a1{^#Dj%9)KH^7B4 zaApPUSpoQOz{F=@zyj!U1ZtdtT$jrv*Yj}C!;p|gkJ5=h-6M8=LstF$#(k|Ot(70E zi*!qJ-j^o6E{|8P3zzQlecbOZGU_5e;{;!IQr-Xk=Ip!n#YeqM!_Sv`HkZ18&h?y* zzqqV@_n7|aHt@r%(=xKbG^6HAM)}8#eDjR#-x=v1*~x)v84>9v@mXz|xpTRBoB8<% zh54%``MqWNh~oT`()_%Nf|Qz~gxZRfdSqrpLt$f6MRQACOG|TmdwUlO)!p4qjM<}! z)McO#Ki+zPtvsJ8zFa8=w#tC@CIHt9%nt(Vi{rw0?e!f z^Ao`OAaL9ToHqhzt-vAXm@v7yf?ru$Tp}j$iA7A>TR--Wtqw1YcP`#G)evIvc}dPs@Kw6= z2|5dt0R7ZTcUz3C>)Wt3%}tfpIep18p6Uh4m3hPN{pl*v0gt?xKMAIoH#{vWXfb=| zBBNDZCDdzbI-DQOGRC3!A56^aurqg6KjnSwz$@~YuZIQl}y7yt7A#E z8|(cIZ$SntE3TdSjFvbtdk*9}8W-aG+j8qSm09l>l>!^6z5U_@26$l)(~T!=G{orD z@asu+?ktVni4x{M@7`?Jh;LKVJpF!pP5Dlr?I6#{JBfL%eZ%h%4K4LYgTe|aveURH z>ddYBCrNUnZr5-g)b}0*Kcb;gFf<)fvgb7w40*|I{?dB9Mpf?AYje z%-7`@85;h+1nW+Ll|;KSvu{bJ&+HeIoR8L45^hlzp#3pC@o7n1vd)QBLoN75X<=+ z#q~$)8zs#YP(o=tiyl-K(Gzb0LyK<|R`kk1g)5Ma#zb0UtVbxevfJ3KnOo*)M=bu+ zgB5o%coi!kZ!HjMO>4qN?!lPFkIG@0x}xIcBUP*7$7DoWbEO12kc$d=__}UaoUm1Y z^ioQ&@l?F71OXh;inMT1ez&HgDku=CLp&phv|pVIwCFf9lzQ38u`p%R#qI07fuei( zy}TZg&GoQDsO~}(z0xBk*7A6ogS2>Yt$x2v`dt6Pzzte;yTQ*~!H@cQpqR>^8qb5v zM>;f&nJ%{j}U4`uxaXZ+JUIxN34ZJ?RLT`hGvD6#IF=z<$Iera`oqr7p30 z>Scw2Jf_|6^sy}>`?kc$X9cyOb_dmh<56b|gME>d8LS=0SAEx@nYKz`ALq9c;e&^d zXxObPjtkNGXL~kuvUlpsh(Bzl5<@S)Z3{W!fQ?&8q7nUtPHWar8eXoqUNfY7*v1B) zv6YEQo_F+Jv2j{(sgn*`{LC-e$$?vuJA^te#BW}X)6j1Lv&Ptx(-*4aMJ&e;(%5(0 zsIdPaF-M3`k3|@mf>O!u#ukw3?0B%X8x_wtw=nz}@I;!sQ7J0PU0v=W;a5wec^6x- zhaB*BeXC8MEho<*uF3pAXiY2>@;_-!F$27vmt)Wm`Nr*P3`*hNaz5q%pf#~juK%6Z z(EWJa#_?y@N0ROigA-f3=$SP!m3oIca-v;aaM%BB(4FfAu^rEJZ35{I@T@j>Up|Z& z43bsI;0Q&%d?L;qVw=c&b2IjZ)K~S;$7G+m9sQv2f3L$lg^X|0BNY_xi3S9kEZ(>| z`9kT9cW7khBAXDqlKz*7Fa&<_w#}jP3%p8HuwBBqidoM$CDG`BlRHonw{C5#H~*3g zzwp~A_ZTMI(lw*gL_T~#tKEu>?7mnOeKD!_?d)pwU}E;&f~P184$%aE?7cg6Yv_;J zZxY@3ayT$+FYS6>C;bLYzCDtM{^7v+x@ z8%7rxj+z&nstoHl;0jIEEp&h0aIdaCEc&6IFS@KUUOV8 zZ@B#zeD=lMAYmZ}pT)(sY5fXK?n_SUOV5|bV!nS}G9T~jyHVmyZ)tcbI57l76GPIL zpKfeSjKNsSqk;iT6XAl%DZ}4(cRdomNe31@_IqEM`MAjFb%Abs`B+tmchPqNRlRBR z-+%75j#^2c%ZOcwSGL@M8TspBAyhmNH0Kpty#nl#N;NY62JY)jb=_*nr`q2xbL_>v z;{@|3j9B4f2pQAt?mbrRu{9TM{TJ$9{PJ*;PL4bO@iVN0-s#q$+}`& z#KbLw-#IPpE;}G4e_Ms(&Oe_~AS>lHEW^W{OeXJ5R;wmkMV6c|;$G^s`XrTG6mU&L z$HjZ=X7+#|b#%CgZcowIi+3y@1mVbIAwXs&+n zVUlM2WzFL}_2A0P`CnqLeo$;*(iHsT^Md)@;#N%>NWWutO@EByxdYGDVrzMkdK}(W zq8*46erqQmhTo_@>4*#8_57)iaRhW8SY0U)UV5{pVh^L zY-azIB+Q4LHRiaQW^m7tgnj-vRwV87$oeME5Uj1)rr-P2+kpMN#8W%i*gE){al>i5 zjvXl8Y^J@n)mGelg0iyGcO01e{rs8TkFhn4-Wb-b{(~G@^>?qG9@poFG~~T&Ft4nh z8lAVTk2>;xCKj}9JWl&y2Q%*!lXd$atoI`p?@!a9{Qe5gTQhBJMN=?;`m(KiOG#r z!tF{dl*QVOD%OoU7J6-f2xiqPZ>+lR#kg-P{yiCYKg{_HuzH+Rx{>L4 zkeIsB#ClMo+?Xaj==t4Q*gUWKxpVOUcK?iDQfCY zWH~$bo)V^ii7e-0+*5|%4bJ98CE=y8>h{vX>w2u03Kcz(<^0|8(wv}tBjN3&>HXf6 zL?_n!TB)}_{}sbruTK+RCREo~!QO{2soh9{#x=fvX#f9O|alxf^QLFB*h z)R9)F|HRp2Y`Y`vKE%j58a?rh;hEHPa{PyAjo{LZdqo}V{(;m}IabIh)_0B6KQC59 z56*gqq1{WM0CjJQ<`nw23-#`%a=dfQ&CROYC||(fuJB(VB>PO3_c9 z^qD7V_*84Wo8e1byvte)r(2vmGR|u)&X+wtz%eHKG&!6-#Z@`wyIIN){}kK2lwaK` zA!{j4)Tw3ysandZI%cW*{;5WJsSe$#U)NGKsngyHq-iUs>6xW{jMJiY3?p|;zj7MJ zJQ*f5l};NM&MJ^`T{+_1Mr)hZ==t>DFfH#X}W#vWz&gKgDaUYi57_ll?6odS06SeJa~B9(u@> zWBV-UXFL?N&T)v(ag2v*PUUFCL-m+)J)Y$Pp7Bsc>s-J1Tz{2p)2ZAq`Po(+c@{J| zcDi{s<~e`j^Xz+aT&Hr}RB}@}a((i1GymiUt>>E3#8*qT&mCdkWLn3;SzxoA`^&0-&R3*$9uKUjfkR+8nj`!oTJP^Mbj^XT^$4MGY!N z&3}s8@{2mBiYm{F=Kqu|_Ql%mZ``vXct@=GInO8>2w#?zF! z3YOKVl+~J-1qYOc=a)tIl*O%=C941>hqdIqdii__`K$58k+sEyKY3ec#R(iGd$lw? zfd%-0l2hH{<+I$!78U&G`I5aALbO@3wD}Kdi`Ls@WdBq>pVCvTt$Goksj^PW|N)>uQ9@KU1{U2zM#gbSpadY6~@J*Vq=+)bR>#{u1li zlT>)d=$C*92t>&DROux^bC{43lE_F-!zd?YL;_U16qz`UOe#Po*C9WiA=mdEKD*K9 zCe-E^)JDWG7N6Ia($*nc87uYbsx9h}yfn3Sb@jb<3;Wc~ob|2HdL*8#)2SX6SdUC6 zLr>TDZPcUf#R?=F^7NjT0Zt7il65t84V4LX4d)HD3D7pl#umN$Zl}i1y88aQMs$MK zh-w4UqhX?;0qayZOWW80ZCsRWTn=pPE@&j2*KeIS7H~H0an>E^HB~t^oz(q@XW9dy zfKwxcs~IENOs3y}ux+Lag3^3%LJT#Z211#n>Mtc*)&ij{Ld{LFEroS0T!{_0E?Q{0 zT6qc0M3%DzZQ*yWzfs@HMg%fkZKq+)+~{WBi{@v}t^5~lEL`pEQtc1Fx6=f*(-*ei zMYmrgw3EEy7 z1fF;MPa{GCGl>UzEG;ry4VlVWn^xCTs8;)`Duc*lYWK8itb2((2AK%e39LeV0e2K8B*cZK?jN^!Uw@D_ zsh_=J5X27NO2Y8m9Ga6F;xp*G=Q6aBI3#$pUub6N_+m(8re9Qgc!~&S)M+J>hOaR2&!Gwk4PVds3+NM zj6S-Q`Q$SES$&Moa_k%Zpjpw_bEsUAw+?>8ekG(C7rB%oB1DE4&!C2NJEJu=7 zZT$=a-P^#G*(8O=)1y1SqjSQb=)~C`0(uBL+pmTh4a%sLn!7aZ#Wu_!ZHd4J8aXfr zT+V?>P>9StkOcj1Jx`T9Pi-|%GdoXdrKLzN--M#SZir*C!Xby~IT~?XeK;hZisu%d z7lucYQr~mM^M~M(a@0bzc-R&mIYc2Uv+zh`0hxY9B6;Ci@q+R%oQxrkeHJHIjN_KU zzhodH9K4DZ9tz+!`tT187v8`Yo-{7J4_S}~7G!QM%Fp6nxGvsFUQ|uSs{@MRV8-ha&Ey)2(Mz`?BGRyZ3m%q;90n_AVvBu@^$qQD%^7XzYTa87;o0U%? zPzQ}=MET0M5U6YMGUDp0r3}uCVbR8Ql~ZFSz;z|KcqMFhC6ZzFpUkS4)vE8-+AoH6 z2iUrk#=5K3x_ij_kK%RPzICIm_0J3&Utt?&8XMpHMtLLG;$^QDaPmqh z!l+svfyHK-rB>Ia$coL5|Bb!(;A-mI z+P)K7=u(s}B5LS}fJirhAOa#{LqJ792t`CxdT-Ku@10Pk_a?oB-h1y=dQINN-*e7$ zKi}Yf$8$3F2uUD2$tH8IwXS{5-|X4c+1Z?MTz@CICHH-E&um-)?U zwA=g0UEUT$by3?g}?04Jm1q*r4;)`(Ud3laGQmr zelIqNbpUr?!GE7B@D_*aen8(o(apVT14@NymS?~ARX@Pr`ctaZ?q9vf`q-Z$(u}kz zk$paoX+Ghw<2m`~cy^=sLo@857c2Q<-QkY{rWwaNla z{OIsw81ORa@qd%y_Y&Yu6X0zT!8WL=w%IxMWgj2EdVBm{?@-HR^S$fXt6#l}zdPkx zI+R;m-ZqzMl%>5+i+vLtsUHz$6Y6K-<7n+@Wo`N0>ibu7v(Mkm3=NG8k^keOkJQ&k zBEk00MIUMKN#8_Q&s0~>Oh@;-?#J&sAHM0Rnd+*V=*k;^k}}klKz@|c*O1dwQPx&` zr7QDZS5o_vn4Z31l5GQq|o5ytmp zOa#)t-z{+-~9<`%dFeTbBCRqA{H)es`>5cn49CsuJcP|C+s08n% z6aQ$G@N}K{e2@I>&&{)~yXVXNn286NehEz5TMYUWrbYjv_4`G;^I50w?x6p`N>KSh zXx>>w)mh}|dHl|K`q6p$*+m})bNE+z2iHJ(U&ml(F&B&H=Nl&{yN8E>YqPhz3n(@K zf3v#0ygE0xJvw^a({tL@b%aK5x3^C;HFeb2gW84K&!6r;=E2g^TJfr<0cH|vV;^j`*P2PIUuJQbL)x#o16KO3a@5 zLB;nq5*myfNqo$=#l4mOtPkDqC5-0qXs_L5%R9v}8XaGs?yjxHVo$4EdRT|J0~!P% z@Z)&?Zp8eI3xyo}tn1a+@`;Y5&x0Fo%mwr(xUsmiv$}S(zaSG~PeaXlahI&jdEcFr zqZH&DLeBg0yr%9Fz~xxEWz%vSh4z1BQd%c4`&~vQP)TU@wjNU$h3^5DC3gVrGKgOK~H)jO%^hi<7|>nn&TY_7}%psuNtl- z{!HR%N%9dIV~;gdXI)JRQT2k~iO|c;NYft^QAwvr^t=_N8V2gukOhfAoi89Zh{A|8 z0(HJH>1|_9*-{i23g?N{GBc&8^{tF_8m>>7Iq0ZXV%l}+^v^T z`V2Iif%D4irVm2lx1ox;S6%_oA~}| zx7mn1@DekrDcb7zLW#jJY?L&Z8<}S8!)@<26vv(Hv!91b}R};xhw>}W6Bu6TrVx6 z)G>O{q;giZJJo(@_kH+1tji<Biw>(-iE;g9!CIP3T z1p&*w;!Ndp1E0t8A*o@W1Jj1qp(J7bmZz!fJXa!7|IhlXHDBB?+}ZIYAIfU!+IOr4 zvv1Owcr@^=nErgbl6WNt(#WFv(c{r0XG(v7a~TTo6t)j_44p(Fghf^Ss8W*gH&IgJ<|_X93pzL#BM5|?O2D<-x&&ma zzXdobZS@V4PP*`94`U6ycEy6P{jo^nKD}a$O=6}EM7Ak1^94s~vapGr)ID=Hb53Gf z#8)(ECrjE@hG^bll_Bg<^s4u?wcHgqcJ1({EHM9AjxnveH1NmoR-a%3Opg4@0 z>velySjp9=X&xC$`l2rc6X()|I}MbtXVeSapG`-Q8LEVu7fN{SqgZ}0D1;lThF0un zaJg|jO=sxOUfs_WUNm4oVj0NA2}Lo>u00QJA4nCwmdP8+`O3iXRrdQ=Sr7L!-c)7^ z8hif87Ujx%+wD4(p7|q(zc@qV2k{$Cvmd$Av2oA0Mf(I+uchj58olhsQ;Q-t&wKk6 z&fe=f(l{lM?Q_8%#T>0#qY#$rkhAgWp0`)M>hs*#3rvQ#O!Ui^ppulpP-8=Ar8>GI ztS~=xQy=?8qj&7EqG3CfQX`cMF;Rm)Y3yYuou=;Jf|Hf>+4QYC-qZ-h zfzu6lZA8$Y$crMntCpmb%R9d+7ey~?F0-#&b(dN0$DM5~S3%T!KEaC;hkcN;Es>z2 zs37H!_!`fPiav{S!L*j_)u-v@{c-jJC>aSKRin`%D@p219=o(x`lm{{OZ$=97Y2me zPD84a)G7Jd8=t4kY<9y+h_Co=pjr$}yWVp^~G7 z&Clay@SO^|8@o(@uDRRm{iOC6yFrV&&zorGw=BDDrg{avYtAmV9Ui~n zCU|ySPs{u?SH5bFw$@TGWA0CYzwJWrf@`}5W;gtCFqKgENteacmi3j3$rJM9zN2?L z?$0h}D7%k`*rxsj`CrU)_@9iudA}FeeX%5U*Sb1*YTxm3^%_~X^{@c*VL`a~=4XGa ziS?y zc54!(H5tnK%8E55yA3tc<{HX|md=`9&6+X7`ud3V4LX~fYBskbZ0@Ys=qK4bS->|f zEcJ05d@bO653KaH9U?8@Cr4Jz=JqykT!bvGg(Fm_j~LWa zI1+k%B6PJebyF*YVH5*AyvjVhSAKjv@(3Vg6QY5IsCkAVJ-z1GqsrKkszjh#B7x8= zmd-0>#WPvWE78&mb?h0gEt;CFR!8AoAnaY}0Y5bLE{pIk_kdIOc-I_z*LuJqd_Il9 z>FWVU1o*U<`E+=|rFMLJ=zM!U;4&({Lzce7gx*DEzWGS+ieul>R_{7tziM`$W=p>Y z4hxA)P`mDA3p;!EN*nM~9{YQ?? zj>~+N3;m}7v}DB_X!nf~_wma8KxsrzL;$gYFMyU5mkCg0cvFc4QmO>f5JBi713N9@ zOrDqk{oO#4w&&k(1r?(N?v#7$I|u!?gmYQ>=+6b+Er;C`@fCOy%wk|H%;7y%7JO(L zM4b^tBNFhaJn-IXpkRjY(~}S}j!=wzDAXX7*E5tUBlJdFsPJm&9ip%W_OKQCFnWV9 zm}eMqMi^^b7{zMXJYo1UdpO>c@Ev6MzDGD&M)+B4IF>L(D$-BpNysK)@MC)aCmA6! zS`o({5$7Ypl5hQdMk5^C{G3nxTt)o74oFauQJEQ0NYyL3CsBDs(MVQk(c9<}18@UU zQ662@7LDA+t)q{r=ZHb};Wk^vw0g!M3vkh+Fg@ zPdE)<0-kyTz85?oAc43dfutg)`y>IKp|&NGxIq-ZZNZJ0=>5YNz_c)FU%LY%;=kVIgZ#NM93S&`H^n#6k=r%#nk zE1E3m6)$9+jEPJZRY(vYOC|=|eFd(xXv#WKvRqW6eo)GNYq*kEvi@$0m^EB=Em>bR zwI?G^O+8VzB6Zg@<$XoU$F&q)hE#opRD^Y^T4t&SXZjO`bS1-d%rmca)y#CR_H>!G zbU9*_ASdd90!q{nCEh@sDL#YX4^Psw=vw&-g|~7zM>p_Wyv~s#Va?5K6+y<@>(Khw5oeYWajqgRhghM&(e5uOSKhpI9$GsODGmP~96OdFKzc?nbrNgl2zOG+ zr?JVW^@h9TKOPoq$s ztMEZ(;d}2w3C1Gr^+Fk&B0a^TC*wtiSw+vpicLt0U)dDjjV{*ED13KTEXG)(!&vmm zrsQ#SiP3n`=d+R*jHPDd1>ZGF1#C(!qYJGoOCOGx+G`X!GL|WdmANt&yW5nZ1Auma z#b>h*e+I!X0=u4-+?YCWTB zL+@&ntmP#-Xx~)VRjY$T&de_qsRyLZu#VThHbL zzmZqZJyFl&184nPOW_0O`&&z~@%zk3XW*nEP}rQ5F@M?#T8ghe zaCVgzP)lLq1Lw|b0ksr%K5z^VMJuSKaLHEFvTc3O-KVm5|mn##M?jG z!qY&dMNGS4ReLsROYV8QIa5agx}`|7!`av$>)N4Z%U@~Ru0Pk2X$${7(XKCz&a;KL zhzn8s*E?^Z%cITpi8cY>w)>rJ z4=&n7B-+J3w@X~KKJ@LhLG<8P!(fwb2gcnz32IvbgX-0T8ouz|ok6YZLm#UL2CHCj z(;*}%SM!B)^bDC?41JLph}ImK-Dp7A4u8t1PBy->ZDRI@ zTjq`UfYOH8CUvF)uT=7occWpSN0H9tQPra{ouf!If`sd1NjzgnRf06TF_iBZGKnC2 zax8aq4Czc*ATeI_bR21hTNXQBkux4_H(K*_G;(sZK4&yuVyu~LEH!ql{qtDX#aLJ8 zSpK_lP)jjhT0K7GJ6?S;9)5kIVRE#|ZX!8$q9b;!^J3!H^~wHsS;ru-TM+k zm}egF&1`?3*^Qk!=$tvem^tH_#k`w^`pu5z%uaUB&TP)klg%yh%&k71+xR?J>pRzw zGuP5N*ReU*O*Y@pGe7)vzUK3Mz3+T;&U}04eAniDAKAhX&%(GJ9JV#8pEQ$V2PexN z)5n?3vx8HcjO%O9R(&?c^BYx_Tx@)|_}XtmEp8EAy{Jh(`5v=4aD7SJZ&LUDQk~sW zDAHtMc5=xicVSs#`TN$w=I7uMZE~-g6`!sZwr}L1S|W&d73oX>swE=)R^^qaOqrLJ za;MDgmnn{hQ{JzE!U$B{T4v2!de^V6dz7IcDVJ}q%i*l&D^nIu(SO{#T{X2{)5WYx zb-Pw_qjBnXOApyR3)!uL4fNDT7iObJ@@5|&`GDiCA^XjdxXrPe&AZZM|3du005}Vn z#>s$byzU>Ynx38>5CH$OwZZQ$DQj3rBsLQK0Jv=cGmF#{&=U~QL!Bt-A>t{z$dJO!(FxeEwH00RD206a$k;JYHh19}1g;td{&L|^V0 zf(9=KHa!Ry>@_kHJXHV<-T(yb4h{AMUIXk1JO-W*_OA!_2La&SNU#%hBpUn%yvs-| z1Pz`55x`!)25>A#Mn(d{HehH2ur@F=0}^;cLj%Bo16CaH-gk9%0nRoc=Ku$?rIjrY zuLvjNq~x@oo*uv(2Ox0(52vQS;pyoOSlN%|pNfjf0wy^?WZT+0ic36h zZEZC&{>siF04Uky>k0zlkbZv-jBUG$s$9v`%_f{55J4t z&wQ2mDxa#X{b25e@7qc*iQ`uvNAoq~-LG5HSuy=W%qQN0dr(n9P^5Im|7cc+CxT7dX5kC?owM92bY* zI4@=C+x*a^tIke0Row7TD3cKu3wLBa0-$euu02M;S*4ieIPSPi{-BZ7f2BnR_fo0_@@nT}Bg#`<3z)`}MD?IRY0k{TX!A1Y zC0~^gUCjx2Z9SiAXDrHjZSs>-GfFNbYArtvFrf>wn?)%ia74rcbIO!6vwXv;6pgRF zC1oMVZ)T0oEI`+A@<4iUOmn3NUNf#1-`C5^q5Q`3@iFrZE9X}JLfH6P`D$*oO63*} z^q1FeSC(nn8JsJvcBzk3rH<`Iw!n^$)@UV zD+Wh1ppKA(xUP*LU1_fHjtSZ<|5V796TR4uMz$5r+46PYK1COF%u#h__ud{}%kC5P zZ{7D$E@dj}p^>l7ZhfpV$=)RbOE7~x>?XNip#7NJsOVZD7ktd%*Q7b7iGb};nBsP1_~_WaqD+B&P!Uh-_pdI5Oe-wjD@nu67*``8>sesEkMgomRuqR zwCsv?olKUq4d9T66O}FJR&MER&75u5;XX_wZ>0TF^+)$7$LYHiSitFC`Y+7ses;_I zvx9=^nzO^QBj)p?nkx?H$Bk?^Hh0bMUy<1Fdx5Pu7#Yr$II6U{V!tyN>`-+Q>Hq%p zG{Y$VOdVEx+-YLJ-w}JUbyRu2`pfd1Bo-S3LkxNvIBru1Ll@c7@P?i#kv7IhFqhS~ ztP|-cwW+QEq@625LG>f=eMoPvGc&pR|tb zD;?ze7R7gVZckzdb_6MnsSDx$435kSf=Z6h>w4rIah7l$7XzO+(F7}Q<#FBJ)19BR zEXikRx`%>&&wP(`k}(slRoLhGZqg~Y4mVmQ#GRk{FU;$lo(sz%DpUf7Z!H{>o=80M zdltx{tBb!VFClNX;~{?=dPjET5!)JzbDZKOExVyLAJDSJ2g3De9GOP|ExU0sLcI>q zvirhy>Ky?sTS?o`6Tej{jXzW*?W*uzWRL4oge9WWP`y7&hto-@wC$;hs;@~o=`xSS zNr%}ke?*f~I-h?z+MKm8vezeFAl<6dT2&x=5`#_`s?Eq%_Y#O%5L~|7o6)6R&>!33 znl7IFiE+Q_`|0CXO3P^5*BYGrG8qexWZo~lGy3VTvvbUA@@BRm5t=Z_3HiR zWspp8E%-4tXIcRNOQqX(s0d}w-q(jOQY)&EiZ&U~Bh0_2u@tY$e=}5#OS_6H(KS%? zuINp+7R%g`kE)4Z<`KWlUb~$o*2(#-XzWGK zscx3Sq@i+I+Kb%H6eICX^=DN}FMeHHG?M#3{KBvOd)kehjHj|*{V`$R)5T>oo^cQl zGP9c}_G^BfD{DY-!yoEP>U@nDZYdF z#0?NX_p3|FTAyy|;qo~zi5``AUk@fqQ=D9TZJ|WcnrmKKIeG8tQl&3_KUuTl)NYuC z5~=(z>*30&d-rFmcYMFtJ|~(y6@61N)VcjVzhe@kH(WV$0bt*XGk=o8s|l5V*(`R< z9Q6&?95ns1&*}JpHKVPAnxh>_jHhtfBC1JD@@#LA&aQYF)Su+!xfOfQk_zZGFu{NQ ze3(5&;Z0X_oA>LtJ3ez4UItCHG{N5Yxzp$@)RY7X@*_;d=N8vS8lfwHemIco(vVoS z-sLR_{N%H6d)Tr~sX0G5zG|LYakK@BD!_!|Ixcd)O^ zT*EDI;&smt)Q{0+6cA|e^ zv-y1|yPO5l@p5SWtK6cGRucvq8lk^lIhK@mem3!MBbXsCEo+iU!;LtbSdlx5(5~Au zuNtjDxe@3^O*%~Co5h|c?MZ++Wo#%;^Ghva5oybTrz z@0Pc9`|gI6+AMO%mv(%J+m7fspBDU8ULx!F&CT0jnTA&KbBkxX0u-Ow?B!}w_k{U zV72@LV-@xjvzQ!iwJK$Q>Y83No8@o0&V@PkX{nhnylc7nP1(G9nR%`<-E#XL#>Nl! zaKXIxY?I#JHWPb&M_8?9hy?THnBvjd*i5NraljnQ#-0>*I%jDH(0gw7F%-BE`XkH8WrypB4%P|^`c@)bu^kv&y}{U)sg&5He= zg#&}U!^yA%Gs*$`z>$>@VEP^5NJo-mN6uEqFd-*uH78m+2l_InqhY5TBMvu(opIHi z*&-Y{%ABu^IP;d-^U=9%KL7wA2O&$Bvv3ztI!Eyl7uc~2V#HBe*!3zqT#nsEJIM7u zJ6y@bMSIs(j2*7J;-anUCPgP)tYQ5V`~3xQ@KL3ep`|#|=>Kugf$| zBHW+=07M7Dd{_Gk1q2{Eh;`XdC~(x%K^*BkmLGT!s<>)+xV|rQ{kY<)OXsF9?`BBn z6oK-TYxR7x;;BUF^^Dz1Ro?3r(#yre>mJHWu+{6qikB#%w*7LJZT{L+A#jn9 zM{fh-k{J}CKV0tIoR&MwYX^+X!OL>*OJhKfX{_Cy_NC<{XpD`%39Dim&= z#O{@(lLQ5hao)8gR8azdR00`8qR?6bwRNJXLL&WGqGV>`4bdcN;-ouKNpglsT&GD2 z?Me4Ullj$?A21|}R3y`mB|jWXloCz3rJnLQD(Oi@%H6RP<@N+^s#GCSU@1?0ZJo*( zm8#B=q&b$#cABa+mZS}^KE$bdUdh@)X|Fip#$Kt~yJ;8=PPo}xsKOBsI=<#wA$0O-<;_U>gi3QX)O$? zZ3?L!)~TJDsoi6#y~Jt#Ybk@)$-@=Nqo>K^qA8QsDbp1xv!coK)@earnagX*tDGt8 zhAEqwDch$hDe9SvnaKy7sGPOTs{y|}fG<50x~-OgFX~UOnDEOD=c-0NjZwZ14jw~g zK2t}&4l9&}@e=W)69k3Z6tH_2=ccKdvj0%7ht^AJ^t@@7?{TfKoXN8iCMKa_0vNlDxqKg!x3!a`8-DNC(p-~9b zXtC&Gb?bYa+G32jHQnPQQ)YUxKVxTRu= zc0s8=7aRb9w5iHIcewjVmzgp`ey-8QL}56{ZuUVvMKe=Dh(Jf4JIiITQ7<7 zt}+9%^pB1z%k?T7l4^UdYA3~NSEK58-ql)J)jA#3dh68&BsIodHD45KWQ=Mad)Fvt z)jaE{c>(x7B(>^XwcpOlON?q2yla)SYE?UGU$56bB&mDPRhOVx=LekYXAnZA^50xl z6g_#AXYmx{zYmVpnf@Yv#NiJE>N$a@T^ug3Q_siLa8Df0sL~*0+wedf&YIUCKG7f{ z4yUAOlooH45r+%@OO{rEPc+{4spn>D;@GI?*KE401du>Y_p=)$Cz`}a8y_(>OBpvR zXf{7aH!4pws&F@PD>c35Zcxi^5=J+@-)PX{ZhoZHte4%0jA>@$hGUGGnoQ1{`2YdP zxZ%5Ii#Q+vaW`65wa5bkka)cjQ|k+ExGQOkwpr^pZn&3mi*{eD6*oKp-O_Qt&4CFL zHc=lD)8bn8E4a3CALf!u~rL0JEGW4KiGB7qbL zL^B{~0Wl2-Wk6d4QX3H27#Qz}LrH;>2IMss2q{q5fV@TmAybBu+<}k+F)chIS{h0M zB(+os9tVUJh-pAB^Mev3K=FW926Qtz2r0jSL@5LZ2xb54WTXGSq5rp!0Ac^7Y)Hpa zSLZ@_g+?$U?9RMc`S2`tV8 z*Ko8T_wxVDiL` zfH+rx@I&Y!EdO_$gOdb6uLofX;v;a!*S4{t_q$@&6DJYnj zZ*cyN_AXmI5ab`lNI;x}2w(KXVW%Snu`L570cqmuRhIv^zfu4F^#2==0O1J)_tLel z1%VJ!!^Kf~H>Gj#=tS{YzRopx&~WG}5#FnVcwgg@#^akwZU~@$9Cp>zY+g197ccLn zM_*!l1iwKAp6LrcW!L+Rhx;M|sQSc5_bVPL46r}O;qyGr7KXY!MRBEeDG}aua(2Jd z;8jd`)A5a)cE7qH%MQfZm6R?7PbI-*($jdn2A^U$WwMfdZ!Pwi_KQ?0ISEY2!%|=R zH|~8wb5bjmcNF8qAvGQ}FC=m6oQ{+7IgnT&GGFNU0>5kC-tzr4+WZ`8tpoGBxoeeI} z^6BY;JOGjaNCnayBqT60F!0lnleSvnDe_(p_8<>{`~dO*Hv=ii8wJJ$AlN}302u?E zDFms2m+5lI7q~$R5(3BuASZx?aG4D_Nd7l|5B~d}{(A&K3Rr}|F0;W51OkJyYqpxs z{&pq1#TvtYZ?3_MhC};1>g)U?59YhpFWJp>+XAjXhm14Qq4UgF8zb2- za7pv5gN>gT8(ZL*GMaDpM+;BBuC6RhBPg(`TqEsqGFF9<6Aa`1sl#b)^uiUigs3`o z;@!W39I|yvT!au&yd%pl-FoL_dXhEal%{%_=iu%{!Y z0WshD{3RIL!6X1gzBJ<<5c42AfUpNifsLJCRrU4XsQ_3&fZ<ZBl`dd+Da zK7m|^k+Kl&>MDfZ)CjUA`j(6i5HzwE6rsKe2}Qiw5+}OH)|2?impRn3Ke;cJB3jv% z1)AEOpytN9;7RF>PxQ=1PGXe>;fzbq$bXwSA^U9+;UKTBhStnm2;T16WOO+1Z#=@| ztxt^kyi&MM_|z<d%q!4)kJ{|X2qca`>ATK#KUgFaY@h*n&V>0LcMl31IL6 z5e-rV$OiveB!DadQUpj8AnHMW09gX)0sk{gfD{1|#ecE{$Pa%L#s89T#ed(%e~$n- z0)!C|HWh>;#PPQaA)6+h8pTa%LMSmE3!-jT%7c>ab&)U&0s^5CG}}k2utK5aV)ony z2uQmNCnb&A3>Cc1g^9e!mY(_}VeEYyRwn36M-^8}@-m+c2%-@e7H<7A03zc^dd-kw zL%!I_141c^#OF)k!iBNm7`LAGNjnpe($QH`Ae`Ov$*;>3BAkVWfQ z3)EtT=gOqmW)XLD7`92&;1y9&(38PIl%3rh(8{g5yJiw}I|U9waR}B3*!bH0{T0m3 zA=qdL=y^&*!yN5*6+2{&je%S?KgCvK8^y4NkjsXrvzEi4`6<|BA)pcJ)RZ%@6<&5t zb-y|@W8>zgip6?@)}?K>c-UOXKg~=FK(p5#-^bXD*Vuez(EoH|!h2*S&eQW5Xk*&j zTLBuOVp$*-78=+Yd#n}&`xSyMgJ1_ym%U0+5Nrzs+YCOnIkwju%K^cry~WldSa02N zaJ*)3OEf+0gq4P1`%17l5I;X9?DPMI!T)cLz`sUv6*qqlX}FSBp>z8+XNp_IN?+^c zoxSM<42Am;+K+gy;dwAU2}&Wj$_y(&@F%2pgx`HEy~0k}41s{2egh&1T_>EcQ;(Qf zXO_B>g>8tB-zJbfdT`H+RW>uk04jj{!T34LkF~B$rMr(Pju#Kkmj@j};@@b~e|WK)1Y7w1EnNNL$1y!nB>^Gm|nj$>36rauns6tc%^5PY* z7p2G%-5}SMy2`3KtO_%-4Ul2rkkiFzdj^DE^&tfOQJi;}teY1;fh|QD2HVX|53}7=G!LkMG`3jpW4OS~ausBDn zg82-XkYGoKu`@rx1m^!Y5%AwP`oH4{{9`@Fn{oc>ljL7{N zh?#61elPvj9NL9#;NYBIAK80r)_Gv_tMgjsF9F;ZO?9EoG1)KMOc#Mi8hOm6it}hLJ{L+;x0Xf>@sTDZ1l;mG14!-|#=mrFDq-4Fg z^g42&47~53l zyy%lWo|AF~jU;)~MnYtKaJuB@Wgjrj%=;q%uR7onjln{oTwK|FkWxX2W-u+;AKVZz z$tV9IV!Wj!fA7xmAM5e|A!4*G5tt=CVS`u0v9y7VaKgH=>Yn1Ufnj4a`jS1pRjz@SQQ*FpHJIT6q@ z8j38rYiRZL^zyZeby=c)1tX=BOL8^N9hK}~tvFfSg`JW%+zM^ViaCYN80)Npj^?%O z;wfiF-pqM(#?8{Pttj525ry&GvWa>d)snp=jqT#2c1G^P?Onz`B6eHX?b-$c%|0T* zt4wvj%lVDg8so$G5HMyH@ytesaKk@M^hRi7g4_NIb#3g)KEE>`x%$*~@ZCauZi`Pd z-fPd&=0>3Tx?`N$gt8_$o5ede1bU>X9R;$kyVeO5<6LNd>r<%AY=9}(k{%%BVby|J zl3JhlhalhW3bQf^obHcmk|2HzBCiDCjMG$C?-OR4??6K)>7M)U5#BE1gOJ!7EgV1` zaeHNEeI9Hb&j&o_y|f;ioGeD@=bkLZSZo0z#!ueUm6UK3U_Ayzj5T0AK3xYy3}8JT zHaP=CjNG%Wsv|(esDs@--vQR+b3nxSb-s&!uzkMQ^Z4$?{=l0rfQX_0>*5d)F)of~ ze%=K{jPNg*la=kp^d*a22a(wSoaz3j2Q~vJkSY9k4{Uxk|9>`t27cjM;g|I8|MIJ zbbh{jc6M-ba&UCC59mb)M+bYyN86_-8|PN44$p_p3QchkF}nU z)t`;k9#7QlPgiX(RBbL*ZmpEH8O{TW9eb`w^>~kt^%b zi_1w<(;36Vxr6-${XJDfJxv4sT|)!?U>of3@9XL7?&w9gv^Mf_$ zQ&yK(+E7&5Qd-ttUE0}L*xmlCr#ri^C%eBZXSlg=rmB3kxZ+Pv#c5W_MbBV}jG;z$o=UdSJiwz-g-g`Ip-?S5FP+R|f?$ z$t}M0FH(Ar^T8;^^3>z%6DlxY<|A>{Z0maj3S=JSBLCpHcgNpV&u)DJN_^w)fmSZW z$!@*ra)G?DQAHdBS@$|Z+JgDW<0XEy2mDqTN_m}&zl`RX`9Vydr`^HHbU%|gveI&I ztuxx~Y6iCr?sg8p*8n@GbvMJyEFooj&KIXw3Axnx!s_2nyrxk~-C^enJ_Q!n~DuQjwxVsq<&I3IOGzO?4z?0D~A+$IQtFtOAw z<<$-+LO5#XUj;J%aGR)v;}NiuG*BSJO331D@5VmoK^UhE3S^Sx6P>GzF1byts4gGD zA+>oY8U{MWM}(@gV3fk~Ot1x`)NPMj@YP{2qOq0RzJyD+7DHaDdd?&8$-ghkND!>1 z`h$sfnyozN6V_muVAkS4Q!&SyZl{xX?OxM30=}Qhb=ZT5i?> zv-PjACeh68nr$!o?MS4$O-|k@P37nMGTsg22K#2Jyt=(8#d{SrS&SV&VNprD?c9Yv zS{;19d{{UIhm8w6v&WM5y2ST1C$-m^#-YkIOs7`1saa?3}h!-+Knj8f^h zj>*OURLz4?s@6qIyl7b2U*c#)=Go?0l(d{l$x`z1j>mF8$u&+2sgqqfHNSGAI}v$S zGO-ROhlHu)(xPh<;eIUQyRH4!8+qOD#Wu_4gT=OK!V9js*Hb?fpZAmLI$!AeKwiDn z55~0-R*K2Pil3r`z2wXbT+>cnk1uWVc18WKX?m!{Pde|?Sm#Y&+zx+;e(G-xJB=T0 zb!Wjbd-j9qcKivQrI=037v!~~nYo~1srDZ`F424OgblBysnfO)7><{-IP~(3gkfmJ zpWxZqT%n%~bK3~v#zb<95t$p-S3IO%YH@q1J)&b!_Ebzsq~?M(u^--b6%!waVEJm3VC$reGWv&hp7N<*{qiG+&4cjX?1KxPPq9V^ZV>%((Q=Hw>AR(V`S!U zWVOn1S3EbD5l^99CVa@}PU&xDd;9itK3PiDzMrn>q?_{*^3S}S0u#CC*%eP}UqtbR zN@?Dq)mVL^=Jz}#ASLy{_z6NYO(p!Dcv6`4b1yy7y~ys3rZ!cne+p!hg?o5P+B!|l zsime!*&h7n=rRo|jF}dvxI5I=WnR+n{nR^Me7RgunUzcL9!92z;;^mT{*YP{g(7CdPmn;?m_!=KUQ`k7P1U^S+9ini2W2v zDGHK*xJ@rpd3{$EdYen4(#WK)w`M3?RP_6p@24|dtjPZ)?h8uVNAZPbCS??}9)j~zz zL9&-Wt~uFeSpxi&Klf{tO#4QV*r)thL@pnB1>} z5=7&bF_h%q!{1cBel`*<_qxC~6?Aawc#Sq3gy4^0jP*&3)XI6o@{?W}b1N8)sU-*% zABs~LxvZ%t)eDs{?-*--TOSyQALf1<%+f8sPdt-jUIr_`r+hCKfV<*Sf|w;zh^%zJ z##fqOX*aHu8vWu*D=xD{am2PGo94!tGc96dOU0jyV!OFMMasa*@@drgH53AW%o|^) z_Y8_>4f(ZkfDmtlO=q?rS%fy3+#Lw~Fi+E138C3>{G6qg$3XYH3qNaI^Jr!z&ODte zjn6dy6rWDVpZ&C#Xud+LB@-xJ+BI* zO!DlDvkl%EXiPKopG$0o+FY!DfK?6vZWF?{^Al)~@g-A&qELTxL;uDwr4Rj=YCoDsn&2yqtjO~gRYadF4elI6`F>v6{E6W*ho zD?8h4D>9S#umdLwlQrC?$^@A9k8q66$1kR0BGX}04{GDDdpn+q%`^Dpw@h#T@y`-n zVEuBxP0@bav;}7|IPADRp2GV*9Ry16c=CHP$5dOODv=y3)Y!taAKFj9bT7U<&&Xsi z-Dq@Byt`>2^4$;n5ARkPhpqcxY#s#ue77M9tEkbrx33W|F|SyAIQD$f+*M9uR44t% z=v#Z1Z2fnenpn%}9~g5>>GDr^ZK}|!aH~*yhTe$q-H>#j1S9L3C|~=c9Fdpgo0>8yvTyV$asJ|oO@JZ^brzk3Ub`%i(4e}dIN1v0gGsnnTE z;|?dV%%=UL8y9Q(SnN=#-Sh)Teiq!>H}-_=_RFRAq#l=bF?)zB2Z$@X`uAL99AIV+ zN3K3aSN5L-mMrE-dsvX$dg)Kwm7o6~d+!|-Ww)*SHo3`3NkY@2R-qe;3*pbF$@s33Ahj)aCyu>-`i(a)_*|OuiTJgP8PWU zbqn$K&EcY-;sWppnG&RJ%vX5PfnCA1V2{7xaX)PHtsRtA?XnJnS=!I*3elZ13gNH_ zfs)hlREF@jhd{w}0C%W>Xed;MOvpA=#3vLw0Ff96z6_zzZ6dvlT-areup7<67crl_ z|4un6b+B#j!V>tOR?I=+aT zzN9i7ARJh55N4L-3T}G(WC&ey9DQ)&Nip-WbR_5PXC=wW`IXPv`jVTC;!o3&5#>^n zjv)795I<}qXaEwsFJ+=V&M}AiFqruyI98BHEX68aJvLo~hyGxXzM(HfkB$zyO?w|t zd)X-CfiJwSI0KBp)JccYE@Zsu$S}r7kZ|-bt1^sNGHP+ms<}+2Z!TWp5ED(syP0eAg^HUpP^B0SZQ7ynVBwq1pa++1%V2dcN=kFal$o6Cs~r7@K40 z3vUD_Q23lAyD-^t0o-|LCg3Lc=0Xi<^8q(uCD(5v+us)+qnG#HF7Jm}PGnVHSVvAW zJ}TE^lBmJnIi7Il8fSnmmU{hnasX+TqNgLpio_) z=vRCrFaPFhzN$jr;G5D@XNpB9^Taz#8;i?#C(7IL<=s5re2mLi?aFIn%Nshr&8~dI z;!EJi`M@n|S^nGox8JLMzt`t}r|m4^tImIa{QZSi>HWvwMC`vMAb(Jwp}#<1rj(Tq zT>Jl0C@z8IgxJAj?JARy@RmPR1wVKJOI4~uRa$kX#Y6@ZcSvq^6$8f92v=2nrn=-z zMc|unkNn_0!PQmO)zyCRZd^5>Dm41RXY^_SRpFQTxn0aBm%f^wnfsZ-W2iNQ(gNrKfxdaNnl=XB%;NyR!caHB!YCQt40nLfNduemW=01%iv!b( zC-rKYEo$-gXEJFu@>{s#Yh#jWV{rACwHx~N>S#S%m-AY;6qvxU%(nPX;!EDf=Z3N+ zRzHs>U4oNah9-YTA!%bFt<&PI_f8o45v???zvS~0(=Bz< z#m&$|Dbd5P-9!Jbr@~)Q=u^nylaL_I$}@m6Q=LO&z(B6pN2${XHRyvX_CaMB7^eG} z*889W3~Y-1r*!&rI;*&9t8_D|c&Gbe>-|s+m4M>Fd7T013kHz{;(%DeK%IDv6rmbA zzzDbrmvshXS1V;}D_i_(u0O58xl=3i4XH>DL7S++fQ)*;kR)NC$qzn+8`9$&)(?PB z>J2}19DY|`ZtPU9vM(*ki8!C=IC>VZh zS@V={#BP1~<>m3{7z-v0hboSn>y1ZtQ$=?V0ZXT$zVXCb#^l=JRK*FD-b7fyL}cy6 zh3Sbn#>qs*NyVp=X#tZ?1(Pn_lTz!GUW`+|l2g}prhDa%sBldVD?C7+SGE|JYaf*j~-yNOQdIa`RD;QyCirPuLFOC+gU#rIuv|1rtFwi2+&{{n3vk$AXfo+nq*IjvSy|Ou(sR>v4s_XojOr`;MA-<(~H~F)2nM^go2yN?U zCQRAdYzt)U9;}!t($KrVjwK z*P)?Iz_)3HNp_6sZt=`|VIcemY@@;nUT3l~&$QN(xX~C0e4aMiH`WGWn~5gtVax(ti3y;q#eLwxK+P(dvgnbv9<1Q>o{-UE!x%( z+Pe1)_aF%O=);Z?3HSKI*3)NO&o1DeZ*IK2u=$FA+bn6*qIdHxGw$Yv-BH~BI_zLe z>44$c0sj5=^}DOKA2uwDHk|l(N~Cv^r4H%u9!@(QU=j~G_~{)sw@V;9-BLTj0Pv8s z%eeWe?=!x~9iAP8hrU=Yyt`aL!WRa?bxe-6m<})BSgGozmkz&NM>+!uqgEx#ex zXZMFBJL*rmW>0#Eq?0~=g71sosk4L;XTn$#AYQ{CDm&Mt5K#O?Ob~(MIZbj2_Y$ya zttYO{5;sUhn-H4zi=^FWq8iYFuebqQ&& zE|@nXedO7fTPT=}-DQq^RnOjEu8wVEXXT`W)ZJLlz0Og&i1{UBRBEbSnWf1g2GjU? zHf+A5E!?r#G{!a5G*Q$jpM`J9x+h!0;fYD>igiaeI6mj}rCqpFL_((Vb%CmfSWFqh zEigAU%krV(!|X^hg{+SD%FlWC`v*Dm_+%b*46!;qxO-a6nq9U0QU+a;Y!LrXblIrx z%=I`)RiQ5I;U^b)Tb7_ecDr3w1zWMS4Tq~MZzjH3&g#Y+9K4^Ybrfd!gPx{=NdI;b zI}Ms7#SkxHq_}8=q-5!Ziyap){tE~F|J?&{!U;SL@bOqvLk_`uNg z#MJD}>>L)oFppW9$1X2mRu?gAOPKZLzhecnzB;$Dwz#>rw6V6lwz{&iy1Kl&3O=r` z0v|E(w*LQ!)%CS?@V2q>cWkb0;(+7W^7i%$h@IV)-M!`ggXM$6<-^03!^73X!?nYM z^`pHF{MN?F#`-ZZCRSiwkogmgli+Sj+|ny@^I|pwVk+%nBN_ zh(^z&&{z}(gTkOubC}tA%|k)anAHQ!@)33kk6k3pFA)}&3Crum%}wGKj<~(~ zJ9ait_BM|}Pw!~sCvhjEKXq7-*Iw39{6X)>+?9nmi2#+~9#P03S@9r(`?11aH zy1j)1)@|Ec+uK{ayIV*5xRXO1;b?<^Un8C@5sxwdj5#8Hfq1Y)+*=`TuM%8^6DQba?ci;~0NPAOPM5h(iML1i-F;$I0&?9uh&5B~V{H{7^p^z{oN!{&V52#mZ=kd=~HPCbDdZvBY*!+awO2+-!@`iG;N5OtG0$CflYW(2n|R zbu3=uRxbJDpyKA9u$+=_nxoV5#|#}qZUwd~X0vixsQf|(!_HArCK9dpYg({3@|f8XJBo02q#iQ;#J)Lhp2fHxpK7>%b@=@@_KA(z zyXObfHdsXy&?K3y{W|MFVyu|KFeV=KrOI+1$b(jX1@`j~0np07r(`l8eOGBl47Bnq z7qKYX`Bf|4;>0Cv@imz3Li96T`}sKMLWRYYsEukss*t+AQo#Si26RB2 zsuwVlA1ZCCH)oDFGiYv{th7>my}liMZL?UW0y=ZKht>W z4+1Ao6c1-E^eXJmMLAQ8Wg?nHd%S#ia{_785Yw%<`g!j)-nD;e+;p<^gXviRJ*|eG z+WPjL>ig$!(z356k+jM9g;@HK*=|8>vBhpt2p(*o4~bn3Ju@rBXbmwiqq?_LQf+n`k$mdoW7M%7M>%^{A$@A3kc? zy8Mw=^MsF1!{z3l@<;eYtib7~ql}X8L)=0it1UErRk)x2!a;!H>4$;r+gl7DPSxt8 zCT14pxyFJ&KKTsekD_TK>I5OFE~n#9(f%^;zUUO_V0vEE=hx(eP=~1NDMHsYYW!@s z86Ldzk5jR|!$0#OH1qsRc(<1ZsvtB%)!uc$)2CcGz7ZDm#7nV--S6)v2`{rmrUqa* z--{jTTDbg+!sET&PTcIAknFGV?v#x6_$BSdaSqXfQZ@7I7T%yqBG(=SkZsc?4ac?RC$V+qKOM)iou4sYCh^Phy&s|6J5QL(WJ4$jP94`?8v zZ}0uQdaddn!SVq~Y!dxuugY4H!jKF?v9WPA3l(s|mhA0JLEW(aOk0uDLG|ob1-2eS zEp0#P=e~u)G(dH&T<7hnsT8;xu_>;9!!7PqG;u{r=ZN$(-uak&ZJk@%WgMGgv-R40 z#<#~=;HUe=NMYYri!6mSPne)L`5OV)MfX&_Cdrzzo392{*fE!G+3^@9nyqYwVC53I zTh*1fSg8V@Kvm<=*bc)U>&RYYqLATPUIf!8S!->CCx$#Eqd|>m*W0*owVIpHK)uPj zWd}tQly!ps3uQc5HFHJERdP-7UiC!&)&0oUuy?P7DIsA>1!YO= z3+9{niKg2JMP&u+;^!2`J2{|`SVwB>sJNa1rQ#~b+D(J9>gi3-FX=zDtn4PMyBCO` z>vg7A-*8qHV=N9*E2k}4nYkc$lD`!Ea>Tih+M=74+?t8}Ga3#WQ`4use$<3neGHhy zqbh%Xt-EPr9umYm*7AM3oq}k-?`q$*-E;W-+04fnt+<7e_lIqy0?Wi-_(im`N~c%P zM|=CJ9?h?^10R{TF2(cm>u&CIXTGrTM;YQp*lX8Y3KV)u0+mwKCn zpEd)H2X)8On`SiMY?!Kgo49*EynU2~;p{!+jGEHF6vz{NZnL~C&T{+GRgs%l`1Kn3 zX5x>awM%zy;J3W7yV-XgSAN|K9H}Pm74i4VzP@`rG3s=^s;ze8YyI(-A^M;J7%=`~ z_rRPX9sZ(GT#n+m8_XjfWtl0iWzE`+PZIHEzZ5rr5R>d@=}E`+!b;n}o;l24O4{x5 zs$0pp>p1q9|M>f;(t+)!BaZ&r$=sc~quvYn-8}xiwNaq@bR!&4YAMrI6}z1tb1O2VN-w4#y9)=<=PhV4+a3QOt=`@1fHiTh3vP{H}Lbq;>dBQ|ss*y13G50o3I=7#c%(5O2 zL?rD+T#Va1oS~kD!k(sv-ey+b=GmTpW1cNEULiKr*gIaD2(R-t7vCa0?7TfNTa-@^ zeaxgCt03;4Hr}djp8iYr`qFe-Ty7dr4}-BUaW<|iQLgJ|o};6#I|r`&S^jfox@uw8 z(;TmFnSH^yFwGeTE|>>mL+Al_daW%G`;iLA6|^l9g#AdnZxeLr9keiHb21iWKKNyl zC74_;m_iOVZWRoT38u9OrW+5YS+@1O1T&Ei0Zo$Aq9GhHA?!XOJQg7fw;293NwCiu z{xnIjWDI|rB-s9cHc7B4w6ZZ_@;PDH5?ZCBFlE|sESmO~QTT0(aDKT^O{?}*u}_#HHz=iqsoI9W&)%N-k_5&POA*1{+DT~6$W_E_uX z*e|qkHllG_8gX|m;_msxJ<5rD+#dIAIqoHGyryWpu136pMf^jbc;lRSllJ%*#N~K1 zQ5YauW1h!Ii^5_o!Z3|7*F|ATqT!gcvFb54!5X0em!QX;h|!3cGfFhHOz@G1P730C0i z>!ku0F=2UFba5(h5tB3yI^s&(uLvRIa(ON__EYuhWdNVBhw`xY;*7_3)DcL=Cmk8si;SkmnXly;hxKTq zq+z3dneXwLA2eY)4U})`;1K{lZA_b5e9l%=>_WH~hBd<#pH6)8UFE)ShIekp6JN$p z6O;g8{n9S;wI*Xy-x*cPtmu_Y?3TBdUe;bn)>lm*{uW9bO|cfTY%}R>H{)!bU|PVe z#;|7k`|^#lWVW;9glc9E>Cr}t!D3=_G%{g{n*7Pew8?$9)9td(M0)Au=IYzQ3h}vy zx!HdB4A4};gye-8(>`s;tf$MGW94hs%x}#-*Pfg2vzXs21{23`3&+O!gq0#)c^H6RlxP+QzbU5C z%A~a~Vwfy;j4fp8WB^@|2lB-netF#0C9@MHFh649Q_fNW`r`9%ODX+IY4S?zJ4z*w zi&+#(F98>_$qcTz(z@JY%iJ=m8 zx+Z6}s`R*~NUOR+q4rx|bsa!YSJT%tcEU0GHLZY8-T58USktvy(W}QHkNxT} z==xQ07};8#&yU)C{RV1=I$DQ%hAEnb;|8a`dNziJ)7Kl~^BT818~0Wl8F-tRwVQ}< zo5=i|YEzqLI-Ag|O^5W&$KuT^TFvWPVj|7rT-w)k5scIfRTA2aBXGucK+C9s`l6Z_ zwW@;}DA~H8D)Ft_xN6RGY6I3S=q*@Ma*I}1;}8m}gM#Ww)S2NL{fo~@2{jcfz=Hbt z1+^RNj-g_^t;BIZua^!j`4d!HQw+TC7y~pJ_r+VgI%#ZcC{;~=_1nXiQz-2ns7H^z zSI#!j4=G<&{kdIA83AYX>S`S5g9ar-`}^8Hb~X4mPzFg~48Pul>4yr2w~zV3Lf6`- z`jrAhWkv-qd^7%zy~)_--!)R))gIr4EujJE=>djrU%ggLBh4T5G&Thqh=bmL(v7*^ zgFQ>T;LsC_gX-X*OA-|x$34WcFDhc`%JRKjGBEStUO=!omJkry?mZ_1doA5Z`LvHp z5f&EL7b4zAJ>5rPsY_z%2Tc-oMVPHsKUYFOw<650uOCL}=U0R|O9Qs_fS@AGB^WeG zY6rv>VP3caX}-aWiZBlVmv$Vy#@K(Vc97MwpNBBW+1(FU9O9E45ON$k7cd}EJ0!Y3 zAVV0spff0|IDDmG@VZXF65|k$->B15 zD6)IRjWFUVIqLIt)IVS}sCG1TeKf*xII3VcW_mb|aU?-;B*}6lC1E73doY7>C`)H3 zCtxVAd#HeMxJYNXq;{~3F!)Vz=!fG_W$jQkVW?JdxISPQb93yBB+QG*H-JeQ6PAPp zOb=kl#xF_2!V(63ILA&Cruz7X1{9~&o=%S>42>mB>(ow9@eR)qrXTRlV5SG>p3XF{ z53khD{L&c#osiCg5g$%wY9|1mLN(S7F9yu?UhigM>c(D#{y|S;`>Fq+r?DabKu>Ql z^a|D4JTauDB!E4|SlvyxSUUQS0Q=6dz>I zrPXbvO*6zI%`}c_bM+DZO1jkI7WuO+j-V}Q(bk!I+>sOYSrU$9vQ5T}6G_?@yFo*H zcU>xJ9nQbQ!av9EymPB(gQk9`d}fEKU|spfj*B8p_3kc}^OjuDZdlD^aC4S?MM)C<5K2E+{D2nfg+Xfzsd zIY1r!51IoYH~?y6e}5m3#{*U|;A{Y}27q#a+U>!?0VtY-i30%En4FvhRdNgljlp1W zINbdFJiu$r%*-4f900sAfp7vw6F{LBH4A9&gkwDZ_!xfz-j9y<_xJuDURXFeIi8pp z-`m@zr{|gg2;{>9z~}(ATRi@VaB^~Run(pjaGRT8((r^p0K*Pi+K&$Q_jh)-j}8x) zmzO3cCkZD6{1F~ZL2Per?d|S@Z!$hUaeRC-J~07G`=wP%THJQ4`}6s9Ds>31M~?b#QpNw7d+O6w}jFINav+)D##6Sy^6QSX>0d zk8^YL(^J#y>ua+p)Da#(H$M-iAh8(C^3u}6{QTx7?(p#F`HOe+^NasAhVA~(Jd1w} z4;LqcNq=^+``OBk|8t%Hwm;Wzh_&@O_1_TdCvvzsjY#R2PM?nqGP~oT76UEq>{+@A zif*MRNB6S(LvH!N9z-WwkZaJVY9?rY%N@=$EPK;EahVqRyUx$2^*!GR60s)xwDWtx zpF02K$m%#4hG1Gz%{x5}>iqP7#m+5_(sEzbjV}J!>`vpW`tEas;W;l_zEk9VkQZ(Z zHP401`}D}m#~8&gFC8Ly-ciu=7`mG?j@+bUWD)42Zb$d6h>$`iz--QIXzFDVWPVfA{C^f|*SyM59YGU6=jFn`PI@Q0K zgg%W{VZ6pf<4Q%nc%^|t92sm?w>E-PV^EhpdEfG|g@xuVOA$SUDX#@b&PcMpuFU$N zMVXpO%<19O$NkuR-cR34?P-OShXz`H$i*4o;#E#JFdjK`4o$=IV~Azf<*puC3&Vr+ z3Fqk9PCXIGV$y4HrO-E9cO^<*;tlxHqTa=#N)5SjJj`>yOWLQ)lisnJ^HTPJ`y@nG z&uY+SeGxKa>$lQGFMGX4obiI8{?>O~;TAbIAjq(pKJ1=JdZj5}p7hwwU-nfs6*LmN z6?;9FGF1s*A6G2>Jea9UbH(_rugAQ`o9_7P@3VYNoo8DLJte|#*bl~?@bc#+>x%)QJb_d)?5czD4dkyZ($2JplF{pBNo?A8 zQ%a51?Rhki!&shgDj~>dmZX)0)XDfPK9OIEh6KIr$HJOJ$=IS1RN`(g@H`MQ_Hslp z;=U;q81>Me5A~?PBAK)iync*k7GJtL2V=w9U6WZQ`a*j6)#Q{`5sPmXr*yxX7^zM< zBB&S;E-df3`Jqr3pLkaYWmkrV%gMGl6S9H2Yr77rjG}a3o52e~$e2V1?lU8mp)gxD zu`h#hABZh}oTq7T2`1+FK#vxl?&mb!=eX}JL}{+PqoI3_?NlVRAS-l3BbaV(KBapt z>vU9frvy(rIh<-)jZf1+3}2>2$JoG3Ne-1Nr%vMvb`@&MnrW0PA2-qEZGFn`zRVj45pqYK z>dNhEgqswT{)k*cgoJS||D%@^_tap%nMMU~Z(`|goRQfpWXoWD#wq(Klj-4;%Xt2~ z88(LX@4tB=3Avwm9*_N6IUN~7SbX7VH}3xzhg*!=^6R4`qvpuJ@=`oj%n$60D-Eue zok0;HS8tZNFb+jP<%-Fk4qkKTC$f&}EK#Wps!D(cmMh*rL7v|5qQfJ^d>0}xV z*ICapib%V^msF@bD@#VDb@-Cm&5H6=v`a;_Br|gjfGM0mF!*t~=TmQK=gR9K_M|1U zv;K&Qtwt17QI<+3z?)8U7ePB|x!_`|=MDSZ#2)^#@Kd8*Ot%Y}Iv>F0EhqN})iikaot&d7L0N>d}4FNPcU=iL6Bb+dLw z;fLL|qQu>No!YmCqjr6v8TSgsr{6yPB>$~U@JfM~a?D}Ow@@>-Z?Nm9xn{3d&YP@u!pCy`H893&d!1DlA` z1@1cLXIY=E{;01{)!wX7Ftm*J;CPV&?NGB0Ey$S@ydf2->m(EYBIO60CM(sgWP^Ex z(JOI9oV824>uG(c`x%jIzg%YL1F09&_`9t?2)?_?!d(BOin)C^lT^s~INGkJz=<<` zQehQhWj}>xvJCvhw^1HaJ+mCRj#%W|EEreloi{o7v2(KN=B0o>E*!U|nA!3Psu@$Y ze5-?1+we|0nG(GFvBT_1dGM&ifHVvF21RzuMv0oPqcvoI*ZE-*-+L9; zKNcNN6~*Y52s>^%k|NNPi3dNE2%C$i9Lj{94_ck;$byqj=8JNURx|6iz7cOYY#hws z-!v&zr;!{sS7%OI%oNRAoZs*AC!Gv?k@oK}3hj@cfea+oomAJ8HV5l9_q{$G3OjCm zwI_UaBGK6U(tJI7gzUS9oR30kvLTPDUAD|zcD!6puJJ}T|Mj{=&gI&BW)y_|tMh>n zHIuR|jk?1j4b6vOD(-CiM>y)22xRk3vXAVRG z#>3NAR@BxwXljEqPc&69oF+uit>e-83e&GIOSDortpyOS8?Uv;Q(TBNkP0grLo4b> zaF5C;`a&dCnjTG`u*~^1H&YiGF)T04iX5Iy&h~=8 z8ydpHEyPQE?zEi$ib!bUkH2iZ1EDcNqCP<$UZ!U&{m#)6!vtxqjxora4PNt*flwh9esW>SAM)c$Imu0BKqv12%J(TTv=3_5lLf< zq*m7mSZ*?GX^fOQq*88zE%D`Iy*c+wjHmGidCEmkn*XAtf0%FNVVafrG)~&` zK4_Cvz2f;Hg{;(AU-9sbSedD}cK+U?@Y!Of=nbpz>!q)2FPfAYIUk;kpi#e~#b>+o zZJ%S4;+a2;Fvyj@3eLErc~vo1>eZUSNJK6uLE2MDbm(Hx;XX!95Osmt!_$my~*2>(?$oQ%wF z-<$G0oCQ6t1ZFSqW!G2w@>B~aEX9zX3 zzuq%f%UVul;Q7m`Oh$d(GzG%&1d*b{B;D}b>zrsqgZFuX-56EX)6&X*m#Uz=E@hGn ziSLz9vor*&y;eIDTHN{osjNqyDR}463yX$GgwTAtpK2yGI()%MaN+hVA=c}Rk;QEtay=p>T(E@P7>vtRy?~b zCRPNAMyNuBhIp3Z^7oh65(tbavJmPTmIk^j%32a>4@}h79e>6Yt3ae}@;1ucOuofv z$=4t$7!irXLlt`q#}71wIP6Wm2nFB6^_|e2hL)7e%L%5|+}ACe2hQ?l1m#{m!8Wrf z2wkkSrFNKWP;(TLH0XE2Tjo6tarme$XjZlHQ#6lZu05hIKCk*z@4KC1g=6}A4~v$& zHVPCcM+a-A_#HGdHat#2IP(k5ASPSWXmVXUx~=C<`h3AGS8*Z+gE%~o7eMBHb`cRYP%(%yXwvTB%Y$(vxV^ugZKUX zb}~IA*{JQB5~Fe>?R!o|i5E;a@aSHKsQqS$1M^Ka(=^)FJFMiU4ODPQeCtFslC=Us z!H}H9yJ&bjnFUV8fqOCYunc*M(jfRr9ESUbdizy2>o>0@_@HmwG-O_seu&LC6~1%r zro4%DaGXSnh$45pcC2+odbz16{j#QUsB~nSI~w-ZsGc>GEmVggQ#wMn<%Lj)t6oh8 za{x!Ki2TQZzA4{}j&hx64DLmanZ9O8kkipf432BRE;v#cqs3Ed9sfXE_`*x5u61IW zIJrZ=l){6wGe!n?hDTnaD`Z8H4@Z8IogP!BC2y-_wp6~=;J&Ezz(TtC-CgBy`EpyTP0^)>1A#C1X10+9Nkdk-E4K8+`C^j@1=eZ^WuqEmWk_#xE=CJ=wY+Qnlat2 zwur0%VGSSa3QgIzkJ>V9Iu^Q}c3oxU!erbmWVA}sL%DzLU3`|WK`x;@TBUg@``(^7 zjs6?ml-%K*+X0zI>!tUbo9<^ANUo`plm7<`)P00q=LpQl5&|TmWVIf1h zQObu1gjalqP(YxCbPN#XH;uUvCPOIwx7CM*y!5iXeGnu$?c+3+>&cJ?w+idi@6D5+ zlh4%f^t0{K>08}zzGTLx>wRd2ZVLFKxl#3L3sv?+@=~V%{Yy<9F{(6?6LQwAJ+K>( zM*cI^!vrD-IrZBS`0tJ$31sx2)c#jq13mLcS^%K}5*)~q|2exsUIW4NC&xi{1Hl92 z4Nx+Fi1&YT94HweT|lP){MGS)=|}bNZ}aaf@V|Topk2T7TEq%T3G$kW7mjdm@}eSx z!Xf%-hzq;8K#g}u>Q8TqOHOlK125G=1+S!@QolLZ96_#7p$)y4?CnEF-sc{ztgM?r zsln3#SYCG_a&B~&^$_F`C<8UQ4o`?E84ZLDaTvC&Xeihokcx`h-hU+3*OPX4!Ky-LMfhIM8!os9%ujJw z-WX;v3k37ooNfq(oNl4kQ|MELaFIWwJcNJUFlHpPhE6p+|3F0!rt@w)yui@Ggvh7N z+tQ0$J6S|vhA4MrGoymRDK4F=-6giuFZ1{0tvuCubPSox5&G0rETv?9-?Opb*Sg3I zImWy2O}8T1nNB5*DWhB#duyo?XG>ftSRv|&Ha>XS9u*(tg*Nm;_3>8-n^W>^k9-3e z4ZZ6HC>dPUZ<|6ewHJb*B%YALS({j;PG}JC$*C5Dou*1Y$Gb2piX>>UJqsB?0RMSTG6-OTZ#T zN5=_H27z^=wyqH@545x#s@J&aX_-Nd^t$4mwpW_}(iSTJ))xO=lKj8i>x4!?7{OM) zIQb?8IW^C9E;!imQxhPAGfxUe$a!-Ft1=n$SqPlv>^HX-_H(ihP%FS9qB?cn zxB>CPs`Do8kxUf>=taqcw8)Z&uze*_0#JL% zr9YVpA*Y622AQhj$;8k0E?ZAP&4-;TxdCVaDhl3f+OjSekzU@+6z(jLR8v&0YvXEee`Au?q21;ez^BihdUJhkxvIb=e2%Q8=OpB52Tt4q$(BCk`+Mi zkUH;?MxO!t4QWIGaCk^qUJ{8w`h7OHi$sbhk@88Tb`mL)G$2Wu5F{?h06ve0D=leE zm^3s%>bXeDRPwas(op0C#2)>-^pSongi$@xh$zYBK8dtM+K(VrtCC9Zkn(j&4W^_L zb&}(wxHwVLhzX(p2`N(nQ>E(R#0dC20NT@6BuJ{e+HRpvnoal(?fI!oB6^ZYDa2_t zQsqs;hykhJh(u~74T+O7?-4QbBvK=Zlt3bl17Z&V^#C#t5u-r*aU(8>m(=@+goZge zLN5rN2J{}l^YQRtCk+acNC4(DKqAGG_WektLgL(2Qo16Ev_U#@AuWF-t$ikyrjW*k zNEkjs|5Fk!i8S$oWNu1IQz8K~vuRaQ%roNLHIk=3$;*n=aDfzW;^D^X;X;SOyu*CE zK^heyk+7ug4pOX^g)s+d7EU6i{Wr^i|5`8iAE#{fx;!o*r4i55k27z8Gc9r z#(0FOG<2bU?kaBgDOl62`sl#xU8BU%Gk!6@y!*&{uLZfA3%y_w?FI-~j zOWr{;N~*kk3VFV|G~jiuLXQ#Q;x=ORERNdEeV?4{$EUEX36z1GevM>HN++*-KF$?H z?ObO~_FS3e`k)o0d=44ED!^wq#zlPKrhN6e%FA96s$TzgR%HhN6sI|MZd;*M7eiqe z5(y$?)vT#I#i4SG%~1Lghs5)<jj>QT|k`X>wAVmD20-tEFAC-CgoC9rDW^3OgP0 zM;+IXI#uzV8prLoPkyQp8dV7Os>D(aVypqt)0k+3Aig&xTA2~7-w?ihAlQ9ALE0Vp zxbH=TtfwR{*&7&v$lp^U7ZIL7hv*XX<>P3;qM2(%RT=oem`9JO{D-_ip3@L(lUB! z6}7U4T3)+Wa`rpIvD$$bp^7>yxdFocC!!qO~Z zd6uv`dxAqB?qUyi=MT0Q4{%F|Ys*KQtH+z`C% ze@+ur4*;fz1NLUhkPaMffU643sSl*Ig^Apgk!UYX>xhz8WQx@&184p(y20IWuu31VL?0H)`o`84qMc~p2c{{^2P>zyFr?USoBdF7oS zrzzHeK_Ev^Ls|KIm52C`q9#u(3>2naQH+pfbTgk`{jqiPwql*;0zU05Gp|42TaM_j zrDnXUn)BT?;~(dG!(77IT*rlH2LkSKU@VR_sQX6# zOEG@!eUY3s*N13_%GHG`;mxr+ieDdBIvx>eoC6Z_=lHJoY>#d6)qFwEW1ZCg2U}+y z)K=gAX-bQ0sZmOyr7ap#N{hR+c%e{=OM&7JrMNo*g1fsrgy2p{a3^?>;O>xap7(iw zyF0Uc<~wsT`RmM?$xP<{+}G#Ih?jEP7V2&ae?QIUQFrmpq}$5ii07%=-+PdU%s7tE z;(efJ%YGCh-%Wo8+~a;gYQ-%&qsEe-sH_}FrX!IOWZ!axPYb9=GlY=+@-fi(V^ubzL1a3o&^`9{4r%3hDxcG_A`(D%KP zVM&x^=%-c6|5ww)n9`?^)EW5{2b{0tyipXzlBoR=Ce@B}@{qCJoniW2JH#=HrTsQ( z-O#lB<8#c6Sc1p-s9D8=BXYfy ztYO~K#Oluw=?*(^$>8Sh&!`Vc>!+198VvVph+Ye1+U4YycTGG@fNyzKQ<=01nVk(t_!5nz_S3p3`UKf7B(2sDQ>8QPR~X`BL2sG!OlqWBmVFZeZ*L zQYeA4ALmgQSrno})e)-@M0*d=bu>)tHk-PUy7z;powk;A4$U;=3z6$;xGl(#_Ge!( z|J+f;pVI8Gb4Z_E<4UAAsnM&x$v|Jhj*ntIALlavm6n!Ft_FuX=CVE`l97dSM#p#O(sIFM1YM6~JRVOoOKA+KFmT3| zI2y6($;dR}Nqk$4UVnGA9;7*^tnIG-kY1t@E|RUR-TEny;oWojAE9B1=f!WYfl&d5 zobpLEr{WLCM&wPLsFH!^_aDx2DCqncCp*Rfa67B7;5JWv2&K`+G=Y4uH;=OjrNOxi zx^Xo|*^eTor8_A>?f8FMPiTPl_a6p|KHyn9rnzvGco@jx&wBhZYL&n9OIT*0HPxqR zX=;{-5t$=)npxC=ol6q0&G8rtJ4m1Y>SkX#4D_mgO-@g?MH4LQPp*r~4WxZWl_;x{ z;5i=miA+V=v1Gf7z=nu#o`Nit~#bLrWUK;883QPR?dqIxtk-tS^d?ti0K z|063Gmi#2aHq%&oVL(xz*fCS>T|RG=^HAmaWSUn`p8kQ%j3tdzd59_i(5E2~bj+14 z*21hmNu$j3eJUZLZky{<(@e9|InB=}7&cRh7aB@)HMIpozs&t))C^?7)a_*DwgGPC z1+RBs^edFx#|f0L^Yb(gdXzi#t%q-GUO=ZOUsKqjCEmDRV0ZKB439R0kCQK&H(#om zKK}p|fn2m42H3cvc4A>fhj~B#$>twWKnGFQmsG61Xrc5KekDNhaXmd?4UIk10^B6IZl!ez{XrbK7&?I zyHXkfo0?;Mh(C-q>BI9TT=$`aeqZdf_*~G8Of&;w>GnBgw`hRWbx+KqQJ#hS7Oe@i zFNNH`AnexcSrAQRPQG1{ox3?*0Zo4iqkTyXp!}?N4z}7^6WCK{LKy}hW_+rase#ZK z=}78majJxD%dhB@X!Ugi>mwt>Enl;_&3>`wUQAzDH-$UTP1IM_Rp?l-Iy500r;d4> zI#}(hzyXy=-w)@52c9Qx+88fEneJ#hhKR0>u%SH;A|4l0~ga% zc(ULA^&K89F%rT75V?BSK^}LU<_j!P&@?1K&PC6PwHCHftG;4U9Qa{!e$bh4mxrKC z{GGY77bK6S-%Jgi*EihJ%uD0aKNY)xB3t%R&>OnW<)Ehb2HGKn&C3KCgyJJU!>h$M z9({QPT(&wC;K=5+zR!UJTIrNQ_E)G4lFRkl+zcA$0s3O{B z@B%UJ;NVb5a2PLe^e`Aq9TLS01Yq0xnZ?q;A<2-C6kcH9VMr!*XcjLpNGUYmIkbQ< zxIHKsG!q)a3)G_wtL6=>;RWj0hCvd;8iPYpXTp-uA=%WR4C&B9bx>YmXa^Ip935II z9X3QAR$mx4<{Z|H4of8jbux!`n}Q01K?QxG@R{J%W6&^l_;_L1q-l6^aQG}?D6&61 zA03V^0Ie`bOp8VgnMP~|hiw%`q_s!vnGzllMh3l#oREq~_MRg?puAGXxl6_cA5g9`<90UVjt}_hQ6?^bCRH~H zzH267XeO$fgmf;Gd^;0mKtjcz^;kA*pCyC(Ivut8fOam6ZaeFGD}w==fuh30cKa}g zX5%Gi0?ac>t}{6XG9PMYePYdemYj9mm_-lGp{2=wvz_rSIa}O3n{6(e=sNqub>>IS zoF}e1yu=y&13C2DIU)lYV*I%*nz>(GGqHLEGH9+me-^g+M}aa=c`oa_W}cjFp1Nzc zCN!^kBu^Jgg6-_7=g3zy&o|kAV5XU8IhX(KFkg$c0Hdo}@H4c4x46J$u0Z6vz>2l- zi)^8cd7*r0;q&4`<$=OC+lA`HMeM9aA7qQT%!{Z)iv){{7zTq zSb=WD*{j+4e_4Ru#o3O81;ngC2jZ+%gMz25KqLN)TFnw6*Aj?qHWXSSIakuUo!w4T z%E@2aZJyKXT1t~#IuKecdR;m)P%NccHbq>b04s5>q!M?hV!Bah7M4c&qUv;v-86`2!z{hBi>$J; ziWXT#D*$YxtbTn%s=7?dz*dbCCt+=>2FPJZ5FB_)9KZpNnon9aTyj8_O2E4}lHW4{ z-1pYtChjE$}j3r3?!OOb=qtsN=kLA z-J}Kr^+s~_C{8@ipa&8Y?^Q?Ytp)JDc*k2eDV_Fmd~s`#46BvhsdXo*Q@*KI;lLZP zt*Zjp=^*QrC?ULR^&o+IlazW3x%b#cAp8J(2LYQi##(2$hRT`-_nlhLn}+#zQhzPT zsy!qKPBz2=3`atE+3=#+8e@?4reP0a*u+xcjjP9v884uDa*)6Tb|Uc-?eZ3&OS; zDbTdA(^9*%(+l9`-+pU^98j&5tHy!PA9Zv*1K`9-3 zVVx-@oz3u0wHKX*Bwf8)os90O2p6(`j&4cq?$3h2FR9&h%vIX+J$zCr&L4>k1bdC-dr_-ZrngnAoFta>z1F+Es4;ST!9GX%K0EC` zXFwmyfZTn)&vUmA<96HUi|C_@B@T4&4+iz4s)<4K{n%DDUO*jcl{{7(7H9?><(gBn&d9Lh|Hv-C<=m62vQ#SAfw5 zc}zd^-C(2qz?RbJ*)3d36t7V5tCqJ4Y0YS9DnRLQ^mezYTAy4+pPZ3wgt7&;DLU}= z-N=2N;Ws7^I6TPOZYMsGAw*Ng`O6w!kxi(G;;AT2L^qEmr;;nMOyQ*|E8dNbSdO9( zC!~~yP~X1-UjzBWhsNx&Kgdajmn7Adr07ageRlYuB{>>QPS`b(BWRDNoa8KoMOSK97o~KrGA;?aia8D8?{C3lj

moXBdA)3Le_?rtZ0Y!J{?uw|GJNT}W#N`=nPzbb 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 literal 0 HcmV?d00001 diff --git a/packages/plugins/field-colour/src/blocks/colourBlend.ts b/packages/plugins/field-colour/src/blocks/colourBlend.ts new file mode 100644 index 00000000000..78237c85a89 --- /dev/null +++ b/packages/plugins/field-colour/src/blocks/colourBlend.ts @@ -0,0 +1,294 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {Block, common as BlocklyCommon} from 'blockly/core'; +import { + JavascriptGenerator, + Order as JavascriptOrder, +} from 'blockly/javascript'; +import {DartGenerator, Order as DartOrder} from 'blockly/dart'; +import {LuaGenerator, Order as LuaOrder} from 'blockly/lua'; +import {PhpGenerator, Order as PhpOrder} from 'blockly/php'; +import {PythonGenerator, Order as PythonOrder} from 'blockly/python'; +import {registerFieldColour} from '../field_colour'; +import {Generators} from './generatorsType'; + +/** The name this block is registered under. */ +export const BLOCK_NAME = 'colour_blend'; + +// Block for blending two colours together. +const jsonDefinition = { + type: BLOCK_NAME, + message0: + '%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} ' + + '%1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3', + args0: [ + { + type: 'input_value', + name: 'COLOUR1', + check: 'Colour', + align: 'RIGHT', + }, + { + type: 'input_value', + name: 'COLOUR2', + check: 'Colour', + align: 'RIGHT', + }, + { + type: 'input_value', + name: 'RATIO', + check: 'Number', + align: 'RIGHT', + }, + ], + output: 'Colour', + helpUrl: '%{BKY_COLOUR_BLEND_HELPURL}', + style: 'colour_blocks', + tooltip: '%{BKY_COLOUR_BLEND_TOOLTIP}', +}; + +/** + * Javascript block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The JavascriptGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toJavascript( + block: Block, + generator: JavascriptGenerator, +): [string, JavascriptOrder] { + // Blend two colours together. + const colour1 = + generator.valueToCode(block, 'COLOUR1', JavascriptOrder.NONE) || + "'#000000'"; + const colour2 = + generator.valueToCode(block, 'COLOUR2', JavascriptOrder.NONE) || + "'#000000'"; + const ratio = + generator.valueToCode(block, 'RATIO', JavascriptOrder.NONE) || 0.5; + const functionName = generator.provideFunction_( + 'colourBlend', + ` +function ${generator.FUNCTION_NAME_PLACEHOLDER_}(c1, c2, ratio) { + ratio = Math.max(Math.min(Number(ratio), 1), 0); + var r1 = parseInt(c1.substring(1, 3), 16); + var g1 = parseInt(c1.substring(3, 5), 16); + var b1 = parseInt(c1.substring(5, 7), 16); + var r2 = parseInt(c2.substring(1, 3), 16); + var g2 = parseInt(c2.substring(3, 5), 16); + var b2 = parseInt(c2.substring(5, 7), 16); + var r = Math.round(r1 * (1 - ratio) + r2 * ratio); + var g = Math.round(g1 * (1 - ratio) + g2 * ratio); + var b = Math.round(b1 * (1 - ratio) + b2 * ratio); + r = ('0' + (r || 0).toString(16)).slice(-2); + g = ('0' + (g || 0).toString(16)).slice(-2); + b = ('0' + (b || 0).toString(16)).slice(-2); + return '#' + r + g + b; +} +`, + ); + const code = `${functionName}(${colour1}, ${colour2}, ${ratio})`; + return [code, JavascriptOrder.FUNCTION_CALL]; +} + +/** + * Dart block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The DartGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toDart( + block: Block, + generator: DartGenerator, +): [string, DartOrder] { + // Blend two colours together. + const colour1 = + generator.valueToCode(block, 'COLOUR1', DartOrder.NONE) || "'#000000'"; + const colour2 = + generator.valueToCode(block, 'COLOUR2', DartOrder.NONE) || "'#000000'"; + const ratio = generator.valueToCode(block, 'RATIO', DartOrder.NONE) || 0.5; + + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (generator as any).definitions_['import_dart_math'] = + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'colour_blend', + ` +String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String c1, String c2, num ratio) { + ratio = Math.max(Math.min(ratio, 1), 0); + int r1 = int.parse('0x\${c1.substring(1, 3)}'); + int g1 = int.parse('0x\${c1.substring(3, 5)}'); + int b1 = int.parse('0x\${c1.substring(5, 7)}'); + int r2 = int.parse('0x\${c2.substring(1, 3)}'); + int g2 = int.parse('0x\${c2.substring(3, 5)}'); + int b2 = int.parse('0x\${c2.substring(5, 7)}'); + num rn = (r1 * (1 - ratio) + r2 * ratio).round(); + String rs = rn.toInt().toRadixString(16); + num gn = (g1 * (1 - ratio) + g2 * ratio).round(); + String gs = gn.toInt().toRadixString(16); + num bn = (b1 * (1 - ratio) + b2 * ratio).round(); + String bs = bn.toInt().toRadixString(16); + rs = '0$rs'; + rs = rs.substring(rs.length - 2); + gs = '0$gs'; + gs = gs.substring(gs.length - 2); + bs = '0$bs'; + bs = bs.substring(bs.length - 2); + return '#$rs$gs$bs'; +} +`, + ); + const code = `${functionName}(${colour1}, ${colour2}, ${ratio})`; + return [code, DartOrder.UNARY_POSTFIX]; +} + +/** + * Lua generator definition. + * + * @param block The Block instance to generate code for. + * @param generator The LuaGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toLua( + block: Block, + generator: LuaGenerator, +): [string, LuaOrder] { + // Blend two colours together. + const functionName = generator.provideFunction_( + 'colour_blend', + ` +function ${generator.FUNCTION_NAME_PLACEHOLDER_}(colour1, colour2, ratio) + local r1 = tonumber(string.sub(colour1, 2, 3), 16) + local r2 = tonumber(string.sub(colour2, 2, 3), 16) + local g1 = tonumber(string.sub(colour1, 4, 5), 16) + local g2 = tonumber(string.sub(colour2, 4, 5), 16) + local b1 = tonumber(string.sub(colour1, 6, 7), 16) + local b2 = tonumber(string.sub(colour2, 6, 7), 16) + local ratio = math.min(1, math.max(0, ratio)) + local r = math.floor(r1 * (1 - ratio) + r2 * ratio + .5) + local g = math.floor(g1 * (1 - ratio) + g2 * ratio + .5) + local b = math.floor(b1 * (1 - ratio) + b2 * ratio + .5) + return string.format("#%02x%02x%02x", r, g, b) +end +`, + ); + const colour1 = + generator.valueToCode(block, 'COLOUR1', LuaOrder.NONE) || "'#000000'"; + const colour2 = + generator.valueToCode(block, 'COLOUR2', LuaOrder.NONE) || "'#000000'"; + const ratio = generator.valueToCode(block, 'RATIO', LuaOrder.NONE) || 0; + const code = `${functionName}(${colour1}, ${colour2}, ${ratio})`; + return [code, LuaOrder.HIGH]; +} + +/** + * PHP generator definition. + * + * @param block The Block instance to generate code for. + * @param generator The PhpGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPhp( + block: Block, + generator: PhpGenerator, +): [string, PhpOrder] { + // Blend two colours together. + const colour1 = + generator.valueToCode(block, 'COLOUR1', PhpOrder.NONE) || "'#000000'"; + const colour2 = + generator.valueToCode(block, 'COLOUR2', PhpOrder.NONE) || "'#000000'"; + const ratio = generator.valueToCode(block, 'RATIO', PhpOrder.NONE) || 0.5; + const functionName = generator.provideFunction_( + 'colour_blend', + ` +function ${generator.FUNCTION_NAME_PLACEHOLDER_}($c1, $c2, $ratio) { + $ratio = max(min($ratio, 1), 0); + $r1 = hexdec(substr($c1, 1, 2)); + $g1 = hexdec(substr($c1, 3, 2)); + $b1 = hexdec(substr($c1, 5, 2)); + $r2 = hexdec(substr($c2, 1, 2)); + $g2 = hexdec(substr($c2, 3, 2)); + $b2 = hexdec(substr($c2, 5, 2)); + $r = round($r1 * (1 - $ratio) + $r2 * $ratio); + $g = round($g1 * (1 - $ratio) + $g2 * $ratio); + $b = round($b1 * (1 - $ratio) + $b2 * $ratio); + $hex = '#'; + $hex .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT); + return $hex; +} +`, + ); + const code = `${functionName}(${colour1}, ${colour2}, ${ratio})`; + return [code, PhpOrder.FUNCTION_CALL]; +} + +/** + * Python generator definition. + * + * @param block The Block instance to generate code for. + * @param generator The PythonGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPython( + block: Block, + generator: PythonGenerator, +): [string, PythonOrder] { + // Blend two colours together. + const functionName = generator.provideFunction_( + 'colour_blend', + ` +def ${generator.FUNCTION_NAME_PLACEHOLDER_}(colour1, colour2, ratio): + r1, r2 = int(colour1[1:3], 16), int(colour2[1:3], 16) + g1, g2 = int(colour1[3:5], 16), int(colour2[3:5], 16) + b1, b2 = int(colour1[5:7], 16), int(colour2[5:7], 16) + ratio = min(1, max(0, ratio)) + r = round(r1 * (1 - ratio) + r2 * ratio) + g = round(g1 * (1 - ratio) + g2 * ratio) + b = round(b1 * (1 - ratio) + b2 * ratio) + return '#%02x%02x%02x' % (r, g, b) +`, + ); + const colour1 = + generator.valueToCode(block, 'COLOUR1', PythonOrder.NONE) || "'#000000'"; + const colour2 = + generator.valueToCode(block, 'COLOUR2', PythonOrder.NONE) || "'#000000'"; + const ratio = generator.valueToCode(block, 'RATIO', PythonOrder.NONE) || 0; + const code = `${functionName}(${colour1}, ${colour2}, ${ratio})`; + return [code, PythonOrder.FUNCTION_CALL]; +} + +const definitionsDict = BlocklyCommon.createBlockDefinitionsFromJsonArray([ + jsonDefinition, +]); + +/** The colour_blend BlockDefinition. */ +export const blockDefinition = definitionsDict[BLOCK_NAME]; + +/** + * Install the `colour_blend` block and all of its dependencies. + * + * @param gens The CodeGenerators to install per-block + * generators on. + */ +export function installBlock(gens: Generators = {}) { + registerFieldColour(); + BlocklyCommon.defineBlocks(definitionsDict); + if (gens.javascript) gens.javascript.forBlock[BLOCK_NAME] = toJavascript; + if (gens.dart) { + gens.dart.forBlock[BLOCK_NAME] = toDart; + gens.dart.addReservedWords('Math'); + } + if (gens.lua) gens.lua.forBlock[BLOCK_NAME] = toLua; + if (gens.php) gens.php.forBlock[BLOCK_NAME] = toPhp; + if (gens.python) gens.python.forBlock[BLOCK_NAME] = toPython; +} diff --git a/packages/plugins/field-colour/src/blocks/colourPicker.ts b/packages/plugins/field-colour/src/blocks/colourPicker.ts new file mode 100644 index 00000000000..23c6fcc95ef --- /dev/null +++ b/packages/plugins/field-colour/src/blocks/colourPicker.ts @@ -0,0 +1,141 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {Block, common as BlocklyCommon} from 'blockly/core'; +import { + JavascriptGenerator, + Order as JavascriptOrder, +} from 'blockly/javascript'; +import {DartGenerator, Order as DartOrder} from 'blockly/dart'; +import {LuaGenerator, Order as LuaOrder} from 'blockly/lua'; +import {PhpGenerator, Order as PhpOrder} from 'blockly/php'; +import {PythonGenerator, Order as PythonOrder} from 'blockly/python'; +import {registerFieldColour} from '../field_colour'; +import {Generators} from './generatorsType'; + +/** The name this block is registered under. */ +export const BLOCK_NAME = 'colour_picker'; + +// Block for colour picker. +const jsonDefinition = { + type: BLOCK_NAME, + message0: '%1', + args0: [ + { + type: 'field_colour', + name: 'COLOUR', + colour: '#ff0000', + }, + ], + output: 'Colour', + helpUrl: '%{BKY_COLOUR_PICKER_HELPURL}', + style: 'colour_blocks', + tooltip: '%{BKY_COLOUR_PICKER_TOOLTIP}', + extensions: ['parent_tooltip_when_inline'], +}; + +/** + * Javascript block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The JavascriptGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toJavascript( + block: Block, + generator: JavascriptGenerator, +): [string, JavascriptOrder] { + // Colour picker. + const code = generator.quote_(block.getFieldValue('COLOUR')); + return [code, JavascriptOrder.ATOMIC]; +} + +/** + * Dart block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The DartGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toDart( + block: Block, + generator: DartGenerator, +): [string, DartOrder] { + // Colour picker. + const code = generator.quote_(block.getFieldValue('COLOUR')); + return [code, DartOrder.ATOMIC]; +} + +/** + * Lua block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The LuaGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toLua( + block: Block, + generator: LuaGenerator, +): [string, LuaOrder] { + // Colour picker. + const code = generator.quote_(block.getFieldValue('COLOUR')); + return [code, LuaOrder.ATOMIC]; +} + +/** + * PHP block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The PhpGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPhp( + block: Block, + generator: PhpGenerator, +): [string, PhpOrder] { + // Colour picker. + const code = generator.quote_(block.getFieldValue('COLOUR')); + return [code, PhpOrder.ATOMIC]; +} + +/** + * Python block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The PythonGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPython( + block: Block, + generator: PythonGenerator, +): [string, PythonOrder] { + // Colour picker. + const code = generator.quote_(block.getFieldValue('COLOUR')); + return [code, PythonOrder.ATOMIC]; +} + +const definitionsDict = BlocklyCommon.createBlockDefinitionsFromJsonArray([ + jsonDefinition, +]); + +/** The colour_picker BlockDefinition. */ +export const blockDefinition = definitionsDict[BLOCK_NAME]; + +/** + * Install the `colour_picker` block and all of its dependencies. + * + * @param gens The CodeGenerators to install per-block + * generators on. + */ +export function installBlock(gens: Generators = {}) { + registerFieldColour(); + BlocklyCommon.defineBlocks(definitionsDict); + if (gens.javascript) gens.javascript.forBlock[BLOCK_NAME] = toJavascript; + if (gens.dart) gens.dart.forBlock[BLOCK_NAME] = toDart; + if (gens.lua) gens.lua.forBlock[BLOCK_NAME] = toLua; + if (gens.php) gens.php.forBlock[BLOCK_NAME] = toPhp; + if (gens.python) gens.python.forBlock[BLOCK_NAME] = toPython; +} diff --git a/packages/plugins/field-colour/src/blocks/colourRandom.ts b/packages/plugins/field-colour/src/blocks/colourRandom.ts new file mode 100644 index 00000000000..687ab02168d --- /dev/null +++ b/packages/plugins/field-colour/src/blocks/colourRandom.ts @@ -0,0 +1,171 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {Block, common as BlocklyCommon} from 'blockly/core'; +import { + JavascriptGenerator, + Order as JavascriptOrder, +} from 'blockly/javascript'; +import {DartGenerator, Order as DartOrder} from 'blockly/dart'; +import {LuaGenerator, Order as LuaOrder} from 'blockly/lua'; +import {PhpGenerator, Order as PhpOrder} from 'blockly/php'; +import {PythonGenerator, Order as PythonOrder} from 'blockly/python'; +import {registerFieldColour} from '../field_colour'; +import {Generators} from './generatorsType'; + +/** The name this block is registered under. */ +export const BLOCK_NAME = 'colour_random'; + +// Block for random colour. +const jsonDefinition = { + type: BLOCK_NAME, + message0: '%{BKY_COLOUR_RANDOM_TITLE}', + output: 'Colour', + helpUrl: '%{BKY_COLOUR_RANDOM_HELPURL}', + style: 'colour_blocks', + tooltip: '%{BKY_COLOUR_RANDOM_TOOLTIP}', +}; + +/** + * Javascript block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The JavascriptGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toJavascript( + block: Block, + generator: JavascriptGenerator, +): [string, JavascriptOrder] { + // Generate a random colour. + const functionName = generator.provideFunction_( + 'colourRandom', + ` +function ${generator.FUNCTION_NAME_PLACEHOLDER_}() { + var num = Math.floor(Math.random() * 0x1000000); + return '#' + ('00000' + num.toString(16)).substr(-6); +} +`, + ); + const code = functionName + '()'; + return [code, JavascriptOrder.FUNCTION_CALL]; +} + +/** + * Dart block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The DartGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toDart( + block: Block, + generator: DartGenerator, +): [string, DartOrder] { + // Generate a random colour. + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (generator as any).definitions_['import_dart_math'] = + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'colour_random', + ` +String ${generator.FUNCTION_NAME_PLACEHOLDER_}() { + String hex = '0123456789abcdef'; + var rnd = new Math.Random(); + return '#\${hex[rnd.nextInt(16)]}\${hex[rnd.nextInt(16)]}' + '\${hex[rnd.nextInt(16)]}\${hex[rnd.nextInt(16)]}' + '\${hex[rnd.nextInt(16)]}\${hex[rnd.nextInt(16)]}'; +} +`, + ); + const code = functionName + '()'; + return [code, DartOrder.UNARY_POSTFIX]; +} + +/** + * Lua block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The LuaGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toLua( + block: Block, + generator: LuaGenerator, +): [string, LuaOrder] { + // Generate a random colour. + const code = 'string.format("#%06x", math.random(0, 2^24 - 1))'; + return [code, LuaOrder.HIGH]; +} + +/** + * PHP block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The PhpGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPhp( + block: Block, + generator: PhpGenerator, +): [string, PhpOrder] { + // Generate a random colour. + const functionName = generator.provideFunction_( + 'colour_random', + ` +function ${generator.FUNCTION_NAME_PLACEHOLDER_}() { + return '#' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), 6, '0', STR_PAD_LEFT); +} +`, + ); + const code = functionName + '()'; + return [code, PhpOrder.FUNCTION_CALL]; +} + +/** + * Python block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The PythonGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPython( + block: Block, + generator: PythonGenerator, +): [string, PythonOrder] { + // Generate a random colour. + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (generator as any).definitions_['import_random'] = 'import random'; + const code = "'#%06x' % random.randint(0, 2**24 - 1)"; + return [code, PythonOrder.FUNCTION_CALL]; +} + +const definitionsDict = BlocklyCommon.createBlockDefinitionsFromJsonArray([ + jsonDefinition, +]); + +/** The colour_random BlockDefinition. */ +export const blockDefinition = definitionsDict[BLOCK_NAME]; + +/** + * Install the `colour_picker` block and all of its dependencies. + * + * @param gens The CodeGenerators to install per-block + * generators on. + */ +export function installBlock(gens: Generators = {}) { + registerFieldColour(); + BlocklyCommon.defineBlocks(definitionsDict); + if (gens.javascript) gens.javascript.forBlock[BLOCK_NAME] = toJavascript; + if (gens.dart) gens.dart.forBlock[BLOCK_NAME] = toDart; + if (gens.lua) gens.lua.forBlock[BLOCK_NAME] = toLua; + if (gens.php) gens.php.forBlock[BLOCK_NAME] = toPhp; + if (gens.python) gens.python.forBlock[BLOCK_NAME] = toPython; +} diff --git a/packages/plugins/field-colour/src/blocks/colourRgb.ts b/packages/plugins/field-colour/src/blocks/colourRgb.ts new file mode 100644 index 00000000000..46ee55045aa --- /dev/null +++ b/packages/plugins/field-colour/src/blocks/colourRgb.ts @@ -0,0 +1,249 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {Block, common as BlocklyCommon} from 'blockly/core'; +import { + JavascriptGenerator, + Order as JavascriptOrder, +} from 'blockly/javascript'; +import {DartGenerator, Order as DartOrder} from 'blockly/dart'; +import {LuaGenerator, Order as LuaOrder} from 'blockly/lua'; +import {PhpGenerator, Order as PhpOrder} from 'blockly/php'; +import {PythonGenerator, Order as PythonOrder} from 'blockly/python'; +import {registerFieldColour} from '../field_colour'; +import {Generators} from './generatorsType'; + +/** The name this block is registered under. */ +export const BLOCK_NAME = 'colour_rgb'; + +// Block for composing a colour from RGB components. +const jsonDefinition = { + type: BLOCK_NAME, + message0: + '%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3', + args0: [ + { + type: 'input_value', + name: 'RED', + check: 'Number', + align: 'RIGHT', + }, + { + type: 'input_value', + name: 'GREEN', + check: 'Number', + align: 'RIGHT', + }, + { + type: 'input_value', + name: 'BLUE', + check: 'Number', + align: 'RIGHT', + }, + ], + output: 'Colour', + helpUrl: '%{BKY_COLOUR_RGB_HELPURL}', + style: 'colour_blocks', + tooltip: '%{BKY_COLOUR_RGB_TOOLTIP}', +}; + +/** + * Javascript block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The JavascriptGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toJavascript( + block: Block, + generator: JavascriptGenerator, +): [string, JavascriptOrder] { + // Compose a colour from RGB components expressed as percentages. + const red = generator.valueToCode(block, 'RED', JavascriptOrder.NONE) || 0; + const green = + generator.valueToCode(block, 'GREEN', JavascriptOrder.NONE) || 0; + const blue = generator.valueToCode(block, 'BLUE', JavascriptOrder.NONE) || 0; + const functionName = generator.provideFunction_( + 'colourRgb', + ` +function ${generator.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) { + r = Math.max(Math.min(Number(r), 100), 0) * 2.55; + g = Math.max(Math.min(Number(g), 100), 0) * 2.55; + b = Math.max(Math.min(Number(b), 100), 0) * 2.55; + r = ('0' + (Math.round(r) || 0).toString(16)).slice(-2); + g = ('0' + (Math.round(g) || 0).toString(16)).slice(-2); + b = ('0' + (Math.round(b) || 0).toString(16)).slice(-2); + return '#' + r + g + b; +} +`, + ); + const code = `${functionName}(${red}, ${green}, ${blue})`; + return [code, JavascriptOrder.FUNCTION_CALL]; +} + +/** + * Dart block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The DartGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toDart( + block: Block, + generator: DartGenerator, +): [string, DartOrder] { + // Compose a colour from RGB components expressed as percentages. + const red = generator.valueToCode(block, 'RED', DartOrder.NONE) || 0; + const green = generator.valueToCode(block, 'GREEN', DartOrder.NONE) || 0; + const blue = generator.valueToCode(block, 'BLUE', DartOrder.NONE) || 0; + + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (generator as any).definitions_['import_dart_math'] = + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'colour_rgb', + ` +String ${generator.FUNCTION_NAME_PLACEHOLDER_}(num r, num g, num b) { + num rn = (Math.max(Math.min(r, 100), 0) * 2.55).round(); + String rs = rn.toInt().toRadixString(16); + rs = '0$rs'; + rs = rs.substring(rs.length - 2); + num gn = (Math.max(Math.min(g, 100), 0) * 2.55).round(); + String gs = gn.toInt().toRadixString(16); + gs = '0$gs'; + gs = gs.substring(gs.length - 2); + num bn = (Math.max(Math.min(b, 100), 0) * 2.55).round(); + String bs = bn.toInt().toRadixString(16); + bs = '0$bs'; + bs = bs.substring(bs.length - 2); + return '#$rs$gs$bs'; +} +`, + ); + const code = `${functionName}(${red}, ${green}, ${blue})`; + return [code, DartOrder.UNARY_POSTFIX]; +} + +/** + * Lua block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The LuaGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toLua( + block: Block, + generator: LuaGenerator, +): [string, LuaOrder] { + // Compose a colour from RGB components expressed as percentages. + const functionName = generator.provideFunction_( + 'colour_rgb', + ` +function ${generator.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) + r = math.floor(math.min(100, math.max(0, r)) * 2.55 + .5) + g = math.floor(math.min(100, math.max(0, g)) * 2.55 + .5) + b = math.floor(math.min(100, math.max(0, b)) * 2.55 + .5) + return string.format("#%02x%02x%02x", r, g, b) +end +`, + ); + const red = generator.valueToCode(block, 'RED', LuaOrder.NONE) || 0; + const green = generator.valueToCode(block, 'GREEN', LuaOrder.NONE) || 0; + const blue = generator.valueToCode(block, 'BLUE', LuaOrder.NONE) || 0; + const code = `${functionName}(${red}, ${green}, ${blue})`; + return [code, LuaOrder.HIGH]; +} + +/** + * PHP block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The PhpGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPhp( + block: Block, + generator: PhpGenerator, +): [string, PhpOrder] { + // Compose a colour from RGB components expressed as percentages. + const red = generator.valueToCode(block, 'RED', PhpOrder.NONE) || 0; + const green = generator.valueToCode(block, 'GREEN', PhpOrder.NONE) || 0; + const blue = generator.valueToCode(block, 'BLUE', PhpOrder.NONE) || 0; + const functionName = generator.provideFunction_( + 'colour_rgb', + ` +function ${generator.FUNCTION_NAME_PLACEHOLDER_}($r, $g, $b) { + $r = round(max(min($r, 100), 0) * 2.55); + $g = round(max(min($g, 100), 0) * 2.55); + $b = round(max(min($b, 100), 0) * 2.55); + $hex = '#'; + $hex .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT); + return $hex; +} +`, + ); + const code = `${functionName}(${red}, ${green}, ${blue})`; + return [code, PhpOrder.FUNCTION_CALL]; +} + +/** + * Python block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The PythonGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPython( + block: Block, + generator: PythonGenerator, +): [string, PythonOrder] { + // Compose a colour from RGB components expressed as percentages. + const functionName = generator.provideFunction_( + 'colour_rgb', + ` +def ${generator.FUNCTION_NAME_PLACEHOLDER_}(r, g, b): + r = round(min(100, max(0, r)) * 2.55) + g = round(min(100, max(0, g)) * 2.55) + b = round(min(100, max(0, b)) * 2.55) + return '#%02x%02x%02x' % (r, g, b) +`, + ); + const r = generator.valueToCode(block, 'RED', PythonOrder.NONE) || 0; + const g = generator.valueToCode(block, 'GREEN', PythonOrder.NONE) || 0; + const b = generator.valueToCode(block, 'BLUE', PythonOrder.NONE) || 0; + const code = functionName + '(' + r + ', ' + g + ', ' + b + ')'; + return [code, PythonOrder.FUNCTION_CALL]; +} + +const definitionsDict = BlocklyCommon.createBlockDefinitionsFromJsonArray([ + jsonDefinition, +]); + +/** The colour_rgb BlockDefinition. */ +export const blockDefinition = definitionsDict[BLOCK_NAME]; + +/** + * Install the `colour_rgb` block and all of its dependencies. + * + * @param gens The CodeGenerators to install per-block + * generators on. + */ +export function installBlock(gens: Generators = {}) { + registerFieldColour(); + BlocklyCommon.defineBlocks(definitionsDict); + if (gens.javascript) gens.javascript.forBlock[BLOCK_NAME] = toJavascript; + if (gens.dart) { + gens.dart.forBlock[BLOCK_NAME] = toDart; + gens.dart.addReservedWords('Math'); + } + if (gens.lua) gens.lua.forBlock[BLOCK_NAME] = toLua; + if (gens.php) gens.php.forBlock[BLOCK_NAME] = toPhp; + if (gens.python) gens.python.forBlock[BLOCK_NAME] = toPython; +} diff --git a/packages/plugins/field-colour/src/blocks/generatorsType.ts b/packages/plugins/field-colour/src/blocks/generatorsType.ts new file mode 100644 index 00000000000..e9d2df4609c --- /dev/null +++ b/packages/plugins/field-colour/src/blocks/generatorsType.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type {JavascriptGenerator} from 'blockly/javascript'; +import type {DartGenerator} from 'blockly/dart'; +import type {LuaGenerator} from 'blockly/lua'; +import type {PhpGenerator} from 'blockly/php'; +import type {PythonGenerator} from 'blockly/python'; + +/** + * An object containing zero or more generators. This is passed + * to block installation functions so that they may install + * per-block generators on any languages they support. + */ +export interface Generators { + javascript?: JavascriptGenerator; + dart?: DartGenerator; + lua?: LuaGenerator; + php?: PhpGenerator; + python?: PythonGenerator; +} diff --git a/packages/plugins/field-colour/src/field_colour.ts b/packages/plugins/field-colour/src/field_colour.ts new file mode 100644 index 00000000000..adb3396dec7 --- /dev/null +++ b/packages/plugins/field-colour/src/field_colour.ts @@ -0,0 +1,524 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Colour input field. + */ + +import * as Blockly from 'blockly/core'; +import { + FieldGridDropdown, + FieldGridDropdownConfig, + FieldGridDropdownFromJsonConfig, +} from '@blockly/field-grid-dropdown'; + +/* eslint-disable @typescript-eslint/naming-convention */ +/** + * An array of colour strings for the palette. + * Copied from goog.ui.ColorPicker.SIMPLE_GRID_COLORS + */ +const DEFAULT_COLOURS: Record = { + // greys + '#ffffff': 'White', + '#cccccc': 'Light Grey', + '#c0c0c0': 'Silver', + '#999999': 'Grey', + '#666666': 'Dark Grey', + '#333333': 'Charcoal', + '#000000': 'Black', + // reds + '#ffcccc': 'Pale Red', + '#ff6666': 'Light Red', + '#ff0000': 'Red', + '#cc0000': 'Dark Red', + '#990000': 'Darker Red', + '#660000': 'Maroon', + '#330000': 'Darkest Red', + // oranges + '#ffcc99': 'Peach', + '#ff9966': 'Light Orange', + '#ff9900': 'Orange', + '#ff6600': 'Bright Orange', + '#cc6600': 'Dark Orange', + '#993300': 'Rust', + '#663300': 'Brown', + // yellows + '#ffff99': 'Light Yellow', + '#ffff66': 'Pale Yellow', + '#ffcc66': 'Light Gold', + '#ffcc33': 'Gold', + '#cc9933': 'Goldenrod', + '#996633': 'Light Brown', + '#663333': 'Dark Brown', + // olives + '#ffffcc': 'Cream', + '#ffff33': 'Bright Yellow', + '#ffff00': 'Yellow', + '#ffcc00': 'Amber', + '#999900': 'Olive', + '#666600': 'Dark Olive', + '#333300': 'Darkest Olive', + // greens + '#99ff99': 'Light Green', + '#66ff99': 'Mint', + '#33ff33': 'Bright Green', + '#33cc00': 'Green', + '#009900': 'Dark Green', + '#006600': 'Darker Green', + '#003300': 'Darkest Green', + // turquoises + '#99ffff': 'Pale Cyan', + '#33ffff': 'Bright Cyan', + '#66cccc': 'Light Teal', + '#00cccc': 'Turquoise', + '#339999': 'Teal', + '#336666': 'Dark Teal', + '#003333': 'Darkest Teal', + // blues + '#ccffff': 'Pale Aqua', + '#66ffff': 'Light Cyan', + '#33ccff': 'Sky Blue', + '#3366ff': 'Blue', + '#3333ff': 'Bright Blue', + '#000099': 'Dark Blue', + '#000066': 'Navy', + // purples + '#ccccff': 'Pale Periwinkle', + '#9999ff': 'Periwinkle', + '#6666cc': 'Slate Blue', + '#6633ff': 'Violet', + '#6600cc': 'Purple', + '#333399': 'Indigo', + '#330099': 'Dark Indigo', + // violets + '#ffccff': 'Pale Magenta', + '#ff99ff': 'Light Magenta', + '#cc66cc': 'Orchid', + '#cc33cc': 'Magenta', + '#993399': 'Dark Magenta', + '#663366': 'Plum', + '#330033': 'Darkest Purple', +}; +/* eslint-enable @typescript-eslint/naming-convention */ + +/** + * Class for a colour input field. + */ +export class FieldColour extends FieldGridDropdown { + /** + * Used to tell if the field needs to be rendered the next time the block is + * rendered. Colour fields are statically sized, and only need to be + * rendered at initialization. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override isDirty_ = false; + + protected override ariaTypeName = Blockly.Msg['ARIA_TYPE_FIELD_COLOUR']; + + /** + * @param value The initial value of the field. Should be in '#rrggbb' + * format. Defaults to the first value in the default colour array. Also + * accepts Field.SKIP_SETUP if you wish to skip setup (only used by + * subclasses that want to handle configuration and setting the field + * value after their own constructors have run). + * @param validator A function that is called to validate changes to the + * field's value. Takes in a colour string & returns a validated colour + * string ('#rrggbb' format), or null to abort the change. + * @param config A map of options used to configure the field. + * See the [field creation documentation]{@link + * https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/colour} + * for a list of properties this parameter supports. + */ + constructor( + value?: string | typeof Blockly.Field.SKIP_SETUP, + validator?: FieldColourValidator, + config?: FieldColourConfig, + ) { + const swatches = makeSwatches( + config?.colourOptions ?? Object.keys(DEFAULT_COLOURS), + config?.colourTitles, + ); + super(swatches, validator, {...config, columns: config?.columns ?? 7}); + + if (value === Blockly.Field.SKIP_SETUP) return; + this.setValue(value); + } + + /** + * FieldDropdown has complex behaviors for normalizing options that aren't + * applicable here. Instead, just return the options as-is. + * + * @param options The options (colour swatches) to normalize. + * @returns The colour swatches as-is. + */ + protected override trimOptions(options: Blockly.MenuOption[]) { + return {options}; + } + + /** + * Configure the field based on the given map of options. + * + * @param config A map of options to configure the field based on. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override configure_(config: FieldColourConfig) { + super.configure_(config); + if (config.colourOptions) { + this.setColours(config.colourOptions, config.colourTitles); + } + } + + /** + * Create the block UI for this colour field. + * + * @internal + */ + override initView() { + const constants = this.getConstants(); + // This can't happen, but TypeScript thinks it can and lint forbids `!.`. + if (!constants) throw Error('Constants not found'); + this.size_ = new Blockly.utils.Size( + constants.FIELD_COLOUR_DEFAULT_WIDTH, + constants.FIELD_COLOUR_DEFAULT_HEIGHT, + ); + if (this.isFullBlockField()) { + this.clickTarget_ = (this.sourceBlock_ as Blockly.BlockSvg).getSvgRoot(); + } else { + this.createBorderRect_(); + this.getBorderRect().style['fillOpacity'] = '1'; + } + + if (this.fieldGroup_) { + this.fieldGroup_.classList.add('blocklyField'); + } + + this.recomputeAriaContext(); + } + + /** + * Shows the colour picker dropdown attached to the field. + * + * @param e The event that triggered display of the colour picker dropdown. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override showEditor_(e?: MouseEvent) { + super.showEditor_(e); + Blockly.DropDownDiv.getContentDiv().classList.add('blocklyFieldColour'); + Blockly.DropDownDiv.repositionForWindowResize(); + } + + /** + * Defines whether this field should take up the full block or not. + * + * @returns True if this field should take up the full block. False otherwise. + */ + override isFullBlockField(): boolean { + const block = this.getSourceBlock(); + if (!block) throw new Blockly.UnattachedFieldError(); + + const constants = this.getConstants(); + return ( + this.blockIsSimpleReporter() && + Boolean(constants?.FIELD_COLOUR_FULL_BLOCK) + ); + } + + /** + * @returns True if the source block is a value block with a single editable + * field. + * @internal + */ + blockIsSimpleReporter(): boolean { + const block = this.getSourceBlock(); + if (!block) throw new Blockly.UnattachedFieldError(); + + if (!block.outputConnection) return false; + + for (const input of block.inputList) { + if (input.connection || input.fieldRow.length > 1) return false; + } + return true; + } + + /** + * Updates text field to match the colour/style of the block. + * + * @internal + */ + override applyColour() { + const block = this.getSourceBlock() as Blockly.BlockSvg | null; + if (!block) throw new Blockly.UnattachedFieldError(); + + if (!this.fieldGroup_) return; + + const borderRect = this.borderRect_; + + if (!this.isFullBlockField()) { + if (!borderRect) { + throw new Error('The border rect has not been initialized'); + } + borderRect.style.display = 'block'; + borderRect.style.fill = this.getValue() as string; + } else { + // In general, do *not* let fields control the color of blocks. Having the + // field control the color is unexpected, and could have performance + // impacts. + block.pathObject.svgPath.setAttribute('fill', this.getValue() as string); + } + this.recomputeAriaContext(); + } + + /** + * Returns the height and width of the field. + * + * This should *in general* be the only place render_ gets called from. + * + * @returns Height and width. + */ + override getSize(): Blockly.utils.Size { + if (this.getConstants()?.FIELD_COLOUR_FULL_BLOCK) { + // In general, do *not* let fields control the color of blocks. Having the + // field control the color is unexpected, and could have performance + // impacts. + // Full block fields have more control of the block than they should + // (i.e. updating fill colour) so they always need to be rerendered. + this.render_(); + this.isDirty_ = false; + } + return super.getSize(); + } + + /** + * Updates the colour of the block to reflect whether this is a full + * block field or not. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override render_() { + this.updateSize_(); + + const block = this.getSourceBlock() as Blockly.BlockSvg | null; + if (!block) throw new Blockly.UnattachedFieldError(); + // Calling applyColour updates the UI (full-block vs non-full-block) for the + // colour field, and the colour of the field/block. + block.applyColour(); + } + + /** + * Updates the size of the field based on whether it is a full block field + * or not. + * + * @param margin margin to use when positioning the field. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected updateSize_(margin?: number) { + const constants = this.getConstants(); + if (!constants) return; + let totalWidth; + let totalHeight; + if (this.isFullBlockField()) { + const xOffset = margin ?? 0; + totalWidth = xOffset * 2; + totalHeight = constants.FIELD_TEXT_HEIGHT; + } else { + totalWidth = constants.FIELD_COLOUR_DEFAULT_WIDTH; + totalHeight = constants.FIELD_COLOUR_DEFAULT_HEIGHT; + } + + this.size_.height = totalHeight; + this.size_.width = totalWidth; + + this.positionBorderRect_(); + } + + /** + * Ensure that the input value is a valid colour. + * + * @param newValue The input value. + * @returns A valid colour, or null if invalid. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doClassValidation_( + newValue: string, + ): string | null | undefined; + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doClassValidation_(newValue?: string): string | null; + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doClassValidation_( + newValue?: string, + ): string | null | undefined { + if (typeof newValue !== 'string') { + return null; + } + return Blockly.utils.colour.parse(newValue); + } + + /** + * Get the text for this field. Used when the block is collapsed. + * + * @returns Text representing the value of this field. + */ + override getText(): string { + let colour = this.value_ as string; + // Try to use #rgb format if possible, rather than #rrggbb. + if (/^#(.)\1(.)\2(.)\3$/.test(colour)) { + colour = '#' + colour[1] + colour[3] + colour[5]; + } + return colour; + } + + /** + * Set a custom colour grid for this field. + * + * @param colours Array of colours for this block, or null to use default + * (FieldColour.COLOURS). + * @param titles Optional array of colour tooltips, or null to use default + * (FieldColour.TITLES). + * @returns Returns itself (for method chaining). + */ + setColours(colours: string[], titles?: string[]): FieldColour { + const swatches = makeSwatches(colours, titles); + this.setOptions(swatches); + return this; + } + + /** + * Construct a FieldColour from a JSON arg object. + * + * @param options A JSON object with options (colour). + * @returns The new field instance. + * @nocollapse + * @internal + */ + static fromJson(options: FieldColourFromJsonConfig): FieldColour { + // `this` might be a subclass of FieldColour if that class doesn't override + // the static fromJson method. + return new this(options.colour, undefined, options); + } +} + +/** + * Creates a set of divs representing colour swatches for use in the picker. + * + * @param colours An array of colours to create swatches for. The colours must + * be any legal CSS colour specifier. + * @param titles A corresponding array of titles to be displayed as tooltips on + * the colour swatches. + * @returns An array of pairs of DOM elements representing colour swatches and + * their corresponding colour. + */ +function makeSwatches( + colours: string[], + titles?: string[], +): Blockly.MenuOption[] { + return colours.map((colour, index) => { + const swatch = document.createElement('div'); + swatch.className = 'blocklyColourSwatch'; + swatch.style.backgroundColor = colour; + + if (titles && index < titles.length) { + swatch.title = titles[index]; + } + return [swatch, colour, titles?.[index] ?? DEFAULT_COLOURS[colour]]; + }); +} + +/** The default value for this field. */ +FieldColour.prototype.DEFAULT_VALUE = '#ffffff'; + +/** + * Register the field and any dependencies. + */ +export function registerFieldColour() { + Blockly.fieldRegistry.register('field_colour', FieldColour); +} + +/** + * CSS for colour picker. + */ +Blockly.Css.register(` +.blocklyFieldColour .blocklyFieldGridItemSelected, +.blocklyFieldGridItemSelected:hover { + border-color: #eee !important; + outline: 1px solid #333; + position: relative; +} + +.blocklyColourSwatch { + width: 20px; + height: 20px; +} + +.blocklyGridContainer { + padding: 0px; +} + +.blocklyFieldColour .blocklyFieldGrid { + grid-gap: 0px; + row-gap: 4px; +} + +.blocklyFieldColour .blocklyFieldGrid .blocklyGridItem { + border-radius: 0; + padding: 0; + border: 0.5px solid #888; + cursor: pointer; +} + +.blocklyFieldColour .blocklyFieldGrid .blocklyFieldGridItem { + border: 0.5px solid #888; + padding: 0; + margin: 0; + border-radius: 0; +} + +.blocklyFieldColour .blocklyFieldGrid .blocklyFieldGridItem:focus { + border-color: #eee; + box-shadow: 2px 2px 7px 2px rgba(0, 0, 0, 0.3); + position: relative; + border-radius: 0; + outline: none; +} + +.blocklyKeyboardNavigation .blocklyFieldColour .blocklyFieldGrid .blocklyFieldGridItem:focus { + outline: var(--blockly-selection-width) solid var(--blockly-active-node-color); + outline-offset: -2px; + border: none; + box-shadow: none; + border-radius: 4px; +} +`); + +/** + * Config options for the colour field. + */ +export interface FieldColourConfig extends FieldGridDropdownConfig { + colourOptions?: string[]; + colourTitles?: string[]; +} + +/** + * fromJson config options for the colour field. + */ +export interface FieldColourFromJsonConfig extends FieldGridDropdownFromJsonConfig { + colour?: string; +} + +/** + * A function that is called to validate changes to the field's value before + * they are set. + * + * @see {@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/validators#return_values} + * @param newValue The value to be validated. + * @returns One of three instructions for setting the new value: `T`, `null`, + * or `undefined`. + * + * - `T` to set this function's returned value instead of `newValue`. + * + * - `null` to invoke `doValueInvalid_` and not set a value. + * + * - `undefined` to set `newValue` as is. + */ +export type FieldColourValidator = Blockly.FieldValidator; diff --git a/packages/plugins/field-colour/src/index.ts b/packages/plugins/field-colour/src/index.ts new file mode 100644 index 00000000000..b0474b0b487 --- /dev/null +++ b/packages/plugins/field-colour/src/index.ts @@ -0,0 +1,33 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './field_colour'; + +import * as colourPicker from './blocks/colourPicker'; +import * as colourRandom from './blocks/colourRandom'; +import * as colourRgb from './blocks/colourRgb'; +import * as colourBlend from './blocks/colourBlend'; +import {Generators} from './blocks/generatorsType'; + +// Re-export all parts of the definition. +export * as colourPicker from './blocks/colourPicker'; +export * as colourRandom from './blocks/colourRandom'; +export * as colourRgb from './blocks/colourRgb'; +export * as colourBlend from './blocks/colourBlend'; + +/** + * Install all of the blocks defined in this file and all of their + * dependencies. + * + * @param generators The CodeGenerators to install per-block + * generators on. + */ +export function installAllBlocks(generators: Generators = {}) { + colourPicker.installBlock(generators); + colourRgb.installBlock(generators); + colourRandom.installBlock(generators); + colourBlend.installBlock(generators); +} diff --git a/packages/plugins/field-colour/test/blocks_test.mocha.js b/packages/plugins/field-colour/test/blocks_test.mocha.js new file mode 100644 index 00000000000..fea374f6eba --- /dev/null +++ b/packages/plugins/field-colour/test/blocks_test.mocha.js @@ -0,0 +1,228 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import fs from 'fs'; +import * as Blockly from 'blockly/core'; +import 'blockly/blocks'; + +import {javascriptGenerator} from 'blockly/javascript'; +import {dartGenerator} from 'blockly/dart'; +import {phpGenerator} from 'blockly/php'; +import {pythonGenerator} from 'blockly/python'; +import {luaGenerator} from 'blockly/lua'; +import {installAllBlocks} from '../src/index'; +import {assert} from 'chai'; + +const blockJson = { + blocks: { + languageVersion: 0, + blocks: [ + { + type: 'colour_picker', + x: 13, + y: 13, + fields: { + COLOUR: '#ff0000', + }, + }, + { + type: 'colour_random', + x: 13, + y: 113, + }, + { + type: 'colour_rgb', + x: 13, + y: 263, + inputs: { + RED: { + shadow: { + type: 'math_number', + fields: { + NUM: 0, + }, + }, + }, + GREEN: { + shadow: { + type: 'math_number', + fields: { + NUM: 1, + }, + }, + }, + BLUE: { + shadow: { + type: 'math_number', + fields: { + NUM: 20, + }, + }, + }, + }, + }, + { + type: 'colour_blend', + x: 13, + y: 363, + inputs: { + COLOUR1: { + shadow: { + type: 'colour_picker', + fields: { + COLOUR: '#ff0000', + }, + }, + }, + COLOUR2: { + shadow: { + type: 'colour_picker', + fields: { + COLOUR: '#3333ff', + }, + }, + }, + RATIO: { + shadow: { + type: 'math_number', + fields: { + NUM: 0.5, + }, + }, + }, + }, + }, + { + type: 'colour_picker', + x: 13, + y: 63, + fields: { + COLOUR: '#3333ff', + }, + }, + { + type: 'colour_rgb', + x: 13, + y: 163, + inputs: { + RED: { + shadow: { + type: 'math_number', + fields: { + NUM: 100, + }, + }, + }, + GREEN: { + shadow: { + type: 'math_number', + fields: { + NUM: 50, + }, + }, + }, + BLUE: { + shadow: { + type: 'math_number', + fields: { + NUM: 0, + }, + }, + }, + }, + }, + { + type: 'colour_blend', + x: 13, + y: 463, + inputs: { + COLOUR1: { + shadow: { + type: 'colour_picker', + fields: { + COLOUR: '#000000', + }, + }, + }, + COLOUR2: { + shadow: { + type: 'colour_picker', + fields: { + COLOUR: '#ffffff', + }, + }, + }, + RATIO: { + shadow: { + type: 'math_number', + fields: { + NUM: 0.3, + }, + }, + }, + }, + }, + ], + }, +}; + +/** + * Assert that the generated code matches the golden code for the specified + * language. + * @param {string} suffix The suffix of the golden file. + * @param {string} generated The generated code to compare against the + * golden file. + */ +function checkResult(suffix, generated) { + const fileName = `test/golden/golden.${suffix}`; + const goldenContents = fs.readFileSync(fileName); + // Normalize the line feeds. + const normalized = goldenContents.toString().replace(/(?:\r\n|\r|\n)/g, '\n'); + assert.equal(generated, normalized); +} + +suite('Colour Block Generators', function () { + suiteSetup(function () { + installAllBlocks({ + javascript: javascriptGenerator, + dart: dartGenerator, + lua: luaGenerator, + python: pythonGenerator, + php: phpGenerator, + }); + }); + setup(function () { + this.jsdomCleanup = require('jsdom-global')( + '

', + ); + this.workspace = new Blockly.Workspace(); + Blockly.serialization.workspaces.load(blockJson, this.workspace); + }); + test('JavaScript', function () { + const generated = javascriptGenerator.workspaceToCode(this.workspace); + checkResult('js', generated); + }); + test('Dart', function () { + const generated = dartGenerator.workspaceToCode(this.workspace); + checkResult('dart', generated); + }); + test('Lua', function () { + const generated = luaGenerator.workspaceToCode(this.workspace); + checkResult('lua', generated); + }); + test('Python', function () { + const generated = pythonGenerator.workspaceToCode(this.workspace); + checkResult('py', generated); + }); + test('PHP', function () { + const generated = phpGenerator.workspaceToCode(this.workspace); + checkResult('php', generated); + }); + teardown(function () { + this.jsdomCleanup(); + this.workspace.dispose(); + }); +}); diff --git a/packages/plugins/field-colour/test/field_colour_test.mocha.js b/packages/plugins/field-colour/test/field_colour_test.mocha.js new file mode 100644 index 00000000000..057bcab121a --- /dev/null +++ b/packages/plugins/field-colour/test/field_colour_test.mocha.js @@ -0,0 +1,389 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +const {testHelpers} = require('@blockly/dev-tools'); +const {FieldColour, registerFieldColour} = require('../src/index'); +const {assert} = require('chai'); + +const { + assertFieldValue, + FieldCreationTestCase, + FieldValueTestCase, + runConstructorSuiteTests, + runFromJsonSuiteTests, + runSetValueTests, +} = testHelpers; + +suite('FieldColour', function () { + setup(function () { + registerFieldColour(); + this.jsdomCleanup = require('jsdom-global')( + '
', + ); + }); + + teardown(function () { + this.jsdomCleanup(); + }); + + /** + * Configuration for field tests with invalid values. + * @type {Array} + */ + const invalidValueTestCases = [ + {title: 'Undefined', value: undefined}, + {title: 'Null', value: null}, + {title: 'NaN', value: NaN}, + {title: 'Non-Parsable String', value: 'bad-string'}, + {title: 'Integer', value: 1}, + {title: 'Float', value: 1.5}, + {title: 'Infinity', value: Infinity}, + {title: 'Negative Infinity', value: -Infinity}, + ]; + /** + * Configuration for field tests with valid values. + * @type {Array} + */ + const validValueTestCases = [ + { + title: '#AAAAAA', + value: '#AAAAAA', + expectedValue: '#aaaaaa', + expectedText: '#aaa', + }, + { + title: '#aaaaaa', + value: '#aaaaaa', + expectedValue: '#aaaaaa', + expectedText: '#aaa', + }, + { + title: '#AAAA00', + value: '#AAAA00', + expectedValue: '#aaaa00', + expectedText: '#aa0', + }, + { + title: '#aaaA00', + value: '#aaaA00', + expectedValue: '#aaaa00', + expectedText: '#aa0', + }, + { + title: '#BCBCBC', + value: '#BCBCBC', + expectedValue: '#bcbcbc', + expectedText: '#bcbcbc', + }, + { + title: '#bcbcbc', + value: '#bcbcbc', + expectedValue: '#bcbcbc', + expectedText: '#bcbcbc', + }, + { + title: '#AA0', + value: '#AA0', + expectedValue: '#aaaa00', + expectedText: '#aa0', + }, + { + title: '#aa0', + value: '#aa0', + expectedValue: '#aaaa00', + expectedText: '#aa0', + }, + { + title: 'rgb(170, 170, 0)', + value: 'rgb(170, 170, 0)', + expectedValue: '#aaaa00', + expectedText: '#aa0', + }, + { + title: 'red', + value: 'red', + expectedValue: '#ff0000', + expectedText: '#f00', + }, + ]; + const addArgsAndJson = function (testCase) { + testCase.args = [testCase.value]; + testCase.json = {colour: testCase.value}; + }; + invalidValueTestCases.forEach(addArgsAndJson); + validValueTestCases.forEach(addArgsAndJson); + + /** + * The expected default value for the field being tested. + * @type {*} + */ + const defaultFieldValue = FieldColour.prototype.DEFAULT_VALUE; + + /** + * The expected default text for the field being tested. + * @type {*} + */ + const defaultTextValue = (function () { + let expectedText = defaultFieldValue; + const m = defaultFieldValue.match(/^#(.)\1(.)\2(.)\3$/); + if (m) { + expectedText = '#' + m[1] + m[2] + m[3]; + } + return expectedText; + })(); + /** + * Asserts that the field property values are set to default. + * @param {!FieldColour} field The field to check. + */ + const assertFieldDefault = function (field) { + assertFieldValue(field, defaultFieldValue, defaultTextValue); + }; + + /** + * Asserts that the field properties are correct based on the test case. + * @param {FieldColour} field The field to check. + * @param {FieldValueTestCase} testCase The test case. + */ + const validTestCaseAssertField = function (field, testCase) { + assertFieldValue(field, testCase.expectedValue, testCase.expectedText); + }; + + runConstructorSuiteTests( + FieldColour, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + runFromJsonSuiteTests( + FieldColour, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + suite('setValue', function () { + /** + * Create a mock block that may be used as the source block for a field. + * @returns {!Object} Mock block. + */ + function createBlockMock() { + return { + id: 'test', + rendered: false, + workspace: { + rendered: false, + }, + renameVarById: Blockly.Block.prototype.renameVarById, + updateVarName: Blockly.Block.prototype.updateVarName, + isShadow: () => false, + isDeadOrDying: () => false, + }; + } + + suite('Empty -> New Value', function () { + setup(function () { + this.field = new FieldColour(); + }); + runSetValueTests( + validValueTestCases, + invalidValueTestCases, + defaultFieldValue, + defaultTextValue, + ); + test('With source block', function () { + this.field.setSourceBlock(createBlockMock()); + this.field.setValue('#bcbcbc'); + assertFieldValue(this.field, '#bcbcbc', '#bcbcbc'); + }); + }); + suite('Value -> New Value', function () { + setup(function () { + this.field = new FieldColour('#aaaaaa'); + }); + runSetValueTests( + validValueTestCases, + invalidValueTestCases, + '#aaaaaa', + '#aaa', + ); + test('With source block', function () { + this.field.setSourceBlock(createBlockMock()); + this.field.setValue('#bcbcbc'); + assertFieldValue(this.field, '#bcbcbc', '#bcbcbc'); + }); + }); + }); + + suite('Validators', function () { + setup(function () { + this.field = new FieldColour('#aaaaaa'); + }); + const testSuites = [ + { + title: 'Null Validator', + validator: function () { + return null; + }, + value: '#000000', + expectedValue: '#aaaaaa', + expectedText: '#aaa', + }, + { + title: 'Force Full Red Validator', + validator: function (newValue) { + return '#ff' + newValue.substr(3, 4); + }, + value: '#000000', + expectedValue: '#ff0000', + expectedText: '#f00', + }, + { + title: 'Returns Undefined Validator', + validator: function () {}, + value: '#000000', + expectedValue: '#000000', + expectedText: '#000', + }, + ]; + testSuites.forEach(function (suiteInfo) { + suite(suiteInfo.title, function () { + setup(function () { + this.field.setValidator(suiteInfo.validator); + }); + test('New Value', function () { + this.field.setValue(suiteInfo.value); + assertFieldValue( + this.field, + suiteInfo.expectedValue, + suiteInfo.expectedText, + ); + }); + }); + }); + }); + + suite('Customizations', function () { + suite('Colours and Titles', function () { + /** + * Verify that the list of colours and titles are as expected. + * @param {!FieldColour} field Field to test. + * @param {!Array[string]} expectedColours Array of colour names. + * @param {!Array[string]} expectedTitles Array of title names. + */ + function assertColoursAndTitles(field, expectedColours, expectedTitles) { + const options = field.getOptions(); + const actualColours = options.map((option) => + Blockly.utils.colour.parse(option[0].style.backgroundColor), + ); + const actualTitles = options + .map((option) => option[0].title) + .filter((title) => !!title); + assert.equal(String(actualColours), String(expectedColours)); + assert.equal(String(actualTitles), String(expectedTitles)); + } + test('JS Constructor', function () { + const field = new FieldColour('#aaaaaa', null, { + colourOptions: ['#aaaaaa'], + colourTitles: ['grey'], + }); + assertColoursAndTitles(field, ['#aaaaaa'], ['grey']); + }); + test('JSON Definition', function () { + const field = FieldColour.fromJson({ + colour: '#aaaaaa', + colourOptions: ['#aaaaaa'], + colourTitles: ['grey'], + }); + assertColoursAndTitles(field, ['#aaaaaa'], ['grey']); + }); + test('setColours', function () { + const field = new FieldColour(); + field.setColours(['#aaaaaa'], ['grey']); + assertColoursAndTitles(field, ['#aaaaaa'], ['grey']); + }); + test('Titles Undefined', function () { + const field = new FieldColour(); + field.setColours(['#aaaaaa']); + assertColoursAndTitles(field, ['#aaaaaa'], []); + }); + test('Some Titles Undefined', function () { + const field = new FieldColour(); + field.setColours(['#aaaaaa', '#ff0000'], ['grey']); + assertColoursAndTitles(field, ['#aaaaaa', '#ff0000'], ['grey']); + }); + }); + + suite('Columns', function () { + /** + * Verify that the number of columns is as expected. + * @param {!FieldColour} field Field to test. + * @param {number} expectedColumns Number of columns field should have. + */ + function assertColumns(field, expectedColumns) { + const actualColumns = field.columns || FieldColour.COLUMNS; + assert.equal(actualColumns, expectedColumns); + } + test('JS Constructor', function () { + const field = new FieldColour('#ffffff', null, {columns: 3}); + assertColumns(field, 3); + }); + test('JSON Definition', function () { + const field = FieldColour.fromJson({ + colour: '#ffffff', + columns: 3, + }); + assertColumns(field, 3); + }); + test('setColumns', function () { + const field = new FieldColour(); + field.setColumns(3); + assertColumns(field, 3); + }); + }); + }); + + suite('Serialization', function () { + setup(function () { + this.workspace = new Blockly.Workspace(); + Blockly.defineBlocksWithJsonArray([ + { + type: 'row_block', + message0: '%1', + args0: [ + { + type: 'input_value', + name: 'INPUT', + }, + ], + output: null, + }, + ]); + + this.assertValue = (value) => { + const block = this.workspace.newBlock('row_block'); + const field = new FieldColour(value); + block.getInput('INPUT').appendField(field, 'COLOUR'); + const jso = Blockly.serialization.blocks.save(block); + assert.deepEqual(jso['fields'], {COLOUR: value}); + }; + }); + + teardown(function () { + this.workspace.dispose(); + }); + + test('Three char', function () { + this.assertValue('#001122'); + }); + + test('Six char', function () { + this.assertValue('#012345'); + }); + }); +}); diff --git a/packages/plugins/field-colour/test/golden/golden.dart b/packages/plugins/field-colour/test/golden/golden.dart new file mode 100644 index 00000000000..80f9f819302 --- /dev/null +++ b/packages/plugins/field-colour/test/golden/golden.dart @@ -0,0 +1,65 @@ +import 'dart:math' as Math; + +String colour_random() { + String hex = '0123456789abcdef'; + var rnd = new Math.Random(); + return '#${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}' + '${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}' + '${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}'; +} + +String colour_rgb(num r, num g, num b) { + num rn = (Math.max(Math.min(r, 100), 0) * 2.55).round(); + String rs = rn.toInt().toRadixString(16); + rs = '0$rs'; + rs = rs.substring(rs.length - 2); + num gn = (Math.max(Math.min(g, 100), 0) * 2.55).round(); + String gs = gn.toInt().toRadixString(16); + gs = '0$gs'; + gs = gs.substring(gs.length - 2); + num bn = (Math.max(Math.min(b, 100), 0) * 2.55).round(); + String bs = bn.toInt().toRadixString(16); + bs = '0$bs'; + bs = bs.substring(bs.length - 2); + return '#$rs$gs$bs'; +} + +String colour_blend(String c1, String c2, num ratio) { + ratio = Math.max(Math.min(ratio, 1), 0); + int r1 = int.parse('0x${c1.substring(1, 3)}'); + int g1 = int.parse('0x${c1.substring(3, 5)}'); + int b1 = int.parse('0x${c1.substring(5, 7)}'); + int r2 = int.parse('0x${c2.substring(1, 3)}'); + int g2 = int.parse('0x${c2.substring(3, 5)}'); + int b2 = int.parse('0x${c2.substring(5, 7)}'); + num rn = (r1 * (1 - ratio) + r2 * ratio).round(); + String rs = rn.toInt().toRadixString(16); + num gn = (g1 * (1 - ratio) + g2 * ratio).round(); + String gs = gn.toInt().toRadixString(16); + num bn = (b1 * (1 - ratio) + b2 * ratio).round(); + String bs = bn.toInt().toRadixString(16); + rs = '0$rs'; + rs = rs.substring(rs.length - 2); + gs = '0$gs'; + gs = gs.substring(gs.length - 2); + bs = '0$bs'; + bs = bs.substring(bs.length - 2); + return '#$rs$gs$bs'; +} + + +main() { + '#ff0000'; + + '#3333ff'; + + colour_random(); + + colour_rgb(100, 50, 0); + + colour_rgb(0, 1, 20); + + colour_blend('#ff0000', '#3333ff', 0.5); + + colour_blend('#000000', '#ffffff', 0.3); +} \ No newline at end of file diff --git a/packages/plugins/field-colour/test/golden/golden.js b/packages/plugins/field-colour/test/golden/golden.js new file mode 100644 index 00000000000..368b4f101ac --- /dev/null +++ b/packages/plugins/field-colour/test/golden/golden.js @@ -0,0 +1,46 @@ +function colourRandom() { + var num = Math.floor(Math.random() * 0x1000000); + return '#' + ('00000' + num.toString(16)).substr(-6); +} + +function colourRgb(r, g, b) { + r = Math.max(Math.min(Number(r), 100), 0) * 2.55; + g = Math.max(Math.min(Number(g), 100), 0) * 2.55; + b = Math.max(Math.min(Number(b), 100), 0) * 2.55; + r = ('0' + (Math.round(r) || 0).toString(16)).slice(-2); + g = ('0' + (Math.round(g) || 0).toString(16)).slice(-2); + b = ('0' + (Math.round(b) || 0).toString(16)).slice(-2); + return '#' + r + g + b; +} + +function colourBlend(c1, c2, ratio) { + ratio = Math.max(Math.min(Number(ratio), 1), 0); + var r1 = parseInt(c1.substring(1, 3), 16); + var g1 = parseInt(c1.substring(3, 5), 16); + var b1 = parseInt(c1.substring(5, 7), 16); + var r2 = parseInt(c2.substring(1, 3), 16); + var g2 = parseInt(c2.substring(3, 5), 16); + var b2 = parseInt(c2.substring(5, 7), 16); + var r = Math.round(r1 * (1 - ratio) + r2 * ratio); + var g = Math.round(g1 * (1 - ratio) + g2 * ratio); + var b = Math.round(b1 * (1 - ratio) + b2 * ratio); + r = ('0' + (r || 0).toString(16)).slice(-2); + g = ('0' + (g || 0).toString(16)).slice(-2); + b = ('0' + (b || 0).toString(16)).slice(-2); + return '#' + r + g + b; +} + + +'#ff0000'; + +'#3333ff'; + +colourRandom(); + +colourRgb(100, 50, 0); + +colourRgb(0, 1, 20); + +colourBlend('#ff0000', '#3333ff', 0.5); + +colourBlend('#000000', '#ffffff', 0.3); diff --git a/packages/plugins/field-colour/test/golden/golden.lua b/packages/plugins/field-colour/test/golden/golden.lua new file mode 100644 index 00000000000..4fdf506d46b --- /dev/null +++ b/packages/plugins/field-colour/test/golden/golden.lua @@ -0,0 +1,35 @@ +function colour_rgb(r, g, b) + r = math.floor(math.min(100, math.max(0, r)) * 2.55 + .5) + g = math.floor(math.min(100, math.max(0, g)) * 2.55 + .5) + b = math.floor(math.min(100, math.max(0, b)) * 2.55 + .5) + return string.format("#%02x%02x%02x", r, g, b) +end + +function colour_blend(colour1, colour2, ratio) + local r1 = tonumber(string.sub(colour1, 2, 3), 16) + local r2 = tonumber(string.sub(colour2, 2, 3), 16) + local g1 = tonumber(string.sub(colour1, 4, 5), 16) + local g2 = tonumber(string.sub(colour2, 4, 5), 16) + local b1 = tonumber(string.sub(colour1, 6, 7), 16) + local b2 = tonumber(string.sub(colour2, 6, 7), 16) + local ratio = math.min(1, math.max(0, ratio)) + local r = math.floor(r1 * (1 - ratio) + r2 * ratio + .5) + local g = math.floor(g1 * (1 - ratio) + g2 * ratio + .5) + local b = math.floor(b1 * (1 - ratio) + b2 * ratio + .5) + return string.format("#%02x%02x%02x", r, g, b) +end + + +local _ = '#ff0000' + +local _ = '#3333ff' + +local _ = string.format("#%06x", math.random(0, 2^24 - 1)) + +local _ = colour_rgb(100, 50, 0) + +local _ = colour_rgb(0, 1, 20) + +local _ = colour_blend('#ff0000', '#3333ff', 0.5) + +local _ = colour_blend('#000000', '#ffffff', 0.3) diff --git a/packages/plugins/field-colour/test/golden/golden.php b/packages/plugins/field-colour/test/golden/golden.php new file mode 100644 index 00000000000..9f2fde50aa2 --- /dev/null +++ b/packages/plugins/field-colour/test/golden/golden.php @@ -0,0 +1,47 @@ +function colour_random() { + return '#' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), 6, '0', STR_PAD_LEFT); +} + +function colour_rgb($r, $g, $b) { + $r = round(max(min($r, 100), 0) * 2.55); + $g = round(max(min($g, 100), 0) * 2.55); + $b = round(max(min($b, 100), 0) * 2.55); + $hex = '#'; + $hex .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT); + return $hex; +} + +function colour_blend($c1, $c2, $ratio) { + $ratio = max(min($ratio, 1), 0); + $r1 = hexdec(substr($c1, 1, 2)); + $g1 = hexdec(substr($c1, 3, 2)); + $b1 = hexdec(substr($c1, 5, 2)); + $r2 = hexdec(substr($c2, 1, 2)); + $g2 = hexdec(substr($c2, 3, 2)); + $b2 = hexdec(substr($c2, 5, 2)); + $r = round($r1 * (1 - $ratio) + $r2 * $ratio); + $g = round($g1 * (1 - $ratio) + $g2 * $ratio); + $b = round($b1 * (1 - $ratio) + $b2 * $ratio); + $hex = '#'; + $hex .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT); + return $hex; +} + + +'#ff0000'; + +'#3333ff'; + +colour_random(); + +colour_rgb(100, 50, 0); + +colour_rgb(0, 1, 20); + +colour_blend('#ff0000', '#3333ff', 0.5); + +colour_blend('#000000', '#ffffff', 0.3); diff --git a/packages/plugins/field-colour/test/golden/golden.py b/packages/plugins/field-colour/test/golden/golden.py new file mode 100644 index 00000000000..7320b862473 --- /dev/null +++ b/packages/plugins/field-colour/test/golden/golden.py @@ -0,0 +1,32 @@ +import random + +def colour_rgb(r, g, b): + r = round(min(100, max(0, r)) * 2.55) + g = round(min(100, max(0, g)) * 2.55) + b = round(min(100, max(0, b)) * 2.55) + return '#%02x%02x%02x' % (r, g, b) + +def colour_blend(colour1, colour2, ratio): + r1, r2 = int(colour1[1:3], 16), int(colour2[1:3], 16) + g1, g2 = int(colour1[3:5], 16), int(colour2[3:5], 16) + b1, b2 = int(colour1[5:7], 16), int(colour2[5:7], 16) + ratio = min(1, max(0, ratio)) + r = round(r1 * (1 - ratio) + r2 * ratio) + g = round(g1 * (1 - ratio) + g2 * ratio) + b = round(b1 * (1 - ratio) + b2 * ratio) + return '#%02x%02x%02x' % (r, g, b) + + +'#ff0000' + +'#3333ff' + +'#%06x' % random.randint(0, 2**24 - 1) + +colour_rgb(100, 50, 0) + +colour_rgb(0, 1, 20) + +colour_blend('#ff0000', '#3333ff', 0.5) + +colour_blend('#000000', '#ffffff', 0.3) diff --git a/packages/plugins/field-colour/test/index.html b/packages/plugins/field-colour/test/index.html new file mode 100644 index 00000000000..60fa9f5b29f --- /dev/null +++ b/packages/plugins/field-colour/test/index.html @@ -0,0 +1,18 @@ + + + + + Blockly Field Colour Playground + + + + +
+ + + diff --git a/packages/plugins/field-colour/test/index.ts b/packages/plugins/field-colour/test/index.ts new file mode 100644 index 00000000000..722d0629ea8 --- /dev/null +++ b/packages/plugins/field-colour/test/index.ts @@ -0,0 +1,274 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Colour field test playground. + */ + +import * as Blockly from 'blockly'; +import {javascriptGenerator} from 'blockly/javascript'; +import {dartGenerator} from 'blockly/dart'; +import {phpGenerator} from 'blockly/php'; +import {pythonGenerator} from 'blockly/python'; +import {luaGenerator} from 'blockly/lua'; + +import {createPlayground} from '@blockly/dev-tools'; +import {installAllBlocks as installColourBlocks} from '../src/index'; + +/** + * An array of blocks that are defined only for the purposes of + * manually and visually testing the colour field. + */ +const testBlockDefinitions = [ + { + type: 'test_standard_field_values', + message0: '%1', + args0: [ + { + type: 'field_colour', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_colour`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values', + message0: '%1', + args0: [ + { + type: 'field_colour', + name: 'FIELDNAME', + colour: '#ff4040', + colourOptions: [ + '#ff4040', + '#ff8080', + '#ffc0c0', + '#4040ff', + '#8080ff', + '#c0c0ff', + ], + colourTitles: [ + 'dark pink', + 'pink', + 'light pink', + 'dark blue', + 'blue', + 'light blue', + ], + columns: 3, + alt: { + type: 'field_label', + text: `No field_colour`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_standard_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_colour', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_colour`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_colour', + name: 'FIELDNAME', + colour: '#ff4040', + colourOptions: [ + '#ff4040', + '#ff8080', + '#ffc0c0', + '#4040ff', + '#8080ff', + '#c0c0ff', + ], + colourTitles: [ + 'dark pink', + 'pink', + 'light pink', + 'dark blue', + 'blue', + 'light blue', + ], + columns: 3, + alt: { + type: 'field_label', + text: `No field_colour`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_parent_block', + message0: 'parent %1', + args0: [ + { + type: 'input_value', + name: 'INPUT', + }, + ], + previousStatement: null, + nextStatement: null, + style: 'loop_blocks', + }, +]; + +Blockly.defineBlocksWithJsonArray(testBlockDefinitions); + +/** + * A test toolbox containing the exported blocks and a variety of + * test blocks to exercise the colour field in different contexts + * (on a shadow block, as the only field on a block, etc). + * These are in a simple toolbox, rather than a category toolbox, so that + * they are all instantiated every time the test page is opened. + */ +const jsonToolbox = { + contents: [ + { + kind: 'label', + text: 'Exported blocks', + }, + { + kind: 'block', + type: 'colour_blend', + }, + { + kind: 'block', + type: 'colour_picker', + }, + { + kind: 'block', + type: 'colour_random', + }, + { + kind: 'block', + type: 'colour_rgb', + }, + { + kind: 'label', + text: 'Test blocks: default field values', + }, + { + kind: 'block', + type: 'test_standard_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_standard_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values_and_label', + }, + }, + }, + }, + { + kind: 'label', + text: 'Test blocks: custom field values', + }, + { + kind: 'block', + type: 'test_custom_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_custom_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values_and_label', + }, + }, + }, + }, + ], +}; + +/** + * Create a workspace. + * + * @param blocklyDiv The blockly container div. + * @param options The Blockly options. + * @returns The created workspace. + */ +function createWorkspace( + blocklyDiv: HTMLElement, + options: Blockly.BlocklyOptions, +): Blockly.WorkspaceSvg { + const workspace = Blockly.inject(blocklyDiv, options); + return workspace; +} + +document.addEventListener('DOMContentLoaded', function () { + installColourBlocks({ + javascript: javascriptGenerator, + dart: dartGenerator, + lua: luaGenerator, + python: pythonGenerator, + php: phpGenerator, + }); + + const defaultOptions: Blockly.BlocklyOptions = { + toolbox: jsonToolbox, + }; + const rootElement = document.getElementById('root'); + if (rootElement) { + createPlayground(rootElement, createWorkspace, defaultOptions); + } +}); diff --git a/packages/plugins/field-colour/tsconfig.json b/packages/plugins/field-colour/tsconfig.json new file mode 100644 index 00000000000..46b8e73f3e9 --- /dev/null +++ b/packages/plugins/field-colour/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "module": "es2015", + "moduleResolution": "bundler", + "target": "es6", + "strict": true, + "lib": ["ES2020", "dom"] + }, + // NOTE: `test/**/*` is automatically included in `blockly-scripts start`. + // Only src matters for production builds. + "include": ["src"] +} diff --git a/packages/plugins/field-date/.gitignore b/packages/plugins/field-date/.gitignore new file mode 100644 index 00000000000..800f3a80c34 --- /dev/null +++ b/packages/plugins/field-date/.gitignore @@ -0,0 +1,24 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build +/dist + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/packages/plugins/field-date/CHANGELOG.md b/packages/plugins/field-date/CHANGELOG.md new file mode 100644 index 00000000000..5e3e6a0ffc5 --- /dev/null +++ b/packages/plugins/field-date/CHANGELOG.md @@ -0,0 +1,677 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [13.1.0](https://github.com/RaspberryPiFoundation/blockly-samples/compare/v13.0.0...v13.1.0) (2026-06-30) + +### ⚠ BREAKING CHANGES + +* Bump Blockly dependency to v13 (#2704) + +### Bug Fixes + +* Improve accessibility of `FieldDate` ([#2698](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2698)) + +### Miscellaneous Chores + +* Bump Blockly dependency to v13 ([#2704](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2704)) + + +## [10.0.9](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-date@10.0.8...@blockly/field-date@10.0.9) (2026-04-09) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [10.0.8](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-date@10.0.7...@blockly/field-date@10.0.8) (2026-01-15) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [10.0.7](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-date@10.0.6...@blockly/field-date@10.0.7) (2026-01-05) + + +### Bug Fixes + +* update plugin URLs to RPF ([#2665](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2665)) ([6c34039](https://github.com/RaspberryPiFoundation/blockly-samples/commit/6c3403960b8070eb8e576bd5702bdb53a09ac475)) + + + +## [10.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-date@10.0.5...@blockly/field-date@10.0.6) (2026-01-05) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [10.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-date@10.0.4...@blockly/field-date@10.0.5) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [10.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-date@10.0.3...@blockly/field-date@10.0.4) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [10.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-date@10.0.2...@blockly/field-date@10.0.3) (2025-10-02) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [10.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-date@10.0.1...@blockly/field-date@10.0.2) (2025-07-17) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [10.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-date@10.0.0...@blockly/field-date@10.0.1) (2025-05-22) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [10.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.16...@blockly/field-date@10.0.0) (2025-05-16) + + +### ⚠ BREAKING CHANGES + +* update all plugins to blockly v12 (#2538) + +### Features + +* update all plugins to blockly v12 ([#2538](https://github.com/google/blockly-samples/issues/2538)) ([420ba31](https://github.com/google/blockly-samples/commit/420ba3124b260b2c06e8e1d61be44e870780d95f)) + + + +## [9.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.15...@blockly/field-date@9.0.16) (2025-05-16) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.14...@blockly/field-date@9.0.15) (2025-05-15) + + +### Bug Fixes + +* Wait for an animation frame before showing the date picker ([e3b1054](https://github.com/google/blockly-samples/commit/e3b1054eb0b511aa4ab270725c989369270ee8d6)) + + + + + +## [9.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.13...@blockly/field-date@9.0.14) (2025-02-13) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.12...@blockly/field-date@9.0.13) (2024-12-19) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.11...@blockly/field-date@9.0.12) (2024-12-03) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.10...@blockly/field-date@9.0.11) (2024-11-07) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.9...@blockly/field-date@9.0.10) (2024-09-26) + + +### Bug Fixes + +* **field-*:** Remove unneeded `fieldRegistry.unregister` calls ([#2454](https://github.com/google/blockly-samples/issues/2454)) ([b3ba30e](https://github.com/google/blockly-samples/commit/b3ba30e23dddf0bd98c266659aa229ba6ba685b0)), closes [#2453](https://github.com/google/blockly-samples/issues/2453) + + + + + +## [9.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.8...@blockly/field-date@9.0.9) (2024-08-29) + + +### Bug Fixes + +* predeploy plugins ([#2449](https://github.com/google/blockly-samples/issues/2449)) ([6b36d8b](https://github.com/google/blockly-samples/commit/6b36d8b344a969f79d89bbc7dcee29ae554759f9)) + + + + + +## [9.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.7...@blockly/field-date@9.0.8) (2024-08-22) + + +### Bug Fixes + +* lerna v8 breaking our whole shebangle ([#2446](https://github.com/google/blockly-samples/issues/2446)) ([b8b4c21](https://github.com/google/blockly-samples/commit/b8b4c21d4eaf81f527336ae46f6679ff99ac23c3)) + + + + + +## [9.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.6...@blockly/field-date@9.0.7) (2024-08-08) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.5...@blockly/field-date@9.0.6) (2024-08-01) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.4...@blockly/field-date@9.0.5) (2024-07-25) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.3...@blockly/field-date@9.0.4) (2024-07-11) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.2...@blockly/field-date@9.0.3) (2024-07-04) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.1...@blockly/field-date@9.0.2) (2024-06-06) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [9.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-date@9.0.0...@blockly/field-date@9.0.1) (2024-05-30) + + +### Bug Fixes + +* multiple blockly instances ([#2375](https://github.com/google/blockly-samples/issues/2375)) ([b231e59](https://github.com/google/blockly-samples/commit/b231e598f2f5f5b0abbfd01d981e35572ad50a26)) + + + +## [9.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.17...@blockly/field-date@9.0.0) (2024-05-21) + + +### ⚠ BREAKING CHANGES + +* update all plugins to v11 of blockly + +### Features + +* update all plugins to v11 of blockly ([5fcd3e7](https://github.com/google/blockly-samples/commit/5fcd3e7d53eaadffe9bda9a378b404d34b2f8be2)) +* upgrade ts version, fix field validators ([b2d2191](https://github.com/google/blockly-samples/commit/b2d2191ff11b30347b33f95acf6e58f7ce54d004)) + + +### Bug Fixes + +* Call blocks handle both manual disabling and disabled defs ([#2334](https://github.com/google/blockly-samples/issues/2334)) ([5eade55](https://github.com/google/blockly-samples/commit/5eade55779c4022d14ad4472ff32c93c78199887)) +* remove some uses of generateFieldTextBlocks ([#2357](https://github.com/google/blockly-samples/issues/2357)) ([53b3fa4](https://github.com/google/blockly-samples/commit/53b3fa4604aabb1fc5240d41c8d1d389d4b15301)) + + + +## [8.0.17](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.16...@blockly/field-date@8.0.17) (2024-05-16) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.15...@blockly/field-date@8.0.16) (2024-05-09) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.14...@blockly/field-date@8.0.15) (2024-04-25) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.13...@blockly/field-date@8.0.14) (2024-04-04) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.12...@blockly/field-date@8.0.13) (2024-03-28) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.11...@blockly/field-date@8.0.12) (2024-02-16) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.10...@blockly/field-date@8.0.11) (2024-02-08) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.9...@blockly/field-date@8.0.10) (2023-12-14) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.8...@blockly/field-date@8.0.9) (2023-12-07) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.7...@blockly/field-date@8.0.8) (2023-11-09) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.6...@blockly/field-date@8.0.7) (2023-11-02) + + +### Bug Fixes + +* publish types for field-date ([#2049](https://github.com/google/blockly-samples/issues/2049)) ([2086712](https://github.com/google/blockly-samples/commit/20867125156005b346056080057ba95219f73b29)) + + + +## [8.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.5...@blockly/field-date@8.0.6) (2023-10-30) + + +### Bug Fixes + +* **packaging:** Don't advertise non-existent ESM entrypoints ([#2022](https://github.com/google/blockly-samples/issues/2022)) ([d019198](https://github.com/google/blockly-samples/commit/d0191984399b784e2928b8fb4c58257bfa857655)), closes [#1877](https://github.com/google/blockly-samples/issues/1877) +* update tsconfigs to publish types accurately ([df4b084](https://github.com/google/blockly-samples/commit/df4b0844af712f5025a2ec842458b828f3147676)) +* use includes rather than excludes ([40efc25](https://github.com/google/blockly-samples/commit/40efc255329e3ca476ccc247b95a2d05dd77b45e)) + + + +## [8.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.4...@blockly/field-date@8.0.5) (2023-09-21) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.3...@blockly/field-date@8.0.4) (2023-09-14) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.2...@blockly/field-date@8.0.3) (2023-08-17) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.1...@blockly/field-date@8.0.2) (2023-07-24) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-date@8.0.0...@blockly/field-date@8.0.1) (2023-07-20) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [8.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-date@7.0.1...@blockly/field-date@8.0.0) (2023-07-13) + + +### ⚠ BREAKING CHANGES + +* updated `plugins/field-date` to TypeScript (#1705) + +### Features + +* updated `plugins/field-date` to TypeScript ([#1705](https://github.com/google/blockly-samples/issues/1705)) ([e5531ff](https://github.com/google/blockly-samples/commit/e5531fffe188ee361a16fe48ed126b34e51a8d30)) + + + +## [7.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-date@7.0.0...@blockly/field-date@7.0.1) (2023-07-06) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [7.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.1.2...@blockly/field-date@7.0.0) (2023-06-28) + + +### ⚠ BREAKING CHANGES + +* update all plugins to use blockly-v10.0.0 (#1744) + +### Features + +* update all plugins to use blockly-v10.0.0 ([#1744](https://github.com/google/blockly-samples/issues/1744)) ([6cc88cb](https://github.com/google/blockly-samples/commit/6cc88cbef39d4ad664a668d3d46eb29ba7292f9c)) + + + +## [6.1.2](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.1.1...@blockly/field-date@6.1.2) (2023-06-22) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.1.1](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.1.0...@blockly/field-date@6.1.1) (2023-05-18) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.1.0](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.14...@blockly/field-date@6.1.0) (2023-05-11) + + +### Features + +* Add angle, colour, and multiline fields. (Rebase of [#1674](https://github.com/google/blockly-samples/issues/1674)) ([#1693](https://github.com/google/blockly-samples/issues/1693)) ([1b447f4](https://github.com/google/blockly-samples/commit/1b447f41d6293f14e846b8a4d82d289a5637a99a)) + + + +## [6.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.13...@blockly/field-date@6.0.14) (2023-05-04) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.12...@blockly/field-date@6.0.13) (2023-04-27) + + +### Bug Fixes + +* Correct date field test assertions ([#1673](https://github.com/google/blockly-samples/issues/1673)) ([9974e85](https://github.com/google/blockly-samples/commit/9974e85becaa8ad17e35b588b95391c85865dafd)) + + + +## [6.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.11...@blockly/field-date@6.0.12) (2023-03-23) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.10...@blockly/field-date@6.0.11) (2023-03-09) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.9...@blockly/field-date@6.0.10) (2023-03-02) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.8...@blockly/field-date@6.0.9) (2023-02-23) + + +### Bug Fixes + +* remove prepublishOnly scripts ([#1579](https://github.com/google/blockly-samples/issues/1579)) ([27da6cd](https://github.com/google/blockly-samples/commit/27da6cd04c38f6ba417f4e7446bb6218c475448d)) + + + +## [6.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.7...@blockly/field-date@6.0.8) (2023-02-23) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.6...@blockly/field-date@6.0.7) (2023-02-16) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.5...@blockly/field-date@6.0.6) (2023-02-09) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.4...@blockly/field-date@6.0.5) (2023-01-26) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.3...@blockly/field-date@6.0.4) (2023-01-05) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.2...@blockly/field-date@6.0.3) (2022-12-15) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.1...@blockly/field-date@6.0.2) (2022-10-27) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-date@6.0.0...@blockly/field-date@6.0.1) (2022-10-13) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## [6.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-date@5.0.22...@blockly/field-date@6.0.0) (2022-10-05) + + +### ⚠ BREAKING CHANGES + +* update peer and devDependencies of all plugins to require Blockly v9 (#1314) +* update field-date to es6 class (#1295) + +### Bug Fixes + +* references to deprecated functions in v9 ([#1313](https://github.com/google/blockly-samples/issues/1313)) ([cb2e679](https://github.com/google/blockly-samples/commit/cb2e67987e0b62a77c26adc660cc6ade1ba67954)) +* update field-date to es6 class ([#1295](https://github.com/google/blockly-samples/issues/1295)) ([9b70b03](https://github.com/google/blockly-samples/commit/9b70b0300c625c0cddc1eea241a3d2c2200344f8)) + + +### Miscellaneous Chores + +* update peer and devDependencies of all plugins to require Blockly v9 ([#1314](https://github.com/google/blockly-samples/issues/1314)) ([03d4912](https://github.com/google/blockly-samples/commit/03d4912c42c8de0f30493037ccc28dddaea0f266)) + + + +## 5.0.22 (2022-08-11) + + +### Bug Fixes + +* Remove the deprecated block-extension-tooltip ([#1215](https://github.com/google/blockly-samples/issues/1215)) ([a044478](https://github.com/google/blockly-samples/commit/a044478c86a73e3065bc866e427f175cbec6fc13)) + + + + + +## 5.0.21 (2022-08-04) + + +### Bug Fixes + +* fix the name of the package of suggested-blocks ([#1214](https://github.com/google/blockly-samples/issues/1214)) ([35aa8ec](https://github.com/google/blockly-samples/commit/35aa8ec73a60a4eb5b1e80cb2fc71dcd83d05e27)) + + + + + +## 5.0.19 (2022-07-21) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## 5.0.18 (2022-07-06) + + +### Bug Fixes + +* Update blockly-react to use functional components instead of class components. ([#1178](https://github.com/google/blockly-samples/issues/1178)) ([fa21187](https://github.com/google/blockly-samples/commit/fa21187cdbe4ec3a5c69f185540dd68a98eb69d7)) + + + + + +## 5.0.17 (2022-06-27) + + +### Bug Fixes + +* Update package import for keyboard navigation demo ([#1170](https://github.com/google/blockly-samples/issues/1170)) ([69c1725](https://github.com/google/blockly-samples/commit/69c1725b775279fcc397dc178935208d5f42b08c)) + + + + + +## 5.0.16 (2022-06-21) + +**Note:** Version bump only for package @blockly/field-date + + + + + +## 5.0.15 (2022-06-08) + + +### Bug Fixes + +* package versions to support patch releases ([#1150](https://github.com/google/blockly-samples/issues/1150)) ([e1ae378](https://github.com/google/blockly-samples/commit/e1ae378d779531621c3d948566257d069002963f)) + + + + + +## 5.0.14 (2022-06-02) + + +### Bug Fixes + +* add git identity ([#1156](https://github.com/google/blockly-samples/issues/1156)) ([8d80924](https://github.com/google/blockly-samples/commit/8d809243b277375beb2ce75d4e157b5e17f78193)) diff --git a/packages/plugins/field-date/README.md b/packages/plugins/field-date/README.md new file mode 100644 index 00000000000..8b44897f616 --- /dev/null +++ b/packages/plugins/field-date/README.md @@ -0,0 +1,59 @@ +# @blockly/field-date [![Built on Blockly](https://tinyurl.com/built-on-blockly)](https://github.com/google/blockly) + +A [Blockly](https://www.npmjs.com/package/blockly) plugin that uses a browser default "date" input. + +## Installation + +### Yarn + +``` +yarn add @blockly/field-date +``` + +### npm + +``` +npm install @blockly/field-date --save +``` + +## Usage + +### JavaScript + +```js +import * as Blockly from 'blockly'; +import {FieldDate} from '@blockly/field-date'; + +Blockly.Blocks['test_fields_date'] = { + init: function () { + this.appendDummyInput() + .appendField('date: ') + .appendField(new FieldDate('2020-02-20'), 'FIELDNAME'); + }, +}; +``` + +### JSON + +```js +import * as Blockly from 'blockly'; +import '@blockly/field-date'; + +Blockly.defineBlocksWithJsonArray([ + { + type: 'test_fields_date', + message0: 'date: %1', + args0: [ + { + type: 'field_date', + name: 'FIELDNAME', + date: '2020-02-20', + }, + ], + }, +]); +``` + +## License + +Apache 2.0 diff --git a/packages/plugins/field-date/package.json b/packages/plugins/field-date/package.json new file mode 100644 index 00000000000..d4314a22073 --- /dev/null +++ b/packages/plugins/field-date/package.json @@ -0,0 +1,57 @@ +{ + "name": "@blockly/field-date", + "version": "13.1.0", + "description": "A Blockly date picker field that uses the browser's date picker.", + "scripts": { + "audit:fix": "blockly-scripts auditFix", + "build": "blockly-scripts build", + "clean": "blockly-scripts clean", + "lint": "eslint .", + "predeploy": "blockly-scripts predeploy", + "start": "blockly-scripts start", + "test": "blockly-scripts test" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "unpkg": "./dist/index.js", + "author": "Blockly Team", + "keywords": [ + "blockly", + "blockly-plugin", + "field-date", + "field", + "date", + "datepicker" + ], + "homepage": "https://github.com/RaspberryPiFoundation/blockly-samples/tree/master/plugins/field-date#readme", + "bugs": { + "url": "https://github.com/RaspberryPiFoundation/blockly-samples/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/RaspberryPiFoundation/blockly-samples.git", + "directory": "plugins/field-date" + }, + "license": "Apache-2.0", + "directories": { + "dist": "dist", + "src": "src" + }, + "files": [ + "dist", + "src" + ], + "devDependencies": { + "@blockly/dev-scripts": "^13.1.0", + "@blockly/dev-tools": "^13.1.0", + "chai": "^6.2.2", + "sinon": "^22.0.0", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "blockly": "^13.2.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugins/field-date/src/field_date.ts b/packages/plugins/field-date/src/field_date.ts new file mode 100644 index 00000000000..cf9601b30f6 --- /dev/null +++ b/packages/plugins/field-date/src/field_date.ts @@ -0,0 +1,286 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Plugin overview. + */ +import * as Blockly from 'blockly/core'; +import {getLocaleDateString} from './utils'; + +/** + * Class for a date input field. + */ +export class FieldDate extends Blockly.FieldTextInput { + /** + * Serializable fields are saved by the XML renderer, non-serializable fields + * are not. Editable fields should also be serializable. + */ + SERIALIZABLE = true; + + /** + * Class for a date input field. Derived from the Closure library date + * picker. + * + * @param value The initial value of the field. Should be in + * 'YYYY-MM-DD' format. Defaults to the current date. + * @param validator A function that is called to validate + * changes to the field's value. Takes in a date string & returns a + * validated date string ('YYYY-MM-DD' format), or null to abort the + * change. + * @param config A map of options used to configure the field. + */ + constructor( + value?: string, + validator?: FieldDateValidator, + config?: FieldDateConfig, + ) { + super(value, validator, config); + } + + /** + * Constructs a FieldDate from a JSON arg object. + * + * @param options A JSON object with options (date). + * @returns The new field instance. + * @package + * @nocollapse + */ + static fromJson(options: FieldDateFromJsonConfig): FieldDate { + const {date, ...fieldDateConfig} = options; + // `this` might be a subclass of FieldDate if that class doesn't + // override the static fromJson method. + return new this(date, undefined, fieldDateConfig); + } + + /* eslint-disable @typescript-eslint/naming-convention */ + /** + * Ensures that the input value is a valid date. Additionally, if the date + * string provided includes a time, the time will be removed and the date for + * relative to the user's timezone will be used. + * + * @param newValue The input value. Ex: '2023-04-28' + * @returns A valid date string, or null if invalid. + * @override + */ + protected doClassValidation_(newValue?: string): string | null { + if (!newValue) return null; + + const newDate = typeof newValue === 'string' ? new Date(newValue) : null; + if (!newDate || isNaN(newDate.getTime())) return null; + + // NOTE: 'newValue' should be a valid date format here. + if (isISOFormat(newValue)) return newValue; + + // Assume the time needs to be corrected. + return toLocalISOString(newDate); + } + + /** + * Get the text to display on the block when the input hasn't spawned in. + * + * @returns The text to display on the block. + * @override + */ + protected getText_(): string | null { + const value = this.getValue(); + if (!value) return null; + // NOTE: There may be discrepancies between the text and the input based on + // browser. For example, 'en-US' would display the text '2/14/2020', then + // clicking in Safari on iOS would display 'Feb 14, 2020'. + return getLocaleDateString(value); + } + + /** + * Returns a description of the type of this field for screenreaders. + */ + override getAriaTypeName() { + return Blockly.Msg['ARIA_TYPE_FIELD_DATE']; + } + + /** + * Returns a description of the current date for use by screenreaders. + */ + override getAriaValue() { + const stringValue = this.getValue(); + if (!stringValue) return super.getAriaValue(); + + const date = new Date(stringValue); + // Use a localized long-form description of the date, e.g. January XX, 20XX, + // rather than a short-form/ISO version of the date which may be read out + // with slashes or the like. + return date.toLocaleDateString(undefined, { + year: 'numeric', + month: 'long', + day: 'numeric', + timeZone: 'UTC', + }); + } + + /** + * Renders the field. If the picker is shown make sure it has the current + * date selected. + */ + protected render_() { + super.render_(); + } + + /** + * Shows the inline free-text editor on top of the text along with the date + * editor. + * + * @param e Optional mouse event that triggered the field to + * open, or undefined if triggered programmatically. + * @override + */ + protected showEditor_(e?: Event) { + // Pass in `true` for `quietInput` to disable modal inputs for the date + // block without setting `this.sourceBlock_.workspace.options.modalInputs`, + // which would impact the entire workspace. + super.showEditor_(e, true); + + // Even though `quietInput` was set true, focus on the element. + this.htmlInput_?.focus({ + preventScroll: true, + }); + this.htmlInput_?.select(); + this.showDropdown(); + } + + /** + * Updates the size of the field based on the text. + * + * @param margin margin to use when positioning the text element. + * @override + */ + protected updateSize_(margin?: number) { + // Add margin so that the date input's datepicker icon doesn't clip with + // the text when sized for the date. + super.updateSize_((margin ?? 0) + 20); + } + + /** + * Shows the datepicker. + */ + private showDropdown(): void { + if (!this.htmlInput_) return; + Blockly.utils.dom.addClass(this.htmlInput_, 'blocklyDateInput'); + + // Delay showing the picker until the editor has a chance to position + window.requestAnimationFrame(() => { + // NOTE: HTMLInputElement.showPicker() is not available in earlier + // TypeScript versions (like 4.7.4), so casting to `any` to be compatible + // with dev scripts. Additionally, it's not available for date inputs for + // Safari. For browser compatibility of showPicker, see: + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/showPicker + /* eslint-disable @typescript-eslint/no-explicit-any */ + (this.htmlInput_ as any).showPicker(); + /* eslint-enable @typescript-eslint/no-explicit-any */ + }); + } + + /** + * Create the html input and set it to type date. + * + * @returns The newly created date input editor. + */ + protected widgetCreate_(): HTMLInputElement { + // NOTE: field_input should return HTMLInputElement for this. + const htmlInput = super.widgetCreate_() as HTMLInputElement; + htmlInput.type = 'date'; + + return htmlInput; + } + /* eslint-enable @typescript-eslint/naming-convention */ +} + +/** + * NOTE: There are a few minor ways to tweak the datepicker CSS, though they're + * not consistent across browsers. + * + * @see{@link https://developer.mozilla.org/en-US/docs/Learn/Forms/Property_compatibility_table_for_form_controls#date_pickers} + * + * Below are a few ways this can be tweaked on *some* browsers: + * Blockly.Css.register(` + * ::-webkit-datetime-edit { } + * ::-webkit-datetime-edit-fields-wrapper { } + * ::-webkit-datetime-edit-text { } + * ::-webkit-datetime-edit-month-field { } + * ::-webkit-datetime-edit-day-field { } + * ::-webkit-datetime-edit-year-field { } + * ::-webkit-inner-spin-button { } + * ::-webkit-calendar-picker-indicator { } + * `); + */ +if (Blockly.utils.userAgent.MAC) { + // NOTE: By default, 4 px padding total are added within the User Agent + // Shadow Content on Safari on MAC. Remove the padding so the inner input + // matches the outer input's height and, by extension, the height of the text + // node. + Blockly.Css.register(` +input.blocklyDateInput::-webkit-datetime-edit, +input.blocklyDateInput::-webkit-datetime-edit-month-field, +input.blocklyDateInput::-webkit-datetime-edit-day-field, +input.blocklyDateInput::-webkit-datetime-edit-year-field { + padding: 0; +} +`); +} + +Blockly.fieldRegistry.register('field_date', FieldDate); + +/** + * A config object for defining a field date. + */ +export interface FieldDateConfig extends Blockly.FieldTextInputConfig { + // NOTE: spellcheck is defined for FieldInput though irrelevant for FieldDate. + spellcheck?: never; +} + +/** + * Options used to define a field date from JSON. + */ +export interface FieldDateFromJsonConfig extends FieldDateConfig { + date?: string; +} + +export type FieldDateValidator = Blockly.FieldTextInputValidator; + +/** + * Validate a string value to see if it matches the format. + * + * @param value The value to validate the format for. + * @returns true if the value is in 'yyyy-mm-dd' format. + * @example + * isISOFormat('2000-02-20T00:00:00Z') === false + * isISOFormat('2000-02-20') === true + */ +export function isISOFormat(value: string): boolean { + const valueMatch = value.match(/\d\d\d\d-\d\d-\d\d/); + // If it matches ####-##-## and is the same as its input string, + // then assume this is the right format + return valueMatch !== null && valueMatch[0] === valueMatch.input; +} + +/** + * Convert the date to ISO format for the current timezone. + * + * @param date The date to convert to an ISO string. + * @returns The string in 'yyyy-mm-dd' format, though for the current timezone. + * Ex: new Date('2000-02-20') + */ +export function toLocalISOString(date: Date) { + // NOTE: If the date is Feb 20, 2000 at 23:00 for GMT-6, it would be + // '2000-02-21' at GMT+0, which is what `date.toISOString()` would return. + // For a user whose timezone is GMT-6, this should return '2000-02-20'. + // For a user whose timezone is GMT-5, that date should return '2000-02-21'. + return date + .toLocaleDateString('en-US') + .replace(/(\d+)\/(\d+)\/(\d+)/, '$3-$1-$2') + .replace(/-(\d)(?!\d)/g, '-0$1'); +} + +// NOTE: Set default here instead of in class so it's available at Field. +FieldDate.prototype.DEFAULT_VALUE = toLocalISOString(new Date()); diff --git a/packages/plugins/field-date/src/index.ts b/packages/plugins/field-date/src/index.ts new file mode 100644 index 00000000000..480c3fe80de --- /dev/null +++ b/packages/plugins/field-date/src/index.ts @@ -0,0 +1,7 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './field_date'; diff --git a/packages/plugins/field-date/src/utils.ts b/packages/plugins/field-date/src/utils.ts new file mode 100644 index 00000000000..7059216d215 --- /dev/null +++ b/packages/plugins/field-date/src/utils.ts @@ -0,0 +1,20 @@ +/** + * Get the string formatted locally to the user. + * + * @param dateString A string in the format 'yyyy-mm-dd' + * @returns the locale date string for the date. + */ +export function getLocaleDateString(dateString: string): string { + // NOTE: `date.toLocaleDateString()` will be the day before for western dates + // due to an unspecified time & timezone assuming midnight at GMT+0. + const date = new Date(dateString); + + // NOTE: This format varies per region. + // Ex: "5/12/2023", "12/05/2023", "12.5.2023", "2023/5/12", "१२/५/२०२३" + const language = navigator.language ?? 'en-US'; + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat#using_options + return new Intl.DateTimeFormat(language, { + // Print the date for GMT+0 since the date object assumes midnight at GMT+0. + timeZone: 'UTC', + }).format(date); +} diff --git a/packages/plugins/field-date/test/field_date_test.mocha.js b/packages/plugins/field-date/test/field_date_test.mocha.js new file mode 100644 index 00000000000..f818c08ce80 --- /dev/null +++ b/packages/plugins/field-date/test/field_date_test.mocha.js @@ -0,0 +1,267 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {assert} from 'chai'; +import {testHelpers} from '@blockly/dev-tools'; +import {FieldDate} from '../src/index'; +import {getLocaleDateString} from '../src/utils'; + +const { + assertFieldValue, + FieldCreationTestCase, + FieldValueTestCase, + runConstructorSuiteTests, + runFromJsonSuiteTests, + runSetValueTests, +} = testHelpers; + +// Add polyfill for global variables needed. +if (!global.navigator) { + global.navigator = { + language: 'en-US', + }; +} + +suite('FieldDate', function () { + /** + * Configuration for field tests with invalid values. + * @type {Array} + */ + const invalidValueTestCases = [ + {title: 'Undefined', value: undefined}, + {title: 'Null', value: null}, + {title: 'NaN', value: NaN}, + {title: 'Non-Parsable String', value: 'bad'}, + {title: 'Invalid Date - Month(2020-13-20)', value: '2020-13-20'}, + {title: 'Invalid Date - Day(2020-02-32)', value: '2020-02-32'}, + ]; + /** + * Configuration for field tests with valid values. + * @type {Array} + */ + const validValueTestCases = [ + { + title: 'String', + value: '3030-03-30', + expectedValue: '3030-03-30', + expectedText: '3/30/3030', + }, + ]; + const addArgsAndJson = function (testCase) { + testCase.args = [testCase.value]; + testCase.json = {date: testCase.value}; + }; + invalidValueTestCases.forEach(addArgsAndJson); + validValueTestCases.forEach(addArgsAndJson); + // Construct ISO string using current timezone. + // Cannot use toISOString() because it returns in UTC. + const defaultFieldValue = new Date() + .toLocaleDateString('en-US') + .replace(/(\d+)\/(\d+)\/(\d+)/, '$3-$1-$2') + .replace(/-(\d)(?!\d)/g, '-0$1'); + // NOTE: The actual text depends on system settings of the one running it. + const defaultFieldText = new Date().toLocaleDateString(); + const assertFieldDefault = function (field) { + assertFieldValue(field, defaultFieldValue, defaultFieldText); + }; + + /** + * Asserts that the field properties are correct based on the test case. + * @param {FieldDate} field The field to check. + * @param {FieldValueTestCase} testCase The test case. + */ + const validTestCaseAssertField = function (field, testCase) { + assertFieldValue(field, testCase.expectedValue, testCase.expectedText); + }; + + runConstructorSuiteTests( + FieldDate, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + runFromJsonSuiteTests( + FieldDate, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + suite('setValue', function () { + suite('Empty -> New Value', function () { + setup(function () { + this.field = new FieldDate(); + }); + runSetValueTests( + validValueTestCases, + invalidValueTestCases, + defaultFieldValue, + defaultFieldText, + ); + }); + suite('Value -> New Value', function () { + const initialValue = '2020-02-20'; + const initialText = getLocaleDateString(initialValue); + setup(function () { + this.field = new FieldDate(initialValue); + }); + runSetValueTests( + validValueTestCases, + invalidValueTestCases, + initialValue, + initialText, + ); + }); + }); + + suite('Validators', function () { + setup(function () { + this.field = new FieldDate('2020-02-20'); + }); + teardown(function () { + this.field.setValidator(null); + }); + suite('when validator returns null', function () { + setup(function () { + this.field.setValidator(() => { + return null; + }); + }); + test('should not set the new value', function () { + this.field.setValue('3030-03-30'); + assertFieldValue(this.field, '2020-02-20', '2/20/2020'); + }); + }); + suite('when validator sets day to 20s', function () { + setup(function () { + this.field.setValidator(function (newValue) { + return newValue.substr(0, 8) + '2' + newValue.substr(9, 1); + }); + }); + test('should set the value to a "20s" date', function () { + this.field.setValue('3030-03-30'); + assertFieldValue(this.field, '3030-03-20', '3/20/3030'); + }); + }); + suite('when validator returns undefined', function () { + setup(function () { + this.field.setValidator(() => {}); + }); + test('should set the value without changing it', function () { + this.field.setValue('3030-03-30'); + assertFieldValue(this.field, '3030-03-30', '3/30/3030'); + }); + }); + }); + + suite('Time Zones', function () { + // https://nodejs.org/api/cli.html#tz + const INITIAL_TZ = process.env.TZ; + const TZ_STRINGS = { + WESTERN: 'America/New_York', + UNIVERSAL: 'Etc/Universal', + EASTERN: 'Europe/Paris', + }; + const TZ_OFFSET = { + WESTERN: '-05:00', + UNIVERSAL: '+00:00', + EASTERN: '+01:00', + }; + + suite('Western', () => { + setup(() => { + process.env.TZ = TZ_STRINGS.WESTERN; + }); + + test('should return the same date when provided a date string', () => { + const dateString = '2000-01-23'; + const fieldDate = new FieldDate(dateString); + assert.equal(fieldDate.getValue(), dateString); + }); + + test('should return the same date for 23:00 western datetime', () => { + const dateString = '2000-01-23'; + const dateTimeString = dateString + 'T23:00:00' + TZ_OFFSET.WESTERN; + const fieldDate = new FieldDate(dateTimeString); + assert.equal(fieldDate.getValue(), dateString); + }); + + test('should return the day before for 00:00 universal datetime', () => { + const dateString = '2000-01-23'; + const dateTimeString = dateString + 'T00:00:00' + TZ_OFFSET.UNIVERSAL; + const fieldDate = new FieldDate(dateTimeString); + assert.equal(fieldDate.getValue(), '2000-01-22'); + }); + + teardown(() => { + process.env.TSZ = INITIAL_TZ; + }); + }); + + suite('Universal', () => { + setup(() => { + process.env.TZ = TZ_STRINGS.UNIVERSAL; + }); + + test('should return the same date when provided a date string', () => { + const dateString = '2000-01-23'; + const fieldDate = new FieldDate(dateString); + assert.equal(fieldDate.getValue(), dateString); + }); + + test('should return the day after for 23:00 western datetime', () => { + const dateString = '2000-01-23'; + const dateTimeString = dateString + 'T23:00:00' + TZ_OFFSET.WESTERN; + const fieldDate = new FieldDate(dateTimeString); + assert.equal(fieldDate.getValue(), '2000-01-24'); + }); + + test('should return the same date for 00:00 universal datetime', () => { + const dateString = '2000-01-23'; + const dateTimeString = dateString + 'T00:00:00' + TZ_OFFSET.UNIVERSAL; + const fieldDate = new FieldDate(dateTimeString); + assert.equal(fieldDate.getValue(), dateString); + }); + + teardown(() => { + process.env.TSZ = INITIAL_TZ; + }); + }); + + suite('Eastern', () => { + setup(() => { + process.env.TZ = TZ_STRINGS.EASTERN; + }); + + test('should return the same date when provided a date string', () => { + const dateString = '2000-01-23'; + const fieldDate = new FieldDate(dateString); + assert.equal(fieldDate.getValue(), dateString); + }); + + test('should return the day after for 23:00 universal datetime', () => { + const dateString = '2000-01-23'; + const dateTimeString = dateString + 'T23:00:00' + TZ_OFFSET.UNIVERSAL; + const fieldDate = new FieldDate(dateTimeString); + assert.equal(fieldDate.getValue(), '2000-01-24'); + }); + + test('should return the same date for 00:00 eastern datetime', () => { + const dateString = '2000-01-23'; + const dateTimeString = dateString + 'T00:00:00' + TZ_OFFSET.EASTERN; + const fieldDate = new FieldDate(dateTimeString); + assert.equal(fieldDate.getValue(), dateString); + }); + + teardown(() => { + process.env.TSZ = INITIAL_TZ; + }); + }); + }); +}); diff --git a/packages/plugins/field-date/test/index.html b/packages/plugins/field-date/test/index.html new file mode 100644 index 00000000000..1f568ca9998 --- /dev/null +++ b/packages/plugins/field-date/test/index.html @@ -0,0 +1,18 @@ + + + + + Blockly Field Date Playground + + + + +
+ + + diff --git a/packages/plugins/field-date/test/index.ts b/packages/plugins/field-date/test/index.ts new file mode 100644 index 00000000000..050763dab19 --- /dev/null +++ b/packages/plugins/field-date/test/index.ts @@ -0,0 +1,231 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Plugin test. + */ + +import * as Blockly from 'blockly'; +import {createPlayground} from '@blockly/dev-tools'; +import '../src/index'; + +/** + * An array of blocks that are defined only for the purposes of + * manually and visually testing the date field. + */ +const testBlockDefinitions = [ + { + type: 'test_standard_field_values', + message0: '%1', + args0: [ + { + type: 'field_date', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_date`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values', + message0: '%1', + args0: [ + { + type: 'field_date', + name: 'FIELDNAME', + date: '2020-02-20', + alt: { + type: 'field_label', + text: `No field_date`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_standard_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_date', + name: 'FIELDNAME', + alt: { + type: 'field_label', + text: `No field_date`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_date', + name: 'FIELDNAME', + date: '2020-02-20', + alt: { + type: 'field_label', + text: `No field_date`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_parent_block', + message0: 'parent %1', + args0: [ + { + type: 'input_value', + name: 'INPUT', + }, + ], + previousStatement: null, + nextStatement: null, + style: 'loop_blocks', + }, + { + type: 'test_custom_field_values_and_tooltip', + message0: 'block %1', + args0: [ + { + type: 'field_date', + name: 'FIELDNAME', + date: '2021-03-13', + tooltip: 'This date block has a tooltip!', + alt: { + type: 'field_label', + text: `No field_date`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, +]; + +Blockly.defineBlocksWithJsonArray(testBlockDefinitions); + +/** + * A test toolbox containing the exported blocks and a variety of + * test blocks to exercise the date field in different contexts + * (on a shadow block, as the only field on a block, etc). + * These are in a simple toolbox, rather than a category toolbox, so that + * they are all instantiated every time the test page is opened. + */ +const jsonToolbox = { + contents: [ + { + kind: 'label', + text: 'Test blocks: default field values', + }, + { + kind: 'block', + type: 'test_standard_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_standard_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values_and_label', + }, + }, + }, + }, + { + kind: 'label', + text: 'Test blocks: custom field values', + }, + { + kind: 'block', + type: 'test_custom_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_custom_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values_and_label', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_custom_field_values_and_tooltip', + }, + ], +}; + +/** + * Create a workspace. + * + * @param blocklyDiv The blockly container div. + * @param options The Blockly options. + * @returns The created workspace. + */ +function createWorkspace( + blocklyDiv: HTMLElement, + options: Blockly.BlocklyOptions, +): Blockly.WorkspaceSvg { + const workspace = Blockly.inject(blocklyDiv, options); + return workspace; +} + +document.addEventListener('DOMContentLoaded', function () { + const defaultOptions: Blockly.BlocklyOptions = { + toolbox: jsonToolbox, + }; + const rootElement = document.getElementById('root'); + if (rootElement) { + // NOTE: Will need to update @blockly/dev-tools package JSON to keep it's + // version of Blockly in sync with this package's. Both call for ^9.0.0, + // though the package-locks result in each installing different versions. + createPlayground(rootElement, createWorkspace, defaultOptions); + } +}); diff --git a/packages/plugins/field-date/tsconfig.json b/packages/plugins/field-date/tsconfig.json new file mode 100644 index 00000000000..99858c525f1 --- /dev/null +++ b/packages/plugins/field-date/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "module": "es2015", + "moduleResolution": "bundler", + "target": "es6", + "strict": true + }, + // NOTE: `test/**/*` is automatically included in `blockly-scripts start`. + // Only src matters for production builds. + "include": ["src"] +} diff --git a/packages/plugins/field-dependent-dropdown/CHANGELOG.md b/packages/plugins/field-dependent-dropdown/CHANGELOG.md new file mode 100644 index 00000000000..568ad4ffd0a --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/CHANGELOG.md @@ -0,0 +1,500 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [13.1.0](https://github.com/RaspberryPiFoundation/blockly-samples/compare/v13.0.0...v13.1.0) (2026-06-30) + +### ⚠ BREAKING CHANGES + +* make field-multilineinput keyboard navigable and accessible (#2729) +* Bump Blockly dependency to v13 (#2704) + +### Features + +* make field-multilineinput keyboard navigable and accessible ([#2729](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2729)) + +### Miscellaneous Chores + +* Bump Blockly dependency to v13 ([#2704](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2704)) + + +## [5.0.10](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.9...@blockly/field-dependent-dropdown@5.0.10) (2026-04-09) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [5.0.9](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.8...@blockly/field-dependent-dropdown@5.0.9) (2026-01-15) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [5.0.8](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.7...@blockly/field-dependent-dropdown@5.0.8) (2026-01-05) + + +### Bug Fixes + +* update plugin URLs to RPF ([#2665](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2665)) ([6c34039](https://github.com/RaspberryPiFoundation/blockly-samples/commit/6c3403960b8070eb8e576bd5702bdb53a09ac475)) + + + +## [5.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.6...@blockly/field-dependent-dropdown@5.0.7) (2026-01-05) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [5.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.5...@blockly/field-dependent-dropdown@5.0.6) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [5.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.4...@blockly/field-dependent-dropdown@5.0.5) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [5.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.3...@blockly/field-dependent-dropdown@5.0.4) (2025-10-30) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [5.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.2...@blockly/field-dependent-dropdown@5.0.3) (2025-10-02) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [5.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.1...@blockly/field-dependent-dropdown@5.0.2) (2025-07-17) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [5.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@5.0.0...@blockly/field-dependent-dropdown@5.0.1) (2025-05-22) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [5.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.16...@blockly/field-dependent-dropdown@5.0.0) (2025-05-16) + + +### ⚠ BREAKING CHANGES + +* update all plugins to blockly v12 (#2538) + +### Features + +* update all plugins to blockly v12 ([#2538](https://github.com/google/blockly-samples/issues/2538)) ([420ba31](https://github.com/google/blockly-samples/commit/420ba3124b260b2c06e8e1d61be44e870780d95f)) + + + +## [4.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.15...@blockly/field-dependent-dropdown@4.0.16) (2025-05-16) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.14...@blockly/field-dependent-dropdown@4.0.15) (2025-05-15) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.13...@blockly/field-dependent-dropdown@4.0.14) (2025-02-13) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.12...@blockly/field-dependent-dropdown@4.0.13) (2024-12-19) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.11...@blockly/field-dependent-dropdown@4.0.12) (2024-12-03) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.10...@blockly/field-dependent-dropdown@4.0.11) (2024-11-07) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.9...@blockly/field-dependent-dropdown@4.0.10) (2024-09-26) + + +### Bug Fixes + +* **field-*:** Remove unneeded `fieldRegistry.unregister` calls ([#2454](https://github.com/google/blockly-samples/issues/2454)) ([b3ba30e](https://github.com/google/blockly-samples/commit/b3ba30e23dddf0bd98c266659aa229ba6ba685b0)), closes [#2453](https://github.com/google/blockly-samples/issues/2453) + + + + + +## [4.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.8...@blockly/field-dependent-dropdown@4.0.9) (2024-08-29) + + +### Bug Fixes + +* predeploy plugins ([#2449](https://github.com/google/blockly-samples/issues/2449)) ([6b36d8b](https://github.com/google/blockly-samples/commit/6b36d8b344a969f79d89bbc7dcee29ae554759f9)) + + + + + +## [4.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.7...@blockly/field-dependent-dropdown@4.0.8) (2024-08-22) + + +### Bug Fixes + +* lerna v8 breaking our whole shebangle ([#2446](https://github.com/google/blockly-samples/issues/2446)) ([b8b4c21](https://github.com/google/blockly-samples/commit/b8b4c21d4eaf81f527336ae46f6679ff99ac23c3)) + + + + + +## [4.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.6...@blockly/field-dependent-dropdown@4.0.7) (2024-08-08) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.5...@blockly/field-dependent-dropdown@4.0.6) (2024-08-01) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.4...@blockly/field-dependent-dropdown@4.0.5) (2024-07-25) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.3...@blockly/field-dependent-dropdown@4.0.4) (2024-07-11) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.2...@blockly/field-dependent-dropdown@4.0.3) (2024-07-04) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.1...@blockly/field-dependent-dropdown@4.0.2) (2024-06-06) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [4.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@4.0.0...@blockly/field-dependent-dropdown@4.0.1) (2024-05-30) + + +### Bug Fixes + +* multiple blockly instances ([#2375](https://github.com/google/blockly-samples/issues/2375)) ([b231e59](https://github.com/google/blockly-samples/commit/b231e598f2f5f5b0abbfd01d981e35572ad50a26)) + + + +## [4.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.17...@blockly/field-dependent-dropdown@4.0.0) (2024-05-21) + + +### ⚠ BREAKING CHANGES + +* update all plugins to v11 of blockly +* **dev-scripts:** This PR removes the support that was added + +### Features + +* update all plugins to v11 of blockly ([5fcd3e7](https://github.com/google/blockly-samples/commit/5fcd3e7d53eaadffe9bda9a378b404d34b2f8be2)) +* upgrade ts version, fix field validators ([b2d2191](https://github.com/google/blockly-samples/commit/b2d2191ff11b30347b33f95acf6e58f7ce54d004)) + + +### Bug Fixes + +* Call blocks handle both manual disabling and disabled defs ([#2334](https://github.com/google/blockly-samples/issues/2334)) ([5eade55](https://github.com/google/blockly-samples/commit/5eade55779c4022d14ad4472ff32c93c78199887)) +* **dev-scripts:** Fixes, refactoring and simplification of `webpack.config.js` and `'blockly'` imports ([#2229](https://github.com/google/blockly-samples/issues/2229)) ([f5ffdb9](https://github.com/google/blockly-samples/commit/f5ffdb961e3b60ddb164087f4bddc4e6215906b7)), closes [#335](https://github.com/google/blockly-samples/issues/335) [#335](https://github.com/google/blockly-samples/issues/335) [#1630](https://github.com/google/blockly-samples/issues/1630) [#335](https://github.com/google/blockly-samples/issues/335) [#226](https://github.com/google/blockly-samples/issues/226) [google/blockly#7822](https://github.com/google/blockly/issues/7822) [google/blockly#7822](https://github.com/google/blockly/issues/7822) [/github.com/google/blockly-samples/pull/2229#issuecomment-1979123919](https://github.com/google//github.com/google/blockly-samples/pull/2229/issues/issuecomment-1979123919) + + + +## [3.0.17](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.16...@blockly/field-dependent-dropdown@3.0.17) (2024-05-16) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.15...@blockly/field-dependent-dropdown@3.0.16) (2024-05-09) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.14...@blockly/field-dependent-dropdown@3.0.15) (2024-04-25) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.13...@blockly/field-dependent-dropdown@3.0.14) (2024-04-04) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.12...@blockly/field-dependent-dropdown@3.0.13) (2024-03-28) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.11...@blockly/field-dependent-dropdown@3.0.12) (2024-02-08) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.10...@blockly/field-dependent-dropdown@3.0.11) (2023-12-14) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.9...@blockly/field-dependent-dropdown@3.0.10) (2023-12-07) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.8...@blockly/field-dependent-dropdown@3.0.9) (2023-11-16) + + +### Bug Fixes + +* resolved 15 ESLint warnings ([#2065](https://github.com/google/blockly-samples/issues/2065)) ([2436337](https://github.com/google/blockly-samples/commit/243633746542bb1518fe2893c0421a5a6f79fd32)) + + + +## [3.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.7...@blockly/field-dependent-dropdown@3.0.8) (2023-11-09) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.6...@blockly/field-dependent-dropdown@3.0.7) (2023-11-02) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.5...@blockly/field-dependent-dropdown@3.0.6) (2023-10-30) + + +### Bug Fixes + +* **packaging:** Don't advertise non-existent ESM entrypoints ([#2022](https://github.com/google/blockly-samples/issues/2022)) ([d019198](https://github.com/google/blockly-samples/commit/d0191984399b784e2928b8fb4c58257bfa857655)), closes [#1877](https://github.com/google/blockly-samples/issues/1877) +* Publish types for field-dependent-dropdown ([#1961](https://github.com/google/blockly-samples/issues/1961)) ([9c96dbe](https://github.com/google/blockly-samples/commit/9c96dbeea63b71ce0f5e8be7b8acaf44e77fc8ed)) +* update tsconfigs to publish types accurately ([df4b084](https://github.com/google/blockly-samples/commit/df4b0844af712f5025a2ec842458b828f3147676)) +* use includes rather than excludes ([40efc25](https://github.com/google/blockly-samples/commit/40efc255329e3ca476ccc247b95a2d05dd77b45e)) + + + +## [3.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.4...@blockly/field-dependent-dropdown@3.0.5) (2023-09-21) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.3...@blockly/field-dependent-dropdown@3.0.4) (2023-09-14) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.2...@blockly/field-dependent-dropdown@3.0.3) (2023-08-17) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.1...@blockly/field-dependent-dropdown@3.0.2) (2023-07-24) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@3.0.0...@blockly/field-dependent-dropdown@3.0.1) (2023-07-20) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [3.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@2.0.1...@blockly/field-dependent-dropdown@3.0.0) (2023-07-13) + + +### ⚠ BREAKING CHANGES + +* updated `plugins/field-date` to TypeScript (#1705) + +### Features + +* updated `plugins/field-date` to TypeScript ([#1705](https://github.com/google/blockly-samples/issues/1705)) ([e5531ff](https://github.com/google/blockly-samples/commit/e5531fffe188ee361a16fe48ed126b34e51a8d30)) + + + +## [2.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@2.0.0...@blockly/field-dependent-dropdown@2.0.1) (2023-07-06) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [2.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@1.1.5...@blockly/field-dependent-dropdown@2.0.0) (2023-06-28) + + +### ⚠ BREAKING CHANGES + +* update all plugins to use blockly-v10.0.0 (#1744) + +### Features + +* update all plugins to use blockly-v10.0.0 ([#1744](https://github.com/google/blockly-samples/issues/1744)) ([6cc88cb](https://github.com/google/blockly-samples/commit/6cc88cbef39d4ad664a668d3d46eb29ba7292f9c)) + + +### Bug Fixes + +* renaming build problems ([7087469](https://github.com/google/blockly-samples/commit/7087469ccdaf27e040cd67a386c9284aa6b1cd67)) + + + +## [1.1.5](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@1.1.4...@blockly/field-dependent-dropdown@1.1.5) (2023-06-22) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [1.1.4](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@1.1.3...@blockly/field-dependent-dropdown@1.1.4) (2023-05-18) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [1.1.3](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@1.1.2...@blockly/field-dependent-dropdown@1.1.3) (2023-05-11) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [1.1.2](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@1.1.1...@blockly/field-dependent-dropdown@1.1.2) (2023-05-04) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## [1.1.1](https://github.com/google/blockly-samples/compare/@blockly/field-dependent-dropdown@1.1.0...@blockly/field-dependent-dropdown@1.1.1) (2023-04-27) + +**Note:** Version bump only for package @blockly/field-dependent-dropdown + + + + + +## 1.1.0 (2023-03-23) + + +### Features + +* Create plugin for dependent dropdown fields. ([#1555](https://github.com/google/blockly-samples/issues/1555)) ([1c473d6](https://github.com/google/blockly-samples/commit/1c473d6b2c4aa3754cf332c07585af60342537ef)) diff --git a/packages/plugins/field-dependent-dropdown/README.md b/packages/plugins/field-dependent-dropdown/README.md new file mode 100644 index 00000000000..0b342767dff --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/README.md @@ -0,0 +1,105 @@ +# @blockly/field-dependent-dropdown [![Built on Blockly](https://tinyurl.com/built-on-blockly)](https://github.com/google/blockly) + +A [Blockly](https://www.npmjs.com/package/blockly) dropdown field where the options depend on the value of a parent field. + +## Installation + +### Yarn + +``` +yarn add @blockly/field-dependent-dropdown +``` + +### npm + +``` +npm install @blockly/field-dependent-dropdown --save +``` + +## Usage + +This plugin adds a field type `FieldDependentDropdown` that is an extension of `Blockly.FieldDropdown` and is registered as `'field_dependent_dropdown'` for the JSON API. You can associate it with a parent field that is attached to the same block, along with a mapping from the parent field's possible values to the desired menu options for this child field. Whenever the parent field's value changes, this field will automatically change its own available options to the options that correspond to the new parent value. You can also provide a set of default options that will be used if the parent field's value doesn't match any of the keys in your option mapping. + +These changes are recorded properly in the undo history, and the fields can be [serialized and later deserialized](https://developers.google.com/blockly/guides/configure/web/serialization) while preserving their options, values, and validity. You can also create chains of dependent dropdowns that depend on other dependent dropdowns. + +Note that the parent field must be attached to the block before the child field, and the child field will attach a validator function to the parent field to intercept changes to its value. If you want to add your own [custom field validator](https://developers.google.com/blockly/guides/create-custom-blocks/fields/validators) to the parent field, you need to use [the JavaScript API to define your block](https://developers.google.com/blockly/guides/create-custom-blocks/define/json-and-js) and [pass your validator to the parent field's constructor](https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/dropdown#creating_a_dropdown_validator). If you try to set the parent's validator later, you'll overwrite the one added by this plugin. + +To create a dependent dropdown, you'll need to add this field type to a block definition, and add that block to your toolbox. See below for an example of defining a block that uses this field. + +### JSON + +```js +import * as Blockly from 'blockly'; +import '@blockly/field-dependent-dropdown'; // Import with side effects. + +Blockly.defineBlocksWithJsonArray([ + { + 'type': 'dependent_dropdown_example', + 'message0': 'Category %1 Animal %2', + 'args0': [ + { + 'type': 'field_dropdown', + 'name': 'ANIMAL_CATEGORY', + 'options': [['Mammal', 'mammal'], ['Bird', 'bird'], ['Cryptid', 'cryptid']] + }, + { + 'type': 'field_dependent_dropdown', + 'name': 'ANIMAL', + 'parentName': 'ANIMAL_CATEGORY', + 'optionMapping': { + 'mammal': [['Dog', 'dog'], ['Cat', 'cat'], ['Hamster', 'hamster']], + 'bird': [['Parakeet', 'parakeet'], ['Canary', 'canary']] + } + 'defaultOptions': [['None available', 'noneAvailable']], + } + ] + } +]); +``` + +### JavaScript + +```js +import * as Blockly from 'blockly'; +import {FieldDependentDropdown} from '@blockly/field-dependent-dropdown'; + +Blockly.Blocks['dependent_dropdown_example'] = { + init: function () { + const parentFieldName = 'ANIMAL_CATEGORY'; + const childFieldName = 'ANIMAL'; + const parentOptions = [ + ['Mammal', 'mammal'], + ['Bird', 'bird'], + ['Cryptid', 'cryptid'], + ]; + const optionMapping = { + mammal: [ + ['Dog', 'dog'], + ['Cat', 'cat'], + ['Hamster', 'hamster'], + ], + bird: [ + ['Parakeet', 'parakeet'], + ['Canary', 'canary'], + ], + }; + const defaultOptions = [['None available', 'noneAvailable']]; + this.appendDummyInput() + .appendField('Category') + .appendField(new Blockly.FieldDropdown(parentOptions), parentFieldName) + .appendField('Animal') + .appendField( + new FieldDependentDropdown( + parentFieldName, + optionMapping, + defaultOptions, + ), + childFieldName, + ); + }, +}; +``` + +## License + +Apache 2.0 diff --git a/packages/plugins/field-dependent-dropdown/package.json b/packages/plugins/field-dependent-dropdown/package.json new file mode 100644 index 00000000000..6dc45761ac0 --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/package.json @@ -0,0 +1,56 @@ +{ + "name": "@blockly/field-dependent-dropdown", + "version": "13.1.0", + "description": "A Blockly dropdown field that automatically updates its available options depending on the value of another field.", + "scripts": { + "audit:fix": "blockly-scripts auditFix", + "build": "blockly-scripts build", + "clean": "blockly-scripts clean", + "lint": "eslint .", + "predeploy": "blockly-scripts predeploy", + "start": "blockly-scripts start", + "test": "blockly-scripts test" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "unpkg": "./dist/index.js", + "author": "Blockly Team", + "keywords": [ + "blockly", + "colour", + "field", + "hsv", + "slider" + ], + "homepage": "https://github.com/RaspberryPiFoundation/blockly-samples/tree/master/plugins/field-dependent-dropdown#readme", + "bugs": { + "url": "https://github.com/RaspberryPiFoundation/blockly-samples/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/RaspberryPiFoundation/blockly-samples.git", + "directory": "plugins/field-dependent-dropdown" + }, + "license": "Apache-2.0", + "directories": { + "dist": "dist", + "src": "src" + }, + "files": [ + "dist", + "src" + ], + "devDependencies": { + "@blockly/dev-scripts": "^13.1.0", + "@blockly/dev-tools": "^13.1.0", + "chai": "^6.2.2", + "sinon": "^22.0.0", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "blockly": "^13.2.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugins/field-dependent-dropdown/src/dependent_dropdown_options_change.ts b/packages/plugins/field-dependent-dropdown/src/dependent_dropdown_options_change.ts new file mode 100644 index 00000000000..ec8efd5ed39 --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/src/dependent_dropdown_options_change.ts @@ -0,0 +1,233 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview An event representing when a dependent dropdown field changes + * state. + */ + +import * as Blockly from 'blockly/core'; +import type {FieldDependentDropdown} from './field_dependent_dropdown'; + +/** + * A deep equality comparison between the two provided arrays recursively + * comparing any child elements that are also arrays. + * + * @param a The first array to compare. + * @param b The second array to compare. + * @returns Whether the arrays are deeply equivalent. + */ +function arraysAreEquivalent(a: T[], b: T[]): boolean { + return ( + a.length === b.length && + a.every((aElement, index) => { + const bElement = b[index]; + if (Array.isArray(aElement) && Array.isArray(bElement)) { + return arraysAreEquivalent(aElement, bElement); + } + return aElement === bElement; + }) + ); +} + +/** The structure of a serialized DependentDropdownOptionsChange. */ +export interface DependentDropdownOptionsChangeJson + extends Blockly.Events.BlockBaseJson { + name: string; + newValue: string; + oldValue: string; + oldOptions: Blockly.MenuOption[]; + newOptions: Blockly.MenuOption[]; +} + +/** + * A change event representing a simultaneous change to a dropdown field's + * options and value. The old value must be one of the old options, and the new + * value must be one of the new options. Unlike a normal value change event, + * it's possible for this event to change the value to something that wasn't + * previously one of the valid options--in either direction--by also changing + * the options at the same time. + */ +export class DependentDropdownOptionsChange extends Blockly.Events.BlockBase { + /** The name to register with Blockly for the type of event. */ + + static readonly EVENT_TYPE: string = 'dropdown_options_change'; + + /** The name of the change event type for registering with Blockly. */ + readonly type = DependentDropdownOptionsChange.EVENT_TYPE; + + /** The name of the field that changed. */ + name?: string; + + /** The original value of the field. */ + oldValue?: string; + + /** The new value of the field. */ + newValue?: string; + + /** The original available options for the dropdown field. */ + oldOptions?: Blockly.MenuOption[]; + + /** The new available options for the dropdown field. */ + newOptions?: Blockly.MenuOption[]; + + /** + * Construct a new DependentDropdownOptionsChange. + * + * @param block The changed block. Undefined for a blank event. + * @param name Name of the field affected. + * @param oldValue Previous value of field. + * @param newValue New value of field. + * @param oldOptions Previous options for the dropdown. + * @param newOptions New options for the dropdown. + */ + constructor( + block?: Blockly.Block, + name?: string, + oldValue?: string, + newValue?: string, + oldOptions?: Blockly.MenuOption[], + newOptions?: Blockly.MenuOption[], + ) { + super(block); + + if ( + !block || + !name || + !oldValue || + !newValue || + !oldOptions || + !newOptions + ) { + // Blank event to be populated by fromJson. + return; + } + + this.name = name; + this.oldValue = oldValue; + this.newValue = newValue; + this.oldOptions = oldOptions; + this.newOptions = newOptions; + } + + /** + * Encode the event as JSON. + * + * @returns JSON representation. + */ + toJson(): DependentDropdownOptionsChangeJson { + const json = super.toJson() as DependentDropdownOptionsChangeJson; + if ( + !this.name || + !this.oldValue || + !this.newValue || + !this.oldOptions || + !this.newOptions + ) { + throw new Error( + 'The changed element is undefined. Either pass all needed ' + + 'parameters to the constructor, or call fromJson.', + ); + } + json['name'] = this.name; + json['oldValue'] = this.oldValue; + json['newValue'] = this.newValue; + json['oldOptions'] = this.oldOptions; + json['newOptions'] = this.newOptions; + return json; + } + + /** + * Decode the JSON event. + * + * @param json JSON representation. + * @param workspace + * @param event + * @returns The deserialized event. + */ + static fromJson( + json: DependentDropdownOptionsChangeJson, + workspace: Blockly.Workspace, + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + event?: any, + ): DependentDropdownOptionsChange { + const newEvent = super.fromJson( + json, + workspace, + event, + ) as DependentDropdownOptionsChange; + newEvent.name = json['name']; + newEvent.oldValue = json['oldValue']; + newEvent.newValue = json['newValue']; + newEvent.oldOptions = json['oldOptions']; + newEvent.newOptions = json['newOptions']; + return newEvent; + } + + /** + * Does this event leave all state as it was before? + * + * @returns False if something changed. + */ + isNull(): boolean { + const valuesAreEqual = this.oldValue === this.newValue; + const optionsAreEquivalent = + this.oldOptions === this.newOptions || + (Array.isArray(this.oldOptions) && + Array.isArray(this.newOptions) && + arraysAreEquivalent(this.oldOptions, this.newOptions)); + return valuesAreEqual && optionsAreEquivalent; + } + + /** + * Run a change event. + * + * @param forward True if run forward, false if run backward (undo). + */ + run(forward: boolean): void { + if ( + !this.blockId || + !this.name || + !this.oldValue || + !this.newValue || + !this.oldOptions || + !this.newOptions + ) { + console.warn("Can't run uninitialized event."); + return; + } + const workspace = this.getEventWorkspace_(); + const block = workspace.getBlockById(this.blockId); + if (!block) { + console.warn("Can't change non-existent block: " + this.blockId); + return; + } + + const dropdown = block.getField(this.name) as FieldDependentDropdown; + if (!dropdown) { + console.warn("Can't change non-existent dropdown field: " + this.name); + return; + } + + const value = forward ? this.newValue : this.oldValue; + const options = forward ? this.newOptions : this.oldOptions; + + // Record the options on the dropdown for the option generator to access. + dropdown.dependencyData.derivedOptions = options; + + // Re-run the option generator to update the options on the dropdown. + dropdown.getOptions(false); + + // Set the value to one of the now-available options. + dropdown.setValue(value); + } +} + +Blockly.registry.register( + Blockly.registry.Type.EVENT, + DependentDropdownOptionsChange.EVENT_TYPE, + DependentDropdownOptionsChange, +); diff --git a/packages/plugins/field-dependent-dropdown/src/field_dependent_dropdown.ts b/packages/plugins/field-dependent-dropdown/src/field_dependent_dropdown.ts new file mode 100644 index 00000000000..e2348bcd4d6 --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/src/field_dependent_dropdown.ts @@ -0,0 +1,319 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview A Blockly dropdown menu field where the options can change + * depending on the value of another field. + */ + +import * as Blockly from 'blockly/core'; +import {DependentDropdownOptionsChange} from './dependent_dropdown_options_change'; + +/** The type of the mapping from parent value to child options. */ +export interface ChildOptionMapping { + [key: string]: Blockly.MenuOption[]; +} + +// This type isn't exported from Blockly so we have to derive it from the API. +type FieldConfig = Exclude< + ConstructorParameters[2], + undefined +>; + +/** fromJson config for a dependent dropdown field. */ +export interface FieldDependentDropdownFromJsonConfig extends FieldConfig { + parentName: string; + optionMapping: ChildOptionMapping; + defaultOptions?: Blockly.MenuOption[]; +} + +/** + * A structure for managing data needed by the menu generator of a + * FieldDependentDropdown. + */ +interface DependencyData { + /** + * A reference to the parent field of an associated dependent dropdown. Absent + * until the parent field is initialized. This field's value determines the + * available options of the child field. + */ + parentField?: Blockly.Field; + + /** + * The child field's currently available menu options based on the current + * value of the parent field. Absent until the parent field is initialized. + */ + derivedOptions?: Blockly.MenuOption[]; +} + +/** + * A dropdown field that automatically updates its own options based on a + * mapping from a parent field's value. The parent field must be attached to the + * block before this child field. + * + * When this field is attached to a block, it will find the parent field and + * attach a validator to the parent field that intercepts changes to its value + * to update the options on this field. If the new validator is later removed\ + * or replaced, then this dependent field will no longer function. + */ +export class FieldDependentDropdown extends Blockly.FieldDropdown { + /** + * Contains data used by this dropdown field's menu generator. + * + * The menu generator cannot refer directly to this FieldDependentDropdown + * instance, because it must be created before calling the super constructor + * when the "this" reference is not yet valid. This helper structure is used + * instead. + * + * This is public so that the DependentDropdownOptionsChangeJson event can + * update it while undoing/redoing. + */ + dependencyData: DependencyData; + + /** The name of the field that determines this field's options. */ + private parentName: string; + + /** + * The mapping from the parent field's value to this field's intended + * available options. The keys are strings representing the parent's possible + * values, and the values are the corresponding options to use in this child + * field. + */ + private optionMapping: ChildOptionMapping; + + /** + * An optional fallback set of options to use if the parent field's value does + * not match any of the keys in optionMapping. + */ + private defaultOptions?: Blockly.MenuOption[]; + + /** + * Constructs a new FieldDependentDropdown. + * + * @param parentName The name of the parent field whose value determines this + * field's available options. + * @param optionMapping A mapping from the possible values of the parent field + * to the corresponding available options of this child field. The keys are + * the possible values of the parent field, and the values are the + * corresponding arrays of options for this child field. + * @param defaultOptions An optional fallback set of options to use if the + * parent field's value does not match any of the keys in optionMapping. + * @param validator An optional function that is called to validate changes to + * this field's value. + * @param config An optional map of general options used to configure the + * field, such as a tooltip. + */ + constructor( + parentName: string, + optionMapping: ChildOptionMapping, + defaultOptions?: Blockly.MenuOption[], + validator?: Blockly.FieldValidator, + config?: FieldConfig, + ) { + // A menu generator needs to be passed to the super constructor, but it + // needs to be able to reference data that hasn't been populated yet. We're + // not allowed to refer to "this" in this constructor before calling + // "super", so let's make separate structure to hold data relevant to the + // menu generator and populate that later. + const dependencyData: DependencyData = {}; + + // A menu option generator function for this child field that reads the + // derived options in the dependency data if available. + const menuGenerator: Blockly.MenuGeneratorFunction = () => { + // If derivedOptions has been initialized, use that. + if (dependencyData.derivedOptions) { + return dependencyData.derivedOptions; + } + + // Fall back on the options corresponding to the parent field's current + // value (which is fine when initializing but may be out of date when + // making changes since the parent field's validator function triggers + // this function before the parent field's value is updated). + + // If the parent field exists, and its value is a key in the provided + // option mapping, use the corresponding options. + if (dependencyData.parentField) { + const value = dependencyData.parentField.getValue(); + if (value) { + const options = optionMapping[value]; + if (options) { + return options; + } + } + } + + if (defaultOptions) { + return defaultOptions; + } + + // Fall back on basic default options. + return [['', '']]; + }; + + super(menuGenerator, validator, config); + this.parentName = parentName; + this.optionMapping = optionMapping; + this.defaultOptions = defaultOptions; + this.dependencyData = dependencyData; + } + + /** + * Constructs a FieldDependentDropdown from a JSON arg object. + * + * @param options A JSON object providing "parentName" and "optionMapping". + * @returns The new field instance. + */ + static fromJson( + options: FieldDependentDropdownFromJsonConfig, + ): FieldDependentDropdown { + return new FieldDependentDropdown( + options['parentName'], + options['optionMapping'], + options['defaultOptions'], + undefined, + options, + ); + } + + /** + * Attach this field to a block. + * + * @param block The block containing this field. + */ + setSourceBlock(block: Blockly.Block) { + super.setSourceBlock(block); + + const parentField: Blockly.Field | null = block.getField( + this.parentName, + ); + + if (!parentField) { + throw new Error( + 'Could not find a parent field with the name ' + + this.parentName + + ' for the dependent dropdown.', + ); + } + + this.dependencyData.parentField = parentField; + + const oldValidator = parentField.getValidator(); + + // A validator function for the parent field that has the side effect of + // updating the options of this child dropdown field based on the new value + // of the parent field whenever it changes. The validator function is a good + // place to do this because it is called immediately while deserializing + // workspaces before the following fields are deserialized, so when the + // child value is deserialized the appropriate options will already be + // available. If the parent already had a validator function, it will be + // composed with this one and the new value returned from it will be the + // basis for determining the new available options. + parentField.setValidator((newValue) => { + if (oldValidator) { + const validatedValue = oldValidator(newValue); + // If a validator returns null, that means the new value is invalid and + // the change should be canceled. + if (validatedValue === null) { + return null; + } + // If a validator returns undefined, that means no change. Otherwise, + // use the returned value as the new value. + if (validatedValue !== undefined) { + newValue = validatedValue; + } + } + this.updateOptionsBasedOnNewValue(newValue); + return newValue; + }); + this.updateOptionsBasedOnNewValue(parentField.getValue() ?? undefined); + } + + /** + * Updates the options of this child dropdown field based on the new value of + * the parent field. + * + * @param newValue The newly assigned value. + */ + private updateOptionsBasedOnNewValue(newValue: string | undefined): void { + if (newValue == undefined) { + return; + } + + const block = this.getSourceBlock(); + if (!block) { + throw new Error( + 'Could not validate a field that is not attached to a block: ' + + this.name, + ); + } + + const oldChildValue = this.getValue(); + const oldChildOptions = this.getOptions(false); + let newChildOptions = this.optionMapping[newValue]; + if (!newChildOptions) { + if (this.defaultOptions) { + newChildOptions = this.defaultOptions; + } else { + console.warn( + 'Could not find child options for the parent value: ' + newValue, + ); + return; + } + } + + // If the child field's value is still available in the new options, keep + // it, otherwise change the field's value to the first available option. + const newOptionsIncludeOldValue = + newChildOptions.find((option) => option[1] == oldChildValue) != undefined; + const newChildValue = newOptionsIncludeOldValue + ? oldChildValue + : newChildOptions[0][1]; + + // Record the options so that the option generator can access them. + this.dependencyData.derivedOptions = newChildOptions; + + // Re-run the option generator to update the options on the dropdown. + this.getOptions(false); + + // Update this child field's value without broadcasting the normal change + // event. The normal value change event can't be properly undone, because + // the old value may not be one of the currently valid options, so a custom + // change event will be broadcast instead that handles swapping the options + // and the value at the same time. + Blockly.Events.disable(); + this.setValue(newChildValue); + Blockly.Events.enable(); + + if (Blockly.Events.getRecordUndo()) { + if (!Blockly.Events.getGroup()) { + // Start a change group before the change event. The change event for + // the parent field value will be created after this function returns + // and will be part of the same group. + Blockly.Events.setGroup(true); + // Clear the change group later, after all related events have been + // broadcast, but before the user performs any more actions. + setTimeout(() => Blockly.Events.setGroup(false)); + } + + // Record that the child field's options and value have changed. + Blockly.Events.fire( + new DependentDropdownOptionsChange( + block, + this.name, + oldChildValue ?? undefined, + newChildValue ?? undefined, + oldChildOptions, + newChildOptions, + ), + ); + } + } +} + +Blockly.fieldRegistry.register( + 'field_dependent_dropdown', + FieldDependentDropdown, +); diff --git a/packages/plugins/field-dependent-dropdown/src/index.ts b/packages/plugins/field-dependent-dropdown/src/index.ts new file mode 100644 index 00000000000..e837ff51ff9 --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/src/index.ts @@ -0,0 +1,7 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './field_dependent_dropdown'; diff --git a/packages/plugins/field-dependent-dropdown/test/field_dependent_dropdown.mocha.js b/packages/plugins/field-dependent-dropdown/test/field_dependent_dropdown.mocha.js new file mode 100644 index 00000000000..61d32499a69 --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/test/field_dependent_dropdown.mocha.js @@ -0,0 +1,302 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +const chai = require('chai'); +const sinon = require('sinon'); +const Blockly = require('blockly'); +require('./field_dependent_dropdown_test_block'); + +const assert = chai.assert; + +suite('fieldDependentDropdown', function () { + setup(function () { + this.workspace = new Blockly.Workspace(); + this.clock = sinon.useFakeTimers(); + }); + + teardown(function () { + this.workspace.dispose(); + // Finish any remaining queued events then dispose the sinon environment. + this.clock.runAll(); + this.clock.restore(); + }); + + test('Changing a parent value changes the child options', function () { + const block = this.workspace.newBlock('dependent_dropdown_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + assert.deepEqual(childDropdown.getOptions(true), [ + ['A1', 'a1'], + ['A2', 'a2'], + ['Shared', 'shared'], + ]); + parentDropdown.setValue('b'); + assert.deepEqual(childDropdown.getOptions(true), [ + ['B1', 'b1'], + ['B2', 'b2'], + ['Shared', 'shared'], + ]); + }); + + test('Changing a parent value changes the child value', function () { + const block = this.workspace.newBlock('dependent_dropdown_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + assert.equal(childDropdown.getValue(), 'a1'); + parentDropdown.setValue('b'); + assert.equal(childDropdown.getValue(), 'b1'); + }); + + test('Changing a parent value preserves shared child value', function () { + const block = this.workspace.newBlock('dependent_dropdown_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + childDropdown.setValue('shared'); + parentDropdown.setValue('b'); + assert.equal(childDropdown.getValue(), 'shared'); + }); + + test('Changing a parent value changes the grandchild options', function () { + const block = this.workspace.newBlock('dependent_dropdown_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const grandchildDropdown = block.getField('GRANDCHILD_FIELD'); + assert.deepEqual(grandchildDropdown.getOptions(true), [ + ['A11', 'a11'], + ['A12', 'a12'], + ]); + parentDropdown.setValue('b'); + assert.deepEqual(grandchildDropdown.getOptions(true), [ + ['B11', 'b11'], + ['B12', 'b12'], + ]); + }); + + test('Changing a parent value changes the grandchild value', function () { + const block = this.workspace.newBlock('dependent_dropdown_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const grandchildDropdown = block.getField('GRANDCHILD_FIELD'); + assert.equal(grandchildDropdown.getValue(), 'a11'); + parentDropdown.setValue('b'); + assert.equal(grandchildDropdown.getValue(), 'b11'); + }); + + test('Uses default options if parent field not in mapping', function () { + const block = this.workspace.newBlock( + 'dependent_dropdown_default_options_test', + ); + const parentTextInput = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + assert.deepEqual(childDropdown.getOptions(true), [ + ['Default Option', 'defaultOption'], + ]); + parentTextInput.setValue('a'); + assert.deepEqual(childDropdown.getOptions(true), [ + ['A1', 'a1'], + ['A2', 'a2'], + ]); + parentTextInput.setValue('b'); + assert.deepEqual(childDropdown.getOptions(true), [ + ['Default Option', 'defaultOption'], + ]); + }); + + test('Parent field user validator composes with new validator', function () { + const block = this.workspace.newBlock('dependent_dropdown_validation_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + assert.equal(parentDropdown.getValue(), 'initial'); + assert.equal(childDropdown.getValue(), 'initial1'); + parentDropdown.setValue('invalid'); + assert.equal(parentDropdown.getValue(), 'valid'); + assert.equal(childDropdown.getValue(), 'valid1'); + }); + + test('undoing parent change undoes child and grandchild options', function () { + const block = this.workspace.newBlock('dependent_dropdown_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + const grandchildDropdown = block.getField('GRANDCHILD_FIELD'); + parentDropdown.setValue('b'); + assert.deepEqual(childDropdown.getOptions(true), [ + ['B1', 'b1'], + ['B2', 'b2'], + ['Shared', 'shared'], + ]); + assert.deepEqual(grandchildDropdown.getOptions(true), [ + ['B11', 'b11'], + ['B12', 'b12'], + ]); + // Wait for the change events to get fired and recorded in history. + this.clock.runAll(); + this.workspace.undo(false); + assert.deepEqual(childDropdown.getOptions(true), [ + ['A1', 'a1'], + ['A2', 'a2'], + ['Shared', 'shared'], + ]); + assert.deepEqual(grandchildDropdown.getOptions(true), [ + ['A11', 'a11'], + ['A12', 'a12'], + ]); + }); + + test('undoing parent change undoes child and grandchild values', function () { + const block = this.workspace.newBlock('dependent_dropdown_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + const grandchildDropdown = block.getField('GRANDCHILD_FIELD'); + parentDropdown.setValue('b'); + assert.equal(childDropdown.getValue(), 'b1'); + assert.equal(grandchildDropdown.getValue(), 'b11'); + // Wait for the change events to get fired and recorded in history. + this.clock.runAll(); + this.workspace.undo(false); + assert.equal(childDropdown.getValue(), 'a1'); + assert.equal(grandchildDropdown.getValue(), 'a11'); + }); + + test('redoing parent change redoes child options', function () { + const block = this.workspace.newBlock('dependent_dropdown_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + parentDropdown.setValue('b'); + // Wait for the change events to get fired and recorded in history. + this.clock.runAll(); + this.workspace.undo(false); + assert.deepEqual(childDropdown.getOptions(true), [ + ['A1', 'a1'], + ['A2', 'a2'], + ['Shared', 'shared'], + ]); + this.workspace.undo(true); + assert.deepEqual(childDropdown.getOptions(true), [ + ['B1', 'b1'], + ['B2', 'b2'], + ['Shared', 'shared'], + ]); + }); + + test('redoing parent change redoes child values', function () { + const block = this.workspace.newBlock('dependent_dropdown_test'); + const parentDropdown = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + parentDropdown.setValue('b'); + // Wait for the change events to get fired and recorded in history. + this.clock.runAll(); + this.workspace.undo(false); + assert.equal(childDropdown.getValue(), 'a1'); + this.workspace.undo(true); + assert.equal(childDropdown.getValue(), 'b1'); + }); + + test('deserialized values affect available options', function () { + const serializedWorkspace = { + blocks: { + blocks: [ + { + type: 'dependent_dropdown_test', + fields: { + PARENT_FIELD: 'b', + CHILD_FIELD: 'b2', + GRANDCHILD_FIELD: 'b21', + }, + }, + ], + }, + }; + Blockly.serialization.workspaces.load(serializedWorkspace, this.workspace); + const block = this.workspace.getBlocksByType( + 'dependent_dropdown_test', + false, + )[0]; + const childDropdown = block.getField('CHILD_FIELD'); + const grandchildDropdown = block.getField('GRANDCHILD_FIELD'); + assert.deepEqual(childDropdown.getOptions(true), [ + ['B1', 'b1'], + ['B2', 'b2'], + ['Shared', 'shared'], + ]); + assert.deepEqual(grandchildDropdown.getOptions(true), [ + ['B21', 'b21'], + ['B22', 'b22'], + ]); + }); + + test('deserializing preserves values not in default options', function () { + const serializedWorkspace = { + blocks: { + blocks: [ + { + type: 'dependent_dropdown_test', + fields: { + PARENT_FIELD: 'b', + CHILD_FIELD: 'b2', + GRANDCHILD_FIELD: 'b21', + }, + }, + ], + }, + }; + Blockly.serialization.workspaces.load(serializedWorkspace, this.workspace); + const block = this.workspace.getBlocksByType( + 'dependent_dropdown_test', + false, + )[0]; + const parentDropdown = block.getField('PARENT_FIELD'); + const childDropdown = block.getField('CHILD_FIELD'); + const grandchildDropdown = block.getField('GRANDCHILD_FIELD'); + assert.equal(parentDropdown.getValue(), 'b'); + assert.equal(childDropdown.getValue(), 'b2'); + assert.equal(grandchildDropdown.getValue(), 'b21'); + }); + + test('deserializing invalid value replaces with valid value', function () { + const serializedWorkspace = { + blocks: { + blocks: [ + { + type: 'dependent_dropdown_test', + fields: { + PARENT_FIELD: 'b', + CHILD_FIELD: 'a2', + GRANDCHILD_FIELD: 'b21', + }, + }, + ], + }, + }; + Blockly.serialization.workspaces.load(serializedWorkspace, this.workspace); + const block = this.workspace.getBlocksByType( + 'dependent_dropdown_test', + false, + )[0]; + const childDropdown = block.getField('CHILD_FIELD'); + const grandchildDropdown = block.getField('GRANDCHILD_FIELD'); + assert.equal(childDropdown.getValue(), 'b1'); + assert.equal(grandchildDropdown.getValue(), 'b11'); + }); + + test('round trip serialization/deserialization preserves values', function () { + let block = this.workspace.newBlock('dependent_dropdown_test'); + let parentDropdown = block.getField('PARENT_FIELD'); + let childDropdown = block.getField('CHILD_FIELD'); + let grandchildDropdown = block.getField('GRANDCHILD_FIELD'); + parentDropdown.setValue('b'); + childDropdown.setValue('b2'); + grandchildDropdown.setValue('b21'); + const serializedWorkspace = Blockly.serialization.workspaces.save( + this.workspace, + ); + Blockly.serialization.workspaces.load(serializedWorkspace, this.workspace); + block = this.workspace.getBlocksByType('dependent_dropdown_test', false)[0]; + parentDropdown = block.getField('PARENT_FIELD'); + childDropdown = block.getField('CHILD_FIELD'); + grandchildDropdown = block.getField('GRANDCHILD_FIELD'); + assert.equal(parentDropdown.getValue(), 'b'); + assert.equal(childDropdown.getValue(), 'b2'); + assert.equal(grandchildDropdown.getValue(), 'b21'); + }); +}); diff --git a/packages/plugins/field-dependent-dropdown/test/field_dependent_dropdown_test_block.ts b/packages/plugins/field-dependent-dropdown/test/field_dependent_dropdown_test_block.ts new file mode 100644 index 00000000000..2cf7c180490 --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/test/field_dependent_dropdown_test_block.ts @@ -0,0 +1,130 @@ +import * as Blockly from 'blockly'; +import {ChildOptionMapping, FieldDependentDropdown} from '../src/index'; + +Blockly.defineBlocksWithJsonArray([ + { + type: 'dependent_dropdown_test', + message0: 'Parent %1 Child %2 Grandchild %3', + args0: [ + { + type: 'field_dropdown', + name: 'PARENT_FIELD', + options: [ + ['A', 'a'], + ['B', 'b'], + ], + }, + { + type: 'field_dependent_dropdown', + name: 'CHILD_FIELD', + parentName: 'PARENT_FIELD', + optionMapping: { + a: [ + ['A1', 'a1'], + ['A2', 'a2'], + ['Shared', 'shared'], + ], + b: [ + ['B1', 'b1'], + ['B2', 'b2'], + ['Shared', 'shared'], + ], + }, + }, + { + type: 'field_dependent_dropdown', + name: 'GRANDCHILD_FIELD', + parentName: 'CHILD_FIELD', + optionMapping: { + a1: [ + ['A11', 'a11'], + ['A12', 'a12'], + ], + a2: [ + ['A21', 'a21'], + ['A22', 'a22'], + ], + b1: [ + ['B11', 'b11'], + ['B12', 'b12'], + ], + b2: [ + ['B21', 'b21'], + ['B22', 'b22'], + ], + shared: [['Only', 'only']], + }, + }, + ], + colour: 100, + }, + { + type: 'dependent_dropdown_default_options_test', + message0: 'Parent %1 Child %2', + args0: [ + { + type: 'field_input', + name: 'PARENT_FIELD', + }, + { + type: 'field_dependent_dropdown', + name: 'CHILD_FIELD', + parentName: 'PARENT_FIELD', + optionMapping: { + a: [ + ['A1', 'a1'], + ['A2', 'a2'], + ], + }, + defaultOptions: [['Default Option', 'defaultOption']], + }, + ], + colour: 100, + }, +]); + +Blockly.Blocks['dependent_dropdown_validation_test'] = { + init: function () { + const parentFieldName = 'PARENT_FIELD'; + const childFieldName = 'CHILD_FIELD'; + const parentOptions: Blockly.MenuOption[] = [ + ['Initial', 'initial'], + ['Invalid', 'invalid'], + ['Valid', 'valid'], + ]; + const dependentOptions: ChildOptionMapping = { + initial: [ + ['Initial1', 'initial1'], + ['Initial2', 'initial2'], + ], + invalid: [ + ['Invalid1', 'invalid1'], + ['Invalid2', 'invalid2'], + ], + valid: [ + ['Valid1', 'valid1'], + ['Valid2', 'valid2'], + ], + }; + const parentValidator: Blockly.FieldDropdownValidator = function ( + newValue, + ) { + if (newValue == 'invalid') { + return 'valid'; + } + return undefined; + }; + this.appendDummyInput() + .appendField('Parent') + .appendField( + new Blockly.FieldDropdown(parentOptions, parentValidator), + parentFieldName, + ) + .appendField('Child') + .appendField( + new FieldDependentDropdown(parentFieldName, dependentOptions), + childFieldName, + ); + this.setColour(100); + }, +}; diff --git a/packages/plugins/field-dependent-dropdown/test/index.html b/packages/plugins/field-dependent-dropdown/test/index.html new file mode 100644 index 00000000000..eb2b560a777 --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/test/index.html @@ -0,0 +1,18 @@ + + + + + Blockly Dependent Dropdown Field Playground + + + + +
+ + + diff --git a/packages/plugins/field-dependent-dropdown/test/index.ts b/packages/plugins/field-dependent-dropdown/test/index.ts new file mode 100644 index 00000000000..c69b2e98ce3 --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/test/index.ts @@ -0,0 +1,63 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Dependent dropdown field test playground. + */ + +import * as Blockly from 'blockly'; +import {createPlayground} from '@blockly/dev-tools'; +import './field_dependent_dropdown_test_block'; + +const toolbox: Blockly.utils.toolbox.ToolboxDefinition = { + kind: 'flyoutToolbox', + contents: [ + { + kind: 'block', + type: 'dependent_dropdown_test', + fields: { + /* eslint-disable @typescript-eslint/naming-convention */ + PARENT_FIELD: 'b', + CHILD_FIELD: 'b2', + GRANDCHILD_FIELD: 'b21', + /* eslint-enable @typescript-eslint/naming-convention */ + }, + }, + { + kind: 'block', + type: 'dependent_dropdown_default_options_test', + }, + { + kind: 'block', + type: 'dependent_dropdown_validation_test', + }, + ], +}; + +/** + * Create a workspace. + * + * @param blocklyDiv The blockly container div. + * @param options The Blockly options. + * @returns The created workspace. + */ +function createWorkspace( + blocklyDiv: HTMLElement, + options: Blockly.BlocklyOptions, +): Blockly.WorkspaceSvg { + const workspace = Blockly.inject(blocklyDiv, options); + return workspace; +} + +document.addEventListener('DOMContentLoaded', function () { + const defaultOptions: Blockly.BlocklyOptions = { + toolbox, + }; + const rootElement = document.getElementById('root'); + if (rootElement) { + createPlayground(rootElement, createWorkspace, defaultOptions); + } +}); diff --git a/packages/plugins/field-dependent-dropdown/tsconfig.json b/packages/plugins/field-dependent-dropdown/tsconfig.json new file mode 100644 index 00000000000..99858c525f1 --- /dev/null +++ b/packages/plugins/field-dependent-dropdown/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "module": "es2015", + "moduleResolution": "bundler", + "target": "es6", + "strict": true + }, + // NOTE: `test/**/*` is automatically included in `blockly-scripts start`. + // Only src matters for production builds. + "include": ["src"] +} diff --git a/packages/plugins/field-grid-dropdown/CHANGELOG.md b/packages/plugins/field-grid-dropdown/CHANGELOG.md new file mode 100644 index 00000000000..327ffdc4237 --- /dev/null +++ b/packages/plugins/field-grid-dropdown/CHANGELOG.md @@ -0,0 +1,681 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [13.1.0](https://github.com/RaspberryPiFoundation/blockly-samples/compare/v13.0.0...v13.1.0) (2026-06-30) + +### ⚠ BREAKING CHANGES + +* Bump Blockly dependency to v13 (#2704) + +### Bug Fixes + +* Improve accessibility of grid and colour fields ([#2724](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2724)) + +### Miscellaneous Chores + +* Bump Blockly dependency to v13 ([#2704](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2704)) + + +## [6.0.10](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.9...@blockly/field-grid-dropdown@6.0.10) (2026-04-09) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [6.0.9](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.8...@blockly/field-grid-dropdown@6.0.9) (2026-01-15) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [6.0.8](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.7...@blockly/field-grid-dropdown@6.0.8) (2026-01-05) + + +### Bug Fixes + +* update plugin URLs to RPF ([#2665](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2665)) ([6c34039](https://github.com/RaspberryPiFoundation/blockly-samples/commit/6c3403960b8070eb8e576bd5702bdb53a09ac475)) + + + +## [6.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.6...@blockly/field-grid-dropdown@6.0.7) (2026-01-05) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [6.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.5...@blockly/field-grid-dropdown@6.0.6) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [6.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.4...@blockly/field-grid-dropdown@6.0.5) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [6.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.3...@blockly/field-grid-dropdown@6.0.4) (2025-10-02) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [6.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.2...@blockly/field-grid-dropdown@6.0.3) (2025-07-17) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [6.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.1...@blockly/field-grid-dropdown@6.0.2) (2025-07-03) + + +### Bug Fixes + +* prevent Enter/Space from propagating from Grid ([#2570](https://github.com/google/blockly-samples/issues/2570)) ([25ee5a9](https://github.com/google/blockly-samples/commit/25ee5a9a750d819c9dbf00d6513f3b9c6fd302fe)) + + + +## [6.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@6.0.0...@blockly/field-grid-dropdown@6.0.1) (2025-05-22) + + +### Bug Fixes + +* **field-grid-dropdown:** add overflow:auto to enable scroll in grid dropdown container ([#2543](https://github.com/google/blockly-samples/issues/2543)) ([f38078d](https://github.com/google/blockly-samples/commit/f38078d6449e40903a4725b14d57aabf414e5bf5)) + + + +## [6.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.16...@blockly/field-grid-dropdown@6.0.0) (2025-05-16) + + +### ⚠ BREAKING CHANGES + +* update all plugins to blockly v12 (#2538) + +### Features + +* update all plugins to blockly v12 ([#2538](https://github.com/google/blockly-samples/issues/2538)) ([420ba31](https://github.com/google/blockly-samples/commit/420ba3124b260b2c06e8e1d61be44e870780d95f)) + + + +## [5.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.15...@blockly/field-grid-dropdown@5.0.16) (2025-05-16) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.14...@blockly/field-grid-dropdown@5.0.15) (2025-05-15) + + +### Bug Fixes + +* **FieldGridDropdown:** Handle presence of HTMLElement in MenuOptions ([43bdfab](https://github.com/google/blockly-samples/commit/43bdfabeb658331d3991430ec92589ba415dac75)) +* Fix the field-grid-dropdown tests. ([452051e](https://github.com/google/blockly-samples/commit/452051e02520803e7cf85e63e0bfb8191a838a98)) + + + + + +## [5.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.13...@blockly/field-grid-dropdown@5.0.14) (2025-02-13) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.12...@blockly/field-grid-dropdown@5.0.13) (2024-12-19) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.11...@blockly/field-grid-dropdown@5.0.12) (2024-12-03) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.10...@blockly/field-grid-dropdown@5.0.11) (2024-11-07) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.9...@blockly/field-grid-dropdown@5.0.10) (2024-09-26) + + +### Bug Fixes + +* **field-*:** Remove unneeded `fieldRegistry.unregister` calls ([#2454](https://github.com/google/blockly-samples/issues/2454)) ([b3ba30e](https://github.com/google/blockly-samples/commit/b3ba30e23dddf0bd98c266659aa229ba6ba685b0)), closes [#2453](https://github.com/google/blockly-samples/issues/2453) + + + + + +## [5.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.8...@blockly/field-grid-dropdown@5.0.9) (2024-08-29) + + +### Bug Fixes + +* predeploy plugins ([#2449](https://github.com/google/blockly-samples/issues/2449)) ([6b36d8b](https://github.com/google/blockly-samples/commit/6b36d8b344a969f79d89bbc7dcee29ae554759f9)) + + + + + +## [5.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.7...@blockly/field-grid-dropdown@5.0.8) (2024-08-22) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.6...@blockly/field-grid-dropdown@5.0.7) (2024-08-08) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.5...@blockly/field-grid-dropdown@5.0.6) (2024-08-01) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.4...@blockly/field-grid-dropdown@5.0.5) (2024-07-25) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.3...@blockly/field-grid-dropdown@5.0.4) (2024-07-11) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.2...@blockly/field-grid-dropdown@5.0.3) (2024-07-04) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.1...@blockly/field-grid-dropdown@5.0.2) (2024-06-06) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [5.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@5.0.0...@blockly/field-grid-dropdown@5.0.1) (2024-05-30) + + +### Bug Fixes + +* multiple blockly instances ([#2375](https://github.com/google/blockly-samples/issues/2375)) ([b231e59](https://github.com/google/blockly-samples/commit/b231e598f2f5f5b0abbfd01d981e35572ad50a26)) + + + +## [5.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.16...@blockly/field-grid-dropdown@5.0.0) (2024-05-21) + + +### ⚠ BREAKING CHANGES + +* update all plugins to v11 of blockly + +### Features + +* update all plugins to v11 of blockly ([5fcd3e7](https://github.com/google/blockly-samples/commit/5fcd3e7d53eaadffe9bda9a378b404d34b2f8be2)) +* upgrade ts version, fix field validators ([b2d2191](https://github.com/google/blockly-samples/commit/b2d2191ff11b30347b33f95acf6e58f7ce54d004)) + + +### Bug Fixes + +* Call blocks handle both manual disabling and disabled defs ([#2334](https://github.com/google/blockly-samples/issues/2334)) ([5eade55](https://github.com/google/blockly-samples/commit/5eade55779c4022d14ad4472ff32c93c78199887)) +* remove some uses of generateFieldTextBlocks ([#2357](https://github.com/google/blockly-samples/issues/2357)) ([53b3fa4](https://github.com/google/blockly-samples/commit/53b3fa4604aabb1fc5240d41c8d1d389d4b15301)) + + + +## [4.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.15...@blockly/field-grid-dropdown@4.0.16) (2024-05-16) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.14...@blockly/field-grid-dropdown@4.0.15) (2024-05-09) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.13...@blockly/field-grid-dropdown@4.0.14) (2024-04-25) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.12...@blockly/field-grid-dropdown@4.0.13) (2024-04-04) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.11...@blockly/field-grid-dropdown@4.0.12) (2024-03-28) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.10...@blockly/field-grid-dropdown@4.0.11) (2024-02-08) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.9...@blockly/field-grid-dropdown@4.0.10) (2023-12-14) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.8...@blockly/field-grid-dropdown@4.0.9) (2023-12-07) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.7...@blockly/field-grid-dropdown@4.0.8) (2023-11-09) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.6...@blockly/field-grid-dropdown@4.0.7) (2023-11-02) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.5...@blockly/field-grid-dropdown@4.0.6) (2023-10-30) + + +### Bug Fixes + +* add the type property to the package.json next to the "main" property: ([#1955](https://github.com/google/blockly-samples/issues/1955)) ([abc713b](https://github.com/google/blockly-samples/commit/abc713b5f3c2941dd4e3a6cbde9060a19b80d189)) +* **packaging:** Don't advertise non-existent ESM entrypoints ([#2022](https://github.com/google/blockly-samples/issues/2022)) ([d019198](https://github.com/google/blockly-samples/commit/d0191984399b784e2928b8fb4c58257bfa857655)), closes [#1877](https://github.com/google/blockly-samples/issues/1877) +* update tsconfigs to publish types accurately ([df4b084](https://github.com/google/blockly-samples/commit/df4b0844af712f5025a2ec842458b828f3147676)) +* use includes rather than excludes ([40efc25](https://github.com/google/blockly-samples/commit/40efc255329e3ca476ccc247b95a2d05dd77b45e)) + + + +## [4.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.4...@blockly/field-grid-dropdown@4.0.5) (2023-09-21) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.3...@blockly/field-grid-dropdown@4.0.4) (2023-09-14) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.2...@blockly/field-grid-dropdown@4.0.3) (2023-08-17) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.1...@blockly/field-grid-dropdown@4.0.2) (2023-07-24) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@4.0.0...@blockly/field-grid-dropdown@4.0.1) (2023-07-20) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [4.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@3.0.1...@blockly/field-grid-dropdown@4.0.0) (2023-07-13) + + +### ⚠ BREAKING CHANGES + +* updated `plugins/field-date` to TypeScript (#1705) + +### Features + +* updated `plugins/field-date` to TypeScript ([#1705](https://github.com/google/blockly-samples/issues/1705)) ([e5531ff](https://github.com/google/blockly-samples/commit/e5531fffe188ee361a16fe48ed126b34e51a8d30)) + + + +## [3.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@3.0.0...@blockly/field-grid-dropdown@3.0.1) (2023-07-06) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [3.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.18...@blockly/field-grid-dropdown@3.0.0) (2023-06-28) + + +### ⚠ BREAKING CHANGES + +* update all plugins to use blockly-v10.0.0 (#1744) + +### Features + +* update all plugins to use blockly-v10.0.0 ([#1744](https://github.com/google/blockly-samples/issues/1744)) ([6cc88cb](https://github.com/google/blockly-samples/commit/6cc88cbef39d4ad664a668d3d46eb29ba7292f9c)) + + + +## [2.0.18](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.17...@blockly/field-grid-dropdown@2.0.18) (2023-06-22) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.17](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.16...@blockly/field-grid-dropdown@2.0.17) (2023-05-18) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.15...@blockly/field-grid-dropdown@2.0.16) (2023-05-11) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.14...@blockly/field-grid-dropdown@2.0.15) (2023-05-04) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.13...@blockly/field-grid-dropdown@2.0.14) (2023-04-27) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.12...@blockly/field-grid-dropdown@2.0.13) (2023-04-13) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.11...@blockly/field-grid-dropdown@2.0.12) (2023-03-23) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.10...@blockly/field-grid-dropdown@2.0.11) (2023-03-09) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.9...@blockly/field-grid-dropdown@2.0.10) (2023-03-02) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.8...@blockly/field-grid-dropdown@2.0.9) (2023-02-23) + + +### Bug Fixes + +* remove prepublishOnly scripts ([#1579](https://github.com/google/blockly-samples/issues/1579)) ([27da6cd](https://github.com/google/blockly-samples/commit/27da6cd04c38f6ba417f4e7446bb6218c475448d)) + + + +## [2.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.7...@blockly/field-grid-dropdown@2.0.8) (2023-02-23) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.6...@blockly/field-grid-dropdown@2.0.7) (2023-02-16) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.5...@blockly/field-grid-dropdown@2.0.6) (2023-02-09) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.4...@blockly/field-grid-dropdown@2.0.5) (2023-01-26) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.3...@blockly/field-grid-dropdown@2.0.4) (2023-01-05) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.2...@blockly/field-grid-dropdown@2.0.3) (2022-12-15) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.1...@blockly/field-grid-dropdown@2.0.2) (2022-10-27) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@2.0.0...@blockly/field-grid-dropdown@2.0.1) (2022-10-13) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## [2.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-grid-dropdown@1.0.47...@blockly/field-grid-dropdown@2.0.0) (2022-10-05) + + +### ⚠ BREAKING CHANGES + +* update peer and devDependencies of all plugins to require Blockly v9 (#1314) + +### Bug Fixes + +* references to deprecated functions in v9 ([#1313](https://github.com/google/blockly-samples/issues/1313)) ([cb2e679](https://github.com/google/blockly-samples/commit/cb2e67987e0b62a77c26adc660cc6ade1ba67954)) + + +### Miscellaneous Chores + +* update peer and devDependencies of all plugins to require Blockly v9 ([#1314](https://github.com/google/blockly-samples/issues/1314)) ([03d4912](https://github.com/google/blockly-samples/commit/03d4912c42c8de0f30493037ccc28dddaea0f266)) + + + +## 1.0.47 (2022-08-11) + + +### Bug Fixes + +* Remove the deprecated block-extension-tooltip ([#1215](https://github.com/google/blockly-samples/issues/1215)) ([a044478](https://github.com/google/blockly-samples/commit/a044478c86a73e3065bc866e427f175cbec6fc13)) + + + + + +## 1.0.46 (2022-08-04) + + +### Bug Fixes + +* fix the name of the package of suggested-blocks ([#1214](https://github.com/google/blockly-samples/issues/1214)) ([35aa8ec](https://github.com/google/blockly-samples/commit/35aa8ec73a60a4eb5b1e80cb2fc71dcd83d05e27)) + + + + + +## 1.0.44 (2022-07-21) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## 1.0.43 (2022-07-06) + + +### Bug Fixes + +* Update blockly-react to use functional components instead of class components. ([#1178](https://github.com/google/blockly-samples/issues/1178)) ([fa21187](https://github.com/google/blockly-samples/commit/fa21187cdbe4ec3a5c69f185540dd68a98eb69d7)) + + + + + +## 1.0.42 (2022-06-27) + + +### Bug Fixes + +* Update package import for keyboard navigation demo ([#1170](https://github.com/google/blockly-samples/issues/1170)) ([69c1725](https://github.com/google/blockly-samples/commit/69c1725b775279fcc397dc178935208d5f42b08c)) + + + + + +## 1.0.41 (2022-06-21) + +**Note:** Version bump only for package @blockly/field-grid-dropdown + + + + + +## 1.0.40 (2022-06-08) + + +### Bug Fixes + +* package versions to support patch releases ([#1150](https://github.com/google/blockly-samples/issues/1150)) ([e1ae378](https://github.com/google/blockly-samples/commit/e1ae378d779531621c3d948566257d069002963f)) + + + + + +## 1.0.39 (2022-06-02) + + +### Bug Fixes + +* add git identity ([#1156](https://github.com/google/blockly-samples/issues/1156)) ([8d80924](https://github.com/google/blockly-samples/commit/8d809243b277375beb2ce75d4e157b5e17f78193)) diff --git a/packages/plugins/field-grid-dropdown/README.md b/packages/plugins/field-grid-dropdown/README.md new file mode 100644 index 00000000000..90b8dd6ac98 --- /dev/null +++ b/packages/plugins/field-grid-dropdown/README.md @@ -0,0 +1,92 @@ +# @blockly/field-grid-dropdown [![Built on Blockly](https://tinyurl.com/built-on-blockly)](https://github.com/google/blockly) + +A [Blockly](https://www.npmjs.com/package/blockly) dropdown field with grid layout. + +![](https://github.com/RaspberryPiFoundation/blockly-samples/raw/master/plugins/field-grid-dropdown/readme-media/dropdown.png) + +![](https://github.com/RaspberryPiFoundation/blockly-samples/raw/master/plugins/field-grid-dropdown/readme-media/dropdown-images.png) + +## Installation + +### Yarn + +``` +yarn add @blockly/field-grid-dropdown +``` + +### npm + +``` +npm install @blockly/field-grid-dropdown --save +``` + +## Usage + +This field accepts the same parameters as the [Blockly.FieldDropdown](https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/dropdown#creation) +in Blockly core. The config object bag passed into this field accepts additional optional parameters: + +- `"columns"` to specify the number of columns in the dropdown field (must be an integer greater than 0). + If not provided, the default is 3 columns. +- `"primaryColour"` to specify the colour of the dropdown (must be a string CSS colour). If not provided, + the dropdown color will match the primary colour of the parent block. +- `"borderColour"` to specify the colour of the border of the dropdown (must be a string CSS colour). If + not provided, the border colour will match the tertiary colour of the parent block. + +### JavaScript + +```js +import * as Blockly from 'blockly'; +import {FieldGridDropdown} from '@blockly/field-grid-dropdown'; +Blockly.Blocks['test_field_grid_dropdown'] = { + init: function () { + this.appendDummyInput() + .appendField('grid dropdown: ') + .appendField( + new FieldGridDropdown([ + ['A', 'A'], + ['B', 'B'], + ['C', 'C'], + ['D', 'D'], + ['E', 'E'], + ['F', 'F'], + ['G', 'G'], + ['H', 'H'], + ]), + 'FIELDNAME', + ); + }, +}; +``` + +### JSON + +```js +import * as Blockly from 'blockly'; +import '@blockly/field-grid-dropdown'; +Blockly.defineBlocksWithJsonArray([ + { + type: 'test_field_grid_dropdown', + message0: 'template: %1', + args0: [ + { + type: 'field_grid_dropdown', + name: 'FIELDNAME', + options: [ + ['A', 'A'], + ['B', 'B'], + ['C', 'C'], + ['D', 'D'], + ['E', 'E'], + ['F', 'F'], + ['G', 'G'], + ['H', 'H'], + ], + }, + ], + }, +]); +``` + +## License + +Apache 2.0 diff --git a/packages/plugins/field-grid-dropdown/package.json b/packages/plugins/field-grid-dropdown/package.json new file mode 100644 index 00000000000..942d7307c65 --- /dev/null +++ b/packages/plugins/field-grid-dropdown/package.json @@ -0,0 +1,52 @@ +{ + "name": "@blockly/field-grid-dropdown", + "version": "13.1.0", + "description": "A Blockly dropdown field with grid layout.", + "scripts": { + "build": "blockly-scripts build", + "clean": "blockly-scripts clean", + "lint": "eslint .", + "predeploy": "npm run build && blockly-scripts predeploy", + "start": "blockly-scripts start", + "test": "blockly-scripts test" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "unpkg": "./dist/index.js", + "author": "Blockly Team", + "keywords": [ + "blockly", + "blockly-plugin", + "blockly-field", + "grid-dropdown" + ], + "homepage": "https://github.com/RaspberryPiFoundation/blockly-samples/tree/master/plugins/field-grid-dropdown#readme", + "bugs": { + "url": "https://github.com/RaspberryPiFoundation/blockly-samples/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/RaspberryPiFoundation/blockly-samples.git", + "directory": "plugins/field-grid-dropdown" + }, + "license": "Apache 2.0", + "directories": { + "dist": "dist", + "src": "src" + }, + "files": [ + "dist", + "src" + ], + "devDependencies": { + "@blockly/dev-scripts": "^13.1.0", + "@blockly/dev-tools": "^13.1.0", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "blockly": "^13.2.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugins/field-grid-dropdown/readme-media/dropdown-images.png b/packages/plugins/field-grid-dropdown/readme-media/dropdown-images.png new file mode 100644 index 0000000000000000000000000000000000000000..c5d091dd1ee267e8c0251c6e95abce2cd23f2946 GIT binary patch literal 14349 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 literal 0 HcmV?d00001 diff --git a/packages/plugins/field-grid-dropdown/readme-media/dropdown.png b/packages/plugins/field-grid-dropdown/readme-media/dropdown.png new file mode 100644 index 0000000000000000000000000000000000000000..62fad4e368e22940e6f5bacc1256b28bb1262f77 GIT binary patch literal 8813 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 literal 0 HcmV?d00001 diff --git a/packages/plugins/field-grid-dropdown/src/grid.ts b/packages/plugins/field-grid-dropdown/src/grid.ts new file mode 100644 index 00000000000..3266c611394 --- /dev/null +++ b/packages/plugins/field-grid-dropdown/src/grid.ts @@ -0,0 +1,391 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + browserEvents, + common, + FieldDropdown, + ImageProperties, + MenuOption, + utils, + WorkspaceSvg, +} from 'blockly/core'; +import {GridItem} from './grid_item'; + +/** + * Class for managing a group of items displayed in a grid. + */ +export class Grid { + /** Mapping from grid item ID to index in the items list. */ + private itemIndices = new Map(); + + /** List of items displayed in this grid. */ + private items = new Array(); + + /** Root DOM element of this grid. */ + private root: HTMLDivElement; + + /** Identifier for keydown handler to be unregistered in dispose(). */ + private keyDownHandler: browserEvents.Data | null = null; + + /** Identifier for pointermove handler to be unregistered in dispose(). */ + private pointerMoveHandler: browserEvents.Data | null = null; + + /** Function to be called when an item in this grid is selected. */ + private selectionCallback?: (selectedItem: GridItem) => void; + + /** + * Creates a new Grid instance. + * + * @param container The parent element of this grid in the DOM. + * @param options A list of MenuOption objects representing the items to be + * shown in this grid. + * @param columns The number of columns to display items in. + * @param rtl True if this grid is being shown in a right-to-left environment. + * @param selectionCallback Function to be called when an item in the grid is + * selected. + */ + constructor( + container: HTMLElement, + options: MenuOption[], + private readonly columns: number, + private readonly rtl: boolean, + selectionCallback: (selectedItem: GridItem) => void, + ) { + this.selectionCallback = selectionCallback; + + this.root = document.createElement('div'); + this.root.className = 'blocklyFieldGrid'; + this.root.tabIndex = 0; + utils.aria.setRole(this.root, utils.aria.Role.GRID); + container.appendChild(this.root); + + this.populateItems(options); + + this.keyDownHandler = browserEvents.conditionalBind( + this.root, + 'keydown', + this, + this.onKeyDown, + ); + + this.pointerMoveHandler = browserEvents.conditionalBind( + this.root, + 'pointermove', + this, + this.onPointerMove, + true, + ); + + if (columns >= 1) { + this.columns = columns; + this.root.style.setProperty('--grid-columns', `${this.columns}`); + } else { + throw new Error(`Number of columns must be >= 1; got ${columns}`); + } + } + + /** + * Creates grid items in the DOM given a list of model objects. + * + * @param options A list of grid item model objects. + */ + private populateItems(options: MenuOption[]) { + let row = document.createElement('div'); + for (const [index, item] of options.entries()) { + // TODO(#2507): Don't just ignore separators. + if (item === FieldDropdown.SEPARATOR) continue; + + if (index % this.columns === 0) { + row = document.createElement('div'); + row.className = 'blocklyFieldGridRow'; + utils.aria.setRole(row, utils.aria.Role.ROW); + this.root.appendChild(row); + } + + const [label, value, ariaLabel] = item; + const content = (() => { + if (isImageProperties(label)) { + // Convert ImageProperties to an HTMLImageElement. + const image = new Image(label.width, label.height); + image.src = label.src; + image.alt = label.alt || ''; + return image; + } + return label; + })(); + + const gridItem = new GridItem( + row, + content, + value, + ariaLabel, + (selectedItem: GridItem) => { + this.setSelectedValue(selectedItem.getValue()); + this.selectionCallback?.(selectedItem); + }, + ); + this.itemIndices.set(gridItem.getId(), this.itemIndices.size); + this.items.push(gridItem); + } + } + + /** + * Disposes of this grid. + */ + dispose() { + this.selectionCallback = undefined; + for (const item of this.items) { + item.dispose(); + } + this.itemIndices.clear(); + this.items.length = 0; + if (this.keyDownHandler) { + browserEvents.unbind(this.keyDownHandler); + this.keyDownHandler = null; + } + + if (this.pointerMoveHandler) { + browserEvents.unbind(this.pointerMoveHandler); + this.pointerMoveHandler = null; + } + this.root.remove(); + } + + /** + * Handles a keydown event in the grid, generally by moving focus. + * + * @param e The keydown event to handle. + */ + private onKeyDown(e: KeyboardEvent) { + if ( + !this.items.length || + e.shiftKey || + e.ctrlKey || + e.metaKey || + e.altKey + ) { + return; + } + + switch (e.key) { + case 'ArrowUp': + this.moveFocus(-1, true, false); + break; + case 'ArrowDown': + this.moveFocus(1, true, false); + break; + case 'ArrowLeft': + this.moveFocus(-1 * (this.rtl ? -1 : 1), true, true); + break; + case 'ArrowRight': + this.moveFocus(1 * (this.rtl ? -1 : 1), true, true); + break; + case 'PageUp': + case 'Home': + this.moveFocus(0, false, true); + break; + case 'PageDown': + case 'End': + this.moveFocus(this.items.length - 1, false, true); + break; + case 'Enter': + case 'Space': + // Handled via GridItem click handler, so we want its default but it + // must not propagate. + e.stopPropagation(); + return; + default: + // Not a key the grid is interested in. + return; + } + // The grid used this key, don't let it have secondary effects. + e.preventDefault(); + e.stopPropagation(); + } + + /** + * Handles a pointermove event in the grid by focusing the hovered item. + * + * @param e The pointermove event to handle. + */ + private onPointerMove(e: PointerEvent) { + // Don't highlight grid items on "pointermove" if the pointer didn't + // actually move (but the content under it did due to e.g. scrolling into + // view), or if the target isn't an Element, which should never happen, but + // TS needs to be reassured of that. + if (!(e.movementX || e.movementY) || !(e.target instanceof Element)) return; + + const gridItem = e.target.closest('.blocklyFieldGridItem'); + if (!gridItem) return; + + const targetId = gridItem.id; + const targetIndex = this.itemIndices.get(targetId); + if (targetIndex === undefined) return; + this.moveFocus(targetIndex, false, true); + } + + /** + * Selects the item with the given value in the grid. + * + * @param value The value of the grid item to select. + */ + setSelectedValue(value: string) { + for (const [index, item] of this.items.entries()) { + const selected = item.getValue() === value; + item.setSelected(selected); + if (selected) { + this.moveFocus(index, false, true); + } + } + } + + /** + * Moves browser focus to the grid item at the given index. + * + * @param movementIndex The absolute or directionally relative index of the + * item to focus. + * @param relative True to interpret the index as relative to the currently + * focused item, false to move focus to it as an absolute value. + * @param horizontal True for a left/right move. False for an up/down move. + * Vertical moves wrap to the next/previous column at column edges, but + * do not wrap around the ends of the grid. + */ + private moveFocus( + movementIndex: number, + relative: boolean, + horizontal: boolean, + ) { + let targetIndex = movementIndex; + + if (relative) { + const focusedItem = this.getFocusedItem(); + if (!focusedItem) return; + const currentIndex = this.indexOfItem(focusedItem); + + if (horizontal) { + targetIndex += currentIndex; + } else { + targetIndex = this.getVerticalTargetIndex(currentIndex, movementIndex); + } + } + + const targetItem = this.itemAtIndex(targetIndex); + if (!targetItem) { + const workspace = common.getMainWorkspace(); + if (workspace instanceof WorkspaceSvg) { + workspace.getAudioManager().playErrorBeep(); + } + return; + } + + targetItem.focus(); + utils.aria.setState( + this.root, + utils.aria.State.ACTIVEDESCENDANT, + targetItem.getId(), + ); + } + + /** + * Returns the index to focus after a vertical move from the given index. + * + * Prefers moving within the same column. At a column edge, wraps to the + * first/last item of the adjacent column. Returns an out-of-bounds index + * when movement is blocked at the start or end of the grid. + * + * @param currentIndex The currently focused item index. + * @param direction 1 to move down, -1 to move up. + * @returns The target item index. + */ + private getVerticalTargetIndex( + currentIndex: number, + direction: number, + ): number { + // First try moving to the vertically adjacent item in the same column. + const column = currentIndex % this.columns; + const row = Math.floor(currentIndex / this.columns); + const targetIndex = (row + direction) * this.columns + column; + + if (this.itemAtIndex(targetIndex)) { + return targetIndex; + } + + // We are past this column's edge, so wrap to the adjacent column. + const targetColumn = column + direction; + if (targetColumn < 0 || targetColumn >= this.columns) { + return targetIndex; + } + + const columnItems = this.items.filter( + (item, i) => i % this.columns === targetColumn, + ); + const item = + direction > 0 ? columnItems[0] : columnItems[columnItems.length - 1]; + return this.indexOfItem(item); + } + + /** + * Returns the index of the given item within the grid. + * + * @param item The item to return the index of. + * @returns The index of the given item within the grid. + */ + private indexOfItem(item: GridItem): number { + return this.itemIndices.get(item.getId()) ?? -1; + } + + /** + * Returns the GridItem object at the given index in the grid. + * + * @param index The index to retrieve the grid item at. + * @returns The GridItem at the given index, or undefined if the index is + * invalid. + */ + private itemAtIndex(index: number): GridItem | undefined { + return this.items[index]; + } + + /** + * Returns the currently focused grid item, if any. + * + * @returns The focused grid item, or undefined if no item is focused. + */ + private getFocusedItem(): GridItem | undefined { + const element = + this.root.querySelector('.blocklyFieldGridItem:focus') ?? + this.root.querySelector('.blocklyFieldGridItem'); + if (!element || !element.id) return undefined; + + const index = this.itemIndices.get(element.id); + if (index === undefined) return undefined; + + return this.itemAtIndex(index); + } +} + +/** + * Returns whether or not an object conforms to the ImageProperties + * interface. + * + * @param obj The object to test. + * @returns True iff the object conforms to ImageProperties. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function isImageProperties(obj: any): obj is ImageProperties { + return ( + obj && + typeof obj === 'object' && + 'src' in obj && + typeof obj.src === 'string' && + 'alt' in obj && + typeof obj.alt === 'string' && + 'width' in obj && + typeof obj.width === 'number' && + 'height' in obj && + typeof obj.height === 'number' + ); +} diff --git a/packages/plugins/field-grid-dropdown/src/grid_item.ts b/packages/plugins/field-grid-dropdown/src/grid_item.ts new file mode 100644 index 00000000000..3d78842b582 --- /dev/null +++ b/packages/plugins/field-grid-dropdown/src/grid_item.ts @@ -0,0 +1,198 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {browserEvents, utils} from 'blockly/core'; + +/** + * Class representing an item in a grid. + */ +export class GridItem { + /** The DOM element for the grid item. */ + private element: HTMLButtonElement; + + /** Identifier for a click handler to unregister during dispose(). */ + private clickHandler: browserEvents.Data | null; + + /** Callback to invoke when this item is selected. */ + private selectionCallback: ((selectedItem: GridItem) => void) | null; + + /** Whether or not this item is currently selected. */ + private selected = false; + + /** + * Creates a new GridItem. + * + * @param container The parent element of this grid item in the DOM. + * @param content The content to display in this grid item. + * @param value The programmatic value of this grid item. + * @param ariaLabel An accessibility label that describes the contents of this + * grid item to screenreaders. + * @param selectionCallback Function to call when this item is selected. + */ + constructor( + container: HTMLElement, + content: string | HTMLElement, + private readonly value: string, + ariaLabel: string | undefined, + selectionCallback: (selectedItem: GridItem) => void, + ) { + this.selectionCallback = selectionCallback; + + const cell = document.createElement('div'); + utils.aria.setRole(cell, utils.aria.Role.GRIDCELL); + + this.element = document.createElement('button'); + this.element.id = utils.idGenerator.getNextUniqueId(); + this.element.className = 'blocklyFieldGridItem'; + this.clickHandler = browserEvents.conditionalBind( + this.element, + 'click', + this, + this.onClick, + true, + ); + cell.appendChild(this.element); + container.appendChild(cell); + + const contentDom = + typeof content === 'string' ? document.createTextNode(content) : content; + this.element.appendChild(contentDom); + + if (ariaLabel) { + utils.aria.setState(this.element, utils.aria.State.LABEL, ariaLabel); + } + } + + /** + * Disposes of this grid item. + */ + dispose() { + this.selectionCallback = null; + this.element.remove(); + if (this.clickHandler) { + browserEvents.unbind(this.clickHandler); + this.clickHandler = null; + } + } + + /** + * Gets the unique (within the grid) ID for this grid item. + * + * @returns This item's unique ID. + */ + getId(): string { + return this.element.id; + } + + /** + * Gets the value associated with this grid item. + * + * @returns Value associated with this grid item. + */ + getValue(): string { + return this.value; + } + + /** + * Returns whether or not this grid item is selected. + * + * @returns True if this grid item is selected, otherwise false. + */ + isSelected() { + return this.selected; + } + + /** + * Sets whether or not this grid item is selected. + * + * @param selected True if this grid item should be selected, otherwise false. + */ + setSelected(selected: boolean) { + this.selected = selected; + utils.aria.setState(this.element, utils.aria.State.SELECTED, this.selected); + this.element.classList.toggle( + 'blocklyFieldGridItemSelected', + this.selected, + ); + if (this.isSelected()) { + this.focus(); + } + } + + /** + * Handles clicks on this grid item by marking it as selected. + */ + private onClick() { + this.setSelected(true); + this.selectionCallback?.(this); + } + + /** + * Makes this grid item the browser focus target, and scrolls it into view + * if needed. + */ + focus() { + // Focus the element, but don't scroll the document since that's too + // aggressive. + this.element.focus({preventScroll: true}); + + const scrollingParent = this.element.offsetParent; + if (!scrollingParent) return; + const offsetTop = this.element.offsetTop; + const scrollTop = scrollingParent.scrollTop; + const spacing = this.getInterItemSpacing(); + + // Scroll the element into view if it's offscreen above the grid's viewport. + if (offsetTop < scrollTop) { + scrollingParent.scrollTo(0, offsetTop - spacing); + } else if ( + offsetTop + this.element.offsetHeight > + scrollTop + scrollingParent.clientHeight + ) { + // Scroll into view if this item is below the grid's viewport. + scrollingParent.scrollBy( + 0, + offsetTop + + this.element.clientHeight - + (scrollTop + scrollingParent.clientHeight) + + spacing, + ); + } + } + + /** + * Returns the vertical spacing between grid items in pixels. + * + * This value can be specified by the user in CSS, so we can't just use a + * hardcoded value. Moreover, while we could check our computed style, the + * grid gap can be specified in several units. Instead, this somewhat hackily + * finds all the sibling items in this grid and loops through them until it + * encounters one with a different vertical location from its predecessor, + * then computes the effective gap based on their relative position and + * height. + * + * @returns The vertical distance between items in this grid. + */ + private getInterItemSpacing() { + const grid = this.element.closest('.blocklyFieldGrid'); + if (!grid) return 0; + + const items = [ + ...grid.querySelectorAll('.blocklyFieldGridItem'), + ] as HTMLElement[]; + if (!items.length) return 0; + + const initialTop = items[0].offsetTop; + const initialHeight = items[0].offsetHeight; + for (const item of items) { + if (item.offsetTop !== initialTop) { + return item.offsetTop - initialHeight - initialTop; + } + } + + return 0; + } +} diff --git a/packages/plugins/field-grid-dropdown/src/index.ts b/packages/plugins/field-grid-dropdown/src/index.ts new file mode 100644 index 00000000000..f0c88104569 --- /dev/null +++ b/packages/plugins/field-grid-dropdown/src/index.ts @@ -0,0 +1,283 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Grid dropdown field. + * @author kozbial@google.com (Monica Kozbial) + */ + +import * as Blockly from 'blockly/core'; +import {Grid} from './grid'; +import type {GridItem} from './grid_item'; + +/** + * A config object for defining a field grid dropdown. + */ +export interface FieldGridDropdownConfig extends Blockly.FieldDropdownConfig { + columns?: string | number; + primaryColour?: string; + borderColour?: string; +} + +/** + * Construct a FieldGridDropdown from a JSON arg object. + */ +export interface FieldGridDropdownFromJsonConfig extends FieldGridDropdownConfig { + options?: Blockly.MenuGenerator; +} + +type FieldGridDropdownValidator = Blockly.FieldDropdownValidator; + +/** + * Grid dropdown field. + */ +export class FieldGridDropdown extends Blockly.FieldDropdown { + /** + * The number of columns in the dropdown grid. Must be an integer value + * greater than 0. Defaults to 3. + */ + private columns = 3; + + private primaryColour?: string; + + private borderColour?: string; + + /** Object representing the grid of choices shown in the dropdown. */ + private grid: Grid | null = null; + + protected override ariaTypeName = Blockly.Msg['ARIA_TYPE_FIELD_GRID']; + + /** + * Class for an grid dropdown field. + * + * @param menuGenerator A non-empty array of options for a dropdown list, + * or a function which generates these options. + * @param validator A function that is called to validate + * changes to the field's value. Takes in a language-neutral dropdown + * option & returns a validated language-neutral dropdown option, or null + * to abort the change. + * @param config A map of options used to configure the field. + * See the [field creation documentation]{@link + * https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/dropdown#creation} + * for a list of properties this parameter supports. + * @extends {Blockly.Field} + * @constructor + * @throws {TypeError} If `menuGenerator` options are incorrectly structured. + */ + constructor( + menuGenerator: Blockly.MenuGenerator, + validator?: FieldGridDropdownValidator, + config?: FieldGridDropdownConfig, + ) { + super(menuGenerator, validator, config); + + if (config?.columns) { + this.setColumns(parseInt(`${config.columns}`)); + } + + if (config && config.primaryColour) { + this.primaryColour = config.primaryColour; + } + + if (config && config.borderColour) { + this.borderColour = config.borderColour; + } + } + + /** + * Constructs a FieldGridDropdown from a JSON arg object. + * + * @param config A JSON object with options. + * @returns The new field instance. + * @package + * @nocollapse + */ + static fromJson(config: FieldGridDropdownFromJsonConfig) { + if (!config.options) { + throw new Error( + 'options are required for the dropdown field. The ' + + 'options property must be assigned an array of ' + + '[humanReadableValue, languageNeutralValue] tuples.', + ); + } + // `this` might be a subclass of FieldDropdown if that class doesn't + // override the static fromJson method. + return new this(config.options, undefined, config); + } + + /** + * Sets the number of columns on the grid. Updates the styling to reflect. + * + * @param columns The number of columns. Is rounded to + * an integer value and must be greater than 0. Invalid + * values are ignored. + */ + setColumns(columns: number) { + if (!isNaN(columns) && columns >= 1) { + this.columns = columns; + // If the field is currently being shown, reload the grid. + if ( + Blockly.DropDownDiv.getOwner() === this && + Blockly.DropDownDiv.isVisible() + ) { + this.grid?.dispose(); + this.showEditor_(); + } + } + } + + /** + * Updates the ARIA roles and label for this field. + */ + override recomputeAriaContext(): boolean { + const shouldCustomize = super.recomputeAriaContext(); + if (!shouldCustomize) return false; + const focusableElement = this.getFocusableElement(); + Blockly.utils.aria.setState( + focusableElement, + Blockly.utils.aria.State.HASPOPUP, + 'grid', + ); + Blockly.utils.aria.setState( + focusableElement, + Blockly.utils.aria.State.EXPANDED, + !!this.grid, + ); + return true; + } + + /* eslint-disable @typescript-eslint/naming-convention */ + /** + * Create a dropdown menu under the text. + * + * @param e Optional mouse event that triggered the field to open, or + * undefined if triggered programmatically. + */ + protected showEditor_(e?: MouseEvent) { + Blockly.DropDownDiv.clearContent(); + const rtl = !!this.getSourceBlock()?.workspace.RTL; + this.grid = new Grid( + Blockly.DropDownDiv.getContentDiv(), + this.getOptions(false), + this.columns, + rtl, + (selectedItem: GridItem) => { + Blockly.DropDownDiv.hideIfOwner(this); + this.setValue(selectedItem.getValue()); + }, + ); + + Blockly.DropDownDiv.getContentDiv().classList.add( + 'blocklyFieldGridContainer', + ); + + const colours = this.getColours(); + if (colours && colours.border) { + Blockly.DropDownDiv.setColour(colours.primary, colours.border); + } + + Blockly.DropDownDiv.showPositionedByField( + this, + this.dropdownDispose_.bind(this), + ); + + const selectedValue = this.getValue(); + if (selectedValue) { + this.grid.setSelectedValue(selectedValue); + } + this.recomputeAriaContext(); + } + + /** + * Disposes of events and DOM-references belonging to the dropdown editor. + */ + protected override dropdownDispose_() { + // Keep aria-expanded accurate on later recomputes. + this.grid = null; + super.dropdownDispose_(); + this.recomputeAriaContext(); + } + + /** + * Updates the field's value to the given value. + * + * @param newValue The new value for this field. + */ + protected override doValueUpdate_(newValue: string) { + super.doValueUpdate_(newValue); + this.grid?.setSelectedValue(newValue); + } + + /** + * Determine the colours for the dropdowndiv. The dropdown should match block + * colour unless other colours are specified in the config. + * + * @returns The colours to set for the dropdowndiv. + */ + private getColours() { + if (this.primaryColour && this.borderColour) { + return { + primary: this.primaryColour, + border: this.borderColour, + }; + } + + const sourceBlock = this.getSourceBlock(); + if (!(sourceBlock instanceof Blockly.BlockSvg)) return; + + const colourSource = sourceBlock.isShadow() + ? sourceBlock.getParent() + : sourceBlock; + if (!colourSource) return; + + return { + primary: this.primaryColour ?? colourSource.getColour(), + border: this.borderColour ?? colourSource.getColourTertiary(), + }; + } +} + +Blockly.fieldRegistry.register('field_grid_dropdown', FieldGridDropdown); + +/** + * CSS for grid field. + */ +Blockly.Css.register(` + .blocklyFieldGridContainer { + padding: 7px; + overflow: auto; + } + + .blocklyFieldGrid { + display: grid; + grid-gap: 7px; + grid-template-columns: repeat(var(--grid-columns), min-content); + } + + .blocklyFieldGrid .blocklyFieldGridItem { + border: 1px solid rgba(1, 1, 1, 0.5); + border-radius: 4px; + color: white; + min-width: auto; + background: none; + white-space: nowrap; + cursor: pointer; + padding: 6px 15px; + } + + .blocklyFieldGrid .blocklyFieldGridRow { + display: contents; + } + + .blocklyFieldGrid .blocklyFieldGridItem.blocklyFieldGridItemSelected { + background-color: rgba(1, 1, 1, 0.25); + } + + .blocklyFieldGrid .blocklyFieldGridItem:focus { + box-shadow: 0 0 0 4px hsla(0, 0%, 100%, .2); + outline: none; + } + `); diff --git a/packages/plugins/field-grid-dropdown/test/field_test.mocha.js b/packages/plugins/field-grid-dropdown/test/field_test.mocha.js new file mode 100644 index 00000000000..6ba8fa1bea2 --- /dev/null +++ b/packages/plugins/field-grid-dropdown/test/field_test.mocha.js @@ -0,0 +1,218 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +const {testHelpers} = require('@blockly/dev-tools'); +const {FieldGridDropdown} = require('../src/index'); + +const { + assertFieldValue, + FieldCreationTestCase, + FieldValueTestCase, + runConstructorSuiteTests, + runFromJsonSuiteTests, + runSetValueTests, +} = testHelpers; + +suite('FieldGridDropdown', function () { + /** + * Configuration for field tests with invalid values. + * @type {Array} + */ + const invalidValueCreationTestCases = [ + {title: 'Undefined', args: [undefined]}, + {title: 'Array Items not Arrays', args: [undefined]}, + { + title: 'Array Items with Invalid IDs', + args: [ + [ + ['1', 1], + ['2', 2], + ['3', 3], + ], + ], + }, + { + title: 'Array Items with Invalid Content', + args: [ + [ + [1, '1'], + [2, '2'], + [3, '3'], + ], + ], + }, + ]; + /** + * Configuration for field tests with valid values. + * @type {Array} + */ + const validValueCreationTestCases = [ + { + title: 'Text Dropdown', + value: 'A', + expectedValue: 'A', + expectedText: 'a', + args: [ + [ + ['a', 'A'], + ['b', 'B'], + ['c', 'C'], + ], + ], + }, + { + title: 'Image Dropdown', + value: 'A', + expectedValue: 'A', + expectedText: 'a', + args: [ + [ + [{src: 'scrA', alt: 'a', width: 0, height: 0}, 'A'], + [{src: 'scrB', alt: 'b', width: 0, height: 0}, 'B'], + [{src: 'scrC', alt: 'c', width: 0, height: 0}, 'C'], + ], + ], + }, + { + title: 'Dynamic Text Dropdown', + value: 'A', + expectedValue: 'A', + expectedText: 'a', + args: [ + () => { + return [ + ['a', 'A'], + ['b', 'B'], + ['c', 'C'], + ]; + }, + ], + }, + { + title: 'Dynamic Image Dropdown', + value: 'A', + expectedValue: 'A', + expectedText: 'a', + args: [ + () => { + return [ + [{src: 'scrA', alt: 'a', width: 0, height: 0}, 'A'], + [{src: 'scrB', alt: 'b', width: 0, height: 0}, 'B'], + [{src: 'scrC', alt: 'c', width: 0, height: 0}, 'C'], + ]; + }, + ], + }, + ]; + const addJson = function (testCase) { + testCase.json = {options: testCase.args[0]}; + }; + invalidValueCreationTestCases.forEach(addJson); + validValueCreationTestCases.forEach(addJson); + + /** + * Asserts that the field properties are correct based on the test case. + * @param {!FieldGridDropdown} field The field to check. + * @param {!FieldValueTestCase} testCase The test case. + */ + const validTestCaseAssertField = function (field, testCase) { + assertFieldValue(field, testCase.expectedValue, testCase.expectedText); + }; + + runConstructorSuiteTests( + FieldGridDropdown, + validValueCreationTestCases, + invalidValueCreationTestCases, + validTestCaseAssertField, + ); + + runFromJsonSuiteTests( + FieldGridDropdown, + validValueCreationTestCases, + invalidValueCreationTestCases, + validTestCaseAssertField, + ); + + /** + * Configuration for field tests with invalid values. + * @type {!Array} + */ + const invalidValueSetValueTestCases = [ + {title: 'Null', value: null}, + {title: 'Undefined', value: undefined}, + {title: 'Invalid ID', value: 'bad'}, + ]; + /** + * Configuration for field tests with valid values. + * @type {!Array} + */ + const validValueSetValueTestCases = [ + {title: 'Valid ID', value: 'B', expectedValue: 'B', expectedText: 'b'}, + ]; + + suite('setValue', function () { + setup(function () { + this.field = new FieldGridDropdown([ + ['a', 'A'], + ['b', 'B'], + ['c', 'C'], + ]); + }); + runSetValueTests( + validValueSetValueTestCases, + invalidValueSetValueTestCases, + 'A', + 'a', + ); + }); + + suite('Validators', function () { + setup(function () { + this.dropdownField = new FieldGridDropdown([ + ['1a', '1A'], + ['1b', '1B'], + ['1c', '1C'], + ['2a', '2A'], + ['2b', '2B'], + ['2c', '2C'], + ]); + }); + teardown(function () { + this.dropdownField.setValidator(null); + }); + suite('Null Validator', function () { + setup(function () { + this.dropdownField.setValidator(function () { + return null; + }); + }); + test('New Value', function () { + this.dropdownField.setValue('1B'); + assertFieldValue(this.dropdownField, '1A', '1a'); + }); + }); + suite('Force 1s Validator', function () { + setup(function () { + this.dropdownField.setValidator(function (newValue) { + return '1' + newValue.charAt(1); + }); + }); + test('New Value', function () { + this.dropdownField.setValue('2B'); + assertFieldValue(this.dropdownField, '1B', '1b'); + }); + }); + suite('Returns Undefined Validator', function () { + setup(function () { + this.dropdownField.setValidator(function () {}); + }); + test('New Value', function () { + this.dropdownField.setValue('1B'); + assertFieldValue(this.dropdownField, '1B', '1b'); + }); + }); + }); +}); diff --git a/packages/plugins/field-grid-dropdown/test/index.html b/packages/plugins/field-grid-dropdown/test/index.html new file mode 100644 index 00000000000..10cd0a0c1da --- /dev/null +++ b/packages/plugins/field-grid-dropdown/test/index.html @@ -0,0 +1,18 @@ + + + + + Blockly Field Test + + + + +
+ + + diff --git a/packages/plugins/field-grid-dropdown/test/index.ts b/packages/plugins/field-grid-dropdown/test/index.ts new file mode 100644 index 00000000000..9ccc35fd8af --- /dev/null +++ b/packages/plugins/field-grid-dropdown/test/index.ts @@ -0,0 +1,468 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Field test. + */ + +import * as Blockly from 'blockly'; +import {createPlayground} from '@blockly/dev-tools'; +import '../src/index'; + +/** + * An array of blocks that are defined only for the purposes of + * manually and visually testing the dropdown grid field. + */ +const testBlockDefinitions = [ + { + type: 'test_standard_field_values', + message0: '%1', + args0: [ + { + type: 'field_grid_dropdown', + name: 'FIELDNAME', + options: [['A', 'A']], + alt: { + type: 'field_label', + text: `No field_grid_dropdown`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values', + message0: 'Different text length %1', + args0: [ + { + type: 'field_grid_dropdown', + name: 'FIELDNAME', + options: [ + ['A', 'A'], + ['long text ', 'long text'], + ['B', 'B'], + ['C', 'C'], + [ + 'really really really loooooong text', + 'really really really loooooong text', + ], + ['D', 'D'], + ['E', 'E'], + ], + alt: { + type: 'field_label', + text: `No field_grid_dropdown`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_long_text_list', + message0: 'Long text list %1', + args0: [ + { + type: 'field_grid_dropdown', + name: 'FIELDNAME', + options: [ + ['A', 'A'], + ['B', 'B'], + ['C', 'C'], + ['D', 'D'], + ['E', 'E'], + ['D', 'F'], + ['G', 'G'], + ['H', 'H'], + ['I', 'I'], + ['J', 'J'], + ['K', 'K'], + ['L', 'L'], + ['M', 'M'], + ['N', 'N'], + ['O', 'O'], + ['P', 'P'], + ['Q', 'Q'], + ['R', 'R'], + ['S', 'S'], + ['T', 'T'], + ['U', 'U'], + ['V', 'V'], + ['W', 'W'], + ['X', 'X'], + ['Y', 'Y'], + ['Z', 'Z'], + ], + alt: { + type: 'field_label', + text: `No field_grid_dropdown`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_images', + message0: 'Images %1', + args0: [ + { + type: 'field_grid_dropdown', + name: 'FIELDNAME', + options: [ + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/a.png', + width: 32, + height: 32, + alt: 'A', + }, + 'A', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/b.png', + width: 32, + height: 32, + alt: 'B', + }, + 'B', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/c.png', + width: 32, + height: 32, + alt: 'C', + }, + 'C', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/d.png', + width: 32, + height: 32, + alt: 'D', + }, + 'D', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/e.png', + width: 32, + height: 32, + alt: 'E', + }, + 'E', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/f.png', + width: 32, + height: 32, + alt: 'F', + }, + 'F', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/g.png', + width: 32, + height: 32, + alt: 'G', + }, + 'G', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/h.png', + width: 32, + height: 32, + alt: 'H', + }, + 'H', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/i.png', + width: 32, + height: 32, + alt: 'I', + }, + 'I', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/j.png', + width: 32, + height: 32, + alt: 'J', + }, + 'J', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/k.png', + width: 32, + height: 32, + alt: 'K', + }, + 'K', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/l.png', + width: 32, + height: 32, + alt: 'L', + }, + 'L', + ], + [ + { + src: 'https://blockly-demo.appspot.com/static/tests/media/m.png', + width: 32, + height: 32, + alt: 'M', + }, + 'M', + ], + ], + alt: { + type: 'field_label', + text: `No field_grid_dropdown`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_4_columns', + message0: '4 columns %1', + args0: [ + { + type: 'field_grid_dropdown', + name: 'FIELDNAME', + columns: '4', + options: [ + ['A', 'A'], + ['B', 'B'], + ['C', 'C'], + ['D', 'D'], + ['E', 'E'], + ['F', 'F'], + ['G', 'G'], + ['H', 'H'], + ], + alt: { + type: 'field_label', + text: `No field_grid_dropdown`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_custom_colours', + message0: 'Custom colours %1', + args0: [ + { + type: 'field_grid_dropdown', + name: 'FIELDNAME', + primaryColour: '#783105', + borderColour: '#d6a587', + options: [ + ['A', 'A'], + ['B', 'B'], + ['C', 'C'], + ['D', 'D'], + ['E', 'E'], + ['F', 'F'], + ['G', 'G'], + ['H', 'H'], + ], + alt: { + type: 'field_label', + text: `No field_grid_dropdown`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_standard_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_grid_dropdown', + name: 'FIELDNAME', + options: [['A', 'A']], + alt: { + type: 'field_label', + text: `No field_grid_dropdown`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_custom_field_values_and_label', + message0: 'block %1', + args0: [ + { + type: 'field_grid_dropdown', + name: 'FIELDNAME', + options: [['A', 'A']], + alt: { + type: 'field_label', + text: `No field_grid_dropdown`, + }, + }, + ], + output: null, + style: 'math_blocks', + }, + { + type: 'test_parent_block', + message0: 'parent %1', + args0: [ + { + type: 'input_value', + name: 'INPUT', + }, + ], + previousStatement: null, + nextStatement: null, + style: 'loop_blocks', + }, +]; + +Blockly.defineBlocksWithJsonArray(testBlockDefinitions); + +/** + * A test toolbox containing the exported blocks and a variety of + * test blocks to exercise the dropdown grid field in different contexts + * (on a shadow block, as the only field on a block, etc). + * These are in a simple toolbox, rather than a category toolbox, so that + * they are all instantiated every time the test page is opened. + */ +const jsonToolbox = { + contents: [ + { + kind: 'label', + text: 'Test blocks: default field values', + }, + { + kind: 'block', + type: 'test_standard_field_values', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_standard_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_standard_field_values_and_label', + }, + }, + }, + }, + { + kind: 'label', + text: 'Test blocks: custom field values', + }, + { + kind: 'block', + type: 'test_custom_field_values', + }, + { + kind: 'block', + type: 'test_custom_field_values_long_text_list', + }, + { + kind: 'block', + type: 'test_custom_field_values_images', + }, + { + kind: 'block', + type: 'test_custom_field_values_4_columns', + }, + { + kind: 'block', + type: 'test_custom_field_values_custom_colours', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values', + }, + }, + }, + }, + { + kind: 'block', + type: 'test_custom_field_values_and_label', + }, + { + kind: 'block', + type: 'test_parent_block', + inputs: { + INPUT: { + shadow: { + type: 'test_custom_field_values_and_label', + }, + }, + }, + }, + ], +}; +/** + * Create a workspace. + * + * @param blocklyDiv The blockly container div. + * @param options The Blockly options. + * @returns The created workspace. + */ +function createWorkspace( + blocklyDiv: HTMLElement, + options: Blockly.BlocklyOptions, +): Blockly.Workspace { + const workspace = Blockly.inject(blocklyDiv, options); + return workspace; +} + +document.addEventListener('DOMContentLoaded', function () { + const defaultOptions: Blockly.BlocklyOptions = { + toolbox: jsonToolbox, + }; + const rootElement = document.getElementById('root'); + if (rootElement) { + createPlayground(rootElement, createWorkspace, defaultOptions); + } +}); diff --git a/packages/plugins/field-grid-dropdown/tsconfig.json b/packages/plugins/field-grid-dropdown/tsconfig.json new file mode 100644 index 00000000000..99858c525f1 --- /dev/null +++ b/packages/plugins/field-grid-dropdown/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "module": "es2015", + "moduleResolution": "bundler", + "target": "es6", + "strict": true + }, + // NOTE: `test/**/*` is automatically included in `blockly-scripts start`. + // Only src matters for production builds. + "include": ["src"] +} diff --git a/packages/plugins/field-multilineinput/CHANGELOG.md b/packages/plugins/field-multilineinput/CHANGELOG.md new file mode 100644 index 00000000000..2af0b4e971e --- /dev/null +++ b/packages/plugins/field-multilineinput/CHANGELOG.md @@ -0,0 +1,511 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [13.1.0](https://github.com/RaspberryPiFoundation/blockly-samples/compare/v13.0.0...v13.1.0) (2026-06-30) + +### ⚠ BREAKING CHANGES + +* make field-multilineinput keyboard navigable and accessible (#2729) +* Bump Blockly dependency to v13 (#2704) + +### Features + +* make field-multilineinput keyboard navigable and accessible ([#2729](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2729)) + +### Miscellaneous Chores + +* Bump Blockly dependency to v13 ([#2704](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2704)) + + +## [6.0.9](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-multilineinput@6.0.8...@blockly/field-multilineinput@6.0.9) (2026-04-09) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [6.0.8](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-multilineinput@6.0.7...@blockly/field-multilineinput@6.0.8) (2026-01-15) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [6.0.7](https://github.com/RaspberryPiFoundation/blockly-samples/compare/@blockly/field-multilineinput@6.0.6...@blockly/field-multilineinput@6.0.7) (2026-01-05) + + +### Bug Fixes + +* update plugin URLs to RPF ([#2665](https://github.com/RaspberryPiFoundation/blockly-samples/issues/2665)) ([6c34039](https://github.com/RaspberryPiFoundation/blockly-samples/commit/6c3403960b8070eb8e576bd5702bdb53a09ac475)) + + + +## [6.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@6.0.5...@blockly/field-multilineinput@6.0.6) (2026-01-05) + + +### Bug Fixes + +* Merge multiline text readme to with pre-v11 documentation ([#2660](https://github.com/google/blockly-samples/issues/2660)) ([c8d0bd3](https://github.com/google/blockly-samples/commit/c8d0bd3b37d7e6765e7761066c4033d422ec4470)) + + + +## [6.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@6.0.4...@blockly/field-multilineinput@6.0.5) (2025-12-19) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [6.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@6.0.3...@blockly/field-multilineinput@6.0.4) (2025-12-19) + + +### Bug Fixes + +* Use getTextWidth instead of getFastTextWidth ([#2632](https://github.com/google/blockly-samples/issues/2632)) ([affcd6e](https://github.com/google/blockly-samples/commit/affcd6ed28506c3640f3a77700640d38eec43841)) + + + +## [6.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@6.0.2...@blockly/field-multilineinput@6.0.3) (2025-10-02) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [6.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@6.0.1...@blockly/field-multilineinput@6.0.2) (2025-07-17) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [6.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@6.0.0...@blockly/field-multilineinput@6.0.1) (2025-05-22) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [6.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.17...@blockly/field-multilineinput@6.0.0) (2025-05-16) + + +### ⚠ BREAKING CHANGES + +* update all plugins to blockly v12 (#2538) + +### Features + +* update all plugins to blockly v12 ([#2538](https://github.com/google/blockly-samples/issues/2538)) ([420ba31](https://github.com/google/blockly-samples/commit/420ba3124b260b2c06e8e1d61be44e870780d95f)) + + + +## [5.0.17](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.16...@blockly/field-multilineinput@5.0.17) (2025-05-16) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.16](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.15...@blockly/field-multilineinput@5.0.16) (2025-05-15) + + +### Bug Fixes + +* Fix multi-line text color by using the correct class ([91a8473](https://github.com/google/blockly-samples/commit/91a8473736b5c0cc3e04c809f4a888469bc96f77)) + + +### Reverts + +* "fix(FieldMultilineInput): Use string literal instead of Field.NBSP" ([539ae4f](https://github.com/google/blockly-samples/commit/539ae4fe4499fca3d781c29f462836ac64d45ce6)) + + + + + +## [5.0.15](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.14...@blockly/field-multilineinput@5.0.15) (2025-02-13) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.13...@blockly/field-multilineinput@5.0.14) (2024-12-19) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.12...@blockly/field-multilineinput@5.0.13) (2024-12-03) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.11...@blockly/field-multilineinput@5.0.12) (2024-11-07) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.10...@blockly/field-multilineinput@5.0.11) (2024-09-26) + + +### Bug Fixes + +* **field-*:** Remove unneeded `fieldRegistry.unregister` calls ([#2454](https://github.com/google/blockly-samples/issues/2454)) ([b3ba30e](https://github.com/google/blockly-samples/commit/b3ba30e23dddf0bd98c266659aa229ba6ba685b0)), closes [#2453](https://github.com/google/blockly-samples/issues/2453) + + + + + +## [5.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.9...@blockly/field-multilineinput@5.0.10) (2024-08-29) + + +### Bug Fixes + +* predeploy plugins ([#2449](https://github.com/google/blockly-samples/issues/2449)) ([6b36d8b](https://github.com/google/blockly-samples/commit/6b36d8b344a969f79d89bbc7dcee29ae554759f9)) + + + + + +## [5.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.8...@blockly/field-multilineinput@5.0.9) (2024-08-22) + + +### Bug Fixes + +* lerna v8 breaking our whole shebangle ([#2446](https://github.com/google/blockly-samples/issues/2446)) ([b8b4c21](https://github.com/google/blockly-samples/commit/b8b4c21d4eaf81f527336ae46f6679ff99ac23c3)) + + + + + +## [5.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.7...@blockly/field-multilineinput@5.0.8) (2024-08-08) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.6...@blockly/field-multilineinput@5.0.7) (2024-08-01) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.5...@blockly/field-multilineinput@5.0.6) (2024-07-25) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.4...@blockly/field-multilineinput@5.0.5) (2024-07-11) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.3...@blockly/field-multilineinput@5.0.4) (2024-07-04) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.2...@blockly/field-multilineinput@5.0.3) (2024-06-27) + + +### Bug Fixes + +* **docs:** Update README for multiline field ([#2394](https://github.com/google/blockly-samples/issues/2394)) ([377d38c](https://github.com/google/blockly-samples/commit/377d38c919723861aa979bdcf210bedd474c3481)) + + + +## [5.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.1...@blockly/field-multilineinput@5.0.2) (2024-06-06) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [5.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@5.0.0...@blockly/field-multilineinput@5.0.1) (2024-05-30) + + +### Bug Fixes + +* multiple blockly instances ([#2375](https://github.com/google/blockly-samples/issues/2375)) ([b231e59](https://github.com/google/blockly-samples/commit/b231e598f2f5f5b0abbfd01d981e35572ad50a26)) + + + +## [5.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@4.0.4...@blockly/field-multilineinput@5.0.0) (2024-05-21) + + +### ⚠ BREAKING CHANGES + +* update all plugins to v11 of blockly + +### Features + +* update all plugins to v11 of blockly ([5fcd3e7](https://github.com/google/blockly-samples/commit/5fcd3e7d53eaadffe9bda9a378b404d34b2f8be2)) +* upgrade ts version, fix field validators ([b2d2191](https://github.com/google/blockly-samples/commit/b2d2191ff11b30347b33f95acf6e58f7ce54d004)) + + +### Bug Fixes + +* Call blocks handle both manual disabling and disabled defs ([#2334](https://github.com/google/blockly-samples/issues/2334)) ([5eade55](https://github.com/google/blockly-samples/commit/5eade55779c4022d14ad4472ff32c93c78199887)) + + + +## [4.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@4.0.3...@blockly/field-multilineinput@4.0.4) (2024-05-16) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [4.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@4.0.2...@blockly/field-multilineinput@4.0.3) (2024-05-09) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [4.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@4.0.1...@blockly/field-multilineinput@4.0.2) (2024-04-25) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [4.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@4.0.0...@blockly/field-multilineinput@4.0.1) (2024-04-11) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [4.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.14...@blockly/field-multilineinput@4.0.0) (2024-04-04) + + +### ⚠ BREAKING CHANGES + +* The multiline text input field no longer registers itself on load. The developer must either manually register the field or install blocks, which will install the field. This is part of a move to have no side effects in field and block definitions, so that tree-shaking can remove unwanted fields and blocks. +* The angle field no longer registers itself on load. The developer must manually register the field. This is part of a move to have no side effects in field and block definitions, so that tree-shaking can remove unwanted fields and blocks. + +### Features + +* add block definitions to fields ([#2299](https://github.com/google/blockly-samples/issues/2299)) ([c197af6](https://github.com/google/blockly-samples/commit/c197af6c67bc989bbdb4e83a62abc08167bcbbfe)), closes [#2162](https://github.com/google/blockly-samples/issues/2162) [#2202](https://github.com/google/blockly-samples/issues/2202) [#2221](https://github.com/google/blockly-samples/issues/2221) [#2211](https://github.com/google/blockly-samples/issues/2211) [#2220](https://github.com/google/blockly-samples/issues/2220) [#2232](https://github.com/google/blockly-samples/issues/2232) [#2233](https://github.com/google/blockly-samples/issues/2233) [#2296](https://github.com/google/blockly-samples/issues/2296) + + + +## [3.0.14](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.13...@blockly/field-multilineinput@3.0.14) (2024-03-28) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.13](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.12...@blockly/field-multilineinput@3.0.13) (2024-02-29) + + +### Bug Fixes + +* updated field multiline to have enough width ([#2208](https://github.com/google/blockly-samples/issues/2208)) ([376691d](https://github.com/google/blockly-samples/commit/376691df5b8dad2e4bd5851f6c1d6a1fa5b6d1e3)) + + + +## [3.0.12](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.11...@blockly/field-multilineinput@3.0.12) (2024-02-08) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.11](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.10...@blockly/field-multilineinput@3.0.11) (2023-12-14) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.10](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.9...@blockly/field-multilineinput@3.0.10) (2023-12-07) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.9](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.8...@blockly/field-multilineinput@3.0.9) (2023-11-09) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.8](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.7...@blockly/field-multilineinput@3.0.8) (2023-11-02) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.7](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.6...@blockly/field-multilineinput@3.0.7) (2023-10-30) + + +### Bug Fixes + +* [#1905](https://github.com/google/blockly-samples/issues/1905) publish types for field-multiline ([#1957](https://github.com/google/blockly-samples/issues/1957)) ([4537b8c](https://github.com/google/blockly-samples/commit/4537b8c6479abf29ebb51fdccf6842c9a88e79a1)) +* **packaging:** Don't advertise non-existent ESM entrypoints ([#2022](https://github.com/google/blockly-samples/issues/2022)) ([d019198](https://github.com/google/blockly-samples/commit/d0191984399b784e2928b8fb4c58257bfa857655)), closes [#1877](https://github.com/google/blockly-samples/issues/1877) +* update tsconfigs to publish types accurately ([df4b084](https://github.com/google/blockly-samples/commit/df4b0844af712f5025a2ec842458b828f3147676)) +* use includes rather than excludes ([40efc25](https://github.com/google/blockly-samples/commit/40efc255329e3ca476ccc247b95a2d05dd77b45e)) + + + +## [3.0.6](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.5...@blockly/field-multilineinput@3.0.6) (2023-09-21) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.5](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.4...@blockly/field-multilineinput@3.0.5) (2023-09-14) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.4](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.3...@blockly/field-multilineinput@3.0.4) (2023-08-31) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.3](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.2...@blockly/field-multilineinput@3.0.3) (2023-08-17) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.2](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.1...@blockly/field-multilineinput@3.0.2) (2023-07-24) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@3.0.0...@blockly/field-multilineinput@3.0.1) (2023-07-20) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [3.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@2.0.1...@blockly/field-multilineinput@3.0.0) (2023-07-13) + + +### ⚠ BREAKING CHANGES + +* updated `plugins/field-date` to TypeScript (#1705) + +### Features + +* updated `plugins/field-date` to TypeScript ([#1705](https://github.com/google/blockly-samples/issues/1705)) ([e5531ff](https://github.com/google/blockly-samples/commit/e5531fffe188ee361a16fe48ed126b34e51a8d30)) + + + +## [2.0.1](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@2.0.0...@blockly/field-multilineinput@2.0.1) (2023-07-06) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [2.0.0](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@1.2.1...@blockly/field-multilineinput@2.0.0) (2023-06-28) + + +### ⚠ BREAKING CHANGES + +* update all plugins to use blockly-v10.0.0 (#1744) + +### Features + +* update all plugins to use blockly-v10.0.0 ([#1744](https://github.com/google/blockly-samples/issues/1744)) ([6cc88cb](https://github.com/google/blockly-samples/commit/6cc88cbef39d4ad664a668d3d46eb29ba7292f9c)) + + + +## [1.2.1](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@1.2.0...@blockly/field-multilineinput@1.2.1) (2023-06-22) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## [1.2.0](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@1.1.1...@blockly/field-multilineinput@1.2.0) (2023-05-25) + + +### Features + +* Revise fields ([#1700](https://github.com/google/blockly-samples/issues/1700)) ([c326f7b](https://github.com/google/blockly-samples/commit/c326f7b1ef54fe45387046c693ebd5afe2b2c571)) + + + +## [1.1.1](https://github.com/google/blockly-samples/compare/@blockly/field-multilineinput@1.1.0...@blockly/field-multilineinput@1.1.1) (2023-05-18) + +**Note:** Version bump only for package @blockly/field-multilineinput + + + + + +## 1.1.0 (2023-05-11) + + +### Features + +* Add angle, colour, and multiline fields. (Rebase of [#1674](https://github.com/google/blockly-samples/issues/1674)) ([#1693](https://github.com/google/blockly-samples/issues/1693)) ([1b447f4](https://github.com/google/blockly-samples/commit/1b447f41d6293f14e846b8a4d82d289a5637a99a)) diff --git a/packages/plugins/field-multilineinput/README.md b/packages/plugins/field-multilineinput/README.md new file mode 100644 index 00000000000..002e50a79ea --- /dev/null +++ b/packages/plugins/field-multilineinput/README.md @@ -0,0 +1,237 @@ +# @blockly/field-multilineinput [![Built on Blockly](https://tinyurl.com/built-on-blockly)](https://github.com/google/blockly) + +A [Blockly](https://www.npmjs.com/package/blockly) multiline input field and +associated block. + +#### Multiline input field + +![A block with the label "multiline text input:" and a multiline input field +with the text "default text \n with newline character.](readme-media/on_block.png) + +#### Multiline input field with editor open + +![The same block with editor open.](readme-media/with_editor.png) + +#### Multiline input on collapsed block + +![The same block after being collapsed. It has the label "multiline text input: +defau..." and a jagged right edge to show it is collapsed.](readme-media/collapsed.png) + +## Installation + +### Yarn + +``` +yarn add @blockly/field-multilineinput +``` + +### npm + +``` +npm install @blockly/field-multilineinput --save +``` + +## Usage + +This plugin adds a field of type `FieldMultilineInput` that is registered with +the name `'field_multilinetext'`. It is a subclass of `Blockly.FieldInput`. + +This field stores a string as its value and a string as its text. Its value is +always a valid string, while its text could be any string entered into its +editor. Unlike a text input field, this field also supports newline characters +entered in the editor. + +#### Editor keyboard shortcuts + +The default keyboard mapping is: + +| Key | Action | +| ----------- | ------------------------------------------------- | +| Enter | Commit the value and close the editor | +| Shift+Enter | Insert a newline at the cursor | +| Escape | Revert to the original value and close the editor | + +With `FieldMultilineInput.enterCommits = false` the mapping is swapped: + +| Key | Action | +| ----------- | ------------------------------------------------- | +| Enter | Insert a newline at the cursor | +| Shift+Enter | Commit the value and close the editor | +| Escape | Revert to the original value and close the editor | + +`enterCommits` and `showHint` are global (static) settings that apply to all +fields. Set them once before creating your blocks: + +```js +import {FieldMultilineInput} from '@blockly/field-multilineinput'; + +FieldMultilineInput.enterCommits = false; // default: true +FieldMultilineInput.showHint = false; // default: true +``` + +The constructor for this field accepts three optional parameters: + +- `value`: The default text. Defaults to `""`. +- `validator`: A function that is called to validate what the user entered. +- `config`: An object with optional properties: + - `maxLines`: The maximum number of lines displayed before scrolling + functionality is enabled. Defaults to `Infinity`. + - `spellcheck`: Whether spell checking is enabled. Defaults to `true`. + - `tooltip`: A tooltip. + +If you want to use only the field, you must register it with Blockly. You can +do this by calling `registerFieldMultilineInput` before instantiating your +blocks. If another field is registered under the same name, this field will +overwrite it. + +### JavaScript + +```js +import * as Blockly from 'blockly'; +import {registerFieldMultilineInput} from '@blockly/field-multilineinput'; + +registerFieldMultilineInput(); +Blockly.Blocks['test_field_multilineinput'] = { + init: function () { + this.appendDummyInput() + .appendField('multilineinput: ') + .appendField( + new FieldMultilineInput('some text \n with newlines'), + 'FIELDNAME', + ); + }, +}; +``` + +### JSON + +```js +import * as Blockly from 'blockly'; +import {registerFieldMultilineInput} from '@blockly/field-multilineinput'; + +registerFieldMultilineInput(); +Blockly.defineBlocksWithJsonArray([ + { + "type": "test_field_multilinetext", + "message0": "multilineinput: %1", + "args0": [ + { + "type": "field_multilinetext", + "name": "FIELDNAME", + "text": "some text \n with newlines" + } + }]); +``` + +### Customization + +#### Spellcheck + +The `setSpellcheck` function can be used to set whether the field spellchecks +its input text or not. Spellchecking is on by default. + +![An animation showing multiline text input fields with and without +spellcheck.](readme-media/spellcheck.gif) + +This applies to individual fields. If you want to modify all fields change the +`Blockly.FieldMultilineInput.prototype.spellcheck_` property. + +#### Validation + +A multiline text input field's value is a string, so any validators must accept +a string and return a string, `null`, or `undefined`. + +Here is an example of a validator that removes all 'a' characters from the +string: + +``` +function(newValue) { + return newValue.replace(/a/gm, ''); +} +``` + +![An animation showing validation.](readme-media/validator.gif) + +### Serialization + +#### JSON + +The JSON for a multiline text input field looks like so: + +```json +{ + "fields": { + "FIELDNAME": "line1\nline2" + } +} +``` + +where `FIELDNAME` is a string referencing a multiline text input field, and the +value is the value to apply to the field. The value follows the same rules as +the constructor value. + +#### XML + +The XML for a multiline text input field looks like so: + +```xml +line1&#10;line2 +``` + +where the field's `name` attribute contains a string referencing a multiline +text input field, and the inner text is the value to apply to the field. The +inner text value follows the same rules as the constructor value. + +### Blocks + +This package also provides a simple block containing a multiline input +field. It has generators in JavaScript, Python, PHP, Lua, and Dart. + +You can install the block by calling `textMultiline.installBlock()`. +This will install the block and all of its dependencies, including the +multiline input field. When calling `installBlock` you can supply one or +more `CodeGenerator` instances (e.g. `javascriptGenerator`), and the install +function will also install the correct generator function for the +corresponding language(s). + +```js +import {javascriptGenerator} from 'blockly/javascript'; +import {dartGenerator} from 'blockly/dart'; +import {phpGenerator} from 'blockly/php'; +import {pythonGenerator} from 'blockly/python'; +import {luaGenerator} from 'blockly/lua'; +import {textMultiline} from '@blockly/field-multilineinput'; + +// Installs the block, the field, and all of the language generators. +textMultiline.installBlock({ + javascript: javascriptGenerator, + dart: dartGenerator, + lua: luaGenerator, + python: pythonGenerator, + php: phpGenerator, +}); +``` + +### API reference + +Instance methods: + +- `setMaxLines`: Sets the maximum number of displayed lines before + scrolling functionality is enabled. +- `getMaxLines`: Returns the maximum number of displayed lines before + scrolling functionality is enabled. +- `setSpellcheck`: Sets whether spell checking is enabled. +- `getSpellcheck`: Returns whether spell checking is enabled. + +Static (global) properties: + +- `FieldMultilineInput.enterCommits`: Whether pressing Enter commits the + value (`true`, default) or inserts a newline (`false`). Applies to all + fields. +- `FieldMultilineInput.showHint`: Whether the keyboard-shortcut hint bar is + shown in the editor. When `false`, no space is reserved for the hint bar. + Applies to all fields. + +## License + +Apache 2.0 diff --git a/packages/plugins/field-multilineinput/package.json b/packages/plugins/field-multilineinput/package.json new file mode 100644 index 00000000000..5cfa8a88d02 --- /dev/null +++ b/packages/plugins/field-multilineinput/package.json @@ -0,0 +1,55 @@ +{ + "name": "@blockly/field-multilineinput", + "version": "13.1.0", + "description": "A Blockly multilineinput field.", + "scripts": { + "audit:fix": "blockly-scripts auditFix", + "build": "blockly-scripts build", + "clean": "blockly-scripts clean", + "lint": "eslint .", + "predeploy": "blockly-scripts predeploy", + "prepublishOnly": "npm run clean && npm run build", + "start": "blockly-scripts start", + "test": "blockly-scripts test" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "unpkg": "./dist/index.js", + "author": "Blockly Team", + "keywords": [ + "blockly", + "field", + "multilineinput" + ], + "homepage": "https://github.com/RaspberryPiFoundation/blockly-samples/tree/master/plugins/field-multilineinput#readme", + "bugs": { + "url": "https://github.com/RaspberryPiFoundation/blockly-samples/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/RaspberryPiFoundation/blockly-samples.git", + "directory": "plugins/field-multilineinput" + }, + "license": "Apache-2.0", + "directories": { + "dist": "dist", + "src": "src" + }, + "files": [ + "dist", + "src" + ], + "devDependencies": { + "@blockly/dev-scripts": "^13.1.0", + "@blockly/dev-tools": "^13.1.0", + "chai": "^6.2.2", + "sinon": "^22.0.0", + "typescript": "^6.0.3" + }, + "peerDependencies": { + "blockly": "^13.2.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/plugins/field-multilineinput/readme-media/collapsed.png b/packages/plugins/field-multilineinput/readme-media/collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..a227642a7051649b71a665d9f6fef6fa5db049db GIT binary patch literal 3564 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 m zDwG8nSZi*a$q9%FM_g)Bg&LeNn2KQmX(=gaC{=-(ZXIYTSAmkeumDpr?##D03d)zq zCMUo@CIYV`f{|4qf6l;K?Ch8mk`RN$w3Khp0>X^SWdSJwn#!u^Y-o;3@(ShZOE_7u zKxr+Io|VZds{8NWh@HZ~TI75*=Ur?#GO|AJYG92j8W`5x3U~ZHamW7^swgVKSi2@3 z1$v>9oIHN0+W_tAn!@&~BkF7EVuVF!Y`lAc!`9!Mb;Oe(AGr8T`l*uO6ovLN($575uN#=x?>>X`F3^1URlRjR&3f%PQVo8i>D zjtLfBJqlTA8PrkNMqTx4aPjtlkyed-V8y2SA<@ zZrJR45rJ`$r9Jhd#}WKQ=T-0^`n!pCEyzeo<8iQexvo{3lL=M?NGV8R{}Y!IPf?av zK=Yc0(ATV9;<3U3f(0aLfrPXa+z)t-;P~hY3aok!(OS=#fhF1~haw6r(FsV>K-3pK z$c9?F7;V{Aa=#3m>BVIwDOt7Rr(4XUUo5R0#$`d2#AS*^ILbgz4t9-T=M9aSP$XflPh}e z)dQYy4OLC_H){=Jt(r*ANMl3h_Md~!hK<=X-0FG}@8Tj+TTP2SG~eh@1ja^+&1?Jp z%kYT~&A+Z_NO zva+)9;;|bxELx6*8`nV3pdKPa!{G1bgH~4U3!OrR(lL=yc=^N~?R#|lq-%fGypHc0&w853)Ga9F|Jh)kOy@7`903+cd6e5jjHQm z<@J+LQ>-kOqbdqml*4il_Cb67hWu@AchwQ{va)DXiX0XJ>lgDjXsA;MXJ6dMqrkUl zQd6J9o>LyKcpe&nmbH!Y|4l$Kt6m3f>zU&8b5}flFP_JtxlkUB@?~4^T>`D1R#T~p z;TD}3V3%G$hO(R@x*9hHL44EQi_lP3;d#AinQh;u<>PrRokC&adyKS`DLHc7>z+ZFoim!C1Qn zMz`pS(1ciMDp$eI`_AlPaSp4Pknm%`vNABo+M8!M;a^2T3EhmEqNbV#Hrzg!@8uqN zay>t3UQbtd|5$Y#ik@OFnXI>iiJz`AS9-x;>^qYxas=>%JPaF8d@7Q!Iih} zIQQxy+v8J!)vj(sEOI`AY~(;ip%OY7nlq@@-8#!ZN44n6p(rDJE6+Xnp!^o^5hlC07@wn!65l)AWAuc)whNdRy zJM1@Dc4!MX*9Vw4W&)32u~H?px9$R)`7;KPd#H6;aiuU*ISUHh?m-*&w7@#T)V9~1L-bdHiLJTbPHvAIz<3MlNwC;ghzAxD;6m1zmL1{3VQ>Ist0xRpE5V5gw1+&~L;L{5;(*) zY=f2^+u^mx3#^&F2on~}f{A$}JiK)WyVp4I-%XpC!Rys)oY;K;W|qw`cGk2~18af9 zYA7o!loM`Tx(bJ5`;n2Bj+>XS7CGJ)9opjU^OsmR z&kpUocR^e0&e*VEDGVAIGa&undSmPIHR#Zz8@l)Z8M1P+<$GF%vn7+mA`h#yz)H;^ zV9__f53YGVlSlVMcWobxu~_@Oh4~#5lC5T^~O-u;gTZ zM+0-*_It_yUs74a$3nuAfJI?B0iPBPcm-9vCWbe+!hy%vk&&H+u`R8Um)s}MuZv-0 zSkyMm2Uc0i8j8wcz4P^hgY7a{w*3Kv{usd!@O$fx)w33$opl%Xn0BmOhXzf}@$1-8 zY-37tpWc52A%THdv}ql3L&CWnvKgB-g7+&=M23aKs#o{?<4^59gdqO_*lt;0YG6^) zy{sW&u>ZRf$D5Rxh<}{^#(|AnVba(fNr?%t-MpS*O*t$fB$O;=Kf8w&pOTDU%zuD( zWmQfXS5~NmUJWc+)25(@^*G3j!9WiaC4=9`MRW4Jq{311nu-G!0f`dXy&JT^1pJYY3$VS$W{ z3ZsvHED;s>N*^*khevvQuJWwl{HXEc-Q|GYN%;);+pbJ zv`kOoPe?`OE^`GgwC>bF%*=A~a?sMz;haw% zu#6g+;@0JB*tyys<}I4T%%UkYt5t)?!zTz0exD1hC+^s=a2b~^P!204IfZjor}iBZ z%VB+rhehx#X}mOv0B>I`n>q_IQ8DN<_!kVDWFr7e@~q+>%LT0eXiP%4Ww1bAEu3gr zUWU^@>CmD=!a!etY_eMc@{9)CjN!fmynV2Ct}R+wbwImrojGJAu-MF-$$zK3*R4xe zk&>K*fn!F9g@n~~t8)#Pa%B}vTegso4}Twu-Rm5Xl$eC2+a2Kf)B~$$E#w3_)jEBM zjl;Pk$8g)(r4X>nTGkNc7f|AOqa&j@$Mw?v8M^iT5oZq`!(Yo5!px!>dssqQ!zX>j ziabdGmSpYu*?X%WhyI42h795ayW@^M z81%9zH>j6RFn$=tBRT`9JlX*&l@j{nLZgN<_+^hQ@U}J9nOQKmYzA3bSB+3pAYed2T>VAZTs8*cX=z+vGsRMpgk-8OqJjmT@+ z+1XqLzkc3gP!XxkycsZQ+~}j@rBy%jpw`V>48sP-yhuQ~xHm6eVeOnnXlLCS{e}<5 zrISuLzH2XgT$GF#TKOy>0IPiV`!3HeOQrjFX&_R#~hi9Lp9EfF*nozLW(7 zV0|gef5q1ofb|vMysv!d0fh)Eff3dEKrt61@&*P#9t%G41dVK}mF7fZ*C zoH`NCXV0@1*p^d=#j5E4>p>?aB=WVdoxOlPBolDbr#6gWr#)~8tQ zJ3mL#z#_$lh>%d+x_lKKdRntokEoRK*8(ik=b}n|dV#yokU@}^mi}y?l!^!}f>m*n z)SqG;0a%}6weS2KNdv2(IV0$UE+S?8wE%14!Z}~;lTi_YRq#Ga!Z-r3Bw@wxtdG(F zEAhhzeg}_~80?Skl` z9oh{Q)hevpMeh;PxL>(@6bcD9pFG6lTt00M>$zWby1+^j^e*HOn=vqHY{pt(^rE!m z&V9IZ3I-;|h2}-alh;D>&v%^vL1tzqnzd<(A>+qFvzj)Cgs1l% z<}s7fYkGP*%lM3#GM@iV_oOJfX>FZqcE;VB&kWl`ePE=BsoohJHC5AVj?0D9TA0; zq$G@1C@jc$%~n?WC2VYo8it?7ls1m+(>(?U=Aw}Sks68fhMgiSTZ>y zJp=n3wjwPx4a;|IL|jZPD?v~QIef|le!1J_{NM0+^n_oO)~TsmXkPmrwjsdVoAYIg zN|hK;1j0G%S8zy8ay%Ao+o0dD!3Ya}kK=m|z_MK%44-5JSC<<_%1!?{YZ?P<=7>Mh zsCiR{CT)=y6%kS7IpgL`2T2O8nY|EN)vIC9*pd7tL5dipncsi-5V8A6UmnFtWRtY? zo^>1X?7?IFH1tZID(YqWDFWJ@}o)jJYX%GHU~CyXP|AjPVCjW zUb_y*oqMrrFR4p}WA*HX7(Z_YZ`MJLAiit0J#X6a+n>2I($r|qUN1bcYSseQ!zXPq zTC4~S3TAk=>D&>I@7%*h$5U9c-2pZ9Y9S^n3a9oRMv$)`=Gm_KRD zT!)_Bc)Z0)RL@<%lEo5F?B0)MJ2x|6>6|wnFA(JC&l`(4?%12ZOHoOZ)8ppOz`hMz z5EUAZ)dzO)eJj`mV0}lRkzu1o#r+23Q06@?)nvgloae2EWwD$HVi(O^Os=Jwl#d;dg1p8W3h9U zJ%^*FP0Wi07I_~uCkg>+JdbYOWy$EpTkScNvYqf3PC=eGu9#}K zAb)HW-aWp3k3A@Y3wf-fEiZ>9x*o>)57IHqMvWg0O4azFYyk(bYS;I$xRiLb)Bw$gfm6?%=X-jjJO7noF zQ?f5fsMg8R#QDEfrJj=5r$U#zkkSX;fudY&fy0Z`Y{#ImV#P^+FEq@`szoGhvit0Z78-?bUh;gOu=CUsU)FQrCF^$W*N!k~eP znCD1vr(D;{>GLq^uZbLDlQ-t=`G#efhfe$xfqwpCby%XnBE{BX<)*i7U=K?aSOodv z&RMu&HEuiq!-;YVy|t_BFyvgX-QWcm+K_|te0e)w0PolG-+wczbjbj zu5VIbOv-rl^hr3rYoEk`HFD~B-d*R(-Fy6iP+5b9mL@v(vc`bX!%z*fzi z4{co?-ZPOr7FtQ9m9BaAt6*qqTrOZ~RntazXc#}hl-Em7OGkqyW*9hnIGVO@QRrTh zM-?6t%FrT-XbK%^GuI*G|A4BRTA@mJQ4fney5i)fSMJ%0_W^-o9u`5nB#CM&@$m6{ z%b_x@3aeJFicUZFz`!xXAS)+VNRnDXQK3kQY=WI={oTht%tA=`Ssw9k>mp?V*0(it zVK!gd0s^qU^d0yLuP*@WE4*pn$dHQ!)*|PlIsVaM$jZt7My4uELsAw{mRCUYnugHV ztS+f8g?*9eY}&ObgYGZRuvuN}_`jEg{0`oCMKm#RnMpl{v+in4Mr z)zO2wu6QlbH$QPcmKv$GJJoB;or2)vF6MpkxKVPHij#O0jwaD{(d z1m26pi-KhZ3kVhvEKrdwz#djkPEJm6TomkYIUznR1*w_o6={sZSOg0Q77#4J7RUz{ zy$;|P6^iATk0MP5{6eVE1Pcfj5G+uUERdVS^7PCxF)=}QP7XrjqOteR-|z^12N^k8 r{!dzl2qrYa0)hnu3w&7%5U~CSqxG`bukl?B00000NkvXXu0mjf$;BCc literal 0 HcmV?d00001 diff --git a/packages/plugins/field-multilineinput/readme-media/spellcheck.gif b/packages/plugins/field-multilineinput/readme-media/spellcheck.gif new file mode 100644 index 0000000000000000000000000000000000000000..3f1ae53fa9eefa977afb7234604cbade996aa38a GIT binary patch literal 255733 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 z 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%0DN>A{DY5z0&f$v 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 XFR=$o`_KR67x^0?e*^zJzk&Y)XkD!I literal 0 HcmV?d00001 diff --git a/packages/plugins/field-multilineinput/readme-media/with_editor.png b/packages/plugins/field-multilineinput/readme-media/with_editor.png new file mode 100644 index 0000000000000000000000000000000000000000..98dacf3a9073d5912e7ce106176b178e0b82c3ff GIT binary patch literal 6690 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 literal 0 HcmV?d00001 diff --git a/packages/plugins/field-multilineinput/src/blocks/generatorsType.ts b/packages/plugins/field-multilineinput/src/blocks/generatorsType.ts new file mode 100644 index 00000000000..e9d2df4609c --- /dev/null +++ b/packages/plugins/field-multilineinput/src/blocks/generatorsType.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type {JavascriptGenerator} from 'blockly/javascript'; +import type {DartGenerator} from 'blockly/dart'; +import type {LuaGenerator} from 'blockly/lua'; +import type {PhpGenerator} from 'blockly/php'; +import type {PythonGenerator} from 'blockly/python'; + +/** + * An object containing zero or more generators. This is passed + * to block installation functions so that they may install + * per-block generators on any languages they support. + */ +export interface Generators { + javascript?: JavascriptGenerator; + dart?: DartGenerator; + lua?: LuaGenerator; + php?: PhpGenerator; + python?: PythonGenerator; +} diff --git a/packages/plugins/field-multilineinput/src/blocks/textMultiline.ts b/packages/plugins/field-multilineinput/src/blocks/textMultiline.ts new file mode 100644 index 00000000000..831c79ed10b --- /dev/null +++ b/packages/plugins/field-multilineinput/src/blocks/textMultiline.ts @@ -0,0 +1,169 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {Block, common as BlocklyCommon} from 'blockly/core'; +import { + JavascriptGenerator, + Order as JavascriptOrder, +} from 'blockly/javascript'; +import {DartGenerator, Order as DartOrder} from 'blockly/dart'; +import {LuaGenerator, Order as LuaOrder} from 'blockly/lua'; +import {PhpGenerator, Order as PhpOrder} from 'blockly/php'; +import {PythonGenerator, Order as PythonOrder} from 'blockly/python'; +import {registerFieldMultilineInput} from '../field_multilineinput'; +import {Generators} from './generatorsType'; + +/** The name this block is registered under. */ +export const BLOCK_NAME = 'text_multiline'; + +// Block for multiline text input. +const jsonDefinition = { + type: BLOCK_NAME, + message0: '%1 %2', + args0: [ + { + type: 'field_image', + src: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpP' + + 'U2iAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAdhgAAHYYBXaITgQAAABh0RVh0' + + 'U29mdHdhcmUAcGFpbnQubmV0IDQuMS42/U4J6AAAAP1JREFUOE+Vks0KQUEYhjm' + + 'RIja4ABtZ2dm5A3t3Ia6AUm7CylYuQRaUhZSlLZJiQbFAyRnPN33y01HOW08z88' + + '73zpwzM4F3GWOCruvGIE4/rLaV+Nq1hVGMBqzhqlxgCys4wJA65xnogMHsQ5luj' + + 'nYHTejBBCK2mE4abjCgMGhNxHgDFWjDSG07kdfVa2pZMf4ZyMAdWmpZMfYOsLiD' + + 'MYMjlMB+K613QISRhTnITnsYg5yUd0DETmEoMlkFOeIT/A58iyK5E18BuTBfgYX' + + 'fwNJv4P9/oEBerLylOnRhygmGdPpTTBZAPkde61lbQe4moWUvYUZYLfUNftIY4z' + + 'wA5X2Z9AYnQrEAAAAASUVORK5CYII=', + width: 12, + height: 17, + alt: '\u00B6', + }, + { + type: 'field_multilinetext', + name: 'TEXT', + text: '', + }, + ], + output: 'String', + style: 'text_blocks', + helpUrl: '%{BKY_TEXT_TEXT_HELPURL}', + tooltip: '%{BKY_TEXT_TEXT_TOOLTIP}', + extensions: ['parent_tooltip_when_inline'], +}; + +/** + * Javascript block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The JavascriptGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toJavascript( + block: Block, + generator: JavascriptGenerator, +): [string, JavascriptOrder] { + // Text value. + const code = generator.multiline_quote_(block.getFieldValue('TEXT')); + const order = + code.indexOf('+') !== -1 + ? JavascriptOrder.ADDITION + : JavascriptOrder.ATOMIC; + return [code, order]; +} + +/** + * Dart block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The DartGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toDart( + block: Block, + generator: DartGenerator, +): [string, DartOrder] { + // Text value. + const code = generator.multiline_quote_(block.getFieldValue('TEXT')); + const order = + code.indexOf('+') !== -1 ? DartOrder.ADDITIVE : DartOrder.ATOMIC; + return [code, order]; +} + +/** + * Lua block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The LuaGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toLua( + block: Block, + generator: LuaGenerator, +): [string, LuaOrder] { + // Text value. + const code = generator.multiline_quote_(block.getFieldValue('TEXT')); + const order = + code.indexOf('..') !== -1 ? LuaOrder.CONCATENATION : LuaOrder.ATOMIC; + return [code, order]; +} + +/** + * PHP block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The PhpGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPhp( + block: Block, + generator: PhpGenerator, +): [string, PhpOrder] { + // Text value. + const code = generator.multiline_quote_(block.getFieldValue('TEXT')); + const order = + code.indexOf('.') !== -1 ? PhpOrder.STRING_CONCAT : PhpOrder.ATOMIC; + return [code, order]; +} + +/** + * Python block generator function. + * + * @param block The Block instance to generate code for. + * @param generator The PythonGenerator calling the function. + * @returns A tuple containing the code string and precedence. + */ +export function toPython( + block: Block, + generator: PythonGenerator, +): [string, PythonOrder] { + // Text value. + const code = generator.multiline_quote_(block.getFieldValue('TEXT')); + const order = + code.indexOf('+') !== -1 ? PythonOrder.ADDITIVE : PythonOrder.ATOMIC; + return [code, order]; +} + +const definitionsDict = BlocklyCommon.createBlockDefinitionsFromJsonArray([ + jsonDefinition, +]); + +/** The text_multiline BlockDefinition. */ +export const blockDefinition = definitionsDict[BLOCK_NAME]; + +/** + * Install the `text_multiline` block and all of its dependencies. + * + * @param gens The CodeGenerators to install per-block + * generators on. + */ +export function installBlock(gens: Generators = {}) { + registerFieldMultilineInput(); + BlocklyCommon.defineBlocks(definitionsDict); + if (gens.javascript) gens.javascript.forBlock[BLOCK_NAME] = toJavascript; + if (gens.dart) gens.dart.forBlock[BLOCK_NAME] = toDart; + if (gens.lua) gens.lua.forBlock[BLOCK_NAME] = toLua; + if (gens.php) gens.php.forBlock[BLOCK_NAME] = toPhp; + if (gens.python) gens.python.forBlock[BLOCK_NAME] = toPython; +} diff --git a/packages/plugins/field-multilineinput/src/field_multilineinput.ts b/packages/plugins/field-multilineinput/src/field_multilineinput.ts new file mode 100644 index 00000000000..fd3f6083172 --- /dev/null +++ b/packages/plugins/field-multilineinput/src/field_multilineinput.ts @@ -0,0 +1,781 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Multiline text input field. + */ + +import * as Blockly from 'blockly/core'; + +/** + * Class for an editable text area input field. + */ +export class FieldMultilineInput extends Blockly.FieldTextInput { + /** + * Minimum editor width (in SVG/workspace units) when the field is open. + * Prevents narrow editors when the initial text is very short. + */ + + static readonly EDITOR_MIN_WIDTH = 150; + + /** + * The SVG group element that will contain a text element for each text row + * when initialized. + */ + textGroup: SVGGElement | null = null; + + /** + * Defines the maximum number of lines of field. + * If exceeded, scrolling functionality is enabled. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected maxLines_ = Infinity; + + /** Whether Y overflow is currently occurring. */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected isOverflowedY_ = false; + + /** Whether pressing Enter commits the value (default) or inserts a newline. */ + static enterCommits = true; + + /** Whether to show the keyboard-shortcut hint bar in the editor. */ + static showHint = true; + + /** The hint bar DOM element while the editor is open. */ + private hintElement: HTMLDivElement | null = null; + + /** + * Cached natural width of the hint bar in px, keyed by scale. + * Invalidated when zoom changes to avoid stale measurements. + */ + private cachedHintWidth: {scale: number; widthPx: number} | null = null; + + /** + * @param value The initial content of the field. Should cast to a string. + * Defaults to an empty string if null or undefined. Also accepts + * Field.SKIP_SETUP if you wish to skip setup (only used by subclasses + * that want to handle configuration and setting the field value after + * their own constructors have run). + * @param validator An optional function that is called to validate any + * constraints on what the user entered. Takes the new text as an + * argument and returns either the accepted text, a replacement text, or + * null to abort the change. + * @param config A map of options used to configure the field. + * See the [field creation documentation]{@link + * https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/multiline-text-input#creation} + * for a list of properties this parameter supports. + */ + constructor( + value?: string | typeof Blockly.Field.SKIP_SETUP, + validator?: FieldMultilineInputValidator, + config?: FieldMultilineInputConfig, + ) { + super(Blockly.Field.SKIP_SETUP); + + if (value === Blockly.Field.SKIP_SETUP) return; + if (config) { + this.configure_(config); + } + this.setValue(value); + if (validator) { + this.setValidator(validator); + } + } + + /** + * Configure the field based on the given map of options. + * + * @param config A map of options to configure the field based on. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override configure_(config: FieldMultilineInputConfig) { + super.configure_(config); + if (config.maxLines) this.setMaxLines(config.maxLines); + } + + /** + * Serializes this field's value to XML. + * Should only be called by Blockly.Xml. + * + * @param fieldElement The element to populate with info about the field's + * state. + * @returns The element containing info about the field's state. + */ + override toXml(fieldElement: Element): Element { + // Replace '\n' characters with HTML-escaped equivalent ' '. This is + // needed so the plain-text representation of the XML produced by + // `Blockly.Xml.domToText` will appear on a single line (this is a + // limitation of the plain-text format). + fieldElement.textContent = (this.getValue() as string).replace( + /\n/g, + ' ', + ); + return fieldElement; + } + + /** + * Sets the field's value based on the given XML element. Should only be + * called by Blockly.Xml. + * + * @param fieldElement The element containing info about the field's state. + */ + override fromXml(fieldElement: Element) { + this.setValue((fieldElement.textContent as string).replace(/ /g, '\n')); + } + + /** + * Saves this field's value. + * This function only exists for subclasses of FieldMultilineInput which + * predate the load/saveState API and only define to/fromXml. + * + * @returns The state of this field. + */ + override saveState() { + const legacyState = this.saveLegacyState(FieldMultilineInput); + if (legacyState !== null) { + return legacyState; + } + return this.getValue(); + } + + /** + * Sets the field's value based on the given state. + * This function only exists for subclasses of FieldMultilineInput which + * predate the load/saveState API and only define to/fromXml. + * + * @param state The state of the variable to assign to this variable field. + */ + override loadState(state: unknown) { + if (this.loadLegacyState(Blockly.Field, state)) { + return; + } + this.setValue(state); + } + + /** + * Create the block UI for this field. + */ + override initView() { + this.createBorderRect_(); + this.textGroup = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.G, + { + class: 'blocklyEditableField', + }, + this.fieldGroup_, + ); + if (this.fieldGroup_) { + Blockly.utils.dom.addClass(this.fieldGroup_, 'blocklyField'); + } + } + + /** + * Get the text from this field as displayed on screen. May differ from + * getText due to ellipsis, and other formatting. + * + * @returns Currently displayed text. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override getDisplayText_(): string { + const block = this.getSourceBlock(); + if (!block) { + throw new Error( + 'The field has not yet been attached to its input. ' + + 'Call appendField to attach it.', + ); + } + let textLines = this.getText(); + if (!textLines) { + // Prevent the field from disappearing if empty. + return Blockly.Field.NBSP; + } + const lines = textLines.split('\n'); + textLines = ''; + const displayLinesNumber = this.isOverflowedY_ + ? this.maxLines_ + : lines.length; + for (let i = 0; i < displayLinesNumber; i++) { + let text = lines[i]; + if (text.length > this.maxDisplayLength) { + // Truncate displayed string and add an ellipsis ('...'). + text = text.substring(0, this.maxDisplayLength - 4) + '...'; + } else if (this.isOverflowedY_ && i === displayLinesNumber - 1) { + text = text.substring(0, text.length - 3) + '...'; + } + // Replace whitespace with non-breaking spaces so the text doesn't + // collapse. + text = text.replace(/\s/g, Blockly.Field.NBSP); + + textLines += text; + if (i !== displayLinesNumber - 1) { + textLines += '\n'; + } + } + if (block.RTL) { + // The SVG is LTR, force value to be RTL. + textLines += '\u200F'; + } + return textLines; + } + + /** + * Called by setValue if the text input is valid. Updates the value of the + * field, and updates the text of the field if it is not currently being + * edited (i.e. handled by the htmlInput_). Is being redefined here to update + * overflow state of the field. + * + * @param newValue The value to be saved. The default validator guarantees + * that this is a string. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override doValueUpdate_(newValue: string) { + super.doValueUpdate_(newValue); + if (this.value_ !== null) { + this.isOverflowedY_ = this.value_.split('\n').length > this.maxLines_; + } + } + + /** Updates the text of the textElement. */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override render_() { + const block = this.getSourceBlock(); + if (!block) { + throw new Error( + 'The field has not yet been attached to its input. ' + + 'Call appendField to attach it.', + ); + } + // Remove all text group children. + let currentChild; + const textGroup = this.textGroup as SVGElement; + while ((currentChild = textGroup.firstChild)) { + textGroup.removeChild(currentChild); + } + + const constants = this.getConstants(); + // This can't happen, but TypeScript thinks it can and lint forbids `!.`. + if (!constants) throw Error('Constants not found'); + // Add in text elements into the group. + const lines = this.getDisplayText_().split('\n'); + let y = 0; + for (let i = 0; i < lines.length; i++) { + const lineHeight = + constants.FIELD_TEXT_HEIGHT + constants.FIELD_BORDER_RECT_Y_PADDING; + const span = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.TEXT, + { + class: 'blocklyText blocklyMultilineText', + x: constants.FIELD_BORDER_RECT_X_PADDING, + y: y + constants.FIELD_BORDER_RECT_Y_PADDING, + dy: constants.FIELD_TEXT_BASELINE, + }, + textGroup, + ); + span.appendChild(document.createTextNode(lines[i])); + y += lineHeight; + } + + if (this.isBeingEdited_) { + const htmlInput = this.htmlInput_ as HTMLElement; + if (this.isOverflowedY_) { + Blockly.utils.dom.addClass( + htmlInput, + 'blocklyHtmlTextAreaInputOverflowedY', + ); + } else { + Blockly.utils.dom.removeClass( + htmlInput, + 'blocklyHtmlTextAreaInputOverflowedY', + ); + } + } + + this.updateSize_(); + + if (this.isBeingEdited_) { + if (block.RTL) { + // in RTL, we need to let the browser reflow before resizing + // in order to get the correct bounding box of the borderRect + // avoiding issue #2777. + setTimeout(this.resizeEditor_.bind(this), 0); + } else { + this.resizeEditor_(); + } + const htmlInput = this.htmlInput_ as HTMLElement; + if (!this.isTextValid_) { + Blockly.utils.dom.addClass(htmlInput, 'blocklyInvalidInput'); + Blockly.utils.aria.setState( + htmlInput, + Blockly.utils.aria.State.INVALID, + true, + ); + } else { + Blockly.utils.dom.removeClass(htmlInput, 'blocklyInvalidInput'); + Blockly.utils.aria.setState( + htmlInput, + Blockly.utils.aria.State.INVALID, + false, + ); + } + } + } + + /** Updates the size of the field based on the text. */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override updateSize_() { + const constants = this.getConstants(); + // This can't happen, but TypeScript thinks it can and lint forbids `!.`. + if (!constants) throw Error('Constants not found'); + const nodes = (this.textGroup as SVGElement).childNodes; + let totalWidth = 0; + let totalHeight = 0; + for (let i = 0; i < nodes.length; i++) { + const tspan = nodes[i] as SVGTextElement; + const textWidth = Blockly.utils.dom.getTextWidth(tspan); + if (textWidth > totalWidth) { + totalWidth = textWidth; + } + totalHeight += + constants.FIELD_TEXT_HEIGHT + + (i > 0 ? constants.FIELD_BORDER_RECT_Y_PADDING : 0); + } + if (this.isBeingEdited_) { + // The default width is based on the longest line in the display text, + // but when it's being edited, width should be calculated based on the + // absolute longest line, even if it would be truncated after editing. + // Otherwise we would get wrong editor width when there are more + // lines than this.maxLines_. + const actualEditorLines = String(this.value_).split('\n'); + const dummyTextElement = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.TEXT, + {class: 'blocklyText blocklyMultilineText'}, + ); + + for (let i = 0; i < actualEditorLines.length; i++) { + if (actualEditorLines[i].length > this.maxDisplayLength) { + actualEditorLines[i] = actualEditorLines[i].substring( + 0, + this.maxDisplayLength, + ); + } + dummyTextElement.textContent = actualEditorLines[i]; + const lineWidth = Blockly.utils.dom.getTextWidth(dummyTextElement); + if (lineWidth > totalWidth) { + totalWidth = lineWidth; + } + } + + const htmlInput = this.htmlInput_ as HTMLElement; + const scrollbarWidth = htmlInput.offsetWidth - htmlInput.clientWidth; + totalWidth += scrollbarWidth; + + if (FieldMultilineInput.showHint) { + // Reserve a row at the bottom of the editor for the keyboard hint bar. + // The textarea's padding-bottom (set in widgetCreate_) keeps the text + // and caret out of this reserved strip. + totalHeight += + constants.FIELD_TEXT_HEIGHT + constants.FIELD_BORDER_RECT_Y_PADDING; + } + } + if (this.borderRect_) { + totalHeight += constants.FIELD_BORDER_RECT_Y_PADDING * 2; + // NOTE: Adding 1 extra px to prevent wrapping. Based on browser zoom, + // the rounding of the calculated value can result in the line wrapping + // unintentionally. + totalWidth += constants.FIELD_BORDER_RECT_X_PADDING * 2 + 1; + } + if (this.isBeingEdited_) { + totalWidth = Math.max(totalWidth, FieldMultilineInput.EDITOR_MIN_WIDTH); + + // Measure the hint bar's natural width to make sure the editor is wide enough. + if (FieldMultilineInput.showHint && this.hintElement?.isConnected) { + const scale = (this.workspace_ as Blockly.WorkspaceSvg).getScale(); + if (!this.cachedHintWidth || this.cachedHintWidth.scale !== scale) { + // Temporarily let the element size to its content to measure its + // natural width, unaffected by the current WidgetDiv width. + this.hintElement.style.width = 'max-content'; + const widthPx = this.hintElement.offsetWidth; + this.hintElement.style.width = ''; + if (widthPx > 0) this.cachedHintWidth = {scale, widthPx}; + } + if (this.cachedHintWidth) { + totalWidth = Math.max( + totalWidth, + this.cachedHintWidth.widthPx / scale, + ); + } + } + } + if (this.borderRect_) { + this.borderRect_.setAttribute('width', `${totalWidth}`); + this.borderRect_.setAttribute('height', `${totalHeight}`); + } + this.size_.width = totalWidth; + this.size_.height = totalHeight; + + this.positionBorderRect_(); + } + + /** + * Show the inline free-text editor on top of the text. + * Overrides the default behaviour to force rerender in order to + * correct block size, based on editor text. + * + * @param e Optional mouse event that triggered the field to open, or + * undefined if triggered programmatically. + * @param quietInput True if editor should be created without focus. + * Defaults to false. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + override showEditor_(e?: Event, quietInput?: boolean) { + super.showEditor_(e, quietInput); + this.forceRerender(); + } + + /** + * Create the text input editor widget. + * + * @returns The newly created text input editor. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override widgetCreate_(): HTMLTextAreaElement { + const div = Blockly.WidgetDiv.getDiv() as HTMLDivElement; + const scale = (this.workspace_ as Blockly.WorkspaceSvg).getScale(); + const constants = this.getConstants(); + // This can't happen, but TypeScript thinks it can and lint forbids `!.`. + if (!constants) throw Error('Constants not found'); + + const htmlInput = document.createElement('textarea'); + htmlInput.className = 'blocklyHtmlInput blocklyHtmlTextAreaInput'; + htmlInput.setAttribute('spellcheck', String(this.spellcheck_)); + const fontSize = constants.FIELD_TEXT_FONTSIZE * scale + 'pt'; + div.style.fontSize = fontSize; + htmlInput.style.fontSize = fontSize; + const borderRadius = Blockly.FieldTextInput.BORDERRADIUS * scale + 'px'; + htmlInput.style.borderRadius = borderRadius; + const paddingX = constants.FIELD_BORDER_RECT_X_PADDING * scale; + const paddingY = (constants.FIELD_BORDER_RECT_Y_PADDING * scale) / 2; + const lineHeight = + constants.FIELD_TEXT_HEIGHT + constants.FIELD_BORDER_RECT_Y_PADDING; + const hintHeightPx = Math.ceil(lineHeight * scale); + // When the hint bar is visible it occupies one text row at the bottom; + // pad the textarea by that row so we don't edit text underneath it. + const paddingBottomPx = FieldMultilineInput.showHint + ? paddingY + hintHeightPx + : paddingY; + htmlInput.style.padding = `${paddingY}px ${paddingX}px ${paddingBottomPx}px ${paddingX}px`; + htmlInput.style.lineHeight = lineHeight * scale + 'px'; + div.appendChild(htmlInput); + + htmlInput.value = htmlInput.defaultValue = this.getEditorText_(this.value_); + htmlInput.setAttribute('data-untyped-default-value', String(this.value_)); + htmlInput.setAttribute('data-old-value', ''); + if (Blockly.utils.userAgent.GECKO) { + // In FF, ensure the browser reflows before resizing to avoid issue #2777. + setTimeout(this.resizeEditor_.bind(this), 0); + } else { + this.resizeEditor_(); + } + + this.bindInputEvents_(htmlInput); + + if (FieldMultilineInput.showHint) { + this.hintElement = this.createHint(hintHeightPx); + this.hintElement.style.fontFamily = constants.FIELD_TEXT_FONTFAMILY; + div.appendChild(this.hintElement); + } + + return htmlInput; + } + + /** + * Creates the keyboard hint bar shown at the bottom of the editor. + * + * Keys are rendered as keycaps (bordered boxes). + * + * @param heightPx The height of the hint bar in pixels. + * @returns The hint bar element. + */ + private createHint(heightPx: number): HTMLDivElement { + const shiftKey = '⇧'; + const enterKey = '⏎'; + const hint = document.createElement('div'); + hint.className = 'blocklyMultilineHint'; + hint.setAttribute('aria-hidden', 'true'); + hint.style.height = heightPx + 'px'; + + // Plain Enter + const enterGroup = document.createElement('div'); + enterGroup.className = 'blocklyMultilineHintGroup'; + enterGroup.appendChild(this.createHintKeycap(enterKey)); + enterGroup.appendChild(this.createHintColon()); + enterGroup.appendChild( + this.createHintLabel( + FieldMultilineInput.enterCommits ? 'commit' : 'newline', + ), + ); + hint.appendChild(enterGroup); + + // Modifier+Enter + const modEnterGroup = document.createElement('div'); + modEnterGroup.className = 'blocklyMultilineHintGroup'; + modEnterGroup.appendChild(this.createHintKeycap(shiftKey)); + modEnterGroup.appendChild(this.createHintKeycap(enterKey)); + modEnterGroup.appendChild(this.createHintColon()); + modEnterGroup.appendChild( + this.createHintLabel( + FieldMultilineInput.enterCommits ? 'newline' : 'commit', + ), + ); + hint.appendChild(modEnterGroup); + + return hint; + } + + /** + * Creates a colon separator element used between keycaps and the action icon. + * + * @returns The colon element. + */ + private createHintColon(): HTMLSpanElement { + const colon = document.createElement('span'); + colon.className = 'blocklyMultilineHintColon'; + colon.textContent = ':'; + return colon; + } + + /** + * Creates a keycap element displaying a single key label. + * + * @param label The key symbol to display. + * @returns The keycap element. + */ + private createHintKeycap(label: string): HTMLSpanElement { + const key = document.createElement('span'); + key.className = 'blocklyMultilineHintKey'; + key.textContent = label; + return key; + } + + /** + * Creates a text label describing the result of a key action. + * + * Uses Blockly.Msg for i18n with hardcoded English fallbacks. + * + * @param type Either 'commit' or 'newline'. + * @returns The label element. + */ + private createHintLabel(type: 'commit' | 'newline'): HTMLSpanElement { + const label = document.createElement('span'); + label.className = 'blocklyMultilineHintLabel'; + label.textContent = + type === 'commit' + ? Blockly.Msg['FIELD_MULTILINEINPUT_FINISH_EDITING'] || 'Finish editing' + : Blockly.Msg['FIELD_MULTILINEINPUT_NEW_LINE'] || 'New line'; + return label; + } + + /** + * Sets the maxLines config for this field. + * + * @param maxLines Defines the maximum number of lines allowed, before + * scrolling functionality is enabled. + */ + setMaxLines(maxLines: number) { + if ( + typeof maxLines === 'number' && + maxLines > 0 && + maxLines !== this.maxLines_ + ) { + this.maxLines_ = maxLines; + this.forceRerender(); + } + } + + /** + * Returns the maxLines config of this field. + * + * @returns The maxLines config value. + */ + getMaxLines(): number { + return this.maxLines_; + } + + /** + * Handle key down to the editor. + * + * Enter commits the value and closes the editor (matching FieldTextInput). + * Shift+Enter inserts a newline at the cursor. + * All other keys are handled by the parent class (Escape reverts, Tab navigates). + * + * @param e Keyboard event. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected override onHtmlInputKeyDown_(e: KeyboardEvent) { + if (e.key === 'Enter') { + if (e.isComposing) { + // Let the IME (input method editor) finalize its composition naturally; + // don't commit or insert a newline ourselves. + return; + } + const shiftPressed = e.shiftKey; + const shouldCommit = FieldMultilineInput.enterCommits + ? !shiftPressed + : shiftPressed; + if (shouldCommit) { + super.onHtmlInputKeyDown_(e); + } else { + this.insertNewline(); + e.preventDefault(); + e.stopPropagation(); + } + } else { + super.onHtmlInputKeyDown_(e); + } + } + + /** + * Inserts a newline character at the current cursor position in the textarea. + * + * The browser's default Enter-key behavior inserts a newline, but + * Shift+Enter has no default character-insertion action, so we handle it + * manually here. After splicing the character we dispatch an `input` event + * to trigger Blockly's onHtmlInputChange handler (programmatic `.value` + * assignment doesn't fire `input` on its own). + */ + private insertNewline() { + const htmlInput = this.htmlInput_; + if (!htmlInput) return; + const start = htmlInput.selectionStart ?? htmlInput.value.length; + const end = htmlInput.selectionEnd ?? start; + htmlInput.value = + htmlInput.value.substring(0, start) + + '\n' + + htmlInput.value.substring(end); + htmlInput.selectionStart = htmlInput.selectionEnd = start + 1; + htmlInput.dispatchEvent(new Event('input')); + } + + /** + * Construct a FieldMultilineInput from a JSON arg object, + * dereferencing any string table references. + * + * @param options A JSON object with options (text, and spellcheck). + * @returns The new field instance. + * @nocollapse + */ + static override fromJson( + options: FieldMultilineInputFromJsonConfig, + ): FieldMultilineInput { + const text = Blockly.utils.parsing.replaceMessageReferences(options.text); + // `this` might be a subclass of FieldMultilineInput if that class doesn't + // override the static fromJson method. + return new this(text, undefined, options); + } +} + +/** + * Register the field and any dependencies. + */ +export function registerFieldMultilineInput() { + Blockly.fieldRegistry.register('field_multilinetext', FieldMultilineInput); +} + +/** + * CSS for multiline field. + */ +Blockly.Css.register(` +.blocklyHtmlTextAreaInput { + font-family: monospace; + resize: none; + overflow: hidden; + height: 100%; + text-align: left; +} + +.blocklyHtmlTextAreaInputOverflowedY { + overflow-y: scroll; +} + +.blocklyMultilineHint { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + display: flex; + justify-content: space-around; + align-items: center; + padding: 0 4px; + box-sizing: border-box; + font-size: 0.85em; + color: rgba(0, 0, 0, 0.6); + background-color: white; + border-top: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0 0 4px 4px; + user-select: none; + pointer-events: none; +} + +.blocklyMultilineHintGroup { + display: inline-flex; + align-items: center; +} + +.blocklyMultilineHintKey { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 1.3em; + height: 1.3em; + padding: 0 0.25em; + margin: 0 0.1em; + border: 1px solid rgba(0, 0, 0, 0.35); + border-radius: 3px; + background-color: #f5f5f5; + line-height: 1; + box-sizing: border-box; +} + +.blocklyMultilineHintColon { + margin: 0 0.15em; +} + +.blocklyMultilineHintLabel { + white-space: nowrap; +} +`); + +/** + * Config options for the multiline input field. + */ +export interface FieldMultilineInputConfig + extends Blockly.FieldTextInputConfig { + maxLines?: number; +} + +/** + * fromJson config options for the multiline input field. + */ +export interface FieldMultilineInputFromJsonConfig extends FieldMultilineInputConfig { + text?: string; +} + +/** + * A function that is called to validate changes to the field's value before + * they are set. + * + * @see {@link https://developers.google.com/blockly/guides/create-custom-blocks/fields/validators#return_values} + * @param newValue The value to be validated. + * @returns One of three instructions for setting the new value: `T`, `null`, + * or `undefined`. + * + * - `T` to set this function's returned value instead of `newValue`. + * + * - `null` to invoke `doValueInvalid_` and not set a value. + * + * - `undefined` to set `newValue` as is. + */ +export type FieldMultilineInputValidator = Blockly.FieldTextInputValidator; diff --git a/packages/plugins/field-multilineinput/src/index.ts b/packages/plugins/field-multilineinput/src/index.ts new file mode 100644 index 00000000000..051fded12e4 --- /dev/null +++ b/packages/plugins/field-multilineinput/src/index.ts @@ -0,0 +1,16 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as textMultiline from './blocks/textMultiline'; + +export * from './field_multilineinput'; + +// Re-export all parts of the block definition. +export * as textMultiline from './blocks/textMultiline'; + +// This package currently exports a single block. More may +// be added later. +export const installAllBlocks = textMultiline.installBlock; diff --git a/packages/plugins/field-multilineinput/test/block_test.mocha.js b/packages/plugins/field-multilineinput/test/block_test.mocha.js new file mode 100644 index 00000000000..d60fd2cf156 --- /dev/null +++ b/packages/plugins/field-multilineinput/test/block_test.mocha.js @@ -0,0 +1,94 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import fs from 'fs'; +import * as Blockly from 'blockly/core'; +import * as en from 'blockly/msg/en'; +import 'blockly/blocks'; + +import {javascriptGenerator} from 'blockly/javascript'; +import {dartGenerator} from 'blockly/dart'; +import {phpGenerator} from 'blockly/php'; +import {pythonGenerator} from 'blockly/python'; +import {luaGenerator} from 'blockly/lua'; +import {installAllBlocks} from '../src/index'; +import {assert} from 'chai'; + +const blockJson = { + blocks: { + languageVersion: 0, + blocks: [ + { + type: 'text_print', + inputs: { + TEXT: { + block: { + type: 'text_multiline', + fields: { + TEXT: 'Picard said, "Beam me up!".\nO\'Brien made it so.', + }, + }, + }, + }, + }, + ], + }, +}; + +/** + * Assert that the generated code matches the golden code for the specified + * language. + * @param {string} suffix The suffix of the golden file. + * @param {string} generated The generated code to compare against the + * golden file. + */ +function checkResult(suffix, generated) { + const fileName = `test/golden/golden.${suffix}`; + const goldenContents = fs.readFileSync(fileName); + // Normalize the line feeds. + const normalized = goldenContents.toString().replace(/(?:\r\n|\r|\n)/g, '\n'); + assert.equal(generated, normalized); +} + +suite('Multiline Text Block Generators', function () { + suiteSetup(function () { + Blockly.setLocale(en); + installAllBlocks({ + javascript: javascriptGenerator, + dart: dartGenerator, + lua: luaGenerator, + python: pythonGenerator, + php: phpGenerator, + }); + }); + setup(function () { + this.workspace = new Blockly.Workspace(); + Blockly.serialization.workspaces.load(blockJson, this.workspace); + }); + test('JavaScript', function () { + const generated = javascriptGenerator.workspaceToCode(this.workspace); + checkResult('js', generated); + }); + test('Dart', function () { + const generated = dartGenerator.workspaceToCode(this.workspace); + checkResult('dart', generated); + }); + test('Lua', function () { + const generated = luaGenerator.workspaceToCode(this.workspace); + checkResult('lua', generated); + }); + test('Python', function () { + const generated = pythonGenerator.workspaceToCode(this.workspace); + checkResult('py', generated); + }); + test('PHP', function () { + const generated = phpGenerator.workspaceToCode(this.workspace); + checkResult('php', generated); + }); + teardown(function () { + this.workspace.dispose(); + }); +}); diff --git a/packages/plugins/field-multilineinput/test/field_multilineinput_test.mocha.js b/packages/plugins/field-multilineinput/test/field_multilineinput_test.mocha.js new file mode 100644 index 00000000000..ce46e94d1a2 --- /dev/null +++ b/packages/plugins/field-multilineinput/test/field_multilineinput_test.mocha.js @@ -0,0 +1,300 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +const {testHelpers} = require('@blockly/dev-tools'); +const { + FieldMultilineInput, + registerFieldMultilineInput, +} = require('../src/index'); +const Blockly = require('blockly'); +const {assert} = require('chai'); +const sinon = require('sinon'); + +const { + assertFieldValue, + FieldCreationTestCase, + FieldValueTestCase, + runConstructorSuiteTests, + runFromJsonSuiteTests, + runSetValueTests, +} = testHelpers; + +suite('FieldMultilineInput', function () { + setup(function () { + registerFieldMultilineInput(); + }); + /** + * Configuration for field tests with invalid values. + * @type {Array} + */ + const invalidValueTestCases = [ + {title: 'Undefined', value: undefined}, + {title: 'Null', value: null}, + ]; + /** + * Configuration for field tests with valid values. + * @type {Array} + */ + const validValueTestCases = [ + {title: 'Empty string', value: '', expectedValue: ''}, + {title: 'String no newline', value: 'value', expectedValue: 'value'}, + { + title: 'String with newline', + value: 'bark bark\n bark bark bark\n bark bar bark bark\n', + expectedValue: 'bark bark\n bark bark bark\n bark bar bark bark\n', + }, + {title: 'Boolean true', value: true, expectedValue: 'true'}, + {title: 'Boolean false', value: false, expectedValue: 'false'}, + {title: 'Number (Truthy)', value: 1, expectedValue: '1'}, + {title: 'Number (Falsy)', value: 0, expectedValue: '0'}, + {title: 'NaN', value: NaN, expectedValue: 'NaN'}, + ]; + const addArgsAndJson = function (testCase) { + testCase.args = [testCase.value]; + testCase.json = {text: testCase.value}; + }; + invalidValueTestCases.forEach(addArgsAndJson); + validValueTestCases.forEach(addArgsAndJson); + + /** + * The expected default value for the field being tested. + * @type {*} + */ + const defaultFieldValue = ''; + /** + * Asserts that the field property values are set to default. + * @param {!FieldMultilineInput} field The field to check. + */ + const assertFieldDefault = function (field) { + assertFieldValue(field, defaultFieldValue); + }; + + /** + * Asserts that the field properties are correct based on the test case. + * @param {FieldMultilineInput} field The field to check. + * @param {FieldValueTestCase} testCase The test case. + */ + const validTestCaseAssertField = function (field, testCase) { + assertFieldValue(field, testCase.expectedValue); + }; + + runConstructorSuiteTests( + FieldMultilineInput, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + runFromJsonSuiteTests( + FieldMultilineInput, + validValueTestCases, + invalidValueTestCases, + validTestCaseAssertField, + assertFieldDefault, + ); + + suite('setValue', function () { + suite('Empty -> New Value', function () { + setup(function () { + this.field = new FieldMultilineInput(); + }); + runSetValueTests( + validValueTestCases, + invalidValueTestCases, + defaultFieldValue, + ); + }); + suite('Value -> New Value', function () { + const initialValue = 'oldValue'; + setup(function () { + this.field = new FieldMultilineInput(initialValue); + }); + runSetValueTests( + validValueTestCases, + invalidValueTestCases, + initialValue, + ); + }); + }); + + suite('Keyboard behavior', function () { + /** + * Dispatches a keydown event on the editor's textarea, setting the + * cursor/selection beforehand. + * @param {!HTMLTextAreaElement} textarea The editor's textarea. + * @param {!Object} options KeyboardEvent options (key, shiftKey, etc.). + * @param {number} [selectionStart] Optional selection start to set first. + * @param {number} [selectionEnd] Optional selection end to set first. + */ + const pressKey = function ( + textarea, + options, + selectionStart, + selectionEnd, + ) { + if (selectionStart !== undefined) { + textarea.selectionStart = selectionStart; + textarea.selectionEnd = selectionEnd ?? selectionStart; + } + textarea.dispatchEvent( + new KeyboardEvent('keydown', { + bubbles: true, + cancelable: true, + ...options, + }), + ); + }; + + setup(function () { + this.jsdomCleanup = require('jsdom-global')( + '
', + ); + // See https://github.com/RaspberryPiFoundation/blockly-samples/issues/2528. + global.SVGElement = window.SVGElement; + // Blockly's focus handling constructs FocusEvent, which jsdom exposes on + // window but not as a global. + global.FocusEvent = window.FocusEvent; + // jsdom doesn't provide requestAnimationFrame/cancelAnimationFrame, which + // block rendering relies on. Route them through (faked) timers so they + // exist and can be flushed deterministically in teardown. + this.clock = sinon.useFakeTimers(); + window.requestAnimationFrame = (cb) => setTimeout(cb, 0); + window.cancelAnimationFrame = (id) => clearTimeout(id); + this.workspace = Blockly.inject('blocklyDiv'); + + if (!Blockly.Blocks['multiline_block']) { + Blockly.defineBlocksWithJsonArray([ + { + type: 'multiline_block', + message0: '%1', + args0: [ + {type: 'field_multilinetext', name: 'FIELD', text: 'hello'}, + ], + }, + ]); + } + this.block = this.workspace.newBlock('multiline_block'); + this.block.initSvg(); + this.block.render(); + this.field = this.block.getField('FIELD'); + + // Open the editor so we exercise the real