Vertex drawing
This plugin provides a means for users to draw new nodes and groups onto the canvas, or into existing groups. When drawing a group users can snag existing vertices on the canvas for inclusion in the new group.
Instantiation
Manual mode
To use this plugin "manually" (discussed below), you declare it in the plugins
section of the render options for some Surface:
import { newInstance,
VertexDrawingPlugin } from "@jsplumbtoolkit/browser-ui"
const toolkit = newInstance()
const surface = toolkit.render(someElement, {
"useModelForSizes": true,
"plugins": [
VertexDrawingPlugin.type
]
});
Note the useModelForSizes:true
declaration: this is recommended, if you intend to let your users drag new vertices via a lasso. There is also a mode in which your users can click on the canvas to add new vertices, and in that mode you may not need to use the model for sizes. This is discussed below
Integrated with ShapeLibraryPalette
To use this plugin with a ShapeLibraryPalette
, you need to instruct the palette to run in "tap" mode:
import { DROP_MANAGER_MODE_TAP, ShapeLibraryPalette } from "@jsplumbtoolkit/browser-ui"
const palette = new ShapeLibraryPalette({
container:paletteDOMElement,
surface,
shapeLibrary,
mode:DROP_MANAGER_MODE_TAP
})
With this setup, the ShapeLibraryPalette
will register a suitable configured instance of the vertex drawing plugin on the surface. It also handles setting the surface into vertexDrawing
mode in order to activate the plugin at the appropriate time.
For a full discussion of dragging/drawing new SVG shapes, please see this page.
Manual usage
With the declaration shown above you get a default setup of the plugin, which will draw groups, of type default
. In order to switch on the plugin, you have to set the appropriate mode in the surface:
import { SURFACE_MODE_VERTEX_DRAWING } from "@jsplumbtoolkit/browser-ui"
mySurface.setMode(SURFACE_MODE_VERTEX_DRAWING)
In this first example we've declared a vertex drawing plugin and then made the call shown above to activate the plugin. Try clicking and dragging on the canvas below - the plugin will add new groups. You can also draw groups inside other groups.
We setup the surface using the code snippet shown above - with the default options on the vertex drawing plugin, but also with useModelForSizes
switched on:
import { newInstance,
VertexDrawingPlugin } from "@jsplumbtoolkit/browser-ui"
const toolkit = newInstance()
const surface = toolkit.render(someElement, {
"useModelForSizes": true,
"plugins": [
VertexDrawingPlugin.type
]
});
For comparison, here's the same setup but without useModelForSizes:true
. Notice how the groups you add always end up the same size, regardless of the size you draw them:
Specifying vertex details
The default setup of this plugin will always create new groups, and those groups will have type
of default
. For a more fine-grained control of the plugin, you have a few options.
Specifying group type
You can instruct the plugin to use a specific group type via the groupType
parameter:
import { newInstance,
VertexDrawingPlugin } from "@jsplumbtoolkit/browser-ui"
const toolkit = newInstance()
const surface = toolkit.render(someElement, {
"useModelForSizes": true,
"plugins": [
{
"type": VertexDrawingPlugin.type,
"options": {
"groupType": "userDrawn"
}
}
],
"view": {
"groups": {
"default": {
"template": "<div style=\"background-color:orangered\"/>"
},
"userDrawn": {
"template": "<div style=\"background-color:cadetblue\"/>"
}
}
}
});
In this example we've added a couple of groups manually, whose type is "default", and so, per the view above, they are drawn with an orange-red box. But if you drag new groups in this canvas, their type will be userDrawn
, resulting in a cadetblue
box (one of my favourite default HTML colors).
Drawing nodes
To draw nodes instead of groups, supply a nodeType
parameter to the plugin:
import { newInstance,
VertexDrawingPlugin } from "@jsplumbtoolkit/browser-ui"
const toolkit = newInstance()
const surface = toolkit.render(someElement, {
"useModelForSizes": true,
"plugins": [
{
"type": VertexDrawingPlugin.type,
"options": {
"nodeType": "userDrawn"
}
}
],
"view": {
"groups": {
"default": {
"template": "<div style=\"background-color:orangered\"/>"
},
"userDrawn": {
"template": "<div style=\"background-color:cadetblue\"/>"
}
},
"nodes": {
"default": {
"template": "<div style=\"background-color:forestgreen\"/>"
},
"userDrawn": {
"template": "<div style=\"background-color:darkmagenta\"/>"
}
}
}
});
For the sake of clarity here we've declared a default
and also a userDrawn
type for groups and for nodes. On the canvas we've added a group and a node, both of type default
. But if you now draw a new vertex on here, the nodeType
parameter set on the plugin will instruct the plugin to draw nodes, of type userDrawn
, in this case. So you'll see a box that is coloured darkmagenta
when you draw:
Choosing type at runtime
A common use case for this plugin is to draw vertices of some type that a user has selected in the UI (that's the case for the ShapeLibraryPalette
, for instance, which is discussed below). To support this, instead of supply a hard-coded groupType
or nodeType
, you can supply a typeGenerator
, which allows you to not only choose the type of vertex to create, it allows you to choose between a node or a group, and also to provide an initial payload. The signature for the typeGenerator
function is:
export type VertexDrawingPluginTypeGenerator = (origin:PointXY, e:MouseEvent) => {objectType:typeof Node.objectType | typeof Group.objectType, type:string, data?:ObjectData}
So it is given the origin
of the event (in canvas coordinates) and the originating mouse event, and is expected to return an object of this type:
{
type:string,
objectType:"Group"|"Node",
data?:ObjectData
}
Here we have the same example as just above, but in this case we've added radio button to allow you to specify what type of object to draw. Remember, groups are cadetblue
and nodes are darkmagenta
.
Our plugin spec looks like this:
plugins:[
{
type:tk.VertexDrawingPlugin.type,
options:{
typeGenerator:(origin, evt) => {
return {
objectType:document.querySelector("[type='radio']:checked").value,
type:"userDrawn"
}
}
}
}
]
...we return the value of the currently checked radio button. How you go about determining this value will depend on your app. Notice also that here we've hard-coded the type
, but that is also, of course, something you can derive dynamically.
Clicking to add vertices
All of the preceding examples have been focused on the user dragging an area on the screen to draw new vertices, but it is also possible to allow "click to add":
plugins:[
{
type:tk.VertexDrawingPlugin.type,
options:{
typeGenerator:(origin, evt) => {
return {
objectType:document.querySelector("[type='radio']:checked").value,
type:"userDrawn"
},
allowClickToAdd:true
}
}
}
]
When you have this enabled, the plugin will use a default size for nodes of 80 pixels width and height, and for groups of 250 pixels width and height. You can set those values:
plugins:[
{
type:tk.VertexDrawingPlugin.type,
options:{
typeGenerator:(origin, evt) => {
return {
objectType:document.querySelector("[type='radio']:checked").value,
type:"userDrawn",
defaultNodeSize:{w:100, h:80},
defaultGroupSize:{w:500, h:350}
},
allowClickToAdd:true
}
}
}
]
Advanced usage
Providing a vertex preview element
By default, the vertex drawing plugin uses a [lasso](plugins-overview#lasso)
to draw an area on the canvas that the new vertex will occupy. You can, though, provide a function that will generate an element at the start of a drag to be drawn as a child of the lasso:
vertexPreviewGenerator?:(p:PointXY, e:MouseEvent) => BrowserElement
This is the code for the example above:
plugins:[
{
type:tk.VertexDrawingPlugin.type,
options:{
vertexPreviewGenerator:(p, e) => {
const div = document.createElement("div")
div.style.position="absolute"
div.style.width="100%"
div.style.height="100%"
div.style.display="flex"
div.style.alignItems="center"
const d = document.createElement("marquee")
d.style.width="100%"
d.setAttribute("scrollamount", 15)
d.innerHTML = "I am drawing a new element"
div.appendChild(d)
return div
}
}
}
]
It's almost certain if you use this functionality you'll do something more sensible with it. The ShapeLibraryPalette
, for example, draws the current selected shape into an element and returns that.
Updating the preview element
Depending on what the HTML for your preview element looks like, you may need to update it as the user drags. In our example above we did not; we used some HTML elements and had CSS take care of the positioning for us. But in some cases - particularly if working with SVG - you might want to update the element. For that you can provide a vertexPreviewUpdater
:
vertexPreviewUpdater?:(p:PointXY, size:Size, el:BrowserElement) => any
Try dragging a new group here:
For this we provided a vertexPreviewGenerator
and also a vertexPreviewUpdater
:
plugins:[
{
type:tk.VertexDrawingPlugin.type,
options:{
vertexPreviewGenerator:(p, e) => {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect")
rect.setAttribute("fill", "#567567")
rect.setAttribute("x", "0")
rect.setAttribute("y", "0")
rect.setAttribute("width", "50%")
rect.setAttribute("height", "50%")
svg.appendChild(rect)
const rect2 = document.createElementNS("http://www.w3.org/2000/svg", "rect")
rect2.setAttribute("fill", "#984267")
rect2.setAttribute("x", "50%")
rect2.setAttribute("y", "50%")
rect2.setAttribute("width", "50%")
rect2.setAttribute("height", "50%")
svg.appendChild(rect2)
return svg
},
vertexPreviewUpdater:(p, size, el) => {
el.setAttribute("width", size.w)
el.setAttribute("height", size.h)
}
}
}
]
The preview element is an SVG element, and the updater sets its width and height. It could probably be argued, in fact, that this case could also have been handled via CSS, but there are internal use cases in JsPlumb that cannot, and so there may well be use cases in your app that cannot either.
Usage with ShapeLibraryPalette
The ShapeLibraryPalette
can integrate with a vertex drawing plugin. The way to switch this on is to set the mode
flag on the palette to DROP_MANAGER_MODE_TAP
(or "tap", if you just want to use a string):
import { DROP_MANAGER_MODE_TAP, ShapeLibraryPalette, ShapeLibraryImpl } from "@jsplumbtoolkit/browser-ui"
const shapeLibrary = new ShapeLibraryImpl(...)
new ShapeLibraryPalette({
container:document.querySelector(".node-palette"),
surface,
mode:DROP_MANAGER_MODE_TAP,
shapeLibrary,
dataGenerator:(el) => {
return {
name:el.getAttribute("data-type"),
details:""
}
}
})
The palette will now create a vertex drawing plugin and attach it to the surface. The palette takes advantage of several of the capabilities discussed above:
-
A
typeGenerator
is provided to the plugin which returns theobjectType
andtype
of the currently selected item in the palette. Additionally, anydataGenerator
set on the palette will be invoked and used to prepare an initial dataset for a new vertex. For those of you who have used a shape library palette (or theSurfaceDropManager
) before with JsPlumb will be familiar with this function. -
A
vertexPreviewGenerator
is provided, which draws a copy of the currently selected element (which is SVG). -
A
vertexPreviewUpdated
is provided, which redraws the SVG according to the new dimensions.
Try clicking an element in the palette here and dragging new nodes on the canvas:
Grids
When the associated surface has a grid, this plugin will constrain node/group sizes so that the dimensions of new vertices are a multiple of the grid in each axis.
CSS
Options
Interface VertexDrawingPluginOptions
Name | Type | Description |
---|---|---|
allowClickToAdd? | boolean | Defaults to false. When true, users can click on the canvas to "draw" a new element, and that element's size will use the default |
autoExit? | boolean | When true (which is the default) the lasso exits after a group has been drawn |
defaultGroupSize? | Size | Optional default size for groups, used when allowClickToAdd is set to true. Defaults to {w:250, h:250} |
defaultNodeSize? | Size | Optional default size for nodes, used when allowClickToAdd is set to true. Defaults to {w:80, h:80} |
groupType? | string | The type identifier for new groups. If you have also provided a typeGenerator this will be ignored. If you have not provided a typeGenerator but you have provided a nodeType, this will be ignored. |
invert? | boolean | Defaults to false, meaning the lasso is drawn as a rectangle. If true, the lasso is drawn as a set of masks, with the lasso area drawn as a "hole" in the masks. |
lassoClass? | string | Optional extra class(es) to set on the lasso used for drawing. |
minSize? | Size | The minimum size for vertices. If your user draws a vertex smaller than this it will be discarded. Defaults to {w:50, h:50} |
nodeType? | string | The type identifier for new nodes. If you have also provided a typeGenerator this will be ignored. |
typeGenerator? | VertexDrawingPluginTypeGenerator | Optional function that will be invoked when a new drag commences, and which is responsible for returning the type of object to add (Node or Group), as well as that object's own type, and, optionally, an initial payload for the new object. |
vertexPreviewGenerator? | A function that will be invoked at the start of a drawing operation, and can return an element to draw into the lasso. This is an optional function; if you don't provide it JsPlumb will draw a basic SVG rectangle to indicate the drawing area. | |
vertexPreviewUpdater? | A function that is invoked during mouse drag, to be used in conjunction with vertexPreviewGenerator . This function gives you
a hook to use to make any changes to your preview element, should you need to. This is optional. |