mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-10-04 11:36:22 +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;
|
||||
}
|
||||
|
||||
.pve-edit-tag > i {
|
||||
.pve-edit-tag > i,
|
||||
.pve-add-tag > i {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
@ -667,7 +668,8 @@ table.osds td:first-of-type {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.pve-edit-tag > i.action {
|
||||
.pve-edit-tag > i.action,
|
||||
.pve-add-tag > i.action {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
@ -676,7 +678,9 @@ table.osds td:first-of-type {
|
||||
}
|
||||
|
||||
.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;
|
||||
border: 1px solid #a8a8a8;
|
||||
color: #000;
|
||||
@ -685,6 +689,17 @@ table.osds td:first-of-type {
|
||||
min-width: 2em;
|
||||
}
|
||||
|
||||
.pve-edit-tag.inEdit span {
|
||||
.pve-edit-tag.inEdit span,
|
||||
.pve-add-tag.inEdit span {
|
||||
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/ListField.js \
|
||||
form/Tag.js \
|
||||
form/TagEdit.js \
|
||||
grid/BackupView.js \
|
||||
grid/FirewallAliases.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