Callbacks
You can optionally provide callback listeners. These will be called when the user interacts with the canvas.
- contextMenuHandler
- editActionHandler
- beforeEditActionHandler
- clickActionHandler
- decorationActionHandler
- tipHandler
- idGeneratorHandler
- selectionChangeHandler
- layoutHandler
contextMenuHandler
contextMenuHandler(source, defaultMenu)
f this callback is not provided common canvas will handle context menus, and their actions, internally. You only need to implement this callback if you want to add or remove options to/from the context menu.
The job of this callback is to tell common canvas what items to display in the context menu. This will be dependent on what object or set of objects the context menu was requested for by the user.
This callback will be called if the user performs a context menu gesture (such as mouse ‘right click’) on a:
- node
- link
- comment
- port
- on the canvas background or
- if a number of objects are selected, the combination of objects.
This callback must return an array that defines the menu to be displayed. If the callback is not provided, a default context menu (the defaultMenu passed into the handler) will be displayed by the common canvas.
The source object passed in looks like this:
{type: "node",targetObject: {<object_info>},selectedObjectIds: ["node_1", "node_2"],mousePos: {x: "10", y:"20"}}
type
Indicates type of object for which the context menu was selected. Can be “node”, “port”, “link”, “canvas” or “comment”
targetObject
The object for which the context menu was requested. Only provided when type is “node” or “comment”
selectedObjectIds
An array containing the IDs of all currently selected nodes and/or comments
mousePos
An object containing the coords of the mouse when the context menu was requested
The callback would need to return an array, that describes the context menu to be displayed, that looks something like this:
[{action: "deleteSelectedObjects", label: "Delete"},{divider: true},{action: "myAction", label: "My Action"}{action: "myUnavailableAction", label: "A test disabled item", enable: false}]
There is one element in the array for each entry in the context menu. An entry can be either a context menu item, which consists of a label and an action, or a divider, whose field would need to be set to true.
Actions can be either internal (implemented inside the common canvas, like “deleteSelectedObjects” above) or external (like “myAction”).
Existing internal actions are:
- selectAll
- cut
- copy
- paste
- undo
- redo
- createSupernode
- expandSupernode
- collapseSupernode
- deleteSelectedObjects
- createComment
- deleteLink
- disconnectNode
- highlightBranch
- highlightDownstream
- highlightUpstream
- unhighlight
External actions are custom actions you want common canvas to display for your application. To get common canvas to display your action you would need to return an array from the callback that includes a menu item for the action.
When the user clicks the option in the context menu matching your action common canvas will call the editActionHandler callback so you’ll need to implement some code in that callback to execute the intended action. If you want to simply add your action to the default context menu provided by common canvas you can take the defaultMenu parameter provided to the callback and add your menu item to it. Alternatively, you can provide a complete new context menu of your own.
Here is a sample implementation of contextMenuHandler, which takes a source object (described above) and the defaultMenu as parameters, and adds a custom action to the default menu when the user ‘right clicks’ the canvas background.
contextMenuHandler(source, defaultMenu) {let customMenu = defaultMenu;if (source.type === "canvas") {customMenu = customMenu.concat({ action: "myAction", label: "My Action" });}return customMenu;}
In addition to adding the context menu item, you would also need to implement the editActionHandler callback to execute the action, like this:
editActionHandler(data) {if (data.editType === "myAction") {// Execute my action code here.}}
editActionHandler
editActionHandler(data, command)
This callback is optional. You don’t need to implement anything for it and it doesn’t return anything. It is called whenever the user does the following types of action on the canvas:
- Clicks a tool in the toolbar.
- Clicks an option in the context menu.
- Presses a key combination on the keyboard to cause the canvas to change.
- Performs some direct manipulation on the canvas such as:
- Creates a node (createNode)
- Moves one or a set of nodes/comments (moveObjects)
- Edits a comment (editComment)
- Links two nodes together (linkNodes)
- Links a comment to a node (linkComment)
- Resizes a supernode (resizeObjects)
- Resizes a comment (editComment)
- Expands a supernode in place (expandSuperNodeInPlace)
- Navigates into a sub-flow in a supernode (displaySubPipeline)
- Navigates out of a sub-flow in a supernode (displayPreviousPipeline)
This callback is called after the common-canvas internal object model has been updated. This callback is provided with two parameters: data
and command
.
data parameter
that looks like this. The data provided can vary depending on the action the user performed.
{editType: "createComment",editSource: "contextmenu",selectedObjects: [],selectedObjectIds: [],offsetX: 100,offsetY: 42}
editType
This is the action that originates from either the toolbar, context menu, keyboard action or direct manipulation on the canvas. If you specified your own action in the context menu or in the toolbar this field will be your action’s name.
editSource
This is the source of the action. It can be set to “toolbar”, “contextmenu”, “keyboard” or “canvas” (for an action caused by direct manipulation on the canvas).
selectedObjects
An array of the currently selected objects.
selectedObjectIds
An array of the IDs of currently selected objects. Included for backward compatibility.
Other fields
Numerous other fields which vary based on the action and the source of the action.
command parameter
This is a Javascript class which is the command object that was added to the command stack and executed to run the action ‘requested’ by the user. If the user performed anundo
action this will be the command that has been undone. If the user performed aredo
action this will be the command that was redone. The command object may contain fields which are connected with the execution of the command.
beforeEditActionHandler
beforeEditActionHandler(data, command)
This callback is optional. You don’t need to implement anything for it but if you do implement it you must return either a data object or null. This callback is called in all the same instances where editActionHandler
is called. The difference is that this callback is called before the internal object model is updated. This gives your application the opportunity to examine the action that is about to be performed and either: let it continue; modify it and let it continue; or cancel it.
This callback is provided with two parameters: data
and command
.
data parameter
this is the same as the data object described foreditActionHandler
(see above)command parameter
typically this will be null but forundo
operations (that is where data.editType === “undo”) this will be the command that is about to be undone. Forredo
operations (that is where data.editType === “redo”) this will be the command that is about to be redone.
This callback must return either the data object that was passed in or null. beforeEditActionHandler
will behave as follows based on what is returned:
- If the data object is returned unmodified: the action will be performed as normal.
- If the data object is returned modified: the action will be performed based on the modified data object. This means your application can alter the behavior of the action that will be performed. For example, you could intercept a createNode command and change the label for the new node in the nodeTemplate to something different. Then when the node is created the new label will be used. It is recommended you be very very careful when updating this object since there is no error checking in common-canvas to ensure you modified the object correctly.
- If null is returned: the action will be cancelled and not performed on the internal object model nor will editActionHandler be called.
If you need to do any asynchronous activity in the beforeEditActionHandler callback you can:
- Return null from the callback - which will cancel the current action
- Do your asynchronous activity
- Then call
CanvasController.editActionHandler(data)
passing in thedata
object as a parameter with the modified properties. This will then execute the action as before. Note: This means thebeforeEditActionHandler
will be called a second time so be sure you put in a check to make sure you don’t get into a loop.
clickActionHandler
clickActionHandler(source)
This callback is provided for your information. You don’t need to implement anything for it and it doesn’t need to return anything to common canvas. It is called whenever the user clicks or double clicks on something on the canvas. You could use this callback to implement opening a properties dialog when the user double clicks a node.
Note: When handling selections, it is recommended the selectionChangeHandler be used in preference to this handler when possible. selectionChangeHandler will notify you of all selection changes regardless of how they occur, such as when the user presses Ctrl+A on the keyboard to select all objects.
At the moment only click/double-click on nodes and the canvas background are returned. It is provided with one parameter that looks like this:
{clickType: "DOUBLE_CLICK"id: "node_1",objectType: "node",selectedObjectIds: ["node_1", "node_2"]}
The fields can be:
clickType
This can be either “SINGLE_CLICK”, “SINGLE_CLICK_CONTEXTMENU” or “DOUBLE_CLICK”
objectType
Can be either “node”, “comment”, “canvas” or “region”. “region” is specified when the user pulls out a selection rectangle around a set of objects that might include nodes and comments.
id
The ID of the node or comment clicked. Only provided when objectType is “node” or “comment”
selectedObjectIds
An array of the selected objects (after the click action was performed).
decorationActionHandler
decorationActionHandler(object, id, pipelineId)
Decorations are small images that can be displayed on or near to your nodes and links. They can be for display only or actionable (so the user can click on them). See the canvas JSON schema for information on how to define decorations for your nodes.
This callback is called when the user clicks on an actionable decoration. You don’t need to implement anything for this callback unless you added actionable decorations to your nodes. It doesn’t return anything. It is called whenever the user clicks on a decoration that you added to a node in the canvas JSON.
It is provided with two parameters:
object
the node or link with which the decoration is associated.
id
the ID of the decoration that you provided in the canvas JSON
pipelineId
the ID of the pipeline for the node.
tipHandler
tipHandler(tipType, data)
This callback is called before tips are shown for: palette items; nodes in the canvas; ports or links. The application can handle the callback and return the content of a tip, overwriting the default content. The common canvas provides default implementations for all of the tips except for links.
- For palette items, the tip contains the category, node type and node description.
- For nodes, the tip contains the name, description and status icon and optionally, if the name was modified from the original name, the original node type.
- For ports, the port label is shown.
- For links, no tip is shown by default.
To overwrite the content, you can return either a string or a React element. If your code returns null for a particular type of tip, common-canvas will display the default tip for that object. For an example of an implementation for a tipHandler, see App.js in the test harness code (https://github.com/elyra-ai/canvas/blob/master/canvas_modules/harness/src/client/App.js).
Before a tip is shown, common-canvas calls the tipHandler with the type of the tip and the data specific to the element.
- Palette items:
- tipType: tipTypePaletteItem (common-constants.TIP_TYPE_PALETTE_ITEM)
- data: JSON object with node template information (nodeTemplate):
{"nodeTemplate": {"label": "C50","description": "C50 Model","operator_id_ref": "com.ibm.commonicons.models.c50","type": "model_node","image": "/images/common_node_icons/models/model_c50.svg","input_ports": [{...}],
- Nodes:
- tipType: tipTypeNode (common-constants.TIP_TYPE_NODE)
- data: JSON object with pipelineId and node information:
{"pipelineId": "153651d6-9b88-423c-b01b-861f12d01489","node": {"id": "idGWRVT47XDV","type": "execution_node","operator_id_ref": "type","output_ports": [...],"input_ports": [...],
- Ports:
- tipType: tipTypePort (common-constants.TIP_TYPE_PORT)
- data: JSON object with pipelineId, node and port information:
{"pipelineId": "153651d6-9b88-423c-b01b-861f12d01489","node": {"id": "idGWRVT47XDV","type": "execution_node","operator_id_ref": "type","output_ports": [{...}],"input_ports": [{...}],"label": "Define Types",
- Links
- tipType: tipTypeLink (common-constants.TIP_TYPE_LINK)
- data: JSON object with pipelineId and link information. The link contains the source node (src) and source port id (srcPortId), as well as the target node (trg) and target port id (trgPortId):
{"pipelineId": "153651d6-9b88-423c-b01b-861f12d01489","link": {"id": "canvas_link_3","x1": 515,"y1": 248,"x2": 611,"y2": 180,"class_name": "canvas-data-link",
idGeneratorHandler
idGeneratorHandler(action, data)
This callback is called when new objects are created in the canvas and allows the user to provide their own method to generate a unique id for the object. If no idGeneratorHandler is set or the method returns null, a UUID is generated by common-canvas. The method is called with a string for the action that was performed (create node/comment/link, clone node/comment/link) and a JSON object with additional data.
- create node:
- action: create_node
- data:
nodeType:{"label":"C50","description":"C50 Model","operator_id_ref":"com.ibm.commonicons.models.c50","type":"model_node","image":"/images/common_node_icons/models/model_c50.svg","input_ports":[{
create comment:
- action: create_comment
- data: n/a
create node link:
- action: create_node_link
- data:
sourceNode: {"id":"6a547456-f1ea-48a6-9721-45d6ae70dd6b","label":"Aggregate","type":"execution_node","operator_id_ref":"com.ibm.commonicons.operations.aggregate","image":"/images/common_node_icons/operations/operation_aggregate.svg","class_name":"d3-node-body","input_ports":[...],"output_ports":[...],
- create comment link:
- action: create_comment_link
- data:
comment: {"id":"8c81aac7-ebe5-4f96-9d63-eabc22b09635","class_name":"d3-comment-rect","content":"","height":42,"width":175,"x_pos":50,"y_pos":50},
- clone node: triggered when copying&pasting a node
- action: clone_node
- data:
node: {"id":"56d30c83-3a08-4147-933e-b01d3c348ac1","label":"Append","type":"execution_node","operator_id_ref":"com.ibm.commonicons.operations.append","image":"/images/common_node_icons/operations/operation_append.svg","class_name":"d3-node-body","input_ports":[...],"output_ports":[...],
- clone comment: triggered when copying&pasting a comment
- action: clone_comment
- data:
comment: {"id":"8c81aac7-ebe5-4f96-9d63-eabc22b09635","class_name":"d3-comment-rect","content":"","height":42,"width":175,"x_pos":50,"y_pos":50}
- clone node link: triggered when copying&pasting two connected nodes
- action: clone_node_link
- data:
link: {"id":"12c4308e-f572-402a-8dd3-604d438539d4","class_name":"d3-data-link","srcNodeId":"2b1af6c2-f98b-4728-97b3-416d40224bce","trgNodeId":"b43fffe6-dc01-4d30-8b6d-abd977850a2e","type":"nodeLink"},sourceNodeId: "56d30c83-3a08-4147-933e-b01d3c348ac1",targetNodeId: "815271f0-f4da-4793-ab8f-c4c32d3dd7e0"
Note that the link to be cloned has references to the original source and target nodes, while the sourceNodeId and targetNodeId are the new node ids for the copied nodes. The new nodes are not part of the model yet.
- clone comment link: triggered when copying&pasting a comment and a node that are connected
- action: clone_comment_link
- data:
link: {"id":"12c4308e-f572-402a-8dd3-604d438539d4","class_name":"d3-comment-link","srcNodeId":"2b1af6c2-f98b-4728-97b3-416d40224bce","trgNodeId":"b43fffe6-dc01-4d30-8b6d-abd977850a2e","type":"commentLink"},sourceNodeId: "56d30c83-3a08-4147-933e-b01d3c348ac1",targetNodeId: "815271f0-f4da-4793-ab8f-c4c32d3dd7e0"
selectionChangeHandler
selectionChangeHandler(data)
The selectionChangeHandler callback is triggered whenever the selection in the canvas changes. The callback contains a JSON object with the following format:
{"selection": ["id6PXRG57DGIV"],"selectedNodes": [{"id": "id6PXRG57DGIV","type": "binding","operator_id_ref": "variablefile",
selection:
Array with ids of selected nodes and commentsselectedNodes
Array of selected node objectsselectedComments
Array of selected comment objectsaddedNodes
Array with node objects that were added to the selectionaddedComments
Array with comment objects that were added to the selectiondeselectedNodes
Array with node objects that were removed from the selectiondeselectedComments
Array with comment objects that were removed from the selectionpreviousPipelineId
The ID of the Pipeline for the selected objects prior to the selection actionselectedPipelineId
The ID of the Pipeline for the newly selected objects
layoutHandler
layoutHandler(data)
This is an optional handler you don’t need to implement anything for it unless you want to. The layoutHandler callback, when provided, is called for each node on the canvas. The callback should return a Javascript object whose properties will override the default properties for node layout. The callback is provided with a parameter data which is the node object. Your code can look at the node properties and decide which properties it needs to override. This can be used to change the node shape, styling and position and size of node elements like the image, main label etc.
For more details see: Customizing Node Layout Properties.