/** * @class Ext.button.Button * @extends Ext.Component * Simple Button class * @cfg {String} text The button text to be used as innerHTML (html tags are accepted) * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon') * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event). * The handler is passed the following parameters:* @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width). * See also {@link Ext.panel.Panel}.{@link Ext.panel.Panel#minButtonWidth minButtonWidth}. * @cfg {String/Object} tooltip The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object * @cfg {Boolean} hidden True to start hidden (defaults to false) * @cfg {Boolean} disabled True to start disabled (defaults to false) * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true) * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed) * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false). * @constructor * Create a new button * @param {Object} config The config object * @xtype button */ Ext.define('Ext.button.Button', { /* Begin Definitions */ alias: 'widget.button', extend: 'Ext.Component', requires: [ 'Ext.menu.MenuMgr', 'Ext.util.ClickRepeater', 'Ext.layout.component.Button', 'Ext.util.TextMetrics' ], alternateClassName: 'Ext.Button', /* End Definitions */ isButton: true, componentLayout: 'button', /** * Read-only. True if this button is hidden * @type Boolean */ hidden: false, /** * Read-only. True if this button is disabled * @type Boolean */ disabled: false, /** * Read-only. True if this button is pressed (only if enableToggle = true) * @type Boolean */ pressed: false, /** * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined) */ /** * @cfg {Boolean} allowDepress * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true. */ /** * @cfg {Boolean} enableToggle * True to enable pressed/not pressed toggling (defaults to false) */ enableToggle: false, /** * @cfg {Function} toggleHandler * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:*
- *
b
: ButtonThis Button.- *
e
: EventObjectThe click event.
{@link Ext.toolbar.Toolbar.Item#overflowText overflowText}
.
*/
/**
* @cfg {String} iconCls
* A css class which sets a background image to be used as the icon for this button
*/
/**
* @cfg {String} type
* submit, reset or button - defaults to 'button'
*/
type: 'button',
/**
* @cfg {String} clickEvent
* The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).
* Defaults to 'click'.
*/
clickEvent: 'click',
/**
* @cfg {Boolean} handleMouseEvents
* False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
*/
handleMouseEvents: true,
/**
* @cfg {String} tooltipType
* The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
*/
tooltipType: 'qtip',
/**
* @cfg {String} baseCls
* The base CSS class to add to all buttons. (Defaults to 'x-btn')
*/
baseCls: Ext.baseCSSPrefix + 'btn',
/**
* @cfg {String} pressedCls
* The CSS class to add to a button when it is in the pressed state. (Defaults to 'x-btn-default-small-pressed')
*/
/**
* @cfg {String} overCls
* The CSS class to add to a button when it is in the over (hovered) state. (Defaults to 'x-btn-default-small-over')
*/
/**
* @cfg {String} focusCls
* The CSS class to add to a button when it is in the focussed state. (Defaults to 'x-btn-default-small-focus')
*/
ariaRole: 'button',
// inherited
renderTpl:
'' +
'' +
'',
/**
* @cfg {String} scale
* (Optional) The size of the Button. Three values are allowed:
*Defaults to 'small'.
*/ scale: 'small', /** * @cfg {String} ui *(Optional) The UI specified for the button.
*Defaults to 'default'.
*/ ui: 'default', /** * @cfg {Object} scope The scope (this reference) in which the *{@link #handler}
and {@link #toggleHandler}
is
* executed. Defaults to this Button.
*/
/**
* @cfg {String} iconAlign
* (Optional) The side of the Button box to render the icon. Four values are allowed:
*Defaults to 'left'.
*/ iconAlign: 'left', /** * @cfg {String} arrowAlign *(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}. * Two values are allowed:
*Defaults to 'right'.
*/ arrowAlign: 'right', /** * @cfg arrowCls {String} arrowCls *(Optional) The className used for the inner arrow element if the button has a menu.
*/ arrowCls: 'arrow', /** * @cfg {Ext.Template} template (Optional) *A {@link Ext.Template Template} used to create the Button's DOM structure.
* Instances, or subclasses which need a different DOM structure may provide a different * template layout in conjunction with an implementation of {@link #getTemplateArgs}. * @type Ext.Template * @property template */ /** * @cfg {String} cls * A CSS class string to apply to the button's main element. */ /** * @property menu * @type Menu * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option. */ /** * @cfg {Boolean} autoWidth * By default, if a width is not specified the button will attempt to stretch horizontally to fit its content. * If the button is being managed by a width sizing layout (hbox, fit, anchor), set this to false to prevent * the button from doing this automatic sizing. * Defaults to undefined. */ initComponent: function() { var me = this; me.callParent(arguments); me.addEvents( /** * @event click * Fires when this button is clicked * @param {Button} this * @param {EventObject} e The click event */ 'click', /** * @event toggle * Fires when the 'pressed' state of this button changes (only if enableToggle = true) * @param {Button} this * @param {Boolean} pressed */ 'toggle', /** * @event mouseover * Fires when the mouse hovers over the button * @param {Button} this * @param {Event} e The event object */ 'mouseover', /** * @event mouseout * Fires when the mouse exits the button * @param {Button} this * @param {Event} e The event object */ 'mouseout', /** * @event menushow * If this button has a menu, this event fires when it is shown * @param {Button} this * @param {Menu} menu */ 'menushow', /** * @event menuhide * If this button has a menu, this event fires when it is hidden * @param {Button} this * @param {Menu} menu */ 'menuhide', /** * @event menutriggerover * If this button has a menu, this event fires when the mouse enters the menu triggering element * @param {Button} this * @param {Menu} menu * @param {EventObject} e */ 'menutriggerover', /** * @event menutriggerout * If this button has a menu, this event fires when the mouse leaves the menu triggering element * @param {Button} this * @param {Menu} menu * @param {EventObject} e */ 'menutriggerout' ); if (me.menu) { // Flag that we'll have a splitCls me.split = true; // retrieve menu by id or instantiate instance if needed me.menu = Ext.menu.MenuMgr.get(me.menu); } if (Ext.isString(me.toggleGroup)) { me.enableToggle = true; } // // me.baseCls += ('-' + me.ui + '-' + me.scale); }, initAria: function() { Ext.button.Button.superclass.initAria.call(this); var actionEl = this.getActionEl(); if (this.menu) { actionEl.dom.setAttribute('aria-haspopup', true); } }, getActionEl: function() { return this.btnEl; }, getFocusEl: function() { return this.btnEl; }, // private setButtonCls: function() { var me = this, el = me.el; if (me.useSetClass) { if (!Ext.isEmpty(me.oldCls)) { el.removeCls([me.oldCls, me.baseCls + '-pressed', me.pressedCls]); } me.oldCls = (me.iconCls || me.icon) ? (me.text ? me.baseCls + '-text-icon ' + me.baseCls + '-text-icon-' + me.iconAlign : me.baseCls + '-icon') : ''; el.addCls([me.oldCls, me.pressed ? (me.baseCls + '-pressed ' + me.pressedCls) : null]); } }, // private onRender: function(ct, position) { // classNames for the button var me = this, repeater, btn, btnIconCls; me.cls = (me.cls || '') + ' ' + me.baseCls + '-' + me.ui; if (me.scale) { me.ui += '-' + me.scale; me.addCls(me.baseCls + '-' + me.scale); } me.disabledCls = me.disabledCls + ' ' + me.baseCls + '-' + me.ui + '-disabled'; me.overCls = me.baseCls + '-' + me.ui + '-over'; me.pressedCls = me.baseCls + '-' + me.ui + '-pressed'; me.focusCls = me.baseCls + '-' + me.ui + '-focus'; // only add the icon class if the button has an icon if (me.iconCls || me.icon) { if (me.text) { btnIconCls = me.baseCls + '-' + me.ui + '-icon-text-' + me.iconAlign; } else { btnIconCls = me.baseCls + '-' + me.ui + '-icon'; } } else if (me.text) { me.addCls(me.baseCls + '-' + me.ui + '-noicon'); } Ext.applyIf(me.renderData, me.getTemplateArgs()); // Extract the button, and if we are split, the arrow element Ext.applyIf(me.renderSelectors, { btnEl: 'button' }); if (me.split) { me.renderSelectors.arrowEl = '.' + me.renderData.splitCls; } me.callParent(arguments); if (me.split && me.arrowTooltip) { me.arrowEl.dom[me.tooltipType] = me.arrowTooltip; } me.mon(me.btnEl, { scope: me, focus: me.onFocus, blur : me.onBlur }); btn = me.el; me.addCls(btnIconCls); if (me.icon) { me.setIcon(me.icon); } if (me.iconCls) { me.setIconClass(me.iconCls); } if (me.tooltip) { me.setTooltip(me.tooltip, true); } if (me.handleMouseEvents) { me.mon(btn, { scope: me, mouseover: me.onMouseOver, mousedown: me.onMouseDown }); // new functionality for monitoring on the document level //this.mon(btn, 'mouseout', this.onMouseOut, this); } if (me.menu) { me.mon(me.menu, { scope: me, show: me.onMenuShow, hide: me.onMenuHide }); } if (me.repeat) { repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(me.repeat) ? me.repeat: {}); me.mon(repeater, 'click', me.onRepeatClick, me); } else { me.mon(btn, me.clickEvent, me.onClick, me); } Ext.ButtonToggleMgr.register(me); }, /** *This method returns an object which provides substitution parameters for the {@link #renderTpl XTemplate} used * to create this Button's DOM structure.
*Instances or subclasses which use a different Template to create a different DOM structure may need to provide their * own implementation of this method.
*The default implementation which provides data for the default {@link #template} returns an Object containing the * following properties:
type
: The <button>'s {@link #type}splitCls
: A CSS class to determine the presence and position of an arrow icon. ('x-btn-arrow'
or 'x-btn-arrow-bottom'
or ''
)cls
: A CSS class name applied to the Button's main <tbody> element which determines the button's scale and icon alignment.text
: The {@link #text} to display ion the Button.tabIndex
: The tab index within the input flow.this
reference) in which the handler function is executed.
* Defaults to this Button.
* @return {Ext.button.Button} this
*/
setHandler: function(handler, scope) {
this.handler = handler;
this.scope = scope;
return this;
},
/**
* Sets this Button's text
* @param {String} text The button text
* @return {Ext.button.Button} this
*/
setText: function(text) {
var me = this;
me.text = text;
if (me.el) {
me.btnEl.update(text || ' ');
me.setButtonCls();
}
me.doComponentLayout();
return me;
},
/**
* Sets the background image (inline style) of the button. This method also changes
* the value of the {@link icon} config internally.
* @param {String} icon The path to an image to display in the button
* @return {Ext.button.Button} this
*/
setIcon: function(icon) {
var me = this;
me.icon = icon;
if (me.el) {
me.btnEl.setStyle('background-image', icon ? 'url(' + icon + ')': '');
me.setButtonCls();
}
return me;
},
/**
* Gets the text for this Button
* @return {String} The button text
*/
getText: function() {
return this.text;
},
/**
* If a state it passed, it becomes the pressed state otherwise the current state is toggled.
* @param {Boolean} state (optional) Force a particular state
* @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
* @return {Ext.button.Button} this
*/
toggle: function(state, suppressEvent) {
var me = this;
state = state === undefined ? !me.pressed: !!state;
if (state !== me.pressed) {
if (me.rendered) {
me.el[state ? 'addCls': 'removeCls']([me.baseCls + '-pressed', me.pressedCls]);
}
me.btnEl.dom.setAttribute('aria-pressed', state);
me.pressed = state;
if (!suppressEvent) {
me.fireEvent('toggle', me, state);
Ext.callback(me.toggleHandler, me.scope || me, [me, state]);
}
}
return me;
},
// private
onDisable: function() {
this.onDisableChange(true);
},
// private
onEnable: function() {
this.onDisableChange(false);
},
onDisableChange: function(disabled) {
var me = this;
if (me.el) {
if (!Ext.isIE6 || !me.text) {
me.el[disabled ? 'addCls': 'removeCls'](me.disabledClass);
}
me.el.dom.disabled = disabled;
}
me.disabled = disabled;
},
/**
* Show this button's menu (if it has one)
*/
showMenu: function() {
var me = this;
if (me.rendered && me.menu) {
if (me.tooltip) {
Ext.tip.QuickTips.getQuickTip().cancelShow(me.btnEl);
}
if (me.menu.isVisible()) {
me.menu.hide();
}
// Allow the menu to find a z-index parent on render by examining its ownerCt chain
if (!me.menu.ownerCt) {
me.menu.ownerCt = me.ownerCt;
}
me.menu.showBy(me.el, me.menuAlign);
}
return me;
},
/**
* Hide this button's menu (if it has one)
*/
hideMenu: function() {
if (this.hasVisibleMenu()) {
this.menu.hide();
}
return this;
},
/**
* Returns true if the button has a menu and it is visible
* @return {Boolean}
*/
hasVisibleMenu: function() {
var menu = this.menu;
return menu && menu.rendered && menu.isVisible();
},
// private
onRepeatClick: function(repeat, e) {
this.onClick(e);
},
// private
onClick: function(e) {
var me = this;
if (e) {
e.preventDefault();
}
if (e.button !== 0) {
return;
}
if (!me.disabled) {
if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) {
me.toggle();
}
if (me.menu && !me.hasVisibleMenu() && !me.ignoreNextClick) {
me.showMenu();
}
me.fireEvent('click', me, e);
if (me.handler) {
//this.el.removeCls(me.baseCls + '-over');
me.handler.call(me.scope || me, me, e);
}
}
},
// private
isMenuTriggerOver: function(e, internal) {
return this.menu && !internal;
},
// private
isMenuTriggerOut: function(e, internal) {
return this.menu && !internal;
},
// private
onMouseOver: function(e) {
var me = this,
internal;
if (!me.disabled) {
internal = e.within(me.el, true);
if (!internal) {
me.el.addCls([me.baseCls + '-over', me.overCls]);
if (!me.monitoringMouseOver) {
me.doc.on('mouseover', me.monitorMouseOver, me);
me.monitoringMouseOver = true;
}
me.fireEvent('mouseover', me, e);
}
if (me.isMenuTriggerOver(e, internal)) {
me.fireEvent('menutriggerover', me, me.menu, e);
}
}
},
// private
monitorMouseOver: function(e) {
var me = this;
if (e.target !== me.el.dom && !e.within(me.el)) {
if (me.monitoringMouseOver) {
me.doc.un('mouseover', me.monitorMouseOver, me);
me.monitoringMouseOver = false;
}
me.onMouseOut(e);
}
},
// private
onMouseOut: function(e) {
var me = this,
internal = e.within(me.el) && e.target !== me.el.dom;
me.el.removeCls([me.baseCls + '-over', me.overCls]);
// if (!Ext.supports.CSS3BorderRadius) {
// me.tableEl.removeCls(me.baseCls + '-over');
// me.tbodyEl.removeCls(me.baseCls + '-over');
// }
me.fireEvent('mouseout', me, e);
if (me.isMenuTriggerOut(e, internal)) {
me.fireEvent('menutriggerout', me, me.menu, e);
}
},
focus: function() {
this.btnEl.focus();
},
blur: function() {
this.btnEl.blur();
},
// private
onFocus: function(e) {
var me = this;
if (!me.disabled) {
me.el.addCls([me.baseCls + '-focus', me.focusCls]);
}
},
// private
onBlur: function(e) {
var me = this;
me.el.removeCls([me.baseCls + '-focus', me.focusCls]);
},
// private
onMouseDown: function(e) {
var me = this;
if (!me.disabled && e.button === 0) {
me.el.addCls([me.baseCls + '-pressed', me.pressedCls]);
me.doc.on('mouseup', me.onMouseUp, me);
}
},
// private
onMouseUp: function(e) {
var me = this;
if (e.button === 0) {
me.el.removeCls([me.baseCls + '-pressed', me.pressedCls]);
me.doc.un('mouseup', me.onMouseUp, me);
}
},
// private
onMenuShow: function(e) {
var me = this;
me.ignoreNextClick = 0;
me.el.addCls(me.baseCls + '-menu-active');
me.fireEvent('menushow', me, me.menu);
},
// private
onMenuHide: function(e) {
var me = this;
me.el.removeCls(me.baseCls + '-menu-active');
me.ignoreNextClick = Ext.defer(me.restoreClick, 250, me);
me.fireEvent('menuhide', me, me.menu);
},
// private
restoreClick: function() {
this.ignoreNextClick = 0;
}
}, function() {
// Private utility class used by Button
Ext.ButtonToggleMgr = (function() {
var groups = {},
g, i, l;
function toggleGroup(btn, state) {
if (state) {
g = groups[btn.toggleGroup];
for (i = 0, l = g.length; i < l; i++) {
if (g[i] !== btn) {
g[i].toggle(false);
}
}
}
}
return {
register: function(btn) {
if (!btn.toggleGroup) {
return;
}
var g = groups[btn.toggleGroup];
if (!g) {
g = groups[btn.toggleGroup] = [];
}
g.push(btn);
btn.on('toggle', toggleGroup);
},
unregister: function(btn) {
if (!btn.toggleGroup) {
return;
}
var g = groups[btn.toggleGroup];
if (g) {
g.remove(btn);
btn.un('toggle', toggleGroup);
}
},
/**
* Gets the pressed button in the passed group or null
* @param {String} group
* @return Button
*/
getPressed: function(group) {
var g = groups[group],
i = 0,
len;
if (g) {
for (len = g.length; i < len; i++) {
if (g[i].pressed === true) {
return g[i];
}
}
}
return null;
}
};
}());
});