Custom Element View

An Element with Tabs

by Roman Bruckner

HTML

<html>
<head>
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.3.0/joint.css" />
</head>
<body>
      <!-- content -->
      <div id="paper"></div>

      <!-- dependencies -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.4.0/backbone.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.3.0/joint.js"></script>
    
    </body>
</html>

JavaScript

var graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
var paper = new joint.dia.Paper({
    el: document.getElementById('paper'),
    width: 600,
    height: 600,
    gridSize: 20,
    model: graph,
    async: true,
    sorting: joint.dia.Paper.sorting.APPROX,
    cellViewNamespace: joint.shapes
});

class Shape extends joint.dia.Element {

    defaults() {
        return {
            ...super.defaults,
            type: 'custom.Shape',
            fillColor: 'red',
            outlineColor: 'blue',
            activeTab: 0,
            numberOfTabs: 1,
            faded: false
        };
    }

}

const ShapeView = joint.dia.ElementView.extend({

    events: {
        'dblclick': 'onDblClick',
        'mousedown .tab': 'onTabClick'
    },

    TAB_HEIGHT: 20,
    INACTIVE_TAB_COLOR: 'gray',

    presentationAttributes: joint.dia.ElementView.addPresentationAttributes({
        activeTab: ['UPDATE'],
        fillColor: ['UPDATE'],
        outlineColor: ['UPDATE'],
        faded: ['FADE'],
        numberOfTabs: ['RENDER']
    }),

    confirmUpdate: function(...args) {
        let flags = joint.dia.ElementView.prototype.confirmUpdate.call(this, ...args);
        if (this.hasFlag(flags, 'FADE')) {
            this.toggleFade();
            flags = this.removeFlag(flags, 'FADE');
        }
        return flags;
    },

    render: function() {
        const { vel, model } = this;
        var body = this.vBody = V('rect').addClass('body');
        var tabs = this.vTabs = [];
        var texts = this.vTexts = [];
        for (let i = 0, n = model.prop('numberOfTabs'); i < n; i++) {
            tabs.push(V('rect').addClass('tab'));
            texts.push(V('text').addClass('label').text(`Tab ${i + 1}`));
        }
        vel.empty().append([
            body,
            ...tabs
        ]);
        this.translate();
        this.update();
        this._refreshPorts();
    },

    update: function() {
        this.updateBody();
       ...