I am working with ExtJS 4 framework and also using Raphael.js
in my main application panel.
First I was trying to prevent the regular context menu from showing, and I found out very easy solution.
raphael_element.node.oncontextmenu = function()
{
return false;
}
This is working great, but before returning false I want to add some code to show my own custom menu.
Does anyone know how to do that?
I have something in html, that was thinking to use in the menu:
<ul id="rect_menu" class="contextMenu">
<li><a href="call_to_js_function">Set aaa</a></li>
<li><a href="call_to_js_function">Set xxx</a></li>
<li><a href="call_to_js_function">Set ccc</a></li>
</ul>
As you can see, I have css class
(code is not here, it irrelevant) for styling and id=rect_menu
to show up when user right clicks on the i.e. Raphael rectangle object.
Can anyone show a way, maybe small demo that does not involve jquery? Thnaks
You can use Ext to display your custom HTML in a floating component:
var menu = Ext.create('Ext.Component', {
floating: true
,html: ['<ul id="rect_menu" class="contextMenu">',
'<li><a href="call_to_js_function">Set aaa</a></li>',
'<li><a href="call_to_js_function">Set xxx</a></li>',
'<li><a href="call_to_js_function">Set ccc</a></li>',
'</ul>'].join('')
});
Ext.get(raphael_element.node).on({
contextmenu: function(e) {
menu.showAt(e.getXY());
e.preventDefault();
}
});
However, showing the menu is not the tricky part. The logic to have your menu hidden, either when the user click outside of it, or when the ESC key is pressed is a lot more involved. That's why I would use a regular Menu
to let Ext do the heavy lifting for us.
var menu = Ext.create('Ext.menu.Menu', {
items: [{
text: 'Set aaa'
,handler: function() {
// logic for aaa
}
}, {
text: 'Set bbb'
,handler: function() {
// logic for bbb
}
}]
});
Ext.get(raphael_element.node).on({
contextmenu: function(e) {
menu.showAt(e.getXY());
e.preventDefault();
}
});
Now, if you're not interested in using all the menu system, with Ext menu items, etc., and you really want to render your own HTML, you can still leverage Ext menu to benefit from its hiding mechanics:
var menu = Ext.create('Ext.menu.Menu', {
// This will prevent the icon gutter from showing, and your component from
// being left padded
plain: true
,items: [{
xtype: 'component'
,html: ['<ul id="rect_menu" class="contextMenu">',
'<li><a href="call_to_js_function">Set aaa</a></li>',
'<li><a href="call_to_js_function">Set xxx</a></li>',
'<li><a href="call_to_js_function">Set ccc</a></li>',
'</ul>'].join('')
}]
});
Ext.get(raphael_element.node).on({
contextmenu: function(e) {
menu.showAt(e.getXY());
e.preventDefault();
}
});
Here's a fiddle using that last example. It doesn't use Raphael, but from the moment you get a DOM element that won't bear any importance.
Edit Example with actual Raphael elements
I've updated my fiddle with actual Raphael elements. It does work exactly as intended.
What is cool is that the click event of the element totally respects the vectorial path bounds, meaning you can precisely decide where your menu will pop or not. What is less cool is that with, for example, the famous tiger, that would mean 240 event handlers... Kinda ouch performance-wise.... You can always add the handler to the SVG element instead of individual shape elements, but the menu will be bound to the whole drawing rectangle instead of individual parts of course.
// --- Creating a ball (see http://raphaeljs.com/ball.html)
Raphael.fn.ball = function (x, y, r, hue) {
hue = hue || 0;
return this.set(
this.ellipse(x, y + r - r / 5, r, r / 2).attr({fill: "rhsb(" + hue + ", 1, .25)-hsb(" + hue + ", 1, .25)", stroke: "none", opacity: 0}),
this.ellipse(x, y, r, r).attr({fill: "r(.5,.9)hsb(" + hue + ", 1, .75)-hsb(" + hue + ", .5, .25)", stroke: "none"}),
this.ellipse(x, y, r - r / 5, r - r / 20).attr({stroke: "none", fill: "r(.5,.1)#ccc-#ccc", opacity: 0})
);
};
var R = Raphael("holder"), x = 310, y = 180, r = 150;
var ball = R.ball(x, y, r, Math.random());
// --- Creatin a custom menu
var menu = Ext.create('Ext.menu.Menu', {
plain: true
,cls: 'myContextMenu'
,items: [{
xtype: 'component'
,html: ['<ul id="rect_menu" class="contextMenu">',
'<li><a href="call_to_js_function">Set aaa</a></li>',
'<li><a href="call_to_js_function">Set xxx</a></li>',
'<li><a href="call_to_js_function">Set ccc</a></li>',
'</ul>'].join('')
}]
});
// --- Adding menu handler to all ball's elements
var contextMenuHandler = function(e) {
menu.showAt(e.getXY());
e.preventDefault();
};
Ext.each(ball, function(raphaelElement) {
Ext.fly(raphaelElement.node).on({
contextmenu: contextMenuHandler
});
});