in the general tab in the advanced section.
For that to work, we introduce a new option for the TagEditContainer
named 'editOnly', which controls now the cancel/finish buttons,
automatically enter edit mode and disable enter/escape keypresses.
We also prevent now the loading of tags while in edit mode, so the tags
don't change while editing (this can be jarring and unexpected).
Then we wrap that all in a FieldSet that implements the Field mixin, so
we can easily use that in the wizard. There we set a maxHeight so that
the field can grow so that it still fits in the wizard.
To properly align the input with the '+' button, we have to add a custom
css class there. (In the hbox we could set the alignment, but this is
not possible in the 'column' layout)
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
.pmx-opacity-75 {
opacity: 0.75;
}
+
+/* tag edit fields must be aligned manually in the fieldset */
+.proxmox-tag-fieldset.proxmox-tags-full .x-component.x-column {
+ margin: 2px;
+}
form/Tag.js \
form/TagEdit.js \
form/MultiFileButton.js \
+ form/TagFieldSet.js \
grid/BackupView.js \
grid/FirewallAliases.js \
grid/FirewallOptions.js \
// set to false to hide the 'no tags' field and the edit button
canEdit: true,
+ editOnly: false,
controller: {
xclass: 'Ext.app.ViewController',
me.tagsChanged();
},
keypress: function(key) {
+ if (vm.get('hideFinishButtons')) {
+ return;
+ }
if (key === 'Enter') {
me.editClick();
} else if (key === 'Escape') {
me.loadTags(view.tags);
}
me.getViewModel().set('canEdit', view.canEdit);
+ me.getViewModel().set('editOnly', view.editOnly);
me.mon(Ext.GlobalEvents, 'loadedUiOptions', () => {
+ let vm = me.getViewModel();
view.toggleCls('hide-handles', PVE.UIOptions.shouldSortTags());
- me.loadTags(me.oldTags, true); // refresh tag colors and order
+ me.loadTags(me.oldTags, !vm.get('editMode')); // refresh tag colors and order
});
+
+ if (view.editOnly) {
+ me.toggleEdit();
+ }
},
},
+ getTags: function() {
+ let me =this;
+ let controller = me.getController();
+ let tags = [];
+ controller.forEachTag((cmp) => {
+ if (cmp.tag.length) {
+ tags.push(cmp.tag);
+ }
+ });
+
+ return tags;
+ },
+
viewModel: {
data: {
tagCount: 0,
editMode: false,
canEdit: true,
isDirty: false,
+ editOnly: true,
},
formulas: {
hideEditBtn: function(get) {
return get('editMode') || !get('canEdit');
},
+ hideFinishButtons: function(get) {
+ return !get('editMode') || get('editOnly');
+ },
},
},
xtype: 'tbseparator',
ui: 'horizontal',
bind: {
- hidden: '{!editMode}',
+ hidden: '{hideFinishButtons}',
},
hidden: true,
},
iconCls: 'fa fa-times',
tooltip: gettext('Cancel Edit'),
bind: {
- hidden: '{!editMode}',
+ hidden: '{hideFinishButtons}',
},
hidden: true,
margin: '0 5 0 0',
iconCls: 'fa fa-check',
tooltip: gettext('Finish Edit'),
bind: {
- hidden: '{!editMode}',
+ hidden: '{hideFinishButtons}',
disabled: '{!isDirty}',
},
hidden: true,
--- /dev/null
+Ext.define('PVE.form.TagFieldSet', {
+ extend: 'Ext.form.FieldSet',
+ alias: 'widget.pveTagFieldSet',
+ mixins: ['Ext.form.field.Field'],
+
+ title: gettext('Tags'),
+ padding: '0 5 5 5',
+
+ getValue: function() {
+ let me = this;
+ let tags = me.down('pveTagEditContainer').getTags().filter(t => t !== '');
+ return tags.join(';');
+ },
+
+ setValue: function(value) {
+ let me = this;
+ value ??= [];
+ if (!Ext.isArray(value)) {
+ value = value.split(/[;, ]/).filter(t => t !== '');
+ }
+ me.down('pveTagEditContainer').loadTags(value.join(';'));
+ },
+
+ getErrors: function(value) {
+ value ??= [];
+ if (!Ext.isArray(value)) {
+ value = value.split(/[;, ]/).filter(t => t !== '');
+ }
+ if (value.some(t => !t.match(PVE.Utils.tagCharRegex))) {
+ return [gettext("Tags contain invalid characters.")];
+ }
+ return [];
+ },
+
+ getSubmitData: function() {
+ let me = this;
+ let value = me.getValue();
+ if (me.disabled || !me.submitValue || value === '') {
+ return null;
+ }
+ let data = {};
+ data[me.getName()] = value;
+ return data;
+ },
+
+ layout: 'fit',
+
+ items: [
+ {
+ xtype: 'pveTagEditContainer',
+ userCls: 'proxmox-tags-full proxmox-tag-fieldset',
+ editOnly: true,
+ allowBlank: true,
+ layout: 'column',
+ scrollable: true,
+ },
+ ],
+
+ initComponent: function() {
+ let me = this;
+ me.callParent();
+ me.initField();
+ },
+});
},
},
],
+ advancedColumnB: [
+ {
+ xtype: 'pveTagFieldSet',
+ name: 'tags',
+ maxHeight: 150,
+ },
+ ],
},
{
xtype: 'inputpanel',
fieldLabel: gettext('Shutdown timeout'),
},
],
+
+ advancedColumnB: [
+ {
+ xtype: 'pveTagFieldSet',
+ name: 'tags',
+ maxHeight: 150,
+ },
+ ],
+
onGetValues: function(values) {
['name', 'pool', 'onboot', 'agent'].forEach(function(field) {
if (!values[field]) {