JointJS: Dynamic Markup
by Roman Bruckner
HTML
<html>
<head>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.6.3/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.7.5/joint.js"></script>
</body>
</html>
JavaScript
const { dia, util, shapes: defaultShapes } = joint;
const List = dia.Element.define('List', {
size: { width: 155, height: 0 },
attrs: {
body: {
width: 'calc(w)',
height: 'calc(h)',
fill: '#E0E0E0',
rx: '10',
ry: '10',
},
label: {
html: 'List',
style: {
margin: '5px',
fontSize: '25px'
}
},
bodyHTML: {
width: 'calc(w)',
height: 'calc(h)',
},
},
items: []
}, {
initialize: function() {
dia.Element.prototype.initialize.apply(this, arguments);
this.on('change', this.onChange, this);
this.buildMarkup();
},
onChange: function(_, opt) {
// make sure the markup is not changed from the outside
if (opt.list !== this.id && this.hasChanged('markup')) {
throw new Error('Markup can not be modified.');
}
// when the items has changed, rebuild the markup
if (this.hasChanged('items')) {
this.buildMarkup(opt);
}
},
buildMarkup: function(opt) {
let items = this.get('items');
if (!Array.isArray(items)) {
items = [];
}
const itemsMarkup = items.map((item) => this.buildItemMarkup(item));
const markup = util.svg/* xml */`
<rect @selector="body" />
<foreignObject @selector="bodyHTML">
<div @selector="label" xmlns="http://www.w3.org/1999/xhtml"></div>
<div @selector="itemsWrap" style="padding: 5px;" xmlns="http://www.w3.org/1999/xhtml"></div>
</foreignObject>
`;
const [, bodyHTML] = markup;
bodyHTML.children[1].children = itemsMarkup;
const flags = { list: this.id, dry: true, ...opt };
this.set('markup', markup, flags);
const { width } = this.size();
const height = items.length * 40...