/** * A simple class to display a button. * * There are various different styles of Button you can create by using the {@link #icon}, * {@link #iconCls}, {@link #iconAlign}, {@link #ui}, and {@link #text} * configurations. * * ## Simple Button * * Here is a Button in it's simplest form: * * @example miniphone * var button = Ext.create('Ext.Button', { * text: 'Button' * }); * Ext.Viewport.add({ xtype: 'container', padding: 10, items: [button] }); * * ## Icons * * You can also create a Button with just an icon using the {@link #iconCls} configuration: * * @example miniphone * var button = Ext.create('Ext.Button', { * iconCls: 'refresh' * }); * Ext.Viewport.add({ xtype: 'container', padding: 10, items: [button] }); * * Sencha provides the "Font" and "PNG" icons packs from http://wwww.pictos.cc. * Use icons with the {@link Global_CSS#icon icon} mixin in your Sass. * * ## Badges * * Buttons can also have a badge on them, by using the {@link #badgeText} configuration: * * @example * Ext.create('Ext.Container', { * fullscreen: true, * padding: 10, * items: { * xtype: 'button', * text: 'My Button', * badgeText: '2' * } * }); * * ## UI * * Buttons also come with a range of different default UIs. Here are the included UIs * available (if {@link #$include-button-uis $include-button-uis} is set to `true`): * * - **normal** - a basic gray button * - **back** - a back button * - **forward** - a forward button * - **round** - a round button * - **action** - shaded using the {@link Global_CSS#$active-color $active-color} (dark blue by default) * - **decline** - shaded using the {@link Global_CSS#$alert-color $alert-color} (red by default) * - **confirm** - shaded using the {@link Global_CSS#$confirm-color $confirm-color} (green by default) * * You can also append `-round` to each of the last three UI's to give it a round shape: * * - **action-round** * - **decline-round** * - **confirm-round** * * And setting them is very simple: * * var uiButton = Ext.create('Ext.Button', { * text: 'My Button', * ui: 'action' * }); * * And how they look: * * @example miniphone preview * Ext.create('Ext.Container', { * fullscreen: true, * padding: 4, * defaults: { * xtype: 'button', * margin: 5 * }, * layout: { * type: 'vbox', * align: 'center' * }, * items: [ * { ui: 'normal', text: 'normal' }, * { ui: 'round', text: 'round' }, * { ui: 'action', text: 'action' }, * { ui: 'decline', text: 'decline' }, * { ui: 'confirm', text: 'confirm' } * ] * }); * * Note that the default {@link #ui} is **normal**. * * You can also use the {@link #sencha-button-ui sencha-button-ui} CSS Mixin to create your own UIs. * * ## Example * * This example shows a bunch of icons on the screen in two toolbars. When you click on the center * button, it switches the {@link #iconCls} on every button on the page. * * @example preview * Ext.createWidget('container', { * fullscreen: true, * layout: { * type: 'vbox', * pack:'center', * align: 'center' * }, * items: [ * { * xtype: 'button', * text: 'Change iconCls', * handler: function() { * // classes for all the icons to loop through. * var availableIconCls = [ * 'action', 'add', 'arrow_down', 'arrow_left', * 'arrow_right', 'arrow_up', 'compose', 'delete', * 'organize', 'refresh', 'reply', 'search', * 'settings', 'star', 'trash', 'maps', 'locate', * 'home' * ]; * // get the text of this button, * // so we know which button we don't want to change * var text = this.getText(); * * // use ComponentQuery to find all buttons on the page * // and loop through all of them * Ext.Array.forEach(Ext.ComponentQuery.query('button'), function(button) { * // if the button is the change iconCls button, continue * if (button.getText() === text) { * return; * } * * // get the index of the new available iconCls * var index = availableIconCls.indexOf(button.getIconCls()) + 1; * * // update the iconCls of the button with the next iconCls, if one exists. * // if not, use the first one * button.setIconCls(availableIconCls[(index === availableIconCls.length) ? 0 : index]); * }); * } * }, * { * xtype: 'toolbar', * docked: 'top', * items: [ * { xtype: 'spacer' }, * { iconCls: 'action' }, * { iconCls: 'add' }, * { iconCls: 'arrow_down' }, * { iconCls: 'arrow_left' }, * { iconCls: 'arrow_up' }, * { iconCls: 'compose' }, * { iconCls: 'delete' }, * { iconCls: 'organize' }, * { iconCls: 'refresh' }, * { xtype: 'spacer' } * ] * }, * { * xtype: 'toolbar', * docked: 'bottom', * ui: 'light', * items: [ * { xtype: 'spacer' }, * { iconCls: 'reply' }, * { iconCls: 'search' }, * { iconCls: 'settings' }, * { iconCls: 'star' }, * { iconCls: 'trash' }, * { iconCls: 'maps' }, * { iconCls: 'locate' }, * { iconCls: 'home' }, * { xtype: 'spacer' } * ] * } * ] * }); * */Ext.define('Ext.Button', { extend: 'Ext.Component', xtype: 'button', isButton: true, /** * @event tap * @preventable * Fires whenever a button is tapped. * @param {Ext.Button} this The item added to the Container. * @param {Ext.EventObject} e The event object. */ /** * @event release * @preventable * Fires whenever the button is released. * @param {Ext.Button} this The item added to the Container. * @param {Ext.EventObject} e The event object. */ cachedConfig: { /** * @cfg {String} pressedCls * The CSS class to add to the Button when it is pressed. * @accessor */ pressedCls: Ext.baseCSSPrefix + 'button-pressing', /** * @cfg {String} badgeCls * The CSS class to add to the Button's badge, if it has one. Badges appear as small numbers, letters, or icons that sit on top of your button. For instance, a small red number indicating how many updates are available. * @accessor */ badgeCls: Ext.baseCSSPrefix + 'badge', /** * @cfg {String} hasBadgeCls * The CSS class to add to the Button if it has a badge (note that this goes on the * Button element itself, not on the badge element). * @private * @accessor */ hasBadgeCls: Ext.baseCSSPrefix + 'hasbadge', /** * @cfg {String} labelCls * The CSS class to add to the field's label element. * @accessor */ labelCls: Ext.baseCSSPrefix + 'button-label', /** * @cfg {String} iconCls * One or more space separated CSS classes to be applied to the icon element. * The CSS rule(s) applied should specify a background image to be used as the * icon. * * An example of specifying a custom icon class would be something like: * * // specify the property in the config for the class: * iconCls: 'my-home-icon' * * // css rule specifying the background image to be used as the icon image: * .my-home-icon { * background-image: url(../images/my-home-icon.gif) !important; * } * * In addition to specifying your own classes, you can use the font icons * provided in the SDK using the following syntax: * * // using Font Awesome * iconCls: 'x-fa fa-home' * * // using Pictos * iconCls: 'pictos pictos-home' * * Depending on the theme you're using, you may need include the font icon * packages in your application in order to use the icons included in the * SDK. For more information see: * * - [Font Awesome icons](http://fortawesome.github.io/Font-Awesome/cheatsheet/) * - [Pictos icons](http://docs.sencha.com/extjs/6.0/core_concepts/font_ext.html) * - [Theming Guide](http://docs.sencha.com/extjs/6.0/core_concepts/theming.html) * @accessor */ iconCls: null, /** * @cfg {"left"/"right"/"center"} [textAlign="center"] * @since 6.0.1 */ textAlign: null }, config: { /** * @cfg {String} badgeText * Optional badge text. Badges appear as small numbers, letters, or icons that sit on top of your button. For instance, a small red number indicating how many updates are available. * @accessor */ badgeText: null, /** * @cfg {String} text * The Button text. * @accessor */ text: null, /** * @cfg {String} icon * Url to the icon image to use if you want an icon to appear on your button. * @accessor */ icon: false, /** * @cfg {String} iconAlign * The position within the Button to render the icon Options are: `top`, `right`, `bottom`, `left` and `center` (when you have * no {@link #text} set). * @accessor */ iconAlign: 'left', /** * @cfg {Number/Boolean} pressedDelay * The amount of delay between the `tapstart` and the moment we add the `pressedCls` (in milliseconds). * Settings this to `true` defaults to 100ms. */ pressedDelay: 0, /** * @cfg {Function} handler * The handler function to run when the Button is tapped on. * @accessor */ handler: null, /** * @cfg {Object} scope * The scope to fire the configured {@link #handler} in. * @accessor */ scope: null, /** * @cfg {String} autoEvent * Optional event name that will be fired instead of `tap` when the Button is tapped on. * @accessor */ autoEvent: null, /** * @cfg {String} ui * The ui style to render this button with. The valid default options are: * * - `null` - a basic gray button (default). * - `'back'` - a back button. * - `'forward'` - a forward button. * - `'round'` - a round button. * - `'plain'` * - `'action'` - shaded using the {@link Global_CSS#$active-color $active-color} (dark blue by default). * - `'decline'` - shaded using the {@link Global_CSS#$alert-color $alert-color} (red by default). * - `'confirm'` - shaded using the {@link Global_CSS#$confirm-color $confirm-color} (green by default). * * You can also append `-round` to each of the last three UI's to give it a round shape: * * - **action-round** * - **decline-round** * - **confirm-round** * * @accessor */ ui: null, /** * @cfg {String} html The HTML to put in this button. * * If you want to just add text, please use the {@link #text} configuration. */ /** * @cfg * @inheritdoc */ baseCls: Ext.baseCSSPrefix + 'button' }, defaultBindProperty: 'text', template: [ { tag: 'span', reference: 'badgeElement', hidden: true }, { tag: 'span', className: Ext.baseCSSPrefix + 'button-icon', reference: 'iconElement' }, { tag: 'span', reference: 'textElement', hidden: true } ], initialize: function() { this.callParent(); this.element.on({ scope : this, tap : 'onTap', touchstart : 'onPress', touchend : 'onRelease' }); }, /** * @private */ updateBadgeText: function(badgeText) { var element = this.element, badgeElement = this.badgeElement; if (badgeText) { badgeElement.show(); badgeElement.setText(badgeText); } else { badgeElement.hide(); } element[(badgeText) ? 'addCls' : 'removeCls'](this.getHasBadgeCls()); }, /** * @private */ updateText: function(text) { var textElement = this.textElement; if (textElement) { if (text) { textElement.show(); textElement.setHtml(text); } else { textElement.hide(); } this.refreshIconAlign(); } }, /** * @private */ updateHtml: function(html) { var textElement = this.textElement; if (html) { textElement.show(); textElement.setHtml(html); } else { textElement.hide(); } }, /** * @private */ updateBadgeCls: function(badgeCls, oldBadgeCls) { this.badgeElement.replaceCls(oldBadgeCls, badgeCls); }, /** * @private */ updateHasBadgeCls: function(hasBadgeCls, oldHasBadgeCls) { var element = this.element; if (element.hasCls(oldHasBadgeCls)) { element.replaceCls(oldHasBadgeCls, hasBadgeCls); } }, /** * @private */ updateLabelCls: function(labelCls, oldLabelCls) { this.textElement.replaceCls(oldLabelCls, labelCls); }, /** * @private */ updatePressedCls: function(pressedCls, oldPressedCls) { var element = this.element; if (element.hasCls(oldPressedCls)) { element.replaceCls(oldPressedCls, pressedCls); } }, /** * @private */ updateIcon: function(icon) { var me = this, element = me.iconElement; if (icon) { me.showIconElement(); element.setStyle('background-image', 'url(' + icon + ')'); me.refreshIconAlign(); } else { element.setStyle('background-image', ''); if (!me.getIconCls()) { me.hideIconElement(); } } }, /** * @private */ updateIconCls: function(iconCls, oldIconCls) { var me = this, element = me.iconElement; if (iconCls) { me.showIconElement(); element.replaceCls(oldIconCls, iconCls); me.refreshIconAlign(); } else { element.removeCls(oldIconCls); if (!me.getIcon()) { me.hideIconElement(); } } }, /** * @private */ updateIconAlign: function(alignment, oldAlignment) { var element = this.element, baseCls = Ext.baseCSSPrefix + 'iconalign-'; if (!this.getText()) { alignment = 'center'; } element.removeCls(baseCls + 'center'); element.removeCls(baseCls + oldAlignment); if (this.getIcon() || this.getIconCls()) { element.addCls(baseCls + alignment); } }, _textAlignCls: { left: Ext.baseCSSPrefix + 'text-align-left', right: Ext.baseCSSPrefix + 'text-align-right', center: '' }, updateTextAlign: function (textAlign, oldValue) { var textAlignClasses = this._textAlignCls, add = textAlignClasses[textAlign || 'center'], remove = textAlignClasses[oldValue || 'center']; this.replaceCls(remove, add); }, refreshIconAlign: function() { this.updateIconAlign(this.getIconAlign()); }, applyAutoEvent: function(autoEvent) { var me = this; if (typeof autoEvent == 'string') { autoEvent = { name : autoEvent, scope: me.scope || me }; } return autoEvent; }, /** * @private */ updateAutoEvent: function(autoEvent) { var name = autoEvent.name, scope = autoEvent.scope; this.setHandler(function() { scope.fireEvent(name, scope, this); }); this.setScope(scope); }, /** * Used by `icon` and `iconCls` configurations to hide the icon element. * @private */ hideIconElement: function() { var el = this.iconElement; el.removeCls(Ext.baseCSSPrefix + 'shown'); el.addCls(Ext.baseCSSPrefix + 'hidden'); this.element.addCls(Ext.baseCSSPrefix + 'button-no-icon'); }, /** * Used by `icon` and `iconCls` configurations to show the icon element. * @private */ showIconElement: function() { var el = this.iconElement; el.addCls(Ext.baseCSSPrefix + 'shown'); el.removeCls(Ext.baseCSSPrefix + 'hidden'); this.element.removeCls(Ext.baseCSSPrefix + 'button-no-icon'); }, /** * We override this to check for '{ui}-back'. This is because if you have a UI of back, you need to actually add two class names. * The ui class, and the back class: * * `ui: 'action-back'` would turn into: * * `class="x-button-action x-button-back"` * * But `ui: 'action'` would turn into: * * `class="x-button-action"` * * So we just split it up into an array and add both of them as a UI, when it has `back`. * @private */ applyUi: function(config) { if (config && Ext.isString(config)) { var array = config.split('-'); if (array && (array[1] == "back" || array[1] == "forward")) { return array; } } return config; }, getUi: function() { //Now that the UI can sometimes be an array, we need to check if it an array and return the proper value. var ui = this._ui; if (Ext.isArray(ui)) { return ui.join('-'); } return ui; }, applyPressedDelay: function(delay) { if (Ext.isNumber(delay)) { return delay; } return (delay) ? 100 : 0; }, /** * @private */ onPress: function() { var me = this, element = me.element, pressedDelay = me.getPressedDelay(), pressedCls = me.getPressedCls(); if (!me.getDisabled()) { if (pressedDelay > 0) { me.pressedTimeout = Ext.defer(function() { delete me.pressedTimeout; if (element) { element.addCls(pressedCls); } }, pressedDelay); } else { element.addCls(pressedCls); } } }, /** * @private */ onRelease: function(e) { this.fireAction('release', [this, e], 'doRelease'); }, /** * @private */ doRelease: function(me, e) { if (!me.getDisabled()) { if (me.hasOwnProperty('pressedTimeout')) { clearTimeout(me.pressedTimeout); delete me.pressedTimeout; } else { me.element.removeCls(me.getPressedCls()); } } }, /** * @private */ onTap: function(e) { if (this.getDisabled()) { return false; } this.fireAction('tap', [this, e], 'doTap'); }, /** * @private */ doTap: function(me, e) { var handler = me.getHandler(); if (!handler) { return; } //this is done so if you hide the button in the handler, the tap event will not fire on the new element //where the button was. if (e && e.preventDefault) { e.preventDefault(); } Ext.callback(handler, me.getScope(), [me, e], 0, me); }});