Skip to main content

Shape tools

Element tools​

JointJS allows creating fully customizable user interaction tools for your elements. These tools show up on user interaction (e.g. mouseover) with an element view and allow the user to interact with the underlying element model. For example:

The process of getting element tools up and running on your element view is relatively straightforward:

We will explain every step in turn. In this section we will also touch on creating custom buttons.

Create an element tool​

An element tool (type dia.ToolView) is a view that renders a certain type of control elements on top of the element view it is attached to; for example, the Boundary tool renders an outline around the bounding box of the element.

The JointJS library comes with a collection of pre-made element tool definitions in the elementTools namespace. The pre-made element tools include several common tools such as button, control, etc.

To create a new element tool, we call its constructor:

import { elementTools } from '@joint/core';

const boundaryTool = new elementTools.Boundary();

The element tool constructors also accept optional arguments that modify the appearance and function of the created tools:

import { elementTools } from '@joint/core';

const boundaryTool = new elementTools.Boundary({
padding: 20,
rotate: true,
useModelGeometry: true,
});

Add element tools to tools view​

Element tools always need to come bundled in a tools view object (type dia.ToolsView). This allows them to be shown/hidden as a group above an element. We create a new tools view and add our tools to it:

import { dia, elementTools } from '@joint/core';

const boundaryTool = new joint.elementTools.Boundary();
const removeButton = new joint.elementTools.Remove();

const toolsView = new joint.dia.ToolsView({
tools: [
boundaryTool,
removeButton
]
});

Remember, it is necessary to create a new set of tools for every new tools view object we create; tools are automatically reassigned to the last tools view that uses them.

Add tools view to element view​

Finally, we need to add our tools view to an element view. The dia.ElementView class comes with a suite of tools-related methods:

We can thus show all of our tools in one place:

const elementView = element.findView(paper);
elementView.addTools(toolsView);

Remember, it is necessary to create a new tools view object for every element view; tools view objects are automatically reassigned to the last element view that uses them.

Make element tools interactive​

You can easily toggle visibility of element tools using the 'element:mouseenter'/'element:mouseleave' Paper events:

paper.on('element:mouseenter', elementView => {
elementView.showTools();
});

paper.on('element:mouseleave', elementView => {
elementView.hideTools();
});

More complex interaction scenarios might require showing, hiding or removing all tools at once. You can find relevant functions in the Paper class:

Note that our example toggles visibility of element tools by using the showTools()/hideTools() functions. Element tools may also be toggled by addTools()/removeTools() functions, which are demonstrated in the link tools section. For element tools, there is no practical difference between the two approaches.

Custom element buttons​

It is possible to create custom buttons to complement the built-in Remove button tool; JointJS exposes the elementTools.Button class for you to extend. The markup of the new button can be sent as options.markup, while the behavior of the button on pointerdown interaction is determined by the callback function provided in options.action.

You can add the extended button to the elementTools namespace and then just use that class in the code:

import { dia, elementTools } from '@joint/core';

elementTools.InfoButton = elementTools.Button.extend({
name: 'info-button',
options: {
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}],
x: '100%',
y: '100%'
offset: {
x: 0,
y: 0
},
rotate: true,
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
}
}
});

const infoButton = new elementTools.InfoButton();
const toolsView = new dia.ToolsView({
tools: [infoButton]
});

const elementView = element.findView(paper);
elementView.addTools(toolsView);

A single-use custom button can also be created by direct reference to the Button class, without making an entry in the elementTools namespace:

import { dia, elementTools } from '@joint/core';

const infoButton = new elementTools.Button({
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}],
x: '100%',
y: '100%',
offset: {
x: 0,
y: 0
},
rotate: true,
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
}
});

const toolsView = new dia.ToolsView({
tools: [infoButton]
});

const elementView = element.findView(paper);
elementView.addTools(toolsView);

JointJS allows creating fully customizable user interaction tools for your links. These tools show up on user interaction (e.g. mouseover) with a link view and allow the user to interact with the underlying link model. For example:

The process of getting link tools up and running on your link view is relatively straightforward:

We will explain every step in turn. We will also touch on creating custom buttons.

A link tool (type dia.ToolView) is a view that renders a certain type of control elements on top of the link view it is attached to; for example, the Vertices tool creates an interactive handle above every vertex (these handles then allow the user to move and/or delete each vertex).

The JointJS library comes with a collection of pre-made link tool definitions in the linkTools namespace. The pre-made link tools include:

  • Vertices - adds handles above link vertices.
  • Segments - adds handles above link segments.
  • SourceArrowhead - adds a handle above link source connection point.
  • TargetArrowhead - adds a handle above link target connection point.
  • SourceAnchor - adds a handle above link source anchor.
  • TargetAnchor - adds a handle above link target anchor.
  • Boundary - shows the bounding box of the link.
  • Remove - adds an interactive remove button.

To create a new link tool, we call its constructor:

import { linkTools } from '@joint/core';

const verticesTool = new linkTools.Vertices();

The link tool constructors also accept optional arguments that modify the appearance and function of the created tools:

import { linkTools } from '@joint/core';

const verticesTool = new linkTools.Vertices({
redundancyRemoval: false,
snapRadius: 10,
vertexAdding: false,
});

Link tools always need to come bundled in a tools view object (type dia.ToolsView). This allows them to be shown/hidden as a group above a link. We create a new tools view and add our tools to it:

const verticesTool = new linkTools.Vertices();
const segmentsTool = new linkTools.Segments();
const sourceArrowheadTool = new linkTools.SourceArrowhead();
const targetArrowheadTool = new linkTools.TargetArrowhead();
const sourceAnchorTool = new linkTools.SourceAnchor();
const targetAnchorTool = new linkTools.TargetAnchor();
const boundaryTool = new linkTools.Boundary();
const removeButton = new linkTools.Remove();

const toolsView = new dia.ToolsView({
tools: [
verticesTool, segmentsTool,
sourceArrowheadTool, targetArrowheadTool,
sourceAnchorTool, targetAnchorTool,
boundaryTool, removeButton
]
});

Remember, it is necessary to create a new set of tools for every new tools view object we create; tools are automatically reassigned to the last tools view that uses them.

Finally, we need to add our tools view to a link view. The dia.LinkView class comes with a suite of tools-related methods:

We can thus show all of our tools in one place:

Try playing around with the user controls. Note that redundant vertices are removed whenever they appear; new vertices can be added by clicking anywhere on the link. Double-clicking a vertex removes it. The disconnected handles are link anchors; when moved, they stay within the bounds of their respective end elements.

const linkView = link.findView(paper);
linkView.addTools(toolsView);

Remember, it is necessary to create a new tools view object for every link view; tools view objects are automatically reassigned to the last link view that uses them.

You can easily toggle link tools using the 'link:mouseenter'/'link:mouseleave' Paper events:

paper.on('link:mouseenter', function(linkView) {
linkView.addTools(toolsView);
});

paper.on('link:mouseleave', function(linkView) {
linkView.removeTools();
});

More complex interaction scenarios might require showing, hiding or removing all tools at once. You can find relevant functions in the Paper class:

These functions can also help to make anchor handles (which usually lie outside the path but inside an element) more accessible to users - by removing link tools only if the mouse pointer enters a blank area of the paper:

paper.on('link:mouseenter', function(linkView) {
linkView.addTools(toolsView);
});

paper.on('blank:mouseover', function() {
paper.removeTools();
});

Note that our example toggles visibility of link tools by using the addTools()/removeTools() functions, and not showTools()/hideTools() which we have seen in the element tools tutorial. This is necessary because the tools view in our tutorial includes the SourceArrowhead and TargetArrowhead tools, which may cause the link tools view to not be hidden as expected when the link is reconnected to a topic. Tools views without those two tools may use the showTools()/hideTools() approach illustrated in the element tools tutorial without issue.

It is possible to create custom buttons to complement the pre-made Remove button tool; JointJS exposes the linkTools.Button class for you to extend. The markup of the new button can be sent as options.markup, while the behavior of the button on pointerdown interaction is determined by the callback function provided in options.action.

You can add the extended button to the linkTools namespace and then just use that class in the code:

import { dia, linkTools } from '@joint/core';

linkTools.InfoButton = linkTools.Button.extend({
name: 'info-button',
options: {
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}],
distance: 60,
offset: 0,
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
}
}
});

const infoButton = new linkTools.InfoButton();
const toolsView = new dia.ToolsView({
tools: [infoButton]
});

const linkView = link.findView(paper);
linkView.addTools(toolsView);

A single-use custom button can also be created by direct reference to the Button class, without making an entry in the linkTools namespace:

import { dia, linkTools } from '@joint/core';

const infoButton = new linkTools.Button({
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}],
distance: 60,
offset: 0,
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
}
});

const toolsView = new dia.ToolsView({
tools: [infoButton]
});

const linkView = link.findView(paper);
linkView.addTools(toolsView);