Skip to main content

· 2 min read
Simon Porritt

What's new?

In this release we've made a small tweak to the way we render custom overlays which supports automatically updating their orientation based upon the endpoints of their edge. Let's say we want to put this SVG at each end of our edges:



Since we want this at the start and at the end we'll write a factory to create a spec for a custom overlay:
function customOverlayFactory(location, color) {
return {
type:"Custom",
options:{
create:function(component) {
const d = document.createElement("div")
d.className="rotating-custom-overlay"
d.style.width = "16px"
d.style.height = "20px"
d.innerHTML = `
<svg width="100%" height="100%" viewBox="0 0 16 21">
<rect x="0" y="0" fill="${color}" width="10" height="21"/>
<rect x="10" y="7" fill="${color}" width="6" height="7"/>
</svg>`
return d
},
location:location
}
}
}

Then in the edges section of our view we'll add two of these:

view:{
edges:{
default:{
overlays:[
customOverlayFactory(0, "orangered"),
customOverlayFactory(1, "cadetblue"),
]
}
}
}

And this is the result:

CSS Rules

Part of this is achieved via CSS:

.rotating-custom-overlay[data-anchor-oy='1'] {
transform: translate(-2px, 6.5px) rotate(90deg);
}

.rotating-custom-overlay[data-anchor-oy='-1'] {
transform: translate(-8.5px, -18px) rotate(270deg);
}

.rotating-custom-overlay[data-anchor-ox='1'] {
transform: translate(0px, -11.5px) rotate(0deg);
}

.rotating-custom-overlay[data-anchor-ox='-1'] {
transform: translate(-15px, -8.5px) rotate(180deg);
}

What's next?

This adds a nice new capability to the Toolkit, and we're looking forward to seeing what you do with it. It also links nicely with a concept that we're preparing to introduce in 7.x - that of markers. These will be the natural evolution of overlays, and over the next few months we'll be sharing more information about what they are, how to use them, and why we think they're the future.


Read more

There's a full discussion of customer overlays in our documentation: https://docs.jsplumbtoolkit.com/toolkit/6.x/lib/overlays#custom


Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.

· 10 min read
Simon Porritt

The jsPlumb Toolkit uses individual SVG elements to render edges and individual SVG/HTML elements to render nodes, which is a different approach to other libraries in this space. In this post we're going to discuss the reasons why we do this, give a little historical background, and also share our vision for the near future.

jsPlumb does not use a single SVG context element because we do not consider jsPlumb to be limited to being strictly a diagramming library. jsPlumb's approach to rendering - allowing you to use any HTML or SVG to render your nodes - means that it is capable of building a whole other class of applications that many other libraries in this space cannot, or at least not in a straightforward and maintainable way.

Where it started

The original Community edition of jsPlumb, before it was even known as the Community edition, was released in 2009, at a time when the browser landscape was markedly different from what it is now. Our first release used an HTML canvas for each edge, inspired by the majestic Yahoo Pipes application, beloved of Ajaxians everywhere. But we wanted to support IE6, because it still had a large market share, and IE6 had no canvas or SVG support. What it did have, though, was VML. VML elements behave in a page like canvas elements do: there is no notion of a top level container to group them all, like you can have with SVG.

Since our renderer needed to be written in such a way that the knowledge of canvas vs VML vs SVG was the very last consideration, we standardised on the approach of a separate element for each edge, positioned individually.

Where it's at

Element dragging

In this little snippet we've painted an outline on each edge's SVG element. As you drag the nodes around you can see the way the SVG elements are resized and repositioned:

Do you notice how the node you are dragging turns green and is always positioned above the other nodes in the display? That's because of this CSS rule:

.jtk-show-outline-demo.jtk-drag {
background-color: forestgreen;
z-index:11;
}

We render all of our nodes with the .jtk-show-outline-demo class. When a user starts to drag a node, the Toolkit assigns the .jtk-drag class, and removes it on drag stop. For us it means that with a simple CSS rule we can implement a useful piece of UX.

This is not easily done when rendering to a single SVG context element. If you render all of your nodes and edges inside a single SVG element, z index is defined only implicitly - by the index of each child element inside the parent. The further down in the SVG's list of child nodes an element is, the greater the implicit z index. The true power of UIs inside the browser comes from a combination of markup, javascript and CSS, and a single SVG context removes some of that power.

Rich UIs

The Toolkit's approach excels in the complexity of the elements you can use to render your nodes. The Toolkit allows you to use any HTML or SVG you wish to - you are not restricted to using SVG - and it will automatically respond to the computed size of your nodes. This greatly expands the range of applications that can be built with jsPlumb when compared with other libraries in this space. jsPlumb is not just a diagramming library. jsPlumb is an application building library.

HTML vs SVG rendering

Every now and then we come across misunderstandings such as this, regarding "HTML" node rendering:

Furthermore, relying on HTML for node rendering has consequences for the types of shapes that can be represented. Whereas rectangles are supported out-of-the-box, anything more complicated (e.g. polygons, ellipses or custom paths) is much more difficult to support than in SVG-based libraries.

which is not the case. Consider this HTML:

<div class="someClass">
<h1>Title</h1>
<input placeholder="enter a value here"/>
<svg width="300px" height="300px">
<rect x="50" y="50" width="100" height="70" fill="orangered" stroke-width="1" stroke="yellow"/>
<path d="M 10 10 L 30 200 L 250 150 L 300 300" stroke-width="2" stroke="green" fill="none"></path>
</svg>
</div>

...it has SVG embedded in it:

There is no such thing as HTML versus SVG for node rendering. Choosing HTML means you also get SVG. There is, though, a difference between choosing a single SVG rendering context vs one element per node/edge - more on this below.

Of course, given that SVG is a subset of HTML from the browser's perspective, if you want to render a node that's a pure shape it is easily done. Here's a hexagon:

<svg viewBox="0 0 173.2 200" width="80" height="80">
<path fill="forestgreen"
d="M86.6 0L173.2 50L173.2 150L86.6 200L0 150L0 50Z"></path>
</svg>

In the Toolkit we can use just this SVG on its own as our node template:

Notice also in this demonstration that we implemented the same z-index trick as we did above - the hexagon we are dragging has a different fill and is raised above the others:


.demo-hexagon.jtk-drag {
z-index:11;
}

.demo-hexagon.jtk-drag path {
fill:orangered;
}
Dynamic sizing

We can also very easily - in a declarative way - add support for dynamic sizing to these hexagons:

<svg viewBox="0 0 173.2 200" width="{{size}}" height="{{size}}">
<path fill="forestgreen"
d="M86.6 0L173.2 50L173.2 150L86.6 200L0 150L0 50Z"></path>
</svg>

Here, size is a field in our node data:

{
nodes:[
{ id:"1", left:50, top:50, size:80},
{ id:"2", left:450, top:150, size:120},
{ id:"3", left:250, top:200, size:150},
{ id:"4", left:380, top:350, size:40}
]
}

Notice how in these few examples just discussed there was no need for any programmatic manipulation of elements or of their sizing - we provided an HTML template, the browser figured out the layout using the markup in this template and the CSS in the page, and jsPlumb figured out the necessary placement of the edges afterwards.

HTML markup

Not being constrained to just using SVG, we can very easily create detailed UIs and have most of the heavy lifting in terms of layout handled by the browser. In this example we have a single node template:

<div class="jtk-show-outline-demo d-flex flex-column jtk-auto-size-node">
{{id}}
<textarea rows="{{rows}}">{{notes}}</textarea>
</div>

Which we use to render a few nodes that each have a rows member in their backing data:

{
"nodes":[
{ "id":"1", "left":50, "top":50, "rows":6 },
{ "id":"2", "left":300, "top":250, "rows":9 },
{ "id":"3", "left":500, "top":0, "rows":3 }
]
}

We see the size of the text area is different in each node - and we didn't have to do any special computation to achieve this, it's all just HTML and CSS.

note

You cannot drag the nodes above by their textarea elements. The Toolkit automatically excludes form controls from being used for dragging an element.

Did you press the resize button? We have an onclick handler attached to that button which invokes this function:

function resize() {
toolkit.getNodes().forEach(n => toolkit.updateNode(n, {rows:3}))
}

We set rows to 3 for every node and the Toolkit re-renders everything, repositioning the edges as required. That's all we had to do, because once again we're able to rely on HTML and CSS. In this case, also, the Toolkit is running a ResizeObserver to catch the resize event from the DOM, which is supported in all major browsers - for HTML elements. For SVG elements ResizeObserver support is patchy across browsers, and it isn't clear whether it will ever be supported for SVG shape elements such as rect, circle etc.

You'll have to resize the page if you want to resize again. Once you've resized, there's no going back.

Library integrations

One area where you can really benefit from the Toolkit's approach to rendering is when you use a library such as React, Angular, Vue or Svelte. The Toolkit's integration for each of these libraries lets you render each node as a component, with all of the richness that provides. This is a reworking of the HTML textarea example above that uses the Toolkit's React integration:

We rendered each of the nodes here as a React functional component:

const NodeComponent = ({ctx}) => {

const { vertex, toolkit } = ctx;

function clear() {
toolkit.updateNode(vertex, {notes:""})
}

function update(e) {
toolkit.updateNode(vertex, {notes:e.target.value})
}

return <div>
{vertex.data.id}
<textarea rows={vertex.data.rows} value={vertex.data.notes} onChange={update}></textarea>
<button onClick={clear}>Clear</button>
</div>
}
note

You cannot drag the nodes in the above snippet by their textarea element - the Toolkit automatically excludes a range of input form fields from being able to instigate a drag.

Here we see an example of the complexity that is easily handled by the Toolkit - our NodeComponent encapsulates a specific set of business logic in a React functional component, and the configuration to make the Toolkit use this component could not be simpler:

const viewOptions = () => {
return {
nodes: {
default: {
jsx: (ctx) => {
return <NodeComponent ctx={ctx}/>
}
}
}
}
}

Achieving similar results in Angular, Vue or Svelte is just as easy. The Toolkit is integrated with these libraries, not merely compatible.

Where it's going

There are of course tradeoffs to using a single element per edge and vertex, the most notable being performance. For large datasets there's an overhead having all of those DOM elements around. But not every application regularly deals in very large datasets, and in practise we find that with our current arrangement performance only becomes a concern once the number of vertices is up into the hundreds, which is far more than many applications require.

We said at the start of this article that we do not consider jsPlumb to be limited to being just a diagramming library - it can be used to create applications on a whole other level than just diagrams. But we do acknowledge that using jsPlumb to build a diagram creator does have complications.

We see two separate, but related, areas for us to investigate and to develop jsPlumb.

Performance

We'll be keeping the current rendering approach as we firmly believe that there is a whole class of applications beyond just diagrams for which it is the logical choice, but we are keen to tune it wherever possible. To that end, in the next release - 7.x - we have overhauled our rendering pipeline entirely, stripping away the duplication of code that existed between the original Community edition and the Toolkit edition, and refactoring connection painting to be more performant. We do not yet have a release date for 7.x.

Diagramming support

  • A few versions ago we introduced the concept of shape libraries, which is a means for you to render SVG shapes of various types onto the canvas. At the time we also shipped a set of "flowchart" shapes, and we'll soon be releasing a set of BPMN symbols.

  • Another great new feature in our latest release is client side support for export to SVG/PNG/JPG, used in conjunction with a shape library.

  • We're looking at releasing a single SVG renderer. This would mean that all edges and nodes would have to be SVG, limiting it's capabilities a little, but for diagramming applications a single SVG can suffice.


Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.

· 3 min read
Simon Porritt

What's new?

Export to SVG, PNG and JPG

This is an exciting new capability that the Toolkit offers. When using a Shape library to render the nodes in your UI, you can now export the Surface canvas to either SVG, PNG or JPG.

We've added support for this to our Flowchart builder starter app:

When you click one of these you'll see a preview window, from which you can download the export:

The Toolkit offers a lightweight UI to assist with the export, as shown above, which is easily customised via CSS, and also a lower-level programmatic API if you want to do something more bespoke.

Read more about SVG, PNG and JPG export in the Toolkit documentation.

Also checkout the the Angular integration documentation for a discussion of some useful methods related to this that we added to the Toolkit's angular service.

Miscellaneous updates

  • The Angular service was augmented with new methods to support exporting to SVG/PNG/JPG - see the Angular integration documentation for details.

  • The ShapeLibrary component now supports rendering labels to shapes as part of their SVG element. Previously a user was required to separately manage labels, but the shape library can now handle that for you. For documentation on this, see the shape libraries documentation

  • A new flag useHTMLElement is supported on Label overlays. Setting to false will result in the Toolkit using an SVG text element for the label overlay, rather than an HTML element.

  • A new flag useHTMLLabel was added to edge definitions, which you can use to override the new default behaviour of using an SVG text element for the default label.

Breaking changes

  • The ShapeLibraryPalette no longer registers a jtk-shape tag on the associated Surface. Instead, the shape library needs to be passed in the render options to the Surface. See the shape libraries documentation for details.

  • When you specify a label in an edge type, the Toolkit now creates an SVG text element for it, rather than an HTML element.

  • By default, hover events are now switched off in the UI layer. We expect this change to affect almost nobody at all. For a full discussion of the reasoning behind this, see the documentation on rendering at https://docs.jsplumbtoolkit.com/toolkit/6.x/lib/rendering.

Bugfixes

  • Fixed an issue with the lasso plugin that would cause it to fail on devices with touch + pointer input methods.
  • Fixed an issue whereby a cssClass defined on an edge mapping would not be applied to a newly created edge.

Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.

· 2 min read
Simon Porritt

Release 6.5.0 of the Toolkit is now available.

What's new?

We're pleased to announce the availability of a new connector type in 6.5.0 - Segmented. This connector consists of a set of straight line segments, and can be smoothed to a set of bezier curves. We've also shipped a path editor for this new connector type.

Segmented Connectors

This connector type can be useful for a number of different applications, such as workflow designers, flowchart builders, drawing applications - the list is endless. With the Segmented path editor the user can very easily split/discard segments and drag key points around the canvas.

The connector also supports a "smooth" mode, where the individual line segments are replaced by a set of Bezier splines:

This is how the path looks when the editor has been closed:

To see these connectors in action, take a look at the Segmented connectors demonstration.

For documentation, check out https://docs.jsplumbtoolkit.com/toolkit/6.x/lib/connectors#segmented

Miscellaneous updates

  • Orthogonal connector editor automatically trims any supplied geometry after load, to ensure the internal geometry is in a format that the editor can manage.

  • The jsplumbtoolkit-connector-editors.css stylesheet was refactored slightly, to introduce common base classes for edge handles, and to use CSS variables.

  • The controls component now detects the existence - or otherwise - of a Lasso plugin on the associated Surface, and does not draw the mode change buttons if the Lasso is not present.

Bugfixes

  • Fixed an issue whereby path tracing between groups was failing.

Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.

· 7 min read
Simon Porritt

Release 6.2.5 of the Toolkit is now available. This release contains a bunch of useful new things, and sees an update to our most popular demonstrations to turn them into fully-fledged starter applications from which you can build your own apps quicker than ever.

What's new?

Many things. The full changelog is available here. In this post we're going to discuss all of the changes from 6.2.0 through to 6.2.5.

Shape Libraries

A shape library is an object that can assist in rendering SVG shapes into your nodes. One of our most popular demonstrations for a long time has been the Flowchart Builder, in which nodes are drawn using SVG as various shapes. Prior to release 6.2.0, though, these shapes were "hand-drawn" in the node templates and adding a new shape involved creating the SVG template and mapping it explicitly inside your app.

The Flowchart Builder starter app now uses a shape library to render the SVG in its nodes:

The Toolkit ships a default set of shapes that you can use - FLOWCHART_SHAPES, but it is straightforward to create your own shape sets. It would be easy, for example, to convert the Flowchart Builder into an Entity Relationship Diagram builder simply by swapping out the shapes used by the shape library. We may even make that the topic of a future blog post.

Shape Library Palettes

We've created a component called ShapeLibraryPalette that integrates tightly with a shape library to provide a palette of shapes that a user can drag on to your canvas. You can see this in action in the Flowchart Builder starter app linked above. The shape library palette takes the contents of some shape library (in the screenshot below, that's the FLOWCHART_SHAPES that ship with the Toolkit) and renders each shape, then instantiates a drop manager to allow users to drag new shapes onto a surface.

Our Angular, React, Vue2 and Vue3 integrations all contain support for shape libraries and shape library palettes. Because of the way Svelte operates we haven't added any specific support for these concepts to the Svelte integration - it's straightforward to hook into these things the way you would in a 'vanilla' app. But if you're a user of our Svelte integration and you've got any suggestions we'd be happy to hear from you.

Read the documentation about shape libraries and shape library palettes here

Inspectors

An inspector is an object that can assist you in implementing implementing form-based editors for objects in your dataset. For example, this is a node inspector from the Flowchart Builder starter app:

Inspectors integrate tightly with a Surface component, and have been written in a layered way that makes them agnostic of the specifics of the form they are managing: it is the user's responsibility to render the HTML they wish to use for a given inspector, so you are free to use anything you like, and the inspector will hook into your UI via a custom attribute you write in your code.

Inspectors can manage single objects, as shown above, and they can also manage multiple objects:

Support for inspectors was added to the Angular, Vue2, Vue and React integrations.

EdgeTypePickers

In the FlowchartBuilder's edge inspector a user can select an edge type by clicking on it:

We make use of an EdgeTypePicker to do this. This is a component that we created for the starter apps but which seemed like something that could be useful for licensees of the Toolkit, so we've pulled it into the @jsplumbtoolkit/browser-ui package now. There's a wrapper available for Angular, React, Vue2 and Vue3.

Read the documentation about inspectors and edge type pickers here.

Selection mode

The concept of 'mode' was added to selections: you can specify that a given selection may only contain one type of object (eg nodes, groups or edges), or that it can contain a mix. For more information see the documentation.

We use this concept in the Flowchart Builder starter app: a user can edit one or more nodes in an inspector, or one or more edges, but not a mixture of nodes or edges. Setting SelectionModes.isolated on the underlying Toolkit's selection means that the Toolkit will not support a selection of objects of different types.

Drag and drop updates

Several improvements were made to the SurfaceDropManager and DropManager classes:

  • Added the canvasDropFilter to SurfaceDropManager. The parent class DropManager already supported this but it was not exposed on the SurfaceDropManager prior to 6.2.0.

  • An optional dragSize parameter can be supplied to the drop manager, to set the size of nodes/groups that are being dragged.

  • An object being dragged onto a canvas will be automatically scaled so that it matches the current zoom of the canvas. This makes a big difference to the usability of the drag/drop, as users can see while dragging an element how it will fit into the canvas once it is dropped. This behaviour can be switched off if desired.

Surface updates

  • When dragging a new edge, the Surface element will now honour a type specifier returned from a beforeStartConnect interceptor. In previous versions the type would only be honoured once a new connection had been established. For more information see here.

  • Support for a modelEvents object has been added to the Surface's constructor. Instead of having to register model events after the fact you can now supply them with your render parameters.

Controls component

You may have seen this component in our various starter app/feature demonstrations:

We now ship this as a component that you can include in your apps. Wrappers for Angular, React, Vue2 and Vue3 are available.

Miscellaneous updates

  • Edge editors now snap anchor placeholders to the closest anchor when the user is relocating an anchor. Previously the placeholder would not be snapped until the use released the mouse button. The previous behaviour can be reinstated by setting snapToAnchors:false in the options you pass to the startEditing method.

  • Support for 'fragments' was added to custom tags in the default template engine. This is more of an internal feature, but could be leveraged by advanced users of the Toolkit. See documentation on templating for further information.

  • Label overlays now have a jtk-label-overlay class added to their element.

  • The default CSS stylesheet was updated with a visibility:hidden rule for .jtk-label-overlay:empty. For label overlays on which the label's value is currently an empty string, this will hide the overlay.

  • The CSS for the "controls" component that we ship along with each of the library integrations was copied out from jsplumbtoolkit-demo-support.css and into jsplumbtoolkit-controls.css, and the various selectors were updated to be more specific.

  • Several enhancements were made to the drawing tools plugin, specifically with regard to honouring a grid that is applied to the associated surface.

Bugfixes

  • Fixed issue with anchor placeholder not being centered correctly when editing an edge.
  • Fixed issue with Hierarchy layout where certain combinations of unattached roots and multiple parents could cause the layout to fail, due to a cache not being cleared.
  • Updates to the default template engine to ensure that elements written by custom tags are correctly updated when their vertex is updated.
  • Fixed issue in connector editor package where re-editing an edge that it was already editing would cause any extra overlays to be removed.
  • Fixed issue in bezier connector editor where control points were being dragged at twice the rate the mouse was moving.

Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.

· 6 min read
Simon Porritt

This morning we've released version 6.0.0 of both the Toolkit and Community editions of jsPlumb.

What's new?

There is no functional change between version 6.0.0 and the last 5.x version, 5.13.7. What has changed, though, is the way that jsPlumb is packaged.

In 5.x we distributed both the Toolkit and Community editions as a set of packages, with the intention being that users could keep their code size down by omitting packages they did not need. In practice, though, all Toolkit users needed the core Toolkit packages - which were the largest - and all Community users needed the core Community packages, also the largest. The gain of distributing the code amongst several packages was negligible.

From a development perspective, the practice of spreading code around the various packages meant that the developer needed to know which package to go to in order to import something. For Toolkit users in particular, this could get tedious. Is the import in the Toolkit edition core? The Community edition core? Common? A renderer? etc.

So in 6.x we're distributing everything in a single Toolkit package and a single Community package, and users of each edition need only import one single package - either @jsplumbtoolkit/browser-ui or @jsplumb/browser-ui.

The packages contain a Common JS module, an ES6 module, and a UMD. For users who have a tree shaker incorporated in their build chain this setup will be good for their bundle sizes: exporting everything from a single package means the tree shaker can be very granular.

These packages are available now. For Community Edition users, @jsplumb/browser-ui version 6.0.0 is in the public NPM repository.

For Toolkit users who have an active subscription to new releases and to our NPM repository, @jsplumbtoolkit/browser-ui version 6.0.0 is in our NPM repository and can be downloaded from the download page.

We're still catching up on updating the docs and demonstrations - expect to see these changes over the next few days.

How to migrate?

The process of migrating to 6.x is straightforward.

Toolkit Edition

We've created a 6.x branch of the Flowchart Builder demonstration.

There were three things we needed to do:

  1. Update dependencies:

Previously:

"dependencies": {
"@jsplumbtoolkit/browser-ui-vanilla-2": "^5.12.0",
"@jsplumbtoolkit/drop": "^5.12.0",
"@jsplumbtoolkit/labels": "^5.12.0",
"@jsplumbtoolkit/print": "^5.12.0",
"@jsplumbtoolkit/dialogs": "^5.12.0",
"@jsplumbtoolkit/connector-editors-orthogonal": "^5.12.0",
"@jsplumbtoolkit/connector-orthogonal": "^5.12.0",
"@jsplumbtoolkit/browser-ui-plugin-drawing-tools": "^5.12.0",
"@jsplumbtoolkit/browser-ui-plugin-miniview": "^5.12.0",
"@jsplumbtoolkit/browser-ui-plugin-lasso": "^5.12.0",
"@jsplumbtoolkit/browser-ui-plugin-background": "^5.12.0"
}

Now:

"dependencies": {
"@jsplumbtoolkit/browser-ui": "^6.0.0"
}
  1. Update imports:

Previously:

import * as Dialogs from "@jsplumbtoolkit/dialogs"

import {
SurfaceRenderOptions,
Surface,
EVENT_TAP,
EVENT_CANVAS_CLICK,
EVENT_SURFACE_MODE_CHANGED,
SurfaceMode,
Connection,
BlankEndpoint,
ArrowOverlay,
LabelOverlay,
AnchorLocations,
DEFAULT,
ready,
newInstance
} from "@jsplumbtoolkit/browser-ui-vanilla-2"

import {
Edge,
Vertex,
ObjectInfo,
AbsoluteLayout,
uuid,
forEach,
EVENT_UNDOREDO_UPDATE,
UndoRedoUpdateParams,
ObjectData,
extend
} from "@jsplumbtoolkit/core"

import { EdgePathEditor } from "@jsplumbtoolkit/connector-editors"
import { createSurfaceManager } from "@jsplumbtoolkit/drop"
import { registerHandler } from "@jsplumbtoolkit/print"
import {DrawingToolsPlugin} from "@jsplumbtoolkit/browser-ui-plugin-drawing-tools"
import {MiniviewPlugin} from "@jsplumbtoolkit/browser-ui-plugin-miniview"
import {OrthogonalConnector} from "@jsplumbtoolkit/connector-orthogonal"

import * as ConnectorEditorOrthogonal from "@jsplumbtoolkit/connector-editors-orthogonal"
import {LassoPlugin} from "@jsplumbtoolkit/browser-ui-plugin-lasso"
import {CancelFunction} from "@jsplumbtoolkit/dialogs"

import {GeneratedGridBackground, GridTypes, BackgroundPlugin} from "@jsplumbtoolkit/browser-ui-plugin-background"

ConnectorEditorOrthogonal.initialize()

Now:

import {
Dialogs,
SurfaceRenderOptions,
Surface,
EVENT_TAP,
EVENT_CANVAS_CLICK,
EVENT_SURFACE_MODE_CHANGED,
SurfaceMode,
Connection,
BlankEndpoint,
ArrowOverlay,
LabelOverlay,
AnchorLocations,
DEFAULT,
ready,
newInstance,Edge,
Vertex,
ObjectInfo,
AbsoluteLayout,
uuid,
forEach,
EVENT_UNDOREDO_UPDATE,
UndoRedoUpdateParams,
ObjectData,
EdgePathEditor,
createSurfaceDropManager,
registerHandler,
DrawingToolsPlugin,
MiniviewPlugin,
OrthogonalConnector,
LassoPlugin,
CancelFunction,
GeneratedGridBackground,
GridTypes,
BackgroundPlugin
} from "@jsplumbtoolkit/browser-ui"

Note also that it is not necessary to invoke ConnectorEditorOrthogonal.initialize() in version 6.x of the Toolkit.

  1. Update the Dialogs constructor

Since everything is exported from a single package there were a few method name clashes - multiple packages, for example, exported a newInstance method. Most of those newInstance methods were just convenience methods and didn't do anything particularly useful. For example, this is how we rewrote the code that creates a Dialogs instance:

Previously:

const dialogs = Dialogs.newInstance({
...
})

Now:

const dialogs = new Dialogs ({
...
});

Sometime over the next few days we'll merge all of the 6.x demonstration branches into main.

Community Edition

All of the Community Edition demonstrations have been ported to use 6.0.0 now on the main branch. Here's what we did to migrate the Flowchart demonstration (this is for the Typescript demonstration):

  1. Update dependencies

Previously:

  "dependencies": {
"@jsplumb/browser-ui": "^5.0.0",
"@jsplumb/connector-flowchart": "^5.0.0"
},

Now:

  "dependencies": {
"@jsplumb/browser-ui": "^6.0.0"
},
  1. Update imports

Previous:

import {
ContainmentType,
EVENT_CLICK,
EVENT_CONNECTION_ABORT,
EVENT_CONNECTION_DRAG,
newInstance,
ready
} from "@jsplumb/browser-ui"

import {AnchorLocations, AnchorSpec,} from "@jsplumb/common"

import {
Connection,
ConnectionDetachedParams,
ConnectionMovedParams,
DotEndpoint,
EVENT_CONNECTION_DETACHED,
EVENT_CONNECTION_MOVED,
LabelOverlay
} from "@jsplumb/core"

import {FlowchartConnector} from "@jsplumb/connector-flowchart"

Now:

import {
ContainmentType,
EVENT_CLICK,
EVENT_CONNECTION_ABORT,
EVENT_CONNECTION_DRAG,
newInstance,
ready,
AnchorLocations,
AnchorSpec,
Connection,
ConnectionDetachedParams,
ConnectionMovedParams,
DotEndpoint,
EVENT_CONNECTION_DETACHED,
EVENT_CONNECTION_MOVED,
LabelOverlay,
FlowchartConnector
} from "@jsplumb/browser-ui"

What are the breaking changes?

We'll be updating the changelog in the documentation over the next few days, but for convenience here we include the changelog for each edition.

Toolkit breaking changes

  • Support for the original templates syntax (where variable interpolations are of the form ${...}) has been dropped, throughout the Vanilla toolkit and the dialogs. The code from the previous templates-2, dialogs-2 and browser-ui-vanilla-2 packages has been retained in the @jsplumbtoolkit/browser-ui package, and the code from templates, dialogs and browser-ui-vanilla has been dropped.

  • The Spring layout has been removed. Use ForceDirected instead.

In order to support the single @jsplumbtoolkit/browser-ui package, several factory methods were renamed/removed:

  • newInstance(surface) in previous @jsplumbtoolkit/browser-ui-anim removed. Use new SurfaceAnimator(surface) instead.

  • createManager in previous @jsplumbtoolkit/drop package is now exposed as createDropManager in @jsplumbtoolkit/browser-ui

  • createSurfaceManager in previous @jsplumbtoolkit/drop package is now exposed as createSurfaceDropManager in @jsplumbtoolkit/browser-ui

  • newInstance(surface:Surface, options?:ConnectorEditorOptions) from previous @jsplumbtoolkit/connector-editors method removed - use new EdgePathEditor(surface:Surface, options?:ConnectorEditorOptions) instead.

  • initialize() method from previous @jsplumbtoolkit/connector-editors-bezier is now exposed as initializeBezierConnectorEditors() on @jsplumbtoolkit/browser-ui.

  • initialize() method from previous @jsplumbtoolkit/connector-editors-orthogonal is now exposed as initializeOrthogonalConnectorEditors() on @jsplumbtoolkit/browser-ui.

  • newInstance(params:DialogsOptions) from the previous @jsplumbtoolkit/dialogs-2 package has been removed. To instantiate Toolkit dialogs now, use new Dialogs(params).

  • The HORIZONTAL and VERTICAL axis identifiers from the HierarchicalLayout have been moved into the enum HierarchicalLayoutOrientations.

  • The code from the related -drop package for each library integration (@jsplumbtoolkit/browser-ui-angular-drop etc) has now been pulled into the library integration's main package

Community breaking changes

  • newInstance method removed from @jsplumb/browser-ui-lists. Use new JsPlumbListManager(instance, params) instead.

  • BeforeStartDetachInterceptor renamed to BeforeStartConnectionDetachInterceptor

  • BeforeDetachInterceptor renamed to BeforeConnectionDetachInterceptor

  • BeforeDropInterceptor renamed to BeforeConnectionDropInterceptor


Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.

· 2 min read
Simon Porritt

Release 5.13.1 of the Toolkit is now available. This is a minor release consisting mainly of updates to the Hierarchy layout that came about through work on our series of World Cup visualizations.

Breaking changes

  • The Group class's getAllEdges method does not return edges connected to children of the group as of this release; this method behaves the same for Group as it does for Node now. It's not very likely that users of the API would be using this method.

Updates

  • We made some improvements to the way the Hierarchy layout places elements when invert is switched on.
  • We added a groupUnattachedRoots flag to the options for the Hierarchy layout. When a dataset contains one or more elements that have no child elements, the layout will ordinarily place these elements such that they do not intersect the area that the space child elements of the previous root take up. But when groupUnattachedRoots is set, these root nodes will be placed next to the previous root node. You can see this behaviour in the high level tournament view from our World Cup site:


Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.

· 3 min read
Simon Porritt

Today we released version 5.10.8 of the Toolkit edition of jsPlumb. In fact we're released a few versions in rapid succession as we've honed the API for a new piece of functionality: generated grid backgrounds.

We're excited to have added this functionality to the Toolkit - it provides a polish to applications using the Toolkit with an extremely easy to use API. The backgrounds themselves consist of a single SVG element with a repeated pattern, and so are very performant.

The full documentation for generated grid backgrounds can be found here.

But here's a quick example:

Grid background

This was generated with the following code:

import { 
AbsoluteLayout,
newInstance,
AnchorLocations,
BlankEndpoint,
StateMachineConnector,
GeneratedGridBackground,
BackgroundPlugin
} from "@jsplumbtoolkit/browser-ui"

const tk = newInstance()
const surface = tk.render(someElement, {

layout:{
type:AbsoluteLayout.type
},
defaults:{
anchor:AnchorLocations.Continuous,
endpoint:BlankEndpoint.type,
connector:StateMachineConnector.type
},
plugins:[
{
type:BackgroundPlugin.type,
options:{
type:GeneratedGridBackground.type,
minWidth:1500,
minHeight:1500
}
}

]
})

Changelog

The changelog for versions 5.10.6 through 5.10.8 is:

5.10.8

August 16th 2022

  • Internal refactoring of background plugin to expose type members that can be used when configuring backgrounds in an ES6/TS development environment, to avoid the code being omitted through tree shaking.

5.10.7

August 15th 2022

  • Added support for generated grid backgrounds.

5.10.6

August 12th 2022

  • Fixed a couple of issues with edge placement in nested nodes/groups in the underlying community edition
  • Fixed an issue with the tiled background layer in the background plugin that was preventing it from rendering.
  • Reinstated the zoomToBackground method on the Surface widget
  • Added supportLegacyConnectorData flag to OrthogonalConnectorOptions. This offers a means for people migrating from 2.x to load datasets where there is path data for Orthogonal connectors in the 2.x format.

Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.

· 2 min read
Simon Porritt

jsPlumb uses Docusaurus, a super handy static site generator, for various parts of our ecosystem, including our Documentation and also our recently released stand alone components product.

While developing the site for jsPlumb Components, we wanted to include a couple of pieces of information that would vary depending on whether we were running in development mode locally (ie docusaurus start) or building for production. We looked into the various options available, all based on the dotenv plugin for Webpack, but couldn't find what we were looking for: a solution that worked, with minimal manual intervention, in both development mode and when building for production.

So we ended up building our own plugin. This plugin reads values from a JSON file and makes them available to your site via the customFields map in the Docusaurus siteConfig.

note

When I say "Docusaurus" in this post I am talking about v2. I've not used v1.

Installation

npm i @jsplumb/docusaurus-plugin-env-loader-json

Configuration

You just have to add the plugin to the list of plugins in docusaurus.config.js:

plugins:[
"@jsplumb/docusaurus-plugin-env-loader-json"
],

and then create an env.json file in the Docusaurus project directory.

env.json

Your environment variables should be keyed under a section that identifies the environment you are targetting - "development" when using docusaurus start and "production" when running a build. The plugin reads the environment name from the environment variable NODE_ENV.

{
"production":{
"SERVER_URL":"https://some.server.com/anEndpoint"
},
"development":{
"SERVER_URL":"http://localhost:4200/anEndpoint"
}
}

Accessing values

You can access these values via the customFields section of the Docusaurus site config:

import useDocusaurusContext from '@docusaurus/useDocusaurusContext';

export function MyApp {

const {siteConfig} = useDocusaurusContext();
const serverUrl = siteConfig.customFields.SERVER_URL

...

}

Changing the configuration file name

If you want to specify some file other than env.json in your project's root, you can do so by setting the sourceFile option of the plugin:

plugins:[
[
"@jsplumb/docusaurus-plugin-env-loader-json",
{
"sourceFile":"path/to/aFile.json"
}
]
],

This path should be specified relative to the project root. No leading slash is required.


Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.

· 3 min read
Simon Porritt

One of the core concepts when working with components in Angular is that of @Inputs and @Outputs. For instance, maybe you have some component that expects to be told what text to use as its title, and which will output an event when the user clicks a button:

@Component({
selector:"app-my-component",
template:`
<div>
<h1>{{title}}</h1>
<button class="btnEmit" (click)="emitEvent('test')">Click Me</button>
</div>
`
})
export class MyComponent {
@Input() title:string

@Output() outputEvent = new EventEmitter<string>()

emitEvent(value:string) {
this.outputEvent.emit(value)
}
}

From version 5.6.2, the Toolkit's Angular integration now offers a couple of ways of working with these that earlier versions did not support.

Using @Input in a node component

Imagine the component shown above was being used to render some node type in your UI - with a couple of small changes we can make it so:

import {BaseNodeComponent} from "@jsplumbtoolkit/browser-ui-angular"
import {EventEmitter, Component, Input, Output} from "@angular/core"

@Component({
template:`
<div>
<h1>{{title}}</h1>
<button class="btnEmit" (click)="emitEvent('test')">Click Me</button>
</div>
`
})
export class NodeComponent extends BaseNodeComponent {
@Input() title:string

@Output() outputEvent = new EventEmitter<string>()

emitEvent(value:string) {
this.outputEvent.emit(value)
}
}

We've removed selector from the Component spec, since it's not relevant when rendering via the Surface, and we've wired up the component to extend BaseNodeComponent, but other than that the component is the same as in the first example. Prior to version 5.6.2, this line:

@Input() title:string

would be ignored by the Toolkit. From 5.6.2 onwards, though, the Toolkit will attempt to set this input from the backing data for the node. So if, for instance, you had this data object for a node:

{
"id": "1",
"title": "My First Node"
}

then the Toolkit will set "My First Node" on the title input. This is of course really just a form of syntactic sugar - you could just easily render the title in your template like this (remembering that obj is set on the component by the Toolkit when it creates it):

<h1>{{obj.title}}</h1>

as you can by using the input on the class:

<h1>{{title}}</h1>

but if you're like me, you might find that the approach using @Input feels a little more nicely formalised.

Using @Output in a node component

You can also now wire up listeners to @Outputs in your components. The process to do this involves providing a vertexEvent callback to the Surface component. For example, imagine you embed a surface component in one of your components:

<div class="my-component">
<h3>Here is my component</h3>
<jsplumb-surface surfaceId="surface"
toolkitId="myToolkit"
[view]="view"
[renderParams]="renderParams"
(vertexEvent)="vertexEvent($event)">
</jsplumb-surface>
</div>

Remember from above the @Output() outputEvent... declaration on the node component? Whenever that output emits something, the Surface will invoke vertexEvent:


import { Vertex } from "@jsplumbtoolkit/core"
import { ComponentRef } from "@angular/core"

vertexEvent(detail:{
vertex:Vertex,
event:Event,
payload:any,
component:ComponentRef<any>}) {

}

The callback is given the Toolkit's vertex, the original event, the payload (ie. whatever was emitted from the output), and the Angular ComponentRef. You can access the specific Angular component via the instance field on this component ref.


Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.

Not a user of the jsPlumb Toolkit but thinking of checking it out? Head over to https://jsplumbtoolkit.com/trial. It's a good time to get started with jsPlumb.