Back to jointjs.com.
CHANGELOG
-
upgrade jQuery dependency (v3.5.1)
Fix a security issue.
-
add Chatbot application (VueJS, Angular9, React Redux)
# Angular 9 Typescript {rappid_package_v3.2.0}/apps/Chatbot/Angular9/README.md # React Redux Typescript {rappid_package_v3.2.0}/apps/Chatbot/ReactReduxTs/README.md # Vue Typescript {rappid_package_v3.2.0}/apps/Chatbot/VueTs/README.md
-
add shapes.Measurements (Distance & Angle)
# open in browser {rappid_package_v3.2.0}/apps/Measurements/angle.html
# open in browser {rappid_package_v3.2.0}/apps/Measurements/distance.html
-
add KitchenSink integrated with Angular9, VueJS and VueTs
# Angular 9 Typescript {rappid_package_v3.2.0}/apps/KitchenSinkAngular9/README.md # Vue Javascript {rappid_package_v3.2.0}/apps/KitchenSinkVueJs/README.md # Vue Typescript {rappid_package_v3.2.0}/apps/KitchenSinkVueTs/README.md
-
add Sequence Diagram demo
-
add HTML Elements demo
-
apps.DataMapping: show translucent copy of an element being dragged
// Disable default element dragging paper.setInteractivity({ elementMove: false }); paper.on('element:pointermove', (view, evt, x, y) => { // `data` object is shared between `down`, `move` and `up` events const { data } = evt; let { ghost } = data; if (!ghost) { // Create a copy of the original element view on the first element pointer move const position = view.model.position(); ghost = view.vel.clone().attr('opacity', 0.3).appendTo(paper.viewport); data.ghost = ghost; data.dx = x - position.x; data.dy = y - position.y; } ghost.attr('transform', `translate(${x - data.dx},${y - data.dy})`); }); paper.on('element:pointerup', (view, evt, x, y) => { const { ghost, dx, dy } = evt.data; if (!ghost) return; // Finally, move the actual element and remove the ghost ghost.remove(); view.model.position(x - dx, y - dy); });
-
apps.Layout: implement dragging of embedded elements
-
shapes.Standard - support element ports on the
Record
shapeconst record = new joint.shapes.standard.Record({ ports: { group: { in: { position: { name: 'top' }}, out: { position: { name: 'bottom' }} } } }); record.addPort({ id: 'in1', group: 'in' });
-
format.GridLayout - add
setAttributes
,verticalAlign
,horizontalAlign
,rowGap
,columnGap
options// Layout children of an element into a grid const gridElements = element.getEmbeddedCells(); joint.layout.GridLayout.layout(gridElements, { columns: 3, columnGap: 10, rowGap: 10, // `x` coordinate of each element's origin is at left of the grid cell horizontalAlign: 'left', // Find the minimal column width based on the elements in the column columnWidth: 'compact', // `y` coordinate of each element's center is in the middle of the grid cell verticalAlign: 'middle', rowHeight: 100, parentRelative: true, // dx: 10, @deprecated - use `columnGap` instead // dy: 10, @deprecated - use `rowGap` instead // centre: false, @deprecated - use `verticalAlign` and `horizontalAlign` instead centre: false // needs to be disabled for backwards compatibility reasons }); element.fitEmbeds({ padding: 10 });
-
ui.FreeTransform - support asynchronous updates
Updates of the FreeTransform are now bound to the Paper’s update mechanism (Same as
ui.Selection
).
Thepaper.options.viewport
may be fired with the FreeTransform view.
Callingpaper.freeze()
will now stop the updates of the FreeTransform.
-
ui.FreeTransform - add
usePaperScale
,resizeDirections
andpadding
optionsconst freeTransform = new joint.ui.FreeTransform({ /* ... */ // The FreeTransform receives the same transformations (scale) as the Paper. usePaperScale: true, // Display resize handles on the left and right side only resizeDirections: ['left', 'right'] // The gap between the FreeTransform frame and the element boundary padding: 4, useModelGeometry: true }); freeTransform.render();
-
ui.Halo -
magnet
option callback receivesevent
argument// Implementing a Halo with two link handles (each connects the link to a different magnet) new joint.ui.Halo({ /* ... */ handles: [{ name: 'link-right', position: 'w', events: { pointerdown: 'startLinking', pointermove: 'doLink', pointerup: 'stopLinking' }, attrs: { '.handle': { // This is available under `evt.target.dataset.selector` 'data-selector': 'rightMagnet' } } }, { name: 'link-left', position: 'e', events: { pointerdown: 'startLinking', pointermove: 'doLink', pointerup: 'stopLinking' }, attrs: { '.handle': { // This is available under `evt.target.dataset.selector` 'data-selector': 'leftMagnet' } } }], magnet: (elementView, endType, evt) => { // Here you can decide what sub-element of the elementView // should become the link's magnet. // Selector: https://resources.jointjs.com/docs/jointjs/v3.2/joint.html#dia.Cell.markup const { selector } = evt.target.dataset; const [linkMagnet] = elementView.findBySelector(selector); return linkMagnet; } });
-
ui.Inspector - relative
when
expressions for nested objects and listsnew joint.ui.Inspector({ /* ... */ inputs: { user_list: { // An array of users type: 'list', item: { // An object describing a single user type: 'object', properties: { contact_option: { type: 'select', options: ['email', 'tel'] }, contact_email: { type: 'text', when: { // Show when the current list item `contact_option` equals `email` eq: { 'user_list/${index}/contact_option': 'email' } } }, contact_tel: { type: 'number', when: { // Show when the current list item `contact_option` equals `tel` eq: { 'user_list/${index}/contact_option': 'tel' } } } } } } } });
-
ui.Inspector - add
updateCellOnClose
optionjoint.ui.Inspector.create(inspectorHTMLContainer, { /* ... */ cell: selectedElement, // Do not send values from inputs to the `cell` when the inspector closes updateCellOnClose: false });
-
ui.Inspector - make sure fields are saved before close
Just before the inspector gets closed, the active element focus is now removed. This may trigger an input
change
event resulting in storing the changed value to the cell.
-
ui.Navigator - add
useContentBBox
optionnew joint.ui.Navigator({ /* ... */ // Show the paper area occupied by cells only. useContentBBox: true });
-
ui.Navigator - add
freeze()
,unfreeze()
const navigator - new joint.ui.Navigator({ /* ... */ paperOptions: { frozen: true, async: true, sorting: joint.dia.Paper.sorting.NONE } }); // Unfreeze when the navigator is visible in the DOM // For a large graph, limit the number of updates in a single batch navigator.unfreeze({ batchSize: 100 }); // When the navigator is about to be hidden (e.g collapsed) from the page navigator.freeze();
-
ui.Navigator - trigger
pan:start
,pan:stop
,zoom:start
,zoom:stop
eventsnavigator.on('pan:start zoom:start', () => { console.log('viewport will change.'); }); navigator.on('pan:stop zoom:stop', () => { console.log('viewport has changed.'); });
-
ui.PaperScroller - add
scrollWhileDragging
optionconst scroller = new joint.ui.PaperScroller({ /* ... */ scrollWhileDragging: { interval: 25, // ms padding: -20, // px // `distance` from the mouse coordinates to // the scroller border (inflated by `padding`) in `px` // Returns the number of `px` we move the paper per `interval`. scrollingFunction: (distance) => distance < 20 ? 5 : 20 } });
-
ui.PaperScroller -
padding
option callback accepts paper scroller instanceconst scroller = new joint.ui.PaperScroller({ /* ... */ padding: (scroller) => { // Set the `padding` to 50% of the current dimension of the scroller. const { width, height } = scroller.getClientSize(); const ratio = 0.5; return { left: width * ratio, right: width * ratio, top: height * ratio, bottom: height * ratio }; } });
-
ui.PaperScroller -
positionContent()
,scrollToContent()
acceptsuseModelGeometry
option// Scroll the content (all cells of the graph) to // the the top right corner of the scroller. // Add `useModelGeometry: true` when the paper is set to `async` mode // and the cell views may not be rendered yet. scroller.positionContent('top-right', { useModelGeometry: true });
-
ui.PaperScroller - fix paper size after
zoomToRect()
Fix for missing
scroller.adjustPaper()
call after the paper content is scaled.
-
ui.PaperScroller - fix
autoResizePaper
option for synchronous rendering modeFix for the Paper inside the PaperScroller not getting resized after a graph change.
-
ui.Stencil - add
freeze()
,unfreeze()
// Create and populate the stencil const stencil = new joint.ui.Stencil({ /* ... */ paperOptions: { frozen: true } }); stencil.load(stencilElements); // Unfreeze the Stencil after it's mounted. // Note: Rendering the Stencil elements while it is not mounted may // cause element views to render incorrectly. stencilHTMLContainer.appendChild(stencil.el); stencil.unfreeze();
-
ui.Stencil - add
contentOptions
andcanDrag
optionsnew joint.ui.Stencil({ /* ... */ // The Stencil Paper size depends on the elements in the paper. // To adjust this behavior use `contentOptions`. contentOptions: { // Available options: // https://resources.jointjs.com//docs/JointJS#dia.Paper.prototype.fitToContent padding: 5, useModelGeometry: true, minHeight: 200 }, // paperPadding: 5, @deprecated in favour of contentOptions.padding // A function to determine whether an element from the stencil // can be dragged by the user or not. canDrag: (elementView) => !elementView.model.get('disabled'); });
-
ui.Stencil - add
stencil-filtered
CSS class when stencil is filtered/* Hide group labels when showing result of a search */ .joint-stencil.stencil-filtered .group-label { display: none; }
-
ui.Stencil - fix listeners not being unbound after drag end
Make sure
stencil.onDrag()
is called only when the user drags a stencil element.
-
ui.Tooltip - add
container
optionnew joint.ui.Tooltip({ // Append the tooltip to a custom container element (default is `body`) container: tooltipHTMLContainer });
-
ui.Tooltip - fix default
template
optionFix related to jQuery upgrade.
-
dia.Paper - add
beforeRender
andafterRender
options, addhasScheduledUpdates()
, triggerrender:done
in sync mode// Assert: paper.isAsync() === true paper.freeze(); // Assert: paper.hasScheduledUpdates() === false element.attr('body/fill', 'red'); // The update of the fill color is scheduled. // Assert: paper.hasScheduledUpdates() === true paper.unfreeze({ beforeRender: () => { // The update of the fill is still scheduled. // Assert: paper.hasScheduledUpdates() === true }, afterRender: () => { // All updates have been done. // Assert: paper.hasScheduledUpdates() === false element.attr('body/fill', 'red'); // The fill color of the element is already `red`. // Assert: paper.hasScheduledUpdates() === false } });
// Assert: paper.isAsync() === false graph.on('change:position', function() { // Called once for `parent` and once for `child` }); paper.on('render:done', () => { // Called after both `parent` and the `child` has been translated }); // A different part of an application parent.embed(child); parent.translate(10, 0);
-
dia.Paper - fix missing initial
render:done
eventpaper.on('render:done', () => { // Make sure `render:done` event is triggered after the first render. }); graph.resetCells(arrayOfCells);
-
dia.Paper - prevent the prototype options modification, persist functions passed as options
new joint.dia.Paper({ // e.g. fixes `defaultConnector` option defined as a function defaultConnector: (sourcePoint, targetPoint, route) => { const polyline = new g.Polyline([sourcePoint, ...route, targetPoint]); return `M ${polyline.serialize()}`; } });
-
dia.Paper -
scaleContentToFit()
optionpadding
accepts an objectpaper.scaleContentToFit({ // Set a non-uniform padding padding: { top: 50, bottom: 10, // left and right horizontal: 10 }});
-
dia.Paper - fix
isMounted
argument ofviewport()
optionnew joint.dia.Paper({ viewport: (view, isMounted) => { if (isMounted) { // the view was already mounted // it's visible in the paper } else { // the view was just created or was detached by this function in the previous run // it's not visible in the paper (it's not in the DOM) } // return `true` to mount the view // return `false` to detach the view } });
-
dia.Paper - dynamic link update priorities (fix for “link connected to other two links” update bug)
A link
link1
connected tolink2
andlink3
is updated after bothlink2
andlink3
are up-to-date. The connection points oflink1
located onlink2
andlink3
depend on the latest geometry of both links.
-
dia.Element - port removal runs in batch
// Remove the port and all the connected links in a single batch. // The `CommandManager` will register a single undo/redo command. element.removePort('port_id');
-
dia.Element - add
getGroupPorts()
element.getGroupPorts('out'); // Returns `[{ id: 'out1', group: 'out' }, { id: 'out2', group: 'out' }]`
-
dia.Element - prevent exception in
getPointFromConnectedLink()
when port does not exist// Assert: element.hasPort('invalid_port_id') === false link.set('target', { id: element.id, port: 'invalid_port_id' }); element.getPointFromConnectedLink(link, 'target'); // Expect: No exception is thrown.
-
dia.LinkView - fix never ending batch for legacy link tools
Fix for legacy
joint.dia.Link
(use joint.shapes.standard.Link instead together with linkTools) for which clicking the link tools started a batch, which was never stopped.
-
dia.LinkView - add
requestConnectionUpdate()
// Assert: paper.isAsync() === true // Assert: paper.options.defaultRouter === { name: 'manhattan' } graph.on('change:position', (element) => // Manually request updates for all links (even so, that no link model was changed) graph.getLinks().forEach(link => { // The link's route needs to be recalculated as the `element` moved and // may become an obstacle. link.findView(paper).requestConnectionUpdate(); }); });
-
dia.LinkView - trigger
link:snap:connect
andlink:snap:disconnect
events
-
dia.LinkView - prevent exception when labels and connection require update
paper.freeze(); // Assert: The link is rendered in the paper link.set({ source: { x: 20, y: 20 }, labels: [ { position: 20, attrs: { text: { text: 'label1' }}}, { position: -20, attrs: { text: { text: 'label2' }}} ] }); paper.unfreeze(); // Expect: No exception is thrown.
-
dia.LinkView - measure snap distance for links from magnet’s boundary
Fix snapping for magnets larger than
radius
.
-
dia.LinkView - add
getEndConnectionPoint()
const sourceConnectionPoint = linkView.getEndConnectionPoint('source'); // It returns a geometry point (in local coordinates), // where the link connects to the source element.
-
mvc.View - add
DETACHABLE
property to ignore viewport matching,FLAG_INSERT
&FLAG_REMOVE
defined on per view basisconst CustomFreeTransform = joint.ui.FreeTransform.extend({ // The FreeTransform view will not be checked inside `paper.options.viewport` function. // It will be always mounted in the DOM DETACHABLE: false });
-
linkTools.Anchor: add
resetAnchor
optionnew joint.linkTools.TargetAnchor({ // prevent `double-click` to reset the anchor resetAnchor: false });
-
linkTools.Segments: add
stopPropagation
optionnew joint.linkTools.Segments({ // `link:pointerdown` event will be triggered // on the related linkView on `mousedown` stoPropagation: false });
-
connectionPoints.anchor - add
align
andalignOffset
optionsA way to attach links to elements as shown in the Distance Demo.
link.set('target', { id: targetElement.id, connectionPoint: { name: 'anchor', args: { // Align the target connection point with the source anchor, // if the source anchor `y` coordinate is larger than the target anchor align: 'bottom', alignOffset: 20 } } })
-
attributes.textWrap: add
maxLineCount
optionelement.attr({ header: { text: 'Rappid Component', textWrap: { width: -10, // make sure the header never spans over more than 1 line maxLineCount: 1 } } });
-
util.breakText - retain new line characters, add
maxLineCount
optionjoint.util.breakText('One\n\nThree\nFour', /*...*/); // The empty line is retained /* One↵ ↵ Three↵ Four */
joint.util.breakText('Lorem ipsum dolor sit amet, consectetur adipiscing elit.', { width: 100 }, { 'font-family': 'sans-serif', 'font-size': 14 }, { maxLineCount: 2, ellipsis: true });
-
util.sanitizeHTML: sanitize attribute values with
"data:"
and"vbscript:"
Fix for Improper Input Validation.
joint.util.sanitizeHTML('<a href="data:something">'); // => '<a></a>'
-
Geometry - add
parallel()
to Line, addserialize()
to Point and Lineconst line = new g.Line('0@0', '10@10'); const pathLeft = V('path', { 'd': `M ${ line.parallel(-5).serialize()}` }); const pathRight = V('path', { 'd': `M ${ line.parallel(5).serialize()}` });
-
various Typescript fixes
If you come across any Typescript mismatch, we suggest to fix it directly in your local
rappid.d.ts
file and let us know about the issue, so we can patch it for the next release. Please email us at support@client.io.