/**
* A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements. The button
* automatically cycles through each menu item on click, raising the button's {@link #change} event
* (or calling the button's {@link #changeHandler} function, if supplied) for the active menu item.
* Clicking on the arrow section of the button displays the dropdown menu just like a normal
* SplitButton.
* Example usage:
*
* @example
* Ext.create('Ext.button.Cycle', {
* showText: true,
* prependText: 'View as ',
* renderTo: Ext.getBody(),
* menu: {
* id: 'view-type-menu',
* items: [{
* text: 'text only',
* iconCls: 'view-text',
* checked: true
* },{
* text: 'HTML',
* iconCls: 'view-html'
* }]
* },
* changeHandler: function(cycleBtn, activeItem) {
* Ext.Msg.alert('Change View', activeItem.text);
* }
* });
*/
Ext.define('Ext.button.Cycle', {
extend: 'Ext.button.Split',
alternateClassName: 'Ext.CycleButton',
alias: 'widget.cycle',
/**
* @cfg {Object[]} items
* An array of {@link Ext.menu.CheckItem} **config** objects to be used when creating
* the button's menu items (e.g., `{text:'Foo', iconCls:'foo-icon'}`)
*
* @deprecated 4.0 Use the {@link #cfg-menu} config instead. All menu items will be created as
* {@link Ext.menu.CheckItem CheckItems}.
*/
/**
* @cfg {Boolean} [showText=false]
* True to display the active item's text as the button text. The Button will show its
* configured {@link #text} if this config is omitted.
*/
/**
* @cfg {String} [prependText='']
* A static string to prepend before the active item's text when displayed as the button's text
* (only applies when showText = true).
*/
/**
* @cfg {Function/String} [changeHandler=undefined]
* A callback function that will be invoked each time the active menu item in the button's menu
* has changed. If this callback is not supplied, the SplitButton will instead fire the
* {@link #change} event on active item change. The changeHandler function will be called with
* the following argument list: (SplitButton this, Ext.menu.CheckItem item)
* @controllable
*/
/**
* @cfg {String} forceIcon
* A css class which sets an image to be used as the static icon for this button. This icon will
* always be displayed regardless of which item is selected in the dropdown list. This overrides
* the default behavior of changing the button's icon to match the selected item's icon
* on change.
*/
/**
* @cfg {Number/String} forceGlyph
* The charCode to be used as the static icon for this button. This icon will always be
* displayed regardless of which item is selected in the dropdown list. This override
* the default behavior of changing the button's icon to match the selected item's icon
* on change. This property expects a format consistent with that of {@link #glyph}
*/
/**
* @property {Ext.menu.Menu} menu
* The {@link Ext.menu.Menu Menu} object used to display the
* {@link Ext.menu.CheckItem CheckItems} representing the available choices.
*/
/**
* @event change
* Fires after the button's active menu item has changed. Note that if a {@link #changeHandler}
* function is set on this CycleButton, it will be called instead on active item change and this
* change event will not be fired.
* @param {Ext.button.Cycle} this
* @param {Ext.menu.CheckItem} item The menu item that was selected
*/
/**
* @private
*/
getButtonText: function(item) {
var me = this,
text = '';
if (item && me.showText === true) {
if (me.prependText) {
text += me.prependText;
}
text += item.text;
return text;
}
return me.text;
},
/**
* Sets the button's active menu item.
* @param {Ext.menu.CheckItem} item The item to activate
* @param {Boolean} [suppressEvent=false] True to prevent the {@link #change} event and
* {@link #changeHandler} from firing.
*/
setActiveItem: function(item, suppressEvent) {
var me = this,
changeHandler = me.changeHandler,
forceIcon = me.forceIcon,
forceGlyph = me.forceGlyph;
me.settingActive = true;
if (!Ext.isObject(item)) {
item = me.menu.getComponent(item);
}
if (item) {
me.setText(me.getButtonText(item));
me.setIconCls(forceIcon ? forceIcon : item.iconCls);
me.setGlyph(forceGlyph ? forceGlyph : item.glyph);
me.activeItem = item;
if (!item.checked) {
item.setChecked(true, false);
}
if (!suppressEvent) {
if (changeHandler) {
Ext.callback(changeHandler, me.scope, [me, item], 0, me);
}
me.fireEvent('change', me, item);
}
}
me.settingActive = false;
},
/**
* Gets the currently active menu item.
* @return {Ext.menu.CheckItem} The active item
*/
getActiveItem: function() {
return this.activeItem;
},
/**
* @private
*/
initComponent: function() {
var me = this,
checked = 0,
items, i, iLen, item;
//<debug>
// Ext JS Cycle buttons are implemented in a way that clashes with WAI-ARIA requirements,
// so we warn the developer about that.
Ext.ariaWarn(
me,
"Using Cycle buttons is not recommended in accessible " +
"applications, because their behavior conflicts " +
"with accessibility best practices. See WAI-ARIA 1.0 " +
"Authoring guide: http://www.w3.org/TR/wai-aria-practices/#menubutton"
);
//</debug>
// Allow them to specify a menu config which is a standard Button config.
// Remove direct use of "items" in 5.0.
items = (me.menu.items || []).concat(me.items || []);
me.menu = Ext.applyIf({
cls: Ext.baseCSSPrefix + 'cycle-menu',
items: []
}, me.menu);
iLen = items.length;
// Convert all items to CheckItems
for (i = 0; i < iLen; i++) {
item = items[i];
item = Ext.applyIf({
group: me.id,
itemIndex: i,
checkHandler: me.checkHandler,
scope: me,
checked: item.checked || false
}, item);
me.menu.items.push(item);
if (item.checked) {
checked = i;
}
}
me.itemCount = me.menu.items.length;
me.callParent(arguments);
me.on('click', me.toggleSelected, me);
me.setActiveItem(checked, true);
},
/**
* @private
*/
checkHandler: function(item, pressed) {
if (pressed && !this.settingActive) {
this.setActiveItem(item);
}
},
/**
* This is normally called internally on button click, but can be called externally to advance
* the button's active item programmatically to the next one in the menu. If the current item
* is the last one in the menu the active item will be set to the first item in the menu.
*/
toggleSelected: function() {
var me = this,
m = me.menu,
checkItem;
checkItem = me.activeItem.next(':not([disabled])') || m.items.getAt(0);
checkItem.setChecked(true);
}
});