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