PDA

View Full Version : Nested buttons with menus



mx_starter
26 Apr 2013, 1:52 PM
Hi, i have a button with a menu (with blue dates instead of text below).
The only item within this menu is of xtype: 'panel'.

The panel on his own, contains a fbar and one of its buttons (text 'period') has its own menu. Take a look:

43390

The problem is that clicking on the 'period' button shows his menu, but clicking again on it does not hide it.
Any ideas for this lack of toggle functionality?

suzuki1100nz
26 Apr 2013, 3:04 PM
Can use post a code sample please.
The menu should hide when you click on a menu item or click outside the menu itself.

mx_starter
27 Apr 2013, 12:53 AM
Can use post a code sample please.
The menu should hide when you click on a menu item or click outside the menu itself.


Well, it is quite long but here it is (some of the functions code replaced with ....):



Ext.define('sl.COMPONENTS.DateRangeEn', {

extend: 'Ext.button.Button',
alias: 'widget.slDateRangePickerEn',
id: 'dateRangePicker',
iconCls: 'sl-widget-calendarlaunch-icon',
periodPrefix: '',
text: '',
menu: {plain: true, allowOtherMenus: true, items:
[
{xtype: 'panel', layout: 'hbox',
items:
[
{xtype: 'datepicker', id: 'pickFrom',
listeners: {
select: function(picker, date)
{
this.up('slDateRangePicker').setSecondMinDate();
this.up('panel').describeSelection();

}
}
},
{xtype: 'datepicker', id: 'pickTo',
listeners: {
select: function(picker, date)
{
this.up('panel').describeSelection();
}
}
}
],
buttons: [
{text: 'period', xtype: 'button', menu:
{ allowOtherMenus: true, items:
[
{hideOnClick: false, text: 'This week', handler: function() { this.up('button').up('panel').rangeThisWeek(); } },
{hideOnClick: false, text: 'last week', handler: function() { this.up('button').up('panel').rangeLastWeek(); } },
{hideOnClick: false, text: 'This Month', handler: function() { this.up('button').up('panel').rangeThisMonth(); } },
{hideOnClick: false, text: 'Last Month', handler: function() { this.up('button').up('panel').rangePrevMonth(); } },
{hideOnClick: false, text: 'This year', handler: function() { this.up('button').up('panel').rangeThisYear(); } }
]
}
},

{text: 'Apply', handler: function()
{
this.up('slDateRangePicker').setRange();
this.up('slDateRangePicker').hideMenu();
Ext.getCmp('gridPanel').getStore().loadPage(1);
}

}

],

describeSelection: function()
{
...
},


rangeThisMonth: function()
{
....
},


rangePrevMonth: function()
{
.....
},


rangeThisWeek: function()
{
.....
},



rangeLastWeek: function()
{
.....
},


rangeThisYear: function()
{
....
}


}
]
},

initComponent: function()
{

this.on({ 'afterrender': function()
{
var panel = this.menu.items.items[0];
var pickFrom = panel.down('datepicker[id=pickFrom]');
var pickTo = panel.down('datepicker[id=pickTo]');

if ( this.selectedStart )
{
pickFrom.setValue( selectedStart );
}else{
var dt = new Date();
pickFrom.setValue( Ext.Date.getFirstDateOfMonth(dt) );
}

if ( this.selectedEnd )
{ pickFrom.setValue( selectedEnd ); }


this.setRange();

this.setSecondMinDate();

}

});


this.callParent(arguments);

},

//set main button caption
setRange: function()
{
...

},

//set the min date for the second picker
setSecondMinDate: function()
{
....

}




});

mx_starter
27 Apr 2013, 12:56 AM
Hope it is not too long to understand.
In short - this is a custom button with menu.
The role is to be used as a custom date range picker.
The menu contains a panel and the panel contains the problem button (text: 'period').

The top-level button is part of a gridpanel toolbar

mx_starter
29 Apr 2013, 12:09 AM
Well, let me post a shortened version of the code, may be it will help:

The widget definition:


Ext.define('sl.COMPONENTS.DateRangeEn', {

extend: 'Ext.button.Button',
alias: 'widget.slDateRangePickerEn',
id: 'dateRangePicker',
text: 'top-level button',
menu: {allowOtherMenus: true, items:
[
{
xtype: 'panel',
html: 'The quick brown fox jumps over the lazy dog',

buttons:
[
{text: 'period', xtype: 'button', menu:
{ allowOtherMenus: true, items:
[
{hideOnClick: false, text: 'This week'},
{hideOnClick: false, text: 'last week'}
]
}
},
{text: 'Apply'}


]

}
]
},

initComponent: function()
{
this.callParent(arguments);
}




});



And the instantiation:


Ext.onReady( function()
{
Ext.create('sl.COMPONENTS.DateRangeEn', {

renderTo: Ext.getBody()
});

});


The result is:

43411

So - clicking the 'period' button shows the menu as expected, clicking on 'this/last week' menu items does not hide the menu (again, as expected).
Clicking on 'top-level button' again hides everything as expected.

But clicking on the 'period' button again does not hide the sub-menu - NOT as expected.

Ext is 4.1.3 and i have given up with this...

mx_starter
29 Apr 2013, 12:23 AM
Here it is on JSFiddle:

http://jsfiddle.net/8VXf5/

After all, i'm starting to think if the desired effect using this type of nesting is possible at all...

Any ideas?

Raoyue
29 Apr 2013, 8:37 AM
Sorry, really new here, but if the top-level-button does what you want (hide when you click it again) would it be possible to try and use a menu (like you did for top-level-button) instead of a button to the same effect for period? suzuki1100nz mentioned that menus do that but you're using a button so I'm not sure if that has an effect on what you want to accomplish.

mx_starter
29 Apr 2013, 10:00 AM
Sorry, really new here, but if the top-level-button does what you want (hide when you click it again) would it be possible to try and use a menu (like you did for top-level-button) instead of a button to the same effect for period? suzuki1100nz mentioned that menus do that but you're using a button so I'm not sure if that has an effect on what you want to accomplish.

Not sure if i understood you - can you please try it in the fidde mentioned above?

suzuki1100nz
29 Apr 2013, 1:46 PM
I'm still not sure on the behavior your after.
Do you want the period menu to close when a menu item is selected?
e.g. select "This week" and the period menu closes
BUT the Main menu with the top level-button remains open?

If this is the case the issue lies with a menu inside a menu, default Sencha behavior is when a child menu closes all parent menus close as well.
Option is to make the initial display (top-level-button) component not have a menu but display a floating container with a showBy. Then the child menus (period) will behave and not cause the parent container to close (but you will have to implement the close code for the container).

Otherwise you will need to extend menuitem and override the onClick function which calls deferHideParentMenus function and it calls Ext.menu.Manager.hideAll();
Have a look at the source code

slemmon
29 Apr 2013, 4:03 PM
The problem is that clicking on the 'period' button shows his menu, but clicking again on it does not hide it.


When I test a button with a menu I click the button and see the menu and click the button again the menu hides.

mx_starter
29 Apr 2013, 11:35 PM
I'm still not sure on the behavior your after.
Do you want the period menu to close when a menu item is selected?
e.g. select "This week" and the period menu closes
BUT the Main menu with the top level-button remains open?

If this is the case the issue lies with a menu inside a menu, default Sencha behavior is when a child menu closes all parent menus close as well.
Option is to make the initial display (top-level-button) component not have a menu but display a floating container with a showBy. Then the child menus (period) will behave and not cause the parent container to close (but you will have to implement the close code for the container).

Otherwise you will need to extend menuitem and override the onClick function which calls deferHideParentMenus function and it calls Ext.menu.Manager.hideAll();
Have a look at the source code


Well, the wanted behavior is as follows:
1. The user clicks the top-level button, and sees the entire component
2. The user clicks the 'period' button and sees the period options.
3. Now - i want the sub-menu NOT to be hidden on item click (that is why i use hideOnClick: false for every submenu item). My intent is the user to be able to see how the period changes on every period option (and if needed - to click on the other option without need to re-open the sub-menu).
4. And up to now - everything is working as expected. But here comes the need for closing the sub-menu and, for me, the natural way for doing that is to click on the button again, it's logical, i think...

Of course - the other ways you suggested might help, but the obvious way for closing the button menu is to click the button again and this should work without any additional code.

You mentioned the default Sencha behavior about the menus, i know about it, but 'allowOtherMenus' config option is there for cases like this and it is working. The only problem is with hidding the sub-menu.

By the way, during my testing, i added a handler function listening for the clicks on the 'period' button. The button component has a method called hasVisibleMenu(), which i used to test the state of the sub-menu. On every click this method reports true - even if the menu is not visible.

Still not sure if all this is a bug or simply that's the behavior which must be tweaked somehow...

I just like my component the way it looks on the original screenshot and the need for recreating it only for the purpose of hiding the sub-menu irritates me.

Any other ideas still welcome.:-?

mx_starter
29 Apr 2013, 11:39 PM
When I test a button with a menu I click the button and see the menu and click the button again the menu hides.

Sorry slemmon, i'm afraid you misunderstood me. Did you read my entire question and tested the code snippet i posted?
Or, did you tried it on the fiddle link posted above - http://jsfiddle.net/8VXf5/

mx_starter
7 May 2013, 3:18 AM
Hello again, any other ideas after the Easter :)?

slemmon
7 May 2013, 8:48 AM
I wish I had a more non-complex solution, but at the moment this may have to do (add the listener to your period button):



listeners: {
afterrender: function (btn) {
btn.mon(btn.el, 'mousedown', function () {
if (btn.hasVisibleMenu()) {
btn.menu.hide();
}
});
}
}

mx_starter
8 May 2013, 12:40 AM
I wish I had a more non-complex solution, but at the moment this may have to do (add the listener to your period button):



listeners: {
afterrender: function (btn) {
btn.mon(btn.el, 'mousedown', function () {
if (btn.hasVisibleMenu()) {
btn.menu.hide();
}
});
}
}


=D>
Well - seems that this is working now, slemmon.
Still not sure why this listener is needed at all, but anyway - thanks for your kind support.
Now my picker looks & works exactly the way i want it.
Respect.