How to Change CSS of Custom Template JointJS Element? - javascript

I've been using JointJS for a while now and I'm trying to create an HTML template for my elements. So I've been using the tutorial but it didn't quit do it for me.
The thing I'm trying to accomplish is to adjust the color of the HTML element when an action has been performed, like double clicking the element. I did notice the way that the text is changed in the tutorial, but there is no example of changing any colors.
Edit
I've tried this to get a starting color on the element:
joint.shapes.html = {};
joint.shapes.html.OdinElement = joint.shapes.basic.Rect.extend({
defaults: joint.util.deepSupplement({
type: 'html.Element',
attrs: {
rect: { stroke: 'none', 'fill-opacity': 0 }
}
}, joint.shapes.basic.Rect.prototype.defaults)
});
// Create a custom view for that element that displays an HTML div above it.
// -------------------------------------------------------------------------
joint.shapes.html.OdinElementView = joint.dia.ElementView.extend({
template: [
'<div class="html-element">',
'<button class="delete">x</button>',
'<label></label>',
'<span></span>', '<br/>',
'</div>'
].join(''),
initialize: function() {
_.bindAll(this, 'updateBox');
joint.dia.ElementView.prototype.initialize.apply(this, arguments);
this.$box = $(_.template(this.template)());
this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
// Update the box position whenever the underlying model changes.
this.model.on('change', this.updateBox, this);
// Remove the box when the model gets removed from the graph.
this.model.on('remove', this.removeBox, this);
this.updateBox();
},
render: function() {
joint.dia.ElementView.prototype.render.apply(this, arguments);
this.paper.$el.prepend(this.$box);
this.updateBox();
return this;
},
updateBox: function() {
// Set the position and dimension of the box so that it covers the JointJS element.
var bbox = this.model.getBBox();
// Example of updating the HTML with a data stored in the cell model.
this.$box.find('label').text(this.model.get('label'));
this.$box.css({ width: bbox.width, height: bbox.height, left: bbox.x, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)', background: this.model.get('color')}); // I've tried to add it like a background
},
removeBox: function(evt) {
this.$box.remove();
}
});
//add a new element like this
new joint.shapes.html.OdinElement({
position: { x: 80, y: 80 },
size: { width: 200, height: 50 },
label: 'label',
color: '#ff0000'
});
I've also tried to set it like the text is set in the label, but I've no idea if there is a function for that.
Does anyone have any idea how to do this?
Thanks a lot!
Tim

I found the answer myself!
Apparently it is not allowed to create a different element in the html variable than the given Element. So I had to change the joint.shapes.html.OdinElement to joint.shapes.html.Element and the joint.shapes.html.OdinElementView to joint.shapes.html.ElementView
Now it works all fine :)
Thanks for your help!

Related

jointjs element mouse click event

I'm using jointjs to draw graphs.
I'm wondering how to listen to the mouse click event on an element?
I found on http://www.jointjs.com/api#joint.dia.Element, there is only change:position option but no onclick option lol.
There's only cell:pointerclick option on the whole paper instead of the single element.
How can I achieve only listen to mouse click element on the single element? (Say I want to resize the paper after the click)
Thanks!
You can use the pointerclick event to capture the click events on elements. The view is passed as a parameter to the function and you can obtain the model of the view through cellView.model
paper.on('cell:pointerclick', function (cellView) {
// your logic goes here
);
A way to do that it's using classes and javascript events, look:
First, you assign a class to the joint js element via markup , for example a class called 'myclass' in this case:
var rect1 = new joint.shapes.basic.Rect({
markup: '<g class="rotatable"><g class="scalable"><image id="myrect1" class="myclass"/></g><text/></g>',
size: { width: 30, height: 73.2 },
attrs: {
rect: { fill: bgcolor1,'stroke-width': 0 },
}
});
Then, you capture the click event on that class objects via javascript, not in the canvas but in the document :
$(document).on('click', '.myclass', function () {
//alert('yayy!');
});
Hope it helps !
you need to listen on view not on the model. Trace all caught events on the element:
var a = new joint.shapes.basic.Rect({
size: { width: 100, height: 100 },
position: { x: 300, y: 300 }
}).addTo(graph);
paper.findViewByModel(a).on('all', function() {
console.log(arguments);
});
https://jsfiddle.net/vtalas/0z6jyq70/

ExtJS 5.0 - adding button as overlay to component

We are working with software supplied by a third party, and we are not allowed to modify it, can use only overrides.
I would like to create a new button and overlay it on top of a text input so that they are close together.
I'm having trouble getting the overlay to align, instead it appears top left on the screen. So of course it doesn't align to the text input. Sample code is below, in this case implemented in the view initComponent override after this.callParent([]); is called.
var viewport = Ext.ComponentQuery.query('viewport')[0];
var overlay = viewport.add({
xtype: 'panel',
fullscreen: true,
left: 0,
top: 0,
width: 120,
height: 40,
items:[{
xtype: 'button',
text: 'Find Address',
handler: function() {
alert('Got it!');
}
}],
styleHtmlContent: true
});
var textField = this.query('*[itemId=textField]')[0];
overlay.showBy(textField, 'c-c?');
I've tried using floating: true and lots of other approaches.
Once I get it to position properly, is there a way to have the button respond to tab order correctly? That is, tab out of the text field, then have the button get focus?
As I understand from your question, you have trouble with setting position to a component. If it is the problem, you can set xy coordinate. Look at this fiddle:
https://fiddle.sencha.com/#fiddle/tpl
viewport.down('#idOverlay').setXY([150, 140]);
Edit:
Ext.define('OverriddenViewport', {
override: 'ExampleViewPort',
initComponent: function() {
// Set up first
this.callParent([]);
this.add(overlay);
this.addListener('afterrender', function(viewport) {
viewport.down('#idOverlay').setXY([220,50]);
viewport.down('#idButton').addListener('blur', function(button) {
viewport.down('#idTextfield').focus();
console.log('textfield is focussed');
});
viewport.down('#idTextfield').addListener('blur', function(button) {
viewport.down('#idButton').focus();
console.log('button is focussed');
});
});
}
});
If you can access the source (just to look around) you maybe can create an override of the corresponding class. Create a override, and copy all of the code of the class (form?) into your override.
Here some additional info about creating overrides in ExtJs:
http://moduscreate.com/writing-ext-js-overrides/
https://sencha.guru/2014/12/04/abstract-vs-override/
In your override create a trigger (on the field you want to expand) with your functionality:
{
fieldLabel: 'My Custom Field',
triggers: {
foo: {
cls: 'my-foo-trigger',
handler: function() {
console.log('foo trigger clicked');
}
}
}
}

How to give JointJS elements a remove tool?

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?
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:
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
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);
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);
}
}));

Putting HTML code on JointJS link

I have worked with JointJS now for a while, managing to create elements with HTML in them.
However, I am stuck with another problem, is it possible to place HTML code, like
href, img etc, on a JointJS link and how do I do this?
For example, if I have this link, how do I modify it to contain HTML:
var link = new joint.dia.Link({
source: { id: sourceId },
target: { id: targetId },
attrs: {
'.connection': { 'stroke-width': 3, stroke: '#000000' }
}
});
Thank you!
JointJS doesn't have a direct support for HTML in links. However, it is possible to do with a little bit of JointJS trickery:
// Update position of our HTML whenever source/target or vertices of our link change:
link.on('change:source change:target change:vertices', function() { updateHTMLPosition(link, $html) });
// Update position of our HTML whenever a position of an element in the graph changes:
graph.on('change:position', function() { updateHTMLPosition(link, $html) });
var $html = $('<ul><li>one</li><li>two</li></ul>');
$html.css({ position: 'absolute' }).appendTo(paper.el);
// Function for updating position of our HTML list.
function updateHTMLPosition(link, $html) {
var linkView = paper.findViewByModel(link);
var connectionEl = linkView.$('.connection')[0];
var connectionLength = connectionEl.getTotalLength();
// Position our HTML to the middle of the link.
var position = connectionEl.getPointAtLength(connectionLength/2);
$html.css({ left: position.x, top: position.y });
}
Bit of an old question, but thought I'd add some more ideas. You can add extra svg markup to the label in a link if you like by extending the link object and then setting attributes where needed. For example:
joint.shapes.custom.Link = joint.dia.Link.extend({
labelMarkup: '<g class="label"><rect /><text /></g>'
});
This code overrides the markup for the label, so you can add extra elements in there. You can also update attributes on these elements by:
link.attr('text/text', "new text");
However hyperlinks won't work (at least I haven't got them working in Chrome) and I believe this is because Jointjs listens for all events in the model. So what you should do is use inbuilt events in Jointjs to listen for connection clicks:
paper.on('cell:pointerclick', function(cellView, evt, x, y){
console.log(cellView);
});

Image zoom for Mootools

I whant to include image zoomer like that http://www.magictoolbox.com/magiczoom/ in my page, but I don't found any similar library for mootools. Here its my page image look like:
I tried this:
http://mootools.net/forge/p/zoomer
But it zooms inner image (not near it) and break all my images design (images is cetered by width and height)
here is my fiddle: http://jsfiddle.net/94PLv/
OPTION 1:
Zooming inside the image div
DEMO HERE
You need something like:
document.getElements('.inner a').addEvent('click', function (e) {
e.stop();
dragit = this.getElement('img').makeDraggable();
this.getElement('img').removeClass('max_limit');
});
I fixed your fiddle a bit, is this what you were looking for? I removed some of your inline CSS and added Mootools More so you can have drag instead of scrollbars.
Added also
document.getElements('.fotoDiv .inner img').addClass('max_limit');
document.getElements('.fotoDiv .inner img').setProperty('style','');
to "put things back" on the next thumb click.
OPTION 2:
using Louper plugin
DEMO HERE
Code added:
//NEW ADDED
document.getElements('.inner a').addEvent('click', function(event) {
event.stop();
});
var loupe = {
src: 'http://img.artlebedev.ru/studio/us/2009/loup.png',
x: 101,
y: 102,
radius: 85
};
document.getElements('a.click').addEvent('click', function(event) {
var id = this.getProperty('rel');
var target_el = document.id('im_' + id).getElement('img');
new Louper(target_el, {
big: target_el.src,
radius: 80,
loupe: loupe,
onReady: function() {
this.loupeWrapper.setStyles({
left: this.smallSize.width - this.loupeSize.width + 60,
top: this.smallSize.height - this.loupeSize.height + 120
});
}
});
});

Categories