Ext.define('PVE.form.SizeField', { extend: 'Ext.form.FieldContainer', alias: 'widget.pveSizeField', mixins: ['Proxmox.Mixin.CBind'], viewModel: { data: { unit: 'MiB', unitPostfix: '', }, formulas: { unitlabel: (get) => get('unit') + get('unitPostfix'), }, }, emptyText: '', layout: 'hbox', defaults: { hideLabel: true, }, units: { 'B': 1, 'KiB': 1024, 'MiB': 1024*1024, 'GiB': 1024*1024*1024, 'TiB': 1024*1024*1024*1024, 'KB': 1000, 'MB': 1000*1000, 'GB': 1000*1000*1000, 'TB': 1000*1000*1000*1000, }, // display unit (TODO: make (optionally) selectable) unit: 'MiB', unitPostfix: '', // use this if the backend saves values in another unit tha bytes, e.g., // for KiB set it to 'KiB' backendUnit: undefined, // allow setting 0 and using it as a submit value allowZero: false, emptyValue: null, items: [ { xtype: 'numberfield', cbind: { name: '{name}', emptyText: '{emptyText}', allowZero: '{allowZero}', emptyValue: '{emptyValue}', }, minValue: 0, step: 1, submitLocaleSeparator: false, fieldStyle: 'text-align: right', flex: 1, enableKeyEvents: true, setValue: function(v) { if (!this._transformed) { let fieldContainer = this.up('fieldcontainer'); let vm = fieldContainer.getViewModel(); let unit = vm.get('unit'); v /= fieldContainer.units[unit]; v *= fieldContainer.backendFactor; this._transformed = true; } if (Number(v) === 0 && !this.allowZero) { v = undefined; } return Ext.form.field.Text.prototype.setValue.call(this, v); }, getSubmitValue: function() { let v = this.processRawValue(this.getRawValue()); v = v.replace(this.decimalSeparator, '.'); if (v === undefined || v === '') { return this.emptyValue; } if (Number(v) === 0) { return this.allowZero ? 0 : null; } let fieldContainer = this.up('fieldcontainer'); let vm = fieldContainer.getViewModel(); let unit = vm.get('unit'); v = parseFloat(v) * fieldContainer.units[unit]; v /= fieldContainer.backendFactor; return String(Math.floor(v)); }, listeners: { // our setValue gets only called if we have a value, avoid // transformation of the first user-entered value keydown: function() { this._transformed = true; }, }, }, { xtype: 'displayfield', name: 'unit', submitValue: false, padding: '0 0 0 10', bind: { value: '{unitlabel}', }, listeners: { change: (f, v) => { f.originalValue = v; }, }, width: 40, }, ], initComponent: function() { let me = this; me.unit = me.unit || 'MiB'; if (!(me.unit in me.units)) { throw "unknown unit: " + me.unit; } me.backendFactor = 1; if (me.backendUnit !== undefined) { if (!(me.unit in me.units)) { throw "unknown backend unit: " + me.backendUnit; } me.backendFactor = me.units[me.backendUnit]; } me.callParent(arguments); me.getViewModel().set('unit', me.unit); me.getViewModel().set('unitPostfix', me.unitPostfix); }, }); Ext.define('PVE.form.BandwidthField', { extend: 'PVE.form.SizeField', alias: 'widget.pveBandwidthField', unitPostfix: '/s', });