diff --git a/locales/en-us/blocks.json b/locales/en-us/blocks.json index d8530c44..ca2b5f8f 100644 --- a/locales/en-us/blocks.json +++ b/locales/en-us/blocks.json @@ -40,6 +40,7 @@ "moveRight": "Move right", "moveTo": "Move (with parameter)", "moveUp": "Move up", + "comeToTheEnd": "Am I at the end?", "canMoveDown": "Can I move down?", "canMoveRight": "Can I move to the right?", "canMoveUp": "Can I move up?", diff --git a/locales/es-ar/blocks.json b/locales/es-ar/blocks.json index a0d62291..f3be674e 100644 --- a/locales/es-ar/blocks.json +++ b/locales/es-ar/blocks.json @@ -40,6 +40,7 @@ "moveRight": "Mover a la derecha", "moveTo": "Mover a (con parámetro)", "moveUp": "Mover arriba", + "comeToTheEnd": "¿Llegué al final?", "canMoveDown": "¿Puedo mover abajo?", "canMoveLeft": "¿Puedo mover a la izquierda?", "canMoveRight": "¿Puedo mover a la derecha?", diff --git a/package-lock.json b/package-lock.json index f4510ee6..573e015f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "babel-preset-react-app": "^10.0.1", "bfj": "^7.0.2", "blockly": "^10.4.3", + "blockly-proceds": "^1.0.15", "browserslist": "^4.18.1", "camelcase": "^6.2.1", "case-sensitive-paths-webpack-plugin": "^2.4.0", @@ -7635,9 +7636,18 @@ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" }, + "node_modules/blockly-proceds": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/blockly-proceds/-/blockly-proceds-1.0.15.tgz", + "integrity": "sha512-rrS2iuujX/0gUnbN3NZj5LBoPJct6POgh0S3ljcSXSMoUQ/cB6OP/h3X87VNlsFzaZyH2kv3Ige1kuS2GP6evw==", + "peerDependencies": { + "blockly": "^10.4.3" + } + }, "node_modules/blockly/node_modules/cssstyle": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", + "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", "dependencies": { "rrweb-cssom": "^0.6.0" }, @@ -7647,7 +7657,8 @@ }, "node_modules/blockly/node_modules/data-urls": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", + "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", "dependencies": { "abab": "^2.0.6", "whatwg-mimetype": "^3.0.0", @@ -7659,7 +7670,8 @@ }, "node_modules/blockly/node_modules/jsdom": { "version": "22.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", + "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "dependencies": { "abab": "^2.0.6", "cssstyle": "^3.0.0", @@ -7699,7 +7711,8 @@ }, "node_modules/blockly/node_modules/tr46": { "version": "4.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", "dependencies": { "punycode": "^2.3.0" }, @@ -7709,7 +7722,8 @@ }, "node_modules/blockly/node_modules/whatwg-url": { "version": "12.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", + "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", "dependencies": { "tr46": "^4.1.1", "webidl-conversions": "^7.0.0" @@ -23460,8 +23474,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "license": "MIT", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } @@ -33798,12 +33813,16 @@ "dependencies": { "cssstyle": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", + "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", "requires": { "rrweb-cssom": "^0.6.0" } }, "data-urls": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", + "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", "requires": { "abab": "^2.0.6", "whatwg-mimetype": "^3.0.0", @@ -33812,6 +33831,8 @@ }, "jsdom": { "version": "22.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", + "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "requires": { "abab": "^2.0.6", "cssstyle": "^3.0.0", @@ -33840,12 +33861,16 @@ }, "tr46": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", "requires": { "punycode": "^2.3.0" } }, "whatwg-url": { "version": "12.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", + "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", "requires": { "tr46": "^4.1.1", "webidl-conversions": "^7.0.0" @@ -34208,6 +34233,12 @@ } } }, + "blockly-proceds": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/blockly-proceds/-/blockly-proceds-1.0.15.tgz", + "integrity": "sha512-rrS2iuujX/0gUnbN3NZj5LBoPJct6POgh0S3ljcSXSMoUQ/cB6OP/h3X87VNlsFzaZyH2kv3Ige1kuS2GP6evw==", + "requires": {} + }, "bluebird": { "version": "3.7.2" }, @@ -44328,7 +44359,9 @@ } }, "punycode": { - "version": "2.3.0" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, "pupa": { "version": "3.1.0", diff --git a/package.json b/package.json index e4499a63..dbe2c182 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "babel-preset-react-app": "^10.0.1", "bfj": "^7.0.2", "blockly": "^10.4.3", + "blockly-proceds": "^1.0.15", "browserslist": "^4.18.1", "camelcase": "^6.2.1", "case-sensitive-paths-webpack-plugin": "^2.4.0", diff --git a/public/imagenes/iconos/icono.marcadorBlanco.png b/public/imagenes/iconos/icono.marcadorBlanco.png new file mode 100644 index 00000000..8593bb15 Binary files /dev/null and b/public/imagenes/iconos/icono.marcadorBlanco.png differ diff --git a/src/components/blockly/PBBlocklyWorkspace.tsx b/src/components/blockly/PBBlocklyWorkspace.tsx index d5f95fb0..27786350 100644 --- a/src/components/blockly/PBBlocklyWorkspace.tsx +++ b/src/components/blockly/PBBlocklyWorkspace.tsx @@ -4,7 +4,7 @@ import { Toolbox, categorizedToolbox, setupBlockly, setupBlocklyBlocks, setXml, import { PBCard } from "../PBCard"; import { Box, PaperProps, Typography } from "@mui/material"; import { useState } from "react"; -import Blockly from "blockly/core" +import * as Blockly from 'blockly/core' import { useThemeContext } from "../../theme/ThemeContext"; // inject options https://developers.google.com/blockly/reference/js/blockly.blocklyoptions_interface.md @@ -31,7 +31,7 @@ export const PBBlocklyWorkspace = ({ blockIds, categorized, sx, title, ...props setupBlocklyBlocks(t) - if (blocklyContainer) setupBlockly(blocklyContainer, { theme: blocklyTheme, toolbox, ...props.workspaceConfiguration } ) + if (blocklyContainer) setupBlockly(blocklyContainer, { theme: blocklyTheme, toolbox, ...props.workspaceConfiguration } ) if (blocklyContainer && props.initialXml) setXml(props.initialXml ) diff --git a/src/components/blockly/blockly.ts b/src/components/blockly/blockly.ts index 66b4cab7..5c3698ed 100644 --- a/src/components/blockly/blockly.ts +++ b/src/components/blockly/blockly.ts @@ -32,7 +32,7 @@ export type BlocklyBlockDefinition = { export type Toolbox = { kind: "categoryToolbox" | "flyoutToolbox", contents: ToolboxItem[] } type ToolboxItem = ToolboxBlock | ToolBoxCategory type ToolboxBlock = { kind: "block", type: string } -type ToolBoxCategory = { kind: "category" | '', name: string, contents: ToolboxItem[] } +type ToolBoxCategory = { kind: "category" | '', name: string, contents: ToolboxItem[], custom?: string } export const xmlBloqueEmpezarAEjecutar = ` @@ -67,7 +67,8 @@ export const setXml = (xml: string) => { export const setupBlockly = (container: Element, workspaceConfiguration: Blockly.BlocklyOptions) => { container.replaceChildren() //Removes previous injection, otherwise it might keep inserting below the current workspace container.ariaValueText = 'child-blockly' - Blockly.inject(container, workspaceConfiguration) + const workspace = Blockly.inject(container, workspaceConfiguration) + workspace.addChangeListener(Blockly.Events.disableOrphans); } export const workspaceToCode = () => javascriptGenerator.workspaceToCode(Blockly.getMainWorkspace()) @@ -109,8 +110,9 @@ export const messageBlock = (message: string) => { } const createCommonCode = () => { - javascriptGenerator.addReservedWords('main', 'hacer', 'out_hacer', 'evaluar'); + javascriptGenerator.addReservedWords('main,hacer,out_hacer,evaluar'); + /* javascriptGenerator.required_value = function () { return null }; @@ -118,6 +120,7 @@ const createCommonCode = () => { javascriptGenerator.required_statement = function () { return null }; +*/ javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n'; javascriptGenerator.addReservedWords('highlightBlock'); @@ -155,7 +158,6 @@ const defineBlocklyTranslations = (t: (key: string) => string) => { Blockly.Msg.CLEAN_UP = t("contextMenu.cleanUp") Blockly.Msg.EXTERNAL_INPUTS = t("contextMenu.externalInputs") - // ProcedsBlockly.init() needs all procedure blocks to work, so we need to put them back // After calling init(), we disable unwanted toolbox blocks again enableUnwantedProcedureBlocks() @@ -167,10 +169,15 @@ export const categorizedToolbox = (t: (key: string) => string, blocks: BlockType const categoryBlocksFor = (categoryId: string): ToolboxItem => { const contents = blocks.filter(block => block.categoryId === categoryId).map(blockTypeToToolboxBlock) - return contents.length ? { + return contents.length ? categoryId === 'myprocedures' ? { + kind: "category", + name: `${t(`categories.${categoryId}`)}`, + contents: contents, + custom: "PROCEDURE" + } : { kind: "category", name: `${t(`categories.${categoryId}`)}`, - contents: contents + contents: contents, } : { kind: '', name: '', @@ -187,4 +194,4 @@ export const categorizedToolbox = (t: (key: string) => string, blocks: BlockType export const uncategorizedToolbox = (blocks: BlockType[]): Toolbox => ({ kind: "flyoutToolbox", contents: blocks.map(blockTypeToToolboxBlock) -}) \ No newline at end of file +}) diff --git a/src/components/blockly/blocksGallery/others.ts b/src/components/blockly/blocksGallery/others.ts index c6070a77..afc92c80 100644 --- a/src/components/blockly/blocksGallery/others.ts +++ b/src/components/blockly/blocksGallery/others.ts @@ -1,7 +1,8 @@ -import { createCommonBlocklyBlocks } from "../utils"; +import { createCommonBlocklyBlocks, disableUnwantedProcedureBlocks, enableUnwantedProcedureBlocks } from "../utils"; import Blockly, { Block } from "blockly/core" import { sensorsColor } from "./sensors"; import { javascriptGenerator, Order } from "blockly/javascript"; +import { procedsBlocklyInit } from 'blockly-proceds' const othersColor = '#cc5b22'; const eventsColor = '#00a65a'; // == boton ejecutar @@ -138,4 +139,10 @@ export const createOthersBlocks = (t: (key: string) => string) => { init: Blockly.Blocks["logic_compare"].init, categoryId: 'operators', } + + enableUnwantedProcedureBlocks() + + procedsBlocklyInit(Blockly) + + disableUnwantedProcedureBlocks() } \ No newline at end of file diff --git a/src/components/blockly/utils.ts b/src/components/blockly/utils.ts index f1d0f489..28844de8 100644 --- a/src/components/blockly/utils.ts +++ b/src/components/blockly/utils.ts @@ -1,4 +1,4 @@ -import Blockly from "blockly/core" +import * as Blockly from 'blockly/core' export type optionType = { 'comportamiento'?: string @@ -48,8 +48,9 @@ export const isFlying = (block: { getRootBlock: () => any; }) => export const getParams = (procedureBlock: { getProcedureDef: () => any[]; }) => procedureBlock.getProcedureDef()[1] -export const hasParam = (procedureBlock: { getProcedureDef: () => any[]; }, paramBlock: { getFieldValue: (arg0: string) => any; }) => - getParams(procedureBlock).includes(paramBlock.getFieldValue('VAR')) +export const hasParam = (procedureBlock: { getProcedureDef: () => any[]; }, paramBlock: { getFieldValue: (arg0: string) => any; }) => { + return getParams(procedureBlock).includes(paramBlock.getFieldValue('VAR')) +} export const clearValidations = (workspace: Blockly.Workspace = Blockly.getMainWorkspace()) => { workspace.getAllBlocks(false).forEach(clearValidationsFor) @@ -94,10 +95,14 @@ const drawWarningIcon = (group: Element | null | undefined, colour: any, seconda group); }; -const setWarningColour = (block: { warning: { setVisible: (visible: any) => void; bubble_: { setColour: (arg0: any) => void; }; iconGroup_: Element | null | undefined; }; }, colour: any, secondaryColour: any) => { +const setWarningColour = (block: { warning: { setVisible: (visible: any) => void; textBubble: { setColour: (arg0: any) => void; }; bubble_: { setColour: (arg0: any) => void; }; iconGroup_: Element | null | undefined; }; }, colour: any, secondaryColour: any) => { const unBoundedSetVisible = block.warning.setVisible const boundedSetVisible = unBoundedSetVisible.bind(block.warning) - block.warning.setVisible = (visible) => { boundedSetVisible(visible); if (visible) block.warning.bubble_.setColour(colour) } + block.warning.setVisible = (visible) => { + boundedSetVisible(visible); + if (visible) + block.warning.textBubble.setColour(colour) + } drawWarningIcon(block.warning.iconGroup_, colour, secondaryColour) } @@ -143,16 +148,14 @@ export const createCommonBlocklyBlocks = (t: (key: string) => string, color: str "message0": "%1", "args0": [ { - "type": "field_variable", + "type": "field_label", "name": "VAR", - "variable": t("procedures.variableName") - } + "variable": t("procedures.variableName") } ], "output": null, "style": "variable_blocks", "tooltip": "", "helpUrl": "", - "extensions": ["contextMenu_variableSetterGetter"] }); }, mutationToDom: function () { @@ -177,7 +180,8 @@ export const createCommonBlocklyBlocks = (t: (key: string) => string, color: str if (this.$parent) { // Este if sirve para las soluciones viejas que no tienen $parent var procedureDef = this.workspace.getBlockById(this.$parent) var ok = isInsideProcedureDef(this) && hasParam(procedureDef, this) - this.setDisabled(!ok) + //this.setDisabled(!ok) + this.setEnabled(ok) if (ok || isFlying(this) || !procedureDef) { clearValidationsFor(this) } else { @@ -214,379 +218,6 @@ export const createCommonBlocklyBlocks = (t: (key: string) => string, color: str } } - Blockly.Blocks['procedures_defnoreturn'] = { - init: function () { - var nameField = new Blockly.FieldTextInput(Blockly.Msg['PROCEDURES_DEFNORETURN_PROCEDURE'], - Blockly.Procedures.rename); - nameField.setSpellcheck(false); - this.appendDummyInput() - .appendField(Blockly.Msg['PROCEDURES_DEFNORETURN_TITLE']) - .appendField(nameField, 'NAME') - .appendField('', 'PARAMS'); - this.setMutator(new Blockly.icons.MutatorIcon(['procedures_mutatorarg'], this)); - if ((this.workspace.options.comments || - (this.workspace.options.parentWorkspace && - this.workspace.options.parentWorkspace.options.comments)) && - Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT']) { - this.setCommentText(Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT']); - } - this.setStyle('procedure_blocks'); - this.setTooltip(Blockly.Msg['PROCEDURES_DEFNORETURN_TOOLTIP']); - this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFNORETURN_HELPURL']); - this.arguments_ = []; - this.argumentVarModels_ = []; - this.setStatements_(true); - this.statementConnection_ = null; - - }, - /** - * Add or remove the statement block from this function definition. - * @param {boolean} hasStatements True if a statement block is needed. - * @this {Blockly.Block} - */ - setStatements_: function (hasStatements: boolean) { - if (this.hasStatements_ === hasStatements) { - return; - } - if (hasStatements) { - this.appendStatementInput('STACK') - .appendField(Blockly.Msg['PROCEDURES_DEFNORETURN_DO']); - if (this.getInput('RETURN')) { - this.moveInputBefore('STACK', 'RETURN'); - } - } else { - this.removeInput('STACK', true); - } - this.hasStatements_ = hasStatements; - }, - /** - * Update the display of parameters for this procedure definition block. - * @private - * @this {Blockly.Block} - */ - updateParams_: function () { - - // Merge the arguments into a human-readable list. - var paramString = ''; - if (this.arguments_.length) { - paramString = Blockly.Msg['PROCEDURES_BEFORE_PARAMS'] + - ' ' + this.arguments_.join(', '); - } - // The params field is deterministic based on the mutation, - // no need to fire a change event. - Blockly.Events.disable(); - try { - this.setFieldValue(paramString, 'PARAMS'); - } finally { - Blockly.Events.enable(); - } - }, - /** - * Create XML to represent the argument inputs. - * @param {boolean=} opt_paramIds If true include the IDs of the parameter - * quarks. Used by Blockly.Procedures.mutateCallers for reconnection. - * @return {!Element} XML storage element. - * @this {Blockly.Block} - */ - mutationToDom: function (opt_paramIds: any) { - var container = Blockly.utils.xml.createElement('mutation'); - if (opt_paramIds) { - container.setAttribute('name', this.getFieldValue('NAME')); - } - for (var i = 0; i < this.argumentVarModels_.length; i++) { - var parameter = Blockly.utils.xml.createElement('arg'); - var argModel = this.argumentVarModels_[i]; - parameter.setAttribute('name', argModel.name); - parameter.setAttribute('varid', argModel.getId()); - if (opt_paramIds && this.paramIds_) { - parameter.setAttribute('paramId', this.paramIds_[i]); - } - container.appendChild(parameter); - } - - // Save whether the statement input is visible. - if (!this.hasStatements_) { - container.setAttribute('statements', 'false'); - } - return container; - }, - /** - * Parse XML to restore the argument inputs. - * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} - */ - domToMutation: function (xmlElement: { childNodes: any[]; getAttribute: (arg0: string) => string; }) { - this.arguments_ = []; - this.argumentVarModels_ = []; - for (var i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { - if (childNode.nodeName.toLowerCase() == 'arg') { - var varName = childNode.getAttribute('name'); - var varId = childNode.getAttribute('varid') || childNode.getAttribute('varId'); - this.arguments_.push(varName); - var variable = Blockly.Variables.getOrCreateVariablePackage( - this.workspace, varId, varName, ''); - if (variable != null) { - this.argumentVarModels_.push(variable); - } else { - console.log('Failed to create a variable with name ' + varName + ', ignoring.'); - } - } - } - this.updateParams_(); - Blockly.Procedures.mutateCallers(this); - - // Show or hide the statement input. - this.setStatements_(xmlElement.getAttribute('statements') !== 'false'); - }, - /** - * 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: Blockly.Workspace) { - /* - * Creates the following XML: - * - * - * - * arg1_name - * etc... - * - * - * - */ - - var containerBlockNode = Blockly.utils.xml.createElement('block'); - containerBlockNode.setAttribute('type', 'procedures_mutatorcontainer'); - var statementNode = Blockly.utils.xml.createElement('statement'); - statementNode.setAttribute('name', 'STACK'); - containerBlockNode.appendChild(statementNode); - - var node = statementNode; - for (var i = 0; i < this.arguments_.length; i++) { - var argBlockNode = Blockly.utils.xml.createElement('block'); - argBlockNode.setAttribute('type', 'procedures_mutatorarg'); - var fieldNode = Blockly.utils.xml.createElement('field'); - fieldNode.setAttribute('name', 'NAME'); - var argumentName = Blockly.utils.xml.createTextNode(this.arguments_[i]); - fieldNode.appendChild(argumentName); - argBlockNode.appendChild(fieldNode); - var nextNode = Blockly.utils.xml.createElement('next'); - argBlockNode.appendChild(nextNode); - - node.appendChild(argBlockNode); - node = nextNode; - } - - var containerBlock = Blockly.Xml.domToBlock(containerBlockNode, workspace); - - if (this.type == 'procedures_defreturn') { - containerBlock.setFieldValue(this.hasStatements_, 'STATEMENTS'); - } else { - containerBlock.removeInput('STATEMENT_INPUT'); - } - - // Initialize procedure's callers with blank IDs. - Blockly.Procedures.mutateCallers(this); - 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: { getInputTargetBlock: (arg0: string) => any; getFieldValue: (arg0: string) => any; }) { - // Parameter list. - this.arguments_ = []; - this.paramIds_ = []; - this.argumentVarModels_ = []; - var paramBlock = containerBlock.getInputTargetBlock('STACK'); - while (paramBlock) { - var varName = paramBlock.getFieldValue('NAME'); - this.arguments_.push(varName); - var variable = this.workspace.getVariable(varName, ''); - this.argumentVarModels_.push(variable); - - this.paramIds_.push(paramBlock.id); - paramBlock = paramBlock.nextConnection && - paramBlock.nextConnection.targetBlock(); - } - this.updateParams_(); - Blockly.Procedures.mutateCallers(this); - - // Show/hide the statement input. - var hasStatements = containerBlock.getFieldValue('STATEMENTS'); - if (hasStatements !== null) { - hasStatements = hasStatements == 'TRUE'; - if (this.hasStatements_ != hasStatements) { - if (hasStatements) { - this.setStatements_(true); - // Restore the stack, if one was saved. - Blockly.icons.MutatorIcon.reconnect(this.statementConnection_, this, 'STACK'); - this.statementConnection_ = null; - } else { - // Save the stack, then disconnect it. - var stackConnection = this.getInput('STACK').connection; - this.statementConnection_ = stackConnection.targetConnection; - if (this.statementConnection_) { - var stackBlock = stackConnection.targetBlock(); - stackBlock.unplug(); - stackBlock.bumpNeighbours(); - } - this.setStatements_(false); - } - } - } - }, - /** - * Return the signature of this procedure definition. - * @return {!Array} Tuple containing three elements: - * - the name of the defined procedure, - * - a list of all its arguments, - * - that it DOES NOT have a return value. - * @this {Blockly.Block} - */ - getProcedureDef: function () { - return [this.getFieldValue('NAME'), this.arguments_, false]; - }, - /** - * Return all variables referenced by this block. - * @return {!Array.} List of variable names. - * @this {Blockly.Block} - */ - getVars: function () { - return this.arguments_; - }, - /** - * Return all variables referenced by this block. - * @return {!Array.} List of variable models. - * @this {Blockly.Block} - */ - getVarModels: function () { - return this.argumentVarModels_; - }, - /** - * Notification that a variable is renaming. - * If the ID matches one of this block's variables, rename it. - * @param {string} oldId ID of variable to rename. - * @param {string} newId ID of new variable. May be the same as oldId, but - * with an updated name. Guaranteed to be the same type as the old - * variable. - * @override - * @this {Blockly.Block} - */ - renameVarById: function (oldId: any, newId: any) { - var oldVariable = this.workspace.getVariableById(oldId); - if (oldVariable.type != '') { - // Procedure arguments always have the empty type. - return; - } - var oldName = oldVariable.name; - var newVar = this.workspace.getVariableById(newId); - - var change = false; - for (var i = 0; i < this.argumentVarModels_.length; i++) { - if (this.argumentVarModels_[i].getId() == oldId) { - this.arguments_[i] = newVar.name; - this.argumentVarModels_[i] = newVar; - change = true; - } - } - if (change) { - this.displayRenamedVar_(oldName, newVar.name); - Blockly.Procedures.mutateCallers(this); - } - }, - /** - * Notification that a variable is renaming but keeping the same ID. If the - * variable is in use on this block, rerender to show the new name. - * @param {!Blockly.VariableModel} variable The variable being renamed. - * @package - * @override - * @this {Blockly.Block} - */ - updateVarName: function (variable: { name: any; getId: () => any; }) { - var newName = variable.name; - var change = false; - for (var i = 0; i < this.argumentVarModels_.length; i++) { - if (this.argumentVarModels_[i].getId() == variable.getId()) { - var oldName = this.arguments_[i]; - this.arguments_[i] = newName; - change = true; - } - } - if (change) { - this.displayRenamedVar_(oldName, newName); - Blockly.Procedures.mutateCallers(this); - } - }, - /** - * Update the display to reflect a newly renamed argument. - * @param {string} oldName The old display name of the argument. - * @param {string} newName The new display name of the argument. - * @private - * @this {Blockly.Block} - */ - displayRenamedVar_: function (oldName: string, newName: any) { - this.updateParams_(); - // Update the mutator's variables if the mutator is open. - if (this.mutator && this.mutator.isVisible()) { - var blocks = this.mutator.workspace_.getAllBlocks(false); - for (var i = 0, block; (block = blocks[i]); i++) { - if (block.type == 'procedures_mutatorarg' && - Blockly.Names.equals(oldName, block.getFieldValue('NAME'))) { - block.setFieldValue(newName, 'NAME'); - } - } - } - }, - /** - * Add custom menu options to this block's context menu. - * @param {!Array} options List of menu options to add to. - * @this {Blockly.Block} - */ - customContextMenu: function (options: { enabled: boolean; }[]) { - if (this.isInFlyout) { - return; - } - // Add option to create caller. - var option = { enabled: true, text: '', callback: () => { } }; - var name = this.getFieldValue('NAME'); - option.text = Blockly.Msg['PROCEDURES_CREATE_DO'].replace('%1', name); - var xmlMutation = Blockly.utils.xml.createElement('mutation'); - xmlMutation.setAttribute('name', name); - for (var i = 0; i < this.arguments_.length; i++) { - var xmlArg = Blockly.utils.xml.createElement('arg'); - xmlArg.setAttribute('name', this.arguments_[i]); - xmlMutation.appendChild(xmlArg); - } - var xmlBlock = Blockly.utils.xml.createElement('block'); - xmlBlock.setAttribute('type', this.callType_); - xmlBlock.appendChild(xmlMutation); - option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); - options.push(option); - - // Add options to create getters for each parameter. - if (!this.isCollapsed()) { - for (var i = 0; i < this.argumentVarModels_.length; i++) { - var argOption = { enabled: true, text: '', callback: () => { } }; - var argVar = this.argumentVarModels_[i]; - argOption.text = Blockly.Msg['VARIABLES_SET_CREATE_GET'] - .replace('%1', argVar.name); - - var argXmlField = Blockly.Variables.generateVariableFieldDom(argVar); - var argXmlBlock = Blockly.utils.xml.createElement('block'); - argXmlBlock.setAttribute('type', 'variables_get'); - argXmlBlock.appendChild(argXmlField); - argOption.callback = - Blockly.ContextMenu.callbackFactory(this, argXmlBlock); - options.push(argOption); - } - } - }, - callType_: 'procedures_callnoreturn', - } Blockly.Blocks['procedures_defreturn'] = { /** diff --git a/src/test/integration/ChallengeView.cy.tsx b/src/test/integration/ChallengeView.cy.tsx index c92b4fec..e00eb4dc 100644 --- a/src/test/integration/ChallengeView.cy.tsx +++ b/src/test/integration/ChallengeView.cy.tsx @@ -37,7 +37,7 @@ describe('Challenge view with blocks', () => { //TODO - remove the skip once this issue is resolved: https://github.com/Program-AR/pilas-bloques-app/issues/312 const testExecutionWithBlocks = (name: string, solution: string, expected: any, skip = true) => { - (skip ? it.skip: it)(name, () => { + (skip ? it.skip : it)(name, () => { LocalStorage.saveCreatorChallenge(challenge(solution)) mount( @@ -72,7 +72,7 @@ describe('Challenge view with blocks', () => { ` - + const ifSolution = ` @@ -187,7 +187,7 @@ describe('Challenge view with blocks', () => { ` -const aritmethicSolution = ` + const aritmethicSolution = ` @@ -220,6 +220,60 @@ const aritmethicSolution = ` ` + + const procedureSolution = ` + + + + + + + + + + + Hacer algo + + + + +` + + const procedureWithParameterSolution = ` + + + + + + + + + + + + + + + + + + + + Hacer algo + parámetro 1 + + + + + + + + + + + +` + testExecutionWithBlocks('Execution of a solution has effect on scene view', simpleMoveSolution, 1, false) //Code from blocks have effect @@ -235,4 +289,9 @@ const aritmethicSolution = ` testExecutionWithBlocks('Code from blocks have effect - repeat', repeatSolution, 2) testExecutionWithBlocks('Code from blocks have effect - repeat until', repeatUntilSolution, 1) + + testExecutionWithBlocks('Code from blocks have effect - procedures', procedureSolution, 1) + + testExecutionWithBlocks('Code from blocks have effect - procedures with parameter', procedureWithParameterSolution, 1) + }) diff --git a/src/theme/ThemeContext.tsx b/src/theme/ThemeContext.tsx index af4d7922..e5ff5c94 100644 --- a/src/theme/ThemeContext.tsx +++ b/src/theme/ThemeContext.tsx @@ -3,8 +3,7 @@ import { createContext, FC, PropsWithChildren, useContext, useEffect, useMemo, u import { getDesignTokens } from "./theme"; import { LocalStorage } from "../localStorage"; import { Ember } from "../emberCommunication"; -import Blockly, { Theme as BlocklyTheme } from 'blockly/core' - +import * as Blockly from 'blockly/core' const BlocklyClassicTheme = Blockly.Theme.defineTheme('classicBlockly', { base: Blockly.Themes.Classic, @@ -43,7 +42,7 @@ type ThemeContextType = { setSimpleReadModeEnabled: (mode: boolean) => void; theme: Theme; isSmallScreen: boolean; - blocklyTheme: BlocklyTheme; + blocklyTheme: Blockly.Theme; }; export const ThemeContext = createContext({