mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-08-05 07:06:00 +00:00
use ComboGrid from proxmox-widget-toolkit
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> Reviewed-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
parent
411967db91
commit
0fc95a12ee
@ -31,7 +31,6 @@ JSSRC= \
|
||||
form/TextField.js \
|
||||
form/IntegerField.js \
|
||||
form/RRDTypeSelector.js \
|
||||
form/ComboGrid.js \
|
||||
form/KVComboBox.js \
|
||||
form/Boolean.js \
|
||||
form/CompressionSelector.js \
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.CephDiskSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveCephDiskSelector'],
|
||||
|
||||
diskType: 'journal_disks',
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.BridgeSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.PVE.form.BridgeSelector'],
|
||||
|
||||
bridgeType: 'any_bridge', // bridge, OVSBridge or any_bridge
|
||||
|
@ -1,353 +0,0 @@
|
||||
/*
|
||||
* ComboGrid component: a ComboBox where the dropdown menu (the
|
||||
* "Picker") is a Grid with Rows and Columns expects a listConfig
|
||||
* object with a columns property roughly based on the GridPicker from
|
||||
* https://www.sencha.com/forum/showthread.php?299909
|
||||
*
|
||||
*/
|
||||
|
||||
Ext.define('PVE.form.ComboGrid', {
|
||||
extend: 'Ext.form.field.ComboBox',
|
||||
alias: ['widget.PVE.form.ComboGrid'],
|
||||
|
||||
// this value is used as default value after load()
|
||||
preferredValue: undefined,
|
||||
|
||||
// hack: allow to select empty value
|
||||
// seems extjs does not allow that when 'editable == false'
|
||||
onKeyUp: function(e, t) {
|
||||
var me = this;
|
||||
var key = e.getKey();
|
||||
|
||||
if (!me.editable && me.allowBlank && !me.multiSelect &&
|
||||
(key == e.BACKSPACE || key == e.DELETE)) {
|
||||
me.setValue('');
|
||||
}
|
||||
|
||||
me.callParent(arguments);
|
||||
},
|
||||
|
||||
// needed to trigger onKeyUp etc.
|
||||
enableKeyEvents: true,
|
||||
|
||||
editable: false,
|
||||
|
||||
// override ExtJS method
|
||||
// if the field has multiSelect enabled, the store is not loaded, and
|
||||
// the displayfield == valuefield, it saves the rawvalue as an array
|
||||
// but the getRawValue method is only defined in the textfield class
|
||||
// (which has not to deal with arrays) an returns the string in the
|
||||
// field (not an array)
|
||||
//
|
||||
// so if we have multiselect enabled, return the rawValue (which
|
||||
// should be an array) and else we do callParent so
|
||||
// it should not impact any other use of the class
|
||||
getRawValue: function() {
|
||||
var me = this;
|
||||
if (me.multiSelect) {
|
||||
return me.rawValue;
|
||||
} else {
|
||||
return me.callParent();
|
||||
}
|
||||
},
|
||||
|
||||
// override ExtJS protected method
|
||||
onBindStore: function(store, initial) {
|
||||
var me = this,
|
||||
picker = me.picker,
|
||||
extraKeySpec,
|
||||
valueCollectionConfig;
|
||||
|
||||
// We're being bound, not unbound...
|
||||
if (store) {
|
||||
// If store was created from a 2 dimensional array with generated field names 'field1' and 'field2'
|
||||
if (store.autoCreated) {
|
||||
me.queryMode = 'local';
|
||||
me.valueField = me.displayField = 'field1';
|
||||
if (!store.expanded) {
|
||||
me.displayField = 'field2';
|
||||
}
|
||||
|
||||
// displayTpl config will need regenerating with the autogenerated displayField name 'field1'
|
||||
me.setDisplayTpl(null);
|
||||
}
|
||||
if (!Ext.isDefined(me.valueField)) {
|
||||
me.valueField = me.displayField;
|
||||
}
|
||||
|
||||
// Add a byValue index to the store so that we can efficiently look up records by the value field
|
||||
// when setValue passes string value(s).
|
||||
// The two indices (Ext.util.CollectionKeys) are configured unique: false, so that if duplicate keys
|
||||
// are found, they are all returned by the get call.
|
||||
// This is so that findByText and findByValue are able to return the *FIRST* matching value. By default,
|
||||
// if unique is true, CollectionKey keeps the *last* matching value.
|
||||
extraKeySpec = {
|
||||
byValue: {
|
||||
rootProperty: 'data',
|
||||
unique: false
|
||||
}
|
||||
};
|
||||
extraKeySpec.byValue.property = me.valueField;
|
||||
store.setExtraKeys(extraKeySpec);
|
||||
|
||||
if (me.displayField === me.valueField) {
|
||||
store.byText = store.byValue;
|
||||
} else {
|
||||
extraKeySpec.byText = {
|
||||
rootProperty: 'data',
|
||||
unique: false
|
||||
};
|
||||
extraKeySpec.byText.property = me.displayField;
|
||||
store.setExtraKeys(extraKeySpec);
|
||||
}
|
||||
|
||||
// We hold a collection of the values which have been selected, keyed by this field's valueField.
|
||||
// This collection also functions as the selected items collection for the BoundList's selection model
|
||||
valueCollectionConfig = {
|
||||
rootProperty: 'data',
|
||||
extraKeys: {
|
||||
byInternalId: {
|
||||
property: 'internalId'
|
||||
},
|
||||
byValue: {
|
||||
property: me.valueField,
|
||||
rootProperty: 'data'
|
||||
}
|
||||
},
|
||||
// Whenever this collection is changed by anyone, whether by this field adding to it,
|
||||
// or the BoundList operating, we must refresh our value.
|
||||
listeners: {
|
||||
beginupdate: me.onValueCollectionBeginUpdate,
|
||||
endupdate: me.onValueCollectionEndUpdate,
|
||||
scope: me
|
||||
}
|
||||
};
|
||||
|
||||
// This becomes our collection of selected records for the Field.
|
||||
me.valueCollection = new Ext.util.Collection(valueCollectionConfig);
|
||||
|
||||
// We use the selected Collection as our value collection and the basis
|
||||
// for rendering the tag list.
|
||||
|
||||
//pve override: since the picker is represented by a grid panel,
|
||||
// we changed here the selection to RowModel
|
||||
me.pickerSelectionModel = new Ext.selection.RowModel({
|
||||
mode: me.multiSelect ? 'SIMPLE' : 'SINGLE',
|
||||
// There are situations when a row is selected on mousedown but then the mouse is dragged to another row
|
||||
// and released. In these situations, the event target for the click event won't be the row where the mouse
|
||||
// was released but the boundview. The view will then determine that it should fire a container click, and
|
||||
// the DataViewModel will then deselect all prior selections. Setting `deselectOnContainerClick` here will
|
||||
// prevent the model from deselecting.
|
||||
deselectOnContainerClick: false,
|
||||
enableInitialSelection: false,
|
||||
pruneRemoved: false,
|
||||
selected: me.valueCollection,
|
||||
store: store,
|
||||
listeners: {
|
||||
scope: me,
|
||||
lastselectedchanged: me.updateBindSelection
|
||||
}
|
||||
});
|
||||
|
||||
if (!initial) {
|
||||
me.resetToDefault();
|
||||
}
|
||||
|
||||
if (picker) {
|
||||
picker.setSelectionModel(me.pickerSelectionModel);
|
||||
if (picker.getStore() !== store) {
|
||||
picker.bindStore(store);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// copied from ComboBox
|
||||
createPicker: function() {
|
||||
var me = this;
|
||||
var picker;
|
||||
|
||||
var pickerCfg = Ext.apply({
|
||||
// pve overrides: display a grid for selection
|
||||
xtype: 'gridpanel',
|
||||
id: me.pickerId,
|
||||
pickerField: me,
|
||||
floating: true,
|
||||
hidden: true,
|
||||
store: me.store,
|
||||
displayField: me.displayField,
|
||||
preserveScrollOnRefresh: true,
|
||||
pageSize: me.pageSize,
|
||||
tpl: me.tpl,
|
||||
selModel: me.pickerSelectionModel,
|
||||
focusOnToFront: false
|
||||
}, me.listConfig, me.defaultListConfig);
|
||||
|
||||
picker = me.picker || Ext.widget(pickerCfg);
|
||||
|
||||
if (picker.getStore() !== me.store) {
|
||||
picker.bindStore(me.store);
|
||||
}
|
||||
|
||||
if (me.pageSize) {
|
||||
picker.pagingToolbar.on('beforechange', me.onPageChange, me);
|
||||
}
|
||||
|
||||
// pve overrides: pass missing method in gridPanel to its view
|
||||
picker.refresh = function() {
|
||||
picker.getSelectionModel().select(me.valueCollection.getRange());
|
||||
picker.getView().refresh();
|
||||
};
|
||||
picker.getNodeByRecord = function() {
|
||||
picker.getView().getNodeByRecord(arguments);
|
||||
};
|
||||
|
||||
// We limit the height of the picker to fit in the space above
|
||||
// or below this field unless the picker has its own ideas about that.
|
||||
if (!picker.initialConfig.maxHeight) {
|
||||
picker.on({
|
||||
beforeshow: me.onBeforePickerShow,
|
||||
scope: me
|
||||
});
|
||||
}
|
||||
picker.getSelectionModel().on({
|
||||
beforeselect: me.onBeforeSelect,
|
||||
beforedeselect: me.onBeforeDeselect,
|
||||
focuschange: me.onFocusChange,
|
||||
selectionChange: function (sm, selectedRecords) {
|
||||
var me = this;
|
||||
if (selectedRecords.length) {
|
||||
me.setValue(selectedRecords);
|
||||
me.fireEvent('select', me, selectedRecords);
|
||||
}
|
||||
},
|
||||
scope: me
|
||||
});
|
||||
|
||||
// hack for extjs6
|
||||
// when the clicked item is the same as the previously selected,
|
||||
// it does not select the item
|
||||
// instead we hide the picker
|
||||
if (!me.multiSelect) {
|
||||
picker.on('itemclick', function (sm,record) {
|
||||
if (picker.getSelection()[0] === record) {
|
||||
picker.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// when our store is not yet loaded, we increase
|
||||
// the height of the gridpanel, so that we can see
|
||||
// the loading mask
|
||||
//
|
||||
// we save the minheight to reset it after the load
|
||||
picker.on('show', function() {
|
||||
if (me.enableLoadMask) {
|
||||
me.savedMinHeight = picker.getMinHeight();
|
||||
picker.setMinHeight(100);
|
||||
}
|
||||
});
|
||||
|
||||
picker.getNavigationModel().navigateOnSpace = false;
|
||||
|
||||
return picker;
|
||||
},
|
||||
|
||||
initComponent: function() {
|
||||
var me = this;
|
||||
|
||||
Ext.apply(me, {
|
||||
queryMode: 'local',
|
||||
matchFieldWidth: false
|
||||
});
|
||||
|
||||
Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug
|
||||
|
||||
Ext.applyIf(me.listConfig, { width: 400 });
|
||||
|
||||
me.callParent();
|
||||
|
||||
// Create the picker at an early stage, so it is available to store the previous selection
|
||||
if (!me.picker) {
|
||||
me.createPicker();
|
||||
}
|
||||
|
||||
if (me.editable) {
|
||||
// The trigger.picker causes first a focus event on the field then
|
||||
// toggles the selection picker. Thus skip expanding in this case,
|
||||
// else our focus listner expands and the picker.trigger then
|
||||
// collapses it directly afterwards.
|
||||
Ext.override(me.triggers.picker, {
|
||||
onMouseDown : function (e) {
|
||||
// copied "should we focus" check from Ext.form.trigger.Trigger
|
||||
if (e.pointerType !== 'touch' && !this.field.owns(Ext.Element.getActiveElement())) {
|
||||
me.skip_expand_on_focus = true;
|
||||
}
|
||||
this.callParent(arguments);
|
||||
}
|
||||
});
|
||||
|
||||
me.on("focus", function(me) {
|
||||
if (!me.isExpanded && !me.skip_expand_on_focus) {
|
||||
me.expand();
|
||||
}
|
||||
me.skip_expand_on_focus = false;
|
||||
});
|
||||
}
|
||||
|
||||
me.mon(me.store, 'beforeload', function() {
|
||||
if (!me.isDisabled()) {
|
||||
me.enableLoadMask = true;
|
||||
}
|
||||
});
|
||||
|
||||
// hack: autoSelect does not work
|
||||
me.mon(me.store, 'load', function(store, r, success, o) {
|
||||
if (success) {
|
||||
me.clearInvalid();
|
||||
|
||||
if (me.enableLoadMask) {
|
||||
delete me.enableLoadMask;
|
||||
|
||||
// if the picker exists,
|
||||
// we reset its minheight to the saved var/0
|
||||
// we have to update the layout, otherwise the height
|
||||
// gets not recalculated
|
||||
if (me.picker) {
|
||||
me.picker.setMinHeight(me.savedMinHeight || 0);
|
||||
delete me.savedMinHeight;
|
||||
me.picker.updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
var def = me.getValue() || me.preferredValue;
|
||||
if (def) {
|
||||
me.setValue(def, true); // sync with grid
|
||||
}
|
||||
var found = false;
|
||||
if (def) {
|
||||
if (Ext.isArray(def)) {
|
||||
Ext.Array.each(def, function(v) {
|
||||
if (store.findRecord(me.valueField, v)) {
|
||||
found = true;
|
||||
return false; // break
|
||||
}
|
||||
});
|
||||
} else {
|
||||
found = store.findRecord(me.valueField, def);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
var rec = me.store.first();
|
||||
if (me.autoSelect && rec && rec.data) {
|
||||
def = rec.data[me.valueField];
|
||||
me.setValue(def, true);
|
||||
} else {
|
||||
me.setValue(me.editable ? def : '', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.FileSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: 'widget.pveFileSelector',
|
||||
|
||||
editable: true,
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.GroupSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveGroupSelector'],
|
||||
|
||||
allowBlank: false,
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.IPProtocolSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveIPProtocolSelector'],
|
||||
valueField: 'p',
|
||||
displayField: 'p',
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.IPRefSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveIPRefSelector'],
|
||||
|
||||
base_url: undefined,
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.NodeSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveNodeSelector'],
|
||||
|
||||
// invalidate nodes which are offline
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.PoolSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pvePoolSelector'],
|
||||
|
||||
allowBlank: false,
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.RoleSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveRoleSelector'],
|
||||
|
||||
allowBlank: false,
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.SecurityGroupsSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveSecurityGroupsSelector'],
|
||||
|
||||
valueField: 'group',
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.SnapshotSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.PVE.form.SnapshotSelector'],
|
||||
|
||||
valueField: 'name',
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.StorageSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: 'widget.pveStorageSelector',
|
||||
|
||||
allowBlank: false,
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.USBSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveUSBSelector'],
|
||||
allowBlank: false,
|
||||
autoSelect: false,
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.UserSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveUserSelector'],
|
||||
allowBlank: false,
|
||||
autoSelect: false,
|
||||
|
@ -215,7 +215,7 @@ Ext.define('PVE.form.VMSelector', {
|
||||
|
||||
|
||||
Ext.define('PVE.form.VMComboSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: 'widget.vmComboSelector',
|
||||
|
||||
valueField: 'vmid',
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.form.FWMacroSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: 'widget.pveFWMacroSelector',
|
||||
allowBlank: true,
|
||||
autoSelect: false,
|
||||
|
@ -1,5 +1,5 @@
|
||||
Ext.define('PVE.ha.GroupSelector', {
|
||||
extend: 'PVE.form.ComboGrid',
|
||||
extend: 'Proxmox.form.ComboGrid',
|
||||
alias: ['widget.pveHAGroupSelector'],
|
||||
|
||||
value: [],
|
||||
|
Loading…
Reference in New Issue
Block a user