jointjs
Version:
JavaScript diagramming library
355 lines (296 loc) • 9.64 kB
JavaScript
var erd = joint.shapes.erd;
var graph = new joint.dia.Graph();
var paper = new joint.dia.Paper({
el: document.getElementById('paper'),
width: 695,
height: 600,
model: graph,
linkPinning: false,
highlighting: false,
defaultConnectionPoint: function(line, view) {
var element = view.model;
return element.getConnectionPoint(line.start) || element.getBBox().center();
}
});
// Custom highlighter - display an outline around each element that fits its shape.
var highlighter = V('path', {
'stroke': '#e9fc03',
'stroke-width': '2px',
'fill': 'transparent',
'pointer-events': 'none'
});
// Define a specific highligthing path for every shape.
erd.Attribute.prototype.getHighlighterPath = function(w, h) {
return ['M', 0, h / 2, 'A', w / 2, h / 2, '0 1,0', w, h / 2, 'A', w / 2, h / 2, '0 1,0', 0, h / 2].join(' ');
};
erd.Entity.prototype.getHighlighterPath = function(w, h) {
return ['M', w, 0, w, h, 0, h, 0, 0, 'z'].join(' ');
};
erd.Relationship.prototype.getHighlighterPath = function(w, h) {
return ['M', w / 2, 0, w, w / 2, w / 2, w, 0, w / 2, 'z'].join(' ');
};
erd.ISA.prototype.getHighlighterPath = function(w, h) {
return ['M', -8, 1, w + 8, 1, w / 2, h + 2, 'z'].join(' ');
};
// Define a specific connection points for every shape
erd.Attribute.prototype.getConnectionPoint = function(referencePoint) {
// Intersection with an ellipse
return g.Ellipse.fromRect(this.getBBox()).intersectionWithLineFromCenterToPoint(referencePoint);
};
erd.Entity.prototype.getConnectionPoint = function(referencePoint) {
// Intersection with a rectangle
return this.getBBox().intersectionWithLineFromCenterToPoint(referencePoint);
};
erd.Relationship.prototype.getConnectionPoint = function(referencePoint) {
// Intersection with a rhomb
var bbox = this.getBBox();
var line = new g.Line(bbox.center(), referencePoint);
return (
line.intersection(new g.Line(bbox.topMiddle(), bbox.leftMiddle())) ||
line.intersection(new g.Line(bbox.leftMiddle(), bbox.bottomMiddle())) ||
line.intersection(new g.Line(bbox.bottomMiddle(), bbox.rightMiddle())) ||
line.intersection(new g.Line(bbox.rightMiddle(), bbox.topMiddle()))
);
};
erd.ISA.prototype.getConnectionPoint = function(referencePoint) {
// Intersection with a triangle
var bbox = this.getBBox();
var line = new g.Line(bbox.center(), referencePoint);
return (
line.intersection(new g.Line(bbox.origin(), bbox.topRight())) ||
line.intersection(new g.Line(bbox.origin(), bbox.bottomMiddle())) ||
line.intersection(new g.Line(bbox.topRight(), bbox.bottomMiddle()))
);
};
// Bind custom ones.
paper.on('cell:highlight', function(cellView) {
var padding = 5;
var bbox = cellView.getBBox({ useModelGeometry: true }).inflate(padding);
highlighter.translate(bbox.x, bbox.y, { absolute: true });
highlighter.attr('d', cellView.model.getHighlighterPath(bbox.width, bbox.height));
V(paper.viewport).append(highlighter);
});
paper.on('cell:unhighlight', function() {
highlighter.remove();
});
// Create shapes
var employee = new erd.Entity({
position: { x: 100, y: 200 },
attrs: {
text: {
fill: '#ffffff',
text: 'Employee',
letterSpacing: 0,
style: { textShadow: '1px 0 1px #333333' }
},
'.outer': {
fill: '#31d0c6',
stroke: 'none',
filter: { name: 'dropShadow', args: { dx: 0.5, dy: 2, blur: 2, color: '#333333' }}
},
'.inner': {
fill: '#31d0c6',
stroke: 'none',
filter: { name: 'dropShadow', args: { dx: 0.5, dy: 2, blur: 2, color: '#333333' }}
}
}
});
var wage = new erd.WeakEntity({
position: { x: 530, y: 200 },
attrs: {
text: {
fill: '#ffffff',
text: 'Wage',
letterSpacing: 0,
style: { textShadow: '1px 0 1px #333333' }
},
'.inner': {
fill: '#31d0c6',
stroke: 'none',
points: '155,5 155,55 5,55 5,5'
},
'.outer': {
fill: 'none',
stroke: '#31d0c6',
points: '160,0 160,60 0,60 0,0',
filter: { name: 'dropShadow', args: { dx: 0.5, dy: 2, blur: 2, color: '#333333' }}
}
}
});
var paid = new erd.IdentifyingRelationship({
position: { x: 350, y: 190 },
attrs: {
text: {
fill: '#ffffff',
text: 'Gets paid',
letterSpacing: 0,
style: { textShadow: '1px 0 1px #333333' }
},
'.inner': {
fill: '#7c68fd',
stroke: 'none'
},
'.outer': {
fill: 'none',
stroke: '#7c68fd',
filter: { name: 'dropShadow', args: { dx: 0, dy: 2, blur: 1, color: '#333333' }}
}
}
});
var isa = new erd.ISA({
position: { x: 125, y: 300 },
attrs: {
text: {
text: 'ISA',
fill: '#ffffff',
letterSpacing: 0,
style: { 'text-shadow': '1px 0 1px #333333' }
},
polygon: {
fill: '#fdb664',
stroke: 'none',
filter: { name: 'dropShadow', args: { dx: 0, dy: 2, blur: 1, color: '#333333' }}
}
}
});
var number = new erd.Key({
position: { x: 10, y: 90 },
attrs: {
text: {
fill: '#ffffff',
text: 'Number',
letterSpacing: 0,
style: { textShadow: '1px 0 1px #333333' }
},
'.outer': {
fill: '#feb662',
stroke: 'none',
filter: { name: 'dropShadow', args: { dx: 0, dy: 2, blur: 2, color: '#222138' }}
},
'.inner': {
fill: '#feb662',
stroke: 'none'
}
}
});
var employeeName = new erd.Normal({
position: { x: 75, y: 30 },
attrs: {
text: {
fill: '#ffffff',
text: 'Name',
letterSpacing: 0,
style: { textShadow: '1px 0 1px #333333' }
},
'.outer': {
fill: '#fe8550',
stroke: '#fe854f',
filter: { name: 'dropShadow', args: { dx: 0, dy: 2, blur: 2, color: '#222138' }}
}
}
});
var skills = new erd.Multivalued({
position: { x: 150, y: 90 },
attrs: {
text: {
fill: '#ffffff',
text: 'Skills',
letterSpacing: 0,
style: { 'text-shadow': '1px 0px 1px #333333' }
},
'.inner': {
fill: '#fe8550',
stroke: 'none',
rx: 43,
ry: 21
},
'.outer': {
fill: '#464a65',
stroke: '#fe8550',
filter: { name: 'dropShadow', args: { dx: 0, dy: 2, blur: 2, color: '#222138' }}
}
}
});
var amount = new erd.Derived({
position: { x: 440, y: 80 },
attrs: {
text: {
fill: '#ffffff',
text: 'Amount',
letterSpacing: 0,
style: { textShadow: '1px 0 1px #333333' }
},
'.inner': {
fill: '#fca079',
stroke: 'none',
display: 'block'
},
'.outer': {
fill: '#464a65',
stroke: '#fe854f',
'stroke-dasharray': '3,1',
filter: { name: 'dropShadow', args: { dx: 0, dy: 2, blur: 2, color: '#222138' }}
}
}
});
var uses = new erd.Relationship({
position: { x: 300, y: 390 },
attrs: {
text: {
fill: '#ffffff',
text: 'Uses',
letterSpacing: 0,
style: { textShadow: '1px 0 1px #333333' }
},
'.outer': {
fill: '#797d9a',
stroke: 'none',
filter: { name: 'dropShadow', args: { dx: 0, dy: 2, blur: 1, color: '#333333' }}
}
}
});
// Create new shapes by cloning
var salesman = employee.clone().translate(0, 200).attr('text/text', 'Salesman');
var date = employeeName.clone().position(585, 80).attr('text/text', 'Date');
var car = employee.clone().position(430, 400).attr('text/text', 'Company car');
var plate = number.clone().position(405, 500).attr('text/text', 'Plate');
// Helpers
var createLink = function(elm1, elm2) {
var myLink = new erd.Line({
markup: [
'<path class="connection" stroke="black" d="M 0 0 0 0"/>',
'<path class="connection-wrap" d="M 0 0 0 0"/>',
'<g class="labels"/>',
'<g class="marker-vertices"/>',
'<g class="marker-arrowheads"/>'
].join(''),
source: { id: elm1.id },
target: { id: elm2.id }
});
return myLink.addTo(graph);
};
var createLabel = function(txt) {
return {
labels: [{
position: -20,
attrs: {
text: { dy: -8, text: txt, fill: '#ffffff' },
rect: { fill: 'none' }
}
}]
};
};
// Add shapes to the graph
graph.addCells([employee, salesman, wage, paid, isa, number, employeeName, skills, amount, date, plate, car, uses]);
createLink(employee, paid).set(createLabel('1'));
createLink(employee, number);
createLink(employee, employeeName);
createLink(employee, skills);
createLink(employee, isa);
createLink(isa, salesman);
createLink(salesman, uses).set(createLabel('0..1'));
createLink(car, uses).set(createLabel('1..1'));
createLink(car, plate);
createLink(wage, paid).set(createLabel('N'));
createLink(wage, amount);
createLink(wage, date);