mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-10-18 01:11:46 +00:00
ui: add form for editing tags
This is a wrapper container for holding a list of (editable) tags intended to be used in the lxc/qemu status toolbar To add a new tag, we reuse the 'pveTag' class, but overwrite some of its behaviour and css classes so that it properly adds tags Also handles the drag/drop feature for the tags in the list When done with editing (by clicking the checkmark), sends a 'change' event with the new tags Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
parent
b6f9fc78c0
commit
1b42bb8657
@ -657,7 +657,8 @@ table.osds td:first-of-type {
|
|||||||
padding-bottom: 0px;
|
padding-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pve-edit-tag > i {
|
.pve-edit-tag > i,
|
||||||
|
.pve-add-tag > i {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
@ -667,7 +668,8 @@ table.osds td:first-of-type {
|
|||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pve-edit-tag > i.action {
|
.pve-edit-tag > i.action,
|
||||||
|
.pve-add-tag > i.action {
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,7 +678,9 @@ table.osds td:first-of-type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pve-edit-tag.editable span,
|
.pve-edit-tag.editable span,
|
||||||
.pve-edit-tag.inEdit span {
|
.pve-edit-tag.inEdit span,
|
||||||
|
.pve-add-tag.editable span,
|
||||||
|
.pve-add-tag.inEdit span {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #a8a8a8;
|
border: 1px solid #a8a8a8;
|
||||||
color: #000;
|
color: #000;
|
||||||
@ -685,6 +689,17 @@ table.osds td:first-of-type {
|
|||||||
min-width: 2em;
|
min-width: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pve-edit-tag.inEdit span {
|
.pve-edit-tag.inEdit span,
|
||||||
|
.pve-add-tag.inEdit span {
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pve-add-tag {
|
||||||
|
background-color: #d5d5d5 ! important;
|
||||||
|
color: #000000 ! important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pve-tag-inline-button {
|
||||||
|
cursor: pointer;
|
||||||
|
padding-left: 2px;
|
||||||
|
}
|
||||||
|
@ -76,6 +76,7 @@ JSSRC= \
|
|||||||
form/TagColorGrid.js \
|
form/TagColorGrid.js \
|
||||||
form/ListField.js \
|
form/ListField.js \
|
||||||
form/Tag.js \
|
form/Tag.js \
|
||||||
|
form/TagEdit.js \
|
||||||
grid/BackupView.js \
|
grid/BackupView.js \
|
||||||
grid/FirewallAliases.js \
|
grid/FirewallAliases.js \
|
||||||
grid/FirewallOptions.js \
|
grid/FirewallOptions.js \
|
||||||
|
325
www/manager6/form/TagEdit.js
Normal file
325
www/manager6/form/TagEdit.js
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
Ext.define('PVE.panel.TagEditContainer', {
|
||||||
|
extend: 'Ext.container.Container',
|
||||||
|
alias: 'widget.pveTagEditContainer',
|
||||||
|
|
||||||
|
layout: {
|
||||||
|
type: 'hbox',
|
||||||
|
align: 'stretch',
|
||||||
|
},
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
xclass: 'Ext.app.ViewController',
|
||||||
|
|
||||||
|
loadTags: function(tagstring = '', force = false) {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
|
||||||
|
if (me.oldTags === tagstring && !force) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.suspendLayout = true;
|
||||||
|
me.forEachTag((tag) => {
|
||||||
|
view.remove(tag);
|
||||||
|
});
|
||||||
|
me.getViewModel().set('tagCount', 0);
|
||||||
|
let newtags = tagstring.split(/[;, ]/).filter((t) => !!t) || [];
|
||||||
|
newtags.forEach((tag) => {
|
||||||
|
me.addTag(tag);
|
||||||
|
});
|
||||||
|
view.suspendLayout = false;
|
||||||
|
view.updateLayout();
|
||||||
|
if (!force) {
|
||||||
|
me.oldTags = tagstring;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender: function(v) {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
view.dragzone = Ext.create('Ext.dd.DragZone', v.getEl(), {
|
||||||
|
getDragData: function(e) {
|
||||||
|
let source = e.getTarget('.handle');
|
||||||
|
if (!source) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let sourceId = source.parentNode.id;
|
||||||
|
let cmp = Ext.getCmp(sourceId);
|
||||||
|
let ddel = document.createElement('div');
|
||||||
|
ddel.classList.add('proxmox-tags-full');
|
||||||
|
ddel.innerHTML = Proxmox.Utils.getTagElement(cmp.tag, PVE.Utils.tagOverrides);
|
||||||
|
let repairXY = Ext.fly(source).getXY();
|
||||||
|
cmp.setDisabled(true);
|
||||||
|
ddel.id = Ext.id();
|
||||||
|
return {
|
||||||
|
ddel,
|
||||||
|
repairXY,
|
||||||
|
sourceId,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onMouseUp: function(target, e, id) {
|
||||||
|
let cmp = Ext.getCmp(this.dragData.sourceId);
|
||||||
|
if (cmp && !cmp.isDestroyed) {
|
||||||
|
cmp.setDisabled(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getRepairXY: function() {
|
||||||
|
return this.dragData.repairXY;
|
||||||
|
},
|
||||||
|
beforeInvalidDrop: function(target, e, id) {
|
||||||
|
let cmp = Ext.getCmp(this.dragData.sourceId);
|
||||||
|
if (cmp && !cmp.isDestroyed) {
|
||||||
|
cmp.setDisabled(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
view.dropzone = Ext.create('Ext.dd.DropZone', v.getEl(), {
|
||||||
|
getTargetFromEvent: function(e) {
|
||||||
|
return e.getTarget('.proxmox-tag-dark,.proxmox-tag-light');
|
||||||
|
},
|
||||||
|
getIndicator: function() {
|
||||||
|
if (!view.indicator) {
|
||||||
|
view.indicator = Ext.create('Ext.Component', {
|
||||||
|
floating: true,
|
||||||
|
html: '<i class="fa fa-long-arrow-up"></i>',
|
||||||
|
hidden: true,
|
||||||
|
shadow: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return view.indicator;
|
||||||
|
},
|
||||||
|
onContainerOver: function() {
|
||||||
|
this.getIndicator().setVisible(false);
|
||||||
|
},
|
||||||
|
notifyOut: function() {
|
||||||
|
this.getIndicator().setVisible(false);
|
||||||
|
},
|
||||||
|
onNodeOver: function(target, dd, e, data) {
|
||||||
|
let indicator = this.getIndicator();
|
||||||
|
indicator.setVisible(true);
|
||||||
|
indicator.alignTo(Ext.getCmp(target.id), 't50-bl', [-1, -2]);
|
||||||
|
return this.dropAllowed;
|
||||||
|
},
|
||||||
|
onNodeDrop: function(target, dd, e, data) {
|
||||||
|
this.getIndicator().setVisible(false);
|
||||||
|
let sourceCmp = Ext.getCmp(data.sourceId);
|
||||||
|
if (!sourceCmp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sourceCmp.setDisabled(false);
|
||||||
|
let targetCmp = Ext.getCmp(target.id);
|
||||||
|
view.remove(sourceCmp, { destroy: false });
|
||||||
|
view.insert(view.items.indexOf(targetCmp), sourceCmp);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
forEachTag: function(func) {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
view.items.each((field) => {
|
||||||
|
if (field.reference === 'addTagBtn') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (field.getXType() === 'pveTag') {
|
||||||
|
func(field);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleEdit: function(cancel) {
|
||||||
|
let me = this;
|
||||||
|
let vm = me.getViewModel();
|
||||||
|
let editMode = !vm.get('editMode');
|
||||||
|
vm.set('editMode', editMode);
|
||||||
|
|
||||||
|
// get a current tag list for editing
|
||||||
|
if (editMode) {
|
||||||
|
PVE.Utils.updateUIOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
me.forEachTag((tag) => {
|
||||||
|
tag.setMode(editMode ? 'editable' : 'normal');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!vm.get('editMode')) {
|
||||||
|
let tags = [];
|
||||||
|
if (cancel) {
|
||||||
|
me.loadTags(me.oldTags, true);
|
||||||
|
} else {
|
||||||
|
me.forEachTag((cmp) => {
|
||||||
|
if (cmp.isVisible() && cmp.tag) {
|
||||||
|
tags.push(cmp.tag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tags = tags.join(',');
|
||||||
|
if (me.oldTags !== tags) {
|
||||||
|
me.oldTags = tags;
|
||||||
|
me.getView().fireEvent('change', tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
me.getView().updateLayout();
|
||||||
|
},
|
||||||
|
|
||||||
|
addTag: function(tag) {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
let vm = me.getViewModel();
|
||||||
|
let index = view.items.indexOf(me.lookup('addTagBtn'));
|
||||||
|
view.insert(index, {
|
||||||
|
xtype: 'pveTag',
|
||||||
|
tag,
|
||||||
|
mode: vm.get('editMode') ? 'editable' : 'normal',
|
||||||
|
listeners: {
|
||||||
|
change: (field, newTag) => {
|
||||||
|
if (newTag === '') {
|
||||||
|
view.remove(field);
|
||||||
|
vm.set('tagCount', vm.get('tagCount') - 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
vm.set('tagCount', vm.get('tagCount') + 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
addTagClick: function(event) {
|
||||||
|
let me = this;
|
||||||
|
if (event.target.tagName === 'SPAN') {
|
||||||
|
me.lookup('addTagBtn').tagEl().innerHTML = '';
|
||||||
|
me.lookup('addTagBtn').updateLayout();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addTagMouseDown: function(event) {
|
||||||
|
let me = this;
|
||||||
|
if (event.target.tagName === 'I') {
|
||||||
|
let tag = me.lookup('addTagBtn').tagEl().innerHTML;
|
||||||
|
if (tag !== '') {
|
||||||
|
me.addTag(tag, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addTagChange: function(field, tag) {
|
||||||
|
let me = this;
|
||||||
|
if (tag !== '') {
|
||||||
|
me.addTag(tag, true);
|
||||||
|
}
|
||||||
|
field.tag = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelClick: function() {
|
||||||
|
this.toggleEdit(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
editClick: function() {
|
||||||
|
this.toggleEdit(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function(view) {
|
||||||
|
let me = this;
|
||||||
|
if (view.tags) {
|
||||||
|
me.loadTags(view.tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
me.mon(Ext.GlobalEvents, 'loadedUiOptions', () => {
|
||||||
|
me.loadTags(me.oldTags, true); // refresh tag colors
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
viewModel: {
|
||||||
|
data: {
|
||||||
|
tagCount: 0,
|
||||||
|
editMode: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
formulas: {
|
||||||
|
hideNoTags: function(get) {
|
||||||
|
return get('editMode') || get('tagCount') !== 0;
|
||||||
|
},
|
||||||
|
editBtnHtml: function(get) {
|
||||||
|
let cls = get('editMode') ? 'check' : 'pencil';
|
||||||
|
let qtip = get('editMode') ? gettext('Apply Changes') : gettext('Edit Tags');
|
||||||
|
return `<i data-qtip="${qtip}" class="fa fa-${cls}"></i>`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
loadTags: function() {
|
||||||
|
return this.getController().loadTags(...arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'box',
|
||||||
|
bind: {
|
||||||
|
hidden: '{hideNoTags}',
|
||||||
|
},
|
||||||
|
html: gettext('No Tags'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'pveTag',
|
||||||
|
reference: 'addTagBtn',
|
||||||
|
cls: 'pve-add-tag',
|
||||||
|
mode: 'editable',
|
||||||
|
tag: '',
|
||||||
|
tpl: `<span>${gettext('Add Tag')}</span><i class="action fa fa-plus-square"></i>`,
|
||||||
|
bind: {
|
||||||
|
hidden: '{!editMode}',
|
||||||
|
},
|
||||||
|
hidden: true,
|
||||||
|
onMouseDown: Ext.emptyFn, // prevent default behaviour
|
||||||
|
listeners: {
|
||||||
|
click: {
|
||||||
|
element: 'el',
|
||||||
|
fn: 'addTagClick',
|
||||||
|
},
|
||||||
|
mousedown: {
|
||||||
|
element: 'el',
|
||||||
|
fn: 'addTagMouseDown',
|
||||||
|
},
|
||||||
|
change: 'addTagChange',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'box',
|
||||||
|
html: `<i data-qtip="${gettext('Cancel')}" class="fa fa-times"></i>`,
|
||||||
|
cls: 'pve-tag-inline-button',
|
||||||
|
hidden: true,
|
||||||
|
bind: {
|
||||||
|
hidden: '{!editMode}',
|
||||||
|
},
|
||||||
|
listeners: {
|
||||||
|
click: 'cancelClick',
|
||||||
|
element: 'el',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'box',
|
||||||
|
cls: 'pve-tag-inline-button',
|
||||||
|
bind: {
|
||||||
|
html: '{editBtnHtml}',
|
||||||
|
},
|
||||||
|
listeners: {
|
||||||
|
click: 'editClick',
|
||||||
|
element: 'el',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
render: 'onRender',
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
let me = this;
|
||||||
|
Ext.destroy(me.dragzone);
|
||||||
|
Ext.destroy(me.dropzone);
|
||||||
|
Ext.destroy(me.indicator);
|
||||||
|
me.callParent();
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user