Reputation: 913
In JointJS, links come with a handy responsive tool for removing links (when you hover over the link, an "x" appears, and clicking it removes the link). Elements, on the other hand, have a remove() method in the API, but don't have the UI "x" to expose that method to users.
Is there a straightforward way to give users the ability to delete elements in the UI?
Upvotes: 3
Views: 6977
Reputation: 681
A more native approach could be using the provided elementTools:
const view = element.findView(paper);
const removeButton = new joint.elementTools.Remove({
focusOpacity: 0.5,
rotate: true,
x: '50%',
y: '0%',
offset: { x: 10, y: 10 }
});
const toolsView = new joint.dia.ToolsView({
name: 'basic-tools',
tools: [removeButton]
});
view.addTools(toolsView);
Upvotes: 1
Reputation: 1475
Have a look at the HTML example on the JointJS website.
As you can see the elements have a close button there, so there's no need to complicate things by creating your own. Simply create a view for your element that contains the HTML code for the button, as well as the event handling. It's all in the source code of the example.
Note that the example doesn't provide you the CSS file for the HTML elements, but you also need it: http://resources.jointjs.com/tutorials/joint/tutorials/css/html-elements.css
Upvotes: 2
Reputation: 77
joint.shapes.devs.ModelView = joint.dia.ElementView.extend(_.extend({},joint.shapes.basic.PortsViewInterface,{
initialize:function(){
joint.dia.ElementView.prototype.initialize.apply(this,arguments);
},
render:function(){
joint.dia.ElementView.prototype.render.apply(this,arguments);
this.renderTools();
this.update();
return this;
},
renderTools:function(){
var toolMarkup = this.model.toolMarkup || this.model.get('toolMarkup');
if (toolMarkup) {
var nodes = V(toolMarkup);
V(this.el).append(nodes);
}
return this;
},
pointerclick: function (evt, x, y) {
var className = evt.target.parentNode.getAttribute('class');
switch (className) {
case 'element-tool-remove':
this.model.remove();
return;
break;
default:
}
joint.dia.CellView.prototype.pointerclick.apply(this, arguments);
}
}));
Upvotes: -1
Reputation: 8880
In my project I define a custom shape - toolElement
- that encapsulates this behaviour and then extend this with other custom shapes as required.
Full disclosure: This technique leans heavily on the jointjs code for links - I just adapted it :o)
Here is a jsfiddle showing it working:
http://jsfiddle.net/kj4bqczd/3/
The toolElement
is defined like this:
joint.shapes.tm.toolElement = joint.shapes.basic.Generic.extend({
toolMarkup: ['<g class="element-tools">',
'<g class="element-tool-remove"><circle fill="red" 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 this element from the model</title>',
'</g>',
'</g>'].join(''),
defaults: joint.util.deepSupplement({
attrs: {
text: { 'font-weight': 400, 'font-size': 'small', fill: 'black', 'text-anchor': 'middle', 'ref-x': .5, 'ref-y': .5, 'y-alignment': 'middle' },
},
}, joint.shapes.basic.Generic.prototype.defaults)
});
You can add more markup if you need other tools as well as the remove button.
The remove behaviour is encapsulated in a custom view:
joint.shapes.tm.ToolElementView = joint.dia.ElementView.extend({
initialize: function() {
joint.dia.ElementView.prototype.initialize.apply(this, arguments);
},
render: function () {
joint.dia.ElementView.prototype.render.apply(this, arguments);
this.renderTools();
this.update();
return this;
},
renderTools: function () {
var toolMarkup = this.model.toolMarkup || this.model.get('toolMarkup');
if (toolMarkup) {
var nodes = V(toolMarkup);
V(this.el).append(nodes);
}
return this;
},
pointerclick: function (evt, x, y) {
this._dx = x;
this._dy = y;
this._action = '';
var className = evt.target.parentNode.getAttribute('class');
switch (className) {
case 'element-tool-remove':
this.model.remove();
return;
break;
default:
}
joint.dia.CellView.prototype.pointerclick.apply(this, arguments);
},
});
You can then extend these to make your custom shapes. In my project, I am doing data flow diagrams and here is the definition of the Process
shape:
joint.shapes.tm.Process = joint.shapes.tm.toolElement.extend({
markup: '<g class="rotatable"><g class="scalable"><circle class="element-process"/><title class="tooltip"/></g><text/></g>',
defaults: joint.util.deepSupplement({
type: 'tm.Process',
attrs: {
'.element-process': { 'stroke-width': 1, r: 30, stroke: 'black', transform: 'translate(30, 30)' },
text: { ref: '.element-process'}
},
size: { width: 100, height: 100 }
}, joint.shapes.tm.toolElement.prototype.defaults)
});
and view:
joint.shapes.tm.ProcessView = joint.shapes.tm.ToolElementView;
I show and hide the tool markup, depending whether the element is highlighted using CSS. You could do the same when hovering (like the links do) if you like:
.element .element-tools {
display: none;
cursor: pointer
}
.element.highlighted .element-tools {
display: inline;
}
When rendered, it looks like this (note: in my case, I have another button in the tools, not just the remove - that is what the green chevron button is. I removed this from the code samples above to make them simpler):
When the element is not highlighted:
When it is highlighted:
I can then define other shapes really easily by extending toolElement. Here are the data flow diagram shapes for data stores:
and external actors:
Upvotes: 9