JointJs Top Bottom Ports - jointjs

I'm trying to use JointJS with ports feature:
(...)
var model = joint.shapes.devs.Model({
size: { width: width, height: height },
label: node.label,
inPorts: node.inputPorts,
outPorts: node.outputPorts,
attrs: {
'.label': { text: node.label, 'ref-x': .4, 'ref-y': .2 },
rect: { fill: '#2ECC71' },
'.inPorts circle': { fill: '#16A085' },
'.outPorts circle': { fill: '#E74C3C' }
}
(...)
But input ports appears on the left and output ports on the right.
I want input ports on the top and output on the bottom.
Which is the best way to change the port position to Top-Bottom using joint.shapes.devs.Model ?
Thanks in advance.

The positions of ports are calculated in devs.Model.prototype.getPortAttrs. What you can do is just swap x and y port coordinates like in the example below.
joint.shapes.devs.Model = joint.shapes.basic.Generic.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
markup: '<g class="rotatable"><g class="scalable"><rect class="body"/></g><text class="label"/><g class="inPorts"/><g class="outPorts"/></g>',
portMarkup: '<g class="port port<%= id %>"><circle class="port-body"/><text class="port-label"/></g>',
defaults: joint.util.deepSupplement({
type: 'devs.Model',
size: { width: 1, height: 1 },
inPorts: [],
outPorts: [],
attrs: {
'.': { magnet: false },
'.body': {
width: 150, height: 250,
stroke: 'black'
},
'.port-body': {
r: 10,
magnet: true,
stroke: 'black'
},
text: {
fill: 'black',
'pointer-events': 'none'
},
'.label': { text: 'Model', 'ref-x': 10, 'ref-y': .2, 'ref': '.body' },
// CHANGED: find better positions for port labels
'.inPorts .port-label': { dy:-30, x: 4 },
'.outPorts .port-label':{ dy: 15, x: 4 }
//
}
}, joint.shapes.basic.Generic.prototype.defaults),
getPortAttrs: function(portName, index, total, selector, type) {
var attrs = {};
var portClass = 'port' + index;
var portSelector = selector + '>.' + portClass;
var portLabelSelector = portSelector + '>.port-label';
var portBodySelector = portSelector + '>.port-body';
attrs[portLabelSelector] = { text: portName };
attrs[portBodySelector] = { port: { id: portName || _.uniqueId(type) , type: type } };
// CHANGED: swap x and y ports coordinates ('ref-y' => 'ref-x')
attrs[portSelector] = { ref: '.body', 'ref-x': (index + 0.5) * (1 / total) };
// ('ref-dx' => 'ref-dy')
if (selector === '.outPorts') { attrs[portSelector]['ref-dy'] = 0; }
//
return attrs;
}
}));
JS Fiddle: http://jsfiddle.net/kumilingus/L2f73cbf/
Update:
Here is an example how to achieve the same with JointJS v1.0.1+.
There is no need for extending the class with PortsModelInterface anymore. The ports API is now implemented by joint.dia.Element i.e. arbitrary element can be enriched with ports easily.
var shape = new joint.shapes.devs.Model({
inPorts: ['in1', 'in2'],
outPorts: ['out1', 'out2'],
ports: {
groups: {
'in': { position: 'top'},
'out': { position: 'bottom' }
}
}
});
JSFiddle: http://jsfiddle.net/kumilingus/trk63agg/
For more information please see the docs:
ports API
port layouts

Just change position name in your joint.shapes.devs.Model creation as follow:
new joint.shapes.devs.Model({
position: { x: x, y: y },
size: { width: 90, height: 90 },
inPorts: ['in1'],
outPorts:['out1'],
attrs: {
rect: { fill: '#2ECC71' },
'.inPorts circle': {r:10, fill: '#16A085' },
'.outPorts circle': { fill: '#E74C3C' }
},
ports: {
groups: {
'in': {
position: {
name: 'top'
},
attrs: {
'.port-body': {
r: 1
}
}
},
'out': {
position: {
name: 'bottom'
},
attrs: {
'.port-body': {
r: 1
}
}
}
}
}
});
Consider the position name changed to top and bottom

Related

Link:dbclick event with vertex Adding set to true [Jointjs] [Rappid]

I am unable to get Link:dbclick event on double click of link with vertexAdding: set to true.
with Vertex Adding set to false the dbclick event works fine.
i am using link:pointerup to add Tool view to link like this::
this.paper.on('link:pointerup', (linkView) => {
var tools;
var ns = joint.linkTools;
var toolsView = new joint.dia.ToolsView({
name: 'link:pointerup',
tools: [
new ns.Vertices(),
new ns.Vertices({ vertexAdding: true}),
new ns.Remove({ focusOpacity: 0.5,
distance: 5,
offset: 10}),
new ns.TargetArrowhead(),
new ns.Segments(),
new ns.Boundary({ padding: 10 }),
]
});
joint.ui.Halo.clear(this.paper);
joint.ui.FreeTransform.clear(this.paper);
this.paper.removeTools();
linkView.addTools(toolsView);
});
For Link Creation:::
export class Link2 extends joint.shapes.standard.Link {
defaults() {
return joint.util.defaultsDeep({
router: {
name: 'normal',
},
connector: {
name: 'normal',
},
labels: [],
attrs: {
line: {
stroke: '#284f96',
strokeDasharray: '0',
strokeWidth: 1,
fill: 'none',
sourceMarker: {
d: 'M 0 0 0 0',
fill: {
type: 'color-palette',
group: 'marker-source',
when: { ne: { 'attrs/line/sourceMarker/d': 'M 0 0 0 0' } },
index: 2
},
},
targetMarker: {
d: ' M 0 -3 -6 0 0 3 z',
},
}
}
}, joint.shapes.standard.Link.prototype.defaults);
}
defaultLabel = {
attrs: {
rect: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 2,
refWidth: 10,
refHeight: 10,
refX: 0,
refY: 0
}
}
};
getMarkerWidth(type: any) {
const d = (type === 'source') ? this.attr('line/sourceMarker/d') : this.attr('line/targetMarker/d');
console.log("marker width", this.getDataWidth(d));
return this.getDataWidth(d);
}
getDataWidth = memoize(function (d: any) {
return (new joint.g.Rect(d)).bbox().width;
});
static connectionPoint(line: any, view: any, magnet: any, opt: any, type: any, linkView: any) {
// const markerWidth = linkView.model.getMarkerWidth(type);
opt = { offset: 5, stroke: true };
// connection point for UML shapes lies on the root group containg all the shapes components
const modelType = view.model.get('type');
if (modelType.indexOf('uml') === 0) opt.selector = 'root';
// taking the border stroke-width into account
if (modelType === 'standard.InscribedImage') opt.selector = 'border';
return joint.connectionPoints.boundary.call(this, line, view, magnet, opt, type, linkView);
}
}
and i am using following code to get double click event on Link:::
this.rappid.paper.on(
"link:pointerdblclick", (linkView) => {
// linkView.model.attr("priority",0);
console.log("linkview", linkView);
// this.currentLink = linkView
this.setState({ modalView: "lableModal", visible: true, currentLink: linkView });
});

New on JOINT JS

When cloning Objects I know how to change attributes for example>
Clone Object 1 2 times var m5 = m1.clone().translate(300, 0).attr('rect/fill', '#F69F43').attr('.label/text', 'Model 2').addTo(graph);
here changing name to attr label/text
but having this element:
var m1 = new joint.shapes.devs.Model({
position: { x: 300, y: 50 },
size: { width: 120, height: 150 },
inPorts: ['in1','in2','in3','in4'],
outPorts: ['out'],
attrs: { '.label': { text: 'Model1', 'ref-x': .4, 'ref-y': .2 }, rect: { fill: '#2ECC71' },
'.inPorts circle': { fill: '#16A085', magnet: 'passive', type: 'input' },
'.outPorts circle': { fill: '#E74C3C', type: 'output' },
'.name': { name: 'Bloque1' } } }).addTo(graph);
How to clone changing .inPorts Circle fill ???
Thanks
var m5 = m1.clone().attr('.inPorts circle/fill', 'red').addTo(graph); changes color of each input ports to red. You can change color of particular port with m5.attr('.inPorts>.port3>circle/fill' ,'green')

Connect 2 elements jointjs

I'm a newbie in jointjs. Today I have a small example as below:
I have a start Activity
var startEndActivity = function (x, y, name, fillColor, textColor, size) {
fillColor = fillColor || '#007FBE';
textColor = textColor || "#000";
size = size || { width: 100, height: 40 };
var rect = new joint.shapes.basic.Rect({
position: { x: x, y: y },
size: size,
attrs: {
rect: { fill: fillColor, rx: 5, ry: 5, 'stroke-width': 1, stroke: '#002F5D' },
text: {
text: name, fill: textColor,
'font-size': 14, 'font-family': 'sans-serif'
}
}
});
graph.addCell(rect);
return rect;}
I have a condition Activity
var activityDecision = function (x, y, name, fillColor, textColor, size{
fillColor = fillColor || '#BF664C';
textColor = textColor || "#080808";
size = size || { width: 200, height: 60 };
var node = new joint.shapes.basic.Rhombus({
position: { x: x, y: y },
size: size,
});
node.attr({
rect: { fill: fillColor, 'stroke-width': 1, stroke: 'white' },
text: {
text: name, fill: textColor,
}
});
graph.addCell(node);
return node;}
I want to click on start activity and I can draw a arrow to connect between 2 elements. Thank you so much
The most common approach that I know of is to use ports on your elements. This link should get you started on that route:
WORKING WITH PORTS
If you prefer to have the entire element behave as a port you need to look into the "magnetic" attribute. This link should help you get started researching what you need (especially the first answer):
How to interactively create links in JointJS
I found solution for this. Thank you so much. Just add more attribute like this
el.attr('rect/magnet', true).attr('text/pointer-events', 'none');
function create(type) {
var link = new joint.dia.Link({
source: { x: 10, y: 20 },
target: { x: 350, y: 20 },
attrs: {}
});
link.prop({...});
link.addTo(graph)
}
//init connection:
new joint.dia.Link({
source: { id: 'source-id' },
target: { id: 'target-id', port: 'port_id'}
});
In order to connect two element you have to work with Ports (documentation):
My best advice for you is to learn how to implement ports by looking in the JointJS source code, as a reference look for object: joint.shapes.devs.Model (live demo + source code inside)
something like this:
var myRect = joint.shapes.devs.Model.extend({
portMarkup: '<circle class="port-body myCustomClass"/>',
defaults: _.defaultsDeep({
type: 'myRect',
}, joint.shapes.basic.Generic.prototype.defaults),
});
and inside of startEndActivity function change the var rect to:
var startRect = new myRect({
position: { x: x, y: y },
size: size,
attrs: {
rect: { fill: fillColor, rx: 5, ry: 5, 'stroke-width': 1, stroke: '#002F5D' },
text: {
text: name, fill: textColor,
'font-size': 14, 'font-family': 'sans-serif'
}
},
ports: {
groups: {
'out': {
position: {
name: 'right'
},
attrs: {
'.port-label': {
fill: '#000'
},
'.port-body': {
fill: '#fff',
stroke: '#000',
r: 10,
magnet: true
}
},
label: {
position: {
name: 'right',
args: {
y: 10
}
}
}
}
}
});
do the same for the second element.

JointJS ports: type (input, output) not getting set

I'm creating elements with ports using this code (including the portion to move the ports to top and bottom):
joint.shapes.devs.flowchartProcess = joint.shapes.devs.Model.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
markup: '<g class="rotatable"><g class="scalable"><rect class="body"/></g><text class="body-label"/><g class="inPorts"/><g class="outPorts"/></g>',
portMarkup: '<g class="port port<%= id %>"><circle class="port-body"/><text class="port-label"/></g>',
defaults: joint.util.deepSupplement({
// type: 'devs.flowchartProcess',
attrs: {
'.body': { stroke: 'black' },
'.body-label': { 'ref-x': .5, 'ref-y': .5, ref: '.body', 'y-alignment': 'middle', 'x-alignment': 'middle' },
'.port-body': { r: portRadius, magnet: 'active' },
'.inPorts .port-body': { stroke: portBodyStroke, fill: inPortFill },
'.outPorts .port-body': { stroke: portBodyStroke, fill: outPortFill},
'.inPorts .port-label': { 'font-size': 0},
'.outPorts .port-label': {'font-size': 0 }
},
parentID: none
}, joint.shapes.devs.Model.prototype.defaults),
getPortAttrs: function(portName, index, total, selector, type) {
var attrs = {};
var portClass = 'port' + index;
var portSelector = selector + '>.' + portClass;
var portLabelSelector = portSelector + '>.port-label';
var portBodySelector = portSelector + '>.port-body';
attrs[portLabelSelector] = { text: portName };
attrs[portBodySelector] = { port: { id: portName || _.uniqueId(type) , type: type } };
// CHANGED: swap x and y ports coordinates ('ref-y' => 'ref-x')
attrs[portSelector] = { ref: '.body', 'ref-x': (index + 0.5) * (1 / total) };
// ('ref-dx' => 'ref-dy')
if (selector === '.outPorts') { attrs[portSelector]['ref-dy'] = 0; }
//
return attrs;
}
}));
joint.shapes.devs.flowchartProcessView = joint.shapes.devs.ModelView;
Then I instantiate the above element like this:
newElement = new joint.shapes.devs.flowchartProcess ({
id: id,
size: { width: width, height: height },
inPorts: ['in1'],
outPorts: ['out1'],
attrs: {
text: { text: elementLabel }
}
});
The issue I'm having is in trying to validate connections dragged between element ports. I tried this sample code based on the API and tutorials:
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
// Prevent linking from input ports.
if (magnetS && magnetS.getAttribute('type') === 'input') return false;
// Prevent linking from output ports to input ports within one element.
if (cellViewS === cellViewT) return false;
// Prevent linking to input ports.
return magnetT && magnetT.getAttribute('type') === 'input';
}
No links were validating so I did some digging. I then replaced the above code with this:
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
console.log(cellViewS + " | " + magnetS.getAttribute('type') + " | " + cellViewT + " | " + magnetT.getAttribute('class') + " | " + end + " | " + linkView);
return true;
}
From there I saw that there is no "type" attribute being set, though the "class" for the target port was fine. Setting a watch on those variables in Chrome verified that attribute isn't there.
I was under the impression that using the devs.Model library automatically set up the ports with all the needed stuff. Is whether the type is an input or output port something I still need to set manually? Have I managed to mess up the definition or instantiation somehow, preventing the proper attributes from being defined?
Many thanks in advance!
Okay, so solved it myself. This line:
attrs[portBodySelector] = { port: { id: portName || _.uniqueId(type) , type: type } };
isn't setting type in the main attributes in the magnetic port template so this code:
if (magnetS && magnetS.getAttribute('type') === 'input') return false;
is trying to get an attribute that doesn't exist. The 'in' and 'out' attributes exist in various places but not where the magnetS can find it. There is probably a deeper fix that would be more elegant but this workaround got me rolling again:
attrs: {
'.body': { stroke: 'black' },
'.body-label': { 'ref-x': .5, 'ref-y': .5, ref: '.body', 'y-alignment': 'middle', 'x-alignment': 'middle' },
'.port-body': { r: portRadius, magnet: 'active' },
'.inPorts .port-body': { stroke: portBodyStroke, fill: inPortFill, type: 'input' }, // add type here
'.outPorts .port-body': { stroke: portBodyStroke, fill: outPortFill, type: 'output' }, // and here
'.inPorts .port-label': { 'font-size': 0},
'.outPorts .port-label': {'font-size': 0 }
},
That defines the type attribute in the proper place for the validate code to find it and all is then well.

JointjS: How to get attributes of a given element?

I have an element defined as this:
var m1 = new joint.shapes.devs.Model({
position: { x: 100, y: 50 },
size: { width: 190, height: 50 },
inPorts: ['in'],
outPorts: ['out'],
attrs: {
'.label': { text: 'Model','ref-x': .4, 'ref-y': .25 ,fill: '#fefefe',
'font-size': 14,
'font-weight': 'bold',
'font-variant': 'small-caps' },
rect: { fill: '#fefefe'},
'.inPorts circle': { r:5 ,fill: '#16A085' ,magnet: 'passive', type: 'input'},
'.outPorts circle': { r:5, fill: '#E74C3C',magnet: 'passive',type: 'output' },
}
THe question is how can I get the '.label' attribute?
E.g, I need to get the text "Model", what should I do?
If I want to get the 'fill' attr of 'rect' , I can simply use m1.get('attrs').rect.fill.
But I don't know HOW TO GET the '.label' attr.
Use the attr() method for both setting attributes and getting them back:
m1.attr('.label/text') // 'Model'
m1.attr('.label/text', 'New Model')
m1.attr('.label/text') // 'New Model'
'/' is a path separator into the nested attrs object.
var rootnode = new joint.shapes.basic.Circle({
position: { x: 20, y: 220 },
size: { width: 60, height: 30 },
attrs: {
text: { text: 'parent' },
circle: { fill: 'yellow', hasChildren:false }
},
name: 'parent'
});
graph.addCell(rootnode);

Resources