[pve-devel] [PATCH manager 01/10] use a treelist instead of tabs in configpanel
Dominik Csapak
d.csapak at proxmox.com
Mon Aug 22 17:13:33 CEST 2016
this patch changes configpanel class,
so that instead of creating a tabpanel,
we now are a card panel which uses a treelist
to choose the active card
this changes how the panel looks:
instead of having countless tabs on the top
we now can have a nice tree structure on the left
how the items will be interpreted by the tree
is commented in www/manager6/panel/ConfigPanel.js
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
www/css/ext6-pve.css | 24 ++++
www/manager6/Workspace.js | 1 -
www/manager6/panel/ConfigPanel.js | 294 ++++++++++++++++++++++++++++----------
3 files changed, 239 insertions(+), 80 deletions(-)
diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css
index 8ab383c..ddef394 100644
--- a/www/css/ext6-pve.css
+++ b/www/css/ext6-pve.css
@@ -392,3 +392,27 @@ div.right-aligned {
.x-progress.warning .x-progress-bar{
background-color: #FFCC00;
}
+
+.x-treelist-nav {
+ background-color: #f5f5f5;
+}
+
+.x-treelist-row {
+ margin-top: 5px;
+}
+
+.x-treelist-item-icon {
+ color: #000;
+ margin-left: 2px;
+}
+
+.x-treelist-item-text {
+ color: #000;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+.x-treelist-row-over > * > .x-treelist-item-icon,
+.x-treelist-row-over > * > .x-treelist-item-text{
+ color: #000;
+}
diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js
index c23ea57..83a2c62 100644
--- a/www/manager6/Workspace.js
+++ b/www/manager6/Workspace.js
@@ -306,7 +306,6 @@ Ext.define('PVE.StdWorkspace', {
comp = {
xtype: tlckup[n.data.type || 'root'] ||
'pvePanelConfig',
- layout: { type: 'fit' },
showSearch: (n.data.id === 'root') ||
Ext.isDefined(n.data.groupbyid),
pveSelNode: n,
diff --git a/www/manager6/panel/ConfigPanel.js b/www/manager6/panel/ConfigPanel.js
index 51bdf1f..a5279d8 100644
--- a/www/manager6/panel/ConfigPanel.js
+++ b/www/manager6/panel/ConfigPanel.js
@@ -1,5 +1,43 @@
/*
* Base class for all the multitab config panels
+ *
+ * How to use this:
+ *
+ * You create a subclass of this, and then define your wanted tabs
+ * as items like this:
+ *
+ * items: [{
+ * title: "myTitle",
+ * xytpe: "somextype",
+ * iconCls: 'fa fa-icon',
+ * groups: ['somegroup'],
+ * expandedOnInit: true,
+ * itemId: 'someId'
+ * }]
+ *
+ * this has to be in the declarative syntax, else we
+ * cannot save them for later
+ * (so no Ext.create or Ext.apply of an item in the subclass)
+ *
+ * the groups array expects the itemids of the items
+ * which are the parents, which have to come before they
+ * are used
+ *
+ * if you want following the tree:
+ *
+ * Option1
+ * Option2
+ * -> SubOption1
+ * -> SubSubOption1
+ *
+ * the suboption1 group array has to look like this:
+ * groups: ['itemid-of-option2']
+ *
+ * and of subsuboption1:
+ * groups: ['itemid-of-option2', 'itemid-of-suboption1']
+ *
+ * setting the expandedOnInit determines if the item/group is expanded
+ * initially (false by default)
*/
Ext.define('PVE.panel.Config', {
extend: 'Ext.panel.Panel',
@@ -8,42 +46,117 @@ Ext.define('PVE.panel.Config', {
showSearch: true, // add a ressource grid with a search button as first tab
viewFilter: undefined, // a filter to pass to that ressource grid
+ dockedItems: [{
+ // this is needed for the overflow handler
+ xtype: 'toolbar',
+ overflowHandler: 'scroller',
+ dock: 'left',
+ style: {
+ backgroundColor: '#f5f5f5',
+ padding: 0,
+ margin: 0
+ },
+ items: {
+ xtype: 'treelist',
+ itemId: 'menu',
+ ui: 'nav',
+ expanderOnly: true,
+ expanderFirst: false,
+ animation: false,
+ singleExpand: false,
+ listeners: {
+ selectionchange: function(treeList, selection) {
+ var me = this.up('panel');
+ me.suspendLayout = true;
+ me.activateCard(selection.data.id);
+ me.suspendLayout = false;
+ me.updateLayout();
+ },
+ itemclick: function(treelist, info) {
+ var olditem = treelist.getSelection();
+ var newitem = info.node;
+
+ // when clicking on the expand arrow,
+ // we dont select items, but still want
+ // the original behaviour
+ if (info.select === false) {
+ return;
+ }
+
+ // if you click on a different item which is open,
+ // leave it open
+ // else toggle the clicked item
+ if (olditem.data.id !== newitem.data.id &&
+ newitem.data.expanded === true) {
+ info.toggle = false;
+ } else {
+ info.toggle = true;
+ }
+ }
+ }
+ }
+ },
+ {
+ xtype: 'toolbar',
+ itemId: 'toolbar',
+ dock: 'top',
+ height: 36,
+ overflowHandler: 'menu'
+ }],
+
+ firstItem: '',
+ layout: 'card',
+ border: 0,
+
+ activateCard: function(cardid) {
+ var me = this;
+ if (me.savedItems[cardid]) {
+ var curcard = me.getLayout().getActiveItem();
+ var newcard = me.add(me.savedItems[cardid]);
+ if (curcard) {
+ me.setActiveItem(cardid);
+ me.remove(curcard, true);
+
+ // trigger state change
+
+ var ncard = cardid;
+ // Note: '' is alias for first tab.
+ // First tab can be 'search' or something else
+ if (cardid === me.firstItem) {
+ ncard = '';
+ }
+ if (me.hstateid) {
+ me.sp.set(me.hstateid, { value: ncard });
+ }
+ }
+ }
+ },
+
initComponent: function() {
var me = this;
var stateid = me.hstateid;
- var sp = Ext.state.Manager.getProvider();
+ me.sp = Ext.state.Manager.getProvider();
var activeTab; // leaving this undefined means items[0] will be the default tab
- var hsregex = /^([^\-\s]+)(-\S+)?$/;
-
if (stateid) {
- var state = sp.get(stateid);
+ var state = me.sp.get(stateid);
if (state && state.value) {
- var res = hsregex.exec(state.value);
- if (res && res[1] && Ext.isArray(me.items)) {
- me.items.forEach(function(item) {
- if (item.itemId === res[1]) {
- activeTab = res[1];
- }
- });
- } else if (res && res[1] && me.items && me.items.itemId === res[1]) {
- activeTab = res[1];
- }
+ // if this tab does not exists, it chooses the first
+ activeTab = state.value;
}
}
- var items = me.items || [];
- me.items = undefined;
+ // get title
+ var title = me.title || me.pveSelNode.data.text;
+ me.title = undefined;
+ // create toolbar
var tbar = me.tbar || [];
me.tbar = undefined;
- var title = me.title || me.pveSelNode.data.text;
- me.title = undefined;
-
tbar.unshift('->');
tbar.unshift({
xtype: 'tbtext',
@@ -51,92 +164,115 @@ Ext.define('PVE.panel.Config', {
baseCls: 'x-panel-header-text'
});
+ me.dockedItems[1].items = tbar;
+ // include search tab
+ me.items = me.items || [];
if (me.showSearch) {
- items.unshift({
+ me.items.unshift({
itemId: 'search',
title: gettext('Search'),
- layout: { type:'fit' },
- plugins: [{
- ptype: 'lazyitems',
- items: [{
- xtype: 'pveResourceGrid',
- pveSelNode: me.pveSelNode
- }]
- }]
+ iconCls: 'fa fa-search',
+ xtype: 'pveResourceGrid',
+ pveSelNode: me.pveSelNode
});
}
- var toolbar = Ext.create('Ext.toolbar.Toolbar', {
- items: tbar,
- border: false,
- height: 36
- });
+ me.savedItems = {};
+ /*jslint confusion:true*/
+ if (me.items[0]) {
+ me.firstItem = me.items[0].itemId;
+ }
+ /*jslint confusion:false*/
- var tab = Ext.create('Ext.tab.Panel', {
- flex: 1,
- border: true,
- activeTab: activeTab,
- defaults: Ext.apply(me.defaults || {}, {
- pveSelNode: me.pveSelNode,
- viewFilter: me.viewFilter,
- workspace: me.workspace,
- border: false
- }),
- items: items,
- listeners: {
- tabchange: function(tp, newcard, oldcard) {
- var ntab = newcard.itemId;
+ me.store = Ext.create('Ext.data.TreeStore', {
+ root: {
+ expanded: true
+ }
+ });
+ var root = me.store.getRoot();
+ me.items.forEach(function(item){
+ var treeitem = Ext.create('Ext.data.TreeModel',{
+ id: item.itemId,
+ text: item.title,
+ iconCls: item.iconCls,
+ leaf: true,
+ expanded: item.expandedOnInit
+ });
+ item.header = false;
+ if (me.savedItems[item.itemId] !== undefined) {
+ throw "itemId already exists, please use another";
+ }
+ me.savedItems[item.itemId] = item;
- // Note: '' is alias for first tab.
- // First tab can be 'search' or something else
- if (newcard.itemId === items[0].itemId) {
- ntab = '';
- }
- if (stateid) {
- if (newcard.phstateid) {
- sp.set(newcard.phstateid, newcard.getHState());
- } else {
- sp.set(stateid, { value: ntab });
- }
- }
+ var group;
+ var curnode = root;
- // if we have a tabpanel which we declared lazy (with ptype: lazyitems)
- // then we have the actual item in items.items[0]
- // and there we need to fire the event hide
- // because some tabs use this event (which is not fired in this case)
- if (oldcard.plugins && oldcard.plugins[0] && oldcard.plugins[0].ptype == 'lazyitems') {
- oldcard.items.items[0].fireEvent('hide');
- }
+ // get/create the group items
+ while (Ext.isArray(item.groups) && item.groups.length > 0) {
+ group = item.groups.shift();
- // same for activating
- if (newcard.plugins && newcard.plugins[0] && newcard.plugins[0].ptype == 'lazyitems') {
- newcard.items.items[0].fireEvent('activate');
- }
+ var child = curnode.findChild('id', group);
+ if (child === null) {
+ // did not find the group item
+ // so add it where we are
+ break;
}
+ curnode = child;
+ }
+
+ // insert the item
+
+ // lets see if it already exists
+ var node = curnode.findChild('id', item.itemId);
+
+ if (node === null) {
+ curnode.appendChild(treeitem);
+ } else {
+ // should not happen!
+ throw "id already exists";
}
});
- Ext.apply(me, {
- layout: { type: 'vbox', align: 'stretch' },
- items: [ toolbar, tab]
+ delete me.items;
+ me.defaults = me.defaults || {};
+ Ext.apply(me.defaults, {
+ pveSelNode: me.pveSelNode,
+ viewFilter: me.viewFilter,
+ workspace: me.workspace,
+ border: 0
});
me.callParent();
+ var menu = me.down('#menu');
+ var selection = root.findChild('id', activeTab, true) || root.firstChild;
+ var node = selection;
+ while (node !== root) {
+ node.expand();
+ node = node.parentNode;
+ }
+ menu.setStore(me.store);
+ menu.setSelection(selection);
+
+ // on a state change,
+ // select the new item
var statechange = function(sp, key, state) {
+ // it the state change is for this panel
if (stateid && (key === stateid) && state) {
- var atab = tab.getActiveTab().itemId;
- var res = hsregex.exec(state.value);
- var ntab = (res && res[1]) ? res[1] : items[0].itemId;
- if (ntab && (atab != ntab)) {
- tab.setActiveTab(ntab);
+ // get active item
+ var acard = me.getLayout().getActiveItem().itemId;
+ // get the itemid of the new value
+ var ncard = state.value || me.firstItem;
+ if (ncard && (acard != ncard)) {
+ // select the chosen item
+ menu.setSelection(root.findChild('id', ncard, true) || root.firstChild);
}
}
};
if (stateid) {
- me.mon(sp, 'statechange', statechange);
+ me.mon(me.sp, 'statechange', statechange);
}
}
});
--
2.1.4
More information about the pve-devel
mailing list