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 Recordshapeconst 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,columnGapoptions// 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.viewportmay be fired with the FreeTransform view.
 Callingpaper.freeze()will now stop the updates of the FreeTransform.
- 
    ui.FreeTransform - add usePaperScale,resizeDirectionsandpaddingoptionsconst 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 - magnetoption callback receiveseventargument// 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 whenexpressions 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 updateCellOnCloseoptionjoint.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 changeevent resulting in storing the changed value to the cell.
- 
    ui.Navigator - add useContentBBoxoptionnew 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:stopeventsnavigator.on('pan:start zoom:start', () => { console.log('viewport will change.'); }); navigator.on('pan:stop zoom:stop', () => { console.log('viewport has changed.'); });
- 
    ui.PaperScroller - add scrollWhileDraggingoption const 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 - paddingoption 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()acceptsuseModelGeometryoption// 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 autoResizePaperoption 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 contentOptionsandcanDragoptionsnew 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-filteredCSS 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 containeroptionnew joint.ui.Tooltip({ // Append the tooltip to a custom container element (default is `body`) container: tooltipHTMLContainer });
- 
    ui.Tooltip - fix default templateoptionFix related to jQuery upgrade. 
- 
    dia.Paper - add beforeRenderandafterRenderoptions, addhasScheduledUpdates(), triggerrender:donein 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:doneeventpaper.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()optionpaddingaccepts an objectpaper.scaleContentToFit({ // Set a non-uniform padding padding: { top: 50, bottom: 10, // left and right horizontal: 10 }});
- 
    dia.Paper - fix isMountedargument 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 link1connected tolink2andlink3is updated after bothlink2andlink3are up-to-date. The connection points oflink1located onlink2andlink3depend 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:connectandlink:snap:disconnectevents 
- 
    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 DETACHABLEproperty to ignore viewport matching,FLAG_INSERT&FLAG_REMOVEdefined 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 resetAnchoroptionnew joint.linkTools.TargetAnchor({ // prevent `double-click` to reset the anchor resetAnchor: false });
- 
    linkTools.Segments: add stopPropagationoptionnew joint.linkTools.Segments({ // `link:pointerdown` event will be triggered // on the related linkView on `mousedown` stoPropagation: false });
- 
    connectionPoints.anchor - add alignandalignOffsetoptionsA 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 maxLineCountoptionelement.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 maxLineCountoptionjoint.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.tsfile and let us know about the issue, so we can patch it for the next release. Please email us at support@client.io.