Back to jointjs.com.
Jump to other additions.
CHANGELOG
We are delighted to announce version 4 of JointJS, and that JointJS open-source and JointJS+ now have no external dependencies! 🎉
-
Remove jQuery, backbone, and lodash dependencies from
package.json
Since version 3.7.0, JointJS hasn’t used Lodash internally. Building on this work, JointJS no longer uses jQuery and backbone. This finally allows us to remove these dependencies from our
package.json
, and provide our users with a leaner library without changing core functionality.Details...
In version 3.7.0, Lodash was removed, and these utilities were replaced with our own code. Similarly, jQuery and backbone functionality is now handled with internal code under the
mvc
namespace.jQuery, backbone, and lodash will no longer be installed when working with JointJS. If you are specifying these dependencies in a
script
tag as demonstrated in our installation tutorial, you can remove thescript
tags related to jQuery, backbone, and lodash.<!DOCTYPE html> <html> <head> </head> <body> <div id="paper"></div> <!-- Remove start --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.4.1/backbone.js"></script> <!-- Remove end --> <script src="https://cdnjs.cloudflare.com/ajax/libs/@joint/core/4.0.0/joint.js"></script> <script type="text/javascript"> const graph = new joint.dia.Graph(); const paper = new joint.dia.Paper({ el: document.getElementById('paper'), model: graph // ... }); // ... </script> </body> </html>
-
format.BPMN - Enable import & export of Business Process Model and Notation (BPMN)
BPMN is a graphical representation for specifying business processes in a business process model. Along with the BPMN specification, an XML schema is also defined that allows a BPMN diagram to be imported from and exported to XML.
This new feature enables the import and export process within JointJS+.
Details...
fromBPMN(xmlDoc, options)
functionThis function handles the import process. It takes an
XMLDocument
containing a diagram and an options object. The inclusion of theoptions
parameter provides the flexibility to customize the import process to meet specific requirements.The import options object has the following 2 properties -
cellFactories
andbpmn2Shapes
. They are a collection ofcellFactories
mapped by decisive XML elements, and a namespace that contains definitions of JointJSbpmn2Shapes
respectively.Basic usage
The most basic way to use the package is to convert an
XMLDocument
into JointJS cells by passing the document to thefromBPMN
function without specifying the value of thecellFactories
property in theoptions
object.The resulting cells are stored in the
cells
property of the import result object.const importResult = fromBPMN(XMLDocument, { cellNamespace: joint.shapes.bpmn2 }); // graph.addCells(importResult.cells)
toBPMN(paper, options = {})
functionThis function handles the export process. It takes a
joint.dia.Paper
containing a diagram and optionally theoptions
object. The inclusion of theoptions
parameter provides the flexibility to customize the export process to meet specific requirements.Basic usage
The most basic way to use the package is to convert a paper with a diagram to XML without using an
options
parameter. It’s as simple as callingtoBPMN
with thepaper
as a parameter.const exportResult = toBPMN(paper); // exportResult.cells instanceof XMLDocument
-
Add BPMN import & export feature to BPMNEditor application
-
Add ImageProcessor application
The Image Processor app allows users to easily manipulate images with filters and transformation tools in a node-based manner.
- format.SVG - Add
grid
option to allow users to render the grid in the exported SVG
- format.Raster - Add
grid
option to allow users to render the grid in the exported image
- format.Print - Add
grid
option to allow users to display a grid on the print page
- ui.Halo - Fix to
makeLoopLink
option work with paper size defined as CSS dimension
-
dia.Cell - Remove the
Cell.parent(id)
setter from type definitions and documentationThe
Cell.parent(id)
method is not intended to be used as a way to embed cells. To eliminate the confusion, it was decided to remove it from type definitions and documentation.In case you were using the
Cell.parent(cell)
as a solution to embed cells, use theCell.embed(cell)
method instead.const parent = joint.shapes.standard.Rectangle(); const child = joint.shapes.standard.Rectangle(); parent.embed(child);
- linkTools.Vertices - A fix to trigger
link:mouseleave
event when the user stops dragging a vertex. The pointer is no longer over the vertex, and theredundancyRemoval
option is disabled.
Breaking Changes
-
jointjs dependency deprecated in favour of
@joint/core
- To utilizev4
of the JointJS library, install the@joint/core
dependency instead ofjointjs
Migration Guide
If you have a stand-alone JointJS project, and you want to take advantage of the latest version of the open-source JointJS library, install the
@joint/core
dependency from the npm registry.Previously:
npm i jointjs yarn add jointjs
Version 4.0.0:
npm i @joint/core yarn add @joint/core
-
jQuery removal - Remove
jquery
dependencyjQuery related functionality is now handled with internal code under the
mvc
namespace. The jQuery library was used as a starting point to create our own DOM utility. This new tool is for internal use only.Migration Guide
- Drop support for
jQuery
selectors. Now only CSS3 selectors are recognized.- Attribute not equal selector (
!=
) - Positional selectors (
:first
,:eq(n)
,:odd
, etc.) - Type selectors (
:input
,:checkbox
,:button
, etc.) - State-based selectors (
:animated
,:visible
,:hidden
, etc.) :has(selector)
in browsers without native support:not(complex selector)
in IE- custom selectors via jQuery extensions
- Reliable functionality on XML fragments
- Matching against non-elements
- Reliable sorting of disconnected nodes
- querySelectorAll bug fixes (e.g., unreliable
:focus
on WebKit) - Selectors can not start with combinators (e.g
> div
needs to be converted to:scope > div
)
- Attribute not equal selector (
view.$el
andview.$()
are no longer public properties. Useview.el
andview.el.querySelectorAll()
instead.- Remove the following protected properties from the
paper
:paper.$document
,paper.$grid
andpaper.$background
. utils.sortElement
returns a plain array of Elements, not a$
object.- It’s no longer possible to access
CellView
from a DOM element ($.data(document.querySelector('.joint-cell').view
). cellView.prototype.findBySelector()
has been removed in favor offindNode()
,findPortNode()
andfindLabelNode()
(findNodes()
,findPortNodes()
andfindLabelNodes()
are used to find nodes referenced by a group selector).
- Drop support for
-
Backbone removal - Remove
backbone
dependencyBackbone related functionality is now handled with internal code under the
mvc
namespace. You will now findmvc.Events
,mvc.Model
,mvc.View
, andmvc.Collection
in the JointJS library.Migration Guide
-
mvc.Model - All lodash related methods have been removed from
mvc.Model
, along with any methods related toBackbone.Sync
functionality. -
mvc.Collection - Most lodash related methods have been removed from
mvc.Collection
, along with any methods related toBackbone.Sync
functionality. The followingcollection
methods remain, and if possible, use native equivalents:- each
- find
- findIndex
- filter
- first
- includes
- isEmpty
- last
- map
- reduce
- sortBy
- toArray
If your application uses
Backbone.Sync
,Backbone.Router
,Backbone.History
, or any of the behaviour mentioned above, you should take the necessary steps to replace this functionality in your application. Thebackbone
dependency will no longer be installed when working with JointJS. -
-
format.Print - Use native DOM over jQuery elements in
ready
functionIn previous versions, pages were represented as a collection of jQuery objects. After the change, pages are now represented as a collection of
HTMLDivElement
objects.Migration Guide
Note the change in the
ready
function signature…Previously:
const options = { /** * @param {Array<jQuery>} pages * @param {function} readyToPrint * @param } opt */ ready: function(pages, send, opt) { pages.forEach(function($page) { // custom action - e.g. add a border on every page: $page.css('border', '1px solid gray'); }); send(pages); }, } print(paper, options);
Version 4.0.0:
const options = { /** * @param {Array<HTMLDivElement>} pages * @param {function} readyToPrint * @param } opt */ ready: function(pages, send, opt) { pages.forEach(function(pageEl) { // custom action - e.g. add a border on every page: pageEl.style.border = '1px solid gray'; }); send(pages); }, } print(paper, options);
-
layout.GridLayout - Remove deprecated GridLayout options
- Deprecated options
dx
,dy
andcenter
were removed. - Added options -
verticalAlign
andhorizontalAlign
. - Default values for
verticalAlign
andhorizontalAlign
options are'middle'
now (previously it was'top'
and'left'
respectively).
Migration Guide
dx
anddy
Deprecated options
dx
anddy
were removed in favor ofrowGap
andcolumnGap
.Note, that previously
dx
anddy
were also having an impact on overall layout margin, so you may want to adjustmarginX
andmarginY
options accordingly.Previously:
layout.GridLayout.layout(graph, { /* ... */ dx: 10, dy: 10, marginX: 10, marginY: 10 });
Version 4.0.0:
layout.GridLayout.layout(graph, { /* ... */ columnGap : 10, rowGap: 10, marginX: 20, marginY: 20, });
center
Deprecated
center
option was removed in favor ofverticalAlign
andhorizontalAlign
. Previouslycenter
wastrue
by default, so to keep the default behaviour, the same default values ofverticalAlign
andhorizontalAlign
options were changed tomiddle
.If you didn’t specify the
center
option in your layout, the change has no impact on you.If you were using
center: false
without settingverticalAlign
andhorizontalAlign
options, you might want to specify their values astop
andleft
respectively to preserve old behaviour.Previously:
layout.GridLayout.layout(graph, { /* ... */ center: false });
Version 4.0.0:
layout.GridLayout.layout(graph, { /* ... */ verticalAlign: 'top', horizontalAlign: 'left', });
- Deprecated options
-
shapes.bpmn - Remove
shapes.bpmn
from source code and JointJS+ packageRedefine
bpmn.Choreography
, and make it available through thebpmn2
namespace.Migration Guide
The
bpmn2
namespace should be used in place ofshapes.bpmn
.
-
ui.Inspector - labels can be arbitrary HTML (not only a string) regardless of the type
The labels are always parsed as HTML no matter what type of field they belong too.
Previously, only select-box, select-button-group and color-palette were parsed as HTML.
Previously:
ui.Inspector.create('.inspector', { /* ... */ inputs: { // interpreted as a string ("<b>1</b>" text was displayed) attribute1: { type: 'text', label: '<b>1</b>' }, // interpreted as HTML (bold number was displayed) attribute2: { type: 'select-box', label: '<b>2</b>' } } });
Version 4.0.0:
ui.Inspector.create('.inspector', { /* ... */ inputs: { attribute1: { type: 'text', label: '<b>1</b>' }, // is interpreted as HTML attribute2: { type: 'select-box', label: '<b>2</b>' } // is interpreted as HTML } });
Migration Guide
If reserved HTML characters were intentionally used, they must now be replaced by entities. See MDN for more info.
Previously:
ui.Inspector.create('.inspector', { /* ... */ inputs: { attribute: { type: 'text', label: '<label>' }, } });
Version 4.0.0:
ui.Inspector.create('.inspector', { /* ... */ inputs: { attribute: { type: 'text', label: '<label>' }, } });
-
ui.PaperScroller - The
scroll()
method now accepts local coordinatesFixed incorrect behaviour of the
scroll()
method in PaperScroller. Previously, thescroll()
method didn’t use the scale factor when calculating scroll value.If you were using some scale adjustments before invoking the
scroll()
method, you should now pass unscaled coordinates to thescroll()
method.Migration Guide
Previously:
const localPoint = new g.Point(1000, 0); // untransformed coordinates, the same as graph coordinates const scale = paperScroller.zoom(); // current zoom level const scrollPoint = localPoint.scale(scale, scale); paperScroller.scroll(scrollPoint.x, scrollPoint.y);
Version 4.0.0:
const localPoint = new g.Point(1000, 0); // untransformed coordinates, the same as graph coordinates paperScroller.scroll(localPoint.x, localPoint.y);
-
ui.PaperScroller - A new function to be used instead of
$.fn.animate
which no longer supports scroll animationMigration Guide
- The
opt.animation
in scroll functions no longer accepts parameters of jQuery’sanimate
method. - The
opt.animation
now consists of optional properties:duration
- duration of the animation in millisecondstimingFunction
- the jQuery’s string syntax has been replaced with a callback function that is called on every frame of the animationcomplete
- this remains the same, a callback function executed after the animation is complete
Previously:
paperScroller.scroll(100, 100, { animation: { duration: 'slow', easing: 'linear' } });
Version 4.0.0:
paperScroller.scroll(100, 100, { animation: { duration: 600, timingFunction: (t) => t } });
- The
-
ui.Stencil - fix layout defaults
- Previously when the
layout
option was provided as an object, the passed object was merged with the defaultGridLayout
options. Now, thelayout
object will be used as it is for the purpose of thelayout.GridLayout
plugin. The defaultGridLayout
options will be used only when usinglayout: true
, or another truthy value.
Migration Guide
If you used your custom
layout
function, or were usinglayout: true
, the change has no impact on you.Previously, if you specified the
layout
option as an object, the values would be merged with the default ui.Stencillayout
options. Now, the options will be used as it is, without merging. Because of this, now you shouldn’t reset the layout defaults as it was done previously.Previously:
layout: { columns: 2, marginX: 10, marginY: 10, columnGap: 10, columnWidth: 100, // reset defaults resizeToFit: false, dx: 0, dy: 0 }
Version 4.0.0:
layout: { columns: 2, marginX: 10, marginY: 10, columnGap: 10, columnWidth: 100, rowHeight: 80 // old definition relied on this default value from ui.Stencil }
Also, if you relied on the default
layout
options fromui.Stencil
, you should explicitly specify them in yourlayout
object. The default mergedlayout
object was as follows:{ columnWidth: this.options.width / 2 - 10, columns: 2, rowHeight: 80, resizeToFit: true, dy: 10, // corresponds to `rowGap: 10, marginY: 10` dx: 10 // corresponds to `columnGap: 10, marginX: 10` }
If you relied on one of these properties you should explicitly specify them.
Previously:
const width = 200; const stencil = new ui.Stencil({ /* ... */ width, layout: { marginY: 70, columns: 1, rowHeight: 'compact', dx: 20, dy: 10, resizeToFit: false } });
Version 4.0.0:
const width = 200; const stencil = new ui.Stencil({ /* ... */ width, layout: { marginY: 80, marginX: 20, columns: 1, columnWidth: width / 2 - 10, rowHeight: 'compact', columnGap: 20, rowGap: 10, } });
- Previously when the
-
ui.TreeLayoutView - Plugin no longer changes paper interactivity
Previously, the plugin was disabling the paper interactivity for all views.
This change ensures that the default interaction is prevented only for elements being dragged in the tree.
- The
canInteract(elementView, evt)
callback now receivesevt
as the second parameter.
Migration Guide
If you didn’t use the
canInteract()
callback, the change has no impact on you.Previously:
const paper = new dia.Paper({ /* ... */ }); /* ... */ const treeView = new ui.TreeLayoutView({ paper, model: treeLayout, canInteract: function(elementView) { return elementView.model.get('disabled'); } });
Version 4.0.0:
const paper = new dia.Paper({ /* ... */ interactive: false // prevent the movement of `disabled` elements }); /* ... */ const treeView = new ui.TreeLayoutView({ paper, model: treeLayout, canInteract: function(elementView) { return elementView.model.get('disabled'); } });
- The
-
versionRappid -
versionRappid
property has been renamed toversionPlus
Migration Guide
Previously:
console.log(joint.versionRappid) // => '3.7.7'
Version 4.0.0:
console.log(joint.versionPlus) // => '4.0.0'
-
@types - Remove
dia.Paper
format functions, anddia.Graph
util functionsMigration Guide
Use
joint.format
andjoint.graphUtils
namespaces instead.- If you were using
paper.toSVG(callback, options)
, usejoint.format.toSVG(paper, callback, options)
instead, etc. - The same is applies to
dia.Graph
. Instead ofgraph.shortestPath(source, target, options)
, usejoint.graphUtils(graph, source, target, options)
.
In light of these changes the types were moved to their corresponding namespaces
- format.Print, format.Raster, format.SVG
- Types have been moved from
dia.Paper
namespace toformat
namespace.
- Types have been moved from
- graph-utils
- Types related to graph-utils from
dia.Graph
namespace have been removed.
- Types related to graph-utils from
format.Print, format.Raster, format.SVG
If you were using types for format plugins from the
dia.Paper
namespace, they are now accessible from the theformat
namespace instead.Previously:
import { dia } from '@joint/plus'; const options: dia.Paper.RasterExportOptions;
Version 4.0.0:
import { format } from '@joint/plus'; const options: format.RasterExportOptions;
Here is a full list of changed types:
dia.Paper.PrintActions
->format.PrintActions
dia.Paper.PrintUnits
->format.PrintUnits
dia.Paper.PrintExportOptions
->format.PrintExportOptions
dia.Paper.BeforeSerialize
->format.BeforeSerialize
dia.Paper.SVGExportOptions
->format.SVGExportOptions
dia.Paper.CanvasExportOptions
->format.CanvasExportOptions
dia.Paper.RasterExportOptions
->format.RasterExportOptions
graph-utils
If you were using types for graph-utils from the
dia.Graph
namespace, they are now accessible from thegraphUtils
namespace instead.Previously:
import { dia } from '@joint/plus'; const config: dia.Graph.ConstructTreeConfig;
Version 4.0.0:
import { graphUtils } from '@joint/plus'; const config: graphUtils.ConstructTreeConfig;
Here is a full list of changed types:
dia.Graph.ShortestPathOptions
->graphUtils.ShortestPathOptions
dia.Graph.ConstructTreeNode
->graphUtils.ConstructTreeNode
dia.Graph.ConstructTreeConfig
->graphUtils.ConstructTreeConfig
- If you were using
-
dia.Graph - Throw exception when cell constructor not found
Throws an exception when a new cell is to be created from JSON if a type does not refer to a constructor.
This is to solve the recurring issue with
dia.ElementView: markup required
.-
The
dia.ElementView: markup required
error is thrown when a cell constructor is found, but the model defines no markup.const MyElement = dia.Element.define('MyElement'); const graph = new dia.Graph({}, { cellNamespace: { MyElement }}); graph.addCell({ type: 'MyElement' }); const paper = new dia.Paper({ model: graph, frozen: true }); paper.unfreeze(); // throws `dia.ElementView: markup required`
-
The new
dia.Graph: Could not find cell constructor for type: 'MyElement'. Make sure to add the constructor to 'cellNamespace'.
is thrown when the graph can not find a constructor for given cell type (e.g. when callinggraph.fromJSON())
.const MyElement = dia.Element.define('MyElement'); const graph = new dia.Graph({}, { cellNamespace: { /* MyElement is not defined here */ }}); graph.addCell({ type: 'MyElement' }); // throws `dia.Graph: Could not find cell constructor...`
Migration Guide
Previously:
graph.addCell(new dia.Element); // throws `dia.Graph: cell type must be a string.` graph.addCell(new dia.Link); // the link was successfully added to the graph
Version 4.0.0:
graph.addCell(new dia.Element); // throws `dia.Graph: cell type must be a string.` graph.addCell(new dia.Link); // throws `dia.Graph: cell type must be a string.`
If you only work with models, and don’t intend to draw links or use a custom link view, you can add a type to the link.
graph.addCell(new dia.Link({ type: 'link' })); // the link was successfully added to the graph
In other scenarios, please create a custom link (
dia.Link.define('MyLink', /* ... */)
), or use built-in links such asstandard.Link
. -
-
dia.Paper - Change the default cell sorting to
APPROX
typeDetails...
You can revert the change by setting the paper
sorting
option back todia.Paper.sorting.EXACT
.paper.options.sorting = dia.Paper.sorting.EXACT;
However, it’s recommended to refrain from using
EXACT
sorting for performance reasons, and due to the existence of this issue.
-
dia.Paper - Remove deprecated
dia.Paper.prototype.options.perpendicularLinks
Migration Guide
Instead of using
perpendicularLinks: true
, set the defaultAnchor paper option to the perpendicular connection point.paper.options.defaultAnchor = { name: 'perpendicular' };
-
dia.Paper, util - Remove deprecated
dia.Paper.prototype.options.linkConnectionPoint
, and deprecatedutil.shapePerimeterConnectionPoint
Migration Guide
Instead of using
linkConnectionPoint: util.shapePerimeterConnectionPoint
, set the defaultConnectionPoint paper option to boundary connection point.paper.options.defaultConnectionPoint = { name: 'boundary' };
-
dia.Paper - Change the value of the
defaultConnectionPoint
option toboundary
to make the diagrams more visually appealing by default.Migration Guide
To use the
bbox
connection point set thedefaultConnectionPoint
paper option as shown below:new dia.Paper({ /* ... */ defaultConnectionPoint: { name: 'bbox' } });
-
dia.Paper - Add SVG grid layer
The grid is now rendered as an SVG document inside the paper layer.
- Paper transformations are applied to it in the same way as to cells, i.e. the grid does not need to be completely redrawn when the paper is transformed (unlike the previous solution with a
HTMLDivElement
CSS background). - The grid can now also be easily exported as SVG/PNG.
Migration Guide
API change. Drop
drawGrid()
andclearGrid()
methods.Previously:
// show the grid paper.setGrid('mesh'); paper.drawGrid(); // remove the grid paper.clearGrid(); // change color of grid const paper = new dia.Paper({ drawGrid: { name: 'dots', color: 'red' }}); paper.drawGrid({ color: 'blue' }); // paper.options.drawGrid value const paper = new dia.Paper({ drawGrid: { name: 'dots' }}); paper.drawGrid({ name: 'mesh; }); // paper.options.drawGrid === { name: 'dots' }
Version 4.0.0:
// show the gird paper.setGrid('mesh'); // remove the grid paper.setGrid(null); // change color of grid const paper = new dia.Paper({ drawGrid: { name: 'dots', color: 'red' }}); paper.setGrid({ name: 'dots', color: 'blue' }); // paper.options.drawGrid value const paper = new dia.Paper({ drawGrid: { name: 'dots' }}); paper.setGrid({ name: 'mesh' }); // paper.options.drawGrid === { name: 'mesh' }
Unless you somehow rely on the structure of a paper SVG document, this change shouldn’t affect you in any way. The paper is missing
<div class="joint-paper-grid"/>
, and instead has a new layer<g class="joint-grid-layout" />
. - Paper transformations are applied to it in the same way as to cells, i.e. the grid does not need to be completely redrawn when the paper is transformed (unlike the previous solution with a
-
dia.Paper - Transformation events improved
Improvements to the current transformation API.
- Remove outdated methods:
origin
option removedsetOrigin()
method removedrotate()
(experimental) method removed
- Introducing a new
transform
event which is triggered after bothscale
andtranslate
are finished. - Allows passing custom data along with transformation events (similarly to
mvc.Events
).scale(sx, sy, [data])
translate(tx, ty, [data])
matrix(matrix, [data])
paper.on('scale', (sx, sy, data) => console.log(sx, sy, data.foo)); paper.on('translate', (tx, ty, data) => console.log(tx, ty, data.foo)); paper.on('transform', (matrix, data) => console.log(matrix, data.foo)); paper.matrix({ a: 2, b: 0, c: 0, d: 2, e: 10, f: 10 }, { foo: 'bar' }); // will trigger all events
-
In addition, it allows passing custom data along with the
resize
event.paper.on('resize', (width, height, data) => console.log(width, height, data.foo)); paper.setDimensions(100, 200, { foo: 'bar' });
- Simplifying the “zooming under pointer” functionality by:
- Adding a new method
scaleUniformAtPoint(scale, { x, y }, [data])
scale()
no longer accepts scaling origin- fixing the original scaling at point logic (taking the previous
scale
into account)
- Adding a new method
Migration Guide
dia.Paper.prototype.options.origin
option removedNote that passing
origin
to thedia.Paper
constructor had no effect in version 3.7. If you read the values of origin from the paper options, usepaper.translate()
(orpaper.matrix()
) instead.Previously:
const { x, y } = paper.option.origin;
Version 4.0.0:
const { tx: x, ty: y } = paper.translate();
Paper
scale
event handler arguments changedPreviously:
paper.on('scale', (sx, sy, ox, oy) => {});
Version 4.0.0:
paper.on('scale', (sx, sy, data) => { const { tx: ox, ty: oy } = paper.translate(); });
dia.Paper.prototype.setOrigin
method removedPreviously:
paper.setOrigin(100, 200);
Version 4.0.0:
paper.translate(100, 200);
scale()
no longer accepts scaling originPreviously:
// Zoom at center of the paper const center = paper.getArea.center(); paper.translate(0,0); paper.scale(zoomLevel, zoomLevel, center.x, center.y);
Version 4.0.0:
// Zoom at center of the paper const center = paper.getArea.center(); paper.scaleUniformAtPoint(zoomLevel, center);
Paper panning and zooming can be implemented simply as shown below:
paper.on('paper:pinch', function(evt, x, y, sx) { const { sx: sx0 } = paper.scale(); paper.scaleUniformAtPoint(sx0 * sx, { x, y }); }); paper.on('paper:pan', function(evt, tx, ty) { evt.preventDefault(); const { tx: tx0, ty: ty0 } = paper.translate(); paper.translate(tx0 - tx, ty0 - ty); });
- Remove outdated methods:
-
dia.Paper - Fix
paper:pinch
dispatched event typeFor consistency, all events fired on paper are passed native events wrapped in the
mvc.$.Event
wrapper.Migration Guide
Previously:
paper.on('paper:pinch', (evt) => console.log('this is a native event', evt));
Version 4.0.0:
paper.on('paper:pinch', (evt) => console.log('this is a native event', evt.originalEvent));
-
dia.Cell - Add
mergeArrays
options to constructorThe class array default attributes are now overridden instead of merged. A new option to support the previous behavior was added.
Details...
The reason for this change is to make it easier to instantiate the class, and define the initial attributes, which are arrays.
const MyRect = joint.shapes.standard.Rectangle.define('Rect', { array: [1,2] }); const rect1 = new MyRect({ array: [3] }); console.log(rect1.get('array')); // [3] array was overridden const rect2 = new MyRect({ array: [3] }, { mergeArrays: true }); console.log(rect2.get('array')); // [3,2] array was merged
-
dia.CellView - Early evaluation of calc attributes
Perform the following actions at the start of the cell view update:
- evaluate the
calc
expressions used in the presentation attributes - translate (e.g. to dash-case) the presentation attribute names
This way, their evaluated value, and their true name become available for anyone dealing with them later (it’s no longer needed to evaluate the
calc()
value multiple times or check for the existence of an attribute under different names).Migration Guide
This is only a breaking change if you define custom attributes (undocumented yet).
Previously:
const MyElement = dia.Element.define('MyElement', { root: { myAttribute: 5 } }, { /* prototype */ }, { /* static */ attributes: { // the name must match the attribute as used on the model `root/myAttribute`. myAttribute: { set: function(value, bbox, node, attrs) { return value + (attrs['myOtherAttribute'] || attrs['my-other-attribute']); } } } });
Version 4.0.0:
const MyElement = dia.Element.define('MyElement', { root: { myAttribute: 5 // could be dash-cased or camel-cased } }, { /* prototype */ }, { /* static */ attributes: { // must be dash-cased (more precisely, it must be the true name returned by `V.attributeNames`) 'my-attribute': { set: function(value, bbox, node, attrs) { return value + attrs['my-other-attribute']; // always `true` name } } } });
- evaluate the
-
dia.CellView - Disable
useCSSSelectors
by defaultUse of CSS selectors within the model’s
attrs
is now disabled by default.Migration Guide
Previously:
joint.dia.Element.define('Rectangle', { attrs: { '.rectangle': { // CSS Selector for the <rect /> element fill: 'red' } } }, { markup: '<rect class="rectangle"/>', });
Version 4.0.0 (quick fix - per shape):
joint.dia.Element.define('Rectangle', { attrs: { '.rectangle': { // CSS Selector for the <rect /> element fill: 'red' } } }, { markup: '<rect class="rectangle"/>', useCSSSelectors: true });
Version 4.0.0 (quick fix - global):
joint.config.useCSSSelectors = true; joint.dia.Element.define('Rectangle', { attrs: { '.rectangle': { // CSS Selector for the <rect /> element fill: 'red' } } }, { markup: '<rect class="rectangle"/>' });
Version 4.0.0 (recommended fix):
joint.dia.Element.define('Rectangle', { attrs: { rectangle: { // JSON Selector for the <rect /> element fill: 'red' } } }, { markup: util.svg`<rect @selector="rectangle" />`, });
-
dia.Link - Replace legacy attributes in the default label definition
Legacy attributes in the left colum have now been replaced with the attributes in the right column.
Previously Version 4.0.0 yAlignment
textVerticalAnchor
refX
x
refY
y
refWidth
width
refHeight
height
Migration Guide
If you were changing any of the label legacy attributes (left-side) in your application, you should change it to its new form (right-side).
-
dia.Link - Remove the deprecated
smooth
attribute (syntactic sugar for thesmooth
connector), and the obsoletemanhattan
attribute (syntactic sugar fororthogonal
router)Migration Guide
Previously:
link.set('smooth', true); link.set('manhattan', true);
Version 4.0.0:
link.connector('smooth'); // or link.set('connector', { name: 'smooth' }); link.router('orthogonal'); // or link.set('router', { name: 'orthogonal' });
-
dia.LinkView - Remove support for legacy features of
joint.dia.LinkView
(It has long been deprecated due to poor performance caused by built-in link tools that had to be rendered for each link at start-up (even though they are only visible when the user hovers over them))Details...
joint.dia.Link
has no markup defined, and effectively becomes an abstract class- drop support for semantic string markup (
connection
,connection-wrap
,marker-source
,marker-target
,marker-vertices
,marker-arrowheads
, andlink-tools
in the markup no longer affect user interactivity) - drop support for
vertexAdd
,vertexRemove
,vertexMove
,arrowheadMove
, anduseLinkTools
interactivity paper options - the following protected methods have been removed for LinkView:
dragConnectionStart
,dragConnection
,dragConnectionEnd
,dragVertexStart
,dragVertex
,dragVertexEnd
- the paper
defaultLink
is nowstandard.Link
The
linkTools.Vertices
tools now accept 2 new options:vertexAdding
andvertexRemoving
Migration Guide
Switch to
joint.shapes.standard.Link
, and add link tools dynamically.For the interactive options:
vertexAdd
,vertexRemove
&vertexMove
- addlinkTools.Vertices
to enable users to interact with verticesnew linkTools.Vertices({ vertexAdding: boolean, vertexMoving: boolean, vertexRemoving: boolean })
arrowheadMove
- addlinkTools.Arrowhead
to enable arrowhead moveuseLinkTools
- addlinkTools.Button
to the link view
-
shapes.basic - Remove basic legacy shapes from
joint-core
packagejoint.shapes.basic
were removed completely in favor ofjoint.shapes.standard
shapespn
,uml
,logic
,org
,chess
,fsa
shapes were removed fromjoint-core
, and are implemented only as custom shapes within the demos
Migration Guide
You can copy the shape definition from
v3.7
directly to your application.Examples can be found: umlcd, umlsc, pn, logic, org, fsa, erd.
-
shapes.devs - Remove devs legacy shapes from
joint-core
packagejoint.shapes.devs
were removed completely in favor ofjoint.shapes.standard
shapesdevs
shapes were removed fromjoint-core
, and are implemented only as custom shapes within the demos
Migration Guide
You can copy the shape definition from
v3.7
directly to your application.An example can be found here: devs.
-
shapes.standard - Update standard shapes by removing legacy attributes such as
refWidth
,refHeight
,refX
,refY
, etc, and use native SVG attributes withcalc
expressionsDetails...
For instance
refWidth: 100%;
is replaced withwidth: 'calc(w)'
.If you change these values in your application (e.g. by changing the
refD
attribute of the path, or changing the position of the label usingrefY
), this is another breaking change.Migration Guide
- a. You can copy the shape definition from
v3.7
directly to your application, and override the built-in ones. - b. You need to switch from
ref*
attributes to native attributes in your code, and you might also need to change the attributes in any JSON you try to import (should the JSON contain modifiedstandard
shapesref*
attributes).
- a. You can copy the shape definition from
-
highlighters.opacity - Add
alphaValue
toopacity
highlighter to control the value of transparencyMigration Guide
Opacity no longer adds a CSS class to the node. It’s set to
opacity
via the inline CSS style attribute. Ifopacity
was set on a node via the inline attribute, the original value will be removed when the highlighter is removed.If I add a highlighter to such a node, the
opacity
property will be removed along with the highlighter.<rect style="opacity: 0.5"/>
In this case, everything works as before.
<rect opacity="0.5"/>
If you need to support the
opacity
in the inline style attribute, usehighlighter.addClass
instead.highlighters.addClass.add(cellView, selector, id, { className: 'highlight-opacity' });
.highlight-opacity { opacity: 0.3 }
-
highlighters.stroke - Add
nonScalingStroke
option tostroke
highlighter to addvector-effect=non-scaling-stroke
to the highlighterMigration Guide
It’s visually a breaking change because the highlighter now scales with the paper (when the zoom level of the paper changes, the stroke thickness changes too).
Previously:
highlighters stroke.add(elementView, 'body', id);
Version 4.0.0:
highlighters stroke.add(elementView, 'body', id, { nonScalingStroke: true });
-
layout.DirectedGraph, dia.Graph - Remove the
DirectedGraph
module from thejoint.layout
namespace, and removedia.Graph.toGraphLib()
anddia.Graph.fromGraphLib()
functionsdagre
andgraphlib
are imported via the standard process. It’s no longer necessary to pass instances ofdagre
andgraphlib
as options tolayout()
andtoGraphLib()
methods.Migration Guide
If you need the DirectedGraph functionality (
joint.layout.DirectedGraph.___
), import it from@joint/layout-directed-graph
(e.g.import { DirectedGraph } from @joint/layout-directed-graph
). The functionality of the three exported functions is unchanged:const bbox = DirectedGraph.layout(graph, { opt });
const glGraph = DirectedGraph.toGraphLib(graph, { opt });
const graph = DirectedGraph.fromGraphLib(glGraph, { opt });
If you were previously using
dia.Graph.toGraphLib()
, use thetoGraphLib()
function imported from@joint/layout-directed-graph
instead. Thegraph
is passed in as the first argument of the replacement function:const glGraph = graph.toGraphLib({ opt });
becomesconst glGraph = DirectedGraph.toGraphLib(graph, { opt });
Similarly, if you were previously using
dia.Graph.fromGraphLib()
, use thefromGraphLib()
function imported from@joint/layout-directed-graph
instead. Thegraph
is passed in as the context of the replacement function:graph.fromGraphLib(glGraph, { opt });
becomesDirectedGraph.fromGraphLib.call(graph, glGraph, { opt });
-
linkTools, elementTools - As per documentation “The names of built-in link tools are kebab-case versions of their class names”.
This was not the case for the
linkTools.Remove
andelementTools.Remove
buttons.Migration Guide
You may have styled the
Remove
button using CSS in your app.Previously:
.joint-tool[data-tool-name="button"] circle { fill: #333; }
Version 4.0.0:
.joint-tool[data-tool-name="remove"] circle { fill: #333; }
-
attributes.filter - Change how default filters are rendered (change the coordinate system of the filters from
objectBoundingBox
touserSpaceOnUse
)This fixes applying filters to shapes with a width or height of
0
(such as horizontal/vertical links).Migration Guide
If you need your filter to look the same as the previous version, you can add an
attrs
object to your filter as shown below.element1.attr('body/filter', { // for backwards compatibility attrs: { filterUnits: 'objectBoundingBox', x: -1, y: -1, width: 3, height: 3 }, name: 'dropShadow', args: { dx: 2, dy: 2, blur: 3 } });
-
Vectorizer - Enable camel case attribute support by default, and make the
attributeNames
property publicThe
supportCamelCaseAttributes
property remains private.Previously:
V('g').attr('myAttribute', 'value') // <g myAttribute="value"/>
Version 4.0.0:
V('g').attr('myAttribute', 'value') // <g my-attribute="value"/>
Allows you to use camel case attribute names anywhere in JointJS. e.g
targetMarker
,mvc.View.prototype.attributes
Migration Guide
If you need any attribute to stay camel cased, you have to define it through the
attributeNames
property.V.attributeNames['myAttribute'] = 'myAttribute'; V('g').attr('myAttribute', 'value') // <g myAttribute="value"/>
-
CSS - JointJS is no longer distributed with CSS
Details...
- JointJS is no longer distributed with CSS (
joint.css
). - Custom shapes have no
cursor
set by default. It’s the user’s responsibility to define the right cursor for their shapes. Thestandard
shapes now have a cursor set via an SVG attribute. - Magnets are no longer opaque on hover and have no
cursor: crosshair
. - There are no themes defined in JointJS (they were only used to style the legacy LinkView, and to change the color of the paper).
- Font
lato-light
is no longer installed.
Migration Guide
- Do not load
joint.css
in your application. For instance: Previously:<link rel="stylesheet" type="text/css" href="joint.css" />
Version 4.0.0:<link rel="stylesheet" type="text/css" href="joint.css" />
- Define a
cursor
for your shapes to give the user a hint that they can drag & drop the element. In CSS:.joint-element { cursor: move; }
Or in JavaScript:
element.attr('root/cursor', 'move');
- Add CSS to your application.
[magnet=true]:hover { opacity: 0.7 }
- To change the paper color use the background paper option.
const paper = new dia.Paper({ /* ... */ background: { color: 'black' } })
- Download the font from latofonts.com.
- JointJS is no longer distributed with CSS (
Other Additions:
-
Add Tree of life demo
Explore evolutionary relationships while looking at pressure-sensitive freehand lines, and seeing how we utilized
SVGTextPathElement
to wrap text.You can also view the source code on GitHub.
Important Notes:
-
JointJS open-source is now using a monorepo architecture with workspaces.
If you want to contribute to JointJS, you will now need to ensure Yarn is installed on your system locally. Instructions on how to contribute to JointJS can be found in the relevant repository README file.
Note: JointJS open-source can still be installed from the
npm
registry via the cli tool of your choice such asnpm
. As noted at the beginning of the changelog, to utilizev4
of JointJS, you should install the@joint/core
package. -
JointJS now utilizes ES2020 features in its source code.
If your application is using old versions of some JavaScript tooling such as Webpack version 4, it may be time to update. Webpack 4 doesn’t support some ES2020 features like nullish coalescing, or optional chaining. You can read about it more in the following Webpack issue.