vgapp 1.1.6 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +48 -48
  3. package/app/langs/en/buttons.json +17 -17
  4. package/app/langs/en/messages.json +36 -36
  5. package/app/langs/ru/buttons.json +17 -17
  6. package/app/langs/ru/messages.json +36 -36
  7. package/app/modules/vgfilepreview/js/i18n.js +56 -56
  8. package/app/modules/vgfilepreview/js/renderers/image-modal.js +145 -145
  9. package/app/modules/vgfilepreview/js/renderers/image.js +92 -92
  10. package/app/modules/vgfilepreview/js/renderers/index.js +19 -19
  11. package/app/modules/vgfilepreview/js/renderers/office-modal.js +168 -168
  12. package/app/modules/vgfilepreview/js/renderers/office.js +79 -79
  13. package/app/modules/vgfilepreview/js/renderers/pdf-modal.js +260 -260
  14. package/app/modules/vgfilepreview/js/renderers/pdf.js +76 -76
  15. package/app/modules/vgfilepreview/js/renderers/playlist.js +71 -71
  16. package/app/modules/vgfilepreview/js/renderers/text-modal.js +343 -343
  17. package/app/modules/vgfilepreview/js/renderers/text.js +83 -83
  18. package/app/modules/vgfilepreview/js/renderers/video-modal.js +272 -272
  19. package/app/modules/vgfilepreview/js/renderers/video.js +80 -80
  20. package/app/modules/vgfilepreview/js/renderers/zip-modal.js +522 -522
  21. package/app/modules/vgfilepreview/js/renderers/zip.js +89 -89
  22. package/app/modules/vgfilepreview/js/vgfilepreview.js +7 -7
  23. package/app/modules/vgfilepreview/readme.md +68 -68
  24. package/app/modules/vgfilepreview/scss/_variables.scss +113 -113
  25. package/app/modules/vgfilepreview/scss/vgfilepreview.scss +464 -464
  26. package/app/modules/vgfiles/js/base.js +26 -26
  27. package/app/modules/vgfiles/js/droppable.js +260 -260
  28. package/app/modules/vgfiles/js/render.js +153 -153
  29. package/app/modules/vgfiles/js/vgfiles.js +41 -41
  30. package/app/modules/vgfiles/readme.md +123 -123
  31. package/app/modules/vgfiles/scss/_variables.scss +18 -18
  32. package/app/modules/vgfiles/scss/vgfiles.scss +148 -148
  33. package/app/modules/vgformsender/js/vgformsender.js +1 -1
  34. package/app/modules/vgmodal/js/vgmodal.drag.js +332 -332
  35. package/app/modules/vgmodal/js/vgmodal.js +33 -33
  36. package/app/modules/vgmodal/js/vgmodal.resize.js +435 -435
  37. package/app/modules/vgnav/js/vgnav.js +135 -135
  38. package/app/modules/vgnav/readme.md +67 -67
  39. package/app/modules/vgnestable/README.md +307 -307
  40. package/app/modules/vgnestable/scss/_variables.scss +60 -60
  41. package/app/modules/vgnestable/scss/vgnestable.scss +163 -163
  42. package/app/modules/vgselect/js/vgselect.js +39 -39
  43. package/app/modules/vgselect/scss/vgselect.scss +22 -22
  44. package/app/modules/vgspy/readme.md +28 -28
  45. package/app/utils/js/components/audio-metadata.js +240 -240
  46. package/app/utils/js/components/file-icon.js +109 -109
  47. package/app/utils/js/components/file-preview.js +304 -304
  48. package/app/utils/js/components/sanitize.js +150 -150
  49. package/app/utils/js/components/video-metadata.js +140 -140
  50. package/build/vgapp.css +1 -1
  51. package/build/vgapp.css.map +1 -1
  52. package/build/vgapp.js.map +1 -1
  53. package/index.scss +9 -9
  54. package/package.json +1 -1
@@ -528,10 +528,10 @@ class VGFilesBase extends BaseModule {
528
528
  return;
529
529
  }
530
530
 
531
- if (existingNode) {
532
- VGFilePreview.stopActiveInlineAudioIfDetached([existingNode]);
533
- existingNode.remove();
534
- }
531
+ if (existingNode) {
532
+ VGFilePreview.stopActiveInlineAudioIfDetached([existingNode]);
533
+ existingNode.remove();
534
+ }
535
535
 
536
536
  const nextNode = this._createInfoListItem(file, i, $itemsTemplate, $itemsTemplateClasses, fileKey, signature);
537
537
  nextNodes.push(nextNode);
@@ -935,28 +935,28 @@ class VGFilesBase extends BaseModule {
935
935
  Selectors.findAll('[data-vg-files="generated"]', this._element).forEach(el => el.remove());
936
936
  }
937
937
 
938
- clear(resetInput = true) {
939
- const detachedNodes = [];
940
- if (this._nodes.info) {
941
- const $infoList = Selectors.find(`.${this._getClass('info-list')}`, this._element);
942
- if ($infoList?.children?.length) {
943
- detachedNodes.push(...Array.from($infoList.children));
944
- }
945
- }
946
- if (this._nodes.drop) {
947
- const $dropList = Selectors.find(`.${this._getClass('drop-list')}`, this._element);
948
- if ($dropList?.children?.length) {
949
- detachedNodes.push(...Array.from($dropList.children));
950
- }
951
- }
952
- if (detachedNodes.length) {
953
- VGFilePreview.stopActiveInlineAudioIfDetached(detachedNodes);
954
- }
955
-
956
- this._revokeUrls();
957
- if (resetInput) {
958
- this._resetFileInput();
959
- }
938
+ clear(resetInput = true) {
939
+ const detachedNodes = [];
940
+ if (this._nodes.info) {
941
+ const $infoList = Selectors.find(`.${this._getClass('info-list')}`, this._element);
942
+ if ($infoList?.children?.length) {
943
+ detachedNodes.push(...Array.from($infoList.children));
944
+ }
945
+ }
946
+ if (this._nodes.drop) {
947
+ const $dropList = Selectors.find(`.${this._getClass('drop-list')}`, this._element);
948
+ if ($dropList?.children?.length) {
949
+ detachedNodes.push(...Array.from($dropList.children));
950
+ }
951
+ }
952
+ if (detachedNodes.length) {
953
+ VGFilePreview.stopActiveInlineAudioIfDetached(detachedNodes);
954
+ }
955
+
956
+ this._revokeUrls();
957
+ if (resetInput) {
958
+ this._resetFileInput();
959
+ }
960
960
  this._cleanupFakeInputs();
961
961
  this._cleanupErrors();
962
962
  this._files = [];
@@ -13,11 +13,11 @@ const CLASS_NAME_DROP_HOVER = 'drop-hover';
13
13
  const NAME = 'drop-and-drop';
14
14
  const NAME_KEY = 'vg.drop-and-drop';
15
15
 
16
- class VGFilesDroppable extends BaseModule {
17
- /**
18
- * @param {HTMLElement} element
19
- * @param {Object} params
20
- */
16
+ class VGFilesDroppable extends BaseModule {
17
+ /**
18
+ * @param {HTMLElement} element
19
+ * @param {Object} params
20
+ */
21
21
  constructor(element, params = {}) {
22
22
  super(element, params);
23
23
  this._element = element;
@@ -31,15 +31,15 @@ class VGFilesDroppable extends BaseModule {
31
31
  static get NAME() { return NAME; }
32
32
  static get NAME_KEY() { return NAME_KEY; }
33
33
 
34
- _init() {
35
- VGFilesDroppable._instances.add(this);
36
- if (this._params?.smartdrop) {
37
- VGFilesDroppable._smartInstances.add(this);
38
- this._bindGlobalEvents();
39
- }
40
- this._findInput();
41
- this._setupEvents();
42
- }
34
+ _init() {
35
+ VGFilesDroppable._instances.add(this);
36
+ if (this._params?.smartdrop) {
37
+ VGFilesDroppable._smartInstances.add(this);
38
+ this._bindGlobalEvents();
39
+ }
40
+ this._findInput();
41
+ this._setupEvents();
42
+ }
43
43
 
44
44
  /**
45
45
  * Поиск связанного input[type="file"] внутри или рядом с drop-зоной
@@ -58,8 +58,8 @@ class VGFilesDroppable extends BaseModule {
58
58
  /**
59
59
  * Настройка событий перетаскивания
60
60
  */
61
- _setupEvents() {
62
- if (!isElement(this._element)) return;
61
+ _setupEvents() {
62
+ if (!isElement(this._element)) return;
63
63
 
64
64
  const isSortableDrag = (e) => {
65
65
  // 1) если sortable реально активен — на элементе есть класс dragging
@@ -69,76 +69,76 @@ class VGFilesDroppable extends BaseModule {
69
69
  let plain = '';
70
70
  try { plain = e.dataTransfer?.getData?.('text/plain') || ''; } catch (_) {}
71
71
  return plain === 'vgsortable';
72
- };
72
+ };
73
73
 
74
- EventHandler.on(this._element, 'dragover', (e) => {
75
- e.preventDefault();
76
- e.stopPropagation();
74
+ EventHandler.on(this._element, 'dragover', (e) => {
75
+ e.preventDefault();
76
+ e.stopPropagation();
77
77
 
78
78
  // ✅ Сортировка: НЕ подсвечиваем dropzone
79
- if (isSortableDrag(e)) {
80
- Classes.remove(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
81
- VGFilesDroppable._setDropMessageTitleState(this._element, false);
82
- e.dataTransfer.dropEffect = 'none';
83
- return;
84
- }
79
+ if (isSortableDrag(e)) {
80
+ Classes.remove(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
81
+ VGFilesDroppable._setDropMessageTitleState(this._element, false);
82
+ e.dataTransfer.dropEffect = 'none';
83
+ return;
84
+ }
85
85
 
86
86
  if (!Classes.has(this._element, CLASS_NAME_DROP_ACTIVE)) {
87
87
  Classes.add(this._element, CLASS_NAME_DROP_HOVER);
88
88
  }
89
89
  });
90
90
 
91
- EventHandler.on(this._element, 'dragenter', (e) => {
92
- e.preventDefault();
93
- e.stopPropagation();
91
+ EventHandler.on(this._element, 'dragenter', (e) => {
92
+ e.preventDefault();
93
+ e.stopPropagation();
94
94
 
95
95
  // ✅ Сортировка: НЕ подсвечиваем dropzone
96
- if (isSortableDrag(e)) {
97
- Classes.remove(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
98
- VGFilesDroppable._setDropMessageTitleState(this._element, false);
99
- return;
100
- }
101
-
102
- Classes.add(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
103
- VGFilesDroppable._setDropMessageTitleState(this._element, true);
104
- });
96
+ if (isSortableDrag(e)) {
97
+ Classes.remove(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
98
+ VGFilesDroppable._setDropMessageTitleState(this._element, false);
99
+ return;
100
+ }
101
+
102
+ Classes.add(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
103
+ VGFilesDroppable._setDropMessageTitleState(this._element, true);
104
+ });
105
105
 
106
106
  EventHandler.on(this._element, 'dragleave', (e) => {
107
107
  e.preventDefault();
108
108
  e.stopPropagation();
109
109
 
110
110
  // ✅ Сортировка: гарантированно без подсветки
111
- if (isSortableDrag(e)) {
112
- Classes.remove(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
113
- VGFilesDroppable._setDropMessageTitleState(this._element, false);
114
- return;
115
- }
111
+ if (isSortableDrag(e)) {
112
+ Classes.remove(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
113
+ VGFilesDroppable._setDropMessageTitleState(this._element, false);
114
+ return;
115
+ }
116
116
 
117
117
  if (e.target === this._element || e.target.closest('.' + CLASS_NAME_DROP) === this._element) {
118
118
  Classes.remove(this._element, CLASS_NAME_DROP_HOVER);
119
119
  setTimeout(() => {
120
- if (!this._element.matches(':hover')) {
121
- Classes.remove(this._element, CLASS_NAME_DROP_ACTIVE);
122
- VGFilesDroppable._setDropMessageTitleState(this._element, false);
123
- }
124
- }, 50);
125
- }
126
- });
127
-
128
- EventHandler.on(this._element, 'drop', (e) => {
129
- e.preventDefault();
130
- e.stopPropagation();
131
-
132
- Classes.remove(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
133
- VGFilesDroppable._setDropMessageTitleState(this._element, false);
120
+ if (!this._element.matches(':hover')) {
121
+ Classes.remove(this._element, CLASS_NAME_DROP_ACTIVE);
122
+ VGFilesDroppable._setDropMessageTitleState(this._element, false);
123
+ }
124
+ }, 50);
125
+ }
126
+ });
127
+
128
+ EventHandler.on(this._element, 'drop', (e) => {
129
+ e.preventDefault();
130
+ e.stopPropagation();
131
+
132
+ Classes.remove(this._element, [CLASS_NAME_DROP_ACTIVE, CLASS_NAME_DROP_HOVER]);
133
+ VGFilesDroppable._setDropMessageTitleState(this._element, false);
134
134
 
135
135
  // ✅ сортировка: не трогаем input
136
- if (isSortableDrag(e)) {
137
- return;
138
- }
139
-
140
- const files = e.dataTransfer?.files;
141
- if (!files || !files.length) return;
136
+ if (isSortableDrag(e)) {
137
+ return;
138
+ }
139
+
140
+ const files = e.dataTransfer?.files;
141
+ if (!files || !files.length) return;
142
142
 
143
143
  this._files = files;
144
144
 
@@ -146,209 +146,209 @@ class VGFilesDroppable extends BaseModule {
146
146
  this._input.files = files;
147
147
  EventHandler.trigger(this._input, 'change');
148
148
  }
149
- });
150
- }
151
-
152
- _bindGlobalEvents() {
153
- if (VGFilesDroppable._isGlobalEventsBound) return;
154
-
155
- VGFilesDroppable._globalHandlers = {
156
- dragenter: (e) => this._updateSuggestedDrop(e),
157
- dragover: (e) => this._updateSuggestedDrop(e),
158
- dragleave: (e) => {
159
- if (!this._isFileDrag(e)) return;
160
- if (e.relatedTarget === null || e.target === document || e.target === document.documentElement) {
161
- VGFilesDroppable._clearSuggestedDrop();
162
- }
163
- },
164
- drop: (e) => {
165
- if (!this._isFileDrag(e)) {
166
- VGFilesDroppable._clearSuggestedDrop();
167
- return;
168
- }
169
-
170
- const activeDrop = VGFilesDroppable._activeSuggestedDrop;
171
- if (activeDrop) {
172
- const instance = Array.from(VGFilesDroppable._smartInstances).find(i => i._element === activeDrop);
173
- const files = e.dataTransfer?.files;
174
-
175
- if (instance && files && files.length && isElement(instance._input)) {
176
- e.preventDefault();
177
- e.stopPropagation();
178
- instance._files = files;
179
- instance._input.files = files;
180
- EventHandler.trigger(instance._input, 'change');
181
- }
182
- }
183
-
184
- VGFilesDroppable._clearSuggestedDrop();
185
- },
186
- dragend: () => VGFilesDroppable._clearSuggestedDrop(),
187
- };
188
-
189
- EventHandler.on(document, 'dragenter', VGFilesDroppable._globalHandlers.dragenter);
190
- EventHandler.on(document, 'dragover', VGFilesDroppable._globalHandlers.dragover);
191
- EventHandler.on(document, 'dragleave', VGFilesDroppable._globalHandlers.dragleave);
192
- EventHandler.on(document, 'drop', VGFilesDroppable._globalHandlers.drop);
193
- EventHandler.on(document, 'dragend', VGFilesDroppable._globalHandlers.dragend);
194
-
195
- EventHandler.on(window, 'dragenter', VGFilesDroppable._globalHandlers.dragenter);
196
- EventHandler.on(window, 'dragover', VGFilesDroppable._globalHandlers.dragover);
197
- EventHandler.on(window, 'dragleave', VGFilesDroppable._globalHandlers.dragleave);
198
- EventHandler.on(window, 'drop', VGFilesDroppable._globalHandlers.drop);
199
- EventHandler.on(window, 'dragend', VGFilesDroppable._globalHandlers.dragend);
200
-
201
- VGFilesDroppable._isGlobalEventsBound = true;
202
- }
203
-
204
- _updateSuggestedDrop(e) {
205
- if (!this._isFileDrag(e)) {
206
- VGFilesDroppable._clearSuggestedDrop();
207
- return;
208
- }
209
-
210
- const visibleDrops = this._getVisibleDropZonesInViewport();
211
-
212
- if (visibleDrops.length === 1) {
213
- const [dropZone] = visibleDrops;
214
- if (VGFilesDroppable._activeSuggestedDrop !== dropZone) {
215
- VGFilesDroppable._clearSuggestedDrop();
216
- Classes.add(dropZone, CLASS_NAME_DROP_ACTIVE);
217
- VGFilesDroppable._setDropMessageTitleState(dropZone, true);
218
- VGFilesDroppable._activeSuggestedDrop = dropZone;
219
- }
220
-
221
- if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
222
- e.preventDefault();
223
- return;
224
- }
225
-
226
- VGFilesDroppable._clearSuggestedDrop();
227
- }
228
-
229
- _isFileDrag(e) {
230
- try {
231
- const dt = e?.dataTransfer;
232
- if (!dt) return false;
233
-
234
- if (dt.files && dt.files.length > 0) return true;
235
-
236
- if (dt.items && dt.items.length > 0) {
237
- return Array.from(dt.items).some(item => item.kind === 'file');
238
- }
239
-
240
- if (dt.types) {
241
- return Array.from(dt.types).includes('Files');
242
- }
243
-
244
- return false;
245
- } catch (_) {
246
- return false;
247
- }
248
- }
249
-
250
- _getVisibleDropZonesInViewport() {
251
- const dropZones = Array.from(Selectors.findAll(`.${CLASS_NAME_DROP}`) || []);
252
- return dropZones.filter((el) => this._isVisibleInViewport(el));
253
- }
254
-
255
- _isVisibleInViewport(el) {
256
- if (!isElement(el) || !el.isConnected) return false;
257
-
258
- const style = window.getComputedStyle(el);
259
- if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
260
- return false;
261
- }
262
-
263
- const rect = el.getBoundingClientRect();
264
- if (!rect.width || !rect.height) return false;
265
-
266
- const inViewport =
267
- rect.bottom > 0 &&
268
- rect.right > 0 &&
269
- rect.top < window.innerHeight &&
270
- rect.left < window.innerWidth;
271
-
272
- return inViewport;
273
- }
274
-
275
- static _clearSuggestedDrop() {
276
- if (!VGFilesDroppable._activeSuggestedDrop) return;
277
-
278
- VGFilesDroppable._setDropMessageTitleState(VGFilesDroppable._activeSuggestedDrop, false);
279
- Classes.remove(VGFilesDroppable._activeSuggestedDrop, CLASS_NAME_DROP_ACTIVE);
280
- VGFilesDroppable._activeSuggestedDrop = null;
281
- }
282
-
283
- static _setDropMessageTitleState(dropElement, isActive) {
284
- if (!isElement(dropElement)) return;
285
-
286
- const title = Selectors.find('.vg-files-drop-message .title', dropElement);
287
- if (!title) return;
288
-
289
- const originalText = (title.getAttribute('data-drop-original-text') || '').trim() || (title.textContent || '').trim();
290
- title.setAttribute('data-drop-original-text', originalText);
291
-
292
- const activeText = (dropElement.getAttribute('data-drop-active-text') || '').trim();
293
- if (isActive && activeText) {
294
- title.textContent = activeText;
295
- return;
296
- }
297
-
298
- title.textContent = originalText;
299
- }
300
-
301
- static _unbindGlobalEvents() {
302
- if (!VGFilesDroppable._isGlobalEventsBound) return;
303
-
304
- const handlers = VGFilesDroppable._globalHandlers || {};
305
-
306
- EventHandler.off(document, 'dragenter', handlers.dragenter);
307
- EventHandler.off(document, 'dragover', handlers.dragover);
308
- EventHandler.off(document, 'dragleave', handlers.dragleave);
309
- EventHandler.off(document, 'drop', handlers.drop);
310
- EventHandler.off(document, 'dragend', handlers.dragend);
311
-
312
- EventHandler.off(window, 'dragenter', handlers.dragenter);
313
- EventHandler.off(window, 'dragover', handlers.dragover);
314
- EventHandler.off(window, 'dragleave', handlers.dragleave);
315
- EventHandler.off(window, 'drop', handlers.drop);
316
- EventHandler.off(window, 'dragend', handlers.dragend);
317
-
318
- VGFilesDroppable._isGlobalEventsBound = false;
319
- VGFilesDroppable._globalHandlers = null;
320
- VGFilesDroppable._clearSuggestedDrop();
321
- }
149
+ });
150
+ }
151
+
152
+ _bindGlobalEvents() {
153
+ if (VGFilesDroppable._isGlobalEventsBound) return;
154
+
155
+ VGFilesDroppable._globalHandlers = {
156
+ dragenter: (e) => this._updateSuggestedDrop(e),
157
+ dragover: (e) => this._updateSuggestedDrop(e),
158
+ dragleave: (e) => {
159
+ if (!this._isFileDrag(e)) return;
160
+ if (e.relatedTarget === null || e.target === document || e.target === document.documentElement) {
161
+ VGFilesDroppable._clearSuggestedDrop();
162
+ }
163
+ },
164
+ drop: (e) => {
165
+ if (!this._isFileDrag(e)) {
166
+ VGFilesDroppable._clearSuggestedDrop();
167
+ return;
168
+ }
169
+
170
+ const activeDrop = VGFilesDroppable._activeSuggestedDrop;
171
+ if (activeDrop) {
172
+ const instance = Array.from(VGFilesDroppable._smartInstances).find(i => i._element === activeDrop);
173
+ const files = e.dataTransfer?.files;
174
+
175
+ if (instance && files && files.length && isElement(instance._input)) {
176
+ e.preventDefault();
177
+ e.stopPropagation();
178
+ instance._files = files;
179
+ instance._input.files = files;
180
+ EventHandler.trigger(instance._input, 'change');
181
+ }
182
+ }
183
+
184
+ VGFilesDroppable._clearSuggestedDrop();
185
+ },
186
+ dragend: () => VGFilesDroppable._clearSuggestedDrop(),
187
+ };
188
+
189
+ EventHandler.on(document, 'dragenter', VGFilesDroppable._globalHandlers.dragenter);
190
+ EventHandler.on(document, 'dragover', VGFilesDroppable._globalHandlers.dragover);
191
+ EventHandler.on(document, 'dragleave', VGFilesDroppable._globalHandlers.dragleave);
192
+ EventHandler.on(document, 'drop', VGFilesDroppable._globalHandlers.drop);
193
+ EventHandler.on(document, 'dragend', VGFilesDroppable._globalHandlers.dragend);
194
+
195
+ EventHandler.on(window, 'dragenter', VGFilesDroppable._globalHandlers.dragenter);
196
+ EventHandler.on(window, 'dragover', VGFilesDroppable._globalHandlers.dragover);
197
+ EventHandler.on(window, 'dragleave', VGFilesDroppable._globalHandlers.dragleave);
198
+ EventHandler.on(window, 'drop', VGFilesDroppable._globalHandlers.drop);
199
+ EventHandler.on(window, 'dragend', VGFilesDroppable._globalHandlers.dragend);
200
+
201
+ VGFilesDroppable._isGlobalEventsBound = true;
202
+ }
203
+
204
+ _updateSuggestedDrop(e) {
205
+ if (!this._isFileDrag(e)) {
206
+ VGFilesDroppable._clearSuggestedDrop();
207
+ return;
208
+ }
209
+
210
+ const visibleDrops = this._getVisibleDropZonesInViewport();
211
+
212
+ if (visibleDrops.length === 1) {
213
+ const [dropZone] = visibleDrops;
214
+ if (VGFilesDroppable._activeSuggestedDrop !== dropZone) {
215
+ VGFilesDroppable._clearSuggestedDrop();
216
+ Classes.add(dropZone, CLASS_NAME_DROP_ACTIVE);
217
+ VGFilesDroppable._setDropMessageTitleState(dropZone, true);
218
+ VGFilesDroppable._activeSuggestedDrop = dropZone;
219
+ }
220
+
221
+ if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
222
+ e.preventDefault();
223
+ return;
224
+ }
225
+
226
+ VGFilesDroppable._clearSuggestedDrop();
227
+ }
228
+
229
+ _isFileDrag(e) {
230
+ try {
231
+ const dt = e?.dataTransfer;
232
+ if (!dt) return false;
233
+
234
+ if (dt.files && dt.files.length > 0) return true;
235
+
236
+ if (dt.items && dt.items.length > 0) {
237
+ return Array.from(dt.items).some(item => item.kind === 'file');
238
+ }
239
+
240
+ if (dt.types) {
241
+ return Array.from(dt.types).includes('Files');
242
+ }
243
+
244
+ return false;
245
+ } catch (_) {
246
+ return false;
247
+ }
248
+ }
249
+
250
+ _getVisibleDropZonesInViewport() {
251
+ const dropZones = Array.from(Selectors.findAll(`.${CLASS_NAME_DROP}`) || []);
252
+ return dropZones.filter((el) => this._isVisibleInViewport(el));
253
+ }
254
+
255
+ _isVisibleInViewport(el) {
256
+ if (!isElement(el) || !el.isConnected) return false;
257
+
258
+ const style = window.getComputedStyle(el);
259
+ if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
260
+ return false;
261
+ }
262
+
263
+ const rect = el.getBoundingClientRect();
264
+ if (!rect.width || !rect.height) return false;
265
+
266
+ const inViewport =
267
+ rect.bottom > 0 &&
268
+ rect.right > 0 &&
269
+ rect.top < window.innerHeight &&
270
+ rect.left < window.innerWidth;
271
+
272
+ return inViewport;
273
+ }
274
+
275
+ static _clearSuggestedDrop() {
276
+ if (!VGFilesDroppable._activeSuggestedDrop) return;
277
+
278
+ VGFilesDroppable._setDropMessageTitleState(VGFilesDroppable._activeSuggestedDrop, false);
279
+ Classes.remove(VGFilesDroppable._activeSuggestedDrop, CLASS_NAME_DROP_ACTIVE);
280
+ VGFilesDroppable._activeSuggestedDrop = null;
281
+ }
282
+
283
+ static _setDropMessageTitleState(dropElement, isActive) {
284
+ if (!isElement(dropElement)) return;
285
+
286
+ const title = Selectors.find('.vg-files-drop-message .title', dropElement);
287
+ if (!title) return;
288
+
289
+ const originalText = (title.getAttribute('data-drop-original-text') || '').trim() || (title.textContent || '').trim();
290
+ title.setAttribute('data-drop-original-text', originalText);
291
+
292
+ const activeText = (dropElement.getAttribute('data-drop-active-text') || '').trim();
293
+ if (isActive && activeText) {
294
+ title.textContent = activeText;
295
+ return;
296
+ }
297
+
298
+ title.textContent = originalText;
299
+ }
300
+
301
+ static _unbindGlobalEvents() {
302
+ if (!VGFilesDroppable._isGlobalEventsBound) return;
303
+
304
+ const handlers = VGFilesDroppable._globalHandlers || {};
305
+
306
+ EventHandler.off(document, 'dragenter', handlers.dragenter);
307
+ EventHandler.off(document, 'dragover', handlers.dragover);
308
+ EventHandler.off(document, 'dragleave', handlers.dragleave);
309
+ EventHandler.off(document, 'drop', handlers.drop);
310
+ EventHandler.off(document, 'dragend', handlers.dragend);
311
+
312
+ EventHandler.off(window, 'dragenter', handlers.dragenter);
313
+ EventHandler.off(window, 'dragover', handlers.dragover);
314
+ EventHandler.off(window, 'dragleave', handlers.dragleave);
315
+ EventHandler.off(window, 'drop', handlers.drop);
316
+ EventHandler.off(window, 'dragend', handlers.dragend);
317
+
318
+ VGFilesDroppable._isGlobalEventsBound = false;
319
+ VGFilesDroppable._globalHandlers = null;
320
+ VGFilesDroppable._clearSuggestedDrop();
321
+ }
322
322
 
323
323
  getFiles() {
324
324
  return this._files;
325
325
  }
326
326
 
327
- dispose() {
328
- EventHandler.off(this._element, 'dragover');
329
- EventHandler.off(this._element, 'dragenter');
330
- EventHandler.off(this._element, 'dragleave');
331
- EventHandler.off(this._element, 'drop');
332
-
333
- VGFilesDroppable._instances.delete(this);
334
- VGFilesDroppable._smartInstances.delete(this);
335
- if (!VGFilesDroppable._smartInstances.size) {
336
- VGFilesDroppable._unbindGlobalEvents();
337
- }
338
-
339
- this._input = null;
340
- this._files = null;
341
- }
327
+ dispose() {
328
+ EventHandler.off(this._element, 'dragover');
329
+ EventHandler.off(this._element, 'dragenter');
330
+ EventHandler.off(this._element, 'dragleave');
331
+ EventHandler.off(this._element, 'drop');
332
+
333
+ VGFilesDroppable._instances.delete(this);
334
+ VGFilesDroppable._smartInstances.delete(this);
335
+ if (!VGFilesDroppable._smartInstances.size) {
336
+ VGFilesDroppable._unbindGlobalEvents();
337
+ }
338
+
339
+ this._input = null;
340
+ this._files = null;
341
+ }
342
342
 
343
343
  init() {
344
344
  return this;
345
345
  }
346
- }
347
-
348
- VGFilesDroppable._instances = new Set();
349
- VGFilesDroppable._smartInstances = new Set();
350
- VGFilesDroppable._isGlobalEventsBound = false;
351
- VGFilesDroppable._activeSuggestedDrop = null;
352
- VGFilesDroppable._globalHandlers = null;
353
-
354
- export default VGFilesDroppable;
346
+ }
347
+
348
+ VGFilesDroppable._instances = new Set();
349
+ VGFilesDroppable._smartInstances = new Set();
350
+ VGFilesDroppable._isGlobalEventsBound = false;
351
+ VGFilesDroppable._activeSuggestedDrop = null;
352
+ VGFilesDroppable._globalHandlers = null;
353
+
354
+ export default VGFilesDroppable;