Back to jointjs.com.
CHANGELOG
- add TypeScript shape definition tutorial
-
apps.Database - design an SQL database with this database designer tool
ES2018 & TypeScript version
-
apps.Tokens - animated tokens sent along links
ES2018 & TypeScript version
Play and see the data in the system at any given time.
-
apps.TreeStencil - multi-level stencil demo
ES2018 & TypeScript version
The demo shows that the
ui.Stencil
is just a set of papers. Anything you can do withdia.Paper
, you can do with the stencil.
-
apps.BPMNEditor - add tools for resizing swimlanes
-
demo.Bandwidth - add carrier frequency bandwidth demo
-
demo.VueJS - add VueJS 3.0 demo
-
format.GEXF - support more visualization attributes (label, size, position, shape, color)
<?xml version="1.0" encoding="UTF-8"?> <gexf xmlns="http://www.gexf.net/1.2draft" xmlns:viz="http://www.gexf.net/1.1draft/viz" version="1.2"> <graph mode="static" defaultedgetype="directed"> <nodes> <node id="0" label="Hello"> <viz:position x="15.0" y="40.0" z="0.0"/> <viz:size value="50" /> <viz:color r="239" g="173" b="66" a="0.6"/> </node> <node id="1" label="World"> <viz:position x="15.0" y="140.0" z="0.0"/> <viz:size value="50" /> <viz:color r="239" g="173" b="66" a="0.6"/> </node> </nodes> <edges> <edge id="0" source="0" target="1" /> </edges> </graph> </gexf>
import { format } from '@clientio/rappid'; const cells = format.gexf.toCellsArray( xmlString, function makeElement(data) { return new joint.shapes.standard.Rectangle({ id: data.id, position: { x: data.x, y: data.y }, size: { width: data.width, height: data.height }, attrs: { label: { text: data.label }, body: { fill: data.color } } }); }, function makeLink(data) { return new joint.shapes.standard.Link({ source: { id: data.source }, target: { id: data.target } }); } ); graph.resetCells(cells);
-
format.Print - add
format.print()
to enable no side-effects// No side-effects import { format } from '@clientio/rappid'; format.print(paper, options); // Use only when sideEffects: true // print() method added to the paper's prototype paper.print(options);
-
format.Raster - add
toCanvas()
method to export a paper to an HTMLCanvasimport { format } from '@clientio/rappid'; // Export the paper as an HTMLCanvas and add a watermark format.toCanvas(paper, (canvas) => { const ctx = canvas.getContext('2d'); ctx.font = '20px serif'; ctx.fillText('Watermark', 20, 40); const dataURL = canvas.toDataURL(); /* ... */ });
-
format.Visio - take SVG transformation into account in default export
Read the
rotation
,scale
andtranslate
of SVGElements to find the correct angle, size and position of a VisioShape being exported.
-
format.Visio - improve SVG attributes parsing during export
Use
getComputedStyle()
to read the attribute values (taking inheritance and external CSS into account).
-
format.Visio - fix exported pages not using paper size as intended
The size of a
VisioPage
is set based on thedia.Paper
size the export was made from.
-
format.Visio - add compression method to
VisioArchive.prototype.toVSDX
methodconst blob = await visArchive.toVSDX({ type: 'blob', compression: 'DEFLATE', compressionOptions: { /* 1 best speed, 9 best compression */ level: 9 } });
-
graphUtils: make accessible without the side-effects
// No side-effects import { graphUtils } from '@clientio/rappid'; graphUtils.constructTree(/* ... */); // Use only when sideEffects: true graph.constructTree(/* ... */);
// No side-effects import { graphUtils } from '@clientio/rappid'; graphUtils.shortestPath(graph, el1, el2); // Use only when sideEffects: true graph.shortestPath(el1, el2);
-
elementTool.SwimlaneBoundary - a new element tool to highlight swimlanes
import { elementTools, dia } from '@clientio/rappid'; /* ... */ const swimlaneBoundaryTool = new elementTools.SwimlaneBoundary({ laneId: '0' }); const poolToolsView = new dia.ToolsView({ tools: [swimlaneBoundaryTool] }); poolElementView.addTools(poolToolsView);
-
elementTools.SwimlaneTransform - a new element tool to resize swimlanes
import { elementTools, dia } from '@clientio/rappid'; /* ... */ const swimlaneTransformTool = new elementTools.SwimlaneTransform({ laneId: '0', minSize: 60 }); const poolToolsView = new dia.s({ tools: [swimlaneTransformTool] }); poolElementView.addTools(poolToolsView);
-
ui.Clipboard - fix passing options via constructor
import { ui } from '@clientio/rappid'; const clipboard = new ui.Clipboard({ translate: { dx: 20, dy: 20 }, useLocalStorage: true });
-
ui.Clipboard - fix
pasteCells()
resettingcopyElements()
optionsimport { ui } from '@clientio/rappid'; const clipboard = new ui.Clipboard(); /* ... */ clipboard.copyElements(collection1, graph, { useLocalStorage: true }); clipboard.copyElements(collection2, graph, { useLocalStorage: false }); clipboard.pasteCells(graph, { useLocalStorage: true }); // Assert: elements pasted from localStorage clipboard.pasteCells(graph, { useLocalStorage: false }); // Assert: elements pasted from memory (clipboard collection)
-
ui.FreeTransform -
minWidth
,minHeight
,maxWidth
,maxHeight
options can be defined as a functionimport { ui } from '@clientio/rappid'; new ui.FreeTransform({ /* ... */ minWidth: (pool) => pool.getMinimalSize().width, minHeight: (pool) => pool.getMinimalSize().height });
-
ui.Halo - option
clone()
is used for fork validationimport { ui } from '@clientio/rappid'; // The fork button is shown only if `element` -> `element.clone()` connection // would be valid (`validateConnection()` of paper) new ui.Halo({ /* ... */ clone(element) { // Assert: custom clone() option provided is executed when testing // fork visibility return element.clone(); } });
-
ui.Inspector - prevent exception when an attribute binding is used inside a list
{ "type": "list", "item": { "type": "object", "properties": { "service": { "type": "select", // Assert: reads options from a model's attribute // while being a member of a list "options": "services" } } } }
-
ui.Inspector - fix content editable XSS security issue
// Assert: the content-editable inspector field for this // attribute will not trigger alert() upon rendering el.attr('label/text', 'aaa"<img src=x onerror=alert("XSS")>');
-
ui.Inspector - add
readonly
option tocontent-editable
to make field non-editable.{ "label/text": { "type": "content-editable", "readonly": true } }
-
ui.Inspector - Fix
material
theme style- fix animation height on inputs
- fix toggle state being active for all toggle elements inside list (now changes by which toggle is in focus)
- fix dropdown arrow on select
-
ui.PaperScroller - add
inertia
option to enable inertial scrollingimport { ui } from '@clientio/rappid'; new ui.PaperScroller({ /* ... */ inertia: { friction: 0.92 } });
-
ui.Selection - fix resizing multiple rotated elements
-
ui.Snaplines - add
usePaperGrid
optionWhen an element is snapped to another element, adjust its top-left corner to the paper’s grid. Useful when the elements in the graph (or their centers) are not aligned with the grid.
-
ui.Stencil - support paper
validateUnmbedding()
optionWhen paper’s
embeddingMode
is enabled andvalidateUnembedding()
option defined, the stencil drag & drop action can be reverted if the element can not exist without a parent (unembedded).
-
layout.TreeLayout - fix vertices for negative
firstChildGap
import { layout } from '@clientio/rappid'; const tree = new layout.TreeLayout({ graph, direction: 'BR', firstChildGap: -40 // height of an element }); tree.layout();
-
shapes.bpmn2.Pool - take rotation into account in
getLanesFromPoint()
andgetMilestoneFromPoint()
pool.set('angle', 90); const lanes = pool.getLanesFromPoint({ x: 100, y: 200 }); // Assert: 90 deg angle is taken into account // e.g lanes = ["lanes_0_0", "lanes_0"]
-
shapes.bpmn2.Pool - remove the undesired top
padding
from milestone line and the bottompadding
from sub-lanespool.set('padding', { left: 30, top: 30, bottom: 30 });
-
shapes.bpmn2.Pool - fix
getMilestoneBBox()
returning only the header sizeconst bbox = pool.getMilestoneBBox('milestone_1');
-
shapes.bpmn2.Pool - add
labelAlignment
andlabelMargin
attributesSpecify the horizontal and vertical alignment and margins of labels inside swimlane and milestone headers.
-
shapes.bpmn2.Pool - support custom lane and milestone
id
pool.set('milestones', [ { label: 'Milestone 1' // Selectors // group: 'milestone_0' // header: 'milestoneHeader_0' // label: 'milestoneLabel_0' // line: 'milestoneLine_0' }, { label: 'Milestone 2' // Selectors // group: 'milestone_1' // header: 'milestoneHeader_1' // label: 'milestoneLabel_1' // line: 'milestoneLine_1' }, { label: 'Milestone 3', id: 'my_id', // Selectors (with a custom `id`) // group: 'milestone_my_id' // header: 'milestoneHeader_my_id' // label: 'milestoneLabel_my_id' // line: 'milestoneLine_my_id' } ]); // When setting a presentation attribute pool.attr(['milestoneHeader_my_id', 'fill'], 'lightgray'); // The pool API return a custom `id` if defined. const lanes = pool.getLanesFromPoint(point); // e.g ['my_id']
-
shapes.standard.Record - fix
itemOverflow
for items with multi-group spanimport { shapes } from '@clientio/rappid'; const record = new shapes.standard.HeaderedRecord({ items: [ // column 1 [{ id: 'item1', label: 'Span 1', }, { id: 'item2', label: 'Span 2', span: 2 }], // column 2 [] ], attrs: { itemBodies: { stroke: 'red' } }, padding: { top: 30, left: 30, right: 30 } });
-
dia.Paper - add
allowNegativeBottomRight
option tofitToContent()
const rect = const new shapes.standard.Rectangle(); rect.resize(100, 100); rect.position(-200, -200); rect.addTo(graph); paper.fitContent({ allowNewOrigin: 'any', // without the following option the bottom right corner // of the paper would be at 0,0 at minimum. allowNegativeBottomRight: true, padding: 10 }); // Assert: // paper.getArea() === { x: -210, y: -210, width: 120, height: 120 }
-
dia.Paper - add
routerNamespace
andconnectorNamespace
optionsIt’s necessary for defining custom routers and connectors when Rappid is used as a module. See an example in the Database demo.
-
dia.Paper - add
validateUnembedding()
option to control which elements can be unembedded when the paper is put into theembeddingMode
import { ui } from '@clientio/rappid'; dia.Paper({ /* .. */ embeddingMode: true, validateUnembedding: (childView) => { if (childView.model.get('isBoundaryEvent')) { // The element stays embedded in the current parent. return false; } // The element can be unembedded from the parent // and become a new root (the default). return true; } });
-
dia.Graph - fix order of cells in
getElements()
andgetLinks()
const elements = graph.getElements(); // Assert: elements[n].get('z') >= elements[n + 1].get('z') const links = graph.getLinks(); // Assert: links[m].get('z') >= links[m + 1].get('z')
-
dia.Cell - add
breadthFirst
option to switch between BFD / DFS to index embedded cells intoFront()
andtoBack()
-
dia.CellView - allow
presentationAttributes
andinitFlag
to be defined as a function
-
dia.attributes - simplify and make the shape definition more flexible by adding
calc()
functions to be used in various SVG attributes.import { dia } from '@clientio/rappid'; class Shape extends dia.Element { defaults() { return { ...super.defaults, type: 'Shape', size: { width: 100, // `w` from the calc() expr. height: 101 // `h` from the calc() expr. }, attrs: { body: { // evaluates as width="100" width: 'calc(w)', // evaluates as height="101" height: 'calc(h)', fill: 'white', stroke: 'black', strokeWidth: 2 }, lines: { // evaluates as d="M 0 -5 h 100 M 0 106 h 100" d: 'M 0 -5 h calc(w) M 0 calc(h+5) h calc(w)', fill: 'none', stroke: 'black', strokeWidth: 2 } } } } // <g> <!--implicit, referenced by `root` selector--> // <rect/> <!--referenced by `body` selector--> // <path/> <!--referenced by `lines` selector--> // </g> markup = [{ tagName: 'rect', selector: 'body' }, { tagName: 'path', selector: 'lines' }]; } const cellNamespace = { ...dia.shapes, Shape }; const shape = new Shape(); shape.addTo(graph);
It makes use of
refX
,refX2
,refY
,refY2
,refWidth
,refHeight
,refD
,refCx
,refCy
,refRx
,refRy
effectively deprecated.
-
util.breakText - fix wrapping when the last word contains a hyphen
import { util } from '@clientio/rappid'; // Make sure the resulting string does not contain `undefined` when the last word has a hyphen util.breakText('word1-word1', { width }); util.breakText('word1 word2-word2', { width }); util.breakText('word1 word2 word3-word3', { width });
-
routers.manhattan - improve points comparison performance
-
routers.manhattan - add
isPointObstacle
optionimport { dia, routers } from '@clientio/rappid'; import MyObstacles from 'my-obstacles.mjs'; // a custom implementation of the obstacle map const obstacles = new MyObstacles({ graph }); const paper = new dia.Paper({ /* ... */ model: graph, defaultRouter: (vertices, opt, linkView) => { const isPointObstacle = (point) => obstacles.isPointObstacle(point); return routers.manhattan(vertices, { ...opt, isPointObstacle }, linkView); } });
-
Geometry.Rect - add
fromPointUnion
andfromRectUnion
methods to find a union of rectangles or pointsimport { g } from `@clientio/rappid`; const { Rect } = g; /* ... */ const union = Rect.fromPointUnion(p1, p2, p3); const { x: maxX, y: maxY } = union.bottomRight(); /* ... */ const selectionBBox = Rect.fromRectUnion(...selection.map(el => el.getBBox()); selectionBBox.inflate(padding); // draw a rectangle around the selection ...
-
Geometry.Rect - add
update
method for rect instance reusabilityimport { g } from `@clientio/rappid`; const { Rect } = g; const oneRect = new g.Rect(); /* ... */ oneRect.update(1, 1, 10, 10); /* ... */ oneRect.update(2, 2, 10, 10);
- Remove polyfill for
XMLHttpRequest
support of IE9