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.jsonSince 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
mvcnamespace.jQuery, backbone, and lodash will no longer be installed when working with JointJS. If you are specifying these dependencies in a
scripttag as demonstrated in our installation tutorial, you can remove thescripttags 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
XMLDocumentcontaining a diagram and an options object. The inclusion of theoptionsparameter provides the flexibility to customize the import process to meet specific requirements.The import options object has the following 2 properties -
cellFactoriesandbpmn2Shapes. They are a collection ofcellFactoriesmapped by decisive XML elements, and a namespace that contains definitions of JointJSbpmn2Shapesrespectively.Basic usage
The most basic way to use the package is to convert an
XMLDocumentinto JointJS cells by passing the document to thefromBPMNfunction without specifying the value of thecellFactoriesproperty in theoptionsobject.The resulting cells are stored in the
cellsproperty 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.Papercontaining a diagram and optionally theoptionsobject. The inclusion of theoptionsparameter 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
optionsparameter. It’s as simple as callingtoBPMNwith thepaperas 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
gridoption to allow users to render the grid in the exported SVG
- format.Raster - Add
gridoption to allow users to render the grid in the exported image
- format.Print - Add
gridoption to allow users to display a grid on the print page
- ui.Halo - Fix to
makeLoopLinkoption 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:mouseleaveevent when the user stops dragging a vertex. The pointer is no longer over the vertex, and theredundancyRemovaloption is disabled.
Breaking Changes
-
jointjs dependency deprecated in favour of
@joint/core- To utilizev4of the JointJS library, install the@joint/coredependency instead ofjointjsMigration 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/coredependency from the npm registry.Previously:
npm i jointjs yarn add jointjsVersion 4.0.0:
npm i @joint/core yarn add @joint/core
-
jQuery removal - Remove
jquerydependencyjQuery related functionality is now handled with internal code under the
mvcnamespace. 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
jQueryselectors. 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
:focuson WebKit) - Selectors can not start with combinators (e.g
> divneeds to be converted to:scope > div)
- Attribute not equal selector (
view.$elandview.$()are no longer public properties. Useview.elandview.el.querySelectorAll()instead.- Remove the following protected properties from the
paper:paper.$document,paper.$gridandpaper.$background. utils.sortElementreturns a plain array of Elements, not a$object.- It’s no longer possible to access
CellViewfrom 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
backbonedependencyBackbone related functionality is now handled with internal code under the
mvcnamespace. You will now findmvc.Events,mvc.Model,mvc.View, andmvc.Collectionin the JointJS library.Migration Guide
-
mvc.Model - All lodash related methods have been removed from
mvc.Model, along with any methods related toBackbone.Syncfunctionality. -
mvc.Collection - Most lodash related methods have been removed from
mvc.Collection, along with any methods related toBackbone.Syncfunctionality. The followingcollectionmethods 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. Thebackbonedependency will no longer be installed when working with JointJS. -
-
format.Print - Use native DOM over jQuery elements in
readyfunctionIn previous versions, pages were represented as a collection of jQuery objects. After the change, pages are now represented as a collection of
HTMLDivElementobjects.Migration Guide
Note the change in the
readyfunction 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,dyandcenterwere removed. - Added options -
verticalAlignandhorizontalAlign. - Default values for
verticalAlignandhorizontalAlignoptions are'middle'now (previously it was'top'and'left'respectively).
Migration Guide
dxanddyDeprecated options
dxanddywere removed in favor ofrowGapandcolumnGap.Note, that previously
dxanddywere also having an impact on overall layout margin, so you may want to adjustmarginXandmarginYoptions 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
centeroption was removed in favor ofverticalAlignandhorizontalAlign. Previouslycenterwastrueby default, so to keep the default behaviour, the same default values ofverticalAlignandhorizontalAlignoptions were changed tomiddle.If you didn’t specify the
centeroption in your layout, the change has no impact on you.If you were using
center: falsewithout settingverticalAlignandhorizontalAlignoptions, you might want to specify their values astopandleftrespectively 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.bpmnfrom source code and JointJS+ packageRedefine
bpmn.Choreography, and make it available through thebpmn2namespace.Migration Guide
The
bpmn2namespace 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.animatewhich no longer supports scroll animationMigration Guide
- The
opt.animationin scroll functions no longer accepts parameters of jQuery’sanimatemethod. - The
opt.animationnow 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
layoutoption was provided as an object, the passed object was merged with the defaultGridLayoutoptions. Now, thelayoutobject will be used as it is for the purpose of thelayout.GridLayoutplugin. The defaultGridLayoutoptions will be used only when usinglayout: true, or another truthy value.
Migration Guide
If you used your custom
layoutfunction, or were usinglayout: true, the change has no impact on you.Previously, if you specified the
layoutoption as an object, the values would be merged with the default ui.Stencillayoutoptions. 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
layoutoptions fromui.Stencil, you should explicitly specify them in yourlayoutobject. The default mergedlayoutobject 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 receivesevtas 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 -
versionRappidproperty has been renamed toversionPlusMigration Guide
Previously:
console.log(joint.versionRappid) // => '3.7.7'Version 4.0.0:
console.log(joint.versionPlus) // => '4.0.0'
-
@types - Remove
dia.Paperformat functions, anddia.Graphutil functionsMigration Guide
Use
joint.formatandjoint.graphUtilsnamespaces 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.Papernamespace toformatnamespace.
- Types have been moved from
- graph-utils
- Types related to graph-utils from
dia.Graphnamespace 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.Papernamespace, they are now accessible from the theformatnamespace 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.PrintActionsdia.Paper.PrintUnits->format.PrintUnitsdia.Paper.PrintExportOptions->format.PrintExportOptionsdia.Paper.BeforeSerialize->format.BeforeSerializedia.Paper.SVGExportOptions->format.SVGExportOptionsdia.Paper.CanvasExportOptions->format.CanvasExportOptionsdia.Paper.RasterExportOptions->format.RasterExportOptions
graph-utils
If you were using types for graph-utils from the
dia.Graphnamespace, they are now accessible from thegraphUtilsnamespace 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.ShortestPathOptionsdia.Graph.ConstructTreeNode->graphUtils.ConstructTreeNodedia.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 requirederror 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 graphVersion 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 graphIn 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
APPROXtypeDetails...
You can revert the change by setting the paper
sortingoption back todia.Paper.sorting.EXACT.paper.options.sorting = dia.Paper.sorting.EXACT;However, it’s recommended to refrain from using
EXACTsorting for performance reasons, and due to the existence of this issue.
-
dia.Paper - Remove deprecated
dia.Paper.prototype.options.perpendicularLinksMigration 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.shapePerimeterConnectionPointMigration 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
defaultConnectionPointoption toboundaryto make the diagrams more visually appealing by default.Migration Guide
To use the
bboxconnection point set thedefaultConnectionPointpaper 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
HTMLDivElementCSS 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:
originoption removedsetOrigin()method removedrotate()(experimental) method removed
- Introducing a new
transformevent which is triggered after bothscaleandtranslateare 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
resizeevent.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
scaleinto account)
- Adding a new method
Migration Guide
dia.Paper.prototype.options.originoption removedNote that passing
originto thedia.Paperconstructor 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
scaleevent 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.setOriginmethod 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:pinchdispatched event typeFor consistency, all events fired on paper are passed native events wrapped in the
mvc.$.Eventwrapper.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
mergeArraysoptions 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
calcexpressions 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
useCSSSelectorsby defaultUse of CSS selectors within the model’s
attrsis 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 yAlignmenttextVerticalAnchorrefXxrefYyrefWidthwidthrefHeightheightMigration 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
smoothattribute (syntactic sugar for thesmoothconnector), and the obsoletemanhattanattribute (syntactic sugar fororthogonalrouter)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.Linkhas 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-toolsin the markup no longer affect user interactivity) - drop support for
vertexAdd,vertexRemove,vertexMove,arrowheadMove, anduseLinkToolsinteractivity paper options - the following protected methods have been removed for LinkView:
dragConnectionStart,dragConnection,dragConnectionEnd,dragVertexStart,dragVertex,dragVertexEnd - the paper
defaultLinkis nowstandard.Link
The
linkTools.Verticestools now accept 2 new options:vertexAddingandvertexRemovingMigration Guide
Switch to
joint.shapes.standard.Link, and add link tools dynamically.For the interactive options:
vertexAdd,vertexRemove&vertexMove- addlinkTools.Verticesto enable users to interact with verticesnew linkTools.Vertices({ vertexAdding: boolean, vertexMoving: boolean, vertexRemoving: boolean })arrowheadMove- addlinkTools.Arrowheadto enable arrowhead moveuseLinkTools- addlinkTools.Buttonto the link view
-
shapes.basic - Remove basic legacy shapes from
joint-corepackagejoint.shapes.basicwere removed completely in favor ofjoint.shapes.standardshapespn,uml,logic,org,chess,fsashapes were removed fromjoint-core, and are implemented only as custom shapes within the demos
Migration Guide
You can copy the shape definition from
v3.7directly to your application.Examples can be found: umlcd, umlsc, pn, logic, org, fsa, erd.
-
shapes.devs - Remove devs legacy shapes from
joint-corepackagejoint.shapes.devswere removed completely in favor ofjoint.shapes.standardshapesdevsshapes were removed fromjoint-core, and are implemented only as custom shapes within the demos
Migration Guide
You can copy the shape definition from
v3.7directly 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 withcalcexpressionsDetails...
For instance
refWidth: 100%;is replaced withwidth: 'calc(w)'.If you change these values in your application (e.g. by changing the
refDattribute 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.7directly 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 modifiedstandardshapesref*attributes).
- a. You can copy the shape definition from
-
highlighters.opacity - Add
alphaValuetoopacityhighlighter to control the value of transparencyMigration Guide
Opacity no longer adds a CSS class to the node. It’s set to
opacityvia the inline CSS style attribute. Ifopacitywas 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
opacityproperty 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
opacityin the inline style attribute, usehighlighter.addClassinstead.highlighters.addClass.add(cellView, selector, id, { className: 'highlight-opacity' });.highlight-opacity { opacity: 0.3 }
-
highlighters.stroke - Add
nonScalingStrokeoption tostrokehighlighter to addvector-effect=non-scaling-stroketo 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
DirectedGraphmodule from thejoint.layoutnamespace, and removedia.Graph.toGraphLib()anddia.Graph.fromGraphLib()functionsdagreandgraphlibare imported via the standard process. It’s no longer necessary to pass instances ofdagreandgraphlibas 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-graphinstead. Thegraphis 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-graphinstead. Thegraphis 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.RemoveandelementTools.Removebuttons.Migration Guide
You may have styled the
Removebutton 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
objectBoundingBoxtouserSpaceOnUse)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
attrsobject 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
attributeNamesproperty publicThe
supportCamelCaseAttributesproperty 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.attributesMigration Guide
If you need any attribute to stay camel cased, you have to define it through the
attributeNamesproperty.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
cursorset by default. It’s the user’s responsibility to define the right cursor for their shapes. Thestandardshapes 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-lightis no longer installed.
Migration Guide
- Do not load
joint.cssin 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
cursorfor 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
SVGTextPathElementto 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
npmregistry via the cli tool of your choice such asnpm. As noted at the beginning of the changelog, to utilizev4of JointJS, you should install the@joint/corepackage. -
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.