]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/panel/ConfigPanel.js
ui: panel/ConfigPanel: change ui class to pve-nav
[pve-manager.git] / www / manager6 / panel / ConfigPanel.js
1 /*
2 * Base class for all the multitab config panels
3 *
4 * How to use this:
5 *
6 * You create a subclass of this, and then define your wanted tabs
7 * as items like this:
8 *
9 * items: [{
10 * title: "myTitle",
11 * xytpe: "somextype",
12 * iconCls: 'fa fa-icon',
13 * groups: ['somegroup'],
14 * expandedOnInit: true,
15 * itemId: 'someId'
16 * }]
17 *
18 * this has to be in the declarative syntax, else we
19 * cannot save them for later
20 * (so no Ext.create or Ext.apply of an item in the subclass)
21 *
22 * the groups array expects the itemids of the items
23 * which are the parents, which have to come before they
24 * are used
25 *
26 * if you want following the tree:
27 *
28 * Option1
29 * Option2
30 * -> SubOption1
31 * -> SubSubOption1
32 *
33 * the suboption1 group array has to look like this:
34 * groups: ['itemid-of-option2']
35 *
36 * and of subsuboption1:
37 * groups: ['itemid-of-option2', 'itemid-of-suboption1']
38 *
39 * setting the expandedOnInit determines if the item/group is expanded
40 * initially (false by default)
41 */
42 Ext.define('PVE.panel.Config', {
43 extend: 'Ext.panel.Panel',
44 alias: 'widget.pvePanelConfig',
45
46 showSearch: true, // add a resource grid with a search button as first tab
47 viewFilter: undefined, // a filter to pass to that resource grid
48
49 tbarSpacing: true, // if true, adds a spacer after the title in tbar
50
51 dockedItems: [{
52 // this is needed for the overflow handler
53 xtype: 'toolbar',
54 overflowHandler: 'scroller',
55 dock: 'left',
56 style: {
57 padding: 0,
58 margin: 0,
59 },
60 cls: 'pve-toolbar-bg',
61 items: {
62 xtype: 'treelist',
63 itemId: 'menu',
64 ui: 'pve-nav',
65 expanderOnly: true,
66 expanderFirst: false,
67 animation: false,
68 singleExpand: false,
69 listeners: {
70 selectionchange: function(treeList, selection) {
71 let view = this.up('panel');
72 view.suspendLayout = true;
73 view.activateCard(selection.data.id);
74 view.suspendLayout = false;
75 view.updateLayout();
76 },
77 itemclick: function(treelist, info) {
78 var olditem = treelist.getSelection();
79 var newitem = info.node;
80
81 // when clicking on the expand arrow, we don't select items, but still want the original behaviour
82 if (info.select === false) {
83 return;
84 }
85
86 // click on a different, open item then leave it open, else toggle the clicked item
87 if (olditem.data.id !== newitem.data.id &&
88 newitem.data.expanded === true) {
89 info.toggle = false;
90 } else {
91 info.toggle = true;
92 }
93 },
94 },
95 },
96 },
97 {
98 xtype: 'toolbar',
99 itemId: 'toolbar',
100 dock: 'top',
101 height: 36,
102 overflowHandler: 'scroller',
103 }],
104
105 firstItem: '',
106 layout: 'card',
107 border: 0,
108
109 // used for automated test
110 selectById: function(cardid) {
111 var me = this;
112
113 var root = me.store.getRoot();
114 var selection = root.findChild('id', cardid, true);
115
116 if (selection) {
117 selection.expand();
118 var menu = me.down('#menu');
119 menu.setSelection(selection);
120 return cardid;
121 }
122 return '';
123 },
124
125 activateCard: function(cardid) {
126 var me = this;
127 if (me.savedItems[cardid]) {
128 var curcard = me.getLayout().getActiveItem();
129 var newcard = me.add(me.savedItems[cardid]);
130 me.helpButton.setOnlineHelp(newcard.onlineHelp || me.onlineHelp);
131 if (curcard) {
132 me.setActiveItem(cardid);
133 me.remove(curcard, true);
134
135 // trigger state change
136
137 var ncard = cardid;
138 // Note: '' is alias for first tab.
139 // First tab can be 'search' or something else
140 if (cardid === me.firstItem) {
141 ncard = '';
142 }
143 if (me.hstateid) {
144 me.sp.set(me.hstateid, { value: ncard });
145 }
146 }
147 }
148 },
149
150 initComponent: function() {
151 var me = this;
152
153 var stateid = me.hstateid;
154
155 me.sp = Ext.state.Manager.getProvider();
156
157 var activeTab; // leaving this undefined means items[0] will be the default tab
158
159 if (stateid) {
160 let state = me.sp.get(stateid);
161 if (state && state.value) {
162 // if this tab does not exist, it chooses the first
163 activeTab = state.value;
164 }
165 }
166
167 // get title
168 var title = me.title || me.pveSelNode.data.text;
169 me.title = undefined;
170
171 // create toolbar
172 var tbar = me.tbar || [];
173 me.tbar = undefined;
174
175 if (!me.onlineHelp) {
176 switch (me.pveSelNode.data.id) {
177 case 'type/storage': me.onlineHelp = 'chapter-pvesm.html'; break;
178 case 'type/qemu': me.onlineHelp = 'chapter-qm.html'; break;
179 case 'type/lxc': me.onlineHelp = 'chapter-pct.html'; break;
180 case 'type/pool': me.onlineHelp = 'chapter-pveum.html#_pools'; break;
181 case 'type/node': me.onlineHelp = 'chapter-sysadmin.html'; break;
182 }
183 }
184
185 if (me.tbarSpacing) {
186 tbar.unshift('->');
187 }
188 tbar.unshift({
189 xtype: 'tbtext',
190 text: title,
191 baseCls: 'x-panel-header-text',
192 });
193
194 me.helpButton = Ext.create('Proxmox.button.Help', {
195 hidden: false,
196 listenToGlobalEvent: false,
197 onlineHelp: me.onlineHelp || undefined,
198 });
199
200 tbar.push(me.helpButton);
201
202 me.dockedItems[1].items = tbar;
203
204 // include search tab
205 me.items = me.items || [];
206 if (me.showSearch) {
207 me.items.unshift({
208 itemId: 'search',
209 title: gettext('Search'),
210 iconCls: 'fa fa-search',
211 xtype: 'pveResourceGrid',
212 pveSelNode: me.pveSelNode,
213 });
214 }
215
216 me.savedItems = {};
217 if (me.items[0]) {
218 me.firstItem = me.items[0].itemId;
219 }
220
221 me.store = Ext.create('Ext.data.TreeStore', {
222 root: {
223 expanded: true,
224 },
225 });
226 var root = me.store.getRoot();
227 me.insertNodes(me.items);
228
229 delete me.items;
230 me.defaults = me.defaults || {};
231 Ext.apply(me.defaults, {
232 pveSelNode: me.pveSelNode,
233 viewFilter: me.viewFilter,
234 workspace: me.workspace,
235 border: 0,
236 });
237
238 me.callParent();
239
240 var menu = me.down('#menu');
241 var selection = root.findChild('id', activeTab, true) || root.firstChild;
242 var node = selection;
243 while (node !== root) {
244 node.expand();
245 node = node.parentNode;
246 }
247 menu.setStore(me.store);
248 menu.setSelection(selection);
249
250 // on a state change,
251 // select the new item
252 var statechange = function(sp, key, state) {
253 // it the state change is for this panel
254 if (stateid && key === stateid && state) {
255 // get active item
256 var acard = me.getLayout().getActiveItem().itemId;
257 // get the itemid of the new value
258 var ncard = state.value || me.firstItem;
259 if (ncard && acard !== ncard) {
260 // select the chosen item
261 menu.setSelection(root.findChild('id', ncard, true) || root.firstChild);
262 }
263 }
264 };
265
266 if (stateid) {
267 me.mon(me.sp, 'statechange', statechange);
268 }
269 },
270
271 insertNodes: function(items) {
272 var me = this;
273 var root = me.store.getRoot();
274
275 items.forEach(function(item) {
276 var treeitem = Ext.create('Ext.data.TreeModel', {
277 id: item.itemId,
278 text: item.title,
279 iconCls: item.iconCls,
280 leaf: true,
281 expanded: item.expandedOnInit,
282 });
283 item.header = false;
284 if (me.savedItems[item.itemId] !== undefined) {
285 throw "itemId already exists, please use another";
286 }
287 me.savedItems[item.itemId] = item;
288
289 var group;
290 var curnode = root;
291
292 // get/create the group items
293 while (Ext.isArray(item.groups) && item.groups.length > 0) {
294 group = item.groups.shift();
295
296 var child = curnode.findChild('id', group);
297 if (child === null) {
298 // did not find the group item
299 // so add it where we are
300 break;
301 }
302 curnode = child;
303 }
304
305 // insert the item
306
307 // lets see if it already exists
308 var node = curnode.findChild('id', item.itemId);
309
310 if (node === null) {
311 curnode.appendChild(treeitem);
312 } else {
313 // should not happen!
314 throw "id already exists";
315 }
316 });
317 },
318 });