/**
* @author Ed Spencer
* TabBar is used internally by a {@link Ext.tab.Panel TabPanel} and typically should not need to be created manually.
* The tab bar automatically removes the default title provided by {@link Ext.panel.Header}
*/
Ext.define('Ext.tab.Bar', {
extend: 'Ext.panel.Header',
alias: 'widget.tabbar',
baseCls: Ext.baseCSSPrefix + 'tab-bar',
requires: ['Ext.tab.Tab'],
/**
* @property {Boolean} isTabBar
* `true` in this class to identify an objact as an instantiated Tab Bar, or subclass thereof.
*/
isTabBar: true,
/**
* @cfg {String} title @hide
*/
/**
* @cfg {String} iconCls @hide
*/
// @private
defaultType: 'tab',
/**
* @cfg {Boolean} plain
* True to not show the full background on the tabbar
*/
plain: false,
childEls: [
'body', 'strip'
],
// @private
renderTpl: [
'
{baseCls}-body-{ui} {parent.baseCls}-body-{parent.ui}-{.}" style="{bodyStyle}">',
'{%this.renderContainer(out,values)%}',
'
',
' {baseCls}-strip-{ui} {parent.baseCls}-strip-{parent.ui}-{.}">
'
],
/**
* @cfg {Number} minTabWidth
* The minimum width for a tab in this tab Bar. Defaults to the tab Panel's {@link Ext.tab.Panel#minTabWidth minTabWidth} value.
* @deprecated This config is deprecated. It is much easier to use the {@link Ext.tab.Panel#minTabWidth minTabWidth} config on the TabPanel.
*/
/**
* @cfg {Number} maxTabWidth
* The maximum width for a tab in this tab Bar. Defaults to the tab Panel's {@link Ext.tab.Panel#maxTabWidth maxTabWidth} value.
* @deprecated This config is deprecated. It is much easier to use the {@link Ext.tab.Panel#maxTabWidth maxTabWidth} config on the TabPanel.
*/
// @private
initComponent: function() {
var me = this;
if (me.plain) {
me.setUI(me.ui + '-plain');
}
me.addClsWithUI(me.dock);
me.addEvents(
/**
* @event change
* Fired when the currently-active tab has changed
* @param {Ext.tab.Bar} tabBar The TabBar
* @param {Ext.tab.Tab} tab The new Tab
* @param {Ext.Component} card The card that was just shown in the TabPanel
*/
'change'
);
// Element onClick listener added by Header base class
me.callParent(arguments);
// TabBar must override the Header's align setting.
me.layout.align = (me.orientation == 'vertical') ? 'left' : 'top';
me.layout.overflowHandler = new Ext.layout.container.boxOverflow.Scroller(me.layout);
me.remove(me.titleCmp);
delete me.titleCmp;
Ext.apply(me.renderData, {
bodyCls: me.bodyCls
});
},
getLayout: function() {
var me = this;
me.layout.type = (me.dock === 'top' || me.dock === 'bottom') ? 'hbox' : 'vbox';
return me.callParent(arguments);
},
// @private
onAdd: function(tab) {
tab.position = this.dock;
this.callParent(arguments);
},
onRemove: function(tab) {
var me = this;
if (tab === me.previousTab) {
me.previousTab = null;
}
me.callParent(arguments);
},
afterComponentLayout : function(width) {
this.callParent(arguments);
this.strip.setWidth(width);
},
// @private
onClick: function(e, target) {
// The target might not be a valid tab el.
var me = this,
tabEl = e.getTarget('.' + Ext.tab.Tab.prototype.baseCls),
tab = tabEl && Ext.getCmp(tabEl.id),
tabPanel = me.tabPanel,
isCloseClick = tab && tab.closeEl && (target === tab.closeEl.dom);
if (isCloseClick) {
e.preventDefault();
}
if (tab && tab.isDisabled && !tab.isDisabled()) {
if (tab.closable && isCloseClick) {
tab.onCloseClick();
} else {
if (tabPanel) {
// TabPanel will card setActiveTab of the TabBar
tabPanel.setActiveTab(tab.card);
} else {
me.setActiveTab(tab);
}
tab.focus();
}
}
},
/**
* @private
* Closes the given tab by removing it from the TabBar and removing the corresponding card from the TabPanel
* @param {Ext.tab.Tab} toClose The tab to close
*/
closeTab: function(toClose) {
var me = this,
card = toClose.card,
tabPanel = me.tabPanel,
toActivate;
if (card && card.fireEvent('beforeclose', card) === false) {
return false;
}
// If we are closing the active tab, revert to the previously active tab (or the previous or next enabled sibling if
// there *is* no previously active tab, or the previously active tab is the one that's being closed or the previously
// active tab has since been disabled)
toActivate = me.findNextActivatable(toClose);
// We are going to remove the associated card, and then, if that was sucessful, remove the Tab,
// And then potentially activate another Tab. We should not layout for each of these operations.
Ext.suspendLayouts();
if (tabPanel && card) {
// Remove the ownerCt so the tab doesn't get destroyed if the remove is successful
// We need this so we can have the tab fire it's own close event.
delete toClose.ownerCt;
// we must fire 'close' before removing the card from panel, otherwise
// the event will no loger have any listener
card.fireEvent('close', card);
tabPanel.remove(card);
// Remove succeeded
if (!tabPanel.getComponent(card)) {
/*
* Force the close event to fire. By the time this function returns,
* the tab is already destroyed and all listeners have been purged
* so the tab can't fire itself.
*/
toClose.fireClose();
me.remove(toClose);
} else {
// Restore the ownerCt from above
toClose.ownerCt = me;
Ext.resumeLayouts(true);
return false;
}
}
// If we are closing the active tab, revert to the previously active tab (or the previous sibling or the nnext sibling)
if (toActivate) {
// Our owning TabPanel calls our setActiveTab method, so only call that if this Bar is being used
// in some other context (unlikely)
if (tabPanel) {
tabPanel.setActiveTab(toActivate.card);
} else {
me.setActiveTab(toActivate);
}
toActivate.focus();
}
Ext.resumeLayouts(true);
},
// private - used by TabPanel too.
// Works out the next tab to activate when one tab is closed.
findNextActivatable: function(toClose) {
var me = this;
if (toClose.active && me.items.getCount() > 1) {
return (me.previousTab && me.previousTab !== toClose && !me.previousTab.disabled) ? me.previousTab : (toClose.next('tab[disabled=false]') || toClose.prev('tab[disabled=false]'));
}
},
/**
* @private
* Marks the given tab as active
* @param {Ext.tab.Tab} tab The tab to mark active
*/
setActiveTab: function(tab) {
var me = this;
if (!tab.disabled && tab !== me.activeTab) {
if (me.activeTab) {
if (me.activeTab.isDestroyed) {
me.previousTab = null;
} else {
me.previousTab = me.activeTab;
me.activeTab.deactivate();
}
}
tab.activate();
me.activeTab = tab;
me.fireEvent('change', me, tab, tab.card);
// Ensure that after the currently in progress layout, the active tab is scrolled into view
me.on({
afterlayout: me.afterTabActivate,
scope: me,
single: true
});
me.updateLayout();
}
},
afterTabActivate: function() {
this.layout.overflowHandler.scrollToItem(this.activeTab);
}
});