/** * SegmentedButton is a container for a group of {@link Ext.Button}s. Generally a SegmentedButton would be * a child of a {@link Ext.Toolbar} and would be used to switch between different views. * * ## Example usage: * * @example * var segmentedButton = Ext.create('Ext.SegmentedButton', { * allowMultiple: true, * items: [ * { * text: 'Option 1' * }, * { * text: 'Option 2', * pressed: true * }, * { * text: 'Option 3' * } * ], * listeners: { * toggle: function(container, button, pressed){ * alert("User toggled the '" + button.getText() + "' button: " + (pressed ? 'on' : 'off')); * } * } * }); * Ext.Viewport.add({ xtype: 'container', padding: 10, items: [segmentedButton] }); */Ext.define('Ext.SegmentedButton', { extend: 'Ext.Container', xtype : 'segmentedbutton', alternateClassName: 'Ext.button.Segmented', requires: [ 'Ext.Button' ], isSegmentedButton: true, config: { /** * @cfg * @inheritdoc */ baseCls: Ext.baseCSSPrefix + 'segmentedbutton', /** * @cfg {String} pressedCls * CSS class when a button is in pressed state. * @accessor */ pressedCls: Ext.baseCSSPrefix + 'button-pressed', /** * @cfg {Boolean} allowMultiple * Allow multiple pressed buttons. * @accessor */ allowMultiple: false, /** * @cfg {Boolean} allowDepress * Allow toggling the pressed state of each button. * Defaults to `true` when {@link #allowMultiple} is `true`. * @accessor */ allowDepress: false, /** * @cfg {Boolean} allowToggle Allow child buttons to be pressed when tapped on. Set to `false` to allow tapping but not toggling of the buttons. * @accessor */ allowToggle: true, /** * @cfg {Array} pressedButtons * The pressed buttons for this segmented button. * * You can remove all pressed buttons by calling {@link #setPressedButtons} with an empty array. * @accessor */ pressedButtons: [], /** * @cfg * @inheritdoc */ layout: { type : 'hbox', align: 'stretch' }, /** * @cfg * @inheritdoc */ defaultType: 'button', /** * @cfg {String} * Default {@link Ext.Component#ui ui} to use for buttons in this segmented button. * Buttons can override this default by specifying their own UI */ defaultUI: null }, /** * @event toggle * Fires when any child button's pressed state has changed. * @param {Ext.SegmentedButton} this * @param {Ext.Button} button The toggled button. * @param {Boolean} isPressed Boolean to indicate if the button was pressed or not. */ initialize: function() { var me = this; me.callParent(); me.on({ delegate: '> button', scope : me, tap: 'onButtonRelease' }); me.onAfter({ delegate: '> button', scope : me, hide: 'onButtonHiddenChange', show: 'onButtonHiddenChange' }); }, updateAllowMultiple: function(allowMultiple) { if (!this.initialized && !this.getInitialConfig().hasOwnProperty('allowDepress') && allowMultiple) { this.setAllowDepress(true); } }, /** * We override `initItems` so we can check for the pressed config. */ applyItems: function() { var me = this, pressedButtons = [], ln, i, item, items; //call the parent first so the items get converted into a MixedCollection me.callParent(arguments); items = this.getItems(); ln = items.length; for (i = 0; i < ln; i++) { item = items.items[i]; if (item.getInitialConfig('pressed')) { pressedButtons.push(items.items[i]); } } me.updateFirstAndLastCls(items); me.setPressedButtons(pressedButtons); }, /** * Button sets a timeout of 10ms to remove the {@link #pressedCls} on the release event. * We don't want this to happen, so lets return `false` and cancel the event. * @private */ onButtonRelease: function(button) { if (!this.getAllowToggle()) { return; } var me = this, pressedButtons = me.getPressedButtons() || [], buttons = [], alreadyPressed; if (!me.getDisabled() && !button.getDisabled()) { //if we allow for multiple pressed buttons, use the existing pressed buttons if (me.getAllowMultiple()) { buttons = pressedButtons.concat(buttons); } alreadyPressed = (buttons.indexOf(button) !== -1) || (pressedButtons.indexOf(button) !== -1); //if we allow for depressing buttons, and the new pressed button is currently pressed, remove it if (alreadyPressed && me.getAllowDepress()) { Ext.Array.remove(buttons, button); } else if (!alreadyPressed || !me.getAllowDepress()) { buttons.push(button); } me.setPressedButtons(buttons); } }, onItemAdd: function() { this.callParent(arguments); this.updateFirstAndLastCls(this.getItems()); }, onItemRemove: function() { this.callParent(arguments); this.updateFirstAndLastCls(this.getItems()); }, /** * @private */ onButtonHiddenChange: function() { this.updateFirstAndLastCls(this.getItems()); }, /** * @private */ updateFirstAndLastCls: function(items) { var ln = items.length, basePrefix = Ext.baseCSSPrefix, firstCls = basePrefix + 'first', lastCls = basePrefix + 'last', item, i; //remove all existing classes for (i = 0; i < ln; i++) { item = items.items[i]; item.removeCls(firstCls); item.removeCls(lastCls); } //add a first cls to the first non-hidden button for (i = 0; i < ln; i++) { item = items.items[i]; if (!item.isHidden()) { item.addCls(firstCls); break; } } //add a last cls to the last non-hidden button for (i = ln - 1; i >= 0; i--) { item = items.items[i]; if (!item.isHidden()) { item.addCls(lastCls); break; } } }, /** * @private */ applyPressedButtons: function(newButtons) { var me = this, array = [], button, ln, i; if (me.getAllowToggle()) { if (Ext.isArray(newButtons)) { ln = newButtons.length; for (i = 0; i< ln; i++) { button = me.getComponent(newButtons[i]); if (button && array.indexOf(button) === -1) { array.push(button); } } } else { button = me.getComponent(newButtons); if (button && array.indexOf(button) === -1) { array.push(button); } } } return array; }, /** * Updates the pressed buttons. * @private */ updatePressedButtons: function(newButtons, oldButtons) { var me = this, items = me.getItems(), pressedCls = me.getPressedCls(), events = [], item, button, ln, i, e; //loop through existing items and remove the pressed cls from them ln = items.length; if (oldButtons && oldButtons.length) { for (i = 0; i < ln; i++) { item = items.items[i]; if (oldButtons.indexOf(item) != -1 && newButtons.indexOf(item) == -1) { item.removeCls([pressedCls, item.getPressedCls()]); events.push({ item: item, toggle: false }); } } } //loop through the new pressed buttons and add the pressed cls to them ln = newButtons.length; for (i = 0; i < ln; i++) { button = newButtons[i]; if (!oldButtons || oldButtons.indexOf(button) == -1) { button.addCls(pressedCls); events.push({ item: button, toggle: true }); } } //loop through each of the events and fire them after a delay ln = events.length; if (ln && oldButtons !== undefined) { Ext.defer(function() { for (i = 0; i < ln; i++) { e = events[i]; me.fireEvent('toggle', me, e.item, e.toggle); } }, 50); } }, setPressed: function(button, pressed) { var pressedButtons = this.getPressedButtons().slice(); if (pressed) { Ext.Array.include(pressedButtons, button); } else { Ext.Array.remove(pressedButtons, button); } this.setPressedButtons(pressedButtons); }, /** * Returns `true` if a specified {@link Ext.Button} is pressed. * @param {Ext.Button} button The button to check if pressed. * @return {Boolean} pressed */ isPressed: function(button) { var pressedButtons = this.getPressedButtons(); return pressedButtons.indexOf(button) !== -1; }, /** * @private */ updateDisabled: function(disabled) { var me = this; me.items.each(function(item) { item.setDisabled(disabled); }, me); me.callParent(arguments); }, setValue: function(value) { this.setPressedButtons([this.items.getAt(value)]); }, getValue: function() { var buttons = this.getPressedButtons(), out = -1; if (buttons.length) { out = this.items.indexOf(buttons[0]); } return out; }, updateDefaultUI: function(defaultUI) { var items = this.items && this.items.items, len = items.length, i, item; for (i = 0; i < len; i++) { item = items[i]; if (item.getUi() == null) { item.setUi(defaultUI); } } }, doAdd: function(item, instanced) { var defaultUI = this.getDefaultUI(); if (defaultUI && (item.getUi() == null)) { item.setUi(defaultUI); } this.callParent([item, instanced]); }});