How to give JointJS elements a remove tool?

2019-01-18 07:41发布

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?

3条回答
再贱就再见
2楼-- · 2019-01-18 08:36
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);
}

}));

查看更多
聊天终结者
3楼-- · 2019-01-18 08:43

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

查看更多
聊天终结者
4楼-- · 2019-01-18 08:47

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:

element tool unhighlighted render

When it is highlighted:

rendering of the tool element

I can then define other shapes really easily by extending toolElement. Here are the data flow diagram shapes for data stores:

enter image description here

and external actors:

enter image description here

查看更多
登录 后发表回答