/** * A split button that provides a built-in dropdown arrow that can fire an event separately from the default click event * of the button. Typically this would be used to display a dropdown menu that provides additional options to the * primary button action, but any custom handler can provide the arrowclick implementation. Example usage: * * @example * // display a dropdown menu: * Ext.create('Ext.SplitButton', { * renderTo: Ext.getBody(), * text: 'Options', * // handle a click on the button itself * handler: function() { * alert("The button was clicked"); * }, * menu: new Ext.menu.Menu({ * items: [ * // these will render as dropdown menu items when the arrow is clicked: * {text: 'Item 1', handler: function(){ alert("Item 1 clicked"); }}, * {text: 'Item 2', handler: function(){ alert("Item 2 clicked"); }} * ] * }) * }); * * Provide custom handling to the split button when the dropdown arrow is clicked: * * Ext.create('Ext.SplitButton', { * renderTo: 'button-ct', * text: 'Options', * handler: optionsHandler, * arrowHandler: myCustomHandler * }); * */Ext.define('Ext.SplitButton', { extend: 'Ext.Button', xtype: 'splitbutton', requires: [ 'Ext.menu.Menu' ], isSplitButton: true, baseCls: Ext.baseCSSPrefix + 'splitButton', /** * @event arrowclick * Fires when this button's arrow is clicked. * @param {Ext.SplitButton} this * @param {Event} e The click event. */ config: { /** * @cfg {Function} arrowHandler * @cfg {Ext.SplitButton} arrowHandler.button This Button. * @cfg {Ext.event.Event} arrowHandler.e The triggering event. * The handler function to run when the Button is tapped on. * @controllable */ arrowHandler: null }, /** * @private */ arrowCls: 'split', initialize: function() { var me = this; me.callParent(); me.arrowElement.addClsOnOver(me.hoveredCls, me.isEnabled, me); me.splitInnerElement.addClsOnOver(me.hoveredCls, me.isEnabled, me); me.splitArrowCoverElement.on({ focus: 'handleFocusEvent', blur: 'handleBlurEvent', scope: me }); }, getTemplate: function() { return [{ reference: 'innerElement', cls: Ext.baseCSSPrefix + 'splitBody-el', children: [{ reference: 'splitInnerElement', cls: Ext.baseCSSPrefix + 'splitInner-el', children: [{ reference: 'bodyElement', cls: Ext.baseCSSPrefix + 'body-el', children: [{ cls: Ext.baseCSSPrefix + 'icon-el ' + Ext.baseCSSPrefix + 'font-icon', reference: 'iconElement' }, { reference: 'textElement', cls: Ext.baseCSSPrefix + 'text-el' }] }, this.getButtonTemplate()] }, { reference: 'arrowElement', cls: Ext.baseCSSPrefix + 'splitArrow-el', children: [{ reference: 'splitArrowElement', cls: Ext.baseCSSPrefix + 'arrow-el ' + Ext.baseCSSPrefix + 'font-icon' }, this.getArrowButtonTemplate()] } ] } ]; }, getArrowButtonTemplate: function() { return { tag: 'button', reference: 'splitArrowCoverElement', cls: Ext.baseCSSPrefix + 'button-el' }; }, /** * @private */ doTap: function(me, e) { // 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 (me.preventDefaultAction) { e.preventDefault(); } if (!me.getDisabled()) { if ((e.type === 'keydown' || e.type === 'click') && (e.target === this.splitArrowCoverElement.dom)) { // This is done to give delay in showing menu to match ripple timing if (!Ext.isEmpty(me.menuShowDelay) && me.menuShowDelay > 0) { me.menuShowTimeout = Ext.defer(handleArrowKeyDown, me.menuShowDelay); } else { me.toggleMenu(e, me.getMenu()); me.fireEvent('arrowclick', me, e); Ext.callback(me.getArrowHandler(), me.getScope(), [me, e], 0, me); } } else { if (me.getMenu().isVisible()) { me.hideMenu(e, me.getMenu()); } Ext.callback(me.getHandler(), me.getScope(), [me, e], 0, me); } } }, onDownKey: function(e) { if (e.target === this.splitArrowCoverElement.dom) { this.callParent([e]); } }, updatePressed: function(pressed) { this.callParent([pressed]); this.arrowElement.toggleCls(this.pressedCls, pressed); }, findEventTarget: function(e) { return e.target === this.buttonElement.dom ? this.splitInnerElement : this.arrowElement; }, shouldRipple: function(e) { var arrowEl = this.splitArrowCoverElement, ripple = (arrowEl && e.target === arrowEl.dom) ? this.getArrowRipple() : this.getSplitRipple(); this.setRipple(ripple); return this.callParent([e]); }, enableFocusable: function() { this.splitArrowCoverElement.dom.disabled = false; this.callParent(); }, disableFocusable: function() { this.callParent(); this.splitArrowCoverElement.dom.disabled = true; }, privates: { onButtonFocus: function(e) { this.splitInnerElement.addCls(this.focusCls); }, onButtonBlur: function(e) { this.splitInnerElement.removeCls(this.focusCls); }, onArrowFocus: function(e) { this.arrowElement.addCls(this.focusCls); }, onArrowBlur: function(e) { this.arrowElement.removeCls(this.focusCls); }, handleFocusEvent: function(e) { this.callParent([e]); if (e.target === this.splitArrowCoverElement.dom) { this.onArrowFocus([e]); } else if (e.target === this.buttonElement.dom) { this.onButtonFocus([e]); } }, handleBlurEvent: function(e) { this.callParent([e]); if (e.target === this.splitArrowCoverElement.dom) { this.onArrowBlur([e]); } else if (e.target === this.buttonElement.dom) { this.onButtonBlur([e]); } } }});