]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/form/Tag.js
update shipped appliance info index
[pve-manager.git] / www / manager6 / form / Tag.js
1 Ext.define('Proxmox.form.Tag', {
2 extend: 'Ext.Component',
3 alias: 'widget.pveTag',
4
5 mode: 'editable',
6
7 tag: '',
8 cls: 'pve-edit-tag',
9
10 tpl: [
11 '<i class="handle fa fa-bars"></i>',
12 '<span>{tag}</span>',
13 '<i class="action fa fa-minus-square"></i>',
14 ],
15
16 // contains tags not to show in the picker and not allowing to set
17 filter: [],
18
19 updateFilter: function(tags) {
20 this.filter = tags;
21 },
22
23 onClick: function(event) {
24 let me = this;
25 if (event.target.tagName === 'I' && !event.target.classList.contains('handle')) {
26 if (me.mode === 'editable') {
27 me.destroy();
28 return;
29 }
30 } else if (event.target.tagName !== 'SPAN' || me.mode !== 'editable') {
31 return;
32 }
33 me.selectText();
34 },
35
36 selectText: function(collapseToEnd) {
37 let me = this;
38 let tagEl = me.tagEl();
39 tagEl.contentEditable = true;
40 let range = document.createRange();
41 range.selectNodeContents(tagEl);
42 if (collapseToEnd) {
43 range.collapse(false);
44 }
45 let sel = window.getSelection();
46 sel.removeAllRanges();
47 sel.addRange(range);
48
49 me.showPicker();
50 },
51
52 showPicker: function() {
53 let me = this;
54 if (!me.picker) {
55 me.picker = Ext.widget({
56 xtype: 'boundlist',
57 minWidth: 70,
58 scrollable: true,
59 floating: true,
60 hidden: true,
61 userCls: 'proxmox-tags-full',
62 displayField: 'tag',
63 itemTpl: [
64 '{[Proxmox.Utils.getTagElement(values.tag, PVE.Utils.tagOverrides)]}',
65 ],
66 store: [],
67 listeners: {
68 select: function(picker, rec) {
69 me.tagEl().innerHTML = rec.data.tag;
70 me.setTag(rec.data.tag, true);
71 me.selectText(true);
72 me.setColor(rec.data.tag);
73 me.picker.hide();
74 },
75 },
76 });
77 }
78 me.picker.getStore()?.clearFilter();
79 let taglist = PVE.Utils.tagList.filter(v => !me.filter.includes(v)).map(v => ({ tag: v }));
80 if (taglist.length < 1) {
81 return;
82 }
83 me.picker.getStore().setData(taglist);
84 me.picker.showBy(me, 'tl-bl');
85 me.picker.setMaxHeight(200);
86 },
87
88 setMode: function(mode) {
89 let me = this;
90 let tagEl = me.tagEl();
91 if (tagEl) {
92 tagEl.contentEditable = mode === 'editable';
93 }
94 me.removeCls(me.mode);
95 me.addCls(mode);
96 me.mode = mode;
97 if (me.mode !== 'editable') {
98 me.picker?.hide();
99 }
100 },
101
102 onKeyPress: function(event) {
103 let me = this;
104 let key = event.browserEvent.key;
105 switch (key) {
106 case 'Enter':
107 case 'Escape':
108 me.fireEvent('keypress', key);
109 break;
110 case 'ArrowLeft':
111 case 'ArrowRight':
112 case 'Backspace':
113 case 'Delete':
114 return;
115 default:
116 if (key.match(PVE.Utils.tagCharRegex)) {
117 return;
118 }
119 me.setTag(me.tagEl().innerHTML);
120 }
121 event.browserEvent.preventDefault();
122 event.browserEvent.stopPropagation();
123 },
124
125 // for pasting text
126 beforeInput: function(event) {
127 let me = this;
128 me.updateLayout();
129 let tag = event.event.data ?? event.event.dataTransfer?.getData('text/plain');
130 if (!tag) {
131 return;
132 }
133 if (tag.match(PVE.Utils.tagCharRegex) === null) {
134 event.event.preventDefault();
135 event.event.stopPropagation();
136 }
137 },
138
139 onInput: function(event) {
140 let me = this;
141 me.picker.getStore().filter({
142 property: 'tag',
143 value: me.tagEl().innerHTML,
144 anyMatch: true,
145 });
146 me.setTag(me.tagEl().innerHTML);
147 },
148
149 lostFocus: function(list, event) {
150 let me = this;
151 me.picker?.hide();
152 window.getSelection().removeAllRanges();
153 },
154
155 setColor: function(tag) {
156 let me = this;
157 let rgb = PVE.Utils.tagOverrides[tag] ?? Proxmox.Utils.stringToRGB(tag);
158
159 let cls = Proxmox.Utils.getTextContrastClass(rgb);
160 let color = Proxmox.Utils.rgbToCss(rgb);
161 me.setUserCls(`proxmox-tag-${cls}`);
162 me.setStyle('background-color', color);
163 if (rgb.length > 3) {
164 let fgcolor = Proxmox.Utils.rgbToCss([rgb[3], rgb[4], rgb[5]]);
165
166 me.setStyle('color', fgcolor);
167 } else {
168 me.setStyle('color');
169 }
170 },
171
172 setTag: function(tag) {
173 let me = this;
174 let oldtag = me.tag;
175 me.tag = tag;
176
177 clearTimeout(me.colorTimeout);
178 me.colorTimeout = setTimeout(() => me.setColor(tag), 200);
179
180 me.updateLayout();
181 if (oldtag !== tag) {
182 me.fireEvent('change', me, tag, oldtag);
183 }
184 },
185
186 tagEl: function() {
187 return this.el?.dom?.getElementsByTagName('span')?.[0];
188 },
189
190 listeners: {
191 click: 'onClick',
192 focusleave: 'lostFocus',
193 keydown: 'onKeyPress',
194 beforeInput: 'beforeInput',
195 input: 'onInput',
196 element: 'el',
197 scope: 'this',
198 },
199
200 initComponent: function() {
201 let me = this;
202
203 me.data = {
204 tag: me.tag,
205 };
206
207 me.setTag(me.tag);
208 me.setColor(me.tag);
209 me.setMode(me.mode ?? 'normal');
210 me.callParent();
211 },
212
213 destroy: function() {
214 let me = this;
215 if (me.picker) {
216 Ext.destroy(me.picker);
217 }
218 clearTimeout(me.colorTimeout);
219 me.callParent();
220 },
221 });