From aaa6d7df7da4952739ee7c612ddc6b86a22f9db9 Mon Sep 17 00:00:00 2001 From: Yannick Schaus Date: Sat, 20 Nov 2021 18:34:48 +0100 Subject: [PATCH] Add a Blockly block to define key/value dictionaries Signed-off-by: Yannick Schaus --- .../definitions/blockly/blocks-dicts.js | 174 ++++++++++++++++++ .../src/assets/definitions/blockly/index.js | 2 + .../settings/rules/script/blockly-editor.vue | 2 + 3 files changed, 178 insertions(+) create mode 100644 bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-dicts.js diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-dicts.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-dicts.js new file mode 100644 index 0000000000..4029869c91 --- /dev/null +++ b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/blocks-dicts.js @@ -0,0 +1,174 @@ +import Blockly from 'blockly' + +export default function (f7) { + Blockly.Blocks['dicts_create_with'] = { + /** + * Block for creating a list with any number of elements of any type. + * @this {Blockly.Block} + */ + init: function () { + this.setStyle('list_blocks') + this.itemCount_ = 3 + this.updateShape_() + this.setOutput(true, 'Dictionary') + this.setMutator(new Blockly.Mutator(['dicts_create_with_item'])) + this.setTooltip('Create a key/value dictionary') + }, + /** + * Create XML to represent list inputs. + * @return {!Element} XML storage element. + * @this {Blockly.Block} + */ + mutationToDom: function () { + let container = Blockly.utils.xml.createElement('mutation') + container.setAttribute('items', this.itemCount_) + return container + }, + /** + * Parse XML to restore the list inputs. + * @param {!Element} xmlElement XML storage element. + * @this {Blockly.Block} + */ + domToMutation: function (xmlElement) { + this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10) + this.updateShape_() + }, + /** + * Populate the mutator's dialog with this block's components. + * @param {!Blockly.Workspace} workspace Mutator's workspace. + * @return {!Blockly.Block} Root block in mutator. + * @this {Blockly.Block} + */ + decompose: function (workspace) { + let containerBlock = workspace.newBlock('dicts_create_with_container') + containerBlock.initSvg() + let connection = containerBlock.getInput('STACK').connection + for (let i = 0; i < this.itemCount_; i++) { + let itemBlock = workspace.newBlock('dicts_create_with_item') + itemBlock.initSvg() + connection.connect(itemBlock.previousConnection) + connection = itemBlock.nextConnection + } + return containerBlock + }, + /** + * Reconfigure this block based on the mutator dialog's components. + * @param {!Blockly.Block} containerBlock Root block in mutator. + * @this {Blockly.Block} + */ + compose: function (containerBlock) { + let itemBlock = containerBlock.getInputTargetBlock('STACK') + // Count number of inputs. + let connections = [] + while (itemBlock && !itemBlock.isInsertionMarker()) { + connections.push(itemBlock.valueConnection_) + itemBlock = itemBlock.nextConnection && + itemBlock.nextConnection.targetBlock() + } + // Disconnect any children that don't belong. + for (let i = 0; i < this.itemCount_; i++) { + let input = this.getInput('ADD' + i) + if (!input) continue + let connection = input.connection.targetConnection + if (connection && connections.indexOf(connection) === -1) { + connection.disconnect() + } + } + this.itemCount_ = connections.length + this.updateShape_() + // Reconnect any child blocks. + for (let i = 0; i < this.itemCount_; i++) { + Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i) + } + }, + /** + * Store pointers to any connected child blocks. + * @param {!Blockly.Block} containerBlock Root block in mutator. + * @this {Blockly.Block} + */ + saveConnections: function (containerBlock) { + let itemBlock = containerBlock.getInputTargetBlock('STACK') + let i = 0 + while (itemBlock) { + let input = this.getInput('ADD' + i) + itemBlock.valueConnection_ = input && input.connection.targetConnection + i++ + itemBlock = itemBlock.nextConnection && + itemBlock.nextConnection.targetBlock() + } + }, + /** + * Modify this block to have the correct number of inputs. + * @private + * @this {Blockly.Block} + */ + updateShape_: function () { + if (this.itemCount_ && this.getInput('EMPTY')) { + this.removeInput('EMPTY') + } else if (!this.itemCount_ && !this.getInput('EMPTY')) { + this.appendDummyInput('EMPTY') + .appendField('create empty dictionary') + } + // Add new inputs. + let i + for (i = 0; i < this.itemCount_; i++) { + if (!this.getInput('ADD' + i)) { + let input = this.appendValueInput('ADD' + i) + .setAlign(Blockly.ALIGN_RIGHT) + if (i === 0) { + input.appendField('dictionary of') + } + input.appendField(new Blockly.FieldTextInput('key' + i), 'KEY' + i) + } + } + // Remove deleted inputs. + while (this.getInput('ADD' + i)) { + this.removeInput('ADD' + i) + i++ + } + } + } + + Blockly.Blocks['dicts_create_with_container'] = { + /** + * Mutator block for list container. + * @this {Blockly.Block} + */ + init: function () { + this.setStyle('list_blocks') + this.appendDummyInput() + .appendField('key/values') + this.appendStatementInput('STACK') + this.setTooltip('Initialize a Dictionary') + this.contextMenu = false + } + } + + Blockly.Blocks['dicts_create_with_item'] = { + /** + * Mutator block for adding items. + * @this {Blockly.Block} + */ + init: function () { + this.setStyle('list_blocks') + this.appendDummyInput() + .appendField('key') + this.setPreviousStatement(true) + this.setNextStatement(true) + this.setTooltip('add a key/value to the dictionary') + this.contextMenu = false + } + } + + Blockly.JavaScript['dicts_create_with'] = function (block) { + // Create an object with any number of elements of any type. + let elements = new Array(block.itemCount_) + for (let i = 0; i < block.itemCount_; i++) { + elements[i] = '\'' + block.getFieldValue('KEY' + i) + '\': ' + elements[i] += Blockly.JavaScript.valueToCode(block, 'ADD' + i, + Blockly.JavaScript.ORDER_NONE) || 'null' + } + let code = '{' + elements.join(', ') + '}' + return [code, Blockly.JavaScript.ORDER_ATOMIC] + } +} diff --git a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/index.js b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/index.js index 87b3af5fe5..6e1b698cd2 100644 --- a/bundles/org.openhab.ui/web/src/assets/definitions/blockly/index.js +++ b/bundles/org.openhab.ui/web/src/assets/definitions/blockly/index.js @@ -1,3 +1,4 @@ +import defineDictionaryBlocks from './blocks-dicts' import defineItemBlocks from './blocks-items' import defineThingsBlocks from './blocks-things' import defineAudioBlocks from './blocks-audio' @@ -8,6 +9,7 @@ import defineTimerBlocks from './blocks-timers' import defineValueStorageBlocks from './blocks-valuestorage' export default function (f7, data) { + defineDictionaryBlocks(f7) defineItemBlocks(f7) defineThingsBlocks(f7) defineAudioBlocks(f7, data.sinks, data.voices) diff --git a/bundles/org.openhab.ui/web/src/pages/settings/rules/script/blockly-editor.vue b/bundles/org.openhab.ui/web/src/pages/settings/rules/script/blockly-editor.vue index 3d07c7135e..29b7b6effc 100644 --- a/bundles/org.openhab.ui/web/src/pages/settings/rules/script/blockly-editor.vue +++ b/bundles/org.openhab.ui/web/src/pages/settings/rules/script/blockly-editor.vue @@ -261,6 +261,8 @@ + +