Skip to main content

Drag and drop

A common use case in the sorts of applications for which JsPlumb is useful is the requirement to be able to drag and drop new nodes/groups onto the workspace.

Setup

Vanilla JS
React
Angular
Vue
Svelte

The SurfaceDropManager provides a means for you to configure part of your UI as a palette from which users can drag new nodes/groups onto the canvas.

At the bare minimum, you need to provide these parameters when you create an instance of the SurfaceDropManager:

import { SurfaceDropManager } from "@jsplumbtoolkit/browser-ui"

new SurfaceDropManager({
source:someElement,
selector:"[data-jtk-type]",
surface:renderer
});
  • source The element containing other elements that are draggable
  • selector a CSS3 selector identifying elements within source that are draggable
  • surface The Surface to attach to

source, as mentioned above, identifies the DOM element that contains the draggable elements, and selector identifies the elements within that element that are draggable. It is your responsibility to write out the HTML for source. For example, a simple setup could be:

<div>
<div data-jtk-type="process">Process element</div>
<div data-jtk-type="question">Question element</div>
</div>

Enabling/disabling

Use the setEnabled(enabled:boolean) method on a SurfaceDropManager to enable/disable it.

Providing data for a dragged element

The drop manager uses a dataGenerator function to get a suitable piece of backing data for some element that is being dragged. If you do not provide a dataGenerator, the default generator is used, which gets data values from data-jtk-*** attributes on the element a user is dragging. For instance with this element:

<div data-jtk-type="process" data-jtk-label="Process" data-jtk-width="120" data-jtk-height="80">Process</div>

JsPlumb will prepare this payload for the object representing the drag:

{
"type":"process",
"label":"Process",
"width":"120",
"height":"80"
}
note

The default mechanism used by JsPlumb for determining the type of some object is to test the type member. Providing type as we have here allows JsPlumb to determine which template to use to render the node, how it will behave, etc.

Vanilla JS
React
Angular
Vue
Svelte

You can provide a dataGenerator if you want more control over the dataset that is created:

new SurfaceDropManager({
source:someElement,
selector:"[data-jtk-type]",
surface:renderer,
dataGenerator: (el) => {
return {
label:el.getAttribute("data-jtk-label"),
type:el.getAttribute("data-jtk-type"),
width:el.getAttribute("data-jtk-width"),
height:el.getAttribute("data-jtk-height")
};
}
});

In this example, we provide a data object with label, type, width and height properties - it creates the same dataset as what you'd get from the default data generator. But you can return whatever you like.

Distinguishing between a node and a group

By default, JsPlumb will look for a data-jtk-is-group attribute on a dragged element. If the value of this attribute is "true", JsPlumb will assume the element represents a group. You can provide your own groupIdentifier if you wish; the signature is as shown above.

Specifying the size for a new element

Vanilla JS
React
Angular
Vue
Svelte

By default, the SurfaceDropManager clones the DOM element on which the user started a drag and then sets the size of the element that is being dragged to match the bounding client rectangle of the element that was cloned.

In some situations, particularly when you have a grid in your surface, you may wish to mandate the size for any new elements that are being dragged on to the surface, which you can do by providing a dragSize object:

const dropManager = new SurfaceDropManager({
source:someElement,
selector:"[data-jtk-type]",
surface:renderer,
dragSize:{ w:250, h: 100 }
})

Filtering draggable elements

Vanilla JS
React
Angular
Vue
Svelte

It is possible, when drag starts, to decide whether or not you want the dragged element to be droppable on the canvas:

new SurfaceDropManager({    
surface:SomeSurfaceWidget,
source:someElement,
selector:".draggable-child",
canvasDropFilter:(data:ObjectData):boolean => {
return data.type === "someDroppableOnCanvasType";
}
})

Getting notification of a new vertex

Vanilla JS
React
Angular
Vue
Svelte

You can create a SurfaceDropManager with an onVertexAdded callback, which will be invoked whenever a new vertex has been dropped onto the canvas:

new SurfaceDropManager({    
surface:SomeSurfaceWidget,
source:someElement,
selector:".draggable-child",
onVertexAdded(vertex, dropTarget) => {

}
})

vertex is the new vertex that was added. In the event that the new vertex was dropped on top of some existing node, dropTarget will be provided, containing information about the node onto which the new vertex was dropped, as well as its position and size.

Working with decorators

Vanilla JS
React
Angular
Vue
Svelte

If you have any Decorators in your UI, you may wish to inform the drop manager about the elements they have created, because without doing this the drop manager will not be able to recognise them as background. To do this, you use the canvasSelector option:

new SurfaceDropManager({    
surface:SomeSurfaceWidget,
source:someElement,
selector:".draggable-child",
canvasSelector:".someElementMyDecoratorCreated",
...
})

canvasSelector takes any valid CSS3 selector. This identifies the elements that your decorator has created that the drop manager should treat as background.

CSS

Classes used by the drag/drop new nodes/groups functionality.

ClassDescription
jtk-drag-drop-activeAssigned to all elements that could be the target for some element that is being dragged from a palette
jtk-drag-drop-currentAssigned to an element that is being dragged from a palette
jtk-drag-drop-selected-elementAssigned to the currently selected element in a palette when in tap mode.

In order to use these classes for visual cues in the UI, you'll probably want to define slightly different selectors for each target type. Let's suppose when a drag starts we want to outline our canvas and any nodes/groups with a purple line, and we want to draw any possible target edges with a purple line too:

.jtk-surface.jtk-drag-drop-active, .jtk-node.jtk-drag-drop-active, .jtk-group.jtk-drag-drop-active {
outline:4px solid purple;
}

svg.jtk-drag-drop-active path {
stroke:purple;
}

Now when something is the current drop target, we'll either outline it green or make its path green:

.jtk-surface.jtk-drag-drop-hover, .jtk-node.jtk-drag-drop-hover, .jtk-group.jtk-drag-drop-hover {
outline:4px solid green;
}

svg.jtk-drag-drop-hover path {
stroke:green;
}

This is just an example of course. You can do anything with the CSS that you like.


Options

Vanilla JS
React
Angular
Vue
Svelte

Interface SurfaceDropManagerOptions

NameTypeDescription
allowClickToAdd?booleanWhen in tap mode, allow addition of new vertices simply by clicking, instead of requiring a shape be drawn. (When this is true, the drawing method also still works)
allowDropOnCanvas?booleanDefaults to true. Allows items to be dropped onto whitespace.
allowDropOnEdge?booleanDefaults to true. Allows items to be dropped onto edges in the canvas.
allowDropOnGroup?booleanDefaults to true. Allows items to be dropped onto groups in the canvas.
allowDropOnNode?booleanDefaults to false. Allows items to be dropped onto nodes in the canvas. If this is true and an element is dropped onto a node, the result is the same as if the element has been dropped onto whitespace.
canvasDropFilter?CanvasDropFilterOptional filter to test if objects may be dropped on the canvas. This method is invoked at the start of a drag and is passed the candidate drag object's data.
canvasSelector?stringOptional selector specifying what parts of the surface's canvas should be considered whitespace. If you're using a decorator, for instance, you might want to add a selector for that decorator's elements so that items can be dropped onto them.
dataGenerator?DataGeneratorFunctionOptional function to generate an initial payload from an element that has started to be dragged.
dragSize?SizeOptional size to use for dragged elements.
elementGenerator?Optional function to generate a drag element when the user begins a drag from the drop manager.
groupIdentifier?GroupIdentifierFunctionOptional function to use to determine if the element being dragged represents a group. If you do not provide this, the default behaviour is to check for the presence of a data-jtk-is-group attribute on the element, with a value of true.
ignoreConstrainFunction?booleanIf true, the manager will ignore any element constrain function that may be set on the surface
ignoreGrid?booleanDefaults to false. By default this class will conform to any grid in place in the surface to which it is attached when dragging items around.
ignoreZoom?booleanBy default, the SurfaceDropManager will apply a scale transform to elements that are being dragged so that they appear at the same size as the Surface they're being dragged to. Setting this flag to true will switch off that behaviour.
lassoClass?stringWhen in 'tap' mode, you can optionally provide a class to set on the lasso used by the vertex drawing plugin.
magnetize?booleanIf true, the surface will be instructed to magnetize after dropping a new element.
mode?DropManagerModeMode to operate in - 'drag' or 'tap'. Defaults to 'drag'.
onVertexAdded?OnVertexAddedCallbackOptional callback that will be invoked after a new vertex has been dropped and added to the dataset.
selector?stringA CSS selector identifying children of source that are draggable
sourceElementThe element containing things that will be dragged.
surfaceSurfaceThe surface to attach to.
typeGenerator?TypeGeneratorFunctionOptional function to determine the type of the data object being dragged from an element that has started to be dragged.
vertexPreviewGenerator?When in tap mode, this function can be used to provide the element shown to the user as a new vertex is being drawn.
vertexPreviewUpdater?When in tap mode this function is repeatedly called as the mouse is dragging a new shape. It provides a hook for you to update the element's appearance. This is only called if you provided a vertexPreviewGenerator and it has returned a valid DOM element at the start of a drag.

By default, the SurfaceDropManager is configured to allow nodes/groups to be dropped onto the canvas or an existing edge, and for nodes to be dropped on groups. You can control this via the appropriate allowDropOn*** flags.