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: