ExtJS: keyboard focus specific context menu item - extjs

Currently I'm opening a context menu following the strike of a keyboard shortcut.
How do I focus on (and select/highlight) a particular menu item of the context menu? So that then the item's handler can be executed by hitting the return key. I'm running ExtJS 4.1.
This what I'm currently doing:
myMenu.showBy(divElement); // divElement is a DOM object
myMenu.items.items[2].focus(); // focus on 3rd menu item
myMenu.doConstrain(); // move floating component into a constrain region
Still, focus is maintained on the menu element itself.

A look into the source reveals that this is done using the setActiveItem() method:
var the_menu = Ext.create('Ext.menu.Menu', {
items: [
{
itemId: 'foo',
text: 'Foo'
},
{
itemId: 'bar',
text: 'Bar'
}
]
}).showBy(document.getElementById('some_div'));
the_menu.setActiveItem(the_menu.down('#bar'));
Note that this method is private, however canActivateItem(item) and deactivateActiveItem(andBlurFocusedItem) are public, so it's probably just an oversight.

Related

Duplicate reference when maximizing/restoring dialog

When maximizing/restoring a dialog that contains some form fields with names, like:
Ext.create('Ext.Dialog', {
maximizable: true,
items: {
xtype: 'textfield',
name: 'id',
bind: '{record.id}'
},
buttons: [{
text: 'Save',
bind: {
disabled: '{!record.valid}'
}
}]
}).show();
we're getting an error:
Ext.mixin.Container.attachNameRef(): Duplicate name: "id" on ext-viewport between ext-textfield-1 and ext-textfield-5
Two found workarounds :
Disable animation
Ext.define('Override.Dialog', {
override: 'Ext.Dialog',
config: {
maximizeAnimation: false,
restoreAnimation: false
}
});
Make the proxy used for animation have no items (nor buttons since button disable state may not reflect the bounded value
Ext.define('Override.Dialog', {
override: 'Ext.Dialog',
config: {
maximizeProxy: {
items: null,
buttons: null
}
}
});
Background Information
During maximize and minimize ExtJS creates a shadow clone.
This will create a clone of the window, while you still have the original item.
Using an ID means, there can only be one identical one at any given time.
The clone tries to create the your textfield with the same ID, which does not work.
Typically you want to
for forms you usually do not need to grab each item as you can work with validator and getValues on the form
otherwise you might want to work with references in the view and lookupReference in the controller.
not use animation (because that does not create a clone)
write your own maximize animation and do the animation part yourself (write your own maximize function)

Need to set class/id values on buttons in Extjs MessageBox

Our testing team require IDs or class values to be set on the HTML elements in our message popup boxes. This is for their automated tests.
I can pass in a class value for the dialog panel by passing in a cls value like so:
Ext.Msg.show({
title:'Reset Grid Layout',
msg: 'Are you sure that you want to reset the grid layout?',
cls:'Reset-Grid-Layout-Message',
buttons: Ext.Msg.YESNO,
fn: function (response) {
if (response == 'yes') {
}
},
icon: Ext.window.MessageBox.QUESTION
});
Now we also need it on the buttons, and also on the text being displayed. Is there some way of getting a cls value onto the buttons?
I was thinking it may be possible to expand the button parameter into something like :
buttons : [{name:'but1', cls:'asdf'}, {name:'but2', cls:'asdf2'}]
But google is not giving me back anything useful.
If your testing team uses Selenium for their automated test, adding ids/classes in every component could be difficult for both of you.
Overriding components in Ext is a good solution, but I don't recommend this because it will affect all your components. Unless you know what you're doing.
I suggest, extend Ext.window.MessageBox and generate classes for your buttons based on your parent cls.
// Put this somewhere like /custom/messagebox.js
Ext.define('App.MyMessageBox', {
extend: 'Ext.window.MessageBox'
,initConfig: function(config) {
this.callParent(arguments);
}
,makeButton: function(btnIdx) {
var me = this;
var btnId = me.buttonIds[btnIdx];
return new Ext.button.Button({
handler: me.btnCallback
,cls: me.cls + '-' + btnId
,itemId: btnId
,scope: me
,text: me.buttonText[btnId]
,minWidth: 75
});
}
});
To use:
App.Box = new App.MyMessageBox({
cls:'reset-grid-layout'
}).show({
title:'Reset Grid Layout'
,msg: 'Are you sure that you want to reset the grid layout?'
,buttons: Ext.Msg.YESNO
,icon: Ext.window.MessageBox.QUESTION
});
Your buttons will have reset-grid-layout-yes and reset-grid-layout-no class.
You can do the same with other components you have. Check out the Fiddle. https://fiddle.sencha.com/#fiddle/7qb
You should refer to the API
cls : String A CSS class string to apply to the button's main element.
Overrides: Ext.AbstractComponent.cls
You can also use the filter on right side (not the one in the right top corner). Just type cls and you will see all properties, methods and events containing cls (note that you see by default just public members, use the menu on the right of this searchfield to extend this)
Edit
If you just need it for testing purpose I would recommend to override the responsible method. This should work (untested!)
Ext.window.MessageBox.override({
buttonClasses: [
'okCls', 'yesCls', 'noCls', 'cancelCls'
],
makeButton: function(btnIdx) {
var btnId = this.buttonIds[btnIdx];
var btnCls = this.buttonClasses[btnIdx];
return new Ext.button.Button({
handler: this.btnCallback,
cls: btnCls,
itemId: btnId,
scope: this,
text: this.buttonText[btnId],
minWidth: 75
});
}
});

ExtJS 4 - how to hide context menu when left clicking the Ext.panel.Panel?

I have context menu on a panel (geoext 2 map panel)
This is how I init it :
var ctxMenu;
Ext.get("mapPanel-body").on("contextmenu", function (event, element) {
event.stopEvent();
if (!ctxMenu) {
ctxMenu = Ext.create('Ext.menu.Menu', {
width:100,
height:100,
margin: '0 0 10 0',
items: [{ text: 'test', action: 'test'}]
});
}
ctxMenu.showAt(event.getXY());
return false;
});
What happens is that right click on the map opens the context menu ... but it stays open till I choose an item from the menu (left click outside it doesnt close it)
I'm using ExtJS 4.2.1
Why it behaves like this ?
May be the reason is , panel doesnot contain a default contextMenu event.
But you are defining a contextMenu by using the on on the panel.
For this issue you can define a click event for the panel by using the same on config and check whether the object contextMenu is present or not.
If it is present , then hide the contextMenu by using contextMenuObject.hide().

Saving state of hbox widths after splitter move

I'm trying to save the visual state of a panel. The Panel consists of two child containers, with hbox/flex layout and a splitter between them.
{
xtype:'panel',
layout: {
align: 'stretch',
type: 'hbox'
},
items:[
{
xtype:'container',
title: 'left panel',
flex:1
},
{
xtype:'splitter'
},
{
xtype:'container',
title: 'left panel',
flex:2
}
]
}
I already have a working state manager. The "left panel" contains a grid, and the grid is storing it's column states just fine. The state manager fires from a controllers init function by:
var stateProvider=Ext.create('Ext.state.LocalStorageProvider');
Ext.state.Manager.setProvider(stateProvider);
The grid uses the framework standard approach for storing it's state by setting stateId: 'GridState' and stateful:true.
However I'm unable to figure out how ExtJS want's me to do the same with the flex values of the main layout. I've tried setting stateful and stateId on the splitter. I've tried without events, and with stateEvents: ['move']. I've also tried setting stateful on the leftpanel and with stateEvents to resize. Finally I've tried setting stateful on the parent panel, with and without stateEvents: afterlayout.
I do know that it's possible to fetch the event after the splitter is moved. Then I could store the flex values as custom states and manually look for them somewhere in the layout/render process, however I guess there must be a more standard approach for a problem that seems so trivial.
What is the standard framework approach for storing the "splitter position" / "flex values" of a hbox/vbox layout?
Yes there is a standard way to add states. And there is no default state for your splitter so you'll have to add these functions:
Example state:
getState: function () {
var me = this;
var state = {};
state.yourCustomState = 'state'; //you can save what you want
return state;
},
applyState: function (state) {
var me = this;
if (state && state.yourCustomState) {
//Do stuff
}
}
And you need your state to be triggered, you can use stateEvents: http://docs.sencha.com/extjs/4.1.3/#!/api/Ext.resizer.Splitter-method-addStateEvents
You can add the resize event to your stateEvents.

Context menu within a menu item in ExtJS

I have a Menu that contains a TreePanel. The users need to be able to interact with a the TreePanel's nodes using a context menu. I'm showing the context menu from a function attached to the TreePanel's contextmenu event.
This works except:
Without allowOtherMenus: true, showing the context menu causes the main menu, and therefore the TreePanel, to disappear ;
With allowOtherMenus: true on either menu, the context menu doesn't disappear when the users clicks a blank area of the TreePanel.
I'm looking for a way to have the context menu to work as if the TreePanel were not an item within a menu.
Mockup :
I found this that seems to work on FF3/IE8/Chrome, although it could have side effects that have not shown up yet.
var hide_context_menu = function () { context_menu.hide() };
var context_menu = new Ext.menu.Menu({
allowOtherMenus: true,
items: [...],
listeners: {
show: function () {
Ext.getDoc().on('mouseup', hide_context_menu);
},
hide: function () {
Ext.getDoc().un('mouseup', hide_context_menu);
}
}
});
allowOtherMenus: true prevents the hiding of the main menu by the MenuMgr when the context menu pops up. Hiding the handler to the mouseup event allows for click events to be processed.

Resources