JointJS+ changelog v4.0.0
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, you can remove the script
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)
function
This function handles the import process. It takes an XMLDocument
containing a diagram and an options object. The inclusion of the options
parameter provides the flexibility to customize the import process to meet specific requirements.
The import options object has the following 2 properties - cellFactories
and bpmn2Shapes
. They are a collection of cellFactories
mapped by decisive XML elements, and a namespace that contains definitions of JointJS bpmn2Shapes
respectively.
Basic usage
The most basic way to use the package is to convert an XMLDocument
into JointJS cells by passing the document to the fromBPMN
function without specifying the value of the cellFactories
property in the options
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 = {})
function
This function handles the export process. It takes a joint.dia.Paper
containing a diagram and optionally the options
object. The inclusion of the options
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 calling toBPMN
with the paper
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 documentation
The 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 the Cell.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 the redundancyRemoval
option is disabled.
Breaking changes
jointjs dependency deprecated in favour of @joint/core
- To utilize v4
of the JointJS library, install the @joint/core
dependency instead of jointjs
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
dependency
jQuery 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.
- 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).
Backbone removal - Remove backbone
dependency
Backbone related functionality is now handled with internal code under the mvc
namespace. You will now find mvc.Events
, mvc.Model
, mvc.View
, and mvc.Collection
in the JointJS library.
-
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. The backbone
dependency will no longer be installed when working with JointJS.
format.Print - Use native DOM over jQuery elements in ready
function
In previous versions, pages were represented as a collection of jQuery objects. After the change, pages are now represented as a collection of HTMLDivElement
objects.
Note the change in the ready
function signature…
Previously:
const options = {
/**
* @param {Array<jQuery>} pages
* @param {function} readyToPrint
* @param {{sheetSizePx: {width: number, height: number, cssWidth: string, cssHeight: string}}} 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 {{sheetSizePx: {width: number, height: number, cssWidth: string, cssHeight: string}}} 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).
dx
and dy
Deprecated options dx
and dy
were removed in favor of rowGap
and columnGap
.
Note, that previously dx
and dy
were also having an impact on overall layout margin, so you may want to adjust marginX
and marginY
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 of verticalAlign
and horizontalAlign
. Previously center
was true
by default, so to keep the default behaviour, the same default values of verticalAlign
and horizontalAlign
options were changed to middle
.
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 setting verticalAlign
and horizontalAlign
options, you might want to specify their values as top
and left
respectively to preserve old behaviour.
Previously:
layout.GridLayout.layout(graph, {
/* ... */
center: false,
});
Version 4.0.0:
layout.GridLayout.layout(graph, {
/* ... */
verticalAlign: 'top',
horizontalAlign: 'left',
});
shapes.bpmn - Remove shapes.bpmn
from source code and JointJS+ package
Redefine bpmn.Choreography
, and make it available through the bpmn2
namespace.
The bpmn2
namespace should be used in place of shapes.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
},
});
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 coordinates
Fixed incorrect behaviour of the scroll()
method in PaperScroller. Previously, the scroll()
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 the scroll()
method.
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 animation
- 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,
},
});
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.
If you used your custom layout
function, or were using layout: 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.Stencil layout
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 from ui.Stencil
, you should explicitly specify them in your layout
object. The default merged layout
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,
},
});
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.
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');
},
});
versionRappid - versionRappid
property has been renamed to versionPlus
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, and dia.Graph
util functions
Use joint.format
and joint.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 the format
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 the graphUtils
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
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...`
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 as standard.Link
.
dia.Paper - Change the default cell sorting to APPROX
type
Details...
You can revert the change by setting the paper sorting
option back to dia.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
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 deprecated util.shapePerimeterConnectionPoint
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 to boundary
to make the diagrams more visually appealing by default.
To use the bbox
connection point set the defaultConnectionPoint
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.
API change. Drop drawGrid()
and clearGrid()
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" />
.
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
dia.Paper.prototype.options.origin
option removed
Note that passing origin
to the dia.Paper
constructor had no effect in version 3.7.
If you read the values of origin from the paper options, use paper.translate()
(or paper.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 changed
Previously:
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 removed
Previously:
paper.setOrigin(100, 200);
Version 4.0.0:
paper.translate(100, 200);
scale()
no longer accepts scaling origin
Previously:
// 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);
});
dia.Paper - Fix paper:pinch
dispatched event type
For consistency, all events fired on paper are passed native events wrapped in the mvc.$.Event
wrapper.
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 constructor
The 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).
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
},
},
},
}
);
dia.CellView - Disable useCSSSelectors
by default
Use of CSS selectors within the model's attrs
is now disabled by default.
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 |
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 the smooth
connector), and the obsolete manhattan
attribute (syntactic sugar for orthogonal
router)
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
and vertexRemoving
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 move -
useLinkTools
- addlinkTools.Button
to the link view
shapes.basic - Remove basic legacy shapes from joint-core
package
joint.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
shapes.devs - Remove devs legacy shapes from joint-core
package
joint.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
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 with calc
expressions
Details...
For instance refWidth: 100%;
is replaced with width: '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 using refY
), this is another breaking change.
- 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).
highlighters.opacity - Add alphaValue
to opacity
highlighter to control the value of transparency
Opacity no longer adds a CSS class to the node. It's set to opacity
via the inline CSS style attribute. If opacity
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, use highlighter.addClass
instead.
highlighters.addClass.add(cellView, selector, id, {
className: 'highlight-opacity',
});
.highlight-opacity {
opacity: 0.3;
}
highlighters.stroke - Add nonScalingStroke
option to stroke
highlighter to add vector-effect=non-scaling-stroke
to the highlighter
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 the joint.layout
namespace, and remove dia.Graph.toGraphLib()
and dia.Graph.fromGraphLib()
functions
dagre
and graphlib
are imported via the standard process. It's no longer necessary to pass instances of dagre
and graphlib
as options to layout()
and toGraphLib()
methods.
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 the toGraphLib()
function imported from @joint/layout-directed-graph
instead. The graph
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 the fromGraphLib()
function imported from
@joint/layout-directed-graph
instead. The graph
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
and elementTools.Remove
buttons.
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
to userSpaceOnUse
)
This fixes applying filters to shapes with a width or height of 0
(such as horizontal/vertical links).
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 public
The 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
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.
-
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.
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 as npm
. As noted at the beginning of the changelog, to utilize v4
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.