]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/grid/ObjectGrid.js
object grid: improve/add to documentation
[proxmox-widget-toolkit.git] / src / grid / ObjectGrid.js
1 /** Renders a list of key values objets
2
3 Mandatory Config Parameters:
4
5 rows: an object container where each propery is a key-value object we want to render
6
7 rows: {
8 keyboard: {
9 header: gettext('Keyboard Layout'),
10 editor: 'Your.KeyboardEdit',
11 required: true
12 },
13 // ...
14 },
15
16 Convenience Helper:
17
18 As alternative you can use the common add-row helper like `add_text_row`, but you need to
19 call it in an overridden initComponent before `me.callParent(arguments)` gets executed.
20
21 For a declarative approach you can use the `gridRows` configuration to pass an array of
22 objects with each having at least a `xtype` to match `add_XTYPE_row` and a field-name
23 property, for example:
24
25 gridRows: [
26 {
27 xtype: 'text',
28 name: 'http-proxy',
29 text: gettext('HTTP proxy'),
30 defaultValue: Proxmox.Utils.noneText,
31 vtype: 'HttpProxy',
32 deleteEmpty: true,
33 },
34 ],
35
36 Optional Configs:
37
38 disabled:: setting this parameter to true will disable selection and focus on
39 the proxmoxObjectGrid as well as greying out input elements. Useful for a
40 readonly tabular display
41
42 */
43 Ext.define('Proxmox.grid.ObjectGrid', {
44 extend: 'Ext.grid.GridPanel',
45 alias: ['widget.proxmoxObjectGrid'],
46
47 // can be used as declarative replacement over manually calling the add_XYZ_row helpers,
48 // see top-level doc-comment above for details/example
49 gridRows: [],
50
51 disabled: false,
52 hideHeaders: true,
53
54 monStoreErrors: false,
55
56 add_combobox_row: function(name, text, opts) {
57 let me = this;
58
59 opts = opts || {};
60 me.rows = me.rows || {};
61
62 me.rows[name] = {
63 required: true,
64 defaultValue: opts.defaultValue,
65 header: text,
66 renderer: opts.renderer,
67 editor: {
68 xtype: 'proxmoxWindowEdit',
69 subject: text,
70 onlineHelp: opts.onlineHelp,
71 fieldDefaults: {
72 labelWidth: opts.labelWidth || 100,
73 },
74 items: {
75 xtype: 'proxmoxKVComboBox',
76 name: name,
77 comboItems: opts.comboItems,
78 value: opts.defaultValue,
79 deleteEmpty: !!opts.deleteEmpty,
80 emptyText: opts.defaultValue,
81 labelWidth: Proxmox.Utils.compute_min_label_width(
82 text, opts.labelWidth),
83 fieldLabel: text,
84 },
85 },
86 };
87 },
88
89 add_text_row: function(name, text, opts) {
90 let me = this;
91
92 opts = opts || {};
93 me.rows = me.rows || {};
94
95 me.rows[name] = {
96 required: true,
97 defaultValue: opts.defaultValue,
98 header: text,
99 renderer: opts.renderer,
100 editor: {
101 xtype: 'proxmoxWindowEdit',
102 subject: text,
103 onlineHelp: opts.onlineHelp,
104 fieldDefaults: {
105 labelWidth: opts.labelWidth || 100,
106 },
107 items: {
108 xtype: 'proxmoxtextfield',
109 name: name,
110 deleteEmpty: !!opts.deleteEmpty,
111 emptyText: opts.defaultValue,
112 labelWidth: Proxmox.Utils.compute_min_label_width(
113 text, opts.labelWidth),
114 vtype: opts.vtype,
115 fieldLabel: text,
116 },
117 },
118 };
119 },
120
121 add_boolean_row: function(name, text, opts) {
122 let me = this;
123
124 opts = opts || {};
125 me.rows = me.rows || {};
126
127 me.rows[name] = {
128 required: true,
129 defaultValue: opts.defaultValue || 0,
130 header: text,
131 renderer: opts.renderer || Proxmox.Utils.format_boolean,
132 editor: {
133 xtype: 'proxmoxWindowEdit',
134 subject: text,
135 onlineHelp: opts.onlineHelp,
136 fieldDefaults: {
137 labelWidth: opts.labelWidth || 100,
138 },
139 items: {
140 xtype: 'proxmoxcheckbox',
141 name: name,
142 uncheckedValue: 0,
143 defaultValue: opts.defaultValue || 0,
144 checked: !!opts.defaultValue,
145 deleteDefaultValue: !!opts.deleteDefaultValue,
146 labelWidth: Proxmox.Utils.compute_min_label_width(
147 text, opts.labelWidth),
148 fieldLabel: text,
149 },
150 },
151 };
152 },
153
154 add_integer_row: function(name, text, opts) {
155 let me = this;
156
157 opts = opts || {};
158 me.rows = me.rows || {};
159
160 me.rows[name] = {
161 required: true,
162 defaultValue: opts.defaultValue,
163 header: text,
164 renderer: opts.renderer,
165 editor: {
166 xtype: 'proxmoxWindowEdit',
167 subject: text,
168 onlineHelp: opts.onlineHelp,
169 fieldDefaults: {
170 labelWidth: opts.labelWidth || 100,
171 },
172 items: {
173 xtype: 'proxmoxintegerfield',
174 name: name,
175 minValue: opts.minValue,
176 maxValue: opts.maxValue,
177 emptyText: gettext('Default'),
178 deleteEmpty: !!opts.deleteEmpty,
179 value: opts.defaultValue,
180 labelWidth: Proxmox.Utils.compute_min_label_width(
181 text, opts.labelWidth),
182 fieldLabel: text,
183 },
184 },
185 };
186 },
187
188 editorConfig: {}, // default config passed to editor
189
190 run_editor: function() {
191 let me = this;
192
193 let sm = me.getSelectionModel();
194 let rec = sm.getSelection()[0];
195 if (!rec) {
196 return;
197 }
198
199 let rows = me.rows;
200 let rowdef = rows[rec.data.key];
201 if (!rowdef.editor) {
202 return;
203 }
204
205 let win;
206 let config;
207 if (Ext.isString(rowdef.editor)) {
208 config = Ext.apply({
209 confid: rec.data.key,
210 }, me.editorConfig);
211 win = Ext.create(rowdef.editor, config);
212 } else {
213 config = Ext.apply({
214 confid: rec.data.key,
215 }, me.editorConfig);
216 Ext.apply(config, rowdef.editor);
217 win = Ext.createWidget(rowdef.editor.xtype, config);
218 win.load();
219 }
220
221 win.show();
222 win.on('destroy', me.reload, me);
223 },
224
225 reload: function() {
226 let me = this;
227 me.rstore.load();
228 },
229
230 getObjectValue: function(key, defaultValue) {
231 let me = this;
232 let rec = me.store.getById(key);
233 if (rec) {
234 return rec.data.value;
235 }
236 return defaultValue;
237 },
238
239 renderKey: function(key, metaData, record, rowIndex, colIndex, store) {
240 let me = this;
241 let rows = me.rows;
242 let rowdef = rows && rows[key] ? rows[key] : {};
243 return rowdef.header || key;
244 },
245
246 renderValue: function(value, metaData, record, rowIndex, colIndex, store) {
247 let me = this;
248 let rows = me.rows;
249 let key = record.data.key;
250 let rowdef = rows && rows[key] ? rows[key] : {};
251
252 let renderer = rowdef.renderer;
253 if (renderer) {
254 return renderer(value, metaData, record, rowIndex, colIndex, store);
255 }
256
257 return value;
258 },
259
260 listeners: {
261 itemkeydown: function(view, record, item, index, e) {
262 if (e.getKey() === e.ENTER) {
263 this.pressedIndex = index;
264 }
265 },
266 itemkeyup: function(view, record, item, index, e) {
267 if (e.getKey() === e.ENTER && index === this.pressedIndex) {
268 this.run_editor();
269 }
270
271 this.pressedIndex = undefined;
272 },
273 },
274
275 initComponent: function() {
276 let me = this;
277
278 for (const rowdef of me.gridRows || []) {
279 let addFn = me[`add_${rowdef.xtype}_row`];
280 if (typeof addFn !== 'function') {
281 throw `unknown object-grid row xtype '${rowdef.xtype}'`;
282 } else if (typeof rowdef.name !== 'string') {
283 throw `object-grid row need a valid name string-property!`;
284 } else {
285 addFn.call(me, rowdef.name, rowdef.text || rowdef.name, rowdef);
286 }
287 }
288
289 let rows = me.rows;
290
291 if (!me.rstore) {
292 if (!me.url) {
293 throw "no url specified";
294 }
295
296 me.rstore = Ext.create('Proxmox.data.ObjectStore', {
297 url: me.url,
298 interval: me.interval,
299 extraParams: me.extraParams,
300 rows: me.rows,
301 });
302 }
303
304 let rstore = me.rstore;
305 let store = Ext.create('Proxmox.data.DiffStore', {
306 rstore: rstore,
307 sorters: [],
308 filters: [],
309 });
310
311 if (rows) {
312 for (const [key, rowdef] of Object.entries(rows)) {
313 if (Ext.isDefined(rowdef.defaultValue)) {
314 store.add({ key: key, value: rowdef.defaultValue });
315 } else if (rowdef.required) {
316 store.add({ key: key, value: undefined });
317 }
318 }
319 }
320
321 if (me.sorterFn) {
322 store.sorters.add(Ext.create('Ext.util.Sorter', {
323 sorterFn: me.sorterFn,
324 }));
325 }
326
327 store.filters.add(Ext.create('Ext.util.Filter', {
328 filterFn: function(item) {
329 if (rows) {
330 let rowdef = rows[item.data.key];
331 if (!rowdef || rowdef.visible === false) {
332 return false;
333 }
334 }
335 return true;
336 },
337 }));
338
339 Proxmox.Utils.monStoreErrors(me, rstore);
340
341 Ext.applyIf(me, {
342 store: store,
343 stateful: false,
344 columns: [
345 {
346 header: gettext('Name'),
347 width: me.cwidth1 || 200,
348 dataIndex: 'key',
349 renderer: me.renderKey,
350 },
351 {
352 flex: 1,
353 header: gettext('Value'),
354 dataIndex: 'value',
355 renderer: me.renderValue,
356 },
357 ],
358 });
359
360 me.callParent();
361
362 if (me.monStoreErrors) {
363 Proxmox.Utils.monStoreErrors(me, me.store);
364 }
365 },
366 });