/** * 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: * * ```javascript * @example({ framework: 'extjs' }) * // 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 * }); * * ```html * @example({framework: 'ext-web-components', packages:['ext-web-components'], tab: 1 }) * <ext-container padding="10" layout="vbox"> * <ext-splitbutton * text="SplitButton" * ontap="splitbutton.sayHello" * ui="action alt" * iconCls="x-fa fa-heart" * margin="10" * onready="splitbutton.splitButtonReady" * > * </ext-splitbutton> * </ext-container> * ``` * ```javascript * @example({framework: 'ext-web-components', tab: 2, packages: ['ext-web-components']}) * * import '@sencha/ext-web-components/dist/ext-container.component'; * import '@sencha/ext-web-components/dist/ext-splitbutton.component'; * * export default class SplitButtonComponent { * splitButtonReady = (event) => { * const splitButtonCmp = event.detail.cmp; * splitButtonCmp.setMenu([{ * text: "Menu Item 1", * handler: () => { alert("Item 1 clicked"); } * }, { * text: "Menu Item 2", * handler: () => {alert("Item 2 clicked"); } * }]); * } * * sayHello = () => { * alert("Hello! The button was clicked"); * } * } * * window.splitbutton = new SplitButtonComponent(); * ``` * * ```javascript * @example({framework: 'ext-react', packages:['ext-react']}) * import React, { Component } from 'react'; * import { ExtContainer, ExtSplitButton } from '@sencha/ext-react'; * * export default class SplitButtonExample extends Component { * * render() { * return ( * <ExtContainer padding="10" layout="vbox"> * <ExtSplitButton * text="SplitButton" * handler={this.sayHello} * ui="action alt" * iconCls="x-fa fa-heart" * {...menuProps} margin="10" * /> * </ExtContainer> * ) * } * sayHello = () => { * alert("Hello! The button was clicked"); * } * } * const menuProps ={ * menu: [{ * text: 'Menu Item 1', * handler: function(){ alert("Item 1 clicked"); } * }, { * text: 'Menu Item 2', * handler: function(){ alert("Item 2 clicked"); } * }] * } * ``` * ```javascript * @example({framework: 'ext-angular', packages:['ext-angular']}) * import { Component } from '@angular/core' * declare var Ext: any; * * @Component({ * selector: 'app-root-1', * styles: [` * `], * template: ` * <ExtContainer padding="10" layout="vbox"> * <ExtSplitButton * text="SplitButton" * [handler]="this.sayHello" * ui="action alt" * iconCls="x-fa fa-heart" * margin="10" * [menu]="this.menu" * ></ExtSplitButton> * </ExtContainer> * ` * }) * export class AppComponent { * * sayHello = () => { * alert("Hello! The button was clicked"); * } * * menu = [{ * text: 'Menu Item 1', * handler: function(){ alert("Item 1 clicked"); } * }, { * text: 'Menu Item 2', * handler: function(){ alert("Item 2 clicked"); } * }]; * } * ``` */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) { var menu; // 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(me.getArrowHandler(), me.menuShowDelay); } else { me.toggleMenu(e, me.getMenu()); me.fireEvent('arrowclick', me, e); Ext.callback(me.getArrowHandler(), me.getScope(), [me, e], 0, me); } } else { // Check menu - can throw error in Breadcrumb when there is no menu items menu = me.getMenu(); if (menu && menu.isVisible()) { me.hideMenu(e, menu); } 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]); } } }});