
TabPanel Context Menu can be used for dynamic TabPanels. This feature is available in some IDEs, document editors, and internet browsers. I split the code to three files and did not use MVC pattern, sad but true.
The plugin has the following manipulation functionality:
- Close tab under mouse pointer;
- Close all tabs;
- Close all tabs except the one, which is under mouse pointer;
- Switch the TabBar‘s position to top, right, bottom or left.
You can also add permit-to-close mechanisms for the tabs, which are not allowed to be closed.
app/ux/tab/contextMenu/Plugin.js
Ext.define('app.ux.tab.contextMenu.Plugin', {
extend: 'Ext.plugin.Abstract',
alias: 'plugin.contextmenu',
requires: [
'app.ux.tab.contextMenu.Menu'
],
init: function (tabPanel) {
var tabBar = tabPanel.getTabBar();
tabBar.on('add', this.onTabAdd, this);
},
onTabAdd: function (tabBar, tab) {
if (tab.renderd) {
this.addContextMenuToTab(tab);
} else {
tab.on('afterrender', this.addContextMenuToTab, this, {single: true});
}
},
addContextMenuToTab: function (tab) {
var contextMenu = this.getContextMenu();
tab.getEl().on('contextmenu', function (e) {
e.preventDefault();
contextMenu.setTab(tab);
contextMenu.showAt(e.getX(), e.getY());
}, this);
},
getContextMenu: function () {
if (!this.contextMenu) {
this.contextMenu = Ext.create('app.ux.tab.contextMenu.Menu', {
tabPanel: this.getCmp()
});
}
return this.contextMenu;
}
});
app/ux/tab/contextMenu/Menu.js
Ext.define('app.ux.tab.contextMenu.Menu', {
extend: 'Ext.menu.Menu',
requires: [
'app.ux.tab.contextMenu.TabPlacementMenu'
],
tabPanel: false,
tab: false,
initComponent: function () {
this.items = [
this.getCloseTab(),
this.getCloseAllTabs(),
this.getCloseOtherTabs(),
'-', {
xtype: 'menuitem',
text: "Tab Placement",
menu: this.getTabPlacementMenu()
}
];
this.getTabPanel().on('afterlayout', this.onTabPanelAfterLayout, this);
this.callParent();
},
getCloseTab: function () {
if (!this.closeTab) {
this.closeTab = Ext.create('Ext.menu.Item', {
text: "Close",
handler: this.closeTabHandler,
scope: this
});
}
return this.closeTab;
},
getCloseAllTabs: function () {
if (!this.closeAllTabs) {
this.closeAllTabs = Ext.create('Ext.menu.Item', {
text: "Close All",
handler: this.closeAllTabsHandler,
scope: this
});
}
return this.closeAllTabs;
},
getCloseOtherTabs: function () {
if (!this.closeOtherTabs) {
this.closeOtherTabs = Ext.create('Ext.menu.Item', {
text: "Close Others",
handler: this.closeOtherTabsHandler,
scope: this
});
}
return this.closeOtherTabs;
},
getTabPlacementMenu: function () {
if (!this.tabPlacementMenu) {
this.tabPlacementMenu = Ext.create('app.ux.tab.contextMenu.TabPlacementMenu', {
tabPanel: this.tabPanel
});
}
return this.tabPlacementMenu;
},
getTabPanel: function () {
return this.tabPanel;
},
setTab: function (tab) {
this.tab = tab;
return this;
},
getTab: function (tab) {
return this.tab;
},
onTabPanelAfterLayout: function (tabPanel) {
this.getTabPlacementMenu().setTabPosition(tabPanel.getTabPosition());
},
closeTabHandler: function () {
this.getTabPanel().getTabBar().closeTab(this.getTab());
},
closeAllTabsHandler: function () {
this.getTabPanel().removeAll();
},
closeOtherTabsHandler: function () {
var tabBar = this.getTabPanel().getTabBar(),
tabs = tabBar.query('tab');
Ext.Array.each(tabs, function (tab) {
if (tab.getId() != this.getTab().getId()) {
tabBar.closeTab(tab);
}
}, this);
}
});
app/ux/tab/contextMenu/TabPlacementMenu.js
Ext.define('app.ux.tab.contextMenu.TabPlacementMenu', {
extend: 'Ext.menu.Menu',
tabPanel: false,
initComponent: function () {
this.items = [
this.getTopPlacementItem(),
this.getRightPlacementItem(),
this.getBottomPlacementItem(),
this.getLeftPlacementItem()
];
this.callParent();
},
getTopPlacementItem: function () {
if (!this.topPlacementItem) {
this.topPlacementItem = Ext.create('Ext.menu.Item', {
text: "Top",
glyph: 'xf062@FontAwesome',
position: 'top',
handler: this.changePositionHandler,
scope: this
});
}
return this.topPlacementItem;
},
getRightPlacementItem: function () {
if (!this.rightPlacementItem) {
this.rightPlacementItem = Ext.create('Ext.menu.Item', {
text: "Right",
glyph: 'xf061@FontAwesome',
position: 'right',
handler: this.changePositionHandler,
scope: this
});
}
return this.rightPlacementItem;
},
getBottomPlacementItem: function () {
if (!this.bottomPlacementItem) {
this.bottomPlacementItem = Ext.create('Ext.menu.Item', {
text: "Bottom",
glyph: 'xf063@FontAwesome',
position: 'bottom',
handler: this.changePositionHandler,
scope: this
});
}
return this.bottomPlacementItem;
},
getLeftPlacementItem: function () {
if (!this.leftPlacementItem) {
this.leftPlacementItem = Ext.create('Ext.menu.Item', {
text: "Left",
glyph: 'xf060@FontAwesome',
position: 'left',
handler: this.changePositionHandler,
scope: this
});
}
return this.leftPlacementItem;
},
getTabPanel: function () {
return this.tabPanel;
},
changePositionHandler: function (menuItem) {
this.getTabPanel().setTabPosition(menuItem.position)
},
setTabPosition: function (tabPosition) {
this.items.each(function(menuItem) {
menuItem.setDisabled(menuItem.position == tabPosition);
}, this);
}
});
Sample Usage: