]>
Commit | Line | Data |
---|---|---|
1b42bb86 DC |
1 | Ext.define('PVE.panel.TagEditContainer', { |
2 | extend: 'Ext.container.Container', | |
3 | alias: 'widget.pveTagEditContainer', | |
4 | ||
5 | layout: { | |
6 | type: 'hbox', | |
59e71a08 | 7 | align: 'middle', |
1b42bb86 DC |
8 | }, |
9 | ||
10 | controller: { | |
11 | xclass: 'Ext.app.ViewController', | |
12 | ||
13 | loadTags: function(tagstring = '', force = false) { | |
14 | let me = this; | |
15 | let view = me.getView(); | |
16 | ||
17 | if (me.oldTags === tagstring && !force) { | |
18 | return; | |
19 | } | |
20 | ||
21 | view.suspendLayout = true; | |
22 | me.forEachTag((tag) => { | |
23 | view.remove(tag); | |
24 | }); | |
25 | me.getViewModel().set('tagCount', 0); | |
26 | let newtags = tagstring.split(/[;, ]/).filter((t) => !!t) || []; | |
27 | newtags.forEach((tag) => { | |
28 | me.addTag(tag); | |
29 | }); | |
30 | view.suspendLayout = false; | |
31 | view.updateLayout(); | |
32 | if (!force) { | |
33 | me.oldTags = tagstring; | |
34 | } | |
35 | }, | |
36 | ||
37 | onRender: function(v) { | |
38 | let me = this; | |
39 | let view = me.getView(); | |
8d8ba23d DC |
40 | view.toggleCls('hide-handles', PVE.Utils.shouldSortTags()); |
41 | ||
1b42bb86 DC |
42 | view.dragzone = Ext.create('Ext.dd.DragZone', v.getEl(), { |
43 | getDragData: function(e) { | |
44 | let source = e.getTarget('.handle'); | |
45 | if (!source) { | |
46 | return undefined; | |
47 | } | |
48 | let sourceId = source.parentNode.id; | |
49 | let cmp = Ext.getCmp(sourceId); | |
50 | let ddel = document.createElement('div'); | |
51 | ddel.classList.add('proxmox-tags-full'); | |
52 | ddel.innerHTML = Proxmox.Utils.getTagElement(cmp.tag, PVE.Utils.tagOverrides); | |
53 | let repairXY = Ext.fly(source).getXY(); | |
54 | cmp.setDisabled(true); | |
55 | ddel.id = Ext.id(); | |
56 | return { | |
57 | ddel, | |
58 | repairXY, | |
59 | sourceId, | |
60 | }; | |
61 | }, | |
62 | onMouseUp: function(target, e, id) { | |
63 | let cmp = Ext.getCmp(this.dragData.sourceId); | |
64 | if (cmp && !cmp.isDestroyed) { | |
65 | cmp.setDisabled(false); | |
66 | } | |
67 | }, | |
68 | getRepairXY: function() { | |
69 | return this.dragData.repairXY; | |
70 | }, | |
71 | beforeInvalidDrop: function(target, e, id) { | |
72 | let cmp = Ext.getCmp(this.dragData.sourceId); | |
73 | if (cmp && !cmp.isDestroyed) { | |
74 | cmp.setDisabled(false); | |
75 | } | |
76 | }, | |
77 | }); | |
78 | view.dropzone = Ext.create('Ext.dd.DropZone', v.getEl(), { | |
79 | getTargetFromEvent: function(e) { | |
80 | return e.getTarget('.proxmox-tag-dark,.proxmox-tag-light'); | |
81 | }, | |
82 | getIndicator: function() { | |
83 | if (!view.indicator) { | |
84 | view.indicator = Ext.create('Ext.Component', { | |
85 | floating: true, | |
86 | html: '<i class="fa fa-long-arrow-up"></i>', | |
87 | hidden: true, | |
88 | shadow: false, | |
89 | }); | |
90 | } | |
91 | return view.indicator; | |
92 | }, | |
93 | onContainerOver: function() { | |
94 | this.getIndicator().setVisible(false); | |
95 | }, | |
96 | notifyOut: function() { | |
97 | this.getIndicator().setVisible(false); | |
98 | }, | |
99 | onNodeOver: function(target, dd, e, data) { | |
100 | let indicator = this.getIndicator(); | |
101 | indicator.setVisible(true); | |
102 | indicator.alignTo(Ext.getCmp(target.id), 't50-bl', [-1, -2]); | |
103 | return this.dropAllowed; | |
104 | }, | |
105 | onNodeDrop: function(target, dd, e, data) { | |
106 | this.getIndicator().setVisible(false); | |
107 | let sourceCmp = Ext.getCmp(data.sourceId); | |
108 | if (!sourceCmp) { | |
109 | return; | |
110 | } | |
111 | sourceCmp.setDisabled(false); | |
112 | let targetCmp = Ext.getCmp(target.id); | |
113 | view.remove(sourceCmp, { destroy: false }); | |
114 | view.insert(view.items.indexOf(targetCmp), sourceCmp); | |
115 | }, | |
116 | }); | |
117 | }, | |
118 | ||
119 | forEachTag: function(func) { | |
120 | let me = this; | |
121 | let view = me.getView(); | |
122 | view.items.each((field) => { | |
1b42bb86 DC |
123 | if (field.getXType() === 'pveTag') { |
124 | func(field); | |
125 | } | |
126 | return true; | |
127 | }); | |
128 | }, | |
129 | ||
130 | toggleEdit: function(cancel) { | |
131 | let me = this; | |
132 | let vm = me.getViewModel(); | |
59e71a08 | 133 | let view = me.getView(); |
1b42bb86 DC |
134 | let editMode = !vm.get('editMode'); |
135 | vm.set('editMode', editMode); | |
136 | ||
137 | // get a current tag list for editing | |
138 | if (editMode) { | |
139 | PVE.Utils.updateUIOptions(); | |
140 | } | |
141 | ||
142 | me.forEachTag((tag) => { | |
143 | tag.setMode(editMode ? 'editable' : 'normal'); | |
144 | }); | |
145 | ||
146 | if (!vm.get('editMode')) { | |
147 | let tags = []; | |
148 | if (cancel) { | |
149 | me.loadTags(me.oldTags, true); | |
150 | } else { | |
59e71a08 | 151 | let toRemove = []; |
1b42bb86 DC |
152 | me.forEachTag((cmp) => { |
153 | if (cmp.isVisible() && cmp.tag) { | |
154 | tags.push(cmp.tag); | |
59e71a08 DC |
155 | } else { |
156 | toRemove.push(cmp); | |
1b42bb86 DC |
157 | } |
158 | }); | |
59e71a08 | 159 | toRemove.forEach(cmp => view.remove(cmp)); |
1b42bb86 DC |
160 | tags = tags.join(','); |
161 | if (me.oldTags !== tags) { | |
162 | me.oldTags = tags; | |
59e71a08 | 163 | me.loadTags(tags, true); |
1b42bb86 DC |
164 | me.getView().fireEvent('change', tags); |
165 | } | |
166 | } | |
167 | } | |
168 | me.getView().updateLayout(); | |
169 | }, | |
170 | ||
59e71a08 | 171 | addTag: function(tag, isNew) { |
1b42bb86 DC |
172 | let me = this; |
173 | let view = me.getView(); | |
174 | let vm = me.getViewModel(); | |
59e71a08 DC |
175 | let index = view.items.length - 5; |
176 | if (PVE.Utils.shouldSortTags() && !isNew) { | |
8d8ba23d | 177 | index = view.items.findIndexBy(tagField => { |
59e71a08 DC |
178 | if (tagField.reference === 'noTagsField') { |
179 | return false; | |
180 | } | |
181 | if (tagField.xtype !== 'pveTag') { | |
8d8ba23d DC |
182 | return true; |
183 | } | |
871953db DC |
184 | let a = tagField.tag.toLowerCase() |
185 | let b = tag.toLowerCase(); | |
186 | return a > b ? true : a < b ? false : tagField.tag.localeCompare(tag) > 0; | |
8d8ba23d DC |
187 | }, 1); |
188 | } | |
59e71a08 | 189 | let tagField = view.insert(index, { |
1b42bb86 DC |
190 | xtype: 'pveTag', |
191 | tag, | |
192 | mode: vm.get('editMode') ? 'editable' : 'normal', | |
193 | listeners: { | |
59e71a08 DC |
194 | destroy: function() { |
195 | vm.set('tagCount', vm.get('tagCount') - 1); | |
1b42bb86 DC |
196 | }, |
197 | }, | |
198 | }); | |
199 | ||
59e71a08 DC |
200 | if (isNew) { |
201 | tagField.selectText(); | |
1b42bb86 | 202 | } |
1b42bb86 | 203 | |
59e71a08 | 204 | vm.set('tagCount', vm.get('tagCount') + 1); |
1b42bb86 DC |
205 | }, |
206 | ||
59e71a08 | 207 | addTagClick: function(event) { |
1b42bb86 | 208 | let me = this; |
59e71a08 DC |
209 | me.lookup('noTagsField').setVisible(false); |
210 | me.addTag('', true); | |
1b42bb86 DC |
211 | }, |
212 | ||
213 | cancelClick: function() { | |
214 | this.toggleEdit(true); | |
215 | }, | |
216 | ||
217 | editClick: function() { | |
218 | this.toggleEdit(false); | |
219 | }, | |
220 | ||
221 | init: function(view) { | |
222 | let me = this; | |
223 | if (view.tags) { | |
224 | me.loadTags(view.tags); | |
225 | } | |
226 | ||
227 | me.mon(Ext.GlobalEvents, 'loadedUiOptions', () => { | |
8d8ba23d DC |
228 | view.toggleCls('hide-handles', PVE.Utils.shouldSortTags()); |
229 | me.loadTags(me.oldTags, true); // refresh tag colors and order | |
1b42bb86 DC |
230 | }); |
231 | }, | |
232 | }, | |
233 | ||
234 | viewModel: { | |
235 | data: { | |
236 | tagCount: 0, | |
237 | editMode: false, | |
238 | }, | |
239 | ||
240 | formulas: { | |
241 | hideNoTags: function(get) { | |
59e71a08 | 242 | return get('tagCount') !== 0; |
1b42bb86 DC |
243 | }, |
244 | }, | |
245 | }, | |
246 | ||
247 | loadTags: function() { | |
248 | return this.getController().loadTags(...arguments); | |
249 | }, | |
250 | ||
251 | items: [ | |
252 | { | |
253 | xtype: 'box', | |
59e71a08 | 254 | reference: 'noTagsField', |
1b42bb86 DC |
255 | bind: { |
256 | hidden: '{hideNoTags}', | |
257 | }, | |
258 | html: gettext('No Tags'), | |
259 | }, | |
260 | { | |
59e71a08 DC |
261 | xtype: 'button', |
262 | iconCls: 'fa fa-plus', | |
263 | tooltip: gettext('Add Tag'), | |
1b42bb86 DC |
264 | bind: { |
265 | hidden: '{!editMode}', | |
266 | }, | |
267 | hidden: true, | |
59e71a08 DC |
268 | margin: '0 8 0 5', |
269 | ui: 'default-toolbar', | |
270 | handler: 'addTagClick', | |
1b42bb86 DC |
271 | }, |
272 | { | |
59e71a08 DC |
273 | xtype: 'tbseparator', |
274 | ui: 'horizontal', | |
275 | bind: { | |
276 | hidden: '{!editMode}', | |
277 | }, | |
1b42bb86 | 278 | hidden: true, |
59e71a08 DC |
279 | }, |
280 | { | |
281 | xtype: 'button', | |
282 | iconCls: 'fa fa-times', | |
283 | tooltip: gettext('Cancel Edit'), | |
1b42bb86 DC |
284 | bind: { |
285 | hidden: '{!editMode}', | |
286 | }, | |
59e71a08 DC |
287 | hidden: true, |
288 | margin: '0 5 0 0', | |
289 | ui: 'default-toolbar', | |
290 | handler: 'cancelClick', | |
291 | }, | |
292 | { | |
293 | xtype: 'button', | |
294 | iconCls: 'fa fa-check', | |
295 | tooltip: gettext('Finish Edit'), | |
296 | bind: { | |
297 | hidden: '{!editMode}', | |
1b42bb86 | 298 | }, |
59e71a08 DC |
299 | hidden: true, |
300 | ui: 'default-toolbar', | |
301 | handler: 'editClick', | |
1b42bb86 DC |
302 | }, |
303 | { | |
304 | xtype: 'box', | |
305 | cls: 'pve-tag-inline-button', | |
59e71a08 | 306 | html: `<i data-qtip="${gettext('Edit Tags')}" class="fa fa-pencil"></i>`, |
1b42bb86 | 307 | bind: { |
59e71a08 | 308 | hidden: '{editMode}', |
1b42bb86 DC |
309 | }, |
310 | listeners: { | |
311 | click: 'editClick', | |
312 | element: 'el', | |
313 | }, | |
314 | }, | |
315 | ], | |
316 | ||
317 | listeners: { | |
318 | render: 'onRender', | |
319 | }, | |
320 | ||
321 | destroy: function() { | |
322 | let me = this; | |
323 | Ext.destroy(me.dragzone); | |
324 | Ext.destroy(me.dropzone); | |
325 | Ext.destroy(me.indicator); | |
326 | me.callParent(); | |
327 | }, | |
328 | }); |