Edge path editors
The Toolkit supports editing the path of edges that use the Orthogonal, Bezier, StateMachine or Segmented connectors.
Setup
To enable path editing, you need to create an EdgePathEditor
:
import { EdgePathEditor } from "@jsplumbtoolkit/browser-ui"
const surface = someToolkit.render(...)
const pathEditor = new EdgePathEditor(surface)
Once you have a path editor, when you want to use it you need to call startEditing
on it and pass it some edge. One common way to do this is by responding to tap or click event on an edge:
const surface = someToolkit.render(someElement, {
...
view:{
edges:{
default:{
events:{
[EVENT_CLICK]:(p) => {
pathEditor.startEditing(p.edge)
}
}
}
}
}
})
The editor will now stay active until you subsequently call stopEditing()
on it. Another common setup for this is to do so in response to a click on a surface canvas (ie. a click on whitespace):
const surface = someToolkit.render(someElement, {
...
view:{
edges:{
default:{
events:{
[EVENT_CLICK]:(p) => {
pathEditor.startEditing(p.edge)
}
}
}
}
},
events:{
[EVENT_CANVAS_CLICK]:() => {
pathEditor.stopEditing()
}
}
})
Orthogonal editors
The orthogonal editor draws a handle on each segment of an edge, which can be dragged at 90 degrees to the direction of travel of the segment, ie. for a vertical segment, you can shift it horizontally, and for a horizontal segment you can shift it vertically.
When you are shifting a segment, if you release the mouse at such a point that the segment you just dragged forms a straight line with a previous or subsequent segment, the segments are coalesced into one. If you drag some segment such that an existing segment ceases to be a straight line, the segment is split and new segment is inserted between them.
Static anchors
In this example we use an edge editor to edit the path of some edge whose anchors are at fixed points (in this case, AnchorLocations.Bottom
and AnchorLocations.Top
). We've also already called startEditing(..)
for you:
Dynamic anchors
If the edge you are editing has dynamic anchors, the edge editor will draw a placeholder at each end of the edge, which you can drag around to any supported position for the given anchor - here we use the AutoDefault
dynamic anchor, which is an anchor that has one position on each of the four sides of the element on which it resides:
Continuous anchors
If your edge is using anchor of type AnchorLocations.Continuous
, when you drag an anchor placeholder the Toolkit will highlight the candidate face for the anchor relocation:
User specified anchor positions
If you have specified anchorPositions
for some given vertex in your view, eg:
nodes:{
default:{
template:`<div>{{id}}</div>`,
anchorPositions:[
{ x:0, y:0.5, ox:-1, oy:0, id:"left" },
{ x:1, y:0.5, ox:1, oy:0, id:"right" },
{ x:0.5, y:0, ox:0, oy:-1, id:"top" },
{ x:0.5, y:1, ox:0, oy:1, id:"bottom" }
]
}
}
...then the path editor will find these when you call startEditing()
, and offer the ability to drag the end points of the edge to each of the available positions:
Segmented editors
The segmented editor draws a handle at the end of each segment of an edge, which can be dragged in any direction to alter its location. In addition, each segment is assigned a "split" button, allowing the user to split the segment into two, and, when the connector has more than one segment, each segment is assigned a delete button.
Static anchors
In this example we use an edge editor to edit the path of some edge whose anchors are at fixed points (in this case, AnchorLocations.Bottom
and AnchorLocations.Top
). We've also already called startEditing(..)
for you:
Dynamic anchors
If the edge you are editing has dynamic anchors, the edge editor will draw a placeholder at each end of the edge, which you can drag around to any supported position for the given anchor - here we use the AutoDefault
dynamic anchor, which is an anchor that has one position on each of the four sides of the element on which it resides:
Continuous anchors
If your edge is using anchor of type AnchorLocations.Continuous
, when you drag an anchor placeholder the Toolkit will highlight the candidate face for the anchor relocation:
User specified anchor positions
If you have specified anchorPositions
for some given vertex in your view, eg:
nodes:{
default:{
template:`<div>{{id}}</div>`,
anchorPositions:[
{ x:0, y:0.5, ox:-1, oy:0, id:"left" },
{ x:1, y:0.5, ox:1, oy:0, id:"right" },
{ x:0.5, y:0, ox:0, oy:-1, id:"top" },
{ x:0.5, y:1, ox:0, oy:1, id:"bottom" }
]
}
}
...then the path editor will find these when you call startEditing()
, and offer the ability to drag the end points of the edge to each of the available positions:
Smoothed connectors
When you have smooth:true
set on your connector, the editor functions slightly differently - the drag handles now represent the location of the control points for the splines that make up the Bezier curve, and when you drag them, the control points are moved accordingly. Also when smooth
is set, the editor draws a guideline for each segment.
Bezier editors
The bezier editor draws two handles - one for each control point in the connector. They are placed where the control point is, and as you drag them around the control points are updated accordingly.
Static anchors
In this example we use an edge editor to edit the path of some edge whose anchors are at fixed points (in this case, AnchorLocations.Bottom
and AnchorLocations.Top
). We've also already called startEditing(..)
for you:
StateMachine editors
The StateMachine editor draws one handle, located at the connector's control point (the StateMachine connector is a quadratic Bézier curve, with a single control point). As you drag the handle around, the connector's control point is updated.
Overlays
You can supply a set of overlays to render on an edge for the duration of the edit, for example:
pathEditor.startEditing(someEdge, {
overlays:[
{
type:LabelOverlay.type,
options:{
label:"editing...",
location:0.1
}
}
]
})
With this call we get a label overlay at location 0.1:
Delete button
The edge path editor offers a shortcut method to attach a delete button:
pathEditor.startEditing(someEdge, {
deleteButton:true
})
This results in:
This will delete the edge without prompting the user. If you'd like to hook into the edge deletion, you can provide an onMaybeDelete
function.
pathEditor.startEditing(someEdge, {
deleteButton:true,
onMaybeDelete:(edge, connection, doDelete) => {
if (confirm(`Delete edge ${edge.id}`)) {
doDelete()
}
}
})
Active Mode
When you edit an edge with a path editor, the changes are persisted to the edge as its geometry
. Additionally, via the concept of
activeMode`, the editor will listen for new edges added via the mouse, and whenever a new edge is added it is instantly marked edited and its geometry is persisted.
Since version 6.2.11 this functionality is switched on by default. To switch this off you need to provide an argument to the EdgePathEditor
constructor:
const pathEditor = new EdgePathEditor(surface, { activeMode:false })
When activeMode
is switched off, an edge that has not been edited does not have a geometry
set, and so when you export the dataset with unedited edges, their geometry
is not exported, meaning if you subsequently reload the dataset you may not see the same edge paths as when you saved. It is for this reason that we decided to switch on activeMode
by default: it seems more intuitive.
CSS classes
The Toolkit ships with a stylesheet - css/jsplumbtoolkit-connector-editors.css
- containing default values for these styles used by the edge path editors:
Class | Assigned To | Purpose |
---|---|---|
jtk-orthogonal-handle | Segment drag handles in orthogonal editor | Segments can be dragged by these handles |
jtk-orthogonal-segment-drag | Segment drag handles in orthogonal editor | Segments can be dragged by these handles |
jtk-orthogonal-segment-drag-ns | Segment drag handles on horizontal segments | Indicates a segment can be dragged in the Y axis |
jtk-orthogonal-segment-drag-ew | Segment drag handles on vertical segments | Indicates a segment can be dragged in the X axis |
jtk-bezier-handle | Control point drag handles in bezier editor | Control points can be dragged by these handles |
jtk-bezier-handle-control-point | Control point drag handles in bezier editor | Control points can be dragged by these handles |
jtk-bezier-handle-control-point-1 | First control point handle, bezier editor only | In the StateMachine editor this is not assigned |
jtk-bezier-handle-control-point-2 | Second control point handle, bezier editor only | In the StateMachine editor this is not assigned |
jtk-bezier-guideline | Guidelines in the Bezier/StateMachine editors | |
jtk-anchor-placeholder | Anchor drag placeholders | Added to the element placed at the location of an anchor for a connection that is being edited. This applies to both dynamic and continuous anchors |
jtk-anchor-candidate | Drag candidates for anchors | Added to the elements drawn to show where a certain anchor may be relocated to (for dynamic anchors) |
jtk-edge-delete | Delete button | Assigned to the optional delete button overlay that the edge path editor can add |
In addition to these classes, when an anchor is Continuous
, the edge path editor uses an attribute on the anchor's element to indicate the current face of the element on which a dragged anchor would be located:
[jtk-anchor-face='left'] {
border-left:4px solid gold !important;
}
[jtk-anchor-face='top'] {
border-top:4px solid gold !important;
}
[jtk-anchor-face='right'] {
border-right:4px solid gold !important;
}
[jtk-anchor-face='bottom'] {
border-bottom:4px solid gold !important;
}