PDA

View Full Version : Ext.ux.menu.ToggleSubmenuItem + hmenu grid plugin (click to open/close submenu)



vanstyn
9 Jun 2011, 8:41 AM
There might be a better way to solve this, but I wasn't aware of anything and so I wrote a couple of simple modules to to change the behavior of Ext.menu.Item to show the submenu on click instead of hover. I have several places in my application with submenus with a lot of items that can take a few seconds to load, making the hover behavior not ideal. I also had a lot of users complaining about the menu disappearing when they moved the cursor to the menu (if they moved diagonally and didn't keep the pointer within the item border, a behavior that becomes more frustrating when the menu takes a couple of seconds to open each time).

For a while I used the technique described in this thread: http://www.sencha.com/forum/showthread.php?10070-submenu-open-on-click-instead-of-hover&highlight=menu+hover+expand but it wasn't a complete solution, so I wrote this.


/**
* @class Ext.ux.menu.ToggleSubmenuItem
* @extends Ext.menu.Item
*
* Extended menu Item class that expands submenus on click instead of mouse-over
*
* Works like Ext.menu.Item, except the submenu (if defined) is not displayed on mouse-over.
* The item has to be clicked to display the submenu, and then it stays displayed until the item
* is clicked a second time or if the user clicks outside the menu. This is in contrast to the
* normal Item submenu behavior which operates on mouse-over and disapears if you accidently
* move the mouse outside the border of the item and the menu (which is really easy to do when
* you move the cursor from the item to the menu, and is very frustrating to users).
*
* This class also provides a loading icon feature which will convert the item icon into a loading
* spinner icon after the item is clicked until the sub menu is shown. This is useful because it
* can sometimes take several seconds to show the menu when there are are lot of items.
*
* If there is no 'menu' or if a handler is defined, this class behaves exactly the same as
* Ext.menu.Item
*
* Tested with ExtJS 3.3
*
* @xtype menutoggleitem
*
* @author Henry Van Styn
* @date 8, June 2011
*
*/
Ext.ns('Ext.ux.menu');
Ext.ux.menu.ToggleSubmenuItem = Ext.extend(Ext.menu.Item,{

/**
* @cfg {String} loadingIconCls item icon class (iconCls) to set during submenu show
* set this to null to disable the loading icon feature
*/
loadingIconCls: 'icon-loading', // <--

// private
submenuShowPending: false,

initComponent: function() {
if(this.menu && !this.handler) {

this.itemCls = 'x-menu-item x-menu-item-arrow';

this.origMenu = this.menu;
delete this.menu;

if (typeof this.origMenu.getEl != "function") {
this.origMenu = new Ext.menu.Menu(this.origMenu);
}

this.origMenu.on('show',this.onSubmenuShow,this);
this.origMenu.allowOtherMenus = true;

this.handler = function(btn) {
if(this.submenuShowPending) { return; }

if(this.origMenu.isVisible()) {
this.origMenu.hide();
this.setShowPending(false);
}
else {
this.setShowPending(true);
this.origMenu.show.defer(100,this.origMenu,[btn.getEl(),'tr?']);
}
}
}
Ext.ux.menu.ToggleSubmenuItem.superclass.initComponent.call(this);
},

onSubmenuShow: function() {
this.setShowPending(false);
},

setShowPending: function(val) {
if(val) {
this.submenuShowPending = true;
if(this.loadingIconCls) {
this.setIconClass(this.loadingIconCls);
}
}
else {
this.submenuShowPending = false;
if(this.loadingIconCls) {
this.setIconClass(this.initialConfig.iconCls);
}
}
}
});
Ext.reg('menutoggleitem',Ext.ux.menu.ToggleSubmenuItem);


This module also can change the Item icon during the showing of the menu (to give the user feedback that they clicked). I'm using this css for 'icon-loading':


.icon-loading {
background-image: url(/static/ext/resources/images/default/grid/loading.gif) !important;
}

I also wrote this simple plugin for Ext.grid.GridPanel that converts the hmenu "Columns" submenu to use Ext.ux.menu.ToggleSubmenuItem:


/**
* @class Ext.ux.grid.Plugin.HmenuColumnsToggleItem
* @extends Ext.util.Observable
*
* Plugin for Ext.grid.GridPanel that converts the 'Columns' hmenu submenu item
* into a Ext.ux.menu.ToggleSubmenuItem (instead of Ext.menu.Item)
*
* Tested with ExtJS 3.3
*
* @ptype grid-hmenu-columns-toggle
*
* @author Henry Van Styn
* @date 8, June 2011
*
*/
Ext.ns('Ext.ux.grid.Plugin');
Ext.ux.grid.Plugin.HmenuColumnsToggleItem = Ext.extend(Ext.util.Observable,{
init: function(cmp) {
this.cmp = cmp;
cmp.on('afterrender',this.onAfterrender,this);
},

onAfterrender: function() {

var hmenu = this.cmp.view.hmenu;
if (!hmenu) { return; }

var colsItem = hmenu.getComponent('columns');
if (!colsItem) { return; }

colsItem.hide();

hmenu.add(Ext.apply(Ext.apply({},colsItem.initialConfig),{
xtype: 'menutoggleitem',
itemId:'columns-new'
}));
}
});
Ext.preg('grid-hmenu-columns-toggle',Ext.ux.grid.Plugin.HmenuColumnsToggleItem);

You should be able to use this in any GridPanel by setting this in the config:


plugins: [ 'grid-hmenu-columns-toggle' ]