UNPKG

jointjs

Version:

JavaScript diagramming library

596 lines (551 loc) 15.6 kB
import * as joint from '../../../joint.mjs'; import * as g from '../../../src/g/index.mjs'; import V from '../../../src/V/index.mjs'; var graph = new joint.dia.Graph; var paper = new joint.dia.Paper({ el: document.getElementById('paper'), width: 650, height: 400, gridSize: 10, model: graph }); // Global special attributes joint.dia.attributes.lineStyle = { set: function(lineStyle, refBBox, node, attrs) { var n = attrs['strokeWidth'] || attrs['stroke-width'] || 1; var dasharray = { 'dashed': (4*n) + ',' + (2*n), 'dotted': n + ',' + n }[lineStyle] || 'none'; return { 'stroke-dasharray': dasharray }; } }; joint.dia.attributes.fitRef = { set: function(fitRef, refBBox, node) { switch (node.tagName.toUpperCase()) { case 'ELLIPSE': return { rx: refBBox.width / 2, ry: refBBox.height / 2, cx: refBBox.width / 2, cy: refBBox.height / 2 }; case 'RECT': return { width: refBBox.width, height: refBBox.height }; case 'PATH': var rect = joint.util.assign(refBBox.toJSON(), fitRef); return { d: V.rectToPath(rect) }; } return {}; } }; var Circle = joint.dia.Element.define('custom.Circle', { markup: [{ tagName: 'ellipse', selector: 'body' }, { tagName: 'text', selector: 'label' }, { tagName: 'path', selector: 'lines' }], attrs: { body: { fill: '#FFFFFF', stroke: '#cbd2d7', strokeWidth: 3, lineStyle: 'dashed', fitRef: true }, lines: { stroke: '#cbd2d7', strokeWidth: 3, lineStyle: 'dotted', fill: 'none', d: ['M', 0, '25%', '100%', '25%', 'M', '100%', '75%', 0, '75%'] }, label: { fill: '#cbd2d7', fontSize: 20, fontFamily: 'Arial, helvetica, sans-serif', refX: '50%', refY: '50%', transform: 'rotate(45) scale(0.5,0.5)', yAlignment: 'middle', xAlignment: 'middle' } } }, { setText: function(text) { return this.attr('label/text', text); } }, { // Element specific special attributes attributes: { d: { // The path data `d` attribute to be defined via an array. // e.g. d: ['M', 0, '25%', '100%', '25%', 'M', '100%', '75%', 0, '75%'] qualify: Array.isArray, set: function(value, refBBox) { var i = 0; var attrValue = value.map(function(data, index) { if (typeof data === 'string') { if (data.slice(-1) === '%') { return parseFloat(data) / 100 * refBBox[((index - i) % 2) ? 'height' : 'width']; } else { i++; } } return data; }).join(' '); return { d: attrValue }; } } } }); var circle = (new Circle()) .size(100, 100) .position(500,200) .setText('Special\nAttributes') .rotate(-45) .addTo(graph); circle.transition('angle', 0, { delay: 500 }); var Rectangle = joint.dia.Element.define('custom.Rectangle', { markup: [{ tagName: 'rect', selector: 'body' }, { tagName: 'circle', selector: 'red' }, { tagName: 'path', selector: 'green' }, { tagName: 'text', selector: 'content' }], attrs: { body: { fill: '#ddd', stroke: '#000', refWidth: '100%', refHeight: '100%', rx: 5, ty: 5 }, red: { r: 12, fill: '#d00', stroke: '#000', refX: '100%', cy: 4, cx: -4, event: 'element:delete', cursor: 'pointer' }, green: { r: 4, fill: '#0d0', stroke: '#000', refX: '100%', d: 'M -10 0 -3 -3 0 -10 3 -3 10 0 3 3 0 10 -3 3 z', transform: 'translate(-4,4)', pointerEvents: 'none' }, content: { textWrap: { text: 'An element with text automatically wrapped to fit the rectangle.', width: -10, height: -10 }, fontSize: 14, fontFamily: 'sans-serif', textAnchor: 'middle', refX: '50%', refDy: -5, yAlignment: 'bottom' } } }); var rectangle = (new Rectangle()) .size(100,90) .position(250,50) .addTo(graph); paper.on('element:delete', function(elementView, evt) { evt.stopPropagation(); if (confirm('Are you sure you want to delete this element?')) { elementView.model.remove(); } }); var Header = joint.dia.Element.define('custom.Header', { markup: [{ tagName: 'rect', selector: 'body' }, { tagName: 'rect', selector: 'header' }, { tagName: 'text', selector: 'caption' }, { tagName: 'text', selector: 'description' }, { tagName: 'image', selector: 'icon' }], attrs: { body: { fitRef: true, fill: 'white', stroke: 'gray', strokeWidth: 3 }, header: { fill: 'gray', stroke: 'none', height: 20, refWidth: '100%' }, caption: { refX: '50%', textAnchor: 'middle', fontSize: 12, fontFamily: 'sans-serif', y: 15, textWrap: { text: 'Header', height: 0 }, fill: '#fff' }, description: { refX: '50%', refX2: 15, refY: 25, textAnchor: 'middle', fontSize: 12, fontFamily: 'sans-serif', textWrap: { text: 'Here is a description spread on multiple lines. Obviously wrapped automagically.', width: -40, height: -25 }, fill: '#aaa' }, icon: { x: 3, y: 22, width: 30, height: 40, xlinkHref: 'http://placehold.it/30x40' } } }); var header = (new Header()) .size(200,140) .position(420,40) .addTo(graph); // Animate the element size header.transition('size', { width: 160, height: 100 }, { valueFunction: joint.util.interpolate.object, duration: 1000, delay: 1000 }); new joint.shapes.standard.Link({ source: { id: circle.id }, target: { id: rectangle.id }, router: { name: 'orthogonal' }, labels: [{ position: 0.5, markup: [{ tagName: 'path', selector: 'arrow' }], attrs: { arrow: { d: 'M 30 15 -30 15', stroke: '#666', strokeWidth: 2, fill: 'none', targetMarker: { type: 'path', fill: '#666', stroke: '#000', d: 'M 10 -10 0 0 10 10 z' }, sourceMarker: { type: 'circle', fill: '#666', stroke: '#333', r: 5, cx: 5 } } } }], attrs: { line: { stroke: '#333', strokeWidth: 2, sourceMarker: { type: 'circle', fill: '#666', stroke: '#333', r: 5, cx: 5 }, targetMarker: { type: 'path', fill: '#666', stroke: '#000', d: 'M 10 -10 0 0 10 10 z' }, vertexMarker: { type: 'circle', fill: '#666', stroke: '#333', r: 5 } } } }).addTo(graph); var Shape = joint.dia.Element.define('custom.Shape', { markup: [{ tagName: 'path', selector: 'body' }], attrs: { root: { magnet: false }, body: { fill: 'lightgreen', stroke: 'green' } }, ports: { groups: { main: { markup: [{ tagName: 'path', selector: 'body' }], position: { name: 'absolute', args: { y: '50%' } }, size: { width: 20, height: 20 }, attrs: { body: { fill: 'green', transform: 'translate(-10,-10)', magnet: true } } } } } }, { /* no prototype methods */ }, { attributes: { shape: { qualify: function(value, node) { return ([ 'hexagon', 'rhombus', 'rounded-rectangle' ].indexOf(value) > -1) && (node instanceof SVGPathElement); }, set: function(shape, refBBox) { var data; switch (shape) { case 'hexagon': data = [ g.Line(refBBox.topMiddle(), refBBox.origin()).midpoint(), g.Line(refBBox.topMiddle(), refBBox.topRight()).midpoint(), refBBox.rightMiddle(), g.Line(refBBox.bottomMiddle(), refBBox.corner()).midpoint(), g.Line(refBBox.bottomMiddle(), refBBox.bottomLeft()).midpoint(), refBBox.leftMiddle() ]; break; case 'rhombus': data = [ refBBox.topMiddle(), refBBox.rightMiddle(), refBBox.bottomMiddle(), refBBox.leftMiddle() ]; break; case 'rounded-rectangle': var rect = refBBox.toJSON(); rect.rx = 5; rect.ry = 5; return { d: V.rectToPath(rect) }; } return { d: 'M ' + data.join(' ').replace(/@/g, ' ') + ' Z' }; } } } }); var shape1 = (new Shape()) .attr('path/shape', 'hexagon') .size(100, 100) .position(100, 50) .addPort({ group: 'main', attrs: { body: { shape: 'hexagon' }} }) .addPort({ group: 'main', args: { x: '100%' }, attrs: { body: { shape: 'hexagon' }} }); var shape2 = (new Shape()) .attr('path/shape', 'rhombus') .size(100, 100) .position(100, 170) .addPort({ group: 'main', attrs: { body: { shape: 'rhombus' }} }) .addPort({ group: 'main', args: { x: '100%' }, attrs: { body: { shape: 'rhombus' }} }); var shape3 = (new Shape()) .attr('path/shape', 'rounded-rectangle') .size(100, 100) .position(100, 290) .addPort({ group: 'main', attrs: { path: { shape: 'rounded-rectangle' }} }) .addPort({ id: 'circle-port', group: 'main', args: { x: '100%' }, markup: [{ tagName: 'circle', selector: 'first', groupSelector: ['circles'] }, { tagName: 'circle', selector: 'last', groupSelector: ['circles'] }], attrs: { circles: { fill: 'green' }, first: { r: 15 }, last: { ref: 'first', r: 10, refDx: 10, magnet: true } } }); graph.addCells([shape1, shape2, shape3]); var portIndex = shape3.getPortIndex('circle-port'); shape3.transition('ports/items/' + portIndex + '/attrs/first/r', 5, { delay: 2000 }); var Progress = joint.dia.Element.define('progress', { attrs: { progressBackground: { stroke: 'gray', strokeWidth: 10, fill: 'white', refR: '50%', refCx: '50%', refCy: '50%' }, progressForeground: { stroke: 'red', strokeWidth: 10, strokeLinecap: 'round', fill: 'none', }, progressText: { fill: 'red', fontSize: 25, fontWeight: 'bold', fontFamily: 'sans-serif', textAnchor: 'middle', textVerticalAnchor: 'middle', refX: '50%', refY: '50%' } } }, { markup: [{ tagName: 'circle', selector: 'progressBackground' }, { tagName: 'path', selector: 'progressForeground' }, { tagName: 'text', selector: 'progressText' }], setProgress: function(progress, opt) { this.attr({ progressText: { text: progress + '%' }, progressForeground: { progressD: progress } }, opt); } }, { attributes: { progressD: { set: function(value, bbox) { function polarToCartesian(centerX, centerY, radius, angleInDegrees) { var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0; return { x: centerX + (radius * Math.cos(angleInRadians)), y: centerY + (radius * Math.sin(angleInRadians)) }; } function describeArc(x, y, radius, startAngle, endAngle){ var start = polarToCartesian(x, y, radius, endAngle); var end = polarToCartesian(x, y, radius, startAngle); var largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1'; var d = [ 'M', start.x, start.y, 'A', radius, radius, 0, largeArcFlag, 0, end.x, end.y ].join(' '); return d; } var center = bbox.center(); return { d: describeArc( center.x, center.y, Math.min(bbox.width / 2, bbox.height / 2), 0, Math.min(360 / 100 * value, 359.99) ) }; } } } }); var progress = new Progress(); progress.resize(100, 100); progress.position(400, 280); progress.setProgress(50); progress.addTo(graph);