UNPKG

jointjs

Version:

JavaScript diagramming library

560 lines (478 loc) 16.2 kB
var graph = new joint.dia.Graph; var paper = new joint.dia.Paper({ el: document.getElementById('paper'), model: graph, width: 800, height: 1400, gridSize: 1, perpendicularLinks: false, interactive: function(cellView) { if (cellView.model.get('customLinkInteractions')) return { vertexAdd: false }; return true; // all interactions enabled }, linkView: joint.dia.LinkView.extend({ // custom interactions: pointerdblclick: function(evt, x, y) { if (this.model.get('customLinkInteractions')) { this.addVertex(x, y); } }, contextmenu: function(evt, x, y) { if (this.model.get('customLinkInteractions')) { this.addLabel(x, y, { absoluteDistance: true, reverseDistance: true }); } }, // custom options: options: joint.util.defaults({ doubleLinkTools: true, }, joint.dia.LinkView.prototype.options) }) }); paper.on('link:pointerdown', function(evt, linkView, x, y) { console.log('link:pointerdown'); }); paper.on('link:disconnect', function(linkView, evt, disconnectedFromView, magnetElement, type) { console.log('link:disconnect', type, disconnectedFromView, magnetElement); }); paper.on('link:connect', function(linkView, evt, connectedToView, magnetElement, type) { console.log('link:connect', type, connectedToView, magnetElement); }); $('#perpendicularLinks').on('change', function() { paper.options.perpendicularLinks = $(this).is(':checked') ? true : false; }); // custom link definition var CustomLink = joint.dia.Link.define('examples.CustomLink', { defaultLabel: { markup: [ { tagName: 'circle', selector: 'body' }, { tagName: 'text', selector: 'label' } ], attrs: { label: { text: '%', // default label text fill: '#ff0000', // default text color fontSize: 14, textAnchor: 'middle', yAlignment: 'middle', pointerEvents: 'none' }, body: { ref: 'label', fill: '#ffffff', stroke: '#000000', strokeWidth: 1, refRCircumscribed: '60%', refCx: 0, refCy: 0 } }, position: { distance: 0.5, // place label at midpoint by default offset: { y: -20 // offset label by 20px upwards by default }, args: { absoluteOffset: true // keep offset absolute when moving by default } } } }); var r1 = new joint.shapes.basic.Rect({ position: { x: 335, y: 50 }, size: { width: 70, height: 30 }, attrs: { rect: { fill: 'orange' }, text: { text: 'Box', magnet: true } } }); graph.addCell(r1); function title(x, y, text) { var el = new joint.shapes.basic.Text({ position: { x: x, y: y }, size: { width: text.length * 4, height: text.split('\n').length * 15 }, attrs: { text: { text: text, 'font-size': 12, 'text-anchor': 'end' } } }); graph.addCell(el); } // Default connection of two elements. // ----------------------------------- title(250, 70, 'Default connection'); var r2 = r1.clone(); graph.addCell(r2); r2.translate(300); var link1 = new CustomLink({ source: { id: r1.id }, target: { id: r2.id } }); graph.addCell(link1); // Custom link interactions. // ------------------------- title(250, 150, 'Custom interactions'); var r3 = r1.clone(); graph.addCell(r3); r3.translate(0, 80); var r4 = r3.clone(); graph.addCell(r4); r4.translate(300); var link2 = new CustomLink({ customLinkInteractions: true, source: { id: r3.id }, target: { id: r4.id }, attrs: { '.marker-source': { d: 'M 10 0 L 0 5 L 10 10 z' }, '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } } }); graph.addCell(link2); // Custom .marker-source and .marker-target. // ----------------------------------------- title(250, 230, 'Custom markers'); var r5 = r3.clone(); graph.addCell(r5); r5.translate(0, 80); var r6 = r5.clone(); graph.addCell(r6); r6.translate(300); var link3 = new CustomLink({ source: { id: r5.id }, target: { id: r6.id }, attrs: { '.marker-source': { d: 'M 10 0 L 0 5 L 10 10 z' }, '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } } }); graph.addCell(link3); // Changing source and target selectors of the link. // ------------------------------------------------- title(250, 310, 'Changing source and target selectors of a link'); var r7 = r5.clone(); graph.addCell(r7); r7.translate(0, 80); var r8 = r7.clone(); graph.addCell(r8); r8.translate(300); // Example on setting `magnet === false` on the overall element. In this case, // only the text can be a target of a link for this specific element. r8.attr({ '.': { magnet: false }}); var link4 = new CustomLink({ source: { id: r7.id }, target: { id: r8.id, selector: 'text' }, attrs: { '.marker-source': { d: 'M 10 0 L 0 5 L 10 10 z' }, '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } } }); graph.addCell(link4); // Vertices. // --------- title(250, 390, 'Vertices'); var r9 = r7.clone(); graph.addCell(r9); r9.translate(0, 80); var r10 = r9.clone(); graph.addCell(r10); r10.translate(300); var link5 = new CustomLink({ source: { id: r9.id }, target: { id: r10.id }, vertices: [{ x: 370, y: 470 }, { x: 670, y: 470 }], attrs: { '.marker-source': { d: 'M 10 0 L 0 5 L 10 10 z' }, '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } } }); graph.addCell(link5); // Custom vertex/connection markups. (ADVANCED) // -------------------------------------------- title(250, 510, 'Customized vertex markers, vertex tools and marker elements'); var r11 = r9.clone(); graph.addCell(r11); r11.translate(0, 120); var r12 = r11.clone(); graph.addCell(r12); r12.translate(300); var link6 = new CustomLink({ source: { id: r11.id }, target: { id: r12.id }, vertices: [{ x: 370, y: 600 }, { x: 520, y: 640 }, { x: 670, y: 600 }], vertexMarkup: [ '<g class="marker-vertex-group" transform="translate(<%= x %>, <%= y %>)">', '<image class="marker-vertex" idx="<%= idx %>" xlink:href="http://jointjs.com/images/logo.png" width="25" height="25" transform="translate(-12.5, -12.5)"/>', '<rect class="marker-vertex-remove-area" idx="<%= idx %>" fill="red" width="19.5" height="19" transform="translate(11, -26)" rx="3" ry="3" />', '<path class="marker-vertex-remove" idx="<%= idx %>" transform="scale(.8) translate(9.5, -37)" d="M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z">', '<title>Remove vertex.</title>', '</path>', '</g>' ].join(''), markup: [ '<path class="connection"/>', '<image class="marker-source" xlink:href="http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png" width="25" height="25"/>', '<image class="marker-target" xlink:href="http://cdn3.iconfinder.com/data/icons/49handdrawing/24x24/left.png" width="25" height="25"/>', '<path class="connection-wrap"/>', '<g class="marker-vertices"/>' ].join(''), attrs: { '.connection': { 'stroke-width': 4, 'stroke-dasharray': [5, 5, 5], stroke: 'gray' }, '.marker-source': { d: 'M 10 0 L 0 5 L 10 10 z' }, '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } } }); graph.addCell(link6); // Uncomment just for fun: /* var c = V('circle', { r: 8, fill: 'red' }); c.animateAlongPath({ dur: '4s', repeatCount: 'indefinite' }, paper.findViewByModel(link6).$('.connection')[0]); V(paper.svg).append(c); */ // Labels. // ------- title(250, 740, 'Labels'); var r13 = r11.clone(); graph.addCell(r13); r13.translate(0, 230); var r14 = r13.clone(); graph.addCell(r14); r14.translate(300); var link7 = new CustomLink({ source: { id: r13.id }, target: { id: r14.id }, attrs: { '.marker-source': { d: 'M 10 0 L 0 5 L 10 10 z' }, '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } }, labels: [ { attrs: { label: { text: '1..n' } }, position: { distance: 29, // individual absolute positioning offset: null, // remove default offset args: { absoluteOffset: null // disable absolute offset when moving } } }, { markup: [ // individual markup { tagName: 'rect', selector: 'body' }, { tagName: 'text', selector: 'label' } ], attrs: { label: { text: 'JointJS', fill: 'white', fontFamily: 'sans-serif', textAnchor: 'left' }, body: { stroke: 'red', strokeWidth: 2, fill: '#F39C12', rx: 5, ry: 5, refWidth: '140%', refHeight: '140%', refX: '-20%', refY: '-20%', refRCircumscribed: null, refCx: null, refCy: null } }, position: { // keep default distance offset: { // individual absolute offset x: 10, y: 25 } // keep default args } }, { markup: [ { tagName: 'circle', selector: 'body' }, { tagName: 'path', selector: 'symbol' } ], attrs: { body: { ref: null, fill: 'lightgray', stroke: 'black', strokeWidth: 2, r: 15, refRCircumscribed: null, refCx: null, refCy: null }, symbol: { // add attrs for individually added `path` d: 'M 0 -15 0 -35 20 -35', stroke: 'black', strokeWidth: 2, fill: 'none' } }, position: 0.5, // erase default position object, use relative distance }, { position: { distance: 0.89 // individual relative distance // keep default offset // keep default args } } ] }); graph.addCell(link7); // Custom tools. // ------------- title(250, 840, 'Custom tools'); var r15 = r13.clone(); graph.addCell(r15); r15.translate(0, 100); var r16 = r15.clone(); graph.addCell(r16); r16.translate(300); var link8 = new CustomLink({ source: { id: r15.id }, target: { id: r16.id }, attrs: { '.marker-source': { d: 'M 10 0 L 0 5 L 10 10 z' }, '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' } }, toolMarkup: [ '<g class="link-tool">', '<g class="tool-remove" event="remove">', '<circle r="11" />', '<path transform="scale(.8) translate(-16, -16)" d="M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z"/>', '<title>Remove link.</title>', '</g>', '<g event="link:options">', '<circle r="11" transform="translate(25)"/>', '<path fill="white" transform="scale(.55) translate(29, -16)" d="M31.229,17.736c0.064-0.571,0.104-1.148,0.104-1.736s-0.04-1.166-0.104-1.737l-4.377-1.557c-0.218-0.716-0.504-1.401-0.851-2.05l1.993-4.192c-0.725-0.91-1.549-1.734-2.458-2.459l-4.193,1.994c-0.647-0.347-1.334-0.632-2.049-0.849l-1.558-4.378C17.165,0.708,16.588,0.667,16,0.667s-1.166,0.041-1.737,0.105L12.707,5.15c-0.716,0.217-1.401,0.502-2.05,0.849L6.464,4.005C5.554,4.73,4.73,5.554,4.005,6.464l1.994,4.192c-0.347,0.648-0.632,1.334-0.849,2.05l-4.378,1.557C0.708,14.834,0.667,15.412,0.667,16s0.041,1.165,0.105,1.736l4.378,1.558c0.217,0.715,0.502,1.401,0.849,2.049l-1.994,4.193c0.725,0.909,1.549,1.733,2.459,2.458l4.192-1.993c0.648,0.347,1.334,0.633,2.05,0.851l1.557,4.377c0.571,0.064,1.148,0.104,1.737,0.104c0.588,0,1.165-0.04,1.736-0.104l1.558-4.377c0.715-0.218,1.399-0.504,2.049-0.851l4.193,1.993c0.909-0.725,1.733-1.549,2.458-2.458l-1.993-4.193c0.347-0.647,0.633-1.334,0.851-2.049L31.229,17.736zM16,20.871c-2.69,0-4.872-2.182-4.872-4.871c0-2.69,2.182-4.872,4.872-4.872c2.689,0,4.871,2.182,4.871,4.872C20.871,18.689,18.689,20.871,16,20.871z"/>', '<title>Link options.</title>', '</g>', '</g>' ].join('') }); graph.addCell(link8); paper.on('link:options', function(linkView, evt, x, y) { alert('Opening options for link ' + linkView.model.id); }); // Manhattan routing. // ------------------ title(250, 940, 'Manhattan and Metro routing'); var r17 = r15.clone(); graph.addCell(r17); r17.translate(0, 100); var r18 = r17.clone(); graph.addCell(r18); r18.translate(200, 80); var link9 = new CustomLink({ source: { id: r17.id }, target: { id: r18.id }, vertices: [{ x: 700, y: 990 }], router: { name: 'metro' } }); var link10 = new CustomLink({ source: { id: r17.id }, target: { id: r18.id }, vertices: [{ x: 450, y: 1015 }], router: { name: 'manhattan' }, connector: { name: 'rounded' } }); graph.addCell([link9, link10]); // Markers. // ------------------ title(250, 1140, 'Markers'); var r19 = r17.clone(); graph.addCell(r19); r19.translate(0, 200); var r20 = r19.clone(); graph.addCell(r20); r20.translate(200, 0); var link11 = new CustomLink({ source: { id: r19.id }, target: { id: r20.id }, vertices: [{ x: 400, y: 1080 }, { x: 600, y: 1080 }], attrs: { '.connection': { 'marker-mid': 'url(#circle-marker)' } } }); graph.addCell(link11); var link12 = link11.clone(); link12.set('vertices', [{ x: 400, y: 1190 }, { x: 600, y: 1190 }]); link12.attr('.connection/marker-mid', 'url(#diamond-marker)'); graph.addCell(link12); var circleMarker = V('<marker id="circle-marker" markerUnits="userSpaceOnUse" viewBox = "0 0 12 12" refX = "6" refY = "6" markerWidth = "15" markerHeight = "15" stroke = "none" stroke-width = "0" fill = "red" orient = "auto"> <circle r = "5" cx="6" cy="6" fill="blue"/> </marker>'); V(paper.viewport).defs().append(circleMarker); var diamondMarker = V('<marker id="diamond-marker" viewBox = "0 0 5 20" refX = "0" refY = "6" markerWidth = "30" markerHeight = "30" stroke = "none" stroke-width = "0" fill = "red" > <rect x="0" y="0" width = "10" height="10" transform="rotate(45)" /> </marker>'); V(paper.viewport).defs().append(diamondMarker); // OneSide routing. // ---------------- title(250, 1290, 'OneSide routing'); var r21 = r19.clone(); graph.addCell(r21); r21.translate(0, 150); var r22 = r21.clone(); graph.addCell(r22); r22.translate(200, 0); var link13 = new CustomLink({ source: { id: r21.id }, target: { id: r22.id }, router: { name: 'oneSide', args: { side: 'bottom' }} }); graph.addCell(link13);