Un menú contextual contiene una lista de acciones que un usuario puede realizar en un componente, como un espacio de trabajo, un bloque o un comentario del espacio de trabajo. El menú contextual se muestra en respuesta a un clic con el botón derecho o a una presión prolongada en un dispositivo táctil. Si usas el complemento @blockly/keyboard-navigation
, también se muestra con una combinación de teclas, que, de forma predeterminada, es Ctrl+Enter
en Windows o Command+Enter
en Mac.
Los menús contextuales son un buen lugar para agregar acciones que el usuario realiza con poca frecuencia, como descargar una captura de pantalla. Si crees que una acción se usará con más frecuencia, tal vez quieras crear una forma más fácil de descubrir para invocarla.
Los menús contextuales son compatibles con los espacios de trabajo, los bloques, los comentarios del espacio de trabajo, las burbujas y las conexiones. También puedes implementarlos en tus propios componentes personalizados. Blockly proporciona menús contextuales estándar que puedes personalizar. También puedes personalizar los menús contextuales en los espacios de trabajo y los bloques por espacio de trabajo o por bloque.
Cómo funcionan los menús contextuales
Blockly tiene un registro que contiene plantillas para todos los elementos de menú posibles. Cada plantilla describe cómo construir un solo elemento en un menú contextual. Cuando el usuario invoca un menú contextual en un componente, este hace lo siguiente:
Le solicita al constructo del registro un array de elementos de menú que se aplican al componente. El registro le pregunta a cada plantilla si se aplica al componente y, si es así, agrega un elemento de menú correspondiente al array.
Si el componente es un espacio de trabajo o un bloque, verifica si el espacio de trabajo o el bloque específicos en los que se invocó el menú tienen una función para personalizar el menú contextual. Si es así, pasa el array a la función, que puede agregar, borrar o modificar elementos del array.
Muestra el menú contextual con el array (posiblemente modificado) de elementos del menú contextual.
Blockly define un conjunto estándar de plantillas para los menús contextuales de los espacios de trabajo, los bloques y los comentarios del espacio de trabajo. Precarga las plantillas de los espacios de trabajo y los bloques en el registro. Si quieres usar las plantillas para los comentarios del espacio de trabajo, debes cargarlas en el registro por tu cuenta.
Para obtener información sobre cómo agregar, borrar y modificar plantillas en el registro, consulta Personaliza el registro.
Alcance
Los menús contextuales se implementan con diferentes tipos de componentes, incluidos los espacios de trabajo, los comentarios de espacios de trabajo, las conexiones, los bloques, las burbujas y tus propios componentes personalizados. Los menús contextuales de cada uno de estos tipos de componentes pueden contener diferentes elementos, y los elementos pueden comportarse de manera diferente según el tipo de componente. Por lo tanto, el sistema de menú contextual debe saber en qué componente se invocó.
Para abordar este problema, el registro usa un objeto Scope
. El componente en el que se invocó el menú contextual se almacena en la propiedad focusedNode
como un objeto que implementa IFocusableNode
. (IFocusableNode
se implementa en todos los componentes en los que los usuarios pueden enfocar, incluidos los que implementan menús contextuales. Para obtener más información, consulta Sistema de enfoque.
El objeto Scope
se pasa a varias de las funciones de una plantilla. En cualquier función que obtenga un objeto Scope
, puedes decidir qué hacer según el tipo del objeto en la propiedad focusedNode
. Por ejemplo, puedes verificar si el componente es un bloque con el siguiente código:
if (scope.focusedNode instanceof Blockly.BlockSvg) {
// do something with the block
}
El objeto Scope
tiene otras propiedades opcionales que ya no se recomiendan para su uso, pero que aún se pueden establecer:
block
solo se establece si el componente cuyo menú se muestra es unBlockSvg
.workspace
solo se establece si el componente es unWorkspaceSvg
.comment
solo se establece si el componente es unRenderedWorkspaceComment
.
Estas propiedades no abarcan todos los tipos de componentes que pueden tener un menú contextual, por lo que debes usar la propiedad focusedNode
.
El tipo RegistryItem
Las plantillas tienen el tipo ContextMenuRegistry.RegistryItem
, que contiene las siguientes propiedades. Ten en cuenta que las propiedades preconditionFn
, displayText
y callback
son mutuamente excluyentes con la propiedad separator
.
ID
La propiedad id
debe ser una cadena única que indique lo que hace el elemento de menú contextual.
const collapseTemplate = {
id: 'collapseBlock',
// ...
};
Función de condición previa
Puedes usar preconditionFn
para restringir cuándo y cómo se debe mostrar un elemento del menú contextual.
Debe devolver una de las siguientes cadenas: 'enabled'
, 'disabled'
o 'hidden'
.
Valor | Descripción | Imagen |
---|---|---|
'enabled' |
Indica que el elemento está activo. | ![]() |
'disabled' |
Indica que el elemento no está activo. | ![]() |
'hidden' |
Oculta el elemento. |
También se pasa un Scope
a preconditionFn
, que puedes usar para determinar qué tipo de componente abrió el menú y el estado de ese componente.
Por ejemplo, es posible que desees que un elemento solo aparezca para los bloques y solo cuando esos bloques se encuentren en un estado en particular:
const collapseTemplate = {
// ...
preconditionFn: (scope) => {
if (scope.focusedNode instanceof Blockly.BlockSvg) {
if (!scope.focusedNode.isCollapsed()) {
// The component is a block and it is not already collapsed
return 'enabled';
} else {
// The block is already collapsed
return 'disabled';
}
}
// The component is not a block
return 'hidden';
},
// ...
}
Texto visible
El displayText
es lo que se debe mostrar al usuario como parte del elemento de menú.
El texto visible puede ser una cadena, HTML o una función que muestre una cadena o HTML.
const collapseTemplate = {
// ...
displayText: 'Collapse block',
// ...
};
Si quieres mostrar una traducción de Blockly.Msg
, debes usar una función. Si intentas asignar el valor directamente, es posible que no se carguen los mensajes y, en su lugar, obtendrás un valor de undefined
.
const collapseTemplate = {
// ...
displayText: () => Blockly.Msg['MY_COLLAPSE_BLOCK_TEXT'],
// ...
};
Si usas una función, también se le pasa un valor Scope
. Puedes usarlo para agregar información sobre el elemento al texto visible.
const collapseTemplate = {
// ...
displayText: (scope) => {
if (scope.focusedNode instanceof Blockly.Block) {
return `Collapse ${scope.focusedNode.type} block`;
}
// Shouldn't be possible, as our preconditionFn only shows this item for blocks
return '';
},
// ...
}
Peso
El weight
determina el orden en que se muestran los elementos del menú contextual.
Los valores más positivos se muestran más abajo en la lista que los valores menos positivos.
(Puedes imaginar que los elementos con pesos más altos son "más pesados", por lo que se hunden hasta el fondo).
const collapseTemplate = {
// ...
weight: 10,
// ...
}
Los pesos de los elementos integrados del menú contextual se ordenan de forma creciente a partir de 1 y aumentan de a 1.
Función de devolución de llamada
La propiedad callback
es una función que realiza la acción del elemento de menú contextual. Se le pasan varios parámetros:
scope
: Es un objetoScope
que proporciona una referencia al componente cuyo menú está abierto.menuOpenEvent
: Es elEvent
que activó la apertura del menú contextual. Puede serPointerEvent
oKeyboardEvent
, según cómo el usuario abrió el menú.menuSelectEvent
: Es elEvent
que seleccionó este elemento de menú contextual en particular del menú. Puede serPointerEvent
oKeyboardEvent
, según cómo el usuario haya seleccionado el elemento.location
: Es elCoordinate
en coordenadas de píxeles donde se abrió el menú. Esto te permite, por ejemplo, crear un nuevo bloque en la ubicación del clic.
const collapseTemplate = {
// ...
callback: (scope, menuOpenEvent, menuSelectEvent, location) => {
if (scope.focusedNode instanceof Blockly.BlockSvg) {
scope.focusedNode.collapse();
}
},
}
Puedes usar scope
para diseñar plantillas que funcionen de manera diferente según el componente en el que se abrieron:
const collapseTemplate = {
// ...
callback: (scope) => {
if (scope.focusedNode instance of Blockly.BlockSvg) {
// On a block, collapse just the block.
const block = scope.focusedNode;
block.collapse();
} else if (scope.focusedNode instanceof Blockly.WorkspaceSvg) {
// On a workspace, collapse all the blocks.
let workspace = scope.focusedNode;
collapseAllBlocks(workspace);
}
}
}
Separador
La propiedad separator
dibuja una línea en el menú contextual.
Las plantillas con la propiedad separator
no pueden tener propiedades preconditionFn
, displayText
ni callback
, y solo se pueden definir con la propiedad scopeType
. Esta última restricción significa que solo se pueden usar en los menús contextuales de los espacios de trabajo, los bloques y los comentarios del espacio de trabajo.
const separatorAfterCollapseBlockTemplate = {
id: 'separatorAfterCollapseBlock',
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
weight: 11, // Between the weights of the two items you want to separate.
separator: true,
};
Necesitas una plantilla diferente para cada separador del menú contextual. Usa la propiedad weight
para colocar cada separador.
Tipo de permiso
La propiedad scopeType
está obsoleta. Anteriormente, se usaba para determinar si se debía mostrar un elemento de menú en un menú contextual para un bloque, un comentario del espacio de trabajo o un espacio de trabajo. Dado que los menús contextuales se pueden abrir en otros componentes, la propiedad scopeType
es demasiado restrictiva. En su lugar, debes usar preconditionFn
para mostrar u ocultar tu opción para los componentes correspondientes.
Si tienes plantillas de menú contextual existentes que usan scopeType
, Blockly seguirá mostrando el elemento solo para el componente adecuado.
const collapseTemplate = {
// ...
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
// ...
};
Personaliza el registro
Puedes agregar, borrar o modificar plantillas en el registro. Puedes encontrar las plantillas predeterminadas en contextmenu_items.ts
.
Cómo agregar una plantilla
Puedes agregar una plantilla al registro si la registras. Debes hacerlo una vez cuando se cargue la página. Puede ocurrir antes o después de que insertes tu espacio de trabajo.
const collapseTemplate = { /* properties from above */ };
Blockly.ContextMenuRegistry.registry.register(collapseTemplate);
Borra una plantilla
Puedes quitar una plantilla del registro si la anulas por ID.
Blockly.ContextMenuRegistry.registry.unregister('someID');
Cómo modificar una plantilla
Puedes modificar una plantilla existente. Para ello, obtén la plantilla del registro y, luego, modifícala en el lugar.
const template = Blockly.ContextMenuRegistry.registry.getItem('someID');
template?.displayText = 'some other display text';
Inhabilita los menús contextuales de bloqueo
De forma predeterminada, los bloques tienen un menú contextual que permite a los usuarios realizar acciones como agregar comentarios o duplicar bloques.
Puedes inhabilitar el menú contextual de un bloque individual de la siguiente manera:
block.contextMenu = false;
En la definición JSON de un tipo de bloque, usa la clave enableContextMenu
:
{
// ...,
"enableContextMenu": false,
}
Personaliza los menús contextuales por tipo de bloque o espacio de trabajo
Después de que Blockly genera un array de elementos del menú contextual, puedes personalizarlo para bloques o espacios de trabajo individuales. Para ello, establece BlockSvg.customContextMenu
o WorkspaceSvg.configureContextMenu
en una función que modifique el array en su lugar.
Los objetos del array que se pasan a los bloques tienen el tipo ContextMenuOption
o implementan la interfaz LegacyContextMenuOption
. Los objetos que se pasan a los espacios de trabajo tienen el tipo ContextMenuOption
. Blockly usa las siguientes propiedades de estos objetos:
text
: Es el texto visible.enabled
: Si esfalse
, muestra el elemento con texto gris.callback
: Es la función a la que se llamará cuando se haga clic en el elemento.separator
: El elemento es un separador. Es mutuamente excluyente con las otras tres propiedades.
Consulta la documentación de referencia para conocer los tipos de propiedades y las firmas de funciones.
Por ejemplo, esta es una función que agrega un elemento Hello, World!
al menú contextual de un espacio de trabajo:
workspace.configureContextMenu = function (menuOptions, e) {
const item = {
text: 'Hello, World!',
enabled: true,
callback: function () {
alert('Hello, World!');
},
};
// Add the item to the end of the context menu.
menuOptions.push(item);
}
Cómo mostrar un menú contextual en un objeto personalizado
Para que aparezcan menús contextuales en los componentes personalizados, sigue estos pasos:
- Implementa
IFocusableNode
o extiende una clase que implementeIFocusableNode
. Esta interfaz se usa en el sistema de menú contextual para identificar tu componente. También permite que los usuarios naveguen a tu componente con el complemento de navegación por teclado. Implementa
IContextMenu
, que contiene la funciónshowContextMenu
. Esta función obtiene los elementos del menú contextual del registro, calcula la ubicación en la pantalla en la que se debe mostrar el menú y, finalmente, muestra el menú si hay elementos para mostrar.const MyBubble implements IFocusableNode, IContextMenu { ... showContextMenu(menuOpenEvent) { // Get the items from the context menu registry const scope = {focusedNode: this}; const items = Blockly.ContextMenuRegistry.registry.getContextMenuOptions(scope, menuOpenEvent); // Return early if there are no items available if (!items.length) return; // Show the menu at the same location on screen as this component // The location is in pixel coordinates, so translate from workspace coordinates const location = Blockly.utils.svgMath.wsToScreenCoordinates(new Coordinate(this.x, this.y)); // Show the context menu Blockly.ContextMenu.show(menuOpenEvent, items, this.workspace.RTL, this.workspace, location); } }
Agrega un controlador de eventos que llame a
showContextMenu
cuando el usuario haga clic con el botón derecho en tu componente. Ten en cuenta que el complemento de navegación con el teclado proporciona un controlador de eventos que llama ashowContextMenu
cuando el usuario presionaCtrl+Enter
(Windows) oCommand+Enter
(Mac).Agrega plantillas al registro para los elementos del menú contextual.