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
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"
}
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.
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
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
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
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
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.
Class | Description |
---|---|
jtk-drag-drop-active | Assigned to all elements that could be the target for some element that is being dragged from a palette |
jtk-drag-drop-current | Assigned to an element that is being dragged from a palette |
jtk-drag-drop-selected-element | Assigned 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
Interface SurfaceDropManagerOptions
Name | Type | Description |
---|---|---|
allowClickToAdd? | boolean | When 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? | boolean | Defaults to true. Allows items to be dropped onto whitespace. |
allowDropOnEdge? | boolean | Defaults to true. Allows items to be dropped onto edges in the canvas. |
allowDropOnGroup? | boolean | Defaults to true. Allows items to be dropped onto groups in the canvas. |
allowDropOnNode? | boolean | Defaults 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? | CanvasDropFilter | Optional 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? | string | Optional 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? | DataGeneratorFunction | Optional function to generate an initial payload from an element that has started to be dragged. |
dragSize? | Size | Optional 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? | GroupIdentifierFunction | Optional 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? | boolean | If true, the manager will ignore any element constrain function that may be set on the surface |
ignoreGrid? | boolean | Defaults 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? | boolean | By 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? | string | When in 'tap' mode, you can optionally provide a class to set on the lasso used by the vertex drawing plugin. |
magnetize? | boolean | If true, the surface will be instructed to magnetize after dropping a new element. |
mode? | DropManagerMode | Mode to operate in - 'drag' or 'tap'. Defaults to 'drag'. |
onVertexAdded? | OnVertexAddedCallback | Optional callback that will be invoked after a new vertex has been dropped and added to the dataset. |
selector? | string | A CSS selector identifying children of source that are draggable |
source | Element | The element containing things that will be dragged. |
surface | Surface | The surface to attach to. |
typeGenerator? | TypeGeneratorFunction | Optional 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.