Signed-off-by: Dominik Csapak <d.csa...@proxmox.com>
---
 www/manager6/form/Tag.js     | 22 +++++++--
 www/manager6/form/TagEdit.js | 96 +++++++++++++++++++++++++++++++++++-
 2 files changed, 114 insertions(+), 4 deletions(-)

diff --git a/www/manager6/form/Tag.js b/www/manager6/form/Tag.js
index 91190051..dcbd9597 100644
--- a/www/manager6/form/Tag.js
+++ b/www/manager6/form/Tag.js
@@ -31,6 +31,9 @@ Ext.define('Proxmox.Tag', {
        if (event.target.tagName !== 'I') {
            return;
        }
+       if (event.target.classList.contains('handle')) {
+           return;
+       }
        switch (me.mode) {
            case 'editable':
                if (me.addTag) {
@@ -156,12 +159,14 @@ Ext.define('Proxmox.Tag', {
        let text = me.tag;
        let cursor = 'pointer';
        let padding = '0px';
+       let dragHandleStyle = 'none';
        switch (mode) {
            case 'normal':
                iconStyle += 'display: none;';
                padding = undefined;
                break;
            case 'editable':
+               dragHandleStyle = '';
                break;
            case 'edit':
                me.tagEl().contentEditable = true;
@@ -174,12 +179,14 @@ Ext.define('Proxmox.Tag', {
        if (me.addTag) {
            me.setText(text);
            me.setStyle('cursor', cursor);
+           dragHandleStyle = 'none';
        }
 
        me.setStyle('padding-right', padding);
 
        me.iconEl().classList = `fa fa-${icon}${me.faIconStyle}`;
        me.iconEl().style = iconStyle;
+       me.dragEl().style.display = dragHandleStyle;
        me.mode = mode;
     },
 
@@ -233,14 +240,18 @@ Ext.define('Proxmox.Tag', {
        }
     },
 
-    tagEl: function() {
+    dragEl: function() {
        return this.el?.dom?.children?.[0];
     },
 
-    iconEl: function() {
+    tagEl: function() {
        return this.el?.dom?.children?.[1];
     },
 
+    iconEl: function() {
+       return this.el?.dom?.children?.[2];
+    },
+
     initComponent: function() {
        let me = this;
        if (me.tag === undefined && !me.addTag) {
@@ -256,10 +267,15 @@ Ext.define('Proxmox.Tag', {
 
        let iconStyle = me.mode !== 'editable' ? 'display: none' : 
'padding-right: 6px;';
        let iconCls = me.icons[me.addTag ? 'addTag' : me.mode];
+       let dragHandleStyle = 'cursor: grab; font-size: 14px;';
+       if (me.addTag || me.mode !== 'editable') {
+           dragHandleStyle += 'display: none';
+       }
 
        let adminCls = me.tag[0] === '+' ? 'admin-tag' : '';
+       let dragHandle = `<i class="handle fa fa-bars" 
style="${dragHandleStyle}"></i> `;
        let icon = ` <i style="cursor: pointer; ${iconStyle}" class="fa 
fa-${iconCls}${me.faIconStyle}"></i>`;
-       me.html = `<span style="padding-right: 1px" 
class="${adminCls}">${me.tag}</span>${icon}`;
+       me.html = `${dragHandle}<span style="padding-right: 1px" 
class="${adminCls}">${me.tag}</span>${icon}`;
 
        me.callParent();
        if (me.addTag) {
diff --git a/www/manager6/form/TagEdit.js b/www/manager6/form/TagEdit.js
index 5a267169..85f9f63e 100644
--- a/www/manager6/form/TagEdit.js
+++ b/www/manager6/form/TagEdit.js
@@ -18,7 +18,7 @@ Ext.define('PVE.panel.TagEditContainer', {
 
        let tags = tagstring.split(/[;, ]/).filter((t) => !!t) || [];
        me.suspendLayout = true;
-       me.tags = {};
+       me.tagList = [];
        me.removeAllTags();
        tags.forEach((tag) => {
            me.addTag(tag, inEdit);
@@ -87,6 +87,7 @@ Ext.define('PVE.panel.TagEditContainer', {
            me.remove(tag);
        });
        me.tagFields = [];
+       me.tagList = [];
        me.noTagsField.setVisible(true);
     },
 
@@ -94,11 +95,13 @@ Ext.define('PVE.panel.TagEditContainer', {
        let me = this;
        let tagField = me.insert(me.tagFields.length + 1, {
            xtype: 'pmxTag',
+           tagIndex: me.tagList.length,
            tag,
            mode: inEdit ? 'editable' : 'normal',
            layoutCallback: () => me.updateLayout(),
        });
        me.tagFields.push(tagField);
+       me.tagList.push(tag);
        me.noTagsField.setVisible(false);
     },
 
@@ -147,5 +150,96 @@ Ext.define('PVE.panel.TagEditContainer', {
        if (me.tags) {
            me.loadTags(me.tags);
        }
+
+       me.on('render', function(v) {
+           me.dragzone = Ext.create('Ext.dd.DragZone', v.getEl(), {
+               getDragData: function(e) {
+                   let source = e.getTarget('.handle');
+                   if (source) {
+                       let sourceId = source.parentNode.id;
+                       let cmp = Ext.getCmp(sourceId);
+                       let tag = cmp.getTag();
+                       let ddel = document.createElement('div');
+                       ddel.classList.add('proxmox-tags-full');
+                       ddel.innerHTML = Proxmox.Utils.getTagElement(tag, 
PVE.Utils.tagOverrides);
+                       let repairXY = Ext.fly(source).getXY();
+                       cmp.setDisabled(true);
+                       ddel.id = Ext.id();
+                       return {
+                           ddel,
+                           repairXY,
+                           tagIndex: cmp.tagIndex,
+                           tag: cmp.getTag(),
+                           sourceId,
+                       };
+                   }
+                   return undefined;
+               },
+               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);
+                   }
+               },
+           });
+           me.dropzone = Ext.create('Ext.dd.DropZone', v.getEl(), {
+               getTargetFromEvent: function(e) {
+                   return e.getTarget('.proxmox-tag-dark,.proxmox-tag-light');
+               },
+               getIndicator: function() {
+                   if (!me.indicator) {
+                       me.indicator = Ext.create('Ext.Component', {
+                           floating: true,
+                           html: '<i class="fa fa-long-arrow-up"></i>',
+                           hidden: true,
+                           shadow: false,
+                       });
+                   }
+                   return me.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 tagIdx = data.tagIndex;
+                   let tag = data.tag;
+                   me.tagList.splice(tagIdx, 1);
+                   let targetCmp = Ext.getCmp(target.id);
+                   let targetIdx = targetCmp.tagIndex ?? Infinity;
+                   if (targetIdx > tagIdx) {
+                       targetIdx--;
+                   }
+                   me.tagList.splice(targetIdx, 0, tag);
+                   me.loadTags(me.tagList.join(','), true, true);
+               },
+           });
+       });
+    },
+
+    destroy: function() {
+       let me = this;
+       Ext.destroy(me.dragzone);
+       Ext.destroy(me.dropzone);
+       Ext.destroy(me.indicator);
+       me.callParent();
     },
 });
-- 
2.30.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to